Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix complex transi compact #547

Open
wants to merge 21 commits into
base: master
Choose a base branch
from
Open
Changes from 1 commit
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
3bc41f4
[ruby/prism] Add macGreek encoding
kddnewton Nov 16, 2023
498b086
Skip test_ForwardingArgumentsNode
mame Nov 17, 2023
94c9f16
Refactor rb_obj_evacuate_ivs_to_hash_table
byroot Nov 16, 2023
940f2e7
size_pool_idx_for_size: Include debugging info in error message
byroot Nov 17, 2023
4a26a65
[ruby/prism] Add macTurkish
haldun Nov 16, 2023
9ba49c6
mingw.yml - remove encoding, run tests in cmd shell
MSP-Greg Nov 16, 2023
c2f2090
[ruby/prism] Do not test locale encoding on windows
kddnewton Nov 17, 2023
db4303f
[ruby/prism] Never test locale encoding
kddnewton Nov 17, 2023
e5d6b40
[ruby/prism] Do not allow trailing commas in calls without parenthesis
haldun Nov 13, 2023
85dcfef
[ruby/prism] Add macUkraine
haldun Nov 17, 2023
0a081a3
[ruby/prism] Add macRoman
haldun Nov 17, 2023
50b7b92
[ruby/prism] Add macThai
haldun Nov 17, 2023
229f6e5
[ruby/prism] Update spacing in encoding_test.rb
kddnewton Nov 17, 2023
585fdfe
[ruby/prism] add Windows-874 encoding
pcai Nov 17, 2023
cbdac2f
[ruby/prism] Silence clang analyzer warnings for the memory leaks
haldun Nov 17, 2023
7c99e43
[ruby/prism] Ensure serialized file is little endian
kddnewton Nov 13, 2023
3dd77bc
Fix corruption when out of shape during ivar remove
peterzhu2118 Nov 17, 2023
ef72970
Fix File.directory? doc hidding File::Stat#directory? doc
robotdana Nov 17, 2023
24fe22a
Fix ordering for auto compaction in get_overloaded_cme()
XrXr Nov 17, 2023
f479e62
[ruby/prism] Revert "Ensure serialized file is little endian"
kddnewton Nov 18, 2023
97fb07d
Pin object ivars while they are being copied in a hash
byroot Nov 16, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Fix corruption when out of shape during ivar remove
Reproduction script:

```
o = Object.new
10.times { |i| o.instance_variable_set(:"@A#{i}", i) }

i = 0
a = Object.new
while RubyVM::Shape.shapes_available > 2
  a.instance_variable_set(:"@i#{i}", 1)
  i += 1
end

o.remove_instance_variable(:@a0)

puts o.instance_variable_get(:@A1)
```

Before this patch, it would incorrectly output `2` and now it correctly
outputs `1`.
  • Loading branch information
peterzhu2118 committed Nov 17, 2023
commit 3dd77bc056de19a1cc0ad3fafdb8e49c429dec5a
83 changes: 34 additions & 49 deletions shape.c
Original file line number Diff line number Diff line change
@@ -528,29 +528,8 @@ rb_shape_frozen_shape_p(rb_shape_t* shape)
return SHAPE_FROZEN == (enum shape_type)shape->type;
}

static void
move_iv(VALUE obj, ID id, attr_index_t from, attr_index_t to)
{
switch(BUILTIN_TYPE(obj)) {
case T_CLASS:
case T_MODULE:
RCLASS_IVPTR(obj)[to] = RCLASS_IVPTR(obj)[from];
break;
case T_OBJECT:
RUBY_ASSERT(!rb_shape_obj_too_complex(obj));
ROBJECT_IVPTR(obj)[to] = ROBJECT_IVPTR(obj)[from];
break;
default: {
struct gen_ivtbl *ivtbl;
rb_gen_ivtbl_get(obj, id, &ivtbl);
ivtbl->as.shape.ivptr[to] = ivtbl->as.shape.ivptr[from];
break;
}
}
}

static rb_shape_t *
remove_shape_recursive(VALUE obj, ID id, rb_shape_t * shape, VALUE * removed)
remove_shape_recursive(rb_shape_t *shape, ID id, rb_shape_t **removed_shape)
{
if (shape->parent_id == INVALID_SHAPE_ID) {
// We've hit the top of the shape tree and couldn't find the
@@ -559,30 +538,13 @@ remove_shape_recursive(VALUE obj, ID id, rb_shape_t * shape, VALUE * removed)
}
else {
if (shape->type == SHAPE_IVAR && shape->edge_name == id) {
// We've hit the edge we wanted to remove, return it's _parent_
// as the new parent while we go back down the stack.
attr_index_t index = shape->next_iv_index - 1;

switch(BUILTIN_TYPE(obj)) {
case T_CLASS:
case T_MODULE:
*removed = RCLASS_IVPTR(obj)[index];
break;
case T_OBJECT:
*removed = ROBJECT_IVPTR(obj)[index];
break;
default: {
struct gen_ivtbl *ivtbl;
rb_gen_ivtbl_get(obj, id, &ivtbl);
*removed = ivtbl->as.shape.ivptr[index];
break;
}
}
*removed_shape = shape;

return rb_shape_get_parent(shape);
}
else {
// This isn't the IV we want to remove, keep walking up.
rb_shape_t * new_parent = remove_shape_recursive(obj, id, rb_shape_get_parent(shape), removed);
rb_shape_t *new_parent = remove_shape_recursive(rb_shape_get_parent(shape), id, removed_shape);

// We found a new parent. Create a child of the new parent that
// has the same attributes as this shape.
@@ -592,17 +554,13 @@ remove_shape_recursive(VALUE obj, ID id, rb_shape_t * shape, VALUE * removed)
}

bool dont_care;
rb_shape_t * new_child = get_next_shape_internal(new_parent, shape->edge_name, shape->type, &dont_care, true);
rb_shape_t *new_child = get_next_shape_internal(new_parent, shape->edge_name, shape->type, &dont_care, true);
if (UNLIKELY(new_child->type == SHAPE_OBJ_TOO_COMPLEX)) {
return new_child;
}

RUBY_ASSERT(new_child->capacity <= shape->capacity);

if (new_child->type == SHAPE_IVAR) {
move_iv(obj, id, shape->next_iv_index - 1, new_child->next_iv_index - 1);
}

return new_child;
}
else {
@@ -615,18 +573,45 @@ remove_shape_recursive(VALUE obj, ID id, rb_shape_t * shape, VALUE * removed)
}

bool
rb_shape_transition_shape_remove_ivar(VALUE obj, ID id, rb_shape_t *shape, VALUE * removed)
rb_shape_transition_shape_remove_ivar(VALUE obj, ID id, rb_shape_t *shape, VALUE *removed)
{
if (UNLIKELY(shape->type == SHAPE_OBJ_TOO_COMPLEX)) {
return false;
}

rb_shape_t * new_shape = remove_shape_recursive(obj, id, shape, removed);
rb_shape_t *removed_shape = NULL;
rb_shape_t *new_shape = remove_shape_recursive(shape, id, &removed_shape);
if (new_shape) {
RUBY_ASSERT(removed_shape != NULL);

if (UNLIKELY(new_shape->type == SHAPE_OBJ_TOO_COMPLEX)) {
return false;
}

RUBY_ASSERT(new_shape->next_iv_index == shape->next_iv_index - 1);

VALUE *ivptr;
switch(BUILTIN_TYPE(obj)) {
case T_CLASS:
case T_MODULE:
ivptr = RCLASS_IVPTR(obj);
break;
case T_OBJECT:
ivptr = ROBJECT_IVPTR(obj);
break;
default: {
struct gen_ivtbl *ivtbl;
rb_gen_ivtbl_get(obj, id, &ivtbl);
ivptr = ivtbl->as.shape.ivptr;
break;
}
}

*removed = ivptr[removed_shape->next_iv_index - 1];

memmove(&ivptr[removed_shape->next_iv_index - 1], &ivptr[removed_shape->next_iv_index],
((new_shape->next_iv_index + 1) - removed_shape->next_iv_index) * sizeof(VALUE));

rb_shape_set_shape(obj, new_shape);
}
return true;
21 changes: 21 additions & 0 deletions test/ruby/test_shapes.rb
Original file line number Diff line number Diff line change
@@ -414,6 +414,27 @@ module A
assert_equal true, A.instance_variable_defined?(:@a)
end;
end

def test_run_out_of_shape_during_remove_instance_variable
assert_separately([], "#{<<~"begin;"}\n#{<<~'end;'}")
begin;
o = Object.new
10.times { |i| o.instance_variable_set(:"@a#{i}", i) }

i = 0
a = Object.new
while RubyVM::Shape.shapes_available > 2
a.instance_variable_set(:"@i#{i}", 1)
i += 1
end

o.remove_instance_variable(:@a0)
(1...10).each do |i|
assert_equal(i, o.instance_variable_get(:"@a#{i}"))
end
end;
end

def test_run_out_of_shape_remove_instance_variable
assert_separately([], "#{<<~"begin;"}\n#{<<~'end;'}")
begin;