mirror of
				https://github.com/ruby/ruby.git
				synced 2022-11-09 12:17:21 -05:00 
			
		
		
		
	Support delegates for BasicObject
For BasicObject, bind the Kernel respond_to? instance method to the object and call it instead of calling the method directly. Also, use bind_call(recv, ...) for better performance. Fixes [Bug #16127]
This commit is contained in:
		
							parent
							
								
									4171909695
								
							
						
					
					
						commit
						2322c94dd6
					
				
				
				Notes:
				
					git
				
				2019-10-11 05:15:39 +09:00 
				
			
			
			
		
		
					 2 changed files with 22 additions and 4 deletions
				
			
		| 
						 | 
				
			
			@ -79,10 +79,10 @@ class Delegator < BasicObject
 | 
			
		|||
    r = true
 | 
			
		||||
    target = self.__getobj__ {r = false}
 | 
			
		||||
 | 
			
		||||
    if r && target.respond_to?(m)
 | 
			
		||||
    if r && target_respond_to?(target, m, false)
 | 
			
		||||
      target.__send__(m, *args, &block)
 | 
			
		||||
    elsif ::Kernel.method_defined?(m) || ::Kernel.private_method_defined?(m)
 | 
			
		||||
      ::Kernel.instance_method(m).bind(self).(*args, &block)
 | 
			
		||||
      ::Kernel.instance_method(m).bind_call(self, *args, &block)
 | 
			
		||||
    else
 | 
			
		||||
      super(m, *args, &block)
 | 
			
		||||
    end
 | 
			
		||||
| 
						 | 
				
			
			@ -95,14 +95,24 @@ class Delegator < BasicObject
 | 
			
		|||
  def respond_to_missing?(m, include_private)
 | 
			
		||||
    r = true
 | 
			
		||||
    target = self.__getobj__ {r = false}
 | 
			
		||||
    r &&= target.respond_to?(m, include_private)
 | 
			
		||||
    if r && include_private && !target.respond_to?(m, false)
 | 
			
		||||
    r &&= target_respond_to?(target, m, include_private)
 | 
			
		||||
    if r && include_private && !target_respond_to?(target, m, false)
 | 
			
		||||
      warn "delegator does not forward private method \##{m}", uplevel: 3
 | 
			
		||||
      return false
 | 
			
		||||
    end
 | 
			
		||||
    r
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  # 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)
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  #
 | 
			
		||||
  # Returns the methods available to this delegate object as the union
 | 
			
		||||
  # of this object's and \_\_getobj\_\_ methods.
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -322,4 +322,12 @@ class TestDelegateClass < Test::Unit::TestCase
 | 
			
		|||
      delegate.constants
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def test_basicobject
 | 
			
		||||
    o = BasicObject.new
 | 
			
		||||
    def o.bar; 1; end
 | 
			
		||||
    delegate = SimpleDelegator.new(o)
 | 
			
		||||
    assert_equal(1, delegate.bar)
 | 
			
		||||
    assert_raise(NoMethodError, /undefined method `foo' for/) { delegate.foo }
 | 
			
		||||
  end
 | 
			
		||||
end
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue