platform/shared/ruby/proc.c in rhodes-5.5.18 vs platform/shared/ruby/proc.c in rhodes-6.0.11

- old
+ new

@@ -1,81 +1,91 @@ /********************************************************************** proc.c - Proc, Binding, Env - $Author: yugui $ + $Author$ created at: Wed Jan 17 12:13:14 2007 Copyright (C) 2004-2007 Koichi Sasada **********************************************************************/ #include "eval_intern.h" +#include "internal.h" #include "gc.h" +#include "iseq.h" +/* Proc.new with no block will raise an exception in the future + * versions */ +#define PROC_NEW_REQUIRES_BLOCK 0 + +const rb_cref_t *rb_vm_cref_in_context(VALUE self, VALUE cbase); + struct METHOD { - VALUE recv; - VALUE rclass; - ID id; - rb_method_entry_t me; + const VALUE recv; + const VALUE klass; + const rb_method_entry_t * const me; + /* for bound methods, `me' should be rb_callable_method_entry_t * */ }; VALUE rb_cUnboundMethod; VALUE rb_cMethod; VALUE rb_cBinding; VALUE rb_cProc; -VALUE rb_iseq_parameters(const rb_iseq_t *iseq, int is_proc); - -static VALUE bmcall(VALUE, VALUE); +static VALUE bmcall(VALUE, VALUE, int, VALUE *, VALUE); static int method_arity(VALUE); -static int rb_obj_is_method(VALUE m); -rb_iseq_t *rb_method_get_iseq(VALUE method); +static int method_min_max_arity(VALUE, int *max); +#define attached id__attached__ + /* Proc */ -#define IS_METHOD_PROC_NODE(node) (nd_type(node) == NODE_IFUNC && (node)->nd_cfnc == bmcall) +#define IS_METHOD_PROC_IFUNC(ifunc) ((ifunc)->func == bmcall) +#define IS_METHOD_PROC_ISEQ(iseq) \ + (RUBY_VM_IFUNC_P(iseq) && \ + IS_METHOD_PROC_IFUNC((struct vm_ifunc *)(iseq))) 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(proc->block.proc); - RUBY_MARK_UNLESS_NULL(proc->block.self); - if (proc->block.iseq && RUBY_VM_IFUNC_P(proc->block.iseq)) { - RUBY_MARK_UNLESS_NULL((VALUE)(proc->block.iseq)); - } + rb_proc_t *proc = ptr; + RUBY_MARK_UNLESS_NULL(proc->block.proc); + RUBY_MARK_UNLESS_NULL(proc->block.self); + if (proc->block.ep) { + RUBY_MARK_UNLESS_NULL(rb_vm_proc_envval(proc)); } + if (proc->block.iseq && RUBY_VM_IFUNC_P(proc->block.iseq)) { + rb_gc_mark((VALUE)(proc->block.iseq)); + } RUBY_MARK_LEAVE("proc"); } +typedef struct { + rb_proc_t basic; + VALUE env[3]; /* me, specval, envval */ +} cfunc_proc_t; + +#define IS_SYM_PROC(proc) (proc->block.ep == ((const cfunc_proc_t *)proc)->env+1) + static size_t proc_memsize(const void *ptr) { - return ptr ? sizeof(rb_proc_t) : 0; + const rb_proc_t *proc = ptr; + if (IS_SYM_PROC(proc)) + return sizeof(cfunc_proc_t); + return sizeof(rb_proc_t); } static const rb_data_type_t proc_data_type = { "proc", - proc_mark, - proc_free, - proc_memsize, + { + proc_mark, + RUBY_TYPED_DEFAULT_FREE, + proc_memsize, + }, + 0, 0, RUBY_TYPED_FREE_IMMEDIATELY }; VALUE rb_proc_alloc(VALUE klass) { @@ -96,21 +106,22 @@ /* :nodoc: */ static VALUE proc_dup(VALUE self) { - VALUE procval = rb_proc_alloc(rb_cProc); - rb_proc_t *src, *dst; + VALUE procval; + rb_proc_t *src; + rb_proc_t *dst; + GetProcPtr(self, src); + if (IS_SYM_PROC(src)) + return self; + procval = rb_proc_alloc(rb_cProc); GetProcPtr(procval, dst); - - dst->block = src->block; + *dst = *src; dst->block.proc = procval; - dst->blockprocval = src->blockprocval; - dst->envval = src->envval; - dst->safe_level = src->safe_level; - dst->is_lambda = src->is_lambda; + RB_GC_GUARD(self); /* for: body = proc_dup(body) */ return procval; } /* :nodoc: */ @@ -124,59 +135,59 @@ /* * 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. + * Returns +true+ for a Proc object for which argument handling is rigid. + * Such procs are typically generated by +lambda+. * - * A Proc object generated by proc ignore extra arguments. + * A Proc object generated by +proc+ ignores extra arguments. * * proc {|a,b| [a,b] }.call(1,2,3) #=> [1,2] * - * It provides nil for lacked arguments. + * It provides +nil+ for missing arguments. * * proc {|a,b| [a,b] }.call(1) #=> [1,nil] * - * It expand single-array argument. + * It expands a single array argument. * * proc {|a,b| [a,b] }.call([1,2]) #=> [1,2] * - * A Proc object generated by lambda doesn't have such tricks. + * 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. + * It returns +true+ if no tricks apply. * * lambda {}.lambda? #=> true * proc {}.lambda? #=> false * - * Proc.new is same as proc. + * Proc.new is the same as +proc+. * * Proc.new {}.lambda? #=> false * - * lambda, proc and Proc.new preserves the tricks of - * a Proc object given by & argument. + * +lambda+, +proc+ and Proc.new preserve the tricks of + * a Proc object given by <code>&</code> 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 + * A Proc object generated by <code>&</code> 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. + * The <code>&</code> argument preserves the tricks if a Proc object + * is given by <code>&</code> argument. * * n(&lambda {}) #=> true * n(&proc {}) #=> false * n(&Proc.new {}) #=> false * @@ -186,43 +197,42 @@ * method(:m).to_proc.lambda? #=> true * * n(&method(:m)) #=> true * n(&method(:m).to_proc) #=> true * - * define_method is treated same as method definition. + * +define_method+ is treated the same as method definition. * The defined method has no tricks. * * class C * define_method(:d) {} * end - * C.new.e(1,2) #=> ArgumentError + * C.new.d(1,2) #=> ArgumentError * C.new.method(:d).to_proc.lambda? #=> true * - * define_method always defines a method without the tricks, + * +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. + * This is the only exception for 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. + * This exception insures that methods never have tricks + * and makes it easy to have wrappers to define methods that behave as usual. * - * class << C - * def def2(name, &body) + * class C + * def self.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. + * The wrapper <i>def2</i> defines a method which has no tricks. * */ VALUE rb_proc_lambda_p(VALUE procval) @@ -240,61 +250,64 @@ { rb_binding_t *bind; RUBY_FREE_ENTER("binding"); if (ptr) { bind = ptr; - ruby_xfree(ptr); + ruby_xfree(bind); } RUBY_FREE_LEAVE("binding"); } static void binding_mark(void *ptr) { - rb_binding_t *bind; + rb_binding_t *bind = ptr; + RUBY_MARK_ENTER("binding"); - if (ptr) { - bind = ptr; - RUBY_MARK_UNLESS_NULL(bind->env); - RUBY_MARK_UNLESS_NULL(bind->filename); - } + + RUBY_MARK_UNLESS_NULL(bind->env); + RUBY_MARK_UNLESS_NULL(bind->path); + RUBY_MARK_LEAVE("binding"); } static size_t binding_memsize(const void *ptr) { - return ptr ? sizeof(rb_binding_t) : 0; + return sizeof(rb_binding_t); } -static const rb_data_type_t binding_data_type = { +const rb_data_type_t ruby_binding_data_type = { "binding", - binding_mark, - binding_free, - binding_memsize, + { + binding_mark, + binding_free, + binding_memsize, + }, + 0, 0, RUBY_TYPED_FREE_IMMEDIATELY }; -static VALUE -binding_alloc(VALUE klass) +VALUE +rb_binding_alloc(VALUE klass) { VALUE obj; rb_binding_t *bind; - obj = TypedData_Make_Struct(klass, rb_binding_t, &binding_data_type, bind); + obj = TypedData_Make_Struct(klass, rb_binding_t, &ruby_binding_data_type, bind); return obj; } /* :nodoc: */ static VALUE binding_dup(VALUE self) { - VALUE bindval = binding_alloc(rb_cBinding); + VALUE bindval = rb_binding_alloc(rb_cBinding); rb_binding_t *src, *dst; GetBindingPtr(self, src); GetBindingPtr(bindval, dst); dst->env = src->env; - dst->filename = src->filename; - dst->line_no = src->line_no; + dst->path = src->path; + dst->first_lineno = src->first_lineno; return bindval; } /* :nodoc: */ static VALUE @@ -307,38 +320,26 @@ VALUE rb_binding_new(void) { rb_thread_t *th = GET_THREAD(); - rb_control_frame_t *cfp = rb_vm_get_ruby_level_next_cfp(th, th->cfp); - VALUE bindval = binding_alloc(rb_cBinding); - rb_binding_t *bind; - - if (cfp == 0) { - rb_raise(rb_eRuntimeError, "Can't create Binding Object on top of Fiber."); - } - - GetBindingPtr(bindval, bind); - bind->env = rb_vm_make_env_object(th, cfp); - bind->filename = cfp->iseq->filename; - bind->line_no = rb_vm_get_sourceline(cfp); - return bindval; + return rb_vm_make_binding(th, th->cfp); } /* * 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+. + * environment. See also the description of class +Binding+. * - * def getBinding(param) - * return binding + * def get_binding(param) + * binding * end - * b = getBinding("hello") + * b = get_binding("hello") * eval("param", b) #=> "hello" */ static VALUE rb_f_binding(VALUE self) @@ -353,14 +354,14 @@ * Evaluates the Ruby expression(s) in <em>string</em>, in the * <em>binding</em>'s context. If the optional <em>filename</em> and * <em>lineno</em> parameters are present, they will be used when * reporting syntax errors. * - * def getBinding(param) - * return binding + * def get_binding(param) + * binding * end - * b = getBinding("hello") + * b = get_binding("hello") * b.eval("param") #=> "hello" */ static VALUE bind_eval(int argc, VALUE *argv, VALUE bindval) @@ -370,59 +371,300 @@ 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 * +get_local_variable_ptr(VALUE envval, ID lid) +{ + rb_env_t *env; + + do { + const rb_iseq_t *iseq; + unsigned int i; + + GetEnvPtr(envval, env); + iseq = env->block.iseq; + + if (RUBY_VM_NORMAL_ISEQ_P(iseq)) { + for (i=0; i<iseq->body->local_table_size; i++) { + if (iseq->body->local_table[i] == lid) { + return &env->env[i]; + } + } + } + else { + return NULL; + } + } while ((envval = rb_vm_env_prev_envval(env)) != Qfalse); + + return NULL; +} + +/* + * check local variable name. + * returns ID if it's an already interned symbol, or 0 with setting + * local name in String to *namep. + */ +static ID +check_local_id(VALUE bindval, volatile VALUE *pname) +{ + ID lid = rb_check_id(pname); + VALUE name = *pname; + + if (lid) { + if (!rb_is_local_id(lid)) { + rb_name_err_raise("wrong local variable name `%1$s' for %2$s", + bindval, ID2SYM(lid)); + } + } + else { + if (!rb_is_local_name(name)) { + rb_name_err_raise("wrong local variable name `%1$s' for %2$s", + bindval, name); + } + return 0; + } + return lid; +} + +/* + * call-seq: + * binding.local_variables -> Array + * + * Returns the names of the binding's local variables as symbols. + * + * def foo + * a = 1 + * 2.times do |n| + * binding.local_variables #=> [:a, :n] + * end + * end + * + * This method is the short version of the following code: + * + * binding.eval("local_variables") + * + */ static VALUE -proc_new(VALUE klass, int is_lambda) +bind_local_variables(VALUE bindval) { + const rb_binding_t *bind; + const rb_env_t *env; + + GetBindingPtr(bindval, bind); + GetEnvPtr(bind->env, env); + + return rb_vm_env_local_variables(env); +} + +/* + * call-seq: + * binding.local_variable_get(symbol) -> obj + * + * Returns the value of the local variable +symbol+. + * + * def foo + * a = 1 + * binding.local_variable_get(:a) #=> 1 + * binding.local_variable_get(:b) #=> NameError + * end + * + * This method is the short version of the following code: + * + * binding.eval("#{symbol}") + * + */ +static VALUE +bind_local_variable_get(VALUE bindval, VALUE sym) +{ + ID lid = check_local_id(bindval, &sym); + const rb_binding_t *bind; + const VALUE *ptr; + + if (!lid) goto undefined; + + GetBindingPtr(bindval, bind); + + if ((ptr = get_local_variable_ptr(bind->env, lid)) == NULL) { + sym = ID2SYM(lid); + undefined: + rb_name_err_raise("local variable `%1$s' not defined for %2$s", + bindval, sym); + } + + return *ptr; +} + +/* + * call-seq: + * binding.local_variable_set(symbol, obj) -> obj + * + * Set local variable named +symbol+ as +obj+. + * + * def foo + * a = 1 + * bind = binding + * bind.local_variable_set(:a, 2) # set existing local variable `a' + * bind.local_variable_set(:b, 3) # create new local variable `b' + * # `b' exists only in binding + * + * p bind.local_variable_get(:a) #=> 2 + * p bind.local_variable_get(:b) #=> 3 + * p a #=> 2 + * p b #=> NameError + * end + * + * This method behaves similarly to the following code: + * + * binding.eval("#{symbol} = #{obj}") + * + * if +obj+ can be dumped in Ruby code. + */ +static VALUE +bind_local_variable_set(VALUE bindval, VALUE sym, VALUE val) +{ + ID lid = check_local_id(bindval, &sym); + rb_binding_t *bind; + VALUE *ptr; + + if (!lid) lid = rb_intern_str(sym); + + GetBindingPtr(bindval, bind); + if ((ptr = get_local_variable_ptr(bind->env, lid)) == NULL) { + /* not found. create new env */ + ptr = rb_binding_add_dynavars(bind, 1, &lid); + } + + *ptr = val; + + return val; +} + +/* + * call-seq: + * binding.local_variable_defined?(symbol) -> obj + * + * Returns +true+ if a local variable +symbol+ exists. + * + * def foo + * a = 1 + * binding.local_variable_defined?(:a) #=> true + * binding.local_variable_defined?(:b) #=> false + * end + * + * This method is the short version of the following code: + * + * binding.eval("defined?(#{symbol}) == 'local-variable'") + * + */ +static VALUE +bind_local_variable_defined_p(VALUE bindval, VALUE sym) +{ + ID lid = check_local_id(bindval, &sym); + const rb_binding_t *bind; + + if (!lid) return Qfalse; + + GetBindingPtr(bindval, bind); + return get_local_variable_ptr(bind->env, lid) ? Qtrue : Qfalse; +} + +/* + * call-seq: + * binding.receiver -> object + * + * Returns the bound receiver of the binding object. + */ +static VALUE +bind_receiver(VALUE bindval) +{ + const rb_binding_t *bind; + const rb_env_t *env; + + GetBindingPtr(bindval, bind); + GetEnvPtr(bind->env, env); + return env->block.self; +} + +static VALUE +cfunc_proc_new(VALUE klass, VALUE ifunc, int8_t is_lambda) +{ + rb_proc_t *proc; + cfunc_proc_t *sproc; + VALUE procval = TypedData_Make_Struct(klass, cfunc_proc_t, &proc_data_type, sproc); + sproc->env[1] = VM_ENVVAL_BLOCK_PTR(0); + proc = &sproc->basic; + proc->block.ep = sproc->env+1; + proc->block.iseq = (rb_iseq_t *)ifunc; + proc->block.proc = procval; + proc->is_lambda = is_lambda; + return procval; +} + +static VALUE +sym_proc_new(VALUE klass, VALUE sym) +{ + return cfunc_proc_new(klass, sym, 0); +} + +VALUE +rb_func_proc_new(rb_block_call_func_t func, VALUE val) +{ + return cfunc_proc_new(rb_cProc, (VALUE)IFUNC_NEW(func, val, 0), 0); +} + +VALUE +rb_func_lambda_new(rb_block_call_func_t func, VALUE val) +{ + return cfunc_proc_new(rb_cProc, (VALUE)IFUNC_NEW(func, val, 0), 1); +} + +static const char proc_without_block[] = "tried to create Proc object without a block"; + +static VALUE +proc_new(VALUE klass, int8_t 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) { - - block = GC_GUARDED_PTR_REF(cfp->lfp[0]); - } - else { + if (!(block = rb_vm_control_frame_block_ptr(cfp))) { +#if !PROC_NEW_REQUIRES_BLOCK cfp = RUBY_VM_PREVIOUS_CONTROL_FRAME(cfp); - if ((GC_GUARDED_PTR_REF(cfp->lfp[0])) != 0) { - - block = GC_GUARDED_PTR_REF(cfp->lfp[0]); - + if ((block = rb_vm_control_frame_block_ptr(cfp)) != 0) { if (is_lambda) { - rb_warn("tried to create Proc object without a block"); + rb_warn(proc_without_block); } } +#else + if (0) +#endif else { - rb_raise(rb_eArgError, - "tried to create Proc object without a block"); + rb_raise(rb_eArgError, proc_without_block); } } procval = block->proc; if (procval) { - if (RBASIC(procval)->klass == klass) { + if (SYMBOL_P(procval)) { + return (klass != rb_cProc) ? sym_proc_new(klass, procval) : rb_sym_to_proc(procval); + } + else if (RBASIC_CLASS(procval) == klass) { return procval; } else { VALUE newprocval = proc_dup(procval); - RBASIC(newprocval)->klass = klass; + RBASIC_SET_CLASS(newprocval, klass); return newprocval; } } - procval = rb_vm_make_proc(th, block, klass); - - if (is_lambda) { - rb_proc_t *proc; - GetProcPtr(procval, proc); - proc->is_lambda = TRUE; - } + procval = rb_vm_make_proc_lambda(th, block, klass, is_lambda); return procval; } /* * call-seq: @@ -461,103 +703,101 @@ rb_block_proc(void) { return proc_new(rb_cProc, FALSE); } -VALUE -rb_block_lambda(void) -{ - return proc_new(rb_cProc, TRUE); -} - -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 <code>Proc.new</code>, except the resulting Proc objects * check the number of parameters passed when called. */ -static VALUE -proc_lambda(void) +VALUE +rb_block_lambda(void) { - return rb_block_lambda(); + return proc_new(rb_cProc, TRUE); } +/* Document-method: === + * + * call-seq: + * proc === obj -> result_of_proc + * + * Invokes the block with +obj+ as the proc's parameter like Proc#call. It + * is to allow a proc object to be a target of +when+ clause in a case + * statement. + */ + /* CHECKME: are the argument checking semantics correct? */ /* + * Document-method: [] + * Document-method: call + * Document-method: yield + * * call-seq: * prc.call(params,...) -> obj * prc[params,...] -> obj * prc.(params,...) -> obj + * prc.yield(params,...) -> obj * * Invokes the block, setting the block's parameters to the values in * <i>params</i> 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). Note that prc.() invokes prc.call() with the parameters - * given. It's a syntax sugar to hide "call". + * Returns the value of the last expression evaluated in the block. * - * For procs created using <code>Kernel.proc</code>, generates an - * error if the wrong number of parameters - * are passed to a proc with multiple parameters. For procs created using - * <code>Proc.new</code>, extra parameters are silently discarded. + * a_proc = Proc.new {|scalar, *values| values.map {|value| value*scalar } } + * a_proc.call(9, 1, 2, 3) #=> [9, 18, 27] + * a_proc[9, 1, 2, 3] #=> [9, 18, 27] + * a_proc.(9, 1, 2, 3) #=> [9, 18, 27] + * a_proc.yield(9, 1, 2, 3) #=> [9, 18, 27] * - * Returns the value of the last expression evaluated in the block. See - * also <code>Proc#yield</code>. + * Note that <code>prc.()</code> invokes <code>prc.call()</code> with + * the parameters given. It's syntactic sugar to hide "call". * - * 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) + * For procs created using <code>lambda</code> or <code>->()</code> an error + * is generated if the wrong number of parameters are passed to the proc. + * For procs created using <code>Proc.new</code> or <code>Kernel.proc</code>, + * extra parameters are silently discarded and missing parameters are + * set to +nil+. * - * <em>produces:</em> + * a_proc = proc {|a,b| [a,b] } + * a_proc.call(1) #=> [1, nil] * - * prog.rb:5: wrong number of arguments (3 for 2) (ArgumentError) - * from prog.rb:4:in `call' - * from prog.rb:5 - */ - -/* - * call-seq: - * prc === obj -> result_of_proc + * a_proc = lambda {|a,b| [a,b] } + * a_proc.call(1) # ArgumentError: wrong number of arguments (given 1, expected 2) * - * Invokes the block, with <i>obj</i> as the block's parameter. It is - * to allow a proc object to be a target of +when+ clause in the case statement. + * See also Proc#lambda?. */ - +#if 0 static VALUE proc_call(int argc, VALUE *argv, VALUE procval) { + VALUE vret; + const rb_block_t *blockptr = 0; + const rb_iseq_t *iseq; rb_proc_t *proc; - rb_block_t *blockptr = 0; - rb_iseq_t *iseq; VALUE passed_procval; GetProcPtr(procval, proc); iseq = proc->block.iseq; - if (BUILTIN_TYPE(iseq) == T_NODE || iseq->arg_block != -1) { + if (RUBY_VM_IFUNC_P(iseq) || iseq->body->param.flags.has_block) { if (rb_block_given_p()) { rb_proc_t *passed_proc; - RB_GC_GUARD(passed_procval) = rb_block_proc(); + passed_procval = rb_block_proc(); GetProcPtr(passed_procval, passed_proc); blockptr = &passed_proc->block; } } - return rb_vm_invoke_proc(GET_THREAD(), proc, proc->block.self, - argc, argv, blockptr); + vret = rb_vm_invoke_proc(GET_THREAD(), proc, argc, argv, blockptr); + RB_GC_GUARD(procval); + RB_GC_GUARD(passed_procval); + return vret; } +#endif #if SIZEOF_LONG > SIZEOF_INT static inline int check_argc(long argc) { @@ -569,123 +809,219 @@ } #else #define check_argc(argc) (argc) #endif +static rb_block_t * +passed_block(VALUE pass_procval) +{ + if (!NIL_P(pass_procval)) { + rb_proc_t *pass_proc; + if (SYMBOL_P(pass_procval)) { + pass_procval = sym_proc_new(rb_cProc, pass_procval); + } + GetProcPtr(pass_procval, pass_proc); + return &pass_proc->block; + } + return 0; +} + VALUE rb_proc_call(VALUE self, VALUE args) { + VALUE vret; rb_proc_t *proc; GetProcPtr(self, proc); - return rb_vm_invoke_proc(GET_THREAD(), proc, proc->block.self, - check_argc(RARRAY_LEN(args)), RARRAY_PTR(args), 0); + vret = rb_vm_invoke_proc(GET_THREAD(), proc, check_argc(RARRAY_LEN(args)), RARRAY_CONST_PTR(args), 0); + RB_GC_GUARD(self); + RB_GC_GUARD(args); + return vret; } VALUE -rb_proc_call_with_block(VALUE self, int argc, VALUE *argv, VALUE pass_procval) +rb_proc_call_with_block(VALUE self, int argc, const VALUE *argv, VALUE pass_procval) { + VALUE vret; rb_proc_t *proc; rb_block_t *block = 0; GetProcPtr(self, proc); - if (!NIL_P(pass_procval)) { - rb_proc_t *pass_proc; - GetProcPtr(pass_procval, pass_proc); - block = &pass_proc->block; - } - - return rb_vm_invoke_proc(GET_THREAD(), proc, proc->block.self, - argc, argv, block); + block = passed_block(pass_procval); + vret = rb_vm_invoke_proc(GET_THREAD(), proc, argc, argv, block); + RB_GC_GUARD(self); + RB_GC_GUARD(pass_procval); + return vret; } + /* * call-seq: * prc.arity -> fixnum * - * Returns the number of arguments that would not be ignored. If the block + * Returns the number of mandatory arguments. 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 <code>proc</code> with no argument declarations - * is the same a block declaring <code>||</code> as its arguments. + * to take exactly n arguments, returns n. + * If the block has optional arguments, returns -n-1, where n is the + * number of mandatory arguments, with the exception for blocks that + * are not lambdas and have only a finite number of optional arguments; + * in this latter case, returns n. + * Keywords arguments will considered as a single additional argument, + * that argument being mandatory if any keyword argument is mandatory. + * A <code>proc</code> with no argument declarations + * is the same as a block declaring <code>||</code> 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 + * proc {}.arity #=> 0 + * proc { || }.arity #=> 0 + * proc { |a| }.arity #=> 1 + * proc { |a, b| }.arity #=> 2 + * proc { |a, b, c| }.arity #=> 3 + * proc { |*a| }.arity #=> -1 + * proc { |a, *b| }.arity #=> -2 + * proc { |a, *b, c| }.arity #=> -3 + * proc { |x:, y:, z:0| }.arity #=> 1 + * proc { |*a, x:, y:0| }.arity #=> -2 + * + * proc { |x=0| }.arity #=> 0 + * lambda { |x=0| }.arity #=> -1 + * proc { |x=0, y| }.arity #=> 1 + * lambda { |x=0, y| }.arity #=> -2 + * proc { |x=0, y=0| }.arity #=> 0 + * lambda { |x=0, y=0| }.arity #=> -1 + * proc { |x, y=0| }.arity #=> 1 + * lambda { |x, y=0| }.arity #=> -2 + * proc { |(x, y), z=0| }.arity #=> 1 + * lambda { |(x, y), z=0| }.arity #=> -2 + * proc { |a, x:0, y:0| }.arity #=> 1 + * lambda { |a, x:0, y:0| }.arity #=> -2 */ static VALUE proc_arity(VALUE self) { int arity = rb_proc_arity(self); return INT2FIX(arity); } +static inline int +rb_iseq_min_max_arity(const rb_iseq_t *iseq, int *max) +{ + *max = iseq->body->param.flags.has_rest == FALSE ? + iseq->body->param.lead_num + iseq->body->param.opt_num + iseq->body->param.post_num + + (iseq->body->param.flags.has_kw == TRUE || iseq->body->param.flags.has_kwrest == TRUE) + : UNLIMITED_ARGUMENTS; + return iseq->body->param.lead_num + iseq->body->param.post_num + (iseq->body->param.flags.has_kw && iseq->body->param.keyword->required_num > 0); +} + +static int +rb_block_min_max_arity(rb_block_t *block, int *max) +{ + const rb_iseq_t *iseq = block->iseq; + + if (iseq) { + if (RUBY_VM_NORMAL_ISEQ_P(iseq)) { + return rb_iseq_min_max_arity(iseq, max); + } + else { + if (IS_METHOD_PROC_ISEQ(iseq)) { + const struct vm_ifunc *ifunc = (struct vm_ifunc *)iseq; + /* e.g. method(:foo).to_proc.arity */ + return method_min_max_arity((VALUE)ifunc->data, max); + } + } + } + *max = UNLIMITED_ARGUMENTS; + return 0; +} + +/* + * Returns the number of required parameters and stores the maximum + * number of parameters in max, or UNLIMITED_ARGUMENTS if no max. + * For non-lambda procs, the maximum is the number of non-ignored + * parameters even though there is no actual limit to the number of parameters + */ +static int +rb_proc_min_max_arity(VALUE self, int *max) +{ + rb_proc_t *proc; + rb_block_t *block; + GetProcPtr(self, proc); + block = &proc->block; + return rb_block_min_max_arity(block, max); +} + int rb_proc_arity(VALUE self) { rb_proc_t *proc; - rb_iseq_t *iseq; + int max, min = rb_proc_min_max_arity(self, &max); GetProcPtr(self, proc); - iseq = proc->block.iseq; - if (iseq) { - if (BUILTIN_TYPE(iseq) != T_NODE) { - if (iseq->arg_rest < 0) { - return iseq->argc; - } - else { - return -(iseq->argc + 1 + iseq->arg_post_len); - } + return (proc->is_lambda ? min == max : max != UNLIMITED_ARGUMENTS) ? min : -min-1; +} + +int +rb_block_arity(void) +{ + int min, max; + rb_thread_t *th = GET_THREAD(); + rb_control_frame_t *cfp = th->cfp; + rb_block_t *block = rb_vm_control_frame_block_ptr(cfp); + VALUE proc_value; + + if (!block) rb_raise(rb_eArgError, "no block given"); + min = rb_block_min_max_arity(block, &max); + proc_value = block->proc; + if (proc_value) { + if (SYMBOL_P(proc_value)) { + return -1; } else { - NODE *node = (NODE *)iseq; - if (IS_METHOD_PROC_NODE(node)) { - /* method(:foo).to_proc.arity */ - return method_arity(node->nd_tval); - } + rb_proc_t *proc; + GetProcPtr(proc_value, proc); + if (proc) + return (proc->is_lambda ? min == max : max != UNLIMITED_ARGUMENTS) ? min : -min-1; } } - return -1; + return max != UNLIMITED_ARGUMENTS ? min : -min-1; } -#define get_proc_iseq rb_proc_get_iseq - -rb_iseq_t * +const rb_iseq_t * rb_proc_get_iseq(VALUE self, int *is_proc) { - rb_proc_t *proc; - rb_iseq_t *iseq; + const rb_proc_t *proc; + const rb_iseq_t *iseq; GetProcPtr(self, proc); iseq = proc->block.iseq; if (is_proc) *is_proc = !proc->is_lambda; - if (!RUBY_VM_NORMAL_ISEQ_P(iseq)) { - NODE *node = (NODE *)iseq; + if (RUBY_VM_IFUNC_P(iseq)) { + const struct vm_ifunc *ifunc = (struct vm_ifunc *)iseq; iseq = 0; - if (IS_METHOD_PROC_NODE(node)) { + if (IS_METHOD_PROC_IFUNC(ifunc)) { /* method(:foo).to_proc */ - iseq = rb_method_get_iseq(node->nd_tval); + iseq = rb_method_iseq((VALUE)ifunc->data); if (is_proc) *is_proc = 0; } + return iseq; } - return iseq; + else if (SYMBOL_P(iseq)) { + return NULL; + } + else { + return rb_iseq_check(iseq); + } } static VALUE -iseq_location(rb_iseq_t *iseq) +iseq_location(const rb_iseq_t *iseq) { VALUE loc[2]; if (!iseq) return Qnil; - loc[0] = iseq->filename; - if (iseq->insn_info_table) { - loc[1] = INT2FIX(rb_iseq_first_lineno(iseq)); + rb_iseq_check(iseq); + loc[0] = iseq->body->location.path; + if (iseq->body->line_info_table) { + loc[1] = rb_iseq_first_lineno(iseq); } else { loc[1] = Qnil; } return rb_ary_new4(2, loc); @@ -693,18 +1029,18 @@ /* * call-seq: * prc.source_location -> [String, Fixnum] * - * returns the ruby source filename and line number containing this proc - * or nil if this proc was not defined in ruby (i.e. native) + * Returns the Ruby source filename and line number containing this proc + * or +nil+ if this proc was not defined in Ruby (i.e. native). */ VALUE rb_proc_location(VALUE self) { - return iseq_location(get_proc_iseq(self, 0)); + return iseq_location(rb_proc_get_iseq(self, 0)); } static VALUE unnamed_parameters(int arity) { @@ -724,111 +1060,122 @@ return param; } /* * call-seq: - * proc.parameters -> array + * prc.parameters -> array * - * returns the parameter information of this proc. + * Returns the parameter information of this proc. * - * prc = lambda{|x, y=42, *rest|} - * prc.parameters #=> [[:req, :x], [:opt, :y], [:rest, :rest]] + * prc = lambda{|x, y=42, *other|} + * prc.parameters #=> [[:req, :x], [:opt, :y], [:rest, :other]] */ static VALUE rb_proc_parameters(VALUE self) { int is_proc; - rb_iseq_t *iseq = get_proc_iseq(self, &is_proc); + const rb_iseq_t *iseq = rb_proc_get_iseq(self, &is_proc); if (!iseq) { return unnamed_parameters(rb_proc_arity(self)); } return rb_iseq_parameters(iseq, is_proc); } -/* - * call-seq: - * prc == other_proc -> true or false - * - * Return <code>true</code> if <i>prc</i> is the same object as - * <i>other_proc</i>, or if they are both procs with the same body. - */ +st_index_t +rb_hash_proc(st_index_t hash, VALUE prc) +{ + rb_proc_t *proc; + GetProcPtr(prc, proc); + hash = rb_hash_uint(hash, (st_index_t)proc->block.iseq); + return rb_hash_uint(hash, (st_index_t)proc->block.ep >> 16); +} -static VALUE -proc_eq(VALUE self, VALUE other) +VALUE +rb_sym_to_proc(VALUE sym) { - if (self == other) { - return Qtrue; + static VALUE sym_proc_cache = Qfalse; + enum {SYM_PROC_CACHE_SIZE = 67}; + VALUE proc; + long index; + ID id; + VALUE *aryp; + + if (!sym_proc_cache) { + sym_proc_cache = rb_ary_tmp_new(SYM_PROC_CACHE_SIZE * 2); + rb_gc_register_mark_object(sym_proc_cache); + rb_ary_store(sym_proc_cache, SYM_PROC_CACHE_SIZE*2 - 1, Qnil); } + + id = SYM2ID(sym); + index = (id % SYM_PROC_CACHE_SIZE) << 1; + + aryp = RARRAY_PTR(sym_proc_cache); + if (aryp[index] == sym) { + return aryp[index + 1]; + } else { - if (rb_obj_is_proc(other)) { - rb_proc_t *p1, *p2; - GetProcPtr(self, p1); - GetProcPtr(other, p2); - if (p1->envval == p2->envval && - p1->block.iseq->iseq_size == p2->block.iseq->iseq_size && - p1->block.iseq->local_size == p2->block.iseq->local_size && - MEMCMP(p1->block.iseq->iseq, p2->block.iseq->iseq, VALUE, - p1->block.iseq->iseq_size) == 0) { - return Qtrue; - } - } + proc = sym_proc_new(rb_cProc, ID2SYM(id)); + aryp[index] = sym; + aryp[index + 1] = proc; + return proc; } - return Qfalse; } /* * call-seq: * prc.hash -> integer * - * Return hash value corresponding to proc body. + * Returns a hash value corresponding to proc body. + * + * See also Object#hash. */ static VALUE proc_hash(VALUE self) { st_index_t hash; - rb_proc_t *proc; - GetProcPtr(self, proc); - hash = rb_hash_start((st_index_t)proc->block.iseq); - hash = rb_hash_uint(hash, (st_index_t)proc->envval); - hash = rb_hash_uint(hash, (st_index_t)proc->block.lfp >> 16); + hash = rb_hash_start(0); + hash = rb_hash_proc(hash, self); hash = rb_hash_end(hash); return LONG2FIX(hash); } /* * call-seq: * prc.to_s -> string * - * Shows the unique identifier for this proc, along with + * Returns 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; const char *cname = rb_obj_classname(self); - rb_iseq_t *iseq; + const 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 (RUBY_VM_NORMAL_ISEQ_P(iseq) && rb_iseq_check(iseq)) { + int first_lineno = 0; - if (iseq->insn_info_table) { - line_no = rb_iseq_first_lineno(iseq); + if (iseq->body->line_info_table) { + first_lineno = FIX2INT(rb_iseq_first_lineno(iseq)); } - str = rb_sprintf("#<%s:%p@%s:%d%s>", cname, (void *)self, - RSTRING_PTR(iseq->filename), - line_no, is_lambda); + str = rb_sprintf("#<%s:%p@%"PRIsVALUE":%d%s>", cname, (void *)self, + iseq->body->location.path, first_lineno, is_lambda); } + else if (SYMBOL_P(iseq)) { + str = rb_sprintf("#<%s:%p(&%+"PRIsVALUE")%s>", cname, (void *)self, + (VALUE)iseq, is_lambda); + } else { str = rb_sprintf("#<%s:%p%s>", cname, (void *)proc->block.iseq, is_lambda); } @@ -838,11 +1185,11 @@ return str; } /* * call-seq: - * prc.to_proc -> prc + * prc.to_proc -> proc * * Part of the protocol for converting objects to <code>Proc</code> * objects. Instances of class <code>Proc</code> simply return * themselves. */ @@ -855,131 +1202,158 @@ static void bm_mark(void *ptr) { struct METHOD *data = ptr; - rb_gc_mark(data->rclass); rb_gc_mark(data->recv); - rb_mark_method_entry(&data->me); + rb_gc_mark(data->klass); + rb_gc_mark((VALUE)data->me); } -static void -bm_free(void *ptr) -{ - struct METHOD *data = ptr; - rb_method_definition_t *def = data->me.def; - if (def->alias_count == 0) - xfree(def); - else if (def->alias_count > 0) - def->alias_count--; - xfree(ptr); -} - static size_t bm_memsize(const void *ptr) { - return ptr ? sizeof(struct METHOD) : 0; + return sizeof(struct METHOD); } static const rb_data_type_t method_data_type = { "method", - bm_mark, - bm_free, - bm_memsize, + { + bm_mark, + RUBY_TYPED_DEFAULT_FREE, + bm_memsize, + }, + 0, 0, RUBY_TYPED_FREE_IMMEDIATELY }; -static inline int +VALUE rb_obj_is_method(VALUE m) { - return rb_typeddata_is_kind_of(m, &method_data_type); + if (rb_typeddata_is_kind_of(m, &method_data_type)) { + return Qtrue; + } + else { + return Qfalse; + } } +static int +respond_to_missing_p(VALUE klass, VALUE obj, VALUE sym, int scope) +{ + /* TODO: merge with obj_respond_to() */ + ID rmiss = idRespond_to_missing; + + if (obj == Qundef) return 0; + if (rb_method_basic_definition_p(klass, rmiss)) return 0; + return RTEST(rb_funcall(obj, rmiss, 2, sym, scope ? Qfalse : Qtrue)); +} + + static VALUE -mnew(VALUE klass, VALUE obj, ID id, VALUE mclass, int scope) +mnew_missing(VALUE klass, VALUE obj, ID id, ID rid, VALUE mclass) { - VALUE method; - VALUE rclass = klass; - ID rid = id; struct METHOD *data; - rb_method_entry_t *me, meb; - rb_method_definition_t *def = 0; - rb_method_flag_t flag = NOEX_UNDEF; + VALUE method = TypedData_Make_Struct(mclass, struct METHOD, &method_data_type, data); + rb_method_entry_t *me; + rb_method_definition_t *def; - again: - me = rb_method_entry(klass, id); - if (UNDEFINED_METHOD_ENTRY_P(me)) { - ID rmiss = rb_intern("respond_to_missing?"); - VALUE sym = ID2SYM(id); + RB_OBJ_WRITE(method, &data->recv, obj); + RB_OBJ_WRITE(method, &data->klass, klass); - if (obj != Qundef && !rb_method_basic_definition_p(klass, rmiss)) { - if (RTEST(rb_funcall(obj, rmiss, 2, sym, scope ? Qfalse : Qtrue))) { - def = ALLOC(rb_method_definition_t); - def->type = VM_METHOD_TYPE_MISSING; - def->original_id = id; - def->alias_count = 0; + def = ZALLOC(rb_method_definition_t); + def->type = VM_METHOD_TYPE_MISSING; + def->original_id = id; - meb.flag = 0; - meb.mark = 0; - meb.called_id = id; - meb.klass = klass; - meb.def = def; - me = &meb; - def = 0; + me = rb_method_entry_create(id, klass, METHOD_VISI_UNDEF, def); - goto gen_method; - } + RB_OBJ_WRITE(method, &data->me, me); + + OBJ_INFECT(method, klass); + + return method; +} + +static VALUE +mnew_internal(const rb_method_entry_t *me, VALUE klass, + VALUE obj, ID id, VALUE mclass, int scope, int error) +{ + struct METHOD *data; + VALUE method; + ID rid = id; + rb_method_visibility_t visi = METHOD_VISI_UNDEF; + + again: + if (UNDEFINED_METHOD_ENTRY_P(me)) { + if (respond_to_missing_p(klass, obj, ID2SYM(id), scope)) { + return mnew_missing(klass, obj, id, rid, mclass); } + if (!error) return Qnil; rb_print_undef(klass, id, 0); } - def = me->def; - if (flag == NOEX_UNDEF) { - flag = me->flag; - if (scope && (flag & NOEX_MASK) != NOEX_PUBLIC) { - const char *v = ""; - switch (flag & NOEX_MASK) { - case NOEX_PRIVATE: v = "private"; break; - case NOEX_PROTECTED: v = "protected"; break; - } - rb_name_error(id, "method `%s' for %s `%s' is %s", - rb_id2name(id), - (TYPE(klass) == T_MODULE) ? "module" : "class", - rb_class2name(klass), - v); + if (visi == METHOD_VISI_UNDEF) { + visi = METHOD_ENTRY_VISI(me); + if (scope && (visi != METHOD_VISI_PUBLIC)) { + if (!error) return Qnil; + rb_print_inaccessible(klass, id, visi); } } - if (def && def->type == VM_METHOD_TYPE_ZSUPER) { - klass = RCLASS_SUPER(me->klass); - id = def->original_id; + if (me->def->type == VM_METHOD_TYPE_ZSUPER) { + if (me->defined_class) { + VALUE klass = RCLASS_SUPER(RCLASS_ORIGIN(me->defined_class)); + id = me->def->original_id; + me = (rb_method_entry_t *)rb_callable_method_entry_without_refinements(klass, id); + } + else { + VALUE klass = RCLASS_SUPER(me->owner); + id = me->def->original_id; + me = rb_method_entry_without_refinements(klass, id); + } goto again; } - klass = me->klass; - - while (rclass != klass && - (FL_TEST(rclass, FL_SINGLETON) || TYPE(rclass) == T_ICLASS)) { - rclass = RCLASS_SUPER(rclass); + while (klass != me->owner && (FL_TEST(klass, FL_SINGLETON) || RB_TYPE_P(klass, T_ICLASS))) { + klass = RCLASS_SUPER(klass); } - if (TYPE(klass) == T_ICLASS) { - klass = RBASIC(klass)->klass; - } - - gen_method: method = TypedData_Make_Struct(mclass, struct METHOD, &method_data_type, data); - data->recv = obj; - data->rclass = rclass; - data->id = rid; - data->me = *me; - if (def) def->alias_count++; + RB_OBJ_WRITE(method, &data->recv, obj); + RB_OBJ_WRITE(method, &data->klass, klass); + RB_OBJ_WRITE(method, &data->me, me); OBJ_INFECT(method, klass); - return method; } +static VALUE +mnew_from_me(const rb_method_entry_t *me, VALUE klass, + VALUE obj, ID id, VALUE mclass, int scope) +{ + return mnew_internal(me, klass, obj, id, mclass, scope, TRUE); +} +static VALUE +mnew(VALUE klass, VALUE obj, ID id, VALUE mclass, int scope) +{ + const rb_method_entry_t *me; + + if (obj == Qundef) { /* UnboundMethod */ + me = rb_method_entry_without_refinements(klass, id); + } + else { + me = (rb_method_entry_t *)rb_callable_method_entry_without_refinements(klass, id); + } + return mnew_from_me(me, klass, obj, id, mclass, scope); +} + +static inline VALUE +method_entry_defined_class(const rb_method_entry_t *me) +{ + VALUE defined_class = me->defined_class; + return defined_class ? defined_class : me->owner; +} + /********************************************************************** * * Document-class : Method * * Method objects are created by <code>Object#method</code>, and are @@ -1002,33 +1376,39 @@ * */ /* * call-seq: + * meth.eql?(other_meth) -> true or false * meth == other_meth -> true or false * * Two method objects are equal if they are bound to the same - * object and refer to the same method definition. + * object and refer to the same method definition and their owners are the + * same class or module. */ static VALUE method_eq(VALUE method, VALUE other) { struct METHOD *m1, *m2; - extern int rb_method_entry_eq(rb_method_entry_t *m1, rb_method_entry_t *m2); + VALUE klass1, klass2; if (!rb_obj_is_method(other)) return Qfalse; if (CLASS_OF(method) != CLASS_OF(other)) return Qfalse; Check_TypedStruct(method, &method_data_type); m1 = (struct METHOD *)DATA_PTR(method); m2 = (struct METHOD *)DATA_PTR(other); - if (!rb_method_entry_eq(&m1->me, &m2->me) || - m1->rclass != m2->rclass || + klass1 = method_entry_defined_class(m1->me); + klass2 = method_entry_defined_class(m2->me); + + if (!rb_method_entry_eq(m1->me, m2->me) || + klass1 != klass2 || + m1->klass != m2->klass || m1->recv != m2->recv) { return Qfalse; } return Qtrue; @@ -1036,33 +1416,34 @@ /* * call-seq: * meth.hash -> integer * - * Return a hash value corresponding to the method object. + * Returns a hash value corresponding to the method object. + * + * See also Object#hash. */ static VALUE method_hash(VALUE method) { struct METHOD *m; st_index_t hash; TypedData_Get_Struct(method, struct METHOD, &method_data_type, m); - hash = rb_hash_start((st_index_t)m->rclass); - hash = rb_hash_uint(hash, (st_index_t)m->recv); - hash = rb_hash_uint(hash, (st_index_t)m->me.def); + hash = rb_hash_start((st_index_t)m->recv); + hash = rb_hash_method_entry(hash, m->me); hash = rb_hash_end(hash); return INT2FIX(hash); } /* * call-seq: * meth.unbind -> unbound_method * - * Dissociates <i>meth</i> from it's current receiver. The resulting + * Dissociates <i>meth</i> from its current receiver. The resulting * <code>UnboundMethod</code> can subsequently be bound to a new object * of the same class (see <code>UnboundMethod</code>). */ static VALUE @@ -1072,15 +1453,13 @@ struct METHOD *orig, *data; TypedData_Get_Struct(obj, struct METHOD, &method_data_type, orig); method = TypedData_Make_Struct(rb_cUnboundMethod, struct METHOD, &method_data_type, data); - data->recv = Qundef; - data->id = orig->id; - data->me = orig->me; - if (orig->me.def) orig->me.def->alias_count++; - data->rclass = orig->rclass; + RB_OBJ_WRITE(method, &data->recv, Qundef); + RB_OBJ_WRITE(method, &data->klass, orig->klass); + RB_OBJ_WRITE(method, &data->me, rb_method_entry_clone(orig->me)); OBJ_INFECT(method, obj); return method; } @@ -1111,29 +1490,90 @@ method_name(VALUE obj) { struct METHOD *data; TypedData_Get_Struct(obj, struct METHOD, &method_data_type, data); - return ID2SYM(data->id); + return ID2SYM(data->me->called_id); } /* * call-seq: + * meth.original_name -> symbol + * + * Returns the original name of the method. + */ + +static VALUE +method_original_name(VALUE obj) +{ + struct METHOD *data; + + TypedData_Get_Struct(obj, struct METHOD, &method_data_type, data); + return ID2SYM(data->me->def->original_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; - TypedData_Get_Struct(obj, struct METHOD, &method_data_type, data); - return data->me.klass; + return data->me->owner; } +void +rb_method_name_error(VALUE klass, VALUE str) +{ +#define MSG(s) rb_fstring_cstr("undefined method `%1$s' for"s" `%2$s'") + VALUE c = klass; + VALUE s; + + if (FL_TEST(c, FL_SINGLETON)) { + VALUE obj = rb_ivar_get(klass, attached); + + switch (BUILTIN_TYPE(obj)) { + case T_MODULE: + case T_CLASS: + c = obj; + s = MSG(""); + } + goto normal_class; + } + else if (RB_TYPE_P(c, T_MODULE)) { + s = MSG(" module"); + } + else { + normal_class: + s = MSG(" class"); + } + rb_name_err_raise_str(s, c, str); +#undef MSG +} + +static VALUE +obj_method(VALUE obj, VALUE vid, int scope) +{ + ID id = rb_check_id(&vid); + const VALUE klass = CLASS_OF(obj); + const VALUE mclass = rb_cMethod; + + if (!id) { + if (respond_to_missing_p(klass, obj, vid, scope)) { + id = rb_intern_str(vid); + return mnew_missing(klass, obj, id, id, mclass); + } + rb_method_name_error(klass, vid); + } + return mnew(klass, obj, id, mclass, scope); +} + /* * call-seq: * obj.method(sym) -> method * * Looks up the named method as a receiver in <i>obj</i>, returning a @@ -1161,11 +1601,11 @@ */ VALUE rb_obj_method(VALUE obj, VALUE vid) { - return mnew(CLASS_OF(obj), obj, rb_to_id(vid), rb_cMethod, FALSE); + return obj_method(obj, vid, FALSE); } /* * call-seq: * obj.public_method(sym) -> method @@ -1174,15 +1614,65 @@ */ VALUE rb_obj_public_method(VALUE obj, VALUE vid) { - return mnew(CLASS_OF(obj), obj, rb_to_id(vid), rb_cMethod, TRUE); + return obj_method(obj, vid, TRUE); } /* * call-seq: + * obj.singleton_method(sym) -> method + * + * Similar to _method_, searches singleton method only. + * + * class Demo + * def initialize(n) + * @iv = n + * end + * def hello() + * "Hello, @iv = #{@iv}" + * end + * end + * + * k = Demo.new(99) + * def k.hi + * "Hi, @iv = #{@iv}" + * end + * m = k.singleton_method(:hi) + * m.call #=> "Hi, @iv = 99" + * m = k.singleton_method(:hello) #=> NameError + */ + +VALUE +rb_obj_singleton_method(VALUE obj, VALUE vid) +{ + const rb_method_entry_t *me; + VALUE klass; + ID id = rb_check_id(&vid); + + if (!id) { + if (!NIL_P(klass = rb_singleton_class_get(obj)) && + respond_to_missing_p(klass, obj, vid, FALSE)) { + id = rb_intern_str(vid); + return mnew_missing(klass, obj, id, id, rb_cMethod); + } + undef: + rb_name_err_raise("undefined singleton method `%1$s' for `%2$s'", + obj, vid); + } + if (NIL_P(klass = rb_singleton_class_get(obj)) || + UNDEFINED_METHOD_ENTRY_P(me = rb_method_entry_at(klass, id)) || + UNDEFINED_REFINED_METHOD_P(me->def)) { + vid = ID2SYM(id); + goto undef; + } + return mnew_from_me(me, klass, obj, id, rb_cMethod, FALSE); +} + +/* + * call-seq: * mod.instance_method(symbol) -> unbound_method * * Returns an +UnboundMethod+ representing the given * instance method in _mod_. * @@ -1211,11 +1701,15 @@ */ static VALUE rb_mod_instance_method(VALUE mod, VALUE vid) { - return mnew(mod, Qundef, rb_to_id(vid), rb_cUnboundMethod, FALSE); + ID id = rb_check_id(&vid); + if (!id) { + rb_method_name_error(mod, vid); + } + return mnew(mod, Qundef, id, rb_cUnboundMethod, FALSE); } /* * call-seq: * mod.public_instance_method(symbol) -> unbound_method @@ -1224,17 +1718,21 @@ */ static VALUE rb_mod_public_instance_method(VALUE mod, VALUE vid) { - return mnew(mod, Qundef, rb_to_id(vid), rb_cUnboundMethod, TRUE); + ID id = rb_check_id(&vid); + if (!id) { + rb_method_name_error(mod, vid); + } + return mnew(mod, Qundef, id, rb_cUnboundMethod, TRUE); } /* * call-seq: - * define_method(symbol, method) -> new_method - * define_method(symbol) { block } -> proc + * define_method(symbol, method) -> symbol + * define_method(symbol) { block } -> symbol * * Defines an instance method in the receiver. The _method_ * parameter can be a +Proc+, a +Method+ or an +UnboundMethod+ object. * If a block is specified, it is used as the method body. This block * is evaluated using <code>instance_eval</code>, a point that is @@ -1269,63 +1767,93 @@ static VALUE rb_mod_define_method(int argc, VALUE *argv, VALUE mod) { ID id; VALUE body; - int noex = NOEX_PUBLIC; + VALUE name; + const rb_cref_t *cref = rb_vm_cref_in_context(mod, mod); + const rb_scope_visibility_t default_scope_visi = {METHOD_VISI_PUBLIC, FALSE}; + const rb_scope_visibility_t *scope_visi = &default_scope_visi; + int is_method = FALSE; + if (cref) { + scope_visi = CREF_SCOPE_VISI(cref); + } + + rb_check_arity(argc, 1, 2); + name = argv[0]; + id = rb_check_id(&name); if (argc == 1) { - id = rb_to_id(argv[0]); +#if PROC_NEW_REQUIRES_BLOCK body = rb_block_lambda(); +#else + rb_thread_t *th = GET_THREAD(); + rb_block_t *block = rb_vm_control_frame_block_ptr(th->cfp); + if (!block) rb_raise(rb_eArgError, proc_without_block); + + body = block->proc; + + if (SYMBOL_P(body)) { + body = rb_sym_to_proc(body); + } + else if (!body) { + body = rb_vm_make_proc_lambda(th, block, rb_cProc, TRUE); + } +#endif } - else if (argc == 2) { - id = rb_to_id(argv[0]); + else { body = argv[1]; - if (!rb_obj_is_method(body) && !rb_obj_is_proc(body)) { + + if (rb_obj_is_method(body)) { + is_method = TRUE; + } + else if (rb_obj_is_proc(body)) { + is_method = FALSE; + } + else { 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 (!id) id = rb_to_id(name); - if (rb_obj_is_method(body)) { + if (is_method) { struct METHOD *method = (struct METHOD *)DATA_PTR(body); - VALUE rclass = method->rclass; - if (rclass != mod && !RTEST(rb_class_inherited_p(mod, rclass))) { - if (FL_TEST(rclass, FL_SINGLETON)) { + if (method->me->owner != mod && !RB_TYPE_P(method->me->owner, T_MODULE) && + !RTEST(rb_class_inherited_p(mod, method->me->owner))) { + if (FL_TEST(method->me->owner, FL_SINGLETON)) { rb_raise(rb_eTypeError, "can't bind singleton method to a different class"); } else { rb_raise(rb_eTypeError, - "bind argument must be a subclass of %s", - rb_class2name(rclass)); + "bind argument must be a subclass of % "PRIsVALUE, + rb_class_name(method->me->owner)); } } - rb_method_entry_set(mod, id, &method->me, noex); + rb_method_entry_set(mod, id, method->me, scope_visi->method_visi); + if (scope_visi->module_func) { + rb_method_entry_set(rb_singleton_class(mod), id, method->me, METHOD_VISI_PUBLIC); + } + RB_GC_GUARD(body); } - else if (rb_obj_is_proc(body)) { + else { 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; + if (RUBY_VM_NORMAL_ISEQ_P(proc->block.iseq)) { proc->is_lambda = TRUE; proc->is_from_method = TRUE; } - rb_add_method(mod, id, VM_METHOD_TYPE_BMETHOD, (void *)body, noex); + rb_add_method(mod, id, VM_METHOD_TYPE_BMETHOD, (void *)body, scope_visi->method_visi); + if (scope_visi->module_func) { + rb_add_method(rb_singleton_class(mod), id, VM_METHOD_TYPE_BMETHOD, (void *)body, METHOD_VISI_PUBLIC); + } } - else { - /* type error */ - rb_raise(rb_eTypeError, "wrong argument type (expected Proc/Method)"); - } - return body; + return ID2SYM(id); } /* * call-seq: * define_singleton_method(symbol, method) -> new_method @@ -1358,13 +1886,48 @@ VALUE klass = rb_singleton_class(obj); return rb_mod_define_method(argc, argv, klass); } +/* + * define_method(symbol, method) -> new_method + * define_method(symbol) { block } -> proc + * + * Defines a global function by _method_ or the block. + */ +static VALUE +top_define_method(int argc, VALUE *argv, VALUE obj) +{ + rb_thread_t *th = GET_THREAD(); + VALUE klass; + + klass = th->top_wrapper; + if (klass) { + rb_warning("main.define_method in the wrapped load is effective only in wrapper module"); + } + else { + klass = rb_cObject; + } + return rb_mod_define_method(argc, argv, klass); +} + /* - * MISSING: documentation + * call-seq: + * method.clone -> new_method + * + * Returns a clone of this method. + * + * class A + * def foo + * return "bar" + * end + * end + * + * m = A.new.method(:foo) + * m.call # => "bar" + * n = m.clone.call # => "bar" */ static VALUE method_clone(VALUE self) { @@ -1372,13 +1935,13 @@ struct METHOD *orig, *data; TypedData_Get_Struct(self, struct METHOD, &method_data_type, orig); clone = TypedData_Make_Struct(CLASS_OF(self), struct METHOD, &method_data_type, data); CLONESETUP(clone, self); - *data = *orig; - if (data->me.def) data->me.def->alias_count++; - + RB_OBJ_WRITE(clone, &data->recv, orig->recv); + RB_OBJ_WRITE(clone, &data->klass, orig->klass); + RB_OBJ_WRITE(clone, &data->me, rb_method_entry_clone(orig->me)); return clone; } /* * call-seq: @@ -1392,12 +1955,26 @@ * m.call(3) #=> 15 * m.call(20) #=> 32 */ VALUE -rb_method_call(int argc, VALUE *argv, VALUE method) +rb_method_call(int argc, const VALUE *argv, VALUE method) { + VALUE proc = rb_block_given_p() ? rb_block_proc() : Qnil; + return rb_method_call_with_block(argc, argv, method, proc); +} + +static const rb_callable_method_entry_t * +method_callable_method_entry(struct METHOD *data) +{ + if (data->me->defined_class == 0) rb_bug("method_callable_method_entry: not callable."); + return (const rb_callable_method_entry_t *)data->me; +} + +VALUE +rb_method_call_with_block(int argc, const VALUE *argv, VALUE method, VALUE pass_procval) +{ VALUE result = Qnil; /* OK */ struct METHOD *data; int state; volatile int safe = -1; @@ -1405,22 +1982,22 @@ if (data->recv == Qundef) { rb_raise(rb_eTypeError, "can't call unbound method; bind first"); } PUSH_TAG(); if (OBJ_TAINTED(method)) { + const int safe_level_to_run = RUBY_SAFE_LEVEL_MAX; safe = rb_safe_level(); - if (rb_safe_level() < 4) { - rb_set_safe_level_force(4); + if (safe < safe_level_to_run) { + rb_set_safe_level_force(safe_level_to_run); } } if ((state = EXEC_TAG()) == 0) { rb_thread_t *th = GET_THREAD(); - VALUE rb_vm_call(rb_thread_t *th, VALUE recv, VALUE id, int argc, const VALUE *argv, - const rb_method_entry_t *me); - PASS_PASSED_BLOCK_TH(th); - result = rb_vm_call(th, data->recv, data->id, argc, argv, &data->me); + th->passed_block = passed_block(pass_procval); + VAR_INITIALIZED(data); + result = rb_vm_call(th, data->recv, data->me->called_id, argc, argv, method_callable_method_entry(data)); } POP_TAG(); if (safe >= 0) rb_set_safe_level_force(safe); if (state) @@ -1443,11 +2020,11 @@ * calling <code>Module#instance_method</code> or by calling * <code>unbind</code> on a bound method object. The result of both of * these is an <code>UnboundMethod</code> 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 + * object. That object must be a kind_of? the method's original * class. * * class Square * def area * @side * @side @@ -1521,77 +2098,116 @@ static VALUE umethod_bind(VALUE method, VALUE recv) { struct METHOD *data, *bound; + VALUE methclass, klass; TypedData_Get_Struct(method, struct METHOD, &method_data_type, data); - if (data->rclass != CLASS_OF(recv) && !rb_obj_is_kind_of(recv, data->rclass)) { - if (FL_TEST(data->rclass, FL_SINGLETON)) { + methclass = data->me->owner; + + if (!RB_TYPE_P(methclass, T_MODULE) && + methclass != CLASS_OF(recv) && !rb_obj_is_kind_of(recv, methclass)) { + if (FL_TEST(methclass, FL_SINGLETON)) { rb_raise(rb_eTypeError, "singleton method called for a different object"); } else { - rb_raise(rb_eTypeError, "bind argument must be an instance of %s", - rb_class2name(data->rclass)); + rb_raise(rb_eTypeError, "bind argument must be an instance of % "PRIsVALUE, + rb_class_name(methclass)); } } + klass = CLASS_OF(recv); + method = TypedData_Make_Struct(rb_cMethod, struct METHOD, &method_data_type, bound); - *bound = *data; - if (bound->me.def) bound->me.def->alias_count++; - bound->recv = recv; - bound->rclass = CLASS_OF(recv); + RB_OBJ_WRITE(method, &bound->recv, recv); + RB_OBJ_WRITE(method, &bound->klass, data->klass); + RB_OBJ_WRITE(method, &bound->me, rb_method_entry_clone(data->me)); + if (RB_TYPE_P(bound->me->owner, T_MODULE)) { + VALUE ic = rb_class_search_ancestor(klass, bound->me->owner); + if (ic) { + klass = ic; + } + else { + klass = rb_include_class_new(methclass, klass); + } + RB_OBJ_WRITE(method, &bound->me, rb_method_entry_complement_defined_class(bound->me, bound->me->called_id, klass)); + } + return method; } -int -rb_method_entry_arity(const rb_method_entry_t *me) +/* + * Returns the number of required parameters and stores the maximum + * number of parameters in max, or UNLIMITED_ARGUMENTS + * if there is no maximum. + */ +static int +rb_method_entry_min_max_arity(const rb_method_entry_t *me, int *max) { const rb_method_definition_t *def = me->def; - if (!def) return 0; + + if (!def) return *max = 0; switch (def->type) { case VM_METHOD_TYPE_CFUNC: - if (def->body.cfunc.argc < 0) - return -1; - return check_argc(def->body.cfunc.argc); + if (def->body.cfunc.argc < 0) { + *max = UNLIMITED_ARGUMENTS; + return 0; + } + return *max = check_argc(def->body.cfunc.argc); case VM_METHOD_TYPE_ZSUPER: - return -1; + *max = UNLIMITED_ARGUMENTS; + return 0; case VM_METHOD_TYPE_ATTRSET: - return 1; + return *max = 1; case VM_METHOD_TYPE_IVAR: - return 0; + return *max = 0; + case VM_METHOD_TYPE_ALIAS: + return rb_method_entry_min_max_arity(def->body.alias.original_me, max); case VM_METHOD_TYPE_BMETHOD: - return rb_proc_arity(def->body.proc); + return rb_proc_min_max_arity(def->body.proc, max); case VM_METHOD_TYPE_ISEQ: { - rb_iseq_t *iseq = def->body.iseq; - if (iseq->arg_rest == -1 && iseq->arg_opts == 0) { - return iseq->argc; - } - else { - return -(iseq->argc + 1 + iseq->arg_post_len); - } + const rb_iseq_t *iseq = rb_iseq_check(def->body.iseq.iseqptr); + return rb_iseq_min_max_arity(iseq, max); } case VM_METHOD_TYPE_UNDEF: case VM_METHOD_TYPE_NOTIMPLEMENTED: - return 0; + return *max = 0; case VM_METHOD_TYPE_MISSING: - return -1; + *max = UNLIMITED_ARGUMENTS; + return 0; case VM_METHOD_TYPE_OPTIMIZED: { switch (def->body.optimize_type) { case OPTIMIZED_METHOD_TYPE_SEND: - return -1; + *max = UNLIMITED_ARGUMENTS; + return 0; + case OPTIMIZED_METHOD_TYPE_CALL: + *max = UNLIMITED_ARGUMENTS; + return 0; default: break; } + break; } + case VM_METHOD_TYPE_REFINED: + *max = UNLIMITED_ARGUMENTS; + return 0; } - rb_bug("rb_method_entry_arity: invalid method entry type (%d)", def->type); + rb_bug("rb_method_entry_min_max_arity: invalid method entry type (%d)", def->type); + UNREACHABLE; } +int +rb_method_entry_arity(const rb_method_entry_t *me) +{ + int max, min = rb_method_entry_min_max_arity(me, &max); + return min == max ? min : -min-1; +} + /* * call-seq: * meth.arity -> fixnum * * Returns an indication of the number of arguments accepted by a @@ -1634,85 +2250,162 @@ method_arity(VALUE method) { struct METHOD *data; TypedData_Get_Struct(method, struct METHOD, &method_data_type, data); - return rb_method_entry_arity(&data->me); + return rb_method_entry_arity(data->me); } +static const rb_method_entry_t * +original_method_entry(VALUE mod, ID id) +{ + const rb_method_entry_t *me; + + while ((me = rb_method_entry(mod, id)) != 0) { + const rb_method_definition_t *def = me->def; + if (def->type != VM_METHOD_TYPE_ZSUPER) break; + mod = RCLASS_SUPER(me->owner); + id = def->original_id; + } + return me; +} + +static int +method_min_max_arity(VALUE method, int *max) +{ + const struct METHOD *data; + + TypedData_Get_Struct(method, struct METHOD, &method_data_type, data); + return rb_method_entry_min_max_arity(data->me, max); +} + int rb_mod_method_arity(VALUE mod, ID id) { - rb_method_entry_t *me = rb_method_entry(mod, id); + const rb_method_entry_t *me = original_method_entry(mod, id); + if (!me) return 0; /* should raise? */ return rb_method_entry_arity(me); } int rb_obj_method_arity(VALUE obj, ID id) { return rb_mod_method_arity(CLASS_OF(obj), id); } -static inline rb_method_definition_t * -method_get_def(VALUE method) +static inline const rb_method_definition_t * +method_def(VALUE method) { - struct METHOD *data; + const struct METHOD *data; TypedData_Get_Struct(method, struct METHOD, &method_data_type, data); - return data->me.def; + return data->me->def; } -static rb_iseq_t * -method_get_iseq(rb_method_definition_t *def) +static const rb_iseq_t * +method_def_iseq(const rb_method_definition_t *def) { switch (def->type) { + case VM_METHOD_TYPE_ISEQ: + return rb_iseq_check(def->body.iseq.iseqptr); case VM_METHOD_TYPE_BMETHOD: - return get_proc_iseq(def->body.proc, 0); + return rb_proc_get_iseq(def->body.proc, 0); + case VM_METHOD_TYPE_ALIAS: + return method_def_iseq(def->body.alias.original_me->def); + case VM_METHOD_TYPE_CFUNC: + case VM_METHOD_TYPE_ATTRSET: + case VM_METHOD_TYPE_IVAR: + case VM_METHOD_TYPE_ZSUPER: + case VM_METHOD_TYPE_UNDEF: + case VM_METHOD_TYPE_NOTIMPLEMENTED: + case VM_METHOD_TYPE_OPTIMIZED: + case VM_METHOD_TYPE_MISSING: + case VM_METHOD_TYPE_REFINED: + break; + } + return NULL; +} + +const rb_iseq_t * +rb_method_iseq(VALUE method) +{ + return method_def_iseq(method_def(method)); +} + +static const rb_cref_t * +method_cref(VALUE method) +{ + const rb_method_definition_t *def = method_def(method); + + again: + switch (def->type) { case VM_METHOD_TYPE_ISEQ: - return def->body.iseq; + return def->body.iseq.cref; + case VM_METHOD_TYPE_ALIAS: + def = def->body.alias.original_me->def; + goto again; default: - return 0; + return NULL; } } -rb_iseq_t * -rb_method_get_iseq(VALUE method) +static VALUE +method_def_location(const rb_method_definition_t *def) { - return method_get_iseq(method_get_def(method)); + if (def->type == VM_METHOD_TYPE_ATTRSET || def->type == VM_METHOD_TYPE_IVAR) { + if (!def->body.attr.location) + return Qnil; + return rb_ary_dup(def->body.attr.location); + } + return iseq_location(method_def_iseq(def)); } +VALUE +rb_method_entry_location(const rb_method_entry_t *me) +{ + if (!me) return Qnil; + return method_def_location(me->def); +} + +VALUE +rb_mod_method_location(VALUE mod, ID id) +{ + const rb_method_entry_t *me = original_method_entry(mod, id); + return rb_method_entry_location(me); +} + +VALUE +rb_obj_method_location(VALUE obj, ID id) +{ + return rb_mod_method_location(CLASS_OF(obj), id); +} + /* * call-seq: * meth.source_location -> [String, Fixnum] * - * returns the ruby source filename and line number containing this method - * or nil if this method was not defined in ruby (i.e. native) + * Returns the Ruby source filename and line number containing this method + * or nil if this method was not defined in Ruby (i.e. native). */ VALUE rb_method_location(VALUE method) { - rb_method_definition_t *def = method_get_def(method); - if (def->type == VM_METHOD_TYPE_ATTRSET || def->type == VM_METHOD_TYPE_IVAR) { - if (!def->body.attr.location) - return Qnil; - return rb_ary_dup(def->body.attr.location); - } - return iseq_location(method_get_iseq(def)); + return method_def_location(method_def(method)); } /* * call-seq: * meth.parameters -> array * - * returns the parameter information of this method + * Returns the parameter information of this method. */ static VALUE rb_method_parameters(VALUE method) { - rb_iseq_t *iseq = rb_method_get_iseq(method); + const rb_iseq_t *iseq = rb_method_iseq(method); if (!iseq) { return unnamed_parameters(method_arity(method)); } return rb_iseq_parameters(iseq, 0); } @@ -1720,11 +2413,11 @@ /* * call-seq: * meth.to_s -> string * meth.inspect -> string * - * Show the name of the underlying method. + * Returns the name of the underlying method. * * "cat".method(:count).inspect #=> "#<Method: String#count>" */ static VALUE @@ -1732,22 +2425,37 @@ { struct METHOD *data; VALUE str; const char *s; const char *sharp = "#"; + VALUE mklass; + VALUE defined_class; TypedData_Get_Struct(method, struct METHOD, &method_data_type, 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->me.klass, FL_SINGLETON)) { - VALUE v = rb_iv_get(data->me.klass, "__attached__"); + mklass = data->klass; + if (data->me->def->type == VM_METHOD_TYPE_ALIAS) { + defined_class = data->me->def->body.alias.original_me->owner; + } + else { + defined_class = method_entry_defined_class(data->me); + } + + if (RB_TYPE_P(defined_class, T_ICLASS)) { + defined_class = RBASIC_CLASS(defined_class); + } + + if (FL_TEST(mklass, FL_SINGLETON)) { + VALUE v = rb_ivar_get(mklass, attached); + if (data->recv == Qundef) { - rb_str_buf_append(str, rb_inspect(data->me.klass)); + rb_str_buf_append(str, rb_inspect(mklass)); } else if (data->recv == v) { rb_str_buf_append(str, rb_inspect(v)); sharp = "."; } @@ -1758,54 +2466,57 @@ rb_str_buf_cat2(str, ")"); sharp = "."; } } else { - rb_str_buf_cat2(str, rb_class2name(data->rclass)); - if (data->rclass != data->me.klass) { + rb_str_buf_append(str, rb_class_name(mklass)); + if (defined_class != mklass) { rb_str_buf_cat2(str, "("); - rb_str_buf_cat2(str, rb_class2name(data->me.klass)); + rb_str_buf_append(str, rb_class_name(defined_class)); rb_str_buf_cat2(str, ")"); } } rb_str_buf_cat2(str, sharp); - rb_str_append(str, rb_id2str(data->me.def->original_id)); - if (data->me.def->type == VM_METHOD_TYPE_NOTIMPLEMENTED) { + rb_str_append(str, rb_id2str(data->me->called_id)); + if (data->me->called_id != data->me->def->original_id) { + rb_str_catf(str, "(%"PRIsVALUE")", + rb_id2str(data->me->def->original_id)); + } + if (data->me->def->type == VM_METHOD_TYPE_NOTIMPLEMENTED) { rb_str_buf_cat2(str, " (not-implemented)"); } rb_str_buf_cat2(str, ">"); return str; } static VALUE mproc(VALUE method) { - return rb_funcall(Qnil, rb_intern("proc"), 0); + return rb_funcallv(rb_mRubyVMFrozenCore, idProc, 0, 0); } static VALUE mlambda(VALUE method) { - return rb_funcall(Qnil, rb_intern("lambda"), 0); + return rb_funcallv(rb_mRubyVMFrozenCore, idLambda, 0, 0); } static VALUE -bmcall(VALUE args, VALUE method) +bmcall(VALUE args, VALUE method, int argc, VALUE *argv, VALUE passed_proc) { volatile VALUE a; VALUE ret; - int argc; if (CLASS_OF(args) != rb_cArray) { args = rb_ary_new3(1, args); argc = 1; } else { argc = check_argc(RARRAY_LEN(args)); } - ret = rb_method_call(argc, RARRAY_PTR(args), method); + ret = rb_method_call_with_block(argc, RARRAY_PTR(args), method, passed_proc); RB_GC_GUARD(a) = args; return ret; } VALUE @@ -1817,24 +2528,25 @@ return procval; } /* * call-seq: - * meth.to_proc -> prc + * meth.to_proc -> proc * * Returns a <code>Proc</code> object corresponding to this method. */ static VALUE -method_proc(VALUE method) +method_to_proc(VALUE method) { VALUE procval; rb_proc_t *proc; + /* * class Method * def to_proc - * proc{|*args| + * lambda{|*args| * self.call(*args) * } * end * end */ @@ -1843,11 +2555,34 @@ proc->is_from_method = 1; return procval; } /* - * call_seq: + * call-seq: + * meth.super_method -> method + * + * Returns a Method of superclass which would be called when super is used + * or nil if there is no method on superclass. + */ + +static VALUE +method_super_method(VALUE method) +{ + const struct METHOD *data; + VALUE super_class; + const rb_method_entry_t *me; + + TypedData_Get_Struct(method, struct METHOD, &method_data_type, data); + super_class = RCLASS_SUPER(method_entry_defined_class(data->me)); + if (!super_class) return Qnil; + me = (rb_method_entry_t *)rb_callable_method_entry_without_refinements(super_class, data->me->called_id); + if (!me) return Qnil; + return mnew_internal(me, super_class, data->recv, data->me->called_id, rb_obj_class(method), FALSE, FALSE); +} + +/* + * call-seq: * local_jump_error.exit_value -> obj * * Returns the exit value associated with this +LocalJumpError+. */ static VALUE @@ -1868,10 +2603,33 @@ localjump_reason(VALUE exc) { return rb_iv_get(exc, "@reason"); } +rb_cref_t *rb_vm_cref_new_toplevel(void); /* vm.c */ + +static VALUE +env_clone(VALUE envval, VALUE receiver, const rb_cref_t *cref) +{ + VALUE newenvval = TypedData_Wrap_Struct(RBASIC_CLASS(envval), RTYPEDDATA_TYPE(envval), 0); + rb_env_t *env, *newenv; + int envsize; + + if (cref == NULL) { + cref = rb_vm_cref_new_toplevel(); + } + + GetEnvPtr(envval, env); + envsize = sizeof(rb_env_t) + (env->env_size - 1) * sizeof(VALUE); + newenv = xmalloc(envsize); + memcpy(newenv, env, envsize); + RTYPEDDATA_DATA(newenvval) = newenv; + newenv->block.self = receiver; + newenv->block.ep[-1] = (VALUE)cref; + return newenvval; +} + /* * call-seq: * prc.binding -> binding * * Returns the binding associated with <i>prc</i>. Note that @@ -1886,32 +2644,46 @@ * eval("param", b.binding) #=> 99 */ static VALUE proc_binding(VALUE self) { - rb_proc_t *proc; - VALUE bindval; + VALUE bindval, envval; + const rb_proc_t *proc; + const rb_iseq_t *iseq; rb_binding_t *bind; GetProcPtr(self, proc); - if (TYPE(proc->block.iseq) == T_NODE) { - if (!IS_METHOD_PROC_NODE((NODE *)proc->block.iseq)) { + envval = rb_vm_proc_envval(proc); + iseq = proc->block.iseq; + if (SYMBOL_P(iseq)) goto error; + if (RUBY_VM_IFUNC_P(iseq)) { + struct vm_ifunc *ifunc = (struct vm_ifunc *)iseq; + if (IS_METHOD_PROC_IFUNC(ifunc)) { + VALUE method = (VALUE)ifunc->data; + envval = env_clone(envval, method_receiver(method), method_cref(method)); + iseq = rb_method_iseq(method); + } + else { + error: rb_raise(rb_eArgError, "Can't create Binding from C level Proc"); } } - bindval = binding_alloc(rb_cBinding); + bindval = rb_binding_alloc(rb_cBinding); GetBindingPtr(bindval, bind); - bind->env = proc->envval; - if (RUBY_VM_NORMAL_ISEQ_P(proc->block.iseq)) { - bind->filename = proc->block.iseq->filename; - bind->line_no = rb_iseq_first_lineno(proc->block.iseq); + bind->env = envval; + + if (iseq) { + rb_iseq_check(iseq); + bind->path = iseq->body->location.path; + bind->first_lineno = FIX2INT(rb_iseq_first_lineno(iseq)); } else { - bind->filename = Qnil; - bind->line_no = 0; + bind->path = Qnil; + bind->first_lineno = 0; } + return bindval; } static VALUE curry(VALUE dummy, VALUE args, int argc, VALUE *argv, VALUE passed_proc); @@ -1934,13 +2706,13 @@ static VALUE curry(VALUE dummy, VALUE args, int argc, VALUE *argv, VALUE passed_proc) { VALUE proc, passed, arity; - proc = RARRAY_PTR(args)[0]; - passed = RARRAY_PTR(args)[1]; - arity = RARRAY_PTR(args)[2]; + proc = RARRAY_AREF(args, 0); + passed = RARRAY_AREF(args, 1); + arity = RARRAY_AREF(args, 2); passed = rb_ary_plus(passed, rb_ary_new4(argc, argv)); rb_ary_freeze(passed); if (RARRAY_LEN(passed) < FIX2INT(arity)) { @@ -1949,12 +2721,11 @@ } arity = make_curry_proc(proc, passed, arity); return arity; } else { - return rb_proc_call_with_block(proc, check_argc(RARRAY_LEN(passed)), - RARRAY_PTR(passed), passed_proc); + return rb_proc_call_with_block(proc, check_argc(RARRAY_LEN(passed)), RARRAY_CONST_PTR(passed), passed_proc); } } /* * call-seq: @@ -1982,50 +2753,84 @@ * p b.curry(5)[1, 2][3, 4][5] #=> 15 * p b.curry(1)[1] #=> 1 * * b = lambda {|x, y, z| (x||0) + (y||0) + (z||0) } * p b.curry[1][2][3] #=> 6 - * p b.curry[1, 2][3, 4] #=> wrong number of arguments (4 for 3) - * p b.curry(5) #=> wrong number of arguments (5 for 3) - * p b.curry(1) #=> wrong number of arguments (1 for 3) + * p b.curry[1, 2][3, 4] #=> wrong number of arguments (given 4, expected 3) + * p b.curry(5) #=> wrong number of arguments (given 5, expected 3) + * p b.curry(1) #=> wrong number of arguments (given 1, expected 3) * * b = lambda {|x, y, z, *w| (x||0) + (y||0) + (z||0) + w.inject(0, &:+) } * p b.curry[1][2][3] #=> 6 * p b.curry[1, 2][3, 4] #=> 10 * p b.curry(5)[1][2][3][4][5] #=> 15 * p b.curry(5)[1, 2][3, 4][5] #=> 15 - * p b.curry(1) #=> wrong number of arguments (1 for 3) + * p b.curry(1) #=> wrong number of arguments (given 1, expected 3) * * b = proc { :foo } * p b.curry[] #=> :foo */ static VALUE -proc_curry(int argc, VALUE *argv, VALUE self) +proc_curry(int argc, const VALUE *argv, VALUE self) { - int sarity, marity = rb_proc_arity(self); - VALUE arity, opt = Qfalse; + int sarity, max_arity, min_arity = rb_proc_min_max_arity(self, &max_arity); + VALUE arity; - if (marity < 0) { - marity = -marity - 1; - opt = Qtrue; - } - rb_scan_args(argc, argv, "01", &arity); if (NIL_P(arity)) { - arity = INT2FIX(marity); + arity = INT2FIX(min_arity); } else { sarity = FIX2INT(arity); - if (rb_proc_lambda_p(self) && (sarity < marity || (sarity > marity && !opt))) { - rb_raise(rb_eArgError, "wrong number of arguments (%d for %d)", sarity, marity); + if (rb_proc_lambda_p(self)) { + rb_check_arity(sarity, min_arity, max_arity); } } return make_curry_proc(self, rb_ary_new(), arity); } /* + * call-seq: + * meth.curry -> proc + * meth.curry(arity) -> proc + * + * Returns a curried proc based on the method. When the proc is called with a number of + * arguments that is lower than the method's arity, then another curried proc is returned. + * Only when enough arguments have been supplied to satisfy the method signature, will the + * method actually be called. + * + * The optional <i>arity</i> argument should be supplied when currying methods with + * variable arguments to determine how many arguments are needed before the method is + * called. + * + * def foo(a,b,c) + * [a, b, c] + * end + * + * proc = self.method(:foo).curry + * proc2 = proc.call(1, 2) #=> #<Proc> + * proc2.call(3) #=> [1,2,3] + * + * def vararg(*args) + * args + * end + * + * proc = self.method(:vararg).curry(4) + * proc2 = proc.call(:x) #=> #<Proc> + * proc3 = proc2.call(:y, :z) #=> #<Proc> + * proc3.call(:a) #=> [:x, :y, :z, :a] + */ + +static VALUE +rb_method_curry(int argc, const VALUE *argv, VALUE self) +{ + VALUE proc = method_to_proc(self); + return proc_curry(argc, argv, proc); +} + +/* * Document-class: LocalJumpError * * Raised when Ruby can't yield as requested. * * A typical scenario is attempting to yield when no block is given: @@ -2090,33 +2895,33 @@ /* 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); -#if 0 /* incomplete. */ rb_add_method(rb_cProc, rb_intern("call"), VM_METHOD_TYPE_OPTIMIZED, - (void *)OPTIMIZED_METHOD_TYPE_CALL, 0); + (void *)OPTIMIZED_METHOD_TYPE_CALL, METHOD_VISI_PUBLIC); rb_add_method(rb_cProc, rb_intern("[]"), VM_METHOD_TYPE_OPTIMIZED, - (void *)OPTIMIZED_METHOD_TYPE_CALL, 0); + (void *)OPTIMIZED_METHOD_TYPE_CALL, METHOD_VISI_PUBLIC); rb_add_method(rb_cProc, rb_intern("==="), VM_METHOD_TYPE_OPTIMIZED, - (void *)OPTIMIZED_METHOD_TYPE_CALL, 0); + (void *)OPTIMIZED_METHOD_TYPE_CALL, METHOD_VISI_PUBLIC); rb_add_method(rb_cProc, rb_intern("yield"), VM_METHOD_TYPE_OPTIMIZED, - (void *)OPTIMIZED_METHOD_TYPE_CALL, 0); -#else + (void *)OPTIMIZED_METHOD_TYPE_CALL, METHOD_VISI_PUBLIC); + +#if 0 /* for RDoc */ rb_define_method(rb_cProc, "call", proc_call, -1); rb_define_method(rb_cProc, "[]", proc_call, -1); rb_define_method(rb_cProc, "===", proc_call, -1); rb_define_method(rb_cProc, "yield", proc_call, -1); #endif + 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_alias(rb_cProc, "inspect", "to_s"); rb_define_method(rb_cProc, "lambda?", rb_proc_lambda_p, 0); rb_define_method(rb_cProc, "binding", proc_binding, 0); rb_define_method(rb_cProc, "curry", proc_curry, -1); rb_define_method(rb_cProc, "source_location", rb_proc_location, 0); rb_define_method(rb_cProc, "parameters", rb_proc_parameters, 0); @@ -2125,40 +2930,42 @@ 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_new3(rb_eSysStackError, - rb_obj_freeze(rb_str_new2("stack level too deep"))); - OBJ_TAINT(sysstack_error); + rb_vm_register_special_exception(ruby_error_sysstack, rb_eSysStackError, "stack level too deep"); /* utility functions */ rb_define_global_function("proc", rb_block_proc, 0); - rb_define_global_function("lambda", proc_lambda, 0); + rb_define_global_function("lambda", rb_block_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, "curry", rb_method_curry, -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, "to_proc", method_to_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, "original_name", method_original_name, 0); rb_define_method(rb_cMethod, "owner", method_owner, 0); rb_define_method(rb_cMethod, "unbind", method_unbind, 0); rb_define_method(rb_cMethod, "source_location", rb_method_location, 0); rb_define_method(rb_cMethod, "parameters", rb_method_parameters, 0); + rb_define_method(rb_cMethod, "super_method", method_super_method, 0); rb_define_method(rb_mKernel, "method", rb_obj_method, 1); rb_define_method(rb_mKernel, "public_method", rb_obj_public_method, 1); + rb_define_method(rb_mKernel, "singleton_method", rb_obj_singleton_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"); @@ -2168,22 +2975,27 @@ 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, "original_name", method_original_name, 0); rb_define_method(rb_cUnboundMethod, "owner", method_owner, 0); rb_define_method(rb_cUnboundMethod, "bind", umethod_bind, 1); rb_define_method(rb_cUnboundMethod, "source_location", rb_method_location, 0); rb_define_method(rb_cUnboundMethod, "parameters", rb_method_parameters, 0); + rb_define_method(rb_cUnboundMethod, "super_method", method_super_method, 0); /* 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); + + rb_define_private_method(rb_singleton_class(rb_vm_top_self()), + "define_method", top_define_method, -1); } /* * Objects of class <code>Binding</code> encapsulate the execution * context at some particular place in the code and retain this context @@ -2199,19 +3011,19 @@ * * class Demo * def initialize(n) * @secret = n * end - * def getBinding - * return binding() + * def get_binding + * binding * end * end * * k1 = Demo.new(99) - * b1 = k1.getBinding + * b1 = k1.get_binding * k2 = Demo.new(-3) - * b2 = k2.getBinding + * b2 = k2.get_binding * * eval("@secret", b1) #=> 99 * eval("@secret", b2) #=> -3 * eval("@secret") #=> nil * @@ -2226,8 +3038,12 @@ 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_method(rb_cBinding, "local_variables", bind_local_variables, 0); + rb_define_method(rb_cBinding, "local_variable_get", bind_local_variable_get, 1); + rb_define_method(rb_cBinding, "local_variable_set", bind_local_variable_set, 2); + rb_define_method(rb_cBinding, "local_variable_defined?", bind_local_variable_defined_p, 1); + rb_define_method(rb_cBinding, "receiver", bind_receiver, 0); rb_define_global_function("binding", rb_f_binding, 0); } -