Release builds no longer import runtime bitcode, so program code called scr_map_retain_v for every Map read out of a container and scr_map_size for every size read. A Map retain now uses the emitted increment helper, as string retains do, and Map.size reads the live count through an internal helper.
A typed record encoded from its field hooks queued a plain-object field on
the visit stack and went on to the next field, so getters on later fields
ran before the properties of earlier ones were read. Node's serializer
finishes each field, nested properties included, before the next.
The record path now drains the visit stack down to where it started after
each field, so properties are read in Node's order. Corpus 7139 posts a
record whose fields hold getter-bearing objects and prints the read order.
- An arrow function or method on the right of an untyped `instanceof`
has no prototype. Node throws "Function has non-object prototype
'undefined' in instanceof check" for any object, but the runtime threw
only for plain objects, arrays and functions, and answered false for
typed arrays, native handles, promises and typed references. It now
answers false only for primitives.
- Coverage lowers the body of a signature-blocked function for its
diagnostics. A declared `this` parameter now gives that body its
receiver, as lowerFunction does, so `this.label` reports nothing.
Before, each use reported SC1080.
test_dh.c includes scr_dh.c and still called sd_generate_safe_prime
without its new top_two argument, so dh.test.ts failed to compile. It
now passes false, as the DiffieHellman paths do, and a new case checks
generatePrime's search: the sizes and add forms with one possible
result, top-two-bit safe-prime starts, and checks above OpenSSL's round
count.
- Once a 'message' listener without a typed entry has decoded the
message, the listeners after it read that decoded value, so a typed
listener sees what an earlier untyped listener changed, as in Node.
- A union with a literal discriminator gets no typed message entry: its
checked conversion also tests the discriminator, which the message
readers do not.
Corpus 7361 checks a typed listener after an untyped one that changes
the message, and a listener on a discriminated union.
postMessage of a typed record boxes it as a live record capsule, and the
message encoder materialized the capsule's checked-dynamic view to read
it: it built the view object, listed its own keys, tested each one for
enumerability and read it back, then released the view and the key list.
On an object-only run of bench/channel-messages that was about half of
the encode time.
- A live record capsule whose view was never created (no undeclared
properties, no writes through an untyped view) now encodes from the
record's field hooks (ScrDynTypedRecordOps), which answer exactly the
view's keys, order and boxed values. Nested records encode the same way
to a depth of 32, then through their views as before. Capsules with a
view, published values, and every other value encode as before.
- The mail event that carries a message lives in the message's block, so
a queued message is one allocation instead of two.
Corpus 7139 and 7360 post typed records on a MessageChannel and between a
worker and the main thread: optional fields, nested records, a record
reached twice, a chain deeper than the recursion limit, a parsed record
with undeclared keys, and a function-valued field.
A 'message' listener with a typed parameter received each message as a
checked-dynamic value: the port decoded the message into dyn objects, the
listener's thunk matched the value against each union arm, read every
field again to build the record, and the dyn objects were then released.
For bench/channel-messages that was about a third of the delivery time.
- A closure registered with on, once, addListener, prependListener or
prependOnceListener for 'message' gets a typed message entry when its
parameter is a number, boolean, string, a record of those without union
fields, or a boxed union of those arms with null or undefined. The
entry builds the parameter from the message's nodes and calls the
closure.
- The entry handles a message only when it has the parameter's exact
layout: the union arm the checked conversion picks, and a record's
declared keys in declaration order with no others. Any other message
runs through the thunk on the decoded value, as before, so undeclared
keys, other orders and mismatches behave as they did.
- Ports, workers and parentPort deliver a message through the entries
when no value in it is reached twice and it holds no ports or native
handles. Listeners without an entry share one decoded value, made when
the first of them runs.
- The message readers are runtime entry points, so the runtime ABI is now
27.
Corpus 7130 and 7131 check typed listeners on MessageChannel ports, a
worker and parentPort against Node: exact layouts, extra keys, shared
values, nested records, untyped listeners beside typed ones, once
listeners and a transferred port.
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.
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`.
Readable, Writable, Duplex, Transform and PassThrough take the
objectMode option, and a Duplex, Transform or PassThrough the
readableObjectMode and writableObjectMode options, as true or false
literals in an options object literal or in an options value. An
object-mode side holds any value that converts to a checked-dynamic
value: push, unshift, write, end, pipe, pipeline, for await, 'data'
listeners, Readable.from and the transform callback carry values in
TypeScript and JavaScript, and constructor-function streams and options
held in variables do the same. The object-mode high-water mark counts
values and defaults to 16. A readable buffer that mixes strings, Buffers
and other values holds them all, and push(undefined) and other values on
a byte-mode stream report ERR_INVALID_ARG_TYPE through 'error', as in
Node.
In typed code a write or transform callback, or a _write or _transform
method, may declare its chunk with any such type, which converts when
the callback runs. Writes pass the encoding argument to _write and
_transform as Node does: undefined when absent, null when a callback
took its place, otherwise the value unchanged. A parameter declared
string | null | undefined receives that value and one declared string
receives "" for undefined and null. write(chunk, encoding) on a
byte-mode stream encodes a string chunk with any known encoding and
throws ERR_UNKNOWN_ENCODING for an unknown one. Readable.from takes
typed Maps and Sets of any such value, and arrays that mix strings and
Buffers.
for await over a readable-only stream that will emit 'close' now ends
after 'close', as Node's eos does. Buffered writes that complete inside
clearBuffer run their callbacks in order, and 'finish' waits for a
deferred afterWrite that carries a callback, as Node's pendingcb does,
unless a _final callback schedules it.
The 'data' listener adapters take three slots and write and transform
callbacks read their chunk and encoding through runtime accessors, so
RUNTIME_ABI_VERSION is now 26.
All Buffer.from paths over an untyped ArrayBuffer or SharedArrayBuffer now
go through one function that takes the byte offset explicitly, as Node's
fromArrayBuffer does. The start-offset RangeError prints the offset as
JavaScript does (-1.5, -1e+21, -Infinity) and builds its message without a
fixed-size buffer, which a large negative offset overran.