4 # Method objects are essentially detached, freely passed-around methods. The
5 # Method is a copy of the method on the object at the time of extraction, so
6 # if the method itself is changed, overridden, aliased or removed from the
7 # object, the Method object still contains the old functionality. In addition,
8 # the call itself is not in any way stored so it will reflect the state of the
9 # object at the time of calling.
11 # Methods are normally bound to a particular object but it is possible to use
12 # Method#unbind to create an UnboundMethod object for the purpose of
13 # re-binding to a different object.
18 # Takes and stores the receiver object, the method's bytecodes and the
19 # Module that the method is defined in.
21 def initialize(receiver, defined_in, compiled_method)
23 @pulled_from = receiver.__class__
24 @defined_in = defined_in
25 @compiled_method = compiled_method
29 attr_reader :pulled_from
30 attr_reader :defined_in
31 attr_reader :compiled_method
33 protected :pulled_from
36 # Method objects are equal if they have the same body and are bound to the
40 return true if other.class == Method and
41 @receiver.equal?(other.receiver) and
42 @compiled_method == other.compiled_method
48 # Indication of how many arguments this method takes. It is defined so that
49 # a non-negative Integer means the method takes that fixed amount of
50 # arguments (up to 1024 currently.) A negative Integer is used to indicate a
51 # variable argument count. The number is ((-n) - 1), where n is the number
52 # of required args. Blocks are not counted.
54 # def foo(); end # arity => 0
55 # def foo(a, b); end # arity => 2
56 # def foo(a, &b); end # arity => 1
57 # def foo(a, b = nil); end # arity => ((-1) -1) => -2
58 # def foo(*a); end # arity => ((-0) -1) => -1
59 # def foo(a, b, *c, &d); end # arity => ((-2) -1) => -3
62 @compiled_method.required
66 # Execute the method. This works exactly like calling a method with the same
67 # code on the receiver object. Arguments and a block can be supplied
70 def call(*args, &block)
71 @compiled_method.activate(@receiver, @defined_in, args, &block)
74 alias_method :[], :call
77 # String representation of this Method includes the method name, the Module
78 # it is defined in and the Module that it was extracted from.
81 "#<#{self.class}: #{@pulled_from}##{@compiled_method.name} (defined in #{@defined_in})>"
84 alias_method :to_s, :inspect
87 # Location gives the file and line number of the start of this method's
91 "#{@compiled_method.file}, near line #{@compiled_method.first_line}"
95 # Returns a Proc object corresponding to this Method.
98 env = Method::AsBlockEnvironment.new self
99 Proc.__from_block__(env)
103 # Detach this Method from the receiver object it is bound to and create an
104 # UnboundMethod object. Populates the UnboundMethod with the method data as
105 # well as the Module it is defined in and the Module it was extracted from.
107 # See UnboundMethod for more information.
110 UnboundMethod.new(@defined_in, @compiled_method, @pulled_from)
116 # Wraps the Method into a BlockEnvironment, for use with Method#to_proc.
118 class Method::AsBlockEnvironment < BlockEnvironment
119 def initialize(method)
122 def method; @method.compiled_method; end
123 def file; method.file; end
124 def line; method.first_line; end
126 @method = @method.unbind.bind(obj)
128 def call(*args); @method.call(*args); end
129 def call_on_instance(obj, *args)
130 redirect_to(obj).call(*args)
132 def arity; @method.arity; end
136 # UnboundMethods are similar to Method objects except that they are not
137 # connected to any particular object. They cannot be used standalone for this
138 # reason, and must be bound to an object first. The object must be kind_of?
139 # the Module in which this method was originally defined.
141 # UnboundMethods can be created in two ways: first, any existing Method object
142 # can be sent #unbind to detach it from its current object and return an
143 # UnboundMethod instead. Secondly, they can be directly created by calling
144 # Module#instance_method with the desired method's name.
146 # The UnboundMethod is a copy of the method as it existed at the time of
147 # creation. Any subsequent changes to the original will not affect any
148 # existing UnboundMethods.
153 # Accepts and stores the Module where the method is defined in as well as
154 # the CompiledMethod itself. Class of the object the method was extracted
155 # from can be given but will not be stored. This is always used internally
158 def initialize(mod, compiled_method, pulled_from = nil)
160 @compiled_method = compiled_method
161 @pulled_from = pulled_from
164 attr_reader :compiled_method
165 attr_reader :defined_in
168 # UnboundMethod objects are equal if and only if they refer to the same
169 # method. One may be an alias for the other or both for a common one. Both
170 # must have been extracted from the same class or subclass. Two from
171 # different subclasses will not be considered equal.
174 return true if other.kind_of? UnboundMethod and
175 @defined_in == other.defined_in and
176 @compiled_method == other.compiled_method
185 @compiled_method.required
189 # Creates a new Method object by attaching this method to the supplied
190 # receiver. The receiver must be kind_of? the Module that the method in
191 # question is defined in.
193 # Notably, this is a difference from MRI which requires that the object is
194 # of the exact Module the method was extracted from. This is safe because
195 # any overridden method will be identified as being defined in a different
199 if @defined_in.kind_of? MetaClass
200 unless @defined_in.attached_instance == receiver
201 raise TypeError, "Must be bound to #{@defined_in.attached_instance.inspect} only"
204 unless receiver.__kind_of__ @defined_in
205 raise TypeError, "Must be bound to an object of kind #{@defined_in}"
208 Method.new receiver, @defined_in, @compiled_method
212 # Convenience method for #binding to the given receiver object and calling
213 # it with the optionally supplied arguments.
215 def call_on_instance(obj, *args)
216 bind(obj).call(*args)
220 # String representation for UnboundMethod includes the method name, the
221 # Module it is defined in and the Module that it was extracted from.
224 "#<#{self.class}: #{@pulled_from}##{@compiled_method.name} (defined in #{@defined_in})>"
227 alias_method :to_s, :inspect