Main's FinalizationRegistry took handle tag 72, so the Node island
handle moves to 73. Island handles keep their instanceof path in the
new scr_bytes_instanceof_at.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The libnode packs' smoke program (#1684) still used the pump/alive/
backend_fd entries that the hosted loop (#1696) replaced, so every
Build libnode pack job failed to compile it. It now provides the full
host table and dispatcher and runs Node's event loop once after starting
the asynchronous calls, as the runtime does at the end of a program.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Records are width-tolerant, and a union's arms were tried in canonical
order, which sorts record arms by shape id. When a `{}` arm, or an arm
whose fields are all optional, came first, it took every object, so a
value converted to the union lost the fields only a wider arm has:
const pairs = [["a", { publicKeyEncoding: 5 }], ["b", {}]];
for (const [, v] of pairs) console.log(Object.keys(v));
printed [] twice, where Node prints [ 'publicKeyEncoding' ] and [].
A checked cast to `{ p: number; q: string } | { p?: number }` dropped
`q` the same way.
The checked conversion now tries record arms with more required fields
first, then those with more fields, and keeps the canonical order
otherwise. The union's tags are unchanged.
The test refused unchecked-dependency only because its one-line index.js
looked minified. A dependency another package's JavaScript imports needs
no declarations of its own (npmStaticIneligibleReason's untypedDependency),
so with the minified refusal gone it compiles; the test now builds it and
checks the library's result.
The key-order witness treats records JavaScript builds as having a run-time
order. Two cases this change narrowed are counted again:
- A native callback that a checked JavaScript function finds inside a
record or array it receives is called with records JavaScript builds. The
walk of a checked function's parameters reset to the built direction at
the fields and elements of the records it passes out, so the callback's
own parameters counted as passed out. Children of a container passed out
now count in both directions (written by JavaScript, and read out of it),
and class instances keep the direction too.
- A replaceable method's checked result builds a record when a replacement
returns one. It is left out only while every site the census found for
the method name reads it or assigns a function whose return type is
primitive, as the handlers a router class installs through a computed key
do. The census now records every such site (Lowerer.methodAccessSites).
The LLVM emitter's constructor runs many analyses that each walk every
function body to collect facts from a few node kinds. IrNodeIndex
(ir/node-index.ts) walks the bodies once, before those analyses, and
records each body's nodes in everyStmtList's order with a kind code per
node and the kinds present per function. An analysis names the kinds it
reads and receives exactly those nodes, in the same order, without
recursing through the tree; a function the index does not hold (a
rewritten copy) is walked as before. The index also lists the distinct
type objects of the module, which the boxed-union and runtime-feature
scans read instead of walking every expression's type tree.
These read the index: walk borrows, appended array literals, constant
callbacks and the rebinding census, int32 slots (dynamic field stores,
call facts, the box census, candidate writes and the range walks),
initializer bindings, stack observations, the may-throw call-graph
census, published types and construction facts, final fields, stable
globals, reference writes (private arrays, call targets, definitions,
fresh constructors), boxed unions and runtime features. Analyses whose
walks track their position (statement lists, nesting, ordered
dataflow) keep their own walks.
Callers outside the emitter pass no index and walk as before. The
emitted LLVM IR is unchanged.
packages/compiler/native/cli.profdata.gz is refreshed with
scripts/refresh-cli-profile.mjs on this change, so the native
compiler's optimization profile covers the rewritten analyses.
Refs #1777
An identifier-minified esbuild bundle renames the __commonJS helper
(var j=(u,t)=>()=>(t||u((t={exports:{}}).exports,t),t.exports)). The
recognizer already compared the helper's shape independently of its
parameter names, but only used a renamed match to refuse the package.
A stable top-level declaration with the helper's shape now splits the
bundle's chunks like the named helper, so lru-cache's CommonJS build is
no longer refused.
ToNumber and ToString of a foreign element are reached through a table
that scr_arr_set_foreign installs, so a program that never stores a
foreign element no longer links the dynamic tier through the array
runtime's joins, sorts and numeric reads.
Only number[] and string[] store a value written through a dynamic view
that their element type cannot hold. Other element types keep the
checked conversion's TypeError at the write. Counting every viewed
element type as possibly undefined made callbacks over records whose
arrays reach a package (zod3-infer) take optional elements and refuse
their spreads.
The EventTarget method values are cached for the program, and exit
released them only when an EventTarget, an event or a listener had
registered the unit's teardown. A program that read an AbortSignal's
addEventListener and never added a listener or made a target left the
cached functions live, and RC-audit builds exited with 99. Caching a
method value now registers the teardown too.
Refs #1777
npm packages are no longer refused up front for minified JavaScript. A
bundle whose interop helpers lost their names is still refused by the
bundle recognizers, and a package whose attempt fails is still refused on
its own.
A `var` read in a later declarator of the statement that stores it
(`var a = {...}, b = {...a}`) runs after the store. A read in a function
body is now located at the body's own statement instead of the body, so
JavaScript `var` bindings read after their store in a function keep their
type instead of gaining undefined.
`replace` and `replaceAll` with an untyped search value and a callback
call the regex callback replacement for a RegExp and the string-search
helper otherwise. That helper now accepts untyped callback parameters,
passes undefined to parameters after the subject, and converts number,
boolean and untyped results with ToString.
In JavaScript, `||` and `&&` over primitives of different types whose
result tsc types `any` evaluate in the checked domain.
An AbortSignal answered addEventListener, removeEventListener and
dispatchEvent read as values with functions of its own, which only ran on
AbortSignal handles: calling one on a plain EventTarget threw
ERR_INVALID_THIS. In Node they are EventTarget.prototype's methods,
shared with every target. The signal's property read now answers the
EventTarget method values (scr_event_target_method_value), which run on
any EventTarget receiver, AbortSignal included, so
signal.addEventListener === new EventTarget().addEventListener holds.
The signal's own method table keeps throwIfAborted.
The declaration boundary projected a user-declared interface as plain
data even when it extends a package interface. effect's Request.tagged
constructors create such values (interface Double extends
Request.Request<number>) with a prototype, so the boundary cast threw
"expected a plain object (this value has a prototype)". A user
interface now projects only when every interface it extends is
user-declared too; other values keep their JavaScript-world typing, so
the package finds its prototype members when the value goes back to it.
- With 'readable' listeners the stream stays paused, as in Node: 'data'
listeners, resume() and a parked for-await read no longer keep the loop
reading once the held input reaches the 64 KiB mark (or a larger
read(size)); reading resumes when a read drains it.
- Held input is a list of chunks with a running length, so an arrival
appends without copying what is already held, and read() joins only the
bytes it returns.
- New event-loop case: 'readable', 'data' and resume() with no read() for
400 ms while 1 MiB arrives.