From 71ee81f7d99200041e8b0812028fbefd540aa48a Mon Sep 17 00:00:00 2001 From: Jean Boussier Date: Tue, 14 Jul 2026 14:49:55 +0200 Subject: [PATCH] imemo.c: embed the `st_table` inside `rb_fields` Followup: https://github.com/ruby/ruby/pull/17631 Now that complex `RObject` always have an IMEMO/fields, we no longer need to have an external `st_table` to replicate the memory layout of `RObject`. --- depend | 1 + gc.c | 6 ++-- imemo.c | 53 ++++++++++++----------------- internal/imemo.h | 21 ++++-------- internal/variable.h | 2 +- object.c | 12 ++++--- shape.c | 67 +++++++++++++++++++++---------------- shape.h | 22 +++++++----- test/ruby/test_object_id.rb | 4 ++- variable.c | 64 ++++++++++------------------------- variable.h | 1 - 11 files changed, 115 insertions(+), 138 deletions(-) diff --git a/depend b/depend index 8db2d7a6759..697a9a5eb78 100644 --- a/depend +++ b/depend @@ -16680,6 +16680,7 @@ shape.$(OBJEXT): $(top_srcdir)/internal/object.h shape.$(OBJEXT): $(top_srcdir)/internal/sanitizers.h shape.$(OBJEXT): $(top_srcdir)/internal/serial.h shape.$(OBJEXT): $(top_srcdir)/internal/set_table.h +shape.$(OBJEXT): $(top_srcdir)/internal/st.h shape.$(OBJEXT): $(top_srcdir)/internal/static_assert.h shape.$(OBJEXT): $(top_srcdir)/internal/string.h shape.$(OBJEXT): $(top_srcdir)/internal/struct.h diff --git a/gc.c b/gc.c index e1c14dceb53..29fe600f27c 100644 --- a/gc.c +++ b/gc.c @@ -1101,9 +1101,9 @@ rb_newobj_of(VALUE klass, VALUE flags, size_t size) static VALUE class_allocate_complex_instance(VALUE klass, uint32_t capacity) { - shape_id_t initial_shape_id = rb_shape_transition_robject(0); - VALUE obj = rb_newobj_of_with_shape(klass, T_OBJECT, initial_shape_id, sizeof(struct RObject)); - rb_obj_init_complex(obj, rb_st_init_numtable_with_size(capacity)); + VALUE obj = rb_newobj_of_with_shape(klass, T_OBJECT, rb_shape_transition_extended(ROOT_COMPLEX_SHAPE_ID), sizeof(struct RObject)); + VALUE fields_obj = rb_imemo_fields_new_complex(obj, ROOT_COMPLEX_SHAPE_ID, capacity, false); + ROBJECT_SET_EXTENDED(obj, fields_obj); return obj; } diff --git a/imemo.c b/imemo.c index 06a94d0d19a..f416e219ed1 100644 --- a/imemo.c +++ b/imemo.c @@ -131,11 +131,11 @@ rb_imemo_cdhash_new(size_t size, const struct st_hash_type *type) } static VALUE -imemo_fields_new(VALUE owner, VALUE flags, shape_id_t shape_id, size_t size, bool is_shareable) +imemo_fields_new(VALUE owner, shape_id_t shape_id, size_t size, bool is_shareable) { RUBY_ASSERT(rb_gc_size_allocatable_p(size)); - flags |= T_IMEMO | (imemo_fields << FL_USHIFT) | (is_shareable ? FL_SHAREABLE : 0); + VALUE flags = T_IMEMO | (imemo_fields << FL_USHIFT) | (is_shareable ? FL_SHAREABLE : 0); // imemo fields objects should always have "RObject" layout. The // layout in the shape describes the layout of the thing on which it is set. // Imemo fields have the same layout as robject, therefore the layout @@ -150,7 +150,7 @@ rb_imemo_fields_new(VALUE owner, shape_id_t shape_id, bool shareable) size_t capa = RSHAPE(shape_id)->capacity; size_t embedded_size = offsetof(struct rb_fields, as.embed) + capa * sizeof(VALUE); - VALUE fields = imemo_fields_new(owner, 0, shape_id, embedded_size, shareable); + VALUE fields = imemo_fields_new(owner, shape_id, embedded_size, shareable); RUBY_ASSERT(IMEMO_TYPE_P(fields, imemo_fields)); RUBY_ASSERT(rb_shape_embedded_capacity(RBASIC_SHAPE_ID(fields)) >= capa); @@ -158,20 +158,19 @@ rb_imemo_fields_new(VALUE owner, shape_id_t shape_id, bool shareable) } VALUE -rb_imemo_fields_new_complex(VALUE owner, shape_id_t shape_id, size_t capa, bool shareable) +rb_imemo_fields_new_complex_empty(VALUE owner) { - st_table *tbl = st_init_numtable_with_size(capa); - VALUE fields = imemo_fields_new(owner, OBJ_FIELD_HEAP, shape_id, sizeof(struct rb_fields), shareable); - IMEMO_OBJ_FIELDS(fields)->as.complex.table = tbl; - return fields; + return imemo_fields_new(owner, ROOT_SHAPE_ID, sizeof(struct rb_fields), false); } -static int -imemo_fields_trigger_wb_i(st_data_t key, st_data_t value, st_data_t arg) +VALUE +rb_imemo_fields_new_complex(VALUE owner, shape_id_t shape_id, size_t capa, bool shareable) { - VALUE field_obj = (VALUE)arg; - RB_OBJ_WRITTEN(field_obj, Qundef, (VALUE)value); - return ST_CONTINUE; + st_table tbl; + st_init_existing_numtable_with_size(&tbl, capa); + VALUE fields = imemo_fields_new(owner, shape_id, sizeof(struct rb_fields), shareable); + MEMCPY(&IMEMO_OBJ_FIELDS(fields)->as.complex.table, &tbl, st_table, 1); + return fields; } static int @@ -181,33 +180,26 @@ imemo_fields_complex_wb_i(st_data_t key, st_data_t value, st_data_t arg) return ST_CONTINUE; } -VALUE -rb_imemo_fields_new_complex_tbl(VALUE owner, shape_id_t shape_id, st_table *tbl, bool shareable) -{ - VALUE fields = imemo_fields_new(owner, OBJ_FIELD_HEAP, shape_id, sizeof(struct rb_fields), shareable); - IMEMO_OBJ_FIELDS(fields)->as.complex.table = tbl; - st_foreach(tbl, imemo_fields_trigger_wb_i, (st_data_t)fields); - return fields; -} - VALUE rb_imemo_fields_clone(VALUE fields_obj) { shape_id_t shape_id = RBASIC_SHAPE_ID(fields_obj); + VALUE owner = rb_imemo_fields_owner(fields_obj); VALUE clone; if (rb_shape_complex_p(shape_id)) { st_table *src_table = rb_imemo_fields_complex_tbl(fields_obj); - st_table *dest_table = xcalloc(1, sizeof(st_table)); - clone = rb_imemo_fields_new_complex_tbl(rb_imemo_fields_owner(fields_obj), shape_id, dest_table, false /* TODO: check */); - + // We start with ROOT_SHAPE_ID so that if GC trigger in `st_replace` it won't try + // to mark an uninitialized table. + clone = imemo_fields_new(owner, ROOT_SHAPE_ID, sizeof(struct rb_fields), false /* TODO: check */); + st_table *dest_table = rb_imemo_fields_complex_tbl(clone); st_replace(dest_table, src_table); - st_foreach(dest_table, imemo_fields_complex_wb_i, (st_data_t)clone); + RBASIC_SET_FULL_SHAPE_ID(clone, shape_id); } else { - clone = rb_imemo_fields_new(rb_imemo_fields_owner(fields_obj), shape_id, false /* TODO: check */); + clone = rb_imemo_fields_new(owner, shape_id, false /* TODO: check */); VALUE *fields = rb_imemo_fields_ptr(clone); attr_index_t fields_count = RSHAPE_LEN(shape_id); MEMCPY(fields, rb_imemo_fields_ptr(fields_obj), VALUE, fields_count); @@ -297,7 +289,7 @@ rb_imemo_memsize(VALUE obj) break; case imemo_fields: if (rb_obj_shape_complex_p(obj)) { - size += st_memsize(IMEMO_OBJ_FIELDS(obj)->as.complex.table); + size += st_memsize(rb_imemo_fields_complex_tbl(obj)) - sizeof(st_table); } break; @@ -620,9 +612,8 @@ rb_free_const_table(struct rb_id_table *tbl) static inline void imemo_fields_free(struct rb_fields *fields) { - if (FL_TEST_RAW((VALUE)fields, OBJ_FIELD_HEAP)) { - RUBY_ASSERT(rb_shape_complex_p(RBASIC_SHAPE_ID((VALUE)fields))); - st_free_table(fields->as.complex.table); + if (rb_obj_shape_complex_p((VALUE)fields)) { + st_free_table(&fields->as.complex.table); } } diff --git a/internal/imemo.h b/internal/imemo.h index a66ed1161b4..0bd9fc0bfb5 100644 --- a/internal/imemo.h +++ b/internal/imemo.h @@ -245,8 +245,7 @@ struct rb_fields { VALUE fields[1]; } embed; struct { - // TODO: the st_table should be embedded - st_table *table; + st_table table; } complex; } as; }; @@ -281,7 +280,7 @@ rb_imemo_subclasses_entries(VALUE v) VALUE rb_imemo_fields_new(VALUE owner, /* shape_id_t */ uint32_t shape_id, bool shareable); VALUE rb_imemo_subclasses_new(uint32_t capacity); VALUE rb_imemo_fields_new_complex(VALUE owner, /* shape_id_t */ uint32_t shape_id, size_t capa, bool shareable); -VALUE rb_imemo_fields_new_complex_tbl(VALUE owner, /* shape_id_t */ uint32_t shape_id, st_table *tbl, bool shareable); +VALUE rb_imemo_fields_new_complex_empty(VALUE owner); VALUE rb_imemo_fields_clone(VALUE fields_obj); void rb_imemo_fields_clear(VALUE fields_obj); @@ -303,15 +302,9 @@ rb_imemo_fields_ptr(VALUE fields_obj) RUBY_ASSERT(IMEMO_TYPE_P(fields_obj, imemo_fields) || RB_TYPE_P(fields_obj, T_OBJECT)); if (UNLIKELY(FL_TEST_RAW(fields_obj, OBJ_FIELD_HEAP))) { - if (RB_TYPE_P(fields_obj, T_OBJECT)) { - fields_obj = ROBJECT(fields_obj)->as.extended; - } + RUBY_ASSERT(RB_TYPE_P(fields_obj, T_OBJECT)); + fields_obj = ROBJECT(fields_obj)->as.extended; RUBY_ASSERT(IMEMO_TYPE_P(fields_obj, imemo_fields)); - - // This will go away once we embed the `st_table` inside `IMEMO/fields`. - if (UNLIKELY(FL_TEST_RAW(fields_obj, OBJ_FIELD_HEAP))) { - return (VALUE *)IMEMO_OBJ_FIELDS(fields_obj)->as.complex.table; - } } return IMEMO_OBJ_FIELDS(fields_obj)->as.embed.fields; @@ -325,13 +318,13 @@ rb_imemo_fields_complex_tbl(VALUE fields_obj) } RUBY_ASSERT(IMEMO_TYPE_P(fields_obj, imemo_fields)); - RUBY_ASSERT(FL_TEST_RAW(fields_obj, OBJ_FIELD_HEAP)); + RUBY_ASSERT(!FL_TEST_RAW(fields_obj, OBJ_FIELD_HEAP)); // Some codepaths unconditionally access the fields_ptr, and assume it can be used as st_table if the // shape is complex. - RUBY_ASSERT((st_table *)rb_imemo_fields_ptr(fields_obj) == IMEMO_OBJ_FIELDS(fields_obj)->as.complex.table); + RUBY_ASSERT((st_table *)rb_imemo_fields_ptr(fields_obj) == &IMEMO_OBJ_FIELDS(fields_obj)->as.complex.table); - return IMEMO_OBJ_FIELDS(fields_obj)->as.complex.table; + return &IMEMO_OBJ_FIELDS(fields_obj)->as.complex.table; } #endif /* INTERNAL_IMEMO_H */ diff --git a/internal/variable.h b/internal/variable.h index 70142815e89..d743a7033fd 100644 --- a/internal/variable.h +++ b/internal/variable.h @@ -47,8 +47,8 @@ void rb_gvar_box_dynamic(const char *name); */ VALUE rb_mod_set_temporary_name(VALUE, VALUE); +void rb_obj_replace_fields(VALUE obj, VALUE fields_obj); void rb_obj_copy_ivs_to_hash_table(VALUE obj, st_table *table); -void rb_obj_init_complex(VALUE obj, st_table *table); void rb_evict_ivars_to_hash(VALUE obj); VALUE rb_obj_field_get(VALUE obj, shape_id_t target_shape_id); void rb_ivar_set_internal(VALUE obj, ID id, VALUE val); diff --git a/object.c b/object.c index 3b382bcc5c0..89785f3b1d4 100644 --- a/object.c +++ b/object.c @@ -339,7 +339,11 @@ rb_obj_copy_ivar(VALUE dest, VALUE obj) shape_id_t src_shape_id = RBASIC_SHAPE_ID(obj); if (rb_shape_complex_p(src_shape_id)) { - rb_shape_copy_complex_ivars(dest, obj, src_shape_id, ROBJECT_FIELDS_HASH(obj)); + VALUE fields_obj = ROBJECT_FIELDS_OBJ(obj); + VALUE clone = rb_imemo_fields_new_complex_empty(dest); + rb_shape_copy_complex_ivars(clone, fields_obj); + ROBJECT_SET_EXTENDED(dest, clone); + RBASIC_SET_SHAPE_ID_WITH_LAYOUT(dest, ROOT_COMPLEX_SHAPE_ID, SHAPE_ID_LAYOUT_EXTENDED); return; } @@ -348,10 +352,10 @@ rb_obj_copy_ivar(VALUE dest, VALUE obj) shape_id_t dest_shape_id = rb_shape_rebuild(initial_shape_id, src_shape_id); if (UNLIKELY(rb_shape_complex_p(dest_shape_id))) { - st_table *table = rb_st_init_numtable_with_size(src_num_ivs); + VALUE fields_obj = rb_imemo_fields_new_complex(dest, dest_shape_id, rb_ivar_count(obj), false); + st_table *table = rb_imemo_fields_complex_tbl(fields_obj); rb_obj_copy_ivs_to_hash_table(obj, table); - rb_obj_init_complex(dest, table); - + rb_obj_replace_fields(dest, fields_obj); return; } diff --git a/shape.c b/shape.c index 8c1955fc939..28f079a369c 100644 --- a/shape.c +++ b/shape.c @@ -9,6 +9,7 @@ #include "internal/object.h" #include "internal/symbol.h" #include "internal/variable.h" +#include "internal/st.h" #include "variable.h" #include @@ -1147,16 +1148,21 @@ rb_shape_copy_fields(VALUE dest, VALUE *dest_buf, shape_id_t dest_shape_id, VALU } void -rb_shape_copy_complex_ivars(VALUE dest, VALUE obj, shape_id_t src_shape_id, st_table *fields_table) +rb_shape_copy_complex_ivars(VALUE dest, VALUE src) { - // obj is COMPLEX so we can copy its iv_hash - st_table *table = st_copy(fields_table); - if (rb_shape_has_object_id(src_shape_id)) { - st_data_t id = (st_data_t)id_object_id; - st_delete(table, &id, NULL); + RUBY_ASSERT(IMEMO_TYPE_P(src, imemo_fields)); + RUBY_ASSERT(IMEMO_TYPE_P(dest, imemo_fields)); + + shape_id_t dest_shape_id = RBASIC_SHAPE_ID(src); + st_table *dest_table = rb_imemo_fields_complex_tbl(dest); + st_replace(dest_table, rb_imemo_fields_complex_tbl(src)); + + if (rb_shape_has_object_id(dest_shape_id)) { + st_data_t stkey = (st_data_t)id_object_id; + st_delete(dest_table, &stkey, NULL); } - rb_obj_init_complex(dest, table); - rb_gc_writebarrier_remember(dest); + + RBASIC_SET_SHAPE_ID(dest, ROOT_COMPLEX_SHAPE_ID); } size_t @@ -1295,36 +1301,39 @@ rb_shape_verify_consistency(VALUE obj, shape_id_t shape_id) rb_shape_t *shape = RSHAPE(shape_id); - bool has_object_id = false; - while (shape->parent_offset != INVALID_SHAPE_ID) { - if (shape->type == SHAPE_OBJ_ID) { - has_object_id = true; - break; - } - shape = RSHAPE(shape->parent_offset); - } - - if (rb_shape_has_object_id(shape_id)) { - if (!has_object_id) { - rb_bug("shape_id claim having obj_id but doesn't shape_id=%u, obj=%s", shape_id, rb_obj_info(obj)); - } - } - else { - if (has_object_id) { - rb_bug("shape_id claim not having obj_id but it does shape_id=%u, obj=%s", shape_id, rb_obj_info(obj)); - } - } - // Make sure SHAPE_ID_HAS_IVAR_MASK is valid. if (rb_shape_complex_p(shape_id)) { RUBY_ASSERT(shape_id & SHAPE_ID_HAS_IVAR_MASK); // Ensure complex object don't appear as embedded - if (RB_TYPE_P(obj, T_OBJECT) || IMEMO_TYPE_P(obj, imemo_fields)) { + if (RB_TYPE_P(obj, T_OBJECT)) { RUBY_ASSERT(FL_TEST_RAW(obj, ROBJECT_HEAP)); } + else if (IMEMO_TYPE_P(obj, imemo_fields)) { + RUBY_ASSERT(!FL_TEST_RAW(obj, ROBJECT_HEAP)); + } } else { + bool has_object_id = false; + while (shape->parent_offset != INVALID_SHAPE_ID) { + if (shape->type == SHAPE_OBJ_ID) { + has_object_id = true; + break; + } + shape = RSHAPE(shape->parent_offset); + } + + if (rb_shape_has_object_id(shape_id)) { + if (!has_object_id) { + rb_bug("shape_id claim having obj_id but doesn't shape_id=%u, obj=%s", shape_id, rb_obj_info(obj)); + } + } + else { + if (has_object_id) { + rb_bug("shape_id claim not having obj_id but it does shape_id=%u, obj=%s", shape_id, rb_obj_info(obj)); + } + } + attr_index_t ivar_count = RSHAPE_LEN(shape_id); if (has_object_id) { ivar_count--; diff --git a/shape.h b/shape.h index 51ebf4f72d7..2c67d331231 100644 --- a/shape.h +++ b/shape.h @@ -267,7 +267,7 @@ shape_id_t rb_shape_transition_add_ivar_no_warnings(shape_id_t shape_id, ID id, shape_id_t rb_shape_object_id(shape_id_t original_shape_id); shape_id_t rb_shape_rebuild(shape_id_t initial_shape_id, shape_id_t dest_shape_id); void rb_shape_copy_fields(VALUE dest, VALUE *dest_buf, shape_id_t dest_shape_id, VALUE *src_buf, shape_id_t src_shape_id); -void rb_shape_copy_complex_ivars(VALUE dest, VALUE obj, shape_id_t src_shape_id, st_table *fields_table); +void rb_shape_copy_complex_ivars(VALUE dest, VALUE src); static inline bool rb_shape_frozen_p(shape_id_t shape_id) @@ -497,10 +497,22 @@ rb_obj_using_gen_fields_table_p(VALUE obj) return rb_obj_gen_fields_p(obj); } +static inline shape_id_t +rb_shape_transition_layout(shape_id_t shape_id, shape_id_t layout) +{ + return (shape_id & (~SHAPE_ID_LAYOUT_MASK)) | layout; +} + static inline shape_id_t rb_shape_transition_robject(shape_id_t shape_id) { - return (shape_id & ~SHAPE_ID_LAYOUT_MASK) | SHAPE_ID_LAYOUT_ROBJECT; + return rb_shape_transition_layout(shape_id, SHAPE_ID_LAYOUT_ROBJECT); +} + +static inline shape_id_t +rb_shape_transition_extended(shape_id_t shape_id) +{ + return rb_shape_transition_layout(shape_id, SHAPE_ID_LAYOUT_EXTENDED); } static inline shape_id_t @@ -548,12 +560,6 @@ rb_shape_transition_slot_size(shape_id_t shape_id, size_t slot_size) return rb_shape_transition_capacity(shape_id, rb_shape_capacity_for_slot_size(slot_size)); } -static inline shape_id_t -rb_shape_transition_layout(shape_id_t shape_id, shape_id_t layout) -{ - return (shape_id & (~SHAPE_ID_LAYOUT_MASK)) | layout; -} - shape_id_t rb_shape_transition_object_id(shape_id_t shape_id); static inline shape_id_t diff --git a/test/ruby/test_object_id.rb b/test/ruby/test_object_id.rb index 034674e5be1..acbd96572e1 100644 --- a/test/ruby/test_object_id.rb +++ b/test/ruby/test_object_id.rb @@ -2,8 +2,10 @@ require 'test/unit' require "securerandom" class TestObjectId < Test::Unit::TestCase + SomeClass = Class.new + def setup - @obj = Object.new + @obj = SomeClass.new end def test_dup_new_id diff --git a/variable.c b/variable.c index bcd8c32ea22..6e76e14bd4f 100644 --- a/variable.c +++ b/variable.c @@ -1606,49 +1606,35 @@ rb_attr_get(VALUE obj, ID id) void rb_obj_copy_fields_to_hash_table(VALUE obj, st_table *table); static VALUE imemo_fields_complex_from_obj(VALUE owner, VALUE source_fields_obj, shape_id_t shape_id); -static shape_id_t -obj_transition_complex(VALUE obj, st_table *table) -{ - RUBY_ASSERT(!rb_obj_shape_complex_p(obj)); - RUBY_ASSERT(!RB_TYPE_P(obj, T_IMEMO)); - - shape_id_t shape_id = rb_obj_shape_transition_complex(obj); - VALUE fields_obj = rb_imemo_fields_new_complex_tbl(obj, shape_id, table, RB_OBJ_SHAREABLE_P(obj)); - - rb_obj_set_fields(obj, fields_obj, 0, 0); - RBASIC_SET_SHAPE_ID(obj, shape_id); - - RUBY_ASSERT(FL_TEST_RAW(fields_obj, ROBJECT_HEAP)); - RUBY_ASSERT(rb_obj_shape_complex_p(obj)); - RUBY_ASSERT(rb_obj_shape_complex_p(fields_obj)); - - return shape_id; -} - // Copy all object fields, including ivars and internal object_id, etc static shape_id_t rb_evict_fields_to_hash(VALUE obj) { + RUBY_ASSERT(RB_TYPE_P(obj, T_OBJECT)); RUBY_ASSERT(!rb_obj_shape_complex_p(obj)); - st_table *table = st_init_numtable_with_size(RSHAPE_LEN(RBASIC_SHAPE_ID(obj))); + shape_id_t new_shape_id = rb_obj_shape_transition_complex(obj); + VALUE fields_obj = rb_imemo_fields_new_complex(obj, new_shape_id, RSHAPE_LEN(RBASIC_SHAPE_ID(obj)), false); + st_table *table = rb_imemo_fields_complex_tbl(fields_obj); rb_obj_copy_fields_to_hash_table(obj, table); - shape_id_t new_shape_id = obj_transition_complex(obj, table); + ROBJECT_SET_EXTENDED(obj, fields_obj); + RBASIC_SET_FULL_SHAPE_ID(obj, rb_shape_transition_extended(new_shape_id)); - RUBY_ASSERT(rb_obj_shape_complex_p(obj)); return new_shape_id; } void rb_evict_ivars_to_hash(VALUE obj) { + RUBY_ASSERT(RB_TYPE_P(obj, T_OBJECT)); RUBY_ASSERT(!rb_obj_shape_complex_p(obj)); - st_table *table = st_init_numtable_with_size(rb_ivar_count(obj)); - - // Evacuate all previous values from shape into id_table + shape_id_t new_shape_id = rb_obj_shape_transition_complex(obj); + VALUE fields_obj = rb_imemo_fields_new_complex(obj, new_shape_id, rb_ivar_count(obj), false); + st_table *table = rb_imemo_fields_complex_tbl(fields_obj); rb_obj_copy_ivs_to_hash_table(obj, table); - obj_transition_complex(obj, table); + ROBJECT_SET_EXTENDED(obj, fields_obj); + RBASIC_SET_FULL_SHAPE_ID(obj, rb_shape_transition_extended(new_shape_id)); RUBY_ASSERT(rb_obj_shape_complex_p(obj)); } @@ -1783,23 +1769,6 @@ rb_attr_delete(VALUE obj, ID id) return rb_ivar_delete(obj, id, Qnil); } -void -rb_obj_init_complex(VALUE obj, st_table *table) -{ - // This method is meant to be called on newly allocated object. - RUBY_ASSERT(rb_shape_canonical_p(RBASIC_SHAPE_ID(obj))); - RUBY_ASSERT(RSHAPE_LEN(RBASIC_SHAPE_ID(obj)) == 0); - - if (rb_obj_shape_complex_p(obj)) { - shape_id_t shape_id = rb_obj_shape_transition_complex(obj); - VALUE fields_obj = rb_imemo_fields_new_complex_tbl(obj, shape_id, table, false); - ROBJECT_SET_EXTENDED(obj, fields_obj); - } - else { - obj_transition_complex(obj, table); - } -} - static int imemo_fields_complex_from_obj_i(ID key, VALUE val, st_data_t arg) { @@ -2317,7 +2286,9 @@ rb_copy_generic_ivar(VALUE dest, VALUE obj) } if (rb_shape_complex_p(src_shape_id)) { - rb_shape_copy_complex_ivars(dest, obj, src_shape_id, rb_imemo_fields_complex_tbl(fields_obj)); + VALUE clone = rb_imemo_fields_new_complex_empty(dest); + rb_shape_copy_complex_ivars(clone, fields_obj); + rb_obj_set_fields(dest, clone, 0, 0); return; } @@ -2329,9 +2300,10 @@ rb_copy_generic_ivar(VALUE dest, VALUE obj) dest_shape_id = rb_shape_rebuild(initial_shape_id, src_shape_id); if (UNLIKELY(rb_shape_complex_p(dest_shape_id))) { - st_table *table = rb_st_init_numtable_with_size(src_num_ivs); + new_fields_obj = rb_imemo_fields_new_complex(dest, dest_shape_id, rb_ivar_count(obj), false); + st_table *table = rb_imemo_fields_complex_tbl(new_fields_obj); rb_obj_copy_ivs_to_hash_table(obj, table); - rb_obj_init_complex(dest, table); + rb_obj_replace_fields(dest, new_fields_obj); return; } } diff --git a/variable.h b/variable.h index f2afead9d3a..034f95f9495 100644 --- a/variable.h +++ b/variable.h @@ -12,7 +12,6 @@ #include "shape.h" -void rb_copy_complex_ivars(VALUE dest, VALUE obj, shape_id_t src_shape_id, st_table *fields_table); VALUE rb_obj_fields(VALUE obj, ID field_name); static inline VALUE