Project

General

Profile

Actions

Bug #20175

closed

Broken File.dirname(__FILE__) in eval blocks

Added by kiskoza (Zsolt Kozaroczy) over 1 year ago. Updated over 1 year ago.

Status:
Feedback
Assignee:
-
Target version:
-
ruby -v:
ruby 3.3.0 (2023-12-25 revision 5124f9ac75) [x86_64-linux]
[ruby-core:116140]

Description

Since #19755 eval use caller location by default, however, it broke File.dirname(__FILE__) in some cases.

# eval.rb
eval('puts file: __FILE__, dirname: File.dirname(__FILE__)')

Up to Ruby 3.2.2 it gave back the same results, even if it wasn't technically correct in some cases

## Ruby 3.2.2

ruby eval.rb
# {:file=>"(eval)", :dirname=>"."}

ruby ./eval.rb
# {:file=>"(eval)", :dirname=>"."}

cd folder && ruby ../eval.rb
# {:file=>"(eval)", :dirname=>"."}
# This one is not pointing to the right directory, but still returns a valid path

ruby /Codes/eval.rb
# {:file=>"(eval)", :dirname=>"."}
# This one is not pointing to the right directory, but still returns a valid path

In Ruby 3.3.0 (introduced in 43a5c19135), it gives back different paths, trying to point to the right directory, but it has the (eval at prefix which makes it broken for codes expecting a valid path.

## Ruby 3.3.0

ruby eval.rb
# {:file=>"(eval at eval.rb:1)", :dirname=>"."}

ruby ./eval.rb
# {:file=>"(eval at ./eval.rb:1)", :dirname=>"(eval at ."}
# Broken path

cd folder && ruby ../eval.rb
# {:file=>"(eval at ../eval.rb:1)", :dirname=>"(eval at .."}
# This one is trying to point to the right directory, but it has a broken syntax

ruby /Codes/eval.rb
# {:file=>"(eval at /Codes/eval.rb:1)", :dirname=>"(eval at /Codes"}
# This one is trying to point to the right directory, but it has a broken syntax

I was able to reproduce it on current master as well.

Updated by mame (Yusuke Endoh) over 1 year ago

Changing __FILE__ is an intended change, so it is basically unavoidable. I think it is very accidental that File.dirname("(eval)") returns ".". Please do not depend on it.

You may want to replace File.dirname(__FILE__) with __dir__. If it is difficult for some reason, please elaborate on the context.

Updated by nobu (Nobuyoshi Nakada) over 1 year ago

mame (Yusuke Endoh) wrote in #note-1:

You may want to replace File.dirname(__FILE__) with __dir__.

Note that __dir__ will return nil not a string in that case.

Updated by kiskoza (Zsolt Kozaroczy) over 1 year ago

I see why __FILE__ was an intended change, I'm going to change all my codes inside eval that depends on its value, thanks for pointing it out.

However, as __dir__ returns nil instead of a string, it requires some changes to replace File.dirname(__FILE__) with __dir__.
Is it a viable option to return the already known directory inside evals? The change should be something like this:

diff --git a/vm_eval.c b/vm_eval.c
--- a/vm_eval.c
+++ b/vm_eval.c
@@ -2545,7 +2545,8 @@ rb_current_realfilepath(void)
             const char *const ptr = RSTRING_PTR(path);
             if (ptr[len - 1] == ')' &&
                 memcmp(ptr, "("EVAL_LOCATION_MARK, EVAL_LOCATION_MARK_LEN+1) == 0) {
-                return Qnil;
+                VALUE implicit_path = rb_str_substr(path, 1 + EVAL_LOCATION_MARK_LEN, len - 1);
+                return implicit_path;
             }
         }

and then the output would be this:

# eval.rb
eval('puts file: __FILE__, dir: __dir__')
ruby /Codes/eval.rb
# {:file=>"(eval at /Codes/eval.rb:1)", :dir=>"/Codes"}

Updated by mame (Yusuke Endoh) over 1 year ago

__dir__ || "." is good enough?

Actions #5

Updated by jeremyevans0 (Jeremy Evans) over 1 year ago

  • Status changed from Open to Feedback
Actions

Also available in: Atom PDF

Like0
Like0Like0Like0Like0Like0