mirror of
https://github.com/ruby/ruby.git
synced 2025-08-15 13:39:04 +02:00
Move object_id
in object fields.
And get rid of the `obj_to_id_tbl` It's no longer needed, the `object_id` is now stored inline in the object alongside instance variables. We still need the inverse table in case `_id2ref` is invoked, but we lazily build it by walking the heap if that happens. The `object_id` concern is also no longer a GC implementation concern, but a generic implementation. Co-Authored-By: Matt Valentine-House <matt@eightbitraptor.com>
This commit is contained in:
parent
d34c150547
commit
f48e45d1e9
Notes:
git
2025-05-08 05:58:19 +00:00
23 changed files with 1140 additions and 560 deletions
419
variable.c
419
variable.c
|
@ -26,6 +26,7 @@
|
|||
#include "internal/eval.h"
|
||||
#include "internal/hash.h"
|
||||
#include "internal/object.h"
|
||||
#include "internal/gc.h"
|
||||
#include "internal/re.h"
|
||||
#include "internal/symbol.h"
|
||||
#include "internal/thread.h"
|
||||
|
@ -63,6 +64,9 @@ static void setup_const_entry(rb_const_entry_t *, VALUE, VALUE, rb_const_flag_t)
|
|||
static VALUE rb_const_search(VALUE klass, ID id, int exclude, int recurse, int visibility);
|
||||
static st_table *generic_fields_tbl_;
|
||||
|
||||
typedef int rb_ivar_foreach_callback_func(ID key, VALUE val, st_data_t arg);
|
||||
static void rb_field_foreach(VALUE obj, rb_ivar_foreach_callback_func *func, st_data_t arg, bool ivar_only);
|
||||
|
||||
void
|
||||
Init_var_tables(void)
|
||||
{
|
||||
|
@ -1294,6 +1298,31 @@ gen_fields_tbl_count(VALUE obj, const struct gen_fields_tbl *fields_tbl)
|
|||
return n;
|
||||
}
|
||||
|
||||
VALUE
|
||||
rb_field_get(VALUE obj, rb_shape_t *target_shape)
|
||||
{
|
||||
RUBY_ASSERT(!SPECIAL_CONST_P(obj));
|
||||
RUBY_ASSERT(!rb_shape_obj_too_complex(obj));
|
||||
RUBY_ASSERT(target_shape->type == SHAPE_IVAR || target_shape->type == SHAPE_OBJ_ID);
|
||||
|
||||
attr_index_t attr_index = target_shape->next_field_index - 1;
|
||||
|
||||
switch (BUILTIN_TYPE(obj)) {
|
||||
case T_CLASS:
|
||||
case T_MODULE:
|
||||
ASSERT_vm_locking();
|
||||
return RCLASS_FIELDS(obj)[attr_index];
|
||||
case T_OBJECT:
|
||||
return ROBJECT_FIELDS(obj)[attr_index];
|
||||
default:
|
||||
RUBY_ASSERT(FL_TEST_RAW(obj, FL_EXIVAR));
|
||||
struct gen_fields_tbl *fields_tbl;
|
||||
rb_ivar_generic_fields_tbl_lookup(obj, &fields_tbl);
|
||||
RUBY_ASSERT(fields_tbl);
|
||||
return fields_tbl->as.shape.fields[attr_index];
|
||||
}
|
||||
}
|
||||
|
||||
VALUE
|
||||
rb_ivar_lookup(VALUE obj, ID id, VALUE undef)
|
||||
{
|
||||
|
@ -1439,7 +1468,7 @@ rb_ivar_delete(VALUE obj, ID id, VALUE undef)
|
|||
|
||||
if (!rb_shape_transition_shape_remove_ivar(obj, id, shape, &val)) {
|
||||
if (!rb_shape_obj_too_complex(obj)) {
|
||||
rb_evict_ivars_to_hash(obj);
|
||||
rb_evict_fields_to_hash(obj);
|
||||
}
|
||||
|
||||
st_table *table = NULL;
|
||||
|
@ -1478,11 +1507,11 @@ rb_attr_delete(VALUE obj, ID id)
|
|||
return rb_ivar_delete(obj, id, Qnil);
|
||||
}
|
||||
|
||||
void
|
||||
rb_obj_convert_to_too_complex(VALUE obj, st_table *table)
|
||||
static void
|
||||
obj_transition_too_complex(VALUE obj, st_table *table)
|
||||
{
|
||||
RUBY_ASSERT(!rb_shape_obj_too_complex(obj));
|
||||
rb_shape_t *too_complex_shape = rb_shape_transition_shape_too_complex(obj);
|
||||
shape_id_t shape_id = rb_shape_id(rb_shape_transition_shape_too_complex(obj));
|
||||
|
||||
VALUE *old_fields = NULL;
|
||||
|
||||
|
@ -1491,13 +1520,13 @@ rb_obj_convert_to_too_complex(VALUE obj, st_table *table)
|
|||
if (!(RBASIC(obj)->flags & ROBJECT_EMBED)) {
|
||||
old_fields = ROBJECT_FIELDS(obj);
|
||||
}
|
||||
rb_shape_set_shape(obj, too_complex_shape);
|
||||
rb_shape_set_shape_id(obj, shape_id);
|
||||
ROBJECT_SET_FIELDS_HASH(obj, table);
|
||||
break;
|
||||
case T_CLASS:
|
||||
case T_MODULE:
|
||||
old_fields = RCLASS_FIELDS(obj);
|
||||
rb_shape_set_shape(obj, too_complex_shape);
|
||||
rb_shape_set_shape_id(obj, shape_id);
|
||||
RCLASS_SET_FIELDS_HASH(obj, table);
|
||||
break;
|
||||
default:
|
||||
|
@ -1514,9 +1543,9 @@ rb_obj_convert_to_too_complex(VALUE obj, st_table *table)
|
|||
* compaction. We want the table to be updated rather than
|
||||
* the original fields. */
|
||||
#if SHAPE_IN_BASIC_FLAGS
|
||||
rb_shape_set_shape(obj, too_complex_shape);
|
||||
rb_shape_set_shape_id(obj, shape_id);
|
||||
#else
|
||||
old_fields_tbl->shape_id = rb_shape_id(too_complex_shape);
|
||||
old_fields_tbl->shape_id = shape_id;
|
||||
#endif
|
||||
old_fields_tbl->as.complex.table = table;
|
||||
old_fields = (VALUE *)old_fields_tbl;
|
||||
|
@ -1525,11 +1554,11 @@ rb_obj_convert_to_too_complex(VALUE obj, st_table *table)
|
|||
struct gen_fields_tbl *fields_tbl = xmalloc(sizeof(struct gen_fields_tbl));
|
||||
fields_tbl->as.complex.table = table;
|
||||
st_insert(gen_ivs, (st_data_t)obj, (st_data_t)fields_tbl);
|
||||
|
||||
|
||||
#if SHAPE_IN_BASIC_FLAGS
|
||||
rb_shape_set_shape(obj, too_complex_shape);
|
||||
rb_shape_set_shape_id(obj, shape_id);
|
||||
#else
|
||||
fields_tbl->shape_id = rb_shape_id(too_complex_shape);
|
||||
fields_tbl->shape_id = shape_id;
|
||||
#endif
|
||||
}
|
||||
RB_VM_LOCK_LEAVE();
|
||||
|
@ -1538,6 +1567,33 @@ rb_obj_convert_to_too_complex(VALUE obj, st_table *table)
|
|||
xfree(old_fields);
|
||||
}
|
||||
|
||||
void
|
||||
rb_obj_init_too_complex(VALUE obj, st_table *table)
|
||||
{
|
||||
// This method is meant to be called on newly allocated object.
|
||||
RUBY_ASSERT(!rb_shape_obj_too_complex(obj));
|
||||
RUBY_ASSERT(rb_shape_canonical_p(rb_shape_get_shape(obj)));
|
||||
RUBY_ASSERT(rb_shape_get_shape(obj)->next_field_index == 0);
|
||||
|
||||
obj_transition_too_complex(obj, table);
|
||||
}
|
||||
|
||||
// Copy all object fields, including ivars and internal object_id, etc
|
||||
void
|
||||
rb_evict_fields_to_hash(VALUE obj)
|
||||
{
|
||||
void rb_obj_copy_fields_to_hash_table(VALUE obj, st_table *table);
|
||||
|
||||
RUBY_ASSERT(!rb_shape_obj_too_complex(obj));
|
||||
|
||||
rb_shape_t *shape = rb_shape_get_shape(obj);
|
||||
st_table *table = st_init_numtable_with_size(shape->next_field_index);
|
||||
rb_obj_copy_fields_to_hash_table(obj, table);
|
||||
obj_transition_too_complex(obj, table);
|
||||
|
||||
RUBY_ASSERT(rb_shape_obj_too_complex(obj));
|
||||
}
|
||||
|
||||
void
|
||||
rb_evict_ivars_to_hash(VALUE obj)
|
||||
{
|
||||
|
@ -1547,7 +1603,7 @@ rb_evict_ivars_to_hash(VALUE obj)
|
|||
|
||||
// Evacuate all previous values from shape into id_table
|
||||
rb_obj_copy_ivs_to_hash_table(obj, table);
|
||||
rb_obj_convert_to_too_complex(obj, table);
|
||||
obj_transition_too_complex(obj, table);
|
||||
|
||||
RUBY_ASSERT(rb_shape_obj_too_complex(obj));
|
||||
}
|
||||
|
@ -1618,6 +1674,42 @@ too_complex:
|
|||
return result;
|
||||
}
|
||||
|
||||
static void
|
||||
general_field_set(VALUE obj, rb_shape_t *target_shape, VALUE val, void *data,
|
||||
VALUE *(*shape_fields_func)(VALUE, void *),
|
||||
void (*shape_resize_fields_func)(VALUE, attr_index_t, attr_index_t, void *),
|
||||
void (*set_shape_func)(VALUE, rb_shape_t *, void *),
|
||||
void (*transition_too_complex_func)(VALUE, void *),
|
||||
st_table *(*too_complex_table_func)(VALUE, void *))
|
||||
{
|
||||
rb_shape_t *current_shape = rb_shape_get_shape(obj);
|
||||
|
||||
if (UNLIKELY(rb_shape_too_complex_p(target_shape))) {
|
||||
if (UNLIKELY(!rb_shape_too_complex_p(current_shape))) {
|
||||
transition_too_complex_func(obj, data);
|
||||
}
|
||||
|
||||
set_shape_func(obj, target_shape, data);
|
||||
|
||||
st_table *table = too_complex_table_func(obj, data);
|
||||
st_insert(table, (st_data_t)target_shape->edge_name, (st_data_t)val);
|
||||
RB_OBJ_WRITTEN(obj, Qundef, val);
|
||||
}
|
||||
else {
|
||||
attr_index_t index = target_shape->next_field_index - 1;
|
||||
if (index >= current_shape->capacity) {
|
||||
shape_resize_fields_func(obj, current_shape->capacity, target_shape->capacity, data);
|
||||
}
|
||||
|
||||
if (target_shape->next_field_index > current_shape->next_field_index) {
|
||||
set_shape_func(obj, target_shape, data);
|
||||
}
|
||||
|
||||
VALUE *table = shape_fields_func(obj, data);
|
||||
RB_OBJ_WRITE(obj, &table[index], val);
|
||||
}
|
||||
}
|
||||
|
||||
struct gen_fields_lookup_ensure_size {
|
||||
VALUE obj;
|
||||
ID id;
|
||||
|
@ -1627,34 +1719,34 @@ struct gen_fields_lookup_ensure_size {
|
|||
};
|
||||
|
||||
static int
|
||||
generic_ivar_lookup_ensure_size(st_data_t *k, st_data_t *v, st_data_t u, int existing)
|
||||
generic_fields_lookup_ensure_size(st_data_t *k, st_data_t *v, st_data_t u, int existing)
|
||||
{
|
||||
ASSERT_vm_locking();
|
||||
|
||||
struct gen_fields_lookup_ensure_size *ivar_lookup = (struct gen_fields_lookup_ensure_size *)u;
|
||||
struct gen_fields_lookup_ensure_size *fields_lookup = (struct gen_fields_lookup_ensure_size *)u;
|
||||
struct gen_fields_tbl *fields_tbl = existing ? (struct gen_fields_tbl *)*v : NULL;
|
||||
|
||||
if (!existing || ivar_lookup->resize) {
|
||||
if (!existing || fields_lookup->resize) {
|
||||
if (existing) {
|
||||
RUBY_ASSERT(ivar_lookup->shape->type == SHAPE_IVAR);
|
||||
RUBY_ASSERT(rb_shape_get_shape_by_id(ivar_lookup->shape->parent_id)->capacity < ivar_lookup->shape->capacity);
|
||||
RUBY_ASSERT(fields_lookup->shape->type == SHAPE_IVAR || fields_lookup->shape->type == SHAPE_OBJ_ID);
|
||||
RUBY_ASSERT(rb_shape_get_shape_by_id(fields_lookup->shape->parent_id)->capacity < fields_lookup->shape->capacity);
|
||||
}
|
||||
else {
|
||||
FL_SET_RAW((VALUE)*k, FL_EXIVAR);
|
||||
}
|
||||
|
||||
fields_tbl = gen_fields_tbl_resize(fields_tbl, ivar_lookup->shape->capacity);
|
||||
fields_tbl = gen_fields_tbl_resize(fields_tbl, fields_lookup->shape->capacity);
|
||||
*v = (st_data_t)fields_tbl;
|
||||
}
|
||||
|
||||
RUBY_ASSERT(FL_TEST((VALUE)*k, FL_EXIVAR));
|
||||
|
||||
ivar_lookup->fields_tbl = fields_tbl;
|
||||
if (ivar_lookup->shape) {
|
||||
fields_lookup->fields_tbl = fields_tbl;
|
||||
if (fields_lookup->shape) {
|
||||
#if SHAPE_IN_BASIC_FLAGS
|
||||
rb_shape_set_shape(ivar_lookup->obj, ivar_lookup->shape);
|
||||
rb_shape_set_shape(fields_lookup->obj, fields_lookup->shape);
|
||||
#else
|
||||
fields_tbl->shape_id = rb_shape_id(ivar_lookup->shape);
|
||||
fields_tbl->shape_id = rb_shape_id(fields_lookup->shape);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
@ -1666,46 +1758,46 @@ generic_ivar_set_shape_fields(VALUE obj, void *data)
|
|||
{
|
||||
RUBY_ASSERT(!rb_shape_obj_too_complex(obj));
|
||||
|
||||
struct gen_fields_lookup_ensure_size *ivar_lookup = data;
|
||||
struct gen_fields_lookup_ensure_size *fields_lookup = data;
|
||||
|
||||
RB_VM_LOCK_ENTER();
|
||||
{
|
||||
st_update(generic_fields_tbl(obj, ivar_lookup->id, false), (st_data_t)obj, generic_ivar_lookup_ensure_size, (st_data_t)ivar_lookup);
|
||||
st_update(generic_fields_tbl(obj, fields_lookup->id, false), (st_data_t)obj, generic_fields_lookup_ensure_size, (st_data_t)fields_lookup);
|
||||
}
|
||||
RB_VM_LOCK_LEAVE();
|
||||
|
||||
FL_SET_RAW(obj, FL_EXIVAR);
|
||||
|
||||
return ivar_lookup->fields_tbl->as.shape.fields;
|
||||
return fields_lookup->fields_tbl->as.shape.fields;
|
||||
}
|
||||
|
||||
static void
|
||||
generic_ivar_set_shape_resize_fields(VALUE obj, attr_index_t _old_capa, attr_index_t new_capa, void *data)
|
||||
{
|
||||
struct gen_fields_lookup_ensure_size *ivar_lookup = data;
|
||||
struct gen_fields_lookup_ensure_size *fields_lookup = data;
|
||||
|
||||
ivar_lookup->resize = true;
|
||||
fields_lookup->resize = true;
|
||||
}
|
||||
|
||||
static void
|
||||
generic_ivar_set_set_shape(VALUE obj, rb_shape_t *shape, void *data)
|
||||
{
|
||||
struct gen_fields_lookup_ensure_size *ivar_lookup = data;
|
||||
struct gen_fields_lookup_ensure_size *fields_lookup = data;
|
||||
|
||||
ivar_lookup->shape = shape;
|
||||
fields_lookup->shape = shape;
|
||||
}
|
||||
|
||||
static void
|
||||
generic_ivar_set_transition_too_complex(VALUE obj, void *_data)
|
||||
{
|
||||
rb_evict_ivars_to_hash(obj);
|
||||
rb_evict_fields_to_hash(obj);
|
||||
FL_SET_RAW(obj, FL_EXIVAR);
|
||||
}
|
||||
|
||||
static st_table *
|
||||
generic_ivar_set_too_complex_table(VALUE obj, void *data)
|
||||
{
|
||||
struct gen_fields_lookup_ensure_size *ivar_lookup = data;
|
||||
struct gen_fields_lookup_ensure_size *fields_lookup = data;
|
||||
|
||||
struct gen_fields_tbl *fields_tbl;
|
||||
if (!rb_gen_fields_tbl_get(obj, 0, &fields_tbl)) {
|
||||
|
@ -1717,7 +1809,7 @@ generic_ivar_set_too_complex_table(VALUE obj, void *data)
|
|||
|
||||
RB_VM_LOCK_ENTER();
|
||||
{
|
||||
st_insert(generic_fields_tbl(obj, ivar_lookup->id, false), (st_data_t)obj, (st_data_t)fields_tbl);
|
||||
st_insert(generic_fields_tbl(obj, fields_lookup->id, false), (st_data_t)obj, (st_data_t)fields_tbl);
|
||||
}
|
||||
RB_VM_LOCK_LEAVE();
|
||||
|
||||
|
@ -1732,14 +1824,14 @@ generic_ivar_set_too_complex_table(VALUE obj, void *data)
|
|||
static void
|
||||
generic_ivar_set(VALUE obj, ID id, VALUE val)
|
||||
{
|
||||
struct gen_fields_lookup_ensure_size ivar_lookup = {
|
||||
struct gen_fields_lookup_ensure_size fields_lookup = {
|
||||
.obj = obj,
|
||||
.id = id,
|
||||
.resize = false,
|
||||
.shape = NULL,
|
||||
};
|
||||
|
||||
general_ivar_set(obj, id, val, &ivar_lookup,
|
||||
general_ivar_set(obj, id, val, &fields_lookup,
|
||||
generic_ivar_set_shape_fields,
|
||||
generic_ivar_set_shape_resize_fields,
|
||||
generic_ivar_set_set_shape,
|
||||
|
@ -1747,6 +1839,23 @@ generic_ivar_set(VALUE obj, ID id, VALUE val)
|
|||
generic_ivar_set_too_complex_table);
|
||||
}
|
||||
|
||||
static void
|
||||
generic_field_set(VALUE obj, rb_shape_t *target_shape, VALUE val)
|
||||
{
|
||||
struct gen_fields_lookup_ensure_size fields_lookup = {
|
||||
.obj = obj,
|
||||
.resize = false,
|
||||
.shape = NULL,
|
||||
};
|
||||
|
||||
general_field_set(obj, target_shape, val, &fields_lookup,
|
||||
generic_ivar_set_shape_fields,
|
||||
generic_ivar_set_shape_resize_fields,
|
||||
generic_ivar_set_set_shape,
|
||||
generic_ivar_set_transition_too_complex,
|
||||
generic_ivar_set_too_complex_table);
|
||||
}
|
||||
|
||||
void
|
||||
rb_ensure_iv_list_size(VALUE obj, uint32_t current_capacity, uint32_t new_capacity)
|
||||
{
|
||||
|
@ -1779,6 +1888,12 @@ rb_obj_copy_ivs_to_hash_table(VALUE obj, st_table *table)
|
|||
rb_ivar_foreach(obj, rb_obj_copy_ivs_to_hash_table_i, (st_data_t)table);
|
||||
}
|
||||
|
||||
void
|
||||
rb_obj_copy_fields_to_hash_table(VALUE obj, st_table *table)
|
||||
{
|
||||
rb_field_foreach(obj, rb_obj_copy_ivs_to_hash_table_i, (st_data_t)table, false);
|
||||
}
|
||||
|
||||
static VALUE *
|
||||
obj_ivar_set_shape_fields(VALUE obj, void *_data)
|
||||
{
|
||||
|
@ -1802,7 +1917,7 @@ obj_ivar_set_set_shape(VALUE obj, rb_shape_t *shape, void *_data)
|
|||
static void
|
||||
obj_ivar_set_transition_too_complex(VALUE obj, void *_data)
|
||||
{
|
||||
rb_evict_ivars_to_hash(obj);
|
||||
rb_evict_fields_to_hash(obj);
|
||||
}
|
||||
|
||||
static st_table *
|
||||
|
@ -1824,6 +1939,17 @@ rb_obj_ivar_set(VALUE obj, ID id, VALUE val)
|
|||
obj_ivar_set_too_complex_table).index;
|
||||
}
|
||||
|
||||
static void
|
||||
obj_field_set(VALUE obj, rb_shape_t *target_shape, VALUE val)
|
||||
{
|
||||
general_field_set(obj, target_shape, val, NULL,
|
||||
obj_ivar_set_shape_fields,
|
||||
obj_ivar_set_shape_resize_fields,
|
||||
obj_ivar_set_set_shape,
|
||||
obj_ivar_set_transition_too_complex,
|
||||
obj_ivar_set_too_complex_table);
|
||||
}
|
||||
|
||||
/* Set the instance variable +val+ on object +obj+ at ivar name +id+.
|
||||
* This function only works with T_OBJECT objects, so make sure
|
||||
* +obj+ is of type T_OBJECT before using this function.
|
||||
|
@ -1888,8 +2014,8 @@ void rb_obj_freeze_inline(VALUE x)
|
|||
|
||||
// If we're transitioning from "not complex" to "too complex"
|
||||
// then evict ivars. This can happen if we run out of shapes
|
||||
if (!rb_shape_obj_too_complex(x) && rb_shape_too_complex_p(next_shape)) {
|
||||
rb_evict_ivars_to_hash(x);
|
||||
if (rb_shape_too_complex_p(next_shape) && !rb_shape_obj_too_complex(x)) {
|
||||
rb_evict_fields_to_hash(x);
|
||||
}
|
||||
rb_shape_set_shape(x, next_shape);
|
||||
|
||||
|
@ -1939,6 +2065,26 @@ rb_ivar_set_internal(VALUE obj, ID id, VALUE val)
|
|||
ivar_set(obj, id, val);
|
||||
}
|
||||
|
||||
static void class_field_set(VALUE obj, rb_shape_t *target_shape, VALUE val);
|
||||
|
||||
void
|
||||
rb_obj_field_set(VALUE obj, rb_shape_t *target_shape, VALUE val)
|
||||
{
|
||||
switch (BUILTIN_TYPE(obj)) {
|
||||
case T_OBJECT:
|
||||
obj_field_set(obj, target_shape, val);
|
||||
break;
|
||||
case T_CLASS:
|
||||
case T_MODULE:
|
||||
ASSERT_vm_locking();
|
||||
class_field_set(obj, target_shape, val);
|
||||
break;
|
||||
default:
|
||||
generic_field_set(obj, target_shape, val);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
VALUE
|
||||
rb_ivar_defined(VALUE obj, ID id)
|
||||
{
|
||||
|
@ -1978,29 +2124,35 @@ rb_ivar_defined(VALUE obj, ID id)
|
|||
}
|
||||
}
|
||||
|
||||
typedef int rb_ivar_foreach_callback_func(ID key, VALUE val, st_data_t arg);
|
||||
|
||||
struct iv_itr_data {
|
||||
VALUE obj;
|
||||
struct gen_fields_tbl * fields_tbl;
|
||||
struct gen_fields_tbl *fields_tbl;
|
||||
st_data_t arg;
|
||||
rb_ivar_foreach_callback_func *func;
|
||||
bool ivar_only;
|
||||
};
|
||||
|
||||
/*
|
||||
* Returns a flag to stop iterating depending on the result of +callback+.
|
||||
*/
|
||||
static bool
|
||||
iterate_over_shapes_with_callback(rb_shape_t *shape, rb_ivar_foreach_callback_func *callback, struct iv_itr_data * itr_data)
|
||||
iterate_over_shapes_with_callback(rb_shape_t *shape, rb_ivar_foreach_callback_func *callback, struct iv_itr_data *itr_data)
|
||||
{
|
||||
switch ((enum shape_type)shape->type) {
|
||||
case SHAPE_ROOT:
|
||||
case SHAPE_T_OBJECT:
|
||||
return false;
|
||||
case SHAPE_OBJ_ID:
|
||||
if (itr_data->ivar_only) {
|
||||
return iterate_over_shapes_with_callback(rb_shape_get_parent(shape), callback, itr_data);
|
||||
}
|
||||
// fallthrough
|
||||
case SHAPE_IVAR:
|
||||
ASSUME(callback);
|
||||
if (iterate_over_shapes_with_callback(rb_shape_get_parent(shape), callback, itr_data))
|
||||
if (iterate_over_shapes_with_callback(rb_shape_get_parent(shape), callback, itr_data)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
VALUE * iv_list;
|
||||
switch (BUILTIN_TYPE(itr_data->obj)) {
|
||||
case T_OBJECT:
|
||||
|
@ -2044,14 +2196,17 @@ each_hash_iv(st_data_t id, st_data_t val, st_data_t data)
|
|||
}
|
||||
|
||||
static void
|
||||
obj_ivar_each(VALUE obj, rb_ivar_foreach_callback_func *func, st_data_t arg)
|
||||
obj_fields_each(VALUE obj, rb_ivar_foreach_callback_func *func, st_data_t arg, bool ivar_only)
|
||||
{
|
||||
struct iv_itr_data itr_data = {
|
||||
.obj = obj,
|
||||
.arg = arg,
|
||||
.func = func,
|
||||
.ivar_only = ivar_only,
|
||||
};
|
||||
|
||||
rb_shape_t *shape = rb_shape_get_shape(obj);
|
||||
struct iv_itr_data itr_data;
|
||||
itr_data.obj = obj;
|
||||
itr_data.arg = arg;
|
||||
itr_data.func = func;
|
||||
if (rb_shape_obj_too_complex(obj)) {
|
||||
if (rb_shape_too_complex_p(shape)) {
|
||||
rb_st_foreach(ROBJECT_FIELDS_HASH(obj), each_hash_iv, (st_data_t)&itr_data);
|
||||
}
|
||||
else {
|
||||
|
@ -2060,17 +2215,20 @@ obj_ivar_each(VALUE obj, rb_ivar_foreach_callback_func *func, st_data_t arg)
|
|||
}
|
||||
|
||||
static void
|
||||
gen_fields_each(VALUE obj, rb_ivar_foreach_callback_func *func, st_data_t arg)
|
||||
gen_fields_each(VALUE obj, rb_ivar_foreach_callback_func *func, st_data_t arg, bool ivar_only)
|
||||
{
|
||||
rb_shape_t *shape = rb_shape_get_shape(obj);
|
||||
struct gen_fields_tbl *fields_tbl;
|
||||
if (!rb_gen_fields_tbl_get(obj, 0, &fields_tbl)) return;
|
||||
|
||||
struct iv_itr_data itr_data;
|
||||
itr_data.obj = obj;
|
||||
itr_data.fields_tbl = fields_tbl;
|
||||
itr_data.arg = arg;
|
||||
itr_data.func = func;
|
||||
struct iv_itr_data itr_data = {
|
||||
.obj = obj,
|
||||
.fields_tbl = fields_tbl,
|
||||
.arg = arg,
|
||||
.func = func,
|
||||
.ivar_only = ivar_only,
|
||||
};
|
||||
|
||||
if (rb_shape_obj_too_complex(obj)) {
|
||||
rb_st_foreach(fields_tbl->as.complex.table, each_hash_iv, (st_data_t)&itr_data);
|
||||
}
|
||||
|
@ -2080,15 +2238,18 @@ gen_fields_each(VALUE obj, rb_ivar_foreach_callback_func *func, st_data_t arg)
|
|||
}
|
||||
|
||||
static void
|
||||
class_ivar_each(VALUE obj, rb_ivar_foreach_callback_func *func, st_data_t arg)
|
||||
class_fields_each(VALUE obj, rb_ivar_foreach_callback_func *func, st_data_t arg, bool ivar_only)
|
||||
{
|
||||
RUBY_ASSERT(RB_TYPE_P(obj, T_CLASS) || RB_TYPE_P(obj, T_MODULE));
|
||||
|
||||
rb_shape_t *shape = rb_shape_get_shape(obj);
|
||||
struct iv_itr_data itr_data;
|
||||
itr_data.obj = obj;
|
||||
itr_data.arg = arg;
|
||||
itr_data.func = func;
|
||||
struct iv_itr_data itr_data = {
|
||||
.obj = obj,
|
||||
.arg = arg,
|
||||
.func = func,
|
||||
.ivar_only = ivar_only,
|
||||
};
|
||||
|
||||
if (rb_shape_obj_too_complex(obj)) {
|
||||
rb_st_foreach(RCLASS_FIELDS_HASH(obj), each_hash_iv, (st_data_t)&itr_data);
|
||||
}
|
||||
|
@ -2098,35 +2259,89 @@ class_ivar_each(VALUE obj, rb_ivar_foreach_callback_func *func, st_data_t arg)
|
|||
}
|
||||
|
||||
void
|
||||
rb_copy_generic_ivar(VALUE clone, VALUE obj)
|
||||
rb_copy_generic_ivar(VALUE dest, VALUE obj)
|
||||
{
|
||||
struct gen_fields_tbl *obj_fields_tbl;
|
||||
struct gen_fields_tbl *new_fields_tbl;
|
||||
|
||||
rb_check_frozen(clone);
|
||||
rb_check_frozen(dest);
|
||||
|
||||
if (!FL_TEST(obj, FL_EXIVAR)) {
|
||||
goto clear;
|
||||
}
|
||||
|
||||
unsigned long src_num_ivs = rb_ivar_count(obj);
|
||||
if (!src_num_ivs) {
|
||||
goto clear;
|
||||
}
|
||||
|
||||
rb_shape_t *src_shape = rb_shape_get_shape(obj);
|
||||
|
||||
if (rb_gen_fields_tbl_get(obj, 0, &obj_fields_tbl)) {
|
||||
if (gen_fields_tbl_count(obj, obj_fields_tbl) == 0)
|
||||
goto clear;
|
||||
|
||||
FL_SET(clone, FL_EXIVAR);
|
||||
FL_SET(dest, FL_EXIVAR);
|
||||
|
||||
if (rb_shape_obj_too_complex(obj)) {
|
||||
new_fields_tbl = xmalloc(sizeof(struct gen_fields_tbl));
|
||||
#if !SHAPE_IN_BASIC_FLAGS
|
||||
new_fields_tbl->shape_id = old_fields_tbl->shape_id;
|
||||
#endif
|
||||
new_fields_tbl->as.complex.table = st_copy(obj_fields_tbl->as.complex.table);
|
||||
if (rb_shape_too_complex_p(src_shape)) {
|
||||
// obj is TOO_COMPLEX so we can copy its iv_hash
|
||||
st_table *table = st_copy(obj_fields_tbl->as.complex.table);
|
||||
if (rb_shape_has_object_id(src_shape)) {
|
||||
st_data_t id = (st_data_t)ruby_internal_object_id;
|
||||
st_delete(table, &id, NULL);
|
||||
}
|
||||
rb_obj_init_too_complex(dest, table);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
rb_shape_t *shape_to_set_on_dest = src_shape;
|
||||
rb_shape_t *initial_shape = rb_shape_get_shape(dest);
|
||||
|
||||
if (!rb_shape_canonical_p(src_shape)) {
|
||||
RUBY_ASSERT(initial_shape->type == SHAPE_ROOT);
|
||||
|
||||
shape_to_set_on_dest = rb_shape_rebuild_shape(initial_shape, src_shape);
|
||||
if (UNLIKELY(rb_shape_too_complex_p(shape_to_set_on_dest))) {
|
||||
st_table *table = rb_st_init_numtable_with_size(src_num_ivs);
|
||||
rb_obj_copy_ivs_to_hash_table(obj, table);
|
||||
rb_obj_init_too_complex(dest, table);
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (!shape_to_set_on_dest->capacity) {
|
||||
rb_shape_set_shape(dest, shape_to_set_on_dest);
|
||||
FL_UNSET(dest, FL_EXIVAR);
|
||||
return;
|
||||
}
|
||||
|
||||
new_fields_tbl = gen_fields_tbl_resize(0, shape_to_set_on_dest->capacity);
|
||||
|
||||
VALUE *src_buf = obj_fields_tbl->as.shape.fields;
|
||||
VALUE *dest_buf = new_fields_tbl->as.shape.fields;
|
||||
|
||||
if (src_shape->next_field_index == shape_to_set_on_dest->next_field_index) {
|
||||
// Happy path, we can just memcpy the ivptr content
|
||||
MEMCPY(dest_buf, src_buf, VALUE, shape_to_set_on_dest->next_field_index);
|
||||
|
||||
// Fire write barriers
|
||||
for (uint32_t i = 0; i < shape_to_set_on_dest->next_field_index; i++) {
|
||||
RB_OBJ_WRITTEN(dest, Qundef, dest_buf[i]);
|
||||
}
|
||||
}
|
||||
else {
|
||||
new_fields_tbl = gen_fields_tbl_resize(0, obj_fields_tbl->as.shape.fields_count);
|
||||
rb_shape_t *dest_shape = shape_to_set_on_dest;
|
||||
while (src_shape->parent_id != INVALID_SHAPE_ID) {
|
||||
if (src_shape->type == SHAPE_IVAR) {
|
||||
while (dest_shape->edge_name != src_shape->edge_name) {
|
||||
dest_shape = rb_shape_get_shape_by_id(dest_shape->parent_id);
|
||||
}
|
||||
|
||||
for (uint32_t i=0; i<obj_fields_tbl->as.shape.fields_count; i++) {
|
||||
RB_OBJ_WRITE(clone, &new_fields_tbl->as.shape.fields[i], obj_fields_tbl->as.shape.fields[i]);
|
||||
RB_OBJ_WRITE(dest, &dest_buf[dest_shape->next_field_index - 1], src_buf[src_shape->next_field_index - 1]);
|
||||
}
|
||||
src_shape = rb_shape_get_shape_by_id(src_shape->parent_id);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2136,25 +2351,19 @@ rb_copy_generic_ivar(VALUE clone, VALUE obj)
|
|||
*/
|
||||
RB_VM_LOCK_ENTER();
|
||||
{
|
||||
generic_fields_tbl_no_ractor_check(clone);
|
||||
st_insert(generic_fields_tbl_no_ractor_check(obj), (st_data_t)clone, (st_data_t)new_fields_tbl);
|
||||
generic_fields_tbl_no_ractor_check(dest);
|
||||
st_insert(generic_fields_tbl_no_ractor_check(obj), (st_data_t)dest, (st_data_t)new_fields_tbl);
|
||||
}
|
||||
RB_VM_LOCK_LEAVE();
|
||||
|
||||
rb_shape_t *obj_shape = rb_shape_get_shape(obj);
|
||||
if (rb_shape_frozen_shape_p(obj_shape)) {
|
||||
rb_shape_set_shape_id(clone, obj_shape->parent_id);
|
||||
}
|
||||
else {
|
||||
rb_shape_set_shape(clone, obj_shape);
|
||||
}
|
||||
rb_shape_set_shape(dest, shape_to_set_on_dest);
|
||||
}
|
||||
return;
|
||||
|
||||
clear:
|
||||
if (FL_TEST(clone, FL_EXIVAR)) {
|
||||
rb_free_generic_ivar(clone);
|
||||
FL_UNSET(clone, FL_EXIVAR);
|
||||
if (FL_TEST(dest, FL_EXIVAR)) {
|
||||
rb_free_generic_ivar(dest);
|
||||
FL_UNSET(dest, FL_EXIVAR);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2180,52 +2389,66 @@ rb_replace_generic_ivar(VALUE clone, VALUE obj)
|
|||
}
|
||||
|
||||
void
|
||||
rb_ivar_foreach(VALUE obj, rb_ivar_foreach_callback_func *func, st_data_t arg)
|
||||
rb_field_foreach(VALUE obj, rb_ivar_foreach_callback_func *func, st_data_t arg, bool ivar_only)
|
||||
{
|
||||
if (SPECIAL_CONST_P(obj)) return;
|
||||
switch (BUILTIN_TYPE(obj)) {
|
||||
case T_OBJECT:
|
||||
obj_ivar_each(obj, func, arg);
|
||||
obj_fields_each(obj, func, arg, ivar_only);
|
||||
break;
|
||||
case T_CLASS:
|
||||
case T_MODULE:
|
||||
IVAR_ACCESSOR_SHOULD_BE_MAIN_RACTOR(0);
|
||||
RB_VM_LOCK_ENTER();
|
||||
{
|
||||
class_ivar_each(obj, func, arg);
|
||||
class_fields_each(obj, func, arg, ivar_only);
|
||||
}
|
||||
RB_VM_LOCK_LEAVE();
|
||||
break;
|
||||
default:
|
||||
if (FL_TEST(obj, FL_EXIVAR)) {
|
||||
gen_fields_each(obj, func, arg);
|
||||
gen_fields_each(obj, func, arg, ivar_only);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
rb_ivar_foreach(VALUE obj, rb_ivar_foreach_callback_func *func, st_data_t arg)
|
||||
{
|
||||
rb_field_foreach(obj, func, arg, true);
|
||||
}
|
||||
|
||||
st_index_t
|
||||
rb_ivar_count(VALUE obj)
|
||||
{
|
||||
if (SPECIAL_CONST_P(obj)) return 0;
|
||||
|
||||
st_index_t iv_count = 0;
|
||||
switch (BUILTIN_TYPE(obj)) {
|
||||
case T_OBJECT:
|
||||
return ROBJECT_FIELDS_COUNT(obj);
|
||||
iv_count = ROBJECT_FIELDS_COUNT(obj);
|
||||
break;
|
||||
case T_CLASS:
|
||||
case T_MODULE:
|
||||
return RCLASS_FIELDS_COUNT(obj);
|
||||
iv_count = RCLASS_FIELDS_COUNT(obj);
|
||||
break;
|
||||
default:
|
||||
if (FL_TEST(obj, FL_EXIVAR)) {
|
||||
struct gen_fields_tbl *fields_tbl;
|
||||
|
||||
if (rb_gen_fields_tbl_get(obj, 0, &fields_tbl)) {
|
||||
return gen_fields_tbl_count(obj, fields_tbl);
|
||||
iv_count = gen_fields_tbl_count(obj, fields_tbl);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
|
||||
if (rb_shape_obj_has_id(obj)) {
|
||||
iv_count--;
|
||||
}
|
||||
|
||||
return iv_count;
|
||||
}
|
||||
|
||||
static int
|
||||
|
@ -4304,7 +4527,7 @@ class_ivar_set_set_shape(VALUE obj, rb_shape_t *shape, void *_data)
|
|||
static void
|
||||
class_ivar_set_transition_too_complex(VALUE obj, void *_data)
|
||||
{
|
||||
rb_evict_ivars_to_hash(obj);
|
||||
rb_evict_fields_to_hash(obj);
|
||||
}
|
||||
|
||||
static st_table *
|
||||
|
@ -4336,6 +4559,18 @@ rb_class_ivar_set(VALUE obj, ID id, VALUE val)
|
|||
return existing;
|
||||
}
|
||||
|
||||
static void
|
||||
class_field_set(VALUE obj, rb_shape_t *target_shape, VALUE val)
|
||||
{
|
||||
RUBY_ASSERT(RB_TYPE_P(obj, T_CLASS) || RB_TYPE_P(obj, T_MODULE));
|
||||
general_field_set(obj, target_shape, val, NULL,
|
||||
class_ivar_set_shape_fields,
|
||||
class_ivar_set_shape_resize_fields,
|
||||
class_ivar_set_set_shape,
|
||||
class_ivar_set_transition_too_complex,
|
||||
class_ivar_set_too_complex_table);
|
||||
}
|
||||
|
||||
static int
|
||||
tbl_copy_i(ID key, VALUE val, st_data_t dest)
|
||||
{
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue