Commit Graph
743 Commits
Author SHA1 Message Date
Chris Tate 714dbfe2bb Merge main 2026-10-22 06:59:47 -05:00
Chris Tate 3edec984d5 Compile JSX with the classic runtime (#1786) 2026-10-22 06:29:40 -05:00
Chris Tate df6de14c94 Support new tls.TLSSocket and tls.connect({ socket }) (#1791) 2026-10-22 06:01:47 -05:00
Chris Tate daac2a4a15 Support objectMode streams with any value as a chunk (#1785) 2026-10-22 05:55:45 -05:00
Chris Tate 01a467060b Support WebCrypto wrapKey and unwrapKey
crypto.subtle.wrapKey exports a secret key (raw, raw-secret or jwk) and
encrypts it, and unwrapKey decrypts and imports one, as Node's
webcrypto.js does: the wrap algorithm normalizes through the wrapKey
registry (AES-KW) and then the encrypt registry (AES-GCM, AES-CBC and
AES-CTR with their dictionaries), the wrapping key's algorithm and
usages are checked, and a JWK payload is its JSON text, padded with
spaces to a multiple of 8 bytes for AES-KW. A wrapped JWK that is not
UTF-8 JSON with a kty member rejects with Node's DataError.

AES-KW is RFC 3394 key wrap over the vendored mbedTLS block cipher, in
scr_webcrypto_aes.c beside the other AES modes: wrapping takes a
multiple of 8 bytes and at least 16, unwrapping a multiple of 8 and at
least 24, and the integrity check value is compared in constant time.
The cipher's failures, and unwrapKey's import errors, reject on a later
loop turn as Node's job promises do.

This completes jose's symmetric key management: 7132 runs the real
jose 6.2.12 PBES2-HS256+A128KW, PBES2-HS384+A192KW, PBES2-HS512+A256KW,
A128KW and A256KW paths (EncryptJWT and jwtDecrypt) against Node.
getPublicKey replaces wrapKey as the refused method in the fenced
fixture and the JavaScript refusal test.
2026-10-22 05:48:30 -05:00
Chris Tate 1360830149 Support new tls.TLSSocket and tls.connect({ socket }) 2026-10-22 04:52:44 -05:00
Chris Tate c47a0e90ae Compile JSX with the classic runtime
Fixes #889
Co-authored-by: papack <33105506+papack@users.noreply.github.com>
2026-10-22 04:27:35 -05:00
Chris Tate 67247dd26b Support objectMode streams with any value as a chunk
Readable, Writable, Duplex, Transform and PassThrough take the
objectMode option, and a Duplex, Transform or PassThrough the
readableObjectMode and writableObjectMode options, as true or false
literals in an options object literal or in an options value. An
object-mode side holds any value that converts to a checked-dynamic
value: push, unshift, write, end, pipe, pipeline, for await, 'data'
listeners, Readable.from and the transform callback carry values in
TypeScript and JavaScript, and constructor-function streams and options
held in variables do the same. The object-mode high-water mark counts
values and defaults to 16. A readable buffer that mixes strings, Buffers
and other values holds them all, and push(undefined) and other values on
a byte-mode stream report ERR_INVALID_ARG_TYPE through 'error', as in
Node.

In typed code a write or transform callback, or a _write or _transform
method, may declare its chunk with any such type, which converts when
the callback runs. Writes pass the encoding argument to _write and
_transform as Node does: undefined when absent, null when a callback
took its place, otherwise the value unchanged. A parameter declared
string | null | undefined receives that value and one declared string
receives "" for undefined and null. write(chunk, encoding) on a
byte-mode stream encodes a string chunk with any known encoding and
throws ERR_UNKNOWN_ENCODING for an unknown one. Readable.from takes
typed Maps and Sets of any such value, and arrays that mix strings and
Buffers.

for await over a readable-only stream that will emit 'close' now ends
after 'close', as Node's eos does. Buffered writes that complete inside
clearBuffer run their callbacks in order, and 'finish' waits for a
deferred afterWrite that carries a callback, as Node's pendingcb does,
unless a _final callback schedules it.

The 'data' listener adapters take three slots and write and transform
callbacks read their chunk and encoding through runtime accessors, so
RUNTIME_ABI_VERSION is now 26.
2026-10-22 04:21:55 -05:00
Chris Tate b5fb4115e1 Merge main 2026-10-22 03:33:29 -05:00
Chris Tate 7d20212dd4 Merge main 2026-10-22 03:24:23 -05:00
Chris Tate fe02190d1d Compile JavaScript-path calls that Node runs (#1756) 2026-10-22 03:15:48 -05:00
Chris Tate 921f054891 Extend a generic factory's local class returned through a constructor type (#1761) 2026-10-22 03:15:25 -05:00
Chris Tate 1773ffbef3 WebCrypto: the crypto global, node:crypto's webcrypto, and secret-key SubtleCrypto 2026-10-22 02:52:01 -05:00
Chris Tateandsaboursepideh 1a240fbf10 Support asymmetric keys, signatures and Ed25519 in node:crypto and WebCrypto
Co-authored-by: saboursepideh <25549001+saboursepideh@users.noreply.github.com>
2026-10-22 02:49:33 -05:00
Chris Tate d426960f45 Make Cipheriv and Decipheriv Transforms and accept function stages anywhere in pipeline()
Cipheriv and Decipheriv values are Transforms, as in Node: they can be
written and ended, piped into, used as pipeline() and stream/promises
pipeline stages, composed, read, iterated with for await, read by
stream/consumers, passed to finished(), and listened to, and their stream
properties read as in Node. Each chunk goes through the cipher's
update(), and the end pushes final() or reports its error through
'error'; writing after final() throws out of write(). The stream unit
builds a cipher's Transform on its first stream use from operations the
cipher's handle provides (ScrDynHandleOps.transform), so programs that
use ciphers without streams link as before. The cipher is a
collector-headered object behind a traced handle: its Transform's steps
hold it, and the pair is reclaimed as a cycle. Through untyped values, in
a program that uses streams, a cipher's stream properties and its on,
once, off, pipe, unpipe, read, pause, resume, setEncoding, write, end,
destroy, cork and uncork members run on its Transform; other stream
members throw a coded SC2020 error.

A Transform holds a write's callback while its readable side is at the
high-water mark and runs it at the next read, and runs it on a tick once
the readable side has ended, as Transform._write does. write() on a
PassThrough, a Transform, a Hash or a cipher used to return true however
much the readable side buffered. zlib streams applied the rule already
and keep their timing.

pipeline() accepts function stages anywhere among its streams, so runs of
async generator stages can sit on both sides of a stream. A stage can be
named by a function declaration, or a const bound to a function
expression, that nothing reassigns; its body lowers again at the
pipeline with its source typed by the pipeline. The source can also be
an array, a string, a synchronous generator object, or a synchronous
generator function without parameters: an async generator steps the
source's iterator and yields each element, and the next stage receives
each element on the same tick as with Node's for await over the source.
The runtime's function-stage entry points take a bitmask of the function
positions and one (next, read, return) helper triple for each generator
pumped into a stream, so RUNTIME_ABI_VERSION is now 25. In JavaScript,
the chunks a stage receives from the previous stage's generator read as
the strings or Buffers it yields, so their string and Buffer methods
compile.

Corpus 7055-7059 cover cipher streams in TypeScript and JavaScript, their
errors, close order and backpressure, the pipeline stages and sources
with their tick order, and Transform backpressure.
2026-10-22 01:55:38 -05:00
Chris Tateandblurname d68b73e3b4 Extend a generic factory's local class returned through a constructor type
A generic factory declared to return a constructor type (`Struct<S> =
(abstract new () => S) & { decode }`) whose every return gives back one of
its own local classes (`return Base as Struct<S>`) is now that local
class's factory: each instance has its own class, the result maps to the
class value while the instance lowers and at its calls, and `class X
extends struct(...)` extends it like a factory returning the class
directly. Before, the result type mapped to nothing (SC2008) and the
extends clause refused as a computed expression.

Static methods of a function-local class in a TypeScript file now lower as
the class value's runtime statics, as they already do in JavaScript files:
they read the enclosing function's bindings, call each other through
`this`, and inherit into a subclass. Before, they refused with SC1090.
Static fields and static blocks of such classes keep the fence.

Fixes #1372

Co-authored-by: blurname <50226652+blurname@users.noreply.github.com>
2026-10-22 01:10:25 -05:00
Chris Tate 5a414300a9 Store references to ProgramLifetime instances in 4 bytes on Linux (#1748) 2026-10-22 01:02:43 -05:00
Chris Tate 321873b957 Compile JavaScript-path calls that Node runs 2026-10-22 00:18:36 -05:00
Chris Tate 5bdc3be385 Name function expressions by their source names in SC6101 and skip type names and generated helpers 2026-10-21 23:25:48 -05:00
Chris Tate ecee5fdaaf Store references to ProgramLifetime instances in 4 bytes on Linux 2026-10-21 23:25:33 -05:00
Chris Tate f4ab3572c1 Accept file URL paths in synchronous fs functions (#1656) 2026-10-21 19:36:18 -05:00
Chris Tate 029906d861 Merge main 2026-10-21 19:34:10 -05:00
Chris Tate c29ce641b2 Count only the repeated parts of loops as likely-hot work
A `for` loop's initializer and a `for...of` loop's iterable run once, but
SC6101 counted any dynamic operation inside a loop statement's span, so
untyped setup there produced the warning and failed --fail-on=dynamic.
Loop work is now found by walking the IR: a loop's condition, update and
body repeat, its initializer and iterable do not. Locations could not
tell them apart, because lowering gives the index loop of a `for...of`
the whole statement's span.
2026-10-21 19:26:00 -05:00
Chris Tate fa7bc7c7a0 Warn about likely-hot functions that use untyped values
A build now names the functions of the program's own files that compile
to the dynamic tier and likely run often, so a slow program with a little
untyped code says where the time goes:

  warning SC6101: 3 functions that likely run often use untyped values, which runs slower than typed code:
    advance (nbody.js:22): untyped JavaScript file, in a loop
    ...

- The functions come from the tier assignment lowering already makes
  (Lowerer.dynamicFunctions). Without a profile, a function counts as
  likely hot when a dynamic operation runs inside one of its loops, when
  a loop calls it, or when it calls itself (lowering/dynamic-tier.ts).
- A function is named only when its source says why its values are
  untyped: an `any` (for a for...of variable, the value it iterates), an
  untyped JSON.parse result, or an untyped JavaScript file. Generated
  %dyn.* helpers, module top levels and functions that are dynamic for an
  operation without a static lowering are left out.
- At most five functions are named, most likely hot first, with the
  total; dynamic-tier code in npm packages is counted per package on one
  line. All of the warning's text is in diagnostics/dynamic-tier.ts.
- SC6101 opens the SC61xx band, performance notes, with a new
  "performance" category in the diagnostics envelope; SC60xx stay the
  divergences.
- scriptc build prints it after the divergence warnings, and the
  executable cache stores it with the entry, so a cached build (including
  the CLI's routed fast path) repeats it. scriptc coverage reports it, and
  a comment-only edit no longer replays a verdict that carries it, since
  its text holds line numbers.
- --no-dynamic-tier-warning leaves it out of build and coverage;
  coverage --fail-on=dynamic fails on it.
- Docs: coverage.mdx (Dynamic tier, Gating builds and CI, the envelope
  categories) and cli.mdx.
2026-10-21 18:53:43 -05:00
Chris Tate 1c2b80040b Deliver queued worker messages before immediates, as Node does (#1682) 2026-10-21 15:05:13 -05:00
Chris Tate fc38e62791 Stop importing runtime bitcode into program builds 2026-10-21 14:10:23 -05:00
Chris Tate 3a139a8d40 Negotiate ALPN and bound the TLS version in tls.connect and tls.createServer (#1664) 2026-10-21 13:52:22 -05:00
Chris TateandQuino Terrasa 1bf49286d1 Document when builds use multiple cores
How It Works gains a Build parallelism section: the frontend runs on one thread, executables divide into
partitions by the size of their LLVM text, the LLVM helper compiles partitions on a thread per hardware thread,
what dev and optimized builds run in parallel and in order, what a rebuild compiles again, and what
profile-guided optimization adds to build time. The CLI reference links to it from --emit, --optimization,
--profile-use, SCRIPTC_CACHE_DIR, and Profile-guided optimization.

Fixes #1340

Co-authored-by: Quino Terrasa <26749475+espetro@users.noreply.github.com>
2026-10-21 12:50:36 -05:00
Chris Tate 9a504b6f0d Share one listener list between typed and untyped uses of an EventEmitter (#1563) 2026-10-21 11:18:16 -05:00
Chris Tate aea358eed5 Freeze, seal and query functions through their property tables (#1657) 2026-10-21 11:01:40 -05:00
Chris Tate 96c82719b6 Deliver queued worker messages before immediates, as Node does
A thread's event loop delivered one message from its workers or its
parent per loop turn, so timers and setImmediate callbacks ran between
messages that were already queued. Node's MessagePort delivers, in one
callback, the messages queued when it starts and those that arrive
meanwhile until it has taken 1000, with a microtask checkpoint after
each, before the loop goes on to the check phase.

- scr_worker_dispatch starts a batch when it delivers a message: the
  messages queued then, and at least 1000 in all.
- scr_workers_more continues the loop pass while the next event in the
  thread's mailbox is a message and the batch has room; other events
  (online, exit, error, stdio) keep their order and their turns.
- scr_mailbox_head_kind and scr_mailbox_count_kind read the mailbox's
  first event kind and the number of queued events of a kind.
- Corpus 6211 checks the order of messages, nextTick, microtasks and
  setImmediate for worker-to-parent and parent-to-worker messages
  against Node.
2026-10-21 10:07:56 -05:00
Malte Ubl f798f6736c Merge remote-tracking branch 'origin/main' into opt/emitter-mixed-events 2026-10-21 07:53:10 -07:00
Malte UblandClaude Opus 5.5 3f3e90b846 Drop the EventEmitter method-values follow-up
Revert 3a27a92cb ("Forward at most four emit arguments through
EventEmitter method values") and 0cf62ebec ("Answer EventEmitter methods
read as values off compiled emitters"). Under the scope-B plan,
EventEmitter methods read as values, `EventEmitter.prototype.on` in
JavaScript and constructor-function emitters belong to Node islands, not
the native compiler. The typed/untyped listener sharing and the
catchable throw in place of the trap stay.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-21 07:53:10 -07:00
Chris Tateandsaboursepideh 9b70fb5e7b Negotiate ALPN and bound the TLS version in tls.connect and tls.createServer
tls.connect and tls.createServer accept ALPNProtocols as an array of
strings, a Buffer or a Uint8Array in the wire format. The server answers
with the first of its own protocols that the client offers, and both
sockets report it as alpnProtocol. A client that offers none of the
server's protocols fails with ERR_SSL_TLSV1_ALERT_NO_APPLICATION_PROTOCOL.

minVersion and maxVersion bound the handshake, and getProtocol() reports
the version it picked. Ranges that do not overlap fail with Node's
ERR_SSL_TLSV1_ALERT_PROTOCOL_VERSION or ERR_SSL_NO_PROTOCOLS_AVAILABLE,
and invalid versions throw Node's TypeError. tls.connect's timeout option
arms the socket's inactivity timeout before it connects.

Refs #1438

Co-authored-by: saboursepideh <25549001+saboursepideh@users.noreply.github.com>
2026-10-21 08:58:29 -05:00
Chris Tateandjlucaso1 f168402e1f Name the reason in the CompileError for untranslated WebAssembly modules
A WebAssembly module the build finds but does not translate (one that uses
a proposal outside the translated feature set, an invalid one, or one the
translator rejects) now rejects with a CompileError that says the module
was found and names the reason, for example "it uses simd, outside the
translated WebAssembly feature set". Bytes the build did not see keep the
existing message.

The build emits the hash, length and reason of each such module in a
second table, scr_wasm_untranslated, next to scr_wasm_registry; the runtime
reads it when a lookup misses. WebAssembly.validate still answers false for
these modules, and the error stays a catchable CompileError.

Fixes #1617

Co-authored-by: jlucaso1 <55464917+jlucaso1@users.noreply.github.com>
2026-10-21 08:52:18 -05:00
Chris Tateandsaboursepideh 726c2ce3d7 Freeze, seal and query functions through their property tables
A plain function's own properties (name, length, prototype, and the ones a
program assigns, as in `hash.outputLen = 32`) live in its property table.
Object.freeze, Object.seal and Object.preventExtensions of a function now
act on that table and answer the function, and Object.isFrozen,
Object.isSealed and Object.isExtensible answer from it, as in Node:

- Object.freeze of a function was refused as a possibly-aliased value,
  and Object.isFrozen of one threw SC2020;
- Object.isExtensible answered false and Object.isSealed true for every
  function.

Adding a property to a frozen or non-extensible function throws Node's
TypeError; assigning a read-only one throws the runtime's existing
read-only TypeError. Class constructors keep their fences. This lets
@noble/hashes' createHasher return its frozen hasher, so the sha256
program in the issue prints the digest Node prints.

Fixes #1437

Co-authored-by: saboursepideh <25549001+saboursepideh@users.noreply.github.com>
2026-10-21 08:47:37 -05:00
Chris Tateandjlucaso1 d858d840f9 Accept file URL paths in synchronous fs functions
readFileSync, writeFileSync, appendFileSync, statSync, lstatSync,
readdirSync, mkdirSync, rmSync, rmdirSync, mkdtempSync, accessSync,
unlinkSync, chmodSync, chownSync, lchownSync, truncateSync, copyFileSync,
renameSync, realpathSync, realpathSync.native, openSync and opendirSync
accept a file: URL wherever they accept a path. The URL converts as
fileURLToPath converts it, with Node's errors for other schemes, hosts,
encoded separators and null bytes.

Arguments after a URL path that have side effects are evaluated into
hidden locals before the conversion, so they run even when it throws. A
URL path after a string path, or with options that are not literals,
fails with SC2020, because Node checks those arguments before it converts
the URL. existsSync and the fs.promises functions also fail with SC2020
for URL paths: existsSync answers false for a URL it cannot convert, and
fs.promises reports conversion errors as rejections.

Fixes #1616

Co-authored-by: jlucaso1 <55464917+jlucaso1@users.noreply.github.com>
2026-10-21 08:46:44 -05:00
Chris Tate 8065be3ccd Add profile-guided optimization 2026-10-21 03:13:45 -05:00
Chris Tateandejc3 2078392a52 Compile the typed-array callback methods
forEach, map, filter, some, every, find, findIndex, findLast,
findLastIndex, reduce and reduceRight compile on every typed-array kind
through one shared helper per method shape. The receiver, the callback and
reduce's initial value evaluate in order before the helper runs; the
helper reads the length once and each element when it visits it, so the
callback's writes show in later elements. Callbacks receive the element,
its index and the typed array (reduce's receive the accumulator first).
map and filter return a new typed array of the receiver's kind, map
storing each result with that kind's element conversion, and a Buffer's
map and filter return a Buffer. reduce without an initial value throws
Node's TypeError on an empty typed array. A thisArg keeps the fence, as
for arrays.

Fixes #1620

Co-authored-by: ejc3 <1694850+ejc3@users.noreply.github.com>
2026-10-21 02:55:08 -05:00
Chris Tate 012fdb3fb0 Merge main 2026-10-20 21:52:19 -05:00
Chris Tate 24eb054395 Compile the hot-shots StatsD client over UDP (#1568) 2026-10-20 21:29:48 -05:00
Chris Tate 36a5ff72d2 Put each limitations sentence on its own line 2026-10-20 21:12:48 -05:00
Chris Tate 3abf43affd Use the events module and EventEmitter as untyped constructor values (#1544) 2026-10-20 21:12:06 -05:00
Chris Tate d59d8ff6f2 Merge main and count declaration writes to parameters 2026-10-20 21:05:19 -05:00
Chris Tate eb8a148605 Merge main 2026-10-20 21:01:40 -05:00
Chris Tate 115a763df8 Check that the limitations partials keep one sentence per line (#1571) 2026-10-20 20:59:02 -05:00
Chris Tate 06e202a126 Leave the per-chapter status counts out of the compatibility files
The ledger and the public artifact held a block of status counts for
every chapter. Nothing reads them (the docs page uses a chapter's slug
and title), and every classification change rewrote its chapter's block,
so concurrent compatibility changes conflicted there. The rows they were
counted from are unchanged.
2026-10-20 20:46:25 -05:00
Malte UblandClaude Opus 5.5 3a27a92cb4 Forward at most four emit arguments through EventEmitter method values
The dispatch instantiates one bridged emit per argument count and emitter
class; capping it at four keeps programs that only read emitter members
through computed keys from growing by the full table.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-20 18:22:43 -07:00
Chris Tate 08c7930958 Support crypto.createECDH and ECDH.convertKey (#1589) 2026-10-20 20:18:22 -05:00
Malte UblandClaude Opus 5.5 0cf62ebecf Answer EventEmitter methods read as values off compiled emitters
Reading an EventEmitter.prototype method off a compiled emitter (a program
class extending EventEmitter, or new EventEmitter()) through an untyped
value answered undefined (`typeof o.on`, `const f = o.on; f.call(o, ...)`,
`o.emit.bind(o)`), and EventEmitter.prototype.on in JavaScript was a fence.

Such reads, typed value reads, computed keys, `in`, and
EventEmitter.prototype.<method> now answer the runtime's
EventEmitter.prototype functions, the ones constructor-function emitters
inherit, so `o.on === o.addListener === EventEmitter.prototype.on` and
names and lengths match Node. The first read installs a program dispatch
that those functions call for a compiled emitter receiver; it invokes the
method through the existing untyped dispatch with the call's argument
count (emit forwards up to 16 arguments after the name, then fences).

An untyped emit on, or read of emit from, a class that overrides emit now
fences instead of bypassing the override for the native emit. A subclass
visited under the boxed EventEmitter capsule keeps its own dispatch plan
for values boxed as itself.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-20 17:36:37 -07:00