Skip to content

Commit

Permalink
Fix remove_class_variable for too complex classes
Browse files Browse the repository at this point in the history
  • Loading branch information
peterzhu2118 committed Nov 1, 2023
1 parent 90b21b8 commit bdf8ce8
Show file tree
Hide file tree
Showing 2 changed files with 56 additions and 59 deletions.
24 changes: 23 additions & 1 deletion test/ruby/test_shapes.rb
Expand Up @@ -252,7 +252,7 @@ def initialize
end;
end

def test_run_out_of_shape_for_class
def test_run_out_of_shape_for_class_ivar
assert_separately([], "#{<<~"begin;"}\n#{<<~'end;'}")
begin;
i = 0
Expand All @@ -275,6 +275,28 @@ def test_run_out_of_shape_for_class
end;
end

def test_run_out_of_shape_for_class_cvar
assert_separately([], "#{<<~"begin;"}\n#{<<~'end;'}")
begin;
c = Class.new
i = 0
while RubyVM::Shape.shapes_available > 0
c.class_variable_set(:"@@i#{i}", 1)
i += 1
end
c.class_variable_set(:@@a, 1)
assert_equal(1, c.class_variable_get(:@@a))
c.class_eval { remove_class_variable(:@@a) }
assert_false(c.class_variable_defined?(:@@a))
assert_raise(NameError) do
c.class_eval { remove_class_variable(:@@a) }
end
end;
end

def test_run_out_of_shape_generic_ivar_set
assert_separately([], "#{<<~"begin;"}\n#{<<~'end;'}")
begin;
Expand Down
91 changes: 33 additions & 58 deletions variable.c
Expand Up @@ -1372,25 +1372,42 @@ rb_ivar_delete(VALUE obj, ID id, VALUE undef)
rb_check_frozen(obj);

VALUE val = undef;
rb_shape_t * shape = rb_shape_get_shape(obj);
rb_shape_t *shape = rb_shape_get_shape(obj);

switch (BUILTIN_TYPE(obj)) {
case T_CLASS:
case T_MODULE:
if (BUILTIN_TYPE(obj) == T_CLASS || BUILTIN_TYPE(obj) == T_MODULE) {
IVAR_ACCESSOR_SHOULD_BE_MAIN_RACTOR(id);
}

RB_VM_LOCK_ENTER();
{
rb_shape_transition_shape_remove_ivar(obj, id, shape, &val);
if (!rb_shape_transition_shape_remove_ivar(obj, id, shape, &val)) {
if (!rb_shape_obj_too_complex(obj)) {
rb_evict_ivars_to_hash(obj, shape);
}
RB_VM_LOCK_LEAVE();

break;
default: {
rb_shape_transition_shape_remove_ivar(obj, id, shape, &val);
st_table *table = NULL;
switch (BUILTIN_TYPE(obj)) {
case T_CLASS:
case T_MODULE:
table = RCLASS_IV_HASH(obj);
break;

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) {
if (!st_delete(table, (st_data_t *)&id, (st_data_t *)&val)) {
val = undef;
}
}
}

return val;
Expand Down Expand Up @@ -2181,60 +2198,18 @@ check_id_type(VALUE obj, VALUE *pname,
VALUE
rb_obj_remove_instance_variable(VALUE obj, VALUE name)
{
VALUE val = Qundef;
const ID id = id_for_var(obj, name, an, instance);

// Frozen check comes here because it's expected that we raise a
// NameError (from the id_for_var check) before we raise a FrozenError
rb_check_frozen(obj);

if (!id) {
goto not_defined;
}

rb_shape_t * shape = rb_shape_get_shape(obj);
if (id) {
VALUE val = rb_ivar_delete(obj, id, Qundef);

if (BUILTIN_TYPE(obj) == T_CLASS || BUILTIN_TYPE(obj) == T_MODULE) {
IVAR_ACCESSOR_SHOULD_BE_MAIN_RACTOR(id);
if (val != Qundef) return val;
}

if (!rb_shape_transition_shape_remove_ivar(obj, id, shape, &val)) {
if (!rb_shape_obj_too_complex(obj)) {
rb_evict_ivars_to_hash(obj, shape);
}

st_table *table = NULL;
switch (BUILTIN_TYPE(obj)) {
case T_CLASS:
case T_MODULE:
table = RCLASS_IV_HASH(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) {
if (!st_delete(table, (st_data_t *)&id, (st_data_t *)&val)) {
val = Qundef;
}
}
}

if (val != Qundef) {
return val;
}

not_defined:
rb_name_err_raise("instance variable %1$s not defined",
obj, name);
UNREACHABLE_RETURN(Qnil);
Expand Down

0 comments on commit bdf8ce8

Please sign in to comment.