[前][次][番号順一覧][スレッド一覧]

ruby-changes:67000

From: Jeremy <ko1@a...>
Date: Fri, 30 Jul 2021 05:51:23 +0900 (JST)
Subject: [ruby-changes:67000] 64ac984129 (master): Make RubyVM::AbstractSyntaxTree.of raise for method/proc created in eval

https://git.ruby-lang.org/ruby.git/commit/?id=64ac984129

From 64ac984129a7a4645efe5ac57c168ef880b479b2 Mon Sep 17 00:00:00 2001
From: Jeremy Evans <code@j...>
Date: Fri, 21 May 2021 11:01:06 -0700
Subject: 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@a...>
---
 ast.c                                              |  3 +++
 iseq.c                                             |  6 ++++++
 iseq.h                                             |  1 +
 .../backtrace/location/absolute_path_spec.rb       | 18 ++++++++++++----
 test/ruby/test_ast.rb                              | 20 +++++++++++++++++
 vm_eval.c                                          | 25 ++++++++++++++++++----
 6 files changed, 65 insertions(+), 8 deletions(-)

diff --git a/ast.c b/ast.c
index 32d8ab9..20c4588 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 https://github.com/ruby/ruby/blob/trunk/ast.c#L213
         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 9af7b54..47bc108 100644
--- a/iseq.c
+++ b/iseq.c
@@ -1115,6 +1115,12 @@ rb_iseq_absolute_path(const rb_iseq_t *iseq) https://github.com/ruby/ruby/blob/trunk/iseq.c#L1115
     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 87e4982..b792e13 100644
--- a/iseq.h
+++ b/iseq.h
@@ -192,6 +192,7 @@ VALUE rb_iseqw_new(const rb_iseq_t *iseq); https://github.com/ruby/ruby/blob/trunk/iseq.h#L192
 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 b0ae28b..4136f09 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 https://github.com/ruby/ruby/blob/trunk/spec/ruby/core/thread/backtrace/location/absolute_path_spec.rb#L18
   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 5a229ea..3b4cbc7 100644
--- a/test/ruby/test_ast.rb
+++ b/test/ruby/test_ast.rb
@@ -211,6 +211,26 @@ class TestAst < Test::Unit::TestCase https://github.com/ruby/ruby/blob/trunk/test/ruby/test_ast.rb#L211
     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 bc40c15..19ba6dd 100644
--- a/vm_eval.c
+++ b/vm_eval.c
@@ -1661,13 +1661,15 @@ rb_each(VALUE obj) https://github.com/ruby/ruby/blob/trunk/vm_eval.c#L1661
 }
 
 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, https://github.com/ruby/ruby/blob/trunk/vm_eval.c#L1696
 
     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, https://github.com/ruby/ruby/blob/trunk/vm_eval.c#L1711
     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) https://github.com/ruby/ruby/blob/trunk/vm_eval.c#L2596
     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;
 }
 
-- 
cgit v1.1


--
ML: ruby-changes@q...
Info: http://www.atdot.net/~ko1/quickml/

[前][次][番号順一覧][スレッド一覧]