summaryrefslogtreecommitdiff
path: root/variable.c
diff options
context:
space:
mode:
Diffstat (limited to 'variable.c')
-rw-r--r--variable.c121
1 files changed, 49 insertions, 72 deletions
diff --git a/variable.c b/variable.c
index 93ae6bb8b2..6bd9f69d06 100644
--- a/variable.c
+++ b/variable.c
@@ -1255,20 +1255,23 @@ rb_mark_generic_ivar(VALUE obj)
void
rb_free_generic_ivar(VALUE obj)
{
- st_data_t key = (st_data_t)obj, value;
+ if (rb_obj_exivar_p(obj)) {
+ st_data_t key = (st_data_t)obj, value;
- bool too_complex = rb_shape_obj_too_complex_p(obj);
+ bool too_complex = rb_shape_obj_too_complex_p(obj);
- RB_VM_LOCKING() {
- if (st_delete(generic_fields_tbl_no_ractor_check(obj), &key, &value)) {
- struct gen_fields_tbl *fields_tbl = (struct gen_fields_tbl *)value;
+ RB_VM_LOCKING() {
+ if (st_delete(generic_fields_tbl_no_ractor_check(obj), &key, &value)) {
+ struct gen_fields_tbl *fields_tbl = (struct gen_fields_tbl *)value;
- if (UNLIKELY(too_complex)) {
- st_free_table(fields_tbl->as.complex.table);
- }
+ if (UNLIKELY(too_complex)) {
+ st_free_table(fields_tbl->as.complex.table);
+ }
- xfree(fields_tbl);
+ xfree(fields_tbl);
+ }
}
+ RBASIC_SET_SHAPE_ID(obj, ROOT_SHAPE_ID);
}
}
@@ -1325,7 +1328,7 @@ rb_obj_field_get(VALUE obj, shape_id_t target_shape_id)
fields_hash = ROBJECT_FIELDS_HASH(obj);
break;
default:
- RUBY_ASSERT(FL_TEST_RAW(obj, FL_EXIVAR));
+ RUBY_ASSERT(rb_obj_exivar_p(obj));
struct gen_fields_tbl *fields_tbl = NULL;
rb_ivar_generic_fields_tbl_lookup(obj, &fields_tbl);
RUBY_ASSERT(fields_tbl);
@@ -1356,7 +1359,7 @@ rb_obj_field_get(VALUE obj, shape_id_t target_shape_id)
fields = ROBJECT_FIELDS(obj);
break;
default:
- RUBY_ASSERT(FL_TEST_RAW(obj, FL_EXIVAR));
+ RUBY_ASSERT(rb_obj_exivar_p(obj));
struct gen_fields_tbl *fields_tbl = NULL;
rb_ivar_generic_fields_tbl_lookup(obj, &fields_tbl);
RUBY_ASSERT(fields_tbl);
@@ -1434,7 +1437,7 @@ rb_ivar_lookup(VALUE obj, ID id, VALUE undef)
}
default:
shape_id = RBASIC_SHAPE_ID(obj);
- if (FL_TEST_RAW(obj, FL_EXIVAR)) {
+ if (rb_obj_exivar_p(obj)) {
struct gen_fields_tbl *fields_tbl;
rb_gen_fields_tbl_get(obj, id, &fields_tbl);
@@ -1542,13 +1545,18 @@ rb_ivar_delete(VALUE obj, ID id, VALUE undef)
RUBY_ASSERT(removed_shape_id != INVALID_SHAPE_ID);
- attr_index_t new_fields_count = RSHAPE_LEN(next_shape_id);
-
attr_index_t removed_index = RSHAPE_INDEX(removed_shape_id);
val = fields[removed_index];
- size_t trailing_fields = new_fields_count - removed_index;
- MEMMOVE(&fields[removed_index], &fields[removed_index + 1], VALUE, trailing_fields);
+ attr_index_t new_fields_count = RSHAPE_LEN(next_shape_id);
+ if (new_fields_count) {
+ size_t trailing_fields = new_fields_count - removed_index;
+
+ MEMMOVE(&fields[removed_index], &fields[removed_index + 1], VALUE, trailing_fields);
+ }
+ else {
+ rb_free_generic_ivar(obj);
+ }
if (RB_TYPE_P(obj, T_OBJECT) &&
!RB_FL_TEST_RAW(obj, ROBJECT_EMBED) &&
@@ -1810,56 +1818,40 @@ general_field_set(VALUE obj, shape_id_t target_shape_id, VALUE val, void *data,
struct gen_fields_lookup_ensure_size {
VALUE obj;
ID id;
- struct gen_fields_tbl *fields_tbl;
shape_id_t shape_id;
bool resize;
};
-static int
-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 *fields_lookup = (struct gen_fields_lookup_ensure_size *)u;
- struct gen_fields_tbl *fields_tbl = existing ? (struct gen_fields_tbl *)*v : NULL;
-
- if (!existing || fields_lookup->resize) {
- if (existing) {
- RUBY_ASSERT(RSHAPE_TYPE_P(fields_lookup->shape_id, SHAPE_IVAR) || RSHAPE_TYPE_P(fields_lookup->shape_id, SHAPE_OBJ_ID));
- RUBY_ASSERT(RSHAPE_CAPACITY(RSHAPE_PARENT(fields_lookup->shape_id)) < RSHAPE_CAPACITY(fields_lookup->shape_id));
- }
- else {
- FL_SET_RAW((VALUE)*k, FL_EXIVAR);
- }
-
- fields_tbl = gen_fields_tbl_resize(fields_tbl, RSHAPE_CAPACITY(fields_lookup->shape_id));
- *v = (st_data_t)fields_tbl;
- }
-
- RUBY_ASSERT(FL_TEST((VALUE)*k, FL_EXIVAR));
-
- fields_lookup->fields_tbl = fields_tbl;
- if (fields_lookup->shape_id) {
- rb_obj_set_shape_id(fields_lookup->obj, fields_lookup->shape_id);
- }
-
- return ST_CONTINUE;
-}
-
static VALUE *
generic_ivar_set_shape_fields(VALUE obj, void *data)
{
RUBY_ASSERT(!rb_shape_obj_too_complex_p(obj));
struct gen_fields_lookup_ensure_size *fields_lookup = data;
+ struct gen_fields_tbl *fields_tbl = NULL;
+ // We can't use st_update, since when resizing the fields table GC can
+ // happen, which will modify the st_table and may rebuild it
RB_VM_LOCKING() {
- st_update(generic_fields_tbl(obj, fields_lookup->id, false), (st_data_t)obj, generic_fields_lookup_ensure_size, (st_data_t)fields_lookup);
- }
+ st_table *tbl = generic_fields_tbl(obj, fields_lookup->id, false);
+ int existing = st_lookup(tbl, (st_data_t)obj, (st_data_t *)&fields_tbl);
+
+ if (!existing || fields_lookup->resize) {
+ if (existing) {
+ RUBY_ASSERT(RSHAPE_TYPE_P(fields_lookup->shape_id, SHAPE_IVAR) || RSHAPE_TYPE_P(fields_lookup->shape_id, SHAPE_OBJ_ID));
+ RUBY_ASSERT(RSHAPE_CAPACITY(RSHAPE_PARENT(fields_lookup->shape_id)) < RSHAPE_CAPACITY(fields_lookup->shape_id));
+ }
- FL_SET_RAW(obj, FL_EXIVAR);
+ fields_tbl = gen_fields_tbl_resize(fields_tbl, RSHAPE_CAPACITY(fields_lookup->shape_id));
+ st_insert(tbl, (st_data_t)obj, (st_data_t)fields_tbl);
+ }
+
+ if (fields_lookup->shape_id) {
+ rb_obj_set_shape_id(fields_lookup->obj, fields_lookup->shape_id);
+ }
+ }
- return fields_lookup->fields_tbl->as.shape.fields;
+ return fields_tbl->as.shape.fields;
}
static void
@@ -1882,7 +1874,6 @@ static shape_id_t
generic_ivar_set_transition_too_complex(VALUE obj, void *_data)
{
shape_id_t new_shape_id = rb_evict_fields_to_hash(obj);
- FL_SET_RAW(obj, FL_EXIVAR);
return new_shape_id;
}
@@ -1899,8 +1890,6 @@ generic_ivar_set_too_complex_table(VALUE obj, void *data)
RB_VM_LOCKING() {
st_insert(generic_fields_tbl(obj, fields_lookup->id, false), (st_data_t)obj, (st_data_t)fields_tbl);
}
-
- FL_SET_RAW(obj, FL_EXIVAR);
}
RUBY_ASSERT(rb_shape_obj_too_complex_p(obj));
@@ -2341,8 +2330,8 @@ rb_copy_generic_ivar(VALUE dest, VALUE obj)
rb_check_frozen(dest);
- if (!FL_TEST(obj, FL_EXIVAR)) {
- goto clear;
+ if (!rb_obj_exivar_p(obj)) {
+ return;
}
unsigned long src_num_ivs = rb_ivar_count(obj);
@@ -2356,8 +2345,6 @@ rb_copy_generic_ivar(VALUE dest, VALUE obj)
if (gen_fields_tbl_count(obj, obj_fields_tbl) == 0)
goto clear;
- FL_SET(dest, FL_EXIVAR);
-
if (rb_shape_too_complex_p(src_shape_id)) {
rb_shape_copy_complex_ivars(dest, obj, src_shape_id, obj_fields_tbl->as.complex.table);
return;
@@ -2381,7 +2368,6 @@ rb_copy_generic_ivar(VALUE dest, VALUE obj)
if (!RSHAPE_LEN(dest_shape_id)) {
rb_obj_set_shape_id(dest, dest_shape_id);
- FL_UNSET(dest, FL_EXIVAR);
return;
}
@@ -2406,25 +2392,16 @@ rb_copy_generic_ivar(VALUE dest, VALUE obj)
return;
clear:
- if (FL_TEST(dest, FL_EXIVAR)) {
- RBASIC_SET_SHAPE_ID(dest, ROOT_SHAPE_ID);
- rb_free_generic_ivar(dest);
- FL_UNSET(dest, FL_EXIVAR);
- }
+ rb_free_generic_ivar(dest);
}
void
rb_replace_generic_ivar(VALUE clone, VALUE obj)
{
- RUBY_ASSERT(FL_TEST(obj, FL_EXIVAR));
-
RB_VM_LOCKING() {
st_data_t fields_tbl, obj_data = (st_data_t)obj;
if (st_delete(generic_fields_tbl_, &obj_data, &fields_tbl)) {
- FL_UNSET_RAW(obj, FL_EXIVAR);
-
st_insert(generic_fields_tbl_, (st_data_t)clone, fields_tbl);
- FL_SET_RAW(clone, FL_EXIVAR);
}
else {
rb_bug("unreachable");
@@ -2456,7 +2433,7 @@ rb_field_foreach(VALUE obj, rb_ivar_foreach_callback_func *func, st_data_t arg,
}
break;
default:
- if (FL_TEST_RAW(obj, FL_EXIVAR)) {
+ if (rb_obj_exivar_p(obj)) {
gen_fields_each(obj, func, arg, ivar_only);
}
break;
@@ -2492,7 +2469,7 @@ rb_ivar_count(VALUE obj)
return RBASIC_FIELDS_COUNT(fields_obj);
}
default:
- if (FL_TEST(obj, FL_EXIVAR)) {
+ if (rb_obj_exivar_p(obj)) {
struct gen_fields_tbl *fields_tbl;
if (rb_gen_fields_tbl_get(obj, 0, &fields_tbl)) {