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`.
This commit is contained in:
Jean Boussier authored and Jean Boussier committed 2026-07-14 23:50:17 +02:00
1 parent 243e640982
commit 71ee81f7d9
11 files changed
+115 -138
Notes: git 2026-07-14 21:50:54 +00:00

No files matched your search

+1
View File
@@ -16680,6 +16680,7 @@ shape.$(OBJEXT): $(top_srcdir)/internal/object.h
shape.$(OBJEXT): $(top_srcdir)/internal/sanitizers.h shape.$(OBJEXT): $(top_srcdir)/internal/sanitizers.h
shape.$(OBJEXT): $(top_srcdir)/internal/serial.h shape.$(OBJEXT): $(top_srcdir)/internal/serial.h
shape.$(OBJEXT): $(top_srcdir)/internal/set_table.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/static_assert.h
shape.$(OBJEXT): $(top_srcdir)/internal/string.h shape.$(OBJEXT): $(top_srcdir)/internal/string.h
shape.$(OBJEXT): $(top_srcdir)/internal/struct.h shape.$(OBJEXT): $(top_srcdir)/internal/struct.h
+3 -3
View File
@@ -1101,9 +1101,9 @@ rb_newobj_of(VALUE klass, VALUE flags, size_t size)
static static
VALUE class_allocate_complex_instance(VALUE klass, uint32_t capacity) 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, rb_shape_transition_extended(ROOT_COMPLEX_SHAPE_ID), sizeof(struct RObject));
VALUE obj = rb_newobj_of_with_shape(klass, T_OBJECT, initial_shape_id, sizeof(struct RObject)); VALUE fields_obj = rb_imemo_fields_new_complex(obj, ROOT_COMPLEX_SHAPE_ID, capacity, false);
rb_obj_init_complex(obj, rb_st_init_numtable_with_size(capacity)); ROBJECT_SET_EXTENDED(obj, fields_obj);
return obj; return obj;
} }
+22 -31
View File
@@ -131,11 +131,11 @@ rb_imemo_cdhash_new(size_t size, const struct st_hash_type *type)
} }
static VALUE 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)); 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 // imemo fields objects should always have "RObject" layout. The
// layout in the shape describes the layout of the thing on which it is set. // 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 // 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 capa = RSHAPE(shape_id)->capacity;
size_t embedded_size = offsetof(struct rb_fields, as.embed) + capa * sizeof(VALUE); 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(IMEMO_TYPE_P(fields, imemo_fields));
RUBY_ASSERT(rb_shape_embedded_capacity(RBASIC_SHAPE_ID(fields)) >= capa); 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 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); return imemo_fields_new(owner, ROOT_SHAPE_ID, sizeof(struct rb_fields), false);
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;
} }
static int VALUE
imemo_fields_trigger_wb_i(st_data_t key, st_data_t value, st_data_t arg) rb_imemo_fields_new_complex(VALUE owner, shape_id_t shape_id, size_t capa, bool shareable)
{ {
VALUE field_obj = (VALUE)arg; st_table tbl;
RB_OBJ_WRITTEN(field_obj, Qundef, (VALUE)value); st_init_existing_numtable_with_size(&tbl, capa);
return ST_CONTINUE; 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 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; 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 VALUE
rb_imemo_fields_clone(VALUE fields_obj) rb_imemo_fields_clone(VALUE fields_obj)
{ {
shape_id_t shape_id = RBASIC_SHAPE_ID(fields_obj); shape_id_t shape_id = RBASIC_SHAPE_ID(fields_obj);
VALUE owner = rb_imemo_fields_owner(fields_obj);
VALUE clone; VALUE clone;
if (rb_shape_complex_p(shape_id)) { if (rb_shape_complex_p(shape_id)) {
st_table *src_table = rb_imemo_fields_complex_tbl(fields_obj); st_table *src_table = rb_imemo_fields_complex_tbl(fields_obj);
st_table *dest_table = xcalloc(1, sizeof(st_table)); // We start with ROOT_SHAPE_ID so that if GC trigger in `st_replace` it won't try
clone = rb_imemo_fields_new_complex_tbl(rb_imemo_fields_owner(fields_obj), shape_id, dest_table, false /* TODO: check */); // 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_replace(dest_table, src_table);
st_foreach(dest_table, imemo_fields_complex_wb_i, (st_data_t)clone); st_foreach(dest_table, imemo_fields_complex_wb_i, (st_data_t)clone);
RBASIC_SET_FULL_SHAPE_ID(clone, shape_id);
} }
else { 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); VALUE *fields = rb_imemo_fields_ptr(clone);
attr_index_t fields_count = RSHAPE_LEN(shape_id); attr_index_t fields_count = RSHAPE_LEN(shape_id);
MEMCPY(fields, rb_imemo_fields_ptr(fields_obj), VALUE, fields_count); MEMCPY(fields, rb_imemo_fields_ptr(fields_obj), VALUE, fields_count);
@@ -297,7 +289,7 @@ rb_imemo_memsize(VALUE obj)
break; break;
case imemo_fields: case imemo_fields:
if (rb_obj_shape_complex_p(obj)) { 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; break;
@@ -620,9 +612,8 @@ rb_free_const_table(struct rb_id_table *tbl)
static inline void static inline void
imemo_fields_free(struct rb_fields *fields) imemo_fields_free(struct rb_fields *fields)
{ {
if (FL_TEST_RAW((VALUE)fields, OBJ_FIELD_HEAP)) { if (rb_obj_shape_complex_p((VALUE)fields)) {
RUBY_ASSERT(rb_shape_complex_p(RBASIC_SHAPE_ID((VALUE)fields))); st_free_table(&fields->as.complex.table);
st_free_table(fields->as.complex.table);
} }
} }
+7 -14
View File
@@ -245,8 +245,7 @@ struct rb_fields {
VALUE fields[1]; VALUE fields[1];
} embed; } embed;
struct { struct {
// TODO: the st_table should be embedded st_table table;
st_table *table;
} complex; } complex;
} as; } 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_fields_new(VALUE owner, /* shape_id_t */ uint32_t shape_id, bool shareable);
VALUE rb_imemo_subclasses_new(uint32_t capacity); 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(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); VALUE rb_imemo_fields_clone(VALUE fields_obj);
void rb_imemo_fields_clear(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)); 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 (UNLIKELY(FL_TEST_RAW(fields_obj, OBJ_FIELD_HEAP))) {
if (RB_TYPE_P(fields_obj, T_OBJECT)) { RUBY_ASSERT(RB_TYPE_P(fields_obj, T_OBJECT));
fields_obj = ROBJECT(fields_obj)->as.extended; fields_obj = ROBJECT(fields_obj)->as.extended;
}
RUBY_ASSERT(IMEMO_TYPE_P(fields_obj, imemo_fields)); 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; 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(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 // Some codepaths unconditionally access the fields_ptr, and assume it can be used as st_table if the
// shape is complex. // 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 */ #endif /* INTERNAL_IMEMO_H */
+1 -1
View File
@@ -47,8 +47,8 @@ void rb_gvar_box_dynamic(const char *name);
*/ */
VALUE rb_mod_set_temporary_name(VALUE, VALUE); 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_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); void rb_evict_ivars_to_hash(VALUE obj);
VALUE rb_obj_field_get(VALUE obj, shape_id_t target_shape_id); VALUE rb_obj_field_get(VALUE obj, shape_id_t target_shape_id);
void rb_ivar_set_internal(VALUE obj, ID id, VALUE val); void rb_ivar_set_internal(VALUE obj, ID id, VALUE val);
+8 -4
View File
@@ -339,7 +339,11 @@ rb_obj_copy_ivar(VALUE dest, VALUE obj)
shape_id_t src_shape_id = RBASIC_SHAPE_ID(obj); shape_id_t src_shape_id = RBASIC_SHAPE_ID(obj);
if (rb_shape_complex_p(src_shape_id)) { 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; 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); shape_id_t dest_shape_id = rb_shape_rebuild(initial_shape_id, src_shape_id);
if (UNLIKELY(rb_shape_complex_p(dest_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_copy_ivs_to_hash_table(obj, table);
rb_obj_init_complex(dest, table); rb_obj_replace_fields(dest, fields_obj);
return; return;
} }
+38 -29
View File
@@ -9,6 +9,7 @@
#include "internal/object.h" #include "internal/object.h"
#include "internal/symbol.h" #include "internal/symbol.h"
#include "internal/variable.h" #include "internal/variable.h"
#include "internal/st.h"
#include "variable.h" #include "variable.h"
#include <stdbool.h> #include <stdbool.h>
@@ -1147,16 +1148,21 @@ rb_shape_copy_fields(VALUE dest, VALUE *dest_buf, shape_id_t dest_shape_id, VALU
} }
void 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 RUBY_ASSERT(IMEMO_TYPE_P(src, imemo_fields));
st_table *table = st_copy(fields_table); RUBY_ASSERT(IMEMO_TYPE_P(dest, imemo_fields));
if (rb_shape_has_object_id(src_shape_id)) {
st_data_t id = (st_data_t)id_object_id; shape_id_t dest_shape_id = RBASIC_SHAPE_ID(src);
st_delete(table, &id, NULL); 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 size_t
@@ -1295,36 +1301,39 @@ rb_shape_verify_consistency(VALUE obj, shape_id_t shape_id)
rb_shape_t *shape = RSHAPE(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. // Make sure SHAPE_ID_HAS_IVAR_MASK is valid.
if (rb_shape_complex_p(shape_id)) { if (rb_shape_complex_p(shape_id)) {
RUBY_ASSERT(shape_id & SHAPE_ID_HAS_IVAR_MASK); RUBY_ASSERT(shape_id & SHAPE_ID_HAS_IVAR_MASK);
// Ensure complex object don't appear as embedded // 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)); 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 { 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); attr_index_t ivar_count = RSHAPE_LEN(shape_id);
if (has_object_id) { if (has_object_id) {
ivar_count--; ivar_count--;
+14 -8
View File
@@ -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_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); 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_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 static inline bool
rb_shape_frozen_p(shape_id_t shape_id) 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); 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 static inline shape_id_t
rb_shape_transition_robject(shape_id_t shape_id) 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 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)); 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); shape_id_t rb_shape_transition_object_id(shape_id_t shape_id);
static inline shape_id_t static inline shape_id_t
+3 -1
View File
@@ -2,8 +2,10 @@ require 'test/unit'
require "securerandom" require "securerandom"
class TestObjectId < Test::Unit::TestCase class TestObjectId < Test::Unit::TestCase
SomeClass = Class.new
def setup def setup
@obj = Object.new @obj = SomeClass.new
end end
def test_dup_new_id def test_dup_new_id
+18 -46
View File
@@ -1606,49 +1606,35 @@ rb_attr_get(VALUE obj, ID id)
void rb_obj_copy_fields_to_hash_table(VALUE obj, st_table *table); 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 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 // Copy all object fields, including ivars and internal object_id, etc
static shape_id_t static shape_id_t
rb_evict_fields_to_hash(VALUE obj) rb_evict_fields_to_hash(VALUE obj)
{ {
RUBY_ASSERT(RB_TYPE_P(obj, T_OBJECT));
RUBY_ASSERT(!rb_obj_shape_complex_p(obj)); 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); 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; return new_shape_id;
} }
void void
rb_evict_ivars_to_hash(VALUE obj) rb_evict_ivars_to_hash(VALUE obj)
{ {
RUBY_ASSERT(RB_TYPE_P(obj, T_OBJECT));
RUBY_ASSERT(!rb_obj_shape_complex_p(obj)); RUBY_ASSERT(!rb_obj_shape_complex_p(obj));
st_table *table = st_init_numtable_with_size(rb_ivar_count(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, rb_ivar_count(obj), false);
// Evacuate all previous values from shape into id_table st_table *table = rb_imemo_fields_complex_tbl(fields_obj);
rb_obj_copy_ivs_to_hash_table(obj, table); 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)); 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); 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 static int
imemo_fields_complex_from_obj_i(ID key, VALUE val, st_data_t arg) 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)) { 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; 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); dest_shape_id = rb_shape_rebuild(initial_shape_id, src_shape_id);
if (UNLIKELY(rb_shape_complex_p(dest_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_copy_ivs_to_hash_table(obj, table);
rb_obj_init_complex(dest, table); rb_obj_replace_fields(dest, new_fields_obj);
return; return;
} }
} }
-1
View File
@@ -12,7 +12,6 @@
#include "shape.h" #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); VALUE rb_obj_fields(VALUE obj, ID field_name);
static inline VALUE static inline VALUE