@scriptc/compiler 0.1.4 → 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 +75 -11
- package/dist/backend/c/c-emitter.d.ts +5 -1
- package/dist/backend/c/c-emitter.js +22 -9
- 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 +264 -21
- 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 +8 -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 +1 -0
- package/dist/backend/link-plan.js +2 -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 +25 -9
- 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 +22 -17
- package/dist/backend/llvm/expr-bytes.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 +6 -2
- package/dist/backend/llvm/expr-primitives.js +22 -7
- package/dist/backend/llvm/expr-primitives.js.map +1 -1
- package/dist/backend/llvm/lib-filesystem.js +12 -0
- package/dist/backend/llvm/lib-filesystem.js.map +1 -1
- package/dist/backend/llvm/lib-network.js +53 -10
- package/dist/backend/llvm/lib-network.js.map +1 -1
- package/dist/backend/llvm/lib-shared.js +64 -0
- package/dist/backend/llvm/lib-shared.js.map +1 -1
- package/dist/backend/native-toolchain.d.ts +2 -0
- package/dist/backend/native-toolchain.js +2 -1
- package/dist/backend/native-toolchain.js.map +1 -1
- package/dist/backend/targets.d.ts +3 -0
- package/dist/backend/targets.js +13 -0
- package/dist/backend/targets.js.map +1 -1
- package/dist/coverage/surface-manifest.js +6 -4
- package/dist/coverage/surface-manifest.js.map +1 -1
- package/dist/executable/executable-cache.d.ts +2 -0
- package/dist/executable/executable-cache.js +3 -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/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-builtins.d.ts +4 -5
- package/dist/frontend/lowering/lower-builtins.js +18 -10
- package/dist/frontend/lowering/lower-builtins.js.map +1 -1
- package/dist/frontend/lowering/lower-calls.d.ts +3 -2
- package/dist/frontend/lowering/lower-calls.js +265 -88
- package/dist/frontend/lowering/lower-calls.js.map +1 -1
- package/dist/frontend/lowering/lower-classes.js +2 -2
- 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 +143 -51
- package/dist/frontend/lowering/lower-containers.js.map +1 -1
- package/dist/frontend/lowering/lower-exprs.d.ts +12 -0
- package/dist/frontend/lowering/lower-exprs.js +128 -10
- package/dist/frontend/lowering/lower-exprs.js.map +1 -1
- package/dist/frontend/lowering/lower-island.js +17 -6
- package/dist/frontend/lowering/lower-island.js.map +1 -1
- package/dist/frontend/lowering/lower-server.d.ts +2 -4
- package/dist/frontend/lowering/lower-server.js +288 -32
- package/dist/frontend/lowering/lower-server.js.map +1 -1
- package/dist/frontend/lowering/lower-stmts.js +4 -4
- package/dist/frontend/lowering/lower-stmts.js.map +1 -1
- package/dist/frontend/lowering/lowerer.js +37 -2
- 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 +8 -14
- package/dist/frontend/lowering/surfaces.js +37 -27
- package/dist/frontend/lowering/surfaces.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.js +7 -6
- package/dist/frontend/npm.js.map +1 -1
- package/dist/frontend/program.js +15 -4
- 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/type-mapper.js +16 -5
- package/dist/frontend/type-mapper.js.map +1 -1
- package/dist/index.d.ts +3 -3
- package/dist/index.js +13 -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 +42 -34
- package/dist/ir/ir.js +7 -1
- package/dist/ir/ir.js.map +1 -1
- package/dist/ir/ir.test.js +6 -0
- 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/validate.js +92 -8
- 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/library-cache.js +0 -1
- package/dist/library/library-cache.js.map +1 -1
- package/dist/library/library-cache.test.js +3 -3
- package/dist/library/library-cache.test.js.map +1 -1
- package/package.json +17 -17
- package/surface-manifest.json +55 -42
|
@@ -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
|
|
@@ -287,14 +287,14 @@ export function lowerArrayMethodCall(lowerer, call, access) {
|
|
|
287
287
|
};
|
|
288
288
|
}
|
|
289
289
|
// The lib declares wider call forms than the lowered surface —
|
|
290
|
-
//
|
|
291
|
-
//
|
|
290
|
+
// join's separator is optional, and the predicate/mapping HOFs take
|
|
291
|
+
// a thisArg. Each unlowered form is fenced
|
|
292
292
|
// per site (SC2020), never silently truncated to the supported
|
|
293
293
|
// arguments. push/unshift lower every declared form (variadic, 0 args
|
|
294
294
|
// included — Node returns the unchanged length); reduce/reduceRight
|
|
295
295
|
// lower both declared forms (with and without an initial value).
|
|
296
296
|
const arity = {
|
|
297
|
-
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],
|
|
298
298
|
concat: [0, Number.MAX_SAFE_INTEGER],
|
|
299
299
|
slice: [0, 2], shift: [0, 0], splice: [1, 2], at: [1, 1],
|
|
300
300
|
map: [1, 1], filter: [1, 1], forEach: [1, 1], find: [1, 1], findIndex: [1, 1], some: [1, 1],
|
|
@@ -306,12 +306,10 @@ export function lowerArrayMethodCall(lowerer, call, access) {
|
|
|
306
306
|
? "the removal forms lower — splice(start, deleteCount?); to insert, build a new array with slice and push"
|
|
307
307
|
: name === "join"
|
|
308
308
|
? 'pass the separator explicitly: join(",")'
|
|
309
|
-
: name === "
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
? "the thisArg parameter has no lowering — use an arrow function"
|
|
314
|
-
: 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;
|
|
315
313
|
lowerer.noLowering(`.${name} with ${call.arguments.length} argument${call.arguments.length === 1 ? "" : "s"}`, call, hint);
|
|
316
314
|
}
|
|
317
315
|
if (name === "push" || name === "unshift") {
|
|
@@ -393,7 +391,7 @@ export function lowerArrayMethodCall(lowerer, call, access) {
|
|
|
393
391
|
loc,
|
|
394
392
|
};
|
|
395
393
|
}
|
|
396
|
-
if (name === "indexOf" || name === "includes") {
|
|
394
|
+
if (name === "indexOf" || name === "lastIndexOf" || name === "includes") {
|
|
397
395
|
return lowerArraySearchCall(lowerer, call, access, name, elem);
|
|
398
396
|
}
|
|
399
397
|
if (name === "concat") {
|
|
@@ -531,14 +529,23 @@ export function lowerArrayMethodCall(lowerer, call, access) {
|
|
|
531
529
|
/** `indexOf` and `includes` normally use the runtime's typed search helpers,
|
|
532
530
|
* but an indexed array read is a `T | undefined` value. Keep that union
|
|
533
531
|
* through a small lowered loop so the needle's missing state is observable:
|
|
534
|
-
* indexOf
|
|
535
|
-
* loop also gives union-element arrays their value
|
|
536
|
-
* 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. */
|
|
537
535
|
function lowerArraySearchCall(lowerer, call, access, method, elem) {
|
|
538
536
|
const loc = locOf(call);
|
|
539
537
|
const receiver = lowerer.lowerExpr(access.expression);
|
|
540
|
-
const argNode = call.arguments[0];
|
|
541
|
-
|
|
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);
|
|
542
549
|
// A derived CLASS VALUE against a base-classval element widens (the same
|
|
543
550
|
// pointer — identity search is exact); the coercion path owns the ABI gate.
|
|
544
551
|
if (needle.type.kind === "classval" && elem.kind === "classval" && !typeEquals(needle.type, elem)) {
|
|
@@ -546,21 +553,25 @@ function lowerArraySearchCall(lowerer, call, access, method, elem) {
|
|
|
546
553
|
}
|
|
547
554
|
// Preserve the existing fast path for an ordinary, non-union needle. Its
|
|
548
555
|
// runtime implementation already has exact primitive/reference equality.
|
|
549
|
-
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)) {
|
|
550
557
|
return {
|
|
551
558
|
kind: "arrIntrinsic",
|
|
552
559
|
method,
|
|
553
560
|
receiver,
|
|
554
561
|
args: [needle],
|
|
555
|
-
type: method === "
|
|
562
|
+
type: method === "includes" ? BOOL : F64,
|
|
556
563
|
loc,
|
|
557
564
|
};
|
|
558
565
|
}
|
|
559
|
-
|
|
566
|
+
let valueT = arrayValueType(lowerer, elem);
|
|
560
567
|
// jsval/dyn arrays have a single engine value representation rather than a
|
|
561
568
|
// static undefined arm. Keep their existing runtime path and its explicit
|
|
562
569
|
// type fence; typed static arrays take the state-aware helper below.
|
|
563
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);
|
|
564
575
|
if (!typeEquals(needle.type, elem))
|
|
565
576
|
lowerer.badType(argNode, lowerer.typeOf(argNode));
|
|
566
577
|
return {
|
|
@@ -568,15 +579,38 @@ function lowerArraySearchCall(lowerer, call, access, method, elem) {
|
|
|
568
579
|
method,
|
|
569
580
|
receiver,
|
|
570
581
|
args: [needle],
|
|
571
|
-
type: method === "
|
|
582
|
+
type: method === "includes" ? BOOL : F64,
|
|
572
583
|
loc,
|
|
573
584
|
};
|
|
574
585
|
}
|
|
575
|
-
|
|
576
|
-
|
|
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))
|
|
577
611
|
lowerer.badType(argNode, lowerer.typeOf(argNode));
|
|
578
|
-
const helper = arraySearchHelper(lowerer, method, elem, valueT, loc);
|
|
579
|
-
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 };
|
|
580
614
|
}
|
|
581
615
|
/** SameValueZero over a static value union. `unionEq` supplies strict
|
|
582
616
|
* equality for every arm; the extra f64-arm test is the only difference for
|
|
@@ -620,25 +654,65 @@ function arraySearchEquality(lowerer, left, right, sameValueZero, loc) {
|
|
|
620
654
|
};
|
|
621
655
|
return { kind: "logical", op: "||", left: strict, right: bothNaN, type: BOOL, loc };
|
|
622
656
|
}
|
|
623
|
-
function arraySearchHelper(lowerer, method, elem, valueT, loc) {
|
|
624
|
-
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)}`;
|
|
625
659
|
const existing = lowerer.arrHofHelpers.get(key);
|
|
626
660
|
if (existing)
|
|
627
661
|
return existing;
|
|
628
662
|
const name = `%arr.${method}.${lowerer.arrHofHelpers.size}`;
|
|
629
663
|
lowerer.arrHofHelpers.set(key, name);
|
|
630
664
|
const arrT = arrayOf(elem);
|
|
631
|
-
const resultT = method === "
|
|
665
|
+
const resultT = method === "includes" ? BOOL : F64;
|
|
632
666
|
const arrRef = varRef("a.0", arrT, loc);
|
|
633
667
|
const indexRef = varRef("i.0", F64, loc);
|
|
634
|
-
const needleRef = varRef("needle.0",
|
|
668
|
+
const needleRef = varRef("needle.0", needleT, loc);
|
|
635
669
|
const stateRef = varRef("state.0", F64, loc);
|
|
636
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;
|
|
637
711
|
const rawRead = { kind: "arrayGet", arr: arrRef, index: indexRef, type: elem, loc };
|
|
638
712
|
const missing = lowerer.wrappedUndefined(valueT, loc);
|
|
639
713
|
if (!missing)
|
|
640
714
|
throw new InternalCompilerError("array search helper needs an undefined arm");
|
|
641
|
-
const
|
|
715
|
+
const optionalValue = {
|
|
642
716
|
kind: "ternary",
|
|
643
717
|
cond: { kind: "bin", op: "===", left: stateRef, right: numLit(1, loc), type: BOOL, loc },
|
|
644
718
|
then: typeEquals(elem, valueT) ? rawRead : lowerer.coerceToExpected(rawRead, valueT),
|
|
@@ -646,12 +720,14 @@ function arraySearchHelper(lowerer, method, elem, valueT, loc) {
|
|
|
646
720
|
type: valueT,
|
|
647
721
|
loc,
|
|
648
722
|
};
|
|
649
|
-
const
|
|
650
|
-
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"
|
|
651
727
|
? {
|
|
652
728
|
kind: "logical",
|
|
653
729
|
op: "&&",
|
|
654
|
-
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 },
|
|
655
731
|
right: equal,
|
|
656
732
|
type: BOOL,
|
|
657
733
|
loc,
|
|
@@ -660,7 +736,7 @@ function arraySearchHelper(lowerer, method, elem, valueT, loc) {
|
|
|
660
736
|
const undefinedTag = lowerer.armTag(valueT.unionId, UNDEFINED_T);
|
|
661
737
|
if (undefinedTag < 0)
|
|
662
738
|
throw new InternalCompilerError("array search helper needs an undefined tag");
|
|
663
|
-
const needleIsUndefined = {
|
|
739
|
+
const needleIsUndefined = directNeedle ? boolLit(false, loc) : {
|
|
664
740
|
kind: "unionIsTag",
|
|
665
741
|
unionId: valueT.unionId,
|
|
666
742
|
tag: undefinedTag,
|
|
@@ -672,7 +748,7 @@ function arraySearchHelper(lowerer, method, elem, valueT, loc) {
|
|
|
672
748
|
const advance = {
|
|
673
749
|
kind: "assign",
|
|
674
750
|
localId: "i.0",
|
|
675
|
-
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 },
|
|
676
752
|
loc,
|
|
677
753
|
};
|
|
678
754
|
const visitPresent = [
|
|
@@ -680,13 +756,13 @@ function arraySearchHelper(lowerer, method, elem, valueT, loc) {
|
|
|
680
756
|
{
|
|
681
757
|
kind: "if",
|
|
682
758
|
cond: match,
|
|
683
|
-
then: [{ kind: "return", value: method === "
|
|
759
|
+
then: [{ kind: "return", value: method === "includes" ? boolLit(true, loc) : indexRef, loc }],
|
|
684
760
|
else_: null,
|
|
685
761
|
loc,
|
|
686
762
|
},
|
|
687
763
|
advance,
|
|
688
764
|
];
|
|
689
|
-
const skipHole = [
|
|
765
|
+
const skipHole = method === "lastIndexOf" ? [advance] : [
|
|
690
766
|
{
|
|
691
767
|
kind: "varDecl",
|
|
692
768
|
localId: "next.0",
|
|
@@ -712,28 +788,36 @@ function arraySearchHelper(lowerer, method, elem, valueT, loc) {
|
|
|
712
788
|
name,
|
|
713
789
|
params: [
|
|
714
790
|
{ localId: "a.0", name: "a", type: arrT },
|
|
715
|
-
{ localId: "needle.0", name: "needle", type:
|
|
791
|
+
{ localId: "needle.0", name: "needle", type: needleT },
|
|
792
|
+
{ localId: "from.0", name: "from", type: fromT },
|
|
716
793
|
],
|
|
717
794
|
returnType: resultT,
|
|
718
795
|
locals: [
|
|
719
796
|
{ id: "a.0", name: "a", type: arrT, mutable: true },
|
|
720
|
-
{ 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 },
|
|
721
801
|
{ id: "n.0", name: "n", type: F64, mutable: false },
|
|
722
802
|
{ id: "i.0", name: "i", type: F64, mutable: true },
|
|
723
803
|
{ id: "state.0", name: "state", type: F64, mutable: false },
|
|
724
804
|
{ id: "next.0", name: "next", type: F64, mutable: false },
|
|
725
|
-
{ id: "value.0", name: "value", type:
|
|
805
|
+
{ id: "value.0", name: "value", type: readT, mutable: false },
|
|
726
806
|
],
|
|
727
807
|
body: [
|
|
728
808
|
readLenStmt(arrT, loc),
|
|
729
|
-
{ 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 },
|
|
730
812
|
{
|
|
731
813
|
kind: "while",
|
|
732
|
-
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 },
|
|
733
817
|
body: loopBody,
|
|
734
818
|
loc,
|
|
735
819
|
},
|
|
736
|
-
{ kind: "return", value: method === "
|
|
820
|
+
{ kind: "return", value: method === "includes" ? boolLit(false, loc) : numLit(-1, loc), loc },
|
|
737
821
|
],
|
|
738
822
|
loc,
|
|
739
823
|
});
|
|
@@ -1291,15 +1375,8 @@ export function lowerTupleReadMethodCall(lowerer, call, access) {
|
|
|
1291
1375
|
const helper = arrayHofHelper(lowerer, "map", snapshotElem, fnRet, arity, loc, outElem);
|
|
1292
1376
|
return { kind: "call", callee: helper, args: [snapshot, fnArg], type: arrayOf(outElem), loc };
|
|
1293
1377
|
}
|
|
1294
|
-
/** An
|
|
1295
|
-
|
|
1296
|
-
* is an integer in [0, len), JS's property-miss undefined otherwise —
|
|
1297
|
-
* instead of the trap divergence 4 documents for program code.
|
|
1298
|
-
* Package JS is inference-typed, guard-style code (`registeredArguments
|
|
1299
|
-
* .slice(-1)[0]`, commander's last-element probe), and the trap would
|
|
1300
|
-
* fire on working Node idioms; program files keep the documented trap
|
|
1301
|
-
* (their annotations can prove bounds). Existing element unions retag
|
|
1302
|
-
* 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. */
|
|
1303
1380
|
export function lowerSafeIndexRead(lowerer, arr, index, loc) {
|
|
1304
1381
|
const elem = arr.type.elem;
|
|
1305
1382
|
if (elem.kind === "void" || elem.kind === "dyn")
|
|
@@ -1328,6 +1405,21 @@ export function lowerSafeIndexRead(lowerer, arr, index, loc) {
|
|
|
1328
1405
|
}
|
|
1329
1406
|
return { kind: "call", callee: name, args: [arr, index], type: resultT, loc };
|
|
1330
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
|
+
}
|
|
1331
1423
|
/** Backward-compatible name for the npm-static probe path. */
|
|
1332
1424
|
export const lowerNpmStaticSafeIndexRead = lowerSafeIndexRead;
|
|
1333
1425
|
/** Interned synthetic function for one (elem, argument-shape) concat —
|