Skip to content

Commit 8b5ac5a

Browse files
committed
Fix class instance variable inside namespaces
Now that classes fields are delegated to an object with its own shape_id, we no longer need to mark all classes as TOO_COMPLEX.
1 parent 8120971 commit 8b5ac5a

File tree

8 files changed

+70
-40
lines changed

8 files changed

+70
-40
lines changed

imemo.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -155,11 +155,14 @@ rb_imemo_class_fields_clone(VALUE fields_obj)
155155

156156
if (rb_shape_too_complex_p(shape_id)) {
157157
clone = rb_imemo_class_fields_new_complex(CLASS_OF(fields_obj), 0);
158+
RBASIC_SET_SHAPE_ID(clone, shape_id);
159+
158160
st_table *src_table = rb_imemo_class_fields_complex_tbl(fields_obj);
159161
st_replace(rb_imemo_class_fields_complex_tbl(clone), src_table);
160162
}
161163
else {
162164
clone = imemo_class_fields_new(CLASS_OF(fields_obj), RSHAPE_CAPACITY(shape_id));
165+
RBASIC_SET_SHAPE_ID(clone, shape_id);
163166
MEMCPY(rb_imemo_class_fields_ptr(clone), rb_imemo_class_fields_ptr(fields_obj), VALUE, RSHAPE_LEN(shape_id));
164167
}
165168

internal/class.h

Lines changed: 12 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -403,10 +403,6 @@ RCLASS_EXT_WRITABLE_LOOKUP(VALUE obj, const rb_namespace_t *ns)
403403
if (ext)
404404
return ext;
405405

406-
if (!rb_shape_obj_too_complex_p(obj)) {
407-
rb_evict_ivars_to_hash(obj); // fallback to ivptr for ivars from shapes
408-
}
409-
410406
RB_VM_LOCKING() {
411407
// re-check the classext is not created to avoid the multi-thread race
412408
ext = RCLASS_EXT_TABLE_LOOKUP_INTERNAL(obj, ns);
@@ -525,50 +521,44 @@ RCLASS_WRITE_SUPER(VALUE klass, VALUE super)
525521
}
526522

527523
static inline VALUE
528-
RCLASS_FIELDS_OBJ(VALUE obj)
529-
{
530-
RUBY_ASSERT(RB_TYPE_P(obj, RUBY_T_CLASS) || RB_TYPE_P(obj, RUBY_T_MODULE));
531-
return RCLASSEXT_FIELDS_OBJ(RCLASS_EXT_READABLE(obj));
532-
}
533-
534-
static inline VALUE
535-
RCLASS_ENSURE_FIELDS_OBJ(VALUE obj)
524+
RCLASS_WRITABLE_ENSURE_FIELDS_OBJ(VALUE obj)
536525
{
537526
RUBY_ASSERT(RB_TYPE_P(obj, RUBY_T_CLASS) || RB_TYPE_P(obj, RUBY_T_MODULE));
538-
rb_classext_t *ext = RCLASS_EXT_READABLE(obj);
527+
rb_classext_t *ext = RCLASS_EXT_WRITABLE(obj);
539528
if (!ext->fields_obj) {
540529
RB_OBJ_WRITE(obj, &ext->fields_obj, rb_imemo_class_fields_new(obj, 1));
541530
}
542531
return ext->fields_obj;
543532
}
544533

545-
static inline VALUE
546-
RCLASS_WRITABLE_FIELDS_OBJ(VALUE obj)
534+
static inline void
535+
RCLASSEXT_SET_FIELDS_OBJ(VALUE obj, rb_classext_t *ext, VALUE fields_obj)
547536
{
548537
RUBY_ASSERT(RB_TYPE_P(obj, RUBY_T_CLASS) || RB_TYPE_P(obj, RUBY_T_MODULE));
549-
return RCLASSEXT_FIELDS_OBJ(RCLASS_EXT_WRITABLE(obj));
538+
RB_OBJ_WRITE(obj, &ext->fields_obj, fields_obj);
550539
}
551540

552-
static inline void
553-
RCLASSEXT_SET_FIELDS_OBJ(VALUE obj, rb_classext_t *ext, VALUE fields_obj)
541+
static inline VALUE
542+
RCLASS_WRITABLE_FIELDS_OBJ(VALUE obj)
554543
{
555544
RUBY_ASSERT(RB_TYPE_P(obj, RUBY_T_CLASS) || RB_TYPE_P(obj, RUBY_T_MODULE));
556-
RB_OBJ_WRITE(obj, &ext->fields_obj, fields_obj);
545+
return RCLASSEXT_FIELDS_OBJ(RCLASS_EXT_WRITABLE(obj));
557546
}
558547

559548
static inline void
560-
RCLASS_SET_FIELDS_OBJ(VALUE obj, VALUE fields_obj)
549+
RCLASS_WRITABLE_SET_FIELDS_OBJ(VALUE obj, VALUE fields_obj)
561550
{
562551
RUBY_ASSERT(RB_TYPE_P(obj, RUBY_T_CLASS) || RB_TYPE_P(obj, RUBY_T_MODULE));
563552

564-
RCLASSEXT_SET_FIELDS_OBJ(obj, RCLASS_EXT_PRIME(obj), fields_obj);
553+
RCLASSEXT_SET_FIELDS_OBJ(obj, RCLASS_EXT_WRITABLE(obj), fields_obj);
565554
}
566555

567556
static inline uint32_t
568557
RCLASS_FIELDS_COUNT(VALUE obj)
569558
{
570559
RUBY_ASSERT(RB_TYPE_P(obj, RUBY_T_CLASS) || RB_TYPE_P(obj, RUBY_T_MODULE));
571-
VALUE fields_obj = RCLASS_FIELDS_OBJ(obj);
560+
561+
VALUE fields_obj = RCLASS_WRITABLE_FIELDS_OBJ(obj);
572562
if (fields_obj) {
573563
if (rb_shape_obj_too_complex_p(fields_obj)) {
574564
return (uint32_t)rb_st_table_size(rb_imemo_class_fields_complex_tbl(fields_obj));

namespace.c

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -450,9 +450,6 @@ namespace_initialize(VALUE namespace)
450450
// If a code in the namespace adds a constant, the constant will be visible even from root/main.
451451
RCLASS_SET_PRIME_CLASSEXT_WRITABLE(namespace, true);
452452

453-
// fallback to ivptr for ivars from shapes to manipulate the constant table
454-
rb_evict_ivars_to_hash(namespace);
455-
456453
// Get a clean constant table of Object even by writable one
457454
// because ns was just created, so it has not touched any constants yet.
458455
object_classext = RCLASS_EXT_WRITABLE_IN_NS(rb_cObject, ns);

shape.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -397,7 +397,7 @@ rb_obj_shape_id(VALUE obj)
397397
}
398398

399399
if (BUILTIN_TYPE(obj) == T_CLASS || BUILTIN_TYPE(obj) == T_MODULE) {
400-
VALUE fields_obj = RCLASS_FIELDS_OBJ(obj);
400+
VALUE fields_obj = RCLASS_WRITABLE_FIELDS_OBJ(obj);
401401
if (fields_obj) {
402402
return RBASIC_SHAPE_ID(fields_obj);
403403
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
class String
2+
class << self
3+
attr_reader :str_ivar1
4+
5+
def str_ivar2
6+
@str_ivar2
7+
end
8+
end
9+
10+
@str_ivar1 = 111
11+
@str_ivar2 = 222
12+
end
13+
14+
class StringDelegator < BasicObject
15+
private
16+
def method_missing(...)
17+
::String.public_send(...)
18+
end
19+
end
20+
21+
StringDelegatorObj = StringDelegator.new

test/ruby/test_namespace.rb

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -222,6 +222,26 @@ def Target.foo
222222
end;
223223
end
224224

225+
def test_instance_variable
226+
pend unless Namespace.enabled?
227+
228+
@n.require_relative('namespace/instance_variables')
229+
230+
assert_equal [], String.instance_variables
231+
assert_equal [:@str_ivar1, :@str_ivar2], @n::StringDelegatorObj.instance_variables
232+
assert_equal 111, @n::StringDelegatorObj.str_ivar1
233+
assert_equal 222, @n::StringDelegatorObj.str_ivar2
234+
assert_equal 222, @n::StringDelegatorObj.instance_variable_get(:@str_ivar2)
235+
236+
@n::StringDelegatorObj.instance_variable_set(:@str_ivar3, 333)
237+
assert_equal 333, @n::StringDelegatorObj.instance_variable_get(:@str_ivar3)
238+
@n::StringDelegatorObj.remove_instance_variable(:@str_ivar1)
239+
assert_nil @n::StringDelegatorObj.str_ivar1
240+
assert_equal [:@str_ivar2, :@str_ivar3], @n::StringDelegatorObj.instance_variables
241+
242+
assert_equal [], String.instance_variables
243+
end
244+
225245
def test_methods_added_in_namespace_are_invisible_globally
226246
pend unless Namespace.enabled?
227247

variable.c

Lines changed: 12 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1307,7 +1307,7 @@ rb_obj_field_get(VALUE obj, shape_id_t target_shape_id)
13071307

13081308
if (BUILTIN_TYPE(obj) == T_CLASS || BUILTIN_TYPE(obj) == T_MODULE) {
13091309
ASSERT_vm_locking();
1310-
VALUE field_obj = RCLASS_FIELDS_OBJ(obj);
1310+
VALUE field_obj = RCLASS_WRITABLE_FIELDS_OBJ(obj);
13111311
if (field_obj) {
13121312
return rb_obj_field_get(field_obj, target_shape_id);
13131313
}
@@ -1375,7 +1375,7 @@ rb_ivar_lookup(VALUE obj, ID id, VALUE undef)
13751375
VALUE val = undef;
13761376
RB_VM_LOCK_ENTER();
13771377
{
1378-
VALUE fields_obj = RCLASS_FIELDS_OBJ(obj);
1378+
VALUE fields_obj = RCLASS_WRITABLE_FIELDS_OBJ(obj);
13791379
if (fields_obj) {
13801380
val = rb_ivar_lookup(fields_obj, id, undef);
13811381
}
@@ -1492,7 +1492,7 @@ rb_ivar_delete(VALUE obj, ID id, VALUE undef)
14921492
if (BUILTIN_TYPE(obj) == T_CLASS || BUILTIN_TYPE(obj) == T_MODULE) {
14931493
IVAR_ACCESSOR_SHOULD_BE_MAIN_RACTOR(id);
14941494

1495-
VALUE fields_obj = RCLASS_FIELDS_OBJ(obj);
1495+
VALUE fields_obj = RCLASS_WRITABLE_FIELDS_OBJ(obj);
14961496
if (fields_obj) {
14971497
RB_VM_LOCK_ENTER();
14981498
{
@@ -1614,8 +1614,7 @@ static shape_id_t
16141614
obj_transition_too_complex(VALUE obj, st_table *table)
16151615
{
16161616
if (BUILTIN_TYPE(obj) == T_CLASS || BUILTIN_TYPE(obj) == T_MODULE) {
1617-
RUBY_ASSERT(RCLASS_FIELDS_OBJ(obj));
1618-
return obj_transition_too_complex(RCLASS_FIELDS_OBJ(obj), table);
1617+
return obj_transition_too_complex(RCLASS_WRITABLE_ENSURE_FIELDS_OBJ(obj), table);
16191618
}
16201619

16211620
RUBY_ASSERT(!rb_shape_obj_too_complex_p(obj));
@@ -2062,7 +2061,7 @@ rb_obj_set_shape_id(VALUE obj, shape_id_t shape_id)
20622061
if (BUILTIN_TYPE(obj) == T_CLASS || BUILTIN_TYPE(obj) == T_MODULE) {
20632062
// Avoid creating the fields_obj just to freeze the class
20642063
if (!(shape_id == SPECIAL_CONST_SHAPE_ID && old_shape_id == ROOT_SHAPE_ID)) {
2065-
RBASIC_SET_SHAPE_ID(RCLASS_ENSURE_FIELDS_OBJ(obj), shape_id);
2064+
RBASIC_SET_SHAPE_ID(RCLASS_WRITABLE_ENSURE_FIELDS_OBJ(obj), shape_id);
20662065
}
20672066
}
20682067
// FIXME: How to do multi-shape?
@@ -2201,7 +2200,7 @@ rb_ivar_defined(VALUE obj, ID id)
22012200
case T_CLASS:
22022201
case T_MODULE:
22032202
RB_VM_LOCKING() {
2204-
VALUE fields_obj = RCLASS_FIELDS_OBJ(obj);
2203+
VALUE fields_obj = RCLASS_WRITABLE_FIELDS_OBJ(obj);
22052204
if (fields_obj) {
22062205
defined = ivar_defined0(fields_obj, id);
22072206
}
@@ -2455,7 +2454,7 @@ rb_field_foreach(VALUE obj, rb_ivar_foreach_callback_func *func, st_data_t arg,
24552454
case T_MODULE:
24562455
IVAR_ACCESSOR_SHOULD_BE_MAIN_RACTOR(0);
24572456
RB_VM_LOCKING() {
2458-
VALUE fields_obj = RCLASS_FIELDS_OBJ(obj);
2457+
VALUE fields_obj = RCLASS_WRITABLE_FIELDS_OBJ(obj);
24592458
if (fields_obj) {
24602459
class_fields_each(fields_obj, func, arg, ivar_only);
24612460
}
@@ -2488,7 +2487,7 @@ rb_ivar_count(VALUE obj)
24882487
case T_CLASS:
24892488
case T_MODULE:
24902489
{
2491-
VALUE fields_obj = RCLASS_FIELDS_OBJ(obj);
2490+
VALUE fields_obj = RCLASS_WRITABLE_FIELDS_OBJ(obj);
24922491
if (!fields_obj) {
24932492
return 0;
24942493
}
@@ -4706,7 +4705,7 @@ static int
47064705
class_ivar_set(VALUE obj, ID id, VALUE val)
47074706
{
47084707
bool existing = true;
4709-
const VALUE original_fields_obj = RCLASS_FIELDS_OBJ(obj);
4708+
const VALUE original_fields_obj = RCLASS_WRITABLE_FIELDS_OBJ(obj);
47104709
VALUE fields_obj = original_fields_obj ? original_fields_obj : rb_imemo_class_fields_new(obj, 1);
47114710

47124711
shape_id_t next_shape_id = 0;
@@ -4758,7 +4757,7 @@ class_ivar_set(VALUE obj, ID id, VALUE val)
47584757
}
47594758

47604759
if (fields_obj != original_fields_obj) {
4761-
RCLASS_SET_FIELDS_OBJ(obj, fields_obj);
4760+
RCLASS_WRITABLE_SET_FIELDS_OBJ(obj, fields_obj);
47624761
// TODO: What should we set as the T_CLASS shape_id?
47634762
// In most case we can replicate the single `fields_obj` shape
47644763
// but in namespaced case?
@@ -4777,7 +4776,7 @@ class_ivar_set(VALUE obj, ID id, VALUE val)
47774776

47784777
if (fields_obj != original_fields_obj) {
47794778
RBASIC_SET_SHAPE_ID(fields_obj, next_shape_id);
4780-
RCLASS_SET_FIELDS_OBJ(obj, fields_obj);
4779+
RCLASS_WRITABLE_SET_FIELDS_OBJ(obj, fields_obj);
47814780
// TODO: What should we set as the T_CLASS shape_id?
47824781
// In most case we can replicate the single `fields_obj` shape
47834782
// but in namespaced case?
@@ -4809,7 +4808,7 @@ static void
48094808
class_field_set(VALUE obj, shape_id_t target_shape_id, VALUE val)
48104809
{
48114810
RUBY_ASSERT(RB_TYPE_P(obj, T_CLASS) || RB_TYPE_P(obj, T_MODULE));
4812-
obj_field_set(RCLASS_ENSURE_FIELDS_OBJ(obj), target_shape_id, val);
4811+
obj_field_set(RCLASS_WRITABLE_ENSURE_FIELDS_OBJ(obj), target_shape_id, val);
48134812
}
48144813

48154814
static int

vm_insnhelper.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1248,7 +1248,7 @@ vm_getivar(VALUE obj, ID id, const rb_iseq_t *iseq, IVC ic, const struct rb_call
12481248
}
12491249
}
12501250

1251-
fields_obj = RCLASS_FIELDS_OBJ(obj);
1251+
fields_obj = RCLASS_WRITABLE_FIELDS_OBJ(obj);
12521252
if (!fields_obj) {
12531253
return default_value;
12541254
}

0 commit comments

Comments
 (0)