diff options
author | Jean Boussier <[email protected]> | 2023-11-02 10:37:09 +0100 |
---|---|---|
committer | Jean Boussier <[email protected]> | 2023-11-02 12:00:42 +0100 |
commit | 33795931a021c6abebe2c60864d88b8f4644ff70 (patch) | |
tree | 51c5eaf97848faf30506ac29b07a3bc286aea0cc | |
parent | 42f368ead50bb51935026f54d698fe18780230d2 (diff) |
Better handle running out of shapes in remove_shape_recursive
-rw-r--r-- | shape.c | 8 | ||||
-rw-r--r-- | test/ruby/test_shapes.rb | 30 |
2 files changed, 36 insertions, 2 deletions
@@ -583,12 +583,16 @@ remove_shape_recursive(VALUE obj, ID id, rb_shape_t * shape, VALUE * removed) // We found a new parent. Create a child of the new parent that // has the same attributes as this shape. if (new_parent) { - bool dont_care; - rb_shape_t * new_child = get_next_shape_internal(new_parent, shape->edge_name, shape->type, &dont_care, true); if (UNLIKELY(new_parent->type == SHAPE_OBJ_TOO_COMPLEX)) { return new_parent; } + bool dont_care; + rb_shape_t * new_child = get_next_shape_internal(new_parent, shape->edge_name, shape->type, &dont_care, true); + if (UNLIKELY(new_child->type == SHAPE_OBJ_TOO_COMPLEX)) { + return new_child; + } + new_child->capacity = shape->capacity; if (new_child->type == SHAPE_IVAR) { move_iv(obj, id, shape->next_iv_index - 1, new_child->next_iv_index - 1); diff --git a/test/ruby/test_shapes.rb b/test/ruby/test_shapes.rb index b052a4a647..b627b58281 100644 --- a/test/ruby/test_shapes.rb +++ b/test/ruby/test_shapes.rb @@ -577,6 +577,36 @@ class TestShapes < Test::Unit::TestCase assert_equal [0, 1, nil, 3, 4], ivars end + def test_remove_instance_variable_when_out_of_shapes + assert_separately([], "#{<<~"begin;"}\n#{<<~'end;'}") + begin; + ivars_count = 5 + object = Object.new + ivars_count.times do |i| + object.instance_variable_set("@ivar_#{i}", i) + end + + ivars = ivars_count.times.map do |i| + object.instance_variable_get("@ivar_#{i}") + end + assert_equal [0, 1, 2, 3, 4], ivars + + o = Object.new + i = 0 + while RubyVM::Shape.shapes_available > 0 + o.instance_variable_set(:"@i#{i}", 1) + i += 1 + end + + object.remove_instance_variable(:@ivar_2) + + ivars = ivars_count.times.map do |i| + object.instance_variable_get("@ivar_#{i}") + end + assert_equal [0, 1, nil, 3, 4], ivars + end; + end + def test_freeze_after_complex ensure_complex |