Skip to content

Commit

Permalink
Store absolute path of main at load time
Browse files Browse the repository at this point in the history
This fix allows __dir__ and Thread::Backtrace::Location and
require_relative to use the proper path for a main script
specified without a full path.

Fixes jruby#7394
  • Loading branch information
headius committed Sep 9, 2023
1 parent 036169d commit 3ba6bf0
Show file tree
Hide file tree
Showing 5 changed files with 50 additions and 6 deletions.
3 changes: 3 additions & 0 deletions core/src/main/java/org/jruby/Ruby.java
Original file line number Diff line number Diff line change
Expand Up @@ -936,6 +936,9 @@ public void runFromMain(InputStream inputStream, String filename) {
getGlobalVariables().define("$PROGRAM_NAME", d, GLOBAL);
getGlobalVariables().define("$0", d, GLOBAL);

// set main script and canonical path for require_relative use
loadService.setMainScript(filename, getCurrentDirectory());

for (Map.Entry<String, String> entry : config.getOptionGlobals().entrySet()) {
final IRubyObject varvalue;
if (entry.getValue() != null) {
Expand Down
13 changes: 10 additions & 3 deletions core/src/main/java/org/jruby/RubyKernel.java
Original file line number Diff line number Diff line change
Expand Up @@ -1090,6 +1090,8 @@ public static IRubyObject require_relative(ThreadContext context, IRubyObject re
throw runtime.newLoadError("cannot infer basepath");
}

file = runtime.getLoadService().getPathForLocation(file);

RubyClass fileClass = runtime.getFile();
IRubyObject realpath = RubyFile.realpath(context, fileClass, runtime.newString(file));
IRubyObject dirname = RubyFile.dirname(context, fileClass, new IRubyObject[] { realpath });
Expand Down Expand Up @@ -2082,10 +2084,15 @@ public static IRubyObject __callee__(ThreadContext context, IRubyObject recv) {

@JRubyMethod(name = "__dir__", module = true, visibility = PRIVATE, reads = FILENAME)
public static IRubyObject __dir__(ThreadContext context, IRubyObject recv) {
Ruby runtime = context.runtime;

// NOTE: not using __FILE__ = context.getFile() since it won't work with JIT
final String __FILE__ = context.getSingleBacktrace().getFileName();
RubyString path = RubyFile.expandPathInternal(context, RubyString.newString(context.runtime, __FILE__), null, false, true);
return RubyString.newString(context.runtime, RubyFile.dirname(context, path.asJavaString()));
String __FILE__ = context.getSingleBacktrace().getFileName();

__FILE__ = runtime.getLoadService().getPathForLocation(__FILE__);

RubyString path = RubyFile.expandPathInternal(context, RubyString.newString(runtime, __FILE__), null, false, true);
return RubyString.newString(runtime, RubyFile.dirname(context, path.asJavaString()));
}

@JRubyMethod(module = true)
Expand Down
4 changes: 3 additions & 1 deletion core/src/main/java/org/jruby/RubyThread.java
Original file line number Diff line number Diff line change
Expand Up @@ -496,7 +496,9 @@ public Location(Ruby runtime, RubyClass klass, RubyStackTraceElement element) {

@JRubyMethod
public IRubyObject absolute_path(ThreadContext context) {
return context.runtime.newString(element.getFileName());
Ruby runtime = context.runtime;
return runtime.newString(
runtime.getLoadService().getPathForLocation(element.getFileName()));
}

@JRubyMethod
Expand Down
34 changes: 34 additions & 0 deletions core/src/main/java/org/jruby/runtime/load/LoadService.java
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,9 @@ public String[] getSuffixes() {
protected final Ruby runtime;
protected LibrarySearcher librarySearcher;

protected String mainScript;
protected String mainScriptPath;

public LoadService(Ruby runtime) {
this.runtime = runtime;
if (RubyInstanceConfig.DEBUG_LOAD_TIMINGS) {
Expand Down Expand Up @@ -983,5 +986,36 @@ protected String resolveLoadName(LoadServiceResource foundResource, String previ
}
return resolveLoadName(foundResource, previousPath);
}

public String getMainScript() {
return mainScript;
}

public String getMainScriptPath() {
return mainScriptPath;
}

public void setMainScript(String filename, String cwd) {
this.mainScript = filename;
File mainFile = new File(filename);

if (!mainFile.isAbsolute()) {
mainFile = new File(cwd, filename);
}

if (mainFile.exists()) {
this.mainScriptPath = mainFile.getAbsolutePath();
} else {
this.mainScriptPath = filename;
}
}

public String getPathForLocation(String filename) {
if (filename.equals(mainScript)) {
return mainScriptPath;
}

return filename;
}
//</editor-fold>
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,5 @@ fails:Thread::Backtrace::Location#absolute_path returns a canonical path without
fails:Thread::Backtrace::Location#absolute_path returns a canonical path without symlinks, even when __FILE__ is removed
fails:Thread::Backtrace::Location#absolute_path canonicalization returns a canonical path without symlinks, even when __FILE__ does not
fails:Thread::Backtrace::Location#absolute_path canonicalization returns a canonical path without symlinks, even when __FILE__ is removed
fails:Thread::Backtrace::Location#absolute_path returns an absolute path when using a relative main script path
fails:Thread::Backtrace::Location#absolute_path when used in a core method returns nil
fails:Thread::Backtrace::Location#absolute_path when used in eval with a given filename returns nil with absolute_path
fails:Thread::Backtrace::Location#absolute_path returns the correct absolute path when using a relative main script path and changing CWD

0 comments on commit 3ba6bf0

Please sign in to comment.