diff options
author | charliesome <charliesome@b2dd03c8-39d4-4d8f-98ff-823fe69b080e> | 2013-09-04 05:25:06 +0000 |
---|---|---|
committer | charliesome <charliesome@b2dd03c8-39d4-4d8f-98ff-823fe69b080e> | 2013-09-04 05:25:06 +0000 |
commit | 2f522b9cc6f3e184404040b12af4486520a73b26 (patch) | |
tree | 7e24db4e9d97f1096442eadb272215340865336f /class.c | |
parent | 4142e8301dd618a775f611bc7bf6c049ce6a4bf9 (diff) |
* 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
Diffstat (limited to 'class.c')
-rw-r--r-- | class.c | 135 |
1 files changed, 127 insertions, 8 deletions
@@ -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); } } |