Commit Graph
4589 Commits
Author SHA1 Message Date
Chris Tate 3cd2f97c7c Ignore SIGPIPE and SIGXFSZ in executables, as Node does (#2578) 2026-10-29 16:05:14 -05:00
Chris Tate 016ee41146 Check untyped destructuring sources in JavaScript files before reading keys (#2577) 2026-10-29 16:01:39 -05:00
Chris Tate e3a42c52a8 Ignore SIGPIPE and SIGXFSZ in executables, as Node does
Node sets SIGPIPE and SIGXFSZ to SIG_IGN at startup, so a write to a
closed pipe or past RLIMIT_FSIZE fails with EPIPE or EFBIG, and libuv's
spawns start children with every signal at its default action. The
runtime instead blocked both signals in the writing thread. On Linux the
signal a write raises goes to that thread, but on macOS it goes to the
process, so a write to a closed pipe from the fs pool's threads ended the
program with SIGPIPE.

- scr_init ignores SIGPIPE and SIGXFSZ. A process.on listener replaces the
  disposition while it is registered, as before.
- scr_fs_write_once writes directly in executables, and the fs pool's
  threads no longer block the two signals. Library builds keep the
  per-thread blocking, since their host owns the dispositions.
- scr_spawn_command starts children with SIGPIPE and SIGXFSZ at their
  default actions (POSIX_SPAWN_SETSIGDEF), for spawnSync, execFileSync
  and spawn alike.

write-signals-ignored.ts and write-signals-listener.mjs check the ignored
signals, a listener, and each spawn path's child against Node.
2026-10-29 16:00:39 -05:00
Chris Tate 4e67c5ce2c Host stdio: typed process.stdout.write slot, write-callback errors, drain, run-time-typed chunks (#2546) 2026-10-29 15:59:21 -05:00
Chris Tate 0725682c87 Check untyped destructuring sources in JavaScript files before reading keys
A JavaScript object pattern over an untyped source (the result of an untyped function, a parameter, a property of an untyped object) now checks the source first when V8 can spell it, as the `any` path in TypeScript files does: a null or undefined source throws "Cannot destructure property 'size' of 'lookup(...)' as it is null." instead of "Cannot read properties of null (reading 'size')". When the first element has a default and a static key (`{ size = 0 }`), the source is not checked first and the read throws, as in V8; this also applies to `any` sources in TypeScript files and to nullable union sources.
2026-10-29 15:58:44 -05:00
Chris Tate b507472888 Take from a Readable's buffered queue by moving its head index (#2573) 2026-10-29 15:50:34 -05:00
Chris Tate 6f019151b7 Test only the exponent of a masked value's shift and sum in speculative integers 2026-10-29 15:45:58 -05:00
Chris Tate f1825611fb Collect garbage cycles across the scriptc and V8 heaps (NI-8) (#1908) 2026-10-29 15:33:24 -05:00
Chris Tate 0e6dec51d4 Test262: private static members with brand checks (#2549) 2026-10-29 15:31:06 -05:00
Chris Tate 714385609e Call private static fields through the general value-call lowering
A call through a function-valued private static field (this.#fn(...))
completed its arguments against the field's fixed parameter list, so a
rest-parameter function lost the arguments for its rest parameter, and a
function that reads arguments was refused. The call now takes the
ordinary call-through-value lanes (typed rests, runtime-arity closures,
checked-dynamic values) and binds this to the checked receiver.
2026-10-29 15:27:36 -05:00
Chris Tate b8a6d3805a Islands: CommonJS island requires, builtin-class handles, JavaScript widened bindings (#1907) 2026-10-29 15:27:09 -05:00
Chris Tate 0117f39da0 pi wave 5: union layouts and reads, provider and tool records (#2547) 2026-10-29 15:25:45 -05:00
Chris Tate 87c8d14510 Test262: String/Number/Boolean wrapper objects in JavaScript sources (#2550) 2026-10-29 15:24:04 -05:00
Chris Tate d972a6bc13 Defer island modules whose recorded load is deterministic (NI-7b/7d/7e/7f) (#1906) 2026-10-29 15:18:28 -05:00
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 9632906b4d Take from a Readable's buffered queue by moving its head index
Taking the head entry of a Readable's buffer moved every remaining entry
down one slot, so draining a queue of n buffered chunks cost O(n²). The
buffer now keeps the index of its first entry: a take advances it, a push
at the tail moves the entries down only when at least half the slots are
free at the front (and grows otherwise), and unshift takes a freed front
slot when there is one. The drop, trace and boxing loops walk the entries
from that index.

The stream-buffered-drain runtime workload buffers 200,000 objects and
100,000 Buffers before draining them; corpus 8533 covers reads across
entries, unshift into a freed slot and onto a part-read head, setEncoding
after takes, and object-mode takes, unshifts and pushes.
2026-10-29 14:59:10 -05:00
Chris Tate b8e678b765 Evaluate a drain name expression, and discard a write callback's return value
events.once(process.stdout, name) with a name typed "drain" returned the
pending promise without evaluating the name, so a call or comma
expression producing "drain" never ran. A name that is not a string
literal now runs before the promise is created.

A write completion callback that returns a value (`() => count++`, a
stored function typed `(): number`) reported SC2020. The stream ignores
the return value, so such a callback is adapted to return nothing, as
before the error-argument form was added.

The stream-shims npm case compiles and matches Node now that write
callbacks take the error argument, so its SC2020 expectation is removed.
2026-10-29 14:48:45 -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 62943d7460 Search wrapper graphs once, brand the wrapper prototypes, read call members once
- The wrapper check before JSON, util.inspect, structuredClone and deep
  equality searched the value's graph recursively with only a depth
  limit, so an object with two references to itself took exponentially
  many calls, and a wrapper more than 64 levels deep was not found. The
  search now keeps a set of the objects it reached and an explicit stack:
  each object and array is searched once, at any depth.
- String.prototype, Number.prototype and Boolean.prototype are wrappers of
  "", 0 and false, as in Node, so their valueOf and toString answer the
  primitive instead of throwing the brand TypeError. String.prototype has
  its own `length` of 0.
- A string-named method call on a dynamic receiver in a program with
  wrappers read the member twice: once to check for the builtin
  String.prototype method and once for the call. The call now reads it
  once, and wrapper.isStringMethod checks that value.
- createsWrappers is listed with the lowerer's first-use caches that the
  unit journal leaves out.

The wrapper corpus program covers the prototypes' values, a getter read
once per call and console.log of a 40-level shared graph. The refusal
test covers a wrapper 100 levels deep, a wrapper under a shared graph and
util.inspect of a cyclic value.
2026-10-29 13:31:39 -05:00
Chris Tate 14babd6231 Call private static function fields and update private static fields through this
A call of a function-valued private static field through a receiver other
than the class's own name (this.#fn(a) in a static accessor) checks the
receiver and calls the stored function with this bound to the receiver,
instead of reading a public "#fn" property. this.#x++, --this.#x and
this.#x op= v check the receiver where the old value is read and update
the field's native storage, instead of a public "#x" property.
2026-10-29 11:13:10 -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 1941a3e353 Merge main 2026-10-29 09:27:38 -05:00
Chris Tate 1ace0b4e88 Expect the map and record-width results the PR's lowering now gives 2026-10-29 09:14:55 -05:00
Chris Tate dbc448efe2 Merge main 2026-10-29 09:14:05 -05:00
Chris Tate 3ee8c41cbd Append long flatMap tuple arms in index order, and read omitted generic members
A flatMap callback's tuple arm appended its elements in the stored field
order, which sorts the names as strings, so a tuple of eleven or more
elements came out as 0, 1, 10, 2, …. The elements now append in index
order.

A generic receiver's member that the instantiation does not store reads
through the checked keyed read, but only for an access that is not a
write. The write test took any binary expression's left operand and any
unary operand for a write, so `options.key ?? fallback`,
`options.key === undefined` and `!options.key` still failed with SC1090.
Only an assignment's target, a delete, and `++`/`--` count as writes now.
2026-10-29 09:12:40 -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