Make AST.of possible even under eval when keep_script_lines is enabled

Now the following code works without an exception.

```
RubyVM.keep_script_lines = true

eval(<<END)
def foo
end
END

p RubyVM::AbstractSyntaxTree.of(method(:foo))
```
This commit is contained in:
Yusuke Endoh 2021-12-19 04:00:51 +09:00
Родитель acac2b8128
Коммит 6a51c3e80c
2 изменённых файлов: 66 добавлений и 5 удалений

4
ast.c
Просмотреть файл

@ -222,11 +222,11 @@ ast_s_of(rb_execution_context_t *ec, VALUE module, VALUE body, VALUE keep_script
if (!iseq) {
return Qnil;
}
if (rb_iseq_from_eval_p(iseq)) {
lines = iseq->body->variable.script_lines;
if (NIL_P(lines) && rb_iseq_from_eval_p(iseq)) {
rb_raise(rb_eArgError, "cannot get AST for method defined in eval");
}
path = rb_iseq_path(iseq);
lines = iseq->body->variable.script_lines;
if (!NIL_P(lines) || !NIL_P(lines = script_lines(path))) {
node = rb_ast_parse_array(lines, keep_script_lines);

Просмотреть файл

@ -226,6 +226,9 @@ class TestAst < Test::Unit::TestCase
end
def test_of_proc_and_method_under_eval
keep_script_lines_back = RubyVM.keep_script_lines
RubyVM.keep_script_lines = false
method = self.method(eval("def example_method_#{$$}; end"))
assert_raise(ArgumentError) { RubyVM::AbstractSyntaxTree.of(method) }
@ -246,18 +249,76 @@ class TestAst < Test::Unit::TestCase
method = eval("Class.new{def example_method; end}.instance_method(:example_method)")
assert_raise(ArgumentError) { RubyVM::AbstractSyntaxTree.of(method) }
ensure
RubyVM.keep_script_lines = keep_script_lines_back
end
def test_of_proc_and_method_under_eval_with_keep_script_lines
keep_script_lines_back = RubyVM.keep_script_lines
RubyVM.keep_script_lines = true
method = self.method(eval("def example_method_#{$$}; end"))
assert_instance_of(RubyVM::AbstractSyntaxTree::Node, RubyVM::AbstractSyntaxTree.of(method))
method = self.method(eval("def self.example_singleton_method_#{$$}; end"))
assert_instance_of(RubyVM::AbstractSyntaxTree::Node, RubyVM::AbstractSyntaxTree.of(method))
method = eval("proc{}")
assert_instance_of(RubyVM::AbstractSyntaxTree::Node, RubyVM::AbstractSyntaxTree.of(method))
method = self.method(eval("singleton_class.define_method(:example_define_method_#{$$}){}"))
assert_instance_of(RubyVM::AbstractSyntaxTree::Node, RubyVM::AbstractSyntaxTree.of(method))
method = self.method(eval("define_singleton_method(:example_dsm_#{$$}){}"))
assert_instance_of(RubyVM::AbstractSyntaxTree::Node, RubyVM::AbstractSyntaxTree.of(method))
method = eval("Class.new{def example_method; end}.instance_method(:example_method)")
assert_instance_of(RubyVM::AbstractSyntaxTree::Node, RubyVM::AbstractSyntaxTree.of(method))
method = eval("Class.new{def example_method; end}.instance_method(:example_method)")
assert_instance_of(RubyVM::AbstractSyntaxTree::Node, RubyVM::AbstractSyntaxTree.of(method))
ensure
RubyVM.keep_script_lines = keep_script_lines_back
end
def test_of_backtrace_location_under_eval
keep_script_lines_back = RubyVM.keep_script_lines
RubyVM.keep_script_lines = false
m = Module.new do
eval(<<-END, nil, __FILE__, __LINE__)
def self.sample_backtrace_location
[caller_locations(0).first, __LINE__]
caller_locations(0).first
end
END
end
backtrace_location, lineno = m.sample_backtrace_location
assert_raise(ArgumentError) { RubyVM::AbstractSyntaxTree.of(backtrace_location) }
backtrace_location = m.sample_backtrace_location
assert_raise(ArgumentError) { RubyVM::AbstractSyntaxTree.of(backtrace_location) }
ensure
RubyVM.keep_script_lines = keep_script_lines_back
end
def test_of_backtrace_location_under_eval_with_keep_script_lines
keep_script_lines_back = RubyVM.keep_script_lines
RubyVM.keep_script_lines = true
m = Module.new do
eval(<<-END, nil, __FILE__, __LINE__)
def self.sample_backtrace_location
caller_locations(0).first
end
END
end
backtrace_location = m.sample_backtrace_location
node = RubyVM::AbstractSyntaxTree.of(backtrace_location)
assert_instance_of(RubyVM::AbstractSyntaxTree::Node, node)
assert_equal(2, node.first_lineno)
ensure
RubyVM.keep_script_lines = keep_script_lines_back
end
def test_of_c_method