Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
16 changes: 16 additions & 0 deletions changelog.d/7382-mirror-remaining-sites.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
**Fixed** the remaining six `mirror_class_object_static_write` call sites in
`js_object_set_field_by_name` passed a stale `obj`, completing #7381.

#7381 fixed two of the eight sites and scoped itself there on the theory that
the `refresh_roots_after_alloc!()` macro — which republishes `obj`, `key`,
`value` and `interned_key` together — could clobber an arm that rebinds `value`
locally. That theory was wrong: none of the eight arms rebinds any of the four
after its handle is taken, so republishing is a no-op except for the relocation
it repairs. Verified by measurement, not inspection — the full-coverage build
scores 58 pass / 2 fail on the object/assign/class/field/shape gap set, byte-identical
to pristine `main` (both failures pre-existing, one already in
`known_failures.json`).

With all eight refreshed, `test_gap_gc_assign_string_source_rooting`'s fault
leaves `mirror_class_object_static_write` entirely and surfaces the next catch in
the chain at `js_jsvalue_equals`, which remains open.
36 changes: 36 additions & 0 deletions crates/perry-runtime/src/object/field_set_by_name.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1574,6 +1574,12 @@ pub extern "C" fn js_object_set_field_by_name(
};
overflow_set(obj as usize, i, vbits);
}
// #7341: same as the two own-data-write arms -- the write above can
// reach an allocator and the mirror's first instruction dereferences
// `obj`. None of these arms rebinds `obj`/`key`/`value` after the
// handles are taken, so republishing all four is a no-op except for
// the relocation it repairs.
refresh_roots_after_alloc!();
mirror_class_object_static_write(obj, key, value);
return;
}
Expand Down Expand Up @@ -1644,6 +1650,12 @@ pub extern "C" fn js_object_set_field_by_name(
super::shapes::shape_keys_grown(prev_keys_usize, new_keys);
}
overflow_set(obj as usize, new_index, vbits);
// #7341: same as the two own-data-write arms -- the write above can
// reach an allocator and the mirror's first instruction dereferences
// `obj`. None of these arms rebinds `obj`/`key`/`value` after the
// handles are taken, so republishing all four is a no-op except for
// the relocation it repairs.
refresh_roots_after_alloc!();
mirror_class_object_static_write(obj, key, value);
transition_cache_insert(
prev_keys_usize,
Expand Down Expand Up @@ -1685,6 +1697,12 @@ pub extern "C" fn js_object_set_field_by_name(
(*obj).field_count = new_index as u32 + 1;
}
js_object_set_field(obj, new_index as u32, JSValue::from_bits(value.to_bits()));
// #7341: same as the two own-data-write arms -- the write above can
// reach an allocator and the mirror's first instruction dereferences
// `obj`. None of these arms rebinds `obj`/`key`/`value` after the
// handles are taken, so republishing all four is a no-op except for
// the relocation it repairs.
refresh_roots_after_alloc!();
mirror_class_object_static_write(obj, key, value);
transition_cache_insert(
prev_keys_usize,
Expand Down Expand Up @@ -1755,6 +1773,12 @@ pub extern "C" fn js_object_set_field_by_name(
};
overflow_set(obj as usize, i, vbits);
}
// #7341: same as the two own-data-write arms -- the write above can
// reach an allocator and the mirror's first instruction dereferences
// `obj`. None of these arms rebinds `obj`/`key`/`value` after the
// handles are taken, so republishing all four is a no-op except for
// the relocation it repairs.
refresh_roots_after_alloc!();
mirror_class_object_static_write(obj, key, value);
return;
}
Expand Down Expand Up @@ -1843,6 +1867,12 @@ pub extern "C" fn js_object_set_field_by_name(
super::shapes::shape_keys_grown(prev_keys_usize, new_keys);
}
overflow_set(obj as usize, new_index, vbits);
// #7341: same as the two own-data-write arms -- the write above can
// reach an allocator and the mirror's first instruction dereferences
// `obj`. None of these arms rebinds `obj`/`key`/`value` after the
// handles are taken, so republishing all four is a no-op except for
// the relocation it repairs.
refresh_roots_after_alloc!();
mirror_class_object_static_write(obj, key, value);
// Record the shape transition so the next object sharing
// `prev_keys` that adds the same key hits the fast path.
Expand Down Expand Up @@ -1886,6 +1916,12 @@ pub extern "C" fn js_object_set_field_by_name(
(*obj).field_count = new_index as u32 + 1;
}
js_object_set_field(obj, new_index as u32, JSValue::from_bits(value.to_bits()));
// #7341: same as the two own-data-write arms -- the write above can
// reach an allocator and the mirror's first instruction dereferences
// `obj`. None of these arms rebinds `obj`/`key`/`value` after the
// handles are taken, so republishing all four is a no-op except for
// the relocation it repairs.
refresh_roots_after_alloc!();
mirror_class_object_static_write(obj, key, value);
// Record the shape transition — see above for semantics.
transition_cache_insert(
Expand Down
Loading