From 2f522b9cc6f3e184404040b12af4486520a73b26 Mon Sep 17 00:00:00 2001 From: charliesome Date: Wed, 4 Sep 2013 05:25:06 +0000 Subject: * 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] git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@42822 b2dd03c8-39d4-4d8f-98ff-823fe69b080e --- class.c | 135 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 127 insertions(+), 8 deletions(-) (limited to 'class.c') diff --git a/class.c b/class.c index f87d2bdcca..c07a20438a 100644 --- a/class.c +++ b/class.c @@ -34,6 +34,109 @@ 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) 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 module) 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, st_data_t value, st_data_t data) 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, VALUE c, VALUE module) 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, VALUE c, VALUE module) 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 module) if (changed < 0) rb_raise(rb_eArgError, "cyclic prepend detected"); if (changed) { - rb_clear_cache(); rb_vm_check_redefinition_by_prepend(klass); } } -- cgit v1.2.3