summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKoichi Sasada <ko1@atdot.net>2019-08-09 11:00:34 +0900
committerKoichi Sasada <ko1@atdot.net>2019-08-09 11:05:11 +0900
commit71efad1ed391ee0c5398a76306fdbaaadd4dc52e (patch)
treed560b4a717ee7d15d23dc4ea02b1708f7d1d71ea
parentc7acb37248d4cef76647f8bc7ebd7dc291d9a853 (diff)
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]
-rw-r--r--class.c8
-rw-r--r--internal.h1
-rw-r--r--test/ruby/test_class.rb22
-rw-r--r--test/ruby/test_module.rb33
-rw-r--r--vm_insnhelper.c8
5 files changed, 59 insertions, 13 deletions
diff --git a/class.c b/class.c
index 243f8c4..08c0a19 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 bd7162e..c502fe0 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 93278a9..f07a1cd 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
+ class CloneTest
+ def foo; TEST; end
+ end
- def self.m
- C
- end
+ CloneTest1 = CloneTest.clone
+ CloneTest2 = CloneTest.clone
+ class CloneTest1
+ TEST = :C1
+ end
+ class CloneTest2
+ TEST = :C2
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
+ 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 cf50db3..b8d6ffb 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 3aea68a..2913386 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;
}