From 3ba6bf06b171f6940b0b29caa92362f6745ad686 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. Fixes #7394 --- core/src/main/java/org/jruby/Ruby.java | 3 ++ core/src/main/java/org/jruby/RubyKernel.java | 13 +++++-- core/src/main/java/org/jruby/RubyThread.java | 4 ++- .../org/jruby/runtime/load/LoadService.java | 34 +++++++++++++++++++ .../backtrace/location/absolute_path_tags.txt | 2 -- 5 files changed, 50 insertions(+), 6 deletions(-) diff --git a/core/src/main/java/org/jruby/Ruby.java b/core/src/main/java/org/jruby/Ruby.java index f3b5957239ad..0a65b89ddde3 100644 --- a/core/src/main/java/org/jruby/Ruby.java +++ b/core/src/main/java/org/jruby/Ruby.java @@ -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 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 3af2517e92a9..dc821baa26e3 100644 --- a/core/src/main/java/org/jruby/RubyKernel.java +++ b/core/src/main/java/org/jruby/RubyKernel.java @@ -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 }); @@ -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) diff --git a/core/src/main/java/org/jruby/RubyThread.java b/core/src/main/java/org/jruby/RubyThread.java index 1c15d2fb2e37..62714af4c222 100644 --- a/core/src/main/java/org/jruby/RubyThread.java +++ b/core/src/main/java/org/jruby/RubyThread.java @@ -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 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 4f2b207dc7c2..e548c6a1f83e 100644 --- a/core/src/main/java/org/jruby/runtime/load/LoadService.java +++ b/core/src/main/java/org/jruby/runtime/load/LoadService.java @@ -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) { @@ -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; + } // } 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 ca40c7831e30..fdfd944c10b4 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,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