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_environment(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)
115 class Method::AsBlockEnvironment < BlockEnvironment
116 def initialize(method)
119 def method; @method.compiled_method; end
120 def file; method.file; end
121 def line; method.first_line; end
123 @method = @method.unbind.bind(obj)
125 def call(*args); @method.call(*args); end
126 def call_on_instance(obj, *args)
127 redirect_to(obj).call(*args)
129 def arity; @method.arity; end
133 # UnboundMethods are similar to Method objects except that they are not
134 # connected to any particular object. They cannot be used standalone for this
135 # reason, and must be bound to an object first. The object must be kind_of?
136 # the Module in which this method was originally defined.
138 # UnboundMethods can be created in two ways: first, any existing Method object
139 # can be sent #unbind to detach it from its current object and return an
140 # UnboundMethod instead. Secondly, they can be directly created by calling
141 # Module#instance_method with the desired method's name.
143 # The UnboundMethod is a copy of the method as it existed at the time of
144 # creation. Any subsequent changes to the original will not affect any
145 # existing UnboundMethods.
150 # Accepts and stores the Module where the method is defined in as well as
151 # the CompiledMethod itself. Class of the object the method was extracted
152 # from can be given but will not be stored. This is always used internally
155 def initialize(mod, compiled_method, pulled_from = nil)
157 @compiled_method = compiled_method
158 @pulled_from = pulled_from
161 attr_reader :compiled_method
162 attr_reader :defined_in
165 # UnboundMethod objects are equal if and only if they refer to the same
166 # method. One may be an alias for the other or both for a common one. Both
167 # must have been extracted from the same class or subclass. Two from
168 # different subclasses will not be considered equal.
171 return true if other.kind_of? UnboundMethod and
172 @defined_in == other.defined_in and
173 @compiled_method == other.compiled_method
182 @compiled_method.required
186 # Creates a new Method object by attaching this method to the supplied
187 # receiver. The receiver must be kind_of? the Module that the method in
188 # question is defined in.
190 # Notably, this is a difference from MRI which requires that the object is
191 # of the exact Module the method was extracted from. This is safe because
192 # any overridden method will be identified as being defined in a different
196 unless receiver.kind_of? @defined_in
197 raise TypeError, "Must be bound to an object of kind #{@defined_in}"
199 Method.new receiver, @defined_in, @compiled_method
203 # Convenience method for #binding to the given receiver object and calling
204 # it with the optionally supplied arguments.
206 def call_on_instance(obj, *args)
207 bind(obj).call(*args)
211 # String representation for UnboundMethod includes the method name, the
212 # Module it is defined in and the Module that it was extracted from.
215 "#<#{self.class}: #{@pulled_from}##{@compiled_method.name} (defined in #{@defined_in})>"
218 alias_method :to_s, :inspect