0.4.9
Highlights
- Faster programs:
- Typed-array and dense
number[]loops run as lane loops: elementwise stores and dense sums, counts and stores run whole vectors and finish the remainder with the scalar loop. The typed-array benchmark runs 1.83x faster, and integer sums and counts 1.56x. - Typed arrays copy in bulk where the element kinds allow it: construction,
set()andTypedArray.from(view)between typed arrays move same-width integer pairs with onememmoveand convert every other pair in one loop. AUint8ClampedArrayover a decoded buffer runs 1.9x faster, at 1.19x Node's time instead of 2.39x. - A callee called in a loop with an array parameter of one known length is compiled for that length, so the loop loses a per-element hole guard: n-body at 50,000,000 iterations is 5.0% faster.
- Byte loops that count a constant product and byte loops that index downwards are versioned on their trip count: 1.31x and 5.98x on probes of those shapes (1,305 ms to 1,000 ms and 1,140 ms to 190 ms).
- Dynamic computed access has a faster path: the fasta benchmark port runs at 1.73x Node's time instead of about 3.2x, a byte-array store falls from 63.3 to 20.4 ns per operation, and an indexed
charCodeAtfrom 172.0 to 90.2 ns. Buffer.writewith a latin1 or ascii encoding uses the word-wise encoder: a 16 MB write is 55% faster, from 3.9x to 1.75x Node's time. Indexed scans of long non-ASCII texts are 47-51% faster.- Untyped
process.stdout.write(text)andprocess.stderr.write(text)calls reach one runtime call: 10,000,000 writes to/dev/nulltake 1.38 s instead of 3.13 s. The opt-in--stdout-bufferingkeeps console output in the 64 KiB buffer when stdout is not a terminal: 10,000,000console.loglines to a pipe take 0.54 s instead of 6.3 s. - The collector does less work at collection points and sizes its nursery window from what it collects: channel-messages is 12.4% faster and worker-messages 5.9% faster. Object chunks of 1 MiB or more come from huge pages, an async function's statement frame stays out of a group when nothing in it awaits, and depth-21 binary-trees runs at 0.84 of its former wall time with 199 MB of peak memory instead of 253 MB.
- Large immutable number tables are built in the program image, so a small CLI program starts in 1.21 ms instead of 1.51 ms. An optional-element array read skips the runtime getter (13% faster, 35% fewer instructions), and 200,000 calls through builtin function values take 4.6 ms against Node's 75.3 ms.
--cpu-dispatchcopies only the functions whose body has a loop: 1.305x on mandelbrot and 1.068x on n-body against the default build.
- Typed-array and dense
- Faster builds:
- A failing program's whole-slot relower runs on the pass whose result is kept, after the general-streams relower: a large program that takes the general-streams relower builds 1.22x faster (1,141.9 s to 932.5 s cold).
- A build whose inputs did not change returns the executable already on disk, without reading a source, lowering, emitting LLVM, generating objects or linking: a no-change build takes 16.6 ms instead of 71.9 ms, 17.6 ms instead of 426.8 ms with an LLVM helper set, and 58.6 ms instead of 959 ms with a profile and an FFI manifest.
- Runtime-pack selections and link-driver jobs are kept in the build cache: cold builds take 16.3 ms less on average over 369 paired builds.
- Partition groups are numbered from the file name relative to the entry module, so a warm one-function edit that adds a module recompiles 3 of 64 partitions and the module-init pass is 19.5% faster.
- A build's repeated frontend loads share one checker project, so parsed files carry between passes: a large multi-package program builds 15.9% faster (652.96 s to 549.05 s). Source parses are cached and declaration type queries batched (59,075 source-parser requests become 2,595), alias answers are memoized for one load (573,047 requests become 190,448), and npm-static package selection is memoized per build: in a separate pairing on the same program the build falls from 634.0 s to 404.6 s and the island reloads from 222.4 s to 188.1 s.
- Lowering a large program reads each mapped symbol name once, builds union arm keys once per sort and answers the library-file question once per file: a large compiler build's coverage analysis falls 20.2%, from 1,360.20 s to 1,085.14 s.
- A build of the compiler's own sources loads a 3.1-3.8 MB frontend graph instead of 15.7 MB, and one analyze and compile of a fixture falls from 111.9 s to 24.6 s. The bundled-library scan asks once per directory, 1.2 ms to 0.1 ms.
- Windows release executables request no debug info: hello world is 146,432 bytes instead of 1,803,264 bytes. Analysis peak memory falls 8.7% on a 1000-module graph and from 1207 MB to 1053 MB on 3000 modules, and a macOS type check's peak memory falls 19.0-21.5% with compact class references.
- Large shared buffers get a mapping of their own: a 16-checker type check's peak memory falls from 2856 MB to 2805 MB.
- Node.js parity:
- node:dns lowers
lookup,lookupService,getServersand the default result-order functions with Node's argument ladder, error codes and error properties, and its address, hint and error-code constants come from the target libc. - node:tls accepts
tls.createServer({ SNICallback })without a defaultcert/keypair. - node:readline interfaces have
prependListener,prependOnceListener,removeListener,off,removeAllListeners,listenerCountandeventNames, with removal by the registered function, andquestion(query, { signal }, callback)cancels on abort. process.allowedNodeEnvironmentFlagsis the target'sNODE_OPTIONSflag names,processemits'beforeExit'and hasabort(), andChildProcessreportsspawnfileandspawnargs.- child_process accepts stdio arrays past fd 2,
detached,options.signalandstdio: "inherit", and spawnSync's error carries Node's errno, code, syscall, path and spawnargs fields. Buffer#toJSONreturns Node's snapshot,Blob#stream()andBlob#textStream()return real ReadableStreams,URL.createObjectURL,URL.revokeObjectURLandbuffer.resolveObjectURLshare Node's table, and fetch sends Blob and File bodies as their bytes with the blob's type.url.Urlis Node's legacy constructor, strict-mode property writes name bigint and symbol receivers,util.formatwalks Error values for%j, and typed emits reach listeners registered through untyped values witherrorMonitorfirst.- Native handles accept own properties, and a class instance's getter-backed fields read through its checked view.
process.reportis answered by the Node island, node:test's context hast.runOnlyandt.waitFor, and static builds read/etc/passwdforos.homedirandos.userInfoand throw Node's catchableSystemError.- The compatibility ledger records the implemented web globals, the
Headersclass and the node:diagnostics_channel surface against the programs that exercise them.
- node:dns lowers
- TypeScript parity:
- Every dynamic-to-typed conversion asks one predicate, and an optional record field whose type maps to the checked-dynamic slot can be absent, so
deleteworks and an omitted field is absent toin,Object.hasOwn,Object.keys,util.inspect,JSON.stringifyand spread. new Set(x)andnew Map(x)seed from a checked-dynamic iterable, and a Map or Set arm of a class field's undefined-armed union lowers.- A function's own properties use one representation, a class instance passed to a callee that reads its fields keeps its record shape, and accessor literals for intersections, unions and mapped types keep their checked-identity view.
- Method calls on a union receiver with a primitive arm lower,
&&spreads that drop the left operand's widened arm lower,Promise.allover a mixed armed-union array literal lowers, andObject.assigntakes dynamic sources into index-signature records. - Overrides that append trailing optional or defaulted parameters, optional captures, computed keys and variadic function values lower.
- Builtin members read as values compile:
Function.prototype'scall,applyandbind,RegExp.prototype'sexec,testandtoString,DataView.prototype, builtin functions carrying Node'snameandlength, and anhttp.requestalias or a client chosen at run time. - Packages that declare their data shapes in declaration files compile, as do package module edges reached through literal
require()andimport()calls, a package's own JSON documents, and rest bindings from a CommonJS module's export names. - More JavaScript semantics compile in typed sources: BigInt operands of the arithmetic, shift and bitwise operators, symbol to string coercion,
Object.prototype.__proto__on dynamic receivers, rest bindings over index-signature sources, anddeletethrough an erased assertion. - Five diagnostics are admitted for JavaScript sources, and a project that checks its JavaScript still reports them.
- Every dynamic-to-typed conversion asks one predicate, and an optional record field whose type maps to the checked-dynamic slot can be absent, so
- CommonJS and islands:
- A constant-decided top-level
ifexports what its branch exports, a failed CommonJS initializer retries on the nextrequire(), and a shared module's exported function exists before the module evaluates. - A local JavaScript import answers its runtime sibling through a typed sidecar, and shared exports publish between declarators.
- An island's module tree stops at a type-only import, and type-only island edges ship as the no-ops Node erases.
- A union type alias a package declares over Node data types lowers as the checked dynamic value, and a callable type from a types-only package stays the island's function handle.
- A constant-decided top-level
- Fixes:
- A nested marker table is a brand marker, so branded primitives stay
Mapkeys. A parameter that shadows a builtin module alias is no longer reported as a patched module, and a generator declared to returnanylowers instead of reporting SC2001. - A boxed union map value widens into a pointer-union
Map.getresult, aTypeErrorfor a non-callable callee names the callee as V8 does, and the unwinding ABI check no longer counts a source-frame push as a pending-flag test. - The LLVM context is declared before the target machine in every partition compiler, which removes the intermittent aarch64 macOS dev build crash and timeout.
- An own property written through a checked view of an
Errorsubclass is kept, and a member read that only an asserted intersection declares answers what the object carries. - The child handle wrapper is installed only when the program links the child-process unit, and the stdio slot order program spawns
/bin/shso it runs on macOS. - SC3004 reports the LLVM helper's exit status or signal together with the output it wrote.
- Reflection on a
Proxyvalue answers its own keys again, and builtin prototypes and array-likeconcatmatch Node.
- A nested marker table is a brand marker, so branded primitives stay