diff --git a/shape.c b/shape.c index 5a4a271b30..de57551fcf 100644 --- a/shape.c +++ b/shape.c @@ -1057,6 +1057,15 @@ rb_shape_shapes_available(VALUE self) return INT2NUM(MAX_SHAPE_ID - (GET_SHAPE_TREE()->next_shape_id - 1)); } +static VALUE +rb_shape_exhaust(int argc, VALUE *argv, VALUE self) +{ + rb_check_arity(argc, 0, 1); + int offset = argc == 1 ? NUM2INT(argv[0]) : 0; + GET_SHAPE_TREE()->next_shape_id = MAX_SHAPE_ID - offset; + return Qnil; +} + VALUE rb_obj_shape(rb_shape_t* shape); static enum rb_id_table_iterator_result collect_keys_and_values(ID key, VALUE value, void *ref) @@ -1239,5 +1248,6 @@ Init_shape(void) rb_define_singleton_method(rb_cShape, "of", rb_shape_debug_shape, 1); rb_define_singleton_method(rb_cShape, "root_shape", rb_shape_root_shape, 0); rb_define_singleton_method(rb_cShape, "shapes_available", rb_shape_shapes_available, 0); + rb_define_singleton_method(rb_cShape, "exhaust_shapes", rb_shape_exhaust, -1); #endif } diff --git a/test/ruby/test_shapes.rb b/test/ruby/test_shapes.rb index 57c97d9031..cf1885bbdf 100644 --- a/test/ruby/test_shapes.rb +++ b/test/ruby/test_shapes.rb @@ -191,12 +191,7 @@ class TestShapes < Test::Unit::TestCase attr_reader :very_unique end - obj = Object.new - i = 0 - while RubyVM::Shape.shapes_available > 0 - obj.instance_variable_set(:"@a#{i}", 1) - i += 1 - end + RubyVM::Shape.exhaust_shapes (RubyVM::Shape::SHAPE_MAX_VARIATIONS * 2).times do TooComplex.new.instance_variable_set(:"@unique_#{_1}", 1) @@ -216,13 +211,7 @@ class TestShapes < Test::Unit::TestCase assert_separately([], "#{<<~"begin;"}\n#{<<~'end;'}") begin; class Hi; end - - obj = Hi.new - i = 0 - while RubyVM::Shape.shapes_available > 2 - obj.instance_variable_set(:"@a#{i}", 1) - i += 1 - end + RubyVM::Shape.exhaust_shapes(3) obj = Hi.new i = 0 @@ -243,26 +232,16 @@ class TestShapes < Test::Unit::TestCase @a = 1 end end - # Try to run out of shapes - o = Object.new - i = 0 - while RubyVM::Shape.shapes_available > 0 - o.instance_variable_set(:"@i#{i}", 1) - i += 1 - A.new - end + RubyVM::Shape.exhaust_shapes + + A.new end; end def test_run_out_of_shape_for_class_ivar assert_separately([], "#{<<~"begin;"}\n#{<<~'end;'}") begin; - i = 0 - while RubyVM::Shape.shapes_available > 0 - c = Class.new - c.instance_variable_set(:"@i#{i}", 1) - i += 1 - end + RubyVM::Shape.exhaust_shapes c = Class.new c.instance_variable_set(:@a, 1) @@ -282,12 +261,7 @@ class TestShapes < Test::Unit::TestCase begin; [].instance_variable_set(:@a, 1) - i = 0 - o = Object.new - while RubyVM::Shape.shapes_available > 0 - o.instance_variable_set(:"@i#{i}", 1) - i += 1 - end + RubyVM::Shape.exhaust_shapes ary = 10.times.map { [] } @@ -302,12 +276,7 @@ class TestShapes < Test::Unit::TestCase def test_run_out_of_shape_for_module_ivar assert_separately([], "#{<<~"begin;"}\n#{<<~'end;'}") begin; - o = Object.new - i = 0 - while RubyVM::Shape.shapes_available > 0 - o.instance_variable_set(:"@i#{i}", 1) - i += 1 - end + RubyVM::Shape.exhaust_shapes module Foo @a = 1 @@ -321,12 +290,7 @@ class TestShapes < Test::Unit::TestCase def test_run_out_of_shape_for_class_cvar assert_separately([], "#{<<~"begin;"}\n#{<<~'end;'}") begin; - i = 0 - while RubyVM::Shape.shapes_available > 0 - c = Class.new - c.class_variable_set(:"@@i#{i}", 1) - i += 1 - end + RubyVM::Shape.exhaust_shapes c = Class.new @@ -348,13 +312,7 @@ class TestShapes < Test::Unit::TestCase class TooComplex < Hash end - # Try to run out of shapes - o = Object.new - i = 0 - while RubyVM::Shape.shapes_available > 0 - o.instance_variable_set(:"@i#{i}", 1) - i += 1 - end + RubyVM::Shape.exhaust_shapes tc = TooComplex.new tc.instance_variable_set(:@a, 1) @@ -387,15 +345,10 @@ class TestShapes < Test::Unit::TestCase a = Hi.new # Try to run out of shapes - o = Object.new - i = 0 - while RubyVM::Shape.shapes_available > 0 - o.instance_variable_set(:"@i#{i}", 1) - i += 1 - end + RubyVM::Shape.exhaust_shapes - assert_equal 1,a.transition - assert_equal 1,a.transition + assert_equal 1, a.transition + assert_equal 1, a.transition end; end @@ -409,12 +362,7 @@ class TestShapes < Test::Unit::TestCase end end - o = Object.new - i = 0 - while RubyVM::Shape.shapes_available > 0 - o.instance_variable_set(:"@i#{i}", 1) - i += 1 - end + RubyVM::Shape.exhaust_shapes a = A.new assert_equal true, a.instance_variable_defined?(:@a) @@ -424,12 +372,7 @@ class TestShapes < Test::Unit::TestCase def test_run_out_of_shape_instance_variable_defined_on_module assert_separately([], "#{<<~"begin;"}\n#{<<~'end;'}") begin; - o = Object.new - i = 0 - while RubyVM::Shape.shapes_available > 0 - o.instance_variable_set(:"@i#{i}", 1) - i += 1 - end + RubyVM::Shape.exhaust_shapes module A @a = @b = @c = @d = 1 @@ -445,12 +388,7 @@ class TestShapes < Test::Unit::TestCase o = Object.new 10.times { |i| o.instance_variable_set(:"@a#{i}", i) } - i = 0 - a = Object.new - while RubyVM::Shape.shapes_available > 2 - a.instance_variable_set(:"@i#{i}", 1) - i += 1 - end + RubyVM::Shape.exhaust_shapes o.remove_instance_variable(:@a0) (1...10).each do |i| @@ -471,12 +409,7 @@ class TestShapes < Test::Unit::TestCase a = A.new - o = Object.new - i = 0 - while RubyVM::Shape.shapes_available > 0 - o.instance_variable_set(:"@i#{i}", 1) - i += 1 - end + RubyVM::Shape.exhaust_shapes a.remove_instance_variable(:@b) assert_nil a.b @@ -506,12 +439,7 @@ class TestShapes < Test::Unit::TestCase a = A.new - o = Object.new - i = 0 - while RubyVM::Shape.shapes_available > 1 - o.instance_variable_set(:"@i#{i}", 1) - i += 1 - end + RubyVM::Shape.exhaust_shapes a.dup end; @@ -522,12 +450,7 @@ class TestShapes < Test::Unit::TestCase o = [] o.instance_variable_set(:@a, 1) - i = 0 - o = Object.new - while RubyVM::Shape.shapes_available > 0 - o.instance_variable_set(:"@i#{i}", 1) - i += 1 - end + RubyVM::Shape.exhaust_shapes ary = 1_000_000.times.map { [] } begin; @@ -546,12 +469,7 @@ class TestShapes < Test::Unit::TestCase begin; class Hi; end - obj = Hi.new - i = 0 - while RubyVM::Shape.shapes_available > 2 - obj.instance_variable_set(:"@a#{i}", 1) - i += 1 - end + RubyVM::Shape.exhaust_shapes(2) obj = Module.new 3.times do @@ -571,12 +489,7 @@ class TestShapes < Test::Unit::TestCase begin; class Hi; end - obj = Hi.new - i = 0 - while RubyVM::Shape.shapes_available > 2 - obj.instance_variable_set(:"@a#{i}", 1) - i += 1 - end + RubyVM::Shape.exhaust_shapes(2) obj = Object.new i = 0 @@ -748,12 +661,7 @@ class TestShapes < Test::Unit::TestCase 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 + RubyVM::Shape.exhaust_shapes object.remove_instance_variable(:@ivar_2)