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
8 changes: 8 additions & 0 deletions changelog.d/9208-raw-handle-debt.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
The raw-handle debt ratchet now counts empty
`RuntimeHandle::across_{mut,const,nanbox}(|| ())` wrappers as debt. Those
wrappers refreshed a handle across no work, so they were equivalent to a bare
pointer read while still receiving credit as a conversion.

All 17 existing no-op wrappers now use scoped handle access or put the real
allocation-capable operation inside `across_*`. The total baseline and every
per-module ceiling remain unchanged.
3 changes: 1 addition & 2 deletions crates/perry-runtime/src/array/iter_object.rs
Original file line number Diff line number Diff line change
Expand Up @@ -83,8 +83,7 @@ unsafe fn alloc_iterator_backing(backing: f64, kind: i32) -> f64 {
// `Object.getPrototypeOf(it)` and the inherited `.next` read resolve.
obj_h
.with_mut_ptr(|obj| crate::object::attach_iterator_prototype(obj, ARRAY_ITERATOR_CLASS_ID));
let (_, obj) = obj_h.across_mut::<ObjectHeader, _>(|| ());
js_nanbox_pointer(obj as i64)
obj_h.with_mut_ptr::<ObjectHeader, _>(|obj| js_nanbox_pointer(obj as i64))
}

unsafe fn alloc_iterator(arr_ptr: *mut ArrayHeader, kind: i32) -> f64 {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -383,8 +383,9 @@ pub extern "C" fn js_boxed_symbol_new(value: f64) -> f64 {
obj.with_mut_ptr::<crate::object::ObjectHeader, _>(|obj| {
attach_boxed_primitive_prototype(obj, CLASS_ID_BOXED_SYMBOL)
});
let (_, obj) = obj.across_mut::<crate::object::ObjectHeader, _>(|| ());
crate::value::js_nanbox_pointer(obj as i64)
obj.with_mut_ptr::<crate::object::ObjectHeader, _>(|obj| {
crate::value::js_nanbox_pointer(obj as i64)
})
}

#[cfg(test)]
Expand Down
34 changes: 19 additions & 15 deletions crates/perry-runtime/src/collection_iter_object.rs
Original file line number Diff line number Diff line change
Expand Up @@ -71,35 +71,40 @@ unsafe fn alloc_iterator(class_id: u32, coll_nanboxed: f64, kind: i32) -> f64 {
let scope = crate::gc::RuntimeHandleScope::new();
let coll_h = scope.root_nanbox_f64(coll_nanboxed);
let obj_h = scope.root_raw_mut_ptr(js_object_alloc(class_id, 6));
let obj = || obj_h.across_mut::<ObjectHeader, _>(|| ()).1;
// Field 0: backing collection (NaN-boxed pointer so the GC scanner keeps it).
js_object_set_field(
obj(),
0,
JSValue::from_bits(coll_h.get_nanbox_f64().to_bits()),
);
obj_h.with_mut_ptr::<ObjectHeader, _>(|obj| {
js_object_set_field(
obj,
0,
JSValue::from_bits(coll_h.get_nanbox_f64().to_bits()),
)
});
// Field 1: cursor index (index just past the last-returned entry), starts at 0.
js_object_set_field(obj(), 1, JSValue::number(0.0));
obj_h.with_mut_ptr::<ObjectHeader, _>(|obj| js_object_set_field(obj, 1, JSValue::number(0.0)));
// Field 2: iterator kind.
js_object_set_field(obj(), 2, JSValue::number(kind as f64));
obj_h.with_mut_ptr::<ObjectHeader, _>(|obj| {
js_object_set_field(obj, 2, JSValue::number(kind as f64))
});
// Field 3: collection size observed at the last `next()`. `-1` sentinel means
// "not started" (no entry returned yet). Used to detect a mid-iteration
// delete (which compacts the entries array, shifting live entries below the
// cursor) so the cursor can be re-derived from the last key (#6075).
js_object_set_field(obj(), 3, JSValue::number(-1.0));
obj_h.with_mut_ptr::<ObjectHeader, _>(|obj| js_object_set_field(obj, 3, JSValue::number(-1.0)));
// Field 4: the KEY of the last-returned entry (a Map key / Set value), used
// to re-derive the cursor after a delete-shift. Undefined until started.
js_object_set_field(obj(), 4, JSValue::undefined());
obj_h.with_mut_ptr::<ObjectHeader, _>(|obj| js_object_set_field(obj, 4, JSValue::undefined()));
// Field 5: the recycled `{value, done}` result the FUSED for-of driver
// mutates in place (one allocation per loop, not per element). Manual
// `.next()` calls never touch it — they keep returning fresh objects, so
// a caller that retains results observes spec behavior.
js_object_set_field(obj(), 5, JSValue::undefined());
obj_h.with_mut_ptr::<ObjectHeader, _>(|obj| js_object_set_field(obj, 5, JSValue::undefined()));
// Link `[[Prototype]]` to the shared `%MapIteratorPrototype%` /
// `%SetIteratorPrototype%` singleton so `Object.getPrototypeOf(it)` and the
// inherited `.next` read resolve.
crate::object::attach_iterator_prototype(obj(), class_id);
js_nanbox_pointer(obj() as i64)
obj_h.with_mut_ptr::<ObjectHeader, _>(|obj| {
crate::object::attach_iterator_prototype(obj, class_id)
});
obj_h.with_mut_ptr::<ObjectHeader, _>(|obj| js_nanbox_pointer(obj as i64))
}

/// Build a fresh Map iterator object for `map` (raw pointer) of the given
Expand Down Expand Up @@ -210,8 +215,7 @@ unsafe fn make_pair_array(a: f64, b: f64) -> f64 {
(*pair).length = 2;
crate::array::rebuild_array_layout_exact(pair);
});
let (_, pair) = pair.across_mut::<ArrayHeader, _>(|| ());
js_nanbox_pointer(pair as i64)
pair.with_mut_ptr::<ArrayHeader, _>(|pair| js_nanbox_pointer(pair as i64))
}

/// Compute the entries-array index to read next, self-correcting for a
Expand Down
16 changes: 6 additions & 10 deletions crates/perry-runtime/src/dyn_eval/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -995,16 +995,12 @@ fn script_literals_use_fresh_populated_realm_prototypes() {
let outer_object_prototype = crate::object::builtin_prototype_value("Object");
let scope = crate::gc::RuntimeHandleScope::new();
let intrinsics = scope.root_raw_mut_ptr(crate::object::js_object_alloc(0, 0));
crate::object::populate_global_this_builtins(
intrinsics
.across_mut::<crate::object::ObjectHeader, _>(|| ())
.1,
);
let intrinsics = crate::value::js_nanbox_pointer(
intrinsics
.across_mut::<crate::object::ObjectHeader, _>(|| ())
.1 as i64,
);
intrinsics.with_mut_ptr::<crate::object::ObjectHeader, _>(|intrinsics| {
crate::object::populate_global_this_builtins(intrinsics)
});
let intrinsics = intrinsics.with_mut_ptr::<crate::object::ObjectHeader, _>(|intrinsics| {
crate::value::js_nanbox_pointer(intrinsics as i64)
});
let realm_object_prototype = bridge::intrinsic_prototype(intrinsics, "Object");
assert_ne!(
realm_object_prototype.to_bits(),
Expand Down
18 changes: 10 additions & 8 deletions crates/perry-runtime/src/intl/list_relative_plural.rs
Original file line number Diff line number Diff line change
Expand Up @@ -809,15 +809,17 @@ pub(super) fn configure_plural_rules(
false,
)
});
obj_handle.with_mut_ptr(|obj| {
install_bound_instance_function(
obj,
"resolvedOptions",
plural_rules_bound_resolved_options_thunk as *const u8,
0,
)
let (_, obj) = obj_handle.across_mut::<ObjectHeader, _>(|| {
obj_handle.with_mut_ptr(|obj| {
install_bound_instance_function(
obj,
"resolvedOptions",
plural_rules_bound_resolved_options_thunk as *const u8,
0,
)
})
});
obj_handle.across_mut::<ObjectHeader, _>(|| ()).1
obj
}

/// en plural-category selection. Cardinal: `i == 1 && v == 0` → "one". Ordinal
Expand Down
6 changes: 4 additions & 2 deletions crates/perry-runtime/src/map.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2999,8 +2999,10 @@ pub extern "C" fn js_map_from_iterable(value: f64) -> *mut MapHeader {
}
}

match constructor_iter(value_handle.get_nanbox_f64()) {
ConstructorIter::Empty => map_handle.across_mut::<MapHeader, _>(|| ()).1,
let (source, map_after_classify) =
map_handle.across_mut::<MapHeader, _>(|| constructor_iter(value_handle.get_nanbox_f64()));
match source {
ConstructorIter::Empty => map_after_classify,
ConstructorIter::Array(arr_value) => {
let arr_handle = scope.root_nanbox_f64(arr_value);
let arr_ptr = crate::value::js_nanbox_get_pointer(arr_handle.get_nanbox_f64())
Expand Down
Loading
Loading