Commit Graph
1007 Commits
Author SHA1 Message Date
Chris Tate 24df6bb0cd Mark socket and stream chunks as Buffers (#1238) 2026-10-17 20:06:16 -05:00
Chris Tate f87b6d7528 Mark socket and stream chunks as Buffers
Chunks from net sockets (including socket.read()), http messages and
upgrade heads, child_process output, dgram messages, http2 streams and
TLS sessions were byte arrays without the Buffer brand. String
conversion of them joined the byte values ("112,105,110,103") instead
of decoding UTF-8, so `body += chunk` and template literals differed
from Node. They are now Buffers, as in Node.

Closes #903
Co-authored-by: João Carlos Magalhães de Castro <88864312+JohnC0de@users.noreply.github.com>
2026-10-17 19:42:11 -05:00
Chris Tate 6d79399862 Tunnel fetch through env proxies with CONNECT
Fixes #1185
Co-authored-by: Andrey Kiselev <35863331+TheAsda@users.noreply.github.com>
2026-10-17 19:38:19 -05:00
Chris Tate 2e0f44c8b7 Call valueOf and Symbol.toPrimitive on values held as any (#1234) 2026-10-17 19:24:42 -05:00
Chris Tate 8bb3fa1077 Call valueOf and Symbol.toPrimitive on values held as any
`value.valueOf()` on a dynamic receiver was refused at compile time, a
class's own toString was not reached by `value.toString()`, and classes
could not declare a `[Symbol.toPrimitive]` method.

- `value.valueOf()` lowers to dyn.valueOf, which answers each kind's own
  valueOf: a primitive itself, a Date its time value, an object with a
  valueOf member that member's result, and any other object itself. A
  null-prototype dictionary throws "<spelling> is not a function", and
  null and undefined throw Node's property-read TypeError.
- When a boxed class declares valueOf or toString, `value.valueOf()` and
  `value.toString(argument)` call the class's method for its instances
  and keep the runtime call for every other value.
- Classes can declare `[Symbol.toPrimitive](hint)`. The dynamic
  conversions call it with "default" for `+` and `==`, "number" for the
  numeric operators and conversions, and "string" for template literals,
  String() and property keys, and throw Node's TypeError when it returns
  an object.
2026-10-17 19:01:59 -05:00
Chris Tate 9fd5c8a73f Widen ASCII to UTF-16 sixteen bytes per step in Buffer.write
Buffer.write(text, "utf16le") zero-extended an ASCII run eight bytes per
step with shifts and masks in 64-bit words. Where the target has vector
unpacks (SSE2 on x86-64, NEON on AArch64) it now widens sixteen bytes
per step: one load, a test of the high bits, and two unpacked stores.
The word loop stays for the remaining bytes and for other targets. A
scanner that fills a Uint16Array of code units this way, as tsc-ts does
once per source file, spends about half as long in the copy.
2026-10-17 18:59:01 -05:00
Chris Tate 6ae3eb5cc8 Match Node 24 in events and timers/promises
- The EventEmitter leak warning reaches process 'warning' listeners on the next tick
- The events listener-count and max-listener helpers work over EventEmitter instances
- timers/promises setTimeout, setImmediate and scheduler.wait accept an AbortSignal
- timers/promises scheduler.wait and scheduler.yield are lowered
2026-10-17 17:48:42 -05:00
Chris Tate 55005ef73d Resolve host names in net.connect
net.connect and net.createConnection dialed only IP literals and
"localhost"; any other host name failed with getaddrinfo ENOTFOUND
without a lookup, so names served by /etc/hosts or a container's DNS
(such as Docker service names) did not connect. The dial now resolves
the name with getaddrinfo using Node's hints, orders the addresses as
Node's autoSelectFamily does, and tries them in order.

Fixes #1156
Co-authored-by: Quim T. <26749475+espetro@users.noreply.github.com>
2026-10-17 16:42:55 -05:00
Chris Tate a1d5d9e1b9 Expose Promise.prototype methods on promises in untyped bindings
Reading then, catch or finally from a promise held in a JavaScript or
checked-dynamic binding now answers the shared method values, so
typeof checks, thenable detection and "then" in p match Node. Calling a
method value uses its receiver; a receiver that is not a promise throws
Node's incompatible-receiver TypeError.

Fixes #978
Co-authored-by: Quim T. <26749475+espetro@users.noreply.github.com>
2026-10-17 16:18:56 -05:00
Chris Tate 4018a1d957 Compile async functions that suspend only at their awaits to stackless coroutines (#1204) 2026-10-17 15:38:36 -05:00
Chris Tate d7ac988fec Return primitive receivers from Object.setPrototypeOf for any object prototype (#1202) 2026-10-17 15:06:23 -05:00
Chris Tate 0b7e777689 Support net.SocketAddress and net.BlockList (#1200) 2026-10-17 14:56:25 -05:00
Chris Tate 4e3138ceda Keep optional, nullable, and dictionary JSON members on the direct schema parser (#1193) 2026-10-17 14:39:47 -05:00
Chris Tate bff94f18ce Return primitive receivers from Object.setPrototypeOf for any object prototype
Object.setPrototypeOf with a boolean, number, string, bigint or symbol
receiver now returns the receiver when the prototype is null or any object
or function, including class instances, classes, Maps, Sets, typed arrays,
promises and Object.prototype. Other prototypes throw a TypeError, as in
Node.
2026-10-17 14:38:11 -05:00
Chris Tate cffe4123d2 Give dense arrays head room so shift, splice(0, n) and unshift runs cost amortized O(1) (#1195) 2026-10-17 14:37:36 -05:00
Chris Tate 0db6d27c7c Support net.SocketAddress and net.BlockList
net.SocketAddress and net.BlockList were refused at compile time. They
are now native checked-dynamic handles in scr_net.c, so their getters
and methods also work on untyped values, and they match Node 24:

- new SocketAddress(options) checks the options in Node's order
  (ERR_INVALID_ARG_TYPE, ERR_INVALID_ARG_VALUE for the family,
  ERR_SOCKET_BAD_PORT, ERR_OUT_OF_RANGE for the flow label) and throws
  ERR_INVALID_ADDRESS for an address that does not parse. parse() reads
  URL.parse(`http://${input}`)'s hostname and port and answers undefined
  on failure; isSocketAddress(), toJSON() and util.inspect match.
- BlockList takes address, range, subnet and CIDR rules (addAddress,
  addAddresses, addRange, addSubnet, addCIDR, addCIDRs), removes them
  (removeAddress, removeRange, removeSubnet, removeCIDR, clear) and lists
  them in Node's order: IPv4 addresses, IPv6 addresses, subnets, then
  ranges, each newest first, with a repeated address kept once.
- check() compares across families through the IPv4-mapped IPv6 form,
  as Node's SocketAddress::compare and is_in_network do, and answers
  false for a string that does not parse in the given family.
- rules, size, toJSON(), fromJSON() (Node's rule patterns), isBlockList(),
  PRIVATE_RANGES, instanceof and util.inspect match, with the validation
  errors of lib/internal/blocklist.js.

Corpus 5659 and the node-types fixture net-blocklist.ts cover both
classes.

The compatibility registry lists the BlockList methods and
SocketAddress.parse as supported, and both classes as partial: their
structured clone and worker transfer forms are not modeled.
2026-10-17 14:32:06 -05:00
Malte UblandClaude Opus 5.5 4aa5d6cbf8 Give dense arrays head room so shift, splice(0, n) and unshift runs cost amortized O(1)
shift() moved every remaining element down one slot, making queue drains
quadratic (100k elements: 0.85 s; 1M: no result in 30 s). Dense arrays of
8-byte slots with their own allocation now remove front elements by
advancing data/present and lowering cap; the vacated slots stay in the
allocation as head room. A flag in `spare` marks it and its size is
implied by the layout (present == data + cap + head), so every reader,
including inline element access in emitted code, is unchanged. Growth
moves the elements back to the start of the allocation (in place only
when that leaves half the moved count free, else geometrically), a
drained array starts over at the front, unshift reuses head room, and an
array unshift already had to move once reserves head room for half its
length. scr_arr_outline drops head room before compiled storage swaps.
Booleans, inline slots, sparse arrays and static arrays keep the old
behavior.

Scaling probes: shift/BFS/splice(0,1)/deque drains and unshift runs go
from exponent ~2.0 to ~1.0 (shift 1e5 847 ms -> 1.6 ms); runtime bench
geomean 1.0045 (neutral).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-17 12:13:53 -07:00
Chris Tate 3032248c11 v0.3.6 2026-10-17 13:47:41 -05:00
Malte UblandClaude Opus 5.5 a40138c8b0 Keep union and dictionary members out of the direct parser's record loop
The record loop inlined the union reader and the overflow insert, which
slowed records whose members are all plain leaves by about 2%
(json-records). Scalar members test their kind first again, and the union
reader, the overflow insert, and the absent fill stay out of line.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-17 11:37:58 -07:00
Malte UblandClaude Opus 5.5 74a9cfb8cc Compile async functions that suspend only at their awaits to stackless coroutines
Native async bodies whose only suspension points are their own awaits now
compile to LLVM switched coroutines, the lowering WASI async bodies and
native synchronous generators already use. A suspended call keeps a heap
frame of the values live across its awaits instead of a fiber with its own
stack. Bodies that reach other suspension points (async generator
consumption, dyn awaits, node:test subtests), module evaluation bodies,
dyn results, worker and library programs, and Windows keep their fibers.

The emitter selects bodies by emitting them as usual first: that text may
suspend only through await runtime calls and await-call entries, and may
call no function that parks. The coroutine form is then emitted and
checked again. Each await prepares its continuation in the runtime (a
pending promise's waiter list, or one READY-queue turn, continuing in place
exactly where a fiber's hop would) and extracts the settled result after
resuming. The eager prefix runs on the spawner's stack and moves to a fresh
stack near the stack guard, so deep async recursion keeps working.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-17 11:29:05 -07:00
Malte UblandClaude Opus 5.5 e415d8fc63 Parse JSON dictionaries on the direct schema parser
Records with a string index signature (`Record<string, string>`,
`{ name: string; [key: string]: number }`) still sent the whole document
through the dynamic tree and the checked cast. Their undeclared members now
join the record's overflow map in source order straight from the text, read
as the index value type, exactly as the checked route copies the parsed
object's entries. Index value types that are unions, `unknown`, a repeated
key, or a `__proto__` member keep the checked route.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-17 11:10:43 -07:00
Malte UblandClaude Opus 5.5 da36100ecc Keep optional and nullable JSON members on the direct schema parser
`JSON.parse(text) as T` parses straight into the native layout only when
every record member is a number, boolean, string, record or array, so one
optional or nullable member sent the whole document through the dynamic
tree and the checked cast. Record members typed as unions of those kinds
with null and undefined now parse directly too, provided each JSON value
kind selects at most one arm (at most one record and one array arm, no
discriminant), so the checked route's first full match is that arm.

Each union describes its slot representation: the nullable reference
payload, the inline scalar tag and bits, or a tagged box built through a
compiler-emitted box function. A missing undefined-armed member takes its
ABSENT state, exactly as the checked route's missing key does; any other
missing member, a value without a matching arm, or a duplicate key still
declines to the checked route.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-17 10:55:47 -07:00
Chris Tate 9c924818b8 Reserve heap growth for worker threads that allocate large blocks (#1191) 2026-10-17 11:47:44 -05:00
Chris Tate 5d02923bba Add readline's terminal mode, cursor helpers and emitKeypressEvents (#1178) 2026-10-17 11:45:52 -05:00
Chris Tate 82ee707ce5 Support request signals, destroy(err), servername, Expect events and maxHeaderSize in http (#1076) 2026-10-17 11:41:29 -05:00
Chris Tate 81142b99c5 Reserve heap growth for worker threads that allocate large blocks 2026-10-17 11:24:45 -05:00
Chris Tate f36767199e Check every TLS client certificate against the request's name
A TLS client set the name mbedTLS verifies only when it also sent that
name as SNI, so the certificate name check depended on SNI:

- https.request to an IP-literal host with servername: "" checked no name,
  so a certificate for another name was accepted.
- tls.connect with servername: "" sent SNI "localhost" and checked the
  certificate against localhost instead of the host.
- A verified request to an IP-literal host always failed the handshake,
  because mbedTLS refuses to verify without a name.

Each client now keeps the name it checks apart from the SNI name. The
checked name is a non-empty servername, otherwise the host; SNI carries
the same name except for an IP literal or servername: "", which send
none, as in Node. When no SNI goes out, mbedTLS is told to skip its own
name check and the verify callback checks the leaf instead: an IP literal
must be one of the certificate's IP SANs (Node's checkServerIdentity), and
any other name follows mbedTLS's DNS SAN and CN rules. A missing name
fails the check. A mismatch fails with ERR_TLS_CERT_ALTNAME_INVALID, or,
for tls.connect with rejectUnauthorized: false, reads as authorized false
with that authorizationError.

Corpus 5664 covers IP-literal hosts with and without servername: "",
tls.connect with servername: "", and that no SNI is sent.
2026-10-17 11:17:59 -05:00
Chris Tate 23056949da Keep readline's terminal cursor on character boundaries, and merge main 2026-10-17 11:06:19 -05:00
Chris Tate a904a552d7 Add the concurrency test option (#1180) 2026-10-17 09:37:02 -05:00
Chris Tate b429d4ca6f Merge pull request #1108 from vercel-labs/ctate/add-messagechannel-receivemessageonport-5d9f36
Add MessageChannel, receiveMessageOnPort and MessagePort transfer
2026-10-17 09:35:15 -05:00
Chris Tate 4b7ff49da8 Support createHash and createHmac values as streams (#1120) 2026-10-17 08:47:55 -05:00
Chris Tate 591994217c Run every subtest at once with concurrency: true
Node sets a test's concurrency to Infinity for `true` whenever the test
has a parent, and to os.availableParallelism() - 1 only for the root
test. Every test and suite a program declares has a parent, so `true`
now runs all of its subtests at once instead of capping them at the CPU
count minus one.

The concurrency fixture adds a suite of 64 concurrent tests that checks
all of them run at once.
2026-10-17 08:39:10 -05:00
Chris Tate 3fadf25b02 Merge main 2026-10-17 08:38:10 -05:00
Chris Tate 89e920ac99 Give a copy of a Hash whose stream closed a stream of its own
copy() of a Hash whose stream was destroyed before a digest, by
destroy() or an error, carried over the closed-stream flag, so the
copy's first stream use threw the SC2020 error. The copy now starts
without a stream, as in Node, and gets one on first stream use.
Corpus 5761 covers a copy written and ended after the original's
'close' event.
2026-10-17 08:24:36 -05:00
Chris Tate 4a53870702 Revert "Do not merge: CI check of the exec source changes on current main" (#1184) 2026-10-17 08:20:45 -05:00
Chris Tate 4060f9135b Do not merge: CI check of the exec source changes on current main 2026-10-17 04:30:49 -05:00
Chris Tate 31ab50381f Give Object.groupBy results a null prototype (#1182) 2026-10-17 03:57:52 -05:00
Chris Tate 844aa87ae0 Give Object.groupBy results a null prototype
Object.groupBy now builds its result record with a null prototype,
stored as a flag on the record's overflow map. Inspection prints
"[Object: null prototype]", Object.getPrototypeOf answers null, `in`
finds no inherited names, and String(), template literals, toString(),
hasOwnProperty() and propertyIsEnumerable() throw Node's TypeErrors.
Spread copies and structuredClone keep Object.prototype, as in Node.
Index-signature records converted from an Object.create(null) value
keep the null prototype too.
2026-10-17 03:35:02 -05:00
Chris Tate d5034758fb Merge main 2026-10-17 03:33:06 -05:00
Chris Tate 53023c615d Merge main 2026-10-17 03:30:55 -05:00
Chris Tate b37c9491fe Fix or refuse Node 24 builtin forms that ran with wrong output
- JavaScript calls of Promise.withResolvers' resolve/reject through the
  record no longer fail with an internal compiler error.
- new RegExp with the d or v flag is refused (SC1120) for literal flags
  and throws a named error for computed flags, instead of SyntaxError.
- JavaScript .groups reads on matches whose regex is not traceable are
  refused like TypeScript's, instead of reading undefined.
- Iterator helpers on the string iterator are refused instead of
  failing at run time.
2026-10-17 03:29:53 -05:00
Chris Tate 05cb9bd609 Add the concurrency test option
node:test changes, matching Node 24.22:

- { concurrency } on describe, suite, test, it and t.test sets how many
  subtests run at once: a number, true for os.availableParallelism() - 1,
  or false for 1. Subtests inherit the parent's value.
- Above 1, each subtest runs on its own fiber. Subtests start in
  declaration order as slots free up, and t.test and test() calls inside a
  concurrent test start the subtest at once; t.test's promise settles when
  it finishes. The parent finishes after all of them.
- Results, diagnostics and failures are reported in declaration order.
- A concurrent subtest waits after its beforeEach hooks, and a concurrent
  suite before its children, for as many turns as Node's runner does, so
  sibling bodies interleave as they do there.
- A concurrency that is not an integer from 1 to 4294967295 throws Node's
  ERR_OUT_OF_RANGE RangeError when the test or suite is created.
- Nested test() calls and hooks inside a concurrent subtest attach to the
  subtest that runs on the calling fiber.

The concurrency fixture compares the reporter output, the order of the
bodies' side effects, and the range errors with Node, plain and with
SCRIPTC_SAN=1.
2026-10-17 03:28:15 -05:00
Chris Tate 0c4056fb0e Add the plan and timeout test options (#1165) 2026-10-17 03:22:28 -05:00
Chris Tate 26aeeb4c6f Lower util.promisify over the zlib callback one-shots (#1179) 2026-10-17 03:10:14 -05:00
Chris Tate 2896e94b20 Merge main 2026-10-17 02:59:21 -05:00
Chris Tate 7dcb1e6c4e Lower util.promisify over the zlib callback one-shots
promisify(zlib.deflate) and the other callback one-shots (inflate,
deflateRaw, inflateRaw, gzip, gunzip, unzip) over a named or namespace
import of node:zlib now lower. A call through the binding,
gzipAsync(data, options?), runs the callback form's work on the worker
pool and returns a promise that settles where the callback would run:
fulfilled with the Buffer, or rejected with the callback form's Error
(errno, code and message). An invalid option or input rejects the promise
instead of throwing, as the throw inside Node's promisify wrapper does.

The runtime adds scr_zlib_codec_promise and its checked-dynamic twin,
which share the callback forms' options ladder and worker-pool operation
and settle a pending promise in place of the callback. The fallback
declarations gain the matching util.promisify overload.
2026-10-17 02:46:20 -05:00
Chris Tate bc0636de7b Merge main 2026-10-17 02:35:31 -05:00
Chris Tate 0777ad7223 Merge main 2026-10-17 02:33:12 -05:00
Chris Tate 5c39821726 Merge main 2026-10-17 02:30:43 -05:00
Chris Tate f61e5dc938 Add readline's terminal mode, cursor helpers and emitKeypressEvents 2026-10-17 02:10:27 -05:00