[前][次][番号順一覧][スレッド一覧]

ruby-changes:61471

From: Jeremy <ko1@a...>
Date: Thu, 4 Jun 2020 01:59:25 +0900 (JST)
Subject: [ruby-changes:61471] 98286e9850 (master): Ensure origins for all included, prepended, and refined modules

https://git.ruby-lang.org/ruby.git/commit/?id=98286e9850

From 98286e9850936e27e8ae5e4f20858cc9c13d2dde Mon Sep 17 00:00:00 2001
From: Jeremy Evans <code@j...>
Date: Sat, 23 May 2020 20:16:27 -0700
Subject: Ensure origins for all included, prepended, and refined modules

This fixes various issues when a module is included in or prepended
to a module or class, and then refined, or refined and then included
or prepended to a module or class.

Implement by renaming ensure_origin to rb_ensure_origin, making it
non-static, and calling it when refining a module.

Fix Module#initialize_copy to handle origins correctly.  Previously,
Module#initialize_copy did not handle origins correctly.  For example,
this code:

```ruby
module B; end
class A
  def b; 2 end
  prepend B
end
a = A.dup.new
class A
  def b; 1 end
end
p a.b
```

Printed 1 instead of 2.  This is because the super chain for
a.singleton_class was:

```
a.singleton_class
A.dup
B(iclass)
B(iclass origin)
A(origin) # not A.dup(origin)
```

The B iclasses would not be modified, so the includer entry would be
still be set to A and not A.dup.

This modifies things so that if the class/module has an origin,
all iclasses between the class/module and the origin are duplicated
and have the correct includer entry set, and the correct origin
is created.

This requires other changes to make sure all tests still pass:

* rb_undef_methods_from doesn't automatically handle classes with
  origins, so pass it the origin for Comparable when undefing
  methods in Complex. This fixed a failure in the Complex tests.

* When adding a method, the method cache was not cleared
  correctly if klass has an origin.  Clear the method cache for
  the klass before switching to the origin of klass.  This fixed
  failures in the autoload tests related to overridding require,
  without breaking the optimization tests.  Also clear the method
  cache for both the module and origin when removing a method.

* Module#include? is fixed to skip origin iclasses.

* Refinements are fixed to use the origin class of the module that
  has an origin.

* RCLASS_REFINED_BY_ANY is removed as it was only used in a single
  place and is no longer needed.

* Marshal#dump is fixed to skip iclass origins.

* rb_method_entry_make is fixed to handled overridden optimized
  methods for modules that have origins.

Fixes [Bug #16852]

diff --git a/class.c b/class.c
index 3cc9c59..78667ad 100644
--- a/class.c
+++ b/class.c
@@ -316,26 +316,9 @@ class_init_copy_check(VALUE clone, VALUE orig) https://github.com/ruby/ruby/blob/trunk/class.c#L316
     }
 }
 
-/* :nodoc: */
-VALUE
-rb_mod_init_copy(VALUE clone, VALUE orig)
+static void
+copy_tables(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);
-    }
-    if (!OBJ_INIT_COPY(clone, orig)) return clone;
-    if (!FL_TEST(CLASS_OF(clone), FL_SINGLETON)) {
-	RBASIC_SET_CLASS(clone, rb_singleton_class_clone(orig));
-	rb_singleton_class_attached(RBASIC(clone)->klass, (VALUE)clone);
-    }
-    RCLASS_SET_SUPER(clone, RCLASS_SUPER(orig));
-    RCLASS_EXT(clone)->allocator = RCLASS_EXT(orig)->allocator;
     if (RCLASS_IV_TBL(clone)) {
 	st_free_table(RCLASS_IV_TBL(clone));
 	RCLASS_IV_TBL(clone) = 0;
@@ -363,6 +346,28 @@ rb_mod_init_copy(VALUE clone, VALUE orig) https://github.com/ruby/ruby/blob/trunk/class.c#L346
 	arg.klass = clone;
 	rb_id_table_foreach(RCLASS_CONST_TBL(orig), clone_const_i, &arg);
     }
+}
+
+/* :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);
+    }
+    if (!OBJ_INIT_COPY(clone, orig)) return clone;
+    if (!FL_TEST(CLASS_OF(clone), FL_SINGLETON)) {
+        RBASIC_SET_CLASS(clone, rb_singleton_class_clone(orig));
+        rb_singleton_class_attached(RBASIC(clone)->klass, (VALUE)clone);
+    }
+    RCLASS_EXT(clone)->allocator = RCLASS_EXT(orig)->allocator;
+    copy_tables(clone, orig);
     if (RCLASS_M_TBL(orig)) {
 	struct clone_method_arg arg;
 	arg.old_klass = orig;
@@ -371,6 +376,75 @@ rb_mod_init_copy(VALUE clone, VALUE orig) https://github.com/ruby/ruby/blob/trunk/class.c#L376
 	rb_id_table_foreach(RCLASS_M_TBL(orig), clone_method_i, &arg);
     }
 
+    if (RCLASS_ORIGIN(orig) == orig) {
+        RCLASS_SET_SUPER(clone, RCLASS_SUPER(orig));
+    }
+    else {
+        VALUE p = RCLASS_SUPER(orig);
+        VALUE orig_origin = RCLASS_ORIGIN(orig);
+        VALUE prev_clone_p = clone;
+        VALUE origin_stack = rb_ary_tmp_new(2);
+        VALUE origin[2];
+        VALUE clone_p = 0;
+        long origin_len;
+        int add_subclass;
+        VALUE clone_origin;
+
+        rb_ensure_origin(clone);
+        clone_origin = RCLASS_ORIGIN(clone);
+
+        while (p && p != orig_origin) {
+            if (BUILTIN_TYPE(p) != T_ICLASS) {
+                rb_bug("non iclass between module/class and origin");
+            }
+            clone_p = class_alloc(RBASIC(p)->flags, RBASIC(p)->klass);
+            RCLASS_SET_SUPER(prev_clone_p, clone_p);
+            prev_clone_p = clone_p;
+            RCLASS_M_TBL(clone_p) = RCLASS_M_TBL(p);
+            RCLASS_CONST_TBL(clone_p) = RCLASS_CONST_TBL(p);
+            RCLASS_IV_TBL(clone_p) = RCLASS_IV_TBL(p);
+            RCLASS_EXT(clone_p)->allocator = RCLASS_EXT(p)->allocator;
+            if (RB_TYPE_P(clone, T_CLASS)) {
+                RCLASS_SET_INCLUDER(clone_p, clone);
+            }
+            add_subclass = TRUE;
+            if (p != RCLASS_ORIGIN(p)) {
+                origin[0] = clone_p;
+                origin[1] = RCLASS_ORIGIN(p);
+                rb_ary_cat(origin_stack, origin, 2);
+            }
+            else if ((origin_len = RARRAY_LEN(origin_stack)) > 1 &&
+                     RARRAY_AREF(origin_stack, origin_len - 1) == p) {
+                RCLASS_SET_ORIGIN(RARRAY_AREF(origin_stack, (origin_len -= 2)), clone_p);
+                RICLASS_SET_ORIGIN_SHARED_MTBL(clone_p);
+                rb_ary_resize(origin_stack, origin_len);
+                add_subclass = FALSE;
+            }
+            if (add_subclass) {
+                rb_module_add_to_subclasses_list(RBASIC(p)->klass, clone_p);
+            }
+            p = RCLASS_SUPER(p);
+        }
+
+        if (p == orig_origin) {
+            if (clone_p) {
+                RCLASS_SET_SUPER(clone_p, clone_origin);
+                RCLASS_SET_SUPER(clone_origin, RCLASS_SUPER(orig_origin));
+            }
+            copy_tables(clone_origin, orig_origin);
+            if (RCLASS_M_TBL(orig_origin)) {
+                struct clone_method_arg arg;
+                arg.old_klass = orig;
+                arg.new_klass = clone;
+                RCLASS_M_TBL_INIT(clone_origin);
+                rb_id_table_foreach(RCLASS_M_TBL(orig_origin), clone_method_i, &arg);
+            }
+        }
+        else {
+            rb_bug("no origin for class that has origin");
+        }
+    }
+
     return clone;
 }
 
@@ -912,8 +986,6 @@ add_refined_method_entry_i(ID key, VALUE value, void *data) https://github.com/ruby/ruby/blob/trunk/class.c#L986
     return ID_TABLE_CONTINUE;
 }
 
-static void ensure_origin(VALUE klass);
-
 static enum rb_id_table_iterator_result
 clear_module_cache_i(ID id, VALUE val, void *data)
 {
@@ -931,9 +1003,7 @@ include_modules_at(const VALUE klass, VALUE c, VALUE module, int search_super) https://github.com/ruby/ruby/blob/trunk/class.c#L1003
     struct rb_id_table *const klass_m_tbl = RCLASS_M_TBL(RCLASS_ORIGIN(klass));
     VALUE original_klass = klass;
 
-    if (FL_TEST(module, RCLASS_REFINED_BY_ANY)) {
-        ensure_origin(module);
-    }
+    rb_ensure_origin(module);
 
     while (module) {
 	int superclass_seen = FALSE;
@@ -1046,8 +1116,8 @@ move_refined_method(ID key, VALUE value, void *data) https://github.com/ruby/ruby/blob/trunk/class.c#L1116
     }
 }
 
-static void
-ensure_origin(VALUE klass)
+void
+rb_ensure_origin(VALUE klass)
 {
     VALUE origin = RCLASS_ORIGIN(klass);
     if (origin == klass) {
@@ -1068,7 +1138,7 @@ rb_prepend_module(VALUE klass, VALUE module) https://github.com/ruby/ruby/blob/trunk/class.c#L1138
     int changed = 0;
 
     ensure_includable(klass, module);
-    ensure_origin(klass);
+    rb_ensure_origin(klass);
     changed = include_modules_at(klass, klass, module, FALSE);
     if (changed < 0)
 	rb_raise(rb_eArgError, "cyclic prepend detected");
@@ -1142,7 +1212,7 @@ rb_mod_include_p(VALUE mod, VALUE mod2) https://github.com/ruby/ruby/blob/trunk/class.c#L1212
 
     Check_Type(mod2, T_MODULE);
     for (p = RCLASS_SUPER(mod); p; p = RCLASS_SUPER(p)) {
-	if (BUILTIN_TYPE(p) == T_ICLASS) {
+        if (BUILTIN_TYPE(p) == T_ICLASS && !FL_TEST(p, RICLASS_IS_ORIGIN)) {
 	    if (RBASIC(p)->klass == mod2) return Qtrue;
 	}
     }
diff --git a/complex.c b/complex.c
index 94cd505..3e11867 100644
--- a/complex.c
+++ b/complex.c
@@ -2356,7 +2356,7 @@ Init_Complex(void) https://github.com/ruby/ruby/blob/trunk/complex.c#L2356
 
     rb_define_global_function("Complex", nucomp_f_complex, -1);
 
-    rb_undef_methods_from(rb_cComplex, rb_mComparable);
+    rb_undef_methods_from(rb_cComplex, RCLASS_ORIGIN(rb_mComparable));
     rb_undef_method(rb_cComplex, "%");
     rb_undef_method(rb_cComplex, "div");
     rb_undef_method(rb_cComplex, "divmod");
diff --git a/eval.c b/eval.c
index b95ca1a..c44bf13 100644
--- a/eval.c
+++ b/eval.c
@@ -1382,7 +1382,7 @@ refinement_superclass(VALUE superclass) https://github.com/ruby/ruby/blob/trunk/eval.c#L1382
 {
     if (RB_TYPE_P(superclass, T_MODULE)) {
 	/* FIXME: Should ancestors of superclass be used here? */
-	return rb_include_class_new(superclass, rb_cBasicObject (... truncated)

--
ML: ruby-changes@q...
Info: http://www.atdot.net/~ko1/quickml/

[前][次][番号順一覧][スレッド一覧]