summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJeremy Evans <code@jeremyevans.net>2021-05-21 11:01:06 -0700
committerJeremy Evans <code@jeremyevans.net>2021-07-29 13:51:03 -0700
commit64ac984129a7a4645efe5ac57c168ef880b479b2 (patch)
tree3d5aa171e3e63e39b9b8d4ab4425d01894880f02
parent6998d758248d778fa95b008c78d05473e48b8428 (diff)
Make RubyVM::AbstractSyntaxTree.of raise for method/proc created in eval
This changes Thread::Location::Backtrace#absolute_path to return nil for methods/procs defined in eval. If the realpath of an iseq is nil, that indicates it was defined in eval, in which case you cannot use RubyVM::AbstractSyntaxTree.of. Fixes [Bug #16983] Co-authored-by: Koichi Sasada <ko1@atdot.net>
Notes
Notes: Merged: https://github.com/ruby/ruby/pull/4519
-rw-r--r--ast.c3
-rw-r--r--iseq.c6
-rw-r--r--iseq.h1
-rw-r--r--spec/ruby/core/thread/backtrace/location/absolute_path_spec.rb18
-rw-r--r--test/ruby/test_ast.rb20
-rw-r--r--vm_eval.c25
6 files changed, 65 insertions, 8 deletions
diff --git a/ast.c b/ast.c
index 32d8ab9eb6..20c4588cd7 100644
--- a/ast.c
+++ b/ast.c
@@ -213,6 +213,9 @@ ast_s_of(rb_execution_context_t *ec, VALUE module, VALUE body, VALUE save_script
else {
iseq = rb_method_iseq(body);
}
+ if (rb_iseq_from_eval_p(iseq)) {
+ rb_raise(rb_eArgError, "cannot get AST for method defined in eval");
+ }
path = rb_iseq_path(iseq);
node_id = iseq->body->location.node_id;
}
diff --git a/iseq.c b/iseq.c
index 9af7b54eff..47bc108d3e 100644
--- a/iseq.c
+++ b/iseq.c
@@ -1115,6 +1115,12 @@ rb_iseq_absolute_path(const rb_iseq_t *iseq)
return rb_iseq_realpath(iseq);
}
+int
+rb_iseq_from_eval_p(const rb_iseq_t *iseq)
+{
+ return NIL_P(rb_iseq_realpath(iseq));
+}
+
VALUE
rb_iseq_label(const rb_iseq_t *iseq)
{
diff --git a/iseq.h b/iseq.h
index 87e498227b..b792e13703 100644
--- a/iseq.h
+++ b/iseq.h
@@ -192,6 +192,7 @@ VALUE rb_iseqw_new(const rb_iseq_t *iseq);
const rb_iseq_t *rb_iseqw_to_iseq(VALUE iseqw);
VALUE rb_iseq_absolute_path(const rb_iseq_t *iseq); /* obsolete */
+int rb_iseq_from_eval_p(const rb_iseq_t *iseq);
VALUE rb_iseq_label(const rb_iseq_t *iseq);
VALUE rb_iseq_base_label(const rb_iseq_t *iseq);
VALUE rb_iseq_first_lineno(const rb_iseq_t *iseq);
diff --git a/spec/ruby/core/thread/backtrace/location/absolute_path_spec.rb b/spec/ruby/core/thread/backtrace/location/absolute_path_spec.rb
index b0ae28beee..4136f09348 100644
--- a/spec/ruby/core/thread/backtrace/location/absolute_path_spec.rb
+++ b/spec/ruby/core/thread/backtrace/location/absolute_path_spec.rb
@@ -18,10 +18,20 @@ describe 'Thread::Backtrace::Location#absolute_path' do
end
context "when used in eval with a given filename" do
- it "returns filename" do
- code = "caller_locations(0)[0].absolute_path"
- eval(code, nil, "foo.rb").should == "foo.rb"
- eval(code, nil, "foo/bar.rb").should == "foo/bar.rb"
+ code = "caller_locations(0)[0].absolute_path"
+
+ ruby_version_is ""..."3.1" do
+ it "returns filename with absolute_path" do
+ eval(code, nil, "foo.rb").should == "foo.rb"
+ eval(code, nil, "foo/bar.rb").should == "foo/bar.rb"
+ end
+ end
+
+ ruby_version_is "3.1" do
+ it "returns nil with absolute_path" do
+ eval(code, nil, "foo.rb").should == nil
+ eval(code, nil, "foo/bar.rb").should == nil
+ end
end
end
diff --git a/test/ruby/test_ast.rb b/test/ruby/test_ast.rb
index 5a229eabd4..3b4cbc7dee 100644
--- a/test/ruby/test_ast.rb
+++ b/test/ruby/test_ast.rb
@@ -211,6 +211,26 @@ class TestAst < Test::Unit::TestCase
end
end
+ def test_of_eval
+ method = self.method(eval("def example_method_#{$$}; end"))
+ assert_raise(ArgumentError) { RubyVM::AbstractSyntaxTree.of(method) }
+
+ method = self.method(eval("def self.example_singleton_method_#{$$}; end"))
+ assert_raise(ArgumentError) { RubyVM::AbstractSyntaxTree.of(method) }
+
+ method = eval("proc{}")
+ assert_raise(ArgumentError) { RubyVM::AbstractSyntaxTree.of(method) }
+
+ method = self.method(eval("singleton_class.define_method(:example_define_method_#{$$}){}"))
+ assert_raise(ArgumentError) { RubyVM::AbstractSyntaxTree.of(method) }
+
+ method = self.method(eval("define_singleton_method(:example_dsm_#{$$}){}"))
+ assert_raise(ArgumentError) { RubyVM::AbstractSyntaxTree.of(method) }
+
+ method = eval("Class.new{def example_method; end}.instance_method(:example_method)")
+ assert_raise(ArgumentError) { RubyVM::AbstractSyntaxTree.of(method) }
+ end
+
def test_scope_local_variables
node = RubyVM::AbstractSyntaxTree.parse("_x = 0")
lv, _, body = *node.children
diff --git a/vm_eval.c b/vm_eval.c
index bc40c15b6c..19ba6dd96b 100644
--- a/vm_eval.c
+++ b/vm_eval.c
@@ -1661,13 +1661,15 @@ rb_each(VALUE obj)
}
void rb_parser_warn_location(VALUE, int);
+
+static VALUE 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)
{
const VALUE parser = rb_parser_new();
const rb_iseq_t *const parent = vm_block_iseq(base_block);
- VALUE realpath = Qnil;
rb_iseq_t *iseq = NULL;
rb_ast_t *ast;
int isolated_depth = 0;
@@ -1694,10 +1696,14 @@ eval_make_iseq(VALUE src, VALUE fname, int line, const rb_binding_t *bind,
if (fname != Qundef) {
if (!NIL_P(fname)) fname = rb_fstring(fname);
- realpath = fname;
}
else {
fname = rb_fstring_lit("(eval)");
+ if (!eval_default_path) {
+ eval_default_path = rb_fstring_lit("(eval)");
+ rb_gc_register_mark_object(eval_default_path);
+ }
+ fname = eval_default_path;
}
rb_parser_set_context(parser, parent, FALSE);
@@ -1705,7 +1711,7 @@ eval_make_iseq(VALUE src, VALUE fname, int line, const rb_binding_t *bind,
if (ast->body.root) {
iseq = rb_iseq_new_eval(&ast->body,
parent->body->location.label,
- fname, realpath, INT2FIX(line),
+ fname, Qnil, INT2FIX(line),
parent, isolated_depth);
}
rb_ast_dispose(ast);
@@ -2590,7 +2596,18 @@ rb_current_realfilepath(void)
const rb_execution_context_t *ec = GET_EC();
rb_control_frame_t *cfp = ec->cfp;
cfp = vm_get_ruby_level_caller_cfp(ec, RUBY_VM_PREVIOUS_CONTROL_FRAME(cfp));
- if (cfp != 0) return rb_iseq_realpath(cfp->iseq);
+ if (cfp != NULL) {
+ VALUE path = rb_iseq_realpath(cfp->iseq);
+ if (RTEST(path)) return path;
+ // eval context
+ path = rb_iseq_path(cfp->iseq);
+ if (path == eval_default_path) {
+ return Qnil;
+ }
+ else {
+ return path;
+ }
+ }
return Qnil;
}