class Proc def self.allocate raise TypeError, "allocator undefined for Proc" end def self.__allocate__ Rubinius.primitive :proc_allocate raise PrimitiveFailure, "Proc#allocate primitive failed" end def call_prim(*args) Rubinius.primitive :proc_call raise PrimitiveFailure, "Proc#call primitive failed" end def call(*args, &block) if @ruby_method @ruby_method.call(*args, &block) else call_prim(*args, &block) end end def call_on_object(*args) Rubinius.primitive :proc_call_on_object raise PrimitiveFailure, "Proc#call_on_object primitive failed" end def lambda_style! @lambda = true end def lambda? !!@lambda end end class Proc def self.__from_block__(env) # The compiler must be fixed before this method can be removed. Rubinius::Mirror::Proc.from_block self, env end def self.new(*args) env = nil Rubinius.asm do push_block # assign a pushed block to the above local variable "env" # Note that "env" is indexed at 1, not 0. "args" is indexed at 0. set_local 1 end unless env # Support for ancient pre-block-pass style: # def something; Proc.new; end # something { a_block } => Proc instance env = Rubinius::BlockEnvironment.of_sender unless env raise ArgumentError, "tried to create a Proc object without a block" end end block = Rubinius::Mirror::Proc.from_block self, env if block.class != self block = block.dup Rubinius::Unsafe.set_class(block, self) end Rubinius.asm(block, args) do |b, a| run b run a run b send_with_splat :initialize, 0, true end return block end attr_accessor :block attr_accessor :bound_method attr_accessor :ruby_method def binding bind = @block.to_binding bind.proc_environment = @block bind end def arity if @ruby_method return @ruby_method.arity elsif @bound_method arity = @bound_method.arity return arity < 0 ? -1 : arity end arity = @block.arity return arity if self.lambda? || @block.compiled_code.splat || @block.compiled_code.total_args == arity -arity - 1 end alias_method :===, :call def curry(curried_arity = nil) if lambda? && curried_arity if arity >= 0 && curried_arity != arity raise ArgumentError, "Wrong number of arguments (%i for %i)" % [ curried_arity, arity ] end if arity < 0 && curried_arity < (-arity - 1) raise ArgumentError, "Wrong number of arguments (%i for %i)" % [ curried_arity, -arity - 1 ] end end args = [] m = Rubinius::Mirror.reflect self f = m.curry self, [], arity f.singleton_class.send(:define_method, :binding) do raise ArgumentError, "cannot create binding from f proc" end f.singleton_class.thunk_method :parameters, [[:rest]] f.singleton_class.thunk_method :source_location, nil f end def source_location if @ruby_method @ruby_method.source_location elsif @bound_method if @bound_method.respond_to?(:source_location) @bound_method.source_location else nil end else @block.source_location end end def to_s file, line = source_location l = " (lambda)" if lambda? if file and line "#<#{self.class}:0x#{self.object_id.to_s(16)}@#{file}:#{line}#{l}>" else "#<#{self.class}:0x#{self.object_id.to_s(16)}#{l}>" end end alias_method :inspect, :to_s def self.__from_method__(meth) obj = __allocate__ obj.ruby_method = meth obj.lambda_style! return obj end def __yield__(*args, &block) @ruby_method.call(*args, &block) end def parameters if @ruby_method return @ruby_method.parameters elsif @bound_method return @bound_method.parameters end code = @block.compiled_code params = [] return params unless code.respond_to? :local_names m = code.required_args - code.post_args o = m + code.total_args - code.required_args p = o + code.post_args p += 1 if code.splat required_status = self.lambda? ? :req : :opt code.local_names.each_with_index do |name, i| if i < m params << [required_status, name] elsif i < o params << [:opt, name] elsif code.splat == i if name == :* params << [:rest] else params << [:rest, name] end elsif i < p params << [required_status, name] elsif code.block_index == i params << [:block, name] end end params end def to_proc self end alias_method :[], :call alias_method :yield, :call def clone copy = self.class.__allocate__ Rubinius.invoke_primitive :object_copy_object, copy, self Rubinius.invoke_primitive :object_copy_singleton_class, copy, self Rubinius.privately do copy.initialize_copy self end copy.freeze if frozen? copy end def dup copy = self.class.__allocate__ Rubinius.invoke_primitive :object_copy_object, copy, self Rubinius.privately do copy.initialize_copy self end copy end def for_define_method(name, klass) if @ruby_method code, scope = @ruby_method.for_define_method(name, klass, self) else be = @block.dup be.change_name name duped_proc = self.dup duped_proc.lambda_style! code = Rubinius::BlockEnvironment::AsMethod.new(be) scope = duped_proc.block.scope end [code, scope] end def self.from_method(meth) if meth.kind_of? Method return __from_method__(meth) else raise ArgumentError, "tried to create a Proc object without a Method" end end end