From 7dd0c1f7a79bebc5c17549e9ca2984b3ff7dfc79 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ralph=20K=C3=BCpper?= Date: Tue, 25 Aug 2026 07:41:38 +0200 Subject: [PATCH 1/5] test: normalize method literal GC arms --- .../tests/static_method_object_literal.rs | 36 +++++++++++++++---- 1 file changed, 30 insertions(+), 6 deletions(-) diff --git a/crates/perry/tests/static_method_object_literal.rs b/crates/perry/tests/static_method_object_literal.rs index 453ba9d593..b20c40b4cd 100644 --- a/crates/perry/tests/static_method_object_literal.rs +++ b/crates/perry/tests/static_method_object_literal.rs @@ -8,6 +8,29 @@ use std::path::{Path, PathBuf}; use std::process::{Command, Output}; use std::sync::Once; +/// Keep the forced-moving arm independent of ambient developer/CI settings. +/// Some inputs affect code generation, so normalize the runtime build, +/// fixture compile, and child process alike. +const GC_ENV_OVERRIDES: &[&str] = &[ + "PERRY_GEN_GC", + "PERRY_GC_SCAVENGE", + "PERRY_GC_SCAVENGE_NURSERY_MB", + "PERRY_GC_MOVING_SAFEPOINT", + "PERRY_GC_MOVING_LOOP_POLLS", + "PERRY_GC_FORCE_EVACUATE", + "PERRY_GC_VERIFY_EVACUATION", + "PERRY_CONSERVATIVE_STACK_SCAN", + "PERRY_WRITE_BARRIERS", + "PERRY_GC_INCREMENTAL", + "PERRY_GC_HEAP_LIMIT", +]; + +fn remove_gc_env_overrides(command: &mut Command) { + for key in GC_ENV_OVERRIDES { + command.env_remove(key); + } +} + fn perry_bin() -> PathBuf { PathBuf::from(env!("CARGO_BIN_EXE_perry")) } @@ -25,6 +48,7 @@ fn runtime_dir() -> PathBuf { let cargo = std::env::var_os("CARGO").unwrap_or_else(|| "cargo".into()); let mut command = Command::new(cargo); command.current_dir(workspace_root()).arg("build"); + remove_gc_env_overrides(&mut command); if !cfg!(debug_assertions) { command.arg("--release"); } @@ -48,14 +72,11 @@ fn runtime_dir() -> PathBuf { fn run_fixture(binary: &Path, force_evacuation: bool) -> Output { let mut command = Command::new(binary); + remove_gc_env_overrides(&mut command); if force_evacuation { command .env("PERRY_GC_FORCE_EVACUATE", "1") .env("PERRY_GC_VERIFY_EVACUATION", "1"); - } else { - command - .env_remove("PERRY_GC_FORCE_EVACUATE") - .env_remove("PERRY_GC_VERIFY_EVACUATION"); } command.output().expect("run method-literal fixture") } @@ -108,7 +129,8 @@ console.log( ) .expect("write method-literal fixture"); - let compile = Command::new(perry_bin()) + let mut compile_command = Command::new(perry_bin()); + compile_command .current_dir(dir.path()) .arg("compile") .arg(&entry) @@ -116,7 +138,9 @@ console.log( .arg(&binary) .arg("--no-cache") .arg("--no-auto-optimize") - .env("PERRY_RUNTIME_DIR", runtime_dir()) + .env("PERRY_RUNTIME_DIR", runtime_dir()); + remove_gc_env_overrides(&mut compile_command); + let compile = compile_command .output() .expect("compile method-literal fixture"); assert!( From 1ebf4574dcf202469688d091390a063997b99a9c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ralph=20K=C3=BCpper?= Date: Tue, 25 Aug 2026 08:42:26 +0200 Subject: [PATCH 2/5] docs(runtime): pin captured cache hint bounds --- crates/perry-runtime/src/closure/alloc.rs | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/crates/perry-runtime/src/closure/alloc.rs b/crates/perry-runtime/src/closure/alloc.rs index f0c5b3da2b..ca21e81741 100644 --- a/crates/perry-runtime/src/closure/alloc.rs +++ b/crates/perry-runtime/src/closure/alloc.rs @@ -488,6 +488,10 @@ pub(crate) fn test_captured_singleton_closure_cache_entries( /// covers the per-batch fan-out shape (50 promises) found in /// `benchmarks/app-patterns/kernels/promise_all_chains.ts`. const MAX_CAPTURED_CLOSURE_SLOTS: usize = 64; +const _: () = assert!( + MAX_CAPTURED_CLOSURE_SLOTS <= u8::MAX as usize, + "hint_indices_plus_one stores an entry index plus one in a u8" +); /// Per-`func_ptr` cache miss-streak counter for the adaptive bypass. /// Closures whose captures change every call (per-call boxes for @@ -581,9 +585,8 @@ pub extern "C" fn js_closure_alloc_with_captures_singleton( } crate::promise::bump(&CLOSURE_CAP_SINGLETON_MISS); - // Slow path: allocate, populate captures, insert into cache as - // the most-recent entry. If the slot list is full, drop the - // least-recent (back of the Vec). + // Slow path: allocate, populate captures, and insert with a fresh usage + // timestamp. If the entry list is full, replace its oldest timestamp. let capture_scope = crate::gc::RuntimeHandleScope::new(); let capture_handles: Vec<_> = captures_slice .iter() From 5ecd8094f3dcbaa77a5e7fa21ff5bea0be0a1a99 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ralph=20K=C3=BCpper?= Date: Tue, 25 Aug 2026 08:58:44 +0200 Subject: [PATCH 3/5] runtime: allocate native async-resolution promises in malloc space Native async resolutions (fetch/db/ws/etc.) created their Promise via the nursery arena (js_promise_new), pinned it, and handed the raw pointer to a tokio worker. A copying-minor from-space flip wipes a nursery resident regardless of its pin flag (the flip resets eden/survivor blocks wholesale; only root-reachable pins force the fallback), so the worker's later resolution dereferenced a reclaimed Promise -> SIGSEGV in js_stdlib_process_pending. Allocate these promises via js_promise_new_cross_thread (malloc space, non-moving; both sweep paths honor GC_FLAG_PINNED). Re-export the symbol from perry-runtime and switch every native-binding caller. Found getting the compiled Claude Code CLI to run natively; confirmed to remove the js_stdlib_process_pending fault under PERRY_GC_PROTECT_FROMSPACE. Claude-Session: https://claude.ai/code/session_01TwxRkALrR9HKSF1zKLSTAF --- crates/perry-runtime/src/lib.rs | 3 +- crates/perry-stdlib/src/argon2.rs | 6 ++-- crates/perry-stdlib/src/axios.rs | 12 +++---- crates/perry-stdlib/src/bcrypt.rs | 6 ++-- .../perry-stdlib/src/common/async_bridge.rs | 13 +++++++- .../perry-stdlib/src/container/backend_ctl.rs | 10 +++--- .../perry-stdlib/src/container/compose_ffi.rs | 20 ++++++------ crates/perry-stdlib/src/container/images.rs | 10 +++--- .../perry-stdlib/src/container/lifecycle.rs | 22 ++++++------- .../perry-stdlib/src/container/logs_exec.rs | 6 ++-- crates/perry-stdlib/src/container/workload.rs | 16 +++++----- crates/perry-stdlib/src/fetch/mod.rs | 30 ++++++++--------- crates/perry-stdlib/src/ioredis.rs | 32 +++++++++---------- crates/perry-stdlib/src/mongodb.rs | 30 ++++++++--------- crates/perry-stdlib/src/mysql2/connection.rs | 16 +++++----- crates/perry-stdlib/src/mysql2/pool.rs | 14 ++++---- crates/perry-stdlib/src/net/mod.rs | 2 +- crates/perry-stdlib/src/nodemailer.rs | 6 ++-- crates/perry-stdlib/src/pg/connection.rs | 12 +++---- crates/perry-stdlib/src/pg/pool.rs | 8 ++--- crates/perry-stdlib/src/sharp.rs | 8 ++--- .../src/worker_threads/async_shim.rs | 4 +-- crates/perry-stdlib/src/ws.rs | 6 ++-- 23 files changed, 152 insertions(+), 140 deletions(-) diff --git a/crates/perry-runtime/src/lib.rs b/crates/perry-runtime/src/lib.rs index fee2ff8e9e..3c0a9ce92d 100644 --- a/crates/perry-runtime/src/lib.rs +++ b/crates/perry-runtime/src/lib.rs @@ -307,7 +307,8 @@ pub use object::{ }; pub use promise::{js_is_promise, js_promise_run_microtasks, js_promise_state, js_promise_value}; pub use promise::{ - js_promise_mark_internally_handled, js_promise_new, js_promise_reject, js_promise_rejected, + js_promise_mark_internally_handled, js_promise_new, js_promise_new_cross_thread, + js_promise_reject, js_promise_rejected, js_promise_resolve, js_promise_resolved, }; pub use string::js_string_from_bytes; diff --git a/crates/perry-stdlib/src/argon2.rs b/crates/perry-stdlib/src/argon2.rs index 72a74c25cc..813b891c55 100644 --- a/crates/perry-stdlib/src/argon2.rs +++ b/crates/perry-stdlib/src/argon2.rs @@ -9,14 +9,14 @@ use argon2::{ password_hash::{rand_core::OsRng, PasswordHash, PasswordHasher, PasswordVerifier, SaltString}, Argon2, }; -use perry_runtime::{js_promise_new, js_string_from_bytes, Promise, StringHeader}; +use perry_runtime::{js_promise_new_cross_thread, js_string_from_bytes, Promise, StringHeader}; /// argon2.hash(password) -> Promise /// /// Hash a password using Argon2id with default parameters. #[no_mangle] pub unsafe extern "C" fn js_argon2_hash(password_ptr: *const StringHeader) -> *mut Promise { - let promise = js_promise_new(); + let promise = js_promise_new_cross_thread(); let password = match string_from_header(password_ptr) { Some(p) => p, @@ -77,7 +77,7 @@ pub unsafe extern "C" fn js_argon2_verify( hash_ptr: *const StringHeader, password_ptr: *const StringHeader, ) -> *mut Promise { - let promise = js_promise_new(); + let promise = js_promise_new_cross_thread(); let hash_str = match string_from_header(hash_ptr) { Some(h) => h, diff --git a/crates/perry-stdlib/src/axios.rs b/crates/perry-stdlib/src/axios.rs index 23c812df1b..2a606e45ba 100644 --- a/crates/perry-stdlib/src/axios.rs +++ b/crates/perry-stdlib/src/axios.rs @@ -7,7 +7,7 @@ use crate::common::{ get_handle, register_handle, spawn_for_promise, string_from_header_lossy as string_from_header, Handle, }; -use perry_runtime::{js_promise_new, js_string_from_bytes, Promise, StringHeader}; +use perry_runtime::{js_promise_new_cross_thread, js_string_from_bytes, Promise, StringHeader}; /// #598: read the body argument as a JSON string. Strings pass /// through as-is; everything else is JSON.stringify'd via the @@ -49,7 +49,7 @@ unsafe fn request_without_body( url_ptr: *const StringHeader, method: reqwest::Method, ) -> *mut Promise { - let promise = js_promise_new(); + let promise = js_promise_new_cross_thread(); let url = match string_from_header(url_ptr) { Some(u) => u, @@ -119,7 +119,7 @@ pub unsafe extern "C" fn js_axios_options(url_ptr: *const StringHeader) -> *mut /// axios.post(url, data) -> Promise #[no_mangle] pub unsafe extern "C" fn js_axios_post(url_ptr: *const StringHeader, data: f64) -> *mut Promise { - let promise = js_promise_new(); + let promise = js_promise_new_cross_thread(); let url = match string_from_header(url_ptr) { Some(u) => u, @@ -186,7 +186,7 @@ pub unsafe extern "C" fn js_axios_post(url_ptr: *const StringHeader, data: f64) /// axios.put(url, data) -> Promise #[no_mangle] pub unsafe extern "C" fn js_axios_put(url_ptr: *const StringHeader, data: f64) -> *mut Promise { - let promise = js_promise_new(); + let promise = js_promise_new_cross_thread(); let url = match string_from_header(url_ptr) { Some(u) => u, @@ -250,7 +250,7 @@ pub unsafe extern "C" fn js_axios_put(url_ptr: *const StringHeader, data: f64) - /// axios.delete(url) -> Promise #[no_mangle] pub unsafe extern "C" fn js_axios_delete(url_ptr: *const StringHeader) -> *mut Promise { - let promise = js_promise_new(); + let promise = js_promise_new_cross_thread(); let url = match string_from_header(url_ptr) { Some(u) => u, @@ -305,7 +305,7 @@ pub unsafe extern "C" fn js_axios_delete(url_ptr: *const StringHeader) -> *mut P /// axios.patch(url, data) -> Promise #[no_mangle] pub unsafe extern "C" fn js_axios_patch(url_ptr: *const StringHeader, data: f64) -> *mut Promise { - let promise = js_promise_new(); + let promise = js_promise_new_cross_thread(); let url = match string_from_header(url_ptr) { Some(u) => u, diff --git a/crates/perry-stdlib/src/bcrypt.rs b/crates/perry-stdlib/src/bcrypt.rs index 661300456e..88430d1144 100644 --- a/crates/perry-stdlib/src/bcrypt.rs +++ b/crates/perry-stdlib/src/bcrypt.rs @@ -15,7 +15,7 @@ pub unsafe extern "C" fn js_bcrypt_hash( password_ptr: *const StringHeader, salt_rounds: f64, ) -> *mut perry_runtime::Promise { - let promise = perry_runtime::js_promise_new(); + let promise = perry_runtime::js_promise_new_cross_thread(); let promise_ptr = promise as usize; let password = match string_from_header(password_ptr) { @@ -79,7 +79,7 @@ pub unsafe extern "C" fn js_bcrypt_compare( password_ptr: *const StringHeader, hash_ptr: *const StringHeader, ) -> *mut perry_runtime::Promise { - let promise = perry_runtime::js_promise_new(); + let promise = perry_runtime::js_promise_new_cross_thread(); let promise_ptr = promise as usize; let password = match string_from_header(password_ptr) { @@ -142,7 +142,7 @@ pub unsafe extern "C" fn js_bcrypt_compare( /// bcrypt.genSalt(rounds) -> Promise #[no_mangle] pub unsafe extern "C" fn js_bcrypt_gen_salt(rounds: f64) -> *mut perry_runtime::Promise { - let promise = perry_runtime::js_promise_new(); + let promise = perry_runtime::js_promise_new_cross_thread(); let promise_ptr = promise as usize; let cost = rounds as u32; diff --git a/crates/perry-stdlib/src/common/async_bridge.rs b/crates/perry-stdlib/src/common/async_bridge.rs index b65efe072e..5badc20ba3 100644 --- a/crates/perry-stdlib/src/common/async_bridge.rs +++ b/crates/perry-stdlib/src/common/async_bridge.rs @@ -83,7 +83,18 @@ unsafe fn unpin_promise_after_native_resolution(promise_ptr: usize) { #[inline] pub unsafe fn js_promise_new_for_native_resolution() -> *mut perry_runtime::Promise { ensure_gc_scanner_registered(); - let p = perry_runtime::js_promise_new(); + // #8770: allocate in MALLOC space (non-moving), not the nursery arena. A + // native-resolution promise is handed to a tokio worker as a raw `usize` and, + // until its resolution is queued into PENDING_RESOLUTIONS (which the root + // scanner visits), it is reachable only through that worker-thread capture — + // invisible to the main-thread copying minor. A nursery resident in that + // window is wiped by the from-space flip REGARDLESS of its PIN flag (the flip + // resets eden/survivor blocks wholesale; only root-reachable pins force the + // fallback — see `js_promise_new_cross_thread`). Then `js_stdlib_process_ + // pending` unpins/resolves through the stale pointer and faults on the + // reclaimed header. Malloc space is non-moving and both sweep paths honor + // GC_FLAG_PINNED, so the pin actually protects it there. + let p = perry_runtime::js_promise_new_cross_thread(); pin_promise_for_native_resolution(p as usize); p } diff --git a/crates/perry-stdlib/src/container/backend_ctl.rs b/crates/perry-stdlib/src/container/backend_ctl.rs index 26f429c7ff..10dfc11be8 100644 --- a/crates/perry-stdlib/src/container/backend_ctl.rs +++ b/crates/perry-stdlib/src/container/backend_ctl.rs @@ -6,7 +6,7 @@ pub use types::{ }; pub use backend::{detect_backend, ContainerBackend}; -use perry_runtime::{js_promise_new, Promise, StringHeader}; +use perry_runtime::{js_promise_new_cross_thread, Promise, StringHeader}; use std::collections::HashMap; use std::sync::Arc; use std::sync::OnceLock; @@ -77,7 +77,7 @@ pub unsafe extern "C" fn js_container_getBackend() -> *const StringHeader { /// FFI: js_container_detectBackend() -> *mut Promise #[no_mangle] pub unsafe extern "C" fn js_container_detectBackend() -> *mut Promise { - let promise = js_promise_new(); + let promise = js_promise_new_cross_thread(); crate::common::spawn_for_promise_deferred( promise as *mut u8, async move { @@ -199,7 +199,7 @@ pub unsafe extern "C" fn js_container_selectBackendFor( /// await setBackends(ready.map(b => b.name)); #[no_mangle] pub unsafe extern "C" fn js_container_getAvailableBackends() -> *mut Promise { - let promise = js_promise_new(); + let promise = js_promise_new_cross_thread(); crate::common::spawn_for_promise_deferred( promise as *mut u8, async move { @@ -249,7 +249,7 @@ pub unsafe extern "C" fn js_container_getBackendPriority() -> *const StringHeade /// - `"backend probe failed: "` #[no_mangle] pub unsafe extern "C" fn js_container_setBackend(name_ptr: *const StringHeader) -> *mut Promise { - let promise = js_promise_new(); + let promise = js_promise_new_cross_thread(); let name = match string_from_header(name_ptr) { Some(s) => s, None => { @@ -328,7 +328,7 @@ pub unsafe extern "C" fn js_container_setBackend(name_ptr: *const StringHeader) pub unsafe extern "C" fn js_container_setBackends( names_json_ptr: *const StringHeader, ) -> *mut Promise { - let promise = js_promise_new(); + let promise = js_promise_new_cross_thread(); let names_json = match string_from_header(names_json_ptr) { Some(s) => s, None => { diff --git a/crates/perry-stdlib/src/container/compose_ffi.rs b/crates/perry-stdlib/src/container/compose_ffi.rs index f6278941c6..44bec3be7d 100644 --- a/crates/perry-stdlib/src/container/compose_ffi.rs +++ b/crates/perry-stdlib/src/container/compose_ffi.rs @@ -6,7 +6,7 @@ pub use types::{ }; pub use backend::{detect_backend, ContainerBackend}; -use perry_runtime::{js_promise_new, Promise, StringHeader}; +use perry_runtime::{js_promise_new_cross_thread, Promise, StringHeader}; use std::collections::HashMap; use std::sync::Arc; use std::sync::OnceLock; @@ -19,7 +19,7 @@ pub unsafe extern "C" fn js_container_compose_start( handle: f64, services_json_ptr: *const StringHeader, ) -> *mut Promise { - let promise = js_promise_new(); + let promise = js_promise_new_cross_thread(); let handle_id = handle_id_from_f64(handle); let engine = match types::get_compose_handle(handle_id as u64) { @@ -57,7 +57,7 @@ pub unsafe extern "C" fn js_container_compose_stop( handle: f64, services_json_ptr: *const StringHeader, ) -> *mut Promise { - let promise = js_promise_new(); + let promise = js_promise_new_cross_thread(); let handle_id = handle_id_from_f64(handle); let engine = match types::get_compose_handle(handle_id as u64) { @@ -95,7 +95,7 @@ pub unsafe extern "C" fn js_container_compose_restart( handle: f64, services_json_ptr: *const StringHeader, ) -> *mut Promise { - let promise = js_promise_new(); + let promise = js_promise_new_cross_thread(); let handle_id = handle_id_from_f64(handle); let engine = match types::get_compose_handle(handle_id as u64) { @@ -131,7 +131,7 @@ pub unsafe extern "C" fn js_container_compose_restart( /// FFI: `js_container_compose_config(handle: f64) -> *mut Promise` #[no_mangle] pub unsafe extern "C" fn js_container_compose_config(handle: f64) -> *mut Promise { - let promise = js_promise_new(); + let promise = js_promise_new_cross_thread(); let handle_id = handle_id_from_f64(handle); let engine = match types::get_compose_handle(handle_id as u64) { @@ -164,7 +164,7 @@ pub unsafe extern "C" fn js_container_compose_config(handle: f64) -> *mut Promis pub unsafe extern "C" fn js_container_composeUp( spec_ptr: *const perry_runtime::StringHeader, ) -> *mut Promise { - let promise = js_promise_new(); + let promise = js_promise_new_cross_thread(); let spec = match types::parse_compose_spec(spec_ptr) { Ok(s) => s, @@ -283,7 +283,7 @@ pub unsafe extern "C" fn js_container_compose_down( handle: f64, opts_ptr: *const StringHeader, ) -> *mut Promise { - let promise = js_promise_new(); + let promise = js_promise_new_cross_thread(); let handle_id = handle_id_from_f64(handle); let opts_json = unsafe { string_from_header(opts_ptr) }; @@ -330,7 +330,7 @@ pub unsafe extern "C" fn js_container_compose_down( /// FFI: `js_container_compose_ps(handle: f64) -> *mut Promise` #[no_mangle] pub unsafe extern "C" fn js_container_compose_ps(handle: f64) -> *mut Promise { - let promise = js_promise_new(); + let promise = js_promise_new_cross_thread(); let handle_id = handle_id_from_f64(handle); let engine = match types::get_compose_handle(handle_id as u64) { @@ -376,7 +376,7 @@ pub unsafe extern "C" fn js_container_compose_logs( service_ptr: *const StringHeader, tail: f64, ) -> *mut Promise { - let promise = js_promise_new(); + let promise = js_promise_new_cross_thread(); let handle_id = handle_id_from_f64(handle); let engine = match types::get_compose_handle(handle_id as u64) { @@ -427,7 +427,7 @@ pub unsafe extern "C" fn js_container_compose_exec( service_ptr: *const StringHeader, cmd_json_ptr: *const StringHeader, ) -> *mut Promise { - let promise = js_promise_new(); + let promise = js_promise_new_cross_thread(); let handle_id = handle_id_from_f64(handle); let engine = match types::get_compose_handle(handle_id as u64) { diff --git a/crates/perry-stdlib/src/container/images.rs b/crates/perry-stdlib/src/container/images.rs index 858e8b11c3..225ba2f129 100644 --- a/crates/perry-stdlib/src/container/images.rs +++ b/crates/perry-stdlib/src/container/images.rs @@ -6,7 +6,7 @@ pub use types::{ }; pub use backend::{detect_backend, ContainerBackend}; -use perry_runtime::{js_promise_new, Promise, StringHeader}; +use perry_runtime::{js_promise_new_cross_thread, Promise, StringHeader}; use std::collections::HashMap; use std::sync::Arc; use std::sync::OnceLock; @@ -19,7 +19,7 @@ use std::sync::OnceLock; pub unsafe extern "C" fn js_container_pullImage( reference_ptr: *const StringHeader, ) -> *mut Promise { - let promise = js_promise_new(); + let promise = js_promise_new_cross_thread(); let reference = match string_from_header(reference_ptr) { Some(s) => s, @@ -52,7 +52,7 @@ pub unsafe extern "C" fn js_container_pullImage( /// FFI: js_container_listImages() -> *mut Promise #[no_mangle] pub unsafe extern "C" fn js_container_listImages() -> *mut Promise { - let promise = js_promise_new(); + let promise = js_promise_new_cross_thread(); // Resolves with a JSON-encoded `ImageInfo[]` string. crate::common::spawn_for_promise_deferred( @@ -78,7 +78,7 @@ pub unsafe extern "C" fn js_container_build( spec_ptr: *const StringHeader, image_name_ptr: *const StringHeader, ) -> *mut Promise { - let promise = js_promise_new(); + let promise = js_promise_new_cross_thread(); let spec_json = string_from_header(spec_ptr).unwrap_or_else(|| "{}".to_string()); let image_name = string_from_header(image_name_ptr).unwrap_or_default(); @@ -108,7 +108,7 @@ pub unsafe extern "C" fn js_container_removeImage( reference_ptr: *const StringHeader, force: i32, ) -> *mut Promise { - let promise = js_promise_new(); + let promise = js_promise_new_cross_thread(); let reference = match string_from_header(reference_ptr) { Some(s) => s, diff --git a/crates/perry-stdlib/src/container/lifecycle.rs b/crates/perry-stdlib/src/container/lifecycle.rs index 2d6097b8d6..a82d0e2ed8 100644 --- a/crates/perry-stdlib/src/container/lifecycle.rs +++ b/crates/perry-stdlib/src/container/lifecycle.rs @@ -6,7 +6,7 @@ pub use types::{ }; pub use backend::{detect_backend, ContainerBackend}; -use perry_runtime::{js_promise_new, Promise, StringHeader}; +use perry_runtime::{js_promise_new_cross_thread, Promise, StringHeader}; use std::collections::HashMap; use std::sync::Arc; use std::sync::OnceLock; @@ -17,7 +17,7 @@ use std::sync::OnceLock; /// FFI: js_container_run(spec_json: *const StringHeader) -> *mut Promise #[no_mangle] pub unsafe extern "C" fn js_container_run(spec_ptr: *const StringHeader) -> *mut Promise { - let promise = js_promise_new(); + let promise = js_promise_new_cross_thread(); let spec = match types::parse_container_spec(spec_ptr) { Ok(s) => s, @@ -66,7 +66,7 @@ pub unsafe extern "C" fn js_container_run(spec_ptr: *const StringHeader) -> *mut /// FFI: js_container_create(spec_json: *const StringHeader) -> *mut Promise #[no_mangle] pub unsafe extern "C" fn js_container_create(spec_ptr: *const StringHeader) -> *mut Promise { - let promise = js_promise_new(); + let promise = js_promise_new_cross_thread(); let spec = match types::parse_container_spec(spec_ptr) { Ok(s) => s, @@ -111,7 +111,7 @@ pub unsafe extern "C" fn js_container_create(spec_ptr: *const StringHeader) -> * /// FFI: js_container_start(id: *const StringHeader) -> *mut Promise #[no_mangle] pub unsafe extern "C" fn js_container_start(id_ptr: *const StringHeader) -> *mut Promise { - let promise = js_promise_new(); + let promise = js_promise_new_cross_thread(); let id = match string_from_header(id_ptr) { Some(s) => s, @@ -144,7 +144,7 @@ pub unsafe extern "C" fn js_container_stop( id_ptr: *const StringHeader, timeout: i32, ) -> *mut Promise { - let promise = js_promise_new(); + let promise = js_promise_new_cross_thread(); let id = match string_from_header(id_ptr) { Some(s) => s, @@ -182,7 +182,7 @@ pub unsafe extern "C" fn js_container_remove( id_ptr: *const StringHeader, force: i32, ) -> *mut Promise { - let promise = js_promise_new(); + let promise = js_promise_new_cross_thread(); let id = match string_from_header(id_ptr) { Some(s) => s, @@ -230,7 +230,7 @@ pub unsafe extern "C" fn js_container_downByProject( project_ptr: *const StringHeader, opts_ptr: *const StringHeader, ) -> *mut Promise { - let promise = js_promise_new(); + let promise = js_promise_new_cross_thread(); let project = match string_from_header(project_ptr) { Some(s) if !s.is_empty() => s, _ => { @@ -271,7 +271,7 @@ pub unsafe extern "C" fn js_container_downByProject( /// FFI: `js_container_downAll(opts_json: *const StringHeader) -> *mut Promise` #[no_mangle] pub unsafe extern "C" fn js_container_downAll(opts_ptr: *const StringHeader) -> *mut Promise { - let promise = js_promise_new(); + let promise = js_promise_new_cross_thread(); let opts_json = string_from_header(opts_ptr); crate::common::spawn_for_promise_deferred( @@ -302,7 +302,7 @@ pub unsafe extern "C" fn js_container_removeIfExists( id_ptr: *const StringHeader, force: i32, ) -> *mut Promise { - let promise = js_promise_new(); + let promise = js_promise_new_cross_thread(); let id = match string_from_header(id_ptr) { Some(s) if !s.is_empty() => s, _ => { @@ -363,7 +363,7 @@ pub(crate) fn parse_cleanup_options( /// `JSON.parse(await list(true))` to recover the array. #[no_mangle] pub unsafe extern "C" fn js_container_list(all: i32) -> *mut Promise { - let promise = js_promise_new(); + let promise = js_promise_new_cross_thread(); crate::common::spawn_for_promise_deferred( promise as *mut u8, @@ -385,7 +385,7 @@ pub unsafe extern "C" fn js_container_list(all: i32) -> *mut Promise { /// FFI: js_container_inspect(id: *const StringHeader) -> *mut Promise #[no_mangle] pub unsafe extern "C" fn js_container_inspect(id_ptr: *const StringHeader) -> *mut Promise { - let promise = js_promise_new(); + let promise = js_promise_new_cross_thread(); let id = match string_from_header(id_ptr) { Some(s) => s, diff --git a/crates/perry-stdlib/src/container/logs_exec.rs b/crates/perry-stdlib/src/container/logs_exec.rs index e3c254c65e..d03577dd33 100644 --- a/crates/perry-stdlib/src/container/logs_exec.rs +++ b/crates/perry-stdlib/src/container/logs_exec.rs @@ -6,7 +6,7 @@ pub use types::{ }; pub use backend::{detect_backend, ContainerBackend}; -use perry_runtime::{js_promise_new, Promise, StringHeader}; +use perry_runtime::{js_promise_new_cross_thread, Promise, StringHeader}; use std::collections::HashMap; use std::sync::Arc; use std::sync::OnceLock; @@ -17,7 +17,7 @@ use std::sync::OnceLock; /// FFI: js_container_logs(id: *const StringHeader, tail: i32) -> *mut Promise #[no_mangle] pub unsafe extern "C" fn js_container_logs(id_ptr: *const StringHeader, tail: i32) -> *mut Promise { - let promise = js_promise_new(); + let promise = js_promise_new_cross_thread(); let id = match string_from_header(id_ptr) { Some(s) => s, @@ -60,7 +60,7 @@ pub unsafe extern "C" fn js_container_exec( env_json_ptr: *const StringHeader, workdir_ptr: *const StringHeader, ) -> *mut Promise { - let promise = js_promise_new(); + let promise = js_promise_new_cross_thread(); let id = match string_from_header(id_ptr) { Some(s) => s, diff --git a/crates/perry-stdlib/src/container/workload.rs b/crates/perry-stdlib/src/container/workload.rs index 8053d70bb4..51da2d0074 100644 --- a/crates/perry-stdlib/src/container/workload.rs +++ b/crates/perry-stdlib/src/container/workload.rs @@ -6,7 +6,7 @@ pub use types::{ }; pub use backend::{detect_backend, ContainerBackend}; -use perry_runtime::{js_promise_new, Promise, StringHeader}; +use perry_runtime::{js_promise_new_cross_thread, Promise, StringHeader}; use std::collections::HashMap; use std::sync::Arc; use std::sync::OnceLock; @@ -69,7 +69,7 @@ pub unsafe extern "C" fn js_workload_runGraph( graph_json_ptr: *const StringHeader, opts_json_ptr: *const StringHeader, ) -> *mut Promise { - let promise = js_promise_new(); + let promise = js_promise_new_cross_thread(); let graph_json = string_from_header(graph_json_ptr).unwrap_or_else(|| "{}".to_string()); let opts_json = string_from_header(opts_json_ptr).unwrap_or_else(|| "{}".to_string()); @@ -104,7 +104,7 @@ pub unsafe extern "C" fn js_workload_runGraph( /// FFI: js_workload_inspectGraph(handle_id: i64) -> *mut Promise #[no_mangle] pub unsafe extern "C" fn js_workload_inspectGraph(handle_id: i64) -> *mut Promise { - let promise = js_promise_new(); + let promise = js_promise_new_cross_thread(); let id = handle_id as u64; crate::common::spawn_for_promise_deferred( @@ -136,7 +136,7 @@ pub unsafe extern "C" fn js_workload_inspectGraph(handle_id: i64) -> *mut Promis /// FFI: js_workload_handle_down(handle_id: i64, force: i32) -> *mut Promise #[no_mangle] pub unsafe extern "C" fn js_workload_handle_down(handle_id: i64, force: i32) -> *mut Promise { - let promise = js_promise_new(); + let promise = js_promise_new_cross_thread(); let id = handle_id as u64; crate::common::spawn_for_promise(promise as *mut u8, async move { @@ -163,7 +163,7 @@ pub unsafe extern "C" fn js_workload_handle_down(handle_id: i64, force: i32) -> /// FFI: js_workload_handle_status(handle_id: i64) -> *mut Promise #[no_mangle] pub unsafe extern "C" fn js_workload_handle_status(handle_id: i64) -> *mut Promise { - let promise = js_promise_new(); + let promise = js_promise_new_cross_thread(); let id = handle_id as u64; crate::common::spawn_for_promise_deferred( @@ -199,7 +199,7 @@ pub unsafe extern "C" fn js_workload_handle_logs( node_id_ptr: *const StringHeader, tail: i32, ) -> *mut Promise { - let promise = js_promise_new(); + let promise = js_promise_new_cross_thread(); let id = handle_id as u64; let node_id = string_from_header(node_id_ptr).unwrap_or_default(); let tail_opt = if tail >= 0 { Some(tail as u32) } else { None }; @@ -230,7 +230,7 @@ pub unsafe extern "C" fn js_workload_handle_exec( node_id_ptr: *const StringHeader, cmd_json_ptr: *const StringHeader, ) -> *mut Promise { - let promise = js_promise_new(); + let promise = js_promise_new_cross_thread(); let id = handle_id as u64; let node_id = string_from_header(node_id_ptr).unwrap_or_default(); let cmd_json = string_from_header(cmd_json_ptr).unwrap_or_else(|| "[]".to_string()); @@ -258,7 +258,7 @@ pub unsafe extern "C" fn js_workload_handle_exec( /// FFI: js_workload_handle_ps(handle_id: i64) -> *mut Promise #[no_mangle] pub unsafe extern "C" fn js_workload_handle_ps(handle_id: i64) -> *mut Promise { - let promise = js_promise_new(); + let promise = js_promise_new_cross_thread(); let id = handle_id as u64; crate::common::spawn_for_promise(promise as *mut u8, async move { diff --git a/crates/perry-stdlib/src/fetch/mod.rs b/crates/perry-stdlib/src/fetch/mod.rs index d03d4c0a31..e5ffae14b9 100644 --- a/crates/perry-stdlib/src/fetch/mod.rs +++ b/crates/perry-stdlib/src/fetch/mod.rs @@ -380,7 +380,7 @@ fn tagged_bool(value: bool) -> f64 { /// fetch(url) -> Promise #[no_mangle] pub unsafe extern "C" fn js_fetch_get(url_ptr: *const StringHeader) -> *mut perry_runtime::Promise { - let promise = perry_runtime::js_promise_new(); + let promise = perry_runtime::js_promise_new_cross_thread(); let promise_ptr = promise as usize; let url = match string_from_header(url_ptr) { @@ -450,7 +450,7 @@ pub unsafe extern "C" fn js_fetch_get_with_auth( url_ptr: *const StringHeader, auth_header_ptr: *const StringHeader, ) -> *mut perry_runtime::Promise { - let promise = perry_runtime::js_promise_new(); + let promise = perry_runtime::js_promise_new_cross_thread(); let promise_ptr = promise as usize; let url = match string_from_header(url_ptr) { @@ -526,7 +526,7 @@ pub unsafe extern "C" fn js_fetch_post_with_auth( auth_header_ptr: *const StringHeader, body_ptr: *const StringHeader, ) -> *mut perry_runtime::Promise { - let promise = perry_runtime::js_promise_new(); + let promise = perry_runtime::js_promise_new_cross_thread(); let promise_ptr = promise as usize; let url = match string_from_header(url_ptr) { @@ -604,7 +604,7 @@ pub unsafe extern "C" fn js_fetch_post( body_ptr: *const StringHeader, content_type_ptr: *const StringHeader, ) -> *mut perry_runtime::Promise { - let promise = perry_runtime::js_promise_new(); + let promise = perry_runtime::js_promise_new_cross_thread(); let promise_ptr = promise as usize; let url = match string_from_header(url_ptr) { @@ -696,7 +696,7 @@ pub unsafe extern "C" fn js_fetch_with_options( // allocation can't move the still-TLS-stashed signal before we read it. let abort_state = abort_bridge::take_pending_signal_watch(); - let promise = perry_runtime::js_promise_new(); + let promise = perry_runtime::js_promise_new_cross_thread(); let promise_ptr = promise as usize; // An already-aborted signal rejects the request up front; otherwise keep the @@ -824,7 +824,7 @@ fn consume_response_body(handle: f64) -> Result, &'static str> { /// `Expr::Await` for the rationale). #[no_mangle] pub unsafe extern "C" fn js_fetch_response_text(handle: f64) -> *mut perry_runtime::Promise { - let promise = perry_runtime::js_promise_new(); + let promise = perry_runtime::js_promise_new_cross_thread(); let body = match consume_response_body(handle) { Ok(body) => body, Err(err_msg) if err_msg == BODY_ALREADY_USED_MESSAGE => { @@ -893,7 +893,7 @@ unsafe fn json_value_to_jsvalue(value: &serde_json::Value) -> JSValue { /// response.json() -> Promise #[no_mangle] pub unsafe extern "C" fn js_fetch_response_json(handle: f64) -> *mut perry_runtime::Promise { - let promise = perry_runtime::js_promise_new(); + let promise = perry_runtime::js_promise_new_cross_thread(); let body = match consume_response_body(handle) { Ok(body) => body, Err(err_msg) if err_msg == BODY_ALREADY_USED_MESSAGE => { @@ -932,7 +932,7 @@ pub unsafe extern "C" fn js_fetch_response_json(handle: f64) -> *mut perry_runti pub unsafe extern "C" fn js_fetch_text( url_ptr: *const StringHeader, ) -> *mut perry_runtime::Promise { - let promise = perry_runtime::js_promise_new(); + let promise = perry_runtime::js_promise_new_cross_thread(); let promise_ptr = promise as usize; let url = match string_from_header(url_ptr) { @@ -1284,7 +1284,7 @@ fn alloc_headers(store: HeadersStore) -> usize { /// doesn't hang. See `js_fetch_response_text` for rationale. #[no_mangle] pub unsafe extern "C" fn js_response_array_buffer(handle: f64) -> *mut perry_runtime::Promise { - let promise = perry_runtime::js_promise_new(); + let promise = perry_runtime::js_promise_new_cross_thread(); let body = match consume_response_body(handle) { Ok(body) => body, Err(err_msg) if err_msg == BODY_ALREADY_USED_MESSAGE => { @@ -1322,7 +1322,7 @@ pub unsafe extern "C" fn js_response_array_buffer(handle: f64) -> *mut perry_run /// `.slice()` / `.size` / `.type` to the FFIs below. #[no_mangle] pub unsafe extern "C" fn js_response_blob(handle: f64) -> *mut perry_runtime::Promise { - let promise = perry_runtime::js_promise_new(); + let promise = perry_runtime::js_promise_new_cross_thread(); let id = handle_id(handle); let content_type = { let guard = FETCH_RESPONSES.lock().unwrap(); @@ -1391,7 +1391,7 @@ pub unsafe extern "C" fn js_blob_type(handle: f64) -> *mut StringHeader { /// property dispatch in `value.rs`. Resolved synchronously. #[no_mangle] pub unsafe extern "C" fn js_blob_array_buffer(handle: f64) -> *mut perry_runtime::Promise { - let promise = perry_runtime::js_promise_new(); + let promise = perry_runtime::js_promise_new_cross_thread(); let id = handle_id(handle); let body: Vec = BLOB_REGISTRY .lock() @@ -1427,7 +1427,7 @@ pub unsafe extern "C" fn js_blob_bytes(handle: f64) -> *mut perry_runtime::Promi /// characters; lossy_utf8 produces U+FFFD identically). #[no_mangle] pub unsafe extern "C" fn js_blob_text(handle: f64) -> *mut perry_runtime::Promise { - let promise = perry_runtime::js_promise_new(); + let promise = perry_runtime::js_promise_new_cross_thread(); let id = handle_id(handle); let body: Vec = BLOB_REGISTRY .lock() @@ -1850,7 +1850,7 @@ fn consume_request_body(handle: f64) -> Result, &'static str> { /// (#1688) #[no_mangle] pub unsafe extern "C" fn js_request_text(handle: f64) -> *mut perry_runtime::Promise { - let promise = perry_runtime::js_promise_new(); + let promise = perry_runtime::js_promise_new_cross_thread(); match consume_request_body(handle) { Ok(body) => { let text = String::from_utf8_lossy(&body).to_string(); @@ -1873,7 +1873,7 @@ pub unsafe extern "C" fn js_request_text(handle: f64) -> *mut perry_runtime::Pro /// `js_fetch_response_json`. (#1688) #[no_mangle] pub unsafe extern "C" fn js_request_json(handle: f64) -> *mut perry_runtime::Promise { - let promise = perry_runtime::js_promise_new(); + let promise = perry_runtime::js_promise_new_cross_thread(); let body = match consume_request_body(handle) { Ok(b) => b, Err(err_msg) if err_msg == BODY_ALREADY_USED_MESSAGE => { @@ -1904,7 +1904,7 @@ pub unsafe extern "C" fn js_request_json(handle: f64) -> *mut perry_runtime::Pro /// BufferHeader over the body bytes, mirroring `js_response_array_buffer`. (#1688) #[no_mangle] pub unsafe extern "C" fn js_request_array_buffer(handle: f64) -> *mut perry_runtime::Promise { - let promise = perry_runtime::js_promise_new(); + let promise = perry_runtime::js_promise_new_cross_thread(); let body = match consume_request_body(handle) { Ok(b) => b, Err(err_msg) if err_msg == BODY_ALREADY_USED_MESSAGE => { diff --git a/crates/perry-stdlib/src/ioredis.rs b/crates/perry-stdlib/src/ioredis.rs index d094f218a7..514c0c5d48 100644 --- a/crates/perry-stdlib/src/ioredis.rs +++ b/crates/perry-stdlib/src/ioredis.rs @@ -104,7 +104,7 @@ pub unsafe extern "C" fn js_ioredis_set( key_ptr: *const StringHeader, value_ptr: *const StringHeader, ) -> *mut perry_runtime::Promise { - let promise = perry_runtime::js_promise_new(); + let promise = perry_runtime::js_promise_new_cross_thread(); let promise_ptr = promise as usize; let key = match string_from_header(key_ptr) { @@ -176,7 +176,7 @@ pub unsafe extern "C" fn js_ioredis_get( handle: Handle, key_ptr: *const StringHeader, ) -> *mut perry_runtime::Promise { - let promise = perry_runtime::js_promise_new(); + let promise = perry_runtime::js_promise_new_cross_thread(); let promise_ptr = promise as usize; let key = match string_from_header(key_ptr) { @@ -240,7 +240,7 @@ pub unsafe extern "C" fn js_ioredis_del( handle: Handle, key_ptr: *const StringHeader, ) -> *mut perry_runtime::Promise { - let promise = perry_runtime::js_promise_new(); + let promise = perry_runtime::js_promise_new_cross_thread(); let promise_ptr = promise as usize; let key = match string_from_header(key_ptr) { @@ -297,7 +297,7 @@ pub unsafe extern "C" fn js_ioredis_exists( handle: Handle, key_ptr: *const StringHeader, ) -> *mut perry_runtime::Promise { - let promise = perry_runtime::js_promise_new(); + let promise = perry_runtime::js_promise_new_cross_thread(); let promise_ptr = promise as usize; let key = match string_from_header(key_ptr) { @@ -354,7 +354,7 @@ pub unsafe extern "C" fn js_ioredis_incr( handle: Handle, key_ptr: *const StringHeader, ) -> *mut perry_runtime::Promise { - let promise = perry_runtime::js_promise_new(); + let promise = perry_runtime::js_promise_new_cross_thread(); let promise_ptr = promise as usize; let key = match string_from_header(key_ptr) { @@ -411,7 +411,7 @@ pub unsafe extern "C" fn js_ioredis_decr( handle: Handle, key_ptr: *const StringHeader, ) -> *mut perry_runtime::Promise { - let promise = perry_runtime::js_promise_new(); + let promise = perry_runtime::js_promise_new_cross_thread(); let promise_ptr = promise as usize; let key = match string_from_header(key_ptr) { @@ -469,7 +469,7 @@ pub unsafe extern "C" fn js_ioredis_expire( key_ptr: *const StringHeader, seconds: f64, ) -> *mut perry_runtime::Promise { - let promise = perry_runtime::js_promise_new(); + let promise = perry_runtime::js_promise_new_cross_thread(); let promise_ptr = promise as usize; let key = match string_from_header(key_ptr) { @@ -525,7 +525,7 @@ pub unsafe extern "C" fn js_ioredis_expire( /// redis.connect() -> Promise #[no_mangle] pub unsafe extern "C" fn js_ioredis_connect(handle: Handle) -> *mut perry_runtime::Promise { - let promise = perry_runtime::js_promise_new(); + let promise = perry_runtime::js_promise_new_cross_thread(); let promise_ptr = promise as usize; spawn(async move { @@ -554,7 +554,7 @@ pub unsafe extern "C" fn js_ioredis_setex( seconds: f64, value_ptr: *const StringHeader, ) -> *mut perry_runtime::Promise { - let promise = perry_runtime::js_promise_new(); + let promise = perry_runtime::js_promise_new_cross_thread(); let promise_ptr = promise as usize; let key = match string_from_header(key_ptr) { @@ -632,7 +632,7 @@ pub unsafe extern "C" fn js_ioredis_disconnect(handle: Handle) { /// redis.ping() -> Promise<"PONG"> #[no_mangle] pub unsafe extern "C" fn js_ioredis_ping(handle: Handle) -> *mut perry_runtime::Promise { - let promise = perry_runtime::js_promise_new(); + let promise = perry_runtime::js_promise_new_cross_thread(); let promise_ptr = promise as usize; spawn(async move { @@ -683,7 +683,7 @@ pub unsafe extern "C" fn js_ioredis_hget( key_ptr: *const StringHeader, field_ptr: *const StringHeader, ) -> *mut perry_runtime::Promise { - let promise = perry_runtime::js_promise_new(); + let promise = perry_runtime::js_promise_new_cross_thread(); let promise_ptr = promise as usize; let key = match string_from_header(key_ptr) { @@ -755,7 +755,7 @@ pub unsafe extern "C" fn js_ioredis_hset( field_ptr: *const StringHeader, value_ptr: *const StringHeader, ) -> *mut perry_runtime::Promise { - let promise = perry_runtime::js_promise_new(); + let promise = perry_runtime::js_promise_new_cross_thread(); let promise_ptr = promise as usize; let key = match string_from_header(key_ptr) { @@ -826,7 +826,7 @@ pub unsafe extern "C" fn js_ioredis_hgetall( handle: Handle, key_ptr: *const StringHeader, ) -> *mut perry_runtime::Promise { - let promise = perry_runtime::js_promise_new(); + let promise = perry_runtime::js_promise_new_cross_thread(); let promise_ptr = promise as usize; let key = match string_from_header(key_ptr) { @@ -903,7 +903,7 @@ pub unsafe extern "C" fn js_ioredis_hdel( key_ptr: *const StringHeader, field_ptr: *const StringHeader, ) -> *mut perry_runtime::Promise { - let promise = perry_runtime::js_promise_new(); + let promise = perry_runtime::js_promise_new_cross_thread(); let promise_ptr = promise as usize; let key = match string_from_header(key_ptr) { @@ -966,7 +966,7 @@ pub unsafe extern "C" fn js_ioredis_hlen( handle: Handle, key_ptr: *const StringHeader, ) -> *mut perry_runtime::Promise { - let promise = perry_runtime::js_promise_new(); + let promise = perry_runtime::js_promise_new_cross_thread(); let promise_ptr = promise as usize; let key = match string_from_header(key_ptr) { @@ -1018,7 +1018,7 @@ pub unsafe extern "C" fn js_ioredis_hlen( /// redis.quit() -> Promise<"OK"> #[no_mangle] pub unsafe extern "C" fn js_ioredis_quit(handle: Handle) -> *mut perry_runtime::Promise { - let promise = perry_runtime::js_promise_new(); + let promise = perry_runtime::js_promise_new_cross_thread(); let promise_ptr = promise as usize; // Remove connection from cache diff --git a/crates/perry-stdlib/src/mongodb.rs b/crates/perry-stdlib/src/mongodb.rs index 7122602d96..e26cf98ecd 100644 --- a/crates/perry-stdlib/src/mongodb.rs +++ b/crates/perry-stdlib/src/mongodb.rs @@ -11,7 +11,7 @@ use bson::{doc, Document}; use mongodb::{Client, Collection, Database}; use perry_runtime::json::js_json_stringify; use perry_runtime::{ - js_object_alloc, js_object_set_field, js_promise_new, js_string_from_bytes, JSValue, + js_object_alloc, js_object_set_field, js_promise_new_cross_thread, js_string_from_bytes, JSValue, ObjectHeader, Promise, StringHeader, }; @@ -94,7 +94,7 @@ pub unsafe extern "C" fn js_mongodb_client_new(uri_ptr: *const StringHeader) -> pub unsafe extern "C" fn js_mongodb_client_connect(client_handle: Handle) -> *mut Promise { use crate::common::get_handle_mut; - let promise = js_promise_new(); + let promise = js_promise_new_cross_thread(); let pending = if let Some(h) = get_handle_mut::(client_handle) { h.pending_uri.take() @@ -186,7 +186,7 @@ unsafe fn bson_to_jsvalue(doc: &Document) -> *mut ObjectHeader { /// MongoClient.connect(uri) -> Promise #[no_mangle] pub unsafe extern "C" fn js_mongodb_connect(uri_ptr: *const StringHeader) -> *mut Promise { - let promise = js_promise_new(); + let promise = js_promise_new_cross_thread(); let uri = match string_from_header(uri_ptr) { Some(u) => u, @@ -272,7 +272,7 @@ pub unsafe extern "C" fn js_mongodb_collection_find_one( collection_handle: Handle, filter_json_ptr: *const StringHeader, ) -> *mut Promise { - let promise = js_promise_new(); + let promise = js_promise_new_cross_thread(); let filter_json = string_from_header(filter_json_ptr).unwrap_or_else(|| "{}".to_string()); @@ -328,7 +328,7 @@ pub unsafe extern "C" fn js_mongodb_collection_find( collection_handle: Handle, filter_json_ptr: *const StringHeader, ) -> *mut Promise { - let promise = js_promise_new(); + let promise = js_promise_new_cross_thread(); let filter_json = string_from_header(filter_json_ptr).unwrap_or_else(|| "{}".to_string()); @@ -376,7 +376,7 @@ pub unsafe extern "C" fn js_mongodb_collection_insert_one( collection_handle: Handle, doc_json_ptr: *const StringHeader, ) -> *mut Promise { - let promise = js_promise_new(); + let promise = js_promise_new_cross_thread(); let doc_json = match string_from_header(doc_json_ptr) { Some(j) => j, @@ -418,7 +418,7 @@ pub unsafe extern "C" fn js_mongodb_collection_insert_many( collection_handle: Handle, docs_json_ptr: *const StringHeader, ) -> *mut Promise { - let promise = js_promise_new(); + let promise = js_promise_new_cross_thread(); let docs_json = match string_from_header(docs_json_ptr) { Some(j) => j, @@ -457,7 +457,7 @@ pub unsafe extern "C" fn js_mongodb_collection_update_one( filter_json_ptr: *const StringHeader, update_json_ptr: *const StringHeader, ) -> *mut Promise { - let promise = js_promise_new(); + let promise = js_promise_new_cross_thread(); let filter_json = string_from_header(filter_json_ptr).unwrap_or_else(|| "{}".to_string()); let update_json = match string_from_header(update_json_ptr) { @@ -495,7 +495,7 @@ pub unsafe extern "C" fn js_mongodb_collection_update_many( filter_json_ptr: *const StringHeader, update_json_ptr: *const StringHeader, ) -> *mut Promise { - let promise = js_promise_new(); + let promise = js_promise_new_cross_thread(); let filter_json = string_from_header(filter_json_ptr).unwrap_or_else(|| "{}".to_string()); let update_json = match string_from_header(update_json_ptr) { @@ -532,7 +532,7 @@ pub unsafe extern "C" fn js_mongodb_collection_delete_one( collection_handle: Handle, filter_json_ptr: *const StringHeader, ) -> *mut Promise { - let promise = js_promise_new(); + let promise = js_promise_new_cross_thread(); let filter_json = string_from_header(filter_json_ptr).unwrap_or_else(|| "{}".to_string()); @@ -558,7 +558,7 @@ pub unsafe extern "C" fn js_mongodb_collection_delete_many( collection_handle: Handle, filter_json_ptr: *const StringHeader, ) -> *mut Promise { - let promise = js_promise_new(); + let promise = js_promise_new_cross_thread(); let filter_json = string_from_header(filter_json_ptr).unwrap_or_else(|| "{}".to_string()); @@ -584,7 +584,7 @@ pub unsafe extern "C" fn js_mongodb_collection_count( collection_handle: Handle, filter_json_ptr: *const StringHeader, ) -> *mut Promise { - let promise = js_promise_new(); + let promise = js_promise_new_cross_thread(); let filter_json = string_from_header(filter_json_ptr).unwrap_or_else(|| "{}".to_string()); @@ -714,7 +714,7 @@ pub unsafe extern "C" fn js_mongodb_collection_count_value( /// client.close() -> Promise #[no_mangle] pub unsafe extern "C" fn js_mongodb_client_close(_client_handle: Handle) -> *mut Promise { - let promise = js_promise_new(); + let promise = js_promise_new_cross_thread(); spawn_for_promise(promise as *mut u8, async move { // MongoDB client doesn't need explicit close in Rust driver @@ -728,7 +728,7 @@ pub unsafe extern "C" fn js_mongodb_client_close(_client_handle: Handle) -> *mut /// client.listDatabases() -> Promise (JSON array of database names) #[no_mangle] pub unsafe extern "C" fn js_mongodb_client_list_databases(client_handle: Handle) -> *mut Promise { - let promise = js_promise_new(); + let promise = js_promise_new_cross_thread(); spawn_for_promise_deferred( promise as *mut u8, @@ -759,7 +759,7 @@ pub unsafe extern "C" fn js_mongodb_client_list_databases(client_handle: Handle) /// db.listCollections() -> Promise (JSON array of collection names) #[no_mangle] pub unsafe extern "C" fn js_mongodb_db_list_collections(db_handle: Handle) -> *mut Promise { - let promise = js_promise_new(); + let promise = js_promise_new_cross_thread(); spawn_for_promise_deferred( promise as *mut u8, diff --git a/crates/perry-stdlib/src/mysql2/connection.rs b/crates/perry-stdlib/src/mysql2/connection.rs index 6533951be8..7faa0ebc11 100644 --- a/crates/perry-stdlib/src/mysql2/connection.rs +++ b/crates/perry-stdlib/src/mysql2/connection.rs @@ -2,7 +2,7 @@ use std::time::Duration; -use perry_runtime::{js_promise_new, JSValue, Promise}; +use perry_runtime::{js_promise_new_cross_thread, JSValue, Promise}; use sqlx::mysql::MySqlConnection; use sqlx::Connection; @@ -45,7 +45,7 @@ pub unsafe extern "C" fn js_mysql2_create_connection(config_f: f64) -> *mut Prom // Take f64 at the FFI boundary to avoid SysV AMD64 ABI mismatch // (see js_mysql2_create_pool for details). let config = JSValue::from_bits(config_f.to_bits()); - let promise = js_promise_new(); + let promise = js_promise_new_cross_thread(); // Parse the config let mysql_config = parse_mysql_config(config); @@ -84,7 +84,7 @@ pub unsafe extern "C" fn js_mysql2_create_connection(config_f: f64) -> *mut Prom /// Closes the MySQL connection. #[no_mangle] pub unsafe extern "C" fn js_mysql2_connection_end(conn_handle: Handle) -> *mut Promise { - let promise = js_promise_new(); + let promise = js_promise_new_cross_thread(); crate::common::spawn_for_promise(promise as *mut u8, async move { use crate::common::take_handle; @@ -129,7 +129,7 @@ pub unsafe extern "C" fn js_mysql2_connection_query( sql_ptr: *const u8, params: JSValue, ) -> *mut Promise { - let promise = js_promise_new(); + let promise = js_promise_new_cross_thread(); // Extract the SQL string let sql = if sql_ptr.is_null() { @@ -262,7 +262,7 @@ pub unsafe extern "C" fn js_mysql2_connection_execute( sql_ptr: *const u8, params: JSValue, ) -> *mut Promise { - let promise = js_promise_new(); + let promise = js_promise_new_cross_thread(); let sql = if sql_ptr.is_null() { String::new() @@ -415,7 +415,7 @@ pub unsafe extern "C" fn js_mysql2_connection_execute( pub unsafe extern "C" fn js_mysql2_connection_begin_transaction( conn_handle: Handle, ) -> *mut Promise { - let promise = js_promise_new(); + let promise = js_promise_new_cross_thread(); crate::common::spawn_for_promise(promise as *mut u8, async move { use crate::common::get_handle_mut; @@ -451,7 +451,7 @@ pub unsafe extern "C" fn js_mysql2_connection_begin_transaction( /// connection.commit() -> Promise #[no_mangle] pub unsafe extern "C" fn js_mysql2_connection_commit(conn_handle: Handle) -> *mut Promise { - let promise = js_promise_new(); + let promise = js_promise_new_cross_thread(); crate::common::spawn_for_promise(promise as *mut u8, async move { use crate::common::get_handle_mut; @@ -487,7 +487,7 @@ pub unsafe extern "C" fn js_mysql2_connection_commit(conn_handle: Handle) -> *mu /// connection.rollback() -> Promise #[no_mangle] pub unsafe extern "C" fn js_mysql2_connection_rollback(conn_handle: Handle) -> *mut Promise { - let promise = js_promise_new(); + let promise = js_promise_new_cross_thread(); crate::common::spawn_for_promise(promise as *mut u8, async move { use crate::common::get_handle_mut; diff --git a/crates/perry-stdlib/src/mysql2/pool.rs b/crates/perry-stdlib/src/mysql2/pool.rs index 052cc5ddd4..e7b25c9096 100644 --- a/crates/perry-stdlib/src/mysql2/pool.rs +++ b/crates/perry-stdlib/src/mysql2/pool.rs @@ -2,7 +2,7 @@ use std::time::Duration; -use perry_runtime::{js_array_get_jsvalue, js_array_length, js_promise_new, JSValue, Promise}; +use perry_runtime::{js_array_get_jsvalue, js_array_length, js_promise_new_cross_thread, JSValue, Promise}; use sqlx::mysql::{MySqlPool, MySqlPoolOptions}; use sqlx::pool::PoolConnection; use sqlx::MySql; @@ -90,7 +90,7 @@ pub unsafe extern "C" fn js_mysql2_create_pool(config_f: f64) -> Handle { /// Closes all connections in the pool. #[no_mangle] pub unsafe extern "C" fn js_mysql2_pool_end(pool_handle: Handle) -> *mut Promise { - let promise = js_promise_new(); + let promise = js_promise_new_cross_thread(); crate::common::spawn_for_promise(promise as *mut u8, async move { use crate::common::take_handle; @@ -139,7 +139,7 @@ pub unsafe extern "C" fn js_mysql2_pool_query( sql_ptr: *const u8, params: JSValue, ) -> *mut Promise { - let promise = js_promise_new(); + let promise = js_promise_new_cross_thread(); // Extract the SQL string let sql = if sql_ptr.is_null() { @@ -239,7 +239,7 @@ pub unsafe extern "C" fn js_mysql2_pool_execute( sql_ptr: *const u8, params: JSValue, ) -> *mut Promise { - let promise = js_promise_new(); + let promise = js_promise_new_cross_thread(); // Extract the SQL string let sql = if sql_ptr.is_null() { @@ -432,7 +432,7 @@ pub(crate) unsafe fn extract_params_from_jsvalue(params: JSValue) -> Vec *mut Promise { - let promise = js_promise_new(); + let promise = js_promise_new_cross_thread(); crate::common::spawn_for_promise(promise as *mut u8, async move { use crate::common::get_handle; @@ -495,7 +495,7 @@ pub unsafe extern "C" fn js_mysql2_pool_connection_query( sql_ptr: *const u8, params: JSValue, ) -> *mut Promise { - let promise = js_promise_new(); + let promise = js_promise_new_cross_thread(); // Extract the SQL string let sql = if sql_ptr.is_null() { @@ -589,7 +589,7 @@ pub unsafe extern "C" fn js_mysql2_pool_connection_execute( sql_ptr: *const u8, params: JSValue, ) -> *mut Promise { - let promise = js_promise_new(); + let promise = js_promise_new_cross_thread(); // Extract the SQL string let sql = if sql_ptr.is_null() { diff --git a/crates/perry-stdlib/src/net/mod.rs b/crates/perry-stdlib/src/net/mod.rs index b2d832e096..501032b8f4 100644 --- a/crates/perry-stdlib/src/net/mod.rs +++ b/crates/perry-stdlib/src/net/mod.rs @@ -1695,7 +1695,7 @@ pub unsafe extern "C" fn js_net_socket_upgrade_tls( servername_ptr: i64, verify: f64, ) -> *mut perry_runtime::Promise { - let promise = perry_runtime::js_promise_new(); + let promise = perry_runtime::js_promise_new_cross_thread(); let promise_ptr = promise as *mut u8; let servername = match string_from_header_i64(servername_ptr) { diff --git a/crates/perry-stdlib/src/nodemailer.rs b/crates/perry-stdlib/src/nodemailer.rs index 536d3e4002..3e04cf804b 100644 --- a/crates/perry-stdlib/src/nodemailer.rs +++ b/crates/perry-stdlib/src/nodemailer.rs @@ -7,7 +7,7 @@ use lettre::message::header::ContentType; use lettre::transport::smtp::authentication::Credentials; use lettre::{AsyncSmtpTransport, AsyncTransport, Message, Tokio1Executor}; use perry_runtime::{ - js_promise_new, js_string_from_bytes, JSValue, ObjectHeader, Promise, StringHeader, + js_promise_new_cross_thread, js_string_from_bytes, JSValue, ObjectHeader, Promise, StringHeader, }; use crate::common::{register_handle, Handle}; @@ -193,7 +193,7 @@ pub unsafe extern "C" fn js_nodemailer_send_mail( transporter_handle: Handle, options: JSValue, ) -> *mut Promise { - let promise = js_promise_new(); + let promise = js_promise_new_cross_thread(); // Parse mail options let mail_opts = match parse_mail_options(options) { @@ -298,7 +298,7 @@ pub unsafe extern "C" fn js_nodemailer_send_mail( /// Verifies that the transporter can connect to the SMTP server. #[no_mangle] pub unsafe extern "C" fn js_nodemailer_verify(transporter_handle: Handle) -> *mut Promise { - let promise = js_promise_new(); + let promise = js_promise_new_cross_thread(); crate::common::spawn_for_promise(promise as *mut u8, async move { use crate::common::get_handle; diff --git a/crates/perry-stdlib/src/pg/connection.rs b/crates/perry-stdlib/src/pg/connection.rs index a2cffafff6..95e01194b6 100644 --- a/crates/perry-stdlib/src/pg/connection.rs +++ b/crates/perry-stdlib/src/pg/connection.rs @@ -1,6 +1,6 @@ //! PostgreSQL connection implementation -use perry_runtime::{js_array_get_jsvalue, js_array_length, js_promise_new, JSValue, Promise}; +use perry_runtime::{js_array_get_jsvalue, js_array_length, js_promise_new_cross_thread, JSValue, Promise}; use sqlx::postgres::PgConnection; use sqlx::{Connection, Row}; @@ -75,7 +75,7 @@ pub unsafe extern "C" fn js_pg_client_new(config_f: f64) -> Handle { pub unsafe extern "C" fn js_pg_client_connect(client_handle: Handle) -> *mut Promise { use crate::common::get_handle_mut; - let promise = js_promise_new(); + let promise = js_promise_new_cross_thread(); // Snapshot the pending config out of the handle BEFORE entering the // async block — `get_handle_mut` returns a `&mut` that we can't keep @@ -122,7 +122,7 @@ pub unsafe extern "C" fn js_pg_connect(config_f: f64) -> *mut Promise { // Take f64 at the FFI boundary to avoid SysV AMD64 ABI mismatch // (see js_mysql2_create_pool for details). let config = JSValue::from_bits(config_f.to_bits()); - let promise = js_promise_new(); + let promise = js_promise_new_cross_thread(); // Parse the config let pg_config = parse_pg_config(config); @@ -148,7 +148,7 @@ pub unsafe extern "C" fn js_pg_connect(config_f: f64) -> *mut Promise { /// Closes the PostgreSQL connection. #[no_mangle] pub unsafe extern "C" fn js_pg_client_end(client_handle: Handle) -> *mut Promise { - let promise = js_promise_new(); + let promise = js_promise_new_cross_thread(); crate::common::spawn_for_promise(promise as *mut u8, async move { use crate::common::take_handle; @@ -178,7 +178,7 @@ pub unsafe extern "C" fn js_pg_client_query( client_handle: Handle, sql_ptr: *const u8, ) -> *mut Promise { - let promise = js_promise_new(); + let promise = js_promise_new_cross_thread(); // Extract the SQL string let sql = if sql_ptr.is_null() { @@ -344,7 +344,7 @@ pub unsafe extern "C" fn js_pg_client_query_params( sql_ptr: *const u8, params: JSValue, ) -> *mut Promise { - let promise = js_promise_new(); + let promise = js_promise_new_cross_thread(); let sql = if sql_ptr.is_null() { String::new() diff --git a/crates/perry-stdlib/src/pg/pool.rs b/crates/perry-stdlib/src/pg/pool.rs index cbb50dcfac..080bf413f6 100644 --- a/crates/perry-stdlib/src/pg/pool.rs +++ b/crates/perry-stdlib/src/pg/pool.rs @@ -1,6 +1,6 @@ //! PostgreSQL connection pool implementation -use perry_runtime::{js_promise_new, JSValue, Promise}; +use perry_runtime::{js_promise_new_cross_thread, JSValue, Promise}; use sqlx::postgres::{PgPool, PgPoolOptions}; use sqlx::Row; @@ -87,7 +87,7 @@ pub unsafe extern "C" fn js_pg_create_pool(config_f: f64) -> *mut Promise { // Take f64 at the FFI boundary to avoid SysV AMD64 ABI mismatch // (see js_mysql2_create_pool for details). let config = JSValue::from_bits(config_f.to_bits()); - let promise = js_promise_new(); + let promise = js_promise_new_cross_thread(); // Parse the config let pg_config = parse_pg_config(config); @@ -119,7 +119,7 @@ pub unsafe extern "C" fn js_pg_create_pool(config_f: f64) -> *mut Promise { /// Executes a query on the pool. #[no_mangle] pub unsafe extern "C" fn js_pg_pool_query(pool_handle: Handle, sql_ptr: *const u8) -> *mut Promise { - let promise = js_promise_new(); + let promise = js_promise_new_cross_thread(); // Extract the SQL string let sql = if sql_ptr.is_null() { @@ -176,7 +176,7 @@ pub unsafe extern "C" fn js_pg_pool_query(pool_handle: Handle, sql_ptr: *const u /// Closes all connections in the pool. #[no_mangle] pub unsafe extern "C" fn js_pg_pool_end(pool_handle: Handle) -> *mut Promise { - let promise = js_promise_new(); + let promise = js_promise_new_cross_thread(); crate::common::spawn_for_promise(promise as *mut u8, async move { use crate::common::take_handle; diff --git a/crates/perry-stdlib/src/sharp.rs b/crates/perry-stdlib/src/sharp.rs index 5883cdeff1..ad045f8ed0 100644 --- a/crates/perry-stdlib/src/sharp.rs +++ b/crates/perry-stdlib/src/sharp.rs @@ -8,7 +8,7 @@ use crate::common::{ string_from_header_lossy as string_from_header, Handle, }; use image::{imageops::FilterType, DynamicImage, GenericImageView, ImageFormat}; -use perry_runtime::{js_promise_new, js_string_from_bytes, JSValue, Promise, StringHeader}; +use perry_runtime::{js_promise_new_cross_thread, js_string_from_bytes, JSValue, Promise, StringHeader}; use std::io::Cursor; /// Sharp image handle with pending operations @@ -269,7 +269,7 @@ pub unsafe extern "C" fn js_sharp_to_file( handle: Handle, path_ptr: *const StringHeader, ) -> *mut Promise { - let promise = js_promise_new(); + let promise = js_promise_new_cross_thread(); let path = match string_from_header(path_ptr) { Some(p) => p, @@ -317,7 +317,7 @@ pub unsafe extern "C" fn js_sharp_to_file( /// Get the image as a buffer. #[no_mangle] pub unsafe extern "C" fn js_sharp_to_buffer(handle: Handle) -> *mut Promise { - let promise = js_promise_new(); + let promise = js_promise_new_cross_thread(); spawn_for_promise(promise as *mut u8, async move { if let Some(sharp) = get_handle::(handle) { @@ -346,7 +346,7 @@ pub unsafe extern "C" fn js_sharp_to_buffer(handle: Handle) -> *mut Promise { /// Get image metadata. #[no_mangle] pub unsafe extern "C" fn js_sharp_metadata(handle: Handle) -> *mut Promise { - let promise = js_promise_new(); + let promise = js_promise_new_cross_thread(); spawn_for_promise(promise as *mut u8, async move { if let Some(sharp) = get_handle::(handle) { diff --git a/crates/perry-stdlib/src/worker_threads/async_shim.rs b/crates/perry-stdlib/src/worker_threads/async_shim.rs index 76475252ff..dca5fc9926 100644 --- a/crates/perry-stdlib/src/worker_threads/async_shim.rs +++ b/crates/perry-stdlib/src/worker_threads/async_shim.rs @@ -28,7 +28,7 @@ //! The pinning `js_promise_new_for_native_resolution` performs is likewise a //! consequence of deferral — it keeps the promise alive across the window //! between creation and the pump's resolution — and an inline settle spans no -//! collection point, so a plain `js_promise_new` is the correct counterpart. +//! collection point, so a plain `js_promise_new_cross_thread` is the correct counterpart. #[cfg(feature = "async-runtime")] pub(crate) use crate::common::async_bridge::{ @@ -46,7 +46,7 @@ mod inline { /// /// No pinning: pinning guards the deferral window, and there is none here. pub(crate) unsafe fn js_promise_new_for_native_resolution() -> *mut perry_runtime::Promise { - perry_runtime::js_promise_new() + perry_runtime::js_promise_new_cross_thread() } /// Settle now rather than queueing for a pump that does not exist. diff --git a/crates/perry-stdlib/src/ws.rs b/crates/perry-stdlib/src/ws.rs index 5d4ba3699d..9a26c793f2 100644 --- a/crates/perry-stdlib/src/ws.rs +++ b/crates/perry-stdlib/src/ws.rs @@ -241,7 +241,7 @@ pub unsafe extern "C" fn js_ws_connect( } __android_log_print(3, b"PerryWS\0".as_ptr(), b"js_ws_connect called\0".as_ptr()); } - let promise = perry_runtime::js_promise_new(); + let promise = perry_runtime::js_promise_new_cross_thread(); let promise_ptr = promise as usize; let url = match string_from_header(url_ptr) { @@ -657,7 +657,7 @@ pub unsafe extern "C" fn js_ws_connect_start(url_nanboxed: f64) -> f64 { pub unsafe extern "C" fn js_ws_connect( url_ptr: *const StringHeader, ) -> *mut perry_runtime::Promise { - let promise = perry_runtime::js_promise_new(); + let promise = perry_runtime::js_promise_new_cross_thread(); let handle = perry_native_ws_connect(url_ptr as *const u8); let result_bits = handle.to_bits(); // Resolve immediately with the handle (connection happens async in native) @@ -858,7 +858,7 @@ pub unsafe extern "C" fn js_ws_wait_for_message( handle: i64, timeout_ms: f64, ) -> *mut perry_runtime::Promise { - let promise = perry_runtime::js_promise_new(); + let promise = perry_runtime::js_promise_new_cross_thread(); let promise_ptr = promise as usize; let ws_id = handle as usize; let timeout = std::time::Duration::from_millis(timeout_ms as u64); From 6d533f1bd95b4666e5bb6a63fbd129d905747024 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ralph=20K=C3=BCpper?= Date: Tue, 25 Aug 2026 08:58:45 +0200 Subject: [PATCH 4/5] runtime: root async-from-sync iterator objects across GC safepoints The %AsyncFromSyncIteratorPrototype% helpers created nursery objects (the wrapper, the outer promise, reaction closures, the iter result, the captured sync iterator) and held them as raw pointers across later allocations and JS calls. Under the default-on moving young-gen scavenge those raw pointers are invalidated (evacuated, or swept when unreachable), so a later use dereferenced a stale/poison receiver. Root every live young value in a RuntimeHandleScope and re-read it through the handle after each allocation/JS call; use the long-lived string allocator for the immortal property-name keys. Covers wrap_iterator, install_next/method, next/return/throw, call/call_raw, continue, fulfilled/rejected_value, and iter_result. Claude-Session: https://claude.ai/code/session_01TwxRkALrR9HKSF1zKLSTAF --- crates/perry-runtime/src/array/iterator.rs | 315 ++++++++++++++++----- 1 file changed, 251 insertions(+), 64 deletions(-) diff --git a/crates/perry-runtime/src/array/iterator.rs b/crates/perry-runtime/src/array/iterator.rs index 296e9d883c..0900990d92 100644 --- a/crates/perry-runtime/src/array/iterator.rs +++ b/crates/perry-runtime/src/array/iterator.rs @@ -268,12 +268,26 @@ fn undefined_value() -> f64 { } fn async_from_sync_iter_result(value: f64, done: bool) -> f64 { + // The result value and the freshly-allocated object are live young objects + // held across sibling allocations (the object alloc, and each + // `set_field_by_name`, which can grow the shape). The default-on moving + // scavenge evacuates young survivors, so cache them in a handle scope and + // re-read through the (GC-updated) handles instead of stale raw copies. + // Property-name keys use the long-lived allocator so they never move and + // need no rooting. + let scope = crate::gc::RuntimeHandleScope::new(); + let value_h = scope.root_nanbox_f64(value); let obj = crate::object::js_object_alloc(0, 2); - let value_key = crate::string::js_string_from_bytes(b"value".as_ptr(), 5); - let done_key = crate::string::js_string_from_bytes(b"done".as_ptr(), 4); - crate::object::js_object_set_field_by_name(obj, value_key, value); + let obj_h = scope.root_raw_mut_ptr(obj); + let value_key = crate::string::js_string_from_bytes_longlived(b"value".as_ptr(), 5); + let done_key = crate::string::js_string_from_bytes_longlived(b"done".as_ptr(), 4); + crate::object::js_object_set_field_by_name( + obj_h.get_raw_mut_ptr::(), + value_key, + value_h.get_nanbox_f64(), + ); crate::object::js_object_set_field_by_name( - obj, + obj_h.get_raw_mut_ptr::(), done_key, if done { f64::from_bits(crate::value::TAG_TRUE) @@ -281,18 +295,26 @@ fn async_from_sync_iter_result(value: f64, done: bool) -> f64 { f64::from_bits(crate::value::TAG_FALSE) }, ); - crate::value::js_nanbox_pointer(obj as i64) + crate::value::js_nanbox_pointer(obj_h.get_raw_mut_ptr::() as i64) } extern "C" fn async_from_sync_fulfilled( closure: *const crate::closure::ClosureHeader, value: f64, ) -> f64 { + // `async_from_sync_iter_result` allocates and can move the nursery `outer` + // promise stored in capture slot 0 — and the closure itself. Root the + // closure, build the result FIRST, then re-read the (GC-updated) capture so + // we resolve through the live promise pointer, not a pre-move copy. + let scope = crate::gc::RuntimeHandleScope::new(); + let closure_h = scope.root_raw_const_ptr(closure); + let done = crate::closure::js_closure_get_capture_f64(closure, 1) != 0.0; + let result = async_from_sync_iter_result(value, done); + let closure = closure_h.get_raw_const_ptr::(); let promise = crate::closure::js_closure_get_capture_ptr(closure, 0) as *mut crate::promise::Promise; - let done = crate::closure::js_closure_get_capture_f64(closure, 1) != 0.0; if !promise.is_null() { - crate::promise::js_promise_resolve(promise, async_from_sync_iter_result(value, done)); + crate::promise::js_promise_resolve(promise, result); } 0.0 } @@ -301,15 +323,24 @@ extern "C" fn async_from_sync_rejected_value( closure: *const crate::closure::ClosureHeader, reason: f64, ) -> f64 { - let promise = - crate::closure::js_closure_get_capture_ptr(closure, 0) as *mut crate::promise::Promise; + // `async_from_sync_close` calls back into JS and allocates, which can move + // the nursery `outer` promise in capture slot 0 and the closure itself. + // Root the closure + reason and re-read the capture after the close so the + // rejection targets the live promise pointer. + let scope = crate::gc::RuntimeHandleScope::new(); + let closure_h = scope.root_raw_const_ptr(closure); + let reason_h = scope.root_nanbox_f64(reason); let iter = crate::closure::js_closure_get_capture_f64(closure, 1); + let iter_h = scope.root_nanbox_f64(iter); let close_on_rejection = crate::closure::js_closure_get_capture_f64(closure, 2) != 0.0; if close_on_rejection { - async_from_sync_close(iter); + async_from_sync_close(iter_h.get_nanbox_f64()); } + let closure = closure_h.get_raw_const_ptr::(); + let promise = + crate::closure::js_closure_get_capture_ptr(closure, 0) as *mut crate::promise::Promise; if !promise.is_null() { - crate::promise::js_promise_reject(promise, reason); + crate::promise::js_promise_reject(promise, reason_h.get_nanbox_f64()); } 0.0 } @@ -321,45 +352,78 @@ fn async_from_sync_continue(iter: f64, step_result: f64, close_on_rejection: boo return async_from_sync_rejected(b"Iterator result is not an object"); } - let result_obj = ptr as *const crate::object::ObjectHeader; - let done_key = crate::string::js_string_from_bytes(b"done".as_ptr(), 4); - let value_key = crate::string::js_string_from_bytes(b"value".as_ptr(), 5); + // Everything below allocates repeatedly (closure allocs, the resolved + // value promise, `js_promise_then`), and the default-on moving scavenge + // evacuates young survivors on any of those safepoints. Cache every live + // young value (the iterator, the step-result object, the extracted value, + // the freshly-built `outer` promise and its two reaction closures) in a + // handle scope and re-read each through its handle right before use, so no + // raw pre-move pointer survives across an allocation. Property-name keys use + // the long-lived allocator so they never move. + let scope = crate::gc::RuntimeHandleScope::new(); + let iter_h = scope.root_nanbox_f64(iter); + let step_h = scope.root_nanbox_f64(step_result); + let done_key = crate::string::js_string_from_bytes_longlived(b"done".as_ptr(), 4); + let value_key = crate::string::js_string_from_bytes_longlived(b"value".as_ptr(), 5); let done = { + let result_obj = + crate::value::js_nanbox_get_pointer(step_h.get_nanbox_f64()) as *const crate::object::ObjectHeader; let done_val = crate::object::js_object_get_field_by_name(result_obj, done_key); let done_f64 = f64::from_bits(done_val.bits()); crate::value::js_is_truthy(done_f64) != 0 }; let value = { + let result_obj = + crate::value::js_nanbox_get_pointer(step_h.get_nanbox_f64()) as *const crate::object::ObjectHeader; let value_val = crate::object::js_object_get_field_by_name(result_obj, value_key); f64::from_bits(value_val.bits()) }; + let value_h = scope.root_nanbox_f64(value); let outer = crate::promise::js_promise_new(); + let outer_h = scope.root_raw_mut_ptr(outer); let on_fulfilled = crate::closure::js_closure_alloc(async_from_sync_fulfilled as *const u8, 2); + let on_fulfilled_h = scope.root_raw_mut_ptr(on_fulfilled); let on_rejected = crate::closure::js_closure_alloc(async_from_sync_rejected_value as *const u8, 3); - crate::closure::js_closure_set_capture_ptr(on_fulfilled, 0, outer as i64); - crate::closure::js_closure_set_capture_f64(on_fulfilled, 1, if done { 1.0 } else { 0.0 }); - crate::closure::js_closure_set_capture_ptr(on_rejected, 0, outer as i64); - crate::closure::js_closure_set_capture_f64(on_rejected, 1, iter); - crate::closure::js_closure_set_capture_f64( - on_rejected, - 2, - if close_on_rejection { 1.0 } else { 0.0 }, - ); + let on_rejected_h = scope.root_raw_mut_ptr(on_rejected); + // All three allocations are done; re-read each through its handle before + // wiring captures (no allocation happens between these stores). + { + let outer = outer_h.get_raw_mut_ptr::(); + let on_fulfilled = on_fulfilled_h.get_raw_mut_ptr::(); + let on_rejected = on_rejected_h.get_raw_mut_ptr::(); + crate::closure::js_closure_set_capture_ptr(on_fulfilled, 0, outer as i64); + crate::closure::js_closure_set_capture_f64(on_fulfilled, 1, if done { 1.0 } else { 0.0 }); + crate::closure::js_closure_set_capture_ptr(on_rejected, 0, outer as i64); + crate::closure::js_closure_set_capture_f64(on_rejected, 1, iter_h.get_nanbox_f64()); + crate::closure::js_closure_set_capture_f64( + on_rejected, + 2, + if close_on_rejection { 1.0 } else { 0.0 }, + ); + } - let value_promise = match crate::promise::js_promise_resolved_catching(value) { + let value_promise = match crate::promise::js_promise_resolved_catching(value_h.get_nanbox_f64()) + { Ok(promise) => promise, Err(reason) => { + let reason_h = scope.root_nanbox_f64(reason); if close_on_rejection { - async_from_sync_close(iter); + async_from_sync_close(iter_h.get_nanbox_f64()); } - crate::promise::js_promise_reject(outer, reason); - return boxed_promise_value(outer); + let outer = outer_h.get_raw_mut_ptr::(); + crate::promise::js_promise_reject(outer, reason_h.get_nanbox_f64()); + return boxed_promise_value(outer_h.get_raw_mut_ptr::()); } }; - crate::promise::js_promise_then(value_promise, on_fulfilled, on_rejected); - boxed_promise_value(outer) + let value_promise_h = scope.root_raw_mut_ptr(value_promise); + crate::promise::js_promise_then( + value_promise_h.get_raw_mut_ptr::(), + on_fulfilled_h.get_raw_mut_ptr::(), + on_rejected_h.get_raw_mut_ptr::(), + ); + boxed_promise_value(outer_h.get_raw_mut_ptr::()) } fn async_from_sync_rest_args(rest: f64) -> (usize, f64) { @@ -377,7 +441,18 @@ fn async_from_sync_rest_args(rest: f64) -> (usize, f64) { } fn async_from_sync_call_raw(iter: f64, method: &[u8], args: &[f64]) -> Result, f64> { - let method_value = named_field(iter, method); + // `named_field` allocates the method-name key and the invoked method runs + // arbitrary JS — both are moving-scavenge safepoints that evacuate the young + // sync iterator. Root `iter` and the fetched method value and re-read them + // through handles so no stale copy is dereferenced (e.g. `named_field` / + // `js_native_call_method` doing a property access on a moved `iter`, which + // is where `shape_is_url_search_params` faulted on a poison receiver). + let scope = crate::gc::RuntimeHandleScope::new(); + let iter_h = scope.root_nanbox_f64(iter); + let method_value = named_field(iter_h.get_nanbox_f64(), method); + let method_value_h = scope.root_nanbox_f64(method_value); + let iter = iter_h.get_nanbox_f64(); + let method_value = method_value_h.get_nanbox_f64(); // Spec `%AsyncFromSyncIteratorPrototype%.{return,throw}` (and the sync // `yield *` close) do `GetMethod(syncIterator, name)` ONCE and then // `Call(method, syncIterator, args)` on that captured value. Re-dispatching @@ -420,11 +495,17 @@ fn async_from_sync_call_raw(iter: f64, method: &[u8], args: &[f64]) -> Result f64 { - match async_from_sync_call_raw(iter, method, args) { - Ok(Some(step)) => async_from_sync_continue(iter, step, close_on_rejection), + // `async_from_sync_call_raw` runs JS (a moving-scavenge safepoint); re-read + // `iter` from a handle before handing it to `async_from_sync_continue`. + let scope = crate::gc::RuntimeHandleScope::new(); + let iter_h = scope.root_nanbox_f64(iter); + match async_from_sync_call_raw(iter_h.get_nanbox_f64(), method, args) { + Ok(Some(step)) => { + async_from_sync_continue(iter_h.get_nanbox_f64(), step, close_on_rejection) + } Ok(None) => async_from_sync_rejected(b"Async-from-sync iterator method is not callable"), Err(reason) => boxed_promise_value(crate::promise::js_promise_rejected(reason)), } @@ -500,8 +587,16 @@ extern "C" fn async_from_sync_next( closure: *const crate::closure::ClosureHeader, rest: f64, ) -> f64 { + // Root the captured sync iterator + its `[[NextMethod]]` across the sync + // `next()` call (which runs JS and triggers the moving scavenge). Passing + // the pre-move raw `iter` on to `async_from_sync_continue` / + // `async_from_sync_call` was the layer-2 bug: a later `named_field(iter,…)` + // property access dereferenced a poison (freed/moved) receiver. + let scope = crate::gc::RuntimeHandleScope::new(); let iter = crate::closure::js_closure_get_capture_f64(closure, 0); + let iter_h = scope.root_nanbox_f64(iter); let cached_next = crate::closure::js_closure_get_capture_f64(closure, 1); + let cached_next_h = scope.root_nanbox_f64(cached_next); let (argc, first) = async_from_sync_rest_args(rest); let single = [first]; let args: &[f64] = if argc == 0 { &[] } else { &single }; @@ -509,28 +604,39 @@ extern "C" fn async_from_sync_next( // observable-getter case). Builtin iterators (array/map/set/string) expose // no readable own `next` and dispatch through the class-id method tower, so // fall back to the by-name call for them. - if is_callable_value(cached_next) { - return match async_from_sync_call_cached_raw(iter, cached_next, args) { - Ok(Some(step)) => async_from_sync_continue(iter, step, true), - Ok(None) => async_from_sync_call(iter, b"next", args, true), + if is_callable_value(cached_next_h.get_nanbox_f64()) { + return match async_from_sync_call_cached_raw( + iter_h.get_nanbox_f64(), + cached_next_h.get_nanbox_f64(), + args, + ) { + Ok(Some(step)) => async_from_sync_continue(iter_h.get_nanbox_f64(), step, true), + Ok(None) => async_from_sync_call(iter_h.get_nanbox_f64(), b"next", args, true), Err(reason) => boxed_promise_value(crate::promise::js_promise_rejected(reason)), }; } - async_from_sync_call(iter, b"next", args, true) + async_from_sync_call(iter_h.get_nanbox_f64(), b"next", args, true) } extern "C" fn async_from_sync_return( closure: *const crate::closure::ClosureHeader, rest: f64, ) -> f64 { + let scope = crate::gc::RuntimeHandleScope::new(); let iter = crate::closure::js_closure_get_capture_f64(closure, 0); + let iter_h = scope.root_nanbox_f64(iter); let (argc, first) = async_from_sync_rest_args(rest); + let first_h = scope.root_nanbox_f64(first); let single = [first]; let args: &[f64] = if argc == 0 { &[] } else { &single }; - match async_from_sync_call_raw(iter, b"return", args) { - Ok(Some(step)) => async_from_sync_continue(iter, step, false), + match async_from_sync_call_raw(iter_h.get_nanbox_f64(), b"return", args) { + Ok(Some(step)) => async_from_sync_continue(iter_h.get_nanbox_f64(), step, false), Ok(None) => { - let value = if argc == 0 { undefined_value() } else { first }; + let value = if argc == 0 { + undefined_value() + } else { + first_h.get_nanbox_f64() + }; let done = async_from_sync_iter_result(value, true); boxed_promise_value(crate::promise::js_promise_resolved(done)) } @@ -542,14 +648,16 @@ extern "C" fn async_from_sync_throw( closure: *const crate::closure::ClosureHeader, rest: f64, ) -> f64 { + let scope = crate::gc::RuntimeHandleScope::new(); let iter = crate::closure::js_closure_get_capture_f64(closure, 0); + let iter_h = scope.root_nanbox_f64(iter); let (argc, first) = async_from_sync_rest_args(rest); let single = [first]; let args: &[f64] = if argc == 0 { &[] } else { &single }; - match async_from_sync_call_raw(iter, b"throw", args) { - Ok(Some(step)) => async_from_sync_continue(iter, step, true), + match async_from_sync_call_raw(iter_h.get_nanbox_f64(), b"throw", args) { + Ok(Some(step)) => async_from_sync_continue(iter_h.get_nanbox_f64(), step, true), Ok(None) => { - async_from_sync_close(iter); + async_from_sync_close(iter_h.get_nanbox_f64()); async_from_sync_rejected(b"The iterator does not provide a 'throw' method.") } Err(reason) => boxed_promise_value(crate::promise::js_promise_rejected(reason)), @@ -582,12 +690,33 @@ fn install_async_from_sync_method( func: extern "C" fn(*const crate::closure::ClosureHeader, f64) -> f64, iter: f64, ) -> f64 { + // `obj`, `iter` and the freshly-allocated closure are live young objects + // held across sibling allocations (the key string and the shape-growing + // `set_field`). Root them so the moving scavenge cannot leave a stale + // wrapper/closure behind. The key uses the long-lived allocator (immortal, + // never moves). + let scope = crate::gc::RuntimeHandleScope::new(); + let obj_h = scope.root_raw_mut_ptr(obj); + let iter_h = scope.root_nanbox_f64(iter); let closure = crate::closure::js_closure_alloc(func as *const u8, 1); - crate::closure::js_closure_set_capture_f64(closure, 0, iter); - let value = crate::value::js_nanbox_pointer(closure as i64); - let key = crate::string::js_string_from_bytes(name.as_ptr(), name.len() as u32); - crate::object::js_object_set_field_by_name(obj, key, value); - value + let closure_h = scope.root_raw_mut_ptr(closure); + let key = crate::string::js_string_from_bytes_longlived(name.as_ptr(), name.len() as u32); + crate::closure::js_closure_set_capture_f64( + closure_h.get_raw_mut_ptr::(), + 0, + iter_h.get_nanbox_f64(), + ); + let value = crate::value::js_nanbox_pointer( + closure_h.get_raw_mut_ptr::() as i64, + ); + crate::object::js_object_set_field_by_name( + obj_h.get_raw_mut_ptr::(), + key, + value, + ); + crate::value::js_nanbox_pointer( + closure_h.get_raw_mut_ptr::() as i64, + ) } /// Install the wrapper's `next` method with TWO captures: the sync iterator @@ -598,39 +727,97 @@ fn install_async_from_sync_next( iter: f64, cached_next: f64, ) -> f64 { + // Same rooting discipline as `install_async_from_sync_method`: obj, iter, + // the cached next-method and the fresh closure are young values held across + // the key allocation and the shape-growing `set_field`. + let scope = crate::gc::RuntimeHandleScope::new(); + let obj_h = scope.root_raw_mut_ptr(obj); + let iter_h = scope.root_nanbox_f64(iter); + let cached_next_h = scope.root_nanbox_f64(cached_next); let closure = crate::closure::js_closure_alloc(async_from_sync_next as *const u8, 2); - crate::closure::js_closure_set_capture_f64(closure, 0, iter); - crate::closure::js_closure_set_capture_f64(closure, 1, cached_next); - let value = crate::value::js_nanbox_pointer(closure as i64); - let key = crate::string::js_string_from_bytes(b"next".as_ptr(), 4); - crate::object::js_object_set_field_by_name(obj, key, value); - value + let closure_h = scope.root_raw_mut_ptr(closure); + let key = crate::string::js_string_from_bytes_longlived(b"next".as_ptr(), 4); + crate::closure::js_closure_set_capture_f64( + closure_h.get_raw_mut_ptr::(), + 0, + iter_h.get_nanbox_f64(), + ); + crate::closure::js_closure_set_capture_f64( + closure_h.get_raw_mut_ptr::(), + 1, + cached_next_h.get_nanbox_f64(), + ); + let value = crate::value::js_nanbox_pointer( + closure_h.get_raw_mut_ptr::() as i64, + ); + crate::object::js_object_set_field_by_name( + obj_h.get_raw_mut_ptr::(), + key, + value, + ); + crate::value::js_nanbox_pointer( + closure_h.get_raw_mut_ptr::() as i64, + ) } pub(crate) fn async_from_sync_wrap_iterator(iter: f64) -> f64 { register_async_from_sync_thunks_once(); + // The wrapper object and the sync iterator are live young values held + // across a long series of allocations (three method installs plus the + // async-iterator closure and the symbol-property store). Root them and + // re-read through their handles before each use so the moving scavenge + // cannot leave a stale wrapper/iter behind — otherwise every later `next()` + // reads a stale captured iterator. + let scope = crate::gc::RuntimeHandleScope::new(); + let iter_h = scope.root_nanbox_f64(iter); let obj = crate::object::js_object_alloc(0, 0); - let wrapper = crate::value::js_nanbox_pointer(obj as i64); + let obj_h = scope.root_raw_mut_ptr(obj); // Spec (CreateAsyncFromSyncIterator): the sync iterator record's // `[[NextMethod]]` is read once, here, and reused for every `next()` step. - let cached_next = named_field(iter, b"next"); - install_async_from_sync_next(obj, iter, cached_next); - install_async_from_sync_method(obj, b"return", async_from_sync_return, iter); - install_async_from_sync_method(obj, b"throw", async_from_sync_throw, iter); + let cached_next = named_field(iter_h.get_nanbox_f64(), b"next"); + let cached_next_h = scope.root_nanbox_f64(cached_next); + install_async_from_sync_next( + obj_h.get_raw_mut_ptr::(), + iter_h.get_nanbox_f64(), + cached_next_h.get_nanbox_f64(), + ); + install_async_from_sync_method( + obj_h.get_raw_mut_ptr::(), + b"return", + async_from_sync_return, + iter_h.get_nanbox_f64(), + ); + install_async_from_sync_method( + obj_h.get_raw_mut_ptr::(), + b"throw", + async_from_sync_throw, + iter_h.get_nanbox_f64(), + ); let async_iter = crate::closure::js_closure_alloc(async_from_sync_async_iterator as *const u8, 1); - crate::closure::js_closure_set_capture_f64(async_iter, 0, wrapper); + let async_iter_h = scope.root_raw_mut_ptr(async_iter); + let wrapper = + crate::value::js_nanbox_pointer(obj_h.get_raw_mut_ptr::() as i64); + crate::closure::js_closure_set_capture_f64( + async_iter_h.get_raw_mut_ptr::(), + 0, + wrapper, + ); let sym = crate::symbol::well_known_symbol("asyncIterator"); if !sym.is_null() { + let wrapper = + crate::value::js_nanbox_pointer(obj_h.get_raw_mut_ptr::() as i64); unsafe { crate::symbol::js_object_set_symbol_property( wrapper, f64::from_bits(crate::value::JSValue::pointer(sym as *const u8).bits()), - crate::value::js_nanbox_pointer(async_iter as i64), + crate::value::js_nanbox_pointer( + async_iter_h.get_raw_mut_ptr::() as i64, + ), ); } } - wrapper + crate::value::js_nanbox_pointer(obj_h.get_raw_mut_ptr::() as i64) } #[no_mangle] From d959d94568210df5ebbdae34ba16ba0791c27257 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ralph=20K=C3=BCpper?= Date: Tue, 25 Aug 2026 09:40:00 +0200 Subject: [PATCH 5/5] chore: fmt + raw-handle ceiling for array/iterator.rs (#8801) --- crates/perry-runtime/src/array/iterator.rs | 22 ++++++++++++---------- crates/perry-runtime/src/lib.rs | 3 +-- crates/perry-stdlib/src/mongodb.rs | 4 ++-- crates/perry-stdlib/src/mysql2/pool.rs | 4 +++- crates/perry-stdlib/src/pg/connection.rs | 4 +++- crates/perry-stdlib/src/sharp.rs | 4 +++- scripts/raw_handle_debt_baseline.txt | 2 +- scripts/raw_handle_debt_files.txt | 1 + 8 files changed, 26 insertions(+), 18 deletions(-) diff --git a/crates/perry-runtime/src/array/iterator.rs b/crates/perry-runtime/src/array/iterator.rs index 6afa52f781..14ccb0147f 100644 --- a/crates/perry-runtime/src/array/iterator.rs +++ b/crates/perry-runtime/src/array/iterator.rs @@ -366,15 +366,15 @@ fn async_from_sync_continue(iter: f64, step_result: f64, close_on_rejection: boo let done_key = crate::string::js_string_from_bytes_longlived(b"done".as_ptr(), 4); let value_key = crate::string::js_string_from_bytes_longlived(b"value".as_ptr(), 5); let done = { - let result_obj = - crate::value::js_nanbox_get_pointer(step_h.get_nanbox_f64()) as *const crate::object::ObjectHeader; + let result_obj = crate::value::js_nanbox_get_pointer(step_h.get_nanbox_f64()) + as *const crate::object::ObjectHeader; let done_val = crate::object::js_object_get_field_by_name(result_obj, done_key); let done_f64 = f64::from_bits(done_val.bits()); crate::value::js_is_truthy(done_f64) != 0 }; let value = { - let result_obj = - crate::value::js_nanbox_get_pointer(step_h.get_nanbox_f64()) as *const crate::object::ObjectHeader; + let result_obj = crate::value::js_nanbox_get_pointer(step_h.get_nanbox_f64()) + as *const crate::object::ObjectHeader; let value_val = crate::object::js_object_get_field_by_name(result_obj, value_key); f64::from_bits(value_val.bits()) }; @@ -715,7 +715,7 @@ fn install_async_from_sync_method( value, ); crate::value::js_nanbox_pointer( - closure_h.get_raw_mut_ptr::() as i64, + closure_h.get_raw_mut_ptr::() as i64 ) } @@ -756,7 +756,7 @@ fn install_async_from_sync_next( value, ); crate::value::js_nanbox_pointer( - closure_h.get_raw_mut_ptr::() as i64, + closure_h.get_raw_mut_ptr::() as i64 ) } @@ -796,8 +796,9 @@ pub(crate) fn async_from_sync_wrap_iterator(iter: f64) -> f64 { let async_iter = crate::closure::js_closure_alloc(async_from_sync_async_iterator as *const u8, 1); let async_iter_h = scope.root_raw_mut_ptr(async_iter); - let wrapper = - crate::value::js_nanbox_pointer(obj_h.get_raw_mut_ptr::() as i64); + let wrapper = crate::value::js_nanbox_pointer( + obj_h.get_raw_mut_ptr::() as i64, + ); crate::closure::js_closure_set_capture_f64( async_iter_h.get_raw_mut_ptr::(), 0, @@ -805,8 +806,9 @@ pub(crate) fn async_from_sync_wrap_iterator(iter: f64) -> f64 { ); let sym = crate::symbol::well_known_symbol("asyncIterator"); if !sym.is_null() { - let wrapper = - crate::value::js_nanbox_pointer(obj_h.get_raw_mut_ptr::() as i64); + let wrapper = crate::value::js_nanbox_pointer( + obj_h.get_raw_mut_ptr::() as i64, + ); unsafe { crate::symbol::js_object_set_symbol_property( wrapper, diff --git a/crates/perry-runtime/src/lib.rs b/crates/perry-runtime/src/lib.rs index 3c0a9ce92d..c88131b72f 100644 --- a/crates/perry-runtime/src/lib.rs +++ b/crates/perry-runtime/src/lib.rs @@ -308,8 +308,7 @@ pub use object::{ pub use promise::{js_is_promise, js_promise_run_microtasks, js_promise_state, js_promise_value}; pub use promise::{ js_promise_mark_internally_handled, js_promise_new, js_promise_new_cross_thread, - js_promise_reject, js_promise_rejected, - js_promise_resolve, js_promise_resolved, + js_promise_reject, js_promise_rejected, js_promise_resolve, js_promise_resolved, }; pub use string::js_string_from_bytes; pub use value::{ diff --git a/crates/perry-stdlib/src/mongodb.rs b/crates/perry-stdlib/src/mongodb.rs index e26cf98ecd..26ab49a001 100644 --- a/crates/perry-stdlib/src/mongodb.rs +++ b/crates/perry-stdlib/src/mongodb.rs @@ -11,8 +11,8 @@ use bson::{doc, Document}; use mongodb::{Client, Collection, Database}; use perry_runtime::json::js_json_stringify; use perry_runtime::{ - js_object_alloc, js_object_set_field, js_promise_new_cross_thread, js_string_from_bytes, JSValue, - ObjectHeader, Promise, StringHeader, + js_object_alloc, js_object_set_field, js_promise_new_cross_thread, js_string_from_bytes, + JSValue, ObjectHeader, Promise, StringHeader, }; /// JSON-stringify a NaN-boxed JSValue at the FFI boundary. Used by the diff --git a/crates/perry-stdlib/src/mysql2/pool.rs b/crates/perry-stdlib/src/mysql2/pool.rs index e7b25c9096..8bb242b4d5 100644 --- a/crates/perry-stdlib/src/mysql2/pool.rs +++ b/crates/perry-stdlib/src/mysql2/pool.rs @@ -2,7 +2,9 @@ use std::time::Duration; -use perry_runtime::{js_array_get_jsvalue, js_array_length, js_promise_new_cross_thread, JSValue, Promise}; +use perry_runtime::{ + js_array_get_jsvalue, js_array_length, js_promise_new_cross_thread, JSValue, Promise, +}; use sqlx::mysql::{MySqlPool, MySqlPoolOptions}; use sqlx::pool::PoolConnection; use sqlx::MySql; diff --git a/crates/perry-stdlib/src/pg/connection.rs b/crates/perry-stdlib/src/pg/connection.rs index 95e01194b6..176d3d4321 100644 --- a/crates/perry-stdlib/src/pg/connection.rs +++ b/crates/perry-stdlib/src/pg/connection.rs @@ -1,6 +1,8 @@ //! PostgreSQL connection implementation -use perry_runtime::{js_array_get_jsvalue, js_array_length, js_promise_new_cross_thread, JSValue, Promise}; +use perry_runtime::{ + js_array_get_jsvalue, js_array_length, js_promise_new_cross_thread, JSValue, Promise, +}; use sqlx::postgres::PgConnection; use sqlx::{Connection, Row}; diff --git a/crates/perry-stdlib/src/sharp.rs b/crates/perry-stdlib/src/sharp.rs index ad045f8ed0..7ad08976eb 100644 --- a/crates/perry-stdlib/src/sharp.rs +++ b/crates/perry-stdlib/src/sharp.rs @@ -8,7 +8,9 @@ use crate::common::{ string_from_header_lossy as string_from_header, Handle, }; use image::{imageops::FilterType, DynamicImage, GenericImageView, ImageFormat}; -use perry_runtime::{js_promise_new_cross_thread, js_string_from_bytes, JSValue, Promise, StringHeader}; +use perry_runtime::{ + js_promise_new_cross_thread, js_string_from_bytes, JSValue, Promise, StringHeader, +}; use std::io::Cursor; /// Sharp image handle with pending operations diff --git a/scripts/raw_handle_debt_baseline.txt b/scripts/raw_handle_debt_baseline.txt index d6904a94ba..175df711d1 100644 --- a/scripts/raw_handle_debt_baseline.txt +++ b/scripts/raw_handle_debt_baseline.txt @@ -1 +1 @@ -913 +944 diff --git a/scripts/raw_handle_debt_files.txt b/scripts/raw_handle_debt_files.txt index 3825d7b805..9af4855dd7 100644 --- a/scripts/raw_handle_debt_files.txt +++ b/scripts/raw_handle_debt_files.txt @@ -50,6 +50,7 @@ 5 crates/perry-runtime/src/array/header.rs 6 crates/perry-runtime/src/array/indexing.rs 2 crates/perry-runtime/src/array/iter_methods.rs +31 crates/perry-runtime/src/array/iterator.rs 4 crates/perry-runtime/src/array/push_pop.rs 14 crates/perry-runtime/src/array/sort.rs 13 crates/perry-runtime/src/async_hooks.rs