From 37e22f07ceb7a0a9d5f9fec7710870294d7b14bf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ralph=20K=C3=BCpper?= Date: Sun, 14 Jun 2026 21:33:22 +0200 Subject: [PATCH] fix(dayjs): arguments in seq exprs + computed Date-method dispatch (#5133) - expr_uses_arguments descends into Seq/Await/Yield/OptChain/computed SuperProp/TaggedTpl so synthetic-arguments pre-scan catches minified dayjs wrappers (return n.date=t, n.args=arguments, ...). - Route any method on a date receiver through Date.prototype (not just toString) so computed/dynamic calls (date[m](...)) no longer drop setter mutations. v0.5.1171 --- CHANGELOG.md | 10 ++ CLAUDE.md | 2 +- Cargo.lock | 148 +++++++++--------- Cargo.toml | 2 +- crates/perry-hir/src/lower_decl/helpers.rs | 22 +++ .../src/object/native_call_method.rs | 18 ++- 6 files changed, 123 insertions(+), 79 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0fddc074a8..8aa90fb53c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,13 @@ +## v0.5.1171 — fix(dayjs): `arguments` in sequence exprs + computed Date-method dispatch (#5133) + +`compilePackages: dayjs` threw `ReferenceError: arguments is not defined`, and once that was resolved `.add()`/`.date(n)` silently no-op'd. Two distinct, general root causes: + +1. **`arguments` hidden inside a sequence expression.** Minified dayjs builds the wrapper as `O=function(t,e){...return n.date=t,n.args=arguments,new _(n)}` — the `arguments` reference lives inside a comma/sequence expression in the `return`. The synthetic-arguments pre-scan `expr_uses_arguments` (`crates/perry-hir/src/lower_decl/helpers.rs`) had no `Expr::Seq` arm, fell through its `_ => false` catch-all, so the enclosing function never synthesized its hidden raw-`arguments` param. Fix: descend into `Expr::Seq`, plus the other operand-bearing forms the catch-all skipped — `Await`, `Yield`, `OptChain`, computed `SuperProp`, `TaggedTpl`. + +2. **Computed/dynamic Date-method calls dropped to generic dispatch.** dayjs mutates dates via `this.$d[l]($)` (e.g. `date["setDate"](44)`). A `DateCell` is a NaN-boxed pointer but not an `ObjectHeader`; `js_native_call_method` special-cased only `toString` for date receivers, so every other computed/dynamic call fell through to generic object dispatch, returned `[object Object]` and dropped the mutation. Fix: route any method on a date receiver through `Date.prototype` (where all getter/setter/`toISOString`/`toJSON` thunks are installed and read `IMPLICIT_THIS`), with `this` bound to the cell. + +Known residual: a SIGSEGV in dayjs's month arithmetic (`.endOf('month')` / `.set('month', …)` — a number-as-pointer deref in `js_object_get_field_by_name`, unrelated to either fix here) is left for a follow-up. + ## v0.5.1170 — fix(codegen): register rest/`arguments` metadata for renamed-export value wrappers (#5134) `.apply()`/`.call()` on a cross-module **default**-exported (or otherwise diff --git a/CLAUDE.md b/CLAUDE.md index 8d8472ff55..d711039d3e 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.1170 +**Current Version:** 0.5.1171 ## TypeScript Parity Status diff --git a/Cargo.lock b/Cargo.lock index 909d6c3803..5fd22c4abf 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5283,7 +5283,7 @@ checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" [[package]] name = "perry" -version = "0.5.1168" +version = "0.5.1171" dependencies = [ "anyhow", "base64", @@ -5339,14 +5339,14 @@ dependencies = [ [[package]] name = "perry-api-manifest" -version = "0.5.1168" +version = "0.5.1171" dependencies = [ "serde", ] [[package]] name = "perry-audio-miniaudio" -version = "0.5.1168" +version = "0.5.1171" dependencies = [ "cc", "libc", @@ -5354,7 +5354,7 @@ dependencies = [ [[package]] name = "perry-codegen" -version = "0.5.1168" +version = "0.5.1171" dependencies = [ "anyhow", "log", @@ -5369,7 +5369,7 @@ dependencies = [ [[package]] name = "perry-codegen-arkts" -version = "0.5.1168" +version = "0.5.1171" dependencies = [ "anyhow", "perry-hir", @@ -5378,7 +5378,7 @@ dependencies = [ [[package]] name = "perry-codegen-glance" -version = "0.5.1168" +version = "0.5.1171" dependencies = [ "anyhow", "perry-hir", @@ -5386,7 +5386,7 @@ dependencies = [ [[package]] name = "perry-codegen-js" -version = "0.5.1168" +version = "0.5.1171" dependencies = [ "anyhow", "perry-dispatch", @@ -5396,7 +5396,7 @@ dependencies = [ [[package]] name = "perry-codegen-swiftui" -version = "0.5.1168" +version = "0.5.1171" dependencies = [ "anyhow", "perry-hir", @@ -5405,7 +5405,7 @@ dependencies = [ [[package]] name = "perry-codegen-wasm" -version = "0.5.1168" +version = "0.5.1171" dependencies = [ "anyhow", "base64", @@ -5418,7 +5418,7 @@ dependencies = [ [[package]] name = "perry-codegen-wear-tiles" -version = "0.5.1168" +version = "0.5.1171" dependencies = [ "anyhow", "perry-hir", @@ -5426,7 +5426,7 @@ dependencies = [ [[package]] name = "perry-container-compose" -version = "0.5.1168" +version = "0.5.1171" dependencies = [ "anyhow", "async-trait", @@ -5455,14 +5455,14 @@ dependencies = [ [[package]] name = "perry-container-e2e" -version = "0.5.1168" +version = "0.5.1171" dependencies = [ "anyhow", ] [[package]] name = "perry-diagnostics" -version = "0.5.1168" +version = "0.5.1171" dependencies = [ "serde", "serde_json", @@ -5470,7 +5470,7 @@ dependencies = [ [[package]] name = "perry-dispatch" -version = "0.5.1168" +version = "0.5.1171" [[package]] name = "perry-doc-fixture-my-bindings" @@ -5481,7 +5481,7 @@ dependencies = [ [[package]] name = "perry-doc-tests" -version = "0.5.1168" +version = "0.5.1171" dependencies = [ "anyhow", "clap", @@ -5496,14 +5496,14 @@ dependencies = [ [[package]] name = "perry-ext-ads" -version = "0.5.1168" +version = "0.5.1171" dependencies = [ "perry-ffi", ] [[package]] name = "perry-ext-argon2" -version = "0.5.1168" +version = "0.5.1171" dependencies = [ "argon2", "perry-ffi", @@ -5511,7 +5511,7 @@ dependencies = [ [[package]] name = "perry-ext-axios" -version = "0.5.1168" +version = "0.5.1171" dependencies = [ "perry-ffi", "reqwest", @@ -5520,7 +5520,7 @@ dependencies = [ [[package]] name = "perry-ext-bcrypt" -version = "0.5.1168" +version = "0.5.1171" dependencies = [ "bcrypt", "perry-ffi", @@ -5528,7 +5528,7 @@ dependencies = [ [[package]] name = "perry-ext-better-sqlite3" -version = "0.5.1168" +version = "0.5.1171" dependencies = [ "perry-ffi", "rusqlite", @@ -5536,7 +5536,7 @@ dependencies = [ [[package]] name = "perry-ext-cheerio" -version = "0.5.1168" +version = "0.5.1171" dependencies = [ "perry-ffi", "scraper", @@ -5544,7 +5544,7 @@ dependencies = [ [[package]] name = "perry-ext-commander" -version = "0.5.1168" +version = "0.5.1171" dependencies = [ "perry-ffi", "perry-runtime", @@ -5552,7 +5552,7 @@ dependencies = [ [[package]] name = "perry-ext-cron" -version = "0.5.1168" +version = "0.5.1171" dependencies = [ "chrono", "cron 0.16.0", @@ -5562,7 +5562,7 @@ dependencies = [ [[package]] name = "perry-ext-dayjs" -version = "0.5.1168" +version = "0.5.1171" dependencies = [ "chrono", "perry-ffi", @@ -5570,7 +5570,7 @@ dependencies = [ [[package]] name = "perry-ext-decimal" -version = "0.5.1168" +version = "0.5.1171" dependencies = [ "perry-ffi", "rust_decimal", @@ -5578,7 +5578,7 @@ dependencies = [ [[package]] name = "perry-ext-dotenv" -version = "0.5.1168" +version = "0.5.1171" dependencies = [ "perry-ffi", "serde_json", @@ -5586,7 +5586,7 @@ dependencies = [ [[package]] name = "perry-ext-ethers" -version = "0.5.1168" +version = "0.5.1171" dependencies = [ "perry-ffi", "rand 0.8.6", @@ -5594,7 +5594,7 @@ dependencies = [ [[package]] name = "perry-ext-events" -version = "0.5.1168" +version = "0.5.1171" dependencies = [ "perry-ffi", "perry-runtime", @@ -5602,14 +5602,14 @@ dependencies = [ [[package]] name = "perry-ext-exponential-backoff" -version = "0.5.1168" +version = "0.5.1171" dependencies = [ "perry-ffi", ] [[package]] name = "perry-ext-fastify" -version = "0.5.1168" +version = "0.5.1171" dependencies = [ "bytes", "http-body-util", @@ -5626,7 +5626,7 @@ dependencies = [ [[package]] name = "perry-ext-fetch" -version = "0.5.1168" +version = "0.5.1171" dependencies = [ "lazy_static", "perry-ffi", @@ -5638,7 +5638,7 @@ dependencies = [ [[package]] name = "perry-ext-http" -version = "0.5.1168" +version = "0.5.1171" dependencies = [ "lazy_static", "perry-ext-http-server", @@ -5651,7 +5651,7 @@ dependencies = [ [[package]] name = "perry-ext-http-server" -version = "0.5.1168" +version = "0.5.1171" dependencies = [ "bytes", "h2", @@ -5674,7 +5674,7 @@ dependencies = [ [[package]] name = "perry-ext-ioredis" -version = "0.5.1168" +version = "0.5.1171" dependencies = [ "lazy_static", "perry-ffi", @@ -5684,7 +5684,7 @@ dependencies = [ [[package]] name = "perry-ext-jsonwebtoken" -version = "0.5.1168" +version = "0.5.1171" dependencies = [ "base64", "jsonwebtoken", @@ -5695,7 +5695,7 @@ dependencies = [ [[package]] name = "perry-ext-lru-cache" -version = "0.5.1168" +version = "0.5.1171" dependencies = [ "lru", "perry-ffi", @@ -5703,7 +5703,7 @@ dependencies = [ [[package]] name = "perry-ext-moment" -version = "0.5.1168" +version = "0.5.1171" dependencies = [ "chrono", "perry-ffi", @@ -5711,7 +5711,7 @@ dependencies = [ [[package]] name = "perry-ext-mongodb" -version = "0.5.1168" +version = "0.5.1171" dependencies = [ "bson", "futures-util", @@ -5723,7 +5723,7 @@ dependencies = [ [[package]] name = "perry-ext-mysql2" -version = "0.5.1168" +version = "0.5.1171" dependencies = [ "chrono", "perry-ffi", @@ -5733,7 +5733,7 @@ dependencies = [ [[package]] name = "perry-ext-nanoid" -version = "0.5.1168" +version = "0.5.1171" dependencies = [ "nanoid", "perry-ffi", @@ -5742,7 +5742,7 @@ dependencies = [ [[package]] name = "perry-ext-net" -version = "0.5.1168" +version = "0.5.1171" dependencies = [ "perry-ffi", "perry-runtime", @@ -5754,7 +5754,7 @@ dependencies = [ [[package]] name = "perry-ext-nodemailer" -version = "0.5.1168" +version = "0.5.1171" dependencies = [ "lettre", "perry-ffi", @@ -5764,7 +5764,7 @@ dependencies = [ [[package]] name = "perry-ext-pdf" -version = "0.5.1168" +version = "0.5.1171" dependencies = [ "perry-ffi", "printpdf", @@ -5772,7 +5772,7 @@ dependencies = [ [[package]] name = "perry-ext-pg" -version = "0.5.1168" +version = "0.5.1171" dependencies = [ "perry-ffi", "sqlx", @@ -5781,7 +5781,7 @@ dependencies = [ [[package]] name = "perry-ext-ratelimit" -version = "0.5.1168" +version = "0.5.1171" dependencies = [ "governor", "perry-ffi", @@ -5789,7 +5789,7 @@ dependencies = [ [[package]] name = "perry-ext-sharp" -version = "0.5.1168" +version = "0.5.1171" dependencies = [ "base64", "image", @@ -5798,14 +5798,14 @@ dependencies = [ [[package]] name = "perry-ext-slugify" -version = "0.5.1168" +version = "0.5.1171" dependencies = [ "perry-ffi", ] [[package]] name = "perry-ext-streams" -version = "0.5.1168" +version = "0.5.1171" dependencies = [ "lazy_static", "perry-ffi", @@ -5814,7 +5814,7 @@ dependencies = [ [[package]] name = "perry-ext-uuid" -version = "0.5.1168" +version = "0.5.1171" dependencies = [ "perry-ffi", "uuid", @@ -5822,7 +5822,7 @@ dependencies = [ [[package]] name = "perry-ext-validator" -version = "0.5.1168" +version = "0.5.1171" dependencies = [ "perry-ffi", "regex", @@ -5832,7 +5832,7 @@ dependencies = [ [[package]] name = "perry-ext-ws" -version = "0.5.1168" +version = "0.5.1171" dependencies = [ "futures-util", "lazy_static", @@ -5844,7 +5844,7 @@ dependencies = [ [[package]] name = "perry-ext-zlib" -version = "0.5.1168" +version = "0.5.1171" dependencies = [ "brotli", "flate2", @@ -5853,7 +5853,7 @@ dependencies = [ [[package]] name = "perry-ffi" -version = "0.5.1168" +version = "0.5.1171" dependencies = [ "dashmap", "once_cell", @@ -5862,7 +5862,7 @@ dependencies = [ [[package]] name = "perry-hir" -version = "0.5.1168" +version = "0.5.1171" dependencies = [ "anyhow", "perry-api-manifest", @@ -5879,7 +5879,7 @@ dependencies = [ [[package]] name = "perry-parser" -version = "0.5.1168" +version = "0.5.1171" dependencies = [ "anyhow", "perry-diagnostics", @@ -5891,7 +5891,7 @@ dependencies = [ [[package]] name = "perry-runtime" -version = "0.5.1168" +version = "0.5.1171" dependencies = [ "anyhow", "base64", @@ -5923,7 +5923,7 @@ dependencies = [ [[package]] name = "perry-stdlib" -version = "0.5.1168" +version = "0.5.1171" dependencies = [ "aes 0.8.4", "aes-gcm", @@ -6015,7 +6015,7 @@ dependencies = [ [[package]] name = "perry-transform" -version = "0.5.1168" +version = "0.5.1171" dependencies = [ "anyhow", "perry-hir", @@ -6025,7 +6025,7 @@ dependencies = [ [[package]] name = "perry-types" -version = "0.5.1168" +version = "0.5.1171" dependencies = [ "anyhow", "thiserror 1.0.69", @@ -6033,14 +6033,14 @@ dependencies = [ [[package]] name = "perry-ui" -version = "0.5.1168" +version = "0.5.1171" dependencies = [ "perry-ui-model", ] [[package]] name = "perry-ui-android" -version = "0.5.1168" +version = "0.5.1171" dependencies = [ "base64", "itoa", @@ -6057,7 +6057,7 @@ dependencies = [ [[package]] name = "perry-ui-geisterhand" -version = "0.5.1168" +version = "0.5.1171" dependencies = [ "rand 0.8.6", "serde", @@ -6067,7 +6067,7 @@ dependencies = [ [[package]] name = "perry-ui-gtk4" -version = "0.5.1168" +version = "0.5.1171" dependencies = [ "base64", "cairo-rs", @@ -6090,7 +6090,7 @@ dependencies = [ [[package]] name = "perry-ui-ios" -version = "0.5.1168" +version = "0.5.1171" dependencies = [ "base64", "block2", @@ -6106,7 +6106,7 @@ dependencies = [ [[package]] name = "perry-ui-macos" -version = "0.5.1168" +version = "0.5.1171" dependencies = [ "base64", "block2", @@ -6121,7 +6121,7 @@ dependencies = [ [[package]] name = "perry-ui-model" -version = "0.5.1168" +version = "0.5.1171" [[package]] name = "perry-ui-test" @@ -6129,11 +6129,11 @@ version = "0.1.0" [[package]] name = "perry-ui-testkit" -version = "0.5.1168" +version = "0.5.1171" [[package]] name = "perry-ui-tvos" -version = "0.5.1168" +version = "0.5.1171" dependencies = [ "base64", "block2", @@ -6149,7 +6149,7 @@ dependencies = [ [[package]] name = "perry-ui-visionos" -version = "0.5.1168" +version = "0.5.1171" dependencies = [ "base64", "block2", @@ -6165,7 +6165,7 @@ dependencies = [ [[package]] name = "perry-ui-watchos" -version = "0.5.1168" +version = "0.5.1171" dependencies = [ "block2", "libc", @@ -6178,7 +6178,7 @@ dependencies = [ [[package]] name = "perry-ui-windows" -version = "0.5.1168" +version = "0.5.1171" dependencies = [ "base64", "libc", @@ -6195,14 +6195,14 @@ dependencies = [ [[package]] name = "perry-ui-windows-winui" -version = "0.5.1168" +version = "0.5.1171" dependencies = [ "perry-ui-windows", ] [[package]] name = "perry-updater" -version = "0.5.1168" +version = "0.5.1171" dependencies = [ "base64", "ed25519-dalek", @@ -6216,7 +6216,7 @@ dependencies = [ [[package]] name = "perry-wasm-host" -version = "0.5.1168" +version = "0.5.1171" dependencies = [ "wasmi", ] diff --git a/Cargo.toml b/Cargo.toml index 56bafff13d..a775640a3e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -201,7 +201,7 @@ codegen-units = 16 opt-level = "s" # Optimize for size in stdlib [workspace.package] -version = "0.5.1170" +version = "0.5.1171" edition = "2021" license = "MIT" repository = "https://github.com/PerryTS/perry" diff --git a/crates/perry-hir/src/lower_decl/helpers.rs b/crates/perry-hir/src/lower_decl/helpers.rs index ae53e4e8f5..82f5ec60d9 100644 --- a/crates/perry-hir/src/lower_decl/helpers.rs +++ b/crates/perry-hir/src/lower_decl/helpers.rs @@ -472,6 +472,28 @@ fn expr_uses_arguments(expr: &ast::Expr) -> bool { ast::BlockStmtOrExpr::BlockStmt(b) => body_uses_arguments(&b.stmts), ast::BlockStmtOrExpr::Expr(e) => expr_uses_arguments(e), }, + // Sequence (comma) expressions: `return n.date = t, n.args = arguments, x`. + // Minified bundlers (e.g. dayjs) hide `arguments` inside these, so the + // synthetic-arguments pre-scan must descend through every operand. + ast::Expr::Seq(s) => s.exprs.iter().any(|e| expr_uses_arguments(e)), + ast::Expr::Await(a) => expr_uses_arguments(&a.arg), + ast::Expr::Yield(y) => y.arg.as_deref().map(expr_uses_arguments).unwrap_or(false), + ast::Expr::OptChain(o) => match &*o.base { + ast::OptChainBase::Member(m) => { + expr_uses_arguments(&m.obj) + || matches!(&m.prop, ast::MemberProp::Computed(c) if expr_uses_arguments(&c.expr)) + } + ast::OptChainBase::Call(c) => { + expr_uses_arguments(&c.callee) + || c.args.iter().any(|a| expr_uses_arguments(&a.expr)) + } + }, + ast::Expr::SuperProp(sp) => { + matches!(&sp.prop, ast::SuperProp::Computed(c) if expr_uses_arguments(&c.expr)) + } + ast::Expr::TaggedTpl(t) => { + expr_uses_arguments(&t.tag) || t.tpl.exprs.iter().any(|e| expr_uses_arguments(e)) + } // Don't descend into nested function declarations or function // expressions — those have their own `arguments` binding that // shadows the enclosing scope. diff --git a/crates/perry-runtime/src/object/native_call_method.rs b/crates/perry-runtime/src/object/native_call_method.rs index b227fde632..8d729ab53a 100644 --- a/crates/perry-runtime/src/object/native_call_method.rs +++ b/crates/perry-runtime/src/object/native_call_method.rs @@ -2061,7 +2061,17 @@ pub unsafe extern "C" fn js_native_call_method( } } - if crate::date::is_date_value(object) && method_name == "toString" { + // A `DateCell` is a NaN-boxed pointer but NOT an `ObjectHeader`, so a date + // receiver must never reach the generic object dispatch below — that path + // reinterprets the cell's bytes as an object and returns garbage. Every + // `Date.prototype` method (getters, setters, `toISOString`, `toJSON`, + // `toString`, …) is installed on `Date.prototype` and reads the + // `IMPLICIT_THIS` receiver, so resolve the method there and dispatch with + // `this` bound to the cell. Previously only `toString` was routed this way; + // every other dynamic/computed call (`date[m](...)`, `Reflect.apply`) fell + // through and silently dropped setter mutations — e.g. dayjs's + // `this.$d[l]($)` made `.add()`/`.date(n)` no-ops (#5133). + if crate::date::is_date_value(object) { let ctor = crate::object::js_get_global_this_builtin_value(b"Date".as_ptr(), 4); let ctor_ptr = crate::value::js_nanbox_get_pointer(ctor) as usize; if ctor_ptr != 0 { @@ -2082,8 +2092,10 @@ pub unsafe extern "C" fn js_native_call_method( } } } - let string = crate::date::js_date_to_string(object); - return f64::from_bits(JSValue::string_ptr(string).bits()); + if method_name == "toString" { + let string = crate::date::js_date_to_string(object); + return f64::from_bits(JSValue::string_ptr(string).bits()); + } } // Symbols: Symbol.for() pointers are Box-leaked (no GcHeader), so the