diff --git a/changelog.d/11911-raw-handle-debt-roots.md b/changelog.d/11911-raw-handle-debt-roots.md new file mode 100644 index 0000000000..b0d948626d --- /dev/null +++ b/changelog.d/11911-raw-handle-debt-roots.md @@ -0,0 +1,7 @@ +Rooted the raw-pointer reads that pushed `raw_handle_debt` over its ceiling. +JSON.stringify of a proxy with a function replacer now hands the replacer to +its consumer through the root (`with_const_ptr`) instead of binding a raw +closure pointer, and keeps the replacer result rooted across the key write. +Two test-only reads (private storage cache, eden entry residency) moved into +scoped-pointer closures. The baseline falls from 871 to 869 and +`reflect_support.rs` drops its stale ceiling from 3 to 2. diff --git a/crates/perry-runtime/src/gc/tests/eden_entry_residency.rs b/crates/perry-runtime/src/gc/tests/eden_entry_residency.rs index b49a7e5428..b8cf857546 100644 --- a/crates/perry-runtime/src/gc/tests/eden_entry_residency.rs +++ b/crates/perry-runtime/src/gc/tests/eden_entry_residency.rs @@ -79,12 +79,14 @@ fn real_full_collection_gives_eden_a_reuse_interval_then_discards_idle_pages() { ); } for root in [live, extra_root] { - let string = root.get_raw_const_ptr::(); - assert_eq!((*string).byte_len, bytes.len() as u32); - assert_eq!( - std::slice::from_raw_parts(crate::string::string_data(string), bytes.len()), - bytes - ); + // The closure only reads the string; nothing here allocates. + root.with_const_ptr(|string: *const crate::string::StringHeader| { + assert_eq!((*string).byte_len, bytes.len() as u32); + assert_eq!( + std::slice::from_raw_parts(crate::string::string_data(string), bytes.len()), + bytes + ); + }); } }) .join() diff --git a/crates/perry-runtime/src/json/stringify_proxy.rs b/crates/perry-runtime/src/json/stringify_proxy.rs index 5912cd45d4..4064161f05 100644 --- a/crates/perry-runtime/src/json/stringify_proxy.rs +++ b/crates/perry-runtime/src/json/stringify_proxy.rs @@ -112,21 +112,22 @@ pub(super) unsafe fn try_stringify( let member = crate::proxy::js_proxy_get(receiver.get_nanbox_f64(), key.get_nanbox_f64()); let member = super::replacer::apply_to_json_keyed(member, key.get_nanbox_f64()); let member = member_scope.root_nanbox_f64(member); - let replacer = match replacer_root.as_ref() { - Some(root) => Replacer::Function(root.get_raw_const_ptr()), - None => replacer, - }; - let member = if let Replacer::Function(f) = replacer { - super::replacer::call_replacer( - f, - key.get_nanbox_f64(), - member.get_nanbox_f64(), - receiver.get_nanbox_f64(), - ) - } else { - member.get_nanbox_f64() + let member = match replacer_root.as_ref() { + Some(root) => root.with_const_ptr(|f: *const crate::ClosureHeader| { + super::replacer::call_replacer( + f, + key.get_nanbox_f64(), + member.get_nanbox_f64(), + receiver.get_nanbox_f64(), + ) + }), + None => member.get_nanbox_f64(), }; - if !is_array && omitted(member) { + // The replacer result is the value emitted below; keep it rooted across + // the key write and any allocation in the emit. + let member = member_scope.root_nanbox_f64(member); + let member_bits = member.get_nanbox_f64(); + if !is_array && omitted(member_bits) { continue; } if !first { @@ -141,11 +142,13 @@ pub(super) unsafe fn try_stringify( buf.push(' '); } } - let replacer = match replacer_root.as_ref() { - Some(root) => Replacer::Function(root.get_raw_const_ptr()), - None => replacer, - }; - emit_prepared(member, buf, indent, depth + 1, replacer); + let member = member.get_nanbox_f64(); + match replacer_root.as_ref() { + Some(root) => root.with_const_ptr(|f: *const crate::ClosureHeader| { + emit_prepared(member, buf, indent, depth + 1, Replacer::Function(f)) + }), + None => emit_prepared(member, buf, indent, depth + 1, replacer), + } } if !first { newline(buf, indent, depth); diff --git a/crates/perry-runtime/src/object/field_get_set/ic_miss/private_storage_cache.rs b/crates/perry-runtime/src/object/field_get_set/ic_miss/private_storage_cache.rs index ec14cc8788..d700ed4064 100644 --- a/crates/perry-runtime/src/object/field_get_set/ic_miss/private_storage_cache.rs +++ b/crates/perry-runtime/src/object/field_get_set/ic_miss/private_storage_cache.rs @@ -290,13 +290,13 @@ mod private_storage_cache_tests { }); let held = scope.root_raw_mut_ptr(crate::object::js_object_alloc(62_641, 0)); let _ = held; - unsafe { - let holder = other.get_raw_mut_ptr::(); + // `apply_edits` suppresses moving collection for its whole body. + other.with_mut_ptr(|holder: *mut ObjectHeader| unsafe { crate::object::key_attrs::apply_edits( holder, &[crate::object::key_attrs::AttrsEdit::Private(key.as_bytes())], ); - } + }); assert!(key.set_cached(other_receiver(), 11.0)); assert_eq!(key.get_cached(other_receiver()), Some(11.0)); let first_shape = key.slot.get().unwrap().0; diff --git a/scripts/raw_handle_debt_baseline.txt b/scripts/raw_handle_debt_baseline.txt index 54b472ca76..978c515a2f 100644 --- a/scripts/raw_handle_debt_baseline.txt +++ b/scripts/raw_handle_debt_baseline.txt @@ -1 +1 @@ -871 +869 diff --git a/scripts/raw_handle_debt_files.txt b/scripts/raw_handle_debt_files.txt index 198ef67103..8bc8c8fb98 100644 --- a/scripts/raw_handle_debt_files.txt +++ b/scripts/raw_handle_debt_files.txt @@ -131,7 +131,7 @@ 4 crates/perry-runtime/src/object/object_ops/from_entries.rs 5 crates/perry-runtime/src/object/object_ops/keys_array.rs 6 crates/perry-runtime/src/object/polymorphic_index.rs -3 crates/perry-runtime/src/object/reflect_support.rs +2 crates/perry-runtime/src/object/reflect_support.rs 2 crates/perry-runtime/src/object/spill.rs 2 crates/perry-runtime/src/object/typed_array_define.rs 3 crates/perry-runtime/src/os.rs