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

ruby-changes:60412

From: Jean <ko1@a...>
Date: Sun, 15 Mar 2020 19:35:51 +0900 (JST)
Subject: [ruby-changes:60412] c6944377d7 (ruby_2_7): Fix SimpleDelegator respond_to? regression

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

From c6944377d798e088042f2944b81aa3fa8a4e5411 Mon Sep 17 00:00:00 2001
From: Jean Boussier <jean.boussier@g...>
Date: Mon, 3 Feb 2020 12:29:37 +0100
Subject: Fix SimpleDelegator respond_to? regression

In 2.6, SimpleDelegator would always use the target `respond_to?`

In 2.7.0 it doesn't if the target does not inherit from Object.

This breaks compatibility for delegated objects that inherit
from BasicObject and redefine `respond_to?`. [Bug #16606]

(cherry picked from commit f2552216d43040cd42bbb9fd484eab6c70856fe6)

diff --git a/lib/delegate.rb b/lib/delegate.rb
index 6365764..1587c7e 100644
--- a/lib/delegate.rb
+++ b/lib/delegate.rb
@@ -103,13 +103,20 @@ class Delegator < BasicObject https://github.com/ruby/ruby/blob/trunk/lib/delegate.rb#L103
     r
   end
 
+  KERNEL_RESPOND_TO = ::Kernel.instance_method(:respond_to?)
+  private_constant :KERNEL_RESPOND_TO
+
   # Handle BasicObject instances
   private def target_respond_to?(target, m, include_private)
     case target
     when Object
       target.respond_to?(m, include_private)
     else
-      ::Kernel.instance_method(:respond_to?).bind_call(target, m, include_private)
+      if KERNEL_RESPOND_TO.bind_call(target, :respond_to?)
+        target.respond_to?(m, include_private)
+      else
+        KERNEL_RESPOND_TO.bind_call(target, m, include_private)
+      end
     end
   end
 
diff --git a/test/test_delegate.rb b/test/test_delegate.rb
index 5660adc..426a647 100644
--- a/test/test_delegate.rb
+++ b/test/test_delegate.rb
@@ -331,6 +331,20 @@ class TestDelegateClass < Test::Unit::TestCase https://github.com/ruby/ruby/blob/trunk/test/test_delegate.rb#L331
     assert_raise(NoMethodError, /undefined method `foo' for/) { delegate.foo }
   end
 
+  def test_basicobject_respond_to
+    o = BasicObject.new
+    def o.bar
+      nil
+    end
+
+    def o.respond_to?(method, include_private=false)
+      return false if method == :bar
+      ::Kernel.instance_method(:respond_to?).bind_call(self, method, include_private)
+    end
+    delegate = SimpleDelegator.new(o)
+    refute delegate.respond_to?(:bar)
+  end
+
   def test_keyword_argument
     k = EnvUtil.labeled_class("Target") do
       def test(a, k:) [a, k]; end
-- 
cgit v0.10.2


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

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