diff options
Diffstat (limited to 'proc.c')
-rw-r--r-- | proc.c | 1182 |
1 files changed, 572 insertions, 610 deletions
@@ -10,11 +10,11 @@ **********************************************************************/ #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" @@ -23,24 +23,18 @@ #include "vm_core.h" #include "yjit.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) - 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 * */ - rb_method_visibility_t visibility; }; VALUE rb_cUnboundMethod; @@ -53,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) +proc_mark_and_move(void *ptr) { rb_proc_t *proc = ptr; - block_compact((struct rb_block *)&proc->block); -} - -static void -proc_mark(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 { @@ -146,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 }; @@ -179,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); } /* @@ -306,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 @@ -334,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 }; @@ -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; + unsigned int i; - VM_ASSERT(rb_obj_is_iseq((VALUE)iseq)); + 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; - } + 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,9 +579,9 @@ 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 @@ -727,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); @@ -770,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(); @@ -784,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; @@ -835,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; @@ -844,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); } /* @@ -857,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"); } /* @@ -905,7 +875,7 @@ f_lambda_warn(void) static VALUE f_lambda(VALUE _) { - f_lambda_warn(); + f_lambda_filter_non_literal(); return rb_block_lambda(); } @@ -972,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; } @@ -1091,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; @@ -1142,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); } } @@ -1169,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); @@ -1180,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; @@ -1204,21 +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); switch (vm_block_type(&block)) { case block_handler_type_symbol: - return -1; + return -1; case block_handler_type_proc: return rb_proc_arity(block_handler); default: min = rb_vm_block_min_max_arity(&block, &max); - return max != UNLIMITED_ARGUMENTS ? min : -min-1; + return max != UNLIMITED_ARGUMENTS ? min : -min-1; } } @@ -1231,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); @@ -1250,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; - } - } + 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); @@ -1364,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(iseq)->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); @@ -1399,11 +1369,11 @@ 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; } @@ -1451,7 +1421,7 @@ rb_proc_parameters(int argc, VALUE *argv, VALUE self) } 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); } @@ -1481,7 +1451,7 @@ rb_hash_proc(st_index_t hash, VALUE prc) * */ -MJIT_FUNC_EXPORTED VALUE +VALUE rb_sym_to_proc(VALUE sym) { static VALUE sym_proc_cache = Qfalse; @@ -1491,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); @@ -1506,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; } } @@ -1538,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(iseq)->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); @@ -1592,40 +1562,25 @@ proc_to_proc(VALUE self) } static void -bm_mark(void *ptr) +bm_mark_and_move(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) -{ - 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 @@ -1640,7 +1595,7 @@ 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, RBOOL(!scope))); } @@ -1656,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; @@ -1664,7 +1620,6 @@ mnew_missing(VALUE klass, VALUE obj, ID id, VALUE mclass) me = rb_method_entry_create(id, klass, METHOD_VISI_UNDEF, def); RB_OBJ_WRITE(method, &data->me, me); - data->visibility = METHOD_ENTRY_VISI(me); return method; } @@ -1680,56 +1635,63 @@ 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); - 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); - } + 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); - data->visibility = visi; return method; } 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); } @@ -1740,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); } @@ -1807,23 +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->visibility != m2->visibility || - m1->klass != m2->klass || - m1->recv != m2->recv) { - return Qfalse; + klass1 != klass2 || + m1->klass != m2->klass || + m1->recv != m2->recv) { + return Qfalse; } return Qtrue; @@ -1831,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. @@ -1869,12 +1846,12 @@ 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)); - data->visibility = orig->visibility; return method; } @@ -1939,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 @@ -1950,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 @@ -1992,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); } @@ -2085,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) { @@ -2112,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); } @@ -2153,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); } @@ -2170,7 +2154,7 @@ 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); } @@ -2187,57 +2171,57 @@ rb_mod_define_method_with_visibility(int argc, VALUE *argv, VALUE mod, const str name = argv[0]; id = rb_check_id(&name); if (argc == 1) { - body = rb_block_lambda(); + 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)); - } + 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); + 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); - } + 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); @@ -2347,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")); } /* @@ -2385,12 +2359,30 @@ 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)); - data->visibility = orig->visibility; return clone; } @@ -2440,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 @@ -2481,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); } @@ -2549,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); @@ -2559,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); } @@ -2633,15 +2634,15 @@ 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); - bound->visibility = data->visibility; return method; } @@ -2676,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, METHOD_ENTRY_VISI(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); } @@ -2695,56 +2696,56 @@ method_def_min_max_arity(const rb_method_definition_t *def, int *max) 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.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; + 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; + default: + break; + } + break; } case VM_METHOD_TYPE_REFINED: - *max = UNLIMITED_ARGUMENTS; - return 0; + *max = UNLIMITED_ARGUMENTS; + return 0; } rb_bug("method_def_min_max_arity: invalid method entry type (%d)", def->type); UNREACHABLE_RETURN(Qnil); @@ -2828,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; } @@ -2888,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: @@ -2902,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; } @@ -2921,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; } } @@ -2934,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)); } @@ -3107,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 (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)) { + // UnboundMethod + rb_str_buf_append(str, rb_inspect(defined_class)); + } + else if (RCLASS_SINGLETON_P(mklass)) { + VALUE v = RCLASS_ATTACHED_OBJECT(mklass); + + 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)"); @@ -3355,51 +3360,6 @@ method_super_method(VALUE method) } /* - * call-seq: - * meth.public? -> true or false - * - * Returns whether the method is public. - */ - -static VALUE -method_public_p(VALUE method) -{ - const struct METHOD *data; - TypedData_Get_Struct(method, struct METHOD, &method_data_type, data); - return RBOOL(data->visibility == METHOD_VISI_PUBLIC); -} - -/* - * call-seq: - * meth.protected? -> true or false - * - * Returns whether the method is protected. - */ - -static VALUE -method_protected_p(VALUE method) -{ - const struct METHOD *data; - TypedData_Get_Struct(method, struct METHOD, &method_data_type, data); - return RBOOL(data->visibility == METHOD_VISI_PROTECTED); -} - -/* - * call-seq: - * meth.private? -> true or false - * - * Returns whether the method is private. - */ - -static VALUE -method_private_p(VALUE method) -{ - const struct METHOD *data; - TypedData_Get_Struct(method, struct METHOD, &method_data_type, data); - return RBOOL(data->visibility == METHOD_VISI_PRIVATE); -} - -/* * call-seq: * local_jump_error.exit_value -> obj * @@ -3438,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; @@ -3481,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(NULL, 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"); @@ -3520,14 +3486,14 @@ proc_binding(VALUE self) RB_OBJ_WRITTEN(bindval, Qundef, VM_ENV_ENVVAL(env->ep)); if (iseq) { - rb_iseq_check(iseq); + rb_iseq_check(iseq); RB_OBJ_WRITE(bindval, &bind->pathobj, ISEQ_BODY(iseq)->location.pathobj); - bind->first_lineno = FIX2INT(rb_iseq_first_lineno(iseq)); + 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; @@ -3565,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); @@ -3628,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); @@ -4285,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"); @@ -4321,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); @@ -4339,9 +4306,6 @@ Init_Proc(void) 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_cMethod, "public?", method_public_p, 0); - rb_define_method(rb_cMethod, "protected?", method_protected_p, 0); - rb_define_method(rb_cMethod, "private?", method_private_p, 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); @@ -4350,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); @@ -4365,9 +4330,6 @@ Init_Proc(void) 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); - rb_define_method(rb_cUnboundMethod, "public?", method_public_p, 0); - rb_define_method(rb_cUnboundMethod, "protected?", method_protected_p, 0); - rb_define_method(rb_cUnboundMethod, "private?", method_private_p, 0); /* Module#*_method */ rb_define_method(rb_cModule, "instance_method", rb_mod_instance_method, 1); @@ -4378,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); } /* |