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/7455-raw-handle-debt.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
- **The Layer 3 raw-handle debt ratchet is green again, at a lower number.** It had been red on `main` since #7424 took it 1006 → 1008, and its baseline may only move *down*, so re-pinning was not an option. Both #7424 sites are correct — they re-derive a receiver after a proxy trap that can move it — but they spell that as a manual call/re-read pair rather than the `RuntimeHandle::across_*` combinator the RFC prescribes. Five such pairs are now converted (`array/push_pop.rs`, `promise/then.rs` ×2, `object/async_generator_queue.rs`, `string/append.rs`); each is semantically identical, but the ordering becomes structural instead of conventional — which is the point of #7341, where every fix in the family was an ordering bug rather than a missing root. 1008 → 1003, baseline locked at 1003. (#7455)
12 changes: 8 additions & 4 deletions crates/perry-runtime/src/array/push_pop.rs
Original file line number Diff line number Diff line change
Expand Up @@ -443,10 +443,14 @@ pub(super) fn proxy_array_mutator(
for k in 0..actual_delete_count {
let from = (actual_start + k).to_string();
if proxy_has_str_key(p(), from.as_bytes()) {
let v = proxy_get_str_key(p(), from.as_bytes());
// Re-derive `removed` from its handle: the traps above
// run arbitrary JS, which can move it.
let removed = removed_handle.get_raw_mut_ptr::<ArrayHeader>();
// The trap runs arbitrary JS and can move `removed`, so
// its address is only valid after the call. `across_mut`
// is that pattern as one combinator: it runs the call and
// hands back the post-collection address, so a stale
// pointer is never bound in between (#7341).
let (v, removed) = removed_handle.across_mut::<ArrayHeader, _>(|| {
proxy_get_str_key(p(), from.as_bytes())
});
let elems = (removed as *mut u8).add(std::mem::size_of::<ArrayHeader>())
as *mut f64;
// GC_STORE_AUDIT(BARRIERED): note_array_slot re-stores
Expand Down
6 changes: 4 additions & 2 deletions crates/perry-runtime/src/object/async_generator_queue.rs
Original file line number Diff line number Diff line change
Expand Up @@ -268,8 +268,10 @@ fn async_generator_request(closure: *const ClosureHeader, arg: f64, kind: Reques
let scope = crate::gc::RuntimeHandleScope::new();
let original_handle = scope.root_raw_const_ptr(original);
let arg_handle = scope.root_nanbox_f64(arg);
let promise = js_promise_new();
let original = original_handle.get_raw_const_ptr::<ClosureHeader>();
// `across_const` pairs the allocating call with the re-read, so the
// closure pointer cannot be bound stale in between (#7341).
let (promise, original) =
original_handle.across_const::<ClosureHeader, _>(|| js_promise_new());
let arg = arg_handle.get_nanbox_f64();
STATES.with(|states| {
if let Some(state) = states.borrow_mut().get_mut(state_id - 1) {
Expand Down
12 changes: 8 additions & 4 deletions crates/perry-runtime/src/promise/then.rs
Original file line number Diff line number Diff line change
Expand Up @@ -488,8 +488,10 @@ pub extern "C" fn js_promise_then(
let promise_handle = scope.root_raw_mut_ptr(promise);
let on_fulfilled_handle = scope.root_raw_const_ptr(on_fulfilled);
let on_rejected_handle = scope.root_raw_const_ptr(on_rejected);
let next = js_promise_new_with_parent(promise);
let promise = promise_handle.get_raw_mut_ptr::<Promise>();
// `across_mut` runs the allocating call and returns the post-collection
// address, so the receiver is never bound stale in between (#7341).
let (next, promise) =
promise_handle.across_mut::<Promise, _>(|| js_promise_new_with_parent(promise));
let on_fulfilled = on_fulfilled_handle.get_raw_const_ptr::<crate::closure::ClosureHeader>();
let on_rejected = on_rejected_handle.get_raw_const_ptr::<crate::closure::ClosureHeader>();

Expand Down Expand Up @@ -764,8 +766,10 @@ pub extern "C" fn js_promise_finally(
let scope = crate::gc::RuntimeHandleScope::new();
let promise_handle = scope.root_raw_mut_ptr(promise);
let on_finally_handle = scope.root_raw_const_ptr(on_finally);
let next = js_promise_new_with_parent(promise);
let promise = promise_handle.get_raw_mut_ptr::<Promise>();
// See the sibling in `then`: the allocating call and the re-read are one
// combinator so the order cannot drift apart (#7341).
let (next, promise) =
promise_handle.across_mut::<Promise, _>(|| js_promise_new_with_parent(promise));
let on_finally = on_finally_handle.get_raw_const_ptr::<crate::closure::ClosureHeader>();
let next_i64 = next as i64;

Expand Down
7 changes: 5 additions & 2 deletions crates/perry-runtime/src/string/append.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,11 @@ pub extern "C" fn js_string_append(
let scope = crate::gc::RuntimeHandleScope::new();
let src_handle = scope.root_string_ptr(src);
let src_blen = unsafe { (*src).byte_len };
let new_ptr = js_string_from_bytes_with_capacity(ptr::null(), 0, src_blen);
let src = src_handle.get_raw_const_ptr::<StringHeader>();
// `across_const` pairs the allocating call with the re-read, so the
// source pointer cannot be bound stale in between (#7341).
let (new_ptr, src) = src_handle.across_const::<StringHeader, _>(|| {
js_string_from_bytes_with_capacity(ptr::null(), 0, src_blen)
});
if is_valid_string_ptr(src) {
unsafe {
let src_data = string_data(src);
Expand Down
2 changes: 1 addition & 1 deletion scripts/raw_handle_debt_baseline.txt
Original file line number Diff line number Diff line change
@@ -1 +1 @@
1006
1003
Loading