diff --git a/ChangeLog b/ChangeLog index 737e6222d0..cfcf7a7a00 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,49 @@ +Fri Mar 6 20:18:38 2015 Koichi Sasada + + * fix namespace issue on singleton class expressions. [Bug #10943] + + * vm_core.h, method.h: remove rb_iseq_t::cref_stack. CREF is stored + to rb_method_definition_t::body.iseq_body.cref. + + * vm_insnhelper.c: modify SVAR usage. + When calling ISEQ type method, push CREF information onto method + frame, SVAR located place. Before this fix, SVAR is simply nil. + After this patch, CREF (or NULL == Qfalse for not iseq methods) + is stored at the method invocation. + + When SVAR is requierd, then put NODE_IF onto SVAR location, + and NDOE_IF::nd_reserved points CREF itself. + + * vm.c (vm_cref_new, vm_cref_dump, vm_cref_new_toplevel): added. + + * vm_insnhelper.c (vm_push_frame): accept CREF. + + * method.h, vm_method.c (rb_add_method_iseq): added. This function + accepts iseq and CREF. + + * class.c (clone_method): use rb_add_method_iseq(). + + * gc.c (mark_method_entry): mark method_entry::body.iseq_body.cref. + + * iseq.c: remove CREF related codes. + + * insns.def (getinlinecache/setinlinecache): CREF should be cache key + because a different CREF has a different namespace. + + * node.c (rb_gc_mark_node): mark NODE_IF::nd_reserved for SVAR. + + * proc.c: catch up changes. + + * struct.c: ditto. + + * insns.def: ditto. + + * vm_args.c (raise_argument_error): ditto. + + * vm_eval.c: ditto. + + * test/ruby/test_class.rb: add a test. + Fri Mar 6 18:19:13 2015 Koichi Sasada * test/webrick/test_filehandler.rb: on vboxsf (on VirtualBox diff --git a/class.c b/class.c index 12bac24487..42c27b5f32 100644 --- a/class.c +++ b/class.c @@ -247,11 +247,10 @@ clone_method(VALUE klass, ID mid, const rb_method_entry_t *me) if (me->def && me->def->type == VM_METHOD_TYPE_ISEQ) { rb_iseq_t *iseq; NODE *new_cref; - newiseqval = rb_iseq_clone(me->def->body.iseq->self, klass); + newiseqval = rb_iseq_clone(me->def->body.iseq_body.iseq->self, klass); GetISeqPtr(newiseqval, iseq); - rb_vm_rewrite_cref_stack(me->def->body.iseq->cref_stack, me->klass, klass, &new_cref); - RB_OBJ_WRITE(iseq->self, &iseq->cref_stack, new_cref); - rb_add_method(klass, mid, VM_METHOD_TYPE_ISEQ, iseq, me->flag); + rb_vm_rewrite_cref_stack(me->def->body.iseq_body.cref, me->klass, klass, &new_cref); + rb_add_method_iseq(klass, mid, iseq, new_cref, me->flag); RB_GC_GUARD(newiseqval); } else { diff --git a/gc.c b/gc.c index d69276ee3e..10b74b0cae 100644 --- a/gc.c +++ b/gc.c @@ -3786,11 +3786,14 @@ mark_method_entry(rb_objspace_t *objspace, const rb_method_entry_t *me) const rb_method_definition_t *def = me->def; gc_mark(objspace, me->klass); + again: if (!def) return; + switch (def->type) { case VM_METHOD_TYPE_ISEQ: - gc_mark(objspace, def->body.iseq->self); + gc_mark(objspace, def->body.iseq_body.iseq->self); + gc_mark(objspace, (VALUE)def->body.iseq_body.cref); break; case VM_METHOD_TYPE_BMETHOD: gc_mark(objspace, def->body.proc); diff --git a/insns.def b/insns.def index 778999d7db..761d1fc5eb 100644 --- a/insns.def +++ b/insns.def @@ -159,7 +159,7 @@ getclassvariable () (VALUE val) { - NODE *cref = rb_vm_get_cref(GET_ISEQ(), GET_EP()); + NODE *cref = rb_vm_get_cref(GET_EP()); val = rb_cvar_get(vm_get_cvar_base(cref, GET_CFP()), id); } @@ -174,7 +174,7 @@ setclassvariable (VALUE val) () { - NODE *cref = rb_vm_get_cref(GET_ISEQ(), GET_EP()); + NODE *cref = rb_vm_get_cref(GET_EP()); rb_cvar_set(vm_get_cvar_base(cref, GET_CFP()), id, val); } @@ -196,7 +196,7 @@ getconstant (VALUE klass) (VALUE val) { - val = vm_get_ev_const(th, GET_ISEQ(), klass, id, 0); + val = vm_get_ev_const(th, klass, id, 0); } /** @@ -318,10 +318,10 @@ putspecialobject val = rb_mRubyVMFrozenCore; break; case VM_SPECIAL_OBJECT_CBASE: - val = vm_get_cbase(GET_ISEQ(), GET_EP()); + val = vm_get_cbase(GET_EP()); break; case VM_SPECIAL_OBJECT_CONST_BASE: - val = vm_get_const_base(GET_ISEQ(), GET_EP()); + val = vm_get_const_base(GET_EP()); break; default: rb_bug("putspecialobject insn: unknown value_type"); @@ -730,7 +730,7 @@ defined } break; case DEFINED_IVAR2: - klass = vm_get_cbase(GET_ISEQ(), GET_EP()); + klass = vm_get_cbase(GET_EP()); break; case DEFINED_GVAR: if (rb_gvar_defined(rb_global_entry(SYM2ID(obj)))) { @@ -738,7 +738,7 @@ defined } break; case DEFINED_CVAR: { - NODE *cref = rb_vm_get_cref(GET_ISEQ(), GET_EP()); + NODE *cref = rb_vm_get_cref(GET_EP()); klass = vm_get_cvar_base(cref, GET_CFP()); if (rb_cvar_defined(klass, SYM2ID(obj))) { expr_type = DEFINED_CVAR; @@ -747,7 +747,7 @@ defined } case DEFINED_CONST: klass = v; - if (vm_get_ev_const(th, GET_ISEQ(), klass, SYM2ID(obj), 1)) { + if (vm_get_ev_const(th, klass, SYM2ID(obj), 1)) { expr_type = DEFINED_CONST; } break; @@ -1013,13 +1013,14 @@ defineclass rb_bug("unknown defineclass type: %d", (int)type); } - COPY_CREF(class_iseq->cref_stack, vm_cref_push(th, klass, NOEX_PUBLIC, NULL)); - /* enter scope */ vm_push_frame(th, class_iseq, VM_FRAME_MAGIC_CLASS, - klass, 0, VM_ENVVAL_BLOCK_PTR(GET_BLOCK_PTR()), + klass, 0, + VM_ENVVAL_BLOCK_PTR(GET_BLOCK_PTR()), + vm_cref_push(th, klass, NOEX_PUBLIC, NULL), class_iseq->iseq_encoded, GET_SP(), class_iseq->local_size, 0, class_iseq->stack_max); + RESTORE_REGS(); NEXT_INSN(); } @@ -1241,7 +1242,8 @@ getinlinecache () (VALUE val) { - if (ic->ic_serial == GET_GLOBAL_CONSTANT_STATE()) { + if (ic->ic_serial == GET_GLOBAL_CONSTANT_STATE() && + ic->ic_cref == rb_vm_get_cref(GET_EP())) { val = ic->ic_value.value; JUMP(dst); } @@ -1267,6 +1269,7 @@ setinlinecache } ic->ic_value.value = val; ic->ic_serial = GET_GLOBAL_CONSTANT_STATE() - ruby_vm_const_missing_count; + ic->ic_cref = rb_vm_get_cref(GET_EP()); ruby_vm_const_missing_count = 0; } diff --git a/iseq.c b/iseq.c index de65dcbf33..1eac9309f3 100644 --- a/iseq.c +++ b/iseq.c @@ -121,7 +121,6 @@ iseq_mark(void *ptr) RUBY_MARK_UNLESS_NULL(iseq->location.path); RUBY_MARK_UNLESS_NULL(iseq->location.absolute_path); - RUBY_MARK_UNLESS_NULL((VALUE)iseq->cref_stack); RUBY_MARK_UNLESS_NULL(iseq->klass); RUBY_MARK_UNLESS_NULL(iseq->coverage); RUBY_MARK_UNLESS_NULL(iseq->orig); @@ -213,38 +212,21 @@ iseq_location_setup(rb_iseq_t *iseq, VALUE path, VALUE absolute_path, VALUE name return loc; } -#define ISEQ_SET_CREF(iseq, cref) RB_OBJ_WRITE((iseq)->self, &(iseq)->cref_stack, (cref)) - static void set_relation(rb_iseq_t *iseq, const VALUE parent) { const VALUE type = iseq->type; - rb_thread_t *th = GET_THREAD(); rb_iseq_t *piseq; /* set class nest stack */ if (type == ISEQ_TYPE_TOP) { - /* toplevel is private */ - RB_OBJ_WRITE(iseq->self, &iseq->cref_stack, NEW_CREF(rb_cObject)); - iseq->cref_stack->nd_refinements = Qnil; - iseq->cref_stack->nd_visi = NOEX_PRIVATE; - if (th->top_wrapper) { - NODE *cref = NEW_CREF(th->top_wrapper); - cref->nd_refinements = Qnil; - cref->nd_visi = NOEX_PRIVATE; - RB_OBJ_WRITE(cref, &cref->nd_next, iseq->cref_stack); - ISEQ_SET_CREF(iseq, cref); - } iseq->local_iseq = iseq; } else if (type == ISEQ_TYPE_METHOD || type == ISEQ_TYPE_CLASS) { - ISEQ_SET_CREF(iseq, NEW_CREF(0)); /* place holder */ - iseq->cref_stack->nd_refinements = Qnil; iseq->local_iseq = iseq; } else if (RTEST(parent)) { GetISeqPtr(parent, piseq); - ISEQ_SET_CREF(iseq, piseq->cref_stack); iseq->local_iseq = piseq->local_iseq; } @@ -1973,13 +1955,8 @@ rb_iseq_clone(VALUE iseqval, VALUE newcbase) if (iseq0->local_iseq == iseq0) { iseq1->local_iseq = iseq1; } + if (newcbase) { - ISEQ_SET_CREF(iseq1, NEW_CREF(newcbase)); - RB_OBJ_WRITE(iseq1->cref_stack, &iseq1->cref_stack->nd_refinements, iseq0->cref_stack->nd_refinements); - iseq1->cref_stack->nd_visi = iseq0->cref_stack->nd_visi; - if (iseq0->cref_stack->nd_next) { - RB_OBJ_WRITE(iseq1->cref_stack, &iseq1->cref_stack->nd_next, iseq0->cref_stack->nd_next); - } RB_OBJ_WRITE(iseq1->self, &iseq1->klass, newcbase); } diff --git a/method.h b/method.h index 65070532aa..9df9430946 100644 --- a/method.h +++ b/method.h @@ -12,6 +12,7 @@ #define METHOD_H #include "internal.h" +#include "node.h" #ifndef END_OF_ENUMERATION # if defined(__GNUC__) &&! defined(__STRICT_ANSI__) @@ -79,8 +80,12 @@ typedef struct rb_method_definition_struct { rb_method_type_t type; /* method type */ int alias_count; ID original_id; + union { - rb_iseq_t * const iseq; /* should be mark */ + struct { + rb_iseq_t *const iseq; /* should be mark */ + NODE *cref; + } iseq_body; rb_method_cfunc_t cfunc; rb_method_attr_t attr; const VALUE proc; /* should be mark */ @@ -113,6 +118,7 @@ struct unlinked_method_entry_list_entry { UNDEFINED_METHOD_ENTRY_P((def)->body.orig_me)) void rb_add_method_cfunc(VALUE klass, ID mid, VALUE (*func)(ANYARGS), int argc, rb_method_flag_t noex); +void rb_add_method_iseq(VALUE klass, ID mid, rb_iseq_t *iseq, NODE *cref, rb_method_flag_t noex); rb_method_entry_t *rb_add_method(VALUE klass, ID mid, rb_method_type_t type, void *option, rb_method_flag_t noex); rb_method_entry_t *rb_method_entry(VALUE klass, ID id, VALUE *define_class_ptr); rb_method_entry_t *rb_method_entry_at(VALUE obj, ID id); diff --git a/node.c b/node.c index 32518d3154..8e2dcdf8b7 100644 --- a/node.c +++ b/node.c @@ -947,6 +947,7 @@ rb_gc_mark_node(NODE *obj) { switch (nd_type(obj)) { case NODE_IF: /* 1,2,3 */ + rb_gc_mark(obj->nd_refinements); /* use as SVAR */ case NODE_FOR: case NODE_ITER: case NODE_WHEN: diff --git a/proc.c b/proc.c index 224df0120f..6da2f547c5 100644 --- a/proc.c +++ b/proc.c @@ -1208,7 +1208,6 @@ mnew_internal(rb_method_entry_t *me, VALUE defined_class, VALUE klass, def->type = VM_METHOD_TYPE_MISSING; def->original_id = id; def->alias_count = 0; - } data->ume = ALLOC(struct unlinked_method_entry_list_entry); data->me->def->alias_count++; @@ -2025,7 +2024,7 @@ rb_method_entry_min_max_arity(const rb_method_entry_t *me, int *max) case VM_METHOD_TYPE_BMETHOD: return rb_proc_min_max_arity(def->body.proc, max); case VM_METHOD_TYPE_ISEQ: { - rb_iseq_t *iseq = def->body.iseq; + rb_iseq_t *iseq = def->body.iseq_body.iseq; return rb_iseq_min_max_arity(iseq, max); } case VM_METHOD_TYPE_UNDEF: @@ -2162,12 +2161,24 @@ method_get_iseq(rb_method_definition_t *def) case VM_METHOD_TYPE_BMETHOD: return get_proc_iseq(def->body.proc, 0); case VM_METHOD_TYPE_ISEQ: - return def->body.iseq; + return def->body.iseq_body.iseq; default: - return 0; + return NULL; } } +static NODE * +method_get_cref(rb_method_definition_t *def) +{ + switch (def->type) { + case VM_METHOD_TYPE_ISEQ: + return def->body.iseq_body.cref; + default: + return NULL; + } +} + + rb_iseq_t * rb_method_get_iseq(VALUE method) { @@ -2376,6 +2387,7 @@ method_proc(VALUE method) env->block.self = meth->recv; env->block.klass = meth->defined_class; env->block.iseq = method_get_iseq(meth->me->def); + env->block.ep[-1] = (VALUE)method_get_cref(meth->me->def); return procval; } diff --git a/struct.c b/struct.c index c123318b81..cd427ffef5 100644 --- a/struct.c +++ b/struct.c @@ -179,7 +179,7 @@ define_aref_method(VALUE nstr, VALUE name, VALUE off) VALUE iseqval = rb_method_for_self_aref(name, off, rb_vm_opt_struct_aref); rb_iseq_t *iseq = DATA_PTR(iseqval); - rb_add_method(nstr, SYM2ID(name), VM_METHOD_TYPE_ISEQ, iseq, NOEX_PUBLIC); + rb_add_method_iseq(nstr, SYM2ID(name), iseq, NULL, NOEX_PUBLIC); RB_GC_GUARD(iseqval); } @@ -190,7 +190,7 @@ define_aset_method(VALUE nstr, VALUE name, VALUE off) VALUE iseqval = rb_method_for_self_aset(name, off, rb_vm_opt_struct_aset); rb_iseq_t *iseq = DATA_PTR(iseqval); - rb_add_method(nstr, SYM2ID(name), VM_METHOD_TYPE_ISEQ, iseq, NOEX_PUBLIC); + rb_add_method_iseq(nstr, SYM2ID(name), iseq, NULL, NOEX_PUBLIC); RB_GC_GUARD(iseqval); } diff --git a/test/ruby/test_class.rb b/test/ruby/test_class.rb index e17f56f913..36aeaf2bdf 100644 --- a/test/ruby/test_class.rb +++ b/test/ruby/test_class.rb @@ -399,4 +399,124 @@ class TestClass < Test::Unit::TestCase def c.f; end } end + + def test_singleton_class_should_has_own_namespace + # CONST in singleton class + objs = [] + $i = 0 + + 2.times{ + objs << obj = Object.new + class << obj + CONST = ($i += 1) + def foo + CONST + end + end + } + assert_equal(1, objs[0].foo, '[Bug #10943]') + assert_equal(2, objs[1].foo, '[Bug #10943]') + + # CONST in block in singleton class + objs = [] + $i = 0 + + 2.times{ + objs << obj = Object.new + class << obj + 1.times{ + CONST = ($i += 1) + } + def foo + [nil].map{ + CONST + } + end + end + } + assert_equal([1], objs[0].foo, '[Bug #10943]') + assert_equal([2], objs[1].foo, '[Bug #10943]') + + # class def in singleton class + objs = [] + $xs = [] + $i = 0 + + 2.times{ + objs << obj = Object.new + class << obj + CONST = ($i += 1) + class X + $xs << self + CONST = ($i += 1) + def foo + CONST + end + end + + def x + X + end + end + } + assert_not_equal($xs[0], $xs[1], '[Bug #10943]') + assert_not_equal(objs[0].x, objs[1].x, '[Bug #10943]') + assert_equal(2, $xs[0]::CONST, '[Bug #10943]') + assert_equal(2, $xs[0].new.foo, '[Bug #10943]') + assert_equal(4, $xs[1]::CONST, '[Bug #10943]') + assert_equal(4, $xs[1].new.foo, '[Bug #10943]') + + # class def in block in singleton class + objs = [] + $xs = [] + $i = 0 + + 2.times{ + objs << obj = Object.new + class << obj + 1.times{ + CONST = ($i += 1) + } + 1.times{ + class X + $xs << self + CONST = ($i += 1) + def foo + CONST + end + end + + def x + X + end + } + end + } + assert_not_equal($xs[0], $xs[1], '[Bug #10943]') + assert_not_equal(objs[0].x, objs[1].x, '[Bug #10943]') + assert_equal(2, $xs[0]::CONST, '[Bug #10943]') + assert_equal(2, $xs[0].new.foo, '[Bug #10943]') + assert_equal(4, $xs[1]::CONST, '[Bug #10943]') + assert_equal(4, $xs[1].new.foo, '[Bug #10943]') + + # method def in singleton class + ms = [] + ps = $test_singleton_class_shared_cref_ps = [] + 2.times{ + ms << Module.new do + class << self + $test_singleton_class_shared_cref_ps << Proc.new{ + def xyzzy + self + end + } + end + end + } + + ps.each{|p| p.call} # define xyzzy methods for each singleton classes + ms.each{|m| + assert_equal(m, m.xyzzy, "Bug #10871") + } + end end diff --git a/vm.c b/vm.c index b84ba788c2..cc9acbcb7d 100644 --- a/vm.c +++ b/vm.c @@ -79,6 +79,39 @@ rb_vm_control_frame_block_ptr(const rb_control_frame_t *cfp) return VM_CF_BLOCK_PTR(cfp); } +static NODE * +vm_cref_new(VALUE klass, long visi, NODE *prev_cref) +{ + NODE *cref = NEW_CREF(klass); + cref->nd_refinements = Qnil; + cref->nd_visi = visi; + cref->nd_next = prev_cref; + return cref; +} + +static NODE * +vm_cref_new_toplevel(rb_thread_t *th) +{ + NODE *cref = vm_cref_new(rb_cObject, NOEX_PRIVATE /* toplevel visibility is private */, NULL); + + if (th->top_wrapper) { + cref = vm_cref_new(th->top_wrapper, NOEX_PRIVATE, cref); + } + + return cref; +} + +static void +vm_cref_dump(const char *mesg, const NODE *cref) +{ + fprintf(stderr, "vm_cref_dump: %s (%p)\n", mesg, cref); + + while (cref) { + fprintf(stderr, "= cref| klass: %s\n", RSTRING_PTR(rb_class_path(cref->nd_clss))); + cref = cref->nd_next; + } +} + #if VM_COLLECT_USAGE_DETAILS static void vm_collect_usage_operand(int insn, int n, VALUE op); static void vm_collect_usage_insn(int insn); @@ -200,7 +233,7 @@ vm_stat(int argc, VALUE *argv, VALUE self) /* control stack frame */ static void -vm_set_top_stack(rb_thread_t * th, VALUE iseqval) +vm_set_top_stack(rb_thread_t *th, VALUE iseqval) { rb_iseq_t *iseq; GetISeqPtr(iseqval, iseq); @@ -211,7 +244,9 @@ vm_set_top_stack(rb_thread_t * th, VALUE iseqval) /* for return */ vm_push_frame(th, iseq, VM_FRAME_MAGIC_TOP | VM_FRAME_FLAG_FINISH, - th->top_self, rb_cObject, VM_ENVVAL_BLOCK_PTR(0), + th->top_self, rb_cObject, + VM_ENVVAL_BLOCK_PTR(0), + vm_cref_new_toplevel(th), iseq->iseq_encoded, th->cfp->sp, iseq->local_size, 0, iseq->stack_max); } @@ -223,12 +258,10 @@ vm_set_eval_stack(rb_thread_t * th, VALUE iseqval, const NODE *cref, rb_block_t vm_push_frame(th, iseq, VM_FRAME_MAGIC_EVAL | VM_FRAME_FLAG_FINISH, base_block->self, base_block->klass, - VM_ENVVAL_PREV_EP_PTR(base_block->ep), iseq->iseq_encoded, + VM_ENVVAL_PREV_EP_PTR(base_block->ep), + cref, + iseq->iseq_encoded, th->cfp->sp, iseq->local_size, 0, iseq->stack_max); - - if (cref) { - th->cfp->ep[-1] = (VALUE)cref; - } } static void @@ -790,6 +823,7 @@ invoke_block_from_c(rb_thread_t *th, const rb_block_t *block, vm_push_frame(th, iseq, type | VM_FRAME_FLAG_FINISH | VM_FRAME_FLAG_BMETHOD, self, defined_class, VM_ENVVAL_PREV_EP_PTR(block->ep), + cref, iseq->iseq_encoded + opt_pc, cfp->sp + arg_size, iseq->local_size - arg_size, me, iseq->stack_max); @@ -801,15 +835,12 @@ invoke_block_from_c(rb_thread_t *th, const rb_block_t *block, vm_push_frame(th, iseq, type | VM_FRAME_FLAG_FINISH, self, defined_class, VM_ENVVAL_PREV_EP_PTR(block->ep), + cref, iseq->iseq_encoded + opt_pc, cfp->sp + arg_size, iseq->local_size - arg_size, 0, iseq->stack_max); } - if (cref) { - th->cfp->ep[-1] = (VALUE)cref; - } - ret = vm_exec(th); if (me) { @@ -1017,7 +1048,8 @@ rb_vm_cref(void) if (cfp == 0) { return NULL; } - return rb_vm_get_cref(cfp->iseq, cfp->ep); + + return rb_vm_get_cref(cfp->ep); } const NODE * @@ -1027,7 +1059,7 @@ rb_vm_cref_in_context(VALUE self, VALUE cbase) const rb_control_frame_t *cfp = rb_vm_get_ruby_level_next_cfp(th, th->cfp); const NODE *cref; if (cfp->self != self) return NULL; - cref = rb_vm_get_cref(cfp->iseq, cfp->ep); + cref = rb_vm_get_cref(cfp->ep); if (cref->nd_clss != cbase) return NULL; return cref; } @@ -1053,7 +1085,7 @@ rb_vm_cbase(void) if (cfp == 0) { rb_raise(rb_eRuntimeError, "Can't call on top of Fiber or Thread"); } - return vm_get_cbase(cfp->iseq, cfp->ep); + return vm_get_cbase(cfp->ep); } /* jump */ @@ -1594,6 +1626,7 @@ vm_exec(rb_thread_t *th) vm_push_frame(th, catch_iseq, VM_FRAME_MAGIC_RESCUE, cfp->self, cfp->klass, VM_ENVVAL_PREV_EP_PTR(cfp->ep), + NULL, catch_iseq->iseq_encoded, cfp->sp + 1 /* push value */, catch_iseq->local_size - 1, @@ -1745,7 +1778,8 @@ rb_vm_call_cfunc(VALUE recv, VALUE (*func)(VALUE), VALUE arg, VALUE val; vm_push_frame(th, DATA_PTR(iseqval), VM_FRAME_MAGIC_TOP | VM_FRAME_FLAG_FINISH, - recv, CLASS_OF(recv), VM_ENVVAL_BLOCK_PTR(blockptr), 0, reg_cfp->sp, 1, 0, 0); + recv, CLASS_OF(recv), VM_ENVVAL_BLOCK_PTR(blockptr), vm_cref_new_toplevel(th), + 0, reg_cfp->sp, 1, 0, 0); val = (*func)(arg); @@ -2183,7 +2217,9 @@ th_init(rb_thread_t *th, VALUE self) th->cfp = (void *)(th->stack + th->stack_size); vm_push_frame(th, 0 /* dummy iseq */, VM_FRAME_MAGIC_TOP | VM_FRAME_FLAG_FINISH, - Qnil /* dummy self */, Qnil /* dummy klass */, VM_ENVVAL_BLOCK_PTR(0), 0 /* dummy pc */, th->stack, 1, 0, 0); + Qnil /* dummy self */, Qnil /* dummy klass */, VM_ENVVAL_BLOCK_PTR(0), + NULL /* dummy cref */, + 0 /* dummy pc */, th->stack, 1, 0, 0); th->status = THREAD_RUNNABLE; th->errinfo = Qnil; @@ -2250,15 +2286,13 @@ vm_define_method(rb_thread_t *th, VALUE obj, ID id, VALUE iseqval, } /* dup */ - COPY_CREF(miseq->cref_stack, cref); - miseq->cref_stack->nd_visi = NOEX_PUBLIC; RB_OBJ_WRITE(miseq->self, &miseq->klass, klass); miseq->defined_method_id = id; - rb_add_method(klass, id, VM_METHOD_TYPE_ISEQ, miseq, noex); + rb_add_method_iseq(klass, id, miseq, cref, noex); if (!is_singleton && noex == NOEX_MODFUNC) { klass = rb_singleton_class(klass); - rb_add_method(klass, id, VM_METHOD_TYPE_ISEQ, miseq, NOEX_PUBLIC); + rb_add_method_iseq(klass, id, miseq, cref, NOEX_PUBLIC); } } @@ -2745,6 +2779,8 @@ Init_VM(void) th->cfp->self = th->top_self; th->cfp->klass = Qnil; + th->cfp->ep[-1] = (VALUE)vm_cref_new(rb_cObject, NOEX_PRIVATE, NULL); + /* * The Binding of the top level scope */ diff --git a/vm_args.c b/vm_args.c index cf7256b2d7..e4ade6bb2f 100644 --- a/vm_args.c +++ b/vm_args.c @@ -675,7 +675,7 @@ raise_argument_error(rb_thread_t *th, const rb_iseq_t *iseq, const VALUE exc) VALUE at; if (iseq) { - vm_push_frame(th, iseq, VM_FRAME_MAGIC_METHOD, Qnil /* self */, Qnil /* klass */, Qnil /* specval*/, + vm_push_frame(th, iseq, VM_FRAME_MAGIC_METHOD, Qnil /* self */, Qnil /* klass */, Qnil /* specval*/, NULL /* cref */, iseq->iseq_encoded, th->cfp->sp, 0 /* local_size */, 0 /* me */, 0 /* stack_max */); at = rb_vm_backtrace_object(); vm_pop_frame(th); diff --git a/vm_core.h b/vm_core.h index 1f17dfd56a..5b364129bb 100644 --- a/vm_core.h +++ b/vm_core.h @@ -117,6 +117,7 @@ typedef struct rb_compile_option_struct rb_compile_option_t; struct iseq_inline_cache_entry { rb_serial_t ic_serial; + NODE *ic_cref; union { size_t index; VALUE value; @@ -322,17 +323,6 @@ struct rb_iseq_struct { VALUE self; const VALUE orig; /* non-NULL if its data have origin */ - - /* block inlining */ - /* - * NODE *node; - * void *special_block_builder; - * void *cached_special_block_builder; - * VALUE cached_special_block; - */ - - /* klass/module nest information stack (cref) */ - NODE * const cref_stack; const VALUE klass; /* misc */ @@ -416,7 +406,6 @@ typedef struct rb_vm_struct { /* object management */ VALUE mark_object_ary; - const VALUE special_exceptions[ruby_special_error_count]; /* load */ diff --git a/vm_eval.c b/vm_eval.c index 4039b6c9f2..add2aaf50a 100644 --- a/vm_eval.c +++ b/vm_eval.c @@ -119,7 +119,8 @@ vm_call0_cfunc_with_frame(rb_thread_t* th, rb_call_info_t *ci, const VALUE *argv rb_control_frame_t *reg_cfp = th->cfp; vm_push_frame(th, 0, VM_FRAME_MAGIC_CFUNC, recv, defined_class, - VM_ENVVAL_BLOCK_PTR(blockptr), 0, reg_cfp->sp, 1, me, 0); + VM_ENVVAL_BLOCK_PTR(blockptr), NULL /* cref */, + 0, reg_cfp->sp, 1, me, 0); if (len >= 0) rb_check_arity(argc, len, len); @@ -1306,13 +1307,13 @@ eval_string_with_cref(VALUE self, VALUE src, VALUE scope, NODE *const cref_arg, if (!cref && base_block->iseq) { if (NIL_P(scope)) { - orig_cref = rb_vm_get_cref(base_block->iseq, base_block->ep); + orig_cref = rb_vm_get_cref(base_block->ep); cref = NEW_CREF(Qnil); crefval = (VALUE) cref; COPY_CREF(cref, orig_cref); } else { - cref = rb_vm_get_cref(base_block->iseq, base_block->ep); + cref = rb_vm_get_cref(base_block->ep); } } vm_set_eval_stack(th, iseqval, cref, base_block); diff --git a/vm_insnhelper.c b/vm_insnhelper.c index 815cf4e30b..e4afbdb0fb 100644 --- a/vm_insnhelper.c +++ b/vm_insnhelper.c @@ -46,6 +46,7 @@ vm_push_frame(rb_thread_t *th, VALUE self, VALUE klass, VALUE specval, + const NODE *cref, const VALUE *pc, VALUE *sp, int local_size, @@ -68,7 +69,8 @@ vm_push_frame(rb_thread_t *th, } /* set special val */ - *sp = specval; + sp[-1] = (VALUE)cref; + sp[ 0] = specval; /* setup vm control frame stack */ @@ -161,6 +163,7 @@ lep_svar_get(rb_thread_t *th, const VALUE *lep, rb_num_t key) const NODE * const svar = *svar_place; if (NIL_P((VALUE)svar)) return Qnil; + if (nd_type(svar) == NODE_CREF) return Qnil; switch (key) { case VM_SVAR_LASTLINE: @@ -188,6 +191,12 @@ lep_svar_set(rb_thread_t *th, VALUE *lep, rb_num_t key, VALUE val) if (NIL_P((VALUE)svar)) { svar = *svar_place = NEW_IF(Qnil, Qnil, Qnil); + svar->nd_reserved = Qfalse; + } + else if (nd_type(svar) == NODE_CREF) { + NODE *cref = svar; + svar = *svar_place = NEW_IF(Qnil, Qnil, Qnil); + svar->nd_reserved = (VALUE)cref; } switch (key) { @@ -245,28 +254,42 @@ vm_getspecial(rb_thread_t *th, VALUE *lep, rb_num_t key, rb_num_t type) } static NODE * -vm_get_cref0(const rb_iseq_t *iseq, const VALUE *ep) +ep_cref(const VALUE *ep) { - while (1) { - if (VM_EP_LEP_P(ep)) { - if (!RUBY_VM_NORMAL_ISEQ_P(iseq)) return NULL; - return iseq->cref_stack; - } - else if (ep[-1] != Qnil) { + const VALUE svar = ep[-1]; + + if (!svar) { + return NULL; + } + else if (nd_type(svar) == NODE_CREF) { + return (NODE *)svar; + } + else { + return (NODE *)((NODE *)svar)->nd_reserved; + } +} + +static NODE * +vm_get_cref0(const VALUE *ep) +{ + while (!VM_EP_LEP_P(ep)) { + if (ep[-1]) { return (NODE *)ep[-1]; } ep = VM_EP_PREV_EP(ep); } + return ep_cref(ep); } NODE * -rb_vm_get_cref(const rb_iseq_t *iseq, const VALUE *ep) +rb_vm_get_cref(const VALUE *ep) { - NODE *cref = vm_get_cref0(iseq, ep); + NODE *cref = vm_get_cref0(ep); if (cref == 0) { rb_bug("rb_vm_get_cref: unreachable"); } + return cref; } @@ -274,6 +297,7 @@ void rb_vm_rewrite_cref_stack(NODE *node, VALUE old_klass, VALUE new_klass, NODE **new_cref_ptr) { NODE *new_node; + while (node) { if (node->nd_clss == old_klass) { new_node = NEW_CREF(new_klass); @@ -294,17 +318,21 @@ rb_vm_rewrite_cref_stack(NODE *node, VALUE old_klass, VALUE new_klass, NODE **ne static NODE * vm_cref_push(rb_thread_t *th, VALUE klass, int noex, rb_block_t *blockptr) { - rb_control_frame_t *cfp = vm_get_ruby_level_caller_cfp(th, th->cfp); - NODE *cref = NEW_CREF(klass); - cref->nd_refinements = Qnil; - cref->nd_visi = noex; + NODE *prev_cref = NULL; + NODE *cref = NULL; if (blockptr) { - RB_OBJ_WRITE(cref, &cref->nd_next, vm_get_cref0(blockptr->iseq, blockptr->ep)); + prev_cref = vm_get_cref0(blockptr->ep); } - else if (cfp) { - RB_OBJ_WRITE(cref, &cref->nd_next, vm_get_cref0(cfp->iseq, cfp->ep)); + else { + rb_control_frame_t *cfp = vm_get_ruby_level_caller_cfp(th, th->cfp); + + if (cfp) { + prev_cref = vm_get_cref0(cfp->ep); + } } + cref = vm_cref_new(klass, noex, prev_cref); + /* TODO: why cref->nd_next is 1? */ if (cref->nd_next && cref->nd_next != (void *) 1 && !NIL_P(cref->nd_next->nd_refinements)) { @@ -315,9 +343,9 @@ vm_cref_push(rb_thread_t *th, VALUE klass, int noex, rb_block_t *blockptr) } static inline VALUE -vm_get_cbase(const rb_iseq_t *iseq, const VALUE *ep) +vm_get_cbase(const VALUE *ep) { - NODE *cref = rb_vm_get_cref(iseq, ep); + NODE *cref = rb_vm_get_cref(ep); VALUE klass = Qundef; while (cref) { @@ -331,9 +359,9 @@ vm_get_cbase(const rb_iseq_t *iseq, const VALUE *ep) } static inline VALUE -vm_get_const_base(const rb_iseq_t *iseq, const VALUE *ep) +vm_get_const_base(const VALUE *ep) { - NODE *cref = rb_vm_get_cref(iseq, ep); + NODE *cref = rb_vm_get_cref(ep); VALUE klass = Qundef; while (cref) { @@ -373,14 +401,13 @@ vm_get_iclass(rb_control_frame_t *cfp, VALUE klass) } static inline VALUE -vm_get_ev_const(rb_thread_t *th, const rb_iseq_t *iseq, - VALUE orig_klass, ID id, int is_defined) +vm_get_ev_const(rb_thread_t *th, VALUE orig_klass, ID id, int is_defined) { VALUE val; if (orig_klass == Qnil) { /* in current lexical scope */ - const NODE *root_cref = rb_vm_get_cref(iseq, th->cfp->ep); + const NODE *root_cref = rb_vm_get_cref(th->cfp->ep); const NODE *cref; VALUE klass = orig_klass; @@ -1128,7 +1155,7 @@ vm_callee_setup_arg(rb_thread_t *th, rb_call_info_t *ci, const rb_iseq_t *iseq, static VALUE vm_call_iseq_setup(rb_thread_t *th, rb_control_frame_t *cfp, rb_call_info_t *ci) { - vm_callee_setup_arg(th, ci, ci->me->def->body.iseq, cfp->sp - ci->argc); + vm_callee_setup_arg(th, ci, ci->me->def->body.iseq_body.iseq, cfp->sp - ci->argc); return vm_call_iseq_setup_2(th, cfp, ci); } @@ -1148,7 +1175,7 @@ vm_call_iseq_setup_normal(rb_thread_t *th, rb_control_frame_t *cfp, rb_call_info { int i, local_size; VALUE *argv = cfp->sp - ci->argc; - rb_iseq_t *iseq = ci->me->def->body.iseq; + rb_iseq_t *iseq = ci->me->def->body.iseq_body.iseq; VALUE *sp = argv + iseq->param.size; /* clear local variables (arg_size...local_size) */ @@ -1157,7 +1184,7 @@ vm_call_iseq_setup_normal(rb_thread_t *th, rb_control_frame_t *cfp, rb_call_info } vm_push_frame(th, iseq, VM_FRAME_MAGIC_METHOD, ci->recv, ci->defined_class, - VM_ENVVAL_BLOCK_PTR(ci->blockptr), + VM_ENVVAL_BLOCK_PTR(ci->blockptr), ci->me->def->body.iseq_body.cref, iseq->iseq_encoded + ci->aux.opt_pc, sp, 0, ci->me, iseq->stack_max); cfp->sp = argv - 1 /* recv */; @@ -1169,7 +1196,7 @@ vm_call_iseq_setup_tailcall(rb_thread_t *th, rb_control_frame_t *cfp, rb_call_in { int i; VALUE *argv = cfp->sp - ci->argc; - rb_iseq_t *iseq = ci->me->def->body.iseq; + rb_iseq_t *iseq = ci->me->def->body.iseq_body.iseq; VALUE *src_argv = argv; VALUE *sp_orig, *sp; VALUE finish_flag = VM_FRAME_TYPE_FINISH_P(cfp) ? VM_FRAME_FLAG_FINISH : 0; @@ -1195,7 +1222,8 @@ vm_call_iseq_setup_tailcall(rb_thread_t *th, rb_control_frame_t *cfp, rb_call_in } vm_push_frame(th, iseq, VM_FRAME_MAGIC_METHOD | finish_flag, - ci->recv, ci->defined_class, VM_ENVVAL_BLOCK_PTR(ci->blockptr), + ci->recv, ci->defined_class, + VM_ENVVAL_BLOCK_PTR(ci->blockptr), ci->me->def->body.iseq_body.cref, iseq->iseq_encoded + ci->aux.opt_pc, sp, 0, ci->me, iseq->stack_max); cfp->sp = sp_orig; @@ -1377,7 +1405,8 @@ vm_call_cfunc_with_frame(rb_thread_t *th, rb_control_frame_t *reg_cfp, rb_call_i EXEC_EVENT_HOOK(th, RUBY_EVENT_C_CALL, recv, me->called_id, me->klass, Qundef); vm_push_frame(th, 0, VM_FRAME_MAGIC_CFUNC, recv, defined_class, - VM_ENVVAL_BLOCK_PTR(blockptr), 0, th->cfp->sp, 1, me, 0); + VM_ENVVAL_BLOCK_PTR(blockptr), NULL /* cref */, + 0, th->cfp->sp, 1, me, 0); if (len >= 0) rb_check_arity(argc, len, len); @@ -1465,7 +1494,8 @@ vm_call_cfunc_push_frame(rb_thread_t *th) th->passed_ci = 0; vm_push_frame(th, 0, VM_FRAME_MAGIC_CFUNC, ci->recv, ci->defined_class, - VM_ENVVAL_BLOCK_PTR(ci->blockptr), 0, th->cfp->sp + ci->aux.inc_sp, 1, me); + VM_ENVVAL_BLOCK_PTR(ci->blockptr), NULL /* cref */, + 0, th->cfp->sp + ci->aux.inc_sp, 1, me); if (ci->call != vm_call_general) { ci->call = vm_call_cfunc_with_frame; @@ -1754,7 +1784,7 @@ vm_call_method(rb_thread_t *th, rb_control_frame_t *cfp, rb_call_info_t *ci) case VM_METHOD_TYPE_UNDEF: break; case VM_METHOD_TYPE_REFINED:{ - NODE *cref = rb_vm_get_cref(cfp->iseq, cfp->ep); + NODE *cref = rb_vm_get_cref(cfp->ep); VALUE refinements = cref ? cref->nd_refinements : Qnil; VALUE refinement, defined_class; rb_method_entry_t *me; @@ -1983,7 +2013,7 @@ vm_search_super_method(rb_thread_t *th, rb_control_frame_t *reg_cfp, rb_call_inf iseq = iseq->parent_iseq; } - if (ci->me && ci->me->def->type == VM_METHOD_TYPE_ISEQ && ci->me->def->body.iseq == iseq) { + if (ci->me && ci->me->def->type == VM_METHOD_TYPE_ISEQ && ci->me->def->body.iseq_body.iseq == iseq) { ci->klass = RCLASS_SUPER(ci->defined_class); ci->me = rb_method_entry(ci->klass, ci->mid, &ci->defined_class); } @@ -2039,8 +2069,8 @@ vm_yield_with_cfunc(rb_thread_t *th, const rb_block_t *block, vm_push_frame(th, (rb_iseq_t *)ifunc, VM_FRAME_MAGIC_IFUNC, self, defined_class, - VM_ENVVAL_PREV_EP_PTR(block->ep), 0, - th->cfp->sp, 1, th->passed_bmethod_me, 0); + VM_ENVVAL_PREV_EP_PTR(block->ep), NULL /* cref */, + 0, th->cfp->sp, 1, th->passed_bmethod_me, 0); val = (*ifunc->nd_cfnc) (arg, ifunc->nd_tval, argc, argv, blockarg); @@ -2095,7 +2125,7 @@ vm_invoke_block(rb_thread_t *th, rb_control_frame_t *reg_cfp, rb_call_info_t *ci is_lambda ? VM_FRAME_MAGIC_LAMBDA : VM_FRAME_MAGIC_BLOCK, block->self, block->klass, - VM_ENVVAL_PREV_EP_PTR(block->ep), + VM_ENVVAL_PREV_EP_PTR(block->ep), NULL /* cref */, iseq->iseq_encoded + opt_pc, rsp + arg_size, iseq->local_size - arg_size, 0, iseq->stack_max); diff --git a/vm_method.c b/vm_method.c index 7dd2f4bff7..d5fe52404a 100644 --- a/vm_method.c +++ b/vm_method.c @@ -245,8 +245,7 @@ rb_add_refined_method_entry(VALUE refined_class, ID mid) rb_clear_method_cache_by_class(refined_class); } else { - rb_add_method(refined_class, mid, VM_METHOD_TYPE_REFINED, 0, - NOEX_PUBLIC); + rb_add_method(refined_class, mid, VM_METHOD_TYPE_REFINED, 0, NOEX_PUBLIC); } } @@ -325,7 +324,7 @@ rb_method_entry_make(VALUE klass, ID mid, rb_method_type_t type, rb_warning("method redefined; discarding old %"PRIsVALUE, rb_id2str(mid)); switch (old_def->type) { case VM_METHOD_TYPE_ISEQ: - iseq = old_def->body.iseq; + iseq = old_def->body.iseq_body.iseq; break; case VM_METHOD_TYPE_BMETHOD: iseq = rb_proc_get_iseq(old_def->body.proc, 0); @@ -359,7 +358,8 @@ rb_method_entry_make(VALUE klass, ID mid, rb_method_type_t type, switch(def->type) { case VM_METHOD_TYPE_ISEQ: - RB_OBJ_WRITTEN(klass, Qundef, def->body.iseq->self); + RB_OBJ_WRITTEN(klass, Qundef, def->body.iseq_body.iseq->self); + RB_OBJ_WRITTEN(klass, Qundef, def->body.iseq_body.cref); break; case VM_METHOD_TYPE_IVAR: RB_OBJ_WRITTEN(klass, Qundef, def->body.attr.location); @@ -447,27 +447,40 @@ setup_method_cfunc_struct(rb_method_cfunc_t *cfunc, VALUE (*func)(), int argc) } rb_method_entry_t * -rb_add_method(VALUE klass, ID mid, rb_method_type_t type, void *opts, rb_method_flag_t noex) +rb_add_method0(VALUE klass, ID mid, rb_method_type_t type, void *opts, rb_method_flag_t noex, NODE *cref) { rb_thread_t *th; rb_control_frame_t *cfp; int line; rb_method_entry_t *me = rb_method_entry_make(klass, mid, type, 0, noex, klass); rb_method_definition_t *def = ALLOC(rb_method_definition_t); + if (me->def && me->def->type == VM_METHOD_TYPE_REFINED) { me->def->body.orig_me->def = def; } else { me->def = def; } + + if (0 && cref) vm_cref_dump("rb_add_method0", cref); + def->type = type; def->original_id = mid; def->alias_count = 0; + switch (type) { case VM_METHOD_TYPE_ISEQ: { rb_iseq_t *iseq = (rb_iseq_t *)opts; - *(rb_iseq_t **)&def->body.iseq = iseq; - RB_OBJ_WRITTEN(klass, Qundef, iseq->self); + NODE *private_cref; + + *(rb_iseq_t **)&def->body.iseq_body.iseq = iseq; + RB_OBJ_WRITTEN(klass, Qundef, iseq->self); /* should be set iseq before newobj */ + def->body.iseq_body.cref = NULL; + + private_cref = vm_cref_new_toplevel(GET_THREAD()); /* TODO: CREF should be shared with other methods */ + if (cref) COPY_CREF(private_cref, cref); + private_cref->nd_visi = NOEX_PUBLIC; + RB_OBJ_WRITE(klass, &def->body.iseq_body.cref, private_cref); break; } case VM_METHOD_TYPE_CFUNC: @@ -511,13 +524,24 @@ rb_add_method(VALUE klass, ID mid, rb_method_type_t type, void *opts, rb_method_ return me; } +rb_method_entry_t * +rb_add_method(VALUE klass, ID mid, rb_method_type_t type, void *opts, rb_method_flag_t noex) +{ + return rb_add_method0(klass, mid, type, opts, noex, NULL); +} + +void +rb_add_method_iseq(VALUE klass, ID mid, rb_iseq_t *iseq, NODE *cref, rb_method_flag_t noex) +{ + rb_add_method0(klass, mid, VM_METHOD_TYPE_ISEQ, iseq, noex, cref); +} + static rb_method_entry_t * method_entry_set(VALUE klass, ID mid, const rb_method_entry_t *me, rb_method_flag_t noex, VALUE defined_class) { rb_method_type_t type = me->def ? me->def->type : VM_METHOD_TYPE_UNDEF; - rb_method_entry_t *newme = rb_method_entry_make(klass, mid, type, me->def, noex, - defined_class); + rb_method_entry_t *newme = rb_method_entry_make(klass, mid, type, me->def, noex, defined_class); method_added(klass, mid); return newme; } @@ -1195,7 +1219,7 @@ rb_method_definition_eq(const rb_method_definition_t *d1, const rb_method_defini } switch (d1->type) { case VM_METHOD_TYPE_ISEQ: - return d1->body.iseq == d2->body.iseq; + return d1->body.iseq_body.iseq == d2->body.iseq_body.iseq; case VM_METHOD_TYPE_CFUNC: return d1->body.cfunc.func == d2->body.cfunc.func && @@ -1226,7 +1250,7 @@ rb_hash_method_definition(st_index_t hash, const rb_method_definition_t *def) hash = rb_hash_uint(hash, def->type); switch (def->type) { case VM_METHOD_TYPE_ISEQ: - return rb_hash_uint(hash, (st_index_t)def->body.iseq); + return rb_hash_uint(hash, (st_index_t)def->body.iseq_body.iseq); case VM_METHOD_TYPE_CFUNC: hash = rb_hash_uint(hash, (st_index_t)def->body.cfunc.func); return rb_hash_uint(hash, def->body.cfunc.argc);