Commit Graph
341 Commits
Author SHA1 Message Date
Chris Tate b8434b0dbe fix(runtime): handle empty Buffer decoding ranges 2026-08-09 17:09:31 -05:00
Chris Tate 6f7c828854 fix(buffer): preserve optional encoding defaults 2026-08-09 16:47:09 -05:00
Chris Tate c64420b95a fix(buffer): preserve unknown encoding messages 2026-08-09 16:12:50 -05:00
Chris Tate de779430b3 Support variable Buffer.toString encodings
- Lower runtime-valued BufferEncoding arguments through checked C and LLVM paths.
- Normalize aliases and casing while preserving Node unknown-encoding errors.
- Add differential coverage and remove the obsolete diagnostic fence.
2026-08-09 14:45:22 -05:00
Chris Tate b3d3101550 Add static array unshift and reverse (#107)
- Lower unshift and reverse through the C and LLVM backends.
- Preserve spread, mutation, evaluation-order, and reference-counting semantics.
- Add runtime, differential, and surface-manifest coverage.
2026-08-09 14:25:53 -05:00
Chris Tateandryoppippi 23a2d3309e feat(fs): support static rename APIs (#104)
* feat(fs): support static rename APIs

- Lower fs.rename, fs.renameSync, and fs/promises.rename through the C and LLVM backends.
- Add runtime callback, error, and promise handling for rename operations.
- Cover typed and check-JS usage with differential corpus tests.

Co-authored-by: ryoppippi <1560508+ryoppippi@users.noreply.github.com>

* fix Windows rename semantics

* fix: move callback rename work off main thread

* fix(runtime): preserve rename callback checkpoints

---------

Co-authored-by: ryoppippi <1560508+ryoppippi@users.noreply.github.com>
2026-08-09 13:29:15 -05:00
Chris Tate a4e9970119 Support static util.parseArgs (#103)
* Support static util.parseArgs

- Lower typed util.parseArgs calls through the C and LLVM backends.
- Add a link-gated native parser with Node-compatible options, tokens, defaults, and errors.
- Cover ESM, CommonJS, default argv, sanitizer, and TS7 behavior.

* fix(runtime): align parseArgs behavior with Node

* fix(runtime): close parseArgs parity gaps

* fix(runtime): match parseArgs edge cases

* fix(runtime): preserve parseArgs short and default semantics
2026-08-09 08:57:03 -05:00
Chris Tateandsensei-woo e5c875bd97 Support Date instance lowering (#101)
* Support Date instance lowering

- Lower read-only Date values and constructors through the C and LLVM backends.

- Add portable calendar helpers, differential coverage, and documented surface contracts.

- Fence unsupported mutation, identity, locale, and component-constructor forms.

Co-authored-by: sensei-woo <sensei-woo@users.noreply.github.com>

* Fix Date exception and timezone semantics

* fix: harden Date lowering edge cases

* fix Date lowering edge cases

* Fix Date union fences and offset clipping

---------

Co-authored-by: sensei-woo <sensei-woo@users.noreply.github.com>
2026-08-08 23:17:58 -05:00
Chris Tateandsensei-woo 89c361a967 Lower AbortController for fetch cancellation (#100)
* Lower AbortController for fetch cancellation

- Add static and dynamic lowering for construction, signal access, and abort calls
- Back controllers with native signal state shared by fetch
- Cover cancellation semantics across C, LLVM, diagnostics, and surface tests

Co-authored-by: sensei-woo <sensei-woo@users.noreply.github.com>

* Fix AbortController abort edge cases

* Fence AbortController method extraction

---------

Co-authored-by: sensei-woo <sensei-woo@users.noreply.github.com>
2026-08-08 20:24:27 -05:00
Chris Tateandsensei-woo 0c45ee7d90 Support positioned fs.readSync calls (#99)
* Support positioned fs.readSync calls

- Lower numeric positions while preserving omitted and null current-offset behavior.

- Add offset-preserving native reads across POSIX and Windows runtimes.

- Cover positioned reads, offset semantics, EOF, and invalid positions.

Co-authored-by: sensei-woo <sensei-woo@users.noreply.github.com>

* fix(runtime): preserve partial positioned reads on Windows

* fix zero-length positioned reads

* fix(runtime): match empty readSync validation order

* fix(compiler): accept parenthesized null read positions

---------

Co-authored-by: sensei-woo <sensei-woo@users.noreply.github.com>
2026-08-08 16:58:34 -05:00
Chris Tateandkongxiangyiren 303ed952e1 fix(fetch): support Response construction (#98)
* fix(fetch): support Response construction

- Lower global Response calls through the native C and LLVM runtimes.
- Match Node 24 coercion, validation, and stream error precedence.
- Cover supported BodyInit and ResponseInit behavior with differential tests.

Co-authored-by: kongxiangyiren <72970235+kongxiangyiren@users.noreply.github.com>

* fix(fetch): align constructed response headers

* Fix Response construction and header iteration

* fix(fetch): align Response constructor conversion

* fix(fetch): preserve Response constructor semantics

---------

Co-authored-by: kongxiangyiren <72970235+kongxiangyiren@users.noreply.github.com>
2026-08-08 16:43:44 -05:00
Chris Tateandmorler 53e2f08227 perf(compiler): specialize typed-array element access (#97)
* perf(compiler): specialize typed-array element access

- Lower typed-array reads, writes, and lengths directly by IR element kind.
- Preserve coercion, bounds, and receiver lifetime semantics across C and LLVM.
- Cover emission, ABI layout, and Node-differential behavior.

Co-authored-by: morler <478444+morler@users.noreply.github.com>

* fix: retain typed-array receiver across nested assignments

---------

Co-authored-by: morler <478444+morler@users.noreply.github.com>
2026-08-08 14:39:41 -05:00
Chris Tate 782d3b8451 fix(compiler): support implicit top-level await modules (#94)
* fix(compiler): support implicit top-level await modules

- Match Node 24 syntax detection by treating top-level await as an ES module marker.

- Add differential corpus coverage for TypeScript files without explicit imports or exports.

* fix Node ESM classification for top-level await

* fix ESM require diagnostics and classification caching

* fix require binding and package type precedence

* fix: align top-level await module detection with Node
2026-08-08 11:35:01 -05:00
Jimmy Miller d41b5dd3c3 Add generations to cycle detection (#30)
* runtime: pin the ScrCycHdr layout the LLVM backend hardcodes

scr_cyc_mark_live is a static inline, so there is no symbol for the LLVM
backend to call — it open-codes the retain hook as a raw `store i32 0` at
obj-16 and reaches the cycle header at obj-32. Three sites emit that
(llvm/shapes.ts, llvm/classes.ts, llvm/emitter.ts), which makes the C
struct's size and its `color` offset a real ABI between the runtime header
and hand-written IR. The stored VALUE is part of that ABI too: the emitted
store is the literal 0, not the enumerator, so SCR_CYC_BLACK's numeric value
is pinned as surely as the offsets are.

Nothing enforced any of it. llvm-runtime-abi.test.ts mechanically checks that
every `declare @scr_*` agrees with its C prototype, but that is the FUNCTION
ABI; a struct-offset skew is invisible to it, to the type system, and to the
C compiler. Narrowing `color` and putting another field in the freed bytes
compiles clean and silently zeroes that field on every retain; reordering the
color enum compiles clean and makes every retain write the wrong color.

Assert the four properties the emitted IR actually depends on, so the next
edit to this struct or that enum fails the build instead of miscompiling.

The three layout assertions bake in 64-bit pointers and size_t, which every
current target has. A 32-bit port would trip them — correctly: the backend's
hardcoded -16/-32 would need fixing too.

* runtime: collect cycles generationally

A trial-deletion pass costs O(objects reachable from its candidates), and a
candidate inside a large live structure reaches all of it. Collecting every
256 candidates therefore made total collection work quadratic in a growing
live heap: the V8 splay benchmark spent 749ms per iteration, ~99% of it in
the collector re-walking a tree that was entirely live and never garbage.

Make the collector generational, in the shape CPython uses for this same
algorithm. Each header carries a generation; a pass names the oldest one it
will walk and skips anything above it, exactly as it already skips NULL and
immortals; survivors are promoted so later nursery passes never re-walk
them. A pass then costs what was allocated since the last one rather than
the whole heap.

Restricting the walk stays sound because trial deletion is already
conservative in the safe direction: an edge from a skipped older object is
never trial-deleted, so its target keeps that count and reads as externally
referenced — the same answer the collector gives for a stack reference.
What it gives up is cycles that span generations, and the full pass (which
skips nothing, and so is exactly the old algorithm) is what finds those.

Three things that are load-bearing and were not obvious:

- A restricted pass may spare a candidate ONLY because the edge keeping it
  alive came from a generation it refused to walk. The candidate buffer is
  the collector's only root set and markRoots consumes the entry, so
  dropping such a survivor retires the one record that the object is a
  possible cycle root — and a dead cycle whose members were all spared that
  way is then invisible to every later pass, full ones included. So a
  restricted pass hands its survivors back as candidates. A full pass needs
  none of this: it walked every edge, so rc > 0 there is a genuine outside
  reference and Bacon-Rajan's own reasoning retires the candidate.

- Live data that is unlinked INTO a dead cycle grows no counter: those
  objects were tallied when they were allocated, and they are not in the
  nursery. A heap-growth trigger alone therefore never fires for a program
  that churns its long-lived structures, so the mature candidate buffer's
  own size is a second full-pass trigger.

- Restricting the walk leaves one edge accounted by NOBODY. markGray does
  not trial-delete an edge into a skipped generation, and the teardown
  contract has free_fn release exactly the UNTRACED children — because every
  traced edge was supposed to have been decremented by markGray. So freeing
  a white object that holds a traced edge into an older generation drops the
  edge while the target keeps the count, and that phantom reference reads as
  an external one to every later pass, full ones included: the target and
  everything it links to would never be reclaimable again.

  The white set pays those edges off explicitly, in two halves, because the
  walk cannot do it inline — a release there could free a survivor that
  scr_promote still points at, or cascade into the white set mid-teardown.
  Each target is PINNED with a retain while `gen` still holds the values the
  walk filtered on, and after the teardowns the pin is dropped and the edge
  given up for real. Two decrements per entry, because the pin is one of
  them; a single release would only undo the pin and leave the phantom edge
  where it was. The pin is also what makes the second decrement safe, since
  a teardown's releases of untraced children can reach a cycle-headered
  object — that is how they re-buffer survivors. One entry per skipped edge,
  so several edges sharing a target settle it once each. Nothing pinned can
  be in the white set: a skipped older object's edges were never
  trial-deleted, so anything it references was scan-blacked rather than
  whitened, by the same conservatism above.

The event loop's between-turns collection now runs one scheduled pass
instead of scr_collect_cycles(); a full sweep per turn would walk the whole
live heap every turn, which is the cost the generations exist to avoid.
scr_collect_cycles() stays the exit / session-reset sweep, where the RC
audit runs straight after and wants nothing reclaimable left.

757-cycle-cross-generation.ts covers the third point: a live list mutated
until its nodes promote, then dead nursery cycles holding traced edges into
it. Unaccounted, the sanitized lane reports 60 objects live at exit — the
whole list, held by phantom counts.

Measured, best-of-7 against the previous collector:

  splay          620.45ms -> 1.45ms   (428x)
  deltablue         1.25  -> 0.81     (1.55x)
  richards          1.196 -> 1.162    (1.03x)
  crypto/navier-stokes/raytrace/regexp   unchanged

  an LRU cache (200k entries, 200k ops)  32137ms -> 93ms   (345x)
  a large live cache + 500 event-loop turns  3095ms -> 776ms  (4.0x)

The trade is space: floating cyclic garbage is now a bounded fraction of the
live heap instead of a fixed candidate count, so peak RSS on
cyclic-garbage-heavy programs runs ~1.4x the old collector's. Bounded and
flat in run length, not a leak — verified by holding a 200k-object live heap
while churning 200k dead cycles per round for 30 rounds.

Test lanes show byte-identical failure sets against the previous collector:
Linux sanitized 164, Linux plain 528, macOS differential corpus 66 — all
pre-existing on those machines, none introduced or fixed here.

* runtime: bound scheduled mature cycle backlog

* runtime: rearm cycle trigger after candidate deaths
2026-08-08 11:32:45 -05:00
Chris Tate 22666584df Restore sandbox test performance (#86)
* Restore sandbox test performance

- Re-enable secure native compiler caching for repo-local test artifacts.
- Isolate strict cache coverage while keeping it off the sandbox critical path.

* fix: respect sandbox test runtime limits
2026-08-03 01:03:34 -05:00
Chris Tate 52459a86d4 feat(compiler): lower stored text codec calls (#85)
* feat(compiler): lower stored text codec calls

- Lower same-scope const TextEncoder and TextDecoder call receivers without runtime instances.
- Preserve fences for escaping, captured, and imported codec values with actionable diagnostics.
- Add differential, diagnostic, and preflight baseline coverage for the supported idiom.

* fix(compiler): fence deferred text codec calls

* fix text codec switch-clause TDZ handling
2026-08-02 21:24:33 -05:00
Chris Tate 0d5bc0dd0d feat(coverage): resolve external host type surfaces (#80)
* feat(coverage): resolve external host type surfaces

- Add repeatable --external-types mappings for checker-only module resolution.
- Keep host runtime values as explicit blockers with tests and documentation.

* fix: validate external type mappings

* fix: fence external host runtime edges

* fix(compiler): skip empty external type lookup

* fix(compiler): honor external type boundaries
2026-08-02 19:41:35 -05:00
Chris Tate 20f2d98fa3 fix(compiler): refine narrowed union payload fields (#84)
* fix(compiler): refine narrowed union payload fields

- Treat pure union discriminant reads as stable within compound guards.
- Cover payload-field range refinement in analysis and library builds.

* fix(compiler): preserve field facts across union discriminants
2026-08-02 19:37:32 -05:00
Chris Tate a0c5d6a549 Document synthesized sidecar structs (#83)
- Define the optional marker as literal true for compiler-created inline records.

- Reject malformed markers and pin the format-1 validator behavior.
2026-08-02 19:13:01 -05:00
Chris Tate 9fb068ae27 Merge pull request #77 from vercel-labs/ctate/node24-fetch-inventory
feat(compat): inventory Node 24 fetch surface
2026-08-02 16:37:36 -05:00
Chris Tate 7de8be18c0 fix(runtime): reject inherited bodies for GET and HEAD 2026-08-02 16:09:08 -05:00
Chris Tate efb7e77281 fix: address fetch compatibility review findings 2026-08-02 15:31:00 -05:00
Chris Tate c356ff0ac0 Merge pull request #73 from vercel-labs/ctate/no-persist-compile
Persist native compilation cache
2026-08-02 14:58:35 -05:00
Chris Tate 68263610e3 fix(compiler): prevent stale native cache hits 2026-08-02 13:59:06 -05:00
Chris Tate 3f2a78894e fix(fetch): address Node 24 compatibility gaps 2026-08-02 13:28:46 -05:00
Chris Tate a1215f6ef9 fix(compiler): bypass cache for opaque wrappers 2026-08-02 13:00:17 -05:00
Chris Tate 0b79e36973 fix: align fetch runtime with Node 24 semantics 2026-08-02 12:29:31 -05:00
Chris Tate f8377a7440 fix fetch computed dispatch semantics 2026-08-02 11:59:05 -05:00
Chris Tate 8c20c9ac73 fix: validate computed fetch dispatch 2026-08-02 11:29:23 -05:00
Chris Tate af685d05f9 fix: close fetch inventory review gaps 2026-08-02 10:41:37 -05:00
Chris Tate b90c0466b2 fix: enforce fetch surface compatibility 2026-08-02 01:09:40 -05:00
Chris Tate f2b9bb9c6f Fix fetch fence aliases and computed unions 2026-08-01 22:38:08 -05:00
Chris Tate 57ce47b273 fix fetch inventory coverage gaps 2026-08-01 21:12:49 -05:00
Chris Tate ae1ce89070 Fix RequestInit fence aliasing 2026-08-01 20:28:38 -05:00
Chris Tate 9aede1ed55 Fix remaining fetch surface fence bypasses 2026-08-01 19:54:37 -05:00
Chris Tate 5ac9dcda3e Fix fetch surface fence coverage 2026-08-01 18:53:02 -05:00
Chris Tate dcc5a03a52 fix: validate native cache state and artifacts 2026-08-01 18:38:22 -05:00
Chris Tate ea3bbddc62 fix: align fetch inventory with lowering 2026-08-01 17:30:06 -05:00
Chris Tate 89b5ab3355 feat(compat): inventory Node 24 fetch surface
- Classify Node 24 fetch APIs and options by static, dynamic-only, unsupported, and out-of-scope support.

- Project the inventory into the surface manifest and add runtime drift and conformance checks.

- Fence unprofiled Headers members with actionable SC2020 diagnostics.
2026-08-01 17:01:32 -05:00
Chris Tate 53e07fc55c fix: harden native build caches 2026-08-01 15:39:44 -05:00
Chris Tate 1f1f7d42a7 fix native cache invalidation 2026-08-01 15:39:44 -05:00
Chris Tate 6b996ea4a8 fix: harden persistent compilation cache 2026-08-01 15:39:44 -05:00
Chris Tate 2c1406325d fix(compiler): harden persistent cache identity 2026-08-01 15:39:43 -05:00
Chris Tate a578a9ab9b Persist native compilation cache
- Enable bounded platform-native caching for production executable and library builds.

- Reuse library runtime objects across edits and avoid duplicate no-npm frontend loading.

- Cover cache defaults, invalidation, bypass behavior, and artifact equivalence; document controls.
2026-08-01 15:39:43 -05:00
Chris Tate 2d0bc2df0b Add generated Node 24 fetch conformance
- Pin the supported fetch surface to an explicit Node and Undici profile.
- Generate deterministic WebIDL, AbortSignal, and stream differential campaigns.
- Match Node stream pull scheduling while preserving typed source identity.
2026-08-01 15:33:37 -05:00
Chris Tate 0d3c006c0d Merge pull request #52 from vercel-labs/ctate/fix-fetch
feat: make fetch static-compatible
2026-08-01 14:49:41 -05:00
Chris Tate 6cf7cd9220 fix: align fetch APIs with Node coercion 2026-08-01 14:22:07 -05:00
Chris Tate bc00d3d65f fix(fetch): preserve WebIDL conversion semantics 2026-08-01 12:31:13 -05:00
Chris Tate 1544ccda88 fix(fetch): align static stream and body coercion 2026-08-01 09:10:35 -05:00
Chris Tate 5985d54a27 fix: preserve fetch coercion semantics 2026-08-01 08:28:01 -05:00