@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
|
@@ -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
|
-
import { PoisonError, dynFallbackType, dynUndefinedExpr, importCallHandleType, jsFuncNameOf, newFnCtx, nodeThrowExpr } from "./lowerer.js";
|
|
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, 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
16
|
import { dynStringReceiver, lowerArrayFromCall, 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";
|
|
19
|
-
import { lowerChildStreamMethodCall, lowerChildWriterMethodCall, lowerCreateRequireCall, lowerCryptoHashMethodCall, lowerDirentMethodCall, lowerFileHandleMethodCall, lowerImportMetaResolveCall, lowerPerfHooksCall, lowerProcStreamMethodCall, lowerReflectApplyCall, lowerRequireResolveCall, lowerWatcherMethodCall } from "./lower-builtins.js";
|
|
20
|
-
import { lowerAbsenceProbe, lowerPromiseAllTupleCall, lowerPromiseRejectCall, templateRawTextOf } from "./lower-exprs.js";
|
|
18
|
+
import { lowerRegexMethodCall, lowerStringMethodCall, lowerStringPaddingCall } from "./containers/string-and-regexp.js";
|
|
19
|
+
import { lowerChildStreamMethodCall, lowerChildWriterMethodCall, lowerCreateRequireCall, lowerCryptoHashMethodCall, lowerDirentMethodCall, lowerFileHandleMethodCall, lowerImportMetaResolveCall, lowerNodeModuleCall, lowerPerfHooksCall, lowerProcStreamMethodCall, lowerReflectApplyCall, lowerRequireResolveCall, lowerWatcherMethodCall } from "./lower-builtins.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,61 @@ 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";
|
|
36
|
+
/** The one func-type projection of completed parameter shapes. Typed rest
|
|
37
|
+
* parameters keep their packed array as the trailing native ABI slot and
|
|
38
|
+
* mark the value variadic so indirect call sites run completeArgs before
|
|
39
|
+
* callValue. Dynamic rest remains the historical hidden ScrDyn slot, while
|
|
40
|
+
* island rest spells its engine-array slot directly. */
|
|
41
|
+
export function funcTypeFromParamShapes(shapes, ret) {
|
|
42
|
+
const typedRest = shapes.some((shape) => shape.mode === "rest");
|
|
43
|
+
const dynRest = shapes.some((shape) => shape.mode === "dynRest");
|
|
44
|
+
const argumentsAll = shapes.some((shape) => shape.mode === "arguments");
|
|
45
|
+
const islandRest = shapes.some((shape) => shape.mode === "islandRest");
|
|
46
|
+
return {
|
|
47
|
+
kind: "func",
|
|
48
|
+
params: shapes.filter((shape) => shape.mode !== "dynRest" && shape.mode !== "arguments").map((shape) => shape.type),
|
|
49
|
+
ret,
|
|
50
|
+
...(typedRest || dynRest || islandRest || argumentsAll ? { rest: true } : {}),
|
|
51
|
+
...(argumentsAll ? { argumentsAll: true } : {}),
|
|
52
|
+
...(typedRest
|
|
53
|
+
? { restAbi: "typed" }
|
|
54
|
+
: islandRest
|
|
55
|
+
? { restAbi: "jsval" }
|
|
56
|
+
: {}),
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
/** Registers `const alias = overloadedDeclaration` as a compile-time
|
|
60
|
+
* callable projection. The source function's one implementation ABI is
|
|
61
|
+
* already collected in fnSigsBySymbol; each direct call still uses the
|
|
62
|
+
* TS7-resolved overload result bridge. The alias owns no storage, and a
|
|
63
|
+
* value read materializes the source function's interned closure, so JS
|
|
64
|
+
* identity (`alias === source`) is preserved. */
|
|
65
|
+
export function registerOverloadedCallableAlias(lowerer, decl) {
|
|
66
|
+
if (!ts.isIdentifier(decl.name) || !decl.initializer)
|
|
67
|
+
return false;
|
|
68
|
+
let init = decl.initializer;
|
|
69
|
+
while (ts.isParenthesizedExpression(init))
|
|
70
|
+
init = init.expression;
|
|
71
|
+
if (!ts.isIdentifier(init))
|
|
72
|
+
return false;
|
|
73
|
+
const aliasType = lowerer.typeOf(decl.name);
|
|
74
|
+
if (lowerer.checker.getCallSignatures(aliasType).length < 2)
|
|
75
|
+
return false;
|
|
76
|
+
const sourceSymbol = lowerer.resolveValueSymbol(init);
|
|
77
|
+
if (!sourceSymbol)
|
|
78
|
+
return false;
|
|
79
|
+
const sourceProjection = lowerer.staticCallables.get(sourceSymbol);
|
|
80
|
+
const signature = lowerer.fnSigsBySymbol.get(sourceSymbol) ??
|
|
81
|
+
(sourceProjection?.kind === "declared-function" ? sourceProjection.signature : undefined);
|
|
82
|
+
if (!signature)
|
|
83
|
+
return false;
|
|
84
|
+
const aliasSymbol = lowerer.checker.getSymbolAtLocation(decl.name);
|
|
85
|
+
if (!aliasSymbol)
|
|
86
|
+
return false;
|
|
87
|
+
lowerer.staticCallables.set(aliasSymbol, { kind: "declared-function", signature });
|
|
88
|
+
return true;
|
|
89
|
+
}
|
|
35
90
|
export function generatorMeta(lowerer, type) {
|
|
36
91
|
const resultType = genResultRecord(type.yieldT, type.retT, lowerer.shapes, lowerer.unions);
|
|
37
92
|
if (resultType === null) {
|
|
@@ -61,6 +116,10 @@ export const MAX_GENERIC_INSTANCES = 100;
|
|
|
61
116
|
* - `...xs: T[]`: the ABI type is the array; call sites pack the surplus.
|
|
62
117
|
*/
|
|
63
118
|
export function paramShape(lowerer, param) {
|
|
119
|
+
const moduleNs = lowerer.moduleNsParamOverrides.get(param);
|
|
120
|
+
if (moduleNs !== undefined) {
|
|
121
|
+
return { type: moduleNs, mode: param.questionToken ? "omittable" : "required" };
|
|
122
|
+
}
|
|
64
123
|
// Island-handle params (a then-handler receiving a dynamic import's
|
|
65
124
|
// namespace handle — markJsvalHandlerParams): jsval, whatever the
|
|
66
125
|
// contextual type spelled.
|
|
@@ -256,6 +315,18 @@ export function checkDefaultParamBodyType(lowerer, param, bodyType) {
|
|
|
256
315
|
* explicitly-passed `undefined` wraps to — both trigger a default, JS-
|
|
257
316
|
* exact); a rest param packs the surplus args (possibly zero) into one
|
|
258
317
|
* array literal, evaluated in source order at the call site. */
|
|
318
|
+
function fixedTupleSpreadInfo(lowerer, node) {
|
|
319
|
+
const type = lowerer.mapTypeOf(lowerer.typeOf(node));
|
|
320
|
+
if (type?.kind !== "record")
|
|
321
|
+
return null;
|
|
322
|
+
const shape = lowerer.shapes.get(type.shapeId);
|
|
323
|
+
if (!shape?.tuple || shape.fields.length === 0)
|
|
324
|
+
return null;
|
|
325
|
+
return {
|
|
326
|
+
type,
|
|
327
|
+
fields: [...shape.fields].sort((a, b) => Number(a.name) - Number(b.name)),
|
|
328
|
+
};
|
|
329
|
+
}
|
|
259
330
|
export function completeArgs(lowerer, argNodes, shapes, loc, blame,
|
|
260
331
|
/** Pre-lowered values virtually PREPENDED to the argument list — the
|
|
261
332
|
* tagged-template strings object, which has no ts.Expression to lower
|
|
@@ -264,9 +335,92 @@ export function completeArgs(lowerer, argNodes, shapes, loc, blame,
|
|
|
264
335
|
* DYN conversion in a dyn rest, element coercion in a typed rest). */
|
|
265
336
|
leading) {
|
|
266
337
|
const isIr = (s) => s !== undefined && !("kind" in s);
|
|
267
|
-
const sources = leading
|
|
268
|
-
const
|
|
338
|
+
const sources = leading?.map((ir) => ({ ir })) ?? [];
|
|
339
|
+
for (const arg of argNodes) {
|
|
340
|
+
if (!ts.isSpreadElement(arg)) {
|
|
341
|
+
sources.push(arg);
|
|
342
|
+
continue;
|
|
343
|
+
}
|
|
344
|
+
const tuple = fixedTupleSpreadInfo(lowerer, arg.expression);
|
|
345
|
+
if (!tuple) {
|
|
346
|
+
sources.push(arg);
|
|
347
|
+
continue;
|
|
348
|
+
}
|
|
349
|
+
// A fixed tuple has a compile-time argument count, so flatten it into
|
|
350
|
+
// the callee's ordinary positional ABI. The operand itself still
|
|
351
|
+
// evaluates exactly once and at the spread's source-order position:
|
|
352
|
+
// the first extracted field carries a seqExpr that initializes one
|
|
353
|
+
// hidden tuple local, and every later field reads that same local.
|
|
354
|
+
// Tuple fields are owned reads, so the call receives the same +1
|
|
355
|
+
// values as separately written arguments while the saved tuple stays
|
|
356
|
+
// alive through the call.
|
|
357
|
+
const spreadLoc = locOf(arg);
|
|
358
|
+
const value = lowerer.coerceInto(arg.expression, lowerer.lowerExpr(arg.expression), tuple.type);
|
|
359
|
+
const saved = lowerer.declareHiddenLocal("%callSpread", tuple.type);
|
|
360
|
+
const savedRef = () => ({
|
|
361
|
+
kind: "varRef",
|
|
362
|
+
localId: saved.id,
|
|
363
|
+
type: tuple.type,
|
|
364
|
+
loc: spreadLoc,
|
|
365
|
+
});
|
|
366
|
+
tuple.fields.forEach((field, i) => {
|
|
367
|
+
const read = {
|
|
368
|
+
kind: "recordGet",
|
|
369
|
+
obj: savedRef(),
|
|
370
|
+
shapeId: tuple.type.shapeId,
|
|
371
|
+
field: field.name,
|
|
372
|
+
type: field.type,
|
|
373
|
+
loc: spreadLoc,
|
|
374
|
+
};
|
|
375
|
+
sources.push({
|
|
376
|
+
ir: i === 0
|
|
377
|
+
? {
|
|
378
|
+
kind: "seqExpr",
|
|
379
|
+
stmts: [{ kind: "varDecl", localId: saved.id, init: value, loc: spreadLoc }],
|
|
380
|
+
result: read,
|
|
381
|
+
type: field.type,
|
|
382
|
+
loc: spreadLoc,
|
|
383
|
+
}
|
|
384
|
+
: read,
|
|
385
|
+
});
|
|
386
|
+
});
|
|
387
|
+
}
|
|
388
|
+
const restAt = shapes.findIndex((s) => s.mode === "rest" || s.mode === "dynRest" || s.mode === "islandRest" || s.mode === "arguments");
|
|
269
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
|
+
}
|
|
270
424
|
const out = positional.map((shape, i) => {
|
|
271
425
|
const src = sources[i];
|
|
272
426
|
if (isIr(src))
|
|
@@ -293,6 +447,8 @@ leading) {
|
|
|
293
447
|
// reaching here means a call form we don't model — defensive.
|
|
294
448
|
lowerer.unsupported("SC1090", blame, "this call form");
|
|
295
449
|
}
|
|
450
|
+
if (shape.callDefault)
|
|
451
|
+
return shape.callDefault;
|
|
296
452
|
return lowerer.undefinedArgFor(shape.type, loc, blame);
|
|
297
453
|
});
|
|
298
454
|
if (restAt >= 0 && shapes[restAt].mode === "islandRest") {
|
|
@@ -439,6 +595,34 @@ export function omittedArgFor(lowerer, type, loc) {
|
|
|
439
595
|
return { kind: "jsOp", op: "undefLit", args: [], type: JSVAL, loc };
|
|
440
596
|
return lowerer.wrappedUndefined(type, loc);
|
|
441
597
|
}
|
|
598
|
+
/** Complete an indirect static call against its func value ABI. Ordinary
|
|
599
|
+
* fixed-width values retain the historical optional-suffix completion.
|
|
600
|
+
* Typed-rest values reinterpret their final array slot as a rest ParamShape
|
|
601
|
+
* and reuse completeArgs, including fixed-tuple and same-element array
|
|
602
|
+
* spreads with source-order/evaluate-once semantics. */
|
|
603
|
+
function completeFuncValueArgs(lowerer, call, funcType, loc) {
|
|
604
|
+
if (funcType.rest === true && funcType.restAbi === "typed") {
|
|
605
|
+
const rest = funcType.params[funcType.params.length - 1];
|
|
606
|
+
if (!rest || rest.kind !== "array") {
|
|
607
|
+
throw new InternalCompilerError("typed-rest function value has no trailing array ABI slot");
|
|
608
|
+
}
|
|
609
|
+
const shapes = funcType.params.slice(0, -1).map((type) => ({
|
|
610
|
+
type,
|
|
611
|
+
mode: omittedArgFor(lowerer, type, loc) ? "omittable" : "required",
|
|
612
|
+
}));
|
|
613
|
+
shapes.push({ type: rest, mode: "rest" });
|
|
614
|
+
return completeArgs(lowerer, call.arguments, shapes, loc, call);
|
|
615
|
+
}
|
|
616
|
+
const args = call.arguments.map((arg, index) => lowerer.lowerExprExpecting(arg, funcType.params[index]));
|
|
617
|
+
for (let i = args.length; i < funcType.params.length; i++) {
|
|
618
|
+
const absent = omittedArgFor(lowerer, funcType.params[i], loc);
|
|
619
|
+
if (!absent) {
|
|
620
|
+
lowerer.unsupported("SC1090", call, "calls omitting a non-optional parameter of the callee's type");
|
|
621
|
+
}
|
|
622
|
+
args.push(absent);
|
|
623
|
+
}
|
|
624
|
+
return args;
|
|
625
|
+
}
|
|
442
626
|
export function undefinedArgFor(lowerer, type, loc, blame) {
|
|
443
627
|
if (type.kind === "dyn")
|
|
444
628
|
return dynUndefinedExpr(loc);
|
|
@@ -454,13 +638,12 @@ export function undefinedArgFor(lowerer, type, loc, blame) {
|
|
|
454
638
|
}
|
|
455
639
|
return wrapped;
|
|
456
640
|
}
|
|
457
|
-
/** DECISION (docs/ir.md): function VALUES
|
|
458
|
-
*
|
|
459
|
-
*
|
|
460
|
-
*
|
|
461
|
-
*
|
|
462
|
-
*
|
|
463
|
-
* types reject). Direct calls get the full feature. */
|
|
641
|
+
/** DECISION (docs/ir.md): function VALUES carry one completed native ABI.
|
|
642
|
+
* Optional/default parameters appear as literal `T | undefined` slots;
|
|
643
|
+
* typed rest parameters appear as one trailing typed-array slot plus the
|
|
644
|
+
* typed-rest marker that makes indirect call sites pack source arguments.
|
|
645
|
+
* A value is admitted only where its target type projects to that same
|
|
646
|
+
* completed signature. */
|
|
464
647
|
export function requireExactArityValue(lowerer, blame, contextual, shapes, funcType) {
|
|
465
648
|
// dynRest params ride the boxed thunk (JS arity — no completed-ABI
|
|
466
649
|
// spelling exists or is needed); they don't gate the value form.
|
|
@@ -471,13 +654,11 @@ export function requireExactArityValue(lowerer, blame, contextual, shapes, funcT
|
|
|
471
654
|
// tier's undefined (omittedArgFor).
|
|
472
655
|
if (shapes.every((s) => s.mode === "required" ||
|
|
473
656
|
s.mode === "dynRest" ||
|
|
657
|
+
s.mode === "arguments" ||
|
|
474
658
|
s.mode === "islandRest" ||
|
|
475
659
|
(s.mode === "omittable" && (s.type.kind === "dyn" || s.type.kind === "jsval")))) {
|
|
476
660
|
return;
|
|
477
661
|
}
|
|
478
|
-
if (shapes.some((s) => s.mode === "rest")) {
|
|
479
|
-
lowerer.unsupported("SC1090", blame, "functions with rest parameters as values (call them directly)");
|
|
480
|
-
}
|
|
481
662
|
// The type the value FLOWS under must spell the completed ABI: the
|
|
482
663
|
// contextual (target) type when one exists, otherwise the expression's
|
|
483
664
|
// OWN inferred type — the unannotated-const case (`const f = (x = 5) =>
|
|
@@ -520,6 +701,20 @@ export function requireExactArityValue(lowerer, blame, contextual, shapes, funcT
|
|
|
520
701
|
lowerer.spawnResFnAdapterPlan(funcType, mappedFn) !== null) {
|
|
521
702
|
return;
|
|
522
703
|
}
|
|
704
|
+
// An uncontextualized npm-static implementation function can still
|
|
705
|
+
// have a declaration-backed completed ABI: the package's .d.ts
|
|
706
|
+
// projection supplies the body-facing parameter/return types while
|
|
707
|
+
// the shipped JavaScript's own inferred type remains wider (`any`
|
|
708
|
+
// callback fields are the common case). lambdaSignature has already
|
|
709
|
+
// constructed and validated that concrete ABI, and every eventual
|
|
710
|
+
// storage/call site independently checks it. Admit the value at birth;
|
|
711
|
+
// do not make the unprojected JS inference veto its authored surface.
|
|
712
|
+
if (target === undefined &&
|
|
713
|
+
contextual !== null &&
|
|
714
|
+
(ts.isArrowFunction(contextual) || ts.isFunctionExpression(contextual)) &&
|
|
715
|
+
npmStaticPackageOfPath(contextual.getSourceFile().fileName) !== null) {
|
|
716
|
+
return;
|
|
717
|
+
}
|
|
523
718
|
// An 'any'-typed slot is the ISLAND boundary: the host-function
|
|
524
719
|
// trampoline already implements JS call semantics over the completed
|
|
525
720
|
// signature — a missing engine argument arrives as undefined and takes
|
|
@@ -758,18 +953,16 @@ export function collectSignatureInner(lowerer, decl) {
|
|
|
758
953
|
}
|
|
759
954
|
}
|
|
760
955
|
const params = lowerer.paramShapes(decl.parameters);
|
|
761
|
-
//
|
|
762
|
-
//
|
|
763
|
-
//
|
|
764
|
-
|
|
765
|
-
// indirect ones; lowerFunction declares the `arguments` local).
|
|
766
|
-
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") &&
|
|
767
960
|
isJsSourceFile(decl.getSourceFile()) &&
|
|
768
961
|
bodyReadsArguments(decl)) {
|
|
769
|
-
if (decl.parameters.length > 0) {
|
|
770
|
-
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)");
|
|
771
964
|
}
|
|
772
|
-
params.push({ type: DYN, mode: "dynRest" });
|
|
965
|
+
params.push({ type: DYN, mode: decl.parameters.length > 0 ? "arguments" : "dynRest" });
|
|
773
966
|
}
|
|
774
967
|
const nameBlame = decl.name ?? decl;
|
|
775
968
|
const returnType = lowerer.declaredReturnType(decl, nameBlame);
|
|
@@ -1426,12 +1619,7 @@ export function lowerGenericFnValue(lowerer, ref, info) {
|
|
|
1426
1619
|
// closure per function, so `f === f` holds like any declaration).
|
|
1427
1620
|
if (info.implicitParams) {
|
|
1428
1621
|
const inst = implicitDefaultInstance(lowerer, ref, info);
|
|
1429
|
-
const funcType =
|
|
1430
|
-
kind: "func",
|
|
1431
|
-
params: inst.params.filter((p) => p.mode !== "dynRest").map((p) => p.type),
|
|
1432
|
-
ret: inst.returnType,
|
|
1433
|
-
...(inst.params.some((p) => p.mode === "dynRest") ? { rest: true } : {}),
|
|
1434
|
-
};
|
|
1622
|
+
const funcType = funcTypeFromParamShapes(inst.params, inst.returnType);
|
|
1435
1623
|
lowerer.requireExactArityValue(ref, ref, inst.params, funcType);
|
|
1436
1624
|
lowerer.noteEdge(inst.name);
|
|
1437
1625
|
return { kind: "closure", fnName: inst.name, captures: [], type: funcType, loc };
|
|
@@ -1477,15 +1665,9 @@ export function lowerGenericFnValue(lowerer, ref, info) {
|
|
|
1477
1665
|
if (info.typeParams.some((tp) => !bindings.get(tp)))
|
|
1478
1666
|
fenceUnpinned();
|
|
1479
1667
|
const inst = genericValueInstance(lowerer, ref, info, bindings);
|
|
1480
|
-
// The value's type is the completed ABI signature
|
|
1481
|
-
//
|
|
1482
|
-
|
|
1483
|
-
const funcType = {
|
|
1484
|
-
kind: "func",
|
|
1485
|
-
params: inst.params.filter((p) => p.mode !== "dynRest").map((p) => p.type),
|
|
1486
|
-
ret: inst.returnType,
|
|
1487
|
-
...(inst.params.some((p) => p.mode === "dynRest") ? { rest: true } : {}),
|
|
1488
|
-
};
|
|
1668
|
+
// The value's type is the completed ABI signature. Typed rest keeps its
|
|
1669
|
+
// packed-array slot; dynRest stays hidden behind the rest marker.
|
|
1670
|
+
const funcType = funcTypeFromParamShapes(inst.params, inst.returnType);
|
|
1489
1671
|
lowerer.requireExactArityValue(ref, ref, inst.params, funcType);
|
|
1490
1672
|
lowerer.noteEdge(inst.name);
|
|
1491
1673
|
return { kind: "closure", fnName: inst.name, captures: [], type: funcType, loc };
|
|
@@ -2780,6 +2962,57 @@ export function lowerFfiCall(lowerer, expr) {
|
|
|
2780
2962
|
loc,
|
|
2781
2963
|
};
|
|
2782
2964
|
}
|
|
2965
|
+
/** Full builtin dispatch for compile-time callable projections. A projected
|
|
2966
|
+
* alias must traverse the same spoke/validation ladder as its direct import;
|
|
2967
|
+
* jumping to the table tail would bypass checked-dynamic argument errors. */
|
|
2968
|
+
function lowerProjectedBuiltinCall(lowerer, expr, bi, loc) {
|
|
2969
|
+
if (bi.module === "child_process" && bi.member === "execFile") {
|
|
2970
|
+
return lowerer.lowerExecFileCall(expr, loc);
|
|
2971
|
+
}
|
|
2972
|
+
if (bi.module === "timers") {
|
|
2973
|
+
const timersServed = lowerTimersMemberCall(lowerer, expr, bi.member, loc);
|
|
2974
|
+
if (timersServed)
|
|
2975
|
+
return timersServed;
|
|
2976
|
+
}
|
|
2977
|
+
const served = lowerer.lowerNetModuleCall(expr, bi, loc);
|
|
2978
|
+
if (served)
|
|
2979
|
+
return served;
|
|
2980
|
+
const dgramServed = lowerer.lowerDgramDnsModuleCall(expr, bi, loc);
|
|
2981
|
+
if (dgramServed)
|
|
2982
|
+
return dgramServed;
|
|
2983
|
+
const assertServed = lowerer.lowerAssertModuleCall(expr, bi, loc);
|
|
2984
|
+
if (assertServed)
|
|
2985
|
+
return assertServed;
|
|
2986
|
+
const testServed = lowerer.lowerNodeTestModuleCall(expr, bi, loc);
|
|
2987
|
+
if (testServed)
|
|
2988
|
+
return testServed;
|
|
2989
|
+
const utilServed = lowerer.lowerUtilModuleCall(expr, bi, loc);
|
|
2990
|
+
if (utilServed)
|
|
2991
|
+
return utilServed;
|
|
2992
|
+
const streamServed = lowerStreamModuleCall(lowerer, expr, bi, loc);
|
|
2993
|
+
if (streamServed)
|
|
2994
|
+
return streamServed;
|
|
2995
|
+
const fsTs = lowerer.lowerFsToUnixTimestampCall(expr, bi, loc);
|
|
2996
|
+
if (fsTs)
|
|
2997
|
+
return fsTs;
|
|
2998
|
+
const fsLadder = lowerer.lowerFsLadderCall(expr, bi, loc);
|
|
2999
|
+
if (fsLadder)
|
|
3000
|
+
return fsLadder;
|
|
3001
|
+
const cryptoServed = lowerer.lowerCryptoModuleCall(expr, bi, loc);
|
|
3002
|
+
if (cryptoServed)
|
|
3003
|
+
return cryptoServed;
|
|
3004
|
+
const timersInterval = lowerer.lowerTimersPromisesSetInterval(expr, bi, loc);
|
|
3005
|
+
if (timersInterval)
|
|
3006
|
+
return timersInterval;
|
|
3007
|
+
const nodeModuleServed = lowerNodeModuleCall(lowerer, expr, bi, loc);
|
|
3008
|
+
if (nodeModuleServed)
|
|
3009
|
+
return nodeModuleServed;
|
|
3010
|
+
const builtinFn = builtinModuleFnOf(lowerer, bi.module, bi.member);
|
|
3011
|
+
if (!builtinFn) {
|
|
3012
|
+
lowerer.noLowering(`${bi.module}.${bi.member}`, expr, builtinFenceHintOf(bi.module, bi.member), ts.isIdentifier(expr.expression) ? lowerer.resolveValueSymbol(expr.expression) : undefined);
|
|
3013
|
+
}
|
|
3014
|
+
return lowerer.lowerBuiltinModuleCall(expr, bi, builtinFn, loc);
|
|
3015
|
+
}
|
|
2783
3016
|
export function lowerCall(lowerer, expr) {
|
|
2784
3017
|
const loc = locOf(expr);
|
|
2785
3018
|
// A call whose chain ROOTS at an ambient-undefined name (`declare
|
|
@@ -3142,13 +3375,12 @@ export function lowerCall(lowerer, expr) {
|
|
|
3142
3375
|
// String(x) is exactly the template-literal ToString, Boolean(x) is
|
|
3143
3376
|
// exactly the condition ToBoolean (union arms included), Number(x) is
|
|
3144
3377
|
// ToNumber where it lowers exactly: numbers pass through, booleans
|
|
3145
|
-
// become 1/0, and
|
|
3146
|
-
// StringToNumber (num.fromString — the full
|
|
3147
|
-
// grammar
|
|
3148
|
-
// 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).
|
|
3149
3381
|
// Provenance-checked like setTimeout; zero-arg forms are the JS
|
|
3150
|
-
// constants ("", false, 0).
|
|
3151
|
-
//
|
|
3382
|
+
// constants ("", false, 0). Wrapper objects stay fenced except where
|
|
3383
|
+
// the receiver is immediately converted by a direct String method call.
|
|
3152
3384
|
if (ts.isIdentifier(expr.expression) &&
|
|
3153
3385
|
expr.expression.text === "BigInt" &&
|
|
3154
3386
|
lowerer.isStdlibSymbol(lowerer.resolveValueSymbol(expr.expression) ?? undefined)) {
|
|
@@ -3209,33 +3441,11 @@ export function lowerCall(lowerer, expr) {
|
|
|
3209
3441
|
// bool) that a value lowering of the `&&` would fence on.
|
|
3210
3442
|
if (name === "Boolean")
|
|
3211
3443
|
return lowerer.lowerCondition(argNode);
|
|
3444
|
+
if (name === "Number")
|
|
3445
|
+
return lowerNumberConstructorValue(lowerer, argNode, loc);
|
|
3212
3446
|
const arg = lowerer.lowerExpr(argNode);
|
|
3213
3447
|
if (name === "String")
|
|
3214
3448
|
return lowerer.ensureString(arg, argNode);
|
|
3215
|
-
if (name === "Number" && arg.type.kind === "bigint") {
|
|
3216
|
-
return { kind: "libCall", fn: "bigint.toF64", args: [arg], type: F64, loc };
|
|
3217
|
-
}
|
|
3218
|
-
if (arg.type.kind === "f64")
|
|
3219
|
-
return arg;
|
|
3220
|
-
if (arg.type.kind === "bool") {
|
|
3221
|
-
return {
|
|
3222
|
-
kind: "ternary",
|
|
3223
|
-
cond: arg,
|
|
3224
|
-
then: { kind: "numLit", value: 1, type: F64, loc },
|
|
3225
|
-
else_: { kind: "numLit", value: 0, type: F64, loc },
|
|
3226
|
-
type: F64,
|
|
3227
|
-
loc,
|
|
3228
|
-
};
|
|
3229
|
-
}
|
|
3230
|
-
if (arg.type.kind === "string") {
|
|
3231
|
-
return { kind: "libCall", fn: "num.fromString", args: [arg], type: F64, loc };
|
|
3232
|
-
}
|
|
3233
|
-
const optionalNumber = lowerOptionalStringNumber(lowerer, arg, loc);
|
|
3234
|
-
if (optionalNumber)
|
|
3235
|
-
return optionalNumber;
|
|
3236
|
-
lowerer.noLowering(`Number of ${lowerer.fmt(arg.type)} values`, argNode, arg.type.kind === "union"
|
|
3237
|
-
? "numbers, booleans, and strings lower (the full ToNumber string grammar included) — narrow the union first"
|
|
3238
|
-
: undefined);
|
|
3239
3449
|
}
|
|
3240
3450
|
// __island_eval: the internal island testing hook (eval in the embedded
|
|
3241
3451
|
// engine, String(result) back). Provenance-checked like setTimeout.
|
|
@@ -3371,15 +3581,21 @@ export function lowerCall(lowerer, expr) {
|
|
|
3371
3581
|
// supported form is fenced here per site. Members with no lowering at
|
|
3372
3582
|
// all (fs.watch, os.cpus, ...) fence with the module-qualified name.
|
|
3373
3583
|
if (ts.isIdentifier(expr.expression)) {
|
|
3374
|
-
// A call through a
|
|
3375
|
-
//
|
|
3376
|
-
//
|
|
3377
|
-
// already-settled promise).
|
|
3584
|
+
// A call through a compile-time util.promisify projection. execFile
|
|
3585
|
+
// keeps its custom result helper; ordinary builtins route through
|
|
3586
|
+
// their existing promise-module lowering.
|
|
3378
3587
|
{
|
|
3379
3588
|
const sym = lowerer.resolveValueSymbol(expr.expression);
|
|
3380
|
-
|
|
3589
|
+
const projection = sym ? lowerer.staticCallables.get(sym) : undefined;
|
|
3590
|
+
if (projection?.kind === "promisified-exec-file") {
|
|
3381
3591
|
return lowerer.lowerExecFileAsyncCall(expr, loc);
|
|
3382
3592
|
}
|
|
3593
|
+
if (projection?.kind === "promisified-builtin") {
|
|
3594
|
+
return lowerProjectedBuiltinCall(lowerer, expr, { module: projection.module, member: projection.member }, loc);
|
|
3595
|
+
}
|
|
3596
|
+
if (projection?.kind === "builtin-function") {
|
|
3597
|
+
return lowerProjectedBuiltinCall(lowerer, expr, { module: projection.module, member: projection.member }, loc);
|
|
3598
|
+
}
|
|
3383
3599
|
// A call through a `const requestFn = tls ? https.request :
|
|
3384
3600
|
// http.request` binding (the client-function ternary): the http
|
|
3385
3601
|
// client lowering with the RUNTIME-secure dial.
|
|
@@ -3389,6 +3605,9 @@ export function lowerCall(lowerer, expr) {
|
|
|
3389
3605
|
}
|
|
3390
3606
|
const bi = lowerer.builtinImportOf(expr.expression);
|
|
3391
3607
|
if (bi) {
|
|
3608
|
+
if (bi.module === "child_process" && bi.member === "execFile") {
|
|
3609
|
+
return lowerer.lowerExecFileCall(expr, loc);
|
|
3610
|
+
}
|
|
3392
3611
|
// The timers spoke: the node:timers module's exports ARE the
|
|
3393
3612
|
// timer globals (Node re-exports them), so a named/destructured
|
|
3394
3613
|
// import lands on the same shared lowering. Unknown members fall
|
|
@@ -3448,6 +3667,9 @@ export function lowerCall(lowerer, expr) {
|
|
|
3448
3667
|
const timersInterval = lowerer.lowerTimersPromisesSetInterval(expr, bi, loc);
|
|
3449
3668
|
if (timersInterval)
|
|
3450
3669
|
return timersInterval;
|
|
3670
|
+
const nodeModuleServed = lowerNodeModuleCall(lowerer, expr, bi, loc);
|
|
3671
|
+
if (nodeModuleServed)
|
|
3672
|
+
return nodeModuleServed;
|
|
3451
3673
|
const builtinFn = builtinModuleFnOf(lowerer, bi.module, bi.member);
|
|
3452
3674
|
if (!builtinFn) {
|
|
3453
3675
|
// Typed by @types/node (the fallback declarations only declare
|
|
@@ -3754,7 +3976,7 @@ export function lowerCall(lowerer, expr) {
|
|
|
3754
3976
|
if (generic && !(jsSpreadArgs && generic.implicitParams))
|
|
3755
3977
|
return lowerer.lowerGenericCall(expr, generic);
|
|
3756
3978
|
const sig = generic ? null : lowerer.fnSigOf(expr.expression);
|
|
3757
|
-
if (sig && !(jsSpreadArgs && spreadNeedsRuntimeArity(sig.params, expr.arguments))) {
|
|
3979
|
+
if (sig && !(jsSpreadArgs && spreadNeedsRuntimeArity(lowerer, sig.params, expr.arguments))) {
|
|
3758
3980
|
lowerer.noteEdge(sig.name);
|
|
3759
3981
|
const args = lowerer.completeArgs(expr.arguments, sig.params, loc, expr);
|
|
3760
3982
|
return reconcileOverloadReturn(lowerer, expr, { kind: "call", callee: sig.name, args, type: sig.returnType, loc });
|
|
@@ -3796,15 +4018,7 @@ export function lowerCall(lowerer, expr) {
|
|
|
3796
4018
|
if (callee) {
|
|
3797
4019
|
if (callee.type.kind !== "func")
|
|
3798
4020
|
lowerer.badType(expr.expression, lowerer.typeOf(expr.expression));
|
|
3799
|
-
const
|
|
3800
|
-
const args = expr.arguments.map((a, i) => lowerer.lowerExprExpecting(a, params[i]));
|
|
3801
|
-
for (let i = args.length; i < params.length; i++) {
|
|
3802
|
-
const absent = omittedArgFor(lowerer, params[i], loc);
|
|
3803
|
-
if (!absent) {
|
|
3804
|
-
lowerer.unsupported("SC1090", expr, "calls omitting a non-optional parameter of the callee's type");
|
|
3805
|
-
}
|
|
3806
|
-
args.push(absent);
|
|
3807
|
-
}
|
|
4021
|
+
const args = completeFuncValueArgs(lowerer, expr, callee.type, loc);
|
|
3808
4022
|
return { kind: "callValue", callee, args, type: callee.type.ret, loc };
|
|
3809
4023
|
}
|
|
3810
4024
|
}
|
|
@@ -3844,15 +4058,7 @@ export function lowerCall(lowerer, expr) {
|
|
|
3844
4058
|
callee = lowerer.hybridCallUnwrap(callee);
|
|
3845
4059
|
if (callee.type.kind !== "func")
|
|
3846
4060
|
lowerer.badType(expr.expression, lowerer.typeOf(expr.expression));
|
|
3847
|
-
const
|
|
3848
|
-
const args = expr.arguments.map((a, i) => lowerer.lowerExprExpecting(a, params[i]));
|
|
3849
|
-
for (let i = args.length; i < params.length; i++) {
|
|
3850
|
-
const absent = omittedArgFor(lowerer, params[i], loc);
|
|
3851
|
-
if (!absent) {
|
|
3852
|
-
lowerer.unsupported("SC1090", expr, "calls omitting a non-optional parameter of the callee's type");
|
|
3853
|
-
}
|
|
3854
|
-
args.push(absent);
|
|
3855
|
-
}
|
|
4061
|
+
const args = completeFuncValueArgs(lowerer, expr, callee.type, loc);
|
|
3856
4062
|
return { kind: "callValue", callee, args, type: callee.type.ret, loc };
|
|
3857
4063
|
}
|
|
3858
4064
|
// An AMBIENT namespace callee (`M.f()` where only `declare
|
|
@@ -3905,15 +4111,7 @@ export function lowerCall(lowerer, expr) {
|
|
|
3905
4111
|
callee = lowerer.hybridCallUnwrap(callee);
|
|
3906
4112
|
if (callee.type.kind !== "func")
|
|
3907
4113
|
lowerer.badType(expr.expression, lowerer.typeOf(expr.expression));
|
|
3908
|
-
const
|
|
3909
|
-
const args = expr.arguments.map((a, i) => lowerer.lowerExprExpecting(a, params[i]));
|
|
3910
|
-
for (let i = args.length; i < params.length; i++) {
|
|
3911
|
-
const absent = omittedArgFor(lowerer, params[i], loc);
|
|
3912
|
-
if (!absent) {
|
|
3913
|
-
lowerer.unsupported("SC1090", expr, "calls omitting a non-optional parameter of the callee's type");
|
|
3914
|
-
}
|
|
3915
|
-
args.push(absent);
|
|
3916
|
-
}
|
|
4114
|
+
const args = completeFuncValueArgs(lowerer, expr, callee.type, loc);
|
|
3917
4115
|
return { kind: "callValue", callee, args, type: callee.type.ret, loc };
|
|
3918
4116
|
}
|
|
3919
4117
|
if (ts.isPropertyAccessExpression(expr.expression)) {
|
|
@@ -3990,11 +4188,12 @@ export function lowerCall(lowerer, expr) {
|
|
|
3990
4188
|
// TestContext surface (t.test/t.skip/t.diagnostic), t.assert.*.
|
|
3991
4189
|
lowerer.lowerTestMethodCall(expr, expr.expression) ??
|
|
3992
4190
|
lowerer.lowerTimeoutMethodCall(expr, expr.expression) ??
|
|
4191
|
+
lowerStringPrototypeCall(lowerer, expr, expr.expression) ??
|
|
3993
4192
|
lowerStringMethodCallWithOptionalArgs(lowerer, expr, expr.expression) ??
|
|
3994
4193
|
// Typed-array/Buffer receivers and the Buffer statics — before the
|
|
3995
4194
|
// island path (bytes never cross the boundary).
|
|
3996
4195
|
lowerer.lowerBytesMethodCall(expr, expr.expression) ??
|
|
3997
|
-
|
|
4196
|
+
lowerBufferStaticCallWithNarrowedArg(lowerer, expr, expr.expression) ??
|
|
3998
4197
|
// URL.revokeObjectURL's zero-argument contract (the one-argument
|
|
3999
4198
|
// form keeps the fence — createObjectURL does too).
|
|
4000
4199
|
lowerUrlStaticCall(lowerer, expr, expr.expression) ??
|
|
@@ -4187,6 +4386,14 @@ export function lowerCall(lowerer, expr) {
|
|
|
4187
4386
|
if (callee.type.kind !== "func") {
|
|
4188
4387
|
lowerer.badType(expr.expression, lowerer.typeOf(expr.expression));
|
|
4189
4388
|
}
|
|
4389
|
+
// Typed-rest closures already have a fixed native ABI: complete the
|
|
4390
|
+
// source-level variadic call into their trailing typed-array slot.
|
|
4391
|
+
// Claim before the runtime-arity spread lane; completeArgs handles
|
|
4392
|
+
// typed spreads without boxing the function.
|
|
4393
|
+
if (callee.type.rest === true && callee.type.restAbi === "typed") {
|
|
4394
|
+
const args = completeFuncValueArgs(lowerer, expr, callee.type, loc);
|
|
4395
|
+
return { kind: "callValue", callee, args, type: callee.type.ret, loc };
|
|
4396
|
+
}
|
|
4190
4397
|
// A SPREAD argument on a func-typed callee — the rest-forwarding
|
|
4191
4398
|
// idiom (`(...args) => from(...args)`): the runtime-arity lane boxes
|
|
4192
4399
|
// or marshals the callee and applies through a runtime-built argument
|
|
@@ -4238,26 +4445,7 @@ export function lowerCall(lowerer, expr) {
|
|
|
4238
4445
|
const boxed = { kind: "dynFrom", value: callee, type: DYN, loc };
|
|
4239
4446
|
return { kind: "dynCall", callee: boxed, calleeName, args, type: DYN, loc };
|
|
4240
4447
|
}
|
|
4241
|
-
const
|
|
4242
|
-
const args = expr.arguments.map((a, i) => lowerer.lowerExprExpecting(a, params[i]));
|
|
4243
|
-
// Optional-param func TYPES map their `x?: T` slots as `T | undefined`
|
|
4244
|
-
// ABI unions, and tsc admits calls that omit the optional suffix —
|
|
4245
|
-
// complete the missing trailing args with the interned undefined arm,
|
|
4246
|
-
// exactly what completeArgs does for direct calls (the ABI stays
|
|
4247
|
-
// count-exact). A missing arg whose param has no undefined arm means
|
|
4248
|
-
// the callee value's type spelled a required param tsc let the caller
|
|
4249
|
-
// skip — not a shape this surface models; fence.
|
|
4250
|
-
for (let i = args.length; i < params.length; i++) {
|
|
4251
|
-
// A missing argument completes with the slot's absent value — the
|
|
4252
|
-
// interned undefined arm, the dyn undefined for checked-dynamic
|
|
4253
|
-
// slots (a JS-inferred wrapper like mustCall's, called short), or
|
|
4254
|
-
// the engine undefined for island slots.
|
|
4255
|
-
const absent = omittedArgFor(lowerer, params[i], loc);
|
|
4256
|
-
if (!absent) {
|
|
4257
|
-
lowerer.unsupported("SC1090", expr, "calls omitting a non-optional parameter of the callee's type");
|
|
4258
|
-
}
|
|
4259
|
-
args.push(absent);
|
|
4260
|
-
}
|
|
4448
|
+
const args = completeFuncValueArgs(lowerer, expr, callee.type, loc);
|
|
4261
4449
|
return { kind: "callValue", callee, args, type: callee.type.ret, loc };
|
|
4262
4450
|
}
|
|
4263
4451
|
function lowerRegexMethodCallWithOptionalArg(lowerer, call, access) {
|
|
@@ -4280,22 +4468,29 @@ function lowerRegexMethodCallWithOptionalArg(lowerer, call, access) {
|
|
|
4280
4468
|
function optionalCallValue(lowerer, node) {
|
|
4281
4469
|
return lowerer.runtimeOptionalIdentifierValue(node)?.value ?? lowerAbsenceProbe(lowerer, node);
|
|
4282
4470
|
}
|
|
4283
|
-
function
|
|
4471
|
+
function lowerBufferStaticCallWithNarrowedArg(lowerer, call, access) {
|
|
4284
4472
|
const lowered = lowerer.lowerBufferStaticCall(call, access);
|
|
4285
4473
|
if (lowered?.kind !== "bytesNew" || !lowered.source || lowered.source.type.kind !== "union")
|
|
4286
4474
|
return lowered;
|
|
4287
|
-
|
|
4288
|
-
|
|
4289
|
-
|
|
4290
|
-
|
|
4291
|
-
|
|
4292
|
-
|
|
4293
|
-
|
|
4294
|
-
|
|
4295
|
-
|
|
4296
|
-
|
|
4297
|
-
|
|
4298
|
-
|
|
4475
|
+
// A callback can store a wider union than the checker sees at this use
|
|
4476
|
+
// (including an added undefined arm). Extract the exact arm proven by
|
|
4477
|
+
// control-flow narrowing, not merely the union with undefined removed.
|
|
4478
|
+
const narrowed = lowerer.mapTypeOf(lowerer.typeOf(call.arguments[0]));
|
|
4479
|
+
if (!narrowed ||
|
|
4480
|
+
!(narrowed.kind === "f64" ||
|
|
4481
|
+
(narrowed.kind === "array" && narrowed.elem.kind === "f64") ||
|
|
4482
|
+
(narrowed.kind === "bytes" && narrowed.elem === "u8")) ||
|
|
4483
|
+
lowerer.armTag(lowered.source.type.unionId, narrowed) < 0) {
|
|
4484
|
+
lowerer.unsupported("SC1090", call.arguments[0], "a Buffer constructor argument whose narrowed type is not a stored source arm");
|
|
4485
|
+
}
|
|
4486
|
+
const helper = lowerer.narrowedArmHelper(lowered.source.type.unionId, narrowed, lowered.source.loc);
|
|
4487
|
+
if (!helper) {
|
|
4488
|
+
lowerer.unsupported("SC1090", call.arguments[0], "a Buffer constructor argument whose narrowed type is not a stored source arm");
|
|
4489
|
+
}
|
|
4490
|
+
return {
|
|
4491
|
+
...lowered,
|
|
4492
|
+
source: { kind: "call", callee: helper, args: [lowered.source], type: narrowed, loc: lowered.source.loc },
|
|
4493
|
+
};
|
|
4299
4494
|
}
|
|
4300
4495
|
function lowerOptionalNumberDefault(lowerer, value, dflt, loc) {
|
|
4301
4496
|
const widened = lowerer.runtimeOptionalWidening(value.type, F64);
|
|
@@ -4335,14 +4530,14 @@ function lowerOptionalNumberDefault(lowerer, value, dflt, loc) {
|
|
|
4335
4530
|
}
|
|
4336
4531
|
return { kind: "call", callee: helper, args: [value], type: F64, loc };
|
|
4337
4532
|
}
|
|
4338
|
-
function lowerStringMethodCallWithOptionalArgs(lowerer, call, access) {
|
|
4339
|
-
const lowered =
|
|
4533
|
+
function lowerStringMethodCallWithOptionalArgs(lowerer, call, access, receiver, argumentNodes = call.arguments) {
|
|
4534
|
+
const lowered = lowerStringMethodCall(lowerer, call, access, receiver, argumentNodes);
|
|
4340
4535
|
if (lowered?.kind !== "strIntrinsic")
|
|
4341
4536
|
return lowered;
|
|
4342
4537
|
const args = [...lowered.args];
|
|
4343
4538
|
let changed = false;
|
|
4344
4539
|
const zeroDefaultArgs = lowered.method === "charCodeAt" || lowered.method === "charAt" ||
|
|
4345
|
-
lowered.method === "repeat"
|
|
4540
|
+
lowered.method === "repeat"
|
|
4346
4541
|
? [0]
|
|
4347
4542
|
: lowered.method === "slice" || lowered.method === "substring"
|
|
4348
4543
|
? [0]
|
|
@@ -4363,11 +4558,170 @@ function lowerStringMethodCallWithOptionalArgs(lowerer, call, access) {
|
|
|
4363
4558
|
lowered.method === "startsWith" || lowered.method === "endsWith") &&
|
|
4364
4559
|
args[0]?.type.kind === "union" &&
|
|
4365
4560
|
lowerer.runtimeOptionalWidening(args[0].type, STRING) !== null) {
|
|
4366
|
-
args[0] = lowerer.ensureString(args[0],
|
|
4561
|
+
args[0] = lowerer.ensureString(args[0], argumentNodes[0]);
|
|
4367
4562
|
changed = true;
|
|
4368
4563
|
}
|
|
4369
4564
|
return changed ? { ...lowered, args } : lowered;
|
|
4370
4565
|
}
|
|
4566
|
+
function lowerNumberConstructorValue(lowerer, argNode, loc) {
|
|
4567
|
+
const undefinedArg = lowerStaticallyUndefinedArgument(lowerer, argNode);
|
|
4568
|
+
if (undefinedArg) {
|
|
4569
|
+
return defaultAfterUndefined(undefinedArg, {
|
|
4570
|
+
kind: "bin", op: "/", left: { kind: "numLit", value: 0, type: F64, loc },
|
|
4571
|
+
right: { kind: "numLit", value: 0, type: F64, loc }, type: F64, loc,
|
|
4572
|
+
});
|
|
4573
|
+
}
|
|
4574
|
+
const arg = lowerer.lowerExpr(argNode);
|
|
4575
|
+
if (arg.type.kind === "nullT")
|
|
4576
|
+
return defaultAfterUndefined(arg, { kind: "numLit", value: 0, type: F64, loc });
|
|
4577
|
+
if (arg.type.kind === "bigint")
|
|
4578
|
+
return { kind: "libCall", fn: "bigint.toF64", args: [arg], type: F64, loc };
|
|
4579
|
+
if (arg.type.kind === "f64")
|
|
4580
|
+
return arg;
|
|
4581
|
+
if (arg.type.kind === "bool") {
|
|
4582
|
+
return {
|
|
4583
|
+
kind: "ternary",
|
|
4584
|
+
cond: arg,
|
|
4585
|
+
then: { kind: "numLit", value: 1, type: F64, loc },
|
|
4586
|
+
else_: { kind: "numLit", value: 0, type: F64, loc },
|
|
4587
|
+
type: F64,
|
|
4588
|
+
loc,
|
|
4589
|
+
};
|
|
4590
|
+
}
|
|
4591
|
+
if (arg.type.kind === "string")
|
|
4592
|
+
return { kind: "libCall", fn: "num.fromString", args: [arg], type: F64, loc };
|
|
4593
|
+
const optionalNumber = lowerOptionalStringNumber(lowerer, arg, loc);
|
|
4594
|
+
if (optionalNumber)
|
|
4595
|
+
return optionalNumber;
|
|
4596
|
+
const scalarUnionNumber = lowerScalarUnionNumber(lowerer, arg, argNode, loc);
|
|
4597
|
+
if (scalarUnionNumber)
|
|
4598
|
+
return scalarUnionNumber;
|
|
4599
|
+
return lowerer.noLowering(`Number of ${lowerer.fmt(arg.type)} values`, argNode, arg.type.kind === "union"
|
|
4600
|
+
? "unions of numbers, booleans, strings, null, and undefined lower — narrow other arms first"
|
|
4601
|
+
: undefined);
|
|
4602
|
+
}
|
|
4603
|
+
function immediatePrimitiveWrapperToString(lowerer, node) {
|
|
4604
|
+
const stringValue = stringWrapperToString(lowerer, node);
|
|
4605
|
+
if (stringValue)
|
|
4606
|
+
return stringValue;
|
|
4607
|
+
let wrapped = node;
|
|
4608
|
+
while (ts.isParenthesizedExpression(wrapped))
|
|
4609
|
+
wrapped = wrapped.expression;
|
|
4610
|
+
if (!ts.isNewExpression(wrapped) || !ts.isIdentifier(wrapped.expression))
|
|
4611
|
+
return null;
|
|
4612
|
+
const name = wrapped.expression.text;
|
|
4613
|
+
if ((name !== "Boolean" && name !== "Number") ||
|
|
4614
|
+
!lowerer.isStdlibSymbol(lowerer.resolveValueSymbol(wrapped.expression) ?? undefined))
|
|
4615
|
+
return null;
|
|
4616
|
+
const args = wrapped.arguments ?? [];
|
|
4617
|
+
if (args.length > 1 || args.some(ts.isSpreadElement))
|
|
4618
|
+
return null;
|
|
4619
|
+
const loc = locOf(wrapped);
|
|
4620
|
+
const primitive = name === "Boolean"
|
|
4621
|
+
? args.length ? lowerer.lowerCondition(args[0]) : { kind: "boolLit", value: false, type: BOOL, loc }
|
|
4622
|
+
: args.length ? lowerNumberConstructorValue(lowerer, args[0], loc) : { kind: "numLit", value: 0, type: F64, loc };
|
|
4623
|
+
return lowerer.ensureString(primitive, wrapped);
|
|
4624
|
+
}
|
|
4625
|
+
function plainObjectCoercionReceiver(lowerer, node) {
|
|
4626
|
+
const plainLiteral = (literal) => literal.properties.every((prop) => ts.isPropertyAssignment(prop) &&
|
|
4627
|
+
(ts.isIdentifier(prop.name) || ts.isStringLiteral(prop.name) || ts.isNumericLiteral(prop.name)) &&
|
|
4628
|
+
prop.name.text !== "__proto__");
|
|
4629
|
+
let value = node;
|
|
4630
|
+
while (ts.isParenthesizedExpression(value))
|
|
4631
|
+
value = value.expression;
|
|
4632
|
+
if (ts.isObjectLiteralExpression(value))
|
|
4633
|
+
return plainLiteral(value);
|
|
4634
|
+
if (!ts.isIdentifier(value))
|
|
4635
|
+
return false;
|
|
4636
|
+
const symbol = lowerer.resolveValueSymbol(value);
|
|
4637
|
+
const decl = symbol ? lowerer.checker.valueDeclarationOf(symbol) : undefined;
|
|
4638
|
+
if (!symbol || !decl || !ts.isVariableDeclaration(decl) || !decl.initializer ||
|
|
4639
|
+
!ts.isObjectLiteralExpression(decl.initializer) || !plainLiteral(decl.initializer))
|
|
4640
|
+
return false;
|
|
4641
|
+
return bindingNeverReassigned(lowerer, symbol, decl);
|
|
4642
|
+
}
|
|
4643
|
+
function lowerStringPrototypeCall(lowerer, call, access) {
|
|
4644
|
+
if (access.name.text !== "call" || !ts.isPropertyAccessExpression(access.expression))
|
|
4645
|
+
return null;
|
|
4646
|
+
const methodAccess = access.expression;
|
|
4647
|
+
const prototypeAccess = methodAccess.expression;
|
|
4648
|
+
if (!ts.isPropertyAccessExpression(prototypeAccess) || prototypeAccess.name.text !== "prototype" ||
|
|
4649
|
+
!ts.isIdentifier(prototypeAccess.expression) || prototypeAccess.expression.text !== "String" ||
|
|
4650
|
+
!Object.hasOwn(STR_METHODS, methodAccess.name.text) || !lowerer.isStdlibMember(methodAccess))
|
|
4651
|
+
return null;
|
|
4652
|
+
if (call.arguments.some(ts.isSpreadElement))
|
|
4653
|
+
return null;
|
|
4654
|
+
const entry = STR_METHODS[methodAccess.name.text];
|
|
4655
|
+
const method = methodAccess.name.text === "trimStart" ? "trimLeft" :
|
|
4656
|
+
methodAccess.name.text === "trimEnd" ? "trimRight" : methodAccess.name.text;
|
|
4657
|
+
const loc = locOf(call);
|
|
4658
|
+
const nullishError = nodeThrowExpr(1, "", `String.prototype.${method} called on null or undefined`, entry.result, loc);
|
|
4659
|
+
const receiverNode = call.arguments[0];
|
|
4660
|
+
if (!receiverNode)
|
|
4661
|
+
return nullishError;
|
|
4662
|
+
// Receiver coercion runs after .call has evaluated its arguments; keep
|
|
4663
|
+
// contextual wrapper lowering on forms without later method arguments.
|
|
4664
|
+
const immediateWrapper = entry.maxArgs === 0 && call.arguments.length === 1
|
|
4665
|
+
? immediatePrimitiveWrapperToString(lowerer, receiverNode) : null;
|
|
4666
|
+
const receiverValue = immediateWrapper ?? lowerer.lowerExpr(receiverNode);
|
|
4667
|
+
const receiverType = receiverValue.type;
|
|
4668
|
+
const nullish = isUnitType(receiverType) || receiverType.kind === "void" ||
|
|
4669
|
+
(receiverType.kind === "union" && (lowerer.unions.get(receiverType.unionId)?.arms.every((arm) => isUnitType(arm) || arm.kind === "void") ?? false));
|
|
4670
|
+
if (nullish) {
|
|
4671
|
+
const values = [receiverValue, ...call.arguments.slice(1).map((arg) => lowerer.lowerExpr(arg))];
|
|
4672
|
+
return {
|
|
4673
|
+
kind: "seqExpr",
|
|
4674
|
+
stmts: values.filter((value) => !isSafeToDiscard(value)).map((value) => ({ kind: "exprStmt", expr: value, loc: value.loc })),
|
|
4675
|
+
result: nullishError,
|
|
4676
|
+
type: entry.result,
|
|
4677
|
+
loc,
|
|
4678
|
+
};
|
|
4679
|
+
}
|
|
4680
|
+
const padding = entry.method === "padStart" || entry.method === "padEnd";
|
|
4681
|
+
const scalar = receiverType.kind === "string" || receiverType.kind === "f64" ||
|
|
4682
|
+
receiverType.kind === "bool" || receiverType.kind === "bigint" ||
|
|
4683
|
+
(receiverType.kind === "union" && (lowerer.unions.get(receiverType.unionId)?.arms.every((arm) => arm.kind === "string" || arm.kind === "f64" || arm.kind === "bool" || arm.kind === "bigint") ?? false));
|
|
4684
|
+
// Other methods convert object receivers before lowering their arguments;
|
|
4685
|
+
// padding uses a helper that delays conversion until every argument is ready.
|
|
4686
|
+
const objectWithoutMethodArgs = entry.maxArgs === 0 && call.arguments.length === 1 &&
|
|
4687
|
+
(receiverType.kind === "record" || receiverType.kind === "array" || receiverType.kind === "object");
|
|
4688
|
+
const dynObjectWithoutMethodArgs = entry.maxArgs === 0 && call.arguments.length === 1 &&
|
|
4689
|
+
receiverType.kind === "dyn" && plainObjectCoercionReceiver(lowerer, receiverNode);
|
|
4690
|
+
if (!scalar && !objectWithoutMethodArgs && !dynObjectWithoutMethodArgs && !(padding && receiverType.kind === "dyn")) {
|
|
4691
|
+
return lowerer.noLowering(`String.prototype.${methodAccess.name.text}.call with ${lowerer.fmt(receiverType)} receivers`, call);
|
|
4692
|
+
}
|
|
4693
|
+
if (padding) {
|
|
4694
|
+
return lowerStringPaddingCall(lowerer, call, entry.method, receiverValue, receiverNode, call.arguments.slice(1));
|
|
4695
|
+
}
|
|
4696
|
+
const receiver = dynObjectWithoutMethodArgs
|
|
4697
|
+
? { kind: "libCall", fn: "dyn.toStringCoerce", args: [receiverValue], type: STRING, loc }
|
|
4698
|
+
: lowerer.ensureString(receiverValue, receiverNode);
|
|
4699
|
+
return lowerStringMethodCallWithOptionalArgs(lowerer, call, methodAccess, () => receiver, call.arguments.slice(1));
|
|
4700
|
+
}
|
|
4701
|
+
function lowerScalarUnionNumber(lowerer, arg, node, loc) {
|
|
4702
|
+
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)))
|
|
4703
|
+
return null;
|
|
4704
|
+
const key = `number.scalar:${arg.type.unionId}`;
|
|
4705
|
+
let helper = lowerer.widthHelpers.get(key);
|
|
4706
|
+
if (!helper) {
|
|
4707
|
+
helper = `%number.scalar.${lowerer.widthHelpers.size}`;
|
|
4708
|
+
lowerer.widthHelpers.set(key, helper);
|
|
4709
|
+
const value = varRef("value.0", arg.type, loc);
|
|
4710
|
+
const nan = {
|
|
4711
|
+
kind: "bin", op: "/", left: { kind: "numLit", value: 0, type: F64, loc },
|
|
4712
|
+
right: { kind: "numLit", value: 0, type: F64, loc }, type: F64, loc,
|
|
4713
|
+
};
|
|
4714
|
+
lowerer.liftedFns.push({
|
|
4715
|
+
name: helper,
|
|
4716
|
+
params: [{ localId: "value.0", name: "value", type: arg.type }],
|
|
4717
|
+
returnType: F64,
|
|
4718
|
+
locals: [{ id: "value.0", name: "value", type: arg.type, mutable: false }],
|
|
4719
|
+
body: [{ kind: "return", value: positionNumber(lowerer, value, nan, node, "Number argument"), loc }],
|
|
4720
|
+
loc,
|
|
4721
|
+
});
|
|
4722
|
+
}
|
|
4723
|
+
return { kind: "call", callee: helper, args: [arg], type: F64, loc };
|
|
4724
|
+
}
|
|
4371
4725
|
function lowerOptionalStringNumber(lowerer, arg, loc, fn = "num.fromString", label = "number") {
|
|
4372
4726
|
if (arg.type.kind !== "union")
|
|
4373
4727
|
return null;
|
|
@@ -4505,9 +4859,22 @@ function lowerSpreadArgsCall(lowerer, expr, callee, loc) {
|
|
|
4505
4859
|
* (dynRest/islandRest, whose packs are built per-argument): the shapes
|
|
4506
4860
|
* the runtime-arity lane (lowerSpreadArgsCall) serves. Typed `rest`
|
|
4507
4861
|
* slots keep completeArgs' same-element spread packing. */
|
|
4508
|
-
function spreadNeedsRuntimeArity(shapes, argNodes) {
|
|
4862
|
+
function spreadNeedsRuntimeArity(lowerer, shapes, argNodes) {
|
|
4509
4863
|
const restAt = shapes.findIndex((s) => s.mode === "rest" || s.mode === "dynRest" || s.mode === "islandRest");
|
|
4510
|
-
|
|
4864
|
+
let position = 0;
|
|
4865
|
+
for (const arg of argNodes) {
|
|
4866
|
+
if (ts.isSpreadElement(arg)) {
|
|
4867
|
+
const tuple = fixedTupleSpreadInfo(lowerer, arg.expression);
|
|
4868
|
+
if (tuple) {
|
|
4869
|
+
position += tuple.fields.length;
|
|
4870
|
+
continue;
|
|
4871
|
+
}
|
|
4872
|
+
if (restAt < 0 || position < restAt || shapes[restAt].mode !== "rest")
|
|
4873
|
+
return true;
|
|
4874
|
+
}
|
|
4875
|
+
position++;
|
|
4876
|
+
}
|
|
4877
|
+
return false;
|
|
4511
4878
|
}
|
|
4512
4879
|
/** METHOD calls on dyn receivers (`pkg.name.replace(...)`, `rawName.split`,
|
|
4513
4880
|
* `ws.packages.filter(...)` — JSON.parse-derived values): validate the
|
|
@@ -4535,8 +4902,9 @@ function lowerDynReceiverMethodCall(lowerer, call, access) {
|
|
|
4535
4902
|
const anyArray = arrayReceiver &&
|
|
4536
4903
|
((lowerer.checker.getTypeArguments(recvTs)[0]?.flags ?? 0) &
|
|
4537
4904
|
(ts.TypeFlags.Any | ts.TypeFlags.Unknown)) !== 0;
|
|
4905
|
+
const checkerUntyped = (recvTs.flags & (ts.TypeFlags.Any | ts.TypeFlags.Unknown)) !== 0 || anyArray;
|
|
4538
4906
|
let recv;
|
|
4539
|
-
if (
|
|
4907
|
+
if (checkerUntyped) {
|
|
4540
4908
|
recv = lowerer.lowerExpr(access.expression);
|
|
4541
4909
|
}
|
|
4542
4910
|
else {
|
|
@@ -4625,7 +4993,23 @@ function lowerDynReceiverMethodCall(lowerer, call, access) {
|
|
|
4625
4993
|
// member models. Order note: JS reads the callee before evaluating
|
|
4626
4994
|
// arguments — dynKeyGet's undefined-receiver TypeError fires first,
|
|
4627
4995
|
// exactly Node.
|
|
4628
|
-
|
|
4996
|
+
// A checker-typed object whose represented value remains dyn still
|
|
4997
|
+
// has an authored own-member contract. Let that member win even when
|
|
4998
|
+
// its name overlaps a primitive prototype (`colors.bold(...)`): the
|
|
4999
|
+
// source value was compiled from that object shape, while genuinely
|
|
5000
|
+
// untyped/any receivers retain the runtime-kind ambiguity and fence.
|
|
5001
|
+
const ownCallable = !checkerUntyped && (() => {
|
|
5002
|
+
const prop = lowerer.checker.getPropertyOfType(recvTs, access.name.text);
|
|
5003
|
+
if (prop === undefined)
|
|
5004
|
+
return false;
|
|
5005
|
+
// getPropertyOfType includes inherited Object.prototype members.
|
|
5006
|
+
// Those are not stored members of a checked-dynamic object, so
|
|
5007
|
+
// dynKeyGet cannot stand in for JavaScript's prototype lookup. Only
|
|
5008
|
+
// an authored declaration can justify the own-member path.
|
|
5009
|
+
const authored = lowerer.checker.declarationsOf(prop).some((decl) => !lowerer.isStdlibFile(decl.getSourceFile()));
|
|
5010
|
+
return authored && lowerer.checker.getCallSignatures(lowerer.checker.getTypeOfSymbol(prop)).length > 0;
|
|
5011
|
+
})();
|
|
5012
|
+
if (DYN_PROTO_METHOD_NAMES.has(access.name.text) && !ownCallable)
|
|
4629
5013
|
return null;
|
|
4630
5014
|
// Optional forms (`obj.cb?.()`, `obj?.cb()`) belong to the chain
|
|
4631
5015
|
// machinery's short-circuit semantics — not modeled here yet.
|
|
@@ -5557,6 +5941,8 @@ function loweredTemplateStrings(lowerer, expr, cooked, loc) {
|
|
|
5557
5941
|
* same collection lists top-level declarations ride. */
|
|
5558
5942
|
export function isTopLevelFnSymbol(lowerer, ident) {
|
|
5559
5943
|
const symbol = lowerer.resolveValueSymbol(ident);
|
|
5944
|
+
if (symbol && lowerer.staticCallables.get(symbol)?.kind === "declared-function")
|
|
5945
|
+
return true;
|
|
5560
5946
|
const decl = symbol ? lowerer.checker.declarationsOf(symbol)[0] : undefined;
|
|
5561
5947
|
return (!!decl &&
|
|
5562
5948
|
ts.isFunctionDeclaration(decl) &&
|
|
@@ -5596,10 +5982,9 @@ export function lowerNestedFunctionDecl(lowerer, stmt) {
|
|
|
5596
5982
|
return { kind: "assign", localId: local.id, value: init, loc: locOf(stmt) };
|
|
5597
5983
|
}
|
|
5598
5984
|
/** Signature checks + param shapes + IR func type for any lambda-like
|
|
5599
|
-
* node. The func type's params are the ABI types
|
|
5600
|
-
*
|
|
5601
|
-
*
|
|
5602
|
-
* value rule (requireExactArityValue decides who may become a value). */
|
|
5985
|
+
* node. The func type's params are the ABI types: optional/default params
|
|
5986
|
+
* become `T | undefined`, and typed rest becomes one packed-array slot.
|
|
5987
|
+
* requireExactArityValue decides whether that completed ABI may escape. */
|
|
5603
5988
|
export function lambdaSignature(lowerer, node) {
|
|
5604
5989
|
if (!node.body)
|
|
5605
5990
|
lowerer.unsupported("SC1090", node, "function overload signatures");
|
|
@@ -5688,39 +6073,25 @@ export function lambdaSignature(lowerer, node) {
|
|
|
5688
6073
|
ret = VOID;
|
|
5689
6074
|
}
|
|
5690
6075
|
}
|
|
5691
|
-
//
|
|
5692
|
-
//
|
|
5693
|
-
//
|
|
5694
|
-
//
|
|
5695
|
-
|
|
5696
|
-
|
|
5697
|
-
|
|
5698
|
-
// declared params the alias story has no model — the fence says to
|
|
5699
|
-
// use a rest parameter. Arrows never claim it (JS: an arrow's
|
|
5700
|
-
// `arguments` is the enclosing function's).
|
|
5701
|
-
const hasDynRest = shapes.some((s) => s.mode === "dynRest");
|
|
5702
|
-
const hasIslandRest = shapes.some((s) => s.mode === "islandRest");
|
|
5703
|
-
const usesArguments = !hasDynRest &&
|
|
5704
|
-
!ts.isArrowFunction(node) &&
|
|
6076
|
+
// A plain JS function reading `arguments` receives one hidden dyn
|
|
6077
|
+
// array. With named parameters, the array contains every actual
|
|
6078
|
+
// argument and only ES modules are admitted: sloppy-mode arguments
|
|
6079
|
+
// alias named parameters. Arrows inherit the enclosing `arguments`.
|
|
6080
|
+
const hasRest = shapes.some((s) => s.mode === "rest" || s.mode === "dynRest" || s.mode === "islandRest");
|
|
6081
|
+
const usesArguments = !hasRest &&
|
|
6082
|
+
(ts.isFunctionExpression(node) || ts.isFunctionDeclaration(node)) &&
|
|
5705
6083
|
isJsSourceFile(node.getSourceFile()) &&
|
|
5706
6084
|
bodyReadsArguments(node);
|
|
5707
6085
|
if (usesArguments && node.parameters.length > 0) {
|
|
5708
|
-
|
|
6086
|
+
if (!isNodeEsmFile(node.getSourceFile())) {
|
|
6087
|
+
lowerer.unsupported("SC1090", node, "parameterized 'arguments' outside an ES module (sloppy-mode parameter aliases)");
|
|
6088
|
+
}
|
|
6089
|
+
shapes.push({ type: DYN, mode: "arguments" });
|
|
5709
6090
|
}
|
|
5710
|
-
|
|
5711
|
-
|
|
5712
|
-
funcType
|
|
5713
|
-
|
|
5714
|
-
// dynRest is EXCLUDED (the boxed thunk fills the trailing dyn
|
|
5715
|
-
// array — no spelled slot); islandRest is INCLUDED (the trailing
|
|
5716
|
-
// jsval param IS the engine arguments array, the REST host-call
|
|
5717
|
-
// adapter's one uniform shape).
|
|
5718
|
-
params: shapes.filter((s) => s.mode !== "dynRest").map((s) => s.type),
|
|
5719
|
-
ret,
|
|
5720
|
-
...(hasDynRest || usesArguments || hasIslandRest ? { rest: true } : {}),
|
|
5721
|
-
...(hasIslandRest ? { restAbi: "jsval" } : {}),
|
|
5722
|
-
},
|
|
5723
|
-
};
|
|
6091
|
+
const funcType = funcTypeFromParamShapes(shapes, ret);
|
|
6092
|
+
if (usesArguments)
|
|
6093
|
+
funcType.rest = true;
|
|
6094
|
+
return { shapes, funcType };
|
|
5724
6095
|
}
|
|
5725
6096
|
/** Does this function's OWN body read `arguments`? Nested plain functions
|
|
5726
6097
|
* and methods have their own `arguments` (the walk skips them); arrows
|
|
@@ -5753,7 +6124,7 @@ export function bodyReadsArguments(fn) {
|
|
|
5753
6124
|
export function lowerLambda(lowerer, node) {
|
|
5754
6125
|
const loc = locOf(node);
|
|
5755
6126
|
const { shapes, funcType } = lowerer.lambdaSignature(node);
|
|
5756
|
-
// A lambda IS a value: the
|
|
6127
|
+
// A lambda IS a value: the completed-ABI rule applies at birth. The
|
|
5757
6128
|
// contextual (target) type decides — `(x?: number) => void` may flow
|
|
5758
6129
|
// into a slot annotated `(x: number | undefined) => void` (same ABI
|
|
5759
6130
|
// signature), anything else is fenced. Nested function declarations and
|
|
@@ -5794,9 +6165,9 @@ export function lowerLambda(lowerer, node) {
|
|
|
5794
6165
|
lowerer.fnStack.push(fnCtx);
|
|
5795
6166
|
try {
|
|
5796
6167
|
const { params, prologue } = lowerer.declareParams(node.parameters, shapes);
|
|
5797
|
-
// The
|
|
5798
|
-
//
|
|
5799
|
-
//
|
|
6168
|
+
// The synthetic trailing dyn-array param carries `arguments`.
|
|
6169
|
+
// Parameterized functions receive all actual arguments; zero-param
|
|
6170
|
+
// functions receive the same array through their rest ABI.
|
|
5800
6171
|
if (funcType.rest && !shapes.some((s) => s.mode === "dynRest" || s.mode === "islandRest")) {
|
|
5801
6172
|
const argsLocal = lowerer.declareHiddenLocal("%arguments", DYN);
|
|
5802
6173
|
params.push({ localId: argsLocal.id, name: "%arguments", type: DYN });
|
|
@@ -5900,7 +6271,7 @@ export function lowerLambda(lowerer, node) {
|
|
|
5900
6271
|
// closure-signature check trips (SC9001). Island rest types SPELL
|
|
5901
6272
|
// their trailing engine-array param, so funcType.params already
|
|
5902
6273
|
// covers those.
|
|
5903
|
-
if (funcType.rest === true && funcType.restAbi
|
|
6274
|
+
if (funcType.rest === true && funcType.restAbi === undefined) {
|
|
5904
6275
|
params.push({ localId: "%pfrest", name: "%pfrest", type: DYN });
|
|
5905
6276
|
}
|
|
5906
6277
|
const fence = lowerer.deferToRuntimeFence(diagsBefore, node, {
|
|
@@ -5955,13 +6326,16 @@ export function lowerLambda(lowerer, node) {
|
|
|
5955
6326
|
* to a promise-of-jsval local or module global answers the binding's
|
|
5956
6327
|
* type. Null everywhere else. */
|
|
5957
6328
|
function islandPromiseStorageTypeOf(lowerer, e) {
|
|
5958
|
-
const direct =
|
|
6329
|
+
const direct = lowerer.dynamic
|
|
6330
|
+
? importCallHandleType(e)
|
|
6331
|
+
: staticImportNamespaceType(lowerer, e);
|
|
5959
6332
|
if (direct?.kind === "promise")
|
|
5960
6333
|
return direct;
|
|
5961
6334
|
if (!ts.isIdentifier(e))
|
|
5962
6335
|
return null;
|
|
5963
6336
|
const local = lowerer.resolveLocal(e);
|
|
5964
|
-
if (local?.type.kind === "promise" &&
|
|
6337
|
+
if (local?.type.kind === "promise" &&
|
|
6338
|
+
(local.type.inner.kind === "jsval" || local.type.inner.kind === "moduleNs"))
|
|
5965
6339
|
return local.type;
|
|
5966
6340
|
if (local)
|
|
5967
6341
|
return null;
|
|
@@ -5969,7 +6343,8 @@ function islandPromiseStorageTypeOf(lowerer, e) {
|
|
|
5969
6343
|
if (sym && sym.flags & ts.SymbolFlags.Alias)
|
|
5970
6344
|
sym = lowerer.checker.getAliasedSymbol(sym);
|
|
5971
6345
|
const g = sym ? lowerer.globalsBySymbol.get(sym) : undefined;
|
|
5972
|
-
if (g?.type.kind === "promise" &&
|
|
6346
|
+
if (g?.type.kind === "promise" &&
|
|
6347
|
+
(g.type.inner.kind === "jsval" || g.type.inner.kind === "moduleNs"))
|
|
5973
6348
|
return g.type;
|
|
5974
6349
|
return null;
|
|
5975
6350
|
}
|
|
@@ -5989,6 +6364,18 @@ function markJsvalHandlerParams(lowerer, handler) {
|
|
|
5989
6364
|
}
|
|
5990
6365
|
}
|
|
5991
6366
|
}
|
|
6367
|
+
function markModuleNsHandlerParams(lowerer, handler, type) {
|
|
6368
|
+
let e = handler;
|
|
6369
|
+
while (ts.isParenthesizedExpression(e))
|
|
6370
|
+
e = e.expression;
|
|
6371
|
+
if (!ts.isArrowFunction(e) && !ts.isFunctionExpression(e))
|
|
6372
|
+
return;
|
|
6373
|
+
for (const p of e.parameters) {
|
|
6374
|
+
if (ts.isIdentifier(p.name) && !p.dotDotDotToken && !p.initializer) {
|
|
6375
|
+
lowerer.moduleNsParamOverrides.set(p, type);
|
|
6376
|
+
}
|
|
6377
|
+
}
|
|
6378
|
+
}
|
|
5992
6379
|
export function lowerPromiseMethodCall(lowerer, call, access) {
|
|
5993
6380
|
if (call.questionDotToken || access.questionDotToken)
|
|
5994
6381
|
return null;
|
|
@@ -6002,7 +6389,7 @@ export function lowerPromiseMethodCall(lowerer, call, access) {
|
|
|
6002
6389
|
// (importCallHandleType / the island-HANDLE var rules), so the storage
|
|
6003
6390
|
// type is the receiver's truth. Direct `import("./m").then(...)`
|
|
6004
6391
|
// spells the same promise with no binding at all.
|
|
6005
|
-
if (!recvT
|
|
6392
|
+
if (!recvT)
|
|
6006
6393
|
recvT = islandPromiseStorageTypeOf(lowerer, access.expression);
|
|
6007
6394
|
if (recvT?.kind !== "promise")
|
|
6008
6395
|
return null;
|
|
@@ -6107,6 +6494,8 @@ export function lowerPromiseMethodCall(lowerer, call, access) {
|
|
|
6107
6494
|
// the handle is the value's only story, isIslandExpr's local rule).
|
|
6108
6495
|
if (inner.kind === "jsval")
|
|
6109
6496
|
markJsvalHandlerParams(lowerer, call.arguments[0]);
|
|
6497
|
+
if (inner.kind === "moduleNs")
|
|
6498
|
+
markModuleNsHandlerParams(lowerer, call.arguments[0], inner);
|
|
6110
6499
|
let cb = lowerer.lowerExpr(call.arguments[0]);
|
|
6111
6500
|
// A TYPED handler on a DYN-settling promise (the tracePromise
|
|
6112
6501
|
// result's `.then((value) => ...)` — the checker's generic
|
|
@@ -6860,9 +7249,10 @@ function lowerBigIntMethodCall(lowerer, call, access) {
|
|
|
6860
7249
|
if (name === "valueOf" && call.arguments.length === 0)
|
|
6861
7250
|
return receiver;
|
|
6862
7251
|
if (name === "toString" && call.arguments.length <= 1) {
|
|
7252
|
+
const defaultRadix = { kind: "numLit", value: 10, type: F64, loc };
|
|
6863
7253
|
const radix = call.arguments[0]
|
|
6864
|
-
? lowerer
|
|
6865
|
-
:
|
|
7254
|
+
? lowerOptionalArgument(lowerer, call.arguments[0], F64, defaultRadix)
|
|
7255
|
+
: defaultRadix;
|
|
6866
7256
|
return { kind: "libCall", fn: "bigint.toString", args: [receiver, radix], type: STRING, loc };
|
|
6867
7257
|
}
|
|
6868
7258
|
return null;
|
|
@@ -7607,6 +7997,48 @@ function lowerObjectStaticCall(lowerer, call, access) {
|
|
|
7607
7997
|
if (call.arguments.length !== 1 || ts.isSpreadElement(call.arguments[0]))
|
|
7608
7998
|
return null;
|
|
7609
7999
|
const argNode = call.arguments[0];
|
|
8000
|
+
// A compiled PROGRAM module namespace is a nominal token whose members
|
|
8001
|
+
// remain live bindings, but its enumerable key set is static and Node
|
|
8002
|
+
// sorts it in code-unit order. Materialize exactly those value-export
|
|
8003
|
+
// names for Object.keys; values/entries would need a heterogeneous live
|
|
8004
|
+
// view and stay explicitly fenced. Builtin namespaces also stay fenced:
|
|
8005
|
+
// their exact runtime export set is Node-owned, not the ambient subset.
|
|
8006
|
+
{
|
|
8007
|
+
const probed = tryLowerExpression(lowerer, argNode);
|
|
8008
|
+
if (probed?.type.kind === "moduleNs") {
|
|
8009
|
+
if (member !== "keys") {
|
|
8010
|
+
lowerer.noLowering(`Object.${member} over a module namespace`, call, "read the named exports directly (module namespace values are live and may have heterogeneous representations)");
|
|
8011
|
+
}
|
|
8012
|
+
const source = lowerer.sourceFileOfModuleNamespace(probed.type);
|
|
8013
|
+
if (source === null) {
|
|
8014
|
+
lowerer.noLowering("Object.keys over a builtin module namespace", call, "access the builtin's named exports directly (the exact runtime key census is Node-owned)");
|
|
8015
|
+
}
|
|
8016
|
+
const moduleSymbol = source ? lowerer.checker.getSymbolAtLocation(source) : undefined;
|
|
8017
|
+
const names = [];
|
|
8018
|
+
moduleSymbol?.getExports().forEach((symbol, key) => {
|
|
8019
|
+
const name = String(key);
|
|
8020
|
+
if (name.startsWith("__") || name === "export=")
|
|
8021
|
+
return;
|
|
8022
|
+
const target = symbol.flags & ts.SymbolFlags.Alias
|
|
8023
|
+
? lowerer.checker.getAliasedSymbol(symbol)
|
|
8024
|
+
: symbol;
|
|
8025
|
+
if (target.flags & ts.SymbolFlags.Value)
|
|
8026
|
+
names.push(name);
|
|
8027
|
+
});
|
|
8028
|
+
names.sort();
|
|
8029
|
+
const loc = locOf(call);
|
|
8030
|
+
const result = {
|
|
8031
|
+
kind: "arrayLit",
|
|
8032
|
+
elems: names.map((name) => ({ kind: "strLit", value: name, type: STRING, loc })),
|
|
8033
|
+
type: arrayOf(STRING),
|
|
8034
|
+
loc,
|
|
8035
|
+
};
|
|
8036
|
+
const receiver = lowerer.lowerExpr(argNode);
|
|
8037
|
+
return isSafeToDiscard(receiver)
|
|
8038
|
+
? result
|
|
8039
|
+
: { kind: "seqExpr", stmts: [{ kind: "exprStmt", expr: receiver, loc }], result, type: result.type, loc };
|
|
8040
|
+
}
|
|
8041
|
+
}
|
|
7610
8042
|
// A CHECKED-DYNAMIC argument — the checker may still spell a record
|
|
7611
8043
|
// type (the JS file-scope object-literal identity story stores the
|
|
7612
8044
|
// dyn object), so the LOWERED value's kind is the dispatch: the
|
|
@@ -7838,10 +8270,9 @@ export function lowerFunction(lowerer, decl) {
|
|
|
7838
8270
|
lowerer.fnStack.push(ctx);
|
|
7839
8271
|
try {
|
|
7840
8272
|
const { params, prologue } = lowerer.declareParams(decl.parameters, sig.params);
|
|
7841
|
-
// The synthetic `arguments` slot
|
|
7842
|
-
//
|
|
7843
|
-
|
|
7844
|
-
if (sig.params.length > decl.parameters.length && sig.params[sig.params.length - 1].mode === "dynRest") {
|
|
8273
|
+
// The synthetic `arguments` slot follows the declared parameters;
|
|
8274
|
+
// reads resolve to this dyn array.
|
|
8275
|
+
if (sig.params.length > decl.parameters.length && (sig.params[sig.params.length - 1].mode === "dynRest" || sig.params[sig.params.length - 1].mode === "arguments")) {
|
|
7845
8276
|
const argsLocal = lowerer.declareHiddenLocal("%arguments", DYN);
|
|
7846
8277
|
params.push({ localId: argsLocal.id, name: "%arguments", type: DYN });
|
|
7847
8278
|
ctx.argumentsLocal = argsLocal;
|
|
@@ -7991,8 +8422,7 @@ export function lowerRecordFieldCall(lowerer, call, access) {
|
|
|
7991
8422
|
callee = lowerer.hybridCallUnwrap(callee);
|
|
7992
8423
|
if (callee.type.kind !== "func")
|
|
7993
8424
|
lowerer.badType(access, lowerer.typeOf(access));
|
|
7994
|
-
const
|
|
7995
|
-
const args = call.arguments.map((a, i) => lowerer.lowerExprExpecting(a, params[i]));
|
|
8425
|
+
const args = completeFuncValueArgs(lowerer, call, callee.type, locOf(call));
|
|
7996
8426
|
return { kind: "callValue", callee, args, type: callee.type.ret, loc: locOf(call) };
|
|
7997
8427
|
}
|
|
7998
8428
|
/** The function-like node behind an object-literal generic-method member:
|
|
@@ -8846,6 +9276,12 @@ export function lowerObjectMethodCall(lowerer, call, access) {
|
|
|
8846
9276
|
if (lowerer.chainBlocked(access, call))
|
|
8847
9277
|
return null;
|
|
8848
9278
|
const mappedReceiver = lowerer.mapTypeOf(lowerer.typeOf(access.expression));
|
|
9279
|
+
if (mappedReceiver?.kind === "union") {
|
|
9280
|
+
const dispatched = lowerUnionObjectMethodCall(lowerer, call, access, mappedReceiver) ??
|
|
9281
|
+
lowerUnionObjectDynFieldCall(lowerer, call, access, mappedReceiver);
|
|
9282
|
+
if (dispatched)
|
|
9283
|
+
return dispatched;
|
|
9284
|
+
}
|
|
8849
9285
|
const receiverIr = mappedReceiver?.kind === "object"
|
|
8850
9286
|
? mappedReceiver
|
|
8851
9287
|
: mappedReceiver?.kind === "union"
|
|
@@ -8927,15 +9363,7 @@ export function lowerObjectMethodCall(lowerer, call, access) {
|
|
|
8927
9363
|
}
|
|
8928
9364
|
}
|
|
8929
9365
|
if (callee?.type.kind === "func") {
|
|
8930
|
-
const
|
|
8931
|
-
const args = call.arguments.map((a, i) => lowerer.lowerExprExpecting(a, params[i]));
|
|
8932
|
-
for (let i = args.length; i < params.length; i++) {
|
|
8933
|
-
const absent = omittedArgFor(lowerer, params[i], locOf(call));
|
|
8934
|
-
if (!absent) {
|
|
8935
|
-
lowerer.unsupported("SC1090", call, "calls omitting a non-optional parameter of the callee's type");
|
|
8936
|
-
}
|
|
8937
|
-
args.push(absent);
|
|
8938
|
-
}
|
|
9366
|
+
const args = completeFuncValueArgs(lowerer, call, callee.type, locOf(call));
|
|
8939
9367
|
return { kind: "callValue", callee, args, type: callee.type.ret, loc: locOf(call) };
|
|
8940
9368
|
}
|
|
8941
9369
|
if (callee?.type.kind === "dyn") {
|
|
@@ -8997,4 +9425,205 @@ export function lowerObjectMethodCall(lowerer, call, access) {
|
|
|
8997
9425
|
loc: locOf(call),
|
|
8998
9426
|
});
|
|
8999
9427
|
}
|
|
9428
|
+
/** A method TypeScript proves callable on every arm of an unrelated class
|
|
9429
|
+
* union. Each runtime tag keeps its own direct/virtual dispatch; only the
|
|
9430
|
+
* completed argument ABI must agree. Results flow into the checker-selected
|
|
9431
|
+
* call type through the ordinary wrap/retag/width machinery. */
|
|
9432
|
+
function lowerUnionObjectMethodCall(lowerer, call, access, receiverT) {
|
|
9433
|
+
const def = lowerer.unions.get(receiverT.unionId);
|
|
9434
|
+
if (!def || def.arms.length < 2 || !def.arms.every((arm) => arm.kind === "object"))
|
|
9435
|
+
return null;
|
|
9436
|
+
const method = access.name.text;
|
|
9437
|
+
const plans = [];
|
|
9438
|
+
for (const arm of def.arms) {
|
|
9439
|
+
const info = lowerer.classes.get(arm.className);
|
|
9440
|
+
if (!info) {
|
|
9441
|
+
lowerer.flushDeferredClass(arm.className);
|
|
9442
|
+
return null;
|
|
9443
|
+
}
|
|
9444
|
+
const found = lowerer.findMethodOn(info, method);
|
|
9445
|
+
if (!found)
|
|
9446
|
+
return null;
|
|
9447
|
+
if (found.sig.abstract === true && !lowerer.overrideBelow(info, method))
|
|
9448
|
+
return null;
|
|
9449
|
+
plans.push({ arm, info, found });
|
|
9450
|
+
}
|
|
9451
|
+
const shapes = plans[0].found.sig.params;
|
|
9452
|
+
if (!plans.every((plan) => paramAbisEqual(shapes, plan.found.sig.params)))
|
|
9453
|
+
return null;
|
|
9454
|
+
const resultT = lowerer.mapTypeOf(lowerer.typeOf(call));
|
|
9455
|
+
if (!resultT || isUnitType(resultT))
|
|
9456
|
+
return null;
|
|
9457
|
+
const loc = locOf(call);
|
|
9458
|
+
const receiver = lowerer.coerceInto(access.expression, lowerer.lowerExpr(access.expression), receiverT);
|
|
9459
|
+
const args = lowerer.completeArgs(call.arguments, shapes, loc, call);
|
|
9460
|
+
const helper = unionObjectMethodHelper(lowerer, call, receiverT, method, plans, shapes, resultT, loc);
|
|
9461
|
+
return { kind: "call", callee: helper, args: [receiver, ...args], type: resultT, loc };
|
|
9462
|
+
}
|
|
9463
|
+
function paramAbisEqual(left, right) {
|
|
9464
|
+
return left.length === right.length && left.every((shape, i) => {
|
|
9465
|
+
const other = right[i];
|
|
9466
|
+
return other !== undefined && shape.mode === other.mode && typeEquals(shape.type, other.type);
|
|
9467
|
+
});
|
|
9468
|
+
}
|
|
9469
|
+
/** The JS-class sibling of union method dispatch: every arm stores the
|
|
9470
|
+
* named callable in a checked-dynamic field. A helper selects and retains
|
|
9471
|
+
* the field value before the dynCall evaluates its source arguments, so an
|
|
9472
|
+
* argument that overwrites the field cannot change this invocation. */
|
|
9473
|
+
function lowerUnionObjectDynFieldCall(lowerer, call, access, receiverT) {
|
|
9474
|
+
const def = lowerer.unions.get(receiverT.unionId);
|
|
9475
|
+
if (!def || def.arms.length < 2 || !def.arms.every((arm) => arm.kind === "object"))
|
|
9476
|
+
return null;
|
|
9477
|
+
const field = access.name.text;
|
|
9478
|
+
const plans = [];
|
|
9479
|
+
for (const arm of def.arms) {
|
|
9480
|
+
const info = lowerer.classes.get(arm.className);
|
|
9481
|
+
if (!info || info.fields.get(field)?.kind !== "dyn")
|
|
9482
|
+
return null;
|
|
9483
|
+
plans.push({ arm, info });
|
|
9484
|
+
}
|
|
9485
|
+
if (call.arguments.some(ts.isSpreadElement)) {
|
|
9486
|
+
lowerer.unsupported("SC1090", call, "spread arguments in calls through 'unknown' values");
|
|
9487
|
+
}
|
|
9488
|
+
const loc = locOf(call);
|
|
9489
|
+
const receiver = lowerer.coerceInto(access.expression, lowerer.lowerExpr(access.expression), receiverT);
|
|
9490
|
+
const key = `${receiverT.unionId}:dyn-field:${field}`;
|
|
9491
|
+
let helper = lowerer.unionCallHelpers.get(key);
|
|
9492
|
+
if (!helper) {
|
|
9493
|
+
helper = `%union.call.${lowerer.unionCallHelpers.size}`;
|
|
9494
|
+
lowerer.unionCallHelpers.set(key, helper);
|
|
9495
|
+
const receiverRef = varRef("this.0", receiverT, loc);
|
|
9496
|
+
const branch = (plan) => {
|
|
9497
|
+
const concrete = {
|
|
9498
|
+
kind: "unionNarrow",
|
|
9499
|
+
unionId: receiverT.unionId,
|
|
9500
|
+
tag: lowerer.armTag(receiverT.unionId, plan.arm),
|
|
9501
|
+
value: receiverRef,
|
|
9502
|
+
type: plan.arm,
|
|
9503
|
+
loc,
|
|
9504
|
+
};
|
|
9505
|
+
return [{
|
|
9506
|
+
kind: "return",
|
|
9507
|
+
value: {
|
|
9508
|
+
kind: "fieldGet",
|
|
9509
|
+
obj: concrete,
|
|
9510
|
+
className: plan.info.def.name,
|
|
9511
|
+
field,
|
|
9512
|
+
type: DYN,
|
|
9513
|
+
loc,
|
|
9514
|
+
},
|
|
9515
|
+
loc,
|
|
9516
|
+
}];
|
|
9517
|
+
};
|
|
9518
|
+
let body = branch(plans[plans.length - 1]);
|
|
9519
|
+
for (let i = plans.length - 2; i >= 0; i--) {
|
|
9520
|
+
const plan = plans[i];
|
|
9521
|
+
body = [{
|
|
9522
|
+
kind: "if",
|
|
9523
|
+
cond: {
|
|
9524
|
+
kind: "unionIsTag",
|
|
9525
|
+
unionId: receiverT.unionId,
|
|
9526
|
+
tag: lowerer.armTag(receiverT.unionId, plan.arm),
|
|
9527
|
+
negated: false,
|
|
9528
|
+
value: receiverRef,
|
|
9529
|
+
type: BOOL,
|
|
9530
|
+
loc,
|
|
9531
|
+
},
|
|
9532
|
+
then: branch(plan),
|
|
9533
|
+
else_: body,
|
|
9534
|
+
loc,
|
|
9535
|
+
}];
|
|
9536
|
+
}
|
|
9537
|
+
const params = [
|
|
9538
|
+
{ localId: "this.0", name: "this", type: receiverT },
|
|
9539
|
+
];
|
|
9540
|
+
const locals = params.map((param) => ({ id: param.localId, name: param.name, type: param.type, mutable: false }));
|
|
9541
|
+
lowerer.liftedFns.push({ name: helper, params, returnType: DYN, locals, body, loc });
|
|
9542
|
+
}
|
|
9543
|
+
const callee = { kind: "call", callee: helper, args: [receiver], type: DYN, loc };
|
|
9544
|
+
const args = call.arguments.map((arg) => lowerer.lowerExprExpecting(arg, DYN));
|
|
9545
|
+
return { kind: "dynCall", callee, calleeName: access.getText(), args, type: DYN, loc };
|
|
9546
|
+
}
|
|
9547
|
+
function unionObjectMethodHelper(lowerer, sourceCall, receiverT, method, plans, shapes, resultT, loc) {
|
|
9548
|
+
const key = `${receiverT.unionId}:${method}:${shapes.map((shape) => `${shape.mode}:${typeKey(shape.type)}`).join(",")}:${typeKey(resultT)}`;
|
|
9549
|
+
const existing = lowerer.unionCallHelpers.get(key);
|
|
9550
|
+
if (existing)
|
|
9551
|
+
return existing;
|
|
9552
|
+
const name = `%union.call.${lowerer.unionCallHelpers.size}`;
|
|
9553
|
+
lowerer.unionCallHelpers.set(key, name);
|
|
9554
|
+
const receiver = varRef("this.0", receiverT, loc);
|
|
9555
|
+
const argRefs = shapes.map((shape, i) => varRef(`a.${i}`, shape.type, loc));
|
|
9556
|
+
const branch = (plan) => {
|
|
9557
|
+
const concrete = {
|
|
9558
|
+
kind: "unionNarrow",
|
|
9559
|
+
unionId: receiverT.unionId,
|
|
9560
|
+
tag: lowerer.armTag(receiverT.unionId, plan.arm),
|
|
9561
|
+
value: receiver,
|
|
9562
|
+
type: plan.arm,
|
|
9563
|
+
loc,
|
|
9564
|
+
};
|
|
9565
|
+
let invoke;
|
|
9566
|
+
if (plan.found.declarer.builtinError) {
|
|
9567
|
+
invoke = {
|
|
9568
|
+
kind: "libCall",
|
|
9569
|
+
fn: "error.toString",
|
|
9570
|
+
args: [lowerer.upcastTo(concrete, plan.found.declarer.def.name)],
|
|
9571
|
+
type: STRING,
|
|
9572
|
+
loc,
|
|
9573
|
+
};
|
|
9574
|
+
}
|
|
9575
|
+
else if (lowerer.overrideBelow(plan.info, method)) {
|
|
9576
|
+
lowerer.noteVirtualEdge(plan.info, method);
|
|
9577
|
+
invoke = {
|
|
9578
|
+
kind: "virtualCall",
|
|
9579
|
+
className: plan.info.def.name,
|
|
9580
|
+
method,
|
|
9581
|
+
args: [lowerer.upcastTo(concrete, plan.info.def.name), ...argRefs],
|
|
9582
|
+
type: plan.found.sig.ret,
|
|
9583
|
+
loc,
|
|
9584
|
+
};
|
|
9585
|
+
}
|
|
9586
|
+
else {
|
|
9587
|
+
lowerer.noteEdge(`%${plan.found.declarer.def.name}.${method}`);
|
|
9588
|
+
invoke = {
|
|
9589
|
+
kind: "call",
|
|
9590
|
+
callee: `%${plan.found.declarer.def.name}.${method}`,
|
|
9591
|
+
args: [lowerer.upcastTo(concrete, plan.found.declarer.def.name), ...argRefs],
|
|
9592
|
+
type: plan.found.sig.ret,
|
|
9593
|
+
loc,
|
|
9594
|
+
};
|
|
9595
|
+
}
|
|
9596
|
+
invoke = reconcileOverloadReturn(lowerer, sourceCall, invoke);
|
|
9597
|
+
const result = lowerer.coerceInto(sourceCall, invoke, resultT);
|
|
9598
|
+
return resultT.kind === "void"
|
|
9599
|
+
? [{ kind: "exprStmt", expr: result, loc }, { kind: "return", value: null, loc }]
|
|
9600
|
+
: [{ kind: "return", value: result, loc }];
|
|
9601
|
+
};
|
|
9602
|
+
let body = branch(plans[plans.length - 1]);
|
|
9603
|
+
for (let i = plans.length - 2; i >= 0; i--) {
|
|
9604
|
+
const plan = plans[i];
|
|
9605
|
+
body = [{
|
|
9606
|
+
kind: "if",
|
|
9607
|
+
cond: {
|
|
9608
|
+
kind: "unionIsTag",
|
|
9609
|
+
unionId: receiverT.unionId,
|
|
9610
|
+
tag: lowerer.armTag(receiverT.unionId, plan.arm),
|
|
9611
|
+
negated: false,
|
|
9612
|
+
value: receiver,
|
|
9613
|
+
type: BOOL,
|
|
9614
|
+
loc,
|
|
9615
|
+
},
|
|
9616
|
+
then: branch(plan),
|
|
9617
|
+
else_: body,
|
|
9618
|
+
loc,
|
|
9619
|
+
}];
|
|
9620
|
+
}
|
|
9621
|
+
const params = [
|
|
9622
|
+
{ localId: "this.0", name: "this", type: receiverT },
|
|
9623
|
+
...shapes.map((shape, i) => ({ localId: `a.${i}`, name: `a${i}`, type: shape.type })),
|
|
9624
|
+
];
|
|
9625
|
+
const locals = params.map((param) => ({ id: param.localId, name: param.name, type: param.type, mutable: false }));
|
|
9626
|
+
lowerer.liftedFns.push({ name, params, returnType: resultT, locals, body, loc });
|
|
9627
|
+
return name;
|
|
9628
|
+
}
|
|
9000
9629
|
//# sourceMappingURL=lower-calls.js.map
|