mirror of
https://github.com/ruby/ruby.git
synced 2022-11-09 12:17:21 -05:00
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>
This commit is contained in:
parent
6998d75824
commit
64ac984129
Notes:
git
2021-07-30 05:51:26 +09:00
6 changed files with 65 additions and 8 deletions
3
ast.c
3
ast.c
|
@ -213,6 +213,9 @@ ast_s_of(rb_execution_context_t *ec, VALUE module, VALUE body, VALUE save_script
|
||||||
else {
|
else {
|
||||||
iseq = rb_method_iseq(body);
|
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);
|
path = rb_iseq_path(iseq);
|
||||||
node_id = iseq->body->location.node_id;
|
node_id = iseq->body->location.node_id;
|
||||||
}
|
}
|
||||||
|
|
6
iseq.c
6
iseq.c
|
@ -1115,6 +1115,12 @@ rb_iseq_absolute_path(const rb_iseq_t *iseq)
|
||||||
return rb_iseq_realpath(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
|
VALUE
|
||||||
rb_iseq_label(const rb_iseq_t *iseq)
|
rb_iseq_label(const rb_iseq_t *iseq)
|
||||||
{
|
{
|
||||||
|
|
1
iseq.h
1
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);
|
const rb_iseq_t *rb_iseqw_to_iseq(VALUE iseqw);
|
||||||
|
|
||||||
VALUE rb_iseq_absolute_path(const rb_iseq_t *iseq); /* obsolete */
|
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_label(const rb_iseq_t *iseq);
|
||||||
VALUE rb_iseq_base_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);
|
VALUE rb_iseq_first_lineno(const rb_iseq_t *iseq);
|
||||||
|
|
|
@ -18,10 +18,20 @@ describe 'Thread::Backtrace::Location#absolute_path' do
|
||||||
end
|
end
|
||||||
|
|
||||||
context "when used in eval with a given filename" do
|
context "when used in eval with a given filename" do
|
||||||
it "returns filename" do
|
code = "caller_locations(0)[0].absolute_path"
|
||||||
code = "caller_locations(0)[0].absolute_path"
|
|
||||||
eval(code, nil, "foo.rb").should == "foo.rb"
|
ruby_version_is ""..."3.1" do
|
||||||
eval(code, nil, "foo/bar.rb").should == "foo/bar.rb"
|
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
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -211,6 +211,26 @@ class TestAst < Test::Unit::TestCase
|
||||||
end
|
end
|
||||||
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
|
def test_scope_local_variables
|
||||||
node = RubyVM::AbstractSyntaxTree.parse("_x = 0")
|
node = RubyVM::AbstractSyntaxTree.parse("_x = 0")
|
||||||
lv, _, body = *node.children
|
lv, _, body = *node.children
|
||||||
|
|
25
vm_eval.c
25
vm_eval.c
|
@ -1661,13 +1661,15 @@ rb_each(VALUE obj)
|
||||||
}
|
}
|
||||||
|
|
||||||
void rb_parser_warn_location(VALUE, int);
|
void rb_parser_warn_location(VALUE, int);
|
||||||
|
|
||||||
|
static VALUE eval_default_path;
|
||||||
|
|
||||||
static const rb_iseq_t *
|
static const rb_iseq_t *
|
||||||
eval_make_iseq(VALUE src, VALUE fname, int line, const rb_binding_t *bind,
|
eval_make_iseq(VALUE src, VALUE fname, int line, const rb_binding_t *bind,
|
||||||
const struct rb_block *base_block)
|
const struct rb_block *base_block)
|
||||||
{
|
{
|
||||||
const VALUE parser = rb_parser_new();
|
const VALUE parser = rb_parser_new();
|
||||||
const rb_iseq_t *const parent = vm_block_iseq(base_block);
|
const rb_iseq_t *const parent = vm_block_iseq(base_block);
|
||||||
VALUE realpath = Qnil;
|
|
||||||
rb_iseq_t *iseq = NULL;
|
rb_iseq_t *iseq = NULL;
|
||||||
rb_ast_t *ast;
|
rb_ast_t *ast;
|
||||||
int isolated_depth = 0;
|
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 (fname != Qundef) {
|
||||||
if (!NIL_P(fname)) fname = rb_fstring(fname);
|
if (!NIL_P(fname)) fname = rb_fstring(fname);
|
||||||
realpath = fname;
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
fname = rb_fstring_lit("(eval)");
|
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);
|
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) {
|
if (ast->body.root) {
|
||||||
iseq = rb_iseq_new_eval(&ast->body,
|
iseq = rb_iseq_new_eval(&ast->body,
|
||||||
parent->body->location.label,
|
parent->body->location.label,
|
||||||
fname, realpath, INT2FIX(line),
|
fname, Qnil, INT2FIX(line),
|
||||||
parent, isolated_depth);
|
parent, isolated_depth);
|
||||||
}
|
}
|
||||||
rb_ast_dispose(ast);
|
rb_ast_dispose(ast);
|
||||||
|
@ -2590,7 +2596,18 @@ rb_current_realfilepath(void)
|
||||||
const rb_execution_context_t *ec = GET_EC();
|
const rb_execution_context_t *ec = GET_EC();
|
||||||
rb_control_frame_t *cfp = ec->cfp;
|
rb_control_frame_t *cfp = ec->cfp;
|
||||||
cfp = vm_get_ruby_level_caller_cfp(ec, RUBY_VM_PREVIOUS_CONTROL_FRAME(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;
|
return Qnil;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue