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.

It's a big hacky to store the path specifically for the main
script this way, but it's the only such special case and works
well enough to get these cases working.

Fixes jruby#7394
  • Loading branch information
headius committed Sep 9, 2023
1 parent 036169d commit a743076
Show file tree
Hide file tree
Showing 5 changed files with 52 additions and 7 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
15 changes: 11 additions & 4 deletions core/src/main/java/org/jruby/RubyKernel.java
Original file line number Diff line number Diff line change
Expand Up @@ -1090,9 +1090,11 @@ 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 });
IRubyObject dirname = RubyFile.dirname(context, fileClass, new IRubyObject[]{realpath});
IRubyObject absoluteFeature = RubyFile.expand_path(context, fileClass, relativePath, dirname);

return RubyKernel.require(context, runtime.getKernel(), absoluteFeature, Block.NULL_BLOCK);
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
5 changes: 4 additions & 1 deletion core/src/main/java/org/jruby/RubyThread.java
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@
import org.jruby.runtime.backtrace.FrameType;
import org.jruby.runtime.backtrace.RubyStackTraceElement;
import org.jruby.runtime.builtin.IRubyObject;
import org.jruby.runtime.load.LoadService;
import org.jruby.util.ByteList;
import org.jruby.util.StringSupport;
import org.jruby.util.io.BlockingIO;
Expand Down Expand Up @@ -496,7 +497,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 a743076

Please sign in to comment.