summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--test/ruby/test_shapes.rb39
-rw-r--r--variable.c22
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;
}