ruby-changes:40598
From: ko1 <ko1@a...>
Date: Fri, 20 Nov 2015 09:20:02 +0900 (JST)
Subject: [ruby-changes:40598] ko1:r52677 (trunk): * vm.c (rb_vm_cref_replace_with_duplicated_cref): added.
ko1 2015-11-20 09:17:25 +0900 (Fri, 20 Nov 2015) New Revision: 52677 http://svn.ruby-lang.org/cgi-bin/viewvc.cgi?view=revision&revision=52677 Log: * vm.c (rb_vm_cref_replace_with_duplicated_cref): added. CREFs should not be shared by methods between `using'. [Bug #11247] * vm_insnhelper.c (vm_cref_replace_with_duplicated_cref): ditto. * vm.c (vm_cref_dup): should copy refinements correctly. * eval.c: use rb_vm_cref_replace_with_duplicated_cref(). * eval_intern.h: add a decl. of rb_vm_cref_replace_with_duplicated_cref(). * vm_eval.c (eval_string_with_cref): do not need to pass scope's CREF because VM can find out CREF from stack frames. * test/ruby/test_refinement.rb: add a test. Modified files: trunk/ChangeLog trunk/eval.c trunk/eval_intern.h trunk/test/ruby/test_refinement.rb trunk/vm.c trunk/vm_eval.c trunk/vm_insnhelper.c Index: eval_intern.h =================================================================== --- eval_intern.h (revision 52676) +++ eval_intern.h (revision 52677) @@ -274,6 +274,7 @@ NORETURN(void rb_raise_method_missing(rb https://github.com/ruby/ruby/blob/trunk/eval_intern.h#L274 VALUE rb_vm_make_jump_tag_but_local_jump(int state, VALUE val); rb_cref_t *rb_vm_cref(void); +rb_cref_t *rb_vm_cref_replace_with_duplicated_cref(void); VALUE rb_vm_call_cfunc(VALUE recv, VALUE (*func)(VALUE), VALUE arg, const rb_block_t *blockptr, VALUE filename); void rb_vm_set_progname(VALUE filename); void rb_thread_terminate_all(void); Index: ChangeLog =================================================================== --- ChangeLog (revision 52676) +++ ChangeLog (revision 52677) @@ -1,3 +1,24 @@ https://github.com/ruby/ruby/blob/trunk/ChangeLog#L1 +Fri Nov 20 09:05:21 2015 Koichi Sasada <ko1@a...> + + * vm.c (rb_vm_cref_replace_with_duplicated_cref): added. + + CREFs should not be shared by methods between `using'. + [Bug #11247] + + * vm_insnhelper.c (vm_cref_replace_with_duplicated_cref): ditto. + + * vm.c (vm_cref_dup): should copy refinements correctly. + + * eval.c: use rb_vm_cref_replace_with_duplicated_cref(). + + * eval_intern.h: add a decl. of + rb_vm_cref_replace_with_duplicated_cref(). + + * vm_eval.c (eval_string_with_cref): do not need to pass + scope's CREF because VM can find out CREF from stack frames. + + * test/ruby/test_refinement.rb: add a test. + Fri Nov 20 06:52:53 2015 Eric Wong <e@8...> * .gitattributes: new file for git users Index: vm_eval.c =================================================================== --- vm_eval.c (revision 52676) +++ vm_eval.c (revision 52677) @@ -1271,7 +1271,6 @@ eval_string_with_cref(VALUE self, VALUE https://github.com/ruby/ruby/blob/trunk/vm_eval.c#L1271 rb_block_t block, *base_block; volatile int parse_in_eval; volatile int mild_compile_error; - rb_cref_t *orig_cref; volatile VALUE file; volatile int line; @@ -1337,11 +1336,11 @@ eval_string_with_cref(VALUE self, VALUE https://github.com/ruby/ruby/blob/trunk/vm_eval.c#L1336 if (!cref && base_block->iseq) { if (NIL_P(scope)) { - orig_cref = rb_vm_get_cref(base_block->ep); + rb_cref_t *orig_cref = rb_vm_get_cref(base_block->ep); cref = vm_cref_dup(orig_cref); } else { - cref = rb_vm_get_cref(base_block->ep); + cref = NULL; /* use stacked CREF */ } } vm_set_eval_stack(th, iseq, cref, base_block); Index: eval.c =================================================================== --- eval.c (revision 52676) +++ eval.c (revision 52677) @@ -1284,7 +1284,6 @@ rb_mod_refine(VALUE module, VALUE klass) https://github.com/ruby/ruby/blob/trunk/eval.c#L1284 static VALUE mod_using(VALUE self, VALUE module) { - const rb_cref_t *cref = rb_vm_cref(); rb_control_frame_t *prev_cfp = previous_frame(GET_THREAD()); if (prev_frame_func()) { @@ -1294,7 +1293,7 @@ mod_using(VALUE self, VALUE module) https://github.com/ruby/ruby/blob/trunk/eval.c#L1293 if (prev_cfp && prev_cfp->self != self) { rb_raise(rb_eRuntimeError, "Module#using is not called on self"); } - rb_using_module(cref, module); + rb_using_module(rb_vm_cref_replace_with_duplicated_cref(), module); return self; } @@ -1427,7 +1426,7 @@ top_using(VALUE self, VALUE module) https://github.com/ruby/ruby/blob/trunk/eval.c#L1426 if (CREF_NEXT(cref) || (prev_cfp && rb_vm_frame_method_entry(prev_cfp))) { rb_raise(rb_eRuntimeError, "main.using is permitted only at toplevel"); } - rb_using_module(cref, module); + rb_using_module(rb_vm_cref_replace_with_duplicated_cref(), module); return self; } Index: vm.c =================================================================== --- vm.c (revision 52676) +++ vm.c (revision 52677) @@ -136,10 +136,17 @@ vm_cref_dup(const rb_cref_t *cref) https://github.com/ruby/ruby/blob/trunk/vm.c#L136 { VALUE klass = CREF_CLASS(cref); const rb_scope_visibility_t *visi = CREF_SCOPE_VISI(cref); - rb_cref_t *next_cref = CREF_NEXT(cref); + rb_cref_t *next_cref = CREF_NEXT(cref), *new_cref; int pushed_by_eval = CREF_PUSHED_BY_EVAL(cref); - return vm_cref_new(klass, visi->method_visi, visi->module_func, next_cref, pushed_by_eval); + new_cref = vm_cref_new(klass, visi->method_visi, visi->module_func, next_cref, pushed_by_eval); + + if (!NIL_P(CREF_REFINEMENTS(cref))) { + CREF_REFINEMENTS_SET(new_cref, rb_hash_dup(CREF_REFINEMENTS(cref))); + CREF_OMOD_SHARED_UNSET(new_cref); + } + + return new_cref; } static rb_cref_t * @@ -1192,6 +1199,15 @@ rb_vm_cref(void) https://github.com/ruby/ruby/blob/trunk/vm.c#L1199 return rb_vm_get_cref(cfp->ep); } +rb_cref_t * +rb_vm_cref_replace_with_duplicated_cref(void) +{ + rb_thread_t *th = GET_THREAD(); + rb_control_frame_t *cfp = rb_vm_get_ruby_level_next_cfp(th, th->cfp); + rb_cref_t *cref = vm_cref_replace_with_duplicated_cref(cfp->ep); + return cref; +} + const rb_cref_t * rb_vm_cref_in_context(VALUE self, VALUE cbase) { Index: vm_insnhelper.c =================================================================== --- vm_insnhelper.c (revision 52676) +++ vm_insnhelper.c (revision 52677) @@ -488,6 +488,58 @@ vm_env_cref_by_cref(const VALUE *ep) https://github.com/ruby/ruby/blob/trunk/vm_insnhelper.c#L488 } static rb_cref_t * +cref_replace_with_duplicated_cref_each_frame(VALUE *vptr, int can_be_svar, VALUE parent) +{ + const VALUE v = *vptr; + rb_cref_t *cref, *new_cref; + + if (RB_TYPE_P(v, T_IMEMO)) { + switch (imemo_type(v)) { + case imemo_cref: + cref = (rb_cref_t *)v; + new_cref = vm_cref_dup(cref); + if (parent) { + /* this pointer is in svar */ + RB_OBJ_WRITE(parent, vptr, new_cref); + } + else { + *vptr = (VALUE)new_cref; + } + return (rb_cref_t *)new_cref; + case imemo_svar: + if (can_be_svar) { + return cref_replace_with_duplicated_cref_each_frame((VALUE *)&((struct vm_svar *)v)->cref_or_me, FALSE, v); + } + case imemo_ment: + rb_bug("cref_replace_with_duplicated_cref_each_frame: unreachable"); + default: + break; + } + } + return FALSE; +} + +static rb_cref_t * +vm_cref_replace_with_duplicated_cref(const VALUE *ep) +{ + if (vm_env_cref_by_cref(ep)) { + rb_cref_t *cref; + + while (!VM_EP_LEP_P(ep)) { + if ((cref = cref_replace_with_duplicated_cref_each_frame((VALUE *)&ep[-1], FALSE, Qfalse)) != NULL) { + return cref; + } + ep = VM_EP_PREV_EP(ep); + } + return cref_replace_with_duplicated_cref_each_frame((VALUE *)&ep[-1], TRUE, Qfalse); + } + else { + rb_bug("vm_cref_dup: unreachable"); + } +} + + +static rb_cref_t * rb_vm_get_cref(const VALUE *ep) { rb_cref_t *cref = vm_env_cref(ep); Index: test/ruby/test_refinement.rb =================================================================== --- test/ruby/test_refinement.rb (revision 52676) +++ test/ruby/test_refinement.rb (revision 52677) @@ -1523,6 +1523,77 @@ class TestRefinement < Test::Unit::TestC https://github.com/ruby/ruby/blob/trunk/test/ruby/test_refinement.rb#L1523 end; end + module MixedUsing1 + class C + def foo + :orig_foo + end + end + + module R1 + refine C do + def foo + [:R1, super] + end + end + end + + module_function + + def foo + [:foo, C.new.foo] + end + + using R1 + + def bar + [:bar, C.new.foo] + end + end + + module MixedUsing2 + class C + def foo + :orig_foo + end + end + + module R1 + refine C do + def foo + [:R1_foo, super] + end + end + end + + module R2 + refine C do + def bar + [:R2_bar, C.new.foo] + end + + using R1 + + def baz + [:R2_baz, C.new.foo] + end + end + end + + using R2 + module_function + def f1; C.new.bar; end + def f2; C.new.baz; end + end + + def test_mixed_using + assert_equal([:foo, :orig_foo], MixedUsing1.foo) + assert_equal([:bar, [:R1, :orig_foo]], MixedUsing1.bar) + + assert_equal([:R2_bar, :orig_foo], MixedUsing2.f1) + assert_equal([:R2_baz, [:R1_foo, :orig_foo]], MixedUsing2.f2) + end + private def eval_using(mod, s) -- ML: ruby-changes@q... Info: http://www.atdot.net/~ko1/quickml/