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/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
+3 -3
View File
@@ -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;
}
+22 -31
View File
@@ -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);
}
}
+7 -14
View File
@@ -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 */
+1 -1
View File
@@ -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);
+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);
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;
}
+38 -29
View File
@@ -9,6 +9,7 @@
#include "internal/object.h"
#include "internal/symbol.h"
#include "internal/variable.h"
#include "internal/st.h"
#include "variable.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
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--;
+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_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
+3 -1
View File
@@ -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
+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);
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;
}
}
-1
View File
@@ -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