Skip to content

Commit f2e5f6d

Browse files
committed
Allow T_CLASS and generic types to be too_complex
The intial complex shape implementation never allowed objects other than T_OBJECT to become too complex, unless we run out of shapes. I don't see any reason to prevent that. Ref: ruby#6931
1 parent 54c2edc commit f2e5f6d

File tree

3 files changed

+53
-50
lines changed

3 files changed

+53
-50
lines changed

gc.c

Lines changed: 1 addition & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1872,26 +1872,7 @@ object_id(VALUE obj)
18721872
// we'd at least need to generate the object_id using atomics.
18731873
lock_lev = rb_gc_vm_lock();
18741874

1875-
if (rb_shape_too_complex_p(shape)) {
1876-
st_table *table = ROBJECT_FIELDS_HASH(obj);
1877-
if (rb_shape_has_object_id(shape)) {
1878-
st_lookup(table, (st_data_t)ruby_internal_object_id, (st_data_t *)&id);
1879-
RUBY_ASSERT(id, "object_id missing");
1880-
1881-
rb_gc_vm_unlock(lock_lev);
1882-
return id;
1883-
}
1884-
1885-
id = ULL2NUM(next_object_id);
1886-
next_object_id += OBJ_ID_INCREMENT;
1887-
rb_shape_t *object_id_shape = rb_shape_object_id_shape(obj);
1888-
st_insert(table, (st_data_t)ruby_internal_object_id, (st_data_t)id);
1889-
rb_shape_set_shape(obj, object_id_shape);
1890-
if (RB_UNLIKELY(id_to_obj_tbl)) {
1891-
st_insert(id_to_obj_tbl, (st_data_t)id, (st_data_t)obj);
1892-
}
1893-
}
1894-
else if (rb_shape_has_object_id(shape)) {
1875+
if (rb_shape_has_object_id(shape)) {
18951876
rb_shape_t *object_id_shape = rb_shape_object_id_shape(obj);
18961877
id = rb_obj_field_get(obj, object_id_shape);
18971878
}

shape.c

Lines changed: 26 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -799,35 +799,38 @@ shape_get_next(rb_shape_t *shape, VALUE obj, ID id, bool emit_warnings)
799799
}
800800
#endif
801801

802-
bool allow_new_shape = true;
803-
804-
if (BUILTIN_TYPE(obj) == T_OBJECT) {
805-
VALUE klass = rb_obj_class(obj);
806-
allow_new_shape = RCLASS_VARIATION_COUNT(klass) < SHAPE_MAX_VARIATIONS;
802+
VALUE klass;
803+
switch (BUILTIN_TYPE(obj)) {
804+
case T_CLASS:
805+
case T_MODULE:
806+
klass = rb_singleton_class(obj);
807+
break;
808+
default:
809+
klass = rb_obj_class(obj);
810+
break;
807811
}
808812

813+
bool allow_new_shape = RCLASS_VARIATION_COUNT(klass) < SHAPE_MAX_VARIATIONS;
809814
bool variation_created = false;
810815
rb_shape_t *new_shape = get_next_shape_internal(shape, id, SHAPE_IVAR, &variation_created, allow_new_shape);
811816

812817
// Check if we should update max_iv_count on the object's class
813-
if (BUILTIN_TYPE(obj) == T_OBJECT) {
814-
VALUE klass = rb_obj_class(obj);
815-
if (new_shape->next_field_index > RCLASS_MAX_IV_COUNT(klass)) {
816-
RCLASS_SET_MAX_IV_COUNT(klass, new_shape->next_field_index);
817-
}
818-
819-
if (variation_created) {
820-
RCLASS_VARIATION_COUNT(klass)++;
821-
if (emit_warnings && rb_warning_category_enabled_p(RB_WARN_CATEGORY_PERFORMANCE)) {
822-
if (RCLASS_VARIATION_COUNT(klass) >= SHAPE_MAX_VARIATIONS) {
823-
rb_category_warn(
824-
RB_WARN_CATEGORY_PERFORMANCE,
825-
"The class %"PRIsVALUE" reached %d shape variations, instance variables accesses will be slower and memory usage increased.\n"
826-
"It is recommended to define instance variables in a consistent order, for instance by eagerly defining them all in the #initialize method.",
827-
rb_class_path(klass),
828-
SHAPE_MAX_VARIATIONS
829-
);
830-
}
818+
if (obj != klass && new_shape->next_field_index > RCLASS_MAX_IV_COUNT(klass)) {
819+
RCLASS_SET_MAX_IV_COUNT(klass, new_shape->next_field_index);
820+
}
821+
822+
if (variation_created) {
823+
RCLASS_VARIATION_COUNT(klass)++;
824+
825+
if (emit_warnings && rb_warning_category_enabled_p(RB_WARN_CATEGORY_PERFORMANCE)) {
826+
if (RCLASS_VARIATION_COUNT(klass) >= SHAPE_MAX_VARIATIONS) {
827+
rb_category_warn(
828+
RB_WARN_CATEGORY_PERFORMANCE,
829+
"The class %"PRIsVALUE" reached %d shape variations, instance variables accesses will be slower and memory usage increased.\n"
830+
"It is recommended to define instance variables in a consistent order, for instance by eagerly defining them all in the #initialize method.",
831+
rb_class_path(klass),
832+
SHAPE_MAX_VARIATIONS
833+
);
831834
}
832835
}
833836
}

test/ruby/test_object_id.rb

Lines changed: 26 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -137,10 +137,14 @@ def setup
137137
assert_equal 8, RubyVM::Shape::SHAPE_MAX_VARIATIONS
138138
end
139139
8.times do |i|
140-
TooComplex.new.instance_variable_set("@a#{i}", 1)
140+
TooComplex.new.instance_variable_set("@TestObjectIdTooComplex#{i}", 1)
141141
end
142142
@obj = TooComplex.new
143-
@obj.instance_variable_set(:@test, 1)
143+
@obj.instance_variable_set("@a#{rand(10_000)}", 1)
144+
145+
if defined?(RubyVM::Shape)
146+
assert_predicate(RubyVM::Shape.of(@obj), :too_complex?)
147+
end
144148
end
145149
end
146150

@@ -152,11 +156,21 @@ def setup
152156
if defined?(RubyVM::Shape::SHAPE_MAX_VARIATIONS)
153157
assert_equal 8, RubyVM::Shape::SHAPE_MAX_VARIATIONS
154158
end
159+
160+
@obj = TooComplex.new
161+
162+
@obj.instance_variable_set("@___#{rand(100_000)}", 1)
163+
155164
8.times do |i|
156-
TooComplex.new.instance_variable_set("@a#{i}", 1)
165+
@obj.instance_variable_set("@TestObjectIdTooComplexClass#{i}", 1)
166+
@obj.remove_instance_variable("@TestObjectIdTooComplexClass#{i}")
167+
end
168+
169+
@obj.instance_variable_set("@___#{rand(100_000)}", 1)
170+
171+
if defined?(RubyVM::Shape)
172+
assert_predicate(RubyVM::Shape.of(@obj), :too_complex?)
157173
end
158-
@obj = TooComplex.new
159-
@obj.instance_variable_set(:@test, 1)
160174
end
161175
end
162176

@@ -169,9 +183,14 @@ def setup
169183
assert_equal 8, RubyVM::Shape::SHAPE_MAX_VARIATIONS
170184
end
171185
8.times do |i|
172-
TooComplex.new.instance_variable_set("@a#{i}", 1)
186+
TooComplex.new.instance_variable_set("@TestObjectIdTooComplexGeneric#{i}", 1)
173187
end
174188
@obj = TooComplex.new
175-
@obj.instance_variable_set(:@test, 1)
189+
@obj.instance_variable_set("@a#{rand(10_000)}", 1)
190+
@obj.instance_variable_set("@a#{rand(10_000)}", 1)
191+
192+
if defined?(RubyVM::Shape)
193+
assert_predicate(RubyVM::Shape.of(@obj), :too_complex?)
194+
end
176195
end
177196
end

0 commit comments

Comments
 (0)