/********************************************************************** proc.c - Proc, Binding, Env $Author$ created at: Wed Jan 17 12:13:14 2007 Copyright (C) 2004-2007 Koichi Sasada **********************************************************************/ #include "eval_intern.h" #include "gc.h" struct METHOD { VALUE oclass; /* class that holds the method */ VALUE rclass; /* class of the recevier */ VALUE recv; ID id, oid; NODE *body; }; VALUE rb_cUnboundMethod; VALUE rb_cMethod; VALUE rb_cBinding; VALUE rb_cProc; static VALUE bmcall(VALUE, VALUE); static int method_arity(VALUE); static VALUE rb_obj_is_method(VALUE m); /* Proc */ static void proc_free(void *ptr) { RUBY_FREE_ENTER("proc"); if (ptr) { ruby_xfree(ptr); } RUBY_FREE_LEAVE("proc"); } static void proc_mark(void *ptr) { rb_proc_t *proc; RUBY_MARK_ENTER("proc"); if (ptr) { proc = ptr; RUBY_MARK_UNLESS_NULL(proc->envval); RUBY_MARK_UNLESS_NULL(proc->blockprocval); RUBY_MARK_UNLESS_NULL((VALUE)proc->special_cref_stack); RUBY_MARK_UNLESS_NULL(proc->block.proc); if (proc->block.iseq && RUBY_VM_IFUNC_P(proc->block.iseq)) { RUBY_MARK_UNLESS_NULL((VALUE)(proc->block.iseq)); } } RUBY_MARK_LEAVE("proc"); } VALUE rb_proc_alloc(VALUE klass) { VALUE obj; rb_proc_t *proc; obj = Data_Make_Struct(klass, rb_proc_t, proc_mark, proc_free, proc); MEMZERO(proc, rb_proc_t, 1); return obj; } VALUE rb_obj_is_proc(VALUE proc) { if (TYPE(proc) == T_DATA && RDATA(proc)->dfree == (RUBY_DATA_FUNC) proc_free) { return Qtrue; } else { return Qfalse; } } static VALUE proc_dup(VALUE self) { VALUE procval = rb_proc_alloc(rb_cProc); rb_proc_t *src, *dst; GetProcPtr(self, src); GetProcPtr(procval, dst); dst->block = src->block; dst->block.proc = procval; dst->envval = src->envval; dst->safe_level = dst->safe_level; dst->special_cref_stack = src->special_cref_stack; return procval; } static VALUE proc_clone(VALUE self) { VALUE procval = proc_dup(self); CLONESETUP(procval, self); return procval; } /* * call-seq: * prc.lambda? => true or false * * Returns true for a Proc object which argument handling is rigid. * Such procs are typically generated by lambda. * * A Proc object generated by proc ignore extra arguments. * * proc {|a,b| [a,b] }.call(1,2,3) => [1,2] * * It provides nil for lacked arguments. * * proc {|a,b| [a,b] }.call(1) => [1,nil] * * It expand single-array argument. * * proc {|a,b| [a,b] }.call([1,2]) => [1,2] * * A Proc object generated by lambda doesn't have such tricks. * * lambda {|a,b| [a,b] }.call(1,2,3) => ArgumentError * lambda {|a,b| [a,b] }.call(1) => ArgumentError * lambda {|a,b| [a,b] }.call([1,2]) => ArgumentError * * Proc#lambda? is a predicate for the tricks. * It returns true if no tricks. * * lambda {}.lambda? => true * proc {}.lambda? => false * * Proc.new is same as proc. * * Proc.new {}.lambda? => false * * lambda, proc and Proc.new preserves the tricks of * a Proc object given by & argument. * * lambda(&lambda {}).lambda? => true * proc(&lambda {}).lambda? => true * Proc.new(&lambda {}).lambda? => true * * lambda(&proc {}).lambda? => false * proc(&proc {}).lambda? => false * Proc.new(&proc {}).lambda? => false * * A Proc object generated by & argument has the tricks * * def n(&b) b.lambda? end * n {} => false * * The & argument preserves the tricks if a Proc object is given * by & argument. * * n(&lambda {}) => true * n(&proc {}) => false * n(&Proc.new {}) => false * * A Proc object converted from a method has no tricks. * * def m() end * method(:m).to_proc.lambda? => true * * n(&method(:m)) => true * n(&method(:m)).to_proc => true * * define_method is treated same as method definition. * The defined method has no tricks. * * class C * define_method(:d) {} * end * C.new.e(1,2) => ArgumentError * C.new.method(:d).to_proc.lambda? => true * * define_method always defines a method without the tricks, * even if a non-lambda Proc object is given. * This is the only exception which the tricks are not preserved. * * class C * define_method(:e, &proc {}) * end * C.new.e(1,2) => ArgumentError * C.new.method(:e).to_proc.lambda? => true * * This exception is for a wrapper of define_method. * It eases defining a method defining method which defines a usual method which has no tricks. * * class << C * def def2(name, &body) * define_method(name, &body) * end * end * class C * def2(:f) {} * end * C.new.f(1,2) => ArgumentError * * The wrapper, def2, defines a method which has no tricks. * */ static VALUE proc_lambda_p(VALUE procval) { rb_proc_t *proc; GetProcPtr(procval, proc); return proc->is_lambda ? Qtrue : Qfalse; } /* Binding */ static void binding_free(void *ptr) { rb_binding_t *bind; RUBY_FREE_ENTER("binding"); if (ptr) { bind = ptr; ruby_xfree(ptr); } RUBY_FREE_LEAVE("binding"); } static void binding_mark(void *ptr) { rb_binding_t *bind; RUBY_MARK_ENTER("binding"); if (ptr) { bind = ptr; RUBY_MARK_UNLESS_NULL(bind->env); RUBY_MARK_UNLESS_NULL((VALUE)bind->cref_stack); } RUBY_MARK_LEAVE("binding"); } static VALUE binding_alloc(VALUE klass) { VALUE obj; rb_binding_t *bind; obj = Data_Make_Struct(klass, rb_binding_t, binding_mark, binding_free, bind); return obj; } static VALUE binding_dup(VALUE self) { VALUE bindval = binding_alloc(rb_cBinding); rb_binding_t *src, *dst; GetBindingPtr(self, src); GetBindingPtr(bindval, dst); dst->env = src->env; dst->cref_stack = src->cref_stack; return bindval; } static VALUE binding_clone(VALUE self) { VALUE bindval = binding_dup(self); CLONESETUP(bindval, self); return bindval; } VALUE rb_binding_new(void) { rb_thread_t *th = GET_THREAD(); rb_control_frame_t *cfp = vm_get_ruby_level_cfp(th, th->cfp); VALUE bindval = binding_alloc(rb_cBinding); rb_binding_t *bind; GetBindingPtr(bindval, bind); bind->env = vm_make_env_object(th, cfp); bind->cref_stack = ruby_cref(); return bindval; } /* * call-seq: * binding -> a_binding * * Returns a +Binding+ object, describing the variable and * method bindings at the point of call. This object can be used when * calling +eval+ to execute the evaluated command in this * environment. Also see the description of class +Binding+. * * def getBinding(param) * return binding * end * b = getBinding("hello") * eval("param", b) #=> "hello" */ static VALUE rb_f_binding(VALUE self) { return rb_binding_new(); } /* * call-seq: * binding.eval(string [, filename [,lineno]]) => obj * * Evaluates the Ruby expression(s) in string, in the * binding's context. If the optional filename and * lineno parameters are present, they will be used when * reporting syntax errors. * * def getBinding(param) * return binding * end * b = getBinding("hello") * b.eval("param") #=> "hello" */ static VALUE bind_eval(int argc, VALUE *argv, VALUE bindval) { VALUE args[4]; rb_scan_args(argc, argv, "12", &args[0], &args[2], &args[3]); args[1] = bindval; return rb_f_eval(argc+1, args, Qnil /* self will be searched in eval */); } static VALUE proc_new(VALUE klass, int is_lambda) { VALUE procval = Qnil; rb_thread_t *th = GET_THREAD(); rb_control_frame_t *cfp = th->cfp; rb_block_t *block; if ((GC_GUARDED_PTR_REF(cfp->lfp[0])) != 0 && !RUBY_VM_CLASS_SPECIAL_P(cfp->lfp[0])) { block = GC_GUARDED_PTR_REF(cfp->lfp[0]); cfp = RUBY_VM_PREVIOUS_CONTROL_FRAME(cfp); } else { cfp = RUBY_VM_PREVIOUS_CONTROL_FRAME(cfp); if ((GC_GUARDED_PTR_REF(cfp->lfp[0])) != 0 && !RUBY_VM_CLASS_SPECIAL_P(cfp->lfp[0])) { block = GC_GUARDED_PTR_REF(cfp->lfp[0]); /* TODO: check more (cfp limit, called via cfunc, etc) */ while (cfp->dfp != block->dfp) { cfp = RUBY_VM_PREVIOUS_CONTROL_FRAME(cfp); } if (is_lambda) { rb_warn("tried to create Proc object without a block"); } } else { rb_raise(rb_eArgError, "tried to create Proc object without a block"); } } if (block->proc) { return block->proc; } procval = vm_make_proc(th, cfp, block); if (is_lambda) { rb_proc_t *proc; GetProcPtr(procval, proc); proc->is_lambda = Qtrue; } return procval; } /* * call-seq: * Proc.new {|...| block } => a_proc * Proc.new => a_proc * * Creates a new Proc object, bound to the current * context. Proc::new may be called without a block only * within a method with an attached block, in which case that block is * converted to the Proc object. * * def proc_from * Proc.new * end * proc = proc_from { "hello" } * proc.call #=> "hello" */ static VALUE rb_proc_s_new(int argc, VALUE *argv, VALUE klass) { VALUE block = proc_new(klass, Qfalse); rb_obj_call_init(block, argc, argv); return block; } /* * call-seq: * proc { |...| block } => a_proc * * Equivalent to Proc.new. */ VALUE rb_block_proc(void) { return proc_new(rb_cProc, Qfalse); } VALUE rb_block_lambda(void) { return proc_new(rb_cProc, Qtrue); } VALUE rb_f_lambda(void) { rb_warn("rb_f_lambda() is deprecated; use rb_block_proc() instead"); return rb_block_lambda(); } /* * call-seq: * lambda { |...| block } => a_proc * * Equivalent to Proc.new, except the resulting Proc objects * check the number of parameters passed when called. */ static VALUE proc_lambda(void) { return rb_block_lambda(); } /* CHECKME: are the argument checking semantics correct? */ /* * call-seq: * prc.call(params,...) => obj * prc[params,...] => obj * * Invokes the block, setting the block's parameters to the values in * params using something close to method calling semantics. * Generates a warning if multiple values are passed to a proc that * expects just one (previously this silently converted the parameters * to an array). * * For procs created using Kernel.proc, generates an * error if the wrong number of parameters * are passed to a proc with multiple parameters. For procs created using * Proc.new, extra parameters are silently discarded. * * Returns the value of the last expression evaluated in the block. See * also Proc#yield. * * a_proc = Proc.new {|a, *b| b.collect {|i| i*a }} * a_proc.call(9, 1, 2, 3) #=> [9, 18, 27] * a_proc[9, 1, 2, 3] #=> [9, 18, 27] * a_proc = Proc.new {|a,b| a} * a_proc.call(1,2,3) * * produces: * * prog.rb:5: wrong number of arguments (3 for 2) (ArgumentError) * from prog.rb:4:in `call' * from prog.rb:5 */ static VALUE proc_call(int argc, VALUE *argv, VALUE procval) { rb_proc_t *proc; rb_block_t *blockptr = 0; GetProcPtr(procval, proc); if (BUILTIN_TYPE(proc->block.iseq) != T_NODE && proc->block.iseq->arg_block != -1) { if (rb_block_given_p()) { rb_proc_t *proc; VALUE procval; procval = rb_block_proc(); GetProcPtr(procval, proc); blockptr = &proc->block; } } return vm_invoke_proc(GET_THREAD(), proc, proc->block.self, argc, argv, blockptr); } VALUE rb_proc_call(VALUE self, VALUE args) { rb_proc_t *proc; GetProcPtr(self, proc); return vm_invoke_proc(GET_THREAD(), proc, proc->block.self, RARRAY_LEN(args), RARRAY_PTR(args), 0); } /* * call-seq: * prc.arity -> fixnum * * Returns the number of arguments that would not be ignored. If the block * is declared to take no arguments, returns 0. If the block is known * to take exactly n arguments, returns n. If the block has optional * arguments, return -n-1, where n is the number of mandatory * arguments. A proc with no argument declarations * is the same a block declaring || as its arguments. * * Proc.new {}.arity #=> 0 * Proc.new {||}.arity #=> 0 * Proc.new {|a|}.arity #=> 1 * Proc.new {|a,b|}.arity #=> 2 * Proc.new {|a,b,c|}.arity #=> 3 * Proc.new {|*a|}.arity #=> -1 * Proc.new {|a,*b|}.arity #=> -2 * Proc.new {|a,*b, c|}.arity #=> -3 */ static VALUE proc_arity(VALUE self) { rb_proc_t *proc; rb_iseq_t *iseq; GetProcPtr(self, proc); iseq = proc->block.iseq; if (iseq) { if (BUILTIN_TYPE(iseq) != T_NODE) { if (iseq->arg_rest < 0) { return INT2FIX(iseq->argc); } else { return INT2FIX(-(iseq->argc + 1 + iseq->arg_post_len)); } } else { NODE *node = (NODE *)iseq; if (nd_type(node) == NODE_IFUNC && node->nd_cfnc == bmcall) { /* method(:foo).to_proc.arity */ return INT2FIX(method_arity(node->nd_tval)); } } } return INT2FIX(-1); } int rb_proc_arity(VALUE proc) { return FIX2INT(proc_arity(proc)); } static rb_iseq_t * get_proc_iseq(VALUE self) { rb_proc_t *proc; rb_iseq_t *iseq; GetProcPtr(self, proc); iseq = proc->block.iseq; if (!RUBY_VM_NORMAL_ISEQ_P(iseq)) return 0; return iseq; } VALUE rb_proc_location(VALUE self) { rb_iseq_t *iseq = get_proc_iseq(self); VALUE loc[2]; if (!iseq) return Qnil; loc[0] = iseq->filename; if (iseq->insn_info_table) { loc[1] = INT2FIX(iseq->insn_info_table[0].line_no); } else { loc[1] = Qnil; } return rb_ary_new4(2, loc); } /* * call-seq: * prc == other_proc => true or false * * Return true if prc is the same object as * other_proc, or if they are both procs with the same body. */ static VALUE proc_eq(VALUE self, VALUE other) { if (self == other) { return Qtrue; } else { if (TYPE(other) == T_DATA && RBASIC(other)->klass == rb_cProc && CLASS_OF(self) == CLASS_OF(other)) { rb_proc_t *p1, *p2; GetProcPtr(self, p1); GetProcPtr(other, p2); if (p1->block.iseq == p2->block.iseq && p1->envval == p2->envval) { return Qtrue; } } } return Qfalse; } /* * call-seq: * prc.hash => integer * * Return hash value corresponding to proc body. */ static VALUE proc_hash(VALUE self) { int hash; rb_proc_t *proc; GetProcPtr(self, proc); hash = (long)proc->block.iseq; hash ^= (long)proc->envval; hash ^= (long)proc->block.lfp >> 16; return INT2FIX(hash); } /* * call-seq: * prc.to_s => string * * Shows the unique identifier for this proc, along with * an indication of where the proc was defined. */ static VALUE proc_to_s(VALUE self) { VALUE str = 0; rb_proc_t *proc; char *cname = rb_obj_classname(self); rb_iseq_t *iseq; const char *is_lambda; GetProcPtr(self, proc); iseq = proc->block.iseq; is_lambda = proc->is_lambda ? " (lambda)" : ""; if (RUBY_VM_NORMAL_ISEQ_P(iseq)) { int line_no = 0; if (iseq->insn_info_table) { line_no = iseq->insn_info_table[0].line_no; } str = rb_sprintf("#<%s:%p@%s:%d%s>", cname, (void *)self, RSTRING_PTR(iseq->filename), line_no, is_lambda); } else { str = rb_sprintf("#<%s:%p%s>", cname, proc->block.iseq, is_lambda); } if (OBJ_TAINTED(self)) { OBJ_TAINT(str); } return str; } /* * call-seq: * prc.to_proc -> prc * * Part of the protocol for converting objects to Proc * objects. Instances of class Proc simply return * themselves. */ static VALUE proc_to_proc(VALUE self) { return self; } /* * call-seq: * prc.binding => binding * * Returns the binding associated with prc. Note that * Kernel#eval accepts either a Proc or a * Binding object as its second parameter. * * def fred(param) * proc {} * end * * b = fred(99) * eval("param", b.binding) #=> 99 * eval("param", b) #=> 99 */ static void bm_mark(struct METHOD *data) { rb_gc_mark(data->rclass); rb_gc_mark(data->oclass); rb_gc_mark(data->recv); rb_gc_mark((VALUE)data->body); } NODE * rb_method_body(VALUE method) { struct METHOD *data; if (TYPE(method) == T_DATA && RDATA(method)->dmark == (RUBY_DATA_FUNC) bm_mark) { Data_Get_Struct(method, struct METHOD, data); return data->body; } else { return 0; } } NODE *rb_get_method_body(VALUE klass, ID id, ID *idp); static VALUE mnew(VALUE klass, VALUE obj, ID id, VALUE mclass, int scope) { VALUE method; NODE *body; struct METHOD *data; VALUE rclass = klass; ID oid = id; again: if ((body = rb_get_method_body(klass, id, 0)) == 0) { rb_print_undef(rclass, oid, 0); } if (scope && (body->nd_noex & NOEX_MASK) != NOEX_PUBLIC) { rb_print_undef(rclass, oid, (body->nd_noex & NOEX_MASK)); } klass = body->nd_clss; body = body->nd_body; if (nd_type(body) == NODE_ZSUPER) { klass = RCLASS_SUPER(klass); goto again; } while (rclass != klass && (FL_TEST(rclass, FL_SINGLETON) || TYPE(rclass) == T_ICLASS)) { rclass = RCLASS_SUPER(rclass); } if (TYPE(klass) == T_ICLASS) klass = RBASIC(klass)->klass; method = Data_Make_Struct(mclass, struct METHOD, bm_mark, -1, data); data->oclass = klass; data->recv = obj; data->id = id; data->body = body; data->rclass = rclass; data->oid = oid; OBJ_INFECT(method, klass); return method; } /********************************************************************** * * Document-class : Method * * Method objects are created by Object#method, and are * associated with a particular object (not just with a class). They * may be used to invoke the method within the object, and as a block * associated with an iterator. They may also be unbound from one * object (creating an UnboundMethod) and bound to * another. * * class Thing * def square(n) * n*n * end * end * thing = Thing.new * meth = thing.method(:square) * * meth.call(9) #=> 81 * [ 1, 2, 3 ].collect(&meth) #=> [1, 4, 9] * */ /* * call-seq: * meth == other_meth => true or false * * Two method objects are equal if that are bound to the same * object and contain the same body. */ static VALUE method_eq(VALUE method, VALUE other) { struct METHOD *m1, *m2; if (TYPE(other) != T_DATA || RDATA(other)->dmark != (RUBY_DATA_FUNC) bm_mark) return Qfalse; if (CLASS_OF(method) != CLASS_OF(other)) return Qfalse; Data_Get_Struct(method, struct METHOD, m1); Data_Get_Struct(other, struct METHOD, m2); if (m1->oclass != m2->oclass || m1->rclass != m2->rclass || m1->recv != m2->recv || m1->body != m2->body) return Qfalse; return Qtrue; } /* * call-seq: * meth.hash => integer * * Return a hash value corresponding to the method object. */ static VALUE method_hash(VALUE method) { struct METHOD *m; long hash; Data_Get_Struct(method, struct METHOD, m); hash = (long)m->oclass; hash ^= (long)m->rclass; hash ^= (long)m->recv; hash ^= (long)m->body; return INT2FIX(hash); } /* * call-seq: * meth.unbind => unbound_method * * Dissociates meth from it's current receiver. The resulting * UnboundMethod can subsequently be bound to a new object * of the same class (see UnboundMethod). */ static VALUE method_unbind(VALUE obj) { VALUE method; struct METHOD *orig, *data; Data_Get_Struct(obj, struct METHOD, orig); method = Data_Make_Struct(rb_cUnboundMethod, struct METHOD, bm_mark, free, data); data->oclass = orig->oclass; data->recv = Qundef; data->id = orig->id; data->body = orig->body; data->rclass = orig->rclass; data->oid = orig->oid; OBJ_INFECT(method, obj); return method; } /* * call-seq: * meth.receiver => object * * Returns the bound receiver of the method object. */ static VALUE method_receiver(VALUE obj) { struct METHOD *data; Data_Get_Struct(obj, struct METHOD, data); return data->recv; } /* * call-seq: * meth.name => string * * Returns the name of the method. */ static VALUE method_name(VALUE obj) { struct METHOD *data; Data_Get_Struct(obj, struct METHOD, data); return rb_str_dup(rb_id2str(data->id)); } /* * call-seq: * meth.owner => class_or_module * * Returns the class or module that defines the method. */ static VALUE method_owner(VALUE obj) { struct METHOD *data; Data_Get_Struct(obj, struct METHOD, data); return data->oclass; } /* * call-seq: * obj.method(sym) => method * * Looks up the named method as a receiver in obj, returning a * Method object (or raising NameError). The * Method object acts as a closure in obj's object * instance, so instance variables and the value of self * remain available. * * class Demo * def initialize(n) * @iv = n * end * def hello() * "Hello, @iv = #{@iv}" * end * end * * k = Demo.new(99) * m = k.method(:hello) * m.call #=> "Hello, @iv = 99" * * l = Demo.new('Fred') * m = l.method("hello") * m.call #=> "Hello, @iv = Fred" */ VALUE rb_obj_method(VALUE obj, VALUE vid) { return mnew(CLASS_OF(obj), obj, rb_to_id(vid), rb_cMethod, Qfalse); } VALUE rb_obj_public_method(VALUE obj, VALUE vid) { return mnew(CLASS_OF(obj), obj, rb_to_id(vid), rb_cMethod, Qtrue); } /* * call-seq: * mod.instance_method(symbol) => unbound_method * * Returns an +UnboundMethod+ representing the given * instance method in _mod_. * * class Interpreter * def do_a() print "there, "; end * def do_d() print "Hello "; end * def do_e() print "!\n"; end * def do_v() print "Dave"; end * Dispatcher = { * ?a => instance_method(:do_a), * ?d => instance_method(:do_d), * ?e => instance_method(:do_e), * ?v => instance_method(:do_v) * } * def interpret(string) * string.each_byte {|b| Dispatcher[b].bind(self).call } * end * end * * * interpreter = Interpreter.new * interpreter.interpret('dave') * * produces: * * Hello there, Dave! */ static VALUE rb_mod_instance_method(VALUE mod, VALUE vid) { return mnew(mod, Qundef, rb_to_id(vid), rb_cUnboundMethod, Qfalse); } static VALUE rb_mod_public_instance_method(VALUE mod, VALUE vid) { return mnew(mod, Qundef, rb_to_id(vid), rb_cUnboundMethod, Qtrue); } /* * call-seq: * define_method(symbol, method) => new_method * define_method(symbol) { block } => proc * * Defines an instance method in the receiver. The _method_ * parameter can be a +Proc+ or +Method+ object. * If a block is specified, it is used as the method body. This block * is evaluated using instance_eval, a point that is * tricky to demonstrate because define_method is private. * (This is why we resort to the +send+ hack in this example.) * * class A * def fred * puts "In Fred" * end * def create_method(name, &block) * self.class.send(:define_method, name, &block) * end * define_method(:wilma) { puts "Charge it!" } * end * class B < A * define_method(:barney, instance_method(:fred)) * end * a = B.new * a.barney * a.wilma * a.create_method(:betty) { p self } * a.betty * * produces: * * In Fred * Charge it! * # */ static VALUE rb_mod_define_method(int argc, VALUE *argv, VALUE mod) { ID id; VALUE body; NODE *node; int noex = NOEX_PUBLIC; if (argc == 1) { id = rb_to_id(argv[0]); body = rb_block_lambda(); } else if (argc == 2) { id = rb_to_id(argv[0]); body = argv[1]; if (!rb_obj_is_method(body) && !rb_obj_is_proc(body)) { rb_raise(rb_eTypeError, "wrong argument type %s (expected Proc/Method)", rb_obj_classname(body)); } } else { rb_raise(rb_eArgError, "wrong number of arguments (%d for 1)", argc); } if (RDATA(body)->dmark == (RUBY_DATA_FUNC) bm_mark) { struct METHOD *method = (struct METHOD *)DATA_PTR(body); VALUE rclass = method->rclass; if (rclass != mod) { if (FL_TEST(rclass, FL_SINGLETON)) { rb_raise(rb_eTypeError, "can't bind singleton method to a different class"); } if (!RTEST(rb_class_inherited_p(mod, rclass))) { rb_raise(rb_eTypeError, "bind argument must be a subclass of %s", rb_class2name(rclass)); } } node = method->body; } else if (rb_obj_is_proc(body)) { rb_proc_t *proc; body = proc_dup(body); GetProcPtr(body, proc); if (BUILTIN_TYPE(proc->block.iseq) != T_NODE) { proc->block.iseq->defined_method_id = id; proc->block.iseq->klass = mod; proc->is_lambda = Qtrue; proc->is_from_method = Qtrue; } node = NEW_BMETHOD(body); } else { /* type error */ rb_raise(rb_eTypeError, "wrong argument type (expected Proc/Method)"); } /* TODO: visibility */ rb_add_method(mod, id, node, noex); return body; } static VALUE rb_obj_define_method(int argc, VALUE *argv, VALUE obj) { VALUE klass = rb_singleton_class(obj); return rb_mod_define_method(argc, argv, klass); } /* * MISSING: documentation */ static VALUE method_clone(VALUE self) { VALUE clone; struct METHOD *orig, *data; Data_Get_Struct(self, struct METHOD, orig); clone = Data_Make_Struct(CLASS_OF(self), struct METHOD, bm_mark, free, data); CLONESETUP(clone, self); *data = *orig; return clone; } /* * call-seq: * meth.call(args, ...) => obj * meth[args, ...] => obj * * Invokes the meth with the specified arguments, returning the * method's return value. * * m = 12.method("+") * m.call(3) #=> 15 * m.call(20) #=> 32 */ VALUE rb_method_call(int argc, VALUE *argv, VALUE method) { VALUE result = Qnil; /* OK */ struct METHOD *data; int state; volatile int safe = -1; Data_Get_Struct(method, struct METHOD, data); if (data->recv == Qundef) { rb_raise(rb_eTypeError, "can't call unbound method; bind first"); } PUSH_TAG(); if (OBJ_TAINTED(method)) { safe = rb_safe_level(); if (rb_safe_level() < 4) { rb_set_safe_level_force(4); } } if ((state = EXEC_TAG()) == 0) { PASS_PASSED_BLOCK(); result = vm_call0(GET_THREAD(), data->oclass, data->recv, data->id, data->oid, argc, argv, data->body, 0); } POP_TAG(); if (safe >= 0) rb_set_safe_level_force(safe); if (state) JUMP_TAG(state); return result; } /********************************************************************** * * Document-class: UnboundMethod * * Ruby supports two forms of objectified methods. Class * Method is used to represent methods that are associated * with a particular object: these method objects are bound to that * object. Bound method objects for an object can be created using * Object#method. * * Ruby also supports unbound methods; methods objects that are not * associated with a particular object. These can be created either by * calling Module#instance_method or by calling * unbind on a bound method object. The result of both of * these is an UnboundMethod object. * * Unbound methods can only be called after they are bound to an * object. That object must be be a kind_of? the method's original * class. * * class Square * def area * @side * @side * end * def initialize(side) * @side = side * end * end * * area_un = Square.instance_method(:area) * * s = Square.new(12) * area = area_un.bind(s) * area.call #=> 144 * * Unbound methods are a reference to the method at the time it was * objectified: subsequent changes to the underlying class will not * affect the unbound method. * * class Test * def test * :original * end * end * um = Test.instance_method(:test) * class Test * def test * :modified * end * end * t = Test.new * t.test #=> :modified * um.bind(t).call #=> :original * */ /* * call-seq: * umeth.bind(obj) -> method * * Bind umeth to obj. If Klass was the class * from which umeth was obtained, * obj.kind_of?(Klass) must be true. * * class A * def test * puts "In test, class = #{self.class}" * end * end * class B < A * end * class C < B * end * * * um = B.instance_method(:test) * bm = um.bind(C.new) * bm.call * bm = um.bind(B.new) * bm.call * bm = um.bind(A.new) * bm.call * * produces: * * In test, class = C * In test, class = B * prog.rb:16:in `bind': bind argument must be an instance of B (TypeError) * from prog.rb:16 */ static VALUE umethod_bind(VALUE method, VALUE recv) { struct METHOD *data, *bound; Data_Get_Struct(method, struct METHOD, data); if (data->rclass != CLASS_OF(recv)) { if (FL_TEST(data->rclass, FL_SINGLETON)) { rb_raise(rb_eTypeError, "singleton method called for a different object"); } if (!rb_obj_is_kind_of(recv, data->rclass)) { rb_raise(rb_eTypeError, "bind argument must be an instance of %s", rb_class2name(data->rclass)); } } method = Data_Make_Struct(rb_cMethod, struct METHOD, bm_mark, free, bound); *bound = *data; bound->recv = recv; bound->rclass = CLASS_OF(recv); return method; } int rb_node_arity(NODE* body) { switch (nd_type(body)) { case NODE_CFUNC: if (body->nd_argc < 0) return -1; return body->nd_argc; case NODE_ZSUPER: return -1; case NODE_ATTRSET: return 1; case NODE_IVAR: return 0; case NODE_BMETHOD: return rb_proc_arity(body->nd_cval); case RUBY_VM_METHOD_NODE: { rb_iseq_t *iseq; GetISeqPtr((VALUE)body->nd_body, iseq); if (iseq->arg_rest == -1 && iseq->arg_opts == 0) { return iseq->argc; } else { return -(iseq->argc + 1 + iseq->arg_post_len); } } default: rb_raise(rb_eArgError, "invalid node 0x%x", nd_type(body)); } } /* * call-seq: * meth.arity => fixnum * * Returns an indication of the number of arguments accepted by a * method. Returns a nonnegative integer for methods that take a fixed * number of arguments. For Ruby methods that take a variable number of * arguments, returns -n-1, where n is the number of required * arguments. For methods written in C, returns -1 if the call takes a * variable number of arguments. * * class C * def one; end * def two(a); end * def three(*a); end * def four(a, b); end * def five(a, b, *c); end * def six(a, b, *c, &d); end * end * c = C.new * c.method(:one).arity #=> 0 * c.method(:two).arity #=> 1 * c.method(:three).arity #=> -1 * c.method(:four).arity #=> 2 * c.method(:five).arity #=> -3 * c.method(:six).arity #=> -3 * * "cat".method(:size).arity #=> 0 * "cat".method(:replace).arity #=> 1 * "cat".method(:squeeze).arity #=> -1 * "cat".method(:count).arity #=> -1 */ static VALUE method_arity_m(VALUE method) { int n = method_arity(method); return INT2FIX(n); } static int method_arity(VALUE method) { struct METHOD *data; Data_Get_Struct(method, struct METHOD, data); return rb_node_arity(data->body); } int rb_mod_method_arity(VALUE mod, ID id) { NODE *node = rb_method_node(mod, id); return rb_node_arity(node); } int rb_obj_method_arity(VALUE obj, ID id) { return rb_mod_method_arity(CLASS_OF(obj), id); } /* * call-seq: * meth.to_s => string * meth.inspect => string * * Show the name of the underlying method. * * "cat".method(:count).inspect #=> "#" */ static VALUE method_inspect(VALUE method) { struct METHOD *data; VALUE str; const char *s; char *sharp = "#"; Data_Get_Struct(method, struct METHOD, data); str = rb_str_buf_new2("#<"); s = rb_obj_classname(method); rb_str_buf_cat2(str, s); rb_str_buf_cat2(str, ": "); if (FL_TEST(data->oclass, FL_SINGLETON)) { VALUE v = rb_iv_get(data->oclass, "__attached__"); if (data->recv == Qundef) { rb_str_buf_append(str, rb_inspect(data->oclass)); } else if (data->recv == v) { rb_str_buf_append(str, rb_inspect(v)); sharp = "."; } else { rb_str_buf_append(str, rb_inspect(data->recv)); rb_str_buf_cat2(str, "("); rb_str_buf_append(str, rb_inspect(v)); rb_str_buf_cat2(str, ")"); sharp = "."; } } else { rb_str_buf_cat2(str, rb_class2name(data->rclass)); if (data->rclass != data->oclass) { rb_str_buf_cat2(str, "("); rb_str_buf_cat2(str, rb_class2name(data->oclass)); rb_str_buf_cat2(str, ")"); } } rb_str_buf_cat2(str, sharp); rb_str_append(str, rb_id2str(data->oid)); rb_str_buf_cat2(str, ">"); return str; } static VALUE mproc(VALUE method) { return rb_funcall(Qnil, rb_intern("proc"), 0); } static VALUE mlambda(VALUE method) { return rb_funcall(Qnil, rb_intern("lambda"), 0); } static VALUE bmcall(VALUE args, VALUE method) { volatile VALUE a; if (CLASS_OF(args) != rb_cArray) { args = rb_ary_new3(1, args); } a = args; return rb_method_call(RARRAY_LEN(a), RARRAY_PTR(a), method); } VALUE rb_proc_new( VALUE (*func)(ANYARGS), /* VALUE yieldarg[, VALUE procarg] */ VALUE val) { VALUE procval = rb_iterate(mproc, 0, func, val); return procval; } /* * call-seq: * meth.to_proc => prc * * Returns a Proc object corresponding to this method. */ static VALUE method_proc(VALUE method) { VALUE procval; rb_proc_t *proc; /* * class Method * def to_proc * proc{|*args| * self.call(*args) * } * end * end */ procval = rb_iterate(mlambda, 0, bmcall, method); GetProcPtr(procval, proc); proc->is_from_method = 1; return procval; } static VALUE rb_obj_is_method(VALUE m) { if (TYPE(m) == T_DATA && RDATA(m)->dmark == (RUBY_DATA_FUNC) bm_mark) { return Qtrue; } return Qfalse; } /* * call_seq: * local_jump_error.exit_value => obj * * Returns the exit value associated with this +LocalJumpError+. */ static VALUE localjump_xvalue(VALUE exc) { return rb_iv_get(exc, "@exit_value"); } /* * call-seq: * local_jump_error.reason => symbol * * The reason this block was terminated: * :break, :redo, :retry, :next, :return, or :noreason. */ static VALUE localjump_reason(VALUE exc) { return rb_iv_get(exc, "@reason"); } /* * call-seq: * prc.binding => binding * * Returns the binding associated with prc. Note that * Kernel#eval accepts either a Proc or a * Binding object as its second parameter. * * def fred(param) * proc {} * end * * b = fred(99) * eval("param", b.binding) #=> 99 * eval("param", b) #=> 99 */ static VALUE proc_binding(VALUE self) { rb_proc_t *proc; VALUE bindval = binding_alloc(rb_cBinding); rb_binding_t *bind; GetProcPtr(self, proc); GetBindingPtr(bindval, bind); if (TYPE(proc->block.iseq) == T_NODE) { rb_raise(rb_eArgError, "Can't create Binding from C level Proc"); } bind->env = proc->envval; bind->cref_stack = proc->special_cref_stack; return bindval; } /* * Proc objects are blocks of code that have been bound to * a set of local variables. Once bound, the code may be called in * different contexts and still access those variables. * * def gen_times(factor) * return Proc.new {|n| n*factor } * end * * times3 = gen_times(3) * times5 = gen_times(5) * * times3.call(12) #=> 36 * times5.call(5) #=> 25 * times3.call(times5.call(4)) #=> 60 * */ void Init_Proc(void) { /* Proc */ rb_cProc = rb_define_class("Proc", rb_cObject); rb_undef_alloc_func(rb_cProc); rb_define_singleton_method(rb_cProc, "new", rb_proc_s_new, -1); rb_define_method(rb_cProc, "call", proc_call, -1); rb_define_method(rb_cProc, "[]", proc_call, -1); rb_define_method(rb_cProc, "yield", proc_call, -1); rb_define_method(rb_cProc, "to_proc", proc_to_proc, 0); rb_define_method(rb_cProc, "arity", proc_arity, 0); rb_define_method(rb_cProc, "clone", proc_clone, 0); rb_define_method(rb_cProc, "dup", proc_dup, 0); rb_define_method(rb_cProc, "==", proc_eq, 1); rb_define_method(rb_cProc, "eql?", proc_eq, 1); rb_define_method(rb_cProc, "hash", proc_hash, 0); rb_define_method(rb_cProc, "to_s", proc_to_s, 0); rb_define_method(rb_cProc, "lambda?", proc_lambda_p, 0); rb_define_method(rb_cProc, "binding", proc_binding, 0); /* Exceptions */ rb_eLocalJumpError = rb_define_class("LocalJumpError", rb_eStandardError); rb_define_method(rb_eLocalJumpError, "exit_value", localjump_xvalue, 0); rb_define_method(rb_eLocalJumpError, "reason", localjump_reason, 0); rb_eSysStackError = rb_define_class("SystemStackError", rb_eException); sysstack_error = rb_exc_new2(rb_eSysStackError, "stack level too deep"); OBJ_TAINT(sysstack_error); rb_register_mark_object(sysstack_error); /* utility functions */ rb_define_global_function("proc", rb_block_proc, 0); rb_define_global_function("lambda", proc_lambda, 0); /* Method */ rb_cMethod = rb_define_class("Method", rb_cObject); rb_undef_alloc_func(rb_cMethod); rb_undef_method(CLASS_OF(rb_cMethod), "new"); rb_define_method(rb_cMethod, "==", method_eq, 1); rb_define_method(rb_cMethod, "eql?", method_eq, 1); rb_define_method(rb_cMethod, "hash", method_hash, 0); rb_define_method(rb_cMethod, "clone", method_clone, 0); rb_define_method(rb_cMethod, "call", rb_method_call, -1); rb_define_method(rb_cMethod, "[]", rb_method_call, -1); rb_define_method(rb_cMethod, "arity", method_arity_m, 0); rb_define_method(rb_cMethod, "inspect", method_inspect, 0); rb_define_method(rb_cMethod, "to_s", method_inspect, 0); rb_define_method(rb_cMethod, "to_proc", method_proc, 0); rb_define_method(rb_cMethod, "receiver", method_receiver, 0); rb_define_method(rb_cMethod, "name", method_name, 0); rb_define_method(rb_cMethod, "owner", method_owner, 0); rb_define_method(rb_cMethod, "unbind", method_unbind, 0); rb_define_method(rb_mKernel, "method", rb_obj_method, 1); rb_define_method(rb_mKernel, "public_method", rb_obj_public_method, 1); /* UnboundMethod */ rb_cUnboundMethod = rb_define_class("UnboundMethod", rb_cObject); rb_undef_alloc_func(rb_cUnboundMethod); rb_undef_method(CLASS_OF(rb_cUnboundMethod), "new"); rb_define_method(rb_cUnboundMethod, "==", method_eq, 1); rb_define_method(rb_cUnboundMethod, "eql?", method_eq, 1); rb_define_method(rb_cUnboundMethod, "hash", method_hash, 0); rb_define_method(rb_cUnboundMethod, "clone", method_clone, 0); rb_define_method(rb_cUnboundMethod, "arity", method_arity_m, 0); rb_define_method(rb_cUnboundMethod, "inspect", method_inspect, 0); rb_define_method(rb_cUnboundMethod, "to_s", method_inspect, 0); rb_define_method(rb_cUnboundMethod, "name", method_name, 0); rb_define_method(rb_cUnboundMethod, "owner", method_owner, 0); rb_define_method(rb_cUnboundMethod, "bind", umethod_bind, 1); /* Module#*_method */ rb_define_method(rb_cModule, "instance_method", rb_mod_instance_method, 1); rb_define_method(rb_cModule, "public_instance_method", rb_mod_public_instance_method, 1); rb_define_private_method(rb_cModule, "define_method", rb_mod_define_method, -1); /* Kernel */ rb_define_method(rb_mKernel, "define_singleton_method", rb_obj_define_method, -1); } /* * Objects of class Binding encapsulate the execution * context at some particular place in the code and retain this context * for future use. The variables, methods, value of self, * and possibly an iterator block that can be accessed in this context * are all retained. Binding objects can be created using * Kernel#binding, and are made available to the callback * of Kernel#set_trace_func. * * These binding objects can be passed as the second argument of the * Kernel#eval method, establishing an environment for the * evaluation. * * class Demo * def initialize(n) * @secret = n * end * def getBinding * return binding() * end * end * * k1 = Demo.new(99) * b1 = k1.getBinding * k2 = Demo.new(-3) * b2 = k2.getBinding * * eval("@secret", b1) #=> 99 * eval("@secret", b2) #=> -3 * eval("@secret") #=> nil * * Binding objects have no class-specific methods. * */ void Init_Binding(void) { rb_cBinding = rb_define_class("Binding", rb_cObject); rb_undef_alloc_func(rb_cBinding); rb_undef_method(CLASS_OF(rb_cBinding), "new"); rb_define_method(rb_cBinding, "clone", binding_clone, 0); rb_define_method(rb_cBinding, "dup", binding_dup, 0); rb_define_method(rb_cBinding, "eval", bind_eval, -1); rb_define_global_function("binding", rb_f_binding, 0); }