@scriptc/compiler 0.1.4 → 0.1.6
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 +298 -27
- 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 +58 -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 +20 -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-process.js +4 -3
- package/dist/backend/llvm/lib-process.js.map +1 -1
- package/dist/backend/llvm/lib-shared.js +65 -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 +12 -5
- 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/array-indexed-mutation.d.ts +8 -0
- package/dist/frontend/lowering/array-indexed-mutation.js +176 -0
- package/dist/frontend/lowering/array-indexed-mutation.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 +14 -6
- package/dist/frontend/lowering/containers/string-and-regexp.js +420 -34
- 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/indexed-comparison.d.ts +6 -0
- package/dist/frontend/lowering/indexed-comparison.js +30 -0
- package/dist/frontend/lowering/indexed-comparison.js.map +1 -0
- package/dist/frontend/lowering/lower-builtins.d.ts +5 -6
- package/dist/frontend/lowering/lower-builtins.js +24 -16
- 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 +522 -89
- package/dist/frontend/lowering/lower-calls.js.map +1 -1
- package/dist/frontend/lowering/lower-classes.js +5 -29
- package/dist/frontend/lowering/lower-classes.js.map +1 -1
- package/dist/frontend/lowering/lower-containers.d.ts +15 -11
- package/dist/frontend/lowering/lower-containers.js +588 -146
- 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 +145 -11
- 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 +10 -14
- package/dist/frontend/lowering/surfaces.js +50 -28
- 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 +56 -35
- package/dist/ir/ir.js +9 -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 +135 -34
- package/dist/ir/validate.js.map +1 -1
- package/dist/ir/validate.test.d.ts +1 -0
- package/dist/ir/validate.test.js +48 -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 +90 -45
|
@@ -6,18 +6,18 @@ import { InternalCompilerError } from "../../errors.js";
|
|
|
6
6
|
import * as ts from "../ts7/adapter.js";
|
|
7
7
|
import { lowerGenMethodCall } from "./lower-generators.js";
|
|
8
8
|
import { BIGINT_T, BOOL, CAUGHT, DYN, F64, JSVAL, STRING, SYMBOL_T, UNDEFINED_T, VOID, arrayOf, canBoxFuncIntoDyn, canConvertToDyn, canDynCheckTo, canMarshalTypedFuncIntoIsland, ffiClassType, ffiSourceParamTypes, funcOf, isFfiCallbackParam, isFfiContextParam, isFfiReleaseParam, isUnitType, shapeHasAccessorSlots, typeEquals } from "../../ir/ir.js";
|
|
9
|
-
import { isJsSourceFile, locOf } from "../program.js";
|
|
9
|
+
import { isJsSourceFile, isNodeEsmFile, locOf } from "../program.js";
|
|
10
10
|
import { genResultRecord, isGenericCallableMemberType, typeKey } from "../type-mapper.js";
|
|
11
11
|
import { PoisonError, dynFallbackType, dynUndefinedExpr, importCallHandleType, jsFuncNameOf, newFnCtx, nodeThrowExpr, staticImportNamespaceType } from "./lowerer.js";
|
|
12
12
|
import { enforceLibBoundary } from "./lib-boundary.js";
|
|
13
|
-
import { NARROW_FIRST, builtinFenceHintOf, builtinModuleFnOf } from "./surfaces.js";
|
|
13
|
+
import { NARROW_FIRST, STRING_INDEX_METHODS, STR_METHODS, builtinFenceHintOf, builtinModuleFnOf } from "./surfaces.js";
|
|
14
14
|
import { ffiBindingDiag, ffiSignatureDiag, libCallbackDiag, requiresDynamicDiag } from "../../diagnostics/diagnostic.js";
|
|
15
15
|
import { mixinFnShapeOf } from "./lower-mixins.js";
|
|
16
|
-
import { dynStringReceiver, lowerArrayFromCall, lowerDynArrayFilterCall, lowerDynArrayFlatMapCall, lowerGroupByStaticCall, lowerIteratorHelperCall, lowerObjectAssignIndexShape, lowerObjectFromEntriesCall, lowerObjectIterOverIndexShape, lowerTupleReadMethodCall } from "./lower-containers.js";
|
|
16
|
+
import { dynStringReceiver, lowerArrayConstructor, lowerArrayFromCall, lowerArrayOfCall, lowerDynArrayFilterCall, lowerDynArrayFlatMapCall, lowerGroupByStaticCall, lowerIteratorHelperCall, lowerObjectAssignIndexShape, lowerObjectFromEntriesCall, lowerObjectIterOverIndexShape, lowerTupleReadMethodCall } from "./lower-containers.js";
|
|
17
17
|
import { bufEncoding } from "./containers/bytes.js";
|
|
18
|
-
import { lowerRegexMethodCall, lowerStringMethodCall } from "./containers/string-and-regexp.js";
|
|
18
|
+
import { lowerRegexMethodCall, lowerStringIndexCall, lowerStringMethodCall, lowerStringPaddingCall, lowerStringSplitCall } from "./containers/string-and-regexp.js";
|
|
19
19
|
import { lowerChildStreamMethodCall, lowerChildWriterMethodCall, lowerCreateRequireCall, lowerCryptoHashMethodCall, lowerDirentMethodCall, lowerFileHandleMethodCall, lowerImportMetaResolveCall, lowerNodeModuleCall, lowerPerfHooksCall, lowerProcStreamMethodCall, lowerReflectApplyCall, lowerRequireResolveCall, lowerWatcherMethodCall } from "./lower-builtins.js";
|
|
20
|
-
import { lowerAbsenceProbe, lowerPromiseAllTupleCall, lowerPromiseRejectCall, templateRawTextOf } from "./lower-exprs.js";
|
|
20
|
+
import { lowerAbsenceProbe, lowerPromiseAllTupleCall, lowerPromiseRejectCall, stringWrapperToString, templateRawTextOf } from "./lower-exprs.js";
|
|
21
21
|
import { isSafeToDiscard } from "./expressions/evaluation-safety.js";
|
|
22
22
|
import { tryLowerExpression } from "./expressions/try-lower-expression.js";
|
|
23
23
|
import { httpClientFnBindingOf, isStreamUndefCallExpr, lowerCompatReqStreamOptionalCall, lowerHttpClientFnCall } from "./lower-server.js";
|
|
@@ -32,6 +32,7 @@ import { npmStaticPackageOfPath } from "../npm-static.js";
|
|
|
32
32
|
import { countedFor, varRef } from "../../ir/build.js";
|
|
33
33
|
import { rejectStaticThis } from "./static-this.js";
|
|
34
34
|
import { fenceNodeModuleMutationCall, lowerRequireCacheKeys } from "./lower-node-module.js";
|
|
35
|
+
import { defaultAfterUndefined, lowerOptionalArgument, lowerStaticallyUndefinedArgument, positionNumber } from "./optional-arguments.js";
|
|
35
36
|
/** The one func-type projection of completed parameter shapes. Typed rest
|
|
36
37
|
* parameters keep their packed array as the trailing native ABI slot and
|
|
37
38
|
* mark the value variadic so indirect call sites run completeArgs before
|
|
@@ -40,12 +41,14 @@ import { fenceNodeModuleMutationCall, lowerRequireCacheKeys } from "./lower-node
|
|
|
40
41
|
export function funcTypeFromParamShapes(shapes, ret) {
|
|
41
42
|
const typedRest = shapes.some((shape) => shape.mode === "rest");
|
|
42
43
|
const dynRest = shapes.some((shape) => shape.mode === "dynRest");
|
|
44
|
+
const argumentsAll = shapes.some((shape) => shape.mode === "arguments");
|
|
43
45
|
const islandRest = shapes.some((shape) => shape.mode === "islandRest");
|
|
44
46
|
return {
|
|
45
47
|
kind: "func",
|
|
46
|
-
params: shapes.filter((shape) => shape.mode !== "dynRest").map((shape) => shape.type),
|
|
48
|
+
params: shapes.filter((shape) => shape.mode !== "dynRest" && shape.mode !== "arguments").map((shape) => shape.type),
|
|
47
49
|
ret,
|
|
48
|
-
...(typedRest || dynRest || islandRest ? { rest: true } : {}),
|
|
50
|
+
...(typedRest || dynRest || islandRest || argumentsAll ? { rest: true } : {}),
|
|
51
|
+
...(argumentsAll ? { argumentsAll: true } : {}),
|
|
49
52
|
...(typedRest
|
|
50
53
|
? { restAbi: "typed" }
|
|
51
54
|
: islandRest
|
|
@@ -382,8 +385,42 @@ leading) {
|
|
|
382
385
|
});
|
|
383
386
|
});
|
|
384
387
|
}
|
|
385
|
-
const restAt = shapes.findIndex((s) => s.mode === "rest" || s.mode === "dynRest" || s.mode === "islandRest");
|
|
388
|
+
const restAt = shapes.findIndex((s) => s.mode === "rest" || s.mode === "dynRest" || s.mode === "islandRest" || s.mode === "arguments");
|
|
386
389
|
const positional = restAt >= 0 ? shapes.slice(0, restAt) : [...shapes];
|
|
390
|
+
if (restAt >= 0 && shapes[restAt].mode === "arguments") {
|
|
391
|
+
// JS `arguments` contains every supplied value, including fixed
|
|
392
|
+
// parameters. Store each call expression once, in source order, so
|
|
393
|
+
// both the native parameter slots and the full array read it.
|
|
394
|
+
const stmts = [];
|
|
395
|
+
const passed = sources.map((source, i) => {
|
|
396
|
+
if (!isIr(source) && ts.isSpreadElement(source)) {
|
|
397
|
+
lowerer.unsupported("SC1090", source, "spread arguments into fixed parameter positions (a spread can only fill a rest parameter)");
|
|
398
|
+
}
|
|
399
|
+
const slotType = i < restAt ? positional[i].type : DYN;
|
|
400
|
+
const value = isIr(source)
|
|
401
|
+
? lowerer.coerceInto(blame, source.ir, slotType)
|
|
402
|
+
: lowerer.lowerExprExpecting(source, slotType);
|
|
403
|
+
const saved = lowerer.declareHiddenLocal("%callArg", slotType);
|
|
404
|
+
stmts.push({ kind: "varDecl", localId: saved.id, init: value, loc: value.loc });
|
|
405
|
+
return { kind: "varRef", localId: saved.id, type: slotType, loc: value.loc };
|
|
406
|
+
});
|
|
407
|
+
const fixed = positional.map((shape, i) => {
|
|
408
|
+
if (i < passed.length)
|
|
409
|
+
return passed[i];
|
|
410
|
+
if (shape.mode === "omittable")
|
|
411
|
+
return shape.callDefault ?? lowerer.undefinedArgFor(shape.type, loc, blame);
|
|
412
|
+
if (shape.type.kind === "dyn")
|
|
413
|
+
return { kind: "dynFrom", value: { kind: "unitLit", unit: "undefined", type: UNDEFINED_T, loc }, type: DYN, loc };
|
|
414
|
+
lowerer.unsupported("SC1090", blame, "this call form");
|
|
415
|
+
});
|
|
416
|
+
const elems = passed.map((value) => lowerer.coerceInto(blame, value, DYN));
|
|
417
|
+
const full = { kind: "dynArrLit", elems, type: DYN, loc };
|
|
418
|
+
const out = [...fixed, full];
|
|
419
|
+
if (stmts.length > 0) {
|
|
420
|
+
out[0] = { kind: "seqExpr", stmts, result: out[0], type: out[0].type, loc };
|
|
421
|
+
}
|
|
422
|
+
return out;
|
|
423
|
+
}
|
|
387
424
|
const out = positional.map((shape, i) => {
|
|
388
425
|
const src = sources[i];
|
|
389
426
|
if (isIr(src))
|
|
@@ -617,6 +654,7 @@ export function requireExactArityValue(lowerer, blame, contextual, shapes, funcT
|
|
|
617
654
|
// tier's undefined (omittedArgFor).
|
|
618
655
|
if (shapes.every((s) => s.mode === "required" ||
|
|
619
656
|
s.mode === "dynRest" ||
|
|
657
|
+
s.mode === "arguments" ||
|
|
620
658
|
s.mode === "islandRest" ||
|
|
621
659
|
(s.mode === "omittable" && (s.type.kind === "dyn" || s.type.kind === "jsval")))) {
|
|
622
660
|
return;
|
|
@@ -915,18 +953,16 @@ export function collectSignatureInner(lowerer, decl) {
|
|
|
915
953
|
}
|
|
916
954
|
}
|
|
917
955
|
const params = lowerer.paramShapes(decl.parameters);
|
|
918
|
-
//
|
|
919
|
-
//
|
|
920
|
-
//
|
|
921
|
-
|
|
922
|
-
// indirect ones; lowerFunction declares the `arguments` local).
|
|
923
|
-
if (!params.some((sh) => sh.mode === "dynRest") &&
|
|
956
|
+
// A declared function reading `arguments` receives a hidden dyn array.
|
|
957
|
+
// The zero-parameter form uses the existing rest pack; named parameters
|
|
958
|
+
// use the full argument pack for both direct and boxed calls.
|
|
959
|
+
if (!params.some((sh) => sh.mode === "rest" || sh.mode === "dynRest" || sh.mode === "islandRest") &&
|
|
924
960
|
isJsSourceFile(decl.getSourceFile()) &&
|
|
925
961
|
bodyReadsArguments(decl)) {
|
|
926
|
-
if (decl.parameters.length > 0) {
|
|
927
|
-
lowerer.unsupported("SC1090", decl, "'arguments'
|
|
962
|
+
if (decl.parameters.length > 0 && !isNodeEsmFile(decl.getSourceFile())) {
|
|
963
|
+
lowerer.unsupported("SC1090", decl, "parameterized 'arguments' outside an ES module (sloppy-mode parameter aliases)");
|
|
928
964
|
}
|
|
929
|
-
params.push({ type: DYN, mode: "dynRest" });
|
|
965
|
+
params.push({ type: DYN, mode: decl.parameters.length > 0 ? "arguments" : "dynRest" });
|
|
930
966
|
}
|
|
931
967
|
const nameBlame = decl.name ?? decl;
|
|
932
968
|
const returnType = lowerer.declaredReturnType(decl, nameBlame);
|
|
@@ -3339,13 +3375,12 @@ export function lowerCall(lowerer, expr) {
|
|
|
3339
3375
|
// String(x) is exactly the template-literal ToString, Boolean(x) is
|
|
3340
3376
|
// exactly the condition ToBoolean (union arms included), Number(x) is
|
|
3341
3377
|
// ToNumber where it lowers exactly: numbers pass through, booleans
|
|
3342
|
-
// become 1/0, and
|
|
3343
|
-
// StringToNumber (num.fromString — the full
|
|
3344
|
-
// grammar
|
|
3345
|
-
// narrow first) keep the fence.
|
|
3378
|
+
// become 1/0, null becomes 0, and undefined becomes NaN. Strings use
|
|
3379
|
+
// the runtime's StringToNumber parser (num.fromString — the full
|
|
3380
|
+
// StringNumericLiteral grammar in scr_string.c).
|
|
3346
3381
|
// Provenance-checked like setTimeout; zero-arg forms are the JS
|
|
3347
|
-
// constants ("", false, 0).
|
|
3348
|
-
//
|
|
3382
|
+
// constants ("", false, 0). Wrapper objects stay fenced except where
|
|
3383
|
+
// the receiver is immediately converted by a direct String method call.
|
|
3349
3384
|
if (ts.isIdentifier(expr.expression) &&
|
|
3350
3385
|
expr.expression.text === "BigInt" &&
|
|
3351
3386
|
lowerer.isStdlibSymbol(lowerer.resolveValueSymbol(expr.expression) ?? undefined)) {
|
|
@@ -3406,33 +3441,11 @@ export function lowerCall(lowerer, expr) {
|
|
|
3406
3441
|
// bool) that a value lowering of the `&&` would fence on.
|
|
3407
3442
|
if (name === "Boolean")
|
|
3408
3443
|
return lowerer.lowerCondition(argNode);
|
|
3444
|
+
if (name === "Number")
|
|
3445
|
+
return lowerNumberConstructorValue(lowerer, argNode, loc);
|
|
3409
3446
|
const arg = lowerer.lowerExpr(argNode);
|
|
3410
3447
|
if (name === "String")
|
|
3411
3448
|
return lowerer.ensureString(arg, argNode);
|
|
3412
|
-
if (name === "Number" && arg.type.kind === "bigint") {
|
|
3413
|
-
return { kind: "libCall", fn: "bigint.toF64", args: [arg], type: F64, loc };
|
|
3414
|
-
}
|
|
3415
|
-
if (arg.type.kind === "f64")
|
|
3416
|
-
return arg;
|
|
3417
|
-
if (arg.type.kind === "bool") {
|
|
3418
|
-
return {
|
|
3419
|
-
kind: "ternary",
|
|
3420
|
-
cond: arg,
|
|
3421
|
-
then: { kind: "numLit", value: 1, type: F64, loc },
|
|
3422
|
-
else_: { kind: "numLit", value: 0, type: F64, loc },
|
|
3423
|
-
type: F64,
|
|
3424
|
-
loc,
|
|
3425
|
-
};
|
|
3426
|
-
}
|
|
3427
|
-
if (arg.type.kind === "string") {
|
|
3428
|
-
return { kind: "libCall", fn: "num.fromString", args: [arg], type: F64, loc };
|
|
3429
|
-
}
|
|
3430
|
-
const optionalNumber = lowerOptionalStringNumber(lowerer, arg, loc);
|
|
3431
|
-
if (optionalNumber)
|
|
3432
|
-
return optionalNumber;
|
|
3433
|
-
lowerer.noLowering(`Number of ${lowerer.fmt(arg.type)} values`, argNode, arg.type.kind === "union"
|
|
3434
|
-
? "numbers, booleans, and strings lower (the full ToNumber string grammar included) — narrow the union first"
|
|
3435
|
-
: undefined);
|
|
3436
3449
|
}
|
|
3437
3450
|
// __island_eval: the internal island testing hook (eval in the embedded
|
|
3438
3451
|
// engine, String(result) back). Provenance-checked like setTimeout.
|
|
@@ -3568,6 +3581,9 @@ export function lowerCall(lowerer, expr) {
|
|
|
3568
3581
|
// supported form is fenced here per site. Members with no lowering at
|
|
3569
3582
|
// all (fs.watch, os.cpus, ...) fence with the module-qualified name.
|
|
3570
3583
|
if (ts.isIdentifier(expr.expression)) {
|
|
3584
|
+
if (expr.expression.text === "Array" && lowerer.isStdlibGlobal(expr.expression, "Array")) {
|
|
3585
|
+
return lowerArrayConstructor(lowerer, expr, expr.arguments);
|
|
3586
|
+
}
|
|
3571
3587
|
// A call through a compile-time util.promisify projection. execFile
|
|
3572
3588
|
// keeps its custom result helper; ordinary builtins route through
|
|
3573
3589
|
// their existing promise-module lowering.
|
|
@@ -4175,11 +4191,14 @@ export function lowerCall(lowerer, expr) {
|
|
|
4175
4191
|
// TestContext surface (t.test/t.skip/t.diagnostic), t.assert.*.
|
|
4176
4192
|
lowerer.lowerTestMethodCall(expr, expr.expression) ??
|
|
4177
4193
|
lowerer.lowerTimeoutMethodCall(expr, expr.expression) ??
|
|
4194
|
+
lowerObjectOwnPrototypeCall(lowerer, expr, expr.expression) ??
|
|
4195
|
+
lowerObjectPrototypeCall(lowerer, expr, expr.expression) ??
|
|
4196
|
+
lowerStringPrototypeCall(lowerer, expr, expr.expression) ??
|
|
4178
4197
|
lowerStringMethodCallWithOptionalArgs(lowerer, expr, expr.expression) ??
|
|
4179
4198
|
// Typed-array/Buffer receivers and the Buffer statics — before the
|
|
4180
4199
|
// island path (bytes never cross the boundary).
|
|
4181
4200
|
lowerer.lowerBytesMethodCall(expr, expr.expression) ??
|
|
4182
|
-
|
|
4201
|
+
lowerBufferStaticCallWithNarrowedArg(lowerer, expr, expr.expression) ??
|
|
4183
4202
|
// URL.revokeObjectURL's zero-argument contract (the one-argument
|
|
4184
4203
|
// form keeps the fence — createObjectURL does too).
|
|
4185
4204
|
lowerUrlStaticCall(lowerer, expr, expr.expression) ??
|
|
@@ -4203,6 +4222,7 @@ export function lowerCall(lowerer, expr) {
|
|
|
4203
4222
|
// hasOwnProperty on a program class CONSTRUCTOR — own statics are
|
|
4204
4223
|
// compile-time-known, so a literal key folds to a constant.
|
|
4205
4224
|
lowerClassHasOwnPropertyCall(lowerer, expr, expr.expression) ??
|
|
4225
|
+
lowerObjectOwnMethodCall(lowerer, expr, expr.expression) ??
|
|
4206
4226
|
// Response constructor-object operations are unsupported in both
|
|
4207
4227
|
// tiers. Keep their SC2020 inventory contract ahead of the island
|
|
4208
4228
|
// and generic-call fallbacks (Response.json otherwise reports the
|
|
@@ -4243,6 +4263,7 @@ export function lowerCall(lowerer, expr) {
|
|
|
4243
4263
|
lowerObjectStaticCall(lowerer, expr, expr.expression) ??
|
|
4244
4264
|
lowerObjectFromEntriesCall(lowerer, expr, expr.expression) ??
|
|
4245
4265
|
lowerArrayFromCall(lowerer, expr, expr.expression) ??
|
|
4266
|
+
lowerArrayOfCall(lowerer, expr, expr.expression) ??
|
|
4246
4267
|
lowerer.lowerArrayMethodCall(expr, expr.expression) ??
|
|
4247
4268
|
// Read-only array methods (slice/map) on TUPLE receivers — the
|
|
4248
4269
|
// positions snapshot into a fresh array (the for-of stance).
|
|
@@ -4454,22 +4475,29 @@ function lowerRegexMethodCallWithOptionalArg(lowerer, call, access) {
|
|
|
4454
4475
|
function optionalCallValue(lowerer, node) {
|
|
4455
4476
|
return lowerer.runtimeOptionalIdentifierValue(node)?.value ?? lowerAbsenceProbe(lowerer, node);
|
|
4456
4477
|
}
|
|
4457
|
-
function
|
|
4478
|
+
function lowerBufferStaticCallWithNarrowedArg(lowerer, call, access) {
|
|
4458
4479
|
const lowered = lowerer.lowerBufferStaticCall(call, access);
|
|
4459
4480
|
if (lowered?.kind !== "bytesNew" || !lowered.source || lowered.source.type.kind !== "union")
|
|
4460
4481
|
return lowered;
|
|
4461
|
-
|
|
4462
|
-
|
|
4463
|
-
|
|
4464
|
-
|
|
4465
|
-
|
|
4466
|
-
|
|
4467
|
-
|
|
4468
|
-
|
|
4469
|
-
|
|
4470
|
-
|
|
4471
|
-
|
|
4472
|
-
|
|
4482
|
+
// A callback can store a wider union than the checker sees at this use
|
|
4483
|
+
// (including an added undefined arm). Extract the exact arm proven by
|
|
4484
|
+
// control-flow narrowing, not merely the union with undefined removed.
|
|
4485
|
+
const narrowed = lowerer.mapTypeOf(lowerer.typeOf(call.arguments[0]));
|
|
4486
|
+
if (!narrowed ||
|
|
4487
|
+
!(narrowed.kind === "f64" ||
|
|
4488
|
+
(narrowed.kind === "array" && narrowed.elem.kind === "f64") ||
|
|
4489
|
+
(narrowed.kind === "bytes" && narrowed.elem === "u8")) ||
|
|
4490
|
+
lowerer.armTag(lowered.source.type.unionId, narrowed) < 0) {
|
|
4491
|
+
lowerer.unsupported("SC1090", call.arguments[0], "a Buffer constructor argument whose narrowed type is not a stored source arm");
|
|
4492
|
+
}
|
|
4493
|
+
const helper = lowerer.narrowedArmHelper(lowered.source.type.unionId, narrowed, lowered.source.loc);
|
|
4494
|
+
if (!helper) {
|
|
4495
|
+
lowerer.unsupported("SC1090", call.arguments[0], "a Buffer constructor argument whose narrowed type is not a stored source arm");
|
|
4496
|
+
}
|
|
4497
|
+
return {
|
|
4498
|
+
...lowered,
|
|
4499
|
+
source: { kind: "call", callee: helper, args: [lowered.source], type: narrowed, loc: lowered.source.loc },
|
|
4500
|
+
};
|
|
4473
4501
|
}
|
|
4474
4502
|
function lowerOptionalNumberDefault(lowerer, value, dflt, loc) {
|
|
4475
4503
|
const widened = lowerer.runtimeOptionalWidening(value.type, F64);
|
|
@@ -4509,14 +4537,14 @@ function lowerOptionalNumberDefault(lowerer, value, dflt, loc) {
|
|
|
4509
4537
|
}
|
|
4510
4538
|
return { kind: "call", callee: helper, args: [value], type: F64, loc };
|
|
4511
4539
|
}
|
|
4512
|
-
function lowerStringMethodCallWithOptionalArgs(lowerer, call, access) {
|
|
4513
|
-
const lowered =
|
|
4540
|
+
function lowerStringMethodCallWithOptionalArgs(lowerer, call, access, receiver, argumentNodes = call.arguments) {
|
|
4541
|
+
const lowered = lowerStringMethodCall(lowerer, call, access, receiver, argumentNodes);
|
|
4514
4542
|
if (lowered?.kind !== "strIntrinsic")
|
|
4515
4543
|
return lowered;
|
|
4516
4544
|
const args = [...lowered.args];
|
|
4517
4545
|
let changed = false;
|
|
4518
4546
|
const zeroDefaultArgs = lowered.method === "charCodeAt" || lowered.method === "charAt" ||
|
|
4519
|
-
lowered.method === "repeat"
|
|
4547
|
+
lowered.method === "repeat"
|
|
4520
4548
|
? [0]
|
|
4521
4549
|
: lowered.method === "slice" || lowered.method === "substring"
|
|
4522
4550
|
? [0]
|
|
@@ -4537,11 +4565,419 @@ function lowerStringMethodCallWithOptionalArgs(lowerer, call, access) {
|
|
|
4537
4565
|
lowered.method === "startsWith" || lowered.method === "endsWith") &&
|
|
4538
4566
|
args[0]?.type.kind === "union" &&
|
|
4539
4567
|
lowerer.runtimeOptionalWidening(args[0].type, STRING) !== null) {
|
|
4540
|
-
args[0] = lowerer.ensureString(args[0],
|
|
4568
|
+
args[0] = lowerer.ensureString(args[0], argumentNodes[0]);
|
|
4541
4569
|
changed = true;
|
|
4542
4570
|
}
|
|
4543
4571
|
return changed ? { ...lowered, args } : lowered;
|
|
4544
4572
|
}
|
|
4573
|
+
function lowerNumberConstructorValue(lowerer, argNode, loc) {
|
|
4574
|
+
const undefinedArg = lowerStaticallyUndefinedArgument(lowerer, argNode);
|
|
4575
|
+
if (undefinedArg) {
|
|
4576
|
+
return defaultAfterUndefined(undefinedArg, {
|
|
4577
|
+
kind: "bin", op: "/", left: { kind: "numLit", value: 0, type: F64, loc },
|
|
4578
|
+
right: { kind: "numLit", value: 0, type: F64, loc }, type: F64, loc,
|
|
4579
|
+
});
|
|
4580
|
+
}
|
|
4581
|
+
const arg = lowerer.lowerExpr(argNode);
|
|
4582
|
+
if (arg.type.kind === "nullT")
|
|
4583
|
+
return defaultAfterUndefined(arg, { kind: "numLit", value: 0, type: F64, loc });
|
|
4584
|
+
if (arg.type.kind === "bigint")
|
|
4585
|
+
return { kind: "libCall", fn: "bigint.toF64", args: [arg], type: F64, loc };
|
|
4586
|
+
if (arg.type.kind === "f64")
|
|
4587
|
+
return arg;
|
|
4588
|
+
if (arg.type.kind === "bool") {
|
|
4589
|
+
return {
|
|
4590
|
+
kind: "ternary",
|
|
4591
|
+
cond: arg,
|
|
4592
|
+
then: { kind: "numLit", value: 1, type: F64, loc },
|
|
4593
|
+
else_: { kind: "numLit", value: 0, type: F64, loc },
|
|
4594
|
+
type: F64,
|
|
4595
|
+
loc,
|
|
4596
|
+
};
|
|
4597
|
+
}
|
|
4598
|
+
if (arg.type.kind === "string")
|
|
4599
|
+
return { kind: "libCall", fn: "num.fromString", args: [arg], type: F64, loc };
|
|
4600
|
+
const optionalNumber = lowerOptionalStringNumber(lowerer, arg, loc);
|
|
4601
|
+
if (optionalNumber)
|
|
4602
|
+
return optionalNumber;
|
|
4603
|
+
const scalarUnionNumber = lowerScalarUnionNumber(lowerer, arg, argNode, loc);
|
|
4604
|
+
if (scalarUnionNumber)
|
|
4605
|
+
return scalarUnionNumber;
|
|
4606
|
+
return lowerer.noLowering(`Number of ${lowerer.fmt(arg.type)} values`, argNode, arg.type.kind === "union"
|
|
4607
|
+
? "unions of numbers, booleans, strings, null, and undefined lower — narrow other arms first"
|
|
4608
|
+
: undefined);
|
|
4609
|
+
}
|
|
4610
|
+
function immediatePrimitiveWrapperToString(lowerer, node) {
|
|
4611
|
+
const stringValue = stringWrapperToString(lowerer, node);
|
|
4612
|
+
if (stringValue)
|
|
4613
|
+
return stringValue;
|
|
4614
|
+
let wrapped = node;
|
|
4615
|
+
while (ts.isParenthesizedExpression(wrapped))
|
|
4616
|
+
wrapped = wrapped.expression;
|
|
4617
|
+
if (!ts.isNewExpression(wrapped) || !ts.isIdentifier(wrapped.expression))
|
|
4618
|
+
return null;
|
|
4619
|
+
const name = wrapped.expression.text;
|
|
4620
|
+
if ((name !== "Boolean" && name !== "Number") ||
|
|
4621
|
+
!lowerer.isStdlibSymbol(lowerer.resolveValueSymbol(wrapped.expression) ?? undefined))
|
|
4622
|
+
return null;
|
|
4623
|
+
const args = wrapped.arguments ?? [];
|
|
4624
|
+
if (args.length > 1 || args.some(ts.isSpreadElement))
|
|
4625
|
+
return null;
|
|
4626
|
+
const loc = locOf(wrapped);
|
|
4627
|
+
const primitive = name === "Boolean"
|
|
4628
|
+
? args.length ? lowerer.lowerCondition(args[0]) : { kind: "boolLit", value: false, type: BOOL, loc }
|
|
4629
|
+
: args.length ? lowerNumberConstructorValue(lowerer, args[0], loc) : { kind: "numLit", value: 0, type: F64, loc };
|
|
4630
|
+
return lowerer.ensureString(primitive, wrapped);
|
|
4631
|
+
}
|
|
4632
|
+
function plainObjectCoercionReceiver(lowerer, node) {
|
|
4633
|
+
const plainLiteral = (literal) => literal.properties.every((prop) => ts.isPropertyAssignment(prop) &&
|
|
4634
|
+
(ts.isIdentifier(prop.name) || ts.isStringLiteral(prop.name) || ts.isNumericLiteral(prop.name)) &&
|
|
4635
|
+
prop.name.text !== "__proto__");
|
|
4636
|
+
let value = node;
|
|
4637
|
+
while (ts.isParenthesizedExpression(value))
|
|
4638
|
+
value = value.expression;
|
|
4639
|
+
if (ts.isObjectLiteralExpression(value))
|
|
4640
|
+
return plainLiteral(value);
|
|
4641
|
+
if (!ts.isIdentifier(value))
|
|
4642
|
+
return false;
|
|
4643
|
+
const symbol = lowerer.resolveValueSymbol(value);
|
|
4644
|
+
const decl = symbol ? lowerer.checker.valueDeclarationOf(symbol) : undefined;
|
|
4645
|
+
if (!symbol || !decl || !ts.isVariableDeclaration(decl) || !decl.initializer ||
|
|
4646
|
+
!ts.isObjectLiteralExpression(decl.initializer) || !plainLiteral(decl.initializer))
|
|
4647
|
+
return false;
|
|
4648
|
+
return bindingNeverReassigned(lowerer, symbol, decl);
|
|
4649
|
+
}
|
|
4650
|
+
function objectToStringTag(lowerer, type) {
|
|
4651
|
+
switch (type.kind) {
|
|
4652
|
+
case "undefinedT":
|
|
4653
|
+
case "void": return "Undefined";
|
|
4654
|
+
case "nullT": return "Null";
|
|
4655
|
+
case "bool": return "Boolean";
|
|
4656
|
+
case "f64": return "Number";
|
|
4657
|
+
case "string": return "String";
|
|
4658
|
+
case "bigint": return "BigInt";
|
|
4659
|
+
case "symbol": return "Symbol";
|
|
4660
|
+
case "array": return "Array";
|
|
4661
|
+
case "record": return "Object";
|
|
4662
|
+
case "func":
|
|
4663
|
+
case "classval": return "Function";
|
|
4664
|
+
case "date": return "Date";
|
|
4665
|
+
case "regex": return "RegExp";
|
|
4666
|
+
case "map": return "Map";
|
|
4667
|
+
case "set": return "Set";
|
|
4668
|
+
case "promise": return "Promise";
|
|
4669
|
+
case "bytes":
|
|
4670
|
+
return ({ u8: "Uint8Array", u32: "Uint32Array", i32: "Int32Array", f32: "Float32Array", f64: "Float64Array" })[type.elem];
|
|
4671
|
+
case "object":
|
|
4672
|
+
return type.className === "%Error" || lowerer.isSubclassOf(type.className, "%Error") ? "Error" : null;
|
|
4673
|
+
default: return null;
|
|
4674
|
+
}
|
|
4675
|
+
}
|
|
4676
|
+
function immediateObjectTagReceiver(lowerer, node) {
|
|
4677
|
+
let value = node;
|
|
4678
|
+
while (ts.isParenthesizedExpression(value))
|
|
4679
|
+
value = value.expression;
|
|
4680
|
+
const loc = locOf(value);
|
|
4681
|
+
if (ts.isArrayLiteralExpression(value))
|
|
4682
|
+
return { tag: "Array", effect: lowerer.lowerExpr(value) };
|
|
4683
|
+
if ((ts.isCallExpression(value) || ts.isNewExpression(value)) && ts.isIdentifier(value.expression) &&
|
|
4684
|
+
(value.expression.text === "Array" || value.expression.text === "Error") &&
|
|
4685
|
+
(value.arguments?.length ?? 0) === 0 && lowerer.isStdlibSymbol(lowerer.resolveValueSymbol(value.expression) ?? undefined)) {
|
|
4686
|
+
return { tag: value.expression.text, effect: { kind: "numLit", value: 0, type: F64, loc } };
|
|
4687
|
+
}
|
|
4688
|
+
if (ts.isNewExpression(value) && ts.isIdentifier(value.expression) &&
|
|
4689
|
+
lowerer.isStdlibSymbol(lowerer.resolveValueSymbol(value.expression) ?? undefined)) {
|
|
4690
|
+
const name = value.expression.text;
|
|
4691
|
+
const args = value.arguments ?? [];
|
|
4692
|
+
if (args.length <= 1 && !args.some(ts.isSpreadElement)) {
|
|
4693
|
+
if (name === "String") {
|
|
4694
|
+
const effect = stringWrapperToString(lowerer, value);
|
|
4695
|
+
if (effect)
|
|
4696
|
+
return { tag: "String", effect };
|
|
4697
|
+
}
|
|
4698
|
+
if (name === "Number")
|
|
4699
|
+
return {
|
|
4700
|
+
tag: "Number",
|
|
4701
|
+
effect: args.length ? lowerNumberConstructorValue(lowerer, args[0], loc) : { kind: "numLit", value: 0, type: F64, loc },
|
|
4702
|
+
};
|
|
4703
|
+
if (name === "Boolean")
|
|
4704
|
+
return {
|
|
4705
|
+
tag: "Boolean",
|
|
4706
|
+
effect: args.length ? lowerer.lowerCondition(args[0]) : { kind: "boolLit", value: false, type: BOOL, loc },
|
|
4707
|
+
};
|
|
4708
|
+
}
|
|
4709
|
+
}
|
|
4710
|
+
if ((ts.isCallExpression(value) || ts.isNewExpression(value)) &&
|
|
4711
|
+
ts.isIdentifier(value.expression) && value.expression.text === "Object" &&
|
|
4712
|
+
lowerer.isStdlibSymbol(lowerer.resolveValueSymbol(value.expression) ?? undefined)) {
|
|
4713
|
+
const args = value.arguments ?? [];
|
|
4714
|
+
if (args.some(ts.isSpreadElement))
|
|
4715
|
+
return null;
|
|
4716
|
+
const first = args[0];
|
|
4717
|
+
const inner = first && (immediateObjectTagReceiver(lowerer, first) ?? (() => {
|
|
4718
|
+
const effect = lowerer.lowerExpr(first);
|
|
4719
|
+
const tag = isUnitType(effect.type) || effect.type.kind === "void" ? "Object" : objectToStringTag(lowerer, effect.type);
|
|
4720
|
+
return tag === null ? null : { tag, effect };
|
|
4721
|
+
})());
|
|
4722
|
+
if (first && !inner)
|
|
4723
|
+
return null;
|
|
4724
|
+
const stmts = [];
|
|
4725
|
+
if (inner && !isSafeToDiscard(inner.effect))
|
|
4726
|
+
stmts.push({ kind: "exprStmt", expr: inner.effect, loc: inner.effect.loc });
|
|
4727
|
+
for (const extra of args.slice(1)) {
|
|
4728
|
+
const effect = lowerer.lowerExpr(extra);
|
|
4729
|
+
if (!isSafeToDiscard(effect))
|
|
4730
|
+
stmts.push({ kind: "exprStmt", expr: effect, loc: locOf(extra) });
|
|
4731
|
+
}
|
|
4732
|
+
return {
|
|
4733
|
+
tag: inner?.tag ?? "Object",
|
|
4734
|
+
effect: { kind: "seqExpr", stmts, result: { kind: "numLit", value: 0, type: F64, loc }, type: F64, loc },
|
|
4735
|
+
};
|
|
4736
|
+
}
|
|
4737
|
+
return null;
|
|
4738
|
+
}
|
|
4739
|
+
function lowerObjectOwnPrototypeCall(lowerer, call, access) {
|
|
4740
|
+
if (lowerer.dynamic || access.name.text !== "call" || !ts.isPropertyAccessExpression(access.expression))
|
|
4741
|
+
return null;
|
|
4742
|
+
const method = access.expression;
|
|
4743
|
+
const enumerable = method.name.text === "propertyIsEnumerable";
|
|
4744
|
+
if (method.name.text !== "hasOwnProperty" && !enumerable)
|
|
4745
|
+
return null;
|
|
4746
|
+
const prototype = method.expression;
|
|
4747
|
+
if (!ts.isPropertyAccessExpression(prototype) || prototype.name.text !== "prototype" ||
|
|
4748
|
+
!lowerer.isStdlibGlobal(prototype.expression, "Object") || !lowerer.isStdlibMember(method) ||
|
|
4749
|
+
call.arguments.some(ts.isSpreadElement))
|
|
4750
|
+
return null;
|
|
4751
|
+
return lowerObjectOwnCall(lowerer, call, call.arguments[0], call.arguments[1], call.arguments.slice(2), method.name.text);
|
|
4752
|
+
}
|
|
4753
|
+
function lowerObjectOwnMethodCall(lowerer, call, access) {
|
|
4754
|
+
const method = access.name.text;
|
|
4755
|
+
if (lowerer.dynamic || call.questionDotToken || access.questionDotToken ||
|
|
4756
|
+
(method !== "hasOwnProperty" && method !== "propertyIsEnumerable") ||
|
|
4757
|
+
!lowerer.isStdlibMember(access) || call.arguments.some(ts.isSpreadElement))
|
|
4758
|
+
return null;
|
|
4759
|
+
return lowerObjectOwnCall(lowerer, call, access.expression, call.arguments[0], call.arguments.slice(1), method);
|
|
4760
|
+
}
|
|
4761
|
+
function lowerObjectOwnCall(lowerer, call, receiverNode, keyNode, extraNodes, method) {
|
|
4762
|
+
const enumerable = method === "propertyIsEnumerable";
|
|
4763
|
+
const loc = locOf(call);
|
|
4764
|
+
const stmts = [];
|
|
4765
|
+
const receiver = receiverNode ? lowerer.lowerExpr(receiverNode) : { kind: "unitLit", unit: "undefined", type: UNDEFINED_T, loc };
|
|
4766
|
+
let receiverRef = receiver;
|
|
4767
|
+
if (isUnitType(receiver.type) || receiver.type.kind === "void") {
|
|
4768
|
+
if (!isSafeToDiscard(receiver))
|
|
4769
|
+
stmts.push({ kind: "exprStmt", expr: receiver, loc });
|
|
4770
|
+
}
|
|
4771
|
+
else {
|
|
4772
|
+
const local = lowerer.declareHiddenLocal("%ownReceiver", receiver.type);
|
|
4773
|
+
stmts.push({ kind: "varDecl", localId: local.id, init: receiver, loc });
|
|
4774
|
+
receiverRef = varRef(local.id, receiver.type, loc);
|
|
4775
|
+
}
|
|
4776
|
+
const rawKey = keyNode ? lowerer.lowerExpr(keyNode) : null;
|
|
4777
|
+
let keyRef = rawKey;
|
|
4778
|
+
if (rawKey && (isUnitType(rawKey.type) || rawKey.type.kind === "void")) {
|
|
4779
|
+
if (!isSafeToDiscard(rawKey))
|
|
4780
|
+
stmts.push({ kind: "exprStmt", expr: rawKey, loc: rawKey.loc });
|
|
4781
|
+
}
|
|
4782
|
+
else if (rawKey) {
|
|
4783
|
+
const local = lowerer.declareHiddenLocal("%ownKey", rawKey.type);
|
|
4784
|
+
stmts.push({ kind: "varDecl", localId: local.id, init: rawKey, loc: rawKey.loc });
|
|
4785
|
+
keyRef = varRef(local.id, rawKey.type, loc);
|
|
4786
|
+
}
|
|
4787
|
+
for (const extra of extraNodes) {
|
|
4788
|
+
const effect = lowerer.lowerExpr(extra);
|
|
4789
|
+
if (!isSafeToDiscard(effect))
|
|
4790
|
+
stmts.push({ kind: "exprStmt", expr: effect, loc: locOf(extra) });
|
|
4791
|
+
}
|
|
4792
|
+
let key = keyRef;
|
|
4793
|
+
if (!keyNode || rawKey?.type.kind === "undefinedT" || rawKey?.type.kind === "void") {
|
|
4794
|
+
key = { kind: "strLit", value: "undefined", type: STRING, loc };
|
|
4795
|
+
}
|
|
4796
|
+
else if (rawKey?.type.kind === "nullT") {
|
|
4797
|
+
key = { kind: "strLit", value: "null", type: STRING, loc };
|
|
4798
|
+
}
|
|
4799
|
+
else if (key && (key.type.kind === "f64" || key.type.kind === "bool" || key.type.kind === "bigint")) {
|
|
4800
|
+
key = { kind: "toString", operand: key, type: STRING, loc };
|
|
4801
|
+
}
|
|
4802
|
+
if (!key || key.type.kind !== "string") {
|
|
4803
|
+
lowerer.noLowering("Object.prototype." + method + " with this property key", keyNode ?? call);
|
|
4804
|
+
}
|
|
4805
|
+
let result;
|
|
4806
|
+
const type = receiver.type;
|
|
4807
|
+
if (isUnitType(type) || type.kind === "void") {
|
|
4808
|
+
result = nodeThrowExpr(1, "", "Cannot convert undefined or null to object", BOOL, loc);
|
|
4809
|
+
}
|
|
4810
|
+
else if (type.kind === "record") {
|
|
4811
|
+
const shape = lowerer.shapes.get(type.shapeId);
|
|
4812
|
+
if (!shape || shape.tuple || shape.indexValue || shapeHasAccessorSlots(shape)) {
|
|
4813
|
+
lowerer.noLowering("Object.prototype." + method + " over this record shape", receiverNode ?? call);
|
|
4814
|
+
}
|
|
4815
|
+
result = { kind: "call", callee: recordHasOwnHelper(lowerer, type.shapeId, loc), args: [receiverRef, key], type: BOOL, loc };
|
|
4816
|
+
}
|
|
4817
|
+
else if (type.kind === "dyn" && !enumerable) {
|
|
4818
|
+
result = { kind: "libCall", fn: "dyn.hasOwn", args: [receiverRef, key], type: BOOL, loc };
|
|
4819
|
+
}
|
|
4820
|
+
else if (type.kind === "string" || type.kind === "array") {
|
|
4821
|
+
const needleLocal = lowerer.declareHiddenLocal("%ownIndex", F64);
|
|
4822
|
+
stmts.push({ kind: "varDecl", localId: needleLocal.id, init: { kind: "libCall", fn: "num.fromString", args: [key], type: F64, loc }, loc });
|
|
4823
|
+
const needle = varRef(needleLocal.id, F64, loc);
|
|
4824
|
+
const length = type.kind === "string"
|
|
4825
|
+
? { kind: "strIntrinsic", method: "length", receiver: receiverRef, args: [], type: F64, loc }
|
|
4826
|
+
: { kind: "arrIntrinsic", method: "length", receiver: receiverRef, args: [], type: F64, loc };
|
|
4827
|
+
const inRange = { kind: "bin", op: "<", left: needle, right: length, type: BOOL, loc };
|
|
4828
|
+
const nonNegative = { kind: "bin", op: ">=", left: needle, right: { kind: "numLit", value: 0, type: F64, loc }, type: BOOL, loc };
|
|
4829
|
+
const integer = { kind: "bin", op: "===", left: needle,
|
|
4830
|
+
right: { kind: "libCall", fn: "math.floor", args: [needle], type: F64, loc }, type: BOOL, loc };
|
|
4831
|
+
const canonical = { kind: "strEq", negated: false, left: key, right: { kind: "toString", operand: needle, type: STRING, loc }, type: BOOL, loc };
|
|
4832
|
+
const present = type.kind === "string" ? { kind: "boolLit", value: true, type: BOOL, loc } :
|
|
4833
|
+
{ kind: "bin", op: "===", left: { kind: "arrIntrinsic", method: "nextPresent", receiver: receiverRef, args: [needle], type: F64, loc }, right: needle, type: BOOL, loc };
|
|
4834
|
+
const no = { kind: "boolLit", value: false, type: BOOL, loc };
|
|
4835
|
+
const indexOwn = [inRange, integer, canonical, present].reduce((left, right) => ({ kind: "logical", op: "&&", left, right, type: BOOL, loc }), nonNegative);
|
|
4836
|
+
const lengthOwn = !enumerable
|
|
4837
|
+
? { kind: "strEq", negated: false, left: key, right: { kind: "strLit", value: "length", type: STRING, loc }, type: BOOL, loc }
|
|
4838
|
+
: no;
|
|
4839
|
+
result = { kind: "logical", op: "||", left: lengthOwn, right: indexOwn, type: BOOL, loc };
|
|
4840
|
+
}
|
|
4841
|
+
else if (type.kind === "f64" || type.kind === "bool" || type.kind === "bigint" || type.kind === "symbol") {
|
|
4842
|
+
result = { kind: "boolLit", value: false, type: BOOL, loc };
|
|
4843
|
+
}
|
|
4844
|
+
else {
|
|
4845
|
+
lowerer.noLowering("Object.prototype." + method + " over a " + lowerer.fmt(type) + " receiver", receiverNode ?? call);
|
|
4846
|
+
}
|
|
4847
|
+
return { kind: "seqExpr", stmts, result, type: BOOL, loc };
|
|
4848
|
+
}
|
|
4849
|
+
function lowerObjectPrototypeCall(lowerer, call, access) {
|
|
4850
|
+
if (lowerer.dynamic || access.name.text !== "call" || !ts.isPropertyAccessExpression(access.expression))
|
|
4851
|
+
return null;
|
|
4852
|
+
const method = access.expression;
|
|
4853
|
+
const prototype = method.expression;
|
|
4854
|
+
if (method.name.text !== "toString" || !ts.isPropertyAccessExpression(prototype) ||
|
|
4855
|
+
prototype.name.text !== "prototype" || !lowerer.isStdlibGlobal(prototype.expression, "Object") ||
|
|
4856
|
+
!lowerer.isStdlibMember(method) || call.arguments.some(ts.isSpreadElement))
|
|
4857
|
+
return null;
|
|
4858
|
+
const loc = locOf(call);
|
|
4859
|
+
if (call.arguments.length === 0)
|
|
4860
|
+
return { kind: "strLit", value: "[object Undefined]", type: STRING, loc };
|
|
4861
|
+
const receiverNode = call.arguments[0];
|
|
4862
|
+
const immediate = immediateObjectTagReceiver(lowerer, receiverNode);
|
|
4863
|
+
const receiver = immediate?.effect ?? lowerer.lowerExpr(receiverNode);
|
|
4864
|
+
const tag = immediate?.tag ?? objectToStringTag(lowerer, receiver.type);
|
|
4865
|
+
if (tag === null && receiver.type.kind !== "dyn") {
|
|
4866
|
+
lowerer.noLowering("Object.prototype.toString.call over a " + lowerer.fmt(receiver.type) + " receiver", receiverNode);
|
|
4867
|
+
}
|
|
4868
|
+
const stmts = [];
|
|
4869
|
+
let ref = receiver;
|
|
4870
|
+
if (isUnitType(receiver.type) || receiver.type.kind === "void") {
|
|
4871
|
+
if (!isSafeToDiscard(receiver))
|
|
4872
|
+
stmts.push({ kind: "exprStmt", expr: receiver, loc });
|
|
4873
|
+
}
|
|
4874
|
+
else {
|
|
4875
|
+
const local = lowerer.declareHiddenLocal("%objectTagReceiver", receiver.type);
|
|
4876
|
+
stmts.push({ kind: "varDecl", localId: local.id, init: receiver, loc });
|
|
4877
|
+
ref = varRef(local.id, receiver.type, loc);
|
|
4878
|
+
}
|
|
4879
|
+
for (const extra of call.arguments.slice(1)) {
|
|
4880
|
+
stmts.push({ kind: "exprStmt", expr: lowerer.lowerExpr(extra), loc: locOf(extra) });
|
|
4881
|
+
}
|
|
4882
|
+
const result = tag === null
|
|
4883
|
+
? { kind: "libCall", fn: "dyn.objectTag", args: [ref], type: STRING, loc }
|
|
4884
|
+
: { kind: "strLit", value: "[object " + tag + "]", type: STRING, loc };
|
|
4885
|
+
return { kind: "seqExpr", stmts, result, type: STRING, loc };
|
|
4886
|
+
}
|
|
4887
|
+
function lowerStringPrototypeCall(lowerer, call, access) {
|
|
4888
|
+
if (access.name.text !== "call" || !ts.isPropertyAccessExpression(access.expression))
|
|
4889
|
+
return null;
|
|
4890
|
+
const methodAccess = access.expression;
|
|
4891
|
+
const prototypeAccess = methodAccess.expression;
|
|
4892
|
+
if (!ts.isPropertyAccessExpression(prototypeAccess) || prototypeAccess.name.text !== "prototype" ||
|
|
4893
|
+
!ts.isIdentifier(prototypeAccess.expression) || prototypeAccess.expression.text !== "String" ||
|
|
4894
|
+
(!Object.hasOwn(STR_METHODS, methodAccess.name.text) && !STRING_INDEX_METHODS.has(methodAccess.name.text)) ||
|
|
4895
|
+
!lowerer.isStdlibMember(methodAccess))
|
|
4896
|
+
return null;
|
|
4897
|
+
if (call.arguments.some(ts.isSpreadElement))
|
|
4898
|
+
return null;
|
|
4899
|
+
const entry = STR_METHODS[methodAccess.name.text];
|
|
4900
|
+
const indexMethod = STRING_INDEX_METHODS.has(methodAccess.name.text) ? methodAccess.name.text : null;
|
|
4901
|
+
const resultType = indexMethod ? lowerer.withUndefinedArmOf(indexMethod === "at" ? STRING : F64) : entry?.result;
|
|
4902
|
+
if (!resultType)
|
|
4903
|
+
return null;
|
|
4904
|
+
const method = methodAccess.name.text === "trimStart" ? "trimLeft" :
|
|
4905
|
+
methodAccess.name.text === "trimEnd" ? "trimRight" : methodAccess.name.text;
|
|
4906
|
+
const loc = locOf(call);
|
|
4907
|
+
const nullishError = nodeThrowExpr(1, "", `String.prototype.${method} called on null or undefined`, resultType, loc);
|
|
4908
|
+
const receiverNode = call.arguments[0];
|
|
4909
|
+
if (!receiverNode)
|
|
4910
|
+
return nullishError;
|
|
4911
|
+
// Receiver coercion runs after .call has evaluated its arguments; keep
|
|
4912
|
+
// contextual wrapper lowering on forms without later method arguments.
|
|
4913
|
+
const immediateWrapper = entry?.maxArgs === 0 && call.arguments.length === 1
|
|
4914
|
+
? immediatePrimitiveWrapperToString(lowerer, receiverNode) : null;
|
|
4915
|
+
const receiverValue = immediateWrapper ?? lowerer.lowerExpr(receiverNode);
|
|
4916
|
+
const receiverType = receiverValue.type;
|
|
4917
|
+
const nullish = isUnitType(receiverType) || receiverType.kind === "void" ||
|
|
4918
|
+
(receiverType.kind === "union" && (lowerer.unions.get(receiverType.unionId)?.arms.every((arm) => isUnitType(arm) || arm.kind === "void") ?? false));
|
|
4919
|
+
if (nullish) {
|
|
4920
|
+
const values = [receiverValue, ...call.arguments.slice(1).map((arg) => lowerer.lowerExpr(arg))];
|
|
4921
|
+
return {
|
|
4922
|
+
kind: "seqExpr",
|
|
4923
|
+
stmts: values.filter((value) => !isSafeToDiscard(value)).map((value) => ({ kind: "exprStmt", expr: value, loc: value.loc })),
|
|
4924
|
+
result: nullishError,
|
|
4925
|
+
type: resultType,
|
|
4926
|
+
loc,
|
|
4927
|
+
};
|
|
4928
|
+
}
|
|
4929
|
+
const padding = entry?.method === "padStart" || entry?.method === "padEnd";
|
|
4930
|
+
const scalar = receiverType.kind === "string" || receiverType.kind === "f64" ||
|
|
4931
|
+
receiverType.kind === "bool" || receiverType.kind === "bigint" ||
|
|
4932
|
+
(receiverType.kind === "union" && (lowerer.unions.get(receiverType.unionId)?.arms.every((arm) => arm.kind === "string" || arm.kind === "f64" || arm.kind === "bool" || arm.kind === "bigint") ?? false));
|
|
4933
|
+
// Other methods convert object receivers before lowering their arguments;
|
|
4934
|
+
// padding uses a helper that delays conversion until every argument is ready.
|
|
4935
|
+
const objectWithoutMethodArgs = entry?.maxArgs === 0 && call.arguments.length === 1 &&
|
|
4936
|
+
(receiverType.kind === "record" || receiverType.kind === "array" || receiverType.kind === "object");
|
|
4937
|
+
const dynObjectWithoutMethodArgs = entry?.maxArgs === 0 && call.arguments.length === 1 &&
|
|
4938
|
+
receiverType.kind === "dyn" && plainObjectCoercionReceiver(lowerer, receiverNode);
|
|
4939
|
+
if (!scalar && !objectWithoutMethodArgs && !dynObjectWithoutMethodArgs && !(padding && receiverType.kind === "dyn")) {
|
|
4940
|
+
return lowerer.noLowering(`String.prototype.${methodAccess.name.text}.call with ${lowerer.fmt(receiverType)} receivers`, call);
|
|
4941
|
+
}
|
|
4942
|
+
if (indexMethod)
|
|
4943
|
+
return lowerStringIndexCall(lowerer, call, indexMethod, receiverValue, receiverNode, call.arguments.slice(1));
|
|
4944
|
+
if (!entry)
|
|
4945
|
+
return null;
|
|
4946
|
+
if (padding) {
|
|
4947
|
+
return lowerStringPaddingCall(lowerer, call, entry.method, receiverValue, receiverNode, call.arguments.slice(1));
|
|
4948
|
+
}
|
|
4949
|
+
if (entry.method === "split") {
|
|
4950
|
+
return lowerStringSplitCall(lowerer, call, receiverValue, receiverNode, call.arguments.slice(1));
|
|
4951
|
+
}
|
|
4952
|
+
const receiver = dynObjectWithoutMethodArgs
|
|
4953
|
+
? { kind: "libCall", fn: "dyn.toStringCoerce", args: [receiverValue], type: STRING, loc }
|
|
4954
|
+
: lowerer.ensureString(receiverValue, receiverNode);
|
|
4955
|
+
return lowerStringMethodCallWithOptionalArgs(lowerer, call, methodAccess, () => receiver, call.arguments.slice(1));
|
|
4956
|
+
}
|
|
4957
|
+
function lowerScalarUnionNumber(lowerer, arg, node, loc) {
|
|
4958
|
+
if (arg.type.kind !== "union" || !lowerer.unions.get(arg.type.unionId)?.arms.every((arm) => arm.kind === "f64" || arm.kind === "string" || arm.kind === "bool" || isUnitType(arm)))
|
|
4959
|
+
return null;
|
|
4960
|
+
const key = `number.scalar:${arg.type.unionId}`;
|
|
4961
|
+
let helper = lowerer.widthHelpers.get(key);
|
|
4962
|
+
if (!helper) {
|
|
4963
|
+
helper = `%number.scalar.${lowerer.widthHelpers.size}`;
|
|
4964
|
+
lowerer.widthHelpers.set(key, helper);
|
|
4965
|
+
const value = varRef("value.0", arg.type, loc);
|
|
4966
|
+
const nan = {
|
|
4967
|
+
kind: "bin", op: "/", left: { kind: "numLit", value: 0, type: F64, loc },
|
|
4968
|
+
right: { kind: "numLit", value: 0, type: F64, loc }, type: F64, loc,
|
|
4969
|
+
};
|
|
4970
|
+
lowerer.liftedFns.push({
|
|
4971
|
+
name: helper,
|
|
4972
|
+
params: [{ localId: "value.0", name: "value", type: arg.type }],
|
|
4973
|
+
returnType: F64,
|
|
4974
|
+
locals: [{ id: "value.0", name: "value", type: arg.type, mutable: false }],
|
|
4975
|
+
body: [{ kind: "return", value: positionNumber(lowerer, value, nan, node, "Number argument"), loc }],
|
|
4976
|
+
loc,
|
|
4977
|
+
});
|
|
4978
|
+
}
|
|
4979
|
+
return { kind: "call", callee: helper, args: [arg], type: F64, loc };
|
|
4980
|
+
}
|
|
4545
4981
|
function lowerOptionalStringNumber(lowerer, arg, loc, fn = "num.fromString", label = "number") {
|
|
4546
4982
|
if (arg.type.kind !== "union")
|
|
4547
4983
|
return null;
|
|
@@ -5893,26 +6329,23 @@ export function lambdaSignature(lowerer, node) {
|
|
|
5893
6329
|
ret = VOID;
|
|
5894
6330
|
}
|
|
5895
6331
|
}
|
|
5896
|
-
//
|
|
5897
|
-
//
|
|
5898
|
-
//
|
|
5899
|
-
//
|
|
5900
|
-
|
|
5901
|
-
|
|
5902
|
-
|
|
5903
|
-
// declared params the alias story has no model — the fence says to
|
|
5904
|
-
// use a rest parameter. Arrows never claim it (JS: an arrow's
|
|
5905
|
-
// `arguments` is the enclosing function's).
|
|
5906
|
-
const hasDynRest = shapes.some((s) => s.mode === "dynRest");
|
|
5907
|
-
const usesArguments = !hasDynRest &&
|
|
5908
|
-
!ts.isArrowFunction(node) &&
|
|
6332
|
+
// A plain JS function reading `arguments` receives one hidden dyn
|
|
6333
|
+
// array. With named parameters, the array contains every actual
|
|
6334
|
+
// argument and only ES modules are admitted: sloppy-mode arguments
|
|
6335
|
+
// alias named parameters. Arrows inherit the enclosing `arguments`.
|
|
6336
|
+
const hasRest = shapes.some((s) => s.mode === "rest" || s.mode === "dynRest" || s.mode === "islandRest");
|
|
6337
|
+
const usesArguments = !hasRest &&
|
|
6338
|
+
(ts.isFunctionExpression(node) || ts.isFunctionDeclaration(node)) &&
|
|
5909
6339
|
isJsSourceFile(node.getSourceFile()) &&
|
|
5910
6340
|
bodyReadsArguments(node);
|
|
5911
6341
|
if (usesArguments && node.parameters.length > 0) {
|
|
5912
|
-
|
|
6342
|
+
if (!isNodeEsmFile(node.getSourceFile())) {
|
|
6343
|
+
lowerer.unsupported("SC1090", node, "parameterized 'arguments' outside an ES module (sloppy-mode parameter aliases)");
|
|
6344
|
+
}
|
|
6345
|
+
shapes.push({ type: DYN, mode: "arguments" });
|
|
5913
6346
|
}
|
|
5914
6347
|
const funcType = funcTypeFromParamShapes(shapes, ret);
|
|
5915
|
-
if (usesArguments
|
|
6348
|
+
if (usesArguments)
|
|
5916
6349
|
funcType.rest = true;
|
|
5917
6350
|
return { shapes, funcType };
|
|
5918
6351
|
}
|
|
@@ -5988,9 +6421,9 @@ export function lowerLambda(lowerer, node) {
|
|
|
5988
6421
|
lowerer.fnStack.push(fnCtx);
|
|
5989
6422
|
try {
|
|
5990
6423
|
const { params, prologue } = lowerer.declareParams(node.parameters, shapes);
|
|
5991
|
-
// The
|
|
5992
|
-
//
|
|
5993
|
-
//
|
|
6424
|
+
// The synthetic trailing dyn-array param carries `arguments`.
|
|
6425
|
+
// Parameterized functions receive all actual arguments; zero-param
|
|
6426
|
+
// functions receive the same array through their rest ABI.
|
|
5994
6427
|
if (funcType.rest && !shapes.some((s) => s.mode === "dynRest" || s.mode === "islandRest")) {
|
|
5995
6428
|
const argsLocal = lowerer.declareHiddenLocal("%arguments", DYN);
|
|
5996
6429
|
params.push({ localId: argsLocal.id, name: "%arguments", type: DYN });
|
|
@@ -7072,9 +7505,10 @@ function lowerBigIntMethodCall(lowerer, call, access) {
|
|
|
7072
7505
|
if (name === "valueOf" && call.arguments.length === 0)
|
|
7073
7506
|
return receiver;
|
|
7074
7507
|
if (name === "toString" && call.arguments.length <= 1) {
|
|
7508
|
+
const defaultRadix = { kind: "numLit", value: 10, type: F64, loc };
|
|
7075
7509
|
const radix = call.arguments[0]
|
|
7076
|
-
? lowerer
|
|
7077
|
-
:
|
|
7510
|
+
? lowerOptionalArgument(lowerer, call.arguments[0], F64, defaultRadix)
|
|
7511
|
+
: defaultRadix;
|
|
7078
7512
|
return { kind: "libCall", fn: "bigint.toString", args: [receiver, radix], type: STRING, loc };
|
|
7079
7513
|
}
|
|
7080
7514
|
return null;
|
|
@@ -8092,10 +8526,9 @@ export function lowerFunction(lowerer, decl) {
|
|
|
8092
8526
|
lowerer.fnStack.push(ctx);
|
|
8093
8527
|
try {
|
|
8094
8528
|
const { params, prologue } = lowerer.declareParams(decl.parameters, sig.params);
|
|
8095
|
-
// The synthetic `arguments` slot
|
|
8096
|
-
//
|
|
8097
|
-
|
|
8098
|
-
if (sig.params.length > decl.parameters.length && sig.params[sig.params.length - 1].mode === "dynRest") {
|
|
8529
|
+
// The synthetic `arguments` slot follows the declared parameters;
|
|
8530
|
+
// reads resolve to this dyn array.
|
|
8531
|
+
if (sig.params.length > decl.parameters.length && (sig.params[sig.params.length - 1].mode === "dynRest" || sig.params[sig.params.length - 1].mode === "arguments")) {
|
|
8099
8532
|
const argsLocal = lowerer.declareHiddenLocal("%arguments", DYN);
|
|
8100
8533
|
params.push({ localId: argsLocal.id, name: "%arguments", type: DYN });
|
|
8101
8534
|
ctx.argumentsLocal = argsLocal;
|