зеркало из https://github.com/github/ruby.git
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:
Родитель
c7acb37248
Коммит
71efad1ed3
8
class.c
8
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);
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
Загрузка…
Ссылка в новой задаче