Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 15 additions & 1 deletion .github/workflows/release-packages.yml
Original file line number Diff line number Diff line change
Expand Up @@ -1685,8 +1685,15 @@ jobs:
# died on the first package with "You cannot publish over the
# previously published versions" — the remaining six packages could
# never go out (run 27239815088).
# One platform's failure must not starve the packages after it in
# glob order (with `set -e` a single E413/transient registry error
# aborted the loop mid-way — exactly how linux/windows sat at a stale
# version for weeks while darwin kept updating). Record failures,
# keep publishing, fail the step at the end with the full list; the
# already-published skip above keeps re-runs idempotent.
run: |
set -e
failed=""
for pkg in ./npm/perry-*; do
name=$(node -p "require('$pkg/package.json').name")
ver=$(node -p "require('$pkg/package.json').version")
Expand All @@ -1695,8 +1702,15 @@ jobs:
continue
fi
echo "=== publishing $pkg ==="
npm publish "$pkg" --access public --provenance
if ! npm publish "$pkg" --access public --provenance; then
echo "::error::npm publish failed for $name@$ver — continuing with remaining packages"
failed="$failed $name@$ver"
fi
done
if [ -n "$failed" ]; then
echo "::error::failed platform packages:$failed"
exit 1
fi

- name: Publish wrapper (@perryts/perry) last
run: |
Expand Down
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
## v0.5.1223 — audit-found capability-doc drift corrected; npm publish loop survives per-package failures (#5935)

Docs: lookbehind regex WORKS (regex→fancy-regex hybrid) — removed from CLAUDE.md categorical gaps and rewritten in porting.md (section/table/scan-list); CLAUDE.md GC facts refreshed (~55 registered scanners, conservative scan production-off, module vars uniformly NaN-boxed + rooted — module_var_data_ids is long gone); limitations.md prototype section rewritten around reality (prototype-method assignment + setPrototypeOf supported; the two real gaps: `obj.__proto__ = x` assignment form, literal `{__proto__: []}` not visible to instanceof); gap-suite count 235→258. CI: release-packages.yml platform-publish loop no longer aborts on the first failure under set -e (the mechanism that left npm linux/windows stale while darwin updated) — failures are recorded, the loop continues, the step fails at the end with the full list.

## v0.5.1222 — await/yield in loop condition/update runs per iteration (#5934, fixes #5933)

The async hoist's single pre-loop hoist (its own comment: "safe-but-incomplete approximation") evaluated a loop condition's await ONCE — async drain loops (`while ((v = await q.next()) !== undefined)`) never re-awaited (truthy first value spun forever on stale state, falsy never entered), `do…while` evaluated the condition before the first body run, and generators had the sibling defect at the linearize layer (`while (yield …)` cloned the condition into the cond state where residual yields lower to `0.0`). Restructured instead of hoisting out, preserving break/continue: `while (C)` → `while(true){ let t = C; if (!t) break; body }` (continue re-enters at the condition eval, per spec); `do B while (C)` → first-iteration flag + `while (first || C)` (body precedes first cond eval; continue falls through to the eval); `for (init; C; U)` keeps the For — awaited C moves to a body-top check with the condition slot emptied, awaited U moves to body end with every loop-level continue prefixed by it (new shared `prefix_loop_continues`). Linearize gains the three guarded yield-twin arms for direct generators. Review hardening: the update move bails to the previous lowering when a `continue` sits inside `try{}finally{}` (abrupt completion — the finally must run before the update and may override it), including the linearize arm guard so a fully-bailed For can't re-match its own desugar. e2e: `issue_5933_loop_condition_await.rs` (8 scenarios byte-for-byte vs node).
Expand Down
10 changes: 5 additions & 5 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,12 @@ 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.1222
**Current Version:** 0.5.1223


## TypeScript Parity Status

Tracked via the gap test suite (`test-files/test_gap_*.ts`, 235 tests). Compared byte-for-byte against `node --experimental-strip-types`. Run via `./scripts/run_gap_tests.sh` (a thin wrapper over `run_parity_tests.sh --filter test_gap_` that builds the compiler itself and gates on no new untriaged failures).
Tracked via the gap test suite (`test-files/test_gap_*.ts`, 258 tests). Compared byte-for-byte against `node --experimental-strip-types`. Run via `./scripts/run_gap_tests.sh` (a thin wrapper over `run_parity_tests.sh --filter test_gap_` that builds the compiler itself and gates on no new untriaged failures).

**Last full sweep:** run `./run_parity_tests.sh` for the current snapshot. The umbrella tracker is #793 (Node.js + TypeScript compatibility roadmap); the previously-cited #447–#452 batch closed on 2026-05-04. Currently-open trackers worth knowing about:

Expand All @@ -23,7 +23,7 @@ Tracked via the gap test suite (`test-files/test_gap_*.ts`, 235 tests). Compared
- **Test/CI mechanics** — `#794` (per-category parity thresholds), `#796` (gap-suite output truncation + O(n²) `normalize_output`), `#812` (42-module behavioral matrix), `#806/#807/#808` (test harnesses for mixins / async context / ≥300-init scale).
- **Skip-list audit** — `#797` covers `test-parity/known_failures.json` provenance (issue # + date per entry).

**Known categorical gaps**: lookbehind regex (Rust `regex` crate), `console.dir`/`console.group*` formatting, lone surrogate handling (WTF-8).
**Known categorical gaps**: `console.dir`/`console.group*` formatting, lone surrogate handling (WTF-8). (Lookbehind regex is NOT a gap anymore: `perry-runtime/src/regex.rs` falls back from the `regex` crate to `fancy-regex` for lookbehind/backreferences, with capture-group translation and replacement expansion.)

## Workflow Requirements

Expand Down Expand Up @@ -101,11 +101,11 @@ TAG_TRUE = 0x7FFC_0000_0000_0004 STRING_TAG = 0x7FFF (lower 48 = ptr)

Key functions: `js_nanbox_string/pointer/bigint`, `js_nanbox_get_pointer`, `js_get_string_pointer_unified`, `js_jsvalue_to_string`, `js_is_truthy`

**Module-level variables**: Strings stored as F64 (NaN-boxed), Arrays/Objects as I64 (raw pointers). Access via `module_var_data_ids`.
**Module-level variables**: uniform NaN-boxed doubles in `@perry_global_<mod>__<id>` LLVM globals, all registered as GC roots before module init (marked AND rewritten on evacuation). The old F64-strings/raw-I64-arrays split and `module_var_data_ids` no longer exist (a stale comment survives in `perry-transform/src/inline/mod.rs`).

## Garbage Collection

Generational mark-sweep GC in `crates/perry-runtime/src/gc.rs` (default since v0.5.237 / Phase D). Two regions in the per-thread arena: nursery (`ARENA`, fills with new allocations, swept on minor GC) and old-gen (`OLD_ARENA`, holds tenured/evacuated objects). Conservative stack scan + precise shadow-stack roots + 9 registered scanners. Write barriers populate a remembered set so minor GC can avoid retracing the old-gen. Two-bit aging (`HAS_SURVIVED` / `TENURED`) promotes nursery survivors after 2 minor cycles; the C4b evacuation policy moves non-pinned tenured objects into old-gen with full reference rewriting only when generated write barriers are active and nursery/RSS pressure plus measured movable candidates justify the work. Idle nursery blocks observed empty for 2 GC cycles are `dealloc`'d back to the OS (C4b-δ, v0.5.235), and the next-trigger calc is hard-capped at the initial threshold (64 MB) so >90%-freed step-doubling can't blow up peak occupancy (C4b-δ-tune, v0.5.236). Triggers on arena block allocation (1 MB blocks since v0.5.196), malloc count threshold, or explicit `gc()` call. 8-byte GcHeader per allocation.
Generational mark-sweep GC in `crates/perry-runtime/src/gc.rs` (default since v0.5.237 / Phase D). Two regions in the per-thread arena: nursery (`ARENA`, fills with new allocations, swept on minor GC) and old-gen (`OLD_ARENA`, holds tenured/evacuated objects). Precise shadow-stack roots + ~55 registered side-table scanners (`gc/mod.rs:298+`); a conservative stack scan exists but production mode resolves to SkipDisabled, so liveness rests on codegen shadow-stack spilling plus `RuntimeHandleScope` in runtime helpers. Write barriers populate a remembered set so minor GC can avoid retracing the old-gen. Two-bit aging (`HAS_SURVIVED` / `TENURED`) promotes nursery survivors after 2 minor cycles; the C4b evacuation policy moves non-pinned tenured objects into old-gen with full reference rewriting only when generated write barriers are active and nursery/RSS pressure plus measured movable candidates justify the work. Idle nursery blocks observed empty for 2 GC cycles are `dealloc`'d back to the OS (C4b-δ, v0.5.235), and the next-trigger calc is hard-capped at the initial threshold (64 MB) so >90%-freed step-doubling can't blow up peak occupancy (C4b-δ-tune, v0.5.236). Triggers on arena block allocation (1 MB blocks since v0.5.196), malloc count threshold, or explicit `gc()` call. 8-byte GcHeader per allocation.

**Escape hatches**: `PERRY_GEN_GC=0`/`off`/`false` reverts to full mark-sweep (bisection only). `PERRY_GEN_GC_EVACUATE=0`/`off`/`false` disables policy evacuation; `=1`/`on`/`true` is accepted as auto-policy allowed, not unconditional evacuation. `PERRY_GC_FORCE_EVACUATE=1` stress-copies every marked non-pinned nursery object only when generated write barriers are active and policy evacuation is allowed. `PERRY_GC_VERIFY_EVACUATION=1` panics if any mutable live slot still points at a forwarded nursery object after an evacuation/rewrite cycle. `PERRY_WRITE_BARRIERS=0`/`off`/`false` disables codegen-emitted write barriers at compile time and runtime exact helper barriers at runtime for benchmark/debug bisection; unset, `=1`/`on`/`true` keep barriers enabled. `PERRY_GC_DIAG=1` prints per-cycle diagnostics, including evacuation-policy decisions for considered cycles and `barriers_inactive` skips.

Expand Down
Loading
Loading