The native compiler's executable cache restored a linked executable only
on darwin. On Linux every exe build linked again, even when the frontend
and object caches answered the rest of the build.
- The cache now opens on Linux when the linker is a clang driver. One link
with `-v -Wl,-t` lists the driver's linker job and every file the linker
loads; their identities are checked again before an entry is published
and when it is restored.
- On Linux a key's first link only records that it was linked. A later
link of the same key traces and publishes, so a program built once pays
no tracing.
- The key names the runtime pack manifest by its file identity on Linux
instead of by its text.
- The executable and object caches share one digest of the LLVM text.
- Content-addressed entries are hashed once, and identities are compared
by field.
darwin keeps its own driver check, dry run and key.
A quotient x / Math.pow(2, n) divided on every evaluation, and a
division's latency bounds loops that carry the quotient, such as a key
that keeps its low bits with k - Math.floor(k / Math.pow(2, n)) *
Math.pow(2, n). A divisor that is a positive power of two from 2^-1022
to 2^1022 has a reciprocal that is a power of two too, whose bits are
0x7FE0000000000000 minus the divisor's, and the product with it is the
quotient for every dividend. The quotient now multiplies by that
reciprocal when the divisor's bits are such a power, and divides
otherwise.
Corpus 8180 compares the quotient with an ordinary division for every
integer exponent from -1080 to 1030, including divisors that are
subnormal, zero or infinite, and for exponents that are not integers.
`for (let i = 0; i < s.length; i++) if (s.charCodeAt(i) === 32) n++`
over a text with any non-ASCII character failed the loop version's ASCII
guard and ran the checked loop. Each read tested the ASCII bit, compared
the string with the runtime's published scan receiver, tested the
bounds, loaded the units pointer, and kept the opaque fallback.
The guard now also accepts a version's one string receiver when it is
not ASCII. One runtime read publishes the string as the scan receiver
(ScrStrScan), whose UTF-16 units the runtime holds once a scan has
read it enough. The guard then tests the accesses against the published
unit count, and a third copy of the loop reads each unit with one
16-bit load from the units pointer taken in the guard.
The guard takes that path only while the runtime publishes the receiver
itself, so the length it took is the receiver's UTF-16 length on both
fast paths. Both fast copies read it for the receiver's `length`, which
a while loop's condition otherwise asks the runtime for on every
iteration.
The runtime drops a string's units when the string is released or
appended to in place, or when its index entry is evicted because
another non-ASCII string is indexed. The receiver is a binding the loop
does not write, so the first two cannot happen while the loop runs. A
units copy is emitted only for a loop that uses unit-safe operations
(string-unit-versions.ts): numbers, locals, records and class fields,
typed arrays, number arrays, Math, the receiver's charCodeAt and length,
and calls of functions made of the same operations that read no string.
Tests of `s.charAt(i)` against one-unit literals now become charCodeAt
compares in the IR pass (char-unit-locals.ts), so a version sees one
kind of read.
A checked cast into a union whose discriminant has four or more string
literals reads the discriminant field once (scr_dyn_discriminant_case)
and then tests only the arms whose literal it holds and the arms without
a literal, in the union's arm order. Every other arm's literal test would
fail, so the arm chosen and the error for a value no arm matches are
unchanged. A typed capsule, an accessor or an inherited discriminant
still takes the tests arm by arm. Unions with fewer literals emit the
same code as before.
The constructor read asked dyn.hasOwn on the instance's property bag, which
is undefined until the instance has one, and on the bag's prototype, so it
threw "Cannot convert undefined or null to object" for an instance without
own properties (for example `new ParseFailure('p').constructor` for a class
that extends SyntaxError). It now goes through a lifted helper that answers
false before the instance has a bag and when the bag has no prototype, as
the dyn.hasKey read did.
Buffer.from(arrayBuffer, byteOffset[, length]) lowers to
buffer.poolFromDynRange with its operands as checked values. The int32
slot analysis treats any runtime helper with a checked-value argument
as a possible store into a live class instance unless the helper is
listed as read-only, so one such call dropped every int32 class field
fact in the program (tsc-ts's scanner makes one).
buffer.poolFromDynRange and buffer.poolFromDynOffset read their
operands (the ArrayBuffer's view after Node's offset and length
conversions, or a copy of a string's, array-like's or typed array's
values) and store into no class instance, so they join DYN_READ_ONLY.
`t[i]!` on a typed array always took the numeric element read, which is NaN
for an index outside the array. Node reads undefined there, so `t[n]! ===
t[n]!` was false instead of true, `${t[n]!}` was "NaN", and `typeof t[n]!`
was "number".
`t[i]!` now takes the numeric read only where its use converts the value to
a number. Where the use tells undefined from NaN (an equality, a template
span, `+` with a string, a comparison with a string, `!`, `typeof`, an array
literal element, or an argument whose parameter admits undefined, such as
`Object.is` and `String`), it reads undefined for an invalid index, like
`t[i]`. Returns and plain bindings keep the numeric read.
Corpus 7394.
An expected regex or string read from a JavaScript object literal's
field is a checked-dynamic value while its checker type is RegExp or
string. assert.throws chose its regex comparison from the checker type
and passed the value unconverted, an internal compiler error, and
assert.match and the expected-object form refused it. Such a value now
converts to the regex or string its type names.
The checker resolves an overloaded call whose argument is `any` to the
first overload that argument satisfies, but in JavaScript the value can
select another one: generateKeyPairSync("ec", options) returns KeyObjects
or PEM strings by the options, and digest(encoding) returns a Buffer when
the encoding is undefined. The result was converted to the first
overload's return type, so such calls threw a TypeError where Node
returns the other overload's value.
When a checked-dynamic argument could take another overload whose return
type differs (trailing untyped or undefined arguments may also be
absent), the call's result in a JavaScript source is checked-dynamic. So
are the unannotated bindings it initializes, destructured or not, and
the unannotated functions that return it. fs.globSync keeps choosing its
result form from the resolved overload, since the runtime checks that
withFileTypes agrees with it.
TypeScript infers a function's return type as the subtype-reduced union of
the values it returns: returning `{ status }` in one branch and
`{ status, value }` (or a call returning either) in another infers just
`{ status }`. Records have fixed layouts, so that type cannot hold the
second value: the return failed its union wrap at run time ('... is not
representable in the target union'), and values that reached a binding of
the reduced type failed a checked narrowing.
- A function with an inferred return type and no contextual signature
returns the union of the record types it actually returns, including
through calls of other such functions. Only value types the checker's
reduction dropped from the inferred union are added, so functions whose
inferred type already names every returned type keep their layout.
Declared return types keep their meaning.
- Reading such a union through the reduced type (a binding, a member read,
JSON.stringify) uses a dedicated checked extraction that converts the wider
record arms with the ordinary width coercion; other narrowings are
unchanged.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Object.freeze, Object.seal and Object.preventExtensions of a class
instance held in an untyped value threw SC2020 at run time. Node changes
the instance itself: writes through its typed references then throw the
read-only TypeError in strict mode code, adding properties through any
reference fails, and Object.isFrozen, isSealed and isExtensible report
the state through either reference.
- In a program that freezes, seals or prevents extensions of an untyped
value that is not a fresh literal, every class hierarchy that can be held
there gets the integrity word that static Object.freeze already uses, and
with it the existing write checks.
- The runtime applies the operation to the instance's checked view and
commits the view. The commit of such a hierarchy stores the view's
integrity state in the word (scr_dyn_class_view_integrity) and makes the
instance's property bag not extensible with it. A hierarchy without the
word still throws SC2020.
- These commits replace and remove the bag's field entries whatever their
attributes, so a sealed or frozen view does not leave stale field values
in the bag.
- The analysis behind records and arrays now starts from these calls only,
rather than from every untyped freeze the lowering emits, such as the
frozen strings array of a tagged template.
- Programs without such calls compile as before. The runtime ABI moves to
v38.
Corpus 7407 covers freeze, seal and preventExtensions through unknown of
classes and subclasses, writes through typed and untyped references and
through methods, properties added before and after the operation, and the
integrity queries, and matches Node.
Refs #1374
Co-authored-by: blurname <50226652+blurname@users.noreply.github.com>