introduce RCLASS_CLONED flag for inline cache.

Methods on duplicated class/module refer same constant inline
cache (IC). Constant access lookup should be done for cloned
class/modules but inline cache doesn't check it.
To check it, this patch introduce new RCLASS_CLONED flag which
are set when if class/module is cloned (both orig and dst).
[Bug #15877]
This commit is contained in:
Koichi Sasada 2019-08-09 11:00:34 +09:00
parent c7acb37248
commit 71efad1ed3
5 changed files with 60 additions and 14 deletions

View File

@ -308,11 +308,17 @@ class_init_copy_check(VALUE clone, VALUE orig)
rb_raise(rb_eTypeError, "can't copy singleton class"); rb_raise(rb_eTypeError, "can't copy singleton class");
} }
} }
#include "gc.h"
/* :nodoc: */ /* :nodoc: */
VALUE VALUE
rb_mod_init_copy(VALUE clone, VALUE orig) 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)) { if (RB_TYPE_P(clone, T_CLASS)) {
class_init_copy_check(clone, orig); class_init_copy_check(clone, orig);
} }

View File

@ -1056,6 +1056,7 @@ int rb_singleton_class_internal_p(VALUE sklass);
#define RCLASS_REFINED_CLASS(c) (RCLASS_EXT(c)->refined_class) #define RCLASS_REFINED_CLASS(c) (RCLASS_EXT(c)->refined_class)
#define RCLASS_SERIAL(c) (RCLASS_EXT(c)->class_serial) #define RCLASS_SERIAL(c) (RCLASS_EXT(c)->class_serial)
#define RCLASS_CLONED FL_USER6
#define RICLASS_IS_ORIGIN FL_USER5 #define RICLASS_IS_ORIGIN FL_USER5
static inline void static inline void

View File

@ -329,18 +329,22 @@ class TestClass < Test::Unit::TestCase
end; end;
end end
module M class CloneTest
C = 1 def foo; TEST; end
def self.m
C
end
end end
def test_constant_access_from_method_in_cloned_module # [ruby-core:47834] CloneTest1 = CloneTest.clone
m = M.dup CloneTest2 = CloneTest.clone
assert_equal 1, m::C class CloneTest1
assert_equal 1, m.m 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 end
def test_invalid_superclass def test_invalid_superclass

View File

@ -2418,6 +2418,39 @@ class TestModule < Test::Unit::TestCase
} }
end 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 private
def assert_top_method_is_private(method) def assert_top_method_is_private(method)

View File

@ -785,8 +785,9 @@ vm_get_const_key_cref(const VALUE *ep)
const rb_cref_t *key_cref = cref; const rb_cref_t *key_cref = cref;
while (cref) { while (cref) {
if (FL_TEST(CREF_CLASS(cref), FL_SINGLETON)) { if (FL_TEST(CREF_CLASS(cref), FL_SINGLETON) ||
return key_cref; FL_TEST(CREF_CLASS(cref), RCLASS_CLONED)) {
return key_cref;
} }
cref = CREF_NEXT(cref); cref = CREF_NEXT(cref);
} }
@ -3722,7 +3723,8 @@ static int
vm_ic_hit_p(IC ic, const VALUE *reg_ep) vm_ic_hit_p(IC ic, const VALUE *reg_ep)
{ {
if (ic->ic_serial == GET_GLOBAL_CONSTANT_STATE()) { 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; return FALSE;
} }