diff --git a/class.c b/class.c index 243f8c4610..08c0a191e1 100644 --- a/class.c +++ b/class.c @@ -308,11 +308,17 @@ class_init_copy_check(VALUE clone, VALUE orig) rb_raise(rb_eTypeError, "can't copy singleton class"); } } - +#include "gc.h" /* :nodoc: */ VALUE rb_mod_init_copy(VALUE clone, VALUE orig) { + /* cloned flag is refer at constant inline cache + * see vm_get_const_key_cref() in vm_insnhelper.c + */ + FL_SET(clone, RCLASS_CLONED); + FL_SET(orig , RCLASS_CLONED); + if (RB_TYPE_P(clone, T_CLASS)) { class_init_copy_check(clone, orig); } diff --git a/internal.h b/internal.h index bd7162e76c..c502fe076c 100644 --- a/internal.h +++ b/internal.h @@ -1056,6 +1056,7 @@ int rb_singleton_class_internal_p(VALUE sklass); #define RCLASS_REFINED_CLASS(c) (RCLASS_EXT(c)->refined_class) #define RCLASS_SERIAL(c) (RCLASS_EXT(c)->class_serial) +#define RCLASS_CLONED FL_USER6 #define RICLASS_IS_ORIGIN FL_USER5 static inline void diff --git a/test/ruby/test_class.rb b/test/ruby/test_class.rb index 93278a918f..f07a1cdc40 100644 --- a/test/ruby/test_class.rb +++ b/test/ruby/test_class.rb @@ -329,18 +329,22 @@ class TestClass < Test::Unit::TestCase end; end - module M - C = 1 - - def self.m - C - end + class CloneTest + def foo; TEST; end end - def test_constant_access_from_method_in_cloned_module # [ruby-core:47834] - m = M.dup - assert_equal 1, m::C - assert_equal 1, m.m + CloneTest1 = CloneTest.clone + CloneTest2 = CloneTest.clone + class CloneTest1 + TEST = :C1 + end + class CloneTest2 + TEST = :C2 + end + + def test_constant_access_from_method_in_cloned_class + assert_equal :C1, CloneTest1.new.foo, '[Bug #15877]' + assert_equal :C2, CloneTest2.new.foo, '[Bug #15877]' end def test_invalid_superclass diff --git a/test/ruby/test_module.rb b/test/ruby/test_module.rb index cf50db3374..b8d6ffb19d 100644 --- a/test/ruby/test_module.rb +++ b/test/ruby/test_module.rb @@ -2418,6 +2418,39 @@ class TestModule < Test::Unit::TestCase } end + module CloneTestM_simple + C = 1 + def self.m; C; end + end + + module CloneTestM0 + def foo; TEST; end + end + + CloneTestM1 = CloneTestM0.clone + CloneTestM2 = CloneTestM0.clone + module CloneTestM1 + TEST = :M1 + end + module CloneTestM2 + TEST = :M2 + end + class CloneTestC1 + include CloneTestM1 + end + class CloneTestC2 + include CloneTestM2 + end + + def test_constant_access_from_method_in_cloned_module + m = CloneTestM_simple.dup + assert_equal 1, m::C, '[ruby-core:47834]' + assert_equal 1, m.m, '[ruby-core:47834]' + + assert_equal :M1, CloneTestC1.new.foo, '[Bug #15877]' + assert_equal :M2, CloneTestC2.new.foo, '[Bug #15877]' + end + private def assert_top_method_is_private(method) diff --git a/vm_insnhelper.c b/vm_insnhelper.c index 3aea68a370..29133861be 100644 --- a/vm_insnhelper.c +++ b/vm_insnhelper.c @@ -785,8 +785,9 @@ vm_get_const_key_cref(const VALUE *ep) const rb_cref_t *key_cref = cref; while (cref) { - if (FL_TEST(CREF_CLASS(cref), FL_SINGLETON)) { - return key_cref; + if (FL_TEST(CREF_CLASS(cref), FL_SINGLETON) || + FL_TEST(CREF_CLASS(cref), RCLASS_CLONED)) { + return key_cref; } cref = CREF_NEXT(cref); } @@ -3722,7 +3723,8 @@ static int vm_ic_hit_p(IC ic, const VALUE *reg_ep) { if (ic->ic_serial == GET_GLOBAL_CONSTANT_STATE()) { - return (ic->ic_cref == NULL || ic->ic_cref == vm_get_cref(reg_ep)); + return (ic->ic_cref == NULL || // no need to check CREF + ic->ic_cref == vm_get_cref(reg_ep)); } return FALSE; }