Commit Graph
180 Commits
Author SHA1 Message Date
Chris Tate 145f8fcd5e fs.watch names the changed entry; fs.watchFile polls in its own gated unit (#2507) 2026-10-29 15:13:12 -05:00
Malte UblandClaude Opus 5.5 ad8f8daccd Keep scr_lib.c's default object byte-identical for the fs.watchFile poll
Adding scr_stats_of_path and scr_stats_zero to scr_lib.c moved every
program's executable: scr_lib.o is linked everywhere, so its new symbols
and strings shifted addresses even for programs that never watch a file.

The two entry points now live in an SCR_FS_WATCH_FILE build of scr_lib.c,
the matrix's variant for programs whose feature set has watchFile, and the
poll unit calls the renamed scr_stats_poll_path. Programs that do not poll
link the default object unchanged: compiled without the define, scr_lib.o
is byte-identical to the base (verified by comparing the two objects).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-29 05:42:49 -07:00
Chris Tate 60be717d92 Merge main 2026-10-29 04:24:50 -05:00
Chris Tate 5f7a9cc8c7 Merge main 2026-10-29 03:34:56 -05:00
Chris Tate 3e7528ca86 pi wave 4: path values, union delete, NonNullable unions, BigInt stats, optional-param function values, live accessor literals, clearer stdlib-alias errors (#2497) 2026-10-28 22:33:16 -05:00
Chris Tate 2ae08e4002 Route more packages and values through Node islands (NI-4c–4g) (#1904) 2026-10-28 21:53:40 -05:00
Malte UblandClaude Opus 5.5 7176257136 fs.watchFile: poll a path from its own gated runtime unit
fs.watchFile(path[, options], listener) polls the resolved path every
`interval` milliseconds (5007 by default, `persistent` by default) and
calls its listener with (current, previous) Stats when a poll sees a
difference. The first poll only records the baseline, a path that cannot
be stat'd polls as all-zero Stats, and a stat failure that repeats for
the same errno is not re-reported. One poller per RESOLVED path is
shared by every listener on it, and fs.unwatchFile(path[, listener])
removes the last listener registered with that callback, or every
listener when called without one. The call is a statement, since Node's
StatWatcher has no lowering, and bigint: true is refused.

The implementation is scr_watch_file.c, a new gated runtime unit
switched by moduleUsesFsWatchFile, so programs that do not poll link
none of it. The feature travels the usual chain (runtime-features,
executable-features, the native link info, the executable cache keys,
the runtime-pack matrix) and the validator learns that
fs.unwatchFile's trailing listener slot may be omitted.

Evidence:
- tests/corpus/fs-watch-file-names.ts and fs-watch-file-polling.ts match
  Node on stdout, stderr and exit status in the plain and sanitized lanes
- 1564-fs-watch.ts, 1751-options-record-stance.ts and the `fs` corpus stay
  green; the new order baselines and corpus cost files are recorded

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-28 19:49:44 -07:00
Malte UblandClaude Opus 5.5 cf7814d19c Lower fs statSync/lstatSync/fstatSync with { bigint: true }
Node answers a BigIntStats when the stat call is passed `bigint: true`: the
same snapshot, read through a bigint member table. Every number member of
Stats reads as a bigint — the integers, the `*Ms` millisecond members (the
truncated nanosecond quotient, as the binding computes them) and the
BigIntStats-only `*Ns` nanosecond members (sec * 1e9 + nsec, exactly) —
while only the four time members stay Dates. scriptc refused the option
outright.

The representation is static, chosen by the option literal: the lowerer
validates `bigint` against the call's result type (so
`{ bigint: true, throwIfNoEntry: false }` still narrows
`BigIntStats | undefined`), refuses a runtime-valued option and an option
that disagrees with the destination, and keeps every other option key
fenced. fstatSync joins statSync/lstatSync on the same path, with the
descriptor evaluated before the options.

The IR gains a `bigintStats` handle kind beside `stats`: the same RC
family, the same non-traced leaf and non-dynamic handle treatment, so no
shape, tracing or dyn-check behavior moves for any program that never asks
for one. Its member table is a `bigstats.*` lib-call family whose Date
members reuse the existing stat reads.

The runtime support is its own unit, scr_stats_bigint.c: the snapshot
layout moved into scr_stats_private.h so the bigint getters can read the
same fields without scr_lib.c carrying their code. The unit is gated on the
existing `bigint` feature in both the runtime-pack matrix and the
compiler's from-source unit table (the sanitized lane links from that one),
and `-ffunction-sections` keeps a bigint program that never reads a member
from linking any of the getters. No typed program's IR or LLVM changes.

tests/corpus/7018-bigint-stats.ts pins the member table, the Date/bigint
split, ms/ns agreement, arithmetic on a member, lstat over a symlink,
fstatSync over a descriptor, the throwIfNoEntry union, and the plain Stats
answers of the un-optioned and `bigint: false` forms against Node.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-28 17:58:01 -07:00
Chris Tate 7a7fd8ce1d Match Node's node:path behavior from Node 24's path tests
path.posix, path.win32 and the bare path module are one module object
each, the handle process.getBuiltinModule answers, so require('path/posix'),
path.posix and the target platform's bare path compare as Node's do, a
namespace beside other values in one slot is untyped, and path[family]
reads that object. Functions read through them take checked arguments:
each string parameter passes Node's validateString under its own name
(basename's suffix first), resolve validates paths[i] only as far as its
last-first walk reads them, parse answers an untyped { root, dir, base,
ext, name }, format follows _format over any object, and
toNamespacedPath answers a non-string as it is. A JavaScript call whose
arguments are not strings, or omit one, goes through the same checks.
Typed slots keep the typed function values.

path.win32.relative lowercases both resolved paths with
String.prototype.toLowerCase and walks them by UTF-16 unit, comparing
segments instead when lowercasing changed a length, as Node does. It
folded ASCII only, so a path with İ or Ⱥ in a different case gave a
longer relative path. The function moves into its own runtime unit,
scr_path_win32_relative.c, which links with the regex unit whose case
mapping it uses. scr_path.c stays free of it, so library and
cross-target builds that link the path functions without the regex unit
still link.

A JavaScript binding whose inferred type holds a union of functions and
arrays (a test table of [fn, cases] rows, or one destructured row) takes
the checked-dynamic kind that dynFallbackType gives the unmappable
spellings of such tables. Its rows keep their functions' names, and
for-of, forEach and destructuring read the items as untyped values.
TypeScript bindings keep their typed layout.
2026-10-28 19:45:36 -05:00
Malte UblandClaude Opus 5.5 d4445ec628 Merge remote-tracking branch 'origin/ni/i6-subclasses' into ni/i7-routing
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-28 11:57:10 -07:00
Malte UblandClaude Opus 5.5 753532a3da Merge origin/main into fix/pi-wp2-stdlib-node
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-28 11:33:39 -07:00
Malte Ubl e5f57e2093 Merge remote-tracking branch 'origin/main' into HEAD 2026-10-28 08:43:13 -07:00
Malte Ubl 29a31446d3 Merge remote-tracking branch 'origin/main' into HEAD 2026-10-28 08:43:06 -07:00
Malte UblandClaude Opus 5.5 9edf1d3aa3 Merge ni/i6-subclasses into ni/i7-routing
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-28 05:42:18 -07:00
Malte UblandClaude Opus 5.5 39326c55be runtime: ship child.off/signalCode in a gated unit
Adding the six new entry points (scr_child_off_spawn/_exit/_close/_error,
scr_child_has_signal_code, scr_child_signal_code) to scr_child.c changed
the executable of every program that links the unit, although none of
them calls the new functions and IR and LLVM stayed identical: the extra
functions grow the unit's `.eh_frame` contribution, and the linker's
frame bookkeeping for the retained sections changes with it (the first
visible difference is the phdr sizes at byte offset 208). Appending them
after the unit's own functions does not help — the unit's frame section
still differs, so all 89 corpus programs that link scr_child.o got a
different binary.

Move the entries into scr_child_lifecycle.c, a unit of their own
selected only by programs that use them (feature childLifecycle, the
scr_dyn_handle_props.c pattern: the matrix's optional source list, the
RUNTIME_FEATURE_RULES rule for child.offSpawn/offExit/offClose/offError/
signalCode, and the link-feature plumbing through the executable cache,
CcOptions and RUNTIME_UNITS). scr_child.c keeps its original content and
layout; the new unit reads its registries through scr_child_private.h,
which holds the per-platform ScrChild layout both units compile against.

A program that does not call the new entries links exactly the units it
did before.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-28 05:38:17 -07:00
Malte UblandClaude Opus 5.5 efc22a2fd3 Box user module namespaces as dynamic values
A `file:` module namespace handed to typed code now boxes as Node's
namespace object instead of the "converting typed values to 'unknown'"
fence: `const u: unknown = ns`, `take(ns)`, a `Promise<typeof
import("./m")>` widened to `Promise<unknown>` (or `Promise<unknown> |
undefined`), and `await import("./m")` in an `unknown` slot all see one
non-extensible null-prototype object per module whose own properties are
the module's exports in Node's sorted order. They are enumerable
read-only accessors whose reads call the compiled namespace getter, so
they stay live, and the object carries Symbol.toStringTag "Module",
Node's strict-mode write TypeErrors, the not-extensible add error and
the ToPrimitive throw. `node:` builtins have no compiled export table
and keep their SC1101 fence.

The object is built by a new gated runtime unit,
packages/runtime/src/scr_module_namespace_dyn.c, selected by the
`moduleNamespaceDyn` runtime feature (`module.namespaceDyn` in the IR).
It adds no field to ScrDynHandleOps and no branch to a shared unit: the
core reaches it through the ordinary SCR_DYN_OBJ paths, so key listing,
for-in, `in`, JSON, identity, property reads and assignment all reuse
the existing dispatch. The compiler reuses the getter lifted for
module.namespaceValue (existing programs' IR is unchanged) and adds
moduleNamespacePromiseDynValue, one lifted async mapper per module for
the promise slots.

Zero cost: a program that never boxes a namespace links exactly what it
did. The zero-cost gate reports every program identical, with
byte-identical IR and LLVM; the runtime pack's only difference is the
new unit (0 changed runtime functions), and among the namespace corpus
programs only 8412 links it.

Evidence: tests/corpus/8412-module-namespace-untyped is a differential
program against Node in both the plain and sanitized lanes, and the
diagnostics snapshots and TypeScript 7 baselines stay unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-28 05:37:45 -07:00
Malte Ubl 8c4eb85122 Merge branch 'ni/i3-exports' into ni/i4-deferred 2026-10-27 19:14:34 -07:00
Malte Ubl a1742e6377 Merge branch 'ni/i2-handles' into ni/i3-exports 2026-10-27 19:14:25 -07:00
Chris Tate 7d670f0e2a Merge main 2026-10-27 18:53:59 -05:00
Chris Tate 1a86f94239 Merge main 2026-10-27 16:45:14 -05:00
Chris Tate cc6ee85fd7 Run JavaScript web streams on a native ReadableStream (#2411) 2026-10-27 14:20:02 -05:00
Malte UblandClaude Opus 5.5 9773dccee5 Compile executable program shards as a pool
Dev executables compiled their program shards in fixed batches of up
to eight, each batch waiting for its slowest shard before the next one
started. Keep up to eight clangs busy instead, each taking the next
shard when it finishes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-27 10:57:30 -07:00
Chris Tate f12a2b2a23 Take JavaScript ReadableStreams as fetch bodies and in instanceof by value
A JavaScript ReadableStream given to Response, Request or fetch as a body
was converted to the string "[object ReadableStream]". The fetch unit now
reads such a stream through a reader of its own. A locked or disturbed
stream gives Node's TypeError, and the chunks must be Uint8Array
when the body is consumed ("Received non-Uint8Array chunk").

instanceof against a web stream class held in a variable or passed as an
argument walked no prototype for handles and answered false. It now starts
at the class prototype of the handle. The fetch unit's own streams, readers
and controllers, such as Response.body, are instances of the JavaScript
classes, so `response.body instanceof ReadableStream` is true again.

These paths, and the fetch reader functions' hand-off to web stream
readers, are in SCR_HANDLE_CLASSES variants of scr_json.c and scr_fetch.c,
which only programs with JavaScript web streams link; other programs that
use fetch build to the same size as on main.
2026-10-27 10:00:00 -05:00
Chris Tate 8f3a8e3e63 Merge main 2026-10-27 09:58:30 -05:00
Malte UblandClaude Opus 5.5 b97b161c08 Merge the restacked base into ni/i7-routing
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-27 05:41:30 -07:00
Malte UblandClaude Opus 5.5 603fec49b9 Merge the restacked base into ni/i4-deferred
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-27 05:41:03 -07:00
Malte UblandClaude Opus 5.5 562b56a54c Merge the restacked base into ni/i3-exports
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-27 05:41:02 -07:00
Malte Ubl caee8dfef2 Merge the restacked base into ni/i2-handles
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

# Conflicts:
#	packages/compiler/src/executable/executable-cache.ts
#	packages/compiler/src/index.ts
2026-10-27 05:41:01 -07:00
Chris Tate e84d332264 Link AbortController without the fetch unit
AbortController and AbortSignal lowered to fetch.abort* entry points, so
a program that only used them linked the fetch unit with its socket,
HTTP, TLS and zlib dependencies. They now lower to the abort.* entry
points on every target, a new abortSignal feature links scr_abort.c (the
fetch unit's signal implementation built without its transport, which
WASI already ships) and the EventTarget unit, and programs that also
fetch keep the fetch unit's copy.
2026-10-27 07:05:47 -05:00
Chris Tate 9ca9c7af9d Run JavaScript web streams on a native ReadableStream
In JavaScript sources, ReadableStream, ReadableStreamDefaultReader,
ReadableStreamDefaultController, CountQueuingStrategy and
ByteLengthQueuingStrategy (globals and node:stream/web exports) are class
values over a new runtime unit, scr_webstreams.c, which follows Node 24's
webstreams step by step: underlying sources, queuing strategies, default
readers, tee, ReadableStream.from and async iteration settle their promises
in Node's order. scr_dyn_handle_class.c gives a dyn handle tag a
constructor value with a prototype, brand checks and Node's constructor
errors. Stream state carries cycle headers, so the collector reclaims
streams whose callbacks close over their controllers.

Promise steps run as native promise reactions, a variant of scr_async.c
that only programs with web streams link; every other program links the
default object. TypeScript sources keep their static web stream lowerings.
Byte streams, BYOB readers, pipeTo and pipeThrough throw SC2020.

node24-web-stream-readers and node24-web-stream-readable-lifecycle now
match Node.
2026-10-27 06:53:26 -05:00
Chris Tate 659a74acc4 Merge main 2026-10-27 02:27:10 -05:00
Chris Tate 140990d257 Add --large-pages: move executable code onto transparent huge pages
A large program's hot code spans hundreds of 4 KiB pages, more than the
instruction TLB holds. tsc-ts has 12 MB of code, and 90% of its
main-thread samples at --checkers 1 fall in 274 distinct code pages.

`scriptc build --large-pages` links the new runtime unit
scr_text_pages.c, whose constructor moves the executable's code onto
2 MiB transparent huge pages before main. It copies the executable
segment, from its first to its last 2 MiB boundary, into anonymous memory
advised MADV_HUGEPAGE, makes the copy read-only and executable, and moves
it over the original range with one mremap, so addresses and instructions
stay the same. The move runs only on Linux, only when THP is "always" or
"madvise", and only in executables. If a step fails, the code stays
file-mapped. Like Node's --use-largepages, it is off by default: without
the flag the unit is not linked, and executables are unchanged.

The move costs about 3 ms per 10 MiB of code at startup, and all of the
moved code stays resident as private memory. The flag is part of the
executable cache key and the runtime-pack selection, and is refused for
non-executable output, like --detached-exit.
2026-10-26 23:41:46 -05:00
Chris Tate df77671e33 Merge main 2026-10-26 23:13:34 -05:00
Chris Tate 7be9803076 Build node:zlib on the zlib Node bundles
Re-lands #2355, reverted in #2358. #2361 fixed the CI failure it exposed.
2026-10-26 18:40:07 -05:00
Chris Tate a82a0dda93 Revert "Build node:zlib on the zlib Node bundles"
This reverts #2355. The sanitized three.js nodes CI job has failed on every main commit since it landed.
2026-10-26 17:05:37 -05:00
Chris Tate c5e8a876ec Store ordinary objects as a layout plus value slots (#2353) 2026-10-26 16:18:38 -05:00
Chris Tate 133551d76e Build node:zlib on the zlib Node bundles
The runtime vendored zlib 1.3.1 and archived it at -Os, and host C-lane
builds linked the system libz, so deflate, raw deflate and gzip output
differed from Node's at every level. The vendored copy is now Node
v24.22.0's deps/zlib (Chromium's zlib, 1.3.2.1-motley-285e94b), built at
-O3 with the units and defines Node's zlib.gyp selects per target: the
SSSE3 Adler-32 and SSE2 inflate and slide-hash code on x86-64, the NEON
and ARMv8 CRC-32 code on arm64, and the portable code on wasm32. The SIMD
units check the CPU at run time and give the same output, so compressed
bytes match Node's on every target.

zlibInputs (runtime-pack-common/scripts/zlib-inputs.mjs for the runtime
packs, vendor-inputs.ts for Zig cross builds and the sanitized runtime)
holds the per-target table, and a unit test keeps the two identical.
Every C-lane driver now links the vendored objects instead of -lz.
Chromium's BSD license ships beside zlib's in the runtime packs.

Corpus 8405 compares compressed bytes with Node across levels,
strategies, memLevel, windowBits, dictionaries and flushing streams;
8406 covers chunked inflation with small input pieces and output chunks.
2026-10-26 15:12:51 -05:00
Malte Ubl b2fc8c1cd8 Merge remote-tracking branch 'origin/ni/i6-subclasses' into HEAD 2026-10-26 11:28:52 -07:00
Malte Ubl a8067be282 Merge remote-tracking branch 'origin/ni/i3-exports' into HEAD 2026-10-26 11:26:23 -07:00
Malte Ubl 0d054133ad Merge remote-tracking branch 'origin/ni/i2-handles' into HEAD
# Conflicts:
#	packages/compiler/src/frontend/lowering/lower-classes.ts
2026-10-26 11:25:53 -07:00
Malte Ubl 3a5aab5b2f Merge remote-tracking branch 'origin/ni/i1-stack-bound' into HEAD
# Conflicts:
#	packages/compiler/src/cli/command.ts
#	packages/compiler/src/executable/executable-cache.ts
#	packages/compiler/src/frontend/lowering/builtins/module-resolution.ts
#	packages/compiler/src/frontend/lowering/lowerer.ts
#	packages/compiler/src/index.ts
#	packages/runtime/src/scr_json.c
#	tests/harness/npm.test.ts
2026-10-26 11:22:14 -07:00
Malte UblandClaude Opus 5.5 dc1e8acc1d Store ordinary objects as a layout plus value slots
A checked object (SCR_DYN_OBJ) now starts in layout mode: v.obj.layout names
its key sequence, a node of the thread's interned transition tree, and
v.obj.slots holds one 8-byte value per key, where every property used to take
a 48-byte entry. All properties of a layout-mode object are writable,
enumerable, configurable data properties. An accessor, a non-default
attribute, a delete, more than 64 keys, spent layout budgets, publication to
other threads, or any code that reads entries directly moves the object to
dictionary mode (today's entries) for good, so unported paths stay correct.

Layouts replace the dynamic tier's lazily computed hidden classes (ScrJsShape)
and keep their budgets. Inline caches key on the layout pointer and read the
slot directly; JSON.parse builds objects along layouts; stringify, key
enumeration, release, trace and the runtime read cache read slots in place.
The emitted index-signature checks read members through runtime accessors.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-26 09:18:53 -07:00
Chris Tate c3b265f68c Merge main 2026-10-26 04:39:20 -05:00
Chris Tate 98ed422405 Merge ni/i6-subclasses and cascade ES-module routing 2026-10-26 01:00:45 -05:00
Chris Tate a21f4f6823 Localize merged program shards in process on Linux
A dev build splits a large program into LLVM shards, compiles them in
parallel and merges the objects with ld -r. On a native Linux host the
merge then ran objcopy --keep-global-symbols to turn the shards' promoted
symbols back into locals. A sanitized build of three.js's node materials
merges into a 660 MB object with 278k sections, and objcopy spent 425 s
of a 616 s build on it. localizeElfObject, which cross-ELF builds already
use, does the same demotion in about 2 s.

- Program-shard merges for executables, library archives and packed
  libraries use localizeElfObject after ld -r on native Linux. Library
  runtime localization keeps objcopy.
- localizeElfObject reads and writes extended section numbering: with
  SHN_LORESERVE or more sections the counts go in section 0, and symbol
  section indices from SHN_LORESERVE up go in .symtab_shndx. Before, it
  truncated e_shnum and read such indices as reserved values.
- The Linux shard-merge cache identity drops objcopy.
2026-10-25 22:55:22 -05:00
Chris Tate ec6db73f6b Merge ni/i3-exports 2026-10-25 22:15:54 -05:00
Chris Tate 8a71a20e32 Merge ni/i2-handles 2026-10-25 21:39:21 -05:00
Chris Tate 59e78f298a Name the package declaration and its patch when a declared-type check fails (#2268) 2026-10-25 21:20:34 -05:00
Chris Tate 28b76f7b25 Merge ni/i1-stack-bound 2026-10-25 21:08:37 -05:00
Chris Tate 8981b5c0a2 Resolve computed createRequire specifiers at run time (#2079) 2026-10-25 20:29:51 -05:00