Skip to content

Releases: PerryTS/perry

v0.5.294

26 Apr 00:19

Choose a tag to compare

Hotfix for v0.5.293's failed publish. v0.5.293 tagged successfully but release-packages.yml's await-tests gate failed: 19 macOS UI doc-tests + iOS simctl build hit an _js_stdlib_process_pending undefined-symbol link error. v0.5.293's GH release page exists but ships no binaries. v0.5.294 fixes the underlying bug.

Fix

  • fix(ui) — UI crates (perry-ui-{macos,ios,tvos,visionos,gtk4}, 18 files) now call js_run_stdlib_pump instead of hard-linking js_stdlib_process_pending. The trampoline at crates/perry-runtime/src/lib.rs:121 is unconditionally exported and dispatches via the registered-callback pattern (js_stdlib_has_active_handles already worked this way at lib.rs:144).
  • chore(parity) — re-added test_gap_console_methods to test-parity/known_failures.json as ci-env; v0.5.290's drop was premature. Investigate normalize_output broadening separately.

Root cause

Cargo feature unification. perry-stdlib/Cargo.toml:81 says perry-runtime = { features = ["stdlib"] }. When perry's auto-optimize compiles both crates in one cargo invocation (crates/perry/src/commands/compile.rs:1844), the stdlib feature gets activated on perry-runtime — which triggers the #[cfg(not(feature = "stdlib"))] gate at crates/perry-runtime/src/lib.rs:65 and excludes mod stdlib_stubs;, removing _js_stdlib_process_pending from libperry_runtime.a. perry's compile then enters runtime-only link mode (no libperry_stdlib.a in the link line) and the symbol is undefined.

Local single-package builds (cargo build -p perry-runtime) don't unify features, so the stub is included and the bug stays hidden in dev. feedback_recompile_ui_lib.md was a workaround for this latent bug — rebuilding the macOS UI lib happened to align local artifacts with locally-built libperry_runtime.a (which had the symbol).

Verified locally by reproducing the CI auto-optimize path:

./target/release/perry compile docs/examples/ui/animation/fade_in.ts -o /tmp/fade_in_test
…
Linking (runtime-only)...
Linking perry/ui (native UI) from target/release/libperry_ui_macos.a
Wrote executable: /tmp/fade_in_test

What v0.5.293 was supposed to ship

If you want the full changelog from v0.5.178 through v0.5.293 (115 commits — generational GC default ON, SSO default ON, JSON tape-parse + lazy default ON, Windows lightweight toolchain, visionOS support, end-to-end notification work on iOS + Android, etc.), see https://github.com/PerryTS/perry/releases/tag/v0.5.293. The notes there are accurate; only the binaries failed to publish.

v0.5.293

25 Apr 19:40

Choose a tag to compare

First tagged release since v0.5.178 — 115 commits of accumulated work. Headline changes: generational mark-sweep GC and SSO are now default ON, JSON parsing went tape-based + lazy by default, Windows toolchain dropped its Visual Studio dependency, and Native UI grew end-to-end notification support on iOS + Android.

Highlights

  • Generational mark-sweep GC default ON — Phases A-D land the shadow stack, write barriers, nursery/old-gen split, non-moving tenuring, copying evacuation pass, and per-cycle idle-block return-to-OS. PERRY_GEN_GC=0 reverts (bisection only). (v0.5.217 → v0.5.239)
  • SSO (Small String Optimization) default ON — runtime infrastructure + codegen three-way PropertyGet branch + consumer arms behind PERRY_SSO_FORCE while baking, then default-flipped at v0.5.216. (v0.5.213 → v0.5.216)
  • JSON parsing rebuilt — schema-directed JSON.parse<T>(blob), tape-based parse foundation, lazy parse + lazy stringify (default ON), per-element sparse materialization, walk-cursor + adaptive materialize threshold. (v0.5.200 → v0.5.210)
  • Windows lightweight toolchain — winget LLVM + xwin, no Visual Studio needed (#176, v0.5.199); plus actionable clang-missing / link.exe error messages and perry doctor LLVM checks.
  • visionOS simulator support--target visionos-simulator (v0.5.185).

Performance

  • JSON: lazy parse default ON (v0.5.210), per-element sparse materialization (v0.5.208), NEON/SSE2 string scanner (v0.5.197).
  • GC: arena block size 8 MB → 1 MB (v0.5.196), trigger threshold 128 MB → 64 MB (v0.5.198), idle nursery blocks returned to OS (v0.5.235).
  • Buffer.alloc fast-path via per-thread bump slab (#92, v0.5.190); BigInt arena alloc + BigInt(str) === fix (#92, v0.5.187); parseFloat zero-alloc + Infinity fix (#92).

Runtime / Codegen Fixes

  • JSON: JSON.stringify of plain f64 segfaulted (v0.5.286); restore #[no_mangle] on js_json_stringify (v0.5.211).
  • NaN: NaN==NaN + ECMAScript number formatting (v0.5.281); NaN/Infinity ToInt32 in (x) | 0 (v0.5.280); SSO + property-read NaN bug (v0.5.279).
  • Promises: microtask FIFO + thrown-handler propagation (v0.5.284); Promise.all/race/any non-promise discrimination (v0.5.263); Promise.allSettled non-Promise values; queueMicrotask never running + performance.now() always 0 (#156, v0.5.180).
  • Codegen: cross-module class getters/setters returned undefined (twice — c473934, 2a08855); inline pass remaps closure captures (v0.5.273); don't inline functions with rest params (v0.5.278); i32 loop counter for number-typed bounds (#168, v0.5.188); js_try_end on return inside try body; FFI manifest consumption for native-library ABI; for-loop continue skipping update on --target web (#137).
  • fs.readFileSync(path) returns Buffer when no encoding (v0.5.277); BigInt fromTwos/toTwos two's complement (v0.5.255); console.time resolution (#155, v0.5.181); Int32Array length=0 + Uint8ClampedArray no-clamp + negative NaN (#157, v0.5.184); isWellFormed/toWellFormed lone-surrogate detection (#29).
  • WASM: drop excess args at WASM call sites (#183, v0.5.205).
  • Type predicates + lazy audit (v0.5.212); HIR silent fall-throughs in lowerer + monomorph (v0.5.249).

Stdlib & Node Compat (#187 follow-ups)

  • Redis (ioredis) end-to-end + dispatch-table-symbol-mismatch fix (v0.5.270); pg + mongo async-factory pattern (v0.5.275); AsyncLocalStorage end-to-end (v0.5.261); decimal.js (v0.5.259); commander runtime + codegen .action() invocation (v0.5.250); new T() bug on EE/LRU/WSS (v0.5.252); Buffer numeric reads intrinsified (#92, v0.5.183).
  • Fastify end-to-end integration test + two dispatch fixes (#174, v0.5.189); mysql2 + pg connection.execute() param binding (#143, v0.5.182).

Native UI

  • Apple (iOS / macOS): notificationSend (#94, ui-ios v0.5.193), client-side remote-push token + receive (#95, v0.5.243), scheduled + cancellable local notifications (#96, v0.5.244), notification tap callback (#97, v0.5.254).
  • Android: notificationSend (#94, v0.5.256), schedule + cancel notifications (#96, v0.5.260), notification tap callback (#97, v0.5.258), FCM register + receive (#95, v0.5.262).

Platform / Toolchain

  • Windows: lightweight toolchain (#176, v0.5.199); find_lld_link/find_perry_windows_sdk cfg gate hotfix (v0.5.201); /SUBSYSTEM:CONSOLE for non-UI PE builds (#120, v0.5.179); actionable clang/link.exe error messages (#176 follow-ups, v0.5.191/.192).
  • npm: libc:glibc field on linux-x64 and linux-arm64 packages (#116/#161).

Documentation & Benchmarks

  • Polyglot benchmark suite expansions: Kotlin + JSON polyglot (v0.5.241/.242), simdjson + AssemblyScript+json-as peers (v0.5.274), loop_data_dependent + bench_field_access landed (v0.5.272).
  • Methodology hardening: RUNS=11 median + p95 + σ + macOS pinning (v0.5.248); 04_array_read 211 MB peak RSS explained (v0.5.276); FP-contract caveat (FMA-contract vs no-contract clustering) on bench_loop_data_dependent (v0.5.293).
  • Node TS-strip leveling: json_polyglot/run.sh precompiles Node TS to .mjs as untimed setup so Node isn't charged for --experimental-strip-types runtime parse (v0.5.293).
  • GC academic + industry lineage appendix; Phase D roadmap closeout (v0.5.239/.240).
  • CLAUDE.md condense — 124 verbose Recent Changes entries (~242 KB) migrated to CHANGELOG.md, file went 254 KB → 12 KB (v0.5.292).

Repo & CI Hygiene

  • chore(repo) v0.5.293: untrack 465 Android Gradle cache files, gitignore android-build/.gradle//build//app/build/, docs/examples/_reports/, external demo assets/, and stray repro binaries.
  • CI macos-14 jobs no longer OOM on disk space (v0.5.289-.291).
  • Memory stability suite for RSS-leak + GC-aggression regressions (v0.5.233).
  • Stub-audit cleanups: test_json, test_gap_console_methods, test_gap_class_advanced removed from known_failures.json.

Escape Hatches

  • PERRY_GEN_GC=0 — revert generational GC to full mark-sweep.
  • PERRY_SHADOW_STACK=0 — disable shadow-stack precise roots.
  • PERRY_GEN_GC_EVACUATE=1 — opt into copying evacuation (default OFF, work-saving on workloads where nothing tenures).
  • PERRY_WRITE_BARRIERS=1 — opt into codegen-emitted write barriers (runtime barrier always exists).
  • PERRY_GC_DIAG=1 — per-cycle GC diagnostics.

v0.5.178

23 Apr 15:05

Choose a tag to compare

Highlights

Fix-forward release that unblocks the stuck v0.5.177 release-packages pipeline. Three distinct CI regressions fixed — UI doc-tests now link on all three host OSes, cargo-test no longer SIGABRTs on the HIR repro suite, and new Set([...]) without explicit type args stops returning undefined on .has().

Fixes

  • App({...}) doc-tests on macOS/Ubuntu/Windows: every docs/examples/ui/*.ts example started failing with "App(...) requires a config object literal" because Phase 3 anon-class synthesis (v0.5.172) now wraps closed-shape literals into new __AnonShape_N(...). The perry/ui App() handler still hard-matched Expr::Object(props). Swapped to the already-existing extract_options_fields helper that handles both Object and AnonShape-New paths (the same one perry/thread spawn uses). 19/27 doc-tests → 27/27.

  • just_factory SIGABRT in cargo-test: a repro test for a separate pre-existing stack overflow (top-level const x = fn() + factory returning a nested object literal) wasn't marked #[ignore] — so the default 2 MB test stack blew on every CI run. Marked #[ignore] with matching rationale to its sibling; real fix tracked separately.

  • new Set([1,2,3]).has(1) returned undefined: refine_type_from_init emitted HirType::Named("Set") for Expr::SetNewFromArray, but is_set_expr only matches HirType::Generic { base: "Set" }. Without explicit <number> type args, every Set.has()/add()/delete() fell through to the undefined-dispatch path. Same shape in Phase 4's infer_type_from_expr. Both fixed; Map/Set/WeakMap/WeakSet/Array/Promise now always flow as Generic (intrinsic generics can't be Named).

Known

  • test_edge_map_set parity: the simple Set.has regression is fixed, but the specific setA.forEach((v) => setB.has(v)) pattern has a second, path-dependent bug — same MD5 file passes from /tmp/ but the intersection comes back empty from the project root. Added to known_failures.json with the repro; tracked as follow-up.

Infrastructure

  • release-packages.yml's await-tests gate correctly blocked the v0.5.177 publish once Tests turned red — working as designed.

v0.5.177

23 Apr 13:14

Choose a tag to compare

Highlights

The long-hanging object-layout + type-inference arc lands (Phases 1/3/4/4.1 against the Static Hermes side-by-side), along with the first gap-tests bump in months (17 → 23 of 28), a benchmark refresh that finally beats Node on json_roundtrip (was Perry's one outstanding loss), and fixes for seven filed issues.

Fixes

  • #142Math.tan / asin / acos / atan / atan2 were silent identity functions. Codegen now calls the existing runtime symbols. (ce550d9, via PR #148.)
  • #144 — TypeScript decorators were parsed into HIR but silently dropped at codegen. Compiler now hard-errors with a message naming the decoration point.
  • #146perry/thread primitives (parallelMap / parallelFilter / spawn) compiled clean and silently returned undefined. Wired up + added a compile-time mutable-outer-capture check with a broadened bypass-closure for named-function callbacks.
  • #150Object.getOwnPropertyDescriptor returned undefined on Phase 3 anon-shape literals. Scalar-replacement escape check now conservatively escapes all candidates on un-enumerated HIR variants.
  • #158Map / Set treated -0 and +0 as distinct keys. Added SameValueZero normalization.
  • #20console.trace() now emits a real native backtrace to stderr (needs PERRY_DEBUG_SYMBOLS=1 for symbolicated frames).
  • Phase 3 follow-up — Error.cause AST extraction now handles new Error(msg, { cause }) shorthand and ({ cause: e }) paren-wrap (were silently losing .cause).

Features

  • Phase 1 — structural shape inference for closed object literals in infer_type_from_expr.
  • Phase 3 — anonymous class synthesis (__AnonShape_N) for closed literals so property access hits the direct-GEP fast path. Backed by a synthesized constructor so per-literal values survive shape dedup.
  • Phase 4 / 4.1 — body-based return-type inference for free functions, class methods, getters, and arrow expressions; method-call return-type inference via a class_method_return_types registry consulted at callsites.
  • Added an extract_options_fields helper for builtin constructors (Response / Request / Headers / Error) that handles both Expr::Object and Phase 3's Expr::New { __AnonShape_N } shapes.

Benchmarks

Reshot on current main. Perry wins every workload in the main suite vs Node.js and Bun (16 of 16 including json_roundtrip — 314 ms vs Node 377 ms, was 1.58× slower at v0.5.166). Polyglot: leads on 5 of 8 cells vs Rust / C++ / Go / Swift / Java; ties fibonacci with C++; trails by 1-2 ms on three stack-struct-friendly rows. README.md tables + benchmarks/baseline.json updated.

Infrastructure

  • CLAUDE.md "Recent Changes" tightened to 1-2 line summaries.
  • 40-test HIR integration suite added at crates/perry-hir/tests/shape_inference.rs covering Phase 1/3/4/4.1 inference.
  • Four new follow-up issues filed (#154 await using, #155 console.time, #156 queueMicrotask/performance.now, #157 typed-array constructors, plus the existing #29 for lone-surrogate string handling) covering the remaining 5 failing gap tests.

Gap tests

22 / 28 → 23 / 28 passing vs node --experimental-strip-types. Remaining known-failing: async_advanced, console_methods, global_apis, string_methods, typed_arrays. All tracked in GitHub issues.

v0.5.164

22 Apr 17:06

Choose a tag to compare

Highlights

  • #140 SIMD autovectorization restored on pure-accumulator loops. loop_overhead 32→12 ms, math_intensive 48→14 ms, accumulate 97→24 ms — all matching the v0.5.22 baseline exactly. Perry now beats Rust/C++ 7–8× on loop_overhead, 3× on math_intensive, 4× on accumulate again.
  • V2.2 on-disk per-module object cache (.perry-cache/objects/<target>/<key>.o). Warm-cache builds are ~29% faster; the real win is perry dev's watch-loop hot path where only the edited module recompiles.

Fixes

  • #140 Scope the i32 shadow slot to locals actually used as array indices and refine the #74 asm sideeffect barrier to skip bodies with outer-scope writes — the two changes that had knocked the LLVM vectorizer off pure-accumulator reductions between v0.5.91 and v0.5.162 (v0.5.164, 83f05ff)
  • #136 sendToClient(handle, msg) and closeClient(handle) named imports from 'ws' were silently no-ops — the receiver-less native dispatch table was missing entries (v0.5.162, d81acb5)
  • #135 --target web hangs when break / continue is nested inside if / else / switch / try...catch inside a loop — WASM emitter was hardcoding Br(1) instead of consulting break_depth/loop_depth (v0.5.161, bf0a171)
  • PR #138 Windows vswhere discovery now requires VC tools (thanks @NicoAvanzDev) (9033dd4, 76f30c8)
  • winget submission now syncs the WINGET_PAT user's fork at runtime instead of hardcoding PerryTS's (v0.5.159, 58534df)

Features

  • V2.2 per-module on-disk object cache closing #131, building on PR #132 — opt-outs via --no-cache / PERRY_NO_CACHE=1; new perry cache info / perry cache clean subcommands; PERRY_DEV_VERBOSE=1 prints hit/miss ratios (v0.5.160, 1a12afe, 50d09ed)

Infrastructure

  • Cache-key audit hardening: hashed codegen env vars by value (not presence), .perry-cache/ added to .gitignore, BSD-grep-portable smoke test regex (v0.5.160, 1a12afe)
  • codegen-cache verbose output aligned with the #131 spec (de514d5)

Docs

  • Response to #139 benchmark scrutiny + cleanup of 32 stale bench_*.ts / test_inline*.ts / old benchmarks/results/*.txt scratchpads; README.md + benchmarks/polyglot/RESULTS.md updated with v0.5.164 numbers (v0.5.163, 5eb18ff + v0.5.164 doc updates in 83f05ff)

v0.5.158

22 Apr 09:16

Choose a tag to compare

Three versions' worth of fixes (v0.5.156–v0.5.158) plus the watch-mode AST cache and a perry check UX fix.

Fixes

  • --target web / WASM — five compounding bugs (#133): str.charCodeAt and other un-special-cased String.prototype methods fell through __classDispatch → undefined; Math.sin/cos/tan/atan2/exp/… returned undefined because the WASM codegen had no handlers for the trig family; arr.push(1); arr.length === 0 on top-level const arr = [] because ArrayPush/Pop/Shift/Unshift/PushSpread/Splice only looked in local_map; Firefox NaN-canonicalization stripped STRING / POINTER / INT32 tag payloads across the JS↔WASM boundary (split wrapForI64 into legacy-rt + BigInt-safe-ffi variants); INT32_TAG constants crashed wasm-bindgen with TypeError.
  • --target android — obj.field returns NaN (#128): the PIC receiver guard (#51) and .length fast-path guard (#73) both used a Darwin-mimalloc-calibrated heap-window check. Bionic's Scudo allocator places allocations far below the 2 TB floor, so every real object pointer failed. Replaced with a platform-independent NaN-box tag check — same LLVM cost, works everywhere.
  • perry check — emit file:line:column for HIR lowering errors (#129): previously every HIR-lowering error printed location-less. New LowerError type carries an Option<Span>, lower_bail! macro wraps sites, check.rs downcasts and attaches the span to the emitted diagnostic. Two sites migrated (super-prop access, binding patterns); infrastructure is in place for the remaining ~35 sites to land incrementally.
  • Release-packages await-tests gate — query by filename, stop swallowing gh errors (v0.5.156): v0.5.155's gate timed out for 45 min with [] from gh run list --workflow even though the runs existed. Swapped to gh api /repos/{repo}/actions/workflows/{file}/runs and dropped the 2>/dev/null || echo '[]' that masked real API errors. Emergency-republish workflow_dispatch bypass preserved.

Features

  • perry dev — in-memory AST cache across rebuilds (#131, PR #132, by @TheHypnoo): new path-keyed content-addressed ParseCache owned by perry dev for the watch session's lifetime. Unchanged files reuse their prior AST; byte-identical content → hit regardless of mtime. Smoke test: after editing one file, rebuild is 1/2 hit (1 miss) in 502 ms. Scope is strictly perry devperry compile and other entrypoints pass None and behave identically to v0.5.155.

Infrastructure

  • chore: gitignore benchmark binariesimage_conv, issue42_noalloc no longer surface in git status.

Full commit range: v0.5.155..v0.5.158.

v0.5.155

22 Apr 05:49

Choose a tag to compare

Highlights

  • Tier-2 iOS simulator CI gaterelease-packages.yml now waits on both Tests and Simulator Tests (iOS) green on the exact tag-commit SHA before publishing to brew / apt / npm. New await-tests job polls gh run list --commit <sha> every 30s with a 45-min deadline. Test workflow trigger extended to tags: ['v*'] so the Tests run actually exists on the tag. workflow_dispatch emergency-republish bypass preserved.
  • Doc-examples test harness + cross-compile matrix — Every docs/examples/ snippet now compiles under CI against macOS / Linux / Windows (blocking) and iOS / tvOS / watchOS / Android / WASM / Web (advisory until baselines blessed, now blocking on macOS+Linux). Simulator runs use PERRY_UI_TEST_MODE=1 + self-exit timer for bundle-launches smoke signal.
  • watchOS end-to-end--target watchos[-simulator] compiles and bundles; adds --features watchos-game-loop (Metal surface) and --features watchos-swift-app (SwiftUI host) plus perry.nativeLibrary.targets.<plat>.metal_sources for .metallib bundling (closes #124).
  • perry dev watch mode — New subcommand auto-recompiles on save (v0.5.143).
  • perry/ui gap closurealertWithButtons, string values in preferencesGet/Set, onTerminate / onActivate lifecycle hooks, and LazyVStack backed by NSTableView virtualization (v0.5.151).

Features

  • feat: gate release-packages on Tests + Simulator Tests (v0.5.155)
  • feat: close all 4 remaining perry/ui gaps (v0.5.151)
  • feat: metal_sources manifest field for .metallib bundling (v0.5.146, closes #124)
  • feat: compile-only banner + Fastify #125 regression check (v0.5.144)
  • feat: perry dev — watch-mode subcommand for auto-recompile on save (v0.5.143, #126)
  • feat: big doc-tests push — blessings + UI docs + xcompile + Android + PNG compress (v0.5.140)
  • feat: tier-2 follow-ups — watchOS test mode + iOS-sim fix + simctl CI (v0.5.137)
  • feat: wire PERRY_UI_TEST_MODE into perry-ui-ios + perry-ui-tvos (v0.5.135)
  • feat: split xcompile into blocking (wasm+web) + advisory (v0.5.133)
  • feat: bless gallery baselines + flip macOS+Linux blocking (v0.5.131)
  • feat: doc-tests cross-compile matrix (iOS/tvOS/Android/WASM/Web) (v0.5.125)
  • feat: doc-examples test harness + widget gallery (v0.5.123)
  • feat: add --features watchos-swift-app for SwiftUI-hosted rendering (v0.5.122, #118)
  • feat: add --features watchos-game-loop for Metal-surface apps (v0.5.114, #106)

Fixes

  • fix: drop run_with_timeout from simctl launch path (v0.5.154)
  • fix: drop --console-pty from simctl-tests (v0.5.152)
  • fix: honor --app-bundle-id on ios-simulator/ios full-app (v0.5.150)
  • fix: ios-simulator Info.plist platform keys (v0.5.149)
  • fix: simctl env vars via SIMCTL_CHILD_ prefix (v0.5.148)
  • fix: simctl --setenv arg syntax (v0.5.147)
  • fix: simctl-tests — detect timeout/gtimeout + fallback (v0.5.145)
  • fix: async arrow/closure returns — wrap in js_promise_resolved (v0.5.142, closes #125)
  • fix: cross-platform fs/roundtrip path (v0.5.142)
  • fix: Ubuntu gtk4 border symbols + Windows png encoder borrow (v0.5.141)
  • fix: PowerShell arg-splitting, round two — repeat xcompile flag (v0.5.139)
  • fix: PowerShell arg-splitting on Windows xcompile step (v0.5.138)
  • fix: three follow-ups from v0.5.135 CI run (v0.5.136)
  • fix: Windows Picker parent HWND for WS_CHILD (v0.5.132)
  • fix: force async-runtime feature when UI backend is linked (v0.5.130)
  • fix: diagnose why --whole-archive didn't surface on Ubuntu (v0.5.129)
  • fix: use --whole-archive for stdlib on Linux UI links (v0.5.128)
  • fix: Linux link order + Windows MSVC env (v0.5.127)
  • fix: merge stdout + stderr in doc-tests compile-fail detail (v0.5.126)
  • fix: watchos bundle step copies project assets into the .app (#123)
  • fix: platform-split HEAP_MIN for android/linux (v0.5.125)
  • fix: doc-tests CI follow-ups after first run of v0.5.123 (v0.5.124)
  • fix: reject silent perry/ui API misuse at compile time (v0.5.119, #114)
  • fix: drop libc: ["glibc"] from glibc Linux npm manifests (v0.5.118, #116)
  • fix: wire URL/URLSearchParams through LLVM backend (v0.5.117, #111)
  • fix: animateOpacity/animatePosition signature, units, and reactivity (v0.5.116, #109)
  • fix: console.log on Windows by gating MSVC subsystem on needs_ui (v0.5.114, #108)
  • fix: find_native_library target_key honors watchos (v0.5.115, #107)
  • fix: make --target watchos[-simulator] compile end-to-end (v0.5.113, #105)
  • fix: perry-ui-ios StringHeader.length migrated to byte_len
  • fix: ios-game-loop entry point for iOS/tvOS targets

Infrastructure / Docs

  • docs(skill): rewrite /release to tag HEAD, not commit uncommitted work
  • chore: simctl-tests per-phase trace + explicit timeouts (v0.5.153)
  • docs: correct stale version strings + TabBar platform caveat
  • docs: port-npm-to-perry skill + porting guide (experimental, re #115)
  • docs: add CONTRIBUTING, CoC, and issue/PR templates
  • docs: carve out "no version bumps in external PRs" rule
  • docs: add perry dev to CLI commands reference
  • docs: rewrite ui/widgets.md against the real API (v0.5.134)
  • docs: document @perryts/perry npm install path in README + mdBook

v0.5.112

20 Apr 05:53

Choose a tag to compare

Bug Fixes

  • **Auto-reactive Text(\...${state.value}...`)in HIR lowering (#104)** — the docs atstate.mdpromised template literals containingstate.valuereads would bind reactively, but no backend emitted the binding;count.set(...)fired subscribers, nothing had ever subscribed. Fixed incrates/perry-hir/src/lower.rsby desugaringText(Tpl-with-state)into an IIFE closure that creates the widget, registersstateOnChangeper distinct state (each capturing the widget handle and re-rendering viatextSetString), and returns the handle. The outer IIFE is required because collect_module_let_idsonly tracksStmt::Let; bare LocalSet/LocalGetinsideExpr::Sequenceat module top level had no backing WASM global or local and returnedTAG_UNDEFINED`, silently dropping the widget.
  • Wasm codegen collect_strings_in_expr traverses Expr::Sequence — was a Update | Sequence(_) => {} catch-all. Nested bridge-function names (perry_ui_text_create, perry_ui_state_on_change, perry_ui_text_set_string) and string literals ("Count: ") were dropped from the WASM string table, so the memcalls resolved to the empty name slot.
  • Wasm map_ui_method accepts "stateOnChange" — previously only "onChange" / "state_on_change"; the LLVM-side camelCase naming didn't round-trip to wasm.

Verification

  • Web/wasm: jsdom click-through — Text(\Count: ${count.value}`)` → 3 × Increment → "Count: 3".
  • Multi-state: test_ui_state_binding.ts (4 different template forms sharing one count state) all update together through +1 and -1 clicks.
  • macOS native: AppleScript driving AppKit window confirms reactive updates.
  • iOS/tvOS/watchOS/Android/GTK4/Windows: statically audited — identical FFI signatures (perry_ui_state_on_change(i64, f64), perry_ui_text_set_string(i64, i64)), real (non-stub) bodies, same js_closure_call1 extern-C ABI. Not exercised on hardware.

v0.5.110 — perry/ui ForEach codegen

20 Apr 04:20

Choose a tag to compare

Followup to #103. v0.5.109 fixed the type stubs + docs; this release fixes the actual codegen so the todo app visibly shows a window.

Bug Fixes

  • ForEach(state, render) from perry/ui was unimplemented in codegen. The generic dispatch path emitted perry/ui warning: method 'ForEach' not in dispatch table (args: 2) and returned 0/undefined. The outer VStack's widget_add_child then got an invalid handle, AppKit silently refused to attach the window body, and the process ran with lsappinfo type="BackgroundOnly" — CPU burning, no window. Added a special case in crates/perry-codegen/src/lower_call.rs (next to VStack/HStack) that synthesizes a perry_ui_vstack_create(8.0) container, calls perry_ui_for_each_init(container, state_handle, render_closure), and returns the NaN-boxed container pointer.

    Why it's a special case and not a UiSig table entry: same reason as VStack/HStack — variadic-in-shape (needs side-effectful container synthesis + separate for_each_init call + handle return), which doesn't fit the uniform args→runtime_fn table.

Verified

  • test_min4.ts (VStack containing ForEach over State<string[]>) launches with type="Foreground".
  • The corrected todo app from the docs/src/ui/state.md "Complete Example" compiles and launches with type="Foreground".

v0.5.109 — perry init type stubs + UI docs

20 Apr 03:58

Choose a tag to compare

Bug Fixes

  • perry init TypeScript type stubs were broken for non-numeric State (closes #103). types/perry/ui/index.d.ts:

    • State is now generic (State<T = number>). State<string[]>([]) and State("") from the docs now type-check instead of erroring on the old number-only signature.
    • Added the ForEach(count: State<number>, render) export — it was used in the docs todo-app example but missing from the stub.
    • stateBindTextfield now takes State<string> to match its purpose.
  • UI docs examples called a TextField(state, placeholder) form that didn't exist and segfaulted at launch. UiArgKind::Str at crates/perry-codegen/src/lower_call.rs:2557 routes the first arg through get_raw_string_ptr, so a State handle (i64) reinterpreted as a NaN-boxed string derefs garbage. Rewrote the examples in:

    • docs/src/getting-started/first-app.md — counter + todo app
    • docs/src/ui/state.md — two-way binding, onChange, complete example
    • docs/src/ui/widgets.mdTextField / SecureField / Toggle / Slider / Picker / Form
    • docs/src/ui/dialogs.md — the text-editor TextField line

    to use the actual runtime signatures: TextField(placeholder, onChange), Slider(min, max, onChange), Picker(onChange) + pickerAddItem, etc. .onChange method replaced with the free-function stateOnChange(state, cb).

Verified

5 example binaries built from the corrected docs (counter, todo, controls, form, onchange) pass tsc --noEmit against the regenerated stubs AND compile + launch without crashing via perry src.ts -o bin.

No runtime/codegen change — stubs are embedded into the perry binary via include_str! at compile time and ship to users through perry init / perry types.