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
1 change: 1 addition & 0 deletions changelog.d/12138-residual-prototype-test-roots.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Root the atom/intern, shape-table and canonical-keys scanners in the residual prototype GC tests, whose copying-nursery window started with no scanners and freed live key atoms (#12137).
18 changes: 18 additions & 0 deletions crates/perry-runtime/src/gc/tests/residual_prototype_relocation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,21 @@ fn marked_prototype() -> usize {
proto as usize
}

/// `CopyingNurseryTestGuard` starts with an empty root scanner registry. But
/// `Object.setPrototypeOf` and `Object.getPrototypeOf` on a builtin owner build
/// intrinsics lazily inside the window, and that mints key strings in the
/// thread's string atom table. Without that table's scanner, a copying minor
/// frees the atoms while the table still names them. The next intrinsic build
/// then takes a freed address as a key, and a key list later reads a garbage
/// length from it, up to the end of the 2 MiB block (#12137). Register the
/// key tables as production does: the atom and intern table holds its strings,
/// and the shape table and canonical keys trie follow moved keys arrays.
fn register_key_table_scanners() {
gc_register_mutable_root_scanner(crate::string::scan_intern_table_roots_mut);
gc_register_mutable_root_scanner(crate::object::shapes::scan_shape_table_rekey_mut);
gc_register_mutable_root_scanner(crate::object::canonical_keys::scan_canonical_keys_roots_mut);
}

fn forget_owners(owners: &[usize]) {
crate::object::prototype_chain::prune_dead_object_prototype_owners(&|owner| {
owners.contains(&owner)
Expand All @@ -91,6 +106,7 @@ fn test_lazy_array_explicit_prototype_survives_a_copying_minor() {
// slots: a store outside the pushed frame is a silent no-op (#7184).
let _guard = CopyingNurseryTestGuard::new(2);
let _trigger_guard = GcTriggerThresholdTestGuard::suppress_automatic_triggers();
register_key_table_scanners();

let lazy = nursery_lazy_array(b"[1,2,3]");
assert_eq!(
Expand Down Expand Up @@ -194,6 +210,7 @@ fn test_suppressing_the_residual_owner_bit_loses_the_prototype() {
let _latch = ArrayPrototypeLatchRestore::capture();
let _guard = CopyingNurseryTestGuard::new(1);
let _trigger_guard = GcTriggerThresholdTestGuard::suppress_automatic_triggers();
register_key_table_scanners();

let owner = crate::array::js_array_alloc(4) as usize;
let obj_type = obj_type_at(owner);
Expand Down Expand Up @@ -263,6 +280,7 @@ fn test_residual_prototype_owners_of_every_movable_kind_survive_a_copying_minor(
// One rooted owner at a time; its prototype is deliberately unrooted.
let _guard = CopyingNurseryTestGuard::new(1);
let _trigger_guard = GcTriggerThresholdTestGuard::suppress_automatic_triggers();
register_key_table_scanners();

type Alloc = Box<dyn Fn() -> usize>;
let mut owners: Vec<(&str, Alloc)> = vec![
Expand Down
Loading