diff --git a/spec/ruby/core/basicobject/instance_eval_spec.rb b/spec/ruby/core/basicobject/instance_eval_spec.rb index 699c965171f7d5..1f3a43f341b7fb 100644 --- a/spec/ruby/core/basicobject/instance_eval_spec.rb +++ b/spec/ruby/core/basicobject/instance_eval_spec.rb @@ -84,6 +84,13 @@ def foo end + ruby_version_is "3.3" do + it "uses the caller location as default location" do + f = Object.new + f.instance_eval("[__FILE__, __LINE__]").should == ["(eval at #{__FILE__}:#{__LINE__})", 1] + end + end + it "has access to receiver's instance variables" do BasicObjectSpecs::IVars.new.instance_eval { @secret }.should == 99 BasicObjectSpecs::IVars.new.instance_eval("@secret").should == 99 diff --git a/spec/ruby/core/binding/eval_spec.rb b/spec/ruby/core/binding/eval_spec.rb index 1b3b670b5ec6f3..bb2036f73911c4 100644 --- a/spec/ruby/core/binding/eval_spec.rb +++ b/spec/ruby/core/binding/eval_spec.rb @@ -60,10 +60,12 @@ bind.eval("#foo\n__LINE__", "(test)", 88).should == 89 end - it "uses (eval) as __FILE__ if single argument given" do - obj = BindingSpecs::Demo.new(1) - bind = obj.get_binding - bind.eval("__FILE__").should == '(eval)' + ruby_version_is ""..."3.3" do + it "uses (eval) as __FILE__ if single argument given" do + obj = BindingSpecs::Demo.new(1) + bind = obj.get_binding + bind.eval("__FILE__").should == '(eval)' + end end it "uses 1 as __LINE__" do @@ -104,4 +106,10 @@ bind.eval("'bar'.foo").should == "foo" end + + ruby_version_is "3.3" do + it "uses the caller location as default filename" do + binding.eval("[__FILE__, __LINE__]").should == ["(eval at #{__FILE__}:#{__LINE__})", 1] + end + end end diff --git a/spec/ruby/core/kernel/eval_spec.rb b/spec/ruby/core/kernel/eval_spec.rb index e6761ab789dd05..fe710826fae8b3 100644 --- a/spec/ruby/core/kernel/eval_spec.rb +++ b/spec/ruby/core/kernel/eval_spec.rb @@ -159,20 +159,37 @@ class Object end end - it "uses (eval) filename if none is provided" do - eval("__FILE__").should == "(eval)" - eval("__FILE__", binding).should == "(eval)" - eval("__FILE__", binding, "success").should == "success" - eval("eval '__FILE__', binding").should == "(eval)" - eval("eval '__FILE__', binding", binding).should == "(eval)" - eval("eval '__FILE__', binding", binding, 'success').should == '(eval)' - eval("eval '__FILE__', binding, 'success'", binding).should == 'success' - end + ruby_version_is ""..."3.3" do + it "uses (eval) filename if none is provided" do + eval("__FILE__").should == "(eval)" + eval("__FILE__", binding).should == "(eval)" + eval("__FILE__", binding, "success").should == "success" + eval("eval '__FILE__', binding").should == "(eval)" + eval("eval '__FILE__', binding", binding).should == "(eval)" + eval("eval '__FILE__', binding", binding, 'success').should == '(eval)' + eval("eval '__FILE__', binding, 'success'", binding).should == 'success' + end - it 'uses (eval) for __FILE__ and 1 for __LINE__ with a binding argument' do - eval("[__FILE__, __LINE__]", binding).should == ["(eval)", 1] + it 'uses (eval) for __FILE__ and 1 for __LINE__ with a binding argument' do + eval("[__FILE__, __LINE__]", binding).should == ["(eval)", 1] + end end + ruby_version_is "3.3" do + it "uses (eval at __FILE__:__LINE__) if none is provided" do + eval("__FILE__").should == "(eval at #{__FILE__}:#{__LINE__})" + eval("__FILE__", binding).should == "(eval at #{__FILE__}:#{__LINE__})" + eval("__FILE__", binding, "success").should == "success" + eval("eval '__FILE__', binding").should == "(eval at #{__FILE__}:#{__LINE__})" + eval("eval '__FILE__', binding", binding).should == "(eval at #{__FILE__}:#{__LINE__})" + eval("eval '__FILE__', binding", binding, 'success').should == "(eval at #{__FILE__}:#{__LINE__})" + eval("eval '__FILE__', binding, 'success'", binding).should == 'success' + end + + it 'uses (eval at __FILE__:__LINE__) for __FILE__ and 1 for __LINE__ with a binding argument' do + eval("[__FILE__, __LINE__]", binding).should == ["(eval at #{__FILE__}:#{__LINE__})", 1] + end + end # Found via Rubinius bug github:#149 it "does not alter the value of __FILE__ in the binding" do first_time = EvalSpecs.call_eval diff --git a/spec/ruby/core/module/shared/class_eval.rb b/spec/ruby/core/module/shared/class_eval.rb index 9ef7b5be44c5c6..b1d5cb3814edea 100644 --- a/spec/ruby/core/module/shared/class_eval.rb +++ b/spec/ruby/core/module/shared/class_eval.rb @@ -52,6 +52,12 @@ def foo ModuleSpecs.send(@method, "[__FILE__, __LINE__]", "test", 102).should == ["test", 102] end + ruby_version_is "3.3" do + it "uses the caller location as default filename" do + ModuleSpecs.send(@method, "[__FILE__, __LINE__]").should == ["(eval at #{__FILE__}:#{__LINE__})", 1] + end + end + it "converts a non-string filename to a string using to_str" do (file = mock(__FILE__)).should_receive(:to_str).and_return(__FILE__) ModuleSpecs.send(@method, "1+1", file) diff --git a/spec/ruby/core/tracepoint/path_spec.rb b/spec/ruby/core/tracepoint/path_spec.rb index 5b6c6d4cfce02b..dc2ca840b80ca5 100644 --- a/spec/ruby/core/tracepoint/path_spec.rb +++ b/spec/ruby/core/tracepoint/path_spec.rb @@ -13,14 +13,29 @@ path.should == "#{__FILE__}" end - it 'equals (eval) inside an eval for :end event' do - path = nil - TracePoint.new(:end) { |tp| - next unless TracePointSpec.target_thread? - path = tp.path - }.enable do - eval("module TracePointSpec; end") + ruby_version_is ""..."3.3" do + it 'equals (eval) inside an eval for :end event' do + path = nil + TracePoint.new(:end) { |tp| + next unless TracePointSpec.target_thread? + path = tp.path + }.enable do + eval("module TracePointSpec; end") + end + path.should == '(eval)' + end + end + + ruby_version_is "3.3" do + it 'equals "(eval at __FILE__:__LINE__)" inside an eval for :end event' do + path = nil + TracePoint.new(:end) { |tp| + next unless TracePointSpec.target_thread? + path = tp.path + }.enable do + eval("module TracePointSpec; end") + end + path.should == "(eval at #{__FILE__}:#{__LINE__ - 2})" end - path.should == '(eval)' end end diff --git a/spec/ruby/language/defined_spec.rb b/spec/ruby/language/defined_spec.rb index 104434adfe88ad..b84fe9394ad20d 100644 --- a/spec/ruby/language/defined_spec.rb +++ b/spec/ruby/language/defined_spec.rb @@ -203,7 +203,7 @@ it "warns about the void context when parsing it" do -> { eval "defined?(DefinedSpecs.side_effects / 2); 42" - }.should complain("(eval):1: warning: possibly useless use of defined? in void context\n", verbose: true) + }.should complain(/warning: possibly useless use of defined\? in void context/, verbose: true) end end end diff --git a/spec/ruby/language/file_spec.rb b/spec/ruby/language/file_spec.rb index 136b262d07da5b..59563d9642e00e 100644 --- a/spec/ruby/language/file_spec.rb +++ b/spec/ruby/language/file_spec.rb @@ -7,8 +7,16 @@ -> { eval("__FILE__ = 1") }.should raise_error(SyntaxError) end - it "equals (eval) inside an eval" do - eval("__FILE__").should == "(eval)" + ruby_version_is ""..."3.3" do + it "equals (eval) inside an eval" do + eval("__FILE__").should == "(eval)" + end + end + + ruby_version_is "3.3" do + it "equals (eval at __FILE__:__LINE__) inside an eval" do + eval("__FILE__").should == "(eval at #{__FILE__}:#{__LINE__})" + end end end diff --git a/test/-ext-/debug/test_profile_frames.rb b/test/-ext-/debug/test_profile_frames.rb index d6ae953dd298c7..870e4e1d3684a4 100644 --- a/test/-ext-/debug/test_profile_frames.rb +++ b/test/-ext-/debug/test_profile_frames.rb @@ -14,6 +14,8 @@ def self.corge(block) end class Sample2 + EVAL_LINE = __LINE__ + 3 + def baz(block) instance_eval "def zab(block) block.call end" [self, zab(block)] @@ -112,7 +114,7 @@ def test_profile_frames "SampleClassForTestProfileFrames#foo", "TestProfileFrames#test_profile_frames", ] - paths = [ nil, file=__FILE__, "(eval)", file, file, file, file, file, file, nil ] + paths = [ nil, file=__FILE__, "(eval at #{__FILE__}:#{SampleClassForTestProfileFrames::Sample2::EVAL_LINE})", file, file, file, file, file, file, nil ] absolute_paths = [ "", file, nil, file, file, file, file, file, file, nil ] assert_equal(labels.size, frames.size) diff --git a/test/ruby/test_eval.rb b/test/ruby/test_eval.rb index af255c05c8f79e..082d1dc03c9315 100644 --- a/test/ruby/test_eval.rb +++ b/test/ruby/test_eval.rb @@ -547,8 +547,8 @@ def test_eval_location_fstring end def test_eval_location_binding - assert_equal(['(eval)', 1], eval("[__FILE__, __LINE__]", nil)) - assert_equal(['(eval)', 1], eval("[__FILE__, __LINE__]", binding)) + assert_equal(["(eval at #{__FILE__}:#{__LINE__})", 1], eval("[__FILE__, __LINE__]", nil)) + assert_equal(["(eval at #{__FILE__}:#{__LINE__})", 1], eval("[__FILE__, __LINE__]", binding)) assert_equal(['foo', 1], eval("[__FILE__, __LINE__]", nil, 'foo')) assert_equal(['foo', 1], eval("[__FILE__, __LINE__]", binding, 'foo')) assert_equal(['foo', 2], eval("[__FILE__, __LINE__]", nil, 'foo', 2)) diff --git a/vm_eval.c b/vm_eval.c index 3fb622fff6fd50..2903223ec91400 100644 --- a/vm_eval.c +++ b/vm_eval.c @@ -1615,6 +1615,23 @@ rb_each(VALUE obj) static VALUE eval_default_path; +static VALUE +get_eval_default_path(void) +{ + int location_lineno; + VALUE location_path = rb_source_location(&location_lineno); + if (!NIL_P(location_path)) { + return rb_fstring(rb_sprintf("(eval at %s:%d)", RSTRING_PTR(location_path), location_lineno)); + } + + static VALUE eval_default_path; + if (!eval_default_path) { + eval_default_path = rb_fstring_lit("(eval)"); + rb_gc_register_mark_object(eval_default_path); + } + return eval_default_path; +} + static const rb_iseq_t * eval_make_iseq(VALUE src, VALUE fname, int line, const rb_binding_t *bind, const struct rb_block *base_block) @@ -1653,11 +1670,7 @@ eval_make_iseq(VALUE src, VALUE fname, int line, const rb_binding_t *bind, if (!NIL_P(fname)) fname = rb_fstring(fname); } else { - if (!eval_default_path) { - eval_default_path = rb_fstring_lit("(eval)"); - rb_gc_register_mark_object(eval_default_path); - } - fname = eval_default_path; + fname = get_eval_default_path(); coverage_enabled = FALSE; } @@ -1969,7 +1982,7 @@ specific_eval(int argc, const VALUE *argv, VALUE self, int singleton, int kw_spl return yield_under(self, singleton, 1, &self, kw_splat); } else { - VALUE file = Qundef; + VALUE file = Qnil; int line = 1; VALUE code; @@ -1982,6 +1995,12 @@ specific_eval(int argc, const VALUE *argv, VALUE self, int singleton, int kw_spl file = argv[1]; if (!NIL_P(file)) StringValue(file); } + + + if (NIL_P(file)) { + file = get_eval_default_path(); + } + return eval_under(self, singleton, code, file, line); } } @@ -2508,9 +2527,16 @@ rb_current_realfilepath(void) if (path == eval_default_path) { return Qnil; } - else { - return path; + + // [Feature #19755] implicit eval location is "(eval at #{__FILE__}:#{__LINE__})" + if (RSTRING_LEN(path) >= 9) { + if (RSTRING_PTR(path)[RSTRING_LEN(path) - 1] == ')' && + memcmp(RSTRING_PTR(path), "(eval at ", 9) == 0) { + return Qnil; + } } + + return path; } return Qnil; }