## # Method objects are essentially detached, freely passed-around methods. The # Method is a copy of the method on the object at the time of extraction, so # if the method itself is changed, overridden, aliased or removed from the # object, the Method object still contains the old functionality. In addition, # the call itself is not in any way stored so it will reflect the state of the # object at the time of calling. # # Methods are normally bound to a particular object but it is possible to use # Method#unbind to create an UnboundMethod object for the purpose of # re-binding to a different object. class Method ## # Takes and stores the receiver object, the method's bytecodes and the # Module that the method is defined in. def initialize(receiver, defined_in, executable, name) @receiver = receiver @defined_in = defined_in @executable = executable @name = name end private :initialize attr_reader :receiver attr_reader :defined_in attr_reader :executable def name @name end ## # Method objects are equal if they have the same body and are bound to the # same object. def ==(other) other.class == Method && @receiver.equal?(other.receiver) && (@executable == other.executable || Rubinius::MethodEquality.method_equal_to_delegated_method_receiver?(self, other)) end alias_method :eql?, :== ## # Indication of how many arguments this method takes. It is defined so that # a non-negative Integer means the method takes that fixed amount of # arguments (up to 1024 currently.) A negative Integer is used to indicate a # variable argument count. The number is ((-n) - 1), where n is the number # of required args. Blocks are not counted. # # def foo(); end # arity => 0 # def foo(a, b); end # arity => 2 # def foo(a, &b); end # arity => 1 # def foo(a, b = nil); end # arity => ((-1) -1) => -2 # def foo(*a); end # arity => ((-0) -1) => -1 # def foo(a, b, *c, &d); end # arity => ((-2) -1) => -3 def arity @executable.arity end ## # Execute the method. This works exactly like calling a method with the same # code on the receiver object. Arguments and a block can be supplied # optionally. def call(*args, &block) @executable.invoke(@name, @defined_in, @receiver, args, block) end alias_method :[], :call ## # String representation of this Method includes the method name, the Module # it is defined in and the Module that it was extracted from. def inspect file, line = source_location() if @executable.respond_to? :eval_source if src = @executable.eval_source src_line = src.split("\n")[line - 1] return "#<#{self.class}: #{@receiver.class}##{@name} (defined in #{@defined_in} from \"#{src_line}\")>" end end if file "#<#{self.class}: #{@receiver.class}##{@name} (defined in #{@defined_in} at #{file}:#{line})>" else "#<#{self.class}: #{@receiver.class}##{@name} (defined in #{@defined_in})>" end end alias_method :to_s, :inspect ## # Location gives the file and line number of the start of this method's # definition. def source_location if @executable.respond_to? :file [@executable.file.to_s, @executable.defined_line] elsif @executable.respond_to? :source_location @executable.source_location else nil end end def source if @executable.respond_to? :eval_source return @executable.eval_source end return nil end def parameters if @executable.respond_to? :parameters @executable.parameters else [] end end def owner if @defined_in.class == Rubinius::IncludedModule @defined_in.module else @defined_in end end ## # Returns a Proc object corresponding to this Method. def to_proc Proc.from_method self end ## # Calls curry on the method in proc representation def curry(n = nil) to_proc.curry(n) end ## # Detach this Method from the receiver object it is bound to and create an # UnboundMethod object. Populates the UnboundMethod with the method data as # well as the Module it is defined in and the Module it was extracted from. # # See UnboundMethod for more information. def unbind UnboundMethod.new(@defined_in, @executable, @receiver.class, @name) end def super_method superclass = @defined_in.direct_superclass if superclass mod, entry = superclass.lookup_method(@name) if entry && entry.visibility != :undef return Method.new(@receiver, superclass, entry.method, @name) end end return nil end def for_define_method(name, klass, callable_proc = nil) Rubinius::Type.bindable_method? self.defined_in, klass scope = @executable.scope if @executable.is_a? Rubinius::DelegatedMethod code = @executable else if callable_proc code = Rubinius::DelegatedMethod.new(name, :call, callable_proc, false) else code = Rubinius::DelegatedMethod.new(name, :call_on_instance, self.unbind, true) end end [code, scope] end end ## # UnboundMethods are similar to Method objects except that they are not # connected to any particular object. They cannot be used standalone for this # reason, and must be bound to an object first. The object must be kind_of? # the Module in which this method was originally defined. # # UnboundMethods can be created in two ways: first, any existing Method object # can be sent #unbind to detach it from its current object and return an # UnboundMethod instead. Secondly, they can be directly created by calling # Module#instance_method with the desired method's name. # # The UnboundMethod is a copy of the method as it existed at the time of # creation. Any subsequent changes to the original will not affect any # existing UnboundMethods. class UnboundMethod ## # Accepts and stores the Module where the method is defined in as well as # the CompiledCode itself. Class of the object the method was extracted # from can be given but will not be stored. This is always used internally # only. def initialize(mod, executable, pulled_from, name) @defined_in = mod @executable = executable @pulled_from = pulled_from @name = name end private :initialize attr_reader :executable attr_reader :defined_in def name @name end ## # Convenience method for #binding to the given receiver object and calling # it with the optionally supplied arguments. def call_on_instance(obj, *args, &block) @executable.invoke(@name, @defined_in, obj, args, block) end ## # UnboundMethod objects are equal if and only if they refer to the same # method. One may be an alias for the other or both for a common one. Both # must have been extracted from the same class or subclass. Two from # different subclasses will not be considered equal. def ==(other) other.kind_of? UnboundMethod and @defined_in == other.defined_in and @executable == other.executable end def hash @defined_in.hash ^ @executable.hash end ## # See Method#arity. def arity @executable.arity end ## # Creates a new Method object by attaching this method to the supplied # receiver. One of the following must be true: # - the method was defined in a module # - the method is being bound to an instance of a subclass of the method's source # - the method is being bound to an instance of the same class def bind(receiver) from_module = Rubinius::Type.object_kind_of? @defined_in, Module from_class = Rubinius::Type.object_kind_of? @defined_in, Class from_module_not_class = from_module && !from_class unless Rubinius::Type.object_kind_of?(receiver, @defined_in) or from_module_not_class if Rubinius::Type.singleton_class_object(@defined_in) raise TypeError, "illegal attempt to rebind a singleton method to another object" end raise TypeError, "Must be bound to an object of kind #{@defined_in}" end Method.new receiver, @defined_in, @executable, @name end ## # String representation for UnboundMethod includes the method name, the # Module it is defined in and the Module that it was extracted from. def inspect file, line = source_location() if file "#<#{self.class}: #{@pulled_from}##{@name} (defined in #{@defined_in} at #{file}:#{line})>" else "#<#{self.class}: #{@pulled_from}##{@name} (defined in #{@defined_in})>" end end alias_method :to_s, :inspect def source_location if @executable.respond_to? :file [@executable.file.to_s, @executable.defined_line] elsif @executable.respond_to? :source_location @executable.source_location else nil end end def parameters return @executable.respond_to?(:parameters) ? @executable.parameters : [] end def owner if @defined_in.class == Rubinius::IncludedModule @defined_in.module else @defined_in end end def super_method superclass = @defined_in.direct_superclass if superclass mod, entry = superclass.lookup_method(@name) if entry && entry.visibility != :undef return UnboundMethod.new(superclass, entry.method, @defined_in, @name) end end return nil end def for_define_method(name, klass, callable_proc = nil) Rubinius::Type.bindable_method? self.defined_in, klass case @executable when Rubinius::AccessVariable code = @executable scope = nil when Rubinius::DelegatedMethod code = @executable scope = code.scope else if callable_proc code = Rubinius::DelegatedMethod.new(name, :call, callable_proc, false) else code = Rubinius::DelegatedMethod.new(name, :call_on_instance, self, true) end scope = @executable.scope end [code, scope] end end