From 39250a06fe630a730cb1a736b29dda0062992999 Mon Sep 17 00:00:00 2001 From: Charles Oliver Nutter Date: Sat, 9 Sep 2023 18:36:19 +0200 Subject: [PATCH] Store absolute path of main at load time 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 #7394 --- core/src/main/java/org/jruby/Ruby.java | 3 ++ core/src/main/java/org/jruby/RubyKernel.java | 16 ++++++--- core/src/main/java/org/jruby/RubyThread.java | 5 ++- .../org/jruby/runtime/load/LoadService.java | 34 +++++++++++++++++++ .../backtrace/location/absolute_path_tags.txt | 1 - 5 files changed, 52 insertions(+), 7 deletions(-) diff --git a/core/src/main/java/org/jruby/Ruby.java b/core/src/main/java/org/jruby/Ruby.java index c7c9ed0b623..5bc5866d694 100644 --- a/core/src/main/java/org/jruby/Ruby.java +++ b/core/src/main/java/org/jruby/Ruby.java @@ -923,6 +923,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 entry : config.getOptionGlobals().entrySet()) { final IRubyObject varvalue; if (entry.getValue() != null) { diff --git a/core/src/main/java/org/jruby/RubyKernel.java b/core/src/main/java/org/jruby/RubyKernel.java index 9eee86be76e..b8eb27de1c6 100644 --- a/core/src/main/java/org/jruby/RubyKernel.java +++ b/core/src/main/java/org/jruby/RubyKernel.java @@ -1036,10 +1036,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, - realpath); + IRubyObject dirname = RubyFile.dirname(context, fileClass, realpath); IRubyObject absoluteFeature = RubyFile.expand_path(context, fileClass, relativePath, dirname); return RubyKernel.require(context, runtime.getKernel(), absoluteFeature, Block.NULL_BLOCK); @@ -1970,10 +1971,15 @@ public static IRubyObject __method__(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) diff --git a/core/src/main/java/org/jruby/RubyThread.java b/core/src/main/java/org/jruby/RubyThread.java index 3fe578e27d0..131b721f6f0 100644 --- a/core/src/main/java/org/jruby/RubyThread.java +++ b/core/src/main/java/org/jruby/RubyThread.java @@ -81,6 +81,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; @@ -482,7 +483,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 diff --git a/core/src/main/java/org/jruby/runtime/load/LoadService.java b/core/src/main/java/org/jruby/runtime/load/LoadService.java index 8de10f5dc52..affc7d73837 100644 --- a/core/src/main/java/org/jruby/runtime/load/LoadService.java +++ b/core/src/main/java/org/jruby/runtime/load/LoadService.java @@ -192,6 +192,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) { @@ -1028,6 +1031,37 @@ 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; + } + // @Deprecated protected String getFileName(JRubyFile file, String namePlusSuffix) { diff --git a/spec/tags/ruby/core/thread/backtrace/location/absolute_path_tags.txt b/spec/tags/ruby/core/thread/backtrace/location/absolute_path_tags.txt index 61f825539e0..82ac9a0d5c8 100644 --- a/spec/tags/ruby/core/thread/backtrace/location/absolute_path_tags.txt +++ b/spec/tags/ruby/core/thread/backtrace/location/absolute_path_tags.txt @@ -2,5 +2,4 @@ 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