diff options
Diffstat (limited to 'proc.c')
-rw-r--r-- | proc.c | 1471 |
1 files changed, 798 insertions, 673 deletions
@@ -10,33 +10,29 @@ **********************************************************************/ #include "eval_intern.h" -#include "gc.h" #include "internal.h" #include "internal/class.h" #include "internal/error.h" #include "internal/eval.h" +#include "internal/gc.h" #include "internal/object.h" #include "internal/proc.h" #include "internal/symbol.h" #include "method.h" #include "iseq.h" #include "vm_core.h" - -#if !defined(__GNUC__) || __GNUC__ < 5 || defined(__MINGW32__) -# define NO_CLOBBERED(v) (*(volatile VALUE *)&(v)) -#else -# define NO_CLOBBERED(v) (v) -#endif - -#define UPDATE_TYPED_REFERENCE(_type, _ref) *(_type*)&_ref = (_type)rb_gc_location((VALUE)_ref) -#define UPDATE_REFERENCE(_ref) UPDATE_TYPED_REFERENCE(VALUE, _ref) +#include "yjit.h" const rb_cref_t *rb_vm_cref_in_context(VALUE self, VALUE cbase); struct METHOD { const VALUE recv; const VALUE klass; + /* needed for #super_method */ const VALUE iclass; + /* Different than me->owner only for ZSUPER methods. + This is error-prone but unavoidable unless ZSUPER methods are removed. */ + const VALUE owner; const rb_method_entry_t * const me; /* for bound methods, `me' should be rb_callable_method_entry_t * */ }; @@ -51,87 +47,39 @@ static int method_arity(VALUE); static int method_min_max_arity(VALUE, int *max); static VALUE proc_binding(VALUE self); -#define attached id__attached__ - /* Proc */ #define IS_METHOD_PROC_IFUNC(ifunc) ((ifunc)->func == bmcall) -/* :FIXME: The way procs are cloned has been historically different from the - * way everything else are. @shyouhei is not sure for the intention though. - */ -#undef CLONESETUP -static inline void -CLONESETUP(VALUE clone, VALUE obj) -{ - RBIMPL_ASSERT_OR_ASSUME(! RB_SPECIAL_CONST_P(obj)); - RBIMPL_ASSERT_OR_ASSUME(! RB_SPECIAL_CONST_P(clone)); - - const VALUE flags = RUBY_FL_PROMOTED0 | RUBY_FL_PROMOTED1 | RUBY_FL_FINALIZE; - rb_obj_setup(clone, rb_singleton_class_clone(obj), - RB_FL_TEST_RAW(obj, ~flags)); - rb_singleton_class_attached(RBASIC_CLASS(clone), clone); - if (RB_FL_TEST(obj, RUBY_FL_EXIVAR)) rb_copy_generic_ivar(clone, obj); -} - -static void -block_mark(const struct rb_block *block) -{ - switch (vm_block_type(block)) { - case block_type_iseq: - case block_type_ifunc: - { - const struct rb_captured_block *captured = &block->as.captured; - RUBY_MARK_MOVABLE_UNLESS_NULL(captured->self); - RUBY_MARK_MOVABLE_UNLESS_NULL((VALUE)captured->code.val); - if (captured->ep && captured->ep[VM_ENV_DATA_INDEX_ENV] != Qundef /* cfunc_proc_t */) { - rb_gc_mark(VM_ENV_ENVVAL(captured->ep)); - } - } - break; - case block_type_symbol: - RUBY_MARK_MOVABLE_UNLESS_NULL(block->as.symbol); - break; - case block_type_proc: - RUBY_MARK_MOVABLE_UNLESS_NULL(block->as.proc); - break; - } -} - static void -block_compact(struct rb_block *block) +block_mark_and_move(struct rb_block *block) { switch (block->type) { case block_type_iseq: case block_type_ifunc: - { - struct rb_captured_block *captured = &block->as.captured; - captured->self = rb_gc_location(captured->self); - captured->code.val = rb_gc_location(captured->code.val); - } - break; + { + struct rb_captured_block *captured = &block->as.captured; + rb_gc_mark_and_move(&captured->self); + rb_gc_mark_and_move(&captured->code.val); + if (captured->ep) { + rb_gc_mark_and_move((VALUE *)&captured->ep[VM_ENV_DATA_INDEX_ENV]); + } + } + break; case block_type_symbol: - block->as.symbol = rb_gc_location(block->as.symbol); - break; + rb_gc_mark_and_move(&block->as.symbol); + break; case block_type_proc: - block->as.proc = rb_gc_location(block->as.proc); - break; + rb_gc_mark_and_move(&block->as.proc); + break; } } static void -proc_compact(void *ptr) -{ - rb_proc_t *proc = ptr; - block_compact((struct rb_block *)&proc->block); -} - -static void -proc_mark(void *ptr) +proc_mark_and_move(void *ptr) { rb_proc_t *proc = ptr; - block_mark(&proc->block); - RUBY_MARK_LEAVE("proc"); + block_mark_and_move((struct rb_block *)&proc->block); } typedef struct { @@ -144,17 +92,17 @@ proc_memsize(const void *ptr) { const rb_proc_t *proc = ptr; if (proc->block.as.captured.ep == ((const cfunc_proc_t *)ptr)->env+1) - return sizeof(cfunc_proc_t); + return sizeof(cfunc_proc_t); return sizeof(rb_proc_t); } static const rb_data_type_t proc_data_type = { "proc", { - proc_mark, - RUBY_TYPED_DEFAULT_FREE, - proc_memsize, - proc_compact, + proc_mark_and_move, + RUBY_TYPED_DEFAULT_FREE, + proc_memsize, + proc_mark_and_move, }, 0, 0, RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED }; @@ -169,12 +117,7 @@ rb_proc_alloc(VALUE klass) VALUE rb_obj_is_proc(VALUE proc) { - if (rb_typeddata_is_kind_of(proc, &proc_data_type)) { - return Qtrue; - } - else { - return Qfalse; - } + return RBOOL(rb_typeddata_is_kind_of(proc, &proc_data_type)); } /* :nodoc: */ @@ -182,8 +125,15 @@ static VALUE proc_clone(VALUE self) { VALUE procval = rb_proc_dup(self); - CLONESETUP(procval, self); - return procval; + return rb_obj_clone_setup(self, procval, Qnil); +} + +/* :nodoc: */ +static VALUE +proc_dup(VALUE self) +{ + VALUE procval = rb_proc_dup(self); + return rb_obj_dup_setup(self, procval); } /* @@ -295,7 +245,7 @@ rb_proc_lambda_p(VALUE procval) rb_proc_t *proc; GetProcPtr(procval, proc); - return proc->is_lambda ? Qtrue : Qfalse; + return RBOOL(proc->is_lambda); } /* Binding */ @@ -309,23 +259,12 @@ binding_free(void *ptr) } static void -binding_mark(void *ptr) -{ - rb_binding_t *bind = ptr; - - RUBY_MARK_ENTER("binding"); - block_mark(&bind->block); - rb_gc_mark_movable(bind->pathobj); - RUBY_MARK_LEAVE("binding"); -} - -static void -binding_compact(void *ptr) +binding_mark_and_move(void *ptr) { rb_binding_t *bind = ptr; - block_compact((struct rb_block *)&bind->block); - UPDATE_REFERENCE(bind->pathobj); + block_mark_and_move((struct rb_block *)&bind->block); + rb_gc_mark_and_move((VALUE *)&bind->pathobj); } static size_t @@ -337,10 +276,10 @@ binding_memsize(const void *ptr) const rb_data_type_t ruby_binding_data_type = { "binding", { - binding_mark, - binding_free, - binding_memsize, - binding_compact, + binding_mark_and_move, + binding_free, + binding_memsize, + binding_mark_and_move, }, 0, 0, RUBY_TYPED_WB_PROTECTED | RUBY_TYPED_FREE_IMMEDIATELY }; @@ -351,6 +290,9 @@ rb_binding_alloc(VALUE klass) VALUE obj; rb_binding_t *bind; obj = TypedData_Make_Struct(klass, rb_binding_t, &ruby_binding_data_type, bind); +#if YJIT_STATS + rb_yjit_collect_binding_alloc(); +#endif return obj; } @@ -366,7 +308,7 @@ binding_dup(VALUE self) rb_vm_block_copy(bindval, &dst->block, &src->block); RB_OBJ_WRITE(bindval, &dst->pathobj, src->pathobj); dst->first_lineno = src->first_lineno; - return bindval; + return rb_obj_dup_setup(self, bindval); } /* :nodoc: */ @@ -374,8 +316,7 @@ static VALUE binding_clone(VALUE self) { VALUE bindval = binding_dup(self); - CLONESETUP(bindval, self); - return bindval; + return rb_obj_clone_setup(self, bindval, Qnil); } VALUE @@ -389,16 +330,44 @@ rb_binding_new(void) * call-seq: * binding -> a_binding * - * Returns a +Binding+ object, describing the variable and + * 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. See also the description of class +Binding+. + * calling Binding#eval to execute the evaluated command in this + * environment, or extracting its local variables. * - * def get_binding(param) - * binding + * class User + * def initialize(name, position) + * @name = name + * @position = position + * end + * + * def get_binding + * binding + * end * end - * b = get_binding("hello") - * eval("param", b) #=> "hello" + * + * user = User.new('Joan', 'manager') + * template = '{name: @name, position: @position}' + * + * # evaluate template in context of the object + * eval(template, user.get_binding) + * #=> {:name=>"Joan", :position=>"manager"} + * + * Binding#local_variable_get can be used to access the variables + * whose names are reserved Ruby keywords: + * + * # This is valid parameter declaration, but `if` parameter can't + * # be accessed by name, because it is a reserved word. + * def validate(field, validation, if: nil) + * condition = binding.local_variable_get('if') + * return unless condition + * + * # ...Some implementation ... + * end + * + * validate(:name, :empty?, if: false) # skips validation + * validate(:name, :empty?, if: true) # performs validation + * */ static VALUE @@ -438,37 +407,37 @@ get_local_variable_ptr(const rb_env_t **envp, ID lid) { const rb_env_t *env = *envp; do { - if (!VM_ENV_FLAGS(env->ep, VM_FRAME_FLAG_CFRAME)) { + if (!VM_ENV_FLAGS(env->ep, VM_FRAME_FLAG_CFRAME)) { if (VM_ENV_FLAGS(env->ep, VM_ENV_FLAG_ISOLATED)) { return NULL; } const rb_iseq_t *iseq = env->iseq; - unsigned int i; - - VM_ASSERT(rb_obj_is_iseq((VALUE)iseq)); - - for (i=0; i<iseq->body->local_table_size; i++) { - if (iseq->body->local_table[i] == lid) { - if (iseq->body->local_iseq == iseq && - iseq->body->param.flags.has_block && - (unsigned int)iseq->body->param.block_start == i) { - const VALUE *ep = env->ep; - if (!VM_ENV_FLAGS(ep, VM_FRAME_FLAG_MODIFIED_BLOCK_PARAM)) { - RB_OBJ_WRITE(env, &env->env[i], rb_vm_bh_to_procval(GET_EC(), VM_ENV_BLOCK_HANDLER(ep))); - VM_ENV_FLAGS_SET(ep, VM_FRAME_FLAG_MODIFIED_BLOCK_PARAM); - } - } - - *envp = env; - return &env->env[i]; - } - } - } - else { - *envp = NULL; - return NULL; - } + unsigned int i; + + VM_ASSERT(rb_obj_is_iseq((VALUE)iseq)); + + for (i=0; i<ISEQ_BODY(iseq)->local_table_size; i++) { + if (ISEQ_BODY(iseq)->local_table[i] == lid) { + if (ISEQ_BODY(iseq)->local_iseq == iseq && + ISEQ_BODY(iseq)->param.flags.has_block && + (unsigned int)ISEQ_BODY(iseq)->param.block_start == i) { + const VALUE *ep = env->ep; + if (!VM_ENV_FLAGS(ep, VM_FRAME_FLAG_MODIFIED_BLOCK_PARAM)) { + RB_OBJ_WRITE(env, &env->env[i], rb_vm_bh_to_procval(GET_EC(), VM_ENV_BLOCK_HANDLER(ep))); + VM_ENV_FLAGS_SET(ep, VM_FRAME_FLAG_MODIFIED_BLOCK_PARAM); + } + } + + *envp = env; + return &env->env[i]; + } + } + } + else { + *envp = NULL; + return NULL; + } } while ((env = rb_vm_env_prev_env(env)) != NULL); *envp = NULL; @@ -487,17 +456,17 @@ check_local_id(VALUE bindval, volatile VALUE *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)); - } + 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; + 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; } @@ -567,7 +536,7 @@ bind_local_variable_get(VALUE bindval, VALUE sym) sym = ID2SYM(lid); undefined: - rb_name_err_raise("local variable `%1$s' is not defined for %2$s", + rb_name_err_raise("local variable '%1$s' is not defined for %2$s", bindval, sym); UNREACHABLE_RETURN(Qundef); } @@ -610,11 +579,15 @@ bind_local_variable_set(VALUE bindval, VALUE sym, VALUE val) GetBindingPtr(bindval, bind); env = VM_ENV_ENVVAL_PTR(vm_block_ep(&bind->block)); if ((ptr = get_local_variable_ptr(&env, lid)) == NULL) { - /* not found. create new env */ - ptr = rb_binding_add_dynavars(bindval, bind, 1, &lid); - env = VM_ENV_ENVVAL_PTR(vm_block_ep(&bind->block)); + /* not found. create new env */ + ptr = rb_binding_add_dynavars(bindval, bind, 1, &lid); + env = VM_ENV_ENVVAL_PTR(vm_block_ep(&bind->block)); } +#if YJIT_STATS + rb_yjit_collect_binding_set(); +#endif + RB_OBJ_WRITE(env, ptr, val); return val; @@ -648,7 +621,7 @@ bind_local_variable_defined_p(VALUE bindval, VALUE sym) GetBindingPtr(bindval, bind); env = VM_ENV_ENVVAL_PTR(vm_block_ep(&bind->block)); - return get_local_variable_ptr(&env, lid) ? Qtrue : Qfalse; + return RBOOL(get_local_variable_ptr(&env, lid)); } /* @@ -723,40 +696,46 @@ struct vm_ifunc * rb_vm_ifunc_new(rb_block_call_func_t func, const void *data, int min_argc, int max_argc) { union { - struct vm_ifunc_argc argc; - VALUE packed; + struct vm_ifunc_argc argc; + VALUE packed; } arity; if (min_argc < UNLIMITED_ARGUMENTS || #if SIZEOF_INT * 2 > SIZEOF_VALUE - min_argc >= (int)(1U << (SIZEOF_VALUE * CHAR_BIT) / 2) || + min_argc >= (int)(1U << (SIZEOF_VALUE * CHAR_BIT) / 2) || #endif - 0) { - rb_raise(rb_eRangeError, "minimum argument number out of range: %d", - min_argc); + 0) { + rb_raise(rb_eRangeError, "minimum argument number out of range: %d", + min_argc); } if (max_argc < UNLIMITED_ARGUMENTS || #if SIZEOF_INT * 2 > SIZEOF_VALUE - max_argc >= (int)(1U << (SIZEOF_VALUE * CHAR_BIT) / 2) || + max_argc >= (int)(1U << (SIZEOF_VALUE * CHAR_BIT) / 2) || #endif - 0) { - rb_raise(rb_eRangeError, "maximum argument number out of range: %d", - max_argc); + 0) { + rb_raise(rb_eRangeError, "maximum argument number out of range: %d", + max_argc); } arity.argc.min = min_argc; arity.argc.max = max_argc; - VALUE ret = rb_imemo_new(imemo_ifunc, (VALUE)func, (VALUE)data, arity.packed, 0); - return (struct vm_ifunc *)ret; + rb_execution_context_t *ec = GET_EC(); + + struct vm_ifunc *ifunc = IMEMO_NEW(struct vm_ifunc, imemo_ifunc, (VALUE)rb_vm_svar_lep(ec, ec->cfp)); + ifunc->func = func; + ifunc->data = data; + ifunc->argc = arity.argc; + + return ifunc; } -MJIT_FUNC_EXPORTED VALUE +VALUE rb_func_proc_new(rb_block_call_func_t func, VALUE val) { struct vm_ifunc *ifunc = rb_vm_ifunc_proc_new(func, (void *)val); return cfunc_proc_new(rb_cProc, (VALUE)ifunc); } -MJIT_FUNC_EXPORTED VALUE +VALUE rb_func_lambda_new(rb_block_call_func_t func, VALUE val, int min_argc, int max_argc) { struct vm_ifunc *ifunc = rb_vm_ifunc_new(func, (void *)val, min_argc, max_argc); @@ -766,7 +745,7 @@ rb_func_lambda_new(rb_block_call_func_t func, VALUE val, int min_argc, int max_a static const char proc_without_block[] = "tried to create Proc object without a block"; static VALUE -proc_new(VALUE klass, int8_t is_lambda, int8_t kernel) +proc_new(VALUE klass, int8_t is_lambda) { VALUE procval; const rb_execution_context_t *ec = GET_EC(); @@ -780,35 +759,27 @@ proc_new(VALUE klass, int8_t is_lambda, int8_t kernel) /* block is in cf */ switch (vm_block_handler_type(block_handler)) { case block_handler_type_proc: - procval = VM_BH_TO_PROC(block_handler); + procval = VM_BH_TO_PROC(block_handler); - if (RBASIC_CLASS(procval) == klass) { - return procval; - } - else { - VALUE newprocval = rb_proc_dup(procval); + if (RBASIC_CLASS(procval) == klass) { + return procval; + } + else { + VALUE newprocval = rb_proc_dup(procval); RBASIC_SET_CLASS(newprocval, klass); - return newprocval; - } - break; + return newprocval; + } + break; case block_handler_type_symbol: - return (klass != rb_cProc) ? - sym_proc_new(klass, VM_BH_TO_SYMBOL(block_handler)) : - rb_sym_to_proc(VM_BH_TO_SYMBOL(block_handler)); - break; + return (klass != rb_cProc) ? + sym_proc_new(klass, VM_BH_TO_SYMBOL(block_handler)) : + rb_sym_to_proc(VM_BH_TO_SYMBOL(block_handler)); + break; case block_handler_type_ifunc: - return rb_vm_make_proc_lambda(ec, VM_BH_TO_CAPT_BLOCK(block_handler), klass, is_lambda); case block_handler_type_iseq: - { - const struct rb_captured_block *captured = VM_BH_TO_CAPT_BLOCK(block_handler); - rb_control_frame_t *last_ruby_cfp = rb_vm_get_ruby_level_next_cfp(ec, cfp); - if (is_lambda && last_ruby_cfp && vm_cfp_forwarded_bh_p(last_ruby_cfp, block_handler)) { - is_lambda = false; - } - return rb_vm_make_proc_lambda(ec, captured, klass, is_lambda); - } + return rb_vm_make_proc_lambda(ec, VM_BH_TO_CAPT_BLOCK(block_handler), klass, is_lambda); } VM_UNREACHABLE(proc_new); return Qnil; @@ -831,7 +802,7 @@ proc_new(VALUE klass, int8_t is_lambda, int8_t kernel) static VALUE rb_proc_s_new(int argc, VALUE *argv, VALUE klass) { - VALUE block = proc_new(klass, FALSE, FALSE); + VALUE block = proc_new(klass, FALSE); rb_obj_call_init_kw(block, argc, argv, RB_PASS_CALLED_KEYWORDS); return block; @@ -840,7 +811,7 @@ rb_proc_s_new(int argc, VALUE *argv, VALUE klass) VALUE rb_block_proc(void) { - return proc_new(rb_cProc, FALSE, FALSE); + return proc_new(rb_cProc, FALSE); } /* @@ -853,41 +824,44 @@ rb_block_proc(void) static VALUE f_proc(VALUE _) { - return proc_new(rb_cProc, FALSE, TRUE); + return proc_new(rb_cProc, FALSE); } VALUE rb_block_lambda(void) { - return proc_new(rb_cProc, TRUE, FALSE); + return proc_new(rb_cProc, TRUE); } static void -f_lambda_warn(void) +f_lambda_filter_non_literal(void) { rb_control_frame_t *cfp = GET_EC()->cfp; VALUE block_handler = rb_vm_frame_block_handler(cfp); - if (block_handler != VM_BLOCK_HANDLER_NONE) { - switch (vm_block_handler_type(block_handler)) { - case block_handler_type_iseq: - if (RUBY_VM_PREVIOUS_CONTROL_FRAME(cfp)->ep == VM_BH_TO_ISEQ_BLOCK(block_handler)->ep) { - return; - } - break; - case block_handler_type_symbol: + if (block_handler == VM_BLOCK_HANDLER_NONE) { + // no block erorr raised else where + return; + } + + switch (vm_block_handler_type(block_handler)) { + case block_handler_type_iseq: + if (RUBY_VM_PREVIOUS_CONTROL_FRAME(cfp)->ep == VM_BH_TO_ISEQ_BLOCK(block_handler)->ep) { + return; + } + break; + case block_handler_type_symbol: + return; + case block_handler_type_proc: + if (rb_proc_lambda_p(VM_BH_TO_PROC(block_handler))) { return; - case block_handler_type_proc: - if (rb_proc_lambda_p(VM_BH_TO_PROC(block_handler))) { - return; - } - break; - case block_handler_type_ifunc: - break; } + break; + case block_handler_type_ifunc: + break; } - rb_warn_deprecated("lambda without a literal block", "the proc without lambda"); + rb_raise(rb_eArgError, "the lambda method requires a literal block"); } /* @@ -901,7 +875,7 @@ f_lambda_warn(void) static VALUE f_lambda(VALUE _) { - f_lambda_warn(); + f_lambda_filter_non_literal(); return rb_block_lambda(); } @@ -968,8 +942,8 @@ static inline int check_argc(long argc) { if (argc > INT_MAX || argc < 0) { - rb_raise(rb_eArgError, "too many arguments (%lu)", - (unsigned long)argc); + rb_raise(rb_eArgError, "too many arguments (%lu)", + (unsigned long)argc); } return (int)argc; } @@ -995,15 +969,7 @@ rb_proc_call_kw(VALUE self, VALUE args, int kw_splat) VALUE rb_proc_call(VALUE self, VALUE args) { - VALUE vret; - rb_proc_t *proc; - GetProcPtr(self, proc); - vret = rb_vm_invoke_proc(GET_EC(), proc, - check_argc(RARRAY_LEN(args)), RARRAY_CONST_PTR(args), - RB_NO_KEYWORDS, VM_BLOCK_HANDLER_NONE); - RB_GC_GUARD(self); - RB_GC_GUARD(args); - return vret; + return rb_proc_call_kw(self, args, RB_NO_KEYWORDS); } static VALUE @@ -1027,13 +993,7 @@ rb_proc_call_with_block_kw(VALUE self, int argc, const VALUE *argv, VALUE passed VALUE rb_proc_call_with_block(VALUE self, int argc, const VALUE *argv, VALUE passed_procval) { - rb_execution_context_t *ec = GET_EC(); - VALUE vret; - rb_proc_t *proc; - GetProcPtr(self, proc); - vret = rb_vm_invoke_proc(ec, proc, argc, argv, RB_NO_KEYWORDS, proc_to_block_handler(passed_procval)); - RB_GC_GUARD(self); - return vret; + return rb_proc_call_with_block_kw(self, argc, argv, passed_procval, RB_NO_KEYWORDS); } @@ -1088,11 +1048,11 @@ proc_arity(VALUE self) 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) + *max = ISEQ_BODY(iseq)->param.flags.has_rest == FALSE ? + ISEQ_BODY(iseq)->param.lead_num + ISEQ_BODY(iseq)->param.opt_num + ISEQ_BODY(iseq)->param.post_num + + (ISEQ_BODY(iseq)->param.flags.has_kw == TRUE || ISEQ_BODY(iseq)->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); + return ISEQ_BODY(iseq)->param.lead_num + ISEQ_BODY(iseq)->param.post_num + (ISEQ_BODY(iseq)->param.flags.has_kw && ISEQ_BODY(iseq)->param.keyword->required_num > 0); } static int @@ -1101,20 +1061,20 @@ rb_vm_block_min_max_arity(const struct rb_block *block, int *max) again: switch (vm_block_type(block)) { case block_type_iseq: - return rb_iseq_min_max_arity(rb_iseq_check(block->as.captured.code.iseq), max); + return rb_iseq_min_max_arity(rb_iseq_check(block->as.captured.code.iseq), max); case block_type_proc: - block = vm_proc_block(block->as.proc); - goto again; + block = vm_proc_block(block->as.proc); + goto again; case block_type_ifunc: - { - const struct vm_ifunc *ifunc = block->as.captured.code.ifunc; - if (IS_METHOD_PROC_IFUNC(ifunc)) { - /* e.g. method(:foo).to_proc.arity */ - return method_min_max_arity((VALUE)ifunc->data, max); - } - *max = ifunc->argc.max; - return ifunc->argc.min; - } + { + const struct vm_ifunc *ifunc = block->as.captured.code.ifunc; + if (IS_METHOD_PROC_IFUNC(ifunc)) { + /* e.g. method(:foo).to_proc.arity */ + return method_min_max_arity((VALUE)ifunc->data, max); + } + *max = ifunc->argc.max; + return ifunc->argc.min; + } case block_type_symbol: *max = UNLIMITED_ARGUMENTS; return 1; @@ -1152,20 +1112,20 @@ block_setup(struct rb_block *block, VALUE block_handler) { switch (vm_block_handler_type(block_handler)) { case block_handler_type_iseq: - block->type = block_type_iseq; - block->as.captured = *VM_BH_TO_ISEQ_BLOCK(block_handler); - break; + block->type = block_type_iseq; + block->as.captured = *VM_BH_TO_ISEQ_BLOCK(block_handler); + break; case block_handler_type_ifunc: - block->type = block_type_ifunc; - block->as.captured = *VM_BH_TO_IFUNC_BLOCK(block_handler); - break; + block->type = block_type_ifunc; + block->as.captured = *VM_BH_TO_IFUNC_BLOCK(block_handler); + break; case block_handler_type_symbol: - block->type = block_type_symbol; - block->as.symbol = VM_BH_TO_SYMBOL(block_handler); - break; + block->type = block_type_symbol; + block->as.symbol = VM_BH_TO_SYMBOL(block_handler); + break; case block_handler_type_proc: - block->type = block_type_proc; - block->as.proc = VM_BH_TO_PROC(block_handler); + block->type = block_type_proc; + block->as.proc = VM_BH_TO_PROC(block_handler); } } @@ -1179,7 +1139,7 @@ rb_block_pair_yield_optimizable(void) struct rb_block block; if (block_handler == VM_BLOCK_HANDLER_NONE) { - rb_raise(rb_eArgError, "no block given"); + rb_raise(rb_eArgError, "no block given"); } block_setup(&block, block_handler); @@ -1190,14 +1150,14 @@ rb_block_pair_yield_optimizable(void) return 0; case block_handler_type_proc: - { - VALUE procval = block_handler; - rb_proc_t *proc; - GetProcPtr(procval, proc); + { + VALUE procval = block_handler; + rb_proc_t *proc; + GetProcPtr(procval, proc); if (proc->is_lambda) return 0; if (min != max) return 0; return min > 1; - } + } default: return min > 1; @@ -1214,26 +1174,21 @@ rb_block_arity(void) struct rb_block block; if (block_handler == VM_BLOCK_HANDLER_NONE) { - rb_raise(rb_eArgError, "no block given"); + rb_raise(rb_eArgError, "no block given"); } block_setup(&block, block_handler); - min = rb_vm_block_min_max_arity(&block, &max); switch (vm_block_type(&block)) { case block_handler_type_symbol: - return -1; + return -1; case block_handler_type_proc: - { - VALUE procval = block_handler; - rb_proc_t *proc; - GetProcPtr(procval, proc); - return (proc->is_lambda ? min == max : max != UNLIMITED_ARGUMENTS) ? min : -min-1; - } + return rb_proc_arity(block_handler); default: - return max != UNLIMITED_ARGUMENTS ? min : -min-1; + min = rb_vm_block_min_max_arity(&block, &max); + return max != UNLIMITED_ARGUMENTS ? min : -min-1; } } @@ -1246,7 +1201,7 @@ rb_block_min_max_arity(int *max) struct rb_block block; if (block_handler == VM_BLOCK_HANDLER_NONE) { - rb_raise(rb_eArgError, "no block given"); + rb_raise(rb_eArgError, "no block given"); } block_setup(&block, block_handler); @@ -1265,23 +1220,23 @@ rb_proc_get_iseq(VALUE self, int *is_proc) switch (vm_block_type(block)) { case block_type_iseq: - return rb_iseq_check(block->as.captured.code.iseq); + return rb_iseq_check(block->as.captured.code.iseq); case block_type_proc: - return rb_proc_get_iseq(block->as.proc, is_proc); + return rb_proc_get_iseq(block->as.proc, is_proc); case block_type_ifunc: - { - const struct vm_ifunc *ifunc = block->as.captured.code.ifunc; - if (IS_METHOD_PROC_IFUNC(ifunc)) { - /* method(:foo).to_proc */ - if (is_proc) *is_proc = 0; - return rb_method_iseq((VALUE)ifunc->data); - } - else { - return NULL; - } - } + { + const struct vm_ifunc *ifunc = block->as.captured.code.ifunc; + if (IS_METHOD_PROC_IFUNC(ifunc)) { + /* method(:foo).to_proc */ + if (is_proc) *is_proc = 0; + return rb_method_iseq((VALUE)ifunc->data); + } + else { + return NULL; + } + } case block_type_symbol: - return NULL; + return NULL; } VM_UNREACHABLE(rb_proc_get_iseq); @@ -1379,12 +1334,12 @@ iseq_location(const rb_iseq_t *iseq) if (!iseq) return Qnil; rb_iseq_check(iseq); loc[0] = rb_iseq_path(iseq); - loc[1] = iseq->body->location.first_lineno; + loc[1] = RB_INT2NUM(ISEQ_BODY(iseq)->location.first_lineno); return rb_ary_new4(2, loc); } -MJIT_FUNC_EXPORTED VALUE +VALUE rb_iseq_location(const rb_iseq_t *iseq) { return iseq_location(iseq); @@ -1414,32 +1369,59 @@ rb_unnamed_parameters(int arity) a = rb_ary_new3(1, ID2SYM(req)); OBJ_FREEZE(a); for (; n; --n) { - rb_ary_push(param, a); + rb_ary_push(param, a); } if (arity < 0) { - CONST_ID(rest, "rest"); - rb_ary_store(param, ~arity, rb_ary_new3(1, ID2SYM(rest))); + CONST_ID(rest, "rest"); + rb_ary_store(param, ~arity, rb_ary_new3(1, ID2SYM(rest))); } return param; } /* * call-seq: - * prc.parameters -> array + * prc.parameters(lambda: nil) -> array * - * Returns the parameter information of this proc. + * Returns the parameter information of this proc. If the lambda + * keyword is provided and not nil, treats the proc as a lambda if + * true and as a non-lambda if false. * + * prc = proc{|x, y=42, *other|} + * prc.parameters #=> [[:opt, :x], [:opt, :y], [:rest, :other]] * prc = lambda{|x, y=42, *other|} * prc.parameters #=> [[:req, :x], [:opt, :y], [:rest, :other]] + * prc = proc{|x, y=42, *other|} + * prc.parameters(lambda: true) #=> [[:req, :x], [:opt, :y], [:rest, :other]] + * prc = lambda{|x, y=42, *other|} + * prc.parameters(lambda: false) #=> [[:opt, :x], [:opt, :y], [:rest, :other]] */ static VALUE -rb_proc_parameters(VALUE self) +rb_proc_parameters(int argc, VALUE *argv, VALUE self) { - int is_proc; - const rb_iseq_t *iseq = rb_proc_get_iseq(self, &is_proc); + static ID keyword_ids[1]; + VALUE opt, lambda; + VALUE kwargs[1]; + int is_proc ; + const rb_iseq_t *iseq; + + iseq = rb_proc_get_iseq(self, &is_proc); + + if (!keyword_ids[0]) { + CONST_ID(keyword_ids[0], "lambda"); + } + + rb_scan_args(argc, argv, "0:", &opt); + if (!NIL_P(opt)) { + rb_get_kwargs(opt, keyword_ids, 0, 1, kwargs); + lambda = kwargs[0]; + if (!NIL_P(lambda)) { + is_proc = !RTEST(lambda); + } + } + if (!iseq) { - return rb_unnamed_parameters(rb_proc_arity(self)); + return rb_unnamed_parameters(rb_proc_arity(self)); } return rb_iseq_parameters(iseq, is_proc); } @@ -1451,10 +1433,25 @@ rb_hash_proc(st_index_t hash, VALUE prc) GetProcPtr(prc, proc); hash = rb_hash_uint(hash, (st_index_t)proc->block.as.captured.code.val); hash = rb_hash_uint(hash, (st_index_t)proc->block.as.captured.self); - return rb_hash_uint(hash, (st_index_t)proc->block.as.captured.ep >> 16); + return rb_hash_uint(hash, (st_index_t)proc->block.as.captured.ep); } -MJIT_FUNC_EXPORTED VALUE + +/* + * call-seq: + * to_proc + * + * Returns a Proc object which calls the method with name of +self+ + * on the first parameter and passes the remaining parameters to the method. + * + * proc = :to_s.to_proc # => #<Proc:0x000001afe0e48680(&:to_s) (lambda)> + * proc.call(1000) # => "1000" + * proc.call(1000, 16) # => "3e8" + * (1..3).collect(&:to_s) # => ["1", "2", "3"] + * + */ + +VALUE rb_sym_to_proc(VALUE sym) { static VALUE sym_proc_cache = Qfalse; @@ -1464,9 +1461,9 @@ rb_sym_to_proc(VALUE sym) ID id; 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); + sym_proc_cache = rb_ary_hidden_new(SYM_PROC_CACHE_SIZE * 2); + rb_vm_register_global_object(sym_proc_cache); + rb_ary_store(sym_proc_cache, SYM_PROC_CACHE_SIZE*2 - 1, Qnil); } id = SYM2ID(sym); @@ -1479,7 +1476,7 @@ rb_sym_to_proc(VALUE sym) proc = sym_proc_new(rb_cProc, ID2SYM(id)); RARRAY_ASET(sym_proc_cache, index, sym); RARRAY_ASET(sym_proc_cache, index + 1, proc); - return proc; + return proc; } } @@ -1511,22 +1508,22 @@ rb_block_to_s(VALUE self, const struct rb_block *block, const char *additional_i again: switch (vm_block_type(block)) { case block_type_proc: - block = vm_proc_block(block->as.proc); - goto again; + block = vm_proc_block(block->as.proc); + goto again; case block_type_iseq: - { - const rb_iseq_t *iseq = rb_iseq_check(block->as.captured.code.iseq); + { + const rb_iseq_t *iseq = rb_iseq_check(block->as.captured.code.iseq); rb_str_catf(str, "%p %"PRIsVALUE":%d", (void *)self, - rb_iseq_path(iseq), - FIX2INT(iseq->body->location.first_lineno)); - } - break; + rb_iseq_path(iseq), + ISEQ_BODY(iseq)->location.first_lineno); + } + break; case block_type_symbol: - rb_str_catf(str, "%p(&%+"PRIsVALUE")", (void *)self, block->as.symbol); - break; + rb_str_catf(str, "%p(&%+"PRIsVALUE")", (void *)self, block->as.symbol); + break; case block_type_ifunc: - rb_str_catf(str, "%p", (void *)block->as.captured.code.ifunc); - break; + rb_str_catf(str, "%p", (void *)block->as.captured.code.ifunc); + break; } if (additional_info) rb_str_cat_cstr(str, additional_info); @@ -1565,51 +1562,31 @@ proc_to_proc(VALUE self) } static void -bm_mark(void *ptr) -{ - struct METHOD *data = ptr; - rb_gc_mark_movable(data->recv); - rb_gc_mark_movable(data->klass); - rb_gc_mark_movable(data->iclass); - rb_gc_mark_movable((VALUE)data->me); -} - -static void -bm_compact(void *ptr) +bm_mark_and_move(void *ptr) { struct METHOD *data = ptr; - UPDATE_REFERENCE(data->recv); - UPDATE_REFERENCE(data->klass); - UPDATE_REFERENCE(data->iclass); - UPDATE_TYPED_REFERENCE(rb_method_entry_t *, data->me); -} - -static size_t -bm_memsize(const void *ptr) -{ - return sizeof(struct METHOD); + rb_gc_mark_and_move((VALUE *)&data->recv); + rb_gc_mark_and_move((VALUE *)&data->klass); + rb_gc_mark_and_move((VALUE *)&data->iclass); + rb_gc_mark_and_move((VALUE *)&data->owner); + rb_gc_mark_and_move_ptr((rb_method_entry_t **)&data->me); } static const rb_data_type_t method_data_type = { "method", { - bm_mark, - RUBY_TYPED_DEFAULT_FREE, - bm_memsize, - bm_compact, + bm_mark_and_move, + RUBY_TYPED_DEFAULT_FREE, + NULL, // No external memory to report, + bm_mark_and_move, }, - 0, 0, RUBY_TYPED_FREE_IMMEDIATELY + 0, 0, RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED | RUBY_TYPED_EMBEDDABLE }; VALUE rb_obj_is_method(VALUE m) { - if (rb_typeddata_is_kind_of(m, &method_data_type)) { - return Qtrue; - } - else { - return Qfalse; - } + return RBOOL(rb_typeddata_is_kind_of(m, &method_data_type)); } static int @@ -1618,9 +1595,9 @@ 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 (UNDEF_P(obj)) return 0; if (rb_method_basic_definition_p(klass, rmiss)) return 0; - return RTEST(rb_funcall(obj, rmiss, 2, sym, scope ? Qfalse : Qtrue)); + return RTEST(rb_funcall(obj, rmiss, 2, sym, RBOOL(!scope))); } @@ -1634,6 +1611,7 @@ mnew_missing(VALUE klass, VALUE obj, ID id, VALUE mclass) RB_OBJ_WRITE(method, &data->recv, obj); RB_OBJ_WRITE(method, &data->klass, klass); + RB_OBJ_WRITE(method, &data->owner, klass); def = ZALLOC(rb_method_definition_t); def->type = VM_METHOD_TYPE_MISSING; @@ -1657,46 +1635,55 @@ mnew_missing_by_name(VALUE klass, VALUE obj, VALUE *name, int scope, VALUE mclas static VALUE mnew_internal(const rb_method_entry_t *me, VALUE klass, VALUE iclass, - VALUE obj, ID id, VALUE mclass, int scope, int error) + VALUE obj, ID id, VALUE mclass, int scope, int error) { struct METHOD *data; VALUE method; + const rb_method_entry_t *original_me = me; 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, mclass); - } - if (!error) return Qnil; - rb_print_undef(klass, id, METHOD_VISI_UNDEF); + if (respond_to_missing_p(klass, obj, ID2SYM(id), scope)) { + return mnew_missing(klass, obj, id, mclass); + } + if (!error) return Qnil; + rb_print_undef(klass, id, METHOD_VISI_UNDEF); } 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); - } + visi = METHOD_ENTRY_VISI(me); + RUBY_ASSERT(visi != METHOD_VISI_UNDEF); /* !UNDEFINED_METHOD_ENTRY_P(me) */ + if (scope && (visi != METHOD_VISI_PUBLIC)) { + if (!error) return Qnil; + rb_print_inaccessible(klass, id, visi); + } } if (me->def->type == VM_METHOD_TYPE_ZSUPER) { - if (me->defined_class) { + if (me->defined_class) { VALUE klass = RCLASS_SUPER(RCLASS_ORIGIN(me->defined_class)); - id = me->def->original_id; + id = me->def->original_id; me = (rb_method_entry_t *)rb_callable_method_entry_with_refinements(klass, id, &iclass); - } - else { + } + else { VALUE klass = RCLASS_SUPER(RCLASS_ORIGIN(me->owner)); - id = me->def->original_id; - me = rb_method_entry_without_refinements(klass, id, &iclass); - } - goto again; + id = me->def->original_id; + me = rb_method_entry_without_refinements(klass, id, &iclass); + } + goto again; } method = TypedData_Make_Struct(mclass, struct METHOD, &method_data_type, data); - RB_OBJ_WRITE(method, &data->recv, obj); - RB_OBJ_WRITE(method, &data->klass, klass); + if (UNDEF_P(obj)) { + RB_OBJ_WRITE(method, &data->recv, Qundef); + RB_OBJ_WRITE(method, &data->klass, Qundef); + } + else { + RB_OBJ_WRITE(method, &data->recv, obj); + RB_OBJ_WRITE(method, &data->klass, klass); + } RB_OBJ_WRITE(method, &data->iclass, iclass); + RB_OBJ_WRITE(method, &data->owner, original_me->owner); RB_OBJ_WRITE(method, &data->me, me); return method; @@ -1704,7 +1691,7 @@ mnew_internal(const rb_method_entry_t *me, VALUE klass, VALUE iclass, static VALUE mnew_from_me(const rb_method_entry_t *me, VALUE klass, VALUE iclass, - VALUE obj, ID id, VALUE mclass, int scope) + VALUE obj, ID id, VALUE mclass, int scope) { return mnew_internal(me, klass, iclass, obj, id, mclass, scope, TRUE); } @@ -1715,7 +1702,7 @@ mnew_callable(VALUE klass, VALUE obj, ID id, VALUE mclass, int scope) const rb_method_entry_t *me; VALUE iclass = Qnil; - ASSUME(obj != Qundef); + ASSUME(!UNDEF_P(obj)); me = (rb_method_entry_t *)rb_callable_method_entry_with_refinements(klass, id, &iclass); return mnew_from_me(me, klass, iclass, obj, id, mclass, scope); } @@ -1782,22 +1769,22 @@ method_eq(VALUE method, VALUE other) VALUE klass1, klass2; if (!rb_obj_is_method(other)) - return Qfalse; + return Qfalse; if (CLASS_OF(method) != CLASS_OF(other)) - return Qfalse; + return Qfalse; Check_TypedStruct(method, &method_data_type); - m1 = (struct METHOD *)DATA_PTR(method); - m2 = (struct METHOD *)DATA_PTR(other); + m1 = (struct METHOD *)RTYPEDDATA_GET_DATA(method); + m2 = (struct METHOD *)RTYPEDDATA_GET_DATA(other); 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; + klass1 != klass2 || + m1->klass != m2->klass || + m1->recv != m2->recv) { + return Qfalse; } return Qtrue; @@ -1805,6 +1792,22 @@ method_eq(VALUE method, VALUE other) /* * call-seq: + * meth.eql?(other_meth) -> true or false + * meth == other_meth -> true or false + * + * Two unbound method objects are equal if they refer to the same + * method definition. + * + * Array.instance_method(:each_slice) == Enumerable.instance_method(:each_slice) + * #=> true + * + * Array.instance_method(:sum) == Enumerable.instance_method(:sum) + * #=> false, Array redefines the method for efficiency + */ +#define unbound_method_eq method_eq + +/* + * call-seq: * meth.hash -> integer * * Returns a hash value corresponding to the method object. @@ -1843,10 +1846,11 @@ method_unbind(VALUE obj) TypedData_Get_Struct(obj, struct METHOD, &method_data_type, orig); method = TypedData_Make_Struct(rb_cUnboundMethod, struct METHOD, - &method_data_type, data); + &method_data_type, data); RB_OBJ_WRITE(method, &data->recv, Qundef); - RB_OBJ_WRITE(method, &data->klass, orig->klass); + RB_OBJ_WRITE(method, &data->klass, Qundef); RB_OBJ_WRITE(method, &data->iclass, orig->iclass); + RB_OBJ_WRITE(method, &data->owner, orig->me->owner); RB_OBJ_WRITE(method, &data->me, rb_method_entry_clone(orig->me)); return method; @@ -1912,7 +1916,15 @@ method_original_name(VALUE obj) * call-seq: * meth.owner -> class_or_module * - * Returns the class or module that defines the method. + * Returns the class or module on which this method is defined. + * In other words, + * + * meth.owner.instance_methods(false).include?(meth.name) # => true + * + * holds as long as the method is not removed/undefined/replaced, + * (with private_instance_methods instead of instance_methods if the method + * is private). + * * See also Method#receiver. * * (1..3).method(:map).owner #=> Enumerable @@ -1923,33 +1935,33 @@ method_owner(VALUE obj) { struct METHOD *data; TypedData_Get_Struct(obj, struct METHOD, &method_data_type, data); - return data->me->owner; + return data->owner; } void rb_method_name_error(VALUE klass, VALUE str) { -#define MSG(s) rb_fstring_lit("undefined method `%1$s' for"s" `%2$s'") +#define MSG(s) rb_fstring_lit("undefined method '%1$s' for"s" '%2$s'") VALUE c = klass; VALUE s = Qundef; - if (FL_TEST(c, FL_SINGLETON)) { - VALUE obj = rb_ivar_get(klass, attached); + if (RCLASS_SINGLETON_P(c)) { + VALUE obj = RCLASS_ATTACHED_OBJECT(klass); - switch (BUILTIN_TYPE(obj)) { - case T_MODULE: - case T_CLASS: - c = obj; + switch (BUILTIN_TYPE(obj)) { + case T_MODULE: + case T_CLASS: + c = obj; break; default: - break; - } + break; + } } else if (RB_TYPE_P(c, T_MODULE)) { - s = MSG(" module"); + s = MSG(" module"); } - if (s == Qundef) { - s = MSG(" class"); + if (UNDEF_P(s)) { + s = MSG(" class"); } rb_name_err_raise_str(s, c, str); #undef MSG @@ -1965,7 +1977,7 @@ obj_method(VALUE obj, VALUE vid, int scope) if (!id) { VALUE m = mnew_missing_by_name(klass, obj, &vid, scope, mclass); if (m) return m; - rb_method_name_error(klass, vid); + rb_method_name_error(klass, vid); } return mnew_callable(klass, obj, id, mclass, scope); } @@ -2058,10 +2070,9 @@ rb_obj_singleton_method(VALUE obj, VALUE vid) VALUE klass = rb_singleton_class_get(obj); ID id = rb_check_id(&vid); - if (NIL_P(klass)) { - /* goto undef; */ - } - else if (NIL_P(klass = RCLASS_ORIGIN(klass))) { + if (NIL_P(klass) || + NIL_P(klass = RCLASS_ORIGIN(klass)) || + !NIL_P(rb_special_singleton_class(obj))) { /* goto undef; */ } else if (! id) { @@ -2085,7 +2096,7 @@ rb_obj_singleton_method(VALUE obj, VALUE vid) } /* undef: */ - rb_name_err_raise("undefined singleton method `%1$s' for `%2$s'", + rb_name_err_raise("undefined singleton method '%1$s' for '%2$s'", obj, vid); UNREACHABLE_RETURN(Qundef); } @@ -2126,7 +2137,7 @@ rb_mod_instance_method(VALUE mod, VALUE vid) { ID id = rb_check_id(&vid); if (!id) { - rb_method_name_error(mod, vid); + rb_method_name_error(mod, vid); } return mnew_unbound(mod, id, rb_cUnboundMethod, FALSE); } @@ -2143,11 +2154,79 @@ rb_mod_public_instance_method(VALUE mod, VALUE vid) { ID id = rb_check_id(&vid); if (!id) { - rb_method_name_error(mod, vid); + rb_method_name_error(mod, vid); } return mnew_unbound(mod, id, rb_cUnboundMethod, TRUE); } +static VALUE +rb_mod_define_method_with_visibility(int argc, VALUE *argv, VALUE mod, const struct rb_scope_visi_struct* scope_visi) +{ + ID id; + VALUE body; + VALUE name; + int is_method = FALSE; + + rb_check_arity(argc, 1, 2); + name = argv[0]; + id = rb_check_id(&name); + if (argc == 1) { + body = rb_block_lambda(); + } + else { + body = argv[1]; + + 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/UnboundMethod)", + rb_obj_classname(body)); + } + } + if (!id) id = rb_to_id(name); + + if (is_method) { + struct METHOD *method = (struct METHOD *)RTYPEDDATA_GET_DATA(body); + if (method->me->owner != mod && !RB_TYPE_P(method->me->owner, T_MODULE) && + !RTEST(rb_class_inherited_p(mod, method->me->owner))) { + if (RCLASS_SINGLETON_P(method->me->owner)) { + 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 % "PRIsVALUE, + method->me->owner); + } + } + 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 { + VALUE procval = rb_proc_dup(body); + if (vm_proc_iseq(procval) != NULL) { + rb_proc_t *proc; + GetProcPtr(procval, proc); + proc->is_lambda = TRUE; + proc->is_from_method = TRUE; + } + rb_add_method(mod, id, VM_METHOD_TYPE_BMETHOD, (void *)procval, 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); + } + } + + return ID2SYM(id); +} + /* * call-seq: * define_method(symbol, method) -> symbol @@ -2191,76 +2270,15 @@ rb_mod_public_instance_method(VALUE mod, VALUE vid) static VALUE rb_mod_define_method(int argc, VALUE *argv, VALUE mod) { - ID id; - VALUE body; - 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) { - body = rb_block_lambda(); - } - else { - body = argv[1]; - - 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/UnboundMethod)", - rb_obj_classname(body)); - } - } - if (!id) id = rb_to_id(name); - - if (is_method) { - struct METHOD *method = (struct METHOD *)DATA_PTR(body); - 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 % "PRIsVALUE, - method->me->owner); - } - } - 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 { - VALUE procval = rb_proc_dup(body); - if (vm_proc_iseq(procval) != NULL) { - rb_proc_t *proc; - GetProcPtr(procval, proc); - proc->is_lambda = TRUE; - proc->is_from_method = TRUE; - } - rb_add_method(mod, id, VM_METHOD_TYPE_BMETHOD, (void *)procval, 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); - } + scope_visi = CREF_SCOPE_VISI(cref); } - return ID2SYM(id); + return rb_mod_define_method_with_visibility(argc, argv, mod, scope_visi); } /* @@ -2268,7 +2286,7 @@ rb_mod_define_method(int argc, VALUE *argv, VALUE mod) * define_singleton_method(symbol, method) -> symbol * define_singleton_method(symbol) { block } -> symbol * - * Defines a singleton method in the receiver. The _method_ + * Defines a public singleton 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. * If a block or a method has parameters, they're used as method parameters. @@ -2298,8 +2316,9 @@ static VALUE rb_obj_define_method(int argc, VALUE *argv, VALUE obj) { VALUE klass = rb_singleton_class(obj); + const rb_scope_visibility_t scope_visi = {METHOD_VISI_PUBLIC, FALSE}; - return rb_mod_define_method(argc, argv, klass); + return rb_mod_define_method_with_visibility(argc, argv, klass, &scope_visi); } /* @@ -2312,17 +2331,7 @@ rb_obj_define_method(int argc, VALUE *argv, VALUE obj) 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); + return rb_mod_define_method(argc, argv, rb_top_main_class("define_method")); } /* @@ -2350,10 +2359,29 @@ method_clone(VALUE self) 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); + rb_obj_clone_setup(self, clone, Qnil); RB_OBJ_WRITE(clone, &data->recv, orig->recv); RB_OBJ_WRITE(clone, &data->klass, orig->klass); RB_OBJ_WRITE(clone, &data->iclass, orig->iclass); + RB_OBJ_WRITE(clone, &data->owner, orig->owner); + RB_OBJ_WRITE(clone, &data->me, rb_method_entry_clone(orig->me)); + return clone; +} + +/* :nodoc: */ +static VALUE +method_dup(VALUE self) +{ + VALUE clone; + 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); + rb_obj_dup_setup(self, clone); + RB_OBJ_WRITE(clone, &data->recv, orig->recv); + RB_OBJ_WRITE(clone, &data->klass, orig->klass); + RB_OBJ_WRITE(clone, &data->iclass, orig->iclass); + RB_OBJ_WRITE(clone, &data->owner, orig->owner); RB_OBJ_WRITE(clone, &data->me, rb_method_entry_clone(orig->me)); return clone; } @@ -2404,8 +2432,7 @@ method_clone(VALUE self) static VALUE rb_method_call_pass_called_kw(int argc, const VALUE *argv, VALUE method) { - VALUE procval = rb_block_given_p() ? rb_block_proc() : Qnil; - return rb_method_call_with_block_kw(argc, argv, method, procval, RB_PASS_CALLED_KEYWORDS); + return rb_method_call_kw(argc, argv, method, RB_PASS_CALLED_KEYWORDS); } VALUE @@ -2445,8 +2472,8 @@ rb_method_call_with_block_kw(int argc, const VALUE *argv, VALUE method, VALUE pa rb_execution_context_t *ec = GET_EC(); TypedData_Get_Struct(method, struct METHOD, &method_data_type, data); - if (data->recv == Qundef) { - rb_raise(rb_eTypeError, "can't call unbound method; bind first"); + if (UNDEF_P(data->recv)) { + rb_raise(rb_eTypeError, "can't call unbound method; bind first"); } return call_method_data(ec, data, argc, argv, passed_procval, kw_splat); } @@ -2513,9 +2540,9 @@ rb_method_call_with_block(int argc, const VALUE *argv, VALUE method, VALUE passe */ static void -convert_umethod_to_method_components(const struct METHOD *data, VALUE recv, VALUE *methclass_out, VALUE *klass_out, VALUE *iclass_out, const rb_method_entry_t **me_out) +convert_umethod_to_method_components(const struct METHOD *data, VALUE recv, VALUE *methclass_out, VALUE *klass_out, VALUE *iclass_out, const rb_method_entry_t **me_out, const bool clone) { - VALUE methclass = data->me->owner; + VALUE methclass = data->owner; VALUE iclass = data->me->defined_class; VALUE klass = CLASS_OF(recv); @@ -2523,29 +2550,39 @@ convert_umethod_to_method_components(const struct METHOD *data, VALUE recv, VALU VALUE refined_class = rb_refinement_module_get_refined_class(methclass); if (!NIL_P(refined_class)) methclass = refined_class; } - 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 % "PRIsVALUE, - methclass); - } + if (!RB_TYPE_P(methclass, T_MODULE) && !RTEST(rb_obj_is_kind_of(recv, methclass))) { + if (RCLASS_SINGLETON_P(methclass)) { + rb_raise(rb_eTypeError, + "singleton method called for a different object"); + } + else { + rb_raise(rb_eTypeError, "bind argument must be an instance of % "PRIsVALUE, + methclass); + } } - const rb_method_entry_t *me = rb_method_entry_clone(data->me); + const rb_method_entry_t *me; + if (clone) { + me = rb_method_entry_clone(data->me); + } + else { + me = data->me; + } if (RB_TYPE_P(me->owner, T_MODULE)) { - VALUE ic = rb_class_search_ancestor(klass, me->owner); - if (ic) { - klass = ic; + if (!clone) { + // if we didn't previously clone the method entry, then we need to clone it now + // because this branch manipulates it in rb_method_entry_complement_defined_class + me = rb_method_entry_clone(me); + } + VALUE ic = rb_class_search_ancestor(klass, me->owner); + if (ic) { + klass = ic; iclass = ic; - } - else { - klass = rb_include_class_new(methclass, klass); - } + } + else { + klass = rb_include_class_new(methclass, klass); + } me = (const rb_method_entry_t *) rb_method_entry_complement_defined_class(me, me->called_id, klass); } @@ -2597,13 +2634,14 @@ umethod_bind(VALUE method, VALUE recv) const rb_method_entry_t *me; const struct METHOD *data; TypedData_Get_Struct(method, struct METHOD, &method_data_type, data); - convert_umethod_to_method_components(data, recv, &methclass, &klass, &iclass, &me); + convert_umethod_to_method_components(data, recv, &methclass, &klass, &iclass, &me, true); struct METHOD *bound; method = TypedData_Make_Struct(rb_cMethod, struct METHOD, &method_data_type, bound); RB_OBJ_WRITE(method, &bound->recv, recv); RB_OBJ_WRITE(method, &bound->klass, klass); RB_OBJ_WRITE(method, &bound->iclass, iclass); + RB_OBJ_WRITE(method, &bound->owner, methclass); RB_OBJ_WRITE(method, &bound->me, me); return method; @@ -2639,8 +2677,8 @@ umethod_bind_call(int argc, VALUE *argv, VALUE method) else { VALUE methclass, klass, iclass; const rb_method_entry_t *me; - convert_umethod_to_method_components(data, recv, &methclass, &klass, &iclass, &me); - struct METHOD bound = { recv, klass, 0, me }; + convert_umethod_to_method_components(data, recv, &methclass, &klass, &iclass, &me, false); + struct METHOD bound = { recv, klass, 0, methclass, me }; return call_method_data(ec, &bound, argc, argv, passed_procval, RB_PASS_CALLED_KEYWORDS); } @@ -2652,68 +2690,78 @@ umethod_bind_call(int argc, VALUE *argv, VALUE method) * if there is no maximum. */ static int -rb_method_entry_min_max_arity(const rb_method_entry_t *me, int *max) +method_def_min_max_arity(const rb_method_definition_t *def, int *max) { - const rb_method_definition_t *def = me->def; - again: if (!def) return *max = 0; switch (def->type) { case VM_METHOD_TYPE_CFUNC: - if (def->body.cfunc.argc < 0) { - *max = UNLIMITED_ARGUMENTS; - return 0; - } - return *max = 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: - *max = UNLIMITED_ARGUMENTS; - return 0; + *max = UNLIMITED_ARGUMENTS; + return 0; case VM_METHOD_TYPE_ATTRSET: - return *max = 1; + return *max = 1; case VM_METHOD_TYPE_IVAR: - return *max = 0; + return *max = 0; case VM_METHOD_TYPE_ALIAS: - def = def->body.alias.original_me->def; - goto again; + def = def->body.alias.original_me->def; + goto again; case VM_METHOD_TYPE_BMETHOD: return rb_proc_min_max_arity(def->body.bmethod.proc, max); case VM_METHOD_TYPE_ISEQ: - return rb_iseq_min_max_arity(rb_iseq_check(def->body.iseq.iseqptr), max); + return rb_iseq_min_max_arity(rb_iseq_check(def->body.iseq.iseqptr), max); case VM_METHOD_TYPE_UNDEF: case VM_METHOD_TYPE_NOTIMPLEMENTED: - return *max = 0; + return *max = 0; case VM_METHOD_TYPE_MISSING: - *max = UNLIMITED_ARGUMENTS; - return 0; + *max = UNLIMITED_ARGUMENTS; + return 0; case VM_METHOD_TYPE_OPTIMIZED: { - switch (def->body.optimize_type) { - case OPTIMIZED_METHOD_TYPE_SEND: - *max = UNLIMITED_ARGUMENTS; - return 0; - case OPTIMIZED_METHOD_TYPE_CALL: - *max = UNLIMITED_ARGUMENTS; - return 0; - case OPTIMIZED_METHOD_TYPE_BLOCK_CALL: - *max = UNLIMITED_ARGUMENTS; - return 0; - default: - break; - } - break; + switch (def->body.optimized.type) { + case OPTIMIZED_METHOD_TYPE_SEND: + *max = UNLIMITED_ARGUMENTS; + return 0; + case OPTIMIZED_METHOD_TYPE_CALL: + *max = UNLIMITED_ARGUMENTS; + return 0; + case OPTIMIZED_METHOD_TYPE_BLOCK_CALL: + *max = UNLIMITED_ARGUMENTS; + return 0; + case OPTIMIZED_METHOD_TYPE_STRUCT_AREF: + *max = 0; + return 0; + case OPTIMIZED_METHOD_TYPE_STRUCT_ASET: + *max = 1; + return 1; + default: + break; + } + break; } case VM_METHOD_TYPE_REFINED: - *max = UNLIMITED_ARGUMENTS; - return 0; + *max = UNLIMITED_ARGUMENTS; + return 0; } - rb_bug("rb_method_entry_min_max_arity: invalid method entry type (%d)", def->type); + rb_bug("method_def_min_max_arity: invalid method entry type (%d)", def->type); UNREACHABLE_RETURN(Qnil); } +static int +method_def_arity(const rb_method_definition_t *def) +{ + int max, min = method_def_min_max_arity(def, &max); + return min == max ? min : -min-1; +} + 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; + return method_def_arity(me->def); } /* @@ -2781,10 +2829,10 @@ 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; + 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; } @@ -2795,7 +2843,7 @@ 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); + return method_def_min_max_arity(data->me->def, max); } int @@ -2841,11 +2889,11 @@ 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); + return rb_iseq_check(def->body.iseq.iseqptr); case VM_METHOD_TYPE_BMETHOD: return rb_proc_get_iseq(def->body.bmethod.proc, 0); case VM_METHOD_TYPE_ALIAS: - return method_def_iseq(def->body.alias.original_me->def); + 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: @@ -2855,7 +2903,7 @@ method_def_iseq(const rb_method_definition_t *def) case VM_METHOD_TYPE_OPTIMIZED: case VM_METHOD_TYPE_MISSING: case VM_METHOD_TYPE_REFINED: - break; + break; } return NULL; } @@ -2874,12 +2922,12 @@ method_cref(VALUE method) again: switch (def->type) { case VM_METHOD_TYPE_ISEQ: - return def->body.iseq.cref; + return def->body.iseq.cref; case VM_METHOD_TYPE_ALIAS: - def = def->body.alias.original_me->def; - goto again; + def = def->body.alias.original_me->def; + goto again; default: - return NULL; + return NULL; } } @@ -2887,9 +2935,9 @@ static VALUE method_def_location(const rb_method_definition_t *def) { 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); + if (!def->body.attr.location) + return Qnil; + return rb_ary_dup(def->body.attr.location); } return iseq_location(method_def_iseq(def)); } @@ -2915,6 +2963,69 @@ rb_method_location(VALUE method) return method_def_location(rb_method_def(method)); } +static const rb_method_definition_t * +vm_proc_method_def(VALUE procval) +{ + const rb_proc_t *proc; + const struct rb_block *block; + const struct vm_ifunc *ifunc; + + GetProcPtr(procval, proc); + block = &proc->block; + + if (vm_block_type(block) == block_type_ifunc && + IS_METHOD_PROC_IFUNC(ifunc = block->as.captured.code.ifunc)) { + return rb_method_def((VALUE)ifunc->data); + } + else { + return NULL; + } +} + +static VALUE +method_def_parameters(const rb_method_definition_t *def) +{ + const rb_iseq_t *iseq; + const rb_method_definition_t *bmethod_def; + + switch (def->type) { + case VM_METHOD_TYPE_ISEQ: + iseq = method_def_iseq(def); + return rb_iseq_parameters(iseq, 0); + case VM_METHOD_TYPE_BMETHOD: + if ((iseq = method_def_iseq(def)) != NULL) { + return rb_iseq_parameters(iseq, 0); + } + else if ((bmethod_def = vm_proc_method_def(def->body.bmethod.proc)) != NULL) { + return method_def_parameters(bmethod_def); + } + break; + + case VM_METHOD_TYPE_ALIAS: + return method_def_parameters(def->body.alias.original_me->def); + + case VM_METHOD_TYPE_OPTIMIZED: + if (def->body.optimized.type == OPTIMIZED_METHOD_TYPE_STRUCT_ASET) { + VALUE param = rb_ary_new_from_args(2, ID2SYM(rb_intern("req")), ID2SYM(rb_intern("_"))); + return rb_ary_new_from_args(1, param); + } + break; + + 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_MISSING: + case VM_METHOD_TYPE_REFINED: + break; + } + + return rb_unnamed_parameters(method_def_arity(def)); + +} + /* * call-seq: * meth.parameters -> array @@ -2937,11 +3048,7 @@ rb_method_location(VALUE method) static VALUE rb_method_parameters(VALUE method) { - const rb_iseq_t *iseq = rb_method_iseq(method); - if (!iseq) { - return rb_unnamed_parameters(method_arity(method)); - } - return rb_iseq_parameters(iseq, 0); + return method_def_parameters(rb_method_def(method)); } /* @@ -3001,54 +3108,58 @@ method_inspect(VALUE method) } if (data->me->def->type == VM_METHOD_TYPE_ALIAS) { - defined_class = data->me->def->body.alias.original_me->owner; + defined_class = data->me->def->body.alias.original_me->owner; } else { - defined_class = method_entry_defined_class(data->me); + defined_class = method_entry_defined_class(data->me); } if (RB_TYPE_P(defined_class, T_ICLASS)) { - defined_class = RBASIC_CLASS(defined_class); + defined_class = RBASIC_CLASS(defined_class); + } + + if (UNDEF_P(data->recv)) { + // UnboundMethod + rb_str_buf_append(str, rb_inspect(defined_class)); } + else if (RCLASS_SINGLETON_P(mklass)) { + VALUE v = RCLASS_ATTACHED_OBJECT(mklass); - if (FL_TEST(mklass, FL_SINGLETON)) { - VALUE v = rb_ivar_get(mklass, attached); - - if (data->recv == Qundef) { - rb_str_buf_append(str, rb_inspect(mklass)); - } - 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 = "."; - } + if (UNDEF_P(data->recv)) { + rb_str_buf_append(str, rb_inspect(mklass)); + } + 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 { mklass = data->klass; - if (FL_TEST(mklass, FL_SINGLETON)) { - VALUE v = rb_ivar_get(mklass, attached); + if (RCLASS_SINGLETON_P(mklass)) { + VALUE v = RCLASS_ATTACHED_OBJECT(mklass); if (!(RB_TYPE_P(v, T_CLASS) || RB_TYPE_P(v, T_MODULE))) { do { mklass = RCLASS_SUPER(mklass); } while (RB_TYPE_P(mklass, T_ICLASS)); } } - rb_str_buf_append(str, rb_inspect(mklass)); - if (defined_class != mklass) { - rb_str_catf(str, "(% "PRIsVALUE")", defined_class); - } + rb_str_buf_append(str, rb_inspect(mklass)); + if (defined_class != mklass) { + rb_str_catf(str, "(% "PRIsVALUE")", defined_class); + } } rb_str_buf_cat2(str, sharp); 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)); + 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)"); @@ -3070,6 +3181,16 @@ method_inspect(VALUE method) rb_str_buf_cat2(str, "("); + if (RARRAY_LEN(params) == 3 && + RARRAY_AREF(RARRAY_AREF(params, 0), 0) == rest && + RARRAY_AREF(RARRAY_AREF(params, 0), 1) == ID2SYM('*') && + RARRAY_AREF(RARRAY_AREF(params, 1), 0) == keyrest && + RARRAY_AREF(RARRAY_AREF(params, 1), 1) == ID2SYM(idPow) && + RARRAY_AREF(RARRAY_AREF(params, 2), 0) == block && + RARRAY_AREF(RARRAY_AREF(params, 2), 1) == ID2SYM('&')) { + forwarding = 1; + } + for (int i = 0; i < RARRAY_LEN(params); i++) { pair = RARRAY_AREF(params, i); kind = RARRAY_AREF(pair, 0); @@ -3105,15 +3226,22 @@ method_inspect(VALUE method) } else if (kind == rest) { if (name == ID2SYM('*')) { - forwarding = 1; - rb_str_cat_cstr(str, "..."); + rb_str_cat_cstr(str, forwarding ? "..." : "*"); } else { rb_str_catf(str, "*%"PRIsVALUE, name); } } else if (kind == keyrest) { - rb_str_catf(str, "**%"PRIsVALUE, name); + if (name != ID2SYM(idPow)) { + rb_str_catf(str, "**%"PRIsVALUE, name); + } + else if (i > 0) { + rb_str_set_len(str, RSTRING_LEN(str) - 2); + } + else { + rb_str_cat_cstr(str, "**"); + } } else if (kind == block) { if (name == ID2SYM('&')) { @@ -3153,18 +3281,6 @@ method_inspect(VALUE method) } static VALUE -mproc(VALUE method) -{ - return rb_funcallv(rb_mRubyVMFrozenCore, idProc, 0, 0); -} - -static VALUE -mlambda(VALUE method) -{ - return rb_funcallv(rb_mRubyVMFrozenCore, idLambda, 0, 0); -} - -static VALUE bmcall(RB_BLOCK_CALL_FUNC_ARGLIST(args, method)) { return rb_method_call_with_block_kw(argc, argv, method, blockarg, RB_PASS_CALLED_KEYWORDS); @@ -3175,7 +3291,7 @@ rb_proc_new( rb_block_call_func_t func, VALUE val) { - VALUE procval = rb_iterate(mproc, 0, func, val); + VALUE procval = rb_block_call(rb_mRubyVMFrozenCore, idProc, 0, 0, func, val); return procval; } @@ -3201,7 +3317,7 @@ method_to_proc(VALUE method) * end * end */ - procval = rb_iterate(mlambda, 0, bmcall, method); + procval = rb_block_call(rb_mRubyVMFrozenCore, idLambda, 0, 0, bmcall, method); GetProcPtr(procval, proc); proc->is_from_method = 1; return procval; @@ -3282,13 +3398,19 @@ env_clone(const rb_env_t *env, const rb_cref_t *cref) VM_ASSERT(VM_ENV_ESCAPED_P(env->ep)); if (cref == NULL) { - cref = rb_vm_cref_new_toplevel(); + cref = rb_vm_cref_new_toplevel(); } new_body = ALLOC_N(VALUE, env->env_size); - MEMCPY(new_body, env->env, VALUE, env->env_size); new_ep = &new_body[env->ep - env->env]; new_env = vm_env_new(new_ep, new_body, env->env_size, env->iseq); + + /* The memcpy has to happen after the vm_env_new because it can trigger a + * GC compaction which can move the objects in the env. */ + MEMCPY(new_body, env->env, VALUE, env->env_size); + /* VM_ENV_DATA_INDEX_ENV is set in vm_env_new but will get overwritten + * by the memcpy above. */ + new_ep[VM_ENV_DATA_INDEX_ENV] = (VALUE)new_env; RB_OBJ_WRITE(new_env, &new_ep[VM_ENV_DATA_INDEX_ME_CREF], (VALUE)cref); VM_ASSERT(VM_ENV_ESCAPED_P(new_ep)); return new_env; @@ -3325,31 +3447,31 @@ proc_binding(VALUE self) again: switch (vm_block_type(block)) { case block_type_iseq: - iseq = block->as.captured.code.iseq; - binding_self = block->as.captured.self; - env = VM_ENV_ENVVAL_PTR(block->as.captured.ep); - break; + iseq = block->as.captured.code.iseq; + binding_self = block->as.captured.self; + env = VM_ENV_ENVVAL_PTR(block->as.captured.ep); + break; case block_type_proc: - GetProcPtr(block->as.proc, proc); - block = &proc->block; - goto again; + GetProcPtr(block->as.proc, proc); + block = &proc->block; + goto again; case block_type_ifunc: - { - const struct vm_ifunc *ifunc = block->as.captured.code.ifunc; - if (IS_METHOD_PROC_IFUNC(ifunc)) { - VALUE method = (VALUE)ifunc->data; - VALUE name = rb_fstring_lit("<empty_iseq>"); - rb_iseq_t *empty; - binding_self = method_receiver(method); - iseq = rb_method_iseq(method); - env = VM_ENV_ENVVAL_PTR(block->as.captured.ep); - env = env_clone(env, method_cref(method)); - /* set empty iseq */ - empty = rb_iseq_new(NULL, name, name, Qnil, 0, ISEQ_TYPE_TOP); - RB_OBJ_WRITE(env, &env->iseq, empty); - break; - } - } + { + const struct vm_ifunc *ifunc = block->as.captured.code.ifunc; + if (IS_METHOD_PROC_IFUNC(ifunc)) { + VALUE method = (VALUE)ifunc->data; + VALUE name = rb_fstring_lit("<empty_iseq>"); + rb_iseq_t *empty; + binding_self = method_receiver(method); + iseq = rb_method_iseq(method); + env = VM_ENV_ENVVAL_PTR(block->as.captured.ep); + env = env_clone(env, method_cref(method)); + /* set empty iseq */ + empty = rb_iseq_new(Qnil, name, name, Qnil, 0, ISEQ_TYPE_TOP); + RB_OBJ_WRITE(env, &env->iseq, empty); + break; + } + } /* FALLTHROUGH */ case block_type_symbol: rb_raise(rb_eArgError, "Can't create Binding from C level Proc"); @@ -3364,14 +3486,14 @@ proc_binding(VALUE self) RB_OBJ_WRITTEN(bindval, Qundef, VM_ENV_ENVVAL(env->ep)); if (iseq) { - rb_iseq_check(iseq); - RB_OBJ_WRITE(bindval, &bind->pathobj, iseq->body->location.pathobj); - bind->first_lineno = FIX2INT(rb_iseq_first_lineno(iseq)); + rb_iseq_check(iseq); + RB_OBJ_WRITE(bindval, &bind->pathobj, ISEQ_BODY(iseq)->location.pathobj); + bind->first_lineno = ISEQ_BODY(iseq)->location.first_lineno; } else { - RB_OBJ_WRITE(bindval, &bind->pathobj, - rb_iseq_pathobj_new(rb_fstring_lit("(binding)"), Qnil)); - bind->first_lineno = 1; + RB_OBJ_WRITE(bindval, &bind->pathobj, + rb_iseq_pathobj_new(rb_fstring_lit("(binding)"), Qnil)); + bind->first_lineno = 1; } return bindval; @@ -3409,10 +3531,10 @@ curry(RB_BLOCK_CALL_FUNC_ARGLIST(_, args)) if (RARRAY_LEN(passed) < FIX2INT(arity)) { if (!NIL_P(blockarg)) { - rb_warn("given block not used"); - } - arity = make_curry_proc(proc, passed, arity); - return arity; + rb_warn("given block not used"); + } + arity = make_curry_proc(proc, passed, arity); + return arity; } else { return rb_proc_call_with_block(proc, check_argc(RARRAY_LEN(passed)), RARRAY_CONST_PTR(passed), blockarg); @@ -3431,6 +3553,10 @@ curry(RB_BLOCK_CALL_FUNC_ARGLIST(_, args)) * proc and returns the result. Otherwise, returns another curried proc that * takes the rest of arguments. * + * The optional <i>arity</i> argument should be supplied when currying procs with + * variable arguments to determine how many arguments are needed before the proc is + * called. + * * b = proc {|x, y, z| (x||0) + (y||0) + (z||0) } * p b.curry[1][2][3] #=> 6 * p b.curry[1, 2][3, 4] #=> 6 @@ -3468,13 +3594,13 @@ proc_curry(int argc, const VALUE *argv, VALUE self) VALUE arity; if (rb_check_arity(argc, 0, 1) == 0 || NIL_P(arity = argv[0])) { - arity = INT2FIX(min_arity); + arity = INT2FIX(min_arity); } else { - sarity = FIX2INT(arity); - if (rb_proc_lambda_p(self)) { - rb_check_arity(sarity, min_arity, max_arity); - } + sarity = FIX2INT(arity); + if (rb_proc_lambda_p(self)) { + rb_check_arity(sarity, min_arity, max_arity); + } } return make_curry_proc(self, rb_ary_new(), arity); @@ -3747,10 +3873,10 @@ proc_ruby2_keywords(VALUE procval) switch (proc->block.type) { case block_type_iseq: - if (proc->block.as.captured.code.iseq->body->param.flags.has_rest && - !proc->block.as.captured.code.iseq->body->param.flags.has_kw && - !proc->block.as.captured.code.iseq->body->param.flags.has_kwrest) { - proc->block.as.captured.code.iseq->body->param.flags.ruby2_keywords = 1; + if (ISEQ_BODY(proc->block.as.captured.code.iseq)->param.flags.has_rest && + !ISEQ_BODY(proc->block.as.captured.code.iseq)->param.flags.has_kw && + !ISEQ_BODY(proc->block.as.captured.code.iseq)->param.flags.has_kwrest) { + ISEQ_BODY(proc->block.as.captured.code.iseq)->param.flags.ruby2_keywords = 1; } else { rb_warn("Skipping set of ruby2_keywords flag for proc (proc accepts keywords or proc does not accept argument splat)"); @@ -3861,7 +3987,8 @@ proc_ruby2_keywords(VALUE procval) * * lambda1 = lambda {|x| x**2 } * - * * Use the Lambda literal syntax (also constructs a proc with lambda semantics): + * * Use the {Lambda proc literal}[rdoc-ref:syntax/literals.rdoc@Lambda+Proc+Literals] syntax + * (also constructs a proc with lambda semantics): * * lambda2 = ->(x) { x**2 } * @@ -4109,14 +4236,10 @@ Init_Proc(void) rb_undef_alloc_func(rb_cProc); rb_define_singleton_method(rb_cProc, "new", rb_proc_s_new, -1); - rb_add_method(rb_cProc, idCall, VM_METHOD_TYPE_OPTIMIZED, - (void *)OPTIMIZED_METHOD_TYPE_CALL, METHOD_VISI_PUBLIC); - rb_add_method(rb_cProc, rb_intern("[]"), VM_METHOD_TYPE_OPTIMIZED, - (void *)OPTIMIZED_METHOD_TYPE_CALL, METHOD_VISI_PUBLIC); - rb_add_method(rb_cProc, rb_intern("==="), VM_METHOD_TYPE_OPTIMIZED, - (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, METHOD_VISI_PUBLIC); + rb_add_method_optimized(rb_cProc, idCall, OPTIMIZED_METHOD_TYPE_CALL, 0, METHOD_VISI_PUBLIC); + rb_add_method_optimized(rb_cProc, rb_intern("[]"), OPTIMIZED_METHOD_TYPE_CALL, 0, METHOD_VISI_PUBLIC); + rb_add_method_optimized(rb_cProc, rb_intern("==="), OPTIMIZED_METHOD_TYPE_CALL, 0, METHOD_VISI_PUBLIC); + rb_add_method_optimized(rb_cProc, rb_intern("yield"), OPTIMIZED_METHOD_TYPE_CALL, 0, METHOD_VISI_PUBLIC); #if 0 /* for RDoc */ rb_define_method(rb_cProc, "call", proc_call, -1); @@ -4128,7 +4251,7 @@ Init_Proc(void) 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", rb_proc_dup, 0); + rb_define_method(rb_cProc, "dup", proc_dup, 0); 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"); @@ -4140,7 +4263,7 @@ Init_Proc(void) rb_define_method(rb_cProc, "==", proc_eq, 1); rb_define_method(rb_cProc, "eql?", proc_eq, 1); rb_define_method(rb_cProc, "source_location", rb_proc_location, 0); - rb_define_method(rb_cProc, "parameters", rb_proc_parameters, 0); + rb_define_method(rb_cProc, "parameters", rb_proc_parameters, -1); rb_define_method(rb_cProc, "ruby2_keywords", proc_ruby2_keywords, 0); // rb_define_method(rb_cProc, "isolate", rb_proc_isolate, 0); is not accepted. @@ -4164,6 +4287,7 @@ Init_Proc(void) 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, "dup", method_dup, 0); rb_define_method(rb_cMethod, "call", rb_method_call_pass_called_kw, -1); rb_define_method(rb_cMethod, "===", rb_method_call_pass_called_kw, -1); rb_define_method(rb_cMethod, "curry", rb_method_curry, -1); @@ -4190,10 +4314,11 @@ Init_Proc(void) 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, "==", unbound_method_eq, 1); + rb_define_method(rb_cUnboundMethod, "eql?", unbound_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, "dup", method_dup, 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); @@ -4215,7 +4340,7 @@ Init_Proc(void) 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); + "define_method", top_define_method, -1); } /* |