aboutsummaryrefslogtreecommitdiffstats
path: root/variable.c
diff options
context:
space:
mode:
authorAaron Patterson <tenderlove@ruby-lang.org>2023-10-19 11:00:54 -0700
committerAaron Patterson <aaron.patterson@gmail.com>2023-10-24 10:52:06 -0700
commitcaf6a72348431e0e6b61be84919cd06c7a745189 (patch)
treea8340c3033d0885aaa3d0ec460035d52bcac6afc /variable.c
parent27c75319396b8e9fa43b33aca99725b7352a6dcb (diff)
downloadruby-caf6a72348431e0e6b61be84919cd06c7a745189.tar.gz
remove IV limit / support complex shapes on classes
Diffstat (limited to 'variable.c')
-rw-r--r--variable.c212
1 files changed, 151 insertions, 61 deletions
diff --git a/variable.c b/variable.c
index 15dc4831d8..0c9e6b2618 100644
--- a/variable.c
+++ b/variable.c
@@ -72,6 +72,22 @@ struct ivar_update {
#endif
};
+static inline st_table *
+RCLASS_IV_HASH(VALUE obj)
+{
+ RUBY_ASSERT(RB_TYPE_P(obj, RUBY_T_CLASS) || RB_TYPE_P(obj, RUBY_T_MODULE));
+ RUBY_ASSERT(RCLASS_SHAPE_ID(obj) == OBJ_TOO_COMPLEX_SHAPE_ID);
+ return (st_table *)RCLASS_IVPTR(obj);
+}
+
+static inline void
+RCLASS_SET_IV_HASH(VALUE obj, const st_table *tbl)
+{
+ RUBY_ASSERT(RB_TYPE_P(obj, RUBY_T_CLASS) || RB_TYPE_P(obj, RUBY_T_MODULE));
+ RUBY_ASSERT(RCLASS_SHAPE_ID(obj) == OBJ_TOO_COMPLEX_SHAPE_ID);
+ RCLASS_IVPTR(obj) = (VALUE *)tbl;
+}
+
void
Init_var_tables(void)
{
@@ -1231,7 +1247,7 @@ rb_ivar_lookup(VALUE obj, ID id, VALUE undef)
case T_CLASS:
case T_MODULE:
{
- bool found;
+ bool found = false;
VALUE val;
RB_VM_LOCK_ENTER();
@@ -1240,18 +1256,30 @@ rb_ivar_lookup(VALUE obj, ID id, VALUE undef)
shape_id = RCLASS_SHAPE_ID(obj);
#endif
- attr_index_t index = 0;
- shape = rb_shape_get_shape_by_id(shape_id);
- found = rb_shape_get_iv_index(shape, id, &index);
-
- if (found) {
- ivar_list = RCLASS_IVPTR(obj);
- RUBY_ASSERT(ivar_list);
-
- val = ivar_list[index];
+ if (rb_shape_obj_too_complex(obj)) {
+ st_table * iv_table = RCLASS_IV_HASH(obj);
+ if (rb_st_lookup(iv_table, (st_data_t)id, (st_data_t *)&val)) {
+ found = true;
+ }
+ else {
+ val = undef;
+ }
}
else {
- val = undef;
+
+ attr_index_t index = 0;
+ shape = rb_shape_get_shape_by_id(shape_id);
+ found = rb_shape_get_iv_index(shape, id, &index);
+
+ if (found) {
+ ivar_list = RCLASS_IVPTR(obj);
+ RUBY_ASSERT(ivar_list);
+
+ val = ivar_list[index];
+ }
+ else {
+ val = undef;
+ }
}
}
RB_VM_LOCK_LEAVE();
@@ -1439,6 +1467,60 @@ rb_ensure_generic_iv_list_size(VALUE obj, rb_shape_t *shape, uint32_t newsize)
return ivtbl;
}
+static int
+rb_complex_ivar_set(VALUE obj, ID id, VALUE val)
+{
+ st_table * table;
+ RUBY_ASSERT(rb_shape_obj_too_complex(obj));
+
+ switch (BUILTIN_TYPE(obj)) {
+ case T_OBJECT:
+ table = ROBJECT_IV_HASH(obj);
+ break;
+ case T_CLASS:
+ case T_MODULE:
+ table = RCLASS_IV_HASH(obj);
+ break;
+ default:
+ rb_bug("oh no");
+ }
+
+ int found = st_insert(table, (st_data_t)id, (st_data_t)val);
+ RB_OBJ_WRITTEN(obj, Qundef, val);
+ return found;
+}
+
+static void
+rb_evict_ivars_to_hash(VALUE obj, rb_shape_t * shape)
+{
+ RUBY_ASSERT(!rb_shape_obj_too_complex(obj));
+
+ st_table * table = st_init_numtable_with_size(shape->next_iv_index);
+
+ // Evacuate all previous values from shape into id_table
+ rb_ivar_foreach(obj, rb_obj_evacuate_ivs_to_hash_table, (st_data_t)table);
+
+ rb_shape_set_too_complex(obj);
+ RUBY_ASSERT(rb_shape_obj_too_complex(obj));
+
+ switch (BUILTIN_TYPE(obj)) {
+ case T_OBJECT:
+ if (!(RBASIC(obj)->flags & ROBJECT_EMBED)) {
+ xfree(ROBJECT(obj)->as.heap.ivptr);
+ }
+
+ ROBJECT_SET_IV_HASH(obj, table);
+ break;
+ case T_CLASS:
+ case T_MODULE:
+ xfree(RCLASS_IVPTR(obj));
+ RCLASS_SET_IV_HASH(obj, table);
+ break;
+ default:
+ rb_bug("oops!");
+ }
+}
+
// @note May raise when there are too many instance variables.
rb_shape_t *
rb_grow_iv_list(VALUE obj)
@@ -1446,10 +1528,13 @@ rb_grow_iv_list(VALUE obj)
rb_shape_t * initial_shape = rb_shape_get_shape(obj);
RUBY_ASSERT(initial_shape->capacity > 0);
rb_shape_t * res = rb_shape_transition_shape_capa(initial_shape);
-
- rb_ensure_iv_list_size(obj, initial_shape->capacity, res->capacity);
-
- rb_shape_set_shape(obj, res);
+ if (res->type == SHAPE_OBJ_TOO_COMPLEX) { // Out of shapes
+ rb_evict_ivars_to_hash(obj, initial_shape);
+ }
+ else {
+ rb_ensure_iv_list_size(obj, initial_shape->capacity, res->capacity);
+ rb_shape_set_shape(obj, res);
+ }
return res;
}
@@ -1470,12 +1555,12 @@ rb_obj_ivar_set(VALUE obj, ID id, VALUE val)
uint32_t num_iv = shape->capacity;
if (rb_shape_obj_too_complex(obj)) {
- st_table * table = ROBJECT_IV_HASH(obj);
- st_insert(table, (st_data_t)id, (st_data_t)val);
- RB_OBJ_WRITTEN(obj, Qundef, val);
+ rb_complex_ivar_set(obj, id, val);
return 0;
}
+ rb_shape_t *next_shape;
+
if (!rb_shape_get_iv_index(shape, id, &index)) {
index = shape->next_iv_index;
if (index >= MAX_IVARS) {
@@ -1487,31 +1572,20 @@ rb_obj_ivar_set(VALUE obj, ID id, VALUE val)
if (UNLIKELY(shape->next_iv_index >= num_iv)) {
RUBY_ASSERT(shape->next_iv_index == num_iv);
- shape = rb_grow_iv_list(obj);
+ next_shape = rb_grow_iv_list(obj);
+ if (next_shape->type == SHAPE_OBJ_TOO_COMPLEX) {
+ rb_complex_ivar_set(obj, id, val);
+ return 0;
+ }
+ shape = next_shape;
RUBY_ASSERT(shape->type == SHAPE_CAPACITY_CHANGE);
}
- rb_shape_t *next_shape = rb_shape_get_next(shape, obj, id);
+ next_shape = rb_shape_get_next(shape, obj, id);
if (next_shape->type == SHAPE_OBJ_TOO_COMPLEX) {
- st_table * table = st_init_numtable_with_size(shape->next_iv_index);
-
- // Evacuate all previous values from shape into id_table
- rb_ivar_foreach(obj, rb_obj_evacuate_ivs_to_hash_table, (st_data_t)table);
-
- // Insert new value too
- st_insert(table, (st_data_t)id, (st_data_t)val);
- RB_OBJ_WRITTEN(obj, Qundef, val);
-
- rb_shape_set_too_complex(obj);
- RUBY_ASSERT(rb_shape_obj_too_complex(obj));
-
- if (!(RBASIC(obj)->flags & ROBJECT_EMBED)) {
- xfree(ROBJECT(obj)->as.heap.ivptr);
- }
-
- ROBJECT(obj)->as.heap.ivptr = (VALUE *)table;
-
+ rb_evict_ivars_to_hash(obj, shape);
+ rb_complex_ivar_set(obj, id, val);
return 0;
}
else {
@@ -1765,7 +1839,13 @@ class_ivar_each(VALUE obj, rb_ivar_foreach_callback_func *func, st_data_t arg)
struct iv_itr_data itr_data;
itr_data.obj = obj;
itr_data.arg = arg;
- iterate_over_shapes_with_callback(shape, func, &itr_data);
+ itr_data.func = func;
+ if (rb_shape_obj_too_complex(obj)) {
+ rb_st_foreach(RCLASS_IV_HASH(obj), each_hash_iv, (st_data_t)&itr_data);
+ }
+ else {
+ iterate_over_shapes_with_callback(shape, func, &itr_data);
+ }
}
void
@@ -3989,40 +4069,50 @@ int
rb_class_ivar_set(VALUE obj, ID key, VALUE value)
{
RUBY_ASSERT(RB_TYPE_P(obj, T_CLASS) || RB_TYPE_P(obj, T_MODULE));
- int found;
+ int found = 0;
rb_check_frozen(obj);
RB_VM_LOCK_ENTER();
{
rb_shape_t * shape = rb_shape_get_shape(obj);
- attr_index_t idx;
- found = rb_shape_get_iv_index(shape, key, &idx);
-
- if (found) {
- // Changing an existing instance variable
- RUBY_ASSERT(RCLASS_IVPTR(obj));
-
- RCLASS_IVPTR(obj)[idx] = value;
- RB_OBJ_WRITTEN(obj, Qundef, value);
+ if (shape->type == SHAPE_OBJ_TOO_COMPLEX) {
+ found = rb_complex_ivar_set(obj, key, value);
}
else {
- // Creating and setting a new instance variable
+ attr_index_t idx;
+ found = rb_shape_get_iv_index(shape, key, &idx);
- // Move to a shape which fits the new ivar
- idx = shape->next_iv_index;
- shape = rb_shape_get_next(shape, obj, key);
+ if (found) {
+ // Changing an existing instance variable
+ RUBY_ASSERT(RCLASS_IVPTR(obj));
- // We always allocate a power of two sized IV array. This way we
- // only need to realloc when we expand into a new power of two size
- if ((idx & (idx - 1)) == 0) {
- size_t newsize = idx ? idx * 2 : 1;
- REALLOC_N(RCLASS_IVPTR(obj), VALUE, newsize);
+ RCLASS_IVPTR(obj)[idx] = value;
+ RB_OBJ_WRITTEN(obj, Qundef, value);
}
+ else {
+ // Creating and setting a new instance variable
+
+ // Move to a shape which fits the new ivar
+ idx = shape->next_iv_index;
+ rb_shape_t * next_shape = rb_shape_get_next(shape, obj, key);
+ if (next_shape->type == SHAPE_OBJ_TOO_COMPLEX) {
+ rb_evict_ivars_to_hash(obj, shape);
+ rb_complex_ivar_set(obj, key, value);
+ }
+ else {
+ // We always allocate a power of two sized IV array. This way we
+ // only need to realloc when we expand into a new power of two size
+ if ((idx & (idx - 1)) == 0) {
+ size_t newsize = idx ? idx * 2 : 1;
+ REALLOC_N(RCLASS_IVPTR(obj), VALUE, newsize);
+ }
- RUBY_ASSERT(RCLASS_IVPTR(obj));
+ RUBY_ASSERT(RCLASS_IVPTR(obj));
- RB_OBJ_WRITE(obj, &RCLASS_IVPTR(obj)[idx], value);
- rb_shape_set_shape(obj, shape);
+ RB_OBJ_WRITE(obj, &RCLASS_IVPTR(obj)[idx], value);
+ rb_shape_set_shape(obj, next_shape);
+ }
+ }
}
}
RB_VM_LOCK_LEAVE();