diff options
-rw-r--r-- | test/ruby/test_shapes.rb | 39 | ||||
-rw-r--r-- | variable.c | 22 |
2 files changed, 60 insertions, 1 deletions
diff --git a/test/ruby/test_shapes.rb b/test/ruby/test_shapes.rb index 4967b404e2..1135daecbe 100644 --- a/test/ruby/test_shapes.rb +++ b/test/ruby/test_shapes.rb @@ -345,6 +345,45 @@ class TestShapes < Test::Unit::TestCase end; end + def test_run_out_of_shape_instance_variable_defined + assert_separately([], "#{<<~"begin;"}\n#{<<~'end;'}") + begin; + class A + attr_reader :a, :b, :c, :d + def initialize + @a = @b = @c = @d = 1 + end + end + + o = Object.new + i = 0 + while RubyVM::Shape.shapes_available > 0 + o.instance_variable_set(:"@i#{i}", 1) + i += 1 + end + + a = A.new + assert_equal true, a.instance_variable_defined?(:@a) + end; + end + + 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 + + module A + @a = @b = @c = @d = 1 + end + + assert_equal true, A.instance_variable_defined?(:@a) + end; + end def test_run_out_of_shape_remove_instance_variable assert_separately([], "#{<<~"begin;"}\n#{<<~'end;'}") begin; diff --git a/variable.c b/variable.c index 27c10a27dd..6412819972 100644 --- a/variable.c +++ b/variable.c @@ -1809,7 +1809,27 @@ rb_ivar_defined(VALUE obj, ID id) if (SPECIAL_CONST_P(obj)) return Qfalse; if (rb_shape_obj_too_complex(obj)) { VALUE idx; - if (!rb_st_lookup(ROBJECT_IV_HASH(obj), id, &idx)) { + st_table *table = NULL; + switch (BUILTIN_TYPE(obj)) { + case T_CLASS: + case T_MODULE: + table = (st_table *)RCLASS_IVPTR(obj); + break; + + case T_OBJECT: + table = ROBJECT_IV_HASH(obj); + break; + + default: { + struct gen_ivtbl *ivtbl; + if (rb_gen_ivtbl_get(obj, 0, &ivtbl)) { + table = ivtbl->as.complex.table; + } + break; + } + } + + if (!table || !rb_st_lookup(table, id, &idx)) { return Qfalse; } |