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

ruby-changes:59216

From: Jeremy <ko1@a...>
Date: Thu, 12 Dec 2019 15:50:43 +0900 (JST)
Subject: [ruby-changes:59216] 55b7ba3686 (master): Make super in instance_eval in method in module raise TypeError

https://git.ruby-lang.org/ruby.git/commit/?id=55b7ba3686

From 55b7ba368696033f2e89b77cbcd4a05dec97b139 Mon Sep 17 00:00:00 2001
From: Jeremy Evans <code@j...>
Date: Wed, 21 Aug 2019 13:59:36 -0700
Subject: Make super in instance_eval in method in module raise TypeError

This makes behavior the same as super in instance_eval in method
in class.  The reason this wasn't implemented before is that
there is a check to determine if the self in the current context
is of the expected class, and a module itself can be included
in multiple classes, so it doesn't have an expected class.

Implementing this requires giving iclasses knowledge of which
class created them, so that super call in the module method
knows the expected class for super calls.  This reference
is called includer, and should only be set for iclasses.

Note that the approach Ruby uses in this check is not robust. If
you instance_eval another object of the same class and call super,
instead of an TypeError, you get super called with the
instance_eval receiver instead of the method receiver.  Truly
fixing super would require keeping a reference to the super object
(method receiver) in each frame where scope has changed, and using
that instead of current self when calling super.

Fixes [Bug #11636]

diff --git a/class.c b/class.c
index 736e227..4129647 100644
--- a/class.c
+++ b/class.c
@@ -921,6 +921,7 @@ include_modules_at(const VALUE klass, VALUE c, VALUE module, int search_super) https://github.com/ruby/ruby/blob/trunk/class.c#L921
 	}
 	iclass = rb_include_class_new(module, RCLASS_SUPER(c));
 	c = RCLASS_SET_SUPER(c, iclass);
+        RCLASS_SET_INCLUDER(iclass, klass);
 
 	{
 	    VALUE m = module;
diff --git a/internal.h b/internal.h
index 374db5c..3bd95c2 100644
--- a/internal.h
+++ b/internal.h
@@ -1039,6 +1039,7 @@ struct rb_classext_struct { https://github.com/ruby/ruby/blob/trunk/internal.h#L1039
     const VALUE origin_;
     const VALUE refined_class;
     rb_alloc_func_t allocator;
+    const VALUE includer;
 };
 
 typedef struct rb_classext_struct rb_classext_t;
@@ -1078,6 +1079,7 @@ int rb_singleton_class_internal_p(VALUE sklass); https://github.com/ruby/ruby/blob/trunk/internal.h#L1079
 #else
 # define RCLASS_SERIAL(c) (RCLASS_EXT(c)->class_serial)
 #endif
+#define RCLASS_INCLUDER(c) (RCLASS_EXT(c)->includer)
 
 #define RCLASS_CLONED     FL_USER6
 #define RICLASS_IS_ORIGIN FL_USER5
@@ -1090,6 +1092,12 @@ RCLASS_SET_ORIGIN(VALUE klass, VALUE origin) https://github.com/ruby/ruby/blob/trunk/internal.h#L1092
     if (klass != origin) FL_SET(origin, RICLASS_IS_ORIGIN);
 }
 
+static inline void
+RCLASS_SET_INCLUDER(VALUE iclass, VALUE klass)
+{
+    RB_OBJ_WRITE(iclass, &RCLASS_INCLUDER(iclass), klass);
+}
+
 #undef RCLASS_SUPER
 static inline VALUE
 RCLASS_SUPER(VALUE klass)
diff --git a/test/ruby/test_super.rb b/test/ruby/test_super.rb
index bb78ab5..bbfc581 100644
--- a/test/ruby/test_super.rb
+++ b/test/ruby/test_super.rb
@@ -307,6 +307,29 @@ class TestSuper < Test::Unit::TestCase https://github.com/ruby/ruby/blob/trunk/test/ruby/test_super.rb#L307
     end
   end
 
+  def test_super_in_instance_eval_in_module
+    super_class = EnvUtil.labeled_class("Super\u{30af 30e9 30b9}") {
+      def foo
+        return [:super, self]
+      end
+    }
+    mod = EnvUtil.labeled_module("Mod\u{30af 30e9 30b9}") {
+      def foo
+        x = Object.new
+        x.instance_eval do
+          super()
+        end
+      end
+    }
+    sub_class = EnvUtil.labeled_class("Sub\u{30af 30e9 30b9}", super_class) {
+      include mod
+    }
+    obj = sub_class.new
+    assert_raise_with_message(TypeError, /Sub\u{30af 30e9 30b9}/) do
+      obj.foo
+    end
+  end
+
   def test_super_in_orphan_block
     super_class = EnvUtil.labeled_class("Super\u{30af 30e9 30b9}") {
       def foo
diff --git a/vm_insnhelper.c b/vm_insnhelper.c
index c8ea3f9..8487886 100644
--- a/vm_insnhelper.c
+++ b/vm_insnhelper.c
@@ -3126,16 +3126,17 @@ vm_search_super_method(const rb_control_frame_t *reg_cfp, struct rb_call_data *c https://github.com/ruby/ruby/blob/trunk/vm_insnhelper.c#L3126
     }
 
     if (BUILTIN_TYPE(current_defined_class) != T_MODULE &&
-	BUILTIN_TYPE(current_defined_class) != T_ICLASS && /* bound UnboundMethod */
 	!FL_TEST(current_defined_class, RMODULE_INCLUDED_INTO_REFINEMENT) &&
         !rb_obj_is_kind_of(recv, current_defined_class)) {
 	VALUE m = RB_TYPE_P(current_defined_class, T_ICLASS) ?
-	    RBASIC(current_defined_class)->klass : current_defined_class;
+            RCLASS_INCLUDER(current_defined_class) : current_defined_class;
 
-	rb_raise(rb_eTypeError,
-		 "self has wrong type to call super in this context: "
-		 "%"PRIsVALUE" (expected %"PRIsVALUE")",
-                 rb_obj_class(recv), m);
+        if (m) { /* not bound UnboundMethod */
+            rb_raise(rb_eTypeError,
+                     "self has wrong type to call super in this context: "
+                     "%"PRIsVALUE" (expected %"PRIsVALUE")",
+                     rb_obj_class(recv), m);
+        }
     }
 
     if (me->def->type == VM_METHOD_TYPE_BMETHOD && (ci->flag & VM_CALL_ZSUPER)) {
-- 
cgit v0.10.2


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

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