summaryrefslogtreecommitdiff
path: root/variable.c
diff options
context:
space:
mode:
authorJean Boussier <[email protected]>2025-05-23 15:55:38 +0200
committerJean Boussier <[email protected]>2025-05-23 17:33:17 +0200
commit52da5f8bbc705e75d89403df281fcf95d30cbe15 (patch)
tree94fe922559bbb29c8bff960f9f800e8af5247590 /variable.c
parent746d7fef9276c0e018cae24642a87ed486f8e212 (diff)
Refactor `rb_shape_transition_remove_ivar`
Move the fields management logic in `rb_ivar_delete`, and keep shape managment logic in `rb_shape_transition_remove_ivar`.
Notes
Notes: Merged: https://github.com/ruby/ruby/pull/13426
Diffstat (limited to 'variable.c')
-rw-r--r--variable.c66
1 files changed, 61 insertions, 5 deletions
diff --git a/variable.c b/variable.c
index 661a0ef04f..fd88849689 100644
--- a/variable.c
+++ b/variable.c
@@ -1542,11 +1542,68 @@ rb_ivar_delete(VALUE obj, ID id, VALUE undef)
IVAR_ACCESSOR_SHOULD_BE_MAIN_RACTOR(id);
}
- if (!rb_shape_transition_remove_ivar(obj, id, &val)) {
- if (!rb_shape_obj_too_complex_p(obj)) {
- rb_evict_fields_to_hash(obj);
- }
+ shape_id_t old_shape_id = rb_obj_shape_id(obj);
+ if (rb_shape_id_too_complex_p(old_shape_id)) {
+ goto too_complex;
+ }
+
+ shape_id_t removed_shape_id = 0;
+ shape_id_t next_shape_id = rb_shape_transition_remove_ivar(obj, id, &removed_shape_id);
+
+ if (next_shape_id == old_shape_id) {
+ return undef;
+ }
+
+ if (UNLIKELY(rb_shape_id_too_complex_p(next_shape_id))) {
+ rb_evict_fields_to_hash(obj);
+ goto too_complex;
+ }
+
+ RUBY_ASSERT(RSHAPE(next_shape_id)->next_field_index == RSHAPE(old_shape_id)->next_field_index - 1);
+
+ VALUE *fields;
+ switch(BUILTIN_TYPE(obj)) {
+ case T_CLASS:
+ case T_MODULE:
+ fields = RCLASS_PRIME_FIELDS(obj);
+ break;
+ case T_OBJECT:
+ fields = ROBJECT_FIELDS(obj);
+ break;
+ default: {
+ struct gen_fields_tbl *fields_tbl;
+ rb_gen_fields_tbl_get(obj, id, &fields_tbl);
+ fields = fields_tbl->as.shape.fields;
+ break;
+ }
+ }
+
+ RUBY_ASSERT(removed_shape_id != INVALID_SHAPE_ID);
+
+ attr_index_t new_fields_count = RSHAPE(next_shape_id)->next_field_index;
+
+ attr_index_t removed_index = RSHAPE(removed_shape_id)->next_field_index - 1;
+ val = fields[removed_index];
+ size_t trailing_fields = new_fields_count - removed_index;
+ MEMMOVE(&fields[removed_index], &fields[removed_index + 1], VALUE, trailing_fields);
+
+ if (RB_TYPE_P(obj, T_OBJECT) &&
+ !RB_FL_TEST_RAW(obj, ROBJECT_EMBED) &&
+ rb_obj_embedded_size(new_fields_count) <= rb_gc_obj_slot_size(obj)) {
+ // Re-embed objects when instances become small enough
+ // This is necessary because YJIT assumes that objects with the same shape
+ // have the same embeddedness for efficiency (avoid extra checks)
+ RB_FL_SET_RAW(obj, ROBJECT_EMBED);
+ MEMCPY(ROBJECT_FIELDS(obj), fields, VALUE, new_fields_count);
+ xfree(fields);
+ }
+ rb_shape_set_shape_id(obj, next_shape_id);
+
+ return val;
+
+too_complex:
+ {
st_table *table = NULL;
switch (BUILTIN_TYPE(obj)) {
case T_CLASS:
@@ -1573,7 +1630,6 @@ rb_ivar_delete(VALUE obj, ID id, VALUE undef)
}
}
}
-
return val;
}