Commit Graph
1016 Commits
Author SHA1 Message Date
Chris Tate 5eb65cf64e Merge main 2026-10-18 00:24:03 -05:00
Chris Tate ca23c11d10 Add the cstring class to library profiles (#1253) 2026-10-18 00:04:01 -05:00
Chris Tate 87abacc400 Merge main 2026-10-18 00:01:05 -05:00
Chris Tate 370bf5962d Add the cstring class to library profiles
Library exports and callbacks passed text only as a pointer and length.
profile_format 2 adds the cstring class, a NUL-terminated `const char *`
(an i32 pointer in Wasm reactors), for export parameters, export results
and callback parameters. Parameters are copied in and decoded as UTF-8,
with U+FFFD for malformed bytes; a NULL pointer is the SC4012
host-contract trap. Results live in the result arena like string
results. A result or callback argument containing U+0000 traps instead
of being truncated.

Closes #615
Co-authored-by: ailuoku6 <23209876+ailuoku6@users.noreply.github.com>
2026-10-17 23:40:06 -05:00
Chris Tate db18d45dd4 Merge main 2026-10-17 23:39:11 -05:00
Chris Tate aa19f948c5 Settle async generator completions on Node's microtask turns
An async generator that fell off its end, with a return type other than
void, settled its last next() one microtask turn after Node, so for await
loops over it also ended a turn late. The appended implicit return now
marks the completion as settled, like a bare `return;`.

return() at a suspended yield ran the generator's finally blocks before
awaiting the return value. The request now awaits the value first and
resumes the body on the next turn, and the completion that follows settles
without a further turn, also when a finally block yields or awaits first.
An explicit `return x` reached during that unwind still takes its own turn
to await x.

Array.fromAsync accepts typed async generator sources: each next() result
is awaited and its value used as is, a throwing or rejecting mapping
function closes the generator, and a rejected next() rejects the result on
the turn Node rejects it.
2026-10-17 23:19:15 -05:00
Chris Tate ed817c017d Name the rejected value in the Object prototype TypeError
Object.setPrototypeOf and Object.create threw "Object prototype may only be
an Object or null" without the value Node appends. The message now ends
with the value's string form, as in Node: "...: 7", "...: undefined",
"...: Symbol(tag)".

A prototype or receiver that is a valid object of a kind these functions
do not handle yet (a function or Map prototype for Object.create, a Map
prototype or receiver for Object.setPrototypeOf) threw the same TypeError.
It now throws an Error that says the operation is not supported for that
value.
2026-10-17 22:50:45 -05:00
Chris Tate 8d8b96fefa Merge main 2026-10-17 20:59:05 -05:00
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 0580460c46 Match Node 24 in URLSearchParams, querystring, string_decoder and os
- URLSearchParams iterators compile outside a for-of head
- URLSearchParams can be built from [string, string] tuple pairs
- querystring.stringify writes bigint values
- StringDecoder accepts an encoding known only at run time
- os.setPriority is lowered
2026-10-17 17:54:52 -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