@scriptc/compiler 0.1.3 → 0.1.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/ambient/scriptc-node-fallback.d.ts +275 -62
- package/dist/backend/c/async.d.ts +25 -0
- package/dist/backend/c/async.js +167 -0
- package/dist/backend/c/async.js.map +1 -1
- package/dist/backend/c/c-emitter.d.ts +15 -1
- package/dist/backend/c/c-emitter.js +36 -11
- package/dist/backend/c/c-emitter.js.map +1 -1
- package/dist/backend/c/exprs.d.ts +2 -2
- package/dist/backend/c/exprs.js +556 -71
- package/dist/backend/c/exprs.js.map +1 -1
- package/dist/backend/c/stmts.js +5 -3
- package/dist/backend/c/stmts.js.map +1 -1
- package/dist/backend/c/types.d.ts +2 -3
- package/dist/backend/c/types.js +10 -1
- package/dist/backend/c/types.js.map +1 -1
- package/dist/backend/c/walkers.js +1 -1
- package/dist/backend/c/walkers.js.map +1 -1
- package/dist/backend/link-plan.d.ts +3 -1
- package/dist/backend/link-plan.js +3 -1
- package/dist/backend/link-plan.js.map +1 -1
- package/dist/backend/llvm/dyn.js +1 -1
- package/dist/backend/llvm/dyn.js.map +1 -1
- package/dist/backend/llvm/emitter.js +42 -12
- package/dist/backend/llvm/emitter.js.map +1 -1
- package/dist/backend/llvm/expr-bytes.d.ts +1 -2
- package/dist/backend/llvm/expr-bytes.js +28 -23
- package/dist/backend/llvm/expr-bytes.js.map +1 -1
- package/dist/backend/llvm/expr-callbacks.d.ts +9 -0
- package/dist/backend/llvm/expr-callbacks.js +125 -0
- package/dist/backend/llvm/expr-callbacks.js.map +1 -1
- package/dist/backend/llvm/expr-containers.d.ts +1 -0
- package/dist/backend/llvm/expr-containers.js +32 -4
- package/dist/backend/llvm/expr-containers.js.map +1 -1
- package/dist/backend/llvm/expr-context.d.ts +19 -3
- package/dist/backend/llvm/expr-dispatch.js +1 -0
- package/dist/backend/llvm/expr-dispatch.js.map +1 -1
- package/dist/backend/llvm/expr-primitives.d.ts +1 -1
- package/dist/backend/llvm/expr-primitives.js +49 -13
- package/dist/backend/llvm/expr-primitives.js.map +1 -1
- package/dist/backend/llvm/expr-primitives.test.d.ts +1 -0
- package/dist/backend/llvm/expr-primitives.test.js +38 -0
- package/dist/backend/llvm/expr-primitives.test.js.map +1 -0
- package/dist/backend/llvm/expr-stream-callbacks.d.ts +3 -0
- package/dist/backend/llvm/expr-stream-callbacks.js +69 -0
- package/dist/backend/llvm/expr-stream-callbacks.js.map +1 -1
- package/dist/backend/llvm/lib-filesystem.js +80 -10
- package/dist/backend/llvm/lib-filesystem.js.map +1 -1
- package/dist/backend/llvm/lib-network.js +69 -10
- package/dist/backend/llvm/lib-network.js.map +1 -1
- package/dist/backend/llvm/lib-process.js +156 -6
- package/dist/backend/llvm/lib-process.js.map +1 -1
- package/dist/backend/llvm/lib-shared.js +100 -0
- package/dist/backend/llvm/lib-shared.js.map +1 -1
- package/dist/backend/llvm/lib-stream.js +23 -0
- package/dist/backend/llvm/lib-stream.js.map +1 -1
- package/dist/backend/native-toolchain.d.ts +5 -0
- package/dist/backend/native-toolchain.js +4 -1
- package/dist/backend/native-toolchain.js.map +1 -1
- package/dist/backend/runtime-pack.test.js +28 -10
- package/dist/backend/runtime-pack.test.js.map +1 -1
- package/dist/backend/targets.d.ts +7 -0
- package/dist/backend/targets.js +26 -0
- package/dist/backend/targets.js.map +1 -1
- package/dist/backend/targets.test.js +8 -1
- package/dist/backend/targets.test.js.map +1 -1
- package/dist/coverage/surface-manifest.js +14 -4
- package/dist/coverage/surface-manifest.js.map +1 -1
- package/dist/diagnostics/diagnostic.js +1 -1
- package/dist/diagnostics/diagnostic.js.map +1 -1
- package/dist/executable/executable-cache.d.ts +4 -0
- package/dist/executable/executable-cache.js +6 -1
- package/dist/executable/executable-cache.js.map +1 -1
- package/dist/executable/executable-cache.test.js +3 -3
- package/dist/executable/executable-cache.test.js.map +1 -1
- package/dist/frontend/fork-target.d.ts +17 -0
- package/dist/frontend/fork-target.js +256 -0
- package/dist/frontend/fork-target.js.map +1 -0
- package/dist/frontend/lowering/abstract-equality.d.ts +6 -0
- package/dist/frontend/lowering/abstract-equality.js +250 -0
- package/dist/frontend/lowering/abstract-equality.js.map +1 -0
- package/dist/frontend/lowering/containers/bytes.d.ts +1 -1
- package/dist/frontend/lowering/containers/bytes.js +6 -5
- package/dist/frontend/lowering/containers/bytes.js.map +1 -1
- package/dist/frontend/lowering/containers/string-and-regexp.d.ts +7 -6
- package/dist/frontend/lowering/containers/string-and-regexp.js +237 -28
- package/dist/frontend/lowering/containers/string-and-regexp.js.map +1 -1
- package/dist/frontend/lowering/expressions/object-literals.js +8 -0
- package/dist/frontend/lowering/expressions/object-literals.js.map +1 -1
- package/dist/frontend/lowering/lower-assert.js +71 -2
- package/dist/frontend/lowering/lower-assert.js.map +1 -1
- package/dist/frontend/lowering/lower-builtins.d.ts +54 -28
- package/dist/frontend/lowering/lower-builtins.js +957 -116
- package/dist/frontend/lowering/lower-builtins.js.map +1 -1
- package/dist/frontend/lowering/lower-calls.d.ts +32 -23
- package/dist/frontend/lowering/lower-calls.js +835 -206
- package/dist/frontend/lowering/lower-calls.js.map +1 -1
- package/dist/frontend/lowering/lower-classes.js +41 -10
- package/dist/frontend/lowering/lower-classes.js.map +1 -1
- package/dist/frontend/lowering/lower-containers.d.ts +7 -9
- package/dist/frontend/lowering/lower-containers.js +293 -51
- package/dist/frontend/lowering/lower-containers.js.map +1 -1
- package/dist/frontend/lowering/lower-event-emitter.js +212 -21
- package/dist/frontend/lowering/lower-event-emitter.js.map +1 -1
- package/dist/frontend/lowering/lower-exprs.d.ts +12 -0
- package/dist/frontend/lowering/lower-exprs.js +263 -93
- package/dist/frontend/lowering/lower-exprs.js.map +1 -1
- package/dist/frontend/lowering/lower-inspect.d.ts +4 -0
- package/dist/frontend/lowering/lower-inspect.js +1 -1
- package/dist/frontend/lowering/lower-inspect.js.map +1 -1
- package/dist/frontend/lowering/lower-island.d.ts +6 -3
- package/dist/frontend/lowering/lower-island.js +182 -12
- package/dist/frontend/lowering/lower-island.js.map +1 -1
- package/dist/frontend/lowering/lower-modules.d.ts +6 -0
- package/dist/frontend/lowering/lower-modules.js +184 -38
- package/dist/frontend/lowering/lower-modules.js.map +1 -1
- package/dist/frontend/lowering/lower-namespaces.js +7 -0
- package/dist/frontend/lowering/lower-namespaces.js.map +1 -1
- package/dist/frontend/lowering/lower-server.d.ts +3 -5
- package/dist/frontend/lowering/lower-server.js +413 -54
- package/dist/frontend/lowering/lower-server.js.map +1 -1
- package/dist/frontend/lowering/lower-stmts.js +122 -19
- package/dist/frontend/lowering/lower-stmts.js.map +1 -1
- package/dist/frontend/lowering/lower-stream.d.ts +3 -0
- package/dist/frontend/lowering/lower-stream.js +7 -0
- package/dist/frontend/lowering/lower-stream.js.map +1 -1
- package/dist/frontend/lowering/lowerer.d.ts +86 -9
- package/dist/frontend/lowering/lowerer.js +449 -74
- package/dist/frontend/lowering/lowerer.js.map +1 -1
- package/dist/frontend/lowering/optional-arguments.d.ts +9 -1
- package/dist/frontend/lowering/optional-arguments.js +67 -1
- package/dist/frontend/lowering/optional-arguments.js.map +1 -1
- package/dist/frontend/lowering/pairs-snapshot.d.ts +3 -1
- package/dist/frontend/lowering/pairs-snapshot.js +12 -4
- package/dist/frontend/lowering/pairs-snapshot.js.map +1 -1
- package/dist/frontend/lowering/surfaces.d.ts +34 -14
- package/dist/frontend/lowering/surfaces.js +177 -104
- package/dist/frontend/lowering/surfaces.js.map +1 -1
- package/dist/frontend/npm-static-declarations.d.ts +4 -0
- package/dist/frontend/npm-static-declarations.js +56 -0
- package/dist/frontend/npm-static-declarations.js.map +1 -1
- package/dist/frontend/npm-static-declarations.test.js +23 -2
- package/dist/frontend/npm-static-declarations.test.js.map +1 -1
- package/dist/frontend/npm-static-prune.d.ts +6 -0
- package/dist/frontend/npm-static-prune.js +203 -0
- package/dist/frontend/npm-static-prune.js.map +1 -0
- package/dist/frontend/npm-static-prune.test.d.ts +1 -0
- package/dist/frontend/npm-static-prune.test.js +48 -0
- package/dist/frontend/npm-static-prune.test.js.map +1 -0
- package/dist/frontend/npm-static.js +6 -5
- package/dist/frontend/npm-static.js.map +1 -1
- package/dist/frontend/npm.d.ts +15 -3
- package/dist/frontend/npm.js +87 -14
- package/dist/frontend/npm.js.map +1 -1
- package/dist/frontend/npm.test.js +76 -9
- package/dist/frontend/npm.test.js.map +1 -1
- package/dist/frontend/program.js +107 -14
- package/dist/frontend/program.js.map +1 -1
- package/dist/frontend/provenance.js +8 -3
- package/dist/frontend/provenance.js.map +1 -1
- package/dist/frontend/resolve.d.ts +6 -0
- package/dist/frontend/resolve.js +7 -4
- package/dist/frontend/resolve.js.map +1 -1
- package/dist/frontend/type-mapper.d.ts +5 -0
- package/dist/frontend/type-mapper.js +80 -29
- package/dist/frontend/type-mapper.js.map +1 -1
- package/dist/index.d.ts +8 -3
- package/dist/index.js +34 -41
- package/dist/index.js.map +1 -1
- package/dist/ir/analysis.d.ts +5 -3
- package/dist/ir/analysis.js +25 -11
- package/dist/ir/analysis.js.map +1 -1
- package/dist/ir/analysis.test.js +50 -2
- package/dist/ir/analysis.test.js.map +1 -1
- package/dist/ir/constant-tables.d.ts +17 -0
- package/dist/ir/constant-tables.js +99 -0
- package/dist/ir/constant-tables.js.map +1 -0
- package/dist/ir/constant-tables.test.d.ts +1 -0
- package/dist/ir/constant-tables.test.js +78 -0
- package/dist/ir/constant-tables.test.js.map +1 -0
- package/dist/ir/integer-ranges.d.ts +13 -0
- package/dist/ir/integer-ranges.js +122 -0
- package/dist/ir/integer-ranges.js.map +1 -0
- package/dist/ir/integer-ranges.test.d.ts +1 -0
- package/dist/ir/integer-ranges.test.js +63 -0
- package/dist/ir/integer-ranges.test.js.map +1 -0
- package/dist/ir/ir.d.ts +130 -76
- package/dist/ir/ir.js +69 -4
- package/dist/ir/ir.js.map +1 -1
- package/dist/ir/ir.test.js +20 -1
- package/dist/ir/ir.test.js.map +1 -1
- package/dist/ir/scalar-records.d.ts +8 -0
- package/dist/ir/scalar-records.js +216 -0
- package/dist/ir/scalar-records.js.map +1 -0
- package/dist/ir/scalar-records.test.d.ts +1 -0
- package/dist/ir/scalar-records.test.js +142 -0
- package/dist/ir/scalar-records.test.js.map +1 -0
- package/dist/ir/serialize.d.ts +1 -1
- package/dist/ir/serialize.js +1 -1
- package/dist/ir/serialize.js.map +1 -1
- package/dist/ir/validate.js +256 -20
- package/dist/ir/validate.js.map +1 -1
- package/dist/ir/validate.test.d.ts +1 -0
- package/dist/ir/validate.test.js +31 -0
- package/dist/ir/validate.test.js.map +1 -0
- package/dist/library/cache-primitives.d.ts +1 -1
- package/dist/library/cache-primitives.js +3 -3
- package/dist/library/cache-primitives.js.map +1 -1
- package/dist/library/fence-eval.js +11 -4
- package/dist/library/fence-eval.js.map +1 -1
- package/dist/library/int-infer.test.js +4 -4
- package/dist/library/int-infer.test.js.map +1 -1
- package/dist/library/library-cache.js +0 -1
- package/dist/library/library-cache.js.map +1 -1
- package/dist/library/library-cache.test.js +8 -8
- package/dist/library/library-cache.test.js.map +1 -1
- package/package.json +17 -17
- package/surface-manifest.json +186 -79
|
@@ -19,20 +19,18 @@ export declare function lowerArrayMethodCall(lowerer: Lowerer, call: ts.CallExpr
|
|
|
19
19
|
* parameter type). Null when this isn't a tuple slice/map — writers and
|
|
20
20
|
* the rest of the surface keep their fences. */
|
|
21
21
|
export declare function lowerTupleReadMethodCall(lowerer: Lowerer, call: ts.CallExpression, access: ts.PropertyAccessExpression): IrExpr | null;
|
|
22
|
-
/** An
|
|
23
|
-
|
|
24
|
-
* is an integer in [0, len), JS's property-miss undefined otherwise —
|
|
25
|
-
* instead of the trap divergence 4 documents for program code.
|
|
26
|
-
* Package JS is inference-typed, guard-style code (`registeredArguments
|
|
27
|
-
* .slice(-1)[0]`, commander's last-element probe), and the trap would
|
|
28
|
-
* fire on working Node idioms; program files keep the documented trap
|
|
29
|
-
* (their annotations can prove bounds). Existing element unions retag
|
|
30
|
-
* into the canonical union with an added undefined arm. */
|
|
22
|
+
/** An ordinary indexed read: values retain their element type, while
|
|
23
|
+
* holes, missing properties, and present undefined yield undefined. */
|
|
31
24
|
export declare function lowerSafeIndexRead(lowerer: Lowerer, arr: IrExpr & {
|
|
32
25
|
type: {
|
|
33
26
|
kind: "array";
|
|
34
27
|
};
|
|
35
28
|
}, index: IrExpr, loc: SrcLoc): IrExpr | null;
|
|
29
|
+
/** Fuse only our own f64-array read helper with immediate ToNumber.
|
|
30
|
+
* Reuse its arguments verbatim so the receiver and index still evaluate
|
|
31
|
+
* exactly once, in order, before the read. Do not specialize user calls,
|
|
32
|
+
* union-element arrays, or optional values stored in locals. */
|
|
33
|
+
export declare function tryLowerNumericIndexRead(lowerer: Lowerer, operand: IrExpr, loc: SrcLoc): IrExpr | null;
|
|
36
34
|
/** Backward-compatible name for the npm-static probe path. */
|
|
37
35
|
export declare const lowerNpmStaticSafeIndexRead: typeof lowerSafeIndexRead;
|
|
38
36
|
/** `Array.from({ length: n }, mapfn)` — the counted-generation idiom — on
|
|
@@ -15,7 +15,7 @@ import { buildArraySortFn } from "./lower-array-sort.js";
|
|
|
15
15
|
import { arrayIndexPresent, arrayValueRead, arrayValueStore, arrayValueType, currentArrayIndexPresent } from "./array-values.js";
|
|
16
16
|
import { typeKey } from "../type-mapper.js";
|
|
17
17
|
import { boolLit, countedFor, numLit, varRef } from "../../ir/build.js";
|
|
18
|
-
import { lowerOptionalArgument } from "./optional-arguments.js";
|
|
18
|
+
import { lowerOptionalArgument, lowerPositionArgument, positionNumber } from "./optional-arguments.js";
|
|
19
19
|
/** Build the argument array for a mutating/copying operation while keeping
|
|
20
20
|
* the receiver's scalar payload ABI. A missing read is represented as the
|
|
21
21
|
* present UNDEFINED state in this temporary, so push/unshift and toSpliced
|
|
@@ -95,6 +95,11 @@ export function lowerArrayMethodCall(lowerer, call, access) {
|
|
|
95
95
|
probedUntyped = true;
|
|
96
96
|
}
|
|
97
97
|
}
|
|
98
|
+
if (receiverIr?.kind === "union") {
|
|
99
|
+
const unionHof = lowerArrayUnionHofCall(lowerer, call, access, receiverIr);
|
|
100
|
+
if (unionHof)
|
|
101
|
+
return unionHof;
|
|
102
|
+
}
|
|
98
103
|
if (receiverIr?.kind !== "array")
|
|
99
104
|
return null;
|
|
100
105
|
if (!probedUntyped && !lowerer.isStdlibMember(access))
|
|
@@ -282,14 +287,14 @@ export function lowerArrayMethodCall(lowerer, call, access) {
|
|
|
282
287
|
};
|
|
283
288
|
}
|
|
284
289
|
// The lib declares wider call forms than the lowered surface —
|
|
285
|
-
//
|
|
286
|
-
//
|
|
290
|
+
// join's separator is optional, and the predicate/mapping HOFs take
|
|
291
|
+
// a thisArg. Each unlowered form is fenced
|
|
287
292
|
// per site (SC2020), never silently truncated to the supported
|
|
288
293
|
// arguments. push/unshift lower every declared form (variadic, 0 args
|
|
289
294
|
// included — Node returns the unchanged length); reduce/reduceRight
|
|
290
295
|
// lower both declared forms (with and without an initial value).
|
|
291
296
|
const arity = {
|
|
292
|
-
push: [0, Number.MAX_SAFE_INTEGER], unshift: [0, Number.MAX_SAFE_INTEGER], pop: [0, 0], indexOf: [1, 1], includes: [
|
|
297
|
+
push: [0, Number.MAX_SAFE_INTEGER], unshift: [0, Number.MAX_SAFE_INTEGER], pop: [0, 0], indexOf: [1, 2], lastIndexOf: [1, 2], includes: [0, 2], join: [1, 1],
|
|
293
298
|
concat: [0, Number.MAX_SAFE_INTEGER],
|
|
294
299
|
slice: [0, 2], shift: [0, 0], splice: [1, 2], at: [1, 1],
|
|
295
300
|
map: [1, 1], filter: [1, 1], forEach: [1, 1], find: [1, 1], findIndex: [1, 1], some: [1, 1],
|
|
@@ -301,12 +306,10 @@ export function lowerArrayMethodCall(lowerer, call, access) {
|
|
|
301
306
|
? "the removal forms lower — splice(start, deleteCount?); to insert, build a new array with slice and push"
|
|
302
307
|
: name === "join"
|
|
303
308
|
? 'pass the separator explicitly: join(",")'
|
|
304
|
-
: name === "
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
? "the thisArg parameter has no lowering — use an arrow function"
|
|
309
|
-
: undefined;
|
|
309
|
+
: name === "map" || name === "filter" || name === "forEach" ||
|
|
310
|
+
name === "find" || name === "some" || name === "every" || name === "flatMap"
|
|
311
|
+
? "the thisArg parameter has no lowering — use an arrow function"
|
|
312
|
+
: undefined;
|
|
310
313
|
lowerer.noLowering(`.${name} with ${call.arguments.length} argument${call.arguments.length === 1 ? "" : "s"}`, call, hint);
|
|
311
314
|
}
|
|
312
315
|
if (name === "push" || name === "unshift") {
|
|
@@ -388,7 +391,7 @@ export function lowerArrayMethodCall(lowerer, call, access) {
|
|
|
388
391
|
loc,
|
|
389
392
|
};
|
|
390
393
|
}
|
|
391
|
-
if (name === "indexOf" || name === "includes") {
|
|
394
|
+
if (name === "indexOf" || name === "lastIndexOf" || name === "includes") {
|
|
392
395
|
return lowerArraySearchCall(lowerer, call, access, name, elem);
|
|
393
396
|
}
|
|
394
397
|
if (name === "concat") {
|
|
@@ -526,14 +529,23 @@ export function lowerArrayMethodCall(lowerer, call, access) {
|
|
|
526
529
|
/** `indexOf` and `includes` normally use the runtime's typed search helpers,
|
|
527
530
|
* but an indexed array read is a `T | undefined` value. Keep that union
|
|
528
531
|
* through a small lowered loop so the needle's missing state is observable:
|
|
529
|
-
* indexOf
|
|
530
|
-
* loop also gives union-element arrays their value
|
|
531
|
-
* comparing the compiler's union boxes by pointer. */
|
|
532
|
+
* indexOf and lastIndexOf skip holes, while includes treats holes as
|
|
533
|
+
* undefined. The same loop also gives union-element arrays their value
|
|
534
|
+
* equality rather than comparing the compiler's union boxes by pointer. */
|
|
532
535
|
function lowerArraySearchCall(lowerer, call, access, method, elem) {
|
|
533
536
|
const loc = locOf(call);
|
|
534
537
|
const receiver = lowerer.lowerExpr(access.expression);
|
|
535
|
-
const argNode = call.arguments[0];
|
|
536
|
-
|
|
538
|
+
const argNode = call.arguments[0] ?? call;
|
|
539
|
+
if (call.arguments.some(ts.isSpreadElement))
|
|
540
|
+
lowerer.noLowering(`.${method} with spread arguments`, call);
|
|
541
|
+
let needle = call.arguments[0]
|
|
542
|
+
? lowerer.lowerExpr(call.arguments[0])
|
|
543
|
+
: { kind: "unitLit", unit: "undefined", type: UNDEFINED_T, loc };
|
|
544
|
+
const fromIndex = call.arguments[1]
|
|
545
|
+
? lowerPositionArgument(lowerer, call.arguments[1], numLit(0, loc))
|
|
546
|
+
: method === "lastIndexOf"
|
|
547
|
+
? { kind: "bin", op: "/", left: numLit(1, loc), right: numLit(0, loc), type: F64, loc }
|
|
548
|
+
: numLit(0, loc);
|
|
537
549
|
// A derived CLASS VALUE against a base-classval element widens (the same
|
|
538
550
|
// pointer — identity search is exact); the coercion path owns the ABI gate.
|
|
539
551
|
if (needle.type.kind === "classval" && elem.kind === "classval" && !typeEquals(needle.type, elem)) {
|
|
@@ -541,21 +553,25 @@ function lowerArraySearchCall(lowerer, call, access, method, elem) {
|
|
|
541
553
|
}
|
|
542
554
|
// Preserve the existing fast path for an ordinary, non-union needle. Its
|
|
543
555
|
// runtime implementation already has exact primitive/reference equality.
|
|
544
|
-
if (elem.kind !== "union" && elem.kind !== "bigint" && typeEquals(needle.type, elem)) {
|
|
556
|
+
if (method !== "lastIndexOf" && call.arguments.length === 1 && elem.kind !== "union" && elem.kind !== "bigint" && typeEquals(needle.type, elem)) {
|
|
545
557
|
return {
|
|
546
558
|
kind: "arrIntrinsic",
|
|
547
559
|
method,
|
|
548
560
|
receiver,
|
|
549
561
|
args: [needle],
|
|
550
|
-
type: method === "
|
|
562
|
+
type: method === "includes" ? BOOL : F64,
|
|
551
563
|
loc,
|
|
552
564
|
};
|
|
553
565
|
}
|
|
554
|
-
|
|
566
|
+
let valueT = arrayValueType(lowerer, elem);
|
|
555
567
|
// jsval/dyn arrays have a single engine value representation rather than a
|
|
556
568
|
// static undefined arm. Keep their existing runtime path and its explicit
|
|
557
569
|
// type fence; typed static arrays take the state-aware helper below.
|
|
558
570
|
if (valueT.kind !== "union") {
|
|
571
|
+
if (method === "lastIndexOf")
|
|
572
|
+
lowerer.noLowering(".lastIndexOf on an array of engine values", call);
|
|
573
|
+
if (call.arguments.length > 1)
|
|
574
|
+
lowerer.noLowering(`.${method} with fromIndex on '${lowerer.fmt(elem)}' elements`, call);
|
|
559
575
|
if (!typeEquals(needle.type, elem))
|
|
560
576
|
lowerer.badType(argNode, lowerer.typeOf(argNode));
|
|
561
577
|
return {
|
|
@@ -563,15 +579,38 @@ function lowerArraySearchCall(lowerer, call, access, method, elem) {
|
|
|
563
579
|
method,
|
|
564
580
|
receiver,
|
|
565
581
|
args: [needle],
|
|
566
|
-
type: method === "
|
|
582
|
+
type: method === "includes" ? BOOL : F64,
|
|
567
583
|
loc,
|
|
568
584
|
};
|
|
569
585
|
}
|
|
570
|
-
|
|
571
|
-
|
|
586
|
+
// Search uses strict equality, never conversion to the array's element
|
|
587
|
+
// type. Retain primitive needles of a different kind in the comparison
|
|
588
|
+
// union so a nonmatching kind returns a miss instead of throwing.
|
|
589
|
+
const needleArms = needle.type.kind === "union"
|
|
590
|
+
? lowerer.unions.get(needle.type.unionId).arms
|
|
591
|
+
: [needle.type];
|
|
592
|
+
const scalarNeedle = needleArms.every((arm) => arm.kind === "f64" || arm.kind === "string" || arm.kind === "bool" ||
|
|
593
|
+
arm.kind === "bigint" || arm.kind === "symbol" || isUnitType(arm));
|
|
594
|
+
const valueArms = lowerer.unions.get(valueT.unionId).arms;
|
|
595
|
+
if (scalarNeedle && !valueArms.some((arm) => arm.kind === "func" || arm.kind === "set")) {
|
|
596
|
+
const arms = [...valueArms];
|
|
597
|
+
for (const arm of needleArms)
|
|
598
|
+
if (!arms.some((existing) => typeEquals(existing, arm)))
|
|
599
|
+
arms.push(arm);
|
|
600
|
+
if (arms.length !== valueArms.length)
|
|
601
|
+
valueT = { kind: "union", unionId: lowerer.unions.intern(arms) };
|
|
602
|
+
}
|
|
603
|
+
const directNeedle = method === "lastIndexOf" && elem.kind !== "union" && typeEquals(needle.type, elem);
|
|
604
|
+
const searchNeedle = directNeedle ? needle : lowerer.coerceInto(argNode, needle, valueT);
|
|
605
|
+
// Checked dynamic extraction can copy a composite. Its new identity cannot
|
|
606
|
+
// be compared to an array element as though it were the original value.
|
|
607
|
+
if (method === "lastIndexOf" && searchNeedle.kind === "dynCheck" && (elem.kind === "record" || elem.kind === "array")) {
|
|
608
|
+
lowerer.noLowering(".lastIndexOf with a checked dynamic reference needle", call);
|
|
609
|
+
}
|
|
610
|
+
if (!directNeedle && !typeEquals(searchNeedle.type, valueT))
|
|
572
611
|
lowerer.badType(argNode, lowerer.typeOf(argNode));
|
|
573
|
-
const helper = arraySearchHelper(lowerer, method, elem, valueT, loc);
|
|
574
|
-
return { kind: "call", callee: helper, args: [receiver, searchNeedle], type: method === "
|
|
612
|
+
const helper = arraySearchHelper(lowerer, method, elem, valueT, searchNeedle.type, directNeedle, fromIndex.type, call.arguments[1] ?? call, loc);
|
|
613
|
+
return { kind: "call", callee: helper, args: [receiver, searchNeedle, fromIndex], type: method === "includes" ? BOOL : F64, loc };
|
|
575
614
|
}
|
|
576
615
|
/** SameValueZero over a static value union. `unionEq` supplies strict
|
|
577
616
|
* equality for every arm; the extra f64-arm test is the only difference for
|
|
@@ -615,25 +654,65 @@ function arraySearchEquality(lowerer, left, right, sameValueZero, loc) {
|
|
|
615
654
|
};
|
|
616
655
|
return { kind: "logical", op: "||", left: strict, right: bothNaN, type: BOOL, loc };
|
|
617
656
|
}
|
|
618
|
-
function arraySearchHelper(lowerer, method, elem, valueT, loc) {
|
|
619
|
-
const key = `${method}:${typeKey(elem)}:${typeKey(valueT)}`;
|
|
657
|
+
function arraySearchHelper(lowerer, method, elem, valueT, needleT, directNeedle, fromT, fromNode, loc) {
|
|
658
|
+
const key = `${method}:${typeKey(elem)}:${typeKey(valueT)}:${typeKey(needleT)}:${directNeedle}:${typeKey(fromT)}`;
|
|
620
659
|
const existing = lowerer.arrHofHelpers.get(key);
|
|
621
660
|
if (existing)
|
|
622
661
|
return existing;
|
|
623
662
|
const name = `%arr.${method}.${lowerer.arrHofHelpers.size}`;
|
|
624
663
|
lowerer.arrHofHelpers.set(key, name);
|
|
625
664
|
const arrT = arrayOf(elem);
|
|
626
|
-
const resultT = method === "
|
|
665
|
+
const resultT = method === "includes" ? BOOL : F64;
|
|
627
666
|
const arrRef = varRef("a.0", arrT, loc);
|
|
628
667
|
const indexRef = varRef("i.0", F64, loc);
|
|
629
|
-
const needleRef = varRef("needle.0",
|
|
668
|
+
const needleRef = varRef("needle.0", needleT, loc);
|
|
630
669
|
const stateRef = varRef("state.0", F64, loc);
|
|
631
670
|
const nextRef = varRef("next.0", F64, loc);
|
|
671
|
+
const fromRef = varRef("from.0", fromT, loc);
|
|
672
|
+
const numberRef = varRef("number.0", F64, loc);
|
|
673
|
+
const integerRef = varRef("integer.0", F64, loc);
|
|
674
|
+
// Snapshot length before coercing fromIndex, then apply ToIntegerOrInfinity.
|
|
675
|
+
const number = positionNumber(lowerer, fromRef, numLit(0, loc), fromNode, "array search position");
|
|
676
|
+
const integer = {
|
|
677
|
+
kind: "ternary",
|
|
678
|
+
cond: { kind: "libCall", fn: "num.isNaN", args: [numberRef], type: BOOL, loc },
|
|
679
|
+
then: numLit(0, loc),
|
|
680
|
+
else_: { kind: "libCall", fn: "math.trunc", args: [numberRef], type: F64, loc },
|
|
681
|
+
type: F64,
|
|
682
|
+
loc,
|
|
683
|
+
};
|
|
684
|
+
const forwardStart = {
|
|
685
|
+
kind: "ternary",
|
|
686
|
+
cond: { kind: "bin", op: "<", left: integerRef, right: numLit(0, loc), type: BOOL, loc },
|
|
687
|
+
then: {
|
|
688
|
+
kind: "libCall", fn: "math.max",
|
|
689
|
+
args: [{ kind: "bin", op: "+", left: varRef("n.0", F64, loc), right: integerRef, type: F64, loc }, numLit(0, loc)],
|
|
690
|
+
type: F64, loc,
|
|
691
|
+
},
|
|
692
|
+
// Adding positive zero canonicalizes a truncated negative zero.
|
|
693
|
+
else_: { kind: "bin", op: "+", left: integerRef, right: numLit(0, loc), type: F64, loc },
|
|
694
|
+
type: F64,
|
|
695
|
+
loc,
|
|
696
|
+
};
|
|
697
|
+
const start = method === "lastIndexOf" ? {
|
|
698
|
+
kind: "ternary",
|
|
699
|
+
cond: { kind: "bin", op: "<", left: integerRef, right: numLit(0, loc), type: BOOL, loc },
|
|
700
|
+
then: { kind: "bin", op: "+", left: varRef("n.0", F64, loc), right: integerRef, type: F64, loc },
|
|
701
|
+
else_: {
|
|
702
|
+
kind: "libCall", fn: "math.min",
|
|
703
|
+
args: [
|
|
704
|
+
{ kind: "bin", op: "+", left: integerRef, right: numLit(0, loc), type: F64, loc },
|
|
705
|
+
{ kind: "bin", op: "-", left: varRef("n.0", F64, loc), right: numLit(1, loc), type: F64, loc },
|
|
706
|
+
],
|
|
707
|
+
type: F64, loc,
|
|
708
|
+
},
|
|
709
|
+
type: F64, loc,
|
|
710
|
+
} : forwardStart;
|
|
632
711
|
const rawRead = { kind: "arrayGet", arr: arrRef, index: indexRef, type: elem, loc };
|
|
633
712
|
const missing = lowerer.wrappedUndefined(valueT, loc);
|
|
634
713
|
if (!missing)
|
|
635
714
|
throw new InternalCompilerError("array search helper needs an undefined arm");
|
|
636
|
-
const
|
|
715
|
+
const optionalValue = {
|
|
637
716
|
kind: "ternary",
|
|
638
717
|
cond: { kind: "bin", op: "===", left: stateRef, right: numLit(1, loc), type: BOOL, loc },
|
|
639
718
|
then: typeEquals(elem, valueT) ? rawRead : lowerer.coerceToExpected(rawRead, valueT),
|
|
@@ -641,12 +720,14 @@ function arraySearchHelper(lowerer, method, elem, valueT, loc) {
|
|
|
641
720
|
type: valueT,
|
|
642
721
|
loc,
|
|
643
722
|
};
|
|
644
|
-
const
|
|
645
|
-
const
|
|
723
|
+
const value = directNeedle ? rawRead : optionalValue;
|
|
724
|
+
const readT = directNeedle ? elem : valueT;
|
|
725
|
+
const equal = arraySearchEquality(lowerer, varRef("value.0", readT, loc), needleRef, method === "includes", loc);
|
|
726
|
+
const match = method !== "includes"
|
|
646
727
|
? {
|
|
647
728
|
kind: "logical",
|
|
648
729
|
op: "&&",
|
|
649
|
-
left: { kind: "bin", op: "!==", left: stateRef, right: numLit(0, loc), type: BOOL, loc },
|
|
730
|
+
left: { kind: "bin", op: directNeedle ? "===" : "!==", left: stateRef, right: numLit(directNeedle ? 1 : 0, loc), type: BOOL, loc },
|
|
650
731
|
right: equal,
|
|
651
732
|
type: BOOL,
|
|
652
733
|
loc,
|
|
@@ -655,7 +736,7 @@ function arraySearchHelper(lowerer, method, elem, valueT, loc) {
|
|
|
655
736
|
const undefinedTag = lowerer.armTag(valueT.unionId, UNDEFINED_T);
|
|
656
737
|
if (undefinedTag < 0)
|
|
657
738
|
throw new InternalCompilerError("array search helper needs an undefined tag");
|
|
658
|
-
const needleIsUndefined = {
|
|
739
|
+
const needleIsUndefined = directNeedle ? boolLit(false, loc) : {
|
|
659
740
|
kind: "unionIsTag",
|
|
660
741
|
unionId: valueT.unionId,
|
|
661
742
|
tag: undefinedTag,
|
|
@@ -667,7 +748,7 @@ function arraySearchHelper(lowerer, method, elem, valueT, loc) {
|
|
|
667
748
|
const advance = {
|
|
668
749
|
kind: "assign",
|
|
669
750
|
localId: "i.0",
|
|
670
|
-
value: { kind: "bin", op: "+", left: indexRef, right: numLit(1, loc), type: F64, loc },
|
|
751
|
+
value: { kind: "bin", op: method === "lastIndexOf" ? "-" : "+", left: indexRef, right: numLit(1, loc), type: F64, loc },
|
|
671
752
|
loc,
|
|
672
753
|
};
|
|
673
754
|
const visitPresent = [
|
|
@@ -675,13 +756,13 @@ function arraySearchHelper(lowerer, method, elem, valueT, loc) {
|
|
|
675
756
|
{
|
|
676
757
|
kind: "if",
|
|
677
758
|
cond: match,
|
|
678
|
-
then: [{ kind: "return", value: method === "
|
|
759
|
+
then: [{ kind: "return", value: method === "includes" ? boolLit(true, loc) : indexRef, loc }],
|
|
679
760
|
else_: null,
|
|
680
761
|
loc,
|
|
681
762
|
},
|
|
682
763
|
advance,
|
|
683
764
|
];
|
|
684
|
-
const skipHole = [
|
|
765
|
+
const skipHole = method === "lastIndexOf" ? [advance] : [
|
|
685
766
|
{
|
|
686
767
|
kind: "varDecl",
|
|
687
768
|
localId: "next.0",
|
|
@@ -707,28 +788,36 @@ function arraySearchHelper(lowerer, method, elem, valueT, loc) {
|
|
|
707
788
|
name,
|
|
708
789
|
params: [
|
|
709
790
|
{ localId: "a.0", name: "a", type: arrT },
|
|
710
|
-
{ localId: "needle.0", name: "needle", type:
|
|
791
|
+
{ localId: "needle.0", name: "needle", type: needleT },
|
|
792
|
+
{ localId: "from.0", name: "from", type: fromT },
|
|
711
793
|
],
|
|
712
794
|
returnType: resultT,
|
|
713
795
|
locals: [
|
|
714
796
|
{ id: "a.0", name: "a", type: arrT, mutable: true },
|
|
715
|
-
{ id: "needle.0", name: "needle", type:
|
|
797
|
+
{ id: "needle.0", name: "needle", type: needleT, mutable: true },
|
|
798
|
+
{ id: "from.0", name: "from", type: fromT, mutable: false },
|
|
799
|
+
{ id: "number.0", name: "number", type: F64, mutable: false },
|
|
800
|
+
{ id: "integer.0", name: "integer", type: F64, mutable: false },
|
|
716
801
|
{ id: "n.0", name: "n", type: F64, mutable: false },
|
|
717
802
|
{ id: "i.0", name: "i", type: F64, mutable: true },
|
|
718
803
|
{ id: "state.0", name: "state", type: F64, mutable: false },
|
|
719
804
|
{ id: "next.0", name: "next", type: F64, mutable: false },
|
|
720
|
-
{ id: "value.0", name: "value", type:
|
|
805
|
+
{ id: "value.0", name: "value", type: readT, mutable: false },
|
|
721
806
|
],
|
|
722
807
|
body: [
|
|
723
808
|
readLenStmt(arrT, loc),
|
|
724
|
-
{ kind: "varDecl", localId: "
|
|
809
|
+
{ kind: "varDecl", localId: "number.0", init: number, loc },
|
|
810
|
+
{ kind: "varDecl", localId: "integer.0", init: integer, loc },
|
|
811
|
+
{ kind: "varDecl", localId: "i.0", init: start, loc },
|
|
725
812
|
{
|
|
726
813
|
kind: "while",
|
|
727
|
-
cond:
|
|
814
|
+
cond: method === "lastIndexOf"
|
|
815
|
+
? { kind: "bin", op: ">=", left: indexRef, right: numLit(0, loc), type: BOOL, loc }
|
|
816
|
+
: { kind: "bin", op: "<", left: indexRef, right: varRef("n.0", F64, loc), type: BOOL, loc },
|
|
728
817
|
body: loopBody,
|
|
729
818
|
loc,
|
|
730
819
|
},
|
|
731
|
-
{ kind: "return", value: method === "
|
|
820
|
+
{ kind: "return", value: method === "includes" ? boolLit(false, loc) : numLit(-1, loc), loc },
|
|
732
821
|
],
|
|
733
822
|
loc,
|
|
734
823
|
});
|
|
@@ -828,6 +917,18 @@ function hofCallbackArg(lowerer, argNode, lead, arrT, bindUntyped = false) {
|
|
|
828
917
|
}
|
|
829
918
|
lowerer.implicitParamTypes = previousImplicit;
|
|
830
919
|
}
|
|
920
|
+
// Array storage widens indexed reads with undefined so holes remain
|
|
921
|
+
// observable. The HOF helper guards every callback behind arrayHas,
|
|
922
|
+
// so an exact-ABI builtin closure may safely adapt from its declared
|
|
923
|
+
// parameter to that guarded union. Keep this exception scoped to the
|
|
924
|
+
// compiler-generated builtin values; ordinary callback-value policy is
|
|
925
|
+
// unchanged.
|
|
926
|
+
if (fnArg.type.kind === "func" && lowerer.isBuiltinCallableValue(fnArg)) {
|
|
927
|
+
const callbackType = funcOf(full.slice(0, fnArg.type.params.length), fnArg.type.ret);
|
|
928
|
+
if (!typeEquals(fnArg.type, callbackType)) {
|
|
929
|
+
fnArg = lowerer.coerceToExpected(fnArg, callbackType);
|
|
930
|
+
}
|
|
931
|
+
}
|
|
831
932
|
if (fnArg.type.kind !== "func" ||
|
|
832
933
|
fnArg.type.params.length > full.length ||
|
|
833
934
|
!fnArg.type.params.every((p, i) => typeEquals(p, full[i]))) {
|
|
@@ -1043,6 +1144,139 @@ function arrayHofHelper(lowerer, method, elem, fnRet, arity, loc, outElem = fnRe
|
|
|
1043
1144
|
lowerer.liftedFns.push(buildArrayHofFn(lowerer, name, method, elem, fnRet, arity, loc, outElem));
|
|
1044
1145
|
return name;
|
|
1045
1146
|
}
|
|
1147
|
+
/** The TS 5.2+ callable-union array rule: a `T[] | U[]` receiver exposes
|
|
1148
|
+
* common HOFs with a callback over `T | U`. The runtime value remains the
|
|
1149
|
+
* original concrete array arm — never a copied `(T | U)[]` — and an
|
|
1150
|
+
* arm-specific function adapter wraps the element and receiver arguments
|
|
1151
|
+
* into the union-wide callback ABI. That preserves sparse-array reads,
|
|
1152
|
+
* callback mutation, and the callback's third-argument identity. */
|
|
1153
|
+
function lowerArrayUnionHofCall(lowerer, call, access, receiverT) {
|
|
1154
|
+
const method = access.name.text;
|
|
1155
|
+
if (method !== "map" && method !== "forEach")
|
|
1156
|
+
return null;
|
|
1157
|
+
if (!lowerer.isStdlibMember(access))
|
|
1158
|
+
return null;
|
|
1159
|
+
const def = lowerer.unions.get(receiverT.unionId);
|
|
1160
|
+
if (!def || def.arms.length < 2 || !def.arms.every((arm) => arm.kind === "array"))
|
|
1161
|
+
return null;
|
|
1162
|
+
if (call.arguments.length !== 1 || !call.arguments[0])
|
|
1163
|
+
return null;
|
|
1164
|
+
const arrays = def.arms;
|
|
1165
|
+
const valueArms = [];
|
|
1166
|
+
const addValueArm = (type) => {
|
|
1167
|
+
if (type.kind === "dyn" || type.kind === "jsval" || type.kind === "caught" || type.kind === "void")
|
|
1168
|
+
return false;
|
|
1169
|
+
if (type.kind === "union") {
|
|
1170
|
+
const inner = lowerer.unions.get(type.unionId);
|
|
1171
|
+
if (!inner)
|
|
1172
|
+
return false;
|
|
1173
|
+
for (const arm of inner.arms) {
|
|
1174
|
+
if (!valueArms.some((candidate) => typeEquals(candidate, arm)))
|
|
1175
|
+
valueArms.push(arm);
|
|
1176
|
+
}
|
|
1177
|
+
return true;
|
|
1178
|
+
}
|
|
1179
|
+
if (!valueArms.some((candidate) => typeEquals(candidate, type)))
|
|
1180
|
+
valueArms.push(type);
|
|
1181
|
+
return true;
|
|
1182
|
+
};
|
|
1183
|
+
for (const array of arrays) {
|
|
1184
|
+
if (!addValueArm(array.elem))
|
|
1185
|
+
return null;
|
|
1186
|
+
}
|
|
1187
|
+
const valueT = valueArms.length === 1
|
|
1188
|
+
? valueArms[0]
|
|
1189
|
+
: { kind: "union", unionId: lowerer.unions.intern(valueArms) };
|
|
1190
|
+
const argNode = call.arguments[0];
|
|
1191
|
+
const { fnArg, arity } = hofCallbackArg(lowerer, argNode, [valueT], receiverT);
|
|
1192
|
+
const fnRet = fnArg.type.ret;
|
|
1193
|
+
if (method === "map" && (fnRet.kind === "void" || fnRet.kind === "func")) {
|
|
1194
|
+
lowerer.badType(call, lowerer.typeOf(call));
|
|
1195
|
+
}
|
|
1196
|
+
if (method === "map")
|
|
1197
|
+
fenceProducedArrayElem(lowerer, call, "'.map()'", fnRet);
|
|
1198
|
+
const outElem = method === "map" ? callbackArrayElem(lowerer, call, fnRet) : VOID;
|
|
1199
|
+
const resultT = method === "map" ? arrayOf(outElem) : VOID;
|
|
1200
|
+
for (const array of arrays) {
|
|
1201
|
+
const armFnT = funcOf([arrayValueType(lowerer, array.elem), F64, array].slice(0, arity), fnRet);
|
|
1202
|
+
if (!lowerer.cleanFuncAdaptable(fnArg.type, armFnT))
|
|
1203
|
+
return null;
|
|
1204
|
+
}
|
|
1205
|
+
const helper = arrayUnionHofHelper(lowerer, method, receiverT, arrays, fnArg.type, arity, fnRet, outElem, locOf(call));
|
|
1206
|
+
return {
|
|
1207
|
+
kind: "call",
|
|
1208
|
+
callee: helper,
|
|
1209
|
+
args: [lowerer.coerceInto(access.expression, lowerer.lowerExpr(access.expression), receiverT), fnArg],
|
|
1210
|
+
type: resultT,
|
|
1211
|
+
loc: locOf(call),
|
|
1212
|
+
};
|
|
1213
|
+
}
|
|
1214
|
+
function arrayUnionHofHelper(lowerer, method, receiverT, arrays, callbackT, arity, fnRet, outElem, loc) {
|
|
1215
|
+
const resultT = method === "map" ? arrayOf(outElem) : VOID;
|
|
1216
|
+
const key = `union:${method}:${receiverT.unionId}:${typeKey(callbackT)}:${typeKey(resultT)}`;
|
|
1217
|
+
const existing = lowerer.arrHofHelpers.get(key);
|
|
1218
|
+
if (existing)
|
|
1219
|
+
return existing;
|
|
1220
|
+
const name = `%arr.union.${lowerer.arrHofHelpers.size}`;
|
|
1221
|
+
lowerer.arrHofHelpers.set(key, name);
|
|
1222
|
+
const receiver = varRef("a.0", receiverT, loc);
|
|
1223
|
+
const callback = varRef("f.0", callbackT, loc);
|
|
1224
|
+
const branch = (array) => {
|
|
1225
|
+
const tag = lowerer.armTag(receiverT.unionId, array);
|
|
1226
|
+
const concrete = {
|
|
1227
|
+
kind: "unionNarrow",
|
|
1228
|
+
unionId: receiverT.unionId,
|
|
1229
|
+
tag,
|
|
1230
|
+
value: receiver,
|
|
1231
|
+
type: array,
|
|
1232
|
+
loc,
|
|
1233
|
+
};
|
|
1234
|
+
const armFnT = funcOf([arrayValueType(lowerer, array.elem), F64, array].slice(0, arity), fnRet);
|
|
1235
|
+
const adapted = lowerer.coerceToExpected(callback, armFnT);
|
|
1236
|
+
if (!typeEquals(adapted.type, armFnT)) {
|
|
1237
|
+
throw new InternalCompilerError("lowerer bug: union-array callback stopped adapting");
|
|
1238
|
+
}
|
|
1239
|
+
const callee = arrayHofHelper(lowerer, method, array.elem, fnRet, arity, loc, outElem);
|
|
1240
|
+
const invoke = { kind: "call", callee, args: [concrete, adapted], type: resultT, loc };
|
|
1241
|
+
return resultT.kind === "void"
|
|
1242
|
+
? [{ kind: "exprStmt", expr: invoke, loc }, { kind: "return", value: null, loc }]
|
|
1243
|
+
: [{ kind: "return", value: invoke, loc }];
|
|
1244
|
+
};
|
|
1245
|
+
let body = branch(arrays[arrays.length - 1]);
|
|
1246
|
+
for (let i = arrays.length - 2; i >= 0; i--) {
|
|
1247
|
+
const array = arrays[i];
|
|
1248
|
+
body = [{
|
|
1249
|
+
kind: "if",
|
|
1250
|
+
cond: {
|
|
1251
|
+
kind: "unionIsTag",
|
|
1252
|
+
unionId: receiverT.unionId,
|
|
1253
|
+
tag: lowerer.armTag(receiverT.unionId, array),
|
|
1254
|
+
negated: false,
|
|
1255
|
+
value: receiver,
|
|
1256
|
+
type: BOOL,
|
|
1257
|
+
loc,
|
|
1258
|
+
},
|
|
1259
|
+
then: branch(array),
|
|
1260
|
+
else_: body,
|
|
1261
|
+
loc,
|
|
1262
|
+
}];
|
|
1263
|
+
}
|
|
1264
|
+
lowerer.liftedFns.push({
|
|
1265
|
+
name,
|
|
1266
|
+
params: [
|
|
1267
|
+
{ localId: "a.0", name: "a", type: receiverT },
|
|
1268
|
+
{ localId: "f.0", name: "f", type: callbackT },
|
|
1269
|
+
],
|
|
1270
|
+
returnType: resultT,
|
|
1271
|
+
locals: [
|
|
1272
|
+
{ id: "a.0", name: "a", type: receiverT, mutable: false },
|
|
1273
|
+
{ id: "f.0", name: "f", type: callbackT, mutable: false },
|
|
1274
|
+
],
|
|
1275
|
+
body,
|
|
1276
|
+
loc,
|
|
1277
|
+
});
|
|
1278
|
+
return name;
|
|
1279
|
+
}
|
|
1046
1280
|
/** READ-ONLY array methods on TUPLE receivers — `t.slice(...)` and
|
|
1047
1281
|
* `t.map(f)`: a tuple is a fixed-shape record, but these methods never
|
|
1048
1282
|
* write, so the positions snapshot into a fresh array (the for-of-over-
|
|
@@ -1141,15 +1375,8 @@ export function lowerTupleReadMethodCall(lowerer, call, access) {
|
|
|
1141
1375
|
const helper = arrayHofHelper(lowerer, "map", snapshotElem, fnRet, arity, loc, outElem);
|
|
1142
1376
|
return { kind: "call", callee: helper, args: [snapshot, fnArg], type: arrayOf(outElem), loc };
|
|
1143
1377
|
}
|
|
1144
|
-
/** An
|
|
1145
|
-
|
|
1146
|
-
* is an integer in [0, len), JS's property-miss undefined otherwise —
|
|
1147
|
-
* instead of the trap divergence 4 documents for program code.
|
|
1148
|
-
* Package JS is inference-typed, guard-style code (`registeredArguments
|
|
1149
|
-
* .slice(-1)[0]`, commander's last-element probe), and the trap would
|
|
1150
|
-
* fire on working Node idioms; program files keep the documented trap
|
|
1151
|
-
* (their annotations can prove bounds). Existing element unions retag
|
|
1152
|
-
* into the canonical union with an added undefined arm. */
|
|
1378
|
+
/** An ordinary indexed read: values retain their element type, while
|
|
1379
|
+
* holes, missing properties, and present undefined yield undefined. */
|
|
1153
1380
|
export function lowerSafeIndexRead(lowerer, arr, index, loc) {
|
|
1154
1381
|
const elem = arr.type.elem;
|
|
1155
1382
|
if (elem.kind === "void" || elem.kind === "dyn")
|
|
@@ -1178,6 +1405,21 @@ export function lowerSafeIndexRead(lowerer, arr, index, loc) {
|
|
|
1178
1405
|
}
|
|
1179
1406
|
return { kind: "call", callee: name, args: [arr, index], type: resultT, loc };
|
|
1180
1407
|
}
|
|
1408
|
+
/** Fuse only our own f64-array read helper with immediate ToNumber.
|
|
1409
|
+
* Reuse its arguments verbatim so the receiver and index still evaluate
|
|
1410
|
+
* exactly once, in order, before the read. Do not specialize user calls,
|
|
1411
|
+
* union-element arrays, or optional values stored in locals. */
|
|
1412
|
+
export function tryLowerNumericIndexRead(lowerer, operand, loc) {
|
|
1413
|
+
if (operand.kind !== "call" || operand.callee !== lowerer.arrHofHelpers.get(`idxOr:${typeKey(F64)}`))
|
|
1414
|
+
return null;
|
|
1415
|
+
const [arr, index] = operand.args;
|
|
1416
|
+
if (operand.args.length !== 2 || arr?.type.kind !== "array" || arr.type.elem.kind !== "f64" || index?.type.kind !== "f64")
|
|
1417
|
+
return null;
|
|
1418
|
+
// The intrinsic owns the evaluated receiver through index evaluation,
|
|
1419
|
+
// then borrows it for one slot lookup. No extra helper parameter or
|
|
1420
|
+
// separate state/getter expressions need to retain the array again.
|
|
1421
|
+
return { kind: "arrIntrinsic", method: "getNumber", receiver: arr, args: [index], type: F64, loc };
|
|
1422
|
+
}
|
|
1181
1423
|
/** Backward-compatible name for the npm-static probe path. */
|
|
1182
1424
|
export const lowerNpmStaticSafeIndexRead = lowerSafeIndexRead;
|
|
1183
1425
|
/** Interned synthetic function for one (elem, argument-shape) concat —
|