diff --git a/gc.c b/gc.c index df541f54da..86d8f33ea7 100644 --- a/gc.c +++ b/gc.c @@ -3462,7 +3462,7 @@ obj_free(rb_objspace_t *objspace, VALUE obj) case T_OBJECT: if (rb_shape_obj_too_complex(obj)) { RB_DEBUG_COUNTER_INC(obj_obj_too_complex); - rb_id_table_free(ROBJECT_IV_HASH(obj)); + st_free_table(ROBJECT_IV_HASH(obj)); } else if (RANY(obj)->as.basic.flags & ROBJECT_EMBED) { RB_DEBUG_COUNTER_INC(obj_obj_embed); @@ -4889,7 +4889,7 @@ obj_memsize_of(VALUE obj, int use_all_types) switch (BUILTIN_TYPE(obj)) { case T_OBJECT: if (rb_shape_obj_too_complex(obj)) { - size += rb_id_table_memsize(ROBJECT_IV_HASH(obj)); + size += rb_st_memsize(ROBJECT_IV_HASH(obj)); } else if (!(RBASIC(obj)->flags & ROBJECT_EMBED)) { size += ROBJECT_IV_CAPACITY(obj) * sizeof(VALUE); @@ -7333,7 +7333,7 @@ gc_mark_children(rb_objspace_t *objspace, VALUE obj) { rb_shape_t *shape = rb_shape_get_shape_by_id(ROBJECT_SHAPE_ID(obj)); if (rb_shape_obj_too_complex(obj)) { - mark_m_tbl(objspace, ROBJECT_IV_HASH(obj)); + mark_tbl_no_pin(objspace, ROBJECT_IV_HASH(obj)); } else { const VALUE * const ptr = ROBJECT_IVPTR(obj); @@ -10121,15 +10121,13 @@ gc_ref_update_array(rb_objspace_t * objspace, VALUE v) } } -static void update_m_tbl(rb_objspace_t *objspace, struct rb_id_table *tbl); - static void gc_ref_update_object(rb_objspace_t *objspace, VALUE v) { VALUE *ptr = ROBJECT_IVPTR(v); if (rb_shape_obj_too_complex(v)) { - update_m_tbl(objspace, ROBJECT_IV_HASH(v)); + rb_gc_update_tbl_refs(ROBJECT_IV_HASH(v)); return; } diff --git a/include/ruby/st.h b/include/ruby/st.h index 1e4bb80686..f35ab43603 100644 --- a/include/ruby/st.h +++ b/include/ruby/st.h @@ -98,6 +98,8 @@ struct st_table { enum st_retval {ST_CONTINUE, ST_STOP, ST_DELETE, ST_CHECK, ST_REPLACE}; +size_t rb_st_table_size(const struct st_table *tbl); +#define st_table_size rb_st_table_size st_table *rb_st_init_table(const struct st_hash_type *); #define st_init_table rb_st_init_table st_table *rb_st_init_table_with_size(const struct st_hash_type *, st_index_t); diff --git a/object.c b/object.c index cd50960aba..04e2af96e7 100644 --- a/object.c +++ b/object.c @@ -275,7 +275,7 @@ rb_obj_copy_ivar(VALUE dest, VALUE obj) rb_shape_t * src_shape = rb_shape_get_shape(obj); if (rb_shape_id(src_shape) == OBJ_TOO_COMPLEX_SHAPE_ID) { - struct rb_id_table * table = rb_id_table_create(rb_id_table_size(ROBJECT_IV_HASH(obj))); + st_table * table = rb_st_init_numtable_with_size(rb_st_table_size(ROBJECT_IV_HASH(obj))); rb_ivar_foreach(obj, rb_obj_evacuate_ivs_to_hash_table, (st_data_t)table); rb_shape_set_too_complex(dest); diff --git a/ractor.c b/ractor.c index e608e4e232..8fb563fa11 100644 --- a/ractor.c +++ b/ractor.c @@ -2250,17 +2250,17 @@ obj_hash_traverse_i(VALUE key, VALUE val, VALUE ptr) return ST_CONTINUE; } -static enum rb_id_table_iterator_result -obj_hash_iv_traverse_i(VALUE val, void *ptr) +static int +obj_hash_iv_traverse_i(st_data_t key, st_data_t val, st_data_t ptr) { struct obj_traverse_callback_data *d = (struct obj_traverse_callback_data *)ptr; - if (obj_traverse_i(val, d->data)) { + if (obj_traverse_i((VALUE)val, d->data)) { d->stop = true; - return ID_TABLE_STOP; + return ST_STOP; } - return ID_TABLE_CONTINUE; + return ST_CONTINUE; } static void @@ -2326,7 +2326,7 @@ obj_traverse_i(VALUE obj, struct obj_traverse_data *data) .stop = false, .data = data, }; - rb_id_table_foreach_values(ROBJECT_IV_HASH(obj), obj_hash_iv_traverse_i, &d); + rb_st_foreach(ROBJECT_IV_HASH(obj), obj_hash_iv_traverse_i, (st_data_t)&d); if (d.stop) return 1; } else { @@ -2681,28 +2681,28 @@ obj_hash_traverse_replace_i(st_data_t *key, st_data_t *val, st_data_t ptr, int e return ST_CONTINUE; } -static enum rb_id_table_iterator_result -obj_iv_hash_traverse_replace_foreach_i(VALUE val, void *data) +static int +obj_iv_hash_traverse_replace_foreach_i(st_data_t _key, st_data_t _val, st_data_t _data, int _x) { - return ID_TABLE_REPLACE; + return ST_REPLACE; } -static enum rb_id_table_iterator_result -obj_iv_hash_traverse_replace_i(VALUE *val, void *ptr, int exists) +static int +obj_iv_hash_traverse_replace_i(st_data_t * _key, st_data_t * val, st_data_t ptr, int exists) { struct obj_traverse_replace_callback_data *d = (struct obj_traverse_replace_callback_data *)ptr; struct obj_traverse_replace_data *data = d->data; - if (obj_traverse_replace_i(*val, data)) { + if (obj_traverse_replace_i(*(VALUE *)val, data)) { d->stop = true; - return ID_TABLE_STOP; + return ST_STOP; } - else if (*val != data->replacement) { - VALUE v = *val = data->replacement; + else if (*(VALUE *)val != data->replacement) { + VALUE v = *(VALUE *)val = data->replacement; RB_OBJ_WRITTEN(d->src, Qundef, v); } - return ID_TABLE_CONTINUE; + return ST_CONTINUE; } static struct st_table * @@ -2806,16 +2806,17 @@ obj_traverse_replace_i(VALUE obj, struct obj_traverse_replace_data *data) case T_OBJECT: { if (rb_shape_obj_too_complex(obj)) { - struct rb_id_table * table = ROBJECT_IV_HASH(obj); + st_table * table = ROBJECT_IV_HASH(obj); struct obj_traverse_replace_callback_data d = { .stop = false, .data = data, .src = obj, }; - rb_id_table_foreach_values_with_replace(table, - obj_iv_hash_traverse_replace_foreach_i, - obj_iv_hash_traverse_replace_i, - (void *)&d); + rb_st_foreach_with_replace( + table, + obj_iv_hash_traverse_replace_foreach_i, + obj_iv_hash_traverse_replace_i, + (st_data_t)&d); } else { #if USE_TRANSIENT_HEAP diff --git a/shape.h b/shape.h index 8df12d3fb1..8f3451d492 100644 --- a/shape.h +++ b/shape.h @@ -162,12 +162,12 @@ ROBJECT_IV_CAPACITY(VALUE obj) return rb_shape_get_shape_by_id(ROBJECT_SHAPE_ID(obj))->capacity; } -static inline struct rb_id_table * +static inline st_table * ROBJECT_IV_HASH(VALUE obj) { RBIMPL_ASSERT_TYPE(obj, RUBY_T_OBJECT); RUBY_ASSERT(ROBJECT_SHAPE_ID(obj) == OBJ_TOO_COMPLEX_SHAPE_ID); - return (struct rb_id_table *)ROBJECT(obj)->as.heap.ivptr; + return (st_table *)ROBJECT(obj)->as.heap.ivptr; } static inline void @@ -184,7 +184,7 @@ static inline uint32_t ROBJECT_IV_COUNT(VALUE obj) { if (ROBJECT_SHAPE_ID(obj) == OBJ_TOO_COMPLEX_SHAPE_ID) { - return (uint32_t)rb_id_table_size(ROBJECT_IV_HASH(obj)); + return (uint32_t)rb_st_table_size(ROBJECT_IV_HASH(obj)); } else { RBIMPL_ASSERT_TYPE(obj, RUBY_T_OBJECT); diff --git a/st.c b/st.c index 0fb8f82bc3..722b9adedb 100644 --- a/st.c +++ b/st.c @@ -569,6 +569,12 @@ st_init_table_with_size(const struct st_hash_type *type, st_index_t size) return tab; } +size_t +st_table_size(const struct st_table *tbl) +{ + return tbl->num_entries; +} + /* Create and return table with TYPE which can hold a minimal number of entries (see comments for get_power2). */ st_table * diff --git a/test/ruby/test_shapes.rb b/test/ruby/test_shapes.rb index b2cd79ab12..47ade7e70b 100644 --- a/test/ruby/test_shapes.rb +++ b/test/ruby/test_shapes.rb @@ -3,6 +3,17 @@ require 'test/unit' # These test the functionality of object shapes class TestShapes < Test::Unit::TestCase + class IVOrder + def expected_ivs + %w{ @a @b @c @d @e @f @g @h @i @j @k } + end + + def set_ivs + expected_ivs.each { instance_variable_set(_1, 1) } + self + end + end + class ShapeOrder def initialize @b = :b # 5 => 6 @@ -80,6 +91,16 @@ class TestShapes < Test::Unit::TestCase refute_equal(shape1.id, shape2.id) end + def test_iv_order_correct_on_complex_objects + (RubyVM::Shape::SHAPE_MAX_VARIATIONS + 1).times { + IVOrder.new.instance_variable_set("@a#{_1}", 1) + } + + obj = IVOrder.new + iv_list = obj.set_ivs.instance_variables + assert_equal obj.expected_ivs, iv_list.map(&:to_s) + end + def test_too_complex ensure_complex diff --git a/variable.c b/variable.c index ed74e6495e..915db3d5ef 100644 --- a/variable.c +++ b/variable.c @@ -1176,9 +1176,9 @@ rb_ivar_lookup(VALUE obj, ID id, VALUE undef) shape_id = ROBJECT_SHAPE_ID(obj); #endif if (rb_shape_obj_too_complex(obj)) { - struct rb_id_table * iv_table = ROBJECT_IV_HASH(obj); + st_table * iv_table = ROBJECT_IV_HASH(obj); VALUE val; - if (rb_id_table_lookup(iv_table, id, &val)) { + if (rb_st_lookup(iv_table, (st_data_t)id, (st_data_t *)&val)) { return val; } else { @@ -1435,7 +1435,7 @@ rb_grow_iv_list(VALUE obj) int rb_obj_evacuate_ivs_to_hash_table(ID key, VALUE val, st_data_t arg) { - rb_id_table_insert((struct rb_id_table *)arg, key, val); + st_insert((st_table *)arg, (st_data_t)key, (st_data_t)val); return ST_CONTINUE; } @@ -1448,8 +1448,8 @@ rb_obj_ivar_set(VALUE obj, ID id, VALUE val) uint32_t num_iv = shape->capacity; if (rb_shape_obj_too_complex(obj)) { - struct rb_id_table * table = ROBJECT_IV_HASH(obj); - rb_id_table_insert(table, id, val); + st_table * table = ROBJECT_IV_HASH(obj); + st_insert(table, (st_data_t)id, (st_data_t)val); RB_OBJ_WRITTEN(obj, Qundef, val); return 0; } @@ -1472,13 +1472,13 @@ rb_obj_ivar_set(VALUE obj, ID id, VALUE val) rb_shape_t *next_shape = rb_shape_get_next(shape, obj, id); if (next_shape->type == SHAPE_OBJ_TOO_COMPLEX) { - struct rb_id_table * table = rb_id_table_create(shape->next_iv_index); + 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 - rb_id_table_insert(table, id, val); + st_insert(table, (st_data_t)id, (st_data_t)val); RB_OBJ_WRITTEN(obj, Qundef, val); rb_shape_set_too_complex(obj); @@ -1627,7 +1627,7 @@ rb_ivar_defined(VALUE obj, ID id) if (SPECIAL_CONST_P(obj)) return Qfalse; if (rb_shape_obj_too_complex(obj)) { VALUE idx; - if (!rb_id_table_lookup(ROBJECT_IV_HASH(obj), id, &idx)) { + if (!rb_st_lookup(ROBJECT_IV_HASH(obj), id, &idx)) { return Qfalse; } @@ -1686,12 +1686,12 @@ iterate_over_shapes_with_callback(rb_shape_t *shape, rb_ivar_foreach_callback_fu } } -static enum rb_id_table_iterator_result -each_hash_iv(ID id, VALUE val, void *data) +static int +each_hash_iv(st_data_t id, st_data_t val, st_data_t data) { struct iv_itr_data * itr_data = (struct iv_itr_data *)data; rb_ivar_foreach_callback_func *callback = itr_data->func; - return callback(id, val, itr_data->arg); + return callback((ID)id, (VALUE)val, itr_data->arg); } static void @@ -1703,7 +1703,7 @@ obj_ivar_each(VALUE obj, rb_ivar_foreach_callback_func *func, st_data_t arg) itr_data.arg = arg; itr_data.func = func; if (rb_shape_obj_too_complex(obj)) { - rb_id_table_foreach(ROBJECT_IV_HASH(obj), each_hash_iv, &itr_data); + rb_st_foreach(ROBJECT_IV_HASH(obj), each_hash_iv, (st_data_t)&itr_data); } else { iterate_over_shapes_with_callback(shape, func, &itr_data); @@ -1997,8 +1997,8 @@ rb_obj_remove_instance_variable(VALUE obj, VALUE name) break; case T_OBJECT: { if (rb_shape_obj_too_complex(obj)) { - if (rb_id_table_lookup(ROBJECT_IV_HASH(obj), id, &val)) { - rb_id_table_delete(ROBJECT_IV_HASH(obj), id); + if (rb_st_lookup(ROBJECT_IV_HASH(obj), (st_data_t)id, (st_data_t *)&val)) { + rb_st_delete(ROBJECT_IV_HASH(obj), (st_data_t *)&id, 0); } } else { diff --git a/version.h b/version.h index a436eb1260..f5e4d2441c 100644 --- a/version.h +++ b/version.h @@ -11,7 +11,7 @@ # define RUBY_VERSION_MINOR RUBY_API_VERSION_MINOR #define RUBY_VERSION_TEENY 2 #define RUBY_RELEASE_DATE RUBY_RELEASE_YEAR_STR"-"RUBY_RELEASE_MONTH_STR"-"RUBY_RELEASE_DAY_STR -#define RUBY_PATCHLEVEL 98 +#define RUBY_PATCHLEVEL 99 #include "ruby/version.h" #include "ruby/internal/abi.h" diff --git a/vm_insnhelper.c b/vm_insnhelper.c index d3d44cb0a3..09a4bde792 100644 --- a/vm_insnhelper.c +++ b/vm_insnhelper.c @@ -1256,7 +1256,7 @@ vm_getivar(VALUE obj, ID id, const rb_iseq_t *iseq, IVC ic, const struct rb_call rb_shape_t *shape = rb_shape_get_shape_by_id(shape_id); if (shape_id == OBJ_TOO_COMPLEX_SHAPE_ID) { - if (!rb_id_table_lookup(ROBJECT_IV_HASH(obj), id, &val)) { + if (!st_lookup(ROBJECT_IV_HASH(obj), id, &val)) { val = Qnil; } }