mirror of
https://github.com/ruby/ruby.git
synced 2026-10-28 08:25:20 +08:00
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:
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
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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
@@ -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
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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--;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in new issue
Block a user