176 Commits
Author SHA1 Message Date
Chris Tate 8d8e7a7737 Run Atomics on shared storage without a lock (#1090) 2026-10-16 00:27:39 -05:00
Chris Tate 7b83558f52 Run Atomics on shared storage without a lock
Atomics operations on 8-, 16- and 32-bit integer arrays are sequentially
consistent atomic instructions on the element, and no longer take the
process-wide shared-memory lock. Other reads and writes of shared storage
are relaxed atomic accesses: element reads and writes access the element,
and copies, fills, conversions, searches and sorts read shared bytes into
a private copy and write back only the range they change, so concurrent
accesses to the same bytes are free of data races. Non-shared storage
keeps its plain memory accesses. Atomics.wait and Atomics.notify queue
waiters in buckets chosen by location, each with its own lock and
condition variable, so waiting threads no longer serialize other Atomics.

Atomics.store answers +0 for a value that truncates to -0, as Node does.
The shared storage runtime test also runs under ThreadSanitizer.
2026-10-16 00:06:31 -05:00
Chris Tate de0917284d Merge main 2026-10-16 00:05:07 -05:00
Chris Tate 93dcd9865e Merge main 2026-10-16 00:01:13 -05:00
Chris Tate 63b4dddeca Support array keys in InternTable and free replaced indexes (#1072) 2026-10-15 22:50:08 -05:00
Chris Tate 1b4103889b Add assert.deepEqual, partialDeepStrictEqual and the Assert class (#1079) 2026-10-15 22:22:57 -05:00
Chris Tate 7c09e1c020 Add assert.deepEqual, partialDeepStrictEqual and the Assert class 2026-10-15 22:01:38 -05:00
Chris Tate 647bc2a492 Add source functions, promise destinations and { signal } to pipeline stages
pipeline() accepts a source function (an async generator function called
with { signal }) and a destination async function, following
pipeline.js: the destination's promise value resolves the
stream/promises form and reaches the callback's second argument, and
pipeline() answers a PassThrough for it. A rejected destination destroys
that PassThrough with the reason and fails the pipeline.

Stages may take the { signal } argument: the pipeline creates its own
AbortController when one does, and aborts it when the pipeline finishes
or fails, as finishImpl does. Stage functions take the source and
{ signal } through the closure ABI, and the PassThrough and the value
are released with the pipeline.

The stream unit now links the emitter unit whenever it links, so a
pipeline of functions builds without constructing a stream. An async
generator destination and reading the PassThrough a function destination
returns are refused at compile time.
2026-10-15 21:50:01 -05:00
Chris Tate 77c9a2ff23 Merge main 2026-10-15 21:48:48 -05:00
Chris Tate 8dc5dc1249 Refuse InternTable array keys that are too long to encode
Encoding an array key added each element's size to a running length
with unchecked arithmetic, and wrote into a fixed stack buffer whenever
the sum fit. A sparse array with a large length (2^29 elements or more)
wrapped the sum on 32-bit targets, so the write landed before the buffer;
on every target the encoder also visited every index of such an array.

Array keys now have at most 65536 elements, and their encoding at most
2^31 - 1 bytes. The size is computed first with sums that cannot wrap,
the buffer is sized to it, and keys over either limit make the lookup or
insert throw a TypeError before anything is read or written. The Node.js
implementation throws the same TypeError for keys of more than 65536
elements. Sparse elements are walked in one pass in index order, and the
index growth computation checks its size arithmetic too.
2026-10-15 21:44:38 -05:00
Chris Tate d30d2619d7 Support crypto.createCipheriv and createDecipheriv (#986) 2026-10-15 21:30:30 -05:00
Chris Tate 0ebc146405 Merge main 2026-10-15 21:07:04 -05:00
Chris Tate ee49946d32 Support array keys in InternTable and free replaced table indexes
InternTable keys can be arrays of numbers or of strings, compared by their
elements: numbers by SameValueZero, strings by content, and holes equal to
each other only. The runtime stores an array key as one byte string that
encodes its elements; the Node.js implementation encodes it into a Map key
the same way.

Replaced table indexes are now freed. Every thread that uses a table
records the epoch it read at the start of each lookup and insert, and an
index retired in an earlier epoch than every recorded one is freed. Worker
threads stop holding retirements back when they exit.

Number indexes keep a tag byte per slot ahead of the key and value pairs,
so a lookup of an absent key reads the dense tag bytes instead of pairs.

getOrInsertComputed lowers an arrow callback with an expression body in
place, with its parameter bound to the key, so interning through
`(key) => new T(...)` creates no closure.
2026-10-15 20:53:19 -05:00
Chris Tate 6bb9a3e93d Add InternTable, a shared append-only table of published values (#1069) 2026-10-15 20:50:14 -05:00
Chris Tate a3b6b05a4c Merge main 2026-10-15 20:42:11 -05:00
Chris Tate feeba0a166 Add assert.AssertionError with Node's properties and constructor (#1022) 2026-10-15 20:18:29 -05:00
Chris Tate 94b38475b1 Add InternTable, a shared append-only table of published values
@scriptc/experimental exports InternTable<K, V>: a named table that every
thread of a scriptc program shares and that each thread has its own copy
of under Node.js. Keys are strings or numbers with Map equality; values are
published (@scriptc/threads) when stored, and the first stored value of a
key is kept. Members are get, has, size, getOrInsert and
getOrInsertComputed.

The runtime table (scr_itable.c) splits each table into 64 segments.
Lookups take no lock and do not write; inserts take a segment's spin lock
for a probe and a few stores, and callbacks run without a lock. Number
tables keep keys and values in the index; string tables keep entries in
segment-owned chunks behind tagged slots. Replaced indexes are kept.

The compiler lowers the class to a synthetic class with one f64 handle
field and five intrinsics, with interned helpers per member and type and
an in-place lowering of getOrInsertComputed that creates a function
expression callback only on a miss. Stored values go through the publish
intrinsic, so their types get the write guard. scriptc refuses names that
are not string literals, one name opened with different type arguments,
and key or value types a table cannot store.

scr_publish walks small graphs in inline buffers and reads
SCRIPTC_PUBLISH_TRACE once, instead of allocating and calling getenv on
every call.
2026-10-15 19:59:56 -05:00
Chris Tate b00d65b8be Merge main 2026-10-15 19:57:06 -05:00
Chris Tate eb90bf7eee Merge main 2026-10-15 19:50:35 -05:00
Chris Tate 6d7ed1bfbf Add async generator stages to pipeline (#1064) 2026-10-15 19:34:35 -05:00
Chris Tate 346a7c0a70 Match Node's http keep-alive, header and framing behavior (#1046) 2026-10-15 19:21:26 -05:00
Chris Tate f6dc19ad98 Merge main 2026-10-15 19:13:52 -05:00
Chris Tate f0151d8c17 Merge main 2026-10-15 19:06:52 -05:00
Chris Tate 982ebac139 Accept typed-ref arguments in createCipheriv and the cipher methods
createCipheriv, createDecipheriv, update(), setAAD() and setAuthTag()
now read the value a typed-ref box holds when it is a string, view,
ArrayBuffer or KeyObject. ERR_INVALID_ARG_TYPE names the class of a
class instance that arrives through an untyped value, as Node does,
instead of Object. An unknown output encoding that is not a string is
rendered as Node's %s formats it (util.inspect at depth 0, or the
object's own toString); a function, or an instance of a class that
defines toString, throws a coded SC2020 error. Cipher programs link the
inspect unit for that rendering.

5250 passes each argument through `any`, including class instances,
records and arrays that Node rejects, and 5254 covers output encodings
that are not strings from JavaScript.
2026-10-15 18:47:02 -05:00
Chris Tate ba3fcfb7f9 Add async generator stages to pipeline
pipeline() accepts a run of async generator function stages between
streams, following pipeline.js: each stage is called during setup with the
previous stream (a Readable is async iterable) or the previous stage's
generator, and the last generator is pumped into the next stream with
pumpToNode's write, drain and end waits. An error in the pump loop returns
the generator before the pipeline finishes, so the stage's finally runs.

A stage's source parameter is typed as what the pipeline passes it and may
only head for await loops. Function sources and destinations, stages that
take the signal argument, and stages separated by streams are refused.
2026-10-15 18:46:41 -05:00
Chris Tate ae5d9bf81f Add Readable.from over generators and match Node 24.22 stream timing
Readable.from accepts sync and async generators of strings and Buffers,
following from.js: values are pushed while push() accepts, an async
generator's next() promise is awaited once per value, and destroy runs
the generator's throw() and return() before the destroy callback.

for await over a stream follows createAsyncIterator: the loop registers
its 'readable' listener, reads through read(), and a parked loop wakes
through the 'readable' tick. push(null) before the first read ends the
stream after the next read, finished() answers an already settled stream
with its result on a nextTick, read(0) on an object-mode stream reads
nothing, and write(string, 'buffer') throws ERR_UNKNOWN_ENCODING.
2026-10-15 18:07:26 -05:00
Chris Tate 7bb7cfb6a3 Merge main 2026-10-15 17:36:41 -05:00
Chris Tate 1251a04fdb Merge main 2026-10-15 17:34:50 -05:00
Chris Tate 1be58648bc Merge main 2026-10-15 17:31:00 -05:00
Chris Tate a128918c50 Support dns.lookup's Node forms, lookupService, getServers and the result order (#1053) 2026-10-15 17:26:47 -05:00
Chris Tate 55a660f0e2 Add dns.lookup's Node forms, lookupService, getServers and the default result order
- dns.lookup(hostname, callback), lookup(hostname, family, callback)
  and lookup(hostname, options, callback) with family (0/4/6, 'IPv4',
  'IPv6'), all, order and verbatim, validated with Node's messages.
  getaddrinfo answers any family, ordered verbatim or one family first;
  literal addresses answer on the next tick, an empty hostname gives
  Node's null answer and DEP0118 warning, and failures carry code,
  errno, syscall and hostname.
- dns.lookupService(address, port, callback) checks the address and
  port as Node does and answers getnameinfo's host and service.
- dns.getServers() lists the nameservers in /etc/resolv.conf, and
  getDefaultResultOrder/setDefaultResultOrder keep the default order
  ('verbatim').
- A typed callback receives "" and NaN (or []) after a failure, where
  Node passes undefined; the limitations page says so.
Corpus 5648-5650 cover each case; the options-record diagnostics no longer
list the all and hints options as refused.
2026-10-15 16:59:20 -05:00
Chris Tate f65a6fad14 Merge main 2026-10-15 16:34:46 -05:00
Chris Tate 8e5577b8f1 Merge main 2026-10-15 16:32:49 -05:00
Chris Tate aa5e626b06 Merge main 2026-10-15 16:32:19 -05:00
Chris Tate 93371f83c9 Merge main 2026-10-15 13:42:34 -05:00
Chris Tate f7362fc386 Match Node's http keep-alive, header and framing behavior
- server.close() closes idle keep-alive connections, and every open
  connection closes after keepAliveTimeout + keepAliveTimeoutBuffer, so a
  closed server whose clients keep their connections no longer stays up.
  The Keep-Alive response header gives keepAliveTimeout in whole seconds
  and is omitted when it is 0.
- A client request through an Agent with neither keepAlive nor a finite
  maxSockets sends Connection: close.
- "socket hang up" and "aborted" errors carry code ECONNRESET, a client
  head over the header limit fails with HPE_HEADER_OVERFLOW, and TLS
  verification failures carry their openssl code.
- Outgoing header values keep the type they were set with: getHeader and
  getHeaders answer numbers and arrays, appendHeader turns an existing
  value into an array, and setHeader(name, string[]) writes one line per
  element.
- Repeated incoming header fields join with ", " ("; " for cookie),
  Node's single-value fields keep the first value, and set-cookie reads
  as an array under @types/node, on server requests and client responses.
  Fetch reads response headers line by line.
- Expect: 100-continue is answered with 100 Continue before 'request';
  other expectations answer 417.
- HEAD requests and 1xx/204/304 responses send no body or length header,
  and HTTP/1.0 requests without a set length get an unframed body and
  Connection: close.
- The 16 KiB maxHeaderSize applies to the request target plus header
  names and values (431 on the server).
- https.Agent TLS options are refused by name instead of being dropped,
  and server.address() lowers for @types/node's
  string | AddressInfo | null result.

Corpus 5555-5563 and the node-types http-headers-address fixture cover
each case.
2026-10-15 13:06:33 -05:00
Chris Tate edda7b99cd Merge main 2026-10-15 11:19:22 -05:00
Chris Tate 374f9c51cd Inline string retains and dynamic value reference counting in release builds (#1030) 2026-10-15 11:18:39 -05:00
Chris Tate c2940acacc Merge main 2026-10-15 11:13:49 -05:00
Chris Tate b706f2cef8 Merge main 2026-10-15 10:03:55 -05:00
Chris Tate 14bbb00d79 Partition large wasm32 programs like native ones
wasm32-wasi executables always compiled their program as one LLVM module,
so the object step of a large program ran the whole O2 pipeline and code
generation on a single thread. Native targets divide a module of more than
4 MB of LLVM text into up to 64 partitions that the helper optimizes and
compiles concurrently, importing small functions across partitions for
inlining. Nothing in the helper or the link plan is specific to native
objects: partitions link as ordinary objects with wasm-ld.

nativeProgramPartitions now applies the same size rule to every target and
no longer takes the target. A WASI test emits a program whose internal
callees, state and table entries land in different partitions, at release
and dev optimization, and checks that the partitioned objects are
deterministic and link to the same program as one object.
2026-10-15 09:59:04 -05:00
Chris Tate 36d45882bd Close Node 24 gaps in util.types keys, HTTP header limits and unread requests (#1021) 2026-10-15 09:29:01 -05:00
Chris Tate d92b6c4ec8 Merge main 2026-10-15 09:23:58 -05:00
Chris Tate e779d212d8 Merge main 2026-10-15 09:21:42 -05:00
Chris Tate 59cdda3930 Merge main 2026-10-15 09:20:01 -05:00
Chris Tate cffc2ee71f Merge main 2026-10-15 09:18:26 -05:00
Chris Tate 1121fe20c2 Merge main 2026-10-15 08:18:16 -05:00
Chris Tate 2c00672bca Merge main 2026-10-15 08:09:02 -05:00
Chris Tate aecd35b6c2 Merge main 2026-10-15 08:03:25 -05:00
Chris Tate 0b468e40a1 Add fs.globSync, fs.glob and fs/promises glob
The three glob entry points run a port of Node 24's
lib/internal/fs/glob.js walk in scr_glob.c: the same LIFO queue of
(path, patterns) items over the sorted directory listings, the same
lstat, stat, readdir, realpath and seen-pattern caches, the same result
set in insertion order, and the same exclusion checks, so matches come
out in Node's order. Patterns compile with the existing minimatch port.
glob_match is split into a compile step and a test step so the walk can
reuse each compiled pattern and test it segment by segment.
nocaseMagicOnly is now an option, because exclude patterns turn it off.

Patterns (a string or string array) and options (cwd as a path or
file: URL, exclude as patterns or a function, followSymlinks,
withFileTypes) are validated in Node's order with Node's errors,
including the internal assertion Node hits for a non-string cwd with
exclude patterns. withFileTypes results are Dirent rows: name,
parentPath and the type probes. parentPath follows Node's rule for each
form: the joined path for globSync, the resolved path for fs/promises
glob.

fs.glob checks the callback and validates synchronously, then delivers
string[] or the walk's error through the filesystem callback queue.
fs/promises glob lowers as the source of a for await loop. The walk runs
when the loop starts, and the matches found before an error bind before
the error is thrown, as Node's async iterator yields them first. The
older glob(3)-based fs.glob branch is removed.

Corpus 5448-5452 and a node-types fixture check the behavior against
Node.
2026-10-15 07:59:19 -05:00