diff --git a/CLAUDE.md b/CLAUDE.md index 79eb2673f3..d0cd935637 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -8,7 +8,7 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co Perry is a native TypeScript compiler written in Rust that compiles TypeScript source code directly to native executables. It uses SWC for TypeScript parsing and LLVM for code generation. -**Current Version:** 0.5.1370 +**Current Version:** 0.5.1371 ## TypeScript Parity Status diff --git a/Cargo.lock b/Cargo.lock index ec5cc72a43..1e18c36143 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5547,7 +5547,7 @@ checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" [[package]] name = "perry" -version = "0.5.1370" +version = "0.5.1371" dependencies = [ "anyhow", "base64", @@ -5607,14 +5607,14 @@ dependencies = [ [[package]] name = "perry-api-manifest" -version = "0.5.1370" +version = "0.5.1371" dependencies = [ "serde", ] [[package]] name = "perry-audio-miniaudio" -version = "0.5.1370" +version = "0.5.1371" dependencies = [ "cc", "libc", @@ -5622,7 +5622,7 @@ dependencies = [ [[package]] name = "perry-codegen" -version = "0.5.1370" +version = "0.5.1371" dependencies = [ "anyhow", "inkwell", @@ -5639,7 +5639,7 @@ dependencies = [ [[package]] name = "perry-codegen-arkts" -version = "0.5.1370" +version = "0.5.1371" dependencies = [ "anyhow", "perry-hir", @@ -5647,7 +5647,7 @@ dependencies = [ [[package]] name = "perry-codegen-glance" -version = "0.5.1370" +version = "0.5.1371" dependencies = [ "anyhow", "perry-hir", @@ -5655,7 +5655,7 @@ dependencies = [ [[package]] name = "perry-codegen-js" -version = "0.5.1370" +version = "0.5.1371" dependencies = [ "anyhow", "perry-dispatch", @@ -5664,7 +5664,7 @@ dependencies = [ [[package]] name = "perry-codegen-swiftui" -version = "0.5.1370" +version = "0.5.1371" dependencies = [ "anyhow", "perry-hir", @@ -5672,7 +5672,7 @@ dependencies = [ [[package]] name = "perry-codegen-wasm" -version = "0.5.1370" +version = "0.5.1371" dependencies = [ "anyhow", "base64", @@ -5684,7 +5684,7 @@ dependencies = [ [[package]] name = "perry-codegen-wear-tiles" -version = "0.5.1370" +version = "0.5.1371" dependencies = [ "anyhow", "perry-hir", @@ -5692,7 +5692,7 @@ dependencies = [ [[package]] name = "perry-container-compose" -version = "0.5.1370" +version = "0.5.1371" dependencies = [ "anyhow", "async-trait", @@ -5721,14 +5721,14 @@ dependencies = [ [[package]] name = "perry-container-e2e" -version = "0.5.1370" +version = "0.5.1371" dependencies = [ "anyhow", ] [[package]] name = "perry-diagnostics" -version = "0.5.1370" +version = "0.5.1371" dependencies = [ "serde", "serde_json", @@ -5736,7 +5736,7 @@ dependencies = [ [[package]] name = "perry-dispatch" -version = "0.5.1370" +version = "0.5.1371" [[package]] name = "perry-doc-fixture-my-bindings" @@ -5747,7 +5747,7 @@ dependencies = [ [[package]] name = "perry-doc-tests" -version = "0.5.1370" +version = "0.5.1371" dependencies = [ "anyhow", "clap", @@ -5762,7 +5762,7 @@ dependencies = [ [[package]] name = "perry-ext-ads" -version = "0.5.1370" +version = "0.5.1371" dependencies = [ "block2", "objc2", @@ -5772,7 +5772,7 @@ dependencies = [ [[package]] name = "perry-ext-argon2" -version = "0.5.1370" +version = "0.5.1371" dependencies = [ "argon2", "perry-ffi", @@ -5780,7 +5780,7 @@ dependencies = [ [[package]] name = "perry-ext-axios" -version = "0.5.1370" +version = "0.5.1371" dependencies = [ "perry-ffi", "reqwest", @@ -5789,7 +5789,7 @@ dependencies = [ [[package]] name = "perry-ext-bcrypt" -version = "0.5.1370" +version = "0.5.1371" dependencies = [ "bcrypt", "perry-ffi", @@ -5797,7 +5797,7 @@ dependencies = [ [[package]] name = "perry-ext-better-sqlite3" -version = "0.5.1370" +version = "0.5.1371" dependencies = [ "perry-ffi", "rusqlite", @@ -5805,7 +5805,7 @@ dependencies = [ [[package]] name = "perry-ext-cheerio" -version = "0.5.1370" +version = "0.5.1371" dependencies = [ "perry-ffi", "scraper", @@ -5813,7 +5813,7 @@ dependencies = [ [[package]] name = "perry-ext-commander" -version = "0.5.1370" +version = "0.5.1371" dependencies = [ "perry-ffi", "perry-runtime", @@ -5821,7 +5821,7 @@ dependencies = [ [[package]] name = "perry-ext-cron" -version = "0.5.1370" +version = "0.5.1371" dependencies = [ "chrono", "cron", @@ -5831,7 +5831,7 @@ dependencies = [ [[package]] name = "perry-ext-dayjs" -version = "0.5.1370" +version = "0.5.1371" dependencies = [ "chrono", "perry-ffi", @@ -5839,7 +5839,7 @@ dependencies = [ [[package]] name = "perry-ext-decimal" -version = "0.5.1370" +version = "0.5.1371" dependencies = [ "perry-ffi", "rust_decimal", @@ -5847,7 +5847,7 @@ dependencies = [ [[package]] name = "perry-ext-dotenv" -version = "0.5.1370" +version = "0.5.1371" dependencies = [ "perry-ffi", "serde_json", @@ -5855,7 +5855,7 @@ dependencies = [ [[package]] name = "perry-ext-ethers" -version = "0.5.1370" +version = "0.5.1371" dependencies = [ "perry-ffi", "rand 0.10.1", @@ -5863,7 +5863,7 @@ dependencies = [ [[package]] name = "perry-ext-events" -version = "0.5.1370" +version = "0.5.1371" dependencies = [ "perry-ffi", "perry-runtime", @@ -5871,14 +5871,14 @@ dependencies = [ [[package]] name = "perry-ext-exponential-backoff" -version = "0.5.1370" +version = "0.5.1371" dependencies = [ "perry-ffi", ] [[package]] name = "perry-ext-fastify" -version = "0.5.1370" +version = "0.5.1371" dependencies = [ "bytes", "http-body-util", @@ -5896,7 +5896,7 @@ dependencies = [ [[package]] name = "perry-ext-fetch" -version = "0.5.1370" +version = "0.5.1371" dependencies = [ "bytes", "lazy_static", @@ -5909,7 +5909,7 @@ dependencies = [ [[package]] name = "perry-ext-http" -version = "0.5.1370" +version = "0.5.1371" dependencies = [ "bytes", "h2", @@ -5933,7 +5933,7 @@ dependencies = [ [[package]] name = "perry-ext-ioredis" -version = "0.5.1370" +version = "0.5.1371" dependencies = [ "lazy_static", "perry-ffi", @@ -5943,7 +5943,7 @@ dependencies = [ [[package]] name = "perry-ext-jsonwebtoken" -version = "0.5.1370" +version = "0.5.1371" dependencies = [ "base64", "jsonwebtoken", @@ -5954,7 +5954,7 @@ dependencies = [ [[package]] name = "perry-ext-lru-cache" -version = "0.5.1370" +version = "0.5.1371" dependencies = [ "lru", "perry-ffi", @@ -5963,7 +5963,7 @@ dependencies = [ [[package]] name = "perry-ext-moment" -version = "0.5.1370" +version = "0.5.1371" dependencies = [ "chrono", "perry-ffi", @@ -5971,7 +5971,7 @@ dependencies = [ [[package]] name = "perry-ext-mongodb" -version = "0.5.1370" +version = "0.5.1371" dependencies = [ "bson", "futures-util", @@ -5983,7 +5983,7 @@ dependencies = [ [[package]] name = "perry-ext-mysql2" -version = "0.5.1370" +version = "0.5.1371" dependencies = [ "chrono", "perry-ffi", @@ -5993,7 +5993,7 @@ dependencies = [ [[package]] name = "perry-ext-nanoid" -version = "0.5.1370" +version = "0.5.1371" dependencies = [ "nanoid", "perry-ffi", @@ -6002,7 +6002,7 @@ dependencies = [ [[package]] name = "perry-ext-net" -version = "0.5.1370" +version = "0.5.1371" dependencies = [ "bytes", "perry-ffi", @@ -6015,7 +6015,7 @@ dependencies = [ [[package]] name = "perry-ext-node-forge" -version = "0.5.1370" +version = "0.5.1371" dependencies = [ "const-oid 0.9.6", "der 0.7.10", @@ -6034,7 +6034,7 @@ dependencies = [ [[package]] name = "perry-ext-nodemailer" -version = "0.5.1370" +version = "0.5.1371" dependencies = [ "lettre", "perry-ffi", @@ -6044,7 +6044,7 @@ dependencies = [ [[package]] name = "perry-ext-pdf" -version = "0.5.1370" +version = "0.5.1371" dependencies = [ "perry-ffi", "printpdf", @@ -6052,7 +6052,7 @@ dependencies = [ [[package]] name = "perry-ext-pg" -version = "0.5.1370" +version = "0.5.1371" dependencies = [ "perry-ffi", "sqlx", @@ -6061,7 +6061,7 @@ dependencies = [ [[package]] name = "perry-ext-ratelimit" -version = "0.5.1370" +version = "0.5.1371" dependencies = [ "governor", "perry-ffi", @@ -6069,7 +6069,7 @@ dependencies = [ [[package]] name = "perry-ext-sharp" -version = "0.5.1370" +version = "0.5.1371" dependencies = [ "fast_image_resize", "image", @@ -6079,14 +6079,14 @@ dependencies = [ [[package]] name = "perry-ext-slugify" -version = "0.5.1370" +version = "0.5.1371" dependencies = [ "perry-ffi", ] [[package]] name = "perry-ext-streams" -version = "0.5.1370" +version = "0.5.1371" dependencies = [ "lazy_static", "perry-ffi", @@ -6095,7 +6095,7 @@ dependencies = [ [[package]] name = "perry-ext-undici" -version = "0.5.1370" +version = "0.5.1371" dependencies = [ "perry-ffi", "perry-runtime", @@ -6104,7 +6104,7 @@ dependencies = [ [[package]] name = "perry-ext-uuid" -version = "0.5.1370" +version = "0.5.1371" dependencies = [ "perry-ffi", "uuid", @@ -6112,7 +6112,7 @@ dependencies = [ [[package]] name = "perry-ext-validator" -version = "0.5.1370" +version = "0.5.1371" dependencies = [ "perry-ffi", "regex", @@ -6122,7 +6122,7 @@ dependencies = [ [[package]] name = "perry-ext-ws" -version = "0.5.1370" +version = "0.5.1371" dependencies = [ "futures-util", "lazy_static", @@ -6135,7 +6135,7 @@ dependencies = [ [[package]] name = "perry-ext-zlib" -version = "0.5.1370" +version = "0.5.1371" dependencies = [ "brotli", "flate2", @@ -6145,7 +6145,7 @@ dependencies = [ [[package]] name = "perry-ffi" -version = "0.5.1370" +version = "0.5.1371" dependencies = [ "dashmap", "once_cell", @@ -6154,7 +6154,7 @@ dependencies = [ [[package]] name = "perry-hir" -version = "0.5.1370" +version = "0.5.1371" dependencies = [ "anyhow", "perry-api-manifest", @@ -6172,7 +6172,7 @@ dependencies = [ [[package]] name = "perry-parser" -version = "0.5.1370" +version = "0.5.1371" dependencies = [ "anyhow", "perry-diagnostics", @@ -6184,7 +6184,7 @@ dependencies = [ [[package]] name = "perry-runtime" -version = "0.5.1370" +version = "0.5.1371" dependencies = [ "anyhow", "base64", @@ -6226,14 +6226,14 @@ dependencies = [ [[package]] name = "perry-runtime-static" -version = "0.5.1370" +version = "0.5.1371" dependencies = [ "perry-runtime", ] [[package]] name = "perry-stdlib" -version = "0.5.1370" +version = "0.5.1371" dependencies = [ "aes 0.8.4", "aes 0.9.1", @@ -6328,14 +6328,14 @@ dependencies = [ [[package]] name = "perry-stdlib-static" -version = "0.5.1370" +version = "0.5.1371" dependencies = [ "perry-stdlib", ] [[package]] name = "perry-transform" -version = "0.5.1370" +version = "0.5.1371" dependencies = [ "anyhow", "perry-hir", @@ -6344,14 +6344,14 @@ dependencies = [ [[package]] name = "perry-ui" -version = "0.5.1370" +version = "0.5.1371" dependencies = [ "perry-ui-model", ] [[package]] name = "perry-ui-android" -version = "0.5.1370" +version = "0.5.1371" dependencies = [ "base64", "itoa", @@ -6368,7 +6368,7 @@ dependencies = [ [[package]] name = "perry-ui-geisterhand" -version = "0.5.1370" +version = "0.5.1371" dependencies = [ "rand 0.10.1", "serde", @@ -6378,7 +6378,7 @@ dependencies = [ [[package]] name = "perry-ui-gtk4" -version = "0.5.1370" +version = "0.5.1371" dependencies = [ "base64", "cairo-rs 0.22.0", @@ -6401,7 +6401,7 @@ dependencies = [ [[package]] name = "perry-ui-ios" -version = "0.5.1370" +version = "0.5.1371" dependencies = [ "base64", "block2", @@ -6417,7 +6417,7 @@ dependencies = [ [[package]] name = "perry-ui-macos" -version = "0.5.1370" +version = "0.5.1371" dependencies = [ "base64", "block2", @@ -6432,7 +6432,7 @@ dependencies = [ [[package]] name = "perry-ui-model" -version = "0.5.1370" +version = "0.5.1371" [[package]] name = "perry-ui-test" @@ -6443,11 +6443,11 @@ dependencies = [ [[package]] name = "perry-ui-testkit" -version = "0.5.1370" +version = "0.5.1371" [[package]] name = "perry-ui-tvos" -version = "0.5.1370" +version = "0.5.1371" dependencies = [ "base64", "block2", @@ -6463,7 +6463,7 @@ dependencies = [ [[package]] name = "perry-ui-visionos" -version = "0.5.1370" +version = "0.5.1371" dependencies = [ "base64", "block2", @@ -6479,7 +6479,7 @@ dependencies = [ [[package]] name = "perry-ui-watchos" -version = "0.5.1370" +version = "0.5.1371" dependencies = [ "block2", "libc", @@ -6492,7 +6492,7 @@ dependencies = [ [[package]] name = "perry-ui-windows" -version = "0.5.1370" +version = "0.5.1371" dependencies = [ "base64", "libc", @@ -6509,14 +6509,14 @@ dependencies = [ [[package]] name = "perry-ui-windows-winui" -version = "0.5.1370" +version = "0.5.1371" dependencies = [ "perry-ui-windows", ] [[package]] name = "perry-updater" -version = "0.5.1370" +version = "0.5.1371" dependencies = [ "anyhow", "base64", @@ -6532,7 +6532,7 @@ dependencies = [ [[package]] name = "perry-wasm-host" -version = "0.5.1370" +version = "0.5.1371" dependencies = [ "wasmi", ] diff --git a/Cargo.toml b/Cargo.toml index 1f26d934ba..0d34ec862e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -315,7 +315,7 @@ codegen-units = 16 codegen-units = 16 [workspace.package] -version = "0.5.1370" +version = "0.5.1371" edition = "2021" license = "MIT" repository = "https://github.com/PerryTS/perry" diff --git a/changelog.d/7653-pin-object-ext-link.md b/changelog.d/7653-pin-object-ext-link.md new file mode 100644 index 0000000000..d936bbca4a --- /dev/null +++ b/changelog.d/7653-pin-object-ext-link.md @@ -0,0 +1,44 @@ +**`fix(gc)`: pin long-lived and malloc-resident objects without the space classifier (#7650 follow-up).** + +#7650 routed every `GC_FLAG_PINNED` write through `gc::pin_object`, which reaches +`arena::classify_heap_space`. That new edge kept a reference chain alive that +`-Wl,-dead_strip` had been removing, and **five `perry-ext-*` crates stopped +linking**: + +``` +Undefined symbols for architecture arm64: + "_js_blob_new", referenced from: … fetch_globals::global_this_blob_thunk + "_js_fetch_with_options", referenced from: … global_fetch::global_this_fetch_thunk + "_js_fetch_notify_signal_aborted", referenced from: … url::abort::fire_abort_listeners +``` + +`perry-ext-{pdf,lru-cache,node-forge,mongodb,http}`. They link a **feature-stripped** +runtime through `perry-ffi`'s `runtime-link`, so those thunks have no definition +and only survived because the stripper removed them. + +Bisected rather than guessed: the commit before #7650 builds all five clean, +#7650 does not, and reverting **only** the two `perry-runtime` call sites +restores the link. `perry-stdlib`'s `async_bridge` keeps `pin_object` — its +`js_promise_new()` promises really are Eden-resident and must arm the young-pin +latch. + +`pin_object_non_young` does the flag write directly for the sites #7650's own +comments already document as long-lived (`string/format.rs`, the interned format +buffer) and malloc-resident (`thread.rs`, the spawn promise and its handle) — +they never needed the classifier. Making `pin_object` conservative instead +(arming for any `GC_FLAG_ARENA` object) would also remove the edge, but it would +arm on exactly these long-lived pins and throw away the preflight skip #7645 +bought. + +**The claim is checked, not asserted.** `debug_assert` catches a young object in +test builds, and `pin_object_non_young_call_sites_are_never_young` +(`gc/tests/copying/latch.rs`) asserts non-youngness for each real call site plus +a control proving the predicate is not vacuously false for everything. Sabotage: +forcing the predicate false reddens the control (0 compile errors, test binary +reached). + +**Why no gate caught it.** `cargo-test` scopes per-PR runs to the changed crates' +reverse-dependency closure (`scripts/ci_test_scope.py`); the full workspace runs +on tags and nightly only. `perry-ext-*` is outside the closure of a +`perry-runtime` GC change, so this could only have surfaced at the next tag. +Found by running `cargo test --release --workspace` by hand against `main`. diff --git a/crates/perry-runtime/src/gc/mod.rs b/crates/perry-runtime/src/gc/mod.rs index f5f4304d59..547e2a2710 100644 --- a/crates/perry-runtime/src/gc/mod.rs +++ b/crates/perry-runtime/src/gc/mod.rs @@ -103,7 +103,8 @@ mod pin; #[cfg(test)] pub(crate) use pin::test_reset_young_pin_latch; pub use pin::{ - copied_minor_preflight_skips, copied_minor_preflight_walks, pin_object, unpin_object, + copied_minor_preflight_skips, copied_minor_preflight_walks, pin_object, pin_object_non_young, + unpin_object, }; use pin::{note_preflight_skipped, note_preflight_walked, young_pin_latch_armed}; mod copying; diff --git a/crates/perry-runtime/src/gc/pin.rs b/crates/perry-runtime/src/gc/pin.rs index b6fdcf2d20..5f3f7146ee 100644 --- a/crates/perry-runtime/src/gc/pin.rs +++ b/crates/perry-runtime/src/gc/pin.rs @@ -114,6 +114,58 @@ pub unsafe fn pin_object(header: *mut GcHeader) { (*header).gc_flags |= GC_FLAG_PINNED; } +/// Set `GC_FLAG_PINNED` on an object the CALLER has already proven cannot be +/// young-arena resident, without consulting the space classifier. +/// +/// # Why this exists, and why it is not merely an optimisation +/// +/// [`pin_object`] reaches `crate::arena::classify_heap_space`, and that edge is +/// load-bearing for a reason that has nothing to do with the GC: the +/// `perry-ext-*` crates link a **feature-stripped** runtime through +/// `perry-ffi`'s `runtime-link` and are built with `-Wl,-dead_strip`. +/// Introducing this call from `thread.rs` / `string/format.rs` kept a reference +/// chain alive that the stripper had previously removed, and five ext crates +/// stopped linking with `Undefined symbols: _js_blob_new, +/// _js_fetch_with_options, _js_fetch_notify_signal_aborted` (#7650, bisected to +/// that commit against a clean parent). `cargo-test` scopes per-PR runs to the +/// changed crates' reverse-dependency closure and the FULL workspace is +/// tag/nightly-only, so no per-PR gate could have seen it. +/// +/// Making [`pin_object`] conservative instead — arming the latch for any +/// `GC_FLAG_ARENA` object — would also remove the edge, but it would arm on +/// exactly the long-lived pins this variant serves (`format.rs` pins long-lived +/// strings), throwing away the preflight skip #7645 bought. +/// +/// # Safety +/// +/// As [`pin_object`], **plus** the caller must guarantee `header` is malloc +/// space, `Longlived`, or `Old`. Pinning a young-arena object through here +/// leaves the latch disarmed, and a copying minor will then relocate a pinned +/// object — memory corruption, not a missed optimisation. `debug_assert` catches +/// it in test builds, and the claim is checked for every real call site by +/// `pin_object_non_young_call_sites_are_never_young` in +/// `gc/tests/copying/latch.rs`; **add a case there when you add a caller.** +#[inline] +pub unsafe fn pin_object_non_young(header: *mut GcHeader) { + if header.is_null() { + return; + } + debug_assert!( + !pin_constrains_copying_minor(header), + "pin_object_non_young called on a young-arena object: the young-pin \ + latch stays disarmed and the copying minor will relocate it" + ); + (*header).gc_flags |= GC_FLAG_PINNED; +} + +/// Test accessor for the young-pin predicate, so +/// `pin_object_non_young_call_sites_are_never_young` can assert the invariant +/// its callers rest on without duplicating the classification logic. +#[cfg(test)] +pub(crate) unsafe fn pin_constrains_copying_minor_for_tests(header: *mut GcHeader) -> bool { + pin_constrains_copying_minor(header) +} + /// Clear `GC_FLAG_PINNED` on `header`. Does **not** disarm the latch — see the /// module docs on why the latch is monotone. /// diff --git a/crates/perry-runtime/src/gc/tests/copying/latch.rs b/crates/perry-runtime/src/gc/tests/copying/latch.rs index 7742fcfba7..bafa500250 100644 --- a/crates/perry-runtime/src/gc/tests/copying/latch.rs +++ b/crates/perry-runtime/src/gc/tests/copying/latch.rs @@ -277,3 +277,56 @@ fn pin_latch_sabotage_child() { let _ = collect_minor_trace(GcTriggerKind::Direct); panic!("copying minor relocated a pinned young object without aborting"); } + +/// #7650 follow-up: every `pin_object_non_young` call site must really be +/// non-young, because that variant deliberately skips the space classifier and +/// therefore leaves the latch disarmed. +/// +/// The variant exists for a LINK reason, not a GC one — `pin_object` reaches +/// `arena::classify_heap_space`, and that edge kept a reference chain alive +/// that `-Wl,-dead_strip` had been removing, breaking five `perry-ext-*` crates +/// with `Undefined symbols: _js_blob_new, _js_fetch_with_options, …` (#7650, +/// bisected). The full workspace test is tag/nightly-only, so a per-PR run +/// cannot see it. The safety property is therefore asserted here rather than at +/// the call, and it is only as good as this list: **add a case when you add a +/// caller.** +/// +/// Callers as of this change: +/// * `string/format.rs` — the interned format buffer, allocated long-lived. +/// * `thread.rs` (x2) — the spawn promise and its handle, malloc-resident, so +/// they carry no `GC_FLAG_ARENA` and the predicate short-circuits. +#[test] +fn pin_object_non_young_call_sites_are_never_young() { + let _guard = CopyingNurseryTestGuard::new(1); + + unsafe { + // `string/format.rs` pins a LONG-LIVED allocation. + let longlived = crate::string::js_string_from_bytes_longlived(b"x".as_ptr(), 1); + let ll_header = header_from_user_ptr(longlived as *const u8) as *mut GcHeader; + assert!( + !crate::gc::pin::pin_constrains_copying_minor_for_tests(ll_header), + "format.rs pins a long-lived string; if that allocation ever moved \ + to Eden, pin_object_non_young there would become memory corruption" + ); + + // `thread.rs` pins MALLOC-resident promise state: no GC_FLAG_ARENA, so + // the predicate short-circuits before any space classification. + let mut synthetic = std::ptr::read(ll_header); + synthetic.gc_flags &= !crate::gc::GC_FLAG_ARENA; + assert!( + !crate::gc::pin::pin_constrains_copying_minor_for_tests(&mut synthetic), + "a header without GC_FLAG_ARENA is malloc space and can never be \ + Eden/FromSurvivor" + ); + + // Control: a plain nursery object IS young, so the predicate the two + // assertions above rely on is not vacuously false for everything. + let young = young_leaf(); + let y_header = header_from_user_ptr(young as *const u8) as *mut GcHeader; + assert!( + crate::gc::pin::pin_constrains_copying_minor_for_tests(y_header), + "control: a nursery object must be reported young, or this test \ + proves nothing about the two assertions above" + ); + } +} diff --git a/crates/perry-runtime/src/string/format.rs b/crates/perry-runtime/src/string/format.rs index 0d8d4c8ae9..fe0a8ef083 100644 --- a/crates/perry-runtime/src/string/format.rs +++ b/crates/perry-runtime/src/string/format.rs @@ -88,7 +88,7 @@ pub extern "C" fn js_number_to_string(value: f64) -> *mut StringHeader { // this does not arm the young-pin latch (#7645). let gc_header = (ptr as *const u8).sub(crate::gc::GC_HEADER_SIZE) as *mut crate::gc::GcHeader; - crate::gc::pin_object(gc_header); + crate::gc::pin_object_non_young(gc_header); } SMALL_INT_CACHE.with(|c| unsafe { // GC_STORE_AUDIT(ROOT): SMALL_INT_CACHE is scanned by scan_small_int_cache_roots_mut. diff --git a/crates/perry-runtime/src/thread.rs b/crates/perry-runtime/src/thread.rs index 1aeda5db14..dbb4bc4a83 100644 --- a/crates/perry-runtime/src/thread.rs +++ b/crates/perry-runtime/src/thread.rs @@ -1488,7 +1488,7 @@ unsafe fn spawn_impl(closure_val: f64) -> *mut crate::promise::Promise { // Pin the promise so GC doesn't collect it while the thread is running. // Malloc-resident (see above), so this does not arm the young-pin latch. let promise_header = (promise as *mut u8).sub(gc::GC_HEADER_SIZE) as *mut gc::GcHeader; - gc::pin_object(promise_header); + gc::pin_object_non_young(promise_header); let promise_usize = promise as usize; // #6185: the promise lives in the SPAWNING agent's heap, so that is the @@ -1615,7 +1615,7 @@ pub fn thread_job_begin() { /// `promise` must be a live promise allocation preceded by an 8-byte GcHeader. pub unsafe fn pin_promise(promise: *mut crate::promise::Promise) { let header = (promise as *mut u8).sub(gc::GC_HEADER_SIZE) as *mut gc::GcHeader; - gc::pin_object(header); + gc::pin_object_non_young(header); } /// Resolve the promise at `promise_usize` with a UTF-8 string on the agent that