From 1c94138b2a6c8ff4386d914ed595ec7a4ae37425 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ralph=20K=C3=BCpper?= Date: Sun, 9 Aug 2026 21:18:43 +0200 Subject: [PATCH 1/3] fix(gc): warm_generator_intrinsics must call the tower builder, not a no-op crates/perry-runtime/src/gc/tests/runtime_roots/generator_attach_prototype.rs's three tests were red on main: js_generator_attach_prototype and js_generator_attach_closure_prototype no longer moved their receiver under an alloc-point copying minor, and the shipped-default witness never saw its trigger armed. warm_generator_intrinsics() called js_generator_attach_prototype(TAG_UNDEFINED, 0) to pre-build the generator intrinsic tower before the timed call under test. That never worked: js_generator_attach_prototype returns at its very first line for any non-pointer obj, so the "warm-up" touched nothing. It went unnoticed because GENERATOR_FUNCTION_INTRINSIC_PTR and its five siblings were plain process-global AtomicI64s pre-#7723 - some earlier test in the same binary had almost always already built the tower, so the real call under test found it cached regardless of what warm_generator_intrinsics() did. #7723 converted those six statics to per_test_global! specifically so each test starts from a guaranteed first-touch state (crates/perry-runtime/src/gc/tests/lazy_intrinsic_towers.rs's whole point). That is a correct, deliberate change - it took away the accidental cross-test priming these three tests had been relying on. With nothing pre-built, the real call now pays the dozens-of-allocations tower build itself, inside build_generator_tower's GcSuppressScope (#7251's no-move window for that build). That suppression window swallows the arena trigger the test injected via arm_collection_on_next_block for the rest of the call: no copying minor ever runs before the tower build's own scope closes, and by then intermediate's own allocation no longer needs a new arena block, so the trigger is never serviced. Confirmed with instrumented gc_check_trigger / GcSuppressScope traces comparing the last-good commit against #7723: on the last-good commit the real call's first allocation reaches gc_check_trigger unsuppressed and services the trigger directly; on #7723 the entire ~1800-call tower build runs suppressed first and nothing ever re-triggers afterward. Fix warm_generator_intrinsics() to call crate::object::ensure_generator_intrinsics() directly - the same builder lazy_intrinsic_towers.rs uses - so it does what its name and doc comment always claimed. This does not touch the liveness/deferral assertions those tests make; it only repairs the test's own setup helper. Bisected via git checkout of each of today's three merges in an isolated worktree: c9079531d (pre-#7721) passes; ca8c0d617 (#7721, moving-loop poll default flip) passes; cbb682d31 (#7723, no-move window + per_test_global towers) is the first commit where all three fail. #7724 is uninvolved. --- .../generator_attach_prototype.rs | 26 ++++++++++++++++--- 1 file changed, 22 insertions(+), 4 deletions(-) diff --git a/crates/perry-runtime/src/gc/tests/runtime_roots/generator_attach_prototype.rs b/crates/perry-runtime/src/gc/tests/runtime_roots/generator_attach_prototype.rs index 5434360b7c..c5e6dde260 100644 --- a/crates/perry-runtime/src/gc/tests/runtime_roots/generator_attach_prototype.rs +++ b/crates/perry-runtime/src/gc/tests/runtime_roots/generator_attach_prototype.rs @@ -44,11 +44,29 @@ extern "C" fn fake_generator_body(_c: *const crate::closure::ClosureHeader, _a: /// the first `generator_prototype_ptr` call and costs dozens of allocations; /// paying it here keeps the call under test down to its own two, so the /// injected trigger lands where we intend. +/// +/// **Must call the builder directly.** This used to go through +/// `js_generator_attach_prototype(TAG_UNDEFINED, 0)`, banking on that helper +/// reaching `generator_prototype_ptr` and lazily building the tower as a side +/// effect. It never did: `js_generator_attach_prototype` returns at its very +/// first line for any non-pointer `obj` (`!jv.is_pointer()`), so the "warm-up" +/// call was a no-op that touched nothing. That went unnoticed only because +/// `GENERATOR_FUNCTION_INTRINSIC_PTR` & co. were plain process-global statics +/// pre-#7723 — some earlier test in the same binary had almost always already +/// built the tower, so the *real* call under test found it cached regardless +/// of what this function did. #7723 converted those statics to +/// `per_test_global!` so each test starts from a guaranteed first-touch state +/// (its whole point, for `gc::tests::lazy_intrinsic_towers`), which took away +/// the accidental cross-test priming and exposed this helper as dead code: +/// with nothing pre-built, the real call now pays the dozens-of-allocations +/// tower build itself, inside `GcSuppressScope` (#7251's no-move window for +/// that build), which swallows the injected arena trigger for the rest of the +/// call and no copying minor ever runs — "subject not live" in the two +/// alloc-point tests, and the deferral witness never seeing its trigger armed +/// in the third. Call the builder directly so this function does what its +/// name and doc always claimed. fn warm_generator_intrinsics() { - let _ = crate::object::js_generator_attach_prototype( - f64::from_bits(crate::value::TAG_UNDEFINED), - 0, - ); + crate::object::ensure_generator_intrinsics(); } /// A shadow-rooted, freshly allocated object standing in for a generator From 1e5ba1e25413f74333130136d8fa149c2b1b5fed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ralph=20K=C3=BCpper?= Date: Sun, 9 Aug 2026 21:19:44 +0200 Subject: [PATCH 2/3] changelog: add fragment for #7731 (generator-attach-pacing) --- changelog.d/7731-generator-attach-pacing.md | 34 +++++++++++++++++++++ 1 file changed, 34 insertions(+) create mode 100644 changelog.d/7731-generator-attach-pacing.md diff --git a/changelog.d/7731-generator-attach-pacing.md b/changelog.d/7731-generator-attach-pacing.md new file mode 100644 index 0000000000..4e33ee40fa --- /dev/null +++ b/changelog.d/7731-generator-attach-pacing.md @@ -0,0 +1,34 @@ +Fixed `cargo test -p perry-runtime --lib` being red on `main` — all three +`gc::tests::runtime_roots::generator_attach_prototype` tests +(`attach_prototype_survives_an_alloc_point_copying_minor_inside_the_call`, +`attach_closure_prototype_survives_an_alloc_point_copying_minor_inside_the_call`, +`the_shipped_default_defers_the_trigger_out_of_the_callees_window`) were +failing, which blocks every open PR via the per-PR `--lib --bins` gate. + +Bisected to #7723's `per_test_global!` conversion of the six +generator-intrinsic-tower `AtomicI64` statics +(`GENERATOR_FUNCTION_INTRINSIC_PTR` and siblings). That conversion is +correct — it gives each test a guaranteed first-touch "tower not yet built" +state instead of leaking cross-test priming through the process-global +statics — but it exposed a pre-existing bug in this test file's own setup +helper: `warm_generator_intrinsics()` called +`js_generator_attach_prototype(TAG_UNDEFINED, 0)` to pre-build the tower +before the timed call under test, and that call returns at its very first +line for any non-pointer `obj`, so it never reached +`generator_prototype_ptr` / `ensure_generator_intrinsics()` — it was always a +no-op. Pre-#7723 this went unnoticed because some earlier test in the same +binary had almost always already built the process-global tower, so the real +call under test found it cached regardless. + +With the towers per-test, the real call now pays the tower's own +dozens-of-allocations build inside `build_generator_tower`'s `GcSuppressScope` +(#7251's no-move window for that build), which swallows the test's injected +arena trigger for the rest of the call — no copying minor ever runs, and by +the time the window closes the following allocations no longer need a new +arena block, so the trigger is never serviced. + +Fixed `warm_generator_intrinsics()` to call +`crate::object::ensure_generator_intrinsics()` directly (the same builder +`gc::tests::lazy_intrinsic_towers` uses), so it does what its name and doc +comment always claimed. The tests' own liveness/deferral assertions are +unchanged. From 5a7a8521b42c526ecc3bc7b9dab2a74079869f64 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ralph=20K=C3=BCpper?= Date: Sun, 9 Aug 2026 21:21:11 +0200 Subject: [PATCH 3/3] chore: bump version to 0.5.1422 Claude-Session: https://claude.ai/code/session_01Y1QZ5wUP9gRSwpiweT4Wix --- CLAUDE.md | 2 +- Cargo.lock | 152 ++++++++++++++++++++++++++--------------------------- Cargo.toml | 2 +- 3 files changed, 78 insertions(+), 78 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index 58ab9bab18..77b65a7b4b 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.1421 +**Current Version:** 0.5.1422 ## TypeScript Parity Status diff --git a/Cargo.lock b/Cargo.lock index f450ec7d32..97c03c2ead 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5547,7 +5547,7 @@ checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" [[package]] name = "perry" -version = "0.5.1421" +version = "0.5.1422" dependencies = [ "anyhow", "base64", @@ -5607,14 +5607,14 @@ dependencies = [ [[package]] name = "perry-api-manifest" -version = "0.5.1421" +version = "0.5.1422" dependencies = [ "serde", ] [[package]] name = "perry-audio-miniaudio" -version = "0.5.1421" +version = "0.5.1422" dependencies = [ "cc", "libc", @@ -5622,7 +5622,7 @@ dependencies = [ [[package]] name = "perry-codegen" -version = "0.5.1421" +version = "0.5.1422" dependencies = [ "anyhow", "inkwell", @@ -5639,7 +5639,7 @@ dependencies = [ [[package]] name = "perry-codegen-arkts" -version = "0.5.1421" +version = "0.5.1422" dependencies = [ "anyhow", "perry-hir", @@ -5647,7 +5647,7 @@ dependencies = [ [[package]] name = "perry-codegen-glance" -version = "0.5.1421" +version = "0.5.1422" dependencies = [ "anyhow", "perry-hir", @@ -5655,7 +5655,7 @@ dependencies = [ [[package]] name = "perry-codegen-js" -version = "0.5.1421" +version = "0.5.1422" dependencies = [ "anyhow", "perry-dispatch", @@ -5664,7 +5664,7 @@ dependencies = [ [[package]] name = "perry-codegen-swiftui" -version = "0.5.1421" +version = "0.5.1422" dependencies = [ "anyhow", "perry-hir", @@ -5672,7 +5672,7 @@ dependencies = [ [[package]] name = "perry-codegen-wasm" -version = "0.5.1421" +version = "0.5.1422" dependencies = [ "anyhow", "base64", @@ -5684,7 +5684,7 @@ dependencies = [ [[package]] name = "perry-codegen-wear-tiles" -version = "0.5.1421" +version = "0.5.1422" dependencies = [ "anyhow", "perry-hir", @@ -5692,7 +5692,7 @@ dependencies = [ [[package]] name = "perry-container-compose" -version = "0.5.1421" +version = "0.5.1422" dependencies = [ "anyhow", "async-trait", @@ -5721,14 +5721,14 @@ dependencies = [ [[package]] name = "perry-container-e2e" -version = "0.5.1421" +version = "0.5.1422" dependencies = [ "anyhow", ] [[package]] name = "perry-diagnostics" -version = "0.5.1421" +version = "0.5.1422" dependencies = [ "serde", "serde_json", @@ -5736,7 +5736,7 @@ dependencies = [ [[package]] name = "perry-dispatch" -version = "0.5.1421" +version = "0.5.1422" [[package]] name = "perry-doc-fixture-my-bindings" @@ -5747,7 +5747,7 @@ dependencies = [ [[package]] name = "perry-doc-tests" -version = "0.5.1421" +version = "0.5.1422" dependencies = [ "anyhow", "clap", @@ -5762,7 +5762,7 @@ dependencies = [ [[package]] name = "perry-ext-ads" -version = "0.5.1421" +version = "0.5.1422" dependencies = [ "block2", "objc2", @@ -5772,7 +5772,7 @@ dependencies = [ [[package]] name = "perry-ext-argon2" -version = "0.5.1421" +version = "0.5.1422" dependencies = [ "argon2", "perry-ffi", @@ -5780,7 +5780,7 @@ dependencies = [ [[package]] name = "perry-ext-axios" -version = "0.5.1421" +version = "0.5.1422" dependencies = [ "perry-ffi", "reqwest", @@ -5789,7 +5789,7 @@ dependencies = [ [[package]] name = "perry-ext-bcrypt" -version = "0.5.1421" +version = "0.5.1422" dependencies = [ "bcrypt", "perry-ffi", @@ -5797,7 +5797,7 @@ dependencies = [ [[package]] name = "perry-ext-better-sqlite3" -version = "0.5.1421" +version = "0.5.1422" dependencies = [ "perry-ffi", "rusqlite", @@ -5805,7 +5805,7 @@ dependencies = [ [[package]] name = "perry-ext-cheerio" -version = "0.5.1421" +version = "0.5.1422" dependencies = [ "perry-ffi", "scraper", @@ -5813,7 +5813,7 @@ dependencies = [ [[package]] name = "perry-ext-commander" -version = "0.5.1421" +version = "0.5.1422" dependencies = [ "perry-ffi", "perry-runtime", @@ -5821,7 +5821,7 @@ dependencies = [ [[package]] name = "perry-ext-cron" -version = "0.5.1421" +version = "0.5.1422" dependencies = [ "chrono", "cron", @@ -5831,7 +5831,7 @@ dependencies = [ [[package]] name = "perry-ext-dayjs" -version = "0.5.1421" +version = "0.5.1422" dependencies = [ "chrono", "perry-ffi", @@ -5839,7 +5839,7 @@ dependencies = [ [[package]] name = "perry-ext-decimal" -version = "0.5.1421" +version = "0.5.1422" dependencies = [ "perry-ffi", "rust_decimal", @@ -5847,7 +5847,7 @@ dependencies = [ [[package]] name = "perry-ext-dotenv" -version = "0.5.1421" +version = "0.5.1422" dependencies = [ "perry-ffi", "serde_json", @@ -5855,7 +5855,7 @@ dependencies = [ [[package]] name = "perry-ext-ethers" -version = "0.5.1421" +version = "0.5.1422" dependencies = [ "perry-ffi", "rand 0.10.1", @@ -5863,7 +5863,7 @@ dependencies = [ [[package]] name = "perry-ext-events" -version = "0.5.1421" +version = "0.5.1422" dependencies = [ "perry-ffi", "perry-runtime", @@ -5871,14 +5871,14 @@ dependencies = [ [[package]] name = "perry-ext-exponential-backoff" -version = "0.5.1421" +version = "0.5.1422" dependencies = [ "perry-ffi", ] [[package]] name = "perry-ext-fastify" -version = "0.5.1421" +version = "0.5.1422" dependencies = [ "bytes", "http-body-util", @@ -5896,7 +5896,7 @@ dependencies = [ [[package]] name = "perry-ext-fetch" -version = "0.5.1421" +version = "0.5.1422" dependencies = [ "bytes", "lazy_static", @@ -5909,7 +5909,7 @@ dependencies = [ [[package]] name = "perry-ext-http" -version = "0.5.1421" +version = "0.5.1422" dependencies = [ "bytes", "h2", @@ -5933,7 +5933,7 @@ dependencies = [ [[package]] name = "perry-ext-ioredis" -version = "0.5.1421" +version = "0.5.1422" dependencies = [ "lazy_static", "perry-ffi", @@ -5943,7 +5943,7 @@ dependencies = [ [[package]] name = "perry-ext-jsonwebtoken" -version = "0.5.1421" +version = "0.5.1422" dependencies = [ "base64", "jsonwebtoken", @@ -5954,7 +5954,7 @@ dependencies = [ [[package]] name = "perry-ext-lru-cache" -version = "0.5.1421" +version = "0.5.1422" dependencies = [ "lru", "perry-ffi", @@ -5963,7 +5963,7 @@ dependencies = [ [[package]] name = "perry-ext-moment" -version = "0.5.1421" +version = "0.5.1422" dependencies = [ "chrono", "perry-ffi", @@ -5971,7 +5971,7 @@ dependencies = [ [[package]] name = "perry-ext-mongodb" -version = "0.5.1421" +version = "0.5.1422" dependencies = [ "bson", "futures-util", @@ -5983,7 +5983,7 @@ dependencies = [ [[package]] name = "perry-ext-mysql2" -version = "0.5.1421" +version = "0.5.1422" dependencies = [ "chrono", "perry-ffi", @@ -5993,7 +5993,7 @@ dependencies = [ [[package]] name = "perry-ext-nanoid" -version = "0.5.1421" +version = "0.5.1422" dependencies = [ "nanoid", "perry-ffi", @@ -6002,7 +6002,7 @@ dependencies = [ [[package]] name = "perry-ext-net" -version = "0.5.1421" +version = "0.5.1422" dependencies = [ "bytes", "perry-ffi", @@ -6015,7 +6015,7 @@ dependencies = [ [[package]] name = "perry-ext-node-forge" -version = "0.5.1421" +version = "0.5.1422" dependencies = [ "const-oid 0.9.6", "der 0.7.10", @@ -6034,7 +6034,7 @@ dependencies = [ [[package]] name = "perry-ext-nodemailer" -version = "0.5.1421" +version = "0.5.1422" dependencies = [ "lettre", "perry-ffi", @@ -6044,7 +6044,7 @@ dependencies = [ [[package]] name = "perry-ext-pdf" -version = "0.5.1421" +version = "0.5.1422" dependencies = [ "perry-ffi", "printpdf", @@ -6052,7 +6052,7 @@ dependencies = [ [[package]] name = "perry-ext-pg" -version = "0.5.1421" +version = "0.5.1422" dependencies = [ "perry-ffi", "sqlx", @@ -6061,7 +6061,7 @@ dependencies = [ [[package]] name = "perry-ext-ratelimit" -version = "0.5.1421" +version = "0.5.1422" dependencies = [ "governor", "perry-ffi", @@ -6069,7 +6069,7 @@ dependencies = [ [[package]] name = "perry-ext-sharp" -version = "0.5.1421" +version = "0.5.1422" dependencies = [ "fast_image_resize", "image", @@ -6079,14 +6079,14 @@ dependencies = [ [[package]] name = "perry-ext-slugify" -version = "0.5.1421" +version = "0.5.1422" dependencies = [ "perry-ffi", ] [[package]] name = "perry-ext-streams" -version = "0.5.1421" +version = "0.5.1422" dependencies = [ "lazy_static", "perry-ffi", @@ -6095,7 +6095,7 @@ dependencies = [ [[package]] name = "perry-ext-undici" -version = "0.5.1421" +version = "0.5.1422" dependencies = [ "perry-ffi", "perry-runtime", @@ -6104,7 +6104,7 @@ dependencies = [ [[package]] name = "perry-ext-uuid" -version = "0.5.1421" +version = "0.5.1422" dependencies = [ "perry-ffi", "uuid", @@ -6112,7 +6112,7 @@ dependencies = [ [[package]] name = "perry-ext-validator" -version = "0.5.1421" +version = "0.5.1422" dependencies = [ "perry-ffi", "regex", @@ -6122,7 +6122,7 @@ dependencies = [ [[package]] name = "perry-ext-ws" -version = "0.5.1421" +version = "0.5.1422" dependencies = [ "futures-util", "lazy_static", @@ -6135,7 +6135,7 @@ dependencies = [ [[package]] name = "perry-ext-zlib" -version = "0.5.1421" +version = "0.5.1422" dependencies = [ "brotli", "flate2", @@ -6145,7 +6145,7 @@ dependencies = [ [[package]] name = "perry-ffi" -version = "0.5.1421" +version = "0.5.1422" dependencies = [ "dashmap", "once_cell", @@ -6154,7 +6154,7 @@ dependencies = [ [[package]] name = "perry-hir" -version = "0.5.1421" +version = "0.5.1422" dependencies = [ "anyhow", "perry-api-manifest", @@ -6172,7 +6172,7 @@ dependencies = [ [[package]] name = "perry-parser" -version = "0.5.1421" +version = "0.5.1422" dependencies = [ "anyhow", "perry-diagnostics", @@ -6184,7 +6184,7 @@ dependencies = [ [[package]] name = "perry-runtime" -version = "0.5.1421" +version = "0.5.1422" dependencies = [ "anyhow", "base64", @@ -6226,14 +6226,14 @@ dependencies = [ [[package]] name = "perry-runtime-static" -version = "0.5.1421" +version = "0.5.1422" dependencies = [ "perry-runtime", ] [[package]] name = "perry-stdlib" -version = "0.5.1421" +version = "0.5.1422" dependencies = [ "aes 0.8.4", "aes 0.9.1", @@ -6328,14 +6328,14 @@ dependencies = [ [[package]] name = "perry-stdlib-static" -version = "0.5.1421" +version = "0.5.1422" dependencies = [ "perry-stdlib", ] [[package]] name = "perry-transform" -version = "0.5.1421" +version = "0.5.1422" dependencies = [ "anyhow", "perry-hir", @@ -6344,14 +6344,14 @@ dependencies = [ [[package]] name = "perry-ui" -version = "0.5.1421" +version = "0.5.1422" dependencies = [ "perry-ui-model", ] [[package]] name = "perry-ui-android" -version = "0.5.1421" +version = "0.5.1422" dependencies = [ "base64", "itoa", @@ -6368,7 +6368,7 @@ dependencies = [ [[package]] name = "perry-ui-geisterhand" -version = "0.5.1421" +version = "0.5.1422" dependencies = [ "rand 0.10.1", "serde", @@ -6378,7 +6378,7 @@ dependencies = [ [[package]] name = "perry-ui-gtk4" -version = "0.5.1421" +version = "0.5.1422" dependencies = [ "base64", "cairo-rs 0.22.0", @@ -6401,7 +6401,7 @@ dependencies = [ [[package]] name = "perry-ui-ios" -version = "0.5.1421" +version = "0.5.1422" dependencies = [ "base64", "block2", @@ -6417,7 +6417,7 @@ dependencies = [ [[package]] name = "perry-ui-macos" -version = "0.5.1421" +version = "0.5.1422" dependencies = [ "base64", "block2", @@ -6432,7 +6432,7 @@ dependencies = [ [[package]] name = "perry-ui-model" -version = "0.5.1421" +version = "0.5.1422" [[package]] name = "perry-ui-test" @@ -6443,11 +6443,11 @@ dependencies = [ [[package]] name = "perry-ui-testkit" -version = "0.5.1421" +version = "0.5.1422" [[package]] name = "perry-ui-tvos" -version = "0.5.1421" +version = "0.5.1422" dependencies = [ "base64", "block2", @@ -6463,7 +6463,7 @@ dependencies = [ [[package]] name = "perry-ui-visionos" -version = "0.5.1421" +version = "0.5.1422" dependencies = [ "base64", "block2", @@ -6479,7 +6479,7 @@ dependencies = [ [[package]] name = "perry-ui-watchos" -version = "0.5.1421" +version = "0.5.1422" dependencies = [ "block2", "libc", @@ -6492,7 +6492,7 @@ dependencies = [ [[package]] name = "perry-ui-windows" -version = "0.5.1421" +version = "0.5.1422" dependencies = [ "base64", "libc", @@ -6509,14 +6509,14 @@ dependencies = [ [[package]] name = "perry-ui-windows-winui" -version = "0.5.1421" +version = "0.5.1422" dependencies = [ "perry-ui-windows", ] [[package]] name = "perry-updater" -version = "0.5.1421" +version = "0.5.1422" dependencies = [ "anyhow", "base64", @@ -6532,7 +6532,7 @@ dependencies = [ [[package]] name = "perry-wasm-host" -version = "0.5.1421" +version = "0.5.1422" dependencies = [ "wasmi", ] diff --git a/Cargo.toml b/Cargo.toml index db8820c978..002f5cb6dd 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -315,7 +315,7 @@ codegen-units = 16 codegen-units = 16 [workspace.package] -version = "0.5.1421" +version = "0.5.1422" edition = "2021" license = "MIT" repository = "https://github.com/PerryTS/perry"