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

ruby-changes:72200

From: John <ko1@a...>
Date: Fri, 17 Jun 2022 05:25:05 +0900 (JST)
Subject: [ruby-changes:72200] ae163cae6b (master): Allow calling protected methods from refinements

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

From ae163cae6b3f01e0fb827e0a18d5889f9703617f Mon Sep 17 00:00:00 2001
From: John Hawthorn <john@h...>
Date: Fri, 27 May 2022 13:47:06 -0700
Subject: Allow calling protected methods from refinements

Previously protected methods on refinements could never be called
because they were seen as being "defined" on the hidden refinement
ICLASS.

This commit updates calling refined protected methods so that they are
considered to be defined on the original class (the one being refined).

This ended up using the same behaviour that was used to check whether a
call to super was allowed, so I extracted that into a method.

[Bug #18806]
---
 test/ruby/test_refinement.rb | 35 +++++++++++++++++++++++++++++++++++
 vm_insnhelper.c              | 22 ++++++++++++++++------
 2 files changed, 51 insertions(+), 6 deletions(-)

diff --git a/test/ruby/test_refinement.rb b/test/ruby/test_refinement.rb
index c0754d8cf0..56f33ae00a 100644
--- a/test/ruby/test_refinement.rb
+++ b/test/ruby/test_refinement.rb
@@ -1206,6 +1206,41 @@ class TestRefinement < Test::Unit::TestCase https://github.com/ruby/ruby/blob/trunk/test/ruby/test_refinement.rb#L1206
     INPUT
   end
 
+  def test_refined_protected_methods
+    assert_separately([], <<-"end;")
+    bug18806 = '[ruby-core:108705] [Bug #18806]'
+    class C; end
+
+    module R
+      refine C do
+        def refined_call_foo = foo
+        def refined_call_foo_on(other) = other.foo
+
+        protected
+
+        def foo = :foo
+      end
+    end
+
+    class C
+      using R
+
+      def call_foo = foo
+      def call_foo_on(other) = other.foo
+    end
+
+    c = C.new
+    assert_equal :foo, c.call_foo, bug18806
+    assert_equal :foo, c.call_foo_on(c), bug18806
+    assert_equal :foo, c.call_foo_on(C.new), bug18806
+
+    using R
+    assert_equal :foo, c.refined_call_foo, bug18806
+    assert_equal :foo, c.refined_call_foo_on(c), bug18806
+    assert_equal :foo, c.refined_call_foo_on(C.new), bug18806
+    end;
+  end
+
   def test_refine_basic_object
     assert_separately([], <<-"end;")
     bug10106 = '[ruby-core:64166] [Bug #10106]'
diff --git a/vm_insnhelper.c b/vm_insnhelper.c
index 09fcd2f729..d2f3fc62bd 100644
--- a/vm_insnhelper.c
+++ b/vm_insnhelper.c
@@ -3711,6 +3711,19 @@ vm_call_method_nome(rb_execution_context_t *ec, rb_control_frame_t *cfp, struct https://github.com/ruby/ruby/blob/trunk/vm_insnhelper.c#L3711
     }
 }
 
+/* Protected method calls and super invocations need to check that the receiver
+ * (self for super) inherits the module on which the method is defined.
+ * In the case of refinements, it should consider the original class not the
+ * refinement.
+ */
+static VALUE
+vm_defined_class_for_protected_call(const rb_callable_method_entry_t *me)
+{
+    VALUE defined_class = me->defined_class;
+    VALUE refined_class = RCLASS_REFINED_CLASS(defined_class);
+    return NIL_P(refined_class) ? defined_class : refined_class;
+}
+
 static inline VALUE
 vm_call_method(rb_execution_context_t *ec, rb_control_frame_t *cfp, struct rb_calling_info *calling)
 {
@@ -3737,7 +3750,8 @@ vm_call_method(rb_execution_context_t *ec, rb_control_frame_t *cfp, struct rb_ca https://github.com/ruby/ruby/blob/trunk/vm_insnhelper.c#L3750
 
 	  case METHOD_VISI_PROTECTED:
 	    if (!(vm_ci_flag(ci) & VM_CALL_OPT_SEND)) {
-		if (!rb_obj_is_kind_of(cfp->self, vm_cc_cme(cc)->defined_class)) {
+                VALUE defined_class = vm_defined_class_for_protected_call(vm_cc_cme(cc));
+                if (!rb_obj_is_kind_of(cfp->self, defined_class)) {
                     vm_cc_method_missing_reason_set(cc, MISSING_PROTECTED);
                     return vm_call_method_missing(ec, cfp, calling);
 		}
@@ -3834,11 +3848,7 @@ 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#L3848
 	vm_super_outside();
     }
 
-    current_defined_class = me->defined_class;
-
-    if (!NIL_P(RCLASS_REFINED_CLASS(current_defined_class))) {
-	current_defined_class = RCLASS_REFINED_CLASS(current_defined_class);
-    }
+    current_defined_class = vm_defined_class_for_protected_call(me);
 
     if (BUILTIN_TYPE(current_defined_class) != T_MODULE &&
         reg_cfp->iseq != method_entry_iseqptr(me) &&
-- 
cgit v1.2.1


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

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