- Narrowing `x + c < b` and its other comparison forms subtracted the offset from the bound in floating point. A fractional bound could then round across an integer: `x + 2147483648 > -(2 ** -60)` gave the minimum -2147483647, which leaves out -2147483648. The bound now rounds to an integer first, and the offset comes off in exact integer arithmetic. Integer bounds give the same facts as before.
- A unit test covers that comparison.
- The zone-offset cache keeps an offset over 19 days when both ends agree, as V8's date cache does. America/Argentina/Tucuman changed its offset twice within 12 days in June 2004, so in Node the answers between the two changes depend on the order of the reads: ascending reads keep the May offset through June 12, and descending reads see the June offset.
- The cache's comment now states that behavior, and corpus date-zone-intervals.ts reads those days in both orders. Its output matches Node for each order.
- The bounded-loop analysis collected writes from the loop body only. A binding the condition writes (`i < ((source = values[i]), values).length`) then passed as stable, so a cursor copied from it took the source's entry range, and its integer shadow truncated fractional values. The condition and the update are now scanned too, and a binding either one writes is neither a stable bound nor a copy source.
- Corpus 5686 adds a copy whose source the condition writes with fractional values.
concat, substr and the other typed String and Number extra methods
convert their arguments after evaluating all of them. When an argument's
conversion can run program code (an object's toString or valueOf), a
variable read of the receiver or another argument is now kept in a local
as well, so a conversion that assigns the variable leaves the
already-evaluated argument unchanged, as in Node.
A published graph is immortal and shared by pointer across threads, but a
shape names a node of one thread's transition tree, which ends with that
thread. A worker that read a published object through a cache left its
shape in the object; after the worker exited, the main thread's reads
followed it into freed memory. Immortal objects now stay unshaped, and
publishing (postMessage's shared graphs and @scriptc/threads publish)
clears a shape the object already has.
The key-order witness pass chose a shape's enumeration table from the
order its object literals write their keys, but two gaps sent literal
values back to the declaration order (JSON.stringify, Object.keys and
inspect then differed from Node, silently):
- Witness notes are keyed by node identity, and frontend passes that run
before the witness pass (the caught-value dispatch, the class dynamic
dispatch) rebuild whole function bodies through mapExprChildren, so
every record literal in such a function became an unnoted construction
that pins its shape. The mapper now reports rebuilt record literals to
an observer, and the notes follow them to the rebuilt node.
- A shape a checked conversion (typed JSON.parse) also builds stayed
pinned to today's table even when the program's own literals build it
in another order, such as a message sent to a child and parsed back. It
now takes its literals' order; parsed values in another order keep
their own through the record-source view, and a shape only checked
conversions build keeps today's table.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
execa 5 mixes then/catch/finally into a real ChildProcess with
Reflect.defineProperty, replaces kill and adds cancel by assignment, and
awaits the child.
- A new optional runtime unit, scr_dyn_handle_props.c, keeps those
properties in a side table keyed by the native child. Wrapper ops for
the child tag look there first for reads, member calls and writes.
defineProperty and getOwnPropertyDescriptor use it, and await adopts a
child with a callable then. Class methods read as values call the
class's own method.
- In JavaScript files, writes onto typed children, reads and calls of
added names, method values for bind/call/apply, and members no typed
lowering claims go through the child's checked-dynamic box.
- Only programs whose JavaScript adds properties to children link the
unit and install the table; everything else compiles as before.
- A setTimeout delay that may be a boolean converts with ToNumber, and
`if (t.unref)` over a timer handle folds to true (execa's lib/kill.js).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The dispatch instantiates one bridged emit per argument count and emitter
class; capping it at four keeps programs that only read emitter members
through computed keys from growing by the full table.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A function value whose result is a union of records, assigned to a slot
that returns one record type, lifts each record arm into the slot's
record. The lift declares a scratch local, which went into the function
that requested the adapter, so calling the adapted function failed IR
validation. The adapter body now lowers in its own function context and
the adapter declares those locals.
The enum rewrite turns a package's numeric enum into one object holding
its names and its reverse mapping, so the object's fields have different
types. A reverse read with a runtime key, such as
DiagLogLevel[DiagLogLevel.WARN] in @opentelemetry/api, then failed with
SC1090 ("the declared fields have no one common type").
A keyed read of such a record already answers a checked-dynamic value in
JavaScript files. It now does the same where the checker types the read
'any'. The new npm case tsc-enum-reverse-reads matches Node, as does the
opentelemetry-api case again.
exec on a regular expression held as an untyped value threw "Native RegExp
method has no lowering". It now returns Node's match array: the captures,
with undefined for one that did not participate, then index, input and
groups, or null without a match. Global and sticky expressions read and
advance lastIndex and reset it when nothing matches, as the typed exec
does.
A JavaScript array literal that mixes promises and plain values lowers as
a checked-dynamic value, and for await over it took the synchronous
iteration path, binding the promises themselves. It now takes the async
mode of the checked-dynamic loop, which awaits each value.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- The indexed-read prepass now scans parameter patterns with the parameter's type. A function that destructures a missing runtime-key entry of an index-signature record from a parameter (`function pick(k, { [k]: v }: Record<string, number>) { return v; }`) returns undefined instead of throwing a TypeError, nested patterns included.
- Corpus 6492 adds a function, an arrow and a nested parameter pattern; 6492 and 6493 get their per-program cost files.
`a?.b.c` on an `any` value answered undefined when `a` was present and
`a.b` undefined: every later step of the chain read optionally. Node
short-circuits only on the guarded receiver and throws reading `.c`.
An `any` receiver now makes the tail an optional chain like a dyn one,
and inside the chain's guarded body the consumed `?.` no longer marks
the later dynamic reads optional. (Kept from #1561, whose checked-cast
fallback is reverted.)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Reading an EventEmitter.prototype method off a compiled emitter (a program
class extending EventEmitter, or new EventEmitter()) through an untyped
value answered undefined (`typeof o.on`, `const f = o.on; f.call(o, ...)`,
`o.emit.bind(o)`), and EventEmitter.prototype.on in JavaScript was a fence.
Such reads, typed value reads, computed keys, `in`, and
EventEmitter.prototype.<method> now answer the runtime's
EventEmitter.prototype functions, the ones constructor-function emitters
inherit, so `o.on === o.addListener === EventEmitter.prototype.on` and
names and lengths match Node. The first read installs a program dispatch
that those functions call for a compiled emitter receiver; it invokes the
method through the existing untyped dispatch with the call's argument
count (emit forwards up to 16 arguments after the name, then fences).
An untyped emit on, or read of emit from, a class that overrides emit now
fences instead of bypassing the override for the native emit. A subclass
visited under the boxed EventEmitter capsule keeps its own dispatch plan
for values boxed as itself.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A function stored in a JavaScript object literal's property printed as
[Function (anonymous)] and had an empty `.name`. The property's boxed
function now takes the name Node gives it: a function read by name keeps
that name (`{ f }`, `{ k: f }`), and an anonymous function, arrow or method
takes the property's key (`{ g: function () {} }`, `{ m() {} }`,
`{ 'a-b': () => 1 }`, a computed key that folds to one string).
crypto.createECDH returns a native ECDH handle backed by mbedTLS for
prime192v1, secp224r1, prime256v1, secp384r1, secp521r1, secp192k1,
secp224k1, secp256k1, brainpoolP256r1, brainpoolP384r1 and
brainpoolP512r1. generateKeys, computeSecret, getPublicKey,
getPrivateKey, setPrivateKey, setPublicKey and ECDH.convertKey follow
Node 24: compressed, uncompressed and hybrid points, input and output
encodings, the private key range, key pair validation, and Node's error
codes and messages.
Curve names follow OpenSSL's short-name lookup: names of other curves
from crypto.getCurves() are refused at compile time when literal and
throw a coded SC2020 error when computed, other OpenSSL short names
throw ERR_CRYPTO_OPERATION_FAILED, and unknown names throw
ERR_CRYPTO_INVALID_CURVE. Calls whose arguments no @types/node overload
accepts, and computeSecret(view, encoding), are refused at compile time.
The runtime unit scr_ecdh.c links, with the mbedTLS archive, only into
programs that use ECDH. wasm32-wasi builds refuse it.
Spreading a value held as any that is not iterable into an array literal
threw `TypeError: value`. It now throws V8's message, which names the
spread expression: `x is not iterable` for an identifier or property path,
through parentheses and type assertions, and `f is not a function or its
return value is not iterable` for a call of one. for...of and array spread
share the helper that spells it.
- The indexed-read prepass now visits nested object patterns with the nested element's own type. A module-scope binding from a missing runtime-key entry of a nested index-signature record (`const { limits: { [key]: limit } } = config`) holds undefined instead of throwing a TypeError, with or without a default for the nested source.
- Corpus 6492 adds both nested forms.
for await over a synchronous iterator held as any reads and awaits the value
of the result that finishes the loop, as the async-from-sync iterator does, so
a throwing value getter or a rejected promise there rejects the loop.