mirror of
https://github.com/vercel-labs/scriptc.git
synced 2026-10-26 06:34:20 +08:00
Merge main
This commit is contained in:
@@ -26,7 +26,7 @@ Application-shaped workloads for measuring the speed of compiled executables. Ea
|
||||
| `region-builds` | cpu | a watch-mode build loop over `LifetimeRegion`: each build binds a program in a new region and releases the previous one, which a cache keeps referenced for two builds |
|
||||
| `template-render` | cpu | template literals, escaping, `map`/`join` |
|
||||
| `thread-slots`, `thread-slots-cache` | cpu | worker threads reading one published type graph from an `InternTable` and keeping per-thread state for each type they visit: a `ThreadSlots` family, or a direct-mapped cache by id in front of a `Map` by id (the `-cache` entry); under Node.js both are per thread |
|
||||
| `typed-array-kernels` | cpu | element-wise loops that run to one typed array's length and index others at the same position: `Float64Array` axpy, `Float32Array` blend, `Uint8Array` mask, wrapping `Int32Array` add, RGBA to gray |
|
||||
| `typed-array-kernels` | cpu | element-wise loops that run to one typed array's length and index others at the same position: `Float64Array` axpy, `Float32Array` blend, `Uint8Array` mask, wrapping `Int32Array` add, `Uint8ClampedArray` brighten, RGBA to gray |
|
||||
| `typed-array-scan`, `typed-array-scan-workers` | cpu | element-by-element scan loops over `Uint8Array`, `Uint16Array`, `Int32Array` and `Float64Array`; the second entry is a worker-enabled executable (it can start a worker), so each access also checks for shared storage |
|
||||
| `unicode-lexer` | cpu | a hand-written lexer over a 2.7M-unit bundled module whose comments and strings are in several languages: `charCodeAt` scans, line starts |
|
||||
| `validate-errors` | cpu | throw/catch on a fraction of inputs, small call chains |
|
||||
|
||||
@@ -17,6 +17,10 @@ function addWrap(a: Int32Array, b: Int32Array, out: Int32Array): void {
|
||||
for (let i = 0; i < out.length; i++) out[i] = (a[i]! + b[i]!) | 0;
|
||||
}
|
||||
|
||||
function brighten(src: Uint8ClampedArray, gain: number, out: Uint8ClampedArray): void {
|
||||
for (let i = 0; i < out.length; i++) out[i] = src[i]! * gain + 8;
|
||||
}
|
||||
|
||||
function gray(rgba: Uint8Array, out: Uint8Array): void {
|
||||
for (let i = 0; i < out.length; i++) {
|
||||
const j = i * 4;
|
||||
@@ -38,6 +42,8 @@ const masked = new Uint8Array(n);
|
||||
const ia = new Int32Array(n);
|
||||
const ib = new Int32Array(n);
|
||||
const io = new Int32Array(n);
|
||||
const pixels = new Uint8ClampedArray(n);
|
||||
const bright = new Uint8ClampedArray(n);
|
||||
const rgba = new Uint8Array(n * 4);
|
||||
const luma = new Uint8Array(n);
|
||||
let state = 2463534242;
|
||||
@@ -56,6 +62,7 @@ for (let i = 0; i < n; i++) {
|
||||
key[i] = next() & 255;
|
||||
ia[i] = next() | 0;
|
||||
ib[i] = next() | 0;
|
||||
pixels[i] = next() & 255;
|
||||
}
|
||||
for (let i = 0; i < rgba.length; i++) rgba[i] = next() & 255;
|
||||
|
||||
@@ -64,6 +71,7 @@ let f64Sum = 0;
|
||||
let f32Sum = 0;
|
||||
let byteSum = 0;
|
||||
let intSum = 0;
|
||||
let brightSum = 0;
|
||||
let lumaSum = 0;
|
||||
for (let r = 0; r < rounds; r++) {
|
||||
// Change one input element per round so no round repeats the last.
|
||||
@@ -72,21 +80,25 @@ for (let r = 0; r < rounds; r++) {
|
||||
fa[at] = r;
|
||||
data[at] = r & 255;
|
||||
ia[at] = r * 65537;
|
||||
pixels[at] = r & 255;
|
||||
rgba[at * 4] = r & 255;
|
||||
axpy(1.5, x, y, d);
|
||||
blend((r & 15) / 16, fa, fb, fo);
|
||||
mask(data, key, masked);
|
||||
addWrap(ia, ib, io);
|
||||
brighten(pixels, 0.75 + (r & 7) / 8, bright);
|
||||
gray(rgba, luma);
|
||||
const probe = (r * 104729) & (n - 1);
|
||||
f64Sum += d[probe]!;
|
||||
f32Sum += fo[probe]!;
|
||||
byteSum += masked[probe]!;
|
||||
intSum = (intSum + io[probe]!) | 0;
|
||||
brightSum += bright[probe]!;
|
||||
lumaSum += luma[probe]!;
|
||||
}
|
||||
console.log(`float64 ${f64Sum.toFixed(3)}`);
|
||||
console.log(`float32 ${f32Sum.toFixed(3)}`);
|
||||
console.log(`uint8 ${byteSum}`);
|
||||
console.log(`int32 ${intSum}`);
|
||||
console.log(`clamped ${brightSum}`);
|
||||
console.log(`gray ${lumaSum}`);
|
||||
|
||||
@@ -19,4 +19,7 @@
|
||||
A call on an ECDH value whose arguments no @types/node overload accepts (`getPublicKey("HEX")`) is refused at compile time, as is `computeSecret(view, encoding)`: Node reads that encoding as the input encoding and answers a Buffer where @types/node declares a string.
|
||||
ECDH property reads and writes throw a coded SC2020 error.
|
||||
Other public-key operations remain unsupported.
|
||||
The WebCrypto `crypto` global, which node:crypto also exports as `webcrypto`, supports `randomUUID()` and `getRandomValues()`, also read as values from untyped JavaScript.
|
||||
Its `subtle` object, also node:crypto's `subtle` export, supports `generateKey()` and `exportKey()` for HMAC and AES keys, `importKey()` for HMAC, AES, PBKDF2, and HKDF keys, `encrypt()` and `decrypt()` with AES-GCM, AES-CBC, and AES-CTR on native targets, `deriveBits()` and `deriveKey()` with PBKDF2 and HKDF, HMAC `sign()` and `verify()`, and SHA-1 and SHA-2 `digest()`.
|
||||
Secret keys import and export in the `raw`, `raw-secret`, and `jwk` formats; other SubtleCrypto methods, algorithms, and key formats are refused.
|
||||
Native derivations finish before their deferred callbacks, so computation blocks the caller.
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
Stream constructors accept a `signal` option in an options object literal, and `stream.addAbortSignal` accepts any stream.
|
||||
A `transform` or `flush` option callback whose completion callback types `data` as `any`, as @types/node's `TransformCallback` does, may pass a Buffer, a string, or nothing; any other value throws a TypeError where the callback is called, instead of Node's `ERR_INVALID_ARG_TYPE` stream error.
|
||||
`node:readline` and `node:readline/promises` (also reached as `readline.promises`) interfaces, from `createInterface(options)` or `createInterface(input, output, completer, terminal)`, read `process.stdin` or a readable stream: `question`, `prompt`, `write` (with a key object literal of `name`, `ctrl`, `meta`, and `shift`), `pause`, `resume`, `'line'`/`'close'`/`'pause'`/`'resume'`/`'error'`/`'history'`/`'SIGINT'`/`'SIGTSTP'`/`'SIGCONT'` listeners, and `for await` compile.
|
||||
`on`, `once`, and `addListener` return the interface, so registrations chain.
|
||||
The `history`, `historySize`, `removeHistoryDuplicates`, `completer`, `tabSize`, and `escapeCodeTimeout` options are validated as in Node.
|
||||
With `terminal: true`, or with no `terminal` option and `process.stdout` as the output when it is a TTY, the interface runs Node's terminal mode: the editing keys, the kill ring, undo and redo, history recording and recall, Tab completion through the completer, Ctrl+C, Ctrl+D, and Ctrl+Z, `TERM=dumb`, and the same escape sequences written to the output.
|
||||
The output's `'resize'` event does not redraw the line.
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
- **Local class expressions:** Static members, decorators, private brands, generic members, and references through the class's internal name are unsupported.
|
||||
- **Local class expressions:** Static fields and static blocks in TypeScript files, decorators, private brands, generic members, and references through the class's internal name are unsupported.
|
||||
Static methods read the enclosing function's bindings and are inherited by subclasses.
|
||||
Supported expressions preserve fresh constructor identity and shared captures across constructors, methods, accessors, and arrow fields.
|
||||
Local inheritance requires a compiled base class.
|
||||
Constructors retain identity, `typeof`, `name`, and `length` through `unknown`; cast back to their exact type before construction.
|
||||
|
||||
@@ -12442,9 +12442,9 @@
|
||||
"inherited": true
|
||||
},
|
||||
"static": {
|
||||
"status": "not-implemented",
|
||||
"detail": "No static compiler lowering is registered for this API yet.",
|
||||
"verification": "registry-gap"
|
||||
"status": "partial",
|
||||
"detail": "Implemented by a dedicated static compiler/runtime path.",
|
||||
"verification": "test-backed"
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -12465,9 +12465,9 @@
|
||||
"inherited": true
|
||||
},
|
||||
"static": {
|
||||
"status": "refused",
|
||||
"detail": "Explicitly rejected by a named compiler diagnostic.",
|
||||
"verification": "explicit-refusal"
|
||||
"status": "partial",
|
||||
"detail": "Implemented by a dedicated static compiler/runtime path.",
|
||||
"verification": "test-backed"
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -79656,9 +79656,9 @@
|
||||
"inherited": true
|
||||
},
|
||||
"static": {
|
||||
"status": "not-implemented",
|
||||
"detail": "No static compiler lowering is registered for this API yet.",
|
||||
"verification": "registry-gap"
|
||||
"status": "partial",
|
||||
"detail": "Implemented for the documented scriptc module-loader subset.",
|
||||
"verification": "test-backed"
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -79680,7 +79680,7 @@
|
||||
},
|
||||
"static": {
|
||||
"status": "supported",
|
||||
"detail": "Implemented by a dedicated static compiler/runtime path.",
|
||||
"detail": "Implemented for the documented scriptc module-loader subset.",
|
||||
"verification": "test-backed"
|
||||
}
|
||||
},
|
||||
@@ -79703,7 +79703,7 @@
|
||||
},
|
||||
"static": {
|
||||
"status": "supported",
|
||||
"detail": "Implemented by a dedicated static compiler/runtime path.",
|
||||
"detail": "Implemented for the documented scriptc module-loader subset.",
|
||||
"verification": "test-backed"
|
||||
}
|
||||
},
|
||||
@@ -80001,9 +80001,9 @@
|
||||
"inherited": true
|
||||
},
|
||||
"static": {
|
||||
"status": "not-implemented",
|
||||
"detail": "No static compiler lowering is registered for this API yet.",
|
||||
"verification": "registry-gap"
|
||||
"status": "partial",
|
||||
"detail": "Implemented for the documented scriptc module-loader subset.",
|
||||
"verification": "test-backed"
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -80024,9 +80024,9 @@
|
||||
"inherited": true
|
||||
},
|
||||
"static": {
|
||||
"status": "not-implemented",
|
||||
"detail": "No static compiler lowering is registered for this API yet.",
|
||||
"verification": "registry-gap"
|
||||
"status": "partial",
|
||||
"detail": "Implemented for the documented scriptc module-loader subset.",
|
||||
"verification": "test-backed"
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -80047,9 +80047,9 @@
|
||||
"inherited": true
|
||||
},
|
||||
"static": {
|
||||
"status": "not-implemented",
|
||||
"detail": "No static compiler lowering is registered for this API yet.",
|
||||
"verification": "registry-gap"
|
||||
"status": "partial",
|
||||
"detail": "Implemented for the documented scriptc module-loader subset.",
|
||||
"verification": "test-backed"
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -80139,9 +80139,9 @@
|
||||
"inherited": true
|
||||
},
|
||||
"static": {
|
||||
"status": "not-implemented",
|
||||
"detail": "No static compiler lowering is registered for this API yet.",
|
||||
"verification": "registry-gap"
|
||||
"status": "partial",
|
||||
"detail": "Implemented for the documented scriptc module-loader subset.",
|
||||
"verification": "test-backed"
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -80162,9 +80162,9 @@
|
||||
"inherited": true
|
||||
},
|
||||
"static": {
|
||||
"status": "not-implemented",
|
||||
"detail": "No static compiler lowering is registered for this API yet.",
|
||||
"verification": "registry-gap"
|
||||
"status": "partial",
|
||||
"detail": "Implemented for the documented scriptc module-loader subset.",
|
||||
"verification": "test-backed"
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -80208,9 +80208,9 @@
|
||||
"inherited": true
|
||||
},
|
||||
"static": {
|
||||
"status": "not-implemented",
|
||||
"detail": "No static compiler lowering is registered for this API yet.",
|
||||
"verification": "registry-gap"
|
||||
"status": "partial",
|
||||
"detail": "Implemented for the documented scriptc module-loader subset.",
|
||||
"verification": "test-backed"
|
||||
}
|
||||
},
|
||||
{
|
||||
|
||||
@@ -19316,6 +19316,8 @@
|
||||
"tests/corpus/7055-cipher-streams.ts",
|
||||
"tests/corpus/7056-cipher-stream-errors.ts",
|
||||
"tests/corpus/7057-cipher-streams.mjs",
|
||||
"tests/corpus/7122-webcrypto-node-crypto.ts",
|
||||
"tests/corpus/7123-webcrypto-node-crypto.cjs",
|
||||
"tests/corpus/node24-crypto-kdf-errors.ts",
|
||||
"tests/corpus/node24-crypto-kdf-options.ts",
|
||||
"tests/corpus/node24-crypto-kdf.ts",
|
||||
@@ -21224,8 +21226,12 @@
|
||||
"inherited": true
|
||||
},
|
||||
"static": {
|
||||
"status": "not-implemented",
|
||||
"evidence": "compiler-unmatched:crypto"
|
||||
"status": "partial",
|
||||
"evidence": "compiler-dedicated:subtle",
|
||||
"tests": [
|
||||
"tests/corpus/7122-webcrypto-node-crypto.ts",
|
||||
"tests/corpus/7123-webcrypto-node-crypto.cjs"
|
||||
]
|
||||
},
|
||||
"anchorSource": "ancestor"
|
||||
},
|
||||
@@ -21247,8 +21253,12 @@
|
||||
"inherited": true
|
||||
},
|
||||
"static": {
|
||||
"status": "refused",
|
||||
"evidence": "surface-manifest:unsupported:node-builtin.crypto.webcrypto"
|
||||
"status": "partial",
|
||||
"evidence": "compiler-dedicated:crypto.webcrypto",
|
||||
"tests": [
|
||||
"tests/corpus/7122-webcrypto-node-crypto.ts",
|
||||
"tests/corpus/7123-webcrypto-node-crypto.cjs"
|
||||
]
|
||||
},
|
||||
"anchorSource": "exact"
|
||||
},
|
||||
@@ -106652,8 +106662,6 @@
|
||||
"tests/corpus/1534-crypto-hash-chain.ts",
|
||||
"tests/corpus/2556-crypto-introspection.ts",
|
||||
"tests/corpus/2890-crypto-static-utility.ts",
|
||||
"tests/corpus/5442-crypto-random-values.ts",
|
||||
"tests/corpus/5443-crypto-random-arguments.js",
|
||||
"tests/corpus/6204-crypto-get-random-values.ts",
|
||||
"tests/corpus/6205-crypto-get-random-values.js",
|
||||
"tests/corpus/6291-webcrypto-hmac-sign-verify.ts",
|
||||
@@ -106661,11 +106669,20 @@
|
||||
"tests/corpus/6293-webcrypto-digest.ts",
|
||||
"tests/corpus/6294-webcrypto-js-values.mjs",
|
||||
"tests/corpus/6295-jose-hs256/main.mjs",
|
||||
"tests/corpus/7120-webcrypto-random-uuid.ts",
|
||||
"tests/corpus/7121-webcrypto-crypto-object.mjs",
|
||||
"tests/corpus/7124-webcrypto-secret-keys.ts",
|
||||
"tests/corpus/7125-webcrypto-key-errors.mjs",
|
||||
"tests/corpus/7126-webcrypto-aes.ts",
|
||||
"tests/corpus/7127-webcrypto-aes-errors.mjs",
|
||||
"tests/corpus/7128-webcrypto-derive.ts",
|
||||
"tests/corpus/7129-webcrypto-derive-errors.mjs",
|
||||
"tests/corpus/node24-crypto-kdf-errors.ts",
|
||||
"tests/corpus/node24-crypto-kdf-options.ts",
|
||||
"tests/corpus/node24-crypto-kdf.ts",
|
||||
"tests/corpus/node24-crypto-sha2.ts",
|
||||
"tests/fixtures/node-types/webcrypto-hmac.ts",
|
||||
"tests/fixtures/node-types/webcrypto-keys.ts",
|
||||
"tests/harness/npm-static.test.ts"
|
||||
]
|
||||
},
|
||||
@@ -110123,8 +110140,13 @@
|
||||
"inherited": true
|
||||
},
|
||||
"static": {
|
||||
"status": "not-implemented",
|
||||
"evidence": "compiler-unmatched:webcrypto"
|
||||
"status": "partial",
|
||||
"evidence": "compiler-feature:webcrypto.Crypto",
|
||||
"tests": [
|
||||
"tests/corpus/7120-webcrypto-random-uuid.ts",
|
||||
"tests/corpus/7121-webcrypto-crypto-object.mjs",
|
||||
"tests/corpus/6293-webcrypto-digest.ts"
|
||||
]
|
||||
},
|
||||
"anchorSource": "exact"
|
||||
},
|
||||
@@ -110147,8 +110169,10 @@
|
||||
},
|
||||
"static": {
|
||||
"status": "supported",
|
||||
"evidence": "compiler-dedicated:crypto.getRandomValues",
|
||||
"evidence": "compiler-feature:webcrypto.crypto.getRandomValues",
|
||||
"tests": [
|
||||
"tests/corpus/7120-webcrypto-random-uuid.ts",
|
||||
"tests/corpus/7121-webcrypto-crypto-object.mjs",
|
||||
"tests/corpus/6204-crypto-get-random-values.ts",
|
||||
"tests/corpus/6205-crypto-get-random-values.js"
|
||||
]
|
||||
@@ -110174,11 +110198,12 @@
|
||||
},
|
||||
"static": {
|
||||
"status": "supported",
|
||||
"evidence": "compiler-dedicated:crypto.randomUUID",
|
||||
"evidence": "compiler-feature:webcrypto.crypto.randomUUID",
|
||||
"tests": [
|
||||
"tests/corpus/5442-crypto-random-values.ts",
|
||||
"tests/corpus/5443-crypto-random-arguments.js",
|
||||
"tests/corpus/1358-crypto-random.ts"
|
||||
"tests/corpus/7120-webcrypto-random-uuid.ts",
|
||||
"tests/corpus/7121-webcrypto-crypto-object.mjs",
|
||||
"tests/corpus/6204-crypto-get-random-values.ts",
|
||||
"tests/corpus/6205-crypto-get-random-values.js"
|
||||
]
|
||||
},
|
||||
"anchorSource": "exact"
|
||||
@@ -110234,6 +110259,7 @@
|
||||
"status": "partial",
|
||||
"evidence": "compiler-feature:webcrypto.CryptoKey",
|
||||
"tests": [
|
||||
"tests/corpus/7124-webcrypto-secret-keys.ts",
|
||||
"tests/corpus/6291-webcrypto-hmac-sign-verify.ts",
|
||||
"tests/corpus/6294-webcrypto-js-values.mjs",
|
||||
"tests/corpus/6295-jose-hs256/main.mjs"
|
||||
@@ -110262,6 +110288,7 @@
|
||||
"status": "partial",
|
||||
"evidence": "compiler-feature:webcrypto.cryptoKey.algorithm",
|
||||
"tests": [
|
||||
"tests/corpus/7124-webcrypto-secret-keys.ts",
|
||||
"tests/corpus/6291-webcrypto-hmac-sign-verify.ts",
|
||||
"tests/corpus/6294-webcrypto-js-values.mjs",
|
||||
"tests/corpus/6295-jose-hs256/main.mjs"
|
||||
@@ -110290,6 +110317,7 @@
|
||||
"status": "partial",
|
||||
"evidence": "compiler-feature:webcrypto.extractable",
|
||||
"tests": [
|
||||
"tests/corpus/7124-webcrypto-secret-keys.ts",
|
||||
"tests/corpus/6291-webcrypto-hmac-sign-verify.ts",
|
||||
"tests/corpus/6294-webcrypto-js-values.mjs",
|
||||
"tests/corpus/6295-jose-hs256/main.mjs"
|
||||
@@ -110318,6 +110346,7 @@
|
||||
"status": "partial",
|
||||
"evidence": "compiler-feature:webcrypto.type",
|
||||
"tests": [
|
||||
"tests/corpus/7124-webcrypto-secret-keys.ts",
|
||||
"tests/corpus/6291-webcrypto-hmac-sign-verify.ts",
|
||||
"tests/corpus/6294-webcrypto-js-values.mjs",
|
||||
"tests/corpus/6295-jose-hs256/main.mjs"
|
||||
@@ -110346,6 +110375,7 @@
|
||||
"status": "partial",
|
||||
"evidence": "compiler-feature:webcrypto.usages",
|
||||
"tests": [
|
||||
"tests/corpus/7124-webcrypto-secret-keys.ts",
|
||||
"tests/corpus/6291-webcrypto-hmac-sign-verify.ts",
|
||||
"tests/corpus/6294-webcrypto-js-values.mjs",
|
||||
"tests/corpus/6295-jose-hs256/main.mjs"
|
||||
@@ -110516,8 +110546,23 @@
|
||||
"inherited": true
|
||||
},
|
||||
"static": {
|
||||
"status": "not-implemented",
|
||||
"evidence": "compiler-unmatched:webcrypto"
|
||||
"status": "partial",
|
||||
"evidence": "compiler-feature:webcrypto.subtle.decrypt",
|
||||
"tests": [
|
||||
"tests/corpus/7128-webcrypto-derive.ts",
|
||||
"tests/corpus/7129-webcrypto-derive-errors.mjs",
|
||||
"tests/corpus/7126-webcrypto-aes.ts",
|
||||
"tests/corpus/7127-webcrypto-aes-errors.mjs",
|
||||
"tests/corpus/7124-webcrypto-secret-keys.ts",
|
||||
"tests/corpus/7125-webcrypto-key-errors.mjs",
|
||||
"tests/fixtures/node-types/webcrypto-keys.ts",
|
||||
"tests/corpus/6291-webcrypto-hmac-sign-verify.ts",
|
||||
"tests/corpus/6292-webcrypto-hmac-errors.ts",
|
||||
"tests/corpus/6293-webcrypto-digest.ts",
|
||||
"tests/corpus/6294-webcrypto-js-values.mjs",
|
||||
"tests/corpus/6295-jose-hs256/main.mjs",
|
||||
"tests/fixtures/node-types/webcrypto-hmac.ts"
|
||||
]
|
||||
},
|
||||
"anchorSource": "exact"
|
||||
},
|
||||
@@ -110539,8 +110584,23 @@
|
||||
"inherited": true
|
||||
},
|
||||
"static": {
|
||||
"status": "not-implemented",
|
||||
"evidence": "compiler-unmatched:webcrypto"
|
||||
"status": "partial",
|
||||
"evidence": "compiler-feature:webcrypto.subtle.deriveBits",
|
||||
"tests": [
|
||||
"tests/corpus/7128-webcrypto-derive.ts",
|
||||
"tests/corpus/7129-webcrypto-derive-errors.mjs",
|
||||
"tests/corpus/7126-webcrypto-aes.ts",
|
||||
"tests/corpus/7127-webcrypto-aes-errors.mjs",
|
||||
"tests/corpus/7124-webcrypto-secret-keys.ts",
|
||||
"tests/corpus/7125-webcrypto-key-errors.mjs",
|
||||
"tests/fixtures/node-types/webcrypto-keys.ts",
|
||||
"tests/corpus/6291-webcrypto-hmac-sign-verify.ts",
|
||||
"tests/corpus/6292-webcrypto-hmac-errors.ts",
|
||||
"tests/corpus/6293-webcrypto-digest.ts",
|
||||
"tests/corpus/6294-webcrypto-js-values.mjs",
|
||||
"tests/corpus/6295-jose-hs256/main.mjs",
|
||||
"tests/fixtures/node-types/webcrypto-hmac.ts"
|
||||
]
|
||||
},
|
||||
"anchorSource": "exact"
|
||||
},
|
||||
@@ -110562,8 +110622,23 @@
|
||||
"inherited": true
|
||||
},
|
||||
"static": {
|
||||
"status": "not-implemented",
|
||||
"evidence": "compiler-unmatched:webcrypto"
|
||||
"status": "partial",
|
||||
"evidence": "compiler-feature:webcrypto.subtle.deriveKey",
|
||||
"tests": [
|
||||
"tests/corpus/7128-webcrypto-derive.ts",
|
||||
"tests/corpus/7129-webcrypto-derive-errors.mjs",
|
||||
"tests/corpus/7126-webcrypto-aes.ts",
|
||||
"tests/corpus/7127-webcrypto-aes-errors.mjs",
|
||||
"tests/corpus/7124-webcrypto-secret-keys.ts",
|
||||
"tests/corpus/7125-webcrypto-key-errors.mjs",
|
||||
"tests/fixtures/node-types/webcrypto-keys.ts",
|
||||
"tests/corpus/6291-webcrypto-hmac-sign-verify.ts",
|
||||
"tests/corpus/6292-webcrypto-hmac-errors.ts",
|
||||
"tests/corpus/6293-webcrypto-digest.ts",
|
||||
"tests/corpus/6294-webcrypto-js-values.mjs",
|
||||
"tests/corpus/6295-jose-hs256/main.mjs",
|
||||
"tests/fixtures/node-types/webcrypto-hmac.ts"
|
||||
]
|
||||
},
|
||||
"anchorSource": "exact"
|
||||
},
|
||||
@@ -110588,6 +110663,13 @@
|
||||
"status": "partial",
|
||||
"evidence": "compiler-feature:webcrypto.subtle.digest",
|
||||
"tests": [
|
||||
"tests/corpus/7128-webcrypto-derive.ts",
|
||||
"tests/corpus/7129-webcrypto-derive-errors.mjs",
|
||||
"tests/corpus/7126-webcrypto-aes.ts",
|
||||
"tests/corpus/7127-webcrypto-aes-errors.mjs",
|
||||
"tests/corpus/7124-webcrypto-secret-keys.ts",
|
||||
"tests/corpus/7125-webcrypto-key-errors.mjs",
|
||||
"tests/fixtures/node-types/webcrypto-keys.ts",
|
||||
"tests/corpus/6291-webcrypto-hmac-sign-verify.ts",
|
||||
"tests/corpus/6292-webcrypto-hmac-errors.ts",
|
||||
"tests/corpus/6293-webcrypto-digest.ts",
|
||||
@@ -110662,8 +110744,23 @@
|
||||
"inherited": true
|
||||
},
|
||||
"static": {
|
||||
"status": "not-implemented",
|
||||
"evidence": "compiler-unmatched:webcrypto"
|
||||
"status": "partial",
|
||||
"evidence": "compiler-feature:webcrypto.subtle.encrypt",
|
||||
"tests": [
|
||||
"tests/corpus/7128-webcrypto-derive.ts",
|
||||
"tests/corpus/7129-webcrypto-derive-errors.mjs",
|
||||
"tests/corpus/7126-webcrypto-aes.ts",
|
||||
"tests/corpus/7127-webcrypto-aes-errors.mjs",
|
||||
"tests/corpus/7124-webcrypto-secret-keys.ts",
|
||||
"tests/corpus/7125-webcrypto-key-errors.mjs",
|
||||
"tests/fixtures/node-types/webcrypto-keys.ts",
|
||||
"tests/corpus/6291-webcrypto-hmac-sign-verify.ts",
|
||||
"tests/corpus/6292-webcrypto-hmac-errors.ts",
|
||||
"tests/corpus/6293-webcrypto-digest.ts",
|
||||
"tests/corpus/6294-webcrypto-js-values.mjs",
|
||||
"tests/corpus/6295-jose-hs256/main.mjs",
|
||||
"tests/fixtures/node-types/webcrypto-hmac.ts"
|
||||
]
|
||||
},
|
||||
"anchorSource": "exact"
|
||||
},
|
||||
@@ -110685,8 +110782,23 @@
|
||||
"inherited": true
|
||||
},
|
||||
"static": {
|
||||
"status": "not-implemented",
|
||||
"evidence": "compiler-unmatched:webcrypto"
|
||||
"status": "partial",
|
||||
"evidence": "compiler-feature:webcrypto.subtle.exportKey",
|
||||
"tests": [
|
||||
"tests/corpus/7128-webcrypto-derive.ts",
|
||||
"tests/corpus/7129-webcrypto-derive-errors.mjs",
|
||||
"tests/corpus/7126-webcrypto-aes.ts",
|
||||
"tests/corpus/7127-webcrypto-aes-errors.mjs",
|
||||
"tests/corpus/7124-webcrypto-secret-keys.ts",
|
||||
"tests/corpus/7125-webcrypto-key-errors.mjs",
|
||||
"tests/fixtures/node-types/webcrypto-keys.ts",
|
||||
"tests/corpus/6291-webcrypto-hmac-sign-verify.ts",
|
||||
"tests/corpus/6292-webcrypto-hmac-errors.ts",
|
||||
"tests/corpus/6293-webcrypto-digest.ts",
|
||||
"tests/corpus/6294-webcrypto-js-values.mjs",
|
||||
"tests/corpus/6295-jose-hs256/main.mjs",
|
||||
"tests/fixtures/node-types/webcrypto-hmac.ts"
|
||||
]
|
||||
},
|
||||
"anchorSource": "exact"
|
||||
},
|
||||
@@ -110731,8 +110843,23 @@
|
||||
"inherited": true
|
||||
},
|
||||
"static": {
|
||||
"status": "not-implemented",
|
||||
"evidence": "compiler-unmatched:webcrypto"
|
||||
"status": "partial",
|
||||
"evidence": "compiler-feature:webcrypto.subtle.generateKey",
|
||||
"tests": [
|
||||
"tests/corpus/7128-webcrypto-derive.ts",
|
||||
"tests/corpus/7129-webcrypto-derive-errors.mjs",
|
||||
"tests/corpus/7126-webcrypto-aes.ts",
|
||||
"tests/corpus/7127-webcrypto-aes-errors.mjs",
|
||||
"tests/corpus/7124-webcrypto-secret-keys.ts",
|
||||
"tests/corpus/7125-webcrypto-key-errors.mjs",
|
||||
"tests/fixtures/node-types/webcrypto-keys.ts",
|
||||
"tests/corpus/6291-webcrypto-hmac-sign-verify.ts",
|
||||
"tests/corpus/6292-webcrypto-hmac-errors.ts",
|
||||
"tests/corpus/6293-webcrypto-digest.ts",
|
||||
"tests/corpus/6294-webcrypto-js-values.mjs",
|
||||
"tests/corpus/6295-jose-hs256/main.mjs",
|
||||
"tests/fixtures/node-types/webcrypto-hmac.ts"
|
||||
]
|
||||
},
|
||||
"anchorSource": "exact"
|
||||
},
|
||||
@@ -110757,6 +110884,13 @@
|
||||
"status": "partial",
|
||||
"evidence": "compiler-feature:webcrypto.subtle.importKey",
|
||||
"tests": [
|
||||
"tests/corpus/7128-webcrypto-derive.ts",
|
||||
"tests/corpus/7129-webcrypto-derive-errors.mjs",
|
||||
"tests/corpus/7126-webcrypto-aes.ts",
|
||||
"tests/corpus/7127-webcrypto-aes-errors.mjs",
|
||||
"tests/corpus/7124-webcrypto-secret-keys.ts",
|
||||
"tests/corpus/7125-webcrypto-key-errors.mjs",
|
||||
"tests/fixtures/node-types/webcrypto-keys.ts",
|
||||
"tests/corpus/6291-webcrypto-hmac-sign-verify.ts",
|
||||
"tests/corpus/6292-webcrypto-hmac-errors.ts",
|
||||
"tests/corpus/6293-webcrypto-digest.ts",
|
||||
@@ -110788,6 +110922,13 @@
|
||||
"status": "partial",
|
||||
"evidence": "compiler-feature:webcrypto.subtle.sign",
|
||||
"tests": [
|
||||
"tests/corpus/7128-webcrypto-derive.ts",
|
||||
"tests/corpus/7129-webcrypto-derive-errors.mjs",
|
||||
"tests/corpus/7126-webcrypto-aes.ts",
|
||||
"tests/corpus/7127-webcrypto-aes-errors.mjs",
|
||||
"tests/corpus/7124-webcrypto-secret-keys.ts",
|
||||
"tests/corpus/7125-webcrypto-key-errors.mjs",
|
||||
"tests/fixtures/node-types/webcrypto-keys.ts",
|
||||
"tests/corpus/6291-webcrypto-hmac-sign-verify.ts",
|
||||
"tests/corpus/6292-webcrypto-hmac-errors.ts",
|
||||
"tests/corpus/6293-webcrypto-digest.ts",
|
||||
@@ -110842,6 +110983,13 @@
|
||||
"status": "partial",
|
||||
"evidence": "compiler-feature:webcrypto.subtle.verify",
|
||||
"tests": [
|
||||
"tests/corpus/7128-webcrypto-derive.ts",
|
||||
"tests/corpus/7129-webcrypto-derive-errors.mjs",
|
||||
"tests/corpus/7126-webcrypto-aes.ts",
|
||||
"tests/corpus/7127-webcrypto-aes-errors.mjs",
|
||||
"tests/corpus/7124-webcrypto-secret-keys.ts",
|
||||
"tests/corpus/7125-webcrypto-key-errors.mjs",
|
||||
"tests/fixtures/node-types/webcrypto-keys.ts",
|
||||
"tests/corpus/6291-webcrypto-hmac-sign-verify.ts",
|
||||
"tests/corpus/6292-webcrypto-hmac-errors.ts",
|
||||
"tests/corpus/6293-webcrypto-digest.ts",
|
||||
|
||||
@@ -187,16 +187,18 @@
|
||||
"stdlib.set": ["tests/corpus/typed-collection-iterators.ts", "tests/corpus/typed-collection-iterator-references.ts", "tests/corpus/checked-collection-iterators.ts"],
|
||||
"stdlib.headers": ["tests/harness/fetch-conformance.test.ts"],
|
||||
"stdlib.request": ["tests/harness/fetch-conformance.test.ts"],
|
||||
"stdlib.webcrypto.subtle": ["tests/corpus/6291-webcrypto-hmac-sign-verify.ts", "tests/corpus/6292-webcrypto-hmac-errors.ts", "tests/corpus/6293-webcrypto-digest.ts", "tests/corpus/6294-webcrypto-js-values.mjs", "tests/corpus/6295-jose-hs256/main.mjs"],
|
||||
"stdlib.webcrypto.CryptoKey": ["tests/corpus/6291-webcrypto-hmac-sign-verify.ts", "tests/corpus/6294-webcrypto-js-values.mjs", "tests/corpus/6295-jose-hs256/main.mjs"],
|
||||
"stdlib.webcrypto.subtle": ["tests/corpus/7128-webcrypto-derive.ts", "tests/corpus/7129-webcrypto-derive-errors.mjs", "tests/corpus/7126-webcrypto-aes.ts", "tests/corpus/7127-webcrypto-aes-errors.mjs", "tests/corpus/7124-webcrypto-secret-keys.ts", "tests/corpus/7125-webcrypto-key-errors.mjs", "tests/corpus/6291-webcrypto-hmac-sign-verify.ts", "tests/corpus/6292-webcrypto-hmac-errors.ts", "tests/corpus/6293-webcrypto-digest.ts", "tests/corpus/6294-webcrypto-js-values.mjs", "tests/corpus/6295-jose-hs256/main.mjs"],
|
||||
"stdlib.webcrypto.CryptoKey": ["tests/corpus/7124-webcrypto-secret-keys.ts", "tests/corpus/6291-webcrypto-hmac-sign-verify.ts", "tests/corpus/6294-webcrypto-js-values.mjs", "tests/corpus/6295-jose-hs256/main.mjs"],
|
||||
"stdlib.response": ["tests/harness/fetch-conformance.test.ts"],
|
||||
"stdlib.readable-stream": ["tests/harness/fetch-conformance.test.ts"],
|
||||
"stdlib.abort": ["tests/harness/fetch-conformance.test.ts"]
|
||||
},
|
||||
"features": [
|
||||
{ "chapter": "webcrypto", "symbols": ["subtle.importKey", "subtle.sign", "subtle.verify", "subtle.digest"], "status": "partial", "evidence": ["tests/corpus/6291-webcrypto-hmac-sign-verify.ts", "tests/corpus/6292-webcrypto-hmac-errors.ts", "tests/corpus/6293-webcrypto-digest.ts", "tests/corpus/6294-webcrypto-js-values.mjs", "tests/corpus/6295-jose-hs256/main.mjs", "tests/fixtures/node-types/webcrypto-hmac.ts"] },
|
||||
{ "chapter": "webcrypto", "symbols": ["subtle.importKey", "subtle.generateKey", "subtle.exportKey", "subtle.encrypt", "subtle.decrypt", "subtle.deriveBits", "subtle.deriveKey", "subtle.sign", "subtle.verify", "subtle.digest"], "status": "partial", "evidence": ["tests/corpus/7128-webcrypto-derive.ts", "tests/corpus/7129-webcrypto-derive-errors.mjs", "tests/corpus/7126-webcrypto-aes.ts", "tests/corpus/7127-webcrypto-aes-errors.mjs", "tests/corpus/7124-webcrypto-secret-keys.ts", "tests/corpus/7125-webcrypto-key-errors.mjs", "tests/fixtures/node-types/webcrypto-keys.ts", "tests/corpus/6291-webcrypto-hmac-sign-verify.ts", "tests/corpus/6292-webcrypto-hmac-errors.ts", "tests/corpus/6293-webcrypto-digest.ts", "tests/corpus/6294-webcrypto-js-values.mjs", "tests/corpus/6295-jose-hs256/main.mjs", "tests/fixtures/node-types/webcrypto-hmac.ts"] },
|
||||
{ "chapter": "webcrypto", "signatures": ["Class: `SubtleCrypto`", "`subtle` Type: {SubtleCrypto}"], "status": "partial", "evidence": ["tests/corpus/6291-webcrypto-hmac-sign-verify.ts", "tests/corpus/6292-webcrypto-hmac-errors.ts", "tests/corpus/6293-webcrypto-digest.ts", "tests/corpus/6294-webcrypto-js-values.mjs", "tests/corpus/6295-jose-hs256/main.mjs"] },
|
||||
{ "chapter": "webcrypto", "signatures": ["Class: `CryptoKey`", "`cryptoKey.algorithm`", "`extractable` Type: {boolean}", "`type` Type: {string} One of `'secret'`, `'private'`, or `'public'`.", "`usages` Type: {string\\[]}"], "status": "partial", "evidence": ["tests/corpus/6291-webcrypto-hmac-sign-verify.ts", "tests/corpus/6294-webcrypto-js-values.mjs", "tests/corpus/6295-jose-hs256/main.mjs"] },
|
||||
{ "chapter": "webcrypto", "signatures": ["Class: `CryptoKey`", "`cryptoKey.algorithm`", "`extractable` Type: {boolean}", "`type` Type: {string} One of `'secret'`, `'private'`, or `'public'`.", "`usages` Type: {string\\[]}"], "status": "partial", "evidence": ["tests/corpus/7124-webcrypto-secret-keys.ts", "tests/corpus/6291-webcrypto-hmac-sign-verify.ts", "tests/corpus/6294-webcrypto-js-values.mjs", "tests/corpus/6295-jose-hs256/main.mjs"] },
|
||||
{ "chapter": "webcrypto", "signatures": ["Class: `Crypto`"], "status": "partial", "evidence": ["tests/corpus/7120-webcrypto-random-uuid.ts", "tests/corpus/7121-webcrypto-crypto-object.mjs", "tests/corpus/6293-webcrypto-digest.ts"] },
|
||||
{ "chapter": "webcrypto", "symbols": ["crypto.randomUUID", "crypto.getRandomValues"], "status": "supported", "evidence": ["tests/corpus/7120-webcrypto-random-uuid.ts", "tests/corpus/7121-webcrypto-crypto-object.mjs", "tests/corpus/6204-crypto-get-random-values.ts", "tests/corpus/6205-crypto-get-random-values.js"] },
|
||||
{ "chapter": "globals", "signatures": ["`navigator`"], "status": "partial", "evidence": ["tests/fixtures/node-types/navigator.ts", "tests/harness/project-config.test.ts", "tests/corpus/6331-jose-root-entry/main.ts"] },
|
||||
{ "chapter": "globals", "signatures": ["`userAgent` Type: {string}"], "status": "supported", "evidence": ["tests/fixtures/node-types/navigator.ts", "tests/harness/project-config.test.ts"] },
|
||||
{ "chapter": "globals", "signatures": ["Class: `CryptoKey`", "Class: `SubtleCrypto`"], "status": "partial", "evidence": ["tests/corpus/6291-webcrypto-hmac-sign-verify.ts", "tests/corpus/6292-webcrypto-hmac-errors.ts", "tests/corpus/6293-webcrypto-digest.ts", "tests/corpus/6294-webcrypto-js-values.mjs", "tests/corpus/6295-jose-hs256/main.mjs"] },
|
||||
@@ -413,6 +415,7 @@
|
||||
{ "symbols": ["assert.deepEqual", "assert.notDeepEqual"], "status": "partial", "evidence": ["tests/corpus/5513-assert-deep-equal-loose.js", "tests/corpus/5514-assert-deep-equal-typed.ts", "tests/corpus/5516-assert-deep-equal-edges.js"] },
|
||||
{ "symbols": ["assert", "assert.ok", "assert.equal", "assert.notEqual", "assert.strictEqual", "assert.notStrictEqual", "assert.deepStrictEqual", "assert.notDeepStrictEqual", "assert.fail", "assert.match", "assert.doesNotMatch", "assert.throws", "assert.rejects", "assert.ifError"], "status": "partial", "evidence": ["tests/corpus/1600-assert-passing.ts", "tests/corpus/1604-assert-deep-structures.ts", "tests/corpus/1609-assert-async.ts", "tests/corpus/1721-assert-throws-regex-class.ts", "tests/corpus/2936-static-loose-equality.ts", "tests/corpus/5515-assert-deep-strict-typed-messages.ts"] },
|
||||
{ "chapter": "crypto", "symbols": ["Cipheriv", "cipher.update", "cipher.final", "cipher.setAAD", "cipher.getAuthTag", "cipher.setAutoPadding", "Decipheriv", "decipher.update", "decipher.final", "decipher.setAAD", "decipher.setAuthTag", "decipher.setAutoPadding"], "status": "partial", "evidence": ["tests/corpus/5250-cipher-aes-gcm.ts", "tests/corpus/5251-cipher-aes-cbc.ts", "tests/corpus/5252-cipher-aes-ctr.ts", "tests/corpus/5253-cipher-chacha20-poly1305.ts", "tests/corpus/5254-cipher-errors.js", "tests/corpus/5255-cipher-encodings.cjs", "tests/corpus/5256-cipher-deprecation.cjs", "tests/corpus/5257-cipher-vectors.ts", "tests/corpus/5258-cipher-handles.js", "tests/corpus/5259-cipher-chunked.ts", "tests/corpus/7055-cipher-streams.ts", "tests/corpus/7056-cipher-stream-errors.ts", "tests/corpus/7057-cipher-streams.mjs", "tests/fixtures/node-types/cipher.ts"] },
|
||||
{ "chapter": "crypto", "symbols": ["crypto.webcrypto", "subtle"], "status": "partial", "evidence": ["tests/corpus/7122-webcrypto-node-crypto.ts", "tests/corpus/7123-webcrypto-node-crypto.cjs"] },
|
||||
{ "chapter": "crypto", "symbols": ["ECDH", "ecdh.computeSecret", "ecdh.generateKeys", "ecdh.getPrivateKey", "ecdh.getPublicKey", "ecdh.setPrivateKey", "ecdh.setPublicKey"], "status": "partial", "evidence": ["tests/corpus/6700-ecdh-shared-secret.ts", "tests/corpus/6701-ecdh-formats.ts", "tests/corpus/6702-ecdh-errors.js", "tests/corpus/6703-ecdh-private-keys.ts", "tests/corpus/6704-ecdh-set-public-key.cjs", "tests/corpus/6705-ecdh-handles.ts", "tests/fixtures/node-types/ecdh.ts"] },
|
||||
{ "chapter": "errors", "symbols": ["ERR_CRYPTO_ECDH_INVALID_FORMAT", "ERR_CRYPTO_ECDH_INVALID_PUBLIC_KEY"], "status": "partial", "evidence": ["tests/corpus/6702-ecdh-errors.js"] },
|
||||
{ "symbols": ["crypto.createHash", "Hash", "hash.update", "hash.digest", "hash.copy", "crypto.createHmac", "Hmac", "hmac.update", "hmac.digest", "crypto.hash", "crypto.pbkdf2", "crypto.pbkdf2Sync"], "status": "partial", "evidence": ["tests/corpus/5760-crypto-hash-stream.ts", "tests/corpus/5768-crypto-hash-unknown.ts", "tests/corpus/5769-crypto-hash-unknown.cjs", "tests/corpus/5761-crypto-hash-stream-states.ts", "tests/corpus/5762-crypto-hash-stream.cjs", "tests/corpus/2890-crypto-static-utility.ts", "tests/corpus/node24-crypto-sha2.ts", "tests/corpus/5435-crypto-hash-algorithms.ts", "tests/corpus/5436-crypto-hash-names.js", "tests/corpus/5437-crypto-hmac-algorithms.ts", "tests/corpus/5438-crypto-kdf-digests.ts", "tests/corpus/5440-crypto-hash-encodings.js", "tests/corpus/5441-crypto-hash-output-length.ts", "tests/harness/npm-static.test.ts"] },
|
||||
|
||||
+1
-1
@@ -20,7 +20,7 @@
|
||||
"test:test262": "tsx tests/test262/run.ts",
|
||||
"test:test262:fetch": "node tests/test262/fetch.mjs",
|
||||
"test:cache-identity": "node tests/harness/cache-identity.mjs",
|
||||
"test:lowering-order": "node scripts/lowering-order.mjs --fail-on names",
|
||||
"test:lowering-order": "node scripts/lowering-order.mjs --fail-on content",
|
||||
"test:sandbox": "node scripts/sandbox-test.mjs",
|
||||
"test:sandbox:image": "node scripts/sandbox-image.mjs",
|
||||
"test:watch": "vitest",
|
||||
|
||||
+91
-12
@@ -544,12 +544,15 @@ declare var DOMException: {
|
||||
readonly prototype: DOMException;
|
||||
};
|
||||
|
||||
/* WebCrypto's HMAC slice (scr_webcrypto.c): crypto.subtle's importKey for
|
||||
* raw HMAC keys, HMAC sign/verify and the SHA-1/SHA-2 digests, typed as
|
||||
/* WebCrypto's secret-key slice (scr_webcrypto.c): crypto.subtle's
|
||||
* importKey, generateKey and exportKey for HMAC and AES keys (importKey
|
||||
* also for PBKDF2 and HKDF keys), AES-GCM/CBC/CTR encrypt and decrypt,
|
||||
* PBKDF2 and HKDF deriveBits and deriveKey, HMAC sign/verify and the SHA-1/SHA-2 digests, typed as
|
||||
* exactly that surface (the declarations mirror @types/node's webcrypto
|
||||
* shapes). Every other SubtleCrypto method and crypto member is absent,
|
||||
* so a use is an honest type error. CryptoKey values are native handles
|
||||
* whose four members read through the handle. */
|
||||
* shapes), plus the global's randomUUID and getRandomValues. Every other
|
||||
* SubtleCrypto method and crypto member is absent, so a use is an honest
|
||||
* type error. CryptoKey values are native handles whose four members read
|
||||
* through the handle. */
|
||||
declare var crypto: Crypto;
|
||||
/* The Crypto and SubtleCrypto constructors (feature probes only) stay
|
||||
* `unknown`; the values keep this declaration order. */
|
||||
@@ -582,9 +585,44 @@ interface HmacImportParams extends Algorithm {
|
||||
hash: HashAlgorithmIdentifier;
|
||||
length?: number;
|
||||
}
|
||||
interface HmacKeyGenParams extends Algorithm {
|
||||
hash: HashAlgorithmIdentifier;
|
||||
length?: number;
|
||||
}
|
||||
interface AesKeyGenParams extends Algorithm {
|
||||
length: number;
|
||||
}
|
||||
interface AesGcmParams extends Algorithm {
|
||||
additionalData?: BufferSource;
|
||||
iv: BufferSource;
|
||||
tagLength?: number;
|
||||
}
|
||||
interface AesCbcParams extends Algorithm {
|
||||
iv: BufferSource;
|
||||
}
|
||||
interface AesCtrParams extends Algorithm {
|
||||
counter: BufferSource;
|
||||
length: number;
|
||||
}
|
||||
interface Pbkdf2Params extends Algorithm {
|
||||
hash: HashAlgorithmIdentifier;
|
||||
iterations: number;
|
||||
salt: BufferSource;
|
||||
}
|
||||
interface HkdfParams extends Algorithm {
|
||||
hash: HashAlgorithmIdentifier;
|
||||
info: BufferSource;
|
||||
salt: BufferSource;
|
||||
}
|
||||
interface AesDerivedKeyParams extends Algorithm {
|
||||
length: number;
|
||||
}
|
||||
interface KeyAlgorithm {
|
||||
name: string;
|
||||
}
|
||||
interface AesKeyAlgorithm extends KeyAlgorithm {
|
||||
length: number;
|
||||
}
|
||||
interface HmacKeyAlgorithm extends KeyAlgorithm {
|
||||
hash: KeyAlgorithm;
|
||||
length: number;
|
||||
@@ -623,10 +661,37 @@ interface JsonWebKey {
|
||||
x?: string;
|
||||
y?: string;
|
||||
}
|
||||
/* The 'jwk' form type-checks like @types/node's; the compiler refuses it
|
||||
* (the JsonWebKey import is not implemented). */
|
||||
interface SubtleCrypto {
|
||||
decrypt(
|
||||
algorithm: AesGcmParams | AesCbcParams | AesCtrParams,
|
||||
key: CryptoKey,
|
||||
data: BufferSource,
|
||||
): Promise<ArrayBuffer>;
|
||||
deriveBits(
|
||||
algorithm: Pbkdf2Params | HkdfParams,
|
||||
baseKey: CryptoKey,
|
||||
length?: number | null,
|
||||
): Promise<ArrayBuffer>;
|
||||
deriveKey(
|
||||
algorithm: Pbkdf2Params | HkdfParams,
|
||||
baseKey: CryptoKey,
|
||||
derivedKeyType: AlgorithmIdentifier | AesDerivedKeyParams | HmacImportParams,
|
||||
extractable: boolean,
|
||||
keyUsages: readonly KeyUsage[],
|
||||
): Promise<CryptoKey>;
|
||||
digest(algorithm: AlgorithmIdentifier, data: BufferSource): Promise<ArrayBuffer>;
|
||||
encrypt(
|
||||
algorithm: AesGcmParams | AesCbcParams | AesCtrParams,
|
||||
key: CryptoKey,
|
||||
data: BufferSource,
|
||||
): Promise<ArrayBuffer>;
|
||||
exportKey(format: "jwk", key: CryptoKey): Promise<JsonWebKey>;
|
||||
exportKey(format: Exclude<KeyFormat, "jwk">, key: CryptoKey): Promise<ArrayBuffer>;
|
||||
generateKey(
|
||||
algorithm: AesKeyGenParams | HmacKeyGenParams,
|
||||
extractable: boolean,
|
||||
keyUsages: readonly KeyUsage[],
|
||||
): Promise<CryptoKey>;
|
||||
importKey(
|
||||
format: "jwk",
|
||||
keyData: JsonWebKey,
|
||||
@@ -651,6 +716,21 @@ interface SubtleCrypto {
|
||||
}
|
||||
interface Crypto {
|
||||
readonly subtle: SubtleCrypto;
|
||||
getRandomValues<
|
||||
T extends
|
||||
| Int8Array
|
||||
| Uint8Array
|
||||
| Uint8ClampedArray
|
||||
| Int16Array
|
||||
| Uint16Array
|
||||
| Int32Array
|
||||
| Uint32Array
|
||||
| BigInt64Array
|
||||
| BigUint64Array,
|
||||
>(
|
||||
typedArray: T,
|
||||
): T;
|
||||
randomUUID(): `${string}-${string}-${string}-${string}-${string}`;
|
||||
}
|
||||
|
||||
/* Capability-gated globals harness code probes behind feature guards
|
||||
@@ -2784,8 +2864,7 @@ declare module "crypto" {
|
||||
b: ArrayBufferView | ArrayBufferLike,
|
||||
): boolean;
|
||||
/* WebCrypto's getRandomValues (also the webcrypto object's): integer
|
||||
* typed arrays, filled in place and returned. The crypto GLOBAL stays
|
||||
* unknown in fallback builds. */
|
||||
* typed arrays, filled in place and returned. */
|
||||
type RandomValuesArray =
|
||||
| Int8Array
|
||||
| Uint8Array
|
||||
@@ -2797,9 +2876,9 @@ declare module "crypto" {
|
||||
| BigInt64Array
|
||||
| BigUint64Array;
|
||||
export function getRandomValues<T extends RandomValuesArray>(typedArray: T): T;
|
||||
export const webcrypto: {
|
||||
getRandomValues<T extends RandomValuesArray>(typedArray: T): T;
|
||||
};
|
||||
/* The WebCrypto global's object and its SubtleCrypto. */
|
||||
export const webcrypto: Crypto;
|
||||
export const subtle: SubtleCrypto;
|
||||
export function randomFillSync<T extends ArrayBufferView | ArrayBufferLike>(
|
||||
buffer: T,
|
||||
offset?: number,
|
||||
|
||||
@@ -109,6 +109,8 @@ function match(
|
||||
ranges,
|
||||
initialized: findInitializedByteLoopBindings(f).get(loop) ?? new Set(),
|
||||
proven: new Set(),
|
||||
workers: false,
|
||||
owned: new Set(),
|
||||
});
|
||||
}
|
||||
|
||||
@@ -250,6 +252,79 @@ test("loops to a typed array's length version the other typed arrays they index"
|
||||
expect(rebound).toContain("bytes.index.invalid");
|
||||
});
|
||||
|
||||
test("worker programs test each receiver's shared storage once in the guard", () => {
|
||||
// for (let i = 0; i < n; i++) out[i] = src[i] * 2, where either view may
|
||||
// be shared.
|
||||
const loop = counted([
|
||||
set(ref("out", f64s), ref("i"), bin("*", get(ref("src", f64s), ref("i")), num(2))),
|
||||
]);
|
||||
const kernel = fn([loop]);
|
||||
const mod: IrModule = {
|
||||
irVersion: 17,
|
||||
sourceFile: loc.file,
|
||||
entry: "__main",
|
||||
workers: true,
|
||||
functions: [
|
||||
{ name: "__main", params: [], returnType: VOID, locals: [], body: [], loc },
|
||||
kernel,
|
||||
],
|
||||
};
|
||||
const body = /^define internal [^\n]*@sc_bf_kernel[^]*?^}/m.exec(emitLlvmModule(mod))![0];
|
||||
const guard = body.slice(0, body.search(/^bytes\.version\.(fast|slow)\d+:$/m));
|
||||
expect(guard.match(/icmp eq ptr %\S+, null/g)).toHaveLength(2);
|
||||
const fast = /^bytes\.version\.fast\d+:$[^]*?^bytes\.version\.done\d+:$/m.exec(body)![0];
|
||||
expect(fast).not.toContain("bytes.shared");
|
||||
expect(fast).not.toMatch(/(load|store) atomic double/);
|
||||
// The checked copy keeps the shared-memory path for each access.
|
||||
const slow = body.slice(body.search(/^bytes\.version\.slow\d+:$/m), body.indexOf(fast));
|
||||
expect(slow).toContain("load atomic double");
|
||||
expect(slow).toContain("store atomic double");
|
||||
});
|
||||
|
||||
test("index temps of a version's counter are integers in the fast copy", () => {
|
||||
// for (let i = 0; i < out.length; i++) { const j = i * 4; out[i] = src[j] + src[j + 1] }
|
||||
const length: IrExpr = {
|
||||
kind: "bytesIntrinsic",
|
||||
method: "length",
|
||||
receiver: ref("out", f64s),
|
||||
args: [],
|
||||
type: F64,
|
||||
loc,
|
||||
};
|
||||
const loop = counted(
|
||||
[
|
||||
{ kind: "varDecl", localId: "at", init: bin("*", ref("i"), num(4)), loc },
|
||||
set(
|
||||
ref("out", f64s),
|
||||
ref("i"),
|
||||
bin(
|
||||
"+",
|
||||
get(ref("src", f64s), ref("at")),
|
||||
get(ref("src", f64s), bin("+", ref("at"), num(1))),
|
||||
),
|
||||
),
|
||||
],
|
||||
length,
|
||||
);
|
||||
const mod: IrModule = {
|
||||
irVersion: 17,
|
||||
sourceFile: loc.file,
|
||||
entry: "__main",
|
||||
functions: [
|
||||
{ name: "__main", params: [], returnType: VOID, locals: [], body: [], loc },
|
||||
fn([loop], [local("at", F64)]),
|
||||
],
|
||||
};
|
||||
const body = /^define internal [^\n]*@sc_bf_kernel[^]*?^}/m.exec(emitLlvmModule(mod))![0];
|
||||
const fast = /^bytes\.version\.fast\d+:$[^]*?^bytes\.version\.done\d+:$/m.exec(body)![0];
|
||||
expect(body).toContain("alloca i64 ; integer at");
|
||||
expect(fast).toMatch(/mul nsw i64 %\S+, 4/);
|
||||
expect(fast).toMatch(/add nsw i64 %\S+, 1/);
|
||||
expect(fast).not.toContain("fmul");
|
||||
expect(fast).not.toContain("fptosi");
|
||||
expect(fast).not.toContain("bytes.index");
|
||||
});
|
||||
|
||||
test("Float16Array accesses version in elements and share the element tag", () => {
|
||||
const f16s: IrType = { kind: "bytes", elem: "f16" };
|
||||
const withHalves = (body: IrStmt[]): IrFunction => {
|
||||
|
||||
@@ -6,7 +6,7 @@ import { byteNumberAccess } from "../../ir/byte-numbers.js";
|
||||
import { everyExprChild, everyStmtChild } from "../../ir/traverse.js";
|
||||
import { exactInteger, widenInteger } from "./integer-values.js";
|
||||
import { emitCountedLoopLimit, emitCountedLoopSafety } from "./counted-loops.js";
|
||||
import { emitBytesHeaderLength } from "./expr-bytes.js";
|
||||
import { emitBytesHeaderLength, emitBytesSharedStorage } from "./expr-bytes.js";
|
||||
|
||||
type Node = IrExpr | IrStmt;
|
||||
type VarRef = IrExpr & { kind: "varRef" };
|
||||
@@ -41,8 +41,13 @@ interface AffineCheck {
|
||||
export interface BytesLoopVersion {
|
||||
/** Typed-array accesses that are in bounds whenever the guard holds. */
|
||||
bounds: ReadonlySet<Node>;
|
||||
/** Worker programs: typed-array accesses whose views the guard proves
|
||||
* thread-owned (no shared storage). */
|
||||
owned: ReadonlySet<Node>;
|
||||
/** Root receiver bindings, read once before the loop. */
|
||||
receivers: { ref: VarRef; global: boolean }[];
|
||||
/** Receivers the guard requires to have no shared storage. */
|
||||
ownedReceivers: number[];
|
||||
fixed: FixedCheck[];
|
||||
affine: AffineCheck[];
|
||||
/** The counted loop whose iteration space the affine checks cover. */
|
||||
@@ -66,6 +71,11 @@ export interface BytesLoopFacts {
|
||||
initialized: ReadonlySet<string>;
|
||||
/** Accesses already proved in bounds without a guard. */
|
||||
proven: ReadonlySet<Node>;
|
||||
/** Worker programs: an access of a view that may be shared tests the
|
||||
* view's shared storage first. */
|
||||
workers: boolean;
|
||||
/** Accesses already proved thread-owned by an enclosing version. */
|
||||
owned: ReadonlySet<Node>;
|
||||
}
|
||||
|
||||
const MAX_COST = 400;
|
||||
@@ -87,6 +97,11 @@ const INVARIANT_OPS = new Set(["+", "-", "*", "&", "|", "^", "<<", ">>", ">>>"])
|
||||
* and last values in bounds, such as `out[base + i]` for `i < n` or
|
||||
* `view.getUint32(i * 4)`.
|
||||
*
|
||||
* In worker programs, every access of a view that may be shared tests its
|
||||
* shared storage. A view's storage is also fixed at construction, so the
|
||||
* same guard tests each invariant receiver once, and the fast copy reads
|
||||
* and writes thread-owned views directly.
|
||||
*
|
||||
* Only small synchronous loops are versioned (nested loops included), and
|
||||
* the emitter bounds how deeply versions nest. The checked loop runs
|
||||
* whenever the guard fails, and keeps every out-of-bounds read, ignored
|
||||
@@ -314,10 +329,19 @@ export function matchBytesLoopVersion(
|
||||
}
|
||||
return index;
|
||||
};
|
||||
// Worker programs: accesses through invariant receivers, proven or not,
|
||||
// become thread-owned when the guard finds no shared storage.
|
||||
const owned = new Map<number, Node[]>();
|
||||
const access = (node: Node, array: IrExpr, index: IrExpr, width: number): void => {
|
||||
if (facts.proven.has(node)) return;
|
||||
const ref = root(array);
|
||||
if (!ref) return;
|
||||
if (facts.workers && !facts.owned.has(node)) {
|
||||
const receiver = receiverOf(ref);
|
||||
const nodes = owned.get(receiver);
|
||||
if (nodes) nodes.push(node);
|
||||
else owned.set(receiver, [node]);
|
||||
}
|
||||
if (facts.proven.has(node)) return;
|
||||
const shape = counter !== null ? affine(index) : null;
|
||||
if (shape && shape.counter !== 0) {
|
||||
const operands = new Set<IrExpr>();
|
||||
@@ -383,9 +407,15 @@ export function matchBytesLoopVersion(
|
||||
});
|
||||
const fixedKept = [...fixed.values()].slice(0, MAX_CHECKS);
|
||||
const affineKept = affineGroups.slice(0, MAX_CHECKS - fixedKept.length);
|
||||
if (fixedKept.length === 0 && affineKept.length === 0) return null;
|
||||
const ownedKept = [...owned.entries()].slice(0, MAX_CHECKS);
|
||||
if (fixedKept.length === 0 && affineKept.length === 0 && ownedKept.length === 0) return null;
|
||||
const bounds = new Set<Node>();
|
||||
const ownedNodes = new Set<Node>();
|
||||
const used = new Set<number>();
|
||||
for (const [receiver, nodes] of ownedKept) {
|
||||
used.add(receiver);
|
||||
for (const node of nodes) ownedNodes.add(node);
|
||||
}
|
||||
const ranges = new Map<IrExpr, IntegerRange>();
|
||||
const counterLow = Math.max(counted?.range.min ?? 0, -bound),
|
||||
counterHigh = Math.min(counted?.range.max ?? 0, bound);
|
||||
@@ -397,7 +427,6 @@ export function matchBytesLoopVersion(
|
||||
used.add(group.check.receiver);
|
||||
for (const node of group.nodes) bounds.add(node);
|
||||
for (const operand of group.operands) {
|
||||
if (facts.ranges.get(operand)) continue;
|
||||
const shape = shapes.get(operand)!;
|
||||
const a = shape.counter * counterLow,
|
||||
b = shape.counter * counterHigh;
|
||||
@@ -408,7 +437,14 @@ export function matchBytesLoopVersion(
|
||||
min += term.sign > 0 ? r.min : -r.max;
|
||||
max += term.sign > 0 ? r.max : -r.min;
|
||||
}
|
||||
if (counterLow <= counterHigh) ranges.set(operand, { min, max });
|
||||
if (counterLow > counterHigh) continue;
|
||||
// The guard's bound on the counter can narrow a range the operand
|
||||
// already has (a loop to a length proves only `i < 2^53`), so that
|
||||
// `i * 4` in the fast copy is exact integer arithmetic.
|
||||
const known = facts.ranges.get(operand);
|
||||
if (!known) ranges.set(operand, { min, max });
|
||||
else if (min > known.min || max < known.max)
|
||||
ranges.set(operand, { min: Math.max(min, known.min), max: Math.min(max, known.max) });
|
||||
}
|
||||
}
|
||||
// Renumber the receivers the kept checks use.
|
||||
@@ -420,7 +456,9 @@ export function matchBytesLoopVersion(
|
||||
(counted.range.min < -bound || counted.range.max > bound);
|
||||
return {
|
||||
bounds,
|
||||
owned: ownedNodes,
|
||||
receivers: order.map((index) => receivers[index]!),
|
||||
ownedReceivers: ownedKept.map(([receiver]) => renumber.get(receiver)!),
|
||||
fixed: fixedKept.map((g) => ({
|
||||
receiver: renumber.get(g.check.receiver)!,
|
||||
end: g.check.end,
|
||||
@@ -439,7 +477,8 @@ export function matchBytesLoopVersion(
|
||||
}
|
||||
|
||||
/** Branches to `fast` when every access the version proves is in bounds for
|
||||
* every iteration, and to `slow` otherwise (including a counted loop whose
|
||||
* every iteration and every view it proves thread-owned has no shared
|
||||
* storage, and to `slow` otherwise (including a counted loop whose
|
||||
* integer form is not established, or a module receiver not yet set). */
|
||||
export function emitBytesLoopVersionGuard(
|
||||
host: LlvmEmitterContext,
|
||||
@@ -473,6 +512,11 @@ export function emitBytesLoopVersionGuard(
|
||||
return wide;
|
||||
});
|
||||
const conditions: string[] = [];
|
||||
for (const index of version.ownedReceivers) {
|
||||
const owned = B.tmp();
|
||||
B.line(`${owned} = icmp eq ptr ${emitBytesSharedStorage(host, receivers[index]!)}, null`);
|
||||
conditions.push(owned);
|
||||
}
|
||||
for (const check of version.fixed) {
|
||||
const ok = B.tmp();
|
||||
B.line(`${ok} = icmp ugt i64 ${lengths[check.receiver]}, ${check.end}`);
|
||||
|
||||
@@ -128,6 +128,8 @@ export function emitFunction(emitter: LlEmitter, fn: IrFunction): string {
|
||||
emitter.stableCaptureIds =
|
||||
fn.captures !== undefined ? emitter.lazyCaptures.stableCaptures(fn.name) : new Set();
|
||||
emitter.integerLoopBindings.clear();
|
||||
emitter.integerTemps.clear();
|
||||
emitter.integerTempsEnabled = emitter.debug === null && !fn.async && !fn.generator;
|
||||
emitter.countedLoopsEnabled = emitter.debug === null && !fn.async && !fn.generator;
|
||||
emitter.speculativeLoopsEnabled = emitter.countedLoopsEnabled;
|
||||
emitter.speculativeFunction = fn.name;
|
||||
@@ -228,6 +230,7 @@ export function emitFunction(emitter: LlEmitter, fn: IrFunction): string {
|
||||
emitter.integerLoopRanges = rangeFacts.loopRanges;
|
||||
emitter.int32Elements = slotFacts.elements ?? null;
|
||||
emitter.bytesBounds = findBytesBounds(numericFn, emitter.integerRanges);
|
||||
emitter.bytesOwned = new Set();
|
||||
// Debug builds keep every number local a double for the debugger's
|
||||
// variable view. Lazily boxed captured parameters keep their double,
|
||||
// which the box is created from, and so do plain copies of a capture or
|
||||
|
||||
@@ -50,6 +50,7 @@ import { emitBytesLoopVersionGuard } from "./bytes-loop-versions.js";
|
||||
import { mangleLocal } from "../mangle.js";
|
||||
import { LlvmUnsupportedError } from "./unsupported.js";
|
||||
import type { LlValue } from "./expr-context.js";
|
||||
import { widenInteger } from "./integer-values.js";
|
||||
import { SCALAR_UNION_RESULT } from "./int32-abi.js";
|
||||
import { emitLiteralSwitch } from "./switch-dispatch.js";
|
||||
import {
|
||||
@@ -267,6 +268,7 @@ export function emitStmtBody(emitter: LlEmitter, s: IrStmt): void {
|
||||
else emitter.toFrame(v);
|
||||
B.line(`store ${emitter.llType(b.type)} ${v.name}, ptr ${b.slot}`);
|
||||
emitter.storeIntegerView(s.localId, v, s.init);
|
||||
storeIntegerTemp(emitter, s.localId, b, v);
|
||||
if (isRefCounted(b.type)) {
|
||||
emitter.scopes[emitter.scopes.length - 1]!.push({ slot: b.slot, type: b.type });
|
||||
emitter.noteLastUseMove(s, b);
|
||||
@@ -517,6 +519,7 @@ export function emitStmtBody(emitter: LlEmitter, s: IrStmt): void {
|
||||
s.index,
|
||||
s.value,
|
||||
emitter.bytesBounds.has(s),
|
||||
emitter.bytesOwned.has(s),
|
||||
);
|
||||
break;
|
||||
}
|
||||
@@ -970,6 +973,7 @@ export function emitStmtBody(emitter: LlEmitter, s: IrStmt): void {
|
||||
done = B.newLabel("bytes.version.done");
|
||||
emitBytesLoopVersionGuard(emitter, version, fast, slow);
|
||||
const originalBounds = emitter.bytesBounds,
|
||||
originalOwned = emitter.bytesOwned,
|
||||
originalRanges = emitter.integerRanges;
|
||||
emitter.bytesLoopVersioning.add(s);
|
||||
try {
|
||||
@@ -978,6 +982,8 @@ export function emitStmtBody(emitter: LlEmitter, s: IrStmt): void {
|
||||
B.br(done);
|
||||
B.startBlock(fast);
|
||||
emitter.bytesBounds = new Set([...originalBounds, ...version.bounds]);
|
||||
if (version.owned.size > 0)
|
||||
emitter.bytesOwned = new Set([...originalOwned, ...version.owned]);
|
||||
if (version.ranges.size > 0)
|
||||
emitter.integerRanges = new Map([...originalRanges, ...version.ranges]);
|
||||
if (version.counted?.guarded) emitter.provenCountedGuards.add(s);
|
||||
@@ -985,6 +991,7 @@ export function emitStmtBody(emitter: LlEmitter, s: IrStmt): void {
|
||||
B.br(done);
|
||||
} finally {
|
||||
emitter.bytesBounds = originalBounds;
|
||||
emitter.bytesOwned = originalOwned;
|
||||
emitter.integerRanges = originalRanges;
|
||||
emitter.provenCountedGuards.delete(s);
|
||||
emitter.bytesLoopVersioning.delete(s);
|
||||
@@ -1539,6 +1546,48 @@ export function emitStmtBody(emitter: LlEmitter, s: IrStmt): void {
|
||||
* Abrupt completions inside tryBody/catchBody ride the finallyStack:
|
||||
* returns inline copies at the return site, while break/continue use the
|
||||
* shared region walk before branching to their resolved target. */
|
||||
/** A constant number local whose initializer has an exact integer form
|
||||
* (such as `const j = i * 4` in a typed-array loop version) keeps that
|
||||
* integer in an i64 slot beside the double, so reads such as `px[j + 1]`
|
||||
* index with integer arithmetic instead of converting the double back.
|
||||
* Every other declaration of the local drops the integer: the same
|
||||
* declaration is emitted once per loop copy, and a copy must not read the
|
||||
* integer another copy stored. */
|
||||
function storeIntegerTemp(
|
||||
emitter: LlEmitter,
|
||||
localId: string,
|
||||
b: ReturnType<LlEmitter["binding"]>,
|
||||
v: LlValue,
|
||||
): void {
|
||||
const local = b.local;
|
||||
if (
|
||||
!emitter.integerTempsEnabled ||
|
||||
b.kind !== "local" ||
|
||||
!local ||
|
||||
local.type.kind !== "f64" ||
|
||||
local.mutable ||
|
||||
local.tdz === true ||
|
||||
emitter.captureIds.has(localId) ||
|
||||
!v.integer
|
||||
) {
|
||||
emitter.integerTemps.delete(localId);
|
||||
return;
|
||||
}
|
||||
const B = emitter.B;
|
||||
let slot = emitter.integerTemps.get(localId)?.slot;
|
||||
if (slot === undefined) {
|
||||
slot = B.slot();
|
||||
B.entryAllocas.push(`${slot} = alloca i64 ; integer ${local.name}`);
|
||||
}
|
||||
B.line(`store i64 ${widenInteger(emitter, v.integer)}, ptr ${slot}`);
|
||||
emitter.integerTemps.set(localId, {
|
||||
slot,
|
||||
type: "i64",
|
||||
signed: true,
|
||||
range: v.integer.range,
|
||||
});
|
||||
}
|
||||
|
||||
export function emitTryCatch(emitter: LlEmitter, s: IrStmt & { kind: "tryCatch" }): void {
|
||||
const B = emitter.B;
|
||||
const hasCatch = s.catchBody !== null;
|
||||
|
||||
@@ -1081,6 +1081,17 @@ export class LlEmitter {
|
||||
string,
|
||||
{ slot: string; type: "i32" | "i64"; signed?: boolean; range?: { min: number; max: number } }
|
||||
>();
|
||||
/** Constant number locals whose latest emitted declaration stored an
|
||||
* exact integer beside the double, such as `const j = i * 4` in a
|
||||
* typed-array loop version: an i64 slot and the integer's range. */
|
||||
integerTemps = new Map<
|
||||
string,
|
||||
{ slot: string; type: "i64"; signed: true; range: IntegerRange }
|
||||
>();
|
||||
/** Integer temps are kept only in optimized synchronous functions: an
|
||||
* async or generator body resumes with its locals in its frame, which
|
||||
* an entry alloca is not part of. */
|
||||
integerTempsEnabled = false;
|
||||
countedLoopsEnabled = false;
|
||||
/** Speculative integer loops (speculative-integers.ts) run in this function. */
|
||||
speculativeLoopsEnabled = false;
|
||||
@@ -1125,6 +1136,9 @@ export class LlEmitter {
|
||||
* guard already passed. */
|
||||
readonly provenCountedGuards = new Set<IrStmt>();
|
||||
bytesBounds: ReadonlySet<IrExpr | IrStmt> = new Set();
|
||||
/** Worker programs: typed-array accesses whose views an enclosing version
|
||||
* guard proved thread-owned. */
|
||||
bytesOwned: ReadonlySet<IrExpr | IrStmt> = new Set();
|
||||
integerViews = new Map<string, string>();
|
||||
/** Module globals the current function owns outright (initializer
|
||||
* bindings): their function-local slot, which stands in for the global. */
|
||||
@@ -3836,6 +3850,8 @@ export class LlEmitter {
|
||||
ranges: this.integerRanges,
|
||||
initialized,
|
||||
proven: this.bytesBounds,
|
||||
workers: this.mod.workers === true,
|
||||
owned: this.bytesOwned,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -6196,8 +6212,9 @@ export class LlEmitter {
|
||||
expr?: IrExpr,
|
||||
inBounds = false,
|
||||
invalidNaN = false,
|
||||
owned = false,
|
||||
): LlValue {
|
||||
return emitBytesGet(this, elem, receiver, index, expr, inBounds, invalidNaN);
|
||||
return emitBytesGet(this, elem, receiver, index, expr, inBounds, invalidNaN, owned);
|
||||
}
|
||||
|
||||
emitToUint32(value: string, expr?: IrExpr, uint32?: string): string {
|
||||
@@ -6260,8 +6277,9 @@ export class LlEmitter {
|
||||
indexExpr?: IrExpr,
|
||||
expr?: IrExpr,
|
||||
inBounds = false,
|
||||
owned = false,
|
||||
): void {
|
||||
return emitBytesSet(this, elem, receiver, index, value, indexExpr, expr, inBounds);
|
||||
return emitBytesSet(this, elem, receiver, index, value, indexExpr, expr, inBounds, owned);
|
||||
}
|
||||
|
||||
emitBytesIntrinsic(e: IrExpr & { kind: "bytesIntrinsic" }): LlValue {
|
||||
|
||||
@@ -176,6 +176,17 @@ export function emitBytesLength(
|
||||
return { name: out, type: F64 };
|
||||
}
|
||||
|
||||
/** A view's shared backing (null for a buffer this thread owns). It is
|
||||
* set at construction, before script code can see the view. */
|
||||
export function emitBytesSharedStorage(host: LlvmEmitterContext, receiver: string): string {
|
||||
const B = host.B;
|
||||
const slot = B.tmp(),
|
||||
storage = B.tmp();
|
||||
B.line(`${slot} = getelementptr inbounds %ScrBytes, ptr ${receiver}, i64 0, i32 9`);
|
||||
B.line(`${storage} = load ptr, ptr ${slot}${bytesHeaderAlias(host)}`);
|
||||
return storage;
|
||||
}
|
||||
|
||||
/** Worker programs: an element of a view of a SharedArrayBuffer (`shared` is
|
||||
* fixed before script code can see the view) is read and written with one
|
||||
* relaxed atomic access of its width, as the runtime's shared accessors do;
|
||||
@@ -186,12 +197,9 @@ export function emitBytesLength(
|
||||
function emitSharedBytesSplit(host: LlvmEmitterContext, receiver: string): string {
|
||||
const B = host.B;
|
||||
host.declare(`declare i1 @llvm.expect.i1(i1, i1)`);
|
||||
const slot = B.tmp(),
|
||||
storage = B.tmp(),
|
||||
const storage = emitBytesSharedStorage(host, receiver),
|
||||
isShared = B.tmp(),
|
||||
expected = B.tmp();
|
||||
B.line(`${slot} = getelementptr inbounds %ScrBytes, ptr ${receiver}, i64 0, i32 9`);
|
||||
B.line(`${storage} = load ptr, ptr ${slot}${bytesHeaderAlias(host)}`);
|
||||
B.line(`${isShared} = icmp ne ptr ${storage}, null`);
|
||||
B.line(`${expected} = call i1 @llvm.expect.i1(i1 ${isShared}, i1 false)`);
|
||||
const shared = B.newLabel("bytes.shared"),
|
||||
@@ -209,15 +217,17 @@ export function emitBytesGet(
|
||||
expr?: IrExpr,
|
||||
inBounds = false,
|
||||
invalidNaN = false,
|
||||
owned = false,
|
||||
): LlValue {
|
||||
const B = host.B;
|
||||
// Any invalid index traps (the array runtime's discipline).
|
||||
if (!invalidNaN) return emitBytesLoad(host, elem, receiver, index, expr, inBounds, undefined);
|
||||
if (!invalidNaN)
|
||||
return emitBytesLoad(host, elem, receiver, index, expr, inBounds, undefined, owned);
|
||||
// The numeric view of an ordinary read: an invalid index never touches
|
||||
// storage and reads as NaN — ToNumber of Node's undefined.
|
||||
const invalid = B.newLabel("bytes.read.invalid");
|
||||
const done = B.newLabel("bytes.read.done");
|
||||
const loaded = emitBytesLoad(host, elem, receiver, index, expr, inBounds, invalid);
|
||||
const loaded = emitBytesLoad(host, elem, receiver, index, expr, inBounds, invalid, owned);
|
||||
const ok = B.newLabel("bytes.read.ok");
|
||||
B.br(ok);
|
||||
B.startBlock(ok);
|
||||
@@ -260,8 +270,10 @@ function emitBytesLoad(
|
||||
expr: IrExpr | undefined,
|
||||
inBounds: boolean,
|
||||
invalid: string | undefined,
|
||||
owned: boolean,
|
||||
): LlValue {
|
||||
if (host.mod.workers) {
|
||||
// `owned`: an enclosing version guard proved the view thread-owned.
|
||||
if (host.mod.workers && !owned) {
|
||||
const idx = emitBytesIndex(host, receiver, index, expr, invalid, inBounds);
|
||||
return emitSharedOrLocalLoad(host, elem, receiver, idx);
|
||||
}
|
||||
@@ -515,13 +527,14 @@ export function emitBytesSet(
|
||||
indexExpr?: IrExpr,
|
||||
expr?: IrExpr,
|
||||
inBounds = false,
|
||||
owned = false,
|
||||
): void {
|
||||
const B = host.B;
|
||||
// Invalid integer-indexed writes are ignored after evaluating the RHS.
|
||||
const done = B.newLabel("bytes.store.done");
|
||||
const data = host.emitBytesData(receiver);
|
||||
const idx = emitBytesIndex(host, receiver, index, indexExpr, done, inBounds);
|
||||
if (host.mod.workers && elem === "f16") {
|
||||
if (host.mod.workers && !owned && elem === "f16") {
|
||||
host.declare(`declare void @scr_bytes_shared_set(ptr, ${host.sizeType}, double)`);
|
||||
const local = emitSharedBytesSplit(host, receiver);
|
||||
B.line(
|
||||
@@ -532,10 +545,24 @@ export function emitBytesSet(
|
||||
}
|
||||
let stored: string | null;
|
||||
if (elem === "u8c") {
|
||||
host.declare(`declare double @scr_bytes_to_u8_clamp(double)`);
|
||||
const clamped = B.tmp();
|
||||
// ToUint8Clamp, as scr_bytes_to_u8_clamp computes it: NaN and values
|
||||
// at most 0 store 0, values at least 255 store 255, and values between
|
||||
// round to the nearest integer, ties to even. Adding and subtracting
|
||||
// 2^52 rounds a value in (0, 255) that way in the default rounding mode,
|
||||
// with no call or branch, so loops of clamped stores can vectorize.
|
||||
const positive = B.tmp(),
|
||||
high = B.tmp(),
|
||||
shifted = B.tmp(),
|
||||
rounded = B.tmp(),
|
||||
upper = B.tmp(),
|
||||
clamped = B.tmp();
|
||||
stored = B.tmp();
|
||||
B.line(`${clamped} = call double @scr_bytes_to_u8_clamp(double ${value.name})`);
|
||||
B.line(`${positive} = fcmp ogt double ${value.name}, 0.0`);
|
||||
B.line(`${high} = fcmp oge double ${value.name}, 255.0`);
|
||||
B.line(`${shifted} = fadd double ${value.name}, 0x4330000000000000`);
|
||||
B.line(`${rounded} = fsub double ${shifted}, 0x4330000000000000`);
|
||||
B.line(`${upper} = select i1 ${high}, double 255.0, double ${rounded}`);
|
||||
B.line(`${clamped} = select i1 ${positive}, double ${upper}, double 0.0`);
|
||||
B.line(`${stored} = fptoui double ${clamped} to i32`);
|
||||
} else
|
||||
stored =
|
||||
@@ -572,7 +599,7 @@ export function emitBytesSet(
|
||||
ty = "i32";
|
||||
operand = stored!;
|
||||
}
|
||||
if (host.mod.workers) {
|
||||
if (host.mod.workers && !owned) {
|
||||
// A shared view's element takes a relaxed atomic store of its width.
|
||||
const local = emitSharedBytesSplit(host, receiver);
|
||||
const p = B.tmp();
|
||||
@@ -721,6 +748,7 @@ export function emitBytesIntrinsic(
|
||||
e.args[0],
|
||||
host.bytesBounds.has(e),
|
||||
e.invalidNaN === true,
|
||||
host.bytesOwned.has(e),
|
||||
);
|
||||
case "slice":
|
||||
return call(
|
||||
|
||||
@@ -59,7 +59,8 @@ export function emitLiteralExpr(
|
||||
throw new InternalCompilerError(`llvm emitter bug: bare unitLit '${e.unit}'`);
|
||||
case "varRef": {
|
||||
host.materializeSplitLocal(e.localId);
|
||||
const integerSlot = host.integerLoopBindings.get(e.localId);
|
||||
const integerSlot =
|
||||
host.integerLoopBindings.get(e.localId) ?? host.integerTemps.get(e.localId);
|
||||
if (integerSlot !== undefined) {
|
||||
const integer = B.tmp();
|
||||
const number = B.tmp();
|
||||
|
||||
@@ -448,6 +448,7 @@ export const LIB_FN_SYMS: Record<string, string> = {
|
||||
"buffer.poolFromArray": "scr_buffer_from_arr",
|
||||
"buffer.poolFromDyn": "scr_buffer_from_dyn_pooled",
|
||||
"buffer.poolFromDynRef": "scr_buffer_from_dyn_ref_pooled",
|
||||
"buffer.poolFromDynOffset": "scr_buffer_from_dyn_offset_pooled",
|
||||
"buffer.poolSize": "scr_buffer_pool_size",
|
||||
"buffer.setPoolSize": "scr_buffer_set_pool_size",
|
||||
"buffer.byteLenDyn": "scr_buffer_byte_length_dyn",
|
||||
@@ -615,10 +616,17 @@ export const LIB_FN_SYMS: Record<string, string> = {
|
||||
"crypto.keyObjectInput": "scr_crypto_key_object_input",
|
||||
"crypto.isKeyObject": "scr_crypto_is_key_object",
|
||||
"webcrypto.importKey": "scr_webcrypto_import_key",
|
||||
"webcrypto.generateKey": "scr_webcrypto_generate_key",
|
||||
"webcrypto.exportKey": "scr_webcrypto_export_key",
|
||||
"webcrypto.encrypt": "scr_webcrypto_encrypt",
|
||||
"webcrypto.decrypt": "scr_webcrypto_decrypt",
|
||||
"webcrypto.deriveBits": "scr_webcrypto_derive_bits",
|
||||
"webcrypto.deriveKey": "scr_webcrypto_derive_key",
|
||||
"webcrypto.sign": "scr_webcrypto_sign",
|
||||
"webcrypto.verify": "scr_webcrypto_verify",
|
||||
"webcrypto.digest": "scr_webcrypto_digest",
|
||||
"webcrypto.isCryptoKey": "scr_webcrypto_is_crypto_key",
|
||||
"webcrypto.object": "scr_webcrypto_object",
|
||||
"crypto.randomUUIDv7": "scr_crypto_random_uuid_v7",
|
||||
"crypto.pbkdf2": "scr_crypto_pbkdf2",
|
||||
"crypto.hkdf": "scr_crypto_hkdf",
|
||||
@@ -1038,6 +1046,9 @@ export const LIB_FN_SYMS: Record<string, string> = {
|
||||
"atomics.op": "scr_atomics_op",
|
||||
"atomics.notify": "scr_atomics_notify",
|
||||
"atomics.wait": "scr_atomics_wait",
|
||||
"atomics.opDyn": "scr_atomics_op_dyn",
|
||||
"atomics.waitDyn": "scr_atomics_wait_dyn",
|
||||
"atomics.notifyDyn": "scr_atomics_notify_dyn",
|
||||
"worker.new": "scr_worker_new",
|
||||
"worker.isMainThread": "scr_context_is_main",
|
||||
"worker.threadId": "scr_context_thread_number",
|
||||
@@ -1574,6 +1585,11 @@ export const USES_TIMERS_LIB_FNS = new Set<string>([
|
||||
// ecdh.setPublicKey reports DEP0031 on the next tick.
|
||||
"crypto.ecdhNew",
|
||||
// WebCrypto results settle through the deferred crypto queue.
|
||||
"webcrypto.generateKey",
|
||||
"webcrypto.encrypt",
|
||||
"webcrypto.decrypt",
|
||||
"webcrypto.deriveBits",
|
||||
"webcrypto.deriveKey",
|
||||
"webcrypto.sign",
|
||||
"webcrypto.verify",
|
||||
"webcrypto.digest",
|
||||
|
||||
@@ -84,8 +84,9 @@ export interface NativeLinkFeatures {
|
||||
foreignFfi: boolean;
|
||||
nodeTest: boolean;
|
||||
tls: boolean;
|
||||
/** crypto.createCipheriv/createDecipheriv: scr_cipher.c plus the mbedTLS
|
||||
* archive. */
|
||||
/** crypto.createCipheriv/createDecipheriv and WebCrypto's encrypt and
|
||||
* decrypt: scr_cipher.c plus the mbedTLS archive (and, with webcrypto,
|
||||
* scr_webcrypto_aes.c). */
|
||||
cipher: boolean;
|
||||
/** crypto.createECDH and ECDH.convertKey: scr_ecdh.c plus the mbedTLS
|
||||
* archive. */
|
||||
|
||||
@@ -243,8 +243,9 @@ export interface CcOptions {
|
||||
* The unit also compiles whenever `tls` does — scr_tls.c consults its
|
||||
* default-set override and shared Windows-certificate enumerator. */
|
||||
tlsCa?: boolean;
|
||||
/** crypto.createCipheriv/createDecipheriv: compiles scr_cipher.c and links
|
||||
* the mbedTLS archive (shared with tls). */
|
||||
/** crypto.createCipheriv/createDecipheriv and WebCrypto's encrypt and
|
||||
* decrypt: compiles scr_cipher.c (and, with webcrypto,
|
||||
* scr_webcrypto_aes.c) and links the mbedTLS archive (shared with tls). */
|
||||
cipher?: boolean;
|
||||
/** crypto.createECDH and ECDH.convertKey: compiles scr_ecdh.c and links
|
||||
* the mbedTLS archive (shared with tls). */
|
||||
|
||||
@@ -297,6 +297,8 @@ const RUNTIME_UNITS: readonly RuntimeUnit[] = [
|
||||
unit((u) => u.opts.foreignFfi, "scr_ffi_queue.c"),
|
||||
unit((u) => u.opts.nodeTest, "scr_test.c"),
|
||||
unit((u) => u.opts.webcrypto, "scr_webcrypto.c"),
|
||||
// The crypto global's object extends scr_async_dyn.c's.
|
||||
unit((u) => u.opts.webcrypto && u.opts.dynAsync, "scr_webcrypto_object.c"),
|
||||
// The CA-store unit rides its own gate OR the tls one: scr_tls.c
|
||||
// references its default-set override unconditionally.
|
||||
unit((u) => u.tlsCa, "scr_tls_ca.c"),
|
||||
@@ -308,6 +310,7 @@ const RUNTIME_UNITS: readonly RuntimeUnit[] = [
|
||||
...(u.tls ? [u.rt(join(u.rtDir, "scr_tls.c"))] : []),
|
||||
...(u.cipher ? [u.rt(join(u.rtDir, "scr_cipher.c"))] : []),
|
||||
...(u.ecdh ? [u.rt(join(u.rtDir, "scr_ecdh.c"))] : []),
|
||||
...(u.cipher && u.opts.webcrypto ? [u.rt(join(u.rtDir, "scr_webcrypto_aes.c"))] : []),
|
||||
u.tlsArchive,
|
||||
// mbedTLS's win32 entropy poll is BCryptGenRandom (bcrypt.h).
|
||||
// The unconditional win32 libs above do not carry it.
|
||||
|
||||
@@ -208,6 +208,14 @@ export function moduleWasiUnavailableSurface(
|
||||
};
|
||||
return false;
|
||||
}
|
||||
if (node.fn === "webcrypto.encrypt" || node.fn === "webcrypto.decrypt") {
|
||||
found = {
|
||||
surface:
|
||||
"WebCrypto encrypt and decrypt (the wasm32-wasi runtime does not include the mbedTLS library)",
|
||||
loc,
|
||||
};
|
||||
return false;
|
||||
}
|
||||
if (node.fn === "crypto.ecdhNew" || node.fn === "crypto.ecdhConvertKey") {
|
||||
found = {
|
||||
surface: "ECDH (the wasm32-wasi runtime does not include the mbedTLS library)",
|
||||
|
||||
@@ -174,8 +174,7 @@ class ContractChecker {
|
||||
return `${owner}.${method[2]}`;
|
||||
}
|
||||
if (fn?.sourceName !== undefined && fn.sourceName !== "<anonymous>") return fn.sourceName;
|
||||
if (fn !== undefined && /^%fn\.[0-9a-f]{12}$/.test(fnName))
|
||||
return `the function at ${this.where(fn.loc)}`;
|
||||
if (fn !== undefined && /^%fn\d+$/.test(fnName)) return `the function at ${this.where(fn.loc)}`;
|
||||
return fnName.replace(/^%/, "");
|
||||
}
|
||||
|
||||
|
||||
@@ -9,7 +9,6 @@ import {
|
||||
type SrcLoc,
|
||||
typeEquals,
|
||||
} from "../../ir/ir.js";
|
||||
import { keyedHelperName } from "./stable-names.js";
|
||||
import { numLit, varRef } from "../../ir/build.js";
|
||||
import { typeKey } from "../type-mapper.js";
|
||||
import type { Lowerer } from "./lowerer.js";
|
||||
@@ -126,7 +125,7 @@ export function lowerArrayFill(
|
||||
writeNull;
|
||||
let name = lowerer.arrHofHelpers.get(key);
|
||||
if (!name) {
|
||||
name = keyedHelperName(lowerer, "%arr.fill", key);
|
||||
name = "%arr.fill." + lowerer.arrHofHelpers.size;
|
||||
lowerer.arrHofHelpers.set(key, name);
|
||||
const a = varRef("a.0", arrType, loc);
|
||||
const n = varRef("n.0", F64, loc);
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import { keyedHelperName } from "./stable-names.js";
|
||||
import { InternalCompilerError } from "../../errors.js";
|
||||
import { dynUndefinedExpr, helperLoc, varRef } from "../../ir/build.js";
|
||||
import {
|
||||
@@ -253,7 +252,7 @@ export function lowerSafeIndexRead(
|
||||
let name = lowerer.arrHofHelpers.get(key);
|
||||
if (!name) {
|
||||
const at = helperLoc(loc);
|
||||
name = keyedHelperName(lowerer, "%arr.idxOr", key);
|
||||
name = `%arr.idxOr.${lowerer.arrHofHelpers.size}`;
|
||||
lowerer.arrHofHelpers.set(key, name);
|
||||
const arrT = arr.type;
|
||||
lowerer.liftedFns.push({
|
||||
@@ -299,7 +298,7 @@ export function lowerSafeBytesRead(
|
||||
let name = lowerer.arrHofHelpers.get(key);
|
||||
if (!name) {
|
||||
const at = helperLoc(loc);
|
||||
name = keyedHelperName(lowerer, "%bytes.idxOr", key);
|
||||
name = `%bytes.idxOr.${lowerer.arrHofHelpers.size}`;
|
||||
lowerer.arrHofHelpers.set(key, name);
|
||||
const b = varRef("b.0", bytesT, at);
|
||||
const i = varRef("i.0", F64, at);
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import { keyedHelperName } from "../stable-names.js";
|
||||
import {
|
||||
BYTES_U8,
|
||||
BOOL,
|
||||
@@ -73,7 +72,7 @@ export function lowerOptionalNumberPredicate(
|
||||
const key = `number.optionalPredicate:${fn}:${typeKey(widened)}`;
|
||||
let helper = lowerer.valueHelpers.get(key);
|
||||
if (!helper) {
|
||||
helper = keyedHelperName(lowerer, "%number.optionalPredicate", key);
|
||||
helper = `%number.optionalPredicate.${lowerer.valueHelpers.size}`;
|
||||
lowerer.valueHelpers.set(key, helper);
|
||||
const input = varRef("value.0", widened, loc);
|
||||
lowerer.liftedFns.push({
|
||||
|
||||
@@ -60,10 +60,14 @@ export function lowerAtomicsCall(
|
||||
// has a concrete typed-array binding. Validate the actual lowered storage.
|
||||
const array = lowerer.lowerExpr(source);
|
||||
const type = array.type;
|
||||
// An untyped array (a JavaScript `workerData` or field): the runtime
|
||||
// checks its kind and throws Node's TypeError for any other value.
|
||||
const dynamic = type?.kind === "dyn";
|
||||
if (
|
||||
type?.kind !== "bytes" ||
|
||||
!["i8", "u8", "i16", "u16", "i32", "u32"].includes(type.elem) ||
|
||||
((member === "wait" || member === "notify") && type.elem !== "i32")
|
||||
!dynamic &&
|
||||
(type?.kind !== "bytes" ||
|
||||
!["i8", "u8", "i16", "u16", "i32", "u32"].includes(type.elem) ||
|
||||
((member === "wait" || member === "notify") && type.elem !== "i32"))
|
||||
) {
|
||||
lowerer.noLowering(
|
||||
`Atomics.${member} over this array`,
|
||||
@@ -82,7 +86,7 @@ export function lowerAtomicsCall(
|
||||
if (member === "wait")
|
||||
return {
|
||||
kind: "libCall",
|
||||
fn: "atomics.wait",
|
||||
fn: dynamic ? "atomics.waitDyn" : "atomics.wait",
|
||||
args: [array, index, arg(2, 0), arg(3, Infinity)],
|
||||
type: STRING,
|
||||
loc,
|
||||
@@ -90,14 +94,14 @@ export function lowerAtomicsCall(
|
||||
if (member === "notify")
|
||||
return {
|
||||
kind: "libCall",
|
||||
fn: "atomics.notify",
|
||||
fn: dynamic ? "atomics.notifyDyn" : "atomics.notify",
|
||||
args: [array, index, arg(2, Infinity)],
|
||||
type: F64,
|
||||
loc,
|
||||
};
|
||||
return {
|
||||
kind: "libCall",
|
||||
fn: "atomics.op",
|
||||
fn: dynamic ? "atomics.opDyn" : "atomics.op",
|
||||
args: [array, index, arg(2, 0), arg(3, 0), number(operation)],
|
||||
type: F64,
|
||||
loc,
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import { keyedHelperName } from "../stable-names.js";
|
||||
import { recordToEnvPairs } from "./environment.js";
|
||||
import { lowerChildArgsArg } from "./child-process.js";
|
||||
import { boolLit, numLit, varRef } from "../../../ir/build.js";
|
||||
@@ -219,7 +218,7 @@ function execFileAsyncHelper(
|
||||
const key = shell ? "execAsync" : "execFileAsync";
|
||||
const existing = lowerer.valueHelpers.get(key);
|
||||
if (existing) return { name: existing, shapeId, envShapeId };
|
||||
const name = keyedHelperName(lowerer, `%${key}`, key);
|
||||
const name = `%${key}.${lowerer.valueHelpers.size}`;
|
||||
lowerer.valueHelpers.set(key, name);
|
||||
const recT: IrType = { kind: "record", shapeId };
|
||||
const envRecT: IrType = { kind: "record", shapeId: envShapeId };
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import { keyedHelperName } from "../stable-names.js";
|
||||
import { recordToEnvPairs } from "./environment.js";
|
||||
import { lowerChildArgsArg } from "./child-process.js";
|
||||
import { boolLit, numLit, strLit, varRef } from "../../../ir/build.js";
|
||||
@@ -542,7 +541,7 @@ function execStdioModeHelper(
|
||||
const key = `cp.stdiomode:${shapeId}:${fd}`;
|
||||
const existing = lowerer.arrHofHelpers.get(key);
|
||||
if (existing) return existing;
|
||||
const name = keyedHelperName(lowerer, `%cp.stdioMode.${fd}`, key);
|
||||
const name = `%cp.stdioMode.${fd}.${lowerer.arrHofHelpers.size}`;
|
||||
lowerer.arrHofHelpers.set(key, name);
|
||||
const optsT: IrType = { kind: "record", shapeId };
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@ import { type Lowerer, own } from "../lowerer.js";
|
||||
import { locOf } from "../../program.js";
|
||||
import { CIPHER_TYPE_NAMES } from "../../type-mapper.js";
|
||||
import { CRYPTO_CIPHERS, CRYPTO_CURVES, CRYPTO_HASHES } from "../crypto-tables.js";
|
||||
import { stdlibGlobalNameOf } from "../surfaces.js";
|
||||
import {
|
||||
BOOL,
|
||||
BYTES_U8,
|
||||
@@ -1317,8 +1318,10 @@ export function lowerCryptoGetRandomValues(
|
||||
}
|
||||
|
||||
/** Whether `receiver` names a WebCrypto Crypto object: the `crypto`
|
||||
* global (also through globalThis) or node:crypto's `webcrypto`. */
|
||||
* global (also through globalThis or an alias of it) or node:crypto's
|
||||
* `webcrypto`. */
|
||||
export function isWebCryptoReceiver(lowerer: Lowerer, receiver: ts.Expression): boolean {
|
||||
if (stdlibGlobalNameOf(lowerer, receiver) === "crypto") return true;
|
||||
if (ts.isIdentifier(receiver)) {
|
||||
if (receiver.text === "crypto" && lowerer.isStdlibGlobal(receiver, "crypto")) return true;
|
||||
const imported = lowerer.builtinImportOf(receiver);
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import { keyedHelperName } from "../stable-names.js";
|
||||
import * as ts from "../../ts7/adapter.js";
|
||||
import { type Lowerer } from "../lowerer.js";
|
||||
import { locOf } from "../../program.js";
|
||||
@@ -85,7 +84,7 @@ export function lowerDisposableStackUseCall(
|
||||
const key = `disposableStack.use:${className}`;
|
||||
let helper = lowerer.valueHelpers.get(key);
|
||||
if (!helper) {
|
||||
helper = keyedHelperName(lowerer, "%disposableStack.use", key);
|
||||
helper = `%disposableStack.use.${lowerer.valueHelpers.size}`;
|
||||
lowerer.valueHelpers.set(key, helper);
|
||||
const context = newFnCtx(false, null, null, VOID);
|
||||
lowerer.fnStack.push(context);
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import { keyedHelperName } from "../stable-names.js";
|
||||
import { dynUndefinedExpr, boolLit, numLit, varRef } from "../../../ir/build.js";
|
||||
import * as ts from "../../ts7/adapter.js";
|
||||
import { type Lowerer, ladderFenceExpr } from "../lowerer.js";
|
||||
@@ -443,7 +442,7 @@ export function lowerStringOrBytesWrite(
|
||||
const key = `fs.${append ? "append" : "write"}:${dataType.unionId}`;
|
||||
let helper = lowerer.valueHelpers.get(key);
|
||||
if (!helper) {
|
||||
helper = keyedHelperName(lowerer, "%fs.writeData", key);
|
||||
helper = `%fs.writeData.${lowerer.valueHelpers.size}`;
|
||||
lowerer.valueHelpers.set(key, helper);
|
||||
const body: IrStmt[] = [];
|
||||
for (let tag = 0; tag < arms.length; tag++) {
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import { keyedHelperName } from "../stable-names.js";
|
||||
import { isDateJsonSource } from "./dates.js";
|
||||
import { dynUndefinedExpr, varRef } from "../../../ir/build.js";
|
||||
import { InternalCompilerError } from "../../../errors.js";
|
||||
@@ -254,7 +253,7 @@ function lowerOptionalStringifyRoot(
|
||||
const key = `json.optionalString:${value.type.unionId}:${JSON.stringify(indent)}`;
|
||||
let helper = lowerer.valueHelpers.get(key);
|
||||
if (!helper) {
|
||||
helper = keyedHelperName(lowerer, "%json.optionalString", key);
|
||||
helper = `%json.optionalString.${lowerer.valueHelpers.size}`;
|
||||
lowerer.valueHelpers.set(key, helper);
|
||||
const input = varRef("value.0", value.type, loc);
|
||||
const serialized: IrExpr = {
|
||||
|
||||
@@ -72,17 +72,14 @@ export function lowerNumberStaticCall(
|
||||
return { kind: "libCall", fn, args: [arg], type: BOOL, loc };
|
||||
}
|
||||
if (member === "parseFloat" || member === "parseInt") {
|
||||
const want = member === "parseFloat" ? 1 : 2;
|
||||
if (call.arguments.nodes.length !== want) {
|
||||
lowerer.noLowering(
|
||||
`Number.${member} with ${call.arguments.nodes.length} argument${call.arguments.nodes.length === 1 ? "" : "s"}`,
|
||||
call,
|
||||
member === "parseInt" ? "pass an explicit radix: Number.parseInt(s, 10)" : undefined,
|
||||
);
|
||||
const count = call.arguments.nodes.length;
|
||||
if (count < 1 || count > (member === "parseFloat" ? 1 : 2)) {
|
||||
lowerer.noLowering(`Number.${member} with ${count} argument${count === 1 ? "" : "s"}`, call);
|
||||
}
|
||||
// Number.parseFloat/Number.parseInt ARE the global parsers: over a
|
||||
// string argument they lower to the same static parse (parseInt with
|
||||
// its explicit radix); only the coercing argument shapes are refused.
|
||||
// its radix, or radix 0 when it is missing); only the coercing argument
|
||||
// shapes are refused.
|
||||
const probed = tryLowerExpression(lowerer, call.arguments.nodes[0]!);
|
||||
if (probed !== null && isStringLike(lowerer, probed.type)) {
|
||||
const parsed = lowerStaticNumberGlobal(lowerer, member, call, call.arguments.nodes, loc);
|
||||
@@ -101,7 +98,10 @@ export function lowerNumberStaticCall(
|
||||
};
|
||||
if (member === "parseFloat")
|
||||
return { kind: "libCall", fn: "num.parseFloat", args: [text], type: F64, loc };
|
||||
const radix = lowerer.lowerExprExpecting(call.arguments.nodes[1]!, F64);
|
||||
const radixNode = call.arguments.nodes[1];
|
||||
const radix: IrExpr = radixNode
|
||||
? lowerer.lowerExprExpecting(radixNode, F64)
|
||||
: { kind: "numLit", value: 0, type: F64, loc };
|
||||
return { kind: "libCall", fn: "num.parseInt", args: [text, radix], type: F64, loc };
|
||||
}
|
||||
const argText =
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import { keyedHelperName } from "../stable-names.js";
|
||||
import { numLit, strLit, varRef } from "../../../ir/build.js";
|
||||
import * as ts from "../../ts7/adapter.js";
|
||||
import { type Lowerer } from "../lowerer.js";
|
||||
@@ -220,7 +219,7 @@ export function lowerOsCpusCall(lowerer: Lowerer, call: ts.CallExpression, loc:
|
||||
const key = `os.cpus:${typeKey(result)}`;
|
||||
let helper = lowerer.valueHelpers.get(key);
|
||||
if (!helper) {
|
||||
helper = keyedHelperName(lowerer, "%os.cpus", key);
|
||||
helper = `%os.cpus.${lowerer.valueHelpers.size}`;
|
||||
lowerer.valueHelpers.set(key, helper);
|
||||
const index = varRef("i.0", F64, loc);
|
||||
const field = (k: number): IrExpr => ({
|
||||
@@ -347,7 +346,7 @@ export function lowerPathParseCall(
|
||||
const key = `${fn}:${typeKey(result)}`;
|
||||
let helper = lowerer.valueHelpers.get(key);
|
||||
if (!helper) {
|
||||
helper = keyedHelperName(lowerer, "%path.parse", key);
|
||||
helper = `%path.parse.${lowerer.valueHelpers.size}`;
|
||||
lowerer.valueHelpers.set(key, helper);
|
||||
const parts = varRef("parts.0", arrayOf(STRING), loc);
|
||||
lowerer.liftedFns.push({
|
||||
@@ -449,7 +448,7 @@ export function lowerPathFormatCall(
|
||||
const key = `${fn}:${typeKey(recordT)}`;
|
||||
let helper = lowerer.valueHelpers.get(key);
|
||||
if (!helper) {
|
||||
helper = keyedHelperName(lowerer, "%path.format", key);
|
||||
helper = `%path.format.${lowerer.valueHelpers.size}`;
|
||||
lowerer.valueHelpers.set(key, helper);
|
||||
const object = varRef("object.0", recordT, loc);
|
||||
const read = (name: string): IrExpr => {
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import { keyedHelperName } from "../stable-names.js";
|
||||
import * as ts from "../../ts7/adapter.js";
|
||||
import { type Lowerer } from "../lowerer.js";
|
||||
import { locOf } from "../../program.js";
|
||||
@@ -331,7 +330,7 @@ function lowerPerfTimelineCall(
|
||||
const key = `perf.${many ? "entries" : timeline.fn}:${typeKey(result)}`;
|
||||
let helper = lowerer.valueHelpers.get(key);
|
||||
if (!helper) {
|
||||
helper = keyedHelperName(lowerer, `%perf.${many ? "entries" : member}`, key);
|
||||
helper = `%perf.${many ? "entries" : member}.${lowerer.valueHelpers.size}`;
|
||||
lowerer.valueHelpers.set(key, helper);
|
||||
const argsRef = varRef("a.0", DYN, loc);
|
||||
const whichRef = varRef("w.0", F64, loc);
|
||||
@@ -462,7 +461,7 @@ export function perfEntryTag(
|
||||
const key = `perf.tag:${entryT.shapeId}`;
|
||||
let helper = lowerer.valueHelpers.get(key);
|
||||
if (!helper) {
|
||||
helper = keyedHelperName(lowerer, "%perf.tag", key);
|
||||
helper = `%perf.tag.${lowerer.valueHelpers.size}`;
|
||||
lowerer.valueHelpers.set(key, helper);
|
||||
const e = varRef("e.0", entryT, loc);
|
||||
lowerer.liftedFns.push({
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import { keyedHelperName } from "../stable-names.js";
|
||||
import { stdinKeypressListener } from "./readline.js";
|
||||
import { envSnapshotHelper, lowerProcessLoadEnvFile } from "./environment.js";
|
||||
import { dynUndefinedExpr, boolLit, numLit, strLit, varRef } from "../../../ir/build.js";
|
||||
@@ -1562,7 +1561,7 @@ export function lowerProcessMethodCall(
|
||||
const key = `${prefix}usage.diff:${prevT.shapeId}:${t.shapeId}`;
|
||||
let helper = lowerer.valueHelpers.get(key);
|
||||
if (!helper) {
|
||||
helper = keyedHelperName(lowerer, `%${prefix}usage.diff`, key);
|
||||
helper = `%${prefix}usage.diff.${lowerer.valueHelpers.size}`;
|
||||
lowerer.valueHelpers.set(key, helper);
|
||||
const pRef: IrExpr = { kind: "varRef", localId: "p.0", type: prevT, loc };
|
||||
const fieldOf = (name: string): IrExpr => ({
|
||||
@@ -1637,7 +1636,7 @@ export function lowerProcessMethodCall(
|
||||
const key = `process.memoryUsage:${t.shapeId}`;
|
||||
let helper = lowerer.valueHelpers.get(key);
|
||||
if (!helper) {
|
||||
helper = keyedHelperName(lowerer, "%process.memoryUsage", key);
|
||||
helper = `%process.memoryUsage.${lowerer.valueHelpers.size}`;
|
||||
lowerer.valueHelpers.set(key, helper);
|
||||
lowerer.liftedFns.push({
|
||||
name: helper,
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import { keyedHelperName } from "../stable-names.js";
|
||||
import { checkedPromiseAll, thenReader } from "../checked-promise-all.js";
|
||||
import { InternalCompilerError } from "../../../errors.js";
|
||||
import * as ts from "../../ts7/adapter.js";
|
||||
@@ -56,7 +55,7 @@ function lowerTuplePromiseAll(
|
||||
const key = `${elems.map((e) => typeKey(e.type)).join(",")}->${typeKey(resultT.inner)}`;
|
||||
let name = lowerer.arrHofHelpers.get(`promise.allTuple:${key}`);
|
||||
if (name === undefined) {
|
||||
name = keyedHelperName(lowerer, "%promise.allTuple", `promise.allTuple:${key}`);
|
||||
name = `%promise.allTuple.${lowerer.arrHofHelpers.size}`;
|
||||
lowerer.arrHofHelpers.set(`promise.allTuple:${key}`, name);
|
||||
const ctx = newFnCtx(false, null, null, resultT.inner);
|
||||
ctx.isAsync = true;
|
||||
@@ -558,7 +557,7 @@ export function lowerPromiseStaticCall(
|
||||
const key = `promise.try:${typeKey(f.type)}:${args.map((a) => typeKey(a.type)).join(",")}`;
|
||||
let helper = lowerer.arrHofHelpers.get(key);
|
||||
if (!helper) {
|
||||
helper = keyedHelperName(lowerer, "%promise.try", key);
|
||||
helper = `%promise.try.${lowerer.arrHofHelpers.size}`;
|
||||
lowerer.arrHofHelpers.set(key, helper);
|
||||
const fRef: IrExpr = { kind: "varRef", localId: "f.0", type: f.type, loc };
|
||||
const argLocals = args.map((a, i) => ({ id: `a${i}.0`, name: `a${i}`, type: a.type }));
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import { keyedHelperName } from "../stable-names.js";
|
||||
import * as ts from "../../ts7/adapter.js";
|
||||
import { type Lowerer } from "../lowerer.js";
|
||||
import { varRef } from "../../../ir/build.js";
|
||||
@@ -171,7 +170,7 @@ function promisifyMaker(lowerer: Lowerer, shape: CallbackShape, loc: SrcLoc): st
|
||||
const key = `promisify:${typeKey(shape.fnT)}=>${typeKey(shape.resultT)}`;
|
||||
const existing = lowerer.arrHofHelpers.get(key);
|
||||
if (existing) return existing;
|
||||
const base = keyedHelperName(lowerer, "%promisify", key);
|
||||
const base = `%promisify.${lowerer.arrHofHelpers.size}`;
|
||||
lowerer.arrHofHelpers.set(key, base);
|
||||
const { fnT, resultT, args, callbackT, callbackParamT, inner, error } = shape;
|
||||
const promiseT = resultT.ret;
|
||||
|
||||
@@ -353,7 +353,8 @@ function lowerPromptArg(lowerer: Lowerer, node: ts.Expression): { value: IrExpr;
|
||||
* - on/once/addListener for 'line' ((input: string) or no parameters),
|
||||
* 'error' ((err: Error) or no parameters), 'history' ((history:
|
||||
* string[]) or no parameters), and 'close'/'pause'/'resume'/'SIGINT'/
|
||||
* 'SIGTSTP'/'SIGCONT' (no parameters), as statements.
|
||||
* 'SIGTSTP'/'SIGCONT' (no parameters); as an expression the call yields
|
||||
* the interface, so registrations chain.
|
||||
* Everything else the lib declares fences member-qualified. Null for
|
||||
* non-Interface receivers. */
|
||||
export function lowerReadlineMethodCall(
|
||||
@@ -505,14 +506,19 @@ export function lowerReadlineMethodCall(
|
||||
`${RL_EVENT_LIST} are the supported readline events`,
|
||||
);
|
||||
}
|
||||
if (!ts.isExpressionStatement(call.parent)) {
|
||||
lowerer.unsupported(
|
||||
"SC1090",
|
||||
call,
|
||||
"chaining readline listener registration (register each listener as its own statement)",
|
||||
);
|
||||
}
|
||||
const recv = receiver();
|
||||
// As an expression (`rl.on("line", f).on("close", g)`), the call
|
||||
// registers the listener and yields the interface, as Node's on() does.
|
||||
const recvLocal = ts.isExpressionStatement(call.parent)
|
||||
? undefined
|
||||
: lowerer.declareHiddenLocal("%readlineListener", F64);
|
||||
const saved: IrStmt[] =
|
||||
recvLocal === undefined
|
||||
? []
|
||||
: [{ kind: "varDecl", localId: recvLocal.id, init: receiver(), loc }];
|
||||
const recv: IrExpr =
|
||||
recvLocal === undefined
|
||||
? receiver()
|
||||
: { kind: "varRef", localId: recvLocal.id, type: F64, loc };
|
||||
const cb = lowerer.lowerExpr(args[1]!);
|
||||
const want =
|
||||
event === "line"
|
||||
@@ -547,13 +553,21 @@ export function lowerReadlineMethodCall(
|
||||
: `'${event}' listeners with parameters or a return value (use ())`,
|
||||
);
|
||||
}
|
||||
return {
|
||||
const register: IrExpr = {
|
||||
kind: "libCall",
|
||||
fn: "rl.on",
|
||||
args: [recv, numLit(ev, loc), cb, boolLit(name === "once", loc)],
|
||||
type: VOID,
|
||||
loc,
|
||||
};
|
||||
if (recvLocal === undefined) return register;
|
||||
return {
|
||||
kind: "seqExpr",
|
||||
stmts: [...saved, { kind: "exprStmt", expr: register, loc }],
|
||||
result: { kind: "varRef", localId: recvLocal.id, type: F64, loc },
|
||||
type: F64,
|
||||
loc,
|
||||
};
|
||||
}
|
||||
lowerer.noLowering(
|
||||
`readline.Interface.${name}`,
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import { keyedHelperName } from "../stable-names.js";
|
||||
import { isBuiltinClassInstance } from "./receiver-types.js";
|
||||
import { strLit, varRef, dynUndefinedExpr } from "../../../ir/build.js";
|
||||
import { staticTextDecoderEncoding } from "../text-decoder-encoding.js";
|
||||
@@ -130,7 +129,7 @@ function stringDecoderHelper(
|
||||
const key = `strdec.${op}`;
|
||||
const existing = lowerer.valueHelpers.get(key);
|
||||
if (existing) return existing;
|
||||
const name = keyedHelperName(lowerer, `%strdec.${op}`, key);
|
||||
const name = `%strdec.${op}.${lowerer.valueHelpers.size}`;
|
||||
lowerer.valueHelpers.set(key, name);
|
||||
const recT: IrType = { kind: "record", shapeId };
|
||||
|
||||
|
||||
@@ -85,7 +85,7 @@ export function lowerTimersPromisesSetInterval(
|
||||
const genT = timersPromisesIntervalType(lowerer, expr);
|
||||
if (genT === null) lowerer.badType(expr, lowerer.typeOf(expr));
|
||||
const yielded = lowerer.coerceInto(valueNode, value, genT.yieldT);
|
||||
const fnName = lowerer.closureName(loc, "_tpInterval");
|
||||
const fnName = `%fn${lowerer.lambdaCounter++}_tpInterval`;
|
||||
const msParam: IrLocal = { id: "%tp.ms", name: "delay", type: F64, mutable: false };
|
||||
const valueParam: IrLocal = { id: "%tp.value", name: "value", type: genT.yieldT, mutable: false };
|
||||
const promiseVoid: IrType = { kind: "promise", inner: VOID };
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import { keyedHelperName } from "../stable-names.js";
|
||||
import { nodeThrowExpr, countedFor, strLit, varRef, dynUndefinedExpr } from "../../../ir/build.js";
|
||||
import * as ts from "../../ts7/adapter.js";
|
||||
import { type Lowerer, own } from "../lowerer.js";
|
||||
@@ -293,7 +292,7 @@ function searchParamsFromTuples(
|
||||
const key = `sp.fromTuples:${row.shapeId}`;
|
||||
let helper = lowerer.valueHelpers.get(key);
|
||||
if (!helper) {
|
||||
helper = keyedHelperName(lowerer, "%sp.fromTuples", key);
|
||||
helper = `%sp.fromTuples.${lowerer.valueHelpers.size}`;
|
||||
lowerer.valueHelpers.set(key, helper);
|
||||
const pairsT = init.type;
|
||||
const sp = (): IrExpr => varRef("sp.0", SEARCH_PARAMS_T, loc);
|
||||
@@ -373,7 +372,7 @@ function lowerOptionalStringSearchParams(
|
||||
const key = `sp.optionalString:${init.type.unionId}`;
|
||||
let helper = lowerer.valueHelpers.get(key);
|
||||
if (!helper) {
|
||||
helper = keyedHelperName(lowerer, "%sp.optionalString", key);
|
||||
helper = `%sp.optionalString.${lowerer.valueHelpers.size}`;
|
||||
lowerer.valueHelpers.set(key, helper);
|
||||
const value = varRef("init.0", init.type, loc);
|
||||
lowerer.liftedFns.push({
|
||||
@@ -679,7 +678,7 @@ function lowerSpForEachCall(
|
||||
const key = `${arity}:${typeKey(fnRet)}`;
|
||||
let helper = lowerer.spHofHelpers.get(key);
|
||||
if (!helper) {
|
||||
helper = keyedHelperName(lowerer, "%sp.forEach", key);
|
||||
helper = `%sp.forEach.${lowerer.spHofHelpers.size}`;
|
||||
lowerer.spHofHelpers.set(key, helper);
|
||||
lowerer.liftedFns.push(buildSpForEachFn(helper, arity, fnRet, loc));
|
||||
}
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
* spirit of process.memoryUsage(): approximate values, not V8 heap
|
||||
* figures (a compiled binary embeds no V8 engine). Every other member
|
||||
* fences at its use with the by-design reason (builtinFenceHintOf). */
|
||||
import { keyedHelperName } from "../stable-names.js";
|
||||
import { numLit } from "../../../ir/build.js";
|
||||
import * as ts from "../../ts7/adapter.js";
|
||||
import { type Lowerer } from "../lowerer.js";
|
||||
@@ -56,7 +55,7 @@ export function lowerV8HeapStatisticsCall(
|
||||
const key = `v8.getHeapStatistics:${t.shapeId}`;
|
||||
let helper = lowerer.valueHelpers.get(key);
|
||||
if (!helper) {
|
||||
helper = keyedHelperName(lowerer, "%v8.getHeapStatistics", key);
|
||||
helper = `%v8.getHeapStatistics.${lowerer.valueHelpers.size}`;
|
||||
lowerer.valueHelpers.set(key, helper);
|
||||
const sample = (index: number): IrExpr => ({
|
||||
kind: "libCall",
|
||||
|
||||
@@ -5,34 +5,57 @@ import { BOOL, DYN, type IrExpr, type IrLibFn } from "../../../ir/ir.js";
|
||||
import { lowerDynObjectLiteral } from "../expressions/object-literals.js";
|
||||
import { stdlibGlobalNameOf } from "../surfaces.js";
|
||||
import { WEBCRYPTO_DYN_TYPES } from "../../type-mapper.js";
|
||||
import { isWebCryptoReceiver } from "./crypto.js";
|
||||
|
||||
/* WebCrypto's HMAC slice (scr_webcrypto.c): `crypto.subtle.importKey`,
|
||||
* `sign`, `verify` and `digest` called on the crypto global (also through
|
||||
* globalThis) lower to runtime entry points that take the argument vector
|
||||
* as one dyn array and answer a promise, so Web IDL conversion, algorithm
|
||||
* normalization and every error stay Node-shaped at run time. CryptoKey
|
||||
/* WebCrypto's secret-key slice (scr_webcrypto.c, with encrypt and
|
||||
* decrypt in scr_webcrypto_aes.c): `crypto.subtle`'s `importKey`,
|
||||
* `generateKey`, `exportKey`, `encrypt`, `decrypt`, `deriveBits`,
|
||||
* `deriveKey`, `sign`, `verify` and `digest`
|
||||
* called on the crypto global (also through
|
||||
* globalThis, node:crypto's webcrypto and node:crypto's subtle) lower to
|
||||
* runtime entry points that take the argument vector as one dyn array and
|
||||
* answer a promise, so Web IDL conversion, algorithm normalization and
|
||||
* every error stay Node-shaped at run time. CryptoKey
|
||||
* values are native handles; their type maps to dyn and their members
|
||||
* are handle reads. Other SubtleCrypto methods are refused by name. */
|
||||
|
||||
const SUBTLE_LOWERED: ReadonlyMap<string, IrLibFn> = new Map([
|
||||
["importKey", "webcrypto.importKey"],
|
||||
["generateKey", "webcrypto.generateKey"],
|
||||
["exportKey", "webcrypto.exportKey"],
|
||||
["encrypt", "webcrypto.encrypt"],
|
||||
["decrypt", "webcrypto.decrypt"],
|
||||
["deriveBits", "webcrypto.deriveBits"],
|
||||
["deriveKey", "webcrypto.deriveKey"],
|
||||
["sign", "webcrypto.sign"],
|
||||
["verify", "webcrypto.verify"],
|
||||
["digest", "webcrypto.digest"],
|
||||
]);
|
||||
|
||||
const SUBTLE_HINT =
|
||||
"the native WebCrypto surface is crypto.subtle.importKey('raw', ...) for HMAC keys, sign and verify with HMAC, and digest with SHA-1, SHA-256, SHA-384 or SHA-512, each called directly on crypto.subtle";
|
||||
"the native WebCrypto surface is crypto.subtle's importKey, generateKey and exportKey for HMAC and AES keys (importKey also for PBKDF2 and HKDF keys), encrypt and decrypt with AES-GCM, AES-CBC or AES-CTR, deriveBits and deriveKey with PBKDF2 or HKDF, sign and verify with HMAC, and digest with SHA-1, SHA-256, SHA-384 or SHA-512, each called directly on crypto.subtle";
|
||||
|
||||
/** Whether `node` is `crypto.subtle` on the WebCrypto global. */
|
||||
/** Whether `node` is a SubtleCrypto object: `subtle` on a WebCrypto
|
||||
* Crypto object (the global, globalThis.crypto or node:crypto's
|
||||
* `webcrypto`) or node:crypto's own `subtle` export, named or through
|
||||
* the namespace. */
|
||||
function isSubtleReceiver(lowerer: Lowerer, node: ts.Expression): boolean {
|
||||
while (ts.isParenthesizedExpression(node)) node = node.expression;
|
||||
return (
|
||||
ts.isPropertyAccessExpression(node) &&
|
||||
!node.questionDotToken &&
|
||||
node.name.text === "subtle" &&
|
||||
stdlibGlobalNameOf(lowerer, node.expression) === "crypto"
|
||||
);
|
||||
if (ts.isIdentifier(node)) {
|
||||
const imported = lowerer.builtinImportOf(node);
|
||||
return imported?.module === "crypto" && imported.member === "subtle";
|
||||
}
|
||||
if (
|
||||
!ts.isPropertyAccessExpression(node) ||
|
||||
node.questionDotToken ||
|
||||
node.name.text !== "subtle"
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
if (stdlibGlobalNameOf(lowerer, node.expression) === "crypto") return true;
|
||||
if (isWebCryptoReceiver(lowerer, node.expression)) return true;
|
||||
const member = lowerer.builtinMemberOf(node);
|
||||
return member?.module === "crypto" && member.member === "subtle";
|
||||
}
|
||||
|
||||
/** `crypto.subtle.<method>(...args)`: the lowered methods become their
|
||||
@@ -51,17 +74,6 @@ export function lowerWebCryptoCall(lowerer: Lowerer, call: ts.CallExpression): I
|
||||
if (call.arguments.nodes.some(ts.isSpreadElement)) {
|
||||
lowerer.noLowering(`crypto.subtle.${member} with spread arguments`, call, SUBTLE_HINT);
|
||||
}
|
||||
// JsonWebKey imports are not implemented: a literal 'jwk' format is
|
||||
// refused here (the runtime refuses a computed one).
|
||||
const format = call.arguments.nodes[0];
|
||||
if (
|
||||
member === "importKey" &&
|
||||
format !== undefined &&
|
||||
ts.isStringLiteralLike(format) &&
|
||||
format.text === "jwk"
|
||||
) {
|
||||
lowerer.noLowering("crypto.subtle.importKey with the 'jwk' key format", format, SUBTLE_HINT);
|
||||
}
|
||||
const loc = locOf(call);
|
||||
const args = call.arguments.nodes.map((arg) =>
|
||||
ts.isObjectLiteralExpression(arg)
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import { keyedHelperName } from "./stable-names.js";
|
||||
import { forwardedTupleType, pureForwardedValues, stripTypeWrappers } from "./typed-arguments.js";
|
||||
import { dynUndefinedExpr } from "../../ir/build.js";
|
||||
import { InternalCompilerError } from "../../errors.js";
|
||||
@@ -754,7 +753,7 @@ function holeReadingLift(lowerer: Lowerer, from: IrType, to: IrType, loc: SrcLoc
|
||||
const key = `arr.holes:${typeKey(elem)}`;
|
||||
const existing = lowerer.valueHelpers.get(key);
|
||||
if (existing) return existing;
|
||||
const name = keyedHelperName(lowerer, "%arr.holes", key);
|
||||
const name = `%arr.holes.${lowerer.valueHelpers.size}`;
|
||||
lowerer.valueHelpers.set(key, name);
|
||||
lowerer.liftedFns.push(
|
||||
buildArrayConversion(
|
||||
|
||||
@@ -849,10 +849,7 @@ export function collectSignatureInner(lowerer: Lowerer, decl: ts.FunctionDeclara
|
||||
lowerer.genericFnsBySymbol.set(symbol, {
|
||||
decl,
|
||||
baseName: nameText,
|
||||
qualifiedName: lowerer.qualify(
|
||||
ts.getSourceFileOfNode(decl),
|
||||
nsPathPrefix(lowerer, decl) + nameText,
|
||||
),
|
||||
qualifiedName: lowerer.qualify(ts.getSourceFileOfNode(decl), nsPathPrefix(decl) + nameText),
|
||||
typeParams: [],
|
||||
instances: new Map(),
|
||||
implicitParams: implicit,
|
||||
@@ -900,7 +897,7 @@ export function collectSignatureInner(lowerer: Lowerer, decl: ts.FunctionDeclara
|
||||
// "%default" spelling ('%' cannot appear in a user identifier).
|
||||
name: lowerer.qualify(
|
||||
ts.getSourceFileOfNode(decl),
|
||||
nsPathPrefix(lowerer, decl) + (decl.name?.text ?? "%default"),
|
||||
nsPathPrefix(decl) + (decl.name?.text ?? "%default"),
|
||||
),
|
||||
params,
|
||||
returnType,
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import { keyedHelperName } from "./stable-names.js";
|
||||
import { nodeThrowExpr, varRef } from "../../ir/build.js";
|
||||
import {
|
||||
BOOL,
|
||||
@@ -123,7 +122,7 @@ export function checkedClassAssertion(
|
||||
const key = `classassert:${typeKey(source.type)}:${typeKey(target)}`;
|
||||
let name = lowerer.coercions.retags.get(key);
|
||||
if (!name) {
|
||||
name = keyedHelperName(lowerer, "%class.assert", key);
|
||||
name = `%class.assert.${lowerer.coercions.retags.size}`;
|
||||
lowerer.coercions.retags.set(key, name);
|
||||
const input = varRef("value.0", source.type, loc);
|
||||
const body: IrStmt[] = [];
|
||||
@@ -229,7 +228,7 @@ export function checkedClassUnionAssertion(
|
||||
const key = `classunionassert:${typeKey(value.type)}:${typeKey(target)}`;
|
||||
let name = lowerer.coercions.retags.get(key);
|
||||
if (!name) {
|
||||
name = keyedHelperName(lowerer, "%class.assert", key);
|
||||
name = `%class.assert.${lowerer.coercions.retags.size}`;
|
||||
lowerer.coercions.retags.set(key, name);
|
||||
const input = varRef("value.0", value.type, loc);
|
||||
const locals = [{ id: "value.0", name: "value", type: value.type, mutable: false }];
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import { keyedHelperName } from "./stable-names.js";
|
||||
import * as ts from "../ts7/adapter.js";
|
||||
import { BOOL, typeKey, type IrExpr, type IrStmt, type IrType } from "../../ir/ir.js";
|
||||
import { nodeThrowExpr, varRef } from "../../ir/build.js";
|
||||
@@ -133,7 +132,7 @@ export function narrowClassUnion(
|
||||
const key = `classUnion:${source.className}:${target.unionId}`;
|
||||
let helper = lowerer.valueHelpers.get(key);
|
||||
if (!helper) {
|
||||
helper = keyedHelperName(lowerer, "%class.union", key);
|
||||
helper = `%class.union.${lowerer.valueHelpers.size}`;
|
||||
lowerer.valueHelpers.set(key, helper);
|
||||
const input = varRef("value.0", source, loc);
|
||||
// Rank by ancestry before sorting: returning zero for unrelated pairs
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import { keyedHelperName } from "../stable-names.js";
|
||||
import { dynUndefinedExpr } from "../../../ir/build.js";
|
||||
import { buildFunctionAdapter } from "./builders.js";
|
||||
import { InternalCompilerError } from "../../../errors.js";
|
||||
@@ -34,7 +33,7 @@ export function funcReturnWidthAdapter(
|
||||
const key = `fn:${typeKey(fromT.ret.elem)}:${typeKey(toT.ret.elem)}`;
|
||||
const existing = lowerer.valueHelpers.get(key);
|
||||
if (existing) return existing;
|
||||
const name = keyedHelperName(lowerer, "%fn.width", key);
|
||||
const name = `%fn.width.${lowerer.valueHelpers.size}`;
|
||||
lowerer.valueHelpers.set(key, name);
|
||||
lowerer.freshClosureAdapters.add(name);
|
||||
// The returned closure's body: call the captured original, width-map.
|
||||
@@ -164,7 +163,7 @@ function restPackingAdapter(
|
||||
const key = `fnrestadapt:${typeKey(fromT)}:${typeKey(toT)}`;
|
||||
const existing = lowerer.coercions.retags.get(key);
|
||||
if (existing) return existing;
|
||||
const name = keyedHelperName(lowerer, "%fn.adapt", key);
|
||||
const name = `%fn.adapt.${lowerer.coercions.retags.size}`;
|
||||
lowerer.coercions.retags.set(key, name);
|
||||
lowerer.freshClosureAdapters.add(name);
|
||||
const fixed = fromT.params.length - 1;
|
||||
@@ -221,7 +220,7 @@ function funcIntoDynRestAdapter(
|
||||
const key = `fnadapt:${typeKey(fromT)}:${typeKey(toT)}`;
|
||||
const existing = lowerer.coercions.retags.get(key);
|
||||
if (existing) return existing;
|
||||
const name = keyedHelperName(lowerer, "%fn.adapt", key);
|
||||
const name = `%fn.adapt.${lowerer.coercions.retags.size}`;
|
||||
lowerer.coercions.retags.set(key, name);
|
||||
lowerer.freshClosureAdapters.add(name);
|
||||
const params: IrParam[] = [{ localId: "rest.0", name: "args", type: DYN }];
|
||||
@@ -377,7 +376,7 @@ export function funcCoerceAdapter(
|
||||
const key = `fnadapt:${typeKey(fromT)}:${typeKey(toT)}`;
|
||||
const existing = lowerer.coercions.retags.get(key);
|
||||
if (existing) return existing;
|
||||
const name = keyedHelperName(lowerer, "%fn.adapt", key);
|
||||
const name = `%fn.adapt.${lowerer.coercions.retags.size}`;
|
||||
lowerer.coercions.retags.set(key, name);
|
||||
lowerer.freshClosureAdapters.add(name);
|
||||
const params: IrParam[] = toT.params.map((t, i) => ({
|
||||
@@ -605,7 +604,7 @@ export function spawnResFnAdapter(
|
||||
const key = `fnspawn:${typeKey(fromT)}:${typeKey(toT)}`;
|
||||
const existing = lowerer.valueHelpers.get(key);
|
||||
if (existing) return existing;
|
||||
const name = keyedHelperName(lowerer, "%fnval.spawnres", key);
|
||||
const name = `%fnval.spawnres.${lowerer.valueHelpers.size}`;
|
||||
lowerer.valueHelpers.set(key, name);
|
||||
lowerer.freshClosureAdapters.add(name);
|
||||
const params: IrParam[] = toT.params.map((p, i) => ({
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import { keyedHelperName } from "../stable-names.js";
|
||||
import { constructedValue } from "../constructor-returns.js";
|
||||
import { dynUndefinedExpr, varRef } from "../../../ir/build.js";
|
||||
import type { FieldLift } from "./structural-plans.js";
|
||||
@@ -254,7 +253,7 @@ export function unionRecordWidthHelper(
|
||||
const key = `unionWidth:${fromId}:${target.shapeId}`;
|
||||
const existing = lowerer.valueHelpers.get(key);
|
||||
if (existing) return existing;
|
||||
const name = keyedHelperName(lowerer, "%union.record", key);
|
||||
const name = `%union.record.${lowerer.valueHelpers.size}`;
|
||||
lowerer.valueHelpers.set(key, name);
|
||||
const type: IrType = { kind: "union", unionId: fromId };
|
||||
const value = varRef("value.0", type, loc);
|
||||
@@ -313,7 +312,7 @@ export function recordWidthHelper(
|
||||
const key = `rec:${fromId}:${toId}`;
|
||||
const existing = lowerer.valueHelpers.get(key);
|
||||
if (existing) return existing;
|
||||
const name = keyedHelperName(lowerer, "%rec.width", key);
|
||||
const name = `%rec.width.${lowerer.valueHelpers.size}`;
|
||||
// Interned BEFORE the body builds: a recursive nested-width field
|
||||
// (self-referential shapes) resolves to this helper itself.
|
||||
lowerer.valueHelpers.set(key, name);
|
||||
@@ -342,7 +341,7 @@ export function tupleArrayWidthHelper(
|
||||
const key = `tuparr:${fromId}:${typeKey(toT.elem)}`;
|
||||
const existing = lowerer.valueHelpers.get(key);
|
||||
if (existing) return existing;
|
||||
const name = keyedHelperName(lowerer, "%tup.arr", key);
|
||||
const name = `%tup.arr.${lowerer.valueHelpers.size}`;
|
||||
lowerer.valueHelpers.set(key, name);
|
||||
const fromT: IrType = { kind: "record", shapeId: fromId };
|
||||
const t: IrExpr = { kind: "varRef", localId: "t.0", type: fromT, loc };
|
||||
@@ -389,7 +388,7 @@ export function emptyArrayLiftHelper(
|
||||
const key = `emptyarr:${typeKey(fromT.elem)}:${typeKey(toT.elem)}`;
|
||||
const existing = lowerer.valueHelpers.get(key);
|
||||
if (existing) return existing;
|
||||
const name = keyedHelperName(lowerer, "%arr.empty", key);
|
||||
const name = `%arr.empty.${lowerer.valueHelpers.size}`;
|
||||
lowerer.valueHelpers.set(key, name);
|
||||
const a: IrExpr = { kind: "varRef", localId: "a.0", type: fromT, loc };
|
||||
lowerer.liftedFns.push({
|
||||
@@ -453,7 +452,7 @@ export function arrayWidthHelper(
|
||||
const key = `arr:${typeKey(fromElem)}:${typeKey(toElem)}`;
|
||||
const existing = lowerer.valueHelpers.get(key);
|
||||
if (existing) return existing;
|
||||
const name = keyedHelperName(lowerer, "%arr.width", key);
|
||||
const name = `%arr.width.${lowerer.valueHelpers.size}`;
|
||||
lowerer.valueHelpers.set(key, name);
|
||||
const arrT: IrType = { kind: "array", elem: fromElem };
|
||||
const outT: IrType = { kind: "array", elem: toElem };
|
||||
@@ -493,7 +492,7 @@ export function objRecordWidthHelper(
|
||||
const key = `obj:${className}:${toId}`;
|
||||
const existing = lowerer.valueHelpers.get(key);
|
||||
if (existing) return existing;
|
||||
const name = keyedHelperName(lowerer, "%obj.width", key);
|
||||
const name = `%obj.width.${lowerer.valueHelpers.size}`;
|
||||
lowerer.valueHelpers.set(key, name);
|
||||
const fromT: IrType = { kind: "object", className };
|
||||
lowerer.liftedFns.push(buildRecordProjection(lowerer, name, fromT, to, plan, loc));
|
||||
@@ -518,7 +517,7 @@ export function recordClassWidthHelper(
|
||||
const key = `cls:${fromId}:${className}`;
|
||||
const existing = lowerer.valueHelpers.get(key);
|
||||
if (existing) return existing;
|
||||
const name = keyedHelperName(lowerer, "%cls.width", key);
|
||||
const name = `%cls.width.${lowerer.valueHelpers.size}`;
|
||||
lowerer.valueHelpers.set(key, name);
|
||||
lowerer.noteEdge(`%${className}.constructor`);
|
||||
const fromT: IrType = { kind: "record", shapeId: fromId };
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import { keyedHelperName } from "../stable-names.js";
|
||||
import { buildUnionNarrow, unionNarrowFailureMessage } from "../union-narrow.js";
|
||||
import { isSafeToRepeat } from "../expressions/evaluation-safety.js";
|
||||
import { nodeThrowExpr } from "../../../ir/build.js";
|
||||
@@ -148,7 +147,7 @@ export function strandedUnitTrap(
|
||||
const key = `strandunit:${typeKey(expected)}:${expr.type.kind}`;
|
||||
let name = lowerer.coercions.retags.get(key);
|
||||
if (!name) {
|
||||
name = keyedHelperName(lowerer, "%unit.strand", key);
|
||||
name = `%unit.strand.${lowerer.coercions.retags.size}`;
|
||||
lowerer.coercions.retags.set(key, name);
|
||||
lowerer.liftedFns.push({
|
||||
name,
|
||||
@@ -228,7 +227,7 @@ export function strandedCoercionTrap(
|
||||
const key = `strand:${expected.unionId}:${typeKey(src)}`;
|
||||
let name = lowerer.coercions.retags.get(key);
|
||||
if (!name) {
|
||||
name = keyedHelperName(lowerer, "%union.strand", key);
|
||||
name = `%union.strand.${lowerer.coercions.retags.size}`;
|
||||
lowerer.coercions.retags.set(key, name);
|
||||
const toT: IrType = { kind: "union", unionId: expected.unionId };
|
||||
lowerer.liftedFns.push({
|
||||
@@ -276,7 +275,7 @@ export function recordUnionWrapHelper(
|
||||
const key = `recordWrap:${source.shapeId}:${toId}`;
|
||||
const existing = lowerer.coercions.retags.get(key);
|
||||
if (existing) return existing;
|
||||
const name = keyedHelperName(lowerer, "%record.wrap", key);
|
||||
const name = `%record.wrap.${lowerer.coercions.retags.size}`;
|
||||
lowerer.coercions.retags.set(key, name);
|
||||
lowerer.liftedFns.push(
|
||||
buildRecordUnionWrap(name, source, to, plan, loc, (lift, value, dst) =>
|
||||
@@ -308,7 +307,7 @@ export function recordUnionRouteHelper(
|
||||
(from, dst) => lowerer.widthLiftPlan(from, dst),
|
||||
);
|
||||
if (!plan) return null;
|
||||
const name = keyedHelperName(lowerer, "%record.route", key);
|
||||
const name = `%record.route.${lowerer.coercions.retags.size}`;
|
||||
lowerer.coercions.retags.set(key, name);
|
||||
lowerer.liftedFns.push(
|
||||
buildRecordUnionWrap(name, source, to, plan, loc, (lift, value, dst) =>
|
||||
@@ -353,7 +352,7 @@ export function unionRetagHelper(
|
||||
});
|
||||
const key = `${fromId}:${toId}:${stranded.join(".")}`;
|
||||
const existing = lowerer.coercions.retags.get(key);
|
||||
const name = existing ?? keyedHelperName(lowerer, "%union.retag", key);
|
||||
const name = existing ?? `%union.retag.${lowerer.coercions.retags.size}`;
|
||||
if (!existing) {
|
||||
lowerer.coercions.retags.set(key, name);
|
||||
lowerer.liftedFns.push(
|
||||
@@ -411,7 +410,7 @@ export function narrowedArmHelper(
|
||||
lowerer.coercions.checkedNarrows.add(existing);
|
||||
return existing;
|
||||
}
|
||||
const name = keyedHelperName(lowerer, "%union.narrow", key);
|
||||
const name = `%union.narrow.${lowerer.coercions.narrows.size}`;
|
||||
lowerer.coercions.narrows.set(key, name);
|
||||
lowerer.coercions.checkedNarrows.add(name);
|
||||
const fn = buildUnionNarrow(name, from, target, loc, (type) => lowerer.fmt(type));
|
||||
@@ -588,7 +587,7 @@ export function deferredReadHelper(
|
||||
const key = `deferred:${fromId}:${tag}`;
|
||||
const existing = lowerer.coercions.narrows.get(key);
|
||||
if (existing) return existing;
|
||||
const name = keyedHelperName(lowerer, "%deferred.read", key);
|
||||
const name = `%deferred.read.${lowerer.coercions.narrows.size}`;
|
||||
lowerer.coercions.narrows.set(key, name);
|
||||
const dflt: IrExpr =
|
||||
target.kind === "bool"
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import { keyedHelperName } from "./stable-names.js";
|
||||
import * as ts from "../ts7/adapter.js";
|
||||
import { InternalCompilerError } from "../../errors.js";
|
||||
import { typeKey } from "../type-mapper.js";
|
||||
@@ -198,7 +197,7 @@ export function ingestCollection(
|
||||
const key = `ingest:${input.collection ? typeKey(input.collection) + ":" + input.selection : "cursor:" + typeKey(input.element)}:${typeKey(destination)}:${mapper ? typeKey(mapper.type) : "copy"}`;
|
||||
let name = lowerer.arrHofHelpers.get(key);
|
||||
if (!name) {
|
||||
name = keyedHelperName(lowerer, "%collection.ingest", key);
|
||||
name = `%collection.ingest.${lowerer.arrHofHelpers.size}`;
|
||||
lowerer.arrHofHelpers.set(key, name);
|
||||
const context = newFnCtx(false, null, null, destination);
|
||||
lowerer.fnStack.push(context);
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import { keyedHelperName } from "../stable-names.js";
|
||||
import { boolLit, countedFor, numLit, varRef } from "../../../ir/build.js";
|
||||
import { transformStmtList } from "../../../ir/traverse.js";
|
||||
import type { ScrDiagnostic } from "../../../diagnostics/diagnostic.js";
|
||||
@@ -507,7 +506,7 @@ function arrayHofHelper(
|
||||
const key = `${method}:${typeKey(elem)}:${typeKey(fnRet)}:${arity}:${typeKey(outElem)}${site}${fastT ? ":split" : ""}${present ? ":present" : ""}`;
|
||||
const existing = lowerer.arrHofHelpers.get(key);
|
||||
if (existing) return existing;
|
||||
const name = keyedHelperName(lowerer, `%arr.${method}`, key);
|
||||
const name = `%arr.${method}.${lowerer.arrHofHelpers.size}`;
|
||||
lowerer.arrHofHelpers.set(key, name);
|
||||
lowerer.arrHofInfo.set(name, { method, elem, fnRet, arity, outElem, present, split: !!fastT });
|
||||
const fn = buildArrayHofFn(
|
||||
@@ -620,7 +619,7 @@ function arrayUnionHofHelper(
|
||||
const key = `union:${method}:${receiverT.unionId}:${typeKey(callbackT)}:${typeKey(resultT)}`;
|
||||
const existing = lowerer.arrHofHelpers.get(key);
|
||||
if (existing) return existing;
|
||||
const name = keyedHelperName(lowerer, "%arr.union", key);
|
||||
const name = `%arr.union.${lowerer.arrHofHelpers.size}`;
|
||||
lowerer.arrHofHelpers.set(key, name);
|
||||
const receiver = varRef("a.0", receiverT, loc);
|
||||
const callback = varRef("f.0", callbackT, loc);
|
||||
@@ -1205,7 +1204,7 @@ function findHelper(
|
||||
const key = `${method}:${typeKey(elem)}:${typeKey(resultT)}:${typeKey(fnRet)}:${arity}${site}${present ? ":present" : ""}`;
|
||||
const existing = lowerer.arrHofHelpers.get(key);
|
||||
if (existing) return existing;
|
||||
const name = keyedHelperName(lowerer, `%arr.${method}`, key);
|
||||
const name = `%arr.${method}.${lowerer.arrHofHelpers.size}`;
|
||||
lowerer.arrHofHelpers.set(key, name);
|
||||
const arrT = arrayOf(elem);
|
||||
const valueT = callbackValueType(lowerer, elem, present);
|
||||
@@ -1305,7 +1304,7 @@ function findIndexHelper(
|
||||
const key = `${method}:${typeKey(elem)}:${typeKey(fnRet)}:${arity}${site}${fastT ? ":split" : ""}${present ? ":present" : ""}`;
|
||||
const existing = lowerer.arrHofHelpers.get(key);
|
||||
if (existing) return existing;
|
||||
const name = keyedHelperName(lowerer, `%arr.${method}`, key);
|
||||
const name = `%arr.${method}.${lowerer.arrHofHelpers.size}`;
|
||||
lowerer.arrHofHelpers.set(key, name);
|
||||
if (!last)
|
||||
lowerer.arrHofInfo.set(name, {
|
||||
@@ -1389,7 +1388,7 @@ function someEveryHelper(
|
||||
const key = `${method}:${typeKey(elem)}:${typeKey(fnRet)}:${arity}${site}${fastT ? ":split" : ""}${present ? ":present" : ""}`;
|
||||
const existing = lowerer.arrHofHelpers.get(key);
|
||||
if (existing) return existing;
|
||||
const name = keyedHelperName(lowerer, `%arr.${method}`, key);
|
||||
const name = `%arr.${method}.${lowerer.arrHofHelpers.size}`;
|
||||
lowerer.arrHofHelpers.set(key, name);
|
||||
lowerer.arrHofInfo.set(name, {
|
||||
method,
|
||||
@@ -1588,7 +1587,7 @@ function flatMapHelper(
|
||||
const key = `flatMap:${typeKey(elem)}:${typeKey(fnRet)}:${arity}${site}${fastT ? ":split" : ""}${present ? ":present" : ""}`;
|
||||
const existing = lowerer.arrHofHelpers.get(key);
|
||||
if (existing) return existing;
|
||||
const name = keyedHelperName(lowerer, "%arr.flatMap", key);
|
||||
const name = `%arr.flatMap.${lowerer.arrHofHelpers.size}`;
|
||||
lowerer.arrHofHelpers.set(key, name);
|
||||
lowerer.arrHofInfo.set(name, {
|
||||
method: "flatMapArray",
|
||||
@@ -1830,7 +1829,7 @@ function reduceHelper(
|
||||
const key = `${method}:${typeKey(elem)}:${typeKey(accT)}:${typeKey(fnRet)}:${arity}:${hasInit ? "init" : "seed"}${site}${fastT ? ":split" : ""}`;
|
||||
const existing = lowerer.arrHofHelpers.get(key);
|
||||
if (existing) return existing;
|
||||
const name = keyedHelperName(lowerer, `%arr.${method}`, key);
|
||||
const name = `%arr.${method}.${lowerer.arrHofHelpers.size}`;
|
||||
lowerer.arrHofHelpers.set(key, name);
|
||||
if (method === "reduce")
|
||||
lowerer.arrHofInfo.set(name, {
|
||||
@@ -2122,7 +2121,7 @@ export function lowerArraySortCall(
|
||||
const key = `${method}:${typeKey(elem)}:${arity}${native ? ":native" : ""}${keyed ? `:keyed${order}${reads ? `:field${reads.length}` : ""}` : ""}`;
|
||||
let helper = lowerer.arrHofHelpers.get(key);
|
||||
if (!helper) {
|
||||
helper = keyedHelperName(lowerer, `%arr.${method}`, key);
|
||||
helper = `%arr.${method}.${lowerer.arrHofHelpers.size}`;
|
||||
lowerer.arrHofHelpers.set(key, helper);
|
||||
const undefinedTag = elem.kind === "union" ? lowerer.armTag(elem.unionId, UNDEFINED_T) : -1;
|
||||
lowerer.liftedFns.push(
|
||||
@@ -2168,7 +2167,7 @@ function sortKeyReader(
|
||||
const cacheKey = `sortKey:${typeKey(elem)}:${sortKeysId(reads.map((read) => ({ order: 1, read })))}`;
|
||||
let reader = lowerer.arrHofHelpers.get(cacheKey);
|
||||
if (!reader) {
|
||||
reader = keyedHelperName(lowerer, "%arr.sortKey", cacheKey);
|
||||
reader = `%arr.sortKey.${lowerer.arrHofHelpers.size}`;
|
||||
lowerer.arrHofHelpers.set(cacheKey, reader);
|
||||
lowerer.liftedFns.push(buildSortKeyFn(reader, elem, reads, loc));
|
||||
}
|
||||
@@ -2309,7 +2308,7 @@ function dynFlatMapHelper(lowerer: Lowerer, elemT: IrType, arity: number, loc: S
|
||||
const key = `dynflatmap:${typeKey(elemT)}:${arity}`;
|
||||
const existing = lowerer.arrHofHelpers.get(key);
|
||||
if (existing) return existing;
|
||||
const name = keyedHelperName(lowerer, "%dyn.flatmap", key);
|
||||
const name = `%dyn.flatmap.${lowerer.arrHofHelpers.size}`;
|
||||
lowerer.arrHofHelpers.set(key, name);
|
||||
lowerer.liftedFns.push(buildDynFlatMapFn(name, elemT, arity, loc));
|
||||
return name;
|
||||
@@ -2482,7 +2481,7 @@ function dynFilterHelper(lowerer: Lowerer, elemT: IrType, arity: number, loc: Sr
|
||||
const key = `dynfilter:${typeKey(elemT)}:${arity}`;
|
||||
const existing = lowerer.arrHofHelpers.get(key);
|
||||
if (existing) return existing;
|
||||
const name = keyedHelperName(lowerer, "%dyn.filter", key);
|
||||
const name = `%dyn.filter.${lowerer.arrHofHelpers.size}`;
|
||||
lowerer.arrHofHelpers.set(key, name);
|
||||
lowerer.liftedFns.push(buildDynFilterFn(name, elemT, arity, loc));
|
||||
return name;
|
||||
@@ -2677,7 +2676,7 @@ export function lowerTuplePredicateCall(
|
||||
const key = `tuple.${method}:${typeKey(receiver.type)}:${typeKey(fnArg.type)}:${typeKey(resultT)}:${arity}${callbackSite(fnArg)}`;
|
||||
let helper = lowerer.arrHofHelpers.get(key);
|
||||
if (!helper) {
|
||||
helper = keyedHelperName(lowerer, `%tuple.${method}`, key);
|
||||
helper = `%tuple.${method}.${lowerer.arrHofHelpers.size}`;
|
||||
lowerer.arrHofHelpers.set(key, helper);
|
||||
const v = varRef("v.0", elem, loc);
|
||||
const out = varRef("out.0", resultT, loc);
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import { keyedHelperName } from "../stable-names.js";
|
||||
import { identityPreservingWidening } from "../coercions/identity.js";
|
||||
import { countedFor, numLit, varRef } from "../../../ir/build.js";
|
||||
import {
|
||||
@@ -29,7 +28,7 @@ export function arrayConcatHelper(
|
||||
const key = `concat:${typeKey(elem)}:${shape.map((type) => (type ? `a:${typeKey(type)}` : "e")).join(";")}`;
|
||||
const existing = lowerer.arrHofHelpers.get(key);
|
||||
if (existing) return existing;
|
||||
const name = keyedHelperName(lowerer, "%arr.concat", key);
|
||||
const name = `%arr.concat.${lowerer.arrHofHelpers.size}`;
|
||||
const arrT = arrayOf(elem);
|
||||
const params = [
|
||||
{ localId: "a.0", name: "a", type: arrT },
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import { keyedHelperName } from "../stable-names.js";
|
||||
import { generatorDrain } from "../iterator-adapters.js";
|
||||
import { identityPreservingWidening } from "../coercions/identity.js";
|
||||
import { liftedFreshSpread } from "../coercions/fresh-spread.js";
|
||||
@@ -257,7 +256,7 @@ function appendWidenedArrayValues(
|
||||
const key = `insertValues:${typeKey(sourceType.elem)}:${typeKey(elem)}`;
|
||||
let name = lowerer.arrHofHelpers.get(key);
|
||||
if (!name) {
|
||||
name = keyedHelperName(lowerer, "%arr.insertValues", key);
|
||||
name = `%arr.insertValues.${lowerer.arrHofHelpers.size}`;
|
||||
lowerer.arrHofHelpers.set(key, name);
|
||||
const outType = arrayOf(elem);
|
||||
const valueType = arrayValueType(lowerer, source.type.elem);
|
||||
@@ -1036,7 +1035,7 @@ export function lowerArrayFromCall(
|
||||
const key = `fromArray:${typeKey(arrT.elem)}:${typeKey(outElem)}:${typeKey(fnRet)}:${arity}`;
|
||||
let helper = lowerer.arrHofHelpers.get(key);
|
||||
if (!helper) {
|
||||
helper = keyedHelperName(lowerer, "%arr.fromArray", key);
|
||||
helper = `%arr.fromArray.${lowerer.arrHofHelpers.size}`;
|
||||
lowerer.arrHofHelpers.set(key, helper);
|
||||
lowerer.liftedFns.push(
|
||||
buildArrayFromArrayFn(lowerer, helper, arrT.elem, outElem, fnRet, arity, loc),
|
||||
@@ -1123,7 +1122,7 @@ export function lowerArrayFromCall(
|
||||
const key = `fromLen:${typeKey(fnRet)}:${arity}`;
|
||||
let helper = lowerer.arrHofHelpers.get(key);
|
||||
if (!helper) {
|
||||
helper = keyedHelperName(lowerer, "%arr.fromLen", key);
|
||||
helper = `%arr.fromLen.${lowerer.arrHofHelpers.size}`;
|
||||
lowerer.arrHofHelpers.set(key, helper);
|
||||
lowerer.liftedFns.push(buildArrayFromLenFn(helper, fnRet, arity, loc));
|
||||
}
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import { keyedHelperName } from "../stable-names.js";
|
||||
import { liftedFreshSpread } from "../coercions/fresh-spread.js";
|
||||
import { lowerArrayIteratorValue } from "../iterator-adapters.js";
|
||||
import { identityPreservingWidening } from "../coercions/identity.js";
|
||||
@@ -1617,7 +1616,7 @@ function arraySearchHelper(
|
||||
const key = `${method}:${typeKey(elem)}:${typeKey(valueT)}:${typeKey(needleT)}:${directNeedle}:${typeKey(fromT)}`;
|
||||
const existing = lowerer.arrHofHelpers.get(key);
|
||||
if (existing) return existing;
|
||||
const name = keyedHelperName(lowerer, `%arr.${method}`, key);
|
||||
const name = `%arr.${method}.${lowerer.arrHofHelpers.size}`;
|
||||
lowerer.arrHofHelpers.set(key, name);
|
||||
const arrT = arrayOf(elem);
|
||||
const resultT = method === "includes" ? BOOL : F64;
|
||||
@@ -1973,7 +1972,7 @@ export function lowerTupleReadMethodCall(
|
||||
let helper = lowerer.arrHofHelpers.get(key);
|
||||
const resultType = method === "includes" ? BOOL : F64;
|
||||
if (!helper) {
|
||||
helper = keyedHelperName(lowerer, `%tuple.${method}`, key);
|
||||
helper = `%tuple.${method}.${lowerer.arrHofHelpers.size}`;
|
||||
lowerer.arrHofHelpers.set(key, helper);
|
||||
const params = [
|
||||
{ localId: "a.0", name: "a", type: receiver.type },
|
||||
@@ -2035,7 +2034,7 @@ export function lowerTupleReadMethodCall(
|
||||
const key = `tuple.slice:${typeKey(receiver.type)}:${args.length}`;
|
||||
let helper = lowerer.arrHofHelpers.get(key);
|
||||
if (!helper) {
|
||||
helper = keyedHelperName(lowerer, "%tuple.slice", key);
|
||||
helper = `%tuple.slice.${lowerer.arrHofHelpers.size}`;
|
||||
lowerer.arrHofHelpers.set(key, helper);
|
||||
const params = [receiver, ...args].map((arg, i) => ({
|
||||
localId: i === 0 ? "a.0" : `arg.${i}`,
|
||||
@@ -2126,7 +2125,7 @@ export function lowerTupleReadMethodCall(
|
||||
const key = `tuple.${method}:${typeKey(receiver.type)}:${typeKey(fnArg.type)}:${typeKey(outElem)}`;
|
||||
let helper = lowerer.arrHofHelpers.get(key);
|
||||
if (!helper) {
|
||||
helper = keyedHelperName(lowerer, `%tuple.${method}`, key);
|
||||
helper = `%tuple.${method}.${lowerer.arrHofHelpers.size}`;
|
||||
lowerer.arrHofHelpers.set(key, helper);
|
||||
const out = varRef("out.0", outType, loc);
|
||||
const result = varRef("r.0", ret, loc);
|
||||
@@ -2254,7 +2253,7 @@ function lowerArrayAtCall(
|
||||
const key = `at:${typeKey(elem)}:${typeKey(resultT)}:${typeKey(index.type)}`;
|
||||
let name = lowerer.arrHofHelpers.get(key);
|
||||
if (!name) {
|
||||
name = keyedHelperName(lowerer, "%arr.at", key);
|
||||
name = `%arr.at.${lowerer.arrHofHelpers.size}`;
|
||||
lowerer.arrHofHelpers.set(key, name);
|
||||
const arrT = arrayOf(elem);
|
||||
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import { keyedHelperName } from "../stable-names.js";
|
||||
import { boolLit, countedFor, numLit, varRef } from "../../../ir/build.js";
|
||||
import {
|
||||
BOOL,
|
||||
@@ -852,7 +851,7 @@ class PipelineFusion {
|
||||
if (!built) return null;
|
||||
let name = this.helpers.get(built.key);
|
||||
if (!name) {
|
||||
name = keyedHelperName(this.lowerer, "%arr.fused", built.key);
|
||||
name = `%arr.fused.${this.helpers.size}`;
|
||||
this.helpers.set(built.key, name);
|
||||
this.added.push({ ...built.fn, name });
|
||||
}
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import { keyedHelperName } from "../stable-names.js";
|
||||
import { boolLit, dynUndefinedExpr, numLit, varRef } from "../../../ir/build.js";
|
||||
import { BUF_NUM_METHODS } from "./buffer-numeric-methods.js";
|
||||
import * as ts from "../../ts7/adapter.js";
|
||||
@@ -78,7 +77,7 @@ function lowerBytesSortCall(
|
||||
const cacheKey = `bytes.${method}:${bytesT.elem}:${key}`;
|
||||
let helper = lowerer.arrHofHelpers.get(cacheKey);
|
||||
if (!helper) {
|
||||
helper = keyedHelperName(lowerer, `%bytes.${method}`, cacheKey);
|
||||
helper = `%bytes.${method}.${lowerer.arrHofHelpers.size}`;
|
||||
lowerer.arrHofHelpers.set(cacheKey, helper);
|
||||
lowerer.liftedFns.push(
|
||||
buildBytesSortFn(helper, bytesT, arity, hasComparator, copyFirst, order, loc),
|
||||
@@ -271,7 +270,7 @@ function bytesCallbackHelper(
|
||||
const key = `bytes.${method}:${bytesT.elem}:${typeKey(resultT)}:${typeKey(fnRet)}:${arity}:${hasInit ? "init" : ""}`;
|
||||
const existing = lowerer.arrHofHelpers.get(key);
|
||||
if (existing) return existing;
|
||||
const name = keyedHelperName(lowerer, `%bytes.${method}`, key);
|
||||
const name = `%bytes.${method}.${lowerer.arrHofHelpers.size}`;
|
||||
lowerer.arrHofHelpers.set(key, name);
|
||||
const reduce = method === "reduce" || method === "reduceRight";
|
||||
const backward = method === "reduceRight" || method === "findLast" || method === "findLastIndex";
|
||||
@@ -1753,8 +1752,9 @@ function knownBufferProducer(
|
||||
|
||||
/** Buffer.from over a checked-dynamic source and encoding, both already in
|
||||
* repeatable locals: Node's fromString takes utf8 for an absent, non-string
|
||||
* or empty encoding and throws ERR_UNKNOWN_ENCODING for an unknown name;
|
||||
* other sources ignore the encoding. */
|
||||
* or empty encoding and throws ERR_UNKNOWN_ENCODING for an unknown name; an
|
||||
* ArrayBuffer takes the second argument as its byte offset; other sources
|
||||
* ignore it. */
|
||||
function dynBufferFromParts(source: IrExpr, encoding: IrExpr, loc: SrcLoc): IrExpr {
|
||||
const utf8: IrExpr = { kind: "strLit", value: "utf8", type: STRING, loc };
|
||||
const isString = (x: IrExpr): IrExpr => ({
|
||||
@@ -1796,10 +1796,12 @@ function dynBufferFromParts(source: IrExpr, encoding: IrExpr, loc: SrcLoc): IrEx
|
||||
type: BYTES_U8,
|
||||
loc,
|
||||
},
|
||||
// An ArrayBuffer takes the second argument as its byte offset
|
||||
// (scr_buffer_from_dyn_offset_pooled); other values convert with utf8.
|
||||
else_: {
|
||||
kind: "libCall",
|
||||
fn: "buffer.poolFromDynRef",
|
||||
args: [source, utf8],
|
||||
fn: "buffer.poolFromDynOffset",
|
||||
args: [source, encoding],
|
||||
type: BYTES_U8,
|
||||
loc,
|
||||
},
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import { keyedHelperName } from "../stable-names.js";
|
||||
import * as ts from "../../ts7/adapter.js";
|
||||
import type { Lowerer } from "../lowerer.js";
|
||||
import { isJsSourceFile, locOf } from "../../program.js";
|
||||
@@ -286,7 +285,7 @@ export function truthyFilterCallback(
|
||||
const key = `filterBoolean:${typeKey(valueT)}`;
|
||||
let name = lowerer.arrHofHelpers.get(key);
|
||||
if (!name) {
|
||||
name = keyedHelperName(lowerer, "%arr.filterBoolean", key);
|
||||
name = `%arr.filterBoolean.${lowerer.arrHofHelpers.size}`;
|
||||
lowerer.arrHofHelpers.set(key, name);
|
||||
const value = varRef("v.0", valueT, loc);
|
||||
lowerer.liftedFns.push({
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import { keyedHelperName } from "../stable-names.js";
|
||||
import { countedFor, numLit, varRef } from "../../../ir/build.js";
|
||||
import * as ts from "../../ts7/adapter.js";
|
||||
import type { Lowerer } from "../lowerer.js";
|
||||
@@ -105,7 +104,7 @@ export function lowerGroupByStaticCall(
|
||||
const key = `map.groupBy:${typeKey(items.type)}:${typeKey(f.type)}`;
|
||||
let helper = lowerer.mapHofHelpers.get(key);
|
||||
if (!helper) {
|
||||
helper = keyedHelperName(lowerer, "%map.groupBy", key);
|
||||
helper = `%map.groupBy.${lowerer.mapHofHelpers.size}`;
|
||||
lowerer.mapHofHelpers.set(key, helper);
|
||||
lowerer.liftedFns.push(buildGroupByFn(lowerer, helper, items.type, f.type, resultT, loc));
|
||||
}
|
||||
@@ -142,7 +141,7 @@ export function lowerGroupByStaticCall(
|
||||
const key = `object.groupBy:${typeKey(items.type)}:${typeKey(f.type)}`;
|
||||
let helper = lowerer.mapHofHelpers.get(key);
|
||||
if (!helper) {
|
||||
helper = keyedHelperName(lowerer, "%object.groupBy", key);
|
||||
helper = `%object.groupBy.${lowerer.mapHofHelpers.size}`;
|
||||
lowerer.mapHofHelpers.set(key, helper);
|
||||
lowerer.liftedFns.push(buildGroupByFn(lowerer, helper, items.type, f.type, resultT, loc));
|
||||
}
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import { keyedHelperName } from "../stable-names.js";
|
||||
import { nodeThrowExpr, boolLit, countedFor, numLit, strLit, varRef } from "../../../ir/build.js";
|
||||
import { InternalCompilerError } from "../../../errors.js";
|
||||
import * as ts from "../../ts7/adapter.js";
|
||||
@@ -233,7 +232,7 @@ function lowerMapIterDrainCall(
|
||||
const key = `${method}:${typeKey(mapT.key)}:${typeKey(mapT.value)}`;
|
||||
let helper = lowerer.mapHofHelpers.get(key);
|
||||
if (!helper) {
|
||||
helper = keyedHelperName(lowerer, `%map.${method}`, key);
|
||||
helper = `%map.${method}.${lowerer.mapHofHelpers.size}`;
|
||||
lowerer.mapHofHelpers.set(key, helper);
|
||||
lowerer.liftedFns.push(buildMapIterDrainFn(helper, mapT, method, elemT, tupleT, loc));
|
||||
}
|
||||
@@ -349,7 +348,7 @@ export function mapEntriesArray(lowerer: Lowerer, receiver: IrExpr): IrExpr {
|
||||
const key = `entries:${typeKey(mapT.key)}:${typeKey(mapT.value)}`;
|
||||
let helper = lowerer.mapHofHelpers.get(key);
|
||||
if (!helper) {
|
||||
helper = keyedHelperName(lowerer, "%map.entries", key);
|
||||
helper = `%map.entries.${lowerer.mapHofHelpers.size}`;
|
||||
lowerer.mapHofHelpers.set(key, helper);
|
||||
lowerer.liftedFns.push(
|
||||
buildMapIterDrainFn(helper, mapT, "entries", tupleT, tupleT, receiver.loc),
|
||||
@@ -520,7 +519,7 @@ export function lowerMapForEachCall(
|
||||
const key = `${typeKey(mapT.key)}:${typeKey(mapT.value)}:${arity}:${typeKey(fnRet)}`;
|
||||
let helper = lowerer.mapHofHelpers.get(key);
|
||||
if (!helper) {
|
||||
helper = keyedHelperName(lowerer, "%map.forEach", key);
|
||||
helper = `%map.forEach.${lowerer.mapHofHelpers.size}`;
|
||||
lowerer.mapHofHelpers.set(key, helper);
|
||||
lowerer.liftedFns.push(buildCollectionForEachFn(helper, mapT, arity, fnRet, loc));
|
||||
}
|
||||
@@ -694,7 +693,7 @@ export function lowerSetForEachCall(
|
||||
const key = `${typeKey(setT.elem)}:${arity}:${typeKey(fnRet)}`;
|
||||
let helper = lowerer.setHofHelpers.get(key);
|
||||
if (!helper) {
|
||||
helper = keyedHelperName(lowerer, "%set.forEach", key);
|
||||
helper = `%set.forEach.${lowerer.setHofHelpers.size}`;
|
||||
lowerer.setHofHelpers.set(key, helper);
|
||||
lowerer.liftedFns.push(buildCollectionForEachFn(helper, setT, arity, fnRet, loc));
|
||||
}
|
||||
@@ -746,7 +745,7 @@ function lowerSetCombineCall(
|
||||
const key = `${name}:${typeKey(setT.elem)}:${typeKey(otherT)}`;
|
||||
let helper = lowerer.setHofHelpers.get(key);
|
||||
if (!helper) {
|
||||
helper = keyedHelperName(lowerer, `%set.${name}`, key);
|
||||
helper = `%set.${name}.${lowerer.setHofHelpers.size}`;
|
||||
lowerer.setHofHelpers.set(key, helper);
|
||||
lowerer.liftedFns.push(buildSetCombineFn(helper, name, setT, otherT, loc));
|
||||
}
|
||||
@@ -1358,7 +1357,7 @@ export function mapFromSeedValue(
|
||||
const key = `checked-seed:${typeKey(mapT)}`;
|
||||
let name = lowerer.mapHofHelpers.get(key);
|
||||
if (!name) {
|
||||
name = keyedHelperName(lowerer, "%map.checkedSeed", key);
|
||||
name = `%map.checkedSeed.${lowerer.mapHofHelpers.size}`;
|
||||
lowerer.mapHofHelpers.set(key, name);
|
||||
const source = varRef("source.0", DYN, loc);
|
||||
const entries = varRef("entries.0", DYN, loc);
|
||||
@@ -1458,7 +1457,7 @@ export function mapFromSeedValue(
|
||||
const key = `seed:${typeKey(mapT.key)}:${typeKey(mapT.value)}:${elem.shapeId}`;
|
||||
let helper = lowerer.mapHofHelpers.get(key);
|
||||
if (!helper) {
|
||||
helper = keyedHelperName(lowerer, "%map.seed", key);
|
||||
helper = `%map.seed.${lowerer.mapHofHelpers.size}`;
|
||||
lowerer.mapHofHelpers.set(key, helper);
|
||||
lowerer.liftedFns.push(buildMapSeedFn(mapT, helper, seed.type, elem, kT, vT, loc));
|
||||
}
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import { keyedHelperName } from "../stable-names.js";
|
||||
import { nullProtoRecordShape, recordNullProtoTest } from "../null-prototype.js";
|
||||
import { countedFor, numLit, varRef } from "../../../ir/build.js";
|
||||
import { objectEnumerationReceiver } from "../object-enumeration-receiver.js";
|
||||
@@ -185,7 +184,7 @@ export function objectIterOverIndexShape(
|
||||
const key = `obj.${member}:ovf:${argIr.shapeId}:${typeKey(resultT)}`;
|
||||
let helper = lowerer.arrHofHelpers.get(key);
|
||||
if (!helper) {
|
||||
helper = keyedHelperName(lowerer, `%obj.${member}`, key);
|
||||
helper = `%obj.${member}.${lowerer.arrHofHelpers.size}`;
|
||||
|
||||
const outRef = varRef("out.0", resultT, loc);
|
||||
const rRef = varRef("r.0", argIr, loc);
|
||||
@@ -609,7 +608,7 @@ function lowerTypedObjectFromEntriesCall(
|
||||
const key = `obj.fromEntries:${argIr.elem.shapeId}:${resultT.shapeId}`;
|
||||
let helper = lowerer.arrHofHelpers.get(key);
|
||||
if (!helper) {
|
||||
helper = keyedHelperName(lowerer, "%obj.fromEntries", key);
|
||||
helper = `%obj.fromEntries.${lowerer.arrHofHelpers.size}`;
|
||||
|
||||
const tupleT = argIr.elem as IrType & { kind: "record" };
|
||||
const tRef = varRef("t.0", tupleT, loc);
|
||||
@@ -713,7 +712,7 @@ function lowerFromEntriesStringRows(
|
||||
const key = `obj.fromEntries:strrows:${resultT.shapeId}`;
|
||||
let helper = lowerer.arrHofHelpers.get(key);
|
||||
if (!helper) {
|
||||
helper = keyedHelperName(lowerer, "%obj.fromEntries", key);
|
||||
helper = `%obj.fromEntries.${lowerer.arrHofHelpers.size}`;
|
||||
|
||||
const tRef = varRef("t.0", rowT, loc);
|
||||
const rowLen: IrExpr = {
|
||||
@@ -932,7 +931,7 @@ export function lowerRecordOvfCaptureHelper(
|
||||
const key = `ovf:${fromId}:${toId}`;
|
||||
const existing = lowerer.valueHelpers.get(key);
|
||||
if (existing) return existing;
|
||||
const name = keyedHelperName(lowerer, "%rec.capture", key);
|
||||
const name = `%rec.capture.${lowerer.valueHelpers.size}`;
|
||||
lowerer.valueHelpers.set(key, name);
|
||||
const fromT: IrType = { kind: "record", shapeId: fromId };
|
||||
const toT: IrType = { kind: "record", shapeId: toId };
|
||||
@@ -1205,7 +1204,7 @@ export function lowerObjectAssignIndexShape(
|
||||
const key = `assign:${targetIr.shapeId}:${plan.fromId}`;
|
||||
const existing = lowerer.valueHelpers.get(key);
|
||||
if (existing) return existing;
|
||||
const name = keyedHelperName(lowerer, "%obj.assign", key);
|
||||
const name = `%obj.assign.${lowerer.valueHelpers.size}`;
|
||||
lowerer.valueHelpers.set(key, name);
|
||||
const toT: IrType = { kind: "record", shapeId: targetIr.shapeId };
|
||||
const fromT: IrType = { kind: "record", shapeId: plan.fromId };
|
||||
@@ -1475,7 +1474,7 @@ export function lowerIndexMergeHelper(
|
||||
.join(",");
|
||||
const existing = lowerer.valueHelpers.get(key);
|
||||
if (existing) return existing;
|
||||
const name = keyedHelperName(lowerer, "%rec.merge", key);
|
||||
const name = `%rec.merge.${lowerer.valueHelpers.size}`;
|
||||
lowerer.valueHelpers.set(key, name);
|
||||
const toT: IrType = { kind: "record", shapeId: toId };
|
||||
const ksT = arrayOf(STRING);
|
||||
@@ -1806,7 +1805,7 @@ export function lowerEnvToPairsHelper(
|
||||
const key = `env.pairs:${shapeId}`;
|
||||
const existing = lowerer.valueHelpers.get(key);
|
||||
if (existing) return existing;
|
||||
const name = keyedHelperName(lowerer, "%env.pairs", key);
|
||||
const name = `%env.pairs.${lowerer.valueHelpers.size}`;
|
||||
lowerer.valueHelpers.set(key, name);
|
||||
const recT: IrType = { kind: "record", shapeId };
|
||||
const arrT = arrayOf(STRING);
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import { keyedHelperName } from "../stable-names.js";
|
||||
import { boolLit, numLit, varRef } from "../../../ir/build.js";
|
||||
import { InternalCompilerError } from "../../../errors.js";
|
||||
import * as ts from "../../ts7/adapter.js";
|
||||
@@ -967,7 +966,7 @@ function lowerFusedChain(
|
||||
const key = `iter:${srcKey}:${stageKeys.join(",")}:${terminal}:${termArgs.map((a) => typeKey(a.type)).join(",")}`;
|
||||
let helper = lowerer.arrHofHelpers.get(key);
|
||||
if (!helper) {
|
||||
helper = keyedHelperName(lowerer, `%iter.${terminal}`, key);
|
||||
helper = `%iter.${terminal}.${lowerer.arrHofHelpers.size}`;
|
||||
lowerer.arrHofHelpers.set(key, helper);
|
||||
lowerer.liftedFns.push(
|
||||
buildIterChainFn(
|
||||
@@ -1039,7 +1038,7 @@ function lowerHelperObject(
|
||||
const key = `iterHelper:${stage.kind}:${srcKey}:${typeKey(srcElem)}:${typeKey(arg.type)}:${typeKey(outT)}`;
|
||||
let name = lowerer.arrHofHelpers.get(key);
|
||||
if (!name) {
|
||||
name = keyedHelperName(lowerer, `%iter.${stage.kind}`, key);
|
||||
name = `%iter.${stage.kind}.${lowerer.arrHofHelpers.size}`;
|
||||
lowerer.arrHofHelpers.set(key, name);
|
||||
lowerer.liftedFns.push(
|
||||
buildHelperGenFn(lowerer, name, source, srcElem, stage, arg.type, outT, resultType, loc),
|
||||
@@ -1059,7 +1058,7 @@ function internBudgetChecker(lowerer: Lowerer, loc: SrcLoc, srcT: GenType | null
|
||||
const key = srcT === null ? "iter:budget" : `iter:budget:close:${typeKey(srcT)}`;
|
||||
let name = lowerer.arrHofHelpers.get(key);
|
||||
if (name !== undefined) return name;
|
||||
name = srcT === null ? "%iter.budget" : keyedHelperName(lowerer, "%iter.budget.close", key);
|
||||
name = srcT === null ? "%iter.budget" : `%iter.budget.close.${lowerer.arrHofHelpers.size}`;
|
||||
lowerer.arrHofHelpers.set(key, name);
|
||||
const v = (): IrExpr => ({ kind: "varRef", localId: "v.0", type: F64, loc });
|
||||
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import { keyedHelperName } from "../stable-names.js";
|
||||
import { matchAllRowsReadByElements } from "./match-row-uses.js";
|
||||
import {
|
||||
boolLit,
|
||||
@@ -160,7 +159,7 @@ export function lowerStringSplitCall(
|
||||
const key = `str.split:${receiver.type.kind === "union" ? typeKey(receiver.type) : receiver.type.kind}:${typeKey(separator.type)}:${typeKey(limit.type)}:${absent}`;
|
||||
let helper = lowerer.valueHelpers.get(key);
|
||||
if (!helper) {
|
||||
helper = keyedHelperName(lowerer, "%str.split", key);
|
||||
helper = `%str.split.${lowerer.valueHelpers.size}`;
|
||||
const values = [receiver, separator, limit];
|
||||
const params = values.map((value, index) => ({
|
||||
localId: `arg.${index}`,
|
||||
@@ -396,7 +395,7 @@ function stringIndexHelperCall(
|
||||
const key = `str.index:${method}:${typeKey(receiver.type)}:${typeKey(index.type)}`;
|
||||
let helper = lowerer.valueHelpers.get(key);
|
||||
if (!helper) {
|
||||
helper = keyedHelperName(lowerer, "%str.index", key);
|
||||
helper = `%str.index.${lowerer.valueHelpers.size}`;
|
||||
lowerer.valueHelpers.set(key, helper);
|
||||
const rawReceiver = varRef("arg.0", receiver.type, loc);
|
||||
const rawIndex = varRef("arg.1", index.type, loc);
|
||||
@@ -674,7 +673,7 @@ export function lowerStringPaddingCall(
|
||||
const key = `str.pad:${method}:${values.map((value) => typeKey(value.type)).join(":")}`;
|
||||
let helper = lowerer.valueHelpers.get(key);
|
||||
if (!helper) {
|
||||
helper = keyedHelperName(lowerer, "%str.pad", key);
|
||||
helper = `%str.pad.${lowerer.valueHelpers.size}`;
|
||||
const params = values.map((value, index) => ({
|
||||
localId: `arg.${index}`,
|
||||
name: `arg${index}`,
|
||||
@@ -920,12 +919,12 @@ export function lowerRegexMethodCall(
|
||||
})();
|
||||
if ((receiverKind === "string" || nullableStringRecv) && name === "match") {
|
||||
const arg0 = call.arguments.nodes[0];
|
||||
if (!arg0 || lowerer.mapTypeOf(lowerer.typeOf(arg0))?.kind !== "regex") return null; // string-pattern match: the SC2020 fence
|
||||
if (!arg0 || !isRegexPatternArgument(lowerer, arg0)) return null; // string-pattern match: the SC2020 fence
|
||||
if (call.arguments.nodes.length !== 1) return null;
|
||||
const receiver = nullableStringRecv
|
||||
? lowerMethodReceiver(lowerer, access.expression, STRING, access.name.text)
|
||||
: lowerReceiver();
|
||||
const re = lowerer.lowerExprExpecting(arg0, { kind: "regex" });
|
||||
const re = lowerRegexPatternArgument(lowerer, arg0);
|
||||
// RegExpMatchArray | null maps to the string[] | null union by
|
||||
// itself; intern it directly when the checker's spelling doesn't —
|
||||
// or when it maps to something WIDER (an optional-chain call node
|
||||
@@ -983,10 +982,10 @@ export function lowerRegexMethodCall(
|
||||
// 0, exactly Node. Never throws.
|
||||
if (receiverKind === "string" && name === "search") {
|
||||
const arg0 = call.arguments.nodes[0];
|
||||
if (!arg0 || lowerer.mapTypeOf(lowerer.typeOf(arg0))?.kind !== "regex") return null; // string-pattern form: the SC2020 fence
|
||||
if (!arg0 || !isRegexPatternArgument(lowerer, arg0)) return null; // string-pattern form: the SC2020 fence
|
||||
if (call.arguments.nodes.length !== 1) return null;
|
||||
const receiver = lowerReceiver();
|
||||
const re = lowerer.lowerExprExpecting(arg0, { kind: "regex" });
|
||||
const re = lowerRegexPatternArgument(lowerer, arg0);
|
||||
return { kind: "regexIntrinsic", method: "search", receiver, args: [re], type: F64, loc };
|
||||
}
|
||||
if (receiverKind === "string" && (name === "replace" || name === "replaceAll")) {
|
||||
@@ -1310,7 +1309,7 @@ export function lowerStringMethodCall(
|
||||
const key = `str.positions:${entry.method}:${typeKey(needle.type)}:${typeKey(position.type)}`;
|
||||
let helper = lowerer.valueHelpers.get(key);
|
||||
if (!helper) {
|
||||
helper = keyedHelperName(lowerer, "%str.positions", key);
|
||||
helper = `%str.positions.${lowerer.valueHelpers.size}`;
|
||||
const params = [receiver, needle, position].map((arg, index) => ({
|
||||
localId: `arg.${index}`,
|
||||
name: `arg${index}`,
|
||||
@@ -1412,7 +1411,7 @@ export function lowerStringMethodCall(
|
||||
const key = `str.positions:${entry.method}:${args.map((arg) => typeKey(arg.type)).join(":")}`;
|
||||
let helper = lowerer.valueHelpers.get(key);
|
||||
if (!helper) {
|
||||
helper = keyedHelperName(lowerer, "%str.positions", key);
|
||||
helper = `%str.positions.${lowerer.valueHelpers.size}`;
|
||||
const params = [receiver, ...args].map((arg, index) => ({
|
||||
localId: `arg.${index}`,
|
||||
name: `arg${index}`,
|
||||
@@ -1921,3 +1920,31 @@ export function lowerDynPrimitiveMethodCall(
|
||||
);
|
||||
return seq(stmts, result);
|
||||
}
|
||||
|
||||
/** Whether the pattern of `match` or `search` is a RegExp, or a RegExp that
|
||||
* may be undefined (`s.match(patterns[i])` under noUncheckedIndexedAccess). */
|
||||
function isRegexPatternArgument(lowerer: Lowerer, node: ts.Expression): boolean {
|
||||
const t = lowerer.mapTypeOf(lowerer.typeOf(node));
|
||||
if (t?.kind === "regex") return true;
|
||||
if (t?.kind !== "union") return false;
|
||||
const arms = lowerer.unions.get(t.unionId)?.arms ?? [];
|
||||
return (
|
||||
arms.length === 2 &&
|
||||
arms.some((arm) => arm.kind === "regex") &&
|
||||
arms.some((arm) => arm.kind === "undefinedT")
|
||||
);
|
||||
}
|
||||
|
||||
/** The pattern of `match` or `search` as a RegExp: an undefined pattern is
|
||||
* `new RegExp(undefined)`, the empty pattern, as in Node. */
|
||||
function lowerRegexPatternArgument(lowerer: Lowerer, node: ts.Expression): IrExpr {
|
||||
const regex: IrType = { kind: "regex" };
|
||||
const empty: IrExpr = {
|
||||
kind: "regexLit",
|
||||
pattern: "(?:)",
|
||||
flags: "",
|
||||
type: regex,
|
||||
loc: locOf(node),
|
||||
};
|
||||
return lowerOptionalArgument(lowerer, node, regex, empty);
|
||||
}
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import { keyedHelperName } from "../stable-names.js";
|
||||
import * as ts from "../../ts7/adapter.js";
|
||||
import {
|
||||
BOOL,
|
||||
@@ -163,7 +162,7 @@ function lowerStringReplacementValues(
|
||||
const key = `str.${method}:${typeKey(replacement.type)}`;
|
||||
let helper = lowerer.valueHelpers.get(key);
|
||||
if (!helper) {
|
||||
helper = keyedHelperName(lowerer, `%str.${method}`, key);
|
||||
helper = `%str.${method}.${lowerer.valueHelpers.size}`;
|
||||
lowerer.valueHelpers.set(key, helper);
|
||||
const subject = varRef("s.0", STRING, loc);
|
||||
const needle = varRef("search.0", STRING, loc);
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
/* Binary operators: arithmetic, comparisons and equality, logical and nullish pairs, and string conversion for concatenation. */
|
||||
import { keyedHelperName } from "../stable-names.js";
|
||||
import * as ts from "../../ts7/adapter.js";
|
||||
import { cleanFuncAdaptable } from "../coercions/function-adapters.js";
|
||||
import { type Lowerer, jsFuncNameOf } from "../lowerer.js";
|
||||
@@ -802,7 +801,7 @@ function nullProtoToString(lowerer: Lowerer, e: IrExpr, t: IrType & { kind: "rec
|
||||
const key = `nullProtoToString:${t.shapeId}`;
|
||||
let helper = lowerer.valueHelpers.get(key);
|
||||
if (!helper) {
|
||||
helper = keyedHelperName(lowerer, "%rec.tostr", key);
|
||||
helper = `%rec.tostr.${lowerer.valueHelpers.size}`;
|
||||
lowerer.valueHelpers.set(key, helper);
|
||||
const r = varRef("r.0", t, loc);
|
||||
lowerer.liftedFns.push({
|
||||
|
||||
@@ -549,6 +549,9 @@ export function lowerIdentifierExpr(lowerer: Lowerer, expr: ts.Identifier, loc:
|
||||
const callable = lowerer.lowerBuiltinCallableValue(bi, loc);
|
||||
if (callable) return callable;
|
||||
}
|
||||
// node:crypto's webcrypto is the WebCrypto global's object.
|
||||
if (bi.module === "crypto" && bi.member === "webcrypto")
|
||||
return { kind: "libCall", fn: "webcrypto.object", args: [], type: DYN, loc };
|
||||
// JavaScript sources: an otherwise unsupported builtin VALUE is
|
||||
// the same identity-token story as stdlib globals above (the
|
||||
// harness adds worker_threads.Worker to its identity Set).
|
||||
@@ -721,7 +724,7 @@ export function lowerIdentifierExpr(lowerer: Lowerer, expr: ts.Identifier, loc:
|
||||
return { kind: "closure", fnName: name, captures: [], type, loc };
|
||||
}
|
||||
if (lowerer.isStdlibGlobal(expr, "crypto"))
|
||||
return { kind: "libCall", fn: "crypto.native", args: [], type: DYN, loc };
|
||||
return { kind: "libCall", fn: "webcrypto.object", args: [], type: DYN, loc };
|
||||
const nativeAlias = stdlibGlobalNameOf(lowerer, expr);
|
||||
if (nativeAlias === "Buffer" && expr.text !== "Buffer") {
|
||||
return { kind: "strLit", value: "[builtin Buffer]", type: STRING, loc };
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import { keyedHelperName } from "../stable-names.js";
|
||||
import { dateJsonType, isDateJsonSource } from "../builtins/dates.js";
|
||||
import { dynUndefinedExpr, numLit, strLit, varRef } from "../../../ir/build.js";
|
||||
import { pathModuleValue } from "../lower-builtin-values.js";
|
||||
@@ -407,7 +406,7 @@ export function lowerDynObjectLiteral(
|
||||
if (!(err instanceof PoisonError) || !pureMember) throw err;
|
||||
const fence = lowerer.deferToRuntimeFence(propDiagsBefore, prop, {
|
||||
kind: "closure",
|
||||
name: () => lowerer.closureName(loc, "_dynfence"),
|
||||
name: () => `%fn${lowerer.lambdaCounter++}_dynfence`,
|
||||
returnType: VOID,
|
||||
type: { kind: "func", params: [], ret: VOID },
|
||||
});
|
||||
@@ -462,7 +461,7 @@ export function lowerDynObjectLiteral(
|
||||
if (!(err instanceof PoisonError)) throw err;
|
||||
const fence = lowerer.deferToRuntimeFence(convDiagsBefore, prop, {
|
||||
kind: "closure",
|
||||
name: () => lowerer.closureName(loc, "_dynfence"),
|
||||
name: () => `%fn${lowerer.lambdaCounter++}_dynfence`,
|
||||
returnType: VOID,
|
||||
type: { kind: "func", params: [], ret: VOID },
|
||||
});
|
||||
@@ -2957,7 +2956,7 @@ function lowerDeclaredSpreadMerge(
|
||||
const key = `declmerge-source:${type.shapeId}:${source.shapeId}`;
|
||||
let helper = lowerer.valueHelpers.get(key);
|
||||
if (!helper) {
|
||||
helper = keyedHelperName(lowerer, "%rec.declmerge", key);
|
||||
helper = `%rec.declmerge.${lowerer.valueHelpers.size}`;
|
||||
const sourceType = source.value.type;
|
||||
const destination = varRef("out.0", type, loc);
|
||||
const receiver = varRef("source.0", sourceType, loc);
|
||||
|
||||
@@ -240,7 +240,7 @@ export function lowerOptionalChain(
|
||||
const receiver = lowerer.runtimeOptionalSourceValue(recvNode, loweredReceiver) ?? loweredReceiver;
|
||||
if (receiver.type.kind === "dyn") {
|
||||
if (dotNode === expr && ts.isCallExpression(expr)) {
|
||||
const id = lowerer.closureName(loc, "", "chain");
|
||||
const id = `chain.${lowerer.chainCounter++}`;
|
||||
const callee: IrExpr = { kind: "chainRecv", id, type: DYN, loc };
|
||||
const stmts: IrStmt[] = [];
|
||||
let guarded = receiver;
|
||||
@@ -298,7 +298,7 @@ export function lowerOptionalChain(
|
||||
);
|
||||
}
|
||||
if (dotNode === expr && ts.isElementAccessExpression(expr)) {
|
||||
const id = lowerer.closureName(loc, "", "chain");
|
||||
const id = `chain.${lowerer.chainCounter++}`;
|
||||
const reference: IrExpr = { kind: "chainRecv", id, type: DYN, loc };
|
||||
lowerer.chainRecvByNode.set(recvNode, reference);
|
||||
lowerer.chainHandled.add(dotNode);
|
||||
@@ -331,7 +331,7 @@ export function lowerOptionalChain(
|
||||
dotNode === expr.expression
|
||||
) {
|
||||
const m = dotNode.name.text;
|
||||
const id = lowerer.closureName(loc, "", "chain");
|
||||
const id = `chain.${lowerer.chainCounter++}`;
|
||||
const recvRef: IrExpr = { kind: "chainRecv", id, type: DYN, loc: locOf(recvNode) };
|
||||
lowerer.chainRecvByNode.set(recvNode, recvRef);
|
||||
lowerer.chainHandled.add(dotNode);
|
||||
@@ -358,7 +358,7 @@ export function lowerOptionalChain(
|
||||
}
|
||||
return { kind: "optChain", id, receiver, body, type: DYN, loc };
|
||||
}
|
||||
const id = lowerer.closureName(loc, "", "chain");
|
||||
const id = `chain.${lowerer.chainCounter++}`;
|
||||
const reference: IrExpr = { kind: "chainRecv", id, type: DYN, loc };
|
||||
lowerer.chainRecvByNode.set(recvNode, reference);
|
||||
lowerer.chainHandled.add(dotNode);
|
||||
@@ -420,7 +420,7 @@ export function lowerOptionalChain(
|
||||
rest.length === 1
|
||||
? rest[0]!
|
||||
: { kind: "union" as const, unionId: lowerer.unions.transform(def, rest) };
|
||||
const id = lowerer.closureName(loc, "", "chain");
|
||||
const id = `chain.${lowerer.chainCounter++}`;
|
||||
// A single present arm binds its payload. Multiple present arms bind
|
||||
// the original tagged value, then retag inside the guarded body. The
|
||||
// nullish arms never enter that body, and each record keeps its identity.
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
/* Property reads (`a.b`): lowerExprInner's property-access branch, intrinsic and union properties, field reads and targets, and match-group reads. */
|
||||
import { keyedHelperName } from "../stable-names.js";
|
||||
import { staticallySelected } from "../static-typeof.js";
|
||||
import * as ts from "../../ts7/adapter.js";
|
||||
import { lowerHistogramProperty } from "../builtins/performance.js";
|
||||
@@ -164,6 +163,7 @@ import { lowerUnionFieldRead } from "./union-fields.js";
|
||||
import { errorPropertyRead } from "../error-methods.js";
|
||||
import { isSafeToDiscard, isSafeToRepeat } from "./evaluation-safety.js";
|
||||
import { isZlibStreamMember, lowerZlibStreamBytesWritten } from "../builtins/compression.js";
|
||||
import { isWebCryptoReceiver } from "../builtins/crypto.js";
|
||||
import { isFsStreamMember, lowerFsStreamProperty } from "../builtins/fs-streams.js";
|
||||
import { keyedFieldRead } from "./keyed-field-targets.js";
|
||||
import { promisePrototypeValue } from "../thenables.js";
|
||||
@@ -844,8 +844,16 @@ export function lowerPropertyAccessExpr(
|
||||
if (runtimeOptionalClassField(lowerer, handled, expr)) return handled;
|
||||
return iteratorResultValueRead(lowerer, handled, expr) ?? lowerer.maybeNarrow(handled, expr);
|
||||
}
|
||||
// `globalThis.crypto` is the WebCrypto object the bare `crypto` answers.
|
||||
if (
|
||||
!expr.questionDotToken &&
|
||||
stdlibGlobalNameOf(lowerer, expr.expression) === "globalThis" &&
|
||||
stdlibGlobalNameOf(lowerer, expr) === "crypto"
|
||||
) {
|
||||
return { kind: "libCall", fn: "webcrypto.object", args: [], type: DYN, loc };
|
||||
}
|
||||
// `globalThis.<name>` that no lowering above claimed
|
||||
// (globalThis.crypto, globalThis.SubtleCrypto, globalThis.localStorage
|
||||
// (globalThis.SubtleCrypto, globalThis.localStorage
|
||||
// — the harness's capability-conditional knownGlobals adds): the
|
||||
// PROPERTY spelling of the identifier chokepoint's JS rule — a stdlib
|
||||
// global taken as a VALUE in a JavaScript source is the same opaque
|
||||
@@ -2785,7 +2793,7 @@ export function lowerGroupsProjection(
|
||||
const key = `regexgroups:${shapeId}:${groups.map((g) => `${g.name}=${g.index}`).join(",")}:${recvKey}`;
|
||||
let helper = lowerer.valueHelpers.get(key);
|
||||
if (!helper) {
|
||||
helper = keyedHelperName(lowerer, "%regex.groups", key);
|
||||
helper = `%regex.groups.${lowerer.valueHelpers.size}`;
|
||||
lowerer.valueHelpers.set(key, helper);
|
||||
const body: IrStmt[] = [];
|
||||
const locals: IrLocal[] = [{ id: "m.0", name: "m", type: recv.type, mutable: true }];
|
||||
@@ -2961,6 +2969,9 @@ export function lowerFieldRead(lowerer: Lowerer, expr: ts.PropertyAccessExpressi
|
||||
if (
|
||||
receiverIr?.kind !== "object" &&
|
||||
propSym &&
|
||||
// The WebCrypto object's generic getRandomValues reads off its
|
||||
// dynamic object.
|
||||
!isWebCryptoReceiver(lowerer, expr.expression) &&
|
||||
isGenericCallableMemberType(lowerer.checker.getTypeOfSymbol(propSym), lowerer.checker) &&
|
||||
// CLASS members keep the class-path fences (a poisoned class's own
|
||||
// diagnostics, the bound-method fence above).
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
/* instanceof and in, including private-name and runtime-key in. */
|
||||
import { keyedHelperName } from "../stable-names.js";
|
||||
import * as ts from "../../ts7/adapter.js";
|
||||
import { builtinClassInstanceOfAny, builtinClassKind } from "../builtins/builtin-class-dyn.js";
|
||||
import {
|
||||
@@ -648,7 +647,7 @@ export function lowerInstanceOf(lowerer: Lowerer, expr: ts.BinaryExpression, loc
|
||||
const key = `instanceof.union:${unionId}:${target.def.name}`;
|
||||
let helper = lowerer.valueHelpers.get(key);
|
||||
if (!helper) {
|
||||
helper = keyedHelperName(lowerer, "%instanceof.union", key);
|
||||
helper = `%instanceof.union.${lowerer.valueHelpers.size}`;
|
||||
lowerer.valueHelpers.set(key, helper);
|
||||
const value: IrExpr = { kind: "varRef", localId: "value.0", type: left.type, loc };
|
||||
const body: IrStmt[] = [];
|
||||
@@ -1848,7 +1847,7 @@ function lowerRuntimeKeyIn(
|
||||
const hkey = `haskey:${recvT.shapeId}`;
|
||||
let helper = lowerer.valueHelpers.get(hkey);
|
||||
if (!helper) {
|
||||
helper = keyedHelperName(lowerer, "%rec.haskey", hkey);
|
||||
helper = `%rec.haskey.${lowerer.valueHelpers.size}`;
|
||||
lowerer.valueHelpers.set(hkey, helper);
|
||||
const recT: IrType = { kind: "record", shapeId: recvT.shapeId };
|
||||
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
import { staticNameBindings } from "./static-function-names.js";
|
||||
import { positionTag } from "./structural-positions.js";
|
||||
import { keyedHelperName } from "./stable-names.js";
|
||||
import * as ts from "../ts7/adapter.js";
|
||||
import { bodyReadsArguments } from "../arguments-usage.js";
|
||||
import type { Lowerer } from "./lowerer.js";
|
||||
@@ -131,6 +129,10 @@ export interface GenericInstance {
|
||||
tsBindings?: Map<ts.Symbol, ts.Type>;
|
||||
/** Rendered type arguments ("<number, string>") for diagnostics. */
|
||||
typeArgsText: string;
|
||||
/** A factory returning one of its local classes through a declared
|
||||
* constructor type: that type (by id) and the class value it maps to
|
||||
* while the body lowers (Lowerer.instanceResultClass). */
|
||||
resultClass?: { declared: number; value: IrType };
|
||||
/** Implicit instances only: param symbol → the call site's (widened)
|
||||
* checker type, consulted by the Lowerer's typeOf while this instance's
|
||||
* body lowers (the implicit twin of `bindings`). */
|
||||
@@ -231,10 +233,7 @@ export function collectGenericSignature(lowerer: Lowerer, decl: ts.FunctionDecla
|
||||
lowerer.genericFnsBySymbol.set(symbol, {
|
||||
decl,
|
||||
baseName: nameText,
|
||||
qualifiedName: lowerer.qualify(
|
||||
ts.getSourceFileOfNode(decl),
|
||||
nsPathPrefix(lowerer, decl) + nameText,
|
||||
),
|
||||
qualifiedName: lowerer.qualify(ts.getSourceFileOfNode(decl), nsPathPrefix(decl) + nameText),
|
||||
typeParams,
|
||||
instances: new Map(),
|
||||
});
|
||||
@@ -473,7 +472,8 @@ function localClassResultInstance(
|
||||
params: ParamShape[],
|
||||
retTs: ts.Type,
|
||||
): GenericInstance | null {
|
||||
if (!localClassOfResult(lowerer, info, retTs)) return null;
|
||||
const resultClass = localClassOfResult(lowerer, info, retTs);
|
||||
if (!resultClass) return null;
|
||||
const tsBindings = new Map<ts.Symbol, ts.Type>();
|
||||
const bindings = lowerer.inferTypeParamBindings(expr, info, rsig, tsBindings);
|
||||
const bindingKey = info.typeParams
|
||||
@@ -488,12 +488,37 @@ function localClassResultInstance(
|
||||
tsBindings,
|
||||
});
|
||||
if (inst.returnType !== pending) return inst;
|
||||
const returnType = withInstanceScope(lowerer, info, inst, () => lowerer.mapTypeOf(retTs));
|
||||
// A result typed by a declared constructor type rather than the class
|
||||
// (`Struct<S>`) is the class value of the local class the body returns.
|
||||
const declaredResult = info.decl.type
|
||||
? lowerer.checker.getTypeFromTypeNode(info.decl.type)
|
||||
: undefined;
|
||||
const viaConstructorType = retTs.getSymbol()?.valueDeclaration !== resultClass;
|
||||
const returnType = withInstanceScope(lowerer, info, inst, () => {
|
||||
if (!viaConstructorType) return lowerer.mapTypeOf(retTs);
|
||||
// The local class's own constructor type: a class expression's type is
|
||||
// its constructor type, named or not (`return class { ... } as Struct<S>`).
|
||||
const classSymbol =
|
||||
!ts.isClassExpression(resultClass) && resultClass.name
|
||||
? lowerer.checker.getSymbolAtLocation(resultClass.name)
|
||||
: undefined;
|
||||
const classType = ts.isClassExpression(resultClass)
|
||||
? lowerer.checker.getTypeAtLocation(resultClass)
|
||||
: classSymbol
|
||||
? lowerer.checker.getTypeOfSymbol(classSymbol)
|
||||
: undefined;
|
||||
return classType ? lowerer.mapTypeOf(classType) : null;
|
||||
});
|
||||
if (!returnType) {
|
||||
fenceGenericSignatureResult(lowerer, expr, retTs);
|
||||
lowerer.badType(expr, retTs);
|
||||
}
|
||||
inst.returnType = returnType;
|
||||
if (viaConstructorType && returnType.kind === "classval") {
|
||||
if (declaredResult) inst.resultClass = { declared: declaredResult.id, value: returnType };
|
||||
if (lowerer.typeParamBindings === null) lowerer.localClassValueTypes.set(retTs.id, returnType);
|
||||
return inst;
|
||||
}
|
||||
// Values of the class outside the factory (`const b = new B()`, `b.read()`)
|
||||
// carry the instantiated checker type; record which class it names. A
|
||||
// call inside another generic instance can have a type that depends on
|
||||
@@ -568,12 +593,102 @@ function localClassOfResult(
|
||||
type: ts.Type,
|
||||
): ts.ClassLikeDeclaration | null {
|
||||
const symbol = type.getSymbol();
|
||||
if (!symbol || !(symbol.flags & ts.SymbolFlags.Class)) return null;
|
||||
const decl = symbol.valueDeclaration;
|
||||
if (!decl || !(ts.isClassExpression(decl) || ts.isClassDeclaration(decl))) return null;
|
||||
for (let parent = decl.parent; parent && !ts.isSourceFile(parent); parent = parent.parent)
|
||||
if (parent === info.decl) return decl;
|
||||
return null;
|
||||
const decl = symbol && symbol.flags & ts.SymbolFlags.Class ? symbol.valueDeclaration : undefined;
|
||||
if (decl && (ts.isClassExpression(decl) || ts.isClassDeclaration(decl)))
|
||||
return declaredInside(decl, info.decl) ? decl : null;
|
||||
// A result typed by a constructor type (`Struct<S>`, `new () => S`) whose
|
||||
// every return gives back the one local class, through parentheses and
|
||||
// assertions (`return Base as Struct<S>`).
|
||||
// A mixin (a class extending one of the function's parameters) keeps the
|
||||
// mixin instantiation, which keys on the argument class.
|
||||
if (lowerer.checker.getConstructSignatures(type).length === 0) return null;
|
||||
const returned = returnedLocalClass(lowerer, info);
|
||||
return returned && !extendsParameter(lowerer, returned, info) ? returned : null;
|
||||
}
|
||||
|
||||
function extendsParameter(
|
||||
lowerer: Lowerer,
|
||||
decl: ts.ClassLikeDeclaration,
|
||||
info: GenericFnInfo,
|
||||
): boolean {
|
||||
const params = new Set<ts.Symbol>();
|
||||
const fn = info.decl as { parameters?: { nodes: readonly ts.ParameterDeclaration[] } };
|
||||
for (const param of fn.parameters?.nodes ?? []) {
|
||||
const symbol = ts.isIdentifier(param.name)
|
||||
? lowerer.checker.getSymbolAtLocation(param.name)
|
||||
: undefined;
|
||||
if (symbol !== undefined) params.add(symbol);
|
||||
}
|
||||
for (const clause of decl.heritageClauses?.nodes ?? [])
|
||||
for (const base of clause.types.nodes) {
|
||||
const symbol = ts.isIdentifier(base.expression)
|
||||
? lowerer.checker.getSymbolAtLocation(base.expression)
|
||||
: undefined;
|
||||
if (symbol !== undefined && params.has(symbol)) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function declaredInside(node: ts.Node, owner: ts.Node): boolean {
|
||||
for (let parent = node.parent; parent && !ts.isSourceFile(parent); parent = parent.parent)
|
||||
if (parent === owner) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
/** The local class every return of `info`'s body gives back, or null when a
|
||||
* return gives anything else (or nothing). Nested functions and classes keep
|
||||
* their own returns. */
|
||||
function returnedLocalClass(lowerer: Lowerer, info: GenericFnInfo): ts.ClassLikeDeclaration | null {
|
||||
const fn = info.decl as ts.Node & { body?: ts.Node };
|
||||
const body = fn.body;
|
||||
if (!body) return null;
|
||||
const classOf = (node: ts.Expression): ts.ClassLikeDeclaration | null => {
|
||||
let expr = node;
|
||||
while (
|
||||
ts.isParenthesizedExpression(expr) ||
|
||||
ts.isAsExpression(expr) ||
|
||||
ts.isSatisfiesExpression(expr) ||
|
||||
ts.isNonNullExpression(expr)
|
||||
)
|
||||
expr = expr.expression;
|
||||
if (ts.isClassExpression(expr)) return declaredInside(expr, info.decl) ? expr : null;
|
||||
if (!ts.isIdentifier(expr)) return null;
|
||||
const declaration = lowerer.checker.getSymbolAtLocation(expr)?.valueDeclaration;
|
||||
if (!declaration || !declaredInside(declaration, info.decl)) return null;
|
||||
if (ts.isClassDeclaration(declaration)) return declaration;
|
||||
// A `const` bound to a class expression (`const C = class { ... }; return C`).
|
||||
if (
|
||||
ts.isVariableDeclaration(declaration) &&
|
||||
ts.getCombinedNodeFlags(declaration) & ts.NodeFlags.Const &&
|
||||
declaration.initializer
|
||||
) {
|
||||
let init: ts.Expression = declaration.initializer;
|
||||
while (ts.isParenthesizedExpression(init)) init = init.expression;
|
||||
if (ts.isClassExpression(init)) return init;
|
||||
}
|
||||
return null;
|
||||
};
|
||||
if (!ts.isBlock(body)) return classOf(body as ts.Expression);
|
||||
let found: ts.ClassLikeDeclaration | null = null;
|
||||
let valid = true;
|
||||
const visit = (node: ts.Node): void => {
|
||||
if (
|
||||
!valid ||
|
||||
ts.isFunctionLike(node) ||
|
||||
ts.isClassDeclaration(node) ||
|
||||
ts.isClassExpression(node)
|
||||
)
|
||||
return;
|
||||
if (ts.isReturnStatement(node)) {
|
||||
const returned = node.expression ? classOf(node.expression) : null;
|
||||
if (!returned || (found !== null && found !== returned)) valid = false;
|
||||
else found = returned;
|
||||
return;
|
||||
}
|
||||
ts.forEachChild(node, visit);
|
||||
};
|
||||
ts.forEachChild(body, visit);
|
||||
return valid ? found : null;
|
||||
}
|
||||
|
||||
/** Runs `fn` under an instance's type bindings and local-class names, as
|
||||
@@ -588,14 +703,17 @@ function withInstanceScope<T>(
|
||||
const prevTsBindings = lowerer.typeParamTsBindings;
|
||||
const previousPath = lowerer.genericInstantiationPath;
|
||||
const prevContext = lowerer.instantiationContext;
|
||||
const prevResultClass = lowerer.instanceResultClass;
|
||||
lowerer.typeParamBindings = inst.bindings;
|
||||
lowerer.typeParamTsBindings = inst.tsBindings ?? null;
|
||||
lowerer.genericInstantiationPath = inst.path;
|
||||
lowerer.instantiationContext = `instantiating '${info.baseName}' with ${inst.typeArgsText}`;
|
||||
lowerer.instanceResultClass = inst.resultClass ?? null;
|
||||
lowerer.localClassInstantiations.push({ owner: info.decl, name: inst.name });
|
||||
try {
|
||||
return fn();
|
||||
} finally {
|
||||
lowerer.instanceResultClass = prevResultClass;
|
||||
lowerer.localClassInstantiations.pop();
|
||||
lowerer.typeParamBindings = prevBindings;
|
||||
lowerer.typeParamTsBindings = prevTsBindings;
|
||||
@@ -656,7 +774,7 @@ function internGenericInstance(
|
||||
const typeArgsText = `<${rendered.length > 80 ? rendered.slice(0, 77) + "..." : rendered}>`;
|
||||
inst = {
|
||||
path,
|
||||
name: keyedHelperName(lowerer, `${info.qualifiedName}%`, key, ""),
|
||||
name: `${info.qualifiedName}%${info.instances.size}`,
|
||||
ordinal: info.instances.size,
|
||||
params,
|
||||
returnType,
|
||||
@@ -2134,7 +2252,7 @@ function internImplicitInstance(
|
||||
const declared = dynamicBroadPromise ? null : implicitDeclaredReturn(lowerer, info);
|
||||
inst = {
|
||||
path,
|
||||
name: keyedHelperName(lowerer, `${info.qualifiedName}%`, key, ""),
|
||||
name: `${info.qualifiedName}%${info.instances.size}`,
|
||||
ordinal: info.instances.size,
|
||||
params: shapes,
|
||||
// The promise callers rely on before the body settles it: the
|
||||
@@ -2322,7 +2440,7 @@ export function implicitLocalFnInfoOf(
|
||||
baseName: name,
|
||||
qualifiedName: lowerer.qualify(
|
||||
ts.getSourceFileOfNode(decl),
|
||||
nsPathPrefix(lowerer, stmt ?? decl, decl) + `${name}%l${positionTag(lowerer, decl)}`,
|
||||
nsPathPrefix(stmt ?? decl, decl) + `${name}%l${ts.tokenStartOfNode(decl)}`,
|
||||
),
|
||||
typeParams: [],
|
||||
instances: new Map(),
|
||||
@@ -2418,7 +2536,7 @@ export function objLitGenericFnInfoOf(
|
||||
baseName: name,
|
||||
qualifiedName: lowerer.qualify(
|
||||
ts.getSourceFileOfNode(fnNode),
|
||||
`%ol${positionTag(lowerer, fnNode)}.${name}`,
|
||||
`%ol${ts.tokenStartOfNode(fnNode)}.${name}`,
|
||||
),
|
||||
typeParams,
|
||||
instances: new Map(),
|
||||
@@ -2564,7 +2682,7 @@ export function bindingGenericFnInfoOf(
|
||||
baseName: name,
|
||||
qualifiedName: lowerer.qualify(
|
||||
ts.getSourceFileOfNode(decl),
|
||||
nsPathPrefix(lowerer, stmt ?? decl, decl) +
|
||||
nsPathPrefix(stmt ?? decl, decl) +
|
||||
(topLevel ? name : `${name}%l${ts.tokenStartOfNode(decl)}`),
|
||||
),
|
||||
typeParams,
|
||||
|
||||
@@ -23,7 +23,6 @@
|
||||
* The entry handles that itable.find and itable.insert answer stay inside
|
||||
* these lowerings: f64 locals that only reach toBool and itable.value.
|
||||
* Any other use of the class or its members as values is refused. */
|
||||
import { keyedHelperName } from "./stable-names.js";
|
||||
import { dynUndefinedExpr } from "../../ir/build.js";
|
||||
import { BOOL, F64, STRING, funcOf, typeKey } from "../../ir/ir.js";
|
||||
import type { IrExpr, IrFunction, IrStmt, IrType, SrcLoc } from "../../ir/ir.js";
|
||||
@@ -448,11 +447,7 @@ function helperName(lowerer: Lowerer, member: InternTableMember, signature: stri
|
||||
}
|
||||
|
||||
function registerHelper(lowerer: Lowerer, member: InternTableMember, signature: string): string {
|
||||
const name = keyedHelperName(
|
||||
lowerer,
|
||||
`%${INTERN_TABLE_CLASS}.${member}`,
|
||||
`${member}|${signature}`,
|
||||
);
|
||||
const name = `%${INTERN_TABLE_CLASS}.${member}.${lowerer.internTableHelpers.size}`;
|
||||
lowerer.internTableHelpers.set(`${member}|${signature}`, name);
|
||||
return name;
|
||||
}
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import { keyedHelperName } from "./stable-names.js";
|
||||
import { dynUndefinedExpr, varRef } from "../../ir/build.js";
|
||||
import {
|
||||
BOOL,
|
||||
@@ -87,7 +86,7 @@ function dynIteratorAdapter(
|
||||
const key = `iterAdapt:${typeKey(expected)}`;
|
||||
let name = lowerer.valueHelpers.get(key);
|
||||
if (!name) {
|
||||
name = keyedHelperName(lowerer, "%iter.adapt", key);
|
||||
name = `%iter.adapt.${lowerer.valueHelpers.size}`;
|
||||
lowerer.valueHelpers.set(key, name);
|
||||
const locals: IrLocal[] = [];
|
||||
const local = (id: string, type: IrType, mutable = false): IrExpr => {
|
||||
@@ -314,7 +313,7 @@ export function lowerCollectionIteratorValue(
|
||||
loc,
|
||||
};
|
||||
if (!typeEquals(yielded.type, type.yieldT)) return null;
|
||||
name = keyedHelperName(lowerer, "%coll.iter", key);
|
||||
name = `%coll.iter.${lowerer.valueHelpers.size}`;
|
||||
lowerer.valueHelpers.set(key, name);
|
||||
lowerer.liftedFns.push({
|
||||
name,
|
||||
@@ -412,7 +411,7 @@ export function lowerBytesIteratorValue(
|
||||
const key = `bytesIter:${method}:${typeKey(bytesT)}:${typeKey(type)}`;
|
||||
let name = lowerer.valueHelpers.get(key);
|
||||
if (!name) {
|
||||
name = keyedHelperName(lowerer, "%bytes.iter", key);
|
||||
name = `%bytes.iter.${lowerer.valueHelpers.size}`;
|
||||
lowerer.valueHelpers.set(key, name);
|
||||
const list = varRef("list.0", bytesT, loc);
|
||||
const index = varRef("index.0", F64, loc);
|
||||
@@ -518,7 +517,7 @@ export function lowerStringIteratorValue(
|
||||
const key = `strIter:${typeKey(type)}`;
|
||||
let name = lowerer.valueHelpers.get(key);
|
||||
if (!name) {
|
||||
name = keyedHelperName(lowerer, "%str.iter", key);
|
||||
name = `%str.iter.${lowerer.valueHelpers.size}`;
|
||||
lowerer.valueHelpers.set(key, name);
|
||||
const text = varRef("text.0", STRING, loc);
|
||||
const index = varRef("index.0", F64, loc);
|
||||
@@ -678,7 +677,7 @@ function generatorRetypeAdapter(
|
||||
type: resumeType(expected.nextT),
|
||||
loc,
|
||||
};
|
||||
name = keyedHelperName(lowerer, "%gen.retype", key);
|
||||
name = `%gen.retype.${lowerer.valueHelpers.size}`;
|
||||
lowerer.valueHelpers.set(key, name);
|
||||
lowerer.liftedFns.push({
|
||||
name,
|
||||
@@ -795,7 +794,7 @@ export function generatorDrain(
|
||||
const outRef = varRef("out.0", arrT, loc);
|
||||
const value = lowerer.coerceToExpected(genValue(genRef, loc), elem);
|
||||
if (!typeEquals(value.type, elem)) return null;
|
||||
name = keyedHelperName(lowerer, "%gen.drain", key);
|
||||
name = `%gen.drain.${lowerer.valueHelpers.size}`;
|
||||
lowerer.valueHelpers.set(key, name);
|
||||
lowerer.liftedFns.push({
|
||||
name,
|
||||
@@ -857,7 +856,7 @@ export function generatorDrain(
|
||||
if (!raw) return null;
|
||||
const value = lowerer.coerceToExpected(raw, elem);
|
||||
if (!typeEquals(value.type, elem)) return null;
|
||||
name = keyedHelperName(lowerer, "%gen.drain", key);
|
||||
name = `%gen.drain.${lowerer.valueHelpers.size}`;
|
||||
lowerer.valueHelpers.set(key, name);
|
||||
lowerer.liftedFns.push({
|
||||
name,
|
||||
@@ -979,7 +978,7 @@ export function lowerArrayIteratorValue(
|
||||
};
|
||||
}
|
||||
if (!yielded || !typeEquals(yielded.type, type.yieldT)) return null;
|
||||
name = keyedHelperName(lowerer, "%arr.iter", key);
|
||||
name = `%arr.iter.${lowerer.valueHelpers.size}`;
|
||||
lowerer.valueHelpers.set(key, name);
|
||||
lowerer.liftedFns.push({
|
||||
name,
|
||||
|
||||
@@ -21,7 +21,6 @@
|
||||
* files only, and an empty answer leaves every mapping unchanged, so
|
||||
* TypeScript programs and JavaScript that never uses these patterns
|
||||
* compile exactly as before. */
|
||||
import { keyedHelperName } from "./stable-names.js";
|
||||
import * as ts from "../ts7/adapter.js";
|
||||
import { isJsSourceFile, locOf } from "../program.js";
|
||||
import { BOOL, DYN, type IrExpr, type IrStmt } from "../../ir/ir.js";
|
||||
@@ -216,10 +215,10 @@ export function lowerValueReturningConstructorNew(
|
||||
"spread arguments when constructing a class whose constructor returns a value",
|
||||
);
|
||||
}
|
||||
const key = `ctorValue:${lowerer.positions.of(decl)}`;
|
||||
const key = `ctorValue:${ts.getSourceFileOfNode(decl).fileName}:${ts.tokenStartOfNode(decl)}`;
|
||||
let name = lowerer.valueHelpers.get(key);
|
||||
if (name === undefined) {
|
||||
name = keyedHelperName(lowerer, "%ctorValue", key);
|
||||
name = `%ctorValue.${lowerer.valueHelpers.size}`;
|
||||
lowerer.valueHelpers.set(key, name);
|
||||
const savedClass = lowerer.currentClass;
|
||||
lowerer.currentClass = null;
|
||||
|
||||
@@ -10,7 +10,6 @@
|
||||
* JSON module is ordinary storage holding that object — destructuring
|
||||
* copies its properties at the require, exactly like Node. ES-module JSON
|
||||
* imports keep their own record (Node's ESM loader caches separately). */
|
||||
import { keyedHelperName } from "./stable-names.js";
|
||||
import * as ts from "../ts7/adapter.js";
|
||||
import type { Lowerer } from "./lowerer.js";
|
||||
import { PoisonError } from "./lowerer.js";
|
||||
@@ -85,7 +84,7 @@ export function collectJsonRequires(lowerer: Lowerer, parts: FileParts[]): void
|
||||
);
|
||||
throw new PoisonError();
|
||||
}
|
||||
const id = keyedHelperName(lowerer, "%g.jsonRequire", jsonSf.fileName);
|
||||
const id = `%g.jsonRequire.${lowerer.jsonRequireModules.size}`;
|
||||
const global: IrGlobal = { id, name: "%json", type: mapped, mutable: true };
|
||||
const loadedId = `${id}%loaded`;
|
||||
lowerer.globalsList.push(global, {
|
||||
|
||||
@@ -14,7 +14,6 @@
|
||||
* Any other use of the class or its methods as values is refused. The
|
||||
* module is marked lifetimeRegions, which moves every ProgramLifetime
|
||||
* class into counted regions. */
|
||||
import { keyedHelperName } from "./stable-names.js";
|
||||
import { F64, VOID, funcOf, typeKey } from "../../ir/ir.js";
|
||||
import type { IrExpr, IrFunction, IrStmt, IrType, SrcLoc } from "../../ir/ir.js";
|
||||
import { experimentalImportOf, lifetimeRegionMemberOf } from "../experimental-import.js";
|
||||
@@ -153,7 +152,7 @@ function runHelper(lowerer: Lowerer, fnType: IrType & { kind: "func" }, loc: Src
|
||||
const key = typeKey(fnType);
|
||||
const known = lowerer.regionRunHelpers.get(key);
|
||||
if (known !== undefined) return known;
|
||||
const name = keyedHelperName(lowerer, `%${LIFETIME_REGION_CLASS}.run`, key);
|
||||
const name = `%${LIFETIME_REGION_CLASS}.run.${lowerer.regionRunHelpers.size}`;
|
||||
lowerer.regionRunHelpers.set(key, name);
|
||||
const regionT = lifetimeRegionType(lowerer);
|
||||
const ret = fnType.ret;
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import { keyedHelperName } from "./stable-names.js";
|
||||
import { InternalCompilerError } from "../../errors.js";
|
||||
/* The node:assert lowering (a spoke module like lower-dgram.ts): the
|
||||
* callable module form (`assert(x)` through a default import or a CJS
|
||||
@@ -668,7 +667,7 @@ function partialEqHelper(lowerer: Lowerer, ta: IrType, tb: IrType, loc: SrcLoc):
|
||||
const key = `assert.pdeq:${typeKey(ta)}|${typeKey(tb)}`;
|
||||
const existing = lowerer.assertHelpers.get(key);
|
||||
if (existing) return existing;
|
||||
const name = keyedHelperName(lowerer, "%assert.pdeq", key);
|
||||
const name = `%assert.pdeq.${lowerer.assertHelpers.size}`;
|
||||
lowerer.assertHelpers.set(key, name);
|
||||
const a = (): IrExpr => varRef("a.0", ta, loc);
|
||||
const b = (): IrExpr => varRef("b.0", tb, loc);
|
||||
@@ -1281,7 +1280,7 @@ function deepCheckCall(
|
||||
const key = `assert.dcheck:${variant}:${negated}:${typeKey(a.type)}:${typeKey(b.type)}`;
|
||||
let name = lowerer.assertHelpers.get(key);
|
||||
if (!name) {
|
||||
name = keyedHelperName(lowerer, "%assert.dcheck", key);
|
||||
name = `%assert.dcheck.${lowerer.assertHelpers.size}`;
|
||||
lowerer.assertHelpers.set(key, name);
|
||||
const pa = varRef("a.0", a.type, loc);
|
||||
const pb = varRef("b.0", b.type, loc);
|
||||
@@ -1845,7 +1844,7 @@ function assertThrowsHelper(
|
||||
const key = `assert.${mode}:${recvIsPromise ? "p" : "f"}:${typeKey(recvType)}:${sig}`;
|
||||
const existing = lowerer.assertHelpers.get(key);
|
||||
if (existing) return existing;
|
||||
const name = keyedHelperName(lowerer, `%assert.${mode}`, key);
|
||||
const name = `%assert.${mode}.${lowerer.assertHelpers.size}`;
|
||||
lowerer.assertHelpers.set(key, name);
|
||||
|
||||
const caughtRef = (): IrExpr => varRef("e.0", CAUGHT, loc);
|
||||
@@ -2294,7 +2293,7 @@ function ifErrorHelper(lowerer: Lowerer, t: IrType & { kind: "union" }, loc: Src
|
||||
const key = `assert.iferr:${typeKey(t)}`;
|
||||
const existing = lowerer.assertHelpers.get(key);
|
||||
if (existing) return existing;
|
||||
const name = keyedHelperName(lowerer, "%assert.iferr", key);
|
||||
const name = `%assert.iferr.${lowerer.assertHelpers.size}`;
|
||||
lowerer.assertHelpers.set(key, name);
|
||||
const def = lowerer.unions.get(t.unionId);
|
||||
if (!def) throw new InternalCompilerError(`assert.ifError over unknown union ${t.unionId}`);
|
||||
@@ -2465,7 +2464,7 @@ function deepEqHelper(lowerer: Lowerer, t: IrType, loc: SrcLoc, loose = false):
|
||||
const key = `assert.${prefix}:${typeKey(t)}`;
|
||||
const existing = lowerer.assertHelpers.get(key);
|
||||
if (existing) return existing;
|
||||
const name = keyedHelperName(lowerer, `%assert.${prefix}`, key);
|
||||
const name = `%assert.${prefix}.${lowerer.assertHelpers.size}`;
|
||||
lowerer.assertHelpers.set(key, name);
|
||||
|
||||
const a = (): IrExpr => varRef("a.0", t, loc);
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import { keyedHelperName } from "./stable-names.js";
|
||||
import { isForwardedTupleSpread, lowerForwardingApply } from "./typed-arguments.js";
|
||||
import { guardNullProto, nullProtoRecordShape } from "./null-prototype.js";
|
||||
import { lowerDynStreamMemberCall } from "./dyn-streams.js";
|
||||
@@ -1543,9 +1542,10 @@ export function lowerCall(lowerer: Lowerer, expr: ts.CallExpression): IrExpr {
|
||||
lowerer.fenceStaticReadableStreamMember(expr.expression, "call") ??
|
||||
lowerDisposableStackUseCall(lowerer, expr, expr.expression) ??
|
||||
lowerer.lowerStdlibMethodCall(expr, expr.expression) ??
|
||||
// crypto.getRandomValues on the WebCrypto global (untyped in
|
||||
// fallback builds, so before the dyn receiver path) or webcrypto.
|
||||
lowerWebCryptoGetRandomValues(lowerer, expr, expr.expression) ??
|
||||
// crypto.getRandomValues / randomUUID on the WebCrypto global
|
||||
// (untyped in fallback builds, so before the dyn receiver path) or
|
||||
// webcrypto.
|
||||
lowerWebCryptoMethodCall(lowerer, expr, expr.expression) ??
|
||||
// Dyn receivers (JSON.parse-derived `unknown`/`any` values) —
|
||||
|
||||
lowerDynReceiverMethodCall(lowerer, expr, expr.expression) ??
|
||||
@@ -2095,7 +2095,7 @@ function lowerOptionalNumberDefault(
|
||||
const key = `number.optionalDefault:${widened.unionId}:${dflt}`;
|
||||
let helper = lowerer.valueHelpers.get(key);
|
||||
if (!helper) {
|
||||
helper = keyedHelperName(lowerer, "%number.optionalDefault", key);
|
||||
helper = `%number.optionalDefault.${lowerer.valueHelpers.size}`;
|
||||
lowerer.valueHelpers.set(key, helper);
|
||||
const input = varRef("value.0", widened, loc);
|
||||
lowerer.liftedFns.push({
|
||||
@@ -2204,7 +2204,7 @@ function primitiveConversionHandler(
|
||||
const key = `then.${name}:${typeKey(inner)}`;
|
||||
let fnName = lowerer.arrHofHelpers.get(key);
|
||||
if (fnName === undefined) {
|
||||
fnName = keyedHelperName(lowerer, `%then.${name}`, key);
|
||||
fnName = `%then.${name}.${lowerer.arrHofHelpers.size}`;
|
||||
lowerer.arrHofHelpers.set(key, fnName);
|
||||
const value = varRef("v.0", inner, loc);
|
||||
let body: IrExpr;
|
||||
@@ -2373,7 +2373,7 @@ function lowerBigIntConstructorValue(lowerer: Lowerer, arg: IrExpr, loc: SrcLoc)
|
||||
const key = `bigint.scalar:${unionId}`;
|
||||
let helper = lowerer.valueHelpers.get(key);
|
||||
if (!helper) {
|
||||
helper = keyedHelperName(lowerer, "%bigint.scalar", key);
|
||||
helper = `%bigint.scalar.${lowerer.valueHelpers.size}`;
|
||||
lowerer.valueHelpers.set(key, helper);
|
||||
const value = varRef("value.0", arg.type, loc);
|
||||
const body: IrStmt[] = [];
|
||||
@@ -3235,7 +3235,7 @@ function lowerScalarUnionNumber(
|
||||
const key = `number.scalar:${arg.type.unionId}`;
|
||||
let helper = lowerer.valueHelpers.get(key);
|
||||
if (!helper) {
|
||||
helper = keyedHelperName(lowerer, "%number.scalar", key);
|
||||
helper = `%number.scalar.${lowerer.valueHelpers.size}`;
|
||||
lowerer.valueHelpers.set(key, helper);
|
||||
const value = varRef("value.0", arg.type, loc);
|
||||
const nan: IrExpr = {
|
||||
@@ -3280,7 +3280,7 @@ function lowerOptionalStringNumber(
|
||||
const key = `${label}.optionalString:${arg.type.unionId}`;
|
||||
let helper = lowerer.valueHelpers.get(key);
|
||||
if (!helper) {
|
||||
helper = keyedHelperName(lowerer, `%${label}.optionalString`, key);
|
||||
helper = `%${label}.optionalString.${lowerer.valueHelpers.size}`;
|
||||
lowerer.valueHelpers.set(key, helper);
|
||||
const value = varRef("value.0", arg.type, loc);
|
||||
lowerer.liftedFns.push({
|
||||
@@ -4707,7 +4707,7 @@ function lowerDefaultToStringCall(
|
||||
const key = `objToStr:${typeKey(recv.type)}${missing ? `:${missing}` : ""}`;
|
||||
let helper = lowerer.valueHelpers.get(key);
|
||||
if (!helper) {
|
||||
helper = keyedHelperName(lowerer, "%obj.tostr", key);
|
||||
helper = `%obj.tostr.${lowerer.valueHelpers.size}`;
|
||||
lowerer.valueHelpers.set(key, helper);
|
||||
const o = varRef("o.0", recv.type, loc);
|
||||
lowerer.liftedFns.push({
|
||||
@@ -5394,7 +5394,7 @@ function loweredTemplateStrings(
|
||||
const site = lowerer.globalsList.length;
|
||||
const globalId = `%g.templateStrings.${site}`;
|
||||
const readyId = `%g.templateStringsReady.${site}`;
|
||||
getter = keyedHelperName(lowerer, "%templateStrings.get", key);
|
||||
getter = `%templateStrings.get.${lowerer.valueHelpers.size}`;
|
||||
lowerer.valueHelpers.set(key, getter);
|
||||
lowerer.globalsList.push(
|
||||
{ id: globalId, name: "%templateStrings", type, mutable: true },
|
||||
@@ -5575,7 +5575,7 @@ export function lowerPromiseMethodCall(
|
||||
// IS the spec's identity/thrower/no-op substitution for all three
|
||||
// members. An argument expression, when present, is undefined/null
|
||||
// by construction — nothing to evaluate.
|
||||
const fnName = lowerer.closureName(loc, `_${member}pass`);
|
||||
const fnName = `%fn${lowerer.lambdaCounter++}_${member}pass`;
|
||||
const funcType: IrType & { kind: "func" } = { kind: "func", params: [promT], ret: promT };
|
||||
const fnCtx = newFnCtx(true, null, funcType, inner);
|
||||
fnCtx.isAsync = true;
|
||||
@@ -5672,7 +5672,7 @@ export function lowerPromiseMethodCall(
|
||||
// Evaluate both callback expressions before the wrapper runs, in source order.
|
||||
const fulfilled = fulfilledAbsent ? null : boxHandler(fulfilledNode, true);
|
||||
const rejected = rejectedAbsent ? null : boxHandler(rejectedNode, false);
|
||||
const fnName = lowerer.closureName(loc, "_then2");
|
||||
const fnName = `%fn${lowerer.lambdaCounter++}_then2`;
|
||||
const paramTypes: IrType[] = [
|
||||
promT,
|
||||
...(fulfilled ? [fulfilled.value.type] : []),
|
||||
@@ -5921,7 +5921,7 @@ export function lowerPromiseMethodCall(
|
||||
);
|
||||
};
|
||||
const resultT: IrType & { kind: "promise" } = { kind: "promise", inner: DYN };
|
||||
const fnName = lowerer.closureName(loc, "_then");
|
||||
const fnName = `%fn${lowerer.lambdaCounter++}_then`;
|
||||
const funcType: IrType & { kind: "func" } = {
|
||||
kind: "func",
|
||||
params: [promT, DYN],
|
||||
@@ -6016,7 +6016,7 @@ export function lowerPromiseMethodCall(
|
||||
);
|
||||
}
|
||||
const R = resultT.inner;
|
||||
const fnName = lowerer.closureName(loc, "_then");
|
||||
const fnName = `%fn${lowerer.lambdaCounter++}_then`;
|
||||
const funcType: IrType & { kind: "func" } = {
|
||||
kind: "func",
|
||||
params: [promT, cb.type],
|
||||
@@ -6119,7 +6119,7 @@ export function lowerPromiseMethodCall(
|
||||
"finally callbacks with parameters or a return value (use () => { ... })",
|
||||
);
|
||||
}
|
||||
const fnName = lowerer.closureName(loc, "_finally");
|
||||
const fnName = `%fn${lowerer.lambdaCounter++}_finally`;
|
||||
const funcType: IrType & { kind: "func" } = {
|
||||
kind: "func",
|
||||
params: [promT, cb.type],
|
||||
@@ -6222,7 +6222,7 @@ export function lowerPromiseMethodCall(
|
||||
}
|
||||
if (cb.type.kind === "dyn") {
|
||||
const resultT: IrType & { kind: "promise" } = { kind: "promise", inner: DYN };
|
||||
const fnName = lowerer.closureName(loc, "_catchdyn");
|
||||
const fnName = `%fn${lowerer.lambdaCounter++}_catchdyn`;
|
||||
const funcType: IrType & { kind: "func" } = {
|
||||
kind: "func",
|
||||
params: [promT, DYN],
|
||||
@@ -6352,7 +6352,7 @@ export function lowerPromiseMethodCall(
|
||||
);
|
||||
}
|
||||
const R = resultT.inner;
|
||||
const fnName = lowerer.closureName(loc, "_catch");
|
||||
const fnName = `%fn${lowerer.lambdaCounter++}_catch`;
|
||||
const funcType: IrType & { kind: "func" } = { kind: "func", params: [promT], ret: resultT };
|
||||
const fnCtx = newFnCtx(true, null, funcType, R);
|
||||
fnCtx.isAsync = true;
|
||||
@@ -6589,7 +6589,7 @@ function filterNarrowHelper(
|
||||
const key = `filterNarrow:${test}:${typeKey(elem)}:${typeKey(outElem)}`;
|
||||
const existing = lowerer.arrHofHelpers.get(key);
|
||||
if (existing) return existing;
|
||||
const name = keyedHelperName(lowerer, "%arr.filterNarrow", key);
|
||||
const name = `%arr.filterNarrow.${lowerer.arrHofHelpers.size}`;
|
||||
lowerer.arrHofHelpers.set(key, name);
|
||||
|
||||
const arrT = arrayOf(elem);
|
||||
@@ -6835,7 +6835,7 @@ export function recordKeysArrayCall(
|
||||
const key = `obj.keys:${argIr.shapeId}:${typeKey(resultT)}`;
|
||||
let helper = lowerer.arrHofHelpers.get(key);
|
||||
if (!helper) {
|
||||
helper = keyedHelperName(lowerer, "%obj.keys", key);
|
||||
helper = `%obj.keys.${lowerer.arrHofHelpers.size}`;
|
||||
const ref: IrExpr = { kind: "varRef", localId: "r.0", type: argIr, loc };
|
||||
const outRef: IrExpr = { kind: "varRef", localId: "out.0", type: resultT, loc };
|
||||
lowerer.arrHofHelpers.set(key, helper);
|
||||
@@ -6905,7 +6905,7 @@ function recordHasOwnHelper(lowerer: Lowerer, shapeId: string, loc: SrcLoc): str
|
||||
const key = `obj.hasOwn:${shapeId}`;
|
||||
const existing = lowerer.arrHofHelpers.get(key);
|
||||
if (existing) return existing;
|
||||
const helper = keyedHelperName(lowerer, "%obj.hasOwn", key);
|
||||
const helper = `%obj.hasOwn.${lowerer.arrHofHelpers.size}`;
|
||||
lowerer.arrHofHelpers.set(key, helper);
|
||||
const shape = lowerer.shapes.get(shapeId)!;
|
||||
const recT: IrType = { kind: "record", shapeId };
|
||||
@@ -6990,7 +6990,7 @@ function classAssignHelper(
|
||||
const key = `obj.assignClass:${className}:${srcShapeId}`;
|
||||
const existing = lowerer.arrHofHelpers.get(key);
|
||||
if (existing) return existing;
|
||||
const helper = keyedHelperName(lowerer, "%obj.assignClass", key);
|
||||
const helper = `%obj.assignClass.${lowerer.arrHofHelpers.size}`;
|
||||
lowerer.arrHofHelpers.set(key, helper);
|
||||
const sShape = lowerer.shapes.get(srcShapeId)!;
|
||||
const oT: IrType = { kind: "object", className };
|
||||
@@ -7058,7 +7058,7 @@ function recordAssignHelper(
|
||||
const key = `obj.assign:${targetShapeId}:${srcShapeId}`;
|
||||
const existing = lowerer.arrHofHelpers.get(key);
|
||||
if (existing) return existing;
|
||||
const helper = keyedHelperName(lowerer, "%obj.assign", key);
|
||||
const helper = `%obj.assign.${lowerer.arrHofHelpers.size}`;
|
||||
lowerer.arrHofHelpers.set(key, helper);
|
||||
const sShape = lowerer.shapes.get(srcShapeId)!;
|
||||
const tShape = lowerer.shapes.get(targetShapeId)!;
|
||||
@@ -8515,7 +8515,7 @@ function lowerObjectStaticCall(
|
||||
const key = `obj.${member}:${argIr.shapeId}:${typeKey(resultT)}`;
|
||||
let helper = lowerer.arrHofHelpers.get(key);
|
||||
if (!helper) {
|
||||
helper = keyedHelperName(lowerer, `%obj.${member}`, key);
|
||||
helper = `%obj.${member}.${lowerer.arrHofHelpers.size}`;
|
||||
const recT = argIr;
|
||||
const ref: IrExpr = { kind: "varRef", localId: "r.0", type: recT, loc };
|
||||
const outRef: IrExpr = { kind: "varRef", localId: "out.0", type: resultT, loc };
|
||||
@@ -9151,7 +9151,7 @@ function lowerInlineArrowBind(
|
||||
const length = firstDefault < 0 ? arrow.parameters.nodes.length : firstDefault;
|
||||
const key = `arrow.bind:${typeKey(type)}:${length}`;
|
||||
const existing = lowerer.arrHofHelpers.get(key);
|
||||
const name = existing ?? keyedHelperName(lowerer, "%arrow.bind", key);
|
||||
const name = existing ?? `%arrow.bind.${lowerer.arrHofHelpers.size}`;
|
||||
if (!existing) {
|
||||
lowerer.arrHofHelpers.set(key, name);
|
||||
lowerer.freshClosureAdapters.add(name);
|
||||
@@ -10080,7 +10080,7 @@ function lowerUnionObjectDynFieldCall(
|
||||
const receiverRef = varRef(receiverLocal.id, receiverT, loc);
|
||||
let helper = lowerer.unionCallHelpers.get(key);
|
||||
if (!helper) {
|
||||
helper = keyedHelperName(lowerer, "%union.call", key);
|
||||
helper = `%union.call.${lowerer.unionCallHelpers.size}`;
|
||||
lowerer.unionCallHelpers.set(key, helper);
|
||||
const receiverRef = varRef("this.0", receiverT, loc);
|
||||
const branch = (plan: (typeof plans)[number]): IrStmt[] => {
|
||||
@@ -10173,7 +10173,7 @@ function unionObjectMethodHelper(
|
||||
const key = `${receiverT.unionId}:${method}:${shapes.map((shape) => `${shape.mode}:${typeKey(shape.type)}`).join(",")}:${typeKey(resultT)}`;
|
||||
const existing = lowerer.unionCallHelpers.get(key);
|
||||
if (existing) return existing;
|
||||
const name = keyedHelperName(lowerer, "%union.call", key);
|
||||
const name = `%union.call.${lowerer.unionCallHelpers.size}`;
|
||||
lowerer.unionCallHelpers.set(key, name);
|
||||
// A probe that catches a refusal while the body lowers must not leave
|
||||
// the name registered for a helper that was never emitted.
|
||||
@@ -10367,7 +10367,7 @@ export function lowerStaticNumberGlobal(
|
||||
const key = `parseInt.optional:${unionT.unionId}`;
|
||||
let helper = lowerer.valueHelpers.get(key);
|
||||
if (!helper) {
|
||||
helper = keyedHelperName(lowerer, "%parseInt.optional", key);
|
||||
helper = `%parseInt.optional.${lowerer.valueHelpers.size}`;
|
||||
lowerer.valueHelpers.set(key, helper);
|
||||
const value: IrExpr = { kind: "varRef", localId: "value.0", type: unionT, loc };
|
||||
const radixRef: IrExpr = { kind: "varRef", localId: "radix.0", type: F64, loc };
|
||||
@@ -10493,20 +10493,40 @@ export function lowerStaticNumberGlobal(
|
||||
return null;
|
||||
}
|
||||
|
||||
/** `crypto.getRandomValues(view)` on the WebCrypto global or webcrypto. */
|
||||
function lowerWebCryptoGetRandomValues(
|
||||
/** `crypto.getRandomValues(view)` and `crypto.randomUUID()` on the
|
||||
* WebCrypto global or webcrypto. Other argument counts (JavaScript only)
|
||||
* invoke the dynamic object's method, which ignores extra arguments and
|
||||
* throws Node's missing-argument TypeError. */
|
||||
function lowerWebCryptoMethodCall(
|
||||
lowerer: Lowerer,
|
||||
call: ts.CallExpression,
|
||||
access: ts.PropertyAccessExpression,
|
||||
): IrExpr | null {
|
||||
const member = access.name.text;
|
||||
if (
|
||||
access.name.text !== "getRandomValues" ||
|
||||
(member !== "getRandomValues" && member !== "randomUUID") ||
|
||||
call.questionDotToken ||
|
||||
access.questionDotToken ||
|
||||
!isWebCryptoReceiver(lowerer, access.expression)
|
||||
)
|
||||
return null;
|
||||
return lowerCryptoGetRandomValues(lowerer, call);
|
||||
const args = call.arguments.nodes;
|
||||
if (member === "getRandomValues" && args.length === 1) {
|
||||
return lowerCryptoGetRandomValues(lowerer, call);
|
||||
}
|
||||
if (member === "randomUUID" && args.length === 0) {
|
||||
return { kind: "libCall", fn: "crypto.randomUUID", args: [], type: STRING, loc: locOf(call) };
|
||||
}
|
||||
if (args.some(ts.isSpreadElement)) return null;
|
||||
return {
|
||||
kind: "dynInvoke",
|
||||
recv: lowerer.lowerExprExpecting(access.expression, DYN),
|
||||
method: member,
|
||||
calleeName: v8CalleeText(access),
|
||||
args: args.map((arg) => lowerer.lowerExprExpecting(arg, DYN)),
|
||||
type: DYN,
|
||||
loc: locOf(call),
|
||||
};
|
||||
}
|
||||
|
||||
/** A then handler's promise-armed result (`.then(() => cached ?? load())`
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import { keyedHelperName } from "./stable-names.js";
|
||||
import { lowerValueReturningConstructorNew } from "./js-dynamic-objects.js";
|
||||
import { adaptPromiseExecutor } from "./promise-executor-adapter.js";
|
||||
import { dynUndefinedExpr, nodeThrowExpr, varRef } from "../../ir/build.js";
|
||||
@@ -2405,7 +2404,8 @@ export function collectClassShapeInner(
|
||||
for (const member of decl.members.nodes) {
|
||||
if (
|
||||
(functionLocalClass(decl) || checkedStatics) &&
|
||||
isJsSourceFile(ts.getSourceFileOfNode(decl)) &&
|
||||
(isJsSourceFile(ts.getSourceFileOfNode(decl)) ||
|
||||
(functionLocalClass(decl) && ts.isMethodDeclaration(member))) &&
|
||||
(ts.isClassStaticBlockDeclaration(member) ||
|
||||
ts
|
||||
.getModifiers(member)
|
||||
@@ -4664,7 +4664,7 @@ export function genericClassInstanceType(
|
||||
);
|
||||
}
|
||||
const ordinal = gci.instances.size;
|
||||
const name = keyedHelperName(lowerer, `${gci.family.def.name}%`, key, "");
|
||||
const name = `${gci.family.def.name}%${ordinal}`;
|
||||
const entry: { name: string; info: ClassInfo | null; poisoned?: boolean } = { name, info: null };
|
||||
gci.instances.set(key, entry);
|
||||
const bindings = new Map<ts.Symbol, IrType>();
|
||||
@@ -5873,9 +5873,10 @@ export function lowerClassExpressionInfo(
|
||||
decoratorNodesOf(member).length ||
|
||||
(!isJsSourceFile(ts.getSourceFileOfNode(expr)) &&
|
||||
(ts.isClassStaticBlockDeclaration(member) ||
|
||||
ts
|
||||
.getModifiers(member)
|
||||
?.some((modifier) => modifier.kind === ts.SyntaxKind.StaticKeyword))) ||
|
||||
(!ts.isMethodDeclaration(member) &&
|
||||
ts
|
||||
.getModifiers(member)
|
||||
?.some((modifier) => modifier.kind === ts.SyntaxKind.StaticKeyword)))) ||
|
||||
(ts.isMethodDeclaration(member) && member.typeParameters?.nodes.length),
|
||||
))
|
||||
) {
|
||||
@@ -7911,7 +7912,7 @@ export function classIteratorDrainCall(
|
||||
const key = `${cit.className}:${typeKey(outElem)}`;
|
||||
let name = lowerer.iterDrainHelpers.get(key);
|
||||
if (!name) {
|
||||
name = keyedHelperName(lowerer, "%iter.drain", key);
|
||||
name = `%iter.drain.${lowerer.iterDrainHelpers.size}`;
|
||||
lowerer.iterDrainHelpers.set(key, name);
|
||||
const recvT: IrType = { kind: "object", className: cit.className };
|
||||
const recvRef: IrExpr = { kind: "varRef", localId: "r.0", type: recvT, loc };
|
||||
@@ -8025,7 +8026,7 @@ export function classIteratorRestDrainCall(
|
||||
const key = `it:${cit.iterT.className}`;
|
||||
let name = lowerer.iterDrainHelpers.get(key);
|
||||
if (!name) {
|
||||
name = keyedHelperName(lowerer, "%iter.drain", key);
|
||||
name = `%iter.drain.${lowerer.iterDrainHelpers.size}`;
|
||||
lowerer.iterDrainHelpers.set(key, name);
|
||||
const itRef: IrExpr = { kind: "varRef", localId: "it.0", type: cit.iterT, loc };
|
||||
const outRef: IrExpr = { kind: "varRef", localId: "out.0", type: outT, loc };
|
||||
@@ -10533,7 +10534,7 @@ export function lowerNew(lowerer: Lowerer, expr: ts.NewExpression): IrExpr {
|
||||
const key = `x509.record:${isBytes ? "bytes" : "str"}`;
|
||||
let helper = lowerer.valueHelpers.get(key);
|
||||
if (!helper) {
|
||||
helper = keyedHelperName(lowerer, "%x509.record", key);
|
||||
helper = `%x509.record.${lowerer.valueHelpers.size}`;
|
||||
lowerer.valueHelpers.set(key, helper);
|
||||
const dataT = data.type;
|
||||
const dRef: IrExpr = { kind: "varRef", localId: "d.0", type: dataT, loc };
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import { keyedHelperName } from "./stable-names.js";
|
||||
import { dynUndefinedExpr, boolLit } from "../../ir/build.js";
|
||||
/* The dgram/dns-surface lowering (node:dgram + node:dns — a spoke module
|
||||
* like lower-server.ts): module-function calls (dgram createSocket, dns
|
||||
@@ -358,7 +357,7 @@ function checkedDynPromise(
|
||||
const key = `dns.promise:${typeKey(declared)}`;
|
||||
let helper = lowerer.valueHelpers.get(key);
|
||||
if (!helper) {
|
||||
helper = keyedHelperName(lowerer, "%dns.promise", key);
|
||||
helper = `%dns.promise.${lowerer.valueHelpers.size}`;
|
||||
lowerer.valueHelpers.set(key, helper);
|
||||
const promiseT: IrType = { kind: "promise", inner: DYN };
|
||||
lowerer.liftedFns.push({
|
||||
@@ -892,7 +891,7 @@ export function lowerPromisifiedDnsCall(
|
||||
const key = `dnsPromise:${JSON.stringify(value)}`;
|
||||
let name = lowerer.valueHelpers.get(key);
|
||||
if (!name) {
|
||||
name = keyedHelperName(lowerer, "%dnsPromise", key);
|
||||
name = `%dnsPromise.${lowerer.valueHelpers.size}`;
|
||||
lowerer.valueHelpers.set(key, name);
|
||||
lowerer.liftedFns.push({
|
||||
name,
|
||||
|
||||
@@ -42,7 +42,6 @@
|
||||
* emit-overrides block at the bottom of this file (per-event
|
||||
* monomorphization of the override body, dispatch on the dynamic class,
|
||||
* super.emit as the prototype chain). */
|
||||
import { keyedHelperName } from "./stable-names.js";
|
||||
import { checkKeypressListener } from "./builtins/readline.js";
|
||||
import * as ts from "../ts7/adapter.js";
|
||||
import type { Lowerer } from "./lowerer.js";
|
||||
@@ -632,7 +631,7 @@ function lowerFlexListenerCall(
|
||||
const key = `emitter.${registering ? "onFlex" : "offDyn"}:${typeKey(recvT)}`;
|
||||
let helper = lowerer.arrHofHelpers.get(key);
|
||||
if (!helper) {
|
||||
helper = keyedHelperName(lowerer, `%emitter.${registering ? "onFlex" : "offFlex"}`, key);
|
||||
helper = `%emitter.${registering ? "onFlex" : "offFlex"}.${lowerer.arrHofHelpers.size}`;
|
||||
lowerer.arrHofHelpers.set(key, helper);
|
||||
const params: IrParam[] = [
|
||||
{ localId: "r.0", name: "r", type: recvT },
|
||||
@@ -851,7 +850,7 @@ function lowerDynListenerCall(
|
||||
const existing = lowerer.arrHofHelpers.get(key);
|
||||
let helper = existing;
|
||||
if (!helper) {
|
||||
helper = keyedHelperName(lowerer, `%emitter.${registering ? "onDyn" : "offDyn"}`, key);
|
||||
helper = `%emitter.${registering ? "onDyn" : "offDyn"}.${lowerer.arrHofHelpers.size}`;
|
||||
lowerer.arrHofHelpers.set(key, helper);
|
||||
const check: IrStmt = {
|
||||
kind: "exprStmt",
|
||||
@@ -1001,7 +1000,7 @@ export function lowerDynamicEmitterEmit(
|
||||
const key = `emitter.dynEmit:${typeKey(recvT)}:${k}`;
|
||||
let helper = lowerer.arrHofHelpers.get(key);
|
||||
if (!helper) {
|
||||
helper = keyedHelperName(lowerer, "%emitter.dynEmit", key);
|
||||
helper = `%emitter.dynEmit.${lowerer.arrHofHelpers.size}`;
|
||||
lowerer.arrHofHelpers.set(key, helper);
|
||||
const params: IrParam[] = [
|
||||
{ localId: "r.0", name: "r", type: recvT },
|
||||
@@ -1093,7 +1092,7 @@ export function lowerDynamicEmitterOn(
|
||||
const key = `emitter.dynOn:${typeKey(recvT)}`;
|
||||
let helper = lowerer.arrHofHelpers.get(key);
|
||||
if (!helper) {
|
||||
helper = keyedHelperName(lowerer, "%emitter.dynOn", key);
|
||||
helper = `%emitter.dynOn.${lowerer.arrHofHelpers.size}`;
|
||||
lowerer.arrHofHelpers.set(key, helper);
|
||||
const params: IrParam[] = [
|
||||
{ localId: "r.0", name: "r", type: recvT },
|
||||
@@ -2360,7 +2359,7 @@ function emitDispatchExpr(
|
||||
const key = `emitter.emitDispatch:${typeKey(recvT)}:${name}`;
|
||||
let helper = lowerer.arrHofHelpers.get(key);
|
||||
if (!helper) {
|
||||
helper = keyedHelperName(lowerer, "%emitter.emitDispatch", key);
|
||||
helper = `%emitter.emitDispatch.${lowerer.arrHofHelpers.size}`;
|
||||
lowerer.arrHofHelpers.set(key, helper);
|
||||
const params: IrParam[] = [
|
||||
{ localId: "r.0", name: "r", type: recvT },
|
||||
@@ -2425,7 +2424,7 @@ function eventsOnceBoxer(
|
||||
const key = `eventsOnce:${typeKey(fnType)}`;
|
||||
let name = lowerer.valueHelpers.get(key);
|
||||
if (name === undefined) {
|
||||
name = keyedHelperName(lowerer, "%events.onceArgs", key);
|
||||
name = `%events.onceArgs.${lowerer.valueHelpers.size}`;
|
||||
lowerer.valueHelpers.set(key, name);
|
||||
const params: IrParam[] = tuple.map((type, i) => ({
|
||||
localId: `%once.a${i}`,
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import { keyedHelperName } from "./stable-names.js";
|
||||
import { lowerWebAssemblyValue, refuseWebAssemblyUse } from "./builtins/webassembly.js";
|
||||
import { dynUndefinedExpr, nodeThrowExpr, numLit, strLit, varRef } from "../../ir/build.js";
|
||||
import { InternalCompilerError } from "../../errors.js";
|
||||
@@ -2275,11 +2274,7 @@ export function cjsAccessorSnapshotOf(
|
||||
if (!lexerVisibleAccessors(ts.getSourceFileOfNode(getter)).includes(getter)) return null;
|
||||
const call = cjsAccessorCall(lowerer, getter, locOf(getter));
|
||||
if (call === null) return null;
|
||||
const at = locOf(getter);
|
||||
const snapshot = {
|
||||
id: keyedHelperName(lowerer, "%g.cjsSnapshot", `${at.file}:${at.start}:${at.end}`),
|
||||
type: call.type,
|
||||
};
|
||||
const snapshot = { id: `%g.cjsSnapshot.${lowerer.cjsAccessorSnapshots.size}`, type: call.type };
|
||||
lowerer.globalsList.push({
|
||||
id: snapshot.id,
|
||||
name: "%cjsSnapshot",
|
||||
@@ -2304,7 +2299,7 @@ export function cjsAccessorSnapshotStores(
|
||||
if (getters.length === 0) return [];
|
||||
let readyId = lowerer.cjsSnapshotReady.get(dep);
|
||||
if (readyId === undefined) {
|
||||
readyId = keyedHelperName(lowerer, "%g.cjsSnapshotReady", dep.fileName);
|
||||
readyId = `%g.cjsSnapshotReady.${lowerer.cjsSnapshotReady.size}`;
|
||||
lowerer.globalsList.push({ id: readyId, name: "%cjsSnapshotReady", type: BOOL, mutable: true });
|
||||
lowerer.cjsSnapshotReady.set(dep, readyId);
|
||||
}
|
||||
|
||||
@@ -270,7 +270,7 @@ export function lowerLambda(
|
||||
const nameIdent =
|
||||
!ts.isArrowFunction(node) && node.name && ts.isIdentifier(node.name) ? node.name : null;
|
||||
const baseName = nameIdent ? nameIdent.text : "";
|
||||
const fnName = lowerer.closureName(locOf(node), baseName ? `_${baseName}` : "");
|
||||
const fnName = `%fn${lowerer.lambdaCounter++}${baseName ? `_${baseName}` : ""}`;
|
||||
// Named function expressions/declarations can self-reference by name; an
|
||||
// object-literal method's name is a PROPERTY, not a binding — no self.
|
||||
const selfSymbol =
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import { keyedHelperName } from "./stable-names.js";
|
||||
import {
|
||||
dynUndefinedExpr,
|
||||
boolLit,
|
||||
@@ -269,7 +268,7 @@ function errorInspectHelper(
|
||||
const key = `insp.userError:${typeKey(t)}`;
|
||||
const existing = lowerer.inspectHelpers.get(key);
|
||||
if (existing) return existing;
|
||||
const name = keyedHelperName(lowerer, "%util.insp", key);
|
||||
const name = `%util.insp.${lowerer.inspectHelpers.size}`;
|
||||
lowerer.inspectHelpers.set(key, name);
|
||||
const v = (): IrExpr => varRef("v.0", t, loc);
|
||||
const r = (): IrExpr => varRef("r.0", F64, loc);
|
||||
@@ -1146,7 +1145,7 @@ function inspectHelper(lowerer: Lowerer, t: IrType, loc: SrcLoc): string {
|
||||
const key = `insp:${typeKey(t)}`;
|
||||
const existing = lowerer.inspectHelpers.get(key);
|
||||
if (existing) return existing;
|
||||
const name = keyedHelperName(lowerer, "%util.insp", key);
|
||||
const name = `%util.insp.${lowerer.inspectHelpers.size}`;
|
||||
lowerer.inspectHelpers.set(key, name);
|
||||
|
||||
const v = (): IrExpr => varRef("v.0", t, loc);
|
||||
@@ -2075,7 +2074,7 @@ function formatUnionHelper(
|
||||
const existing = lowerer.inspectHelpers.get(key);
|
||||
if (existing) return existing;
|
||||
const loc = helperLoc(site);
|
||||
const name = keyedHelperName(lowerer, "%util.fmtv", key);
|
||||
const name = `%util.fmtv.${lowerer.inspectHelpers.size}`;
|
||||
lowerer.inspectHelpers.set(key, name);
|
||||
const def = lowerer.unions.get(t.unionId);
|
||||
if (!def) throw new InternalCompilerError(`format value over unknown union ${t.unionId}`);
|
||||
|
||||
@@ -34,7 +34,6 @@
|
||||
* statics-bearing mixin classes outside a plain top-level statement
|
||||
* (their declaration-time code must run exactly where the call
|
||||
* evaluates). */
|
||||
import { positionTag } from "./structural-positions.js";
|
||||
import * as ts from "../ts7/adapter.js";
|
||||
import type { Lowerer } from "./lowerer.js";
|
||||
import { PoisonError } from "./lowerer.js";
|
||||
@@ -453,7 +452,7 @@ function instantiateMixinCall(
|
||||
const base = mixinBaseClassOf(lowerer, call.arguments.nodes[0]!);
|
||||
const instName = lowerer.qualify(
|
||||
ts.getSourceFileOfNode(call),
|
||||
`%mx${positionTag(lowerer, call)}.${shape.classNode.name?.text ?? ""}`,
|
||||
`%mx${ts.tokenStartOfNode(call)}.${shape.classNode.name?.text ?? ""}`,
|
||||
);
|
||||
const ordinal = lowerer.mixinOrdinals.get(shape.classNode) ?? 0;
|
||||
lowerer.mixinOrdinals.set(shape.classNode, ordinal + 1);
|
||||
|
||||
@@ -17,7 +17,6 @@
|
||||
* writes the original back on restore(). Each mock registers a closure
|
||||
* that calls its restore() with its tracker (scr_test.c): the module's
|
||||
* `mock`, or the test's t.mock, which is reset when the test ends. */
|
||||
import { keyedHelperName } from "./stable-names.js";
|
||||
import * as ts from "../ts7/adapter.js";
|
||||
import {
|
||||
BOOL,
|
||||
@@ -137,7 +136,7 @@ function liftOnce(
|
||||
const key = `mock.${role}:${owner}`;
|
||||
const existing = lowerer.assertHelpers.get(key);
|
||||
if (existing) return existing;
|
||||
const name = keyedHelperName(lowerer, `%mock.${role}`, key);
|
||||
const name = `%mock.${role}.${lowerer.assertHelpers.size}`;
|
||||
lowerer.assertHelpers.set(key, name);
|
||||
build(name);
|
||||
return name;
|
||||
|
||||
@@ -34,7 +34,7 @@ test("retains dependencies from function storage, captures, generator channels a
|
||||
const completed = record("completed");
|
||||
const body = record("body");
|
||||
const global = record("global", record("nested"));
|
||||
const wanted = lowerer.shapes.shapes.map((shape) => shape.id).sort();
|
||||
const wanted = lowerer.shapes.shapes.map((shape) => shape.id);
|
||||
record("unused");
|
||||
const unionId = lowerer.unions.intern([body, UNDEFINED_T]);
|
||||
const unusedUnion = lowerer.unions.intern([F64, UNDEFINED_T]);
|
||||
@@ -162,7 +162,7 @@ test("closes nested structural types and index-signature values", () => {
|
||||
sent = record("sent"),
|
||||
indexed = record("indexed");
|
||||
const containerId = lowerer.shapes.intern([], false, indexed);
|
||||
const wanted = lowerer.shapes.shapes.map((shape) => shape.id).sort();
|
||||
const wanted = lowerer.shapes.shapes.map((shape) => shape.id);
|
||||
record("unused");
|
||||
const nested: IrType = {
|
||||
kind: "func",
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import { THREADS_MODULE } from "../threads-import.js";
|
||||
import { positionTag } from "./structural-positions.js";
|
||||
import { EXPERIMENTAL_MODULE } from "../experimental-import.js";
|
||||
import { dynUndefinedExpr } from "../../ir/build.js";
|
||||
import { everyStmtList, everyTypeChild } from "../../ir/traverse.js";
|
||||
@@ -1075,14 +1074,8 @@ export function moduleArtifacts(
|
||||
else delete rest.abstractMethods;
|
||||
return rest;
|
||||
}),
|
||||
// Ids come from what each shape and union is (keyedHelperName), so
|
||||
// ordering by id gives every lowering order the same module.
|
||||
records: lowerer.shapes.shapes
|
||||
.filter((r) => shapeIds.has(r.id))
|
||||
.sort((a, b) => (a.id < b.id ? -1 : a.id > b.id ? 1 : 0)),
|
||||
unions: lowerer.unions.unions
|
||||
.filter((u) => unionIds.has(u.id))
|
||||
.sort((a, b) => (a.id < b.id ? -1 : a.id > b.id ? 1 : 0)),
|
||||
records: lowerer.shapes.shapes.filter((r) => shapeIds.has(r.id)),
|
||||
unions: lowerer.unions.unions.filter((u) => unionIds.has(u.id)),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1684,7 +1677,7 @@ export function collectGlobals(
|
||||
}
|
||||
if (type.kind === "void") lowerer.badType(stmt.name, lowerer.typeOf(stmt.name));
|
||||
const g: IrGlobal = {
|
||||
id: `%g.${tag}${nsPathPrefix(lowerer, stmt)}%alias.${stmt.name.text}%${positionTag(lowerer, stmt)}`,
|
||||
id: `%g.${tag}${nsPathPrefix(stmt)}%alias.${stmt.name.text}%${ts.tokenStartOfNode(stmt)}`,
|
||||
name: stmt.name.text,
|
||||
type,
|
||||
mutable: false,
|
||||
@@ -1761,7 +1754,7 @@ export function collectGlobals(
|
||||
// (`%g.e.A%B.x`), so `namespace A { export const x }` and a
|
||||
// top-level `const x` never collide; non-exported members add the
|
||||
// declaring block's position (block-local bindings).
|
||||
const nsPrefix = nsPathPrefix(lowerer, stmt, list.declarations.nodes[0]);
|
||||
const nsPrefix = nsPathPrefix(stmt, list.declarations.nodes[0]);
|
||||
for (const decl of list.declarations.nodes) {
|
||||
// --provenance-sources: an elided pure-annotated dead const in a
|
||||
// fetched source module registers no global (the statement
|
||||
|
||||
@@ -36,7 +36,6 @@
|
||||
* of the compiler already takes (a body running before the declaring
|
||||
* statement is not modeled — same as top-level const reads through early
|
||||
* calls). */
|
||||
import { positionTag, type PositionTagOwner } from "./structural-positions.js";
|
||||
import * as ts from "../ts7/adapter.js";
|
||||
import { packageShimFor } from "../package-shims.js";
|
||||
import type { Lowerer } from "./lowerer.js";
|
||||
@@ -165,11 +164,7 @@ function registerTypeOnlyBlocks(lowerer: Lowerer, decl: ts.ModuleDeclaration): v
|
||||
* distinct (resolution is by symbol; names only need uniqueness and
|
||||
* determinism). `modifierDecl` carries the declaration whose modifiers
|
||||
* decide exportedness (the first declarator of a variable statement). */
|
||||
export function nsPathPrefix(
|
||||
owner: PositionTagOwner,
|
||||
node: ts.Node,
|
||||
modifierDecl?: ts.Node,
|
||||
): string {
|
||||
export function nsPathPrefix(node: ts.Node, modifierDecl?: ts.Node): string {
|
||||
const parent = node.parent;
|
||||
if (parent === undefined || !ts.isModuleBlock(parent)) return "";
|
||||
const flagsHost = modifierDecl ?? node;
|
||||
@@ -184,10 +179,10 @@ export function nsPathPrefix(
|
||||
const nested = p.parent !== undefined && ts.isModuleBlock(p.parent);
|
||||
const nonExported =
|
||||
nested && (ts.getCombinedModifierFlags(p) & ts.ModifierFlags.Export) === 0;
|
||||
segs.unshift(p.name.text + (nonExported ? `%${positionTag(owner, p)}` : ""));
|
||||
segs.unshift(p.name.text + (nonExported ? `%${ts.tokenStartOfNode(p)}` : ""));
|
||||
}
|
||||
}
|
||||
return `${segs.join("%")}${exported ? "" : `%${positionTag(owner, parent)}`}.`;
|
||||
return `${segs.join("%")}${exported ? "" : `%${ts.tokenStartOfNode(parent)}`}.`;
|
||||
}
|
||||
|
||||
/** The registered namespace-block kind a symbol's declaration sits in
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import { keyedHelperName } from "./stable-names.js";
|
||||
import {
|
||||
dynUndefinedExpr,
|
||||
nodeThrowExpr,
|
||||
@@ -184,7 +183,7 @@ export function voidizedCallback(lowerer: Lowerer, cb: IrExpr, loc: SrcLoc): IrE
|
||||
const toT = funcOf([...fromT.params], VOID);
|
||||
const key = `server.voidcb:${typeKey(fromT)}`;
|
||||
const existing = lowerer.arrHofHelpers.get(key);
|
||||
const name = existing ?? keyedHelperName(lowerer, "%server.voidcb", key);
|
||||
const name = existing ?? `%server.voidcb.${lowerer.arrHofHelpers.size}`;
|
||||
if (!existing) {
|
||||
lowerer.arrHofHelpers.set(key, name);
|
||||
const impl = `${name}.impl`;
|
||||
@@ -280,7 +279,7 @@ function receiverReturningCall(
|
||||
): IrExpr {
|
||||
const key = `server.recvret:${fn}:${opts?.prefix?.fn ?? ""}:${(opts?.mainArgIndices ?? []).join(".")}:${callArgs.map((a) => typeKey(a.type)).join(",")}`;
|
||||
const existing = lowerer.arrHofHelpers.get(key);
|
||||
const name = existing ?? keyedHelperName(lowerer, "%server.recvret", key);
|
||||
const name = existing ?? `%server.recvret.${lowerer.arrHofHelpers.size}`;
|
||||
if (!existing) {
|
||||
lowerer.arrHofHelpers.set(key, name);
|
||||
const params = callArgs.map((a, i) => ({ localId: `p${i}.0`, name: `p${i}`, type: a.type }));
|
||||
@@ -591,7 +590,7 @@ function h2HeadersCallbackAdapter(
|
||||
);
|
||||
const key = `h2.hdrcb:${handleKind}:${withStatus}:${typeKey(fromT)}:${shapeId ?? ""}`;
|
||||
const existing = lowerer.arrHofHelpers.get(key);
|
||||
const name = existing ?? keyedHelperName(lowerer, "%h2.hdrcb", key);
|
||||
const name = existing ?? `%h2.hdrcb.${lowerer.arrHofHelpers.size}`;
|
||||
if (!existing) {
|
||||
lowerer.arrHofHelpers.set(key, name);
|
||||
const impl = `${name}.impl`;
|
||||
@@ -3584,7 +3583,7 @@ export function lowerCompatReqStreamOptionalCall(
|
||||
type: unionT,
|
||||
loc,
|
||||
};
|
||||
const id = lowerer.closureName(loc, "", "chain");
|
||||
const id = `chain.${lowerer.chainCounter++}`;
|
||||
const body = lowerH2StreamMethodCall(lowerer, call, call.expression, {
|
||||
kind: "chainRecv",
|
||||
id,
|
||||
@@ -4425,7 +4424,7 @@ function lowerHttpCreateServerForms(
|
||||
const hasMax = opts.maxHeaderSize !== null;
|
||||
const key = `server.opts:${opts.joinDup ? 1 : 0}:${opts.allowMissingHost ? 1 : 0}:${hasBuffer ? 1 : 0}:${hasMax ? 1 : 0}`;
|
||||
const existing = lowerer.arrHofHelpers.get(key);
|
||||
const name = existing ?? keyedHelperName(lowerer, "%server.opts", key);
|
||||
const name = existing ?? `%server.opts.${lowerer.arrHofHelpers.size}`;
|
||||
if (!existing) {
|
||||
lowerer.arrHofHelpers.set(key, name);
|
||||
const params = [
|
||||
@@ -8010,7 +8009,7 @@ function lowerHttpResMethodCall(
|
||||
const all = [...callArgs, cb];
|
||||
const key = `server.endcb:${fn}:${all.map((a) => typeKey(a.type)).join(",")}`;
|
||||
const existing = lowerer.arrHofHelpers.get(key);
|
||||
const helper = existing ?? keyedHelperName(lowerer, "%server.endcb", key);
|
||||
const helper = existing ?? `%server.endcb.${lowerer.arrHofHelpers.size}`;
|
||||
if (!existing) {
|
||||
lowerer.arrHofHelpers.set(key, helper);
|
||||
const params = all.map((a, i) => ({ localId: `p${i}.0`, name: `p${i}`, type: a.type }));
|
||||
|
||||
@@ -2278,7 +2278,7 @@ function lowerStaticNamespaceImport(
|
||||
): IrExpr {
|
||||
const loc = locOf(call);
|
||||
const resultT: IrType & { kind: "promise" } = { kind: "promise", inner: ns };
|
||||
const fnName = lowerer.closureName(loc, "_staticImport");
|
||||
const fnName = `%fn${lowerer.lambdaCounter++}_staticImport`;
|
||||
const fnType: IrType & { kind: "func" } = { kind: "func", params: [], ret: resultT };
|
||||
const fnCtx = newFnCtx(true, null, fnType, ns);
|
||||
fnCtx.isAsync = true;
|
||||
|
||||
@@ -3965,9 +3965,13 @@ function stdlibGlobalTokenDestructure(
|
||||
const canonical = ts.symbolName(member) === "global" ? "globalThis" : ts.symbolName(member);
|
||||
token = `[builtin ${canonical}]`;
|
||||
alias = canonical;
|
||||
// Builtin constructors read as their interned function values, the
|
||||
// same values the bare and `globalThis.X` spellings read.
|
||||
value = builtinGlobalValue(canonical, locOf(el), true);
|
||||
// Builtin constructors read as their interned function values, and
|
||||
// crypto as the WebCrypto object: the same values the bare and
|
||||
// `globalThis.X` spellings read.
|
||||
value =
|
||||
canonical === "crypto"
|
||||
? { kind: "libCall", fn: "webcrypto.object", args: [], type: DYN, loc: locOf(el) }
|
||||
: builtinGlobalValue(canonical, locOf(el), true);
|
||||
} else if (globalName === "console" && !CONSOLE_CALL_MEMBERS.has(propName)) {
|
||||
token = `[builtin console.${propName}]`;
|
||||
} else if (TOKEN_OPAQUE_GLOBALS.has(globalName)) {
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import { keyedHelperName } from "./stable-names.js";
|
||||
import { nodeThrowExpr, boolLit, numLit, strLit } from "../../ir/build.js";
|
||||
import { isValueStream, valueStreamOf } from "./builtins/hash-stream.js";
|
||||
import { InternalCompilerError } from "../../errors.js";
|
||||
@@ -316,7 +315,7 @@ function lowerStreamCallback(
|
||||
);
|
||||
}
|
||||
}
|
||||
const fnName = lowerer.closureName(loc, `_${which}cb`);
|
||||
const fnName = `%fn${lowerer.lambdaCounter++}_${which}cb`;
|
||||
const fnCtx = newFnCtx(true, null, funcType, funcType.ret);
|
||||
lowerer.fnStack.push(fnCtx);
|
||||
try {
|
||||
@@ -1149,7 +1148,7 @@ function streamMethodWrapper(
|
||||
paramTypes.push(got.type);
|
||||
}
|
||||
const ret = found.sig.ret;
|
||||
const fnName = lowerer.closureName(loc, `_${which}vt`);
|
||||
const fnName = `%fn${lowerer.lambdaCounter++}_${which}vt`;
|
||||
const funcType = funcOf([thisType, ...paramTypes], ret);
|
||||
lowerer.fnStack.push(newFnCtx(true, null, funcType, ret));
|
||||
try {
|
||||
@@ -1695,7 +1694,7 @@ function fromGenHelpers(
|
||||
const key = `streamFromGen:${role}:${typeKey(gen)}`;
|
||||
let name = lowerer.valueHelpers.get(key);
|
||||
if (!name) {
|
||||
name = keyedHelperName(lowerer, `%stream.fromGen.${role}`, key);
|
||||
name = `%stream.fromGen.${role}.${lowerer.valueHelpers.size}`;
|
||||
lowerer.valueHelpers.set(key, name);
|
||||
lowerer.liftedFns.push({
|
||||
name,
|
||||
@@ -1784,7 +1783,7 @@ function classIteratorObject(
|
||||
const key = `streamFromIter:${cit.iterT.className}`;
|
||||
let name = lowerer.valueHelpers.get(key);
|
||||
if (!name) {
|
||||
name = keyedHelperName(lowerer, "%stream.fromIter", key);
|
||||
name = `%stream.fromIter.${lowerer.valueHelpers.size}`;
|
||||
lowerer.valueHelpers.set(key, name);
|
||||
const nextName = `${name}.next`;
|
||||
const nextT = funcOf([], DYN);
|
||||
@@ -1945,7 +1944,7 @@ function classAsyncIteratorObject(
|
||||
const key = `streamFromAsyncIter:${cit.className}`;
|
||||
let name = lowerer.valueHelpers.get(key);
|
||||
if (!name) {
|
||||
name = keyedHelperName(lowerer, "%stream.fromAsyncIter", key);
|
||||
name = `%stream.fromAsyncIter.${lowerer.valueHelpers.size}`;
|
||||
lowerer.valueHelpers.set(key, name);
|
||||
const it: IrLocal = { id: "it.0", name: "it", type: cit.iterT, mutable: false, boxed: true };
|
||||
const src: IrLocal = {
|
||||
@@ -3129,7 +3128,7 @@ function pipelineBoxHelper(
|
||||
const key = `pipelineBox:${typeKey(promiseT)}`;
|
||||
let name = lowerer.valueHelpers.get(key);
|
||||
if (!name) {
|
||||
name = keyedHelperName(lowerer, "%stream.pipelineBox", key);
|
||||
name = `%stream.pipelineBox.${lowerer.valueHelpers.size}`;
|
||||
lowerer.valueHelpers.set(key, name);
|
||||
lowerer.liftedFns.push({
|
||||
name,
|
||||
@@ -3156,7 +3155,7 @@ function pipelineAbortHelper(lowerer: Lowerer, ac: IrExpr, loc: SrcLoc): IrExpr
|
||||
let name = lowerer.valueHelpers.get(key);
|
||||
const abortT = funcOf([], VOID);
|
||||
if (!name) {
|
||||
name = keyedHelperName(lowerer, "%stream.pipelineAbort", key);
|
||||
name = `%stream.pipelineAbort.${lowerer.valueHelpers.size}`;
|
||||
lowerer.valueHelpers.set(key, name);
|
||||
const acLocal: IrLocal = { id: "ac.0", name: "ac", type: DYN, mutable: false, boxed: true };
|
||||
const acRef = varRef(acLocal.id, DYN, loc);
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import { keyedHelperName } from "./stable-names.js";
|
||||
import { dynUndefinedExpr, numLit, varRef } from "../../ir/build.js";
|
||||
import * as ts from "../ts7/adapter.js";
|
||||
import { isJsSourceFile } from "../program.js";
|
||||
@@ -56,7 +55,7 @@ export function adaptZeroArgTimerCallback(
|
||||
const toT: IrType = { kind: "func", params: [], ret: VOID };
|
||||
const key = `timer.dropret:${typeKey(fromT)}`;
|
||||
const existing = lowerer.arrHofHelpers.get(key);
|
||||
const name = existing ?? keyedHelperName(lowerer, "%timer.dropret", key);
|
||||
const name = existing ?? `%timer.dropret.${lowerer.arrHofHelpers.size}`;
|
||||
if (!existing) {
|
||||
lowerer.arrHofHelpers.set(key, name);
|
||||
lowerer.liftedFns.push(
|
||||
@@ -118,7 +117,7 @@ export function adaptZeroArgTimerCallback(
|
||||
const toT: IrType = { kind: "func", params: [], ret: VOID };
|
||||
const key = `timer.droparg:${typeKey(fromT)}`;
|
||||
const existing = lowerer.arrHofHelpers.get(key);
|
||||
const name = existing ?? keyedHelperName(lowerer, "%timer.droparg", key);
|
||||
const name = existing ?? `%timer.droparg.${lowerer.arrHofHelpers.size}`;
|
||||
if (!existing) {
|
||||
lowerer.arrHofHelpers.set(key, name);
|
||||
lowerer.liftedFns.push(
|
||||
@@ -204,7 +203,7 @@ function makeTimerArgsThunk(
|
||||
const toT: IrType = { kind: "func", params: [], ret: VOID };
|
||||
const key = `timer.argsthunk:${n}`;
|
||||
const existing = lowerer.arrHofHelpers.get(key);
|
||||
const name = existing ?? keyedHelperName(lowerer, "%timer.argsthunk", key);
|
||||
const name = existing ?? `%timer.argsthunk.${lowerer.arrHofHelpers.size}`;
|
||||
if (!existing) {
|
||||
lowerer.arrHofHelpers.set(key, name);
|
||||
const impl = `${name}.impl`;
|
||||
|
||||
@@ -10,7 +10,6 @@ import {
|
||||
} from "../../ir/ir.js";
|
||||
import type { Node, Program, SourceFile, Symbol, Type } from "../ts7/adapter.js";
|
||||
import { Lowerer } from "./lowerer.js";
|
||||
import { UnionRegistry } from "../type-mapper.js";
|
||||
import { classMethodValue } from "./class-method-values.js";
|
||||
|
||||
const loc = { file: "island.ts", start: 0, end: 1 };
|
||||
@@ -33,7 +32,7 @@ test("reuses complete copy routes without repeating discriminator planning", ()
|
||||
expect(plan).not.toHaveBeenCalled();
|
||||
expect(lowerer.liftedFns.filter((fn) => fn.name === helper)).toHaveLength(1);
|
||||
const recursive = {} as Type;
|
||||
lowerer.shapes.recursiveRef(recursive, () => "Recursive");
|
||||
lowerer.shapes.recursiveRef(recursive);
|
||||
lowerer.shapes.finalizeRecursive(recursive, [{ name: "kind", type: STRING }]);
|
||||
expect(lowerer.unionRetagHelper(from, to, loc)).toBe(helper);
|
||||
expect(plan).toHaveBeenCalled();
|
||||
@@ -55,7 +54,7 @@ test("replans after a recursive union gains its final arms", () => {
|
||||
const lowerer = context();
|
||||
const recursive = {} as Type;
|
||||
const from = lowerer.unions.intern([UNDEFINED_T, NULL_T]);
|
||||
const to = lowerer.unions.recursiveRef(recursive, () => "Recursive");
|
||||
const to = lowerer.unions.recursiveRef(recursive);
|
||||
const trapped = lowerer.unionRetagHelper(from, to, loc);
|
||||
expect(trapped).not.toBeNull();
|
||||
lowerer.unions.finalizeRecursive(recursive, [UNDEFINED_T]);
|
||||
@@ -76,7 +75,7 @@ test("rechecks width conversions after recursive record definitions settle", ()
|
||||
const lowerer = context();
|
||||
const recursive = {} as Type;
|
||||
const source = record(lowerer.shapes.intern([{ name: "value", type: F64 }]));
|
||||
const target = record(lowerer.shapes.recursiveRef(recursive, () => "Recursive"));
|
||||
const target = record(lowerer.shapes.recursiveRef(recursive));
|
||||
const from = lowerer.unions.intern([source, UNDEFINED_T]);
|
||||
const to = lowerer.unions.intern([target, NULL_T]);
|
||||
expect(lowerer.unionRetagHelper(from, to, loc)).not.toBeNull();
|
||||
@@ -130,7 +129,7 @@ test("literal ownership is reused across values and rechecked when recursive rec
|
||||
expect(inspect).not.toHaveBeenCalled();
|
||||
|
||||
const recursive = {} as Type;
|
||||
const pendingShape = record(lowerer.shapes.recursiveRef(recursive, () => "Recursive"));
|
||||
const pendingShape = record(lowerer.shapes.recursiveRef(recursive));
|
||||
const pending = lowerer.unions.intern([pendingShape], {
|
||||
field: "kind",
|
||||
cases: [{ tag: 0, values: ["a"] }],
|
||||
@@ -144,7 +143,7 @@ test("literal ownership is reused across values and rechecked when recursive rec
|
||||
test("literal ownership sees a recursive union's finalized discriminator", () => {
|
||||
const lowerer = context();
|
||||
const recursive = {} as Type;
|
||||
const union = lowerer.unions.recursiveRef(recursive, () => "Recursive");
|
||||
const union = lowerer.unions.recursiveRef(recursive);
|
||||
const shape = record(lowerer.shapes.intern([{ name: "kind", type: STRING }]));
|
||||
expect(lowerer.literalUnionArm(union, ["ready"])).toBeNull();
|
||||
lowerer.unions.finalizeRecursive(recursive, [shape], {
|
||||
@@ -183,10 +182,7 @@ test("removing undefined reuses the discriminator transformation without hiding
|
||||
);
|
||||
|
||||
const recursive = {} as Type;
|
||||
const pending: IrType = {
|
||||
kind: "union",
|
||||
unionId: lowerer.unions.recursiveRef(recursive, () => "Recursive"),
|
||||
};
|
||||
const pending: IrType = { kind: "union", unionId: lowerer.unions.recursiveRef(recursive) };
|
||||
expect(lowerer.stripUndefinedArm(pending)).toEqual(pending);
|
||||
lowerer.unions.finalizeRecursive(recursive, [STRING, UNDEFINED_T]);
|
||||
expect(lowerer.stripUndefinedArm(pending)).toEqual(STRING);
|
||||
@@ -219,10 +215,7 @@ test("adding undefined reuses semantic union transformations and sees recursive
|
||||
expect(lowerer.unions.get(added.unionId)?.discriminant).toEqual(discriminant);
|
||||
|
||||
const recursive = {} as Type;
|
||||
const pending: IrType = {
|
||||
kind: "union",
|
||||
unionId: lowerer.unions.recursiveRef(recursive, () => "Recursive"),
|
||||
};
|
||||
const pending: IrType = { kind: "union", unionId: lowerer.unions.recursiveRef(recursive) };
|
||||
const before = lowerer.withUndefinedArmOf(pending);
|
||||
lowerer.unions.finalizeRecursive(recursive, [a, b], discriminant);
|
||||
expect(lowerer.withUndefinedArmOf(pending)).toEqual(added);
|
||||
@@ -234,18 +227,13 @@ test("adding undefined reuses semantic union transformations and sees recursive
|
||||
test("optional union widening keeps refused and missing contracts current", () => {
|
||||
const lowerer = context();
|
||||
const recursive = {} as Type;
|
||||
const pending: IrType = {
|
||||
kind: "union",
|
||||
unionId: lowerer.unions.recursiveRef(recursive, () => "Recursive"),
|
||||
};
|
||||
const pending: IrType = { kind: "union", unionId: lowerer.unions.recursiveRef(recursive) };
|
||||
expect(lowerer.withUndefinedArmOf(pending)).not.toBeNull();
|
||||
lowerer.unions.finalizeRecursive(recursive, [{ kind: "date" }, STRING]);
|
||||
expect(lowerer.withUndefinedArmOf(pending)).toBeNull();
|
||||
expect(lowerer.withUndefinedArmOf(pending)).toBeNull();
|
||||
|
||||
// Union ids come from their arms, so a fresh registry names the union
|
||||
// this one has not interned yet.
|
||||
const missing: IrType = { kind: "union", unionId: new UnionRegistry().intern([F64, STRING]) };
|
||||
const missing: IrType = { kind: "union", unionId: `u${lowerer.unions.unions.length}` };
|
||||
expect(lowerer.withUndefinedArmOf(missing)).toBeNull();
|
||||
expect(lowerer.unions.intern([F64, STRING])).toBe(missing.unionId);
|
||||
expect(lowerer.withUndefinedArmOf(missing)?.kind).toBe("union");
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
import { relieveHeapPressure } from "../../heap-pressure.js";
|
||||
import { keyedHelperName } from "./stable-names.js";
|
||||
import { positionTag, StructuralPositions } from "./structural-positions.js";
|
||||
import { onlyForwarded, stripTypeWrappers } from "./typed-arguments.js";
|
||||
import {
|
||||
needsGeneralNodeStreams,
|
||||
@@ -1006,11 +1004,6 @@ function lowerShuffle(): ((wave: string[]) => void) | null {
|
||||
* the bodies reachable emit did NOT mark (plus deferred collection
|
||||
* diagnostics nothing flushed), reported separately: whole-program analysis without
|
||||
* letting unreached code fail builds. */
|
||||
/** The closure root of an emit-override specialization body. */
|
||||
function emitSpecRoot(spec: EmitSpecRequest): string {
|
||||
return `%emit.${spec.info.def.name}.${spec.event}.${spec.ordinal}`;
|
||||
}
|
||||
|
||||
export function lowerToIr(
|
||||
program: ts.Program,
|
||||
entry: ts.SourceFile,
|
||||
@@ -2178,6 +2171,12 @@ export class Lowerer {
|
||||
/** Instance types (by checker type id) of the local classes generic
|
||||
* factory calls return, and the per-instance class each names. */
|
||||
readonly localClassInstanceTypes = new Map<number, string>();
|
||||
/** A generic factory's instantiated result type → the class value of the
|
||||
* local class it returns (TypeMapperCtx.localClassValue). */
|
||||
readonly localClassValueTypes = new Map<number, IrType>();
|
||||
/** While a generic factory instance lowers: its declared result type → the
|
||||
* class value of the local class it returns. */
|
||||
instanceResultClass: { declared: number; value: IrType } | null = null;
|
||||
/** The ts-level twin of typeParamBindings, non-null while a CALL-keyed
|
||||
* instance body lowers: type-parameter symbol → the bound CHECKER type,
|
||||
* consulted where the mapped IrType has already widened away information
|
||||
@@ -2808,8 +2807,6 @@ export class Lowerer {
|
||||
/** Generated value helpers, keyed by operation and type. Callers own
|
||||
* their key prefixes; this includes more than structural conversions. */
|
||||
readonly valueHelpers = new Map<string, string>();
|
||||
/** Each name keyedHelperName made, with a second hash of its key. */
|
||||
readonly keyedNameKeys = new Map<string, number>();
|
||||
/** structuralNarrowing's assignable classes, keyed by static and
|
||||
* narrowed class name. */
|
||||
readonly structuralClassPeers = new Map<string, readonly string[]>();
|
||||
@@ -2946,6 +2943,7 @@ export class Lowerer {
|
||||
ctx: FnCtx;
|
||||
}
|
||||
>();
|
||||
chainCounter = 0;
|
||||
/** Keyed by program-wide qualified class name (what IR object types carry). */
|
||||
readonly classes = new Map<string, ClassInfo>();
|
||||
readonly classBySymbol = new Map<ts.Symbol, ClassInfo>();
|
||||
@@ -3175,47 +3173,7 @@ export class Lowerer {
|
||||
* dispatch revision needs no weak-key identity or process-global state. */
|
||||
errorMethodDispatchRevision: string | null = null;
|
||||
errorPropertyDispatchRevision: string | null = null;
|
||||
/** Structural source positions (structural-positions.ts) for names that
|
||||
* stay the same when unrelated code moves. */
|
||||
readonly positions = new StructuralPositions();
|
||||
|
||||
/** The outermost node whose token span is exactly `loc`, or null. */
|
||||
nodeAtLoc(loc: SrcLoc): ts.Node | null {
|
||||
const sf = this.program.getSourceFile(loc.file);
|
||||
return sf === undefined ? null : (ts.nodeAtSpan(sf, loc.start, loc.end) ?? null);
|
||||
}
|
||||
|
||||
/** The lowering unit whose name scopes the closures it lifts. */
|
||||
private closureRoot = "%pre";
|
||||
private readonly closureNameCounts = new Map<string, number>();
|
||||
|
||||
/** Runs `fn` with the closures it lifts named under `root`. */
|
||||
withClosureRoot<T>(root: string, fn: () => T): T {
|
||||
const previous = this.closureRoot;
|
||||
this.closureRoot = root;
|
||||
try {
|
||||
return fn();
|
||||
} finally {
|
||||
this.closureRoot = previous;
|
||||
}
|
||||
}
|
||||
|
||||
/** The name of a closure (or wrapper) lifted for the source at `loc`:
|
||||
* `<prefix>.<hash>` of the current lowering unit, the location and
|
||||
* `suffix`, then `suffix`. A unit that lifts several functions for the same
|
||||
* location and suffix numbers them in the order it lifts them, so a
|
||||
* closure's name depends only on its unit, not on which units lowered
|
||||
* before it. */
|
||||
closureName(loc: SrcLoc, suffix = "", prefix = "%fn"): string {
|
||||
const node = this.nodeAtLoc(loc);
|
||||
const where = node === null ? `${loc.file}:${loc.start}:${loc.end}` : this.positions.of(node);
|
||||
const key = `${prefix}|${this.closureRoot}|${where}|${suffix}`;
|
||||
const first = keyedHelperName(this, prefix, key);
|
||||
const seen = this.closureNameCounts.get(first) ?? 0;
|
||||
this.closureNameCounts.set(first, seen + 1);
|
||||
return (seen === 0 ? first : keyedHelperName(this, prefix, `${key}|${seen}`)) + suffix;
|
||||
}
|
||||
|
||||
lambdaCounter = 0;
|
||||
readonly preparedClassFactories = new Map<ts.ArrowFunction | ts.FunctionExpression, IrExpr>();
|
||||
readonly preparingClassFactories = new Set<ts.ArrowFunction | ts.FunctionExpression>();
|
||||
|
||||
@@ -3568,7 +3526,6 @@ export class Lowerer {
|
||||
this.checker = program.getTypeChecker();
|
||||
this.typeCtx = {
|
||||
checker: this.checker,
|
||||
positionOf: (node) => this.positions.of(node),
|
||||
shapes: this.shapes,
|
||||
unions: this.unions,
|
||||
classNamer: this.classNamer,
|
||||
@@ -3578,6 +3535,10 @@ export class Lowerer {
|
||||
genericClassInstance: (decl, ref, mapArgument) =>
|
||||
this.genericClassInstanceType(decl, ref, mapArgument),
|
||||
runtimeClassValue: (type) => runtimeClassValueOfType(this, type),
|
||||
localClassValue: (type) =>
|
||||
this.instanceResultClass?.declared === type.id
|
||||
? this.instanceResultClass.value
|
||||
: (this.localClassValueTypes.get(type.id) ?? null),
|
||||
mixinClassInstance: (decl) =>
|
||||
this.mixinTypeContext && this.mixinTypeContext.classNode === decl
|
||||
? { kind: "object", className: this.mixinTypeContext.className }
|
||||
@@ -3857,11 +3818,11 @@ export class Lowerer {
|
||||
})()
|
||||
? this.qualify(
|
||||
ts.getSourceFileOfNode(decl),
|
||||
`%cx${positionTag(this, decl)}.${decl.name?.text ?? ""}`,
|
||||
`%cx${ts.tokenStartOfNode(decl)}.${decl.name?.text ?? ""}`,
|
||||
)
|
||||
: this.qualify(
|
||||
ts.getSourceFileOfNode(decl),
|
||||
nsPathPrefix(this, decl) + (decl.name ? decl.name.text : "%anon"),
|
||||
nsPathPrefix(decl) + (decl.name ? decl.name.text : "%anon"),
|
||||
);
|
||||
for (let index = this.localClassInstantiations.length - 1; index >= 0; index--) {
|
||||
const entry = this.localClassInstantiations[index]!;
|
||||
@@ -7420,10 +7381,7 @@ export class Lowerer {
|
||||
}
|
||||
for (const decl of fp.classDecls) {
|
||||
const info = this.classes.get(this.classNamer(decl));
|
||||
if (info)
|
||||
functions.push(
|
||||
...this.withClosureRoot(info.def.name, () => this.lowerClassMembers(info)),
|
||||
);
|
||||
if (info) functions.push(...this.lowerClassMembers(info));
|
||||
else if (this.countsSkips()) this.countSkippedBody(decl);
|
||||
}
|
||||
}
|
||||
@@ -7436,10 +7394,7 @@ export class Lowerer {
|
||||
// are reachable by definition, already counted by reachable emit.
|
||||
if (!this.remainder) {
|
||||
for (const fp of parts) {
|
||||
const initName = this.initNameOf.get(fp.sf)!;
|
||||
functions.push(
|
||||
this.withClosureRoot(initName, () => this.lowerFileInit(fp.sf, fp.topStmts, initName)),
|
||||
);
|
||||
functions.push(this.lowerFileInit(fp.sf, fp.topStmts, this.initNameOf.get(fp.sf)!));
|
||||
}
|
||||
functions.push(this.buildMain());
|
||||
}
|
||||
@@ -7481,9 +7436,7 @@ export class Lowerer {
|
||||
// default): the diagnostic is recorded — the instance skips
|
||||
// like a signature-blocked function (lowerFunction's rule).
|
||||
try {
|
||||
functions.push(
|
||||
this.withClosureRoot(inst.name, () => this.lowerGenericInstance(info, inst)),
|
||||
);
|
||||
functions.push(this.lowerGenericInstance(info, inst));
|
||||
} catch (e) {
|
||||
if (!(e instanceof PoisonError)) throw e;
|
||||
}
|
||||
@@ -7493,10 +7446,7 @@ export class Lowerer {
|
||||
// instances of its own; the joint fixpoint covers both).
|
||||
while (es < this.emitSpecQueue.length) {
|
||||
try {
|
||||
const spec = this.emitSpecQueue[es++]!;
|
||||
const fn = this.withClosureRoot(emitSpecRoot(spec), () =>
|
||||
lowerEmitOverrideSpec(this, spec),
|
||||
);
|
||||
const fn = lowerEmitOverrideSpec(this, this.emitSpecQueue[es++]!);
|
||||
if (fn) functions.push(fn);
|
||||
} catch (e) {
|
||||
if (!(e instanceof PoisonError)) throw e;
|
||||
@@ -8050,9 +8000,7 @@ export class Lowerer {
|
||||
this.genericClassDemandPriority.set(info, ref);
|
||||
instanceFunctions.push(
|
||||
...this.withGenericDemandOwner(owner, () =>
|
||||
this.shapes.withDeclaredOrderPriority(ref, () =>
|
||||
this.withClosureRoot(info.def.name, () => this.lowerClassMembers(info)),
|
||||
),
|
||||
this.shapes.withDeclaredOrderPriority(ref, () => this.lowerClassMembers(info)),
|
||||
),
|
||||
);
|
||||
relieveHeapPressure();
|
||||
@@ -8079,7 +8027,7 @@ export class Lowerer {
|
||||
instanceFunctions.push(
|
||||
this.withGenericDemandOwner(owner, () =>
|
||||
this.shapes.withDeclaredOrderPriority(ref, () =>
|
||||
this.withClosureRoot(inst.name, () => this.lowerGenericInstance(info, inst)),
|
||||
this.lowerGenericInstance(info, inst),
|
||||
),
|
||||
),
|
||||
);
|
||||
@@ -8112,12 +8060,9 @@ export class Lowerer {
|
||||
);
|
||||
while (specLowered < waveEnd) {
|
||||
try {
|
||||
const fn = this.shapes.withDeclaredOrderPriority([4, instanceMetadataOrder++], () => {
|
||||
const spec = this.emitSpecQueue[specLowered++]!;
|
||||
return this.withClosureRoot(emitSpecRoot(spec), () =>
|
||||
lowerEmitOverrideSpec(this, spec),
|
||||
);
|
||||
});
|
||||
const fn = this.shapes.withDeclaredOrderPriority([4, instanceMetadataOrder++], () =>
|
||||
lowerEmitOverrideSpec(this, this.emitSpecQueue[specLowered++]!),
|
||||
);
|
||||
if (fn) instanceFunctions.push(fn);
|
||||
} catch (e) {
|
||||
if (!(e instanceof PoisonError)) throw e;
|
||||
@@ -8145,9 +8090,7 @@ export class Lowerer {
|
||||
initFunctions.push(
|
||||
this.withGenericDemandOwner(owner, () =>
|
||||
this.shapes.withDeclaredOrderPriority(priority, () =>
|
||||
this.withClosureRoot(this.initNameOf.get(fp.sf)!, () =>
|
||||
this.lowerFileInit(fp.sf, fp.topStmts, this.initNameOf.get(fp.sf)!),
|
||||
),
|
||||
this.lowerFileInit(fp.sf, fp.topStmts, this.initNameOf.get(fp.sf)!),
|
||||
),
|
||||
),
|
||||
);
|
||||
@@ -8173,9 +8116,7 @@ export class Lowerer {
|
||||
const priority = metadataPriority.get(name) ?? [3, expressionMetadataOrder++];
|
||||
const owner = demandOwner(priority);
|
||||
const fn = this.withGenericDemandOwner(owner, () =>
|
||||
this.shapes.withDeclaredOrderPriority(priority, () =>
|
||||
this.withClosureRoot(name, unit.lower),
|
||||
),
|
||||
this.shapes.withDeclaredOrderPriority(priority, unit.lower),
|
||||
);
|
||||
if (fn) loweredUnits.set(name, fn);
|
||||
relieveHeapPressure();
|
||||
@@ -13480,6 +13421,10 @@ export class Lowerer {
|
||||
const callable = this.lowerBuiltinCallableValue(bi, loc);
|
||||
if (callable) return callable;
|
||||
}
|
||||
// node:crypto's webcrypto is the WebCrypto global's object.
|
||||
if (bi.module === "crypto" && bi.member === "webcrypto") {
|
||||
return { kind: "libCall", fn: "webcrypto.object", args: [], type: DYN, loc };
|
||||
}
|
||||
// node:stream/web's exports are the web stream globals themselves: in
|
||||
// JavaScript sources the same identity token as the global spelling
|
||||
// (`require("stream/web").ReadableStream === globalThis.ReadableStream`).
|
||||
@@ -13559,7 +13504,7 @@ export class Lowerer {
|
||||
const key = `${fn.valueFn ?? fn.fn}:${typeKey(funcType)}:${adapterShape}`;
|
||||
let fnName = this.builtinCallableValueFns.get(key);
|
||||
if (!fnName) {
|
||||
fnName = keyedHelperName(this, `%builtin.value.${fn.valueFn ?? fn.fn}`, key);
|
||||
fnName = `%builtin.value.${fn.valueFn ?? fn.fn}.${this.builtinCallableValueFns.size}`;
|
||||
this.builtinCallableValueFns.set(key, fnName);
|
||||
this.builtinCallableValueNames.set(fnName, display.slice(display.lastIndexOf(".") + 1));
|
||||
const params = funcType.params.map((type, index) => ({
|
||||
|
||||
@@ -562,7 +562,7 @@ export function lowerFfiMemoryModule(lowerer: Lowerer, loc: SrcLoc): IrExpr {
|
||||
const entries = lowerer.ffiImports
|
||||
.filter((entry) => entry.library !== undefined)
|
||||
.map((entry) => {
|
||||
const name = lowerer.closureName(loc, "", "%ffi.module");
|
||||
const name = `%ffi.module.${lowerer.lambdaCounter++}`;
|
||||
const sourceTypes = ffiSourceParamTypes(entry.params);
|
||||
const locals: IrLocal[] = sourceTypes.map((_, i) => ({
|
||||
id: `%arg${i}`,
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import { keyedHelperName } from "./stable-names.js";
|
||||
import {
|
||||
BOOL,
|
||||
STRING,
|
||||
@@ -28,7 +27,7 @@ export function objectEnumerationReceiver(
|
||||
const key = `obj.receiver:${source.unionId}:${expected.shapeId}`;
|
||||
let name = lowerer.arrHofHelpers.get(key);
|
||||
if (name === undefined) {
|
||||
name = keyedHelperName(lowerer, "%obj.receiver", key);
|
||||
name = `%obj.receiver.${lowerer.arrHofHelpers.size}`;
|
||||
lowerer.arrHofHelpers.set(key, name);
|
||||
const value = varRef("value.0", source, loc);
|
||||
lowerer.liftedFns.push({
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user