Skip to content

Commit 6eeb9a1

Browse files
author
Charlie Somerville
committed
Merge pull request #4 from github/klasscache
Implement class hierarchy method caching
2 parents de6ba0f + 822a288 commit 6eeb9a1

File tree

14 files changed

+314
-111
lines changed

14 files changed

+314
-111
lines changed

class.c

Lines changed: 134 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,109 @@
3434
extern st_table *rb_class_tbl;
3535
static ID id_attached;
3636

37+
void
38+
rb_class_subclass_add(VALUE super, VALUE klass)
39+
{
40+
rb_subclass_entry_t *entry, *head;
41+
42+
if (super && super != Qundef) {
43+
entry = malloc(sizeof(*entry));
44+
entry->klass = klass;
45+
entry->next = NULL;
46+
47+
head = RCLASS_EXT(super)->subclasses;
48+
if (head) {
49+
entry->next = head;
50+
RCLASS_EXT(head->klass)->parent_subclasses = &entry->next;
51+
}
52+
53+
RCLASS_EXT(super)->subclasses = entry;
54+
RCLASS_EXT(klass)->parent_subclasses = &RCLASS_EXT(super)->subclasses;
55+
}
56+
}
57+
58+
static void
59+
rb_module_add_to_subclasses_list(VALUE module, VALUE iclass)
60+
{
61+
rb_subclass_entry_t *entry, *head;
62+
63+
entry = malloc(sizeof(*entry));
64+
entry->klass = iclass;
65+
entry->next = NULL;
66+
67+
head = RCLASS_EXT(module)->subclasses;
68+
if (head) {
69+
entry->next = head;
70+
RCLASS_EXT(head->klass)->module_subclasses = &entry->next;
71+
}
72+
73+
RCLASS_EXT(module)->subclasses = entry;
74+
RCLASS_EXT(iclass)->module_subclasses = &RCLASS_EXT(module)->subclasses;
75+
}
76+
77+
void
78+
rb_class_remove_from_super_subclasses(VALUE klass)
79+
{
80+
rb_subclass_entry_t *entry;
81+
82+
if (RCLASS_EXT(klass)->parent_subclasses) {
83+
entry = *RCLASS_EXT(klass)->parent_subclasses;
84+
85+
*RCLASS_EXT(klass)->parent_subclasses = entry->next;
86+
if (entry->next) {
87+
RCLASS_EXT(entry->next->klass)->parent_subclasses = RCLASS_EXT(klass)->parent_subclasses;
88+
}
89+
free(entry);
90+
}
91+
92+
RCLASS_EXT(klass)->parent_subclasses = NULL;
93+
}
94+
95+
void
96+
rb_class_remove_from_module_subclasses(VALUE klass)
97+
{
98+
rb_subclass_entry_t *entry;
99+
100+
if (RCLASS_EXT(klass)->module_subclasses) {
101+
entry = *RCLASS_EXT(klass)->module_subclasses;
102+
*RCLASS_EXT(klass)->module_subclasses = entry->next;
103+
104+
if (entry->next) {
105+
RCLASS_EXT(entry->next->klass)->module_subclasses = RCLASS_EXT(klass)->module_subclasses;
106+
}
107+
108+
free(entry);
109+
}
110+
111+
RCLASS_EXT(klass)->module_subclasses = NULL;
112+
}
113+
114+
void
115+
rb_class_foreach_subclass(VALUE klass, void(*f)(VALUE))
116+
{
117+
rb_subclass_entry_t *cur = RCLASS_EXT(klass)->subclasses;
118+
119+
/* do not be tempted to simplify this loop into a for loop, the order of
120+
operations is important here if `f` modifies the linked list */
121+
while (cur) {
122+
VALUE curklass = cur->klass;
123+
cur = cur->next;
124+
f(curklass);
125+
}
126+
}
127+
128+
void
129+
rb_class_detach_subclasses(VALUE klass)
130+
{
131+
rb_class_foreach_subclass(klass, rb_class_remove_from_super_subclasses);
132+
}
133+
134+
void
135+
rb_class_detach_module_subclasses(VALUE klass)
136+
{
137+
rb_class_foreach_subclass(klass, rb_class_remove_from_module_subclasses);
138+
}
139+
37140
/**
38141
* Allocates a struct RClass for a new class.
39142
*
@@ -54,9 +157,15 @@ class_alloc(VALUE flags, VALUE klass)
54157
RCLASS_IV_TBL(obj) = 0;
55158
RCLASS_CONST_TBL(obj) = 0;
56159
RCLASS_M_TBL(obj) = 0;
57-
RCLASS_SUPER(obj) = 0;
160+
RCLASS_SET_SUPER((VALUE)obj, 0);
58161
RCLASS_ORIGIN(obj) = (VALUE)obj;
59162
RCLASS_IV_INDEX_TBL(obj) = 0;
163+
164+
RCLASS_EXT(obj)->subclasses = NULL;
165+
RCLASS_EXT(obj)->parent_subclasses = NULL;
166+
RCLASS_EXT(obj)->module_subclasses = NULL;
167+
RCLASS_EXT(obj)->seq = rb_next_class_sequence();
168+
60169
RCLASS_REFINED_CLASS(obj) = Qnil;
61170
RCLASS_EXT(obj)->allocator = 0;
62171
return (VALUE)obj;
@@ -77,7 +186,7 @@ rb_class_boot(VALUE super)
77186
{
78187
VALUE klass = class_alloc(T_CLASS, rb_cClass);
79188

80-
RCLASS_SUPER(klass) = super;
189+
RCLASS_SET_SUPER(klass, super);
81190
RCLASS_M_TBL(klass) = st_init_numtable();
82191

83192
OBJ_INFECT(klass, super);
@@ -203,7 +312,7 @@ rb_mod_init_copy(VALUE clone, VALUE orig)
203312
RBASIC(clone)->klass = rb_singleton_class_clone(orig);
204313
rb_singleton_class_attached(RBASIC(clone)->klass, (VALUE)clone);
205314
}
206-
RCLASS_SUPER(clone) = RCLASS_SUPER(orig);
315+
RCLASS_SET_SUPER(clone, RCLASS_SUPER(orig));
207316
RCLASS_EXT(clone)->allocator = RCLASS_EXT(orig)->allocator;
208317
if (RCLASS_IV_TBL(orig)) {
209318
st_data_t id;
@@ -261,7 +370,7 @@ rb_singleton_class_clone_and_attach(VALUE obj, VALUE attach)
261370
RBASIC(clone)->klass = rb_singleton_class_clone(klass);
262371
}
263372

264-
RCLASS_SUPER(clone) = RCLASS_SUPER(klass);
373+
RCLASS_SET_SUPER(clone, RCLASS_SUPER(klass));
265374
RCLASS_EXT(clone)->allocator = RCLASS_EXT(klass)->allocator;
266375
if (RCLASS_IV_TBL(klass)) {
267376
RCLASS_IV_TBL(clone) = st_copy(RCLASS_IV_TBL(klass));
@@ -356,7 +465,7 @@ make_metaclass(VALUE klass)
356465

357466
super = RCLASS_SUPER(klass);
358467
while (RB_TYPE_P(super, T_ICLASS)) super = RCLASS_SUPER(super);
359-
RCLASS_SUPER(metaclass) = super ? ENSURE_EIGENCLASS(super) : rb_cClass;
468+
RCLASS_SET_SUPER(metaclass, super ? ENSURE_EIGENCLASS(super) : rb_cClass);
360469

361470
OBJ_INFECT(metaclass, RCLASS_SUPER(metaclass));
362471

@@ -676,7 +785,7 @@ rb_include_class_new(VALUE module, VALUE super)
676785
RCLASS_IV_TBL(klass) = RCLASS_IV_TBL(module);
677786
RCLASS_CONST_TBL(klass) = RCLASS_CONST_TBL(module);
678787
RCLASS_M_TBL(klass) = RCLASS_M_TBL(RCLASS_ORIGIN(module));
679-
RCLASS_SUPER(klass) = super;
788+
RCLASS_SET_SUPER(klass, super);
680789
if (RB_TYPE_P(module, T_ICLASS)) {
681790
RBASIC(klass)->klass = RBASIC(module)->klass;
682791
}
@@ -710,7 +819,6 @@ rb_include_module(VALUE klass, VALUE module)
710819
changed = include_modules_at(klass, RCLASS_ORIGIN(klass), module);
711820
if (changed < 0)
712821
rb_raise(rb_eArgError, "cyclic include detected");
713-
if (changed) rb_clear_cache();
714822
}
715823

716824
static int
@@ -723,8 +831,8 @@ add_refined_method_entry_i(st_data_t key, st_data_t value, st_data_t data)
723831
static int
724832
include_modules_at(const VALUE klass, VALUE c, VALUE module)
725833
{
726-
VALUE p;
727-
int changed = 0;
834+
VALUE p, iclass;
835+
int method_changed = 0, constant_changed = 0;
728836
const st_table *const klass_m_tbl = RCLASS_M_TBL(RCLASS_ORIGIN(klass));
729837

730838
while (module) {
@@ -750,7 +858,15 @@ include_modules_at(const VALUE klass, VALUE c, VALUE module)
750858
break;
751859
}
752860
}
753-
c = RCLASS_SUPER(c) = rb_include_class_new(module, RCLASS_SUPER(c));
861+
iclass = rb_include_class_new(module, RCLASS_SUPER(c));
862+
c = RCLASS_SET_SUPER(c, iclass);
863+
864+
if (BUILTIN_TYPE(module) == T_ICLASS) {
865+
rb_module_add_to_subclasses_list(RBASIC(module)->klass, iclass);
866+
} else {
867+
rb_module_add_to_subclasses_list(module, iclass);
868+
}
869+
754870
if (FL_TEST(klass, RMODULE_IS_REFINEMENT)) {
755871
VALUE refined_class =
756872
rb_refinement_module_get_refined_class(klass);
@@ -760,14 +876,17 @@ include_modules_at(const VALUE klass, VALUE c, VALUE module)
760876
FL_SET(c, RMODULE_INCLUDED_INTO_REFINEMENT);
761877
}
762878
if (RMODULE_M_TBL(module) && RMODULE_M_TBL(module)->num_entries)
763-
changed = 1;
879+
method_changed = 1;
764880
if (RMODULE_CONST_TBL(module) && RMODULE_CONST_TBL(module)->num_entries)
765-
changed = 1;
881+
constant_changed = 1;
766882
skip:
767883
module = RCLASS_SUPER(module);
768884
}
769885

770-
return changed;
886+
if (method_changed) rb_clear_cache_by_class(klass);
887+
if (constant_changed) rb_clear_cache();
888+
889+
return method_changed;
771890
}
772891

773892
static int
@@ -816,8 +935,8 @@ rb_prepend_module(VALUE klass, VALUE module)
816935
origin = RCLASS_ORIGIN(klass);
817936
if (origin == klass) {
818937
origin = class_alloc(T_ICLASS, klass);
819-
RCLASS_SUPER(origin) = RCLASS_SUPER(klass);
820-
RCLASS_SUPER(klass) = origin;
938+
RCLASS_SET_SUPER(origin, RCLASS_SUPER(klass));
939+
RCLASS_SET_SUPER(klass, origin);
821940
RCLASS_ORIGIN(klass) = origin;
822941
RCLASS_M_TBL(origin) = RCLASS_M_TBL(klass);
823942
RCLASS_M_TBL(klass) = st_init_numtable();
@@ -828,7 +947,6 @@ rb_prepend_module(VALUE klass, VALUE module)
828947
if (changed < 0)
829948
rb_raise(rb_eArgError, "cyclic prepend detected");
830949
if (changed) {
831-
rb_clear_cache();
832950
rb_vm_check_redefinition_by_prepend(klass);
833951
}
834952
}

compile.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -959,6 +959,7 @@ new_callinfo(rb_iseq_t *iseq, ID mid, int argc, VALUE block, unsigned long flag)
959959
}
960960
}
961961
ci->vmstat = 0;
962+
ci->seq = 0;
962963
ci->blockptr = 0;
963964
ci->recv = Qundef;
964965
ci->call = 0; /* TODO: should set default function? */

eval.c

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1097,7 +1097,7 @@ rb_using_refinement(NODE *cref, VALUE klass, VALUE module)
10971097
module = RCLASS_SUPER(module);
10981098
while (module && module != klass) {
10991099
FL_SET(module, RMODULE_IS_OVERLAID);
1100-
c = RCLASS_SUPER(c) = rb_include_class_new(module, RCLASS_SUPER(c));
1100+
c = RCLASS_SET_SUPER(c, rb_include_class_new(module, RCLASS_SUPER(c)));
11011101
RCLASS_REFINED_CLASS(c) = klass;
11021102
module = RCLASS_SUPER(module);
11031103
}
@@ -1156,8 +1156,8 @@ add_activated_refinement(VALUE activated_refinements,
11561156
refinement = RCLASS_SUPER(refinement);
11571157
while (refinement) {
11581158
FL_SET(refinement, RMODULE_IS_OVERLAID);
1159-
c = RCLASS_SUPER(c) =
1160-
rb_include_class_new(refinement, RCLASS_SUPER(c));
1159+
c = RCLASS_SET_SUPER(c,
1160+
rb_include_class_new(refinement, RCLASS_SUPER(c)));
11611161
RCLASS_REFINED_CLASS(c) = klass;
11621162
refinement = RCLASS_SUPER(refinement);
11631163
}
@@ -1210,7 +1210,7 @@ rb_mod_refine(VALUE module, VALUE klass)
12101210
refinement = rb_hash_lookup(refinements, klass);
12111211
if (NIL_P(refinement)) {
12121212
refinement = rb_module_new();
1213-
RCLASS_SUPER(refinement) = klass;
1213+
RCLASS_SET_SUPER(refinement, klass);
12141214
FL_SET(refinement, RMODULE_IS_REFINEMENT);
12151215
CONST_ID(id_refined_class, "__refined_class__");
12161216
rb_ivar_set(refinement, id_refined_class, klass);
@@ -1356,7 +1356,7 @@ top_using(VALUE self, VALUE module)
13561356
}
13571357
Check_Type(module, T_MODULE);
13581358
rb_using_module(cref, module);
1359-
rb_clear_cache();
1359+
rb_clear_cache_by_class(rb_cObject);
13601360
return self;
13611361
}
13621362

gc.c

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -930,7 +930,6 @@ obj_free(rb_objspace_t *objspace, VALUE obj)
930930
break;
931931
case T_MODULE:
932932
case T_CLASS:
933-
rb_clear_cache_by_class((VALUE)obj);
934933
if (RCLASS_M_TBL(obj)) {
935934
rb_free_m_table(RCLASS_M_TBL(obj));
936935
}
@@ -943,7 +942,19 @@ obj_free(rb_objspace_t *objspace, VALUE obj)
943942
if (RCLASS_IV_INDEX_TBL(obj)) {
944943
st_free_table(RCLASS_IV_INDEX_TBL(obj));
945944
}
946-
xfree(RANY(obj)->as.klass.ptr);
945+
if (RCLASS_EXT(obj)->subclasses) {
946+
if (BUILTIN_TYPE(obj) == T_MODULE) {
947+
rb_class_detach_module_subclasses(obj);
948+
} else {
949+
rb_class_detach_subclasses(obj);
950+
}
951+
RCLASS_EXT(obj)->subclasses = NULL;
952+
}
953+
rb_class_remove_from_module_subclasses(obj);
954+
rb_class_remove_from_super_subclasses(obj);
955+
if (RANY(obj)->as.klass.ptr)
956+
xfree(RANY(obj)->as.klass.ptr);
957+
RANY(obj)->as.klass.ptr = NULL;
947958
break;
948959
case T_STRING:
949960
rb_str_free(obj);
@@ -995,7 +1006,14 @@ obj_free(rb_objspace_t *objspace, VALUE obj)
9951006
break;
9961007
case T_ICLASS:
9971008
/* iClass shares table with the module */
1009+
if (RCLASS_EXT(obj)->subclasses) {
1010+
rb_class_detach_subclasses(obj);
1011+
RCLASS_EXT(obj)->subclasses = NULL;
1012+
}
1013+
rb_class_remove_from_module_subclasses(obj);
1014+
rb_class_remove_from_super_subclasses(obj);
9981015
xfree(RANY(obj)->as.klass.ptr);
1016+
RANY(obj)->as.klass.ptr = NULL;
9991017
break;
10001018

10011019
case T_FLOAT:
@@ -2792,7 +2810,7 @@ gc_mark_children(rb_objspace_t *objspace, VALUE ptr)
27922810
if (!RCLASS_EXT(obj)) break;
27932811
mark_tbl(objspace, RCLASS_IV_TBL(obj));
27942812
mark_const_tbl(objspace, RCLASS_CONST_TBL(obj));
2795-
ptr = RCLASS_SUPER(obj);
2813+
ptr = RCLASS_SUPER((VALUE)obj);
27962814
goto again;
27972815

27982816
case T_ARRAY:

insns.def

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -218,7 +218,7 @@ setconstant
218218
{
219219
vm_check_if_namespace(cbase);
220220
rb_const_set(cbase, id, val);
221-
INC_VM_STATE_VERSION();
221+
rb_clear_cache_by_class(cbase);
222222
}
223223

224224
/**
@@ -975,7 +975,7 @@ defineclass
975975
class_iseq->local_size, 0);
976976
RESTORE_REGS();
977977

978-
INC_VM_STATE_VERSION();
978+
rb_clear_cache_by_class(klass);
979979
NEXT_INSN();
980980
}
981981

0 commit comments

Comments
 (0)