Commit Graph
4472 Commits
Author SHA1 Message Date
Chris Tate 43bafa67d1 Test262 group C: early errors, undeclared reads, async-iterator relaxation, compareArray harness (#2500) 2026-10-29 15:17:08 -05:00
Chris Tate ffa0753a82 Generic arrows: enclosing-frame captures (incl. mutable, boxed), optional-call arity, declared-arity refusal (#2506) 2026-10-29 15:14:11 -05:00
Chris Tate 145f8fcd5e fs.watch names the changed entry; fs.watchFile polls in its own gated unit (#2507) 2026-10-29 15:13:12 -05:00
Chris Tate 2de17f95df Pass every argument to emitter listeners that read arguments, and deliver the leak warning at exit (#2544) 2026-10-29 15:12:50 -05:00
Chris Tate 03b26a7e8e Stdio write hook: console lines pass (line, errorHandler), byte chunks pass a Buffer (#2511) 2026-10-29 15:12:31 -05:00
Chris Tate dfa940a82d Return freed memory sooner: chunked shared writes, a bounded string spare, idle trims (#2518) 2026-10-29 15:12:10 -05:00
Chris Tate f2e82c68e1 Pack jammed saturating loops one copy per lane and test their exit every fourth iteration (#2522) 2026-10-29 15:11:29 -05:00
Chris Tate 16f3c16752 Match Node's node:path behavior from Node 24's path tests (#2495) 2026-10-29 15:10:50 -05:00
Chris Tate 2d2cd25d26 Count the arguments a replaced stdio write receives (#2530) 2026-10-29 15:10:03 -05:00
Chris Tate 3c9d162a99 Test the direct index once in number-key lookups, and inline the probe in loops (#2525) 2026-10-29 15:09:43 -05:00
Chris Tate 7eda6fa880 Trim the per-chunk work of compiled object-mode stream callbacks (#2523) 2026-10-29 15:09:24 -05:00
Chris Tate 59eced7a6c Lower ReadableStream tee, pipeTo and pipeThrough in TypeScript (#2513) 2026-10-29 15:09:05 -05:00
Chris Tate 771b9a96ba Throw Node's TypeError when a nullable destructuring source is null (#2537) 2026-10-29 15:08:26 -05:00
Chris Tate a9e2cb994f Deliver the lines a chunk split after a line listener closes the interface (#2533) 2026-10-29 15:07:08 -05:00
Chris Tate a391eef92a Read and write cached regular files on the loop thread in fs streams (#2540) 2026-10-29 15:06:47 -05:00
Chris Tate 08e2b4596a Run for-in over a receiver that may be null or undefined (#2534) 2026-10-29 15:06:00 -05:00
Chris Tate 7d283cbda3 Read at the exact bigint position in fs.readSync (#2535) 2026-10-29 15:05:39 -05:00
Chris Tate 5dc4bfd6d8 Read builtins into untyped TypeScript slots as Node's values (#2556) 2026-10-29 15:05:19 -05:00
Chris Tate d19fad3999 Match Node's emitWarning, kill, chdir and userInfo behavior (#2532) 2026-10-29 15:04:59 -05:00
Chris Tate e6f6cd4b98 Release the web view of an abandoned fetch body while its transfer runs (#2570) 2026-10-29 15:04:15 -05:00
Chris Tate 42e57e4ef3 Deliver a line to a single line listener without the listener snapshot (#2531) 2026-10-29 15:03:53 -05:00
Chris Tate ce6d9d0f1a Skip the receiver bracket for adapters of receiver-free callbacks (#2564) 2026-10-29 15:03:34 -05:00
Chris Tate d67f378a50 Release the web view of an abandoned fetch body while its transfer runs
A Response body's stream gets a web ReadableStream view when a member the
fetch stream lacks is read (for example `body.tee`). The view's source holds
the stream, and the stream released the view only when that source was its
last holder. While the transfer runs it holds the stream too, so a dropped
body with a view kept a reference count of 2: the unobserved-response
discard never ran, the paused socket was never resumed, and a compressed
response dropped before HTTP EOF kept the process alive.

The stream now releases the view when only the view's source and the live
transfer hold it, and then takes the unobserved-response discard, as a
body without a view does.

The static-abandon-view fixture reads `body.tee` and `body.locked` of a
4 MiB incompressible gzip response from the new /gzip-incompressible route
and drops it. It hung until the test's timeout before this change and now
exits as Node does.
2026-10-29 13:44:32 -05:00
Chris Tate f9b024dbab Count ArrayBuffer storage in process.memoryUsage()
process.memoryUsage() reported external and arrayBuffers as 0. arrayBuffers
is now the storage of the ArrayBuffers, typed arrays, Buffers, DataViews
and SharedArrayBuffers the program holds, and external the same storage
without SharedArrayBuffers, as Node splits them. A view over existing
storage adds nothing. v8.getHeapStatistics().external_memory reads the
same figure as external.

Each ScrBytes owner records the bytes it counts in the struct's padding,
so ScrBytes stays 64 bytes and the offsets compiled code reads hold; a
static assertion pins them. Owners are counted in a per-instance counter,
because their reference counts are not atomic and they are released on
their instance's thread. Shared storage moves its owner's count to a
process-wide atomic counter, since any thread may release it.
2026-10-29 13:36:41 -05:00
Chris Tate 51c9e31533 Skip the receiver bracket for adapters of receiver-free callbacks
A signature adapter forwards the ambient receiver to its original, and
its closure carries the original's RECEIVER_FREE bit. Calls through the
closure's own pointer tested that bit, but the direct entry of a known
adapter target bracketed every call, because the adapter itself counts
as a receiver reader. The direct entry now tests the bit too.

The receiver readers analysis also admits more node kinds that run no
user code: optional chains, record presence and absent fields, dynamic
boxing, array for-of loops, typed array writes, typed array methods and
constructors over typed operands, the module binding check and region
intrinsics, performance.now, Atomics operations, string and number
library calls over primitives, and new Error(message, options) without
an options object.
2026-10-29 10:42:30 -05:00
Chris Tate 7aaf98b53c Check nullable destructuring sources through their unit arm
The TypeError check for a null or undefined source now reads the unit arm the value holds as a dyn null or undefined, instead of converting the whole union to dyn. A union whose data arm has no dyn form, such as a record holding a generator, compiles again instead of failing with SC9001.
2026-10-29 10:24:12 -05:00
Chris Tate 352842f020 Scope early errors to function bodies and class strictness, and match the upstream compareArray reads
- A function body or class static block treats its top-level function
  declarations as var-scoped: duplicates are legal in strict code, and a
  let/const/class of the same name is SC0006.
- Strictness is the code around a node: a function declaration's own
  "use strict" no longer makes its declaring context strict.
- Every part of a class is strict; object literal methods and accessors
  are strict only by their own prologue or their surroundings, and an
  arrow function's block body can carry "use strict".
- The bare compareArray reads b.length, then a.length, and a.length again
  before every element pair, converting it as the upstream `i < a.length`
  does, so a string length compares its elements.
- Tests that relied on the old handling of undeclared names now use
  constructs that are still refused or still compile: the installed-entry
  diagnostics fixture reports a syntax error (a read of an undeclared name
  is now Node's run-time ReferenceError), and static-objects-order's
  loops.js stores from its for-in head into `undefined` instead of an
  undeclared name (a store into an undeclared name refuses its package with
  SC1091).
2026-10-29 08:37:25 -05:00
Chris Tate dd927301fc Read builtins into untyped TypeScript slots as Node's values
In a TypeScript source, a builtin global used as an object literal member
(`const o: any = { j: JSON }`) failed to lower, and the member silently
became a closure that threw when called. Node reads the global itself, so
`typeof o.j` answered "function" instead of "object", two reads were never
equal, and `o.j.stringify(x)` threw "is not a function". There was no
diagnostic.

A builtin read into a slot the checker types as any or unknown now holds
the value a JavaScript source reads: the JSON, Math and Reflect namespace
objects, Buffer, and functions such as structuredClone, atob and
queueMicrotask. This covers object and array
literal members, arguments, initializers and return values. A member of a
literal whose own slot is any takes the literal's slot. Array, setTimeout
and setImmediate keep their refusal: their values lack Array's statics and
the timers' util.promisify.custom getter.

Object literal members of TypeScript sources no longer defer a failed
lowering to a runtime closure. A builtin with no value (Intl, Proxy,
parseInt, process, a typed slot) reports SC2020 at compile time.

util.inspect now prints a plain object's Symbol.toStringTag as Node does:
`Object [Math] {}`, `[Object [Y]]` past the depth limit, and
`[Object: null prototype] [N] {}`. The tag is read through the prototype
chain and getters, and it is skipped when it is empty, not a string, the
name "Object", listed as an own enumerable key, or its getter throws.
2026-10-29 07:57:38 -05:00
Chris Tate 9021a03133 Pass a shared view whole to a replaced stdout or stderr write
A shared view written to stdout or stderr went out in 64 KiB pieces through
scr_stdio_write, so a replaced process.stdout.write received the first
piece alone, and a false return, which only reports backpressure, ended the
loop: a view larger than 64 KiB lost the rest. With a write hook, the view
is now copied whole and passed in one call, as Node passes it; the bounded
pieces go to the stream itself through scr_stdio_write_raw, which never
stops early.

tests/corpus/shared-bytes-stdio-write.ts replaces process.stdout.write with
a function that records each chunk's length and returns false, and writes a
150,000-byte shared view through it.
2026-10-29 07:50:05 -05:00
Malte UblandClaude Opus 5.5 b1b7aff25c Renumber the new corpus programs to 8514 and 8515
Group C adds two corpus programs whose 8423 prefix another group also
proposes; main's highest corpus number is 8513, so take the next two free
numbers and move the matching per-program cost files with them.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-29 05:34:49 -07:00
Malte UblandClaude Opus 5.5 48d524a321 Lower a JavaScript read of an undeclared name to Node's ReferenceError
D-2: Node resolves a name nothing declares at run time, and the read always
throws the catchable `ReferenceError: x is not defined` at the access. A
`.js` read of such a name now compiles to exactly that throw instead of
refusing the program, with Node's message and Node's catchability.

The read's value is checked-dynamic (the checker's error-any after the
relaxation in the previous commit, narrowed by the use site where one
provides context), and every path through it unwinds before a use observes
the value, so the dynamic slot never carries a wrong value. Shorthand
position (`{ x }`) needs its own declaration test: there the checker
answers the PROPERTY symbol, so the name is bound when
`getShorthandAssignmentValueSymbol` has no value for it.

`typeof` is the one operator that never touches its operand value, and Node
answers "undefined" for an undeclared name, so `lowerDynTypeofTest` folds
the test to a literal before the read is ever lowered — `typeof x ===
"undefined"` stays true and `typeof x` alone prints "undefined".

No name that Node serves from the global object reaches this branch: the
compiler models those as declared (that is how the corpus reads `process`,
`console` and friends), so this is the name Node itself would report
missing.

The corpus program pins every shape: bare read, call, member read,
arithmetic, comparison, shorthand, destructuring default, typeof test, a
guarded read that keeps running after the throw, and a declared control.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-29 05:34:48 -07:00
Malte UblandClaude Opus 5.5 ff93a5a4de Refuse sloppy implicit-global writes by name, and relax undeclared JS names
Node resolves a name nothing declares at run time. In sloppy mode a WRITE
creates a property on the process-wide global object, which a native binary
has no object for, so those programs belong on the Node island route (plan
D-2/D-5). Until that routing exists, an undeclared write in a `.js` source
is refused at build time under a dedicated code, SC1091, whose text names
the island route instead of failing with a generic no-lowering message.

Reaching that refusal at all means the checker has to stop treating an
undeclared name in a JavaScript source as a compile error, so 2304, its
did-you-mean spelling 2552, and shorthand position's 18004 join the JS
relaxation set. The relaxation stays `isJsSourceFileName`-gated: TypeScript
input keeps the compile error, where an undeclared name is a bug rather
than a runtime path.

The refusal is a preflight walk over every JavaScript file, so it reports
once per write, before any code is emitted, at the span the checker used.
`isJsWriteTarget7` is shared with the lowerer's unresolved-reference fence,
which supplies the same refusal for a write that reaches lowering by a path
preflight does not model. An empty identifier is skipped: it is a parse
recovery artifact, never a name a program writes.

The JS-read half of D-2 (the name lowers to Node's catchable
`ReferenceError`) is the next commit; this one only changes what the build
says about a write.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-29 05:34:47 -07:00
Malte UblandClaude Opus 5.5 b234e06f20 Enforce the ECMAScript early errors the checker does not model
Static semantics reject a program before it runs. Until now the compiler
borrowed that rejection from whatever tsc happened to report, and Test262's
negative-parse suites show where the borrowing stops: programs the spec
makes SyntaxErrors compile and run. This adds the two largest families the
grab-bag misses (plan §7.7 D-4, option c), each under its own preflight
code, so a checker relaxation can never remove the only rejection a
parse-negative program had:

  SC0005  a statement list declares one lexical name twice
          (`{ class f {} async function f() {} }`)
  SC0006  a lexical name is `var`-declared in the same statement list
          (`switch (1) { case 1: let x; case 2: var x; }`)
  SC0007  a declaration where the grammar admits only a Statement
          (`while (false) function f() {}`, `l: class c {}`,
          `if (x) async function f() {}`)

frontend/early-errors.ts is a preflight walk over JavaScript sources, so a
rejected program fails where a tsc parse error fails, before any lowering.
A case block is one lexical scope across its clauses; a list's
VarDeclaredNames reach into nested blocks but never into a function or a
class body; and Annex B's allowances are honored exactly: two plain
function declarations may repeat in a sloppy block (B.3.3.1), and a plain
function may be an if/else body or a labelled statement in sloppy code
(B.3.2) — an async function, a generator or a class never gets either
allowance, and neither does any function as a loop or 'with' body.

Strictness comes from Node's own module classification (isNodeEsmFile7),
not from the checker's external-module bit: moduleDetection=force gives
every file module scope, CommonJS included, and taking that bit as
strictness cost the Annex B exemption on the annexB
function-redeclaration tests. Typed sources are untouched — their checker
diagnostics (TS2451 and friends) remain the gate, and re-reporting a
collision under a second code would only duplicate them.

tests/test262/support.mjs counts one of these codes at a location inside
the test's own source as the parse-negative match, the same standard the
helper already applies to tsc's early errors. The diagnostics fixtures pin
both families, including the strict half of SC0007 where Node refuses the
two Annex B shapes as well.

The pass only reads the AST and only refuses programs, so no typed
program's IR changes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-29 05:34:47 -07:00
Malte UblandClaude Opus 5.5 c6db09c8e4 Relax the async-iterator protocol demand in untyped JavaScript
TS2504 demands a `[Symbol.asyncIterator]()` method that returns an async
iterator. In untyped JavaScript, whether a value is async-iterable is a
runtime fact of the value rather than a static one, exactly like the sync
demand (2488) relaxed just above. `for await` over a non-async-iterable
and `yield*` in an async generator both lower through the runtime
protocol openers, which read the method, call it, and throw the same
TypeError Node throws when it is missing, uncallable, or returns a
non-object.

Only diagnostics change, so only variants that previously held a 2504
refusal can move: the relaxed set is consulted only when JavaScript
strictness is suppressed, and 2504 is the sole code this adds. The sync
sibling 2505 stays enforced on the typed lanes, and TypeScript sources
keep 2504 because the relaxation applies to `.js`/`.mjs`/`.cjs`/`.jsx`
only.

Corpus 8423-async-iterator-protocol-shapes.js pins the seven protocol
shapes Node rejects and the two it accepts.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-29 05:34:46 -07:00
Malte UblandClaude Opus 5.5 a4c19ae4b4 Host the bare compareArray from the upstream assert.js harness
harness/assert.js defines `compareArray(a, b)` beside `assert`: a plain
function that RETURNS a boolean, whose throwing counterpart is
`assert.compareArray` (already hosted). The adapter now injects the bare
name into every prepared test and implements the upstream comparison:
read both `.length`s, so a nullish operand throws the same TypeError,
return false on a length mismatch, otherwise compare elements by
SameValue.

That also admits the compareArray.js include when the only use is a
direct call. A value reference or property read stays excluded, because
it would need the upstream function object and its `format` expando.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-29 05:34:44 -07:00
Chris Tate f96217e289 Count loop-body var initializers as writes, and skip overload signatures
- A `var` with an initializer inside a loop body of its own function runs
  once per iteration, so the binding counts as written, and generic
  instances share it instead of copying an earlier iteration's value.
- Only a function declaration with a body initializes its binding:
  overload signatures no longer make an overloaded function count as
  redeclared, so it is still a function that nothing reassigns (for
  example as a pipeline() function stage).

Adds the corpus program pipeline-overloaded-stage.ts, with its corpus cost
and order baseline, and a loop-body case to
generic-arrow-mutable-captures.ts.
2026-10-29 07:24:45 -05:00
Chris Tate 2473207af2 Name the emitter corpus programs instead of numbering them
Rename 8640-emitter-arguments-listeners.cjs and
8641-emitter-leak-warning-exit.cjs to emitter-arguments-listeners.cjs and
emitter-leak-warning-exit.cjs, with their cost files and ts7 baselines, as
AGENTS.md asks for new corpus programs.
2026-10-29 07:18:44 -05:00
Chris Tate b421cf6a38 Pass every argument to emitter listeners that read arguments, and deliver the leak warning at exit 2026-10-29 07:12:59 -05:00
Chris Tate d478dc9ec5 Send a zero from an inline RWF_NOWAIT read to the pool
Linux 5.9 and 5.10 answer 0 instead of EAGAIN for an RWF_NOWAIT read of
data that is not cached. The inline read took that 0 as end of file and
ended the ReadStream early. A zero from the inline read now goes to the
pool, whose read returns the data or confirms end of file; this costs one
pool round trip at the end of each file.

fs-stream-nowait.test.ts preloads a preadv2 shim that answers 0 for every
RWF_NOWAIT read and checks that a ReadStream still reads the whole file,
as Node does.
2026-10-29 07:05:55 -05:00
Chris Tate 1acdda7328 Read and write cached regular files on the loop thread in fs streams
On Linux, an fs stream's read or write of a regular file first tries
preadv2/pwritev2 with RWF_NOWAIT on the loop thread. When the page cache
holds the data, the step completes there and its completion waits for the
next loop turn (scr_work_submit_done), as a pool job's completion does,
so timers and I/O still run between steps. EAGAIN, a kernel or file
system without RWF_NOWAIT, and other files take the pool as before.

- A read the cache holds only part of finishes on the pool, so each chunk
  is the one a single read() returns.
- A partial write completes with the bytes written; the stream submits
  the rest, as after any partial write.
- Writes stay on the pool when RLIMIT_FSIZE is finite, so SIGXFSZ cannot
  reach the loop thread.

scr_work_submit_done queues a completed job on a list the loop moves to
the done queue at its next sleep step, which is skipped while jobs wait.

Corpus 8530 covers a positioned read with a small highWaterMark, a write
stream with a start position into an existing file, and an append stream
over cached files, against Node.
2026-10-29 06:33:43 -05:00
Chris Tate 96739536d2 Read fs.read's position option once and check a bigint against length |= 0
fs.read's options forms read the position option a second time when it
was not a bigint, so a getter ran twice and a stateful one could move the
read. The option is now read once.

A bigint position was checked against the length before `length |= 0`.
A fractional or infinite length made the range check build its bound
from a null BigInt and crash. The check and the read now take the
converted length, so such a read completes with zero or the truncated
count of bytes, as in Node.
2026-10-29 06:22:18 -05:00
Chris Tate 64f9481fb8 Link the web stream classes for an element-access tee, pipeTo or pipeThrough
The scan that decides whether a program uses a web stream operation the
native streams lack only matched property accesses, so a TypeScript
program whose one such operation was `body["tee"]()` kept the native
streams and threw a TypeError at the call. Element accesses with a string
literal key now count the same way, in the TypeScript scan and in the
JavaScript one.

Adds the corpus program web-streams-typed-element-tee.ts, with its corpus
cost and order baseline.
2026-10-29 04:41:05 -05:00
Chris Tate 32745afc07 Bind generic arrow captures before parameter defaults, and count var redeclarations as writes
- A generic arrow instance binds its trailing capture locals before it
  lowers the declared parameters, so a parameter default that reads an
  immutable enclosing binding resolves it instead of failing with SC1090.
  The captures stay trailing parameters.
- A binding is written when one of its own declarations writes it: a
  `var` in a for-in or for-of head (every iteration), or a redeclaration
  that initializes (`var x = 1; ... var x = 2;`, a parameter or function
  declaration that a `var` redeclares). Such a binding is now shared with
  generic instances through the environment form, so a closure an
  instance returns reads the current value instead of a copy.
2026-10-29 04:30:06 -05:00
Chris Tate 60be717d92 Merge main 2026-10-29 04:24:50 -05:00
Chris Tate 442c06fde3 Throw Node's TypeError when a nullable destructuring source is null
In JavaScript files, a destructuring declaration whose source is one object or array type or null/undefined (`text.match(re)`, `items.find(...)`) threw a plain Error when the source was null or undefined. It now throws Node's TypeError with V8's message: "Cannot destructure property 'index' of 'text.match(...)' as it is null." for an object pattern, and "found is not iterable" or "object null is not iterable (cannot read property Symbol(Symbol.iterator))" for an array pattern. An object pattern whose source V8 spells in a form the compiler does not reproduce keeps the runtime fence.
2026-10-29 04:20:01 -05:00
Chris Tate c533750aa3 Test a jammed loop's exit every iteration when its counter may not advance
The jammed loop tests its exit only when `(j & 3) === 3`. When the inner
counter starts at NaN, an infinity, or a value that `j + 1` leaves unchanged,
`j & 3` never becomes 3, and the loop ran past the point where every
accumulator was full, without end when its bound never ends it.

The every-fourth test is now used only when the inner loop starts at an
int32 literal: the counter then takes consecutive integer values, so its low
bits reach 3 within four iterations. From any other start, the jammed loop
tests its exit every iteration.

The unit test checks which starts get each test, and the corpus program adds
loops starting at -Infinity and -(2 ** 54) that end once both pixels escape.
2026-10-29 04:17:06 -05:00
Chris Tate eff8ef743d Read at the exact bigint position in fs.readSync
Co-authored-by: MathurAditya724 <57684218+MathurAditya724@users.noreply.github.com>
Fixes #2404
2026-10-29 04:11:04 -05:00
Chris TateandMathurAditya724 7f33f37384 Run for-in over a receiver that may be null or undefined
for-in over a value whose type adds null or undefined to one enumerable
type (a record, an index-signature record, an array) was refused with
SC1052. JavaScript runs such a loop zero times for null and undefined. The
loop now evaluates the receiver once, skips it when it is null or
undefined, and otherwise walks the value as its own type.

Fixes #2403

Co-authored-by: MathurAditya724 <57684218+MathurAditya724@users.noreply.github.com>
2026-10-29 03:58:21 -05:00
Chris Tate 82fbe022b4 Deliver the lines a chunk split after a line listener closes the interface 2026-10-29 03:51:59 -05:00
Chris Tate 6d170bf082 Check every path namespace key now that format and parse have function values 2026-10-29 03:36:13 -05:00