зеркало из https://github.com/github/ruby.git
Use the caller location as default filename for eval family of methods
[Feature #19755] Before (in /tmp/test.rb): ```ruby Object.class_eval("p __FILE__") # => "(eval)" ``` After: ```ruby Object.class_eval("p __FILE__") # => "(eval at /tmp/test.rb:1)" ``` This makes it much easier to track down generated code in case the author forgot to provide a filename argument.
This commit is contained in:
Родитель
14d16bdb1a
Коммит
43a5c19135
|
@ -84,6 +84,13 @@ describe "BasicObject#instance_eval" do
|
|||
|
||||
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
|
||||
|
|
|
@ -60,11 +60,13 @@ describe "Binding#eval" do
|
|||
bind.eval("#foo\n__LINE__", "(test)", 88).should == 89
|
||||
end
|
||||
|
||||
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
|
||||
obj = BindingSpecs::Demo.new(1)
|
||||
|
@ -104,4 +106,10 @@ describe "Binding#eval" do
|
|||
|
||||
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
|
||||
|
|
|
@ -159,6 +159,7 @@ describe "Kernel#eval" do
|
|||
end
|
||||
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)"
|
||||
|
@ -172,7 +173,23 @@ describe "Kernel#eval" do
|
|||
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 (eval at #{__FILE__}:#{__LINE__}):1)"
|
||||
eval("eval '__FILE__', binding", binding).should == "(eval at (eval at #{__FILE__}:#{__LINE__}):1)"
|
||||
eval("eval '__FILE__', binding", binding, 'success').should == "(eval at success:1)"
|
||||
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
|
||||
|
|
|
@ -52,6 +52,12 @@ describe :module_class_eval, shared: true do
|
|||
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)
|
||||
|
|
|
@ -13,6 +13,7 @@ describe 'TracePoint#path' do
|
|||
path.should == "#{__FILE__}"
|
||||
end
|
||||
|
||||
ruby_version_is ""..."3.3" do
|
||||
it 'equals (eval) inside an eval for :end event' do
|
||||
path = nil
|
||||
TracePoint.new(:end) { |tp|
|
||||
|
@ -24,3 +25,17 @@ describe 'TracePoint#path' do
|
|||
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
|
||||
end
|
||||
end
|
||||
|
|
|
@ -203,7 +203,7 @@ describe "The defined? keyword when called with a method name" do
|
|||
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
|
||||
|
|
|
@ -7,11 +7,19 @@ describe "The __FILE__ pseudo-variable" do
|
|||
-> { eval("__FILE__ = 1") }.should raise_error(SyntaxError)
|
||||
end
|
||||
|
||||
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
|
||||
|
||||
describe "The __FILE__ pseudo-variable with require" do
|
||||
it_behaves_like :language___FILE__, :require, Kernel
|
||||
end
|
||||
|
|
|
@ -14,6 +14,8 @@ class SampleClassForTestProfileFrames
|
|||
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 @@ class TestProfileFrames < Test::Unit::TestCase
|
|||
"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 = [ "<cfunc>", file, nil, file, file, file, file, file, file, nil ]
|
||||
|
||||
assert_equal(labels.size, frames.size)
|
||||
|
|
|
@ -45,9 +45,9 @@ class TestBeginEndBlock < Test::Unit::TestCase
|
|||
end
|
||||
|
||||
def test_endblockwarn_in_eval
|
||||
assert_in_out_err([], "#{<<~"begin;"}\n#{<<~'end;'}", [], ['(eval):2: warning: END in method; use at_exit'])
|
||||
assert_in_out_err([], "#{<<~"begin;"}\n#{<<~'end;'}", [], ['test.rb:1: warning: END in method; use at_exit'])
|
||||
begin;
|
||||
eval <<-EOE
|
||||
eval <<-EOE, nil, "test.rb", 0
|
||||
def end2
|
||||
END {}
|
||||
end
|
||||
|
|
|
@ -547,8 +547,8 @@ class TestEval < Test::Unit::TestCase
|
|||
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))
|
||||
|
|
|
@ -1310,7 +1310,7 @@ $stderr = $stdout; raise "\x82\xa0"') do |outs, errs, status|
|
|||
|
||||
def test_backtrace_in_eval
|
||||
bug = '[ruby-core:84434] [Bug #14229]'
|
||||
assert_in_out_err(['-e', 'eval("raise")'], "", [], /^\(eval\):1:/, bug)
|
||||
assert_in_out_err(['-e', 'eval("raise")'], "", [], /^\(eval at .*\):1:/, bug)
|
||||
end
|
||||
|
||||
def test_full_message
|
||||
|
|
|
@ -1431,8 +1431,8 @@ class TestMethod < Test::Unit::TestCase
|
|||
end
|
||||
|
||||
def test_argument_error_location
|
||||
body = <<-'END_OF_BODY'
|
||||
eval <<-'EOS'
|
||||
body = <<~'END_OF_BODY'
|
||||
eval <<~'EOS', nil, "main.rb"
|
||||
$line_lambda = __LINE__; $f = lambda do
|
||||
_x = 1
|
||||
end
|
||||
|
@ -1442,12 +1442,12 @@ class TestMethod < Test::Unit::TestCase
|
|||
begin
|
||||
$f.call(1)
|
||||
rescue ArgumentError => e
|
||||
assert_equal "(eval):#{$line_lambda.to_s}:in `block in <main>'", e.backtrace.first
|
||||
assert_equal "main.rb:#{$line_lambda}:in `block in <main>'", e.backtrace.first
|
||||
end
|
||||
begin
|
||||
foo(1)
|
||||
rescue ArgumentError => e
|
||||
assert_equal "(eval):#{$line_method}:in `foo'", e.backtrace.first
|
||||
assert_equal "main.rb:#{$line_method}:in `foo'", e.backtrace.first
|
||||
end
|
||||
EOS
|
||||
END_OF_BODY
|
||||
|
|
42
vm_eval.c
42
vm_eval.c
|
@ -1613,7 +1613,23 @@ rb_each(VALUE obj)
|
|||
return rb_call(obj, idEach, 0, 0, CALL_FCALL);
|
||||
}
|
||||
|
||||
static VALUE eval_default_path;
|
||||
static VALUE eval_default_path = Qfalse;
|
||||
|
||||
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 %"PRIsVALUE":%d)", location_path, location_lineno));
|
||||
}
|
||||
|
||||
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,
|
||||
|
@ -1653,11 +1669,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 +1981,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 +1994,11 @@ 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,10 +2525,17 @@ 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;
|
||||
}
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче