Commit Graph
4679 Commits
Author SHA1 Message Date
Chris Tate 530bd9534f Await the values of untyped sync pipeline sources
An untyped pipeline source that is a sync iterable passed each value on
as it came, so a promise reached the next stage as the promise: a
byte-mode destination refused it and an object-mode one received it.
for await over a sync iterator, which Node's pipeline pumps it with,
awaits each value. The source's generator now awaits a value that is
an object (a promise included) before yielding it, and a rejection
closes the source, as Node's async-from-sync iterator does.
2026-10-24 16:10:00 -05:00
Chris Tate 1f621ce2c1 Version string scans over the UTF-16 units of strings that are not ASCII (#2074) 2026-10-24 16:02:58 -05:00
Chris Tate 190fb8d58e Mix weak-collection hashes in 64 bits on 32-bit targets
uintptr_t is 32 bits on wasm32, where the weak-collection hashes shifted it by 33 and 56 bits. The hashes now widen keys, owners and kinds to uint64_t before mixing; 64-bit targets compute the same values as before.
2026-10-24 16:01:33 -05:00
Chris Tate d813fdafd3 Compare termination signals and resolve the Node checkout path in the test/parallel runner
A run that a signal ends has a null exit code, so the runner recorded a
pass when Node and the executable ended by different signals, or when one
ended by a signal and the other with an exit code compared as null. The
verdict now compares both the exit code and the signal (verdict.mjs), and
records Node's signal in nodeExit as it does for the executable.

A relative --node path also made the minimal lane's fixtures symlink
relative to the copied tree, where it does not resolve. The checkout path
is now resolved first.

tests/harness/node-parallel-runner.test.ts covers both: the termination
comparison, and a relative --node path whose fixtures resolve from the
minimal tree.
2026-10-24 16:00:06 -05:00
Chris Tate ebeec4818d Prove 32-bit products exact only below 2^53
The exactness test for a product's 32-bit form multiplied the operands'
magnitude bounds in a double and accepted a result of at most 2^53. A
bound of 2^53 + 1 (3 * 3002399751580331) rounds to 2^53 there, so a
product the double rounds took the integer multiply's low bits:
`(3 * 3002399751580331) | 0` printed 1 where Node prints 0. The test is
now strict: a bound below 2^53 is exact, and no bound of 2^53 or more
rounds below it.
2026-10-24 15:58:14 -05:00
Chris Tate 276542e017 Use fixed timestamps in the fs.cp preserveTimestamps test (#2136) 2026-10-24 15:01:18 -05:00
Chris Tate ea495056d8 Answer 'in' on native Node handles from their declared members (#1949) 2026-10-24 14:51:03 -05:00
Chris Tate 55740d094a Resolve corpus promises 7503 and 7504 with an explicit undefined
tsgo checks JavaScript calls to an untyped Promise's resolve against its
one parameter, so resolve() with no argument is an error there while
tsc-ts accepts it. The two programs pass undefined, which both checkers
accept and which settles the promise the same way.
2026-10-24 14:33:02 -05:00
Chris Tate e5cdc68bab Store AbortController and AbortSignal handles in class fields (#2125) 2026-10-24 14:32:15 -05:00
Chris Tate 9cb8ed3ce5 Map resolved empty mapped types as the empty object type (#2127) 2026-10-24 14:25:04 -05:00
Chris Tate d388cdd9cf Merge main 2026-10-24 14:20:14 -05:00
Chris Tate 02c2c89fd6 Merge main 2026-10-24 14:18:51 -05:00
Chris Tate 3f03c90e86 Keep versioned loops' slow copies free of speculative integer copies
The copy of a dense-array, byte-window, counted, callback or typed-array
versioned loop that runs when its guard fails no longer gives its loops
speculative integer or int32 kind copies, which emitted each loop again for
a path that rarely runs. Proven integer loops, which emit one copy, are kept
in it.
2026-10-24 13:47:17 -05:00
Chris Tate bf02b9d7cc Write a number-keyed Map update through the entry its read found (#2130) 2026-10-24 13:38:20 -05:00
Chris Tate 3896957230 Report JSON.parse syntax errors with V8's messages and positions (#2129) 2026-10-24 13:34:12 -05:00
Chris Tate a8c0fa772b Use fixed timestamps in the fs.cp preserveTimestamps test 2026-10-24 13:28:53 -05:00
Chris Tate c33d693775 Answer instanceof against the primitive wrapper constructors (#2126) 2026-10-24 13:28:00 -05:00
Chris Tate c244ddc4c5 Answer 'send', 'disconnect' and 'channel' in a child process from its IPC channel
A child process has these members only when it was created with an IPC
channel (fork, or an "ipc" stdio entry), and keeps them after the channel
closes and after the child exits. The 'in' test on native handles answered
true for every declared member, so a child spawned without IPC reported
them and entered IPC-only branches. The three names now answer from the
runtime: scr_child_ipc_present reports whether the child was created with
a channel.

Corpus 8290 tests a forked and a spawned child before and after
disconnect and exit.
2026-10-24 13:18:19 -05:00
Chris Tate f8f3194e0c Merge main 2026-10-24 13:13:52 -05:00
Chris Tate 29a78af70b Merge main 2026-10-24 13:13:07 -05:00
Chris Tate 94c650a9c2 Write a number-keyed Map update through the entry its read found
m.set(k, f(m.get(k))) and a get followed by a set of the same key, with a
number or boolean value, now reuse the entry the read resolved, as
string-keyed maps already do: the write overwrites the value in place
when the map is the same and the key has the read's bits, and takes the
ordinary set otherwise. Before, the write probed the table again: the two
lookups were not merged, because each sits behind its own direct-index
test.
2026-10-24 13:07:38 -05:00
Malte UblandClaude Opus 5.5 864feec4cf Map resolved empty mapped types as the empty object type
A mapped type with no members was refused (SC2001) to keep `Partial<T>`
inside a generic body, where keyof T is still unknown, from interning a
wrong empty record. That also refused mapped types the checker resolved
from concrete inputs whose key filter keeps no key: `Record<never, T>`,
`Partial<{}>`, and a catalog type such as
`{ [K in KeyOf<Data, "image"> as Id<K>]: Model }` for data without image
models. When the mapped type comes through an alias whose arguments mention
no type parameter (or a module-level alias without arguments), it is the
empty object type and lowers like `{}`.

pi-ai declares its per-provider image and classifier model catalogs this
way; most providers have none, which refused 105 declaration groups.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-24 10:50:44 -07:00
Malte UblandClaude Opus 5.5 b11cd04ce9 Answer instanceof against the primitive wrapper constructors
`value instanceof Boolean` (or Number, String, BigInt, Symbol) died at run
time in JavaScript with Node's TypeError for a function without a prototype
object, because those constructors are compiled as plain conversion
closures; in TypeScript the right-hand side was refused (SC1090). A
compiled program never creates a boxed primitive (`new Boolean(...)` has
no lowering) and a primitive is never an instance, so the test is false
for every value. It now folds to false after evaluating the operand.

typebox's value guards (IsBoolean, IsNumber, IsString) test this way, so
pi's tool-schema construction and validation died at its first guard.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-24 10:50:43 -07:00
Malte UblandClaude Opus 5.5 684a0abd59 Store AbortController and AbortSignal handles in class fields
A class field typed AbortController or AbortSignal (plain, optional,
nullable, or in an array) maps to checked-dynamic storage, and the
class-field rule refused it (SC1090) although the same handles already
live in locals, Map values, and closures. They now take the checked-dynamic
slot, like the fetch Headers, Request, and Response handles.

controller?.abort() on a nullable controller was refused as a method call
(also for locals): the static abort lowering ran only without an optional
chain. It now runs again once the chain has narrowed the receiver.

Found by pi's TUI Editor (autocompleteAbort: AbortController | undefined).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-24 10:50:42 -07:00
Chris Tate 8796e91f5e Keep while-loop counters bounded by a typed array length in integers 2026-10-24 12:37:29 -05:00
Chris Tate 4f0975cbbe Compile more npm and CommonJS JavaScript dependencies statically 2026-10-24 12:01:26 -05:00
Malte UblandClaude Opus 5.5 d037c66e8c Report JSON.parse syntax errors with V8's messages and positions
JSON.parse's SyntaxError messages were shaped like V8's but differed from
Node's in several ways:
- Every positional message now ends with V8's "(line L column C)", where
  \n, \r and \r\n end lines.
- Positions are UTF-16 offsets, as in Node, not byte offsets.
- An object or array cut off after '{', a property name, a value or an
  array element reports the token V8 expected ("Expected property name or
  '}'", "Expected ':' after property name", "Expected ',' or '}' after
  property value", "Expected ',' or ']' after array element") instead of
  "Unexpected end of JSON input".
- After a comma in an object the message is "Expected double-quoted
  property name".
- An unterminated string reports the end of the input, and a truncated
  \u escape is a "Bad Unicode escape" there.
- A leading zero followed by a digit is an "Unexpected number".
- A misspelled literal names the first differing character, or the end of
  the input.
- An unexpected character is named with the source as V8 does: whole when
  it is under 21 units, otherwise ten units of context on each side; the
  sources "NaN", "Infinity", "undefined" and "[object Object]" name only
  themselves; a quote or a number there is an "Unexpected string" or
  "Unexpected number".

The new corpus program 7993-json-parse-error-messages pins 76 inputs
against Node, including non-ASCII text before the error, line breaks, and
long sources with split surrogate pairs at the context edges.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-24 09:55:05 -07:00
Chris Tate 56b9158de2 Keep integer index arithmetic in integers across nested counted loops 2026-10-24 10:42:05 -05:00
Chris Tate 022428ead6 Write the first stdin chunk after both readers start in the 'readable' EOF test (#2109) 2026-10-24 10:30:31 -05:00
Chris Tate 53baad4ebe Pump untyped iterable pipeline sources
A pipeline whose first stage is a checked-dynamic value now checks it at
run time, as Node's stage ladder does: a stream is piped as before, and a
string or a value with a Symbol.asyncIterator or Symbol.iterator method is
pumped into the next stage through an async generator over its iterator.
A typed Set, Map, generator or async generator object as the first stage
takes the same path. This covers the callback and stream/promises forms,
with or without function stages and pipeline options.

A write whose callback fails synchronously no longer sets needDrain, so no
'drain' follows the error and a pumped source stops at the failed write,
as in Node.

RUNTIME_ABI_VERSION goes from 37 to 42 for scr_stream_dyn_iterable_source.
2026-10-24 10:12:06 -05:00
Chris Tate e35621323d Trust no certificate for an empty ca in TLS clients
tls.connect, https.request and http2.connect verified the server against
the default CAs when ca was an empty Buffer, because the runtime read a
zero-length ca as none. The compiled calls now pass a null ca for none
(an omitted ca, or an empty string, which Node reads as no ca) and a
non-null one for any Buffer, and runtime option records read ca the same
way, so an empty Buffer or array trusts no certificate, as in Node. The
connection pool keys tell an empty ca from none. An empty array, which
threw, is an empty trust store too. A runtime-valued empty string ca in
http2.connect is refused instead of being read as an empty store.
2026-10-24 10:06:35 -05:00
Chris Tate 38730910c8 Write the first stdin chunk after both readers start in the 'readable' EOF case
A reader that starts after the second chunk arrives reads both chunks in
one 'readable' event. On loaded test workers Node started that late while
the compiled program did not, so the two outputs differed. The first chunk
now waits 500 ms.
2026-10-24 09:56:15 -05:00
Chris Tate aa1c26845c Cache untyped property access in programs that declare classes (reland) (#2102) 2026-10-24 09:55:54 -05:00
Chris Tate e792cf4771 Test RSA-PSS verification with an automatic salt length on restricted keys
Node recovers the salt length under RSA_PSS_SALTLEN_AUTO without
enforcing the minimum in an RSA-PSS key's parameters, while an explicit
saltLength below it throws and the default saltLength requires the
key's. The corpus program pins that behavior for MGF1 digests equal to
and different from the message digest, through crypto.verify and
createVerify.
2026-10-24 09:49:01 -05:00
Chris Tate 537dfbeb68 Evaluate assert.fail's value messages after declaring them, with Node's defaults
assert.fail with a message that is not a string read the message's
hidden locals before they were declared: the call stored the message and
its generated flag in temporaries ahead of the message's declarations,
so `assert.fail(new RangeError('re'))` crashed. Arguments computed from
the message now read it after the declarations.

assert.fail also follows Node's defaults for such values: null and
undefined report "Failed" with generatedMessage true, an Error is thrown
as is, and any other value, falsy ones included, becomes the message as
its string, with generatedMessage set to !value.
2026-10-24 09:37:24 -05:00
Chris Tate 905a290817 Copy typed array sets of the same element kind on shared memory as bytes (#2094) 2026-10-24 09:07:57 -05:00
Chris Tate f2e710d65f Answer stream, StringDecoder and handle-object members in untyped code as Node does 2026-10-24 09:05:16 -05:00
Chris Tate 35a1576ba8 Merge main 2026-10-24 08:46:27 -05:00
Chris Tate 878f032886 Crypto parity: RSA key generation lengths, RSA-PSS keys and encrypted private keys 2026-10-24 08:35:30 -05:00
Chris Tate 427e383b5e Cache untyped property access in programs that declare classes (reland)
Reapplies #2017, which #2038 reverted after effect's observability-otlp
fixture crashed in plain builds. The crash was the candidate-buffer
corruption that the previous commit fixes: the cached property sites
change when the collector runs, and with enough environment variables a
pass buffered a string box that was later pooled while still buffered.
2026-10-24 08:22:30 -05:00
Chris Tate d9f1457cd5 Run CommonJS top-level return, untyped assert forms, and untyped instanceof Object as Node does 2026-10-24 08:07:52 -05:00
Chris Tate ab9cfd7e91 Speculate integer loops through pure helper calls, powers of two and byte reads
- A speculated write may call a function whose body is one return of
  arithmetic, Math.floor and Math.pow over its number parameters. The fast
  copy evaluates the expanded body; the double copy still calls the
  function. Union tests on the parameters (`number | undefined`) fold
  against the wrapped argument.
- `Math.pow(2, n)` and `Math.floor(a / 2^n)` run as checked i64 shifts:
  with n an integer in [0, 52], both equal the double results for every
  safe integer a.
- Element reads of integer typed arrays are speculative leaves, read before
  any check, as string and array reads are.
- A write's other calls of program functions are hoisted: the fast copy
  evaluates each once into a local of its own, and a failed check resumes
  at a copy of the write that re-runs only the arithmetic over those
  locals. This applies only when no call can write a local the write reads.
- k-nucleotide's key chain (`key = bitwiseTrimLeft(key, m) * 4 + seq[i]`)
  now runs in i64.
2026-10-24 07:53:04 -05:00
Chris Tate 5ad3576c1e Hold a Map.get result as the entry's reference until the map can change
A local initialized from Map.get of a reference value retained the
value and released it at the end of its scope. In the common update

  const cur = counts.get(key);
  if (cur === undefined) counts.set(key, new Counter());
  else cur.num++;

the map is written only on the branch that does not read the local, so
the entry keeps the value alive for every read. A stable local whose
every use comes before any statement that may change a reference edge
(ReferenceEffects) now holds the entry's own reference: no retain on the
read and no release at scope exit. An if statement passes its
condition's state to both branches and joins them; any other statement
that names the local is judged whole. Number keys and inline string keys
take this path, for nullable results and for the stack-boxed results of
union locals.
2026-10-24 07:39:14 -05:00
Chris Tate a5eca18f1e Read fs.promises as a module, end CommonJS entries at a top-level return, and check fs read and write arguments as Node does
fs.promises read as a member of the fs module (a binding, an inline
require('fs').promises, destructuring) is the node:fs/promises namespace,
as in Node. It failed at run time before.

A return at the top level of the entry CommonJS module ends its
evaluation, as in Node's module wrapper: preflight admits TS1108 in
CommonJS JavaScript files. A CommonJS module the program requires with a
top-level return is refused with SC1090, because the exports assigned
after the return would stay undefined, which their storage cannot hold.

fs.ReadStream(...) and fs.WriteStream(...) called without new construct
their stream, as Node's stream constructors do.

fs.read, fs.readSync and fs.write check their arguments in Node's order
and with Node's errors: the fd, the buffer, the offset, `length |= 0`,
number and bigint positions (validatePosition), the empty-buffer error
and the buffer window. fs.read accepts a Proxy options object. A
readSync call whose JavaScript arguments are untyped or bigint takes the
same checks at run time (fs.readSyncDyn). fs.write threw no error for an
invalid fd, offset or length before.
2026-10-24 07:22:00 -05:00
Chris Tate 7ba3827bd4 Merge main 2026-10-24 06:54:25 -05:00
Chris Tate 73389fd513 Merge main 2026-10-24 06:53:30 -05:00
Chris Tate 5517139b0e Merge main 2026-10-24 06:52:59 -05:00
Chris Tate f23447dc99 Merge main 2026-10-24 06:50:53 -05:00
Chris Tate 102c86b6d2 Merge main 2026-10-24 06:50:08 -05:00
Chris Tate f505e8a044 Merge main 2026-10-24 06:49:45 -05:00