From 6728876703b7009e948f816787a4423228fc9ec5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ralph=20K=C3=BCpper?= Date: Wed, 3 Jun 2026 19:23:43 +0200 Subject: [PATCH] fix(typedarray): Uint8Array.of/from build the buffer-backed representation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Uint8Array.of/from produced garbage bytes (#871 part-2 regression). Perry backs Uint8Array with a BufferHeader (js_uint8array_*), and new Uint8Array([...]) uses it, but Uint8ArrayFrom codegen built a generic TypedArrayHeader (kind=1) via js_typed_array_new_from_array — the wrong representation, so u[i] read garbage. - instance_misc1.rs: Uint8ArrayFrom -> js_uint8array_from_array(arr) - typedarray.rs: js_typed_array_new_from_array reads source via js_array_get_f64 (canonical accessor) for the remaining non-Uint8 .of/.from kinds (Int8Array etc). Verified Uint8Array.of/from + test_issue_871 + Int8Array.of match node. Bumps 0.5.1115. --- CHANGELOG.md | 25 +++ CLAUDE.md | 2 +- Cargo.lock | 144 +++++++++--------- Cargo.toml | 2 +- .../perry-codegen/src/expr/instance_misc1.rs | 15 +- crates/perry-runtime/src/typedarray.rs | 12 +- 6 files changed, 117 insertions(+), 83 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0b0486fec4..77888b5109 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,31 @@ Detailed changelog for Perry. See CLAUDE.md for concise summaries. +## v0.5.1115 — fix(typedarray): Uint8Array.of/from build the buffer-backed representation + +`Uint8Array.of(...)` / `Uint8Array.from(...)` produced garbage bytes +(`Uint8Array.of(1,2,3)[0]` read back as a denormal like `1.27e-321`, issue +#871 part-2 regression). Perry represents `Uint8Array` as a **buffer** +(`BufferHeader`, via `js_uint8array_new` / `js_uint8array_from_array` in +`buffer/from.rs`); `new Uint8Array([...])` already builds that form. But the +`Uint8ArrayFrom` codegen lowered `.of/.from` to +`js_typed_array_new_from_array(kind=1, …)`, which builds the *generic* +`TypedArrayHeader` representation instead. Element reads (`u[i]`) then routed +through the buffer path and mis-read the `TypedArrayHeader` as a plain array, +yielding uninitialized memory. + +Fixes: +- `perry-codegen/src/expr/instance_misc1.rs`: `Uint8ArrayFrom` now calls + `js_uint8array_from_array(arr)` (buffer-backed, matching `new Uint8Array`) + instead of `js_typed_array_new_from_array(1, arr)`, keeping the + representation consistent so `u[i]` reads correctly. +- `perry-runtime/src/typedarray.rs`: `js_typed_array_new_from_array` (still + used by non-`Uint8Array` `.of/.from`, e.g. `Int8Array.of`) now reads source + elements through the canonical `js_array_get_f64` accessor rather than a raw + inline-`f64` read, so it correctly materializes cloned/lazy source arrays + (verified `Int8Array.of(5,6,7)` matches Node). + + ## v0.5.1114 — test(parity): specify expected exit code for three intentional-throw expected-output tests `run_parity_tests.sh` compares an expected-output test's process exit code diff --git a/CLAUDE.md b/CLAUDE.md index b9692d68c8..a48238f1fb 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.1114 +**Current Version:** 0.5.1115 ## TypeScript Parity Status diff --git a/Cargo.lock b/Cargo.lock index 40943432c2..0e73a1c058 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5022,7 +5022,7 @@ checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" [[package]] name = "perry" -version = "0.5.1114" +version = "0.5.1115" dependencies = [ "anyhow", "base64", @@ -5077,14 +5077,14 @@ dependencies = [ [[package]] name = "perry-api-manifest" -version = "0.5.1114" +version = "0.5.1115" dependencies = [ "serde", ] [[package]] name = "perry-audio-miniaudio" -version = "0.5.1114" +version = "0.5.1115" dependencies = [ "cc", "libc", @@ -5092,7 +5092,7 @@ dependencies = [ [[package]] name = "perry-codegen" -version = "0.5.1114" +version = "0.5.1115" dependencies = [ "anyhow", "log", @@ -5107,7 +5107,7 @@ dependencies = [ [[package]] name = "perry-codegen-arkts" -version = "0.5.1114" +version = "0.5.1115" dependencies = [ "anyhow", "perry-hir", @@ -5116,7 +5116,7 @@ dependencies = [ [[package]] name = "perry-codegen-glance" -version = "0.5.1114" +version = "0.5.1115" dependencies = [ "anyhow", "perry-hir", @@ -5124,7 +5124,7 @@ dependencies = [ [[package]] name = "perry-codegen-js" -version = "0.5.1114" +version = "0.5.1115" dependencies = [ "anyhow", "perry-dispatch", @@ -5134,7 +5134,7 @@ dependencies = [ [[package]] name = "perry-codegen-swiftui" -version = "0.5.1114" +version = "0.5.1115" dependencies = [ "anyhow", "perry-hir", @@ -5143,7 +5143,7 @@ dependencies = [ [[package]] name = "perry-codegen-wasm" -version = "0.5.1114" +version = "0.5.1115" dependencies = [ "anyhow", "base64", @@ -5156,7 +5156,7 @@ dependencies = [ [[package]] name = "perry-codegen-wear-tiles" -version = "0.5.1114" +version = "0.5.1115" dependencies = [ "anyhow", "perry-hir", @@ -5164,7 +5164,7 @@ dependencies = [ [[package]] name = "perry-container-compose" -version = "0.5.1114" +version = "0.5.1115" dependencies = [ "anyhow", "async-trait", @@ -5193,7 +5193,7 @@ dependencies = [ [[package]] name = "perry-diagnostics" -version = "0.5.1114" +version = "0.5.1115" dependencies = [ "serde", "serde_json", @@ -5201,7 +5201,7 @@ dependencies = [ [[package]] name = "perry-dispatch" -version = "0.5.1114" +version = "0.5.1115" [[package]] name = "perry-doc-fixture-my-bindings" @@ -5212,7 +5212,7 @@ dependencies = [ [[package]] name = "perry-doc-tests" -version = "0.5.1114" +version = "0.5.1115" dependencies = [ "anyhow", "clap", @@ -5227,14 +5227,14 @@ dependencies = [ [[package]] name = "perry-ext-ads" -version = "0.5.1114" +version = "0.5.1115" dependencies = [ "perry-ffi", ] [[package]] name = "perry-ext-argon2" -version = "0.5.1114" +version = "0.5.1115" dependencies = [ "argon2", "perry-ffi", @@ -5242,7 +5242,7 @@ dependencies = [ [[package]] name = "perry-ext-axios" -version = "0.5.1114" +version = "0.5.1115" dependencies = [ "perry-ffi", "reqwest", @@ -5251,7 +5251,7 @@ dependencies = [ [[package]] name = "perry-ext-bcrypt" -version = "0.5.1114" +version = "0.5.1115" dependencies = [ "bcrypt", "perry-ffi", @@ -5259,7 +5259,7 @@ dependencies = [ [[package]] name = "perry-ext-better-sqlite3" -version = "0.5.1114" +version = "0.5.1115" dependencies = [ "perry-ffi", "rusqlite", @@ -5267,7 +5267,7 @@ dependencies = [ [[package]] name = "perry-ext-cheerio" -version = "0.5.1114" +version = "0.5.1115" dependencies = [ "perry-ffi", "scraper", @@ -5275,7 +5275,7 @@ dependencies = [ [[package]] name = "perry-ext-commander" -version = "0.5.1114" +version = "0.5.1115" dependencies = [ "perry-ffi", "perry-runtime", @@ -5283,7 +5283,7 @@ dependencies = [ [[package]] name = "perry-ext-cron" -version = "0.5.1114" +version = "0.5.1115" dependencies = [ "chrono", "cron", @@ -5293,7 +5293,7 @@ dependencies = [ [[package]] name = "perry-ext-dayjs" -version = "0.5.1114" +version = "0.5.1115" dependencies = [ "chrono", "perry-ffi", @@ -5301,7 +5301,7 @@ dependencies = [ [[package]] name = "perry-ext-decimal" -version = "0.5.1114" +version = "0.5.1115" dependencies = [ "perry-ffi", "rust_decimal", @@ -5309,7 +5309,7 @@ dependencies = [ [[package]] name = "perry-ext-dotenv" -version = "0.5.1114" +version = "0.5.1115" dependencies = [ "perry-ffi", "serde_json", @@ -5317,7 +5317,7 @@ dependencies = [ [[package]] name = "perry-ext-ethers" -version = "0.5.1114" +version = "0.5.1115" dependencies = [ "perry-ffi", "rand 0.8.6", @@ -5325,7 +5325,7 @@ dependencies = [ [[package]] name = "perry-ext-events" -version = "0.5.1114" +version = "0.5.1115" dependencies = [ "perry-ffi", "perry-runtime", @@ -5333,14 +5333,14 @@ dependencies = [ [[package]] name = "perry-ext-exponential-backoff" -version = "0.5.1114" +version = "0.5.1115" dependencies = [ "perry-ffi", ] [[package]] name = "perry-ext-fastify" -version = "0.5.1114" +version = "0.5.1115" dependencies = [ "bytes", "http-body-util", @@ -5357,7 +5357,7 @@ dependencies = [ [[package]] name = "perry-ext-fetch" -version = "0.5.1114" +version = "0.5.1115" dependencies = [ "lazy_static", "perry-ffi", @@ -5369,7 +5369,7 @@ dependencies = [ [[package]] name = "perry-ext-http" -version = "0.5.1114" +version = "0.5.1115" dependencies = [ "lazy_static", "perry-ext-http-server", @@ -5382,7 +5382,7 @@ dependencies = [ [[package]] name = "perry-ext-http-server" -version = "0.5.1114" +version = "0.5.1115" dependencies = [ "bytes", "h2", @@ -5403,7 +5403,7 @@ dependencies = [ [[package]] name = "perry-ext-ioredis" -version = "0.5.1114" +version = "0.5.1115" dependencies = [ "lazy_static", "perry-ffi", @@ -5413,7 +5413,7 @@ dependencies = [ [[package]] name = "perry-ext-jsonwebtoken" -version = "0.5.1114" +version = "0.5.1115" dependencies = [ "base64", "jsonwebtoken", @@ -5424,7 +5424,7 @@ dependencies = [ [[package]] name = "perry-ext-lru-cache" -version = "0.5.1114" +version = "0.5.1115" dependencies = [ "lru", "perry-ffi", @@ -5432,7 +5432,7 @@ dependencies = [ [[package]] name = "perry-ext-moment" -version = "0.5.1114" +version = "0.5.1115" dependencies = [ "chrono", "perry-ffi", @@ -5440,7 +5440,7 @@ dependencies = [ [[package]] name = "perry-ext-mongodb" -version = "0.5.1114" +version = "0.5.1115" dependencies = [ "bson", "futures-util", @@ -5452,7 +5452,7 @@ dependencies = [ [[package]] name = "perry-ext-mysql2" -version = "0.5.1114" +version = "0.5.1115" dependencies = [ "chrono", "perry-ffi", @@ -5462,7 +5462,7 @@ dependencies = [ [[package]] name = "perry-ext-nanoid" -version = "0.5.1114" +version = "0.5.1115" dependencies = [ "nanoid", "perry-ffi", @@ -5471,7 +5471,7 @@ dependencies = [ [[package]] name = "perry-ext-net" -version = "0.5.1114" +version = "0.5.1115" dependencies = [ "perry-ffi", "perry-runtime", @@ -5483,7 +5483,7 @@ dependencies = [ [[package]] name = "perry-ext-nodemailer" -version = "0.5.1114" +version = "0.5.1115" dependencies = [ "lettre", "perry-ffi", @@ -5493,7 +5493,7 @@ dependencies = [ [[package]] name = "perry-ext-pdf" -version = "0.5.1114" +version = "0.5.1115" dependencies = [ "perry-ffi", "printpdf", @@ -5501,7 +5501,7 @@ dependencies = [ [[package]] name = "perry-ext-pg" -version = "0.5.1114" +version = "0.5.1115" dependencies = [ "perry-ffi", "sqlx", @@ -5510,7 +5510,7 @@ dependencies = [ [[package]] name = "perry-ext-ratelimit" -version = "0.5.1114" +version = "0.5.1115" dependencies = [ "governor", "perry-ffi", @@ -5518,7 +5518,7 @@ dependencies = [ [[package]] name = "perry-ext-sharp" -version = "0.5.1114" +version = "0.5.1115" dependencies = [ "base64", "image", @@ -5527,14 +5527,14 @@ dependencies = [ [[package]] name = "perry-ext-slugify" -version = "0.5.1114" +version = "0.5.1115" dependencies = [ "perry-ffi", ] [[package]] name = "perry-ext-streams" -version = "0.5.1114" +version = "0.5.1115" dependencies = [ "lazy_static", "perry-ffi", @@ -5543,7 +5543,7 @@ dependencies = [ [[package]] name = "perry-ext-uuid" -version = "0.5.1114" +version = "0.5.1115" dependencies = [ "perry-ffi", "uuid", @@ -5551,7 +5551,7 @@ dependencies = [ [[package]] name = "perry-ext-validator" -version = "0.5.1114" +version = "0.5.1115" dependencies = [ "perry-ffi", "regex", @@ -5561,7 +5561,7 @@ dependencies = [ [[package]] name = "perry-ext-ws" -version = "0.5.1114" +version = "0.5.1115" dependencies = [ "futures-util", "lazy_static", @@ -5573,7 +5573,7 @@ dependencies = [ [[package]] name = "perry-ext-zlib" -version = "0.5.1114" +version = "0.5.1115" dependencies = [ "brotli", "flate2", @@ -5582,7 +5582,7 @@ dependencies = [ [[package]] name = "perry-ffi" -version = "0.5.1114" +version = "0.5.1115" dependencies = [ "dashmap 6.2.1", "once_cell", @@ -5591,7 +5591,7 @@ dependencies = [ [[package]] name = "perry-hir" -version = "0.5.1114" +version = "0.5.1115" dependencies = [ "anyhow", "perry-api-manifest", @@ -5608,7 +5608,7 @@ dependencies = [ [[package]] name = "perry-parser" -version = "0.5.1114" +version = "0.5.1115" dependencies = [ "anyhow", "perry-diagnostics", @@ -5620,7 +5620,7 @@ dependencies = [ [[package]] name = "perry-runtime" -version = "0.5.1114" +version = "0.5.1115" dependencies = [ "anyhow", "base64", @@ -5647,7 +5647,7 @@ dependencies = [ [[package]] name = "perry-stdlib" -version = "0.5.1114" +version = "0.5.1115" dependencies = [ "aes 0.8.4", "aes-gcm", @@ -5731,7 +5731,7 @@ dependencies = [ [[package]] name = "perry-transform" -version = "0.5.1114" +version = "0.5.1115" dependencies = [ "anyhow", "perry-hir", @@ -5741,7 +5741,7 @@ dependencies = [ [[package]] name = "perry-types" -version = "0.5.1114" +version = "0.5.1115" dependencies = [ "anyhow", "thiserror 1.0.69", @@ -5749,14 +5749,14 @@ dependencies = [ [[package]] name = "perry-ui" -version = "0.5.1114" +version = "0.5.1115" dependencies = [ "perry-ui-model", ] [[package]] name = "perry-ui-android" -version = "0.5.1114" +version = "0.5.1115" dependencies = [ "base64", "itoa", @@ -5773,7 +5773,7 @@ dependencies = [ [[package]] name = "perry-ui-geisterhand" -version = "0.5.1114" +version = "0.5.1115" dependencies = [ "rand 0.8.6", "serde", @@ -5783,7 +5783,7 @@ dependencies = [ [[package]] name = "perry-ui-gtk4" -version = "0.5.1114" +version = "0.5.1115" dependencies = [ "base64", "cairo-rs", @@ -5806,7 +5806,7 @@ dependencies = [ [[package]] name = "perry-ui-ios" -version = "0.5.1114" +version = "0.5.1115" dependencies = [ "base64", "block2", @@ -5822,7 +5822,7 @@ dependencies = [ [[package]] name = "perry-ui-macos" -version = "0.5.1114" +version = "0.5.1115" dependencies = [ "base64", "block2", @@ -5837,7 +5837,7 @@ dependencies = [ [[package]] name = "perry-ui-model" -version = "0.5.1114" +version = "0.5.1115" [[package]] name = "perry-ui-test" @@ -5845,11 +5845,11 @@ version = "0.1.0" [[package]] name = "perry-ui-testkit" -version = "0.5.1114" +version = "0.5.1115" [[package]] name = "perry-ui-tvos" -version = "0.5.1114" +version = "0.5.1115" dependencies = [ "base64", "block2", @@ -5865,7 +5865,7 @@ dependencies = [ [[package]] name = "perry-ui-visionos" -version = "0.5.1114" +version = "0.5.1115" dependencies = [ "base64", "block2", @@ -5881,7 +5881,7 @@ dependencies = [ [[package]] name = "perry-ui-watchos" -version = "0.5.1114" +version = "0.5.1115" dependencies = [ "block2", "libc", @@ -5894,7 +5894,7 @@ dependencies = [ [[package]] name = "perry-ui-windows" -version = "0.5.1114" +version = "0.5.1115" dependencies = [ "base64", "libc", @@ -5910,7 +5910,7 @@ dependencies = [ [[package]] name = "perry-updater" -version = "0.5.1114" +version = "0.5.1115" dependencies = [ "base64", "ed25519-dalek", @@ -5924,7 +5924,7 @@ dependencies = [ [[package]] name = "perry-wasm-host" -version = "0.5.1114" +version = "0.5.1115" dependencies = [ "wasmi", ] diff --git a/Cargo.toml b/Cargo.toml index 76dbb857a8..cb06874053 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -193,7 +193,7 @@ codegen-units = 16 opt-level = "s" # Optimize for size in stdlib [workspace.package] -version = "0.5.1114" +version = "0.5.1115" edition = "2021" license = "MIT" repository = "https://github.com/PerryTS/perry" diff --git a/crates/perry-codegen/src/expr/instance_misc1.rs b/crates/perry-codegen/src/expr/instance_misc1.rs index 875ddb19ab..064bbee02b 100644 --- a/crates/perry-codegen/src/expr/instance_misc1.rs +++ b/crates/perry-codegen/src/expr/instance_misc1.rs @@ -719,11 +719,16 @@ pub(crate) fn lower(ctx: &mut FnCtx<'_>, expr: &Expr) -> Result { let iter_box = lower_expr(ctx, iter)?; let blk = ctx.block(); let arr = blk.call(I64, "js_array_from_value", &[(DOUBLE, &iter_box)]); - let ta = blk.call( - I64, - "js_typed_array_new_from_array", - &[(I32, "1"), (I64, &arr)], - ); + // Perry represents `Uint8Array` as a buffer-backed object + // (`BufferHeader`, see buffer/from.rs), NOT the generic + // `TypedArrayHeader` kind-1 produced by + // `js_typed_array_new_from_array`. `new Uint8Array([...])` already + // builds the buffer form; routing `Uint8Array.of/from` through the + // same `js_uint8array_from_array` keeps the representation + // consistent so element reads (`u[i]`) go through the registered + // buffer path instead of mis-reading a TypedArrayHeader as a plain + // array (issue #871: of/from produced garbage bytes). + let ta = blk.call(I64, "js_uint8array_from_array", &[(I64, &arr)]); Ok(nanbox_pointer_inline(blk, &ta)) } diff --git a/crates/perry-runtime/src/typedarray.rs b/crates/perry-runtime/src/typedarray.rs index d90233c334..c7c106af41 100644 --- a/crates/perry-runtime/src/typedarray.rs +++ b/crates/perry-runtime/src/typedarray.rs @@ -817,11 +817,15 @@ pub extern "C" fn js_typed_array_new_from_array( } unsafe { let len = (*arr).length; + // Snapshot source values via the canonical accessor BEFORE allocating: + // `typed_array_alloc` may GC and free/move an unrooted cloned source + // (`.of/.from` path), and the raw inline read mis-read it (#871). + let vals: Vec = (0..len) + .map(|i| jsvalue_to_f64(crate::array::js_array_get_f64(arr, i))) + .collect(); let ta = typed_array_alloc(kind, len); - let arr_data = (arr as *const u8).add(std::mem::size_of::()) as *const f64; - for i in 0..len as usize { - let raw = *arr_data.add(i); - store_at(ta, i, jsvalue_to_f64(raw)); + for (i, v) in vals.iter().enumerate() { + store_at(ta, i, *v); } ta }