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

ruby-changes:30743

From: charliesome <ko1@a...>
Date: Wed, 4 Sep 2013 14:25:20 +0900 (JST)
Subject: [ruby-changes:30743] charliesome:r42822 (trunk): * class.c, compile.c, eval.c, gc.h, insns.def, internal.h, method.h,

charliesome	2013-09-04 14:25:06 +0900 (Wed, 04 Sep 2013)

  New Revision: 42822

  http://svn.ruby-lang.org/cgi-bin/viewvc.cgi?view=rev&revision=42822

  Log:
    * class.c, compile.c, eval.c, gc.h, insns.def, internal.h, method.h,
      variable.c, vm.c, vm_core.c, vm_insnhelper.c, vm_insnhelper.h,
      vm_method.c: Implement class hierarchy method cache invalidation.
    
      [ruby-core:55053] [Feature #8426] [GH-387]

  Modified files:
    trunk/ChangeLog
    trunk/class.c
    trunk/compile.c
    trunk/eval.c
    trunk/gc.c
    trunk/insns.def
    trunk/internal.h
    trunk/method.h
    trunk/variable.c
    trunk/vm.c
    trunk/vm_core.h
    trunk/vm_insnhelper.c
    trunk/vm_insnhelper.h
    trunk/vm_method.c
Index: method.h
===================================================================
--- method.h	(revision 42821)
+++ method.h	(revision 42822)
@@ -11,6 +11,8 @@ https://github.com/ruby/ruby/blob/trunk/method.h#L11
 #ifndef METHOD_H
 #define METHOD_H
 
+#include "internal.h"
+
 #ifndef END_OF_ENUMERATION
 # ifdef __GNUC__
 #   define END_OF_ENUMERATION(key)
@@ -120,7 +122,7 @@ rb_method_entry_t *rb_method_entry_with_ https://github.com/ruby/ruby/blob/trunk/method.h#L122
 rb_method_entry_t *rb_method_entry_without_refinements(VALUE klass, ID id,
 						       VALUE *defined_class_ptr);
 
-rb_method_entry_t *rb_method_entry_get_without_cache(VALUE klass, ID id, VALUE *define_class_ptr);
+rb_method_entry_t *rb_method_entry_get_without_cache(VALUE klass, ID id, VALUE *define_class_ptr, method_cache_entry_t *ent);
 rb_method_entry_t *rb_method_entry_set(VALUE klass, ID mid, const rb_method_entry_t *, rb_method_flag_t noex);
 
 int rb_method_entry_arity(const rb_method_entry_t *me);
Index: ChangeLog
===================================================================
--- ChangeLog	(revision 42821)
+++ ChangeLog	(revision 42822)
@@ -1,3 +1,11 @@ https://github.com/ruby/ruby/blob/trunk/ChangeLog#L1
+Wed Sep  4 14:08:00 2013  Charlie Somerville  <charliesome@r...>
+
+	* class.c, compile.c, eval.c, gc.h, insns.def, internal.h, method.h,
+	  variable.c, vm.c, vm_core.c, vm_insnhelper.c, vm_insnhelper.h,
+	  vm_method.c: Implement class hierarchy method cache invalidation.
+
+	  [ruby-core:55053] [Feature #8426] [GH-387]
+
 Wed Sep  4 11:13:40 2013  Nobuyoshi Nakada  <nobu@r...>
 
 	* string.c (str_gsub): use BEG(0) for whole matched position not
Index: insns.def
===================================================================
--- insns.def	(revision 42821)
+++ insns.def	(revision 42822)
@@ -218,7 +218,7 @@ setconstant https://github.com/ruby/ruby/blob/trunk/insns.def#L218
 {
     vm_check_if_namespace(cbase);
     rb_const_set(cbase, id, val);
-    INC_VM_STATE_VERSION();
+    rb_clear_cache_by_class(cbase);
 }
 
 /**
@@ -975,7 +975,7 @@ defineclass https://github.com/ruby/ruby/blob/trunk/insns.def#L975
 		  class_iseq->local_size, 0, class_iseq->stack_max);
     RESTORE_REGS();
 
-    INC_VM_STATE_VERSION();
+    rb_clear_cache_by_class(klass);
     NEXT_INSN();
 }
 
Index: variable.c
===================================================================
--- variable.c	(revision 42821)
+++ variable.c	(revision 42822)
@@ -1939,7 +1939,7 @@ rb_const_remove(VALUE mod, ID id) https://github.com/ruby/ruby/blob/trunk/variable.c#L1939
 		      rb_class_name(mod), QUOTE_ID(id));
     }
 
-    rb_vm_change_state();
+    rb_clear_cache();
 
     val = ((rb_const_entry_t*)v)->value;
     if (val == Qundef) {
@@ -2149,7 +2149,8 @@ rb_const_set(VALUE klass, ID id, VALUE v https://github.com/ruby/ruby/blob/trunk/variable.c#L2149
 		load = autoload_data(klass, id);
 		/* for autoloading thread, keep the defined value to autoloading storage */
 		if (load && (ele = check_autoload_data(load)) && (ele->thread == rb_thread_current())) {
-		    rb_vm_change_state();
+		    rb_clear_cache();
+
 		    ele->value = val; /* autoload_i is shady */
 		    return;
 		}
@@ -2172,7 +2173,8 @@ rb_const_set(VALUE klass, ID id, VALUE v https://github.com/ruby/ruby/blob/trunk/variable.c#L2173
 	}
     }
 
-    rb_vm_change_state();
+    rb_clear_cache();
+
 
     ce = ALLOC(rb_const_entry_t);
     MEMZERO(ce, rb_const_entry_t, 1);
@@ -2217,8 +2219,10 @@ set_const_visibility(VALUE mod, int argc https://github.com/ruby/ruby/blob/trunk/variable.c#L2219
 	VALUE val = argv[i];
 	id = rb_check_id(&val);
 	if (!id) {
-	    if (i > 0)
-		rb_clear_cache_by_class(mod);
+	    if (i > 0) {
+		rb_clear_cache();
+	    }
+
 	    rb_name_error_str(val, "constant %"PRIsVALUE"::%"PRIsVALUE" not defined",
 			      rb_class_name(mod), QUOTE(val));
 	}
@@ -2227,13 +2231,14 @@ set_const_visibility(VALUE mod, int argc https://github.com/ruby/ruby/blob/trunk/variable.c#L2231
 	    ((rb_const_entry_t*)v)->flag = flag;
 	}
 	else {
-	    if (i > 0)
-		rb_clear_cache_by_class(mod);
+	    if (i > 0) {
+		rb_clear_cache();
+	    }
 	    rb_name_error(id, "constant %"PRIsVALUE"::%"PRIsVALUE" not defined",
 			  rb_class_name(mod), QUOTE_ID(id));
 	}
     }
-    rb_clear_cache_by_class(mod);
+    rb_clear_cache();
 }
 
 /*
Index: vm_core.h
===================================================================
--- vm_core.h	(revision 42821)
+++ vm_core.h	(revision 42822)
@@ -127,7 +127,8 @@ typedef struct rb_compile_option_struct https://github.com/ruby/ruby/blob/trunk/vm_core.h#L127
 
 
 struct iseq_inline_cache_entry {
-    VALUE ic_vmstat;
+    vm_state_version_t ic_vmstat;
+    vm_state_version_t ic_seq;
     VALUE ic_class;
     union {
 	size_t index;
@@ -157,7 +158,8 @@ typedef struct rb_call_info_struct { https://github.com/ruby/ruby/blob/trunk/vm_core.h#L158
     rb_iseq_t *blockiseq;
 
     /* inline cache: keys */
-    VALUE vmstat;
+    vm_state_version_t vmstat;
+    vm_state_version_t seq;
     VALUE klass;
 
     /* inline cache: values */
Index: compile.c
===================================================================
--- compile.c	(revision 42821)
+++ compile.c	(revision 42822)
@@ -962,6 +962,7 @@ new_callinfo(rb_iseq_t *iseq, ID mid, in https://github.com/ruby/ruby/blob/trunk/compile.c#L962
 	}
     }
     ci->vmstat = 0;
+    ci->seq = 0;
     ci->blockptr = 0;
     ci->recv = Qundef;
     ci->call = 0; /* TODO: should set default function? */
Index: vm_method.c
===================================================================
--- vm_method.c	(revision 42821)
+++ vm_method.c	(revision 42822)
@@ -2,9 +2,7 @@ https://github.com/ruby/ruby/blob/trunk/vm_method.c#L2
  * This file is included by vm.c
  */
 
-#define CACHE_SIZE 0x800
-#define CACHE_MASK 0x7ff
-#define EXPR1(c,m) ((((c)>>3)^(m))&CACHE_MASK)
+#include "method.h"
 
 #define NOEX_NOREDEF 0
 #ifndef NOEX_NOREDEF
@@ -22,53 +20,32 @@ static void rb_vm_check_redefinition_opt https://github.com/ruby/ruby/blob/trunk/vm_method.c#L20
 #define singleton_undefined idSingleton_method_undefined
 #define attached            id__attached__
 
-struct cache_entry {		/* method hash table. */
-    VALUE filled_version;        /* filled state version */
-    ID mid;			/* method's id */
-    VALUE klass;		/* receiver's class */
-    rb_method_entry_t *me;
-    VALUE defined_class;
-};
-
-static struct cache_entry cache[CACHE_SIZE];
 #define ruby_running (GET_VM()->running)
 /* int ruby_running = 0; */
 
 static void
-vm_clear_global_method_cache(void)
+rb_class_clear_method_cache(VALUE klass)
 {
-    struct cache_entry *ent, *end;
-
-    ent = cache;
-    end = ent + CACHE_SIZE;
-    while (ent < end) {
-	ent->filled_version = 0;
-	ent++;
-    }
+    RCLASS_EXT(klass)->seq = rb_next_class_sequence();
+    rb_class_foreach_subclass(klass, rb_class_clear_method_cache);
 }
 
 void
 rb_clear_cache(void)
 {
-    rb_vm_change_state();
-}
-
-static void
-rb_clear_cache_for_undef(VALUE klass, ID id)
-{
-    rb_vm_change_state();
-}
-
-static void
-rb_clear_cache_by_id(ID id)
-{
-    rb_vm_change_state();
+    INC_VM_STATE_VERSION();
 }
 
 void
 rb_clear_cache_by_class(VALUE klass)
 {
-    rb_vm_change_state();
+    if (klass && klass != Qundef) {
+	if (klass == rb_cBasicObject || klass == rb_cObject || klass == rb_mKernel) {
+	    INC_VM_STATE_VERSION();
+	} else {
+	    rb_class_clear_method_cache(klass);
+	}
+    }
 }
 
 VALUE
@@ -310,7 +287,7 @@ rb_method_entry_make(VALUE klass, ID mid https://github.com/ruby/ruby/blob/trunk/vm_method.c#L287
 
     me = ALLOC(rb_method_entry_t);
 
-    rb_clear_cache_by_id(mid);
+    rb_clear_cache_by_class(klass);
 
     me->flag = NOEX_WITH_SAFE(noex);
     me->mark = 0;
@@ -472,6 +449,7 @@ rb_add_method(VALUE klass, ID mid, rb_me https://github.com/ruby/ruby/blob/trunk/vm_method.c#L449
     if (type != VM_METHOD_TYPE_UNDEF && type != VM_METHOD_TYPE_REFINED) {
 	method_added(klass, mid);
     }
+    rb_clear_cache_by_class(klass);
     return me;
 }
 
@@ -540,26 +518,26 @@ rb_method_entry_at(VALUE klass, ID id) https://github.com/ruby/ruby/blob/trunk/vm_method.c#L518
  */
 rb_method_entry_t *
 rb_method_entry_get_without_cache(VALUE klass, ID id,
-				  VALUE *defined_class_ptr)
+				  VALUE *defined_class_ptr,
+				  method_cache_entry_t *ent)
 {
     VALUE defined_class;
     rb_method_entry_t *me = search_method(klass, id, &defined_class);
 
     if (ruby_running) {
-	struct cache_entry *ent;
-	ent = cache + EXPR1(klass, id);
-	ent->filled_version = GET_VM_STATE_VERSION();
-	ent->klass = klass;
-	ent->defined_class = defined_class;
+	ent->seq = RCLASS_EXT(klass)->seq;
+	ent->vm_state = GET_VM_STATE_VERSION();
 
 	if (UNDEFINED_METHOD_ENTRY_P(me)) {
 	    ent->mid = id;
 	    ent->me = 0;
+	    ent->defined_class = defined_class;
 	    me = 0;
 	}
 	else {
 	    ent->mid = id;
 	    ent->me = me;
+	    ent->defined_class = defined_class;
 	}
     }
 
@@ -568,22 +546,52 @@ rb_method_entry_get_without_cache(VALUE https://github.com/ruby/ruby/blob/trunk/vm_method.c#L546
     return me;
 }
 
+#if VM_DEBUG_VERIFY_METHOD_CACHE
+static void
+verify_method_cache(VALUE klass, ID id, VALUE defined_class, rb_method_entry_t *me)
+{
+    VALUE actual_defined_class;
+    method_cache_entry_t ent;
+    rb_method_entry_t *actual_me =
+	rb_method_entry_get_without_cache(klass, id, &actual_defined_class, &ent);
+
+    if (me != actual_me || defined_class != actual_defined_class) {
+	rb_bug("method cache verification failed");
+    }
+}
+#endif
+
 rb_method_entry_t *
 rb_method_entry(VALUE klass, ID id, VALUE *defined_class_ptr)
 {
 #if OPT_GLOBAL_METHOD_CACHE
-    struct cache_entry *ent;
+    method_cache_entry_t *ent;
+
+    if (RCLASS_EXT(klass)->mc_tbl == NULL) {
+	RCLASS_EXT(klass)->mc_tbl = st_init_numtable();
+    }
 
-    ent = cache + EXPR1(klass, id);
-    if (ent->filled_version == GET_VM_STATE_VERSION() &&
-	ent->mid == id && ent->klass == klass) {
+    if (!st_lookup(RCLASS_EXT(klass)->mc_tbl, (st_index_t)id, (st_data_t *)&ent)) {
+	ent = calloc(1, sizeof(*ent));
+	st_insert(RCLASS_EXT(klass)->mc_tbl, (st_index_t)id, (st_data_t)ent);
+    }
+
+    if (ent->seq == RCLASS_EXT(klass)->seq &&
+	ent->vm_state == GET_VM_STATE_VERSION() &&
+	ent->mid == id) {
 	if (defined_class_ptr)
 	    *defined_class_ptr = ent->defined_class;
+#if VM_DEBUG_VERIFY_METHOD_CACHE
+	verify_method_cache(klass, id, ent->defined_class, ent->me);
+#endif
 	return ent->me;
     }
+#else
+    method_cache_entry_t ent_;
+    method_cache_entry_t* ent = &ent_;
 #endif
 
-    return rb_method_entry_get_without_cache(klass, id, defined_class_ptr);
+    return rb_method_entry_get_without_cache(klass, id, defined_class_ptr, ent);
 }
 
 static rb_method_entry_t *
@@ -687,7 +695,7 @@ remove_method(VALUE klass, ID mid) https://github.com/ruby/ruby/blob/trunk/vm_method.c#L695
     st_delete(RCLASS_M_TBL(klass), &key, &data);
 
     rb_vm_check_redefinition_opt_method(me, klass);
-    rb_clear_cache_for_undef(klass, mid);
+    rb_clear_cache_by_class(klass);
     rb_unlink_method_entry(me);
 
     CALL_METHOD_HOOK(self, removed, mid);
@@ -1220,6 +1228,7 @@ rb_alias(VALUE klass, ID name, ID def) https://github.com/ruby/ruby/blob/trunk/vm_method.c#L1228
 
     if (flag == NOEX_UNDEF) flag = orig_me->flag;
     rb_method_entry_set(target_klass, name, orig_me, flag);
+    rb_clear_cache_by_class(target_klass);
 }
 
 /*
Index: eval.c
===================================================================
--- eval.c	(revision 42821)
+++ eval.c	(revision 42822)
@@ -1262,7 +1262,7 @@ mod_using(VALUE self, VALUE module) https://github.com/ruby/ruby/blob/trunk/eval.c#L1262
     }
     Check_Type(module, T_MODULE);
     rb_using_module(cref, module);
-    rb_clear_cache();
+    rb_clear_cache_by_class(rb_cObject);
     return self;
 }
 
@@ -1398,7 +1398,7 @@ top_using(VALUE self, VALUE module) https://github.com/ruby/ruby/blob/trunk/eval.c#L1398
     }
     Check_Type(module, T_MODULE);
     rb_using_module(cref, module);
-    rb_clear_cache();
+    rb_clear_cache_by_class(rb_cObject);
     return self;
 }
 
Index: gc.c
===================================================================
--- gc.c	(revision 42821)
+++ gc.c	(revision 42822)
@@ -1122,6 +1122,20 @@ rb_free_m_table(st_table *tbl) https://github.com/ruby/ruby/blob/trunk/gc.c#L1122
 }
 
 static int
+free_method_cache_entry_i(ID key, method_cache_entry_t *entry, st_data_t data)
+{
+    free(entry);
+    return ST_CONTINUE;
+}
+
+void
+rb_free_mc_table(st_table *tbl)
+{
+    st_foreach(tbl, free_method_cache_entry_i, 0);
+    st_free_table(tbl);
+}
+
+static int
 free_const_entry_i(ID key, rb_const_entry_t *ce, st_data_t data)
 {
     xfree(ce);
@@ -1226,7 +1240,6 @@ obj_free(rb_objspace_t *objspace, VALUE https://github.com/ruby/ruby/blob/trunk/gc.c#L1240
 	break;
       case T_MODULE:
       case T_CLASS:
-	rb_clear_cache_by_class((VALUE)obj);
         if (RCLASS_M_TBL(obj)) {
             rb_free_m_table(RCLASS_M_TBL(obj));
         }
@@ -1239,7 +1252,23 @@ obj_free(rb_objspace_t *objspace, VALUE https://github.com/ruby/ruby/blob/trunk/gc.c#L1252
 	if (RCLASS_IV_INDEX_TBL(obj)) {
 	    st_free_table(RCLASS_IV_INDEX_TBL(obj));
 	}
-        xfree(RANY(obj)->as.klass.ptr);
+	if (RCLASS_EXT(obj)->subclasses) {
+	    if (BUILTIN_TYPE(obj) == T_MODULE) {
+		rb_class_detach_module_subclasses(obj);
+	    } else {
+		rb_class_detach_subclasses(obj);
+	    }
+	    RCLASS_EXT(obj)->subclasses = NULL;
+	}
+	if (RCLASS_EXT(obj)->mc_tbl) {
+	    rb_free_mc_table(RCLASS_EXT(obj)->mc_tbl);
+	    RCLASS_EXT(obj)->mc_tbl = NULL;
+	}
+	rb_class_remove_from_module_subclasses(obj);
+	rb_class_remove_from_super_subclasses(obj);
+	if (RANY(obj)->as.klass.ptr)
+	    xfree(RANY(obj)->as.klass.ptr);
+	RANY(obj)->as.klass.ptr = NULL;
 	break;
       case T_STRING:
 	rb_str_free(obj);
@@ -1291,7 +1320,14 @@ obj_free(rb_objspace_t *objspace, VALUE https://github.com/ruby/ruby/blob/trunk/gc.c#L1320
 	break;
       case T_ICLASS:
 	/* iClass shares table with the module */
+	if (RCLASS_EXT(obj)->subclasses) {
+	    rb_class_detach_subclasses(obj);
+	    RCLASS_EXT(obj)->subclasses = NULL;
+	}
+	rb_class_remove_from_module_subclasses(obj);
+	rb_class_remove_from_super_subclasses(obj);
 	xfree(RANY(obj)->as.klass.ptr);
+	RANY(obj)->as.klass.ptr = NULL;
 	break;
 
       case T_FLOAT:
Index: class.c
===================================================================
--- class.c	(revision 42821)
+++ class.c	(revision 42822)
@@ -34,6 +34,109 @@ https://github.com/ruby/ruby/blob/trunk/class.c#L34
 extern st_table *rb_class_tbl;
 #define id_attached id__attached__
 
+void
+rb_class_subclass_add(VALUE super, VALUE klass)
+{
+    rb_subclass_entry_t *entry, *head;
+
+    if (super && super != Qundef) {
+	entry = malloc(sizeof(*entry));
+	entry->klass = klass;
+	entry->next = NULL;
+
+	head = RCLASS_EXT(super)->subclasses;
+	if (head) {
+	    entry->next = head;
+	    RCLASS_EXT(head->klass)->parent_subclasses = &entry->next;
+	}
+
+	RCLASS_EXT(super)->subclasses = entry;
+	RCLASS_EXT(klass)->parent_subclasses = &RCLASS_EXT(super)->subclasses;
+    }
+}
+
+static void
+rb_module_add_to_subclasses_list(VALUE module, VALUE iclass)
+{
+    rb_subclass_entry_t *entry, *head;
+
+    entry = malloc(sizeof(*entry));
+    entry->klass = iclass;
+    entry->next = NULL;
+
+    head = RCLASS_EXT(module)->subclasses;
+    if (head) {
+	entry->next = head;
+	RCLASS_EXT(head->klass)->module_subclasses = &entry->next;
+    }
+
+    RCLASS_EXT(module)->subclasses = entry;
+    RCLASS_EXT(iclass)->module_subclasses = &RCLASS_EXT(module)->subclasses;
+}
+
+void
+rb_class_remove_from_super_subclasses(VALUE klass)
+{
+    rb_subclass_entry_t *entry;
+
+    if (RCLASS_EXT(klass)->parent_subclasses) {
+	entry = *RCLASS_EXT(klass)->parent_subclasses;
+
+	*RCLASS_EXT(klass)->parent_subclasses = entry->next;
+	if (entry->next) {
+	    RCLASS_EXT(entry->next->klass)->parent_subclasses = RCLASS_EXT(klass)->parent_subclasses;
+	}
+	free(entry);
+    }
+
+    RCLASS_EXT(klass)->parent_subclasses = NULL;
+}
+
+void
+rb_class_remove_from_module_subclasses(VALUE klass)
+{
+    rb_subclass_entry_t *entry;
+
+    if (RCLASS_EXT(klass)->module_subclasses) {
+	entry = *RCLASS_EXT(klass)->module_subclasses;
+	*RCLASS_EXT(klass)->module_subclasses = entry->next;
+
+	if (entry->next) {
+	    RCLASS_EXT(entry->next->klass)->module_subclasses = RCLASS_EXT(klass)->module_subclasses;
+	}
+
+	free(entry);
+    }
+
+    RCLASS_EXT(klass)->module_subclasses = NULL;
+}
+
+void
+rb_class_foreach_subclass(VALUE klass, void(*f)(VALUE))
+{
+    rb_subclass_entry_t *cur = RCLASS_EXT(klass)->subclasses;
+
+    /* do not be tempted to simplify this loop into a for loop, the order of
+       operations is important here if `f` modifies the linked list */
+    while (cur) {
+	VALUE curklass = cur->klass;
+	cur = cur->next;
+	f(curklass);
+    }
+}
+
+void
+rb_class_detach_subclasses(VALUE klass)
+{
+    rb_class_foreach_subclass(klass, rb_class_remove_from_super_subclasses);
+}
+
+void
+rb_class_detach_module_subclasses(VALUE klass)
+{
+    rb_class_foreach_subclass(klass, rb_class_remove_from_module_subclasses);
+}
+
 /**
  * Allocates a struct RClass for a new class.
  *
@@ -57,6 +160,13 @@ class_alloc(VALUE flags, VALUE klass) https://github.com/ruby/ruby/blob/trunk/class.c#L160
     RCLASS_SET_SUPER((VALUE)obj, 0);
     RCLASS_ORIGIN(obj) = (VALUE)obj;
     RCLASS_IV_INDEX_TBL(obj) = 0;
+
+    RCLASS_EXT(obj)->subclasses = NULL;
+    RCLASS_EXT(obj)->parent_subclasses = NULL;
+    RCLASS_EXT(obj)->module_subclasses = NULL;
+    RCLASS_EXT(obj)->seq = rb_next_class_sequence();
+    RCLASS_EXT(obj)->mc_tbl = NULL;
+
     RCLASS_REFINED_CLASS(obj) = Qnil;
     RCLASS_EXT(obj)->allocator = 0;
     return (VALUE)obj;
@@ -723,7 +833,6 @@ rb_include_module(VALUE klass, VALUE mod https://github.com/ruby/ruby/blob/trunk/class.c#L833
     changed = include_modules_at(klass, RCLASS_ORIGIN(klass), module);
     if (changed < 0)
 	rb_raise(rb_eArgError, "cyclic include detected");
-    if (changed) rb_clear_cache();
 }
 
 static int
@@ -736,8 +845,8 @@ add_refined_method_entry_i(st_data_t key https://github.com/ruby/ruby/blob/trunk/class.c#L845
 static int
 include_modules_at(const VALUE klass, VALUE c, VALUE module)
 {
-    VALUE p;
-    int changed = 0;
+    VALUE p, iclass;
+    int method_changed = 0, constant_changed = 0;
     const st_table *const klass_m_tbl = RCLASS_M_TBL(RCLASS_ORIGIN(klass));
 
     while (module) {
@@ -763,7 +872,15 @@ include_modules_at(const VALUE klass, VA https://github.com/ruby/ruby/blob/trunk/class.c#L872
 		break;
 	    }
 	}
-	c = RCLASS_SET_SUPER(c, rb_include_class_new(module, RCLASS_SUPER(c)));
+	iclass = rb_include_class_new(module, RCLASS_SUPER(c));
+	c = RCLASS_SET_SUPER(c, iclass);
+
+	if (BUILTIN_TYPE(module) == T_ICLASS) {
+	    rb_module_add_to_subclasses_list(RBASIC(module)->klass, iclass);
+	} else {
+	    rb_module_add_to_subclasses_list(module, iclass);
+	}
+
 	if (FL_TEST(klass, RMODULE_IS_REFINEMENT)) {
 	    VALUE refined_class =
 		rb_refinement_module_get_refined_class(klass);
@@ -773,14 +890,17 @@ include_modules_at(const VALUE klass, VA https://github.com/ruby/ruby/blob/trunk/class.c#L890
 	    FL_SET(c, RMODULE_INCLUDED_INTO_REFINEMENT);
 	}
 	if (RMODULE_M_TBL(module) && RMODULE_M_TBL(module)->num_entries)
-	    changed = 1;
+	    method_changed = 1;
 	if (RMODULE_CONST_TBL(module) && RMODULE_CONST_TBL(module)->num_entries)
-	    changed = 1;
+	    constant_changed = 1;
       skip:
 	module = RCLASS_SUPER(module);
     }
 
-    return changed;
+    if (method_changed) rb_clear_cache_by_class(klass);
+    if (constant_changed) rb_clear_cache();
+
+    return method_changed;
 }
 
 static int
@@ -839,7 +959,6 @@ rb_prepend_module(VALUE klass, VALUE mod https://github.com/ruby/ruby/blob/trunk/class.c#L959
     if (changed < 0)
 	rb_raise(rb_eArgError, "cyclic prepend detected");
     if (changed) {
-	rb_clear_cache();
 	rb_vm_c (... truncated)

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

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