jsx-source.test.ts compares the JSX rewrite with TypeScript 5's
transpileModule output, so it imports typescript5 as the other test
oracles on the TypeScript 5 island allowlist do. scripts/test-ts7.mjs
did not list it, and the TypeScript 7 parity job failed since #1786.
The fixture's Point literals now each record their own key order, so
they no longer diverge. The fixture adds a spread that sets an optional
key its source lacks, which keeps the type's declaration order, so SC6002
still reports the literal written in another order.
A binding that may hold a value its declared type omits (a function
parameter fed array elements, which can be holes) and that the checker
narrowed to one record or class arm of a union with more than two arms
was read through a checked extraction of that arm. When the binding held
a hole, a member read or method call threw "undefined is not
representable in the target union ..." for a record arm and "Value is
not an instance of 'Shape'" for a class arm, where Node throws "Cannot
read properties of undefined (reading 'a')".
The read now tests for undefined first and throws Node's TypeError, as
the two-arm path already does.
A generic binding inside a function that a nested function read before
the declaration lowered stores its instance into a TDZ box when the
declaration runs. That value was the instance's closure, which the
backend interns for module functions, so every evaluation of the
declaration stored the same function object. JavaScript makes a new one
each time. The stored value is now a closure over a lifted forwarder that
calls the instance, so each evaluation allocates a new closure; the
forwarder is created once per instance. Async and rest-parameter
bindings read this way report SC1090.
Corpus 7180 checks the identities.
ClassProtocols' identity-flow fixpoint asks the contextual type of each
site whose source carries identity, one round trip at a time, and lowering
asks the resolved signature of each call and the contextual type of each
array literal and conditional it lowers the same way. The checker facade
can now batch them: prefetchContextualTypes for the fixpoint's sites before
it runs, and, for each prefetched wave of reachable roots, the resolved
signatures of its calls and the contextual types of its array literals and
conditionals. A batched query that fails is left out of the memo, so a
direct query still throws where it is made.
The batches use the checker's getContextualTypes and getResolvedSignatures
requests. NODE_BATCH_QUERIES keeps them off until scriptc depends on a
tsc-ts release that answers them; while it is off the prefetches do
nothing.
Node runs the callback of an operation that a callback-style API starts
with the AsyncLocalStorage stores active when the call started it. The
runtime invokes these callbacks from the event loop, where the context is
empty, so getStore() answered undefined in fs, crypto, zlib, dns,
child_process, net, http and dgram callbacks.
In programs whose scheduling carries contexts (RuntimeFeatures
asyncContext), the lowering now binds each completion-callback argument to
the context active at the call (completion-callbacks.ts): typed closures
through als.bindCallback, whose bound closure enters through a thunk the
backend emits once per callback signature, and dyn functions through
als.bindCallbackDyn. The fs callback family binds in the runtime, where
scr_fs_cb_schedule queues the callback. Other programs compile as before.
AsyncLocalStorage.bind(fn) binds fn to the active context, also when it
is empty (a typed function keeps its type; in JavaScript files the bound
function is named "bound" and forwards its receiver, as in Node).
AsyncLocalStorage.snapshot() answers a function that runs its callback in
the snapshot's context. new AsyncLocalStorage({ defaultValue, name })
validates its options as Node does; getStore() answers the default value
where the store has no entry, exit() runs its callback with the store
undefined, and the name property answers the name option or "".
Event listeners on sockets, requests, responses and streams, stream
callbacks, server.close(callback) and https and tls callbacks still run
with no store; the limitations page lists them. Corpus 7110-7112.
Keys, primes and generators drop their leading zero bytes before import,
as BN_bin2bn does, and a value still past mbedTLS's 80000-byte limit
throws Node's error or a coded SC2020 error instead of aborting the
process: computeSecret rejects a key longer than the prime as too large,
a long generator is a bad generator, and setPublicKey/setPrivateKey
refuse with SC2020.
Under the RFC 5114 groups, computeSecret rejects a peer key with
key^q mod p != 1 with ERR_CRYPTO_INVALID_KEYTYPE, as OpenSSL's public
key check does, closing small-subgroup confinement of the private key.
The safe-prime groups keep the range check only, as in Node.
Exponents past 8192 bits are split into digits bit by bit, so
generateKeys with a long private key takes linear time.
When an event-loop callback calls an async function or steps a generator
that enters a store before its first await, the store becomes the
callback's at the end of the prefix. On the main slot it now ends with
that callback, as an enterWith there does: the copy schedules the tick
that clears the main slot, so the store does not reach later callbacks.
new tls.TLSSocket(socket, { isServer: true, secureContext, requestCert:
true }) skipped the options walk that refuses requestCert, so the server
asked for no client certificate and accepted every client, even with
rejectUnauthorized: true. With a secureContext, Node still reads the
socket's own options, so a truthy requestCert, SNICallback and
ALPNCallback now throw the fence there too.
A TLS socket's transport kept its 'secure' and 'session' listeners until
the socket was freed. A handshake that fails never fires 'secure', so a
listener that captures the socket kept the socket and its TLS state alive
after it closed. The transport now drops those listeners when the socket
settles, and ignores listeners added after that.
Record shapes are interned by field set, and their key order was one table
per shape. When object literals of one shape wrote the keys in different
orders (`{ id, name }` and `{ name, id }`), every value enumerated in the
first type's order, and anonymous literal types got no warning.
- The key-order witness pass gives such a shape its orders
(IrRecordShape.orderVariants, the declared table first) and a hidden
number field, %order, that each value sets to the index of its order:
a literal of explicit keys stores its own order, a copy from a shape
with one order stores that order, and spreads, rest bindings and
copies of values with orders read their source's index. Allocation
zeroes the field, so checked conversions and runtime producers keep the
table as before, and clones copy it.
- A write of an optional key a value lacks moves the key to the end of
that value's order, as in Node: the orders include each order such
writes reach, and the write site updates the index first, evaluating a
receiver other than a binding once.
- Object.keys, Object.values, Object.entries, for-in, util.inspect,
JSON.stringify, the record-to-dynamic conversion (structuredClone,
postMessage, untyped views) and live record views branch on the index.
The dynamic conversion and checked conversions leave the field out.
- A shape keeps its table, and SC6002 still reports its literals, when a
construction's order cannot be traced: a spread of a value of unknown
order, a clone that adds an optional key, a copy whose source is not a
binding, or more than 16 orders.
Corpus 7122 to 7128 check each surface against Node.
crypto.subtle.wrapKey exports a secret key (raw, raw-secret or jwk) and
encrypts it, and unwrapKey decrypts and imports one, as Node's
webcrypto.js does: the wrap algorithm normalizes through the wrapKey
registry (AES-KW) and then the encrypt registry (AES-GCM, AES-CBC and
AES-CTR with their dictionaries), the wrapping key's algorithm and
usages are checked, and a JWK payload is its JSON text, padded with
spaces to a multiple of 8 bytes for AES-KW. A wrapped JWK that is not
UTF-8 JSON with a kty member rejects with Node's DataError.
AES-KW is RFC 3394 key wrap over the vendored mbedTLS block cipher, in
scr_webcrypto_aes.c beside the other AES modes: wrapping takes a
multiple of 8 bytes and at least 16, unwrapping a multiple of 8 and at
least 24, and the integrity check value is compared in constant time.
The cipher's failures, and unwrapKey's import errors, reject on a later
loop turn as Node's job promises do.
This completes jose's symmetric key management: 7132 runs the real
jose 6.2.12 PBES2-HS256+A128KW, PBES2-HS384+A192KW, PBES2-HS512+A256KW,
A128KW and A256KW paths (EncryptJWT and jwtDecrypt) against Node.
getPublicKey replaces wrapKey as the refused method in the fenced
fixture and the JavaScript refusal test.
The key and signature errors formatted into fixed stack buffers and
passed snprintf's return value on as the message length. A message
longer than the buffer, such as "Invalid digest: " with a long algorithm
name from crypto.sign or crypto.verify, then copied bytes past the end of
the buffer into the error. The messages are now sized to their whole
text, so a long name appears in full, as in Node.
The received values in these messages also follow Node: strings as
util.inspect quotes them, cut at 128 characters, and large integers with
ERR_OUT_OF_RANGE's separators. A fractional, NaN or infinite
modulusLength reports "It must be an integer", as validateUint32 does.
- An empty `[]` whose own type is `never[]` (a `return []` in an async
function, whose contextual type is `T[] | PromiseLike<T[]>`, or an
object literal's `files: []` field) builds as the unit-only element
array, like the empty tuple, so typed array slots receive it through
the checked empty-array lift. It was `number[]`, so `string[]` slots
refused it with SC1090 or SC2002.
- The refusal "instanceof against this native constructor" names its
site, and coverage lists the sites whose right-hand side is `Object` or
`Function`, which always reach it. An arrow function or method on the
right of an untyped `instanceof` throws Node's "Function has non-object
prototype" TypeError for an object.
- Coverage lists the diagnostics of a function whose signature did not
compile. A use of a parameter whose type did not compile reports SC2004.
- Coverage lists deferred fences in a module's top-level code under "runs
at startup", and the diagnostics envelope marks them `startup: true`.
The private copy of the LLVM text had no extension. Builds that compile it
with the C driver (sanitize builds, targets without the LLVM helper) passed
it to clang directly when the native cache was off, and clang read it as a
linker input, so the build failed. The copy is now named with `.ll`.
Those builds also record the output directory in their native cache proof.
Renaming the private copies to the shared names after the native build
changed that directory, so every unchanged repeat missed the proof and
relinked the executable. The shared names are now installed before the
native build, which compiles and caches copies in a private directory
outside the output directory.
A branch can commit the file a state group writes (or that the config
creates from a template) as a symlink. The split tools resolve every path
they read or write through confine.mjs, so move-state refuses such a file
instead of appending the state through the link. The test covers that
path: the group file is listed under create, exists as a link to a file
outside the checkout, and the tool exits with an error and leaves the
outside file unchanged.
Checker replies name every type, symbol and signature they reach in
full, and most of them are objects the client already holds: building
tsc-ts, 96% of the types, 79% of the symbols and 80% of the signatures
in replies are cached by id. Object replies are now read as bytes, each
element's extent is found by a scan that skips strings, and an element
whose leading "id" is cached resolves to the cached object without its
text being decoded. The other elements are parsed as before.
Narrowing stability looked for writes to a binding by walking the whole
file that declares it. For globals such as `console` and `JSON` that file
is a declaration file (scriptc-node-fallback.d.ts, lib.es5.d.ts), so every
build that read one of them materialized about 46,000 AST nodes and kept
them until the end of the build. A declaration file holds no code that
runs, so none of its writes happen: the walk now returns no writes for it.
The dense path of an element read that may be undefined (the
`%arr.idxOr` reads behind `a[i]` as `T | undefined`, the store
`a[x] = a[y]!`, and the element reads of the lowered array methods)
tested the index against the capacity, then against the hole-free prefix
(`filled`), and only above the prefix against the length and the slot's
state. `filled` is at most the length and the capacity (scr_array.h), so
an index below it needs no other test: the read now tests `filled` first
and takes the value path on that test alone. Above the prefix it tests
the capacity, the length and the state as before.
A double index first branches to the runtime lookup when it is not an
exact integer, then takes the same tests on its i64 value, which a 32-bit
target compares before truncating it.