Commit Graph
1551 Commits
Author SHA1 Message Date
Chris Tate 714dbfe2bb Merge main 2026-10-22 06:59:47 -05:00
Chris Tate 655c36c880 Match Node's AsyncLocalStorage context in async prefixes and generator steps (#1774) 2026-10-22 06:09:13 -05:00
Chris Tate fb403d5755 Clear a store that an async prefix enters in a loop callback
When an event-loop callback calls an async function or steps a generator
that enters a store before its first await, the store becomes the
callback's at the end of the prefix. On the main slot it now ends with
that callback, as an enterWith there does: the copy schedules the tick
that clears the main slot, so the store does not reach later callbacks.
2026-10-22 06:06:45 -05:00
Chris Tate 7f19e582e3 Read error constructors as values and destructure workerData in JavaScript (reland) (#1803) 2026-10-22 06:05:36 -05:00
Chris Tate df6de14c94 Support new tls.TLSSocket and tls.connect({ socket }) (#1791) 2026-10-22 06:01:47 -05:00
Chris Tate 4e834f8eed Read error constructors as values and destructure workerData in JavaScript (reland) 2026-10-22 06:00:28 -05:00
Chris Tate 3cefd421a1 Fence requestCert beside a secureContext and free failed TLSSockets
new tls.TLSSocket(socket, { isServer: true, secureContext, requestCert:
true }) skipped the options walk that refuses requestCert, so the server
asked for no client certificate and accepted every client, even with
rejectUnauthorized: true. With a secureContext, Node still reads the
socket's own options, so a truthy requestCert, SNICallback and
ALPNCallback now throw the fence there too.

A TLS socket's transport kept its 'secure' and 'session' listeners until
the socket was freed. A handshake that fails never fires 'secure', so a
listener that captures the socket kept the socket and its TLS state alive
after it closed. The transport now drops those listeners when the socket
settles, and ignores listeners added after that.
2026-10-22 05:58:32 -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 2e29fd6a8c Support asymmetric keys, signatures and Ed25519 in node:crypto and WebCrypto (#1771) 2026-10-22 05:54:11 -05:00
Chris Tate c14e2607c5 Address #1785 review: Buffer|string data adapter and objectMode chunk handling 2026-10-22 05:52:31 -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 6f11afa09e Size crypto error messages to their whole text
The key and signature errors formatted into fixed stack buffers and
passed snprintf's return value on as the message length. A message
longer than the buffer, such as "Invalid digest: " with a long algorithm
name from crypto.sign or crypto.verify, then copied bytes past the end of
the buffer into the error. The messages are now sized to their whole
text, so a long name appears in full, as in Node.

The received values in these messages also follow Node: strings as
util.inspect quotes them, cut at 128 characters, and large integers with
ERR_OUT_OF_RANGE's separators. A fractional, NaN or infinite
modulusLength reports "It must be an integer", as validateUint32 does.
2026-10-22 05:46:53 -05:00
Chris Tate 1360830149 Support new tls.TLSSocket and tls.connect({ socket }) 2026-10-22 04:52:44 -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 6e25c5ddb7 Compile js-yaml and smol-toml statically and match Node (#1751) 2026-10-22 04:15:26 -05:00
Chris Tate d5489cabd7 Merge main 2026-10-22 04:10:59 -05:00
Chris Tate b99094543f Revert "Read error constructors as values and destructure workerData in JavaScript"
This reverts #1776, which made main CI fail.
2026-10-22 04:04:16 -05:00
Chris Tate b5fb4115e1 Merge main 2026-10-22 03:33:29 -05:00
Chris Tate f3efa0b38b Read error constructors as values and destructure workerData in JavaScript (#1776) 2026-10-22 03:29:21 -05:00
Chris Tate 3a879f4f05 Read error constructors as values and destructure workerData in JavaScript 2026-10-22 03:24:50 -05:00
Chris Tate 7d20212dd4 Merge main 2026-10-22 03:24:23 -05:00
Chris Tate c4d91bf2d9 Give runtime globals definitions, export only the declared library symbols, and fence the new libFn spellings 2026-10-22 03:18:35 -05:00
Chris Tate fe02190d1d Compile JavaScript-path calls that Node runs (#1756) 2026-10-22 03:15:48 -05:00
Chris Tate ec0574f44b Read the byte offset of every untyped ArrayBuffer Buffer.from in one place
All Buffer.from paths over an untyped ArrayBuffer or SharedArrayBuffer now
go through one function that takes the byte offset explicitly, as Node's
fromArrayBuffer does. The start-offset RangeError prints the offset as
JavaScript does (-1.5, -1e+21, -Infinity) and builds its message without a
fixed-size buffer, which a large negative offset overran.
2026-10-22 03:13:24 -05:00
Chris Tate 32da078c11 Match Node's AsyncLocalStorage context in async prefixes and generator steps 2026-10-22 03:00:21 -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 Tate c081d5a3a7 Apply the byte offset in Buffer.from over untyped ArrayBuffers
Buffer.from(arrayBuffer, byteOffset) with an untyped ArrayBuffer or
SharedArrayBuffer now returns a Buffer from that offset to the end, as
Node's fromArrayBuffer does, where the offset was dropped and the Buffer
covered the whole buffer.
2026-10-22 01:16:55 -05:00
Chris Tate 2e7db5da9e Encode messages into one block sized to their contents (#1760) 2026-10-22 01:03:36 -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 1a51223cd6 Say where the region's trap and memoryUsage accounting come from
Comments only: scr_trap is the runtime's failure path and the
standalone allocator test defines its own, and the region's chunks
count in process.memoryUsage() through scr_heap_stats.
2026-10-22 01:01:01 -05:00
Chris Tate f1e6addc81 Encode messages into one block sized to their contents
An encoded message allocated a node array (four nodes of 184 bytes to
start, trimmed when encoding ended), a property array per object, a copy
of every key and string, and the encoder's visit, identity and stack
arrays. A port that queues many messages kept all of it until delivery:
a {id, name} object took 15 allocations and 1.9 KB, and most of
postMessage's time went into page faults on that memory.

- The encoder builds nodes, properties and visits in arrays that start on
  the stack (8 entries), keeps borrowed references to keys and strings,
  and copies the result into one allocation: the header, the nodes, the
  properties grouped by node, the typed records and the text. A node is
  40 bytes; the per-kind fields share a union. ArrayBuffer contents stay
  in their own allocation.
- Decoding uses stack arrays for messages of up to 8 nodes and allocates
  the storage table only when the message has ArrayBuffer contents.
- Freeing a message frees the block, and walks the nodes only when the
  message holds ports, native handles or ArrayBuffer contents.
- The publication check reads the root before collecting the graph, so
  a message whose root is not frozen allocates nothing for it.

The same object message now takes 4 allocations and 302 bytes, and a
number 3 allocations and 129 bytes. bench/channel-messages runs in 161 ms
instead of 250 ms (Node: 340 ms), with a peak RSS of 45 MB instead of
115 MB, and bench/worker-messages in 273 ms instead of 348 ms.
2026-10-22 00:51:27 -05:00
Chris Tate 728dc6cd51 Count program-lifetime objects in memoryUsage and fix the runtime tests
- process.memoryUsage(): heapTotal counts the region's chunks that
  threads hold, minus the chunks discarded workers returned, and
  heapUsed counts them less the tails threads left unused when they
  moved to a new chunk.
- The region builds only where it can be used: Linux runtimes with
  the size-class allocator, which excludes the sanitizer runtimes
  (scriptc keeps 8-byte references there). The standalone allocator,
  cycle and young-nursery tests link again: the allocator test
  defines the scr_trap its region needs, and checks the region (below
  32 GiB, aligned, zeroed, chunks larger than one, heap statistics,
  threads).
- scr_plr_chunk has an initializer, like the other runtime globals.
2026-10-22 00:47:17 -05:00
Chris Tate 2d353e6bbe Compute Math.pow(2, n) for integer exponents from the result's bits 2026-10-22 00:33:54 -05:00
Chris Tate 321873b957 Compile JavaScript-path calls that Node runs 2026-10-22 00:18:36 -05:00
Chris Tate 63001cbc13 Await a suspending stackless callee without its promise 2026-10-21 23:55:37 -05:00
Chris Tate 97e85bf238 Compile js-yaml and smol-toml statically and match Node 2026-10-21 23:35:02 -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 56880e110c Keep the slots of short known-count arrays in the array's block
An array constructed with a known element count of up to 16 (array
literals, copies, slices and the arrays the runtime builds) keeps its
slots in its own block, as empty arrays built for appends already do, so
it costs one allocation and one free.

scr_arr_make_ops is inlined into its two constructors, so the size class
of scr_arr_new_ops_small folds to a table index. It writes the element
ops id with the other header fields and records ops past the registry
in the array's ext after the storage is set up: the ext pointer precedes
the dense allocation, so inline slots must move out before the ext is
created.
2026-10-21 23:19:57 -05:00
Chris Tate 2a48d46da3 Key weak collections by net sockets and servers, and carry the socket in undici client diagnostics (#1742) 2026-10-21 22:14:27 -05:00
Chris Tate d0d132080c Skip candidate buffering for classes whose trace visits nothing (#1743) 2026-10-21 22:11:25 -05:00
Chris Tate 4100cd3c04 Never buffer instances of classes whose trace visits nothing
A class carries the collector header when any class in its hierarchy can
be on a cycle, so instances of classes whose own fields hold nothing
cycle-capable were buffered as candidates and walked by every level. Such
an object has no traced children and is never on a cycle.

Their descriptors now name scr_cyc_no_children, and they are allocated
with buf set to SCR_CYC_ACYCLIC. That value has the buffered bit set, so
a release that leaves the object alive makes no call; scr_cyc_is_buffered
is false for it, and an emitted death or publication mark takes the object
out of a candidate buffer only when buf is below -1. A pass that walks
such an object as a child restores the value when it survives.
2026-10-21 22:08:30 -05:00
Chris Tate e80be9c2e5 Keep properties untyped code adds to typed object literals (#1733) 2026-10-21 22:06:32 -05:00
Malte UblandClaude Opus 5.5 fc551c4a8b Key weak collections by net sockets and servers, and carry the socket in undici client diagnostics
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-21 20:06:28 -07:00
Chris Tate 71d5e92dc0 Address review feedback on properties untyped code adds to typed objects 2026-10-21 22:02:22 -05:00
Chris Tate 10820d488c Support SNICallback in tls.createServer (#1700) 2026-10-21 22:00:41 -05:00
Chris Tate 37cdd92755 Keep properties untyped code adds to typed object literals 2026-10-21 20:23:53 -05:00
Chris Tate 143c80eb1c Print class instances with their class name when instanceof sees unknown values 2026-10-21 20:10:43 -05:00
Chris Tate 4ab3850579 Keep named properties written to arrays through checked views (#1715) 2026-10-21 19:34:43 -05:00