@scriptc/compiler 0.1.5 → 0.1.7
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 +0 -1
- package/ambient/scriptc.d.ts +13 -5
- package/dist/backend/c/c-emitter.d.ts +5 -0
- package/dist/backend/c/c-emitter.js +15 -0
- package/dist/backend/c/c-emitter.js.map +1 -1
- package/dist/backend/c/exprs.js +81 -9
- package/dist/backend/c/exprs.js.map +1 -1
- package/dist/backend/c/stmts.js +25 -1
- package/dist/backend/c/stmts.js.map +1 -1
- package/dist/backend/c/walkers.js +2 -3
- package/dist/backend/c/walkers.js.map +1 -1
- package/dist/backend/debug-symbols.d.ts +8 -0
- package/dist/backend/debug-symbols.js +52 -0
- package/dist/backend/debug-symbols.js.map +1 -0
- package/dist/backend/link-plan.d.ts +1 -0
- package/dist/backend/link-plan.js +2 -0
- package/dist/backend/link-plan.js.map +1 -1
- package/dist/backend/linker.js +8 -0
- package/dist/backend/linker.js.map +1 -1
- package/dist/backend/llvm/blocks.d.ts +2 -0
- package/dist/backend/llvm/blocks.js +18 -2
- package/dist/backend/llvm/blocks.js.map +1 -1
- package/dist/backend/llvm/debug-info.d.ts +32 -0
- package/dist/backend/llvm/debug-info.js +203 -0
- package/dist/backend/llvm/debug-info.js.map +1 -0
- package/dist/backend/llvm/dyn.js +4 -10
- package/dist/backend/llvm/dyn.js.map +1 -1
- package/dist/backend/llvm/emitter.d.ts +2 -0
- package/dist/backend/llvm/emitter.js +49 -4
- package/dist/backend/llvm/emitter.js.map +1 -1
- package/dist/backend/llvm/expr-containers.js +28 -2
- package/dist/backend/llvm/expr-containers.js.map +1 -1
- package/dist/backend/llvm/expr-dynamic.js +10 -0
- package/dist/backend/llvm/expr-dynamic.js.map +1 -1
- package/dist/backend/llvm/lib-filesystem.js +37 -0
- package/dist/backend/llvm/lib-filesystem.js.map +1 -1
- package/dist/backend/llvm/lib-process.js +4 -3
- package/dist/backend/llvm/lib-process.js.map +1 -1
- package/dist/backend/llvm/lib-shared.js +9 -0
- package/dist/backend/llvm/lib-shared.js.map +1 -1
- package/dist/backend/llvm/split.js +4 -1
- package/dist/backend/llvm/split.js.map +1 -1
- package/dist/backend/llvm/split.test.js +26 -1
- package/dist/backend/llvm/split.test.js.map +1 -1
- package/dist/backend/native-toolchain.js +58 -8
- package/dist/backend/native-toolchain.js.map +1 -1
- package/dist/backend/native-toolchain.test.js +10 -0
- package/dist/backend/native-toolchain.test.js.map +1 -1
- package/dist/backend/source-locations.d.ts +12 -0
- package/dist/backend/source-locations.js +32 -0
- package/dist/backend/source-locations.js.map +1 -0
- package/dist/backend/source-locations.test.d.ts +1 -0
- package/dist/backend/source-locations.test.js +16 -0
- package/dist/backend/source-locations.test.js.map +1 -0
- package/dist/coverage/surface-manifest.js +6 -1
- package/dist/coverage/surface-manifest.js.map +1 -1
- package/dist/diagnostics/diagnostic.d.ts +4 -0
- package/dist/diagnostics/diagnostic.js +12 -0
- package/dist/diagnostics/diagnostic.js.map +1 -1
- package/dist/executable/executable-cache.js +28 -4
- package/dist/executable/executable-cache.js.map +1 -1
- package/dist/executable/executable-cache.test.js +23 -0
- package/dist/executable/executable-cache.test.js.map +1 -1
- package/dist/frontend/arguments-usage.d.ts +6 -0
- package/dist/frontend/arguments-usage.js +40 -0
- package/dist/frontend/arguments-usage.js.map +1 -0
- package/dist/frontend/binding-source.d.ts +4 -0
- package/dist/frontend/binding-source.js +18 -0
- package/dist/frontend/binding-source.js.map +1 -0
- package/dist/frontend/lowering/array-indexed-mutation.d.ts +8 -0
- package/dist/frontend/lowering/array-indexed-mutation.js +176 -0
- package/dist/frontend/lowering/array-indexed-mutation.js.map +1 -0
- package/dist/frontend/lowering/containers/string-and-regexp.d.ts +7 -0
- package/dist/frontend/lowering/containers/string-and-regexp.js +190 -13
- package/dist/frontend/lowering/containers/string-and-regexp.js.map +1 -1
- package/dist/frontend/lowering/expressions/object-literals.js +13 -8
- package/dist/frontend/lowering/expressions/object-literals.js.map +1 -1
- package/dist/frontend/lowering/expressions/optional-chains.d.ts +5 -6
- package/dist/frontend/lowering/expressions/optional-chains.js +7 -6
- package/dist/frontend/lowering/expressions/optional-chains.js.map +1 -1
- package/dist/frontend/lowering/indexed-comparison.d.ts +6 -0
- package/dist/frontend/lowering/indexed-comparison.js +30 -0
- package/dist/frontend/lowering/indexed-comparison.js.map +1 -0
- package/dist/frontend/lowering/lower-builtins.d.ts +1 -1
- package/dist/frontend/lowering/lower-builtins.js +21 -10
- package/dist/frontend/lowering/lower-builtins.js.map +1 -1
- package/dist/frontend/lowering/lower-calls.d.ts +2 -9
- package/dist/frontend/lowering/lower-calls.js +406 -40
- package/dist/frontend/lowering/lower-calls.js.map +1 -1
- package/dist/frontend/lowering/lower-classes.js +3 -27
- package/dist/frontend/lowering/lower-classes.js.map +1 -1
- package/dist/frontend/lowering/lower-containers.d.ts +8 -2
- package/dist/frontend/lowering/lower-containers.js +454 -104
- package/dist/frontend/lowering/lower-containers.js.map +1 -1
- package/dist/frontend/lowering/lower-exprs.js +134 -18
- package/dist/frontend/lowering/lower-exprs.js.map +1 -1
- package/dist/frontend/lowering/lower-island.d.ts +1 -1
- package/dist/frontend/lowering/lower-island.js +1 -1
- package/dist/frontend/lowering/lower-modules.js +19 -8
- package/dist/frontend/lowering/lower-modules.js.map +1 -1
- package/dist/frontend/lowering/lower-stmts.js +4 -37
- package/dist/frontend/lowering/lower-stmts.js.map +1 -1
- package/dist/frontend/lowering/lowerer.js +14 -3
- package/dist/frontend/lowering/lowerer.js.map +1 -1
- package/dist/frontend/lowering/surfaces.d.ts +3 -1
- package/dist/frontend/lowering/surfaces.js +50 -7
- package/dist/frontend/lowering/surfaces.js.map +1 -1
- package/dist/frontend/program.d.ts +5 -0
- package/dist/frontend/program.js +216 -32
- package/dist/frontend/program.js.map +1 -1
- package/dist/frontend/program.test.d.ts +1 -0
- package/dist/frontend/program.test.js +117 -0
- package/dist/frontend/program.test.js.map +1 -0
- package/dist/frontend/type-mapper.d.ts +4 -2
- package/dist/frontend/type-mapper.js +249 -186
- package/dist/frontend/type-mapper.js.map +1 -1
- package/dist/frontend/type-mapper.test.d.ts +1 -0
- package/dist/frontend/type-mapper.test.js +86 -0
- package/dist/frontend/type-mapper.test.js.map +1 -0
- package/dist/index.d.ts +2 -1
- package/dist/index.js +7 -2
- package/dist/index.js.map +1 -1
- package/dist/ir/analysis.js +4 -2
- package/dist/ir/analysis.js.map +1 -1
- package/dist/ir/ir.d.ts +33 -14
- package/dist/ir/ir.js +5 -1
- package/dist/ir/ir.js.map +1 -1
- package/dist/ir/validate.js +57 -31
- package/dist/ir/validate.js.map +1 -1
- package/dist/ir/validate.test.js +17 -0
- package/dist/ir/validate.test.js.map +1 -1
- package/dist/library/compiler-self-identity.d.ts +1 -1
- package/dist/library/compiler-self-identity.js +31 -15
- package/dist/library/compiler-self-identity.js.map +1 -1
- package/dist/library/compiler-self-identity.test.d.ts +1 -0
- package/dist/library/compiler-self-identity.test.js +93 -0
- package/dist/library/compiler-self-identity.test.js.map +1 -0
- package/package.json +17 -17
- package/surface-manifest.json +155 -4
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import * as ts from "../ts7/adapter.js";
|
|
2
|
+
import { bodyReadsArguments } from "../arguments-usage.js";
|
|
2
3
|
import type { Lowerer } from "./lowerer.js";
|
|
3
4
|
import { IrExpr, IrFunction, IrStmt, IrType, SrcLoc } from "../../ir/ir.js";
|
|
4
5
|
import type { ScrDiagnostic } from "../../diagnostics/diagnostic.js";
|
|
5
6
|
import { type ClassInfo } from "./lower-classes.js";
|
|
7
|
+
export { bodyReadsArguments };
|
|
6
8
|
/** How a parameter participates in CALL-SITE COMPLETION (the frontend
|
|
7
9
|
* completes every call to the one full signature, so the IR and backends
|
|
8
10
|
* stay count-exact — see docs/ir.md). `required` params must be passed;
|
|
@@ -423,15 +425,6 @@ export declare function lambdaSignature(lowerer: Lowerer, node: ts.ArrowFunction
|
|
|
423
425
|
kind: "func";
|
|
424
426
|
};
|
|
425
427
|
};
|
|
426
|
-
/** Does this function's OWN body read `arguments`? Nested plain functions
|
|
427
|
-
* and methods have their own `arguments` (the walk skips them); arrows
|
|
428
|
-
* see the enclosing one (the walk descends). Exported for the lowerer's
|
|
429
|
-
* dynFallbackType: tsgo does not synthesize the `arguments` rest
|
|
430
|
-
* parameter into inferred signatures (5.9.3 did — its param-count
|
|
431
|
-
* mismatch was the detector), so the 7 world asks the BODY directly. */
|
|
432
|
-
export declare function bodyReadsArguments(fn: {
|
|
433
|
-
body?: ts.Node | undefined;
|
|
434
|
-
}): boolean;
|
|
435
428
|
/** Lifts an arrow function / function expression / nested declaration /
|
|
436
429
|
* object-literal shorthand method to a module-level function and yields
|
|
437
430
|
* the `closure` expression creating it. */
|
|
@@ -4,18 +4,19 @@ import { InternalCompilerError } from "../../errors.js";
|
|
|
4
4
|
* function/lambda lowering and signature collection, and monomorphizing
|
|
5
5
|
* generic instantiation (bounded by MAX_GENERIC_INSTANCES). */
|
|
6
6
|
import * as ts from "../ts7/adapter.js";
|
|
7
|
+
import { bodyReadsArguments } from "../arguments-usage.js";
|
|
7
8
|
import { lowerGenMethodCall } from "./lower-generators.js";
|
|
8
9
|
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
10
|
import { isJsSourceFile, isNodeEsmFile, locOf } from "../program.js";
|
|
10
11
|
import { genResultRecord, isGenericCallableMemberType, typeKey } from "../type-mapper.js";
|
|
11
12
|
import { PoisonError, dynFallbackType, dynUndefinedExpr, importCallHandleType, jsFuncNameOf, newFnCtx, nodeThrowExpr, staticImportNamespaceType } from "./lowerer.js";
|
|
12
13
|
import { enforceLibBoundary } from "./lib-boundary.js";
|
|
13
|
-
import { NARROW_FIRST, STR_METHODS, builtinFenceHintOf, builtinModuleFnOf } from "./surfaces.js";
|
|
14
|
+
import { NARROW_FIRST, STRING_INDEX_METHODS, STR_METHODS, builtinFenceHintOf, builtinModuleFnOf } from "./surfaces.js";
|
|
14
15
|
import { ffiBindingDiag, ffiSignatureDiag, libCallbackDiag, requiresDynamicDiag } from "../../diagnostics/diagnostic.js";
|
|
15
16
|
import { mixinFnShapeOf } from "./lower-mixins.js";
|
|
16
|
-
import { dynStringReceiver, lowerArrayFromCall, lowerDynArrayFilterCall, lowerDynArrayFlatMapCall, lowerGroupByStaticCall, lowerIteratorHelperCall, lowerObjectAssignIndexShape, lowerObjectFromEntriesCall, lowerObjectIterOverIndexShape, lowerTupleReadMethodCall } from "./lower-containers.js";
|
|
17
|
+
import { dynStringReceiver, lowerArrayConstructor, lowerArrayFromCall, lowerArrayOfCall, lowerDynArrayFilterCall, lowerDynArrayFlatMapCall, lowerGroupByStaticCall, lowerIteratorHelperCall, lowerObjectAssignIndexShape, lowerObjectFromEntriesCall, lowerObjectIterOverIndexShape, lowerTupleReadMethodCall } from "./lower-containers.js";
|
|
17
18
|
import { bufEncoding } from "./containers/bytes.js";
|
|
18
|
-
import { lowerRegexMethodCall, lowerStringMethodCall, lowerStringPaddingCall } from "./containers/string-and-regexp.js";
|
|
19
|
+
import { lowerRegexMethodCall, lowerStringIndexCall, lowerStringMethodCall, lowerStringPaddingCall, lowerStringSplitCall } from "./containers/string-and-regexp.js";
|
|
19
20
|
import { lowerChildStreamMethodCall, lowerChildWriterMethodCall, lowerCreateRequireCall, lowerCryptoHashMethodCall, lowerDirentMethodCall, lowerFileHandleMethodCall, lowerImportMetaResolveCall, lowerNodeModuleCall, lowerPerfHooksCall, lowerProcStreamMethodCall, lowerReflectApplyCall, lowerRequireResolveCall, lowerWatcherMethodCall } from "./lower-builtins.js";
|
|
20
21
|
import { lowerAbsenceProbe, lowerPromiseAllTupleCall, lowerPromiseRejectCall, stringWrapperToString, templateRawTextOf } from "./lower-exprs.js";
|
|
21
22
|
import { isSafeToDiscard } from "./expressions/evaluation-safety.js";
|
|
@@ -33,6 +34,7 @@ import { countedFor, varRef } from "../../ir/build.js";
|
|
|
33
34
|
import { rejectStaticThis } from "./static-this.js";
|
|
34
35
|
import { fenceNodeModuleMutationCall, lowerRequireCacheKeys } from "./lower-node-module.js";
|
|
35
36
|
import { defaultAfterUndefined, lowerOptionalArgument, lowerStaticallyUndefinedArgument, positionNumber } from "./optional-arguments.js";
|
|
37
|
+
export { bodyReadsArguments };
|
|
36
38
|
/** The one func-type projection of completed parameter shapes. Typed rest
|
|
37
39
|
* parameters keep their packed array as the trailing native ABI slot and
|
|
38
40
|
* mark the value variadic so indirect call sites run completeArgs before
|
|
@@ -3581,6 +3583,9 @@ export function lowerCall(lowerer, expr) {
|
|
|
3581
3583
|
// supported form is fenced here per site. Members with no lowering at
|
|
3582
3584
|
// all (fs.watch, os.cpus, ...) fence with the module-qualified name.
|
|
3583
3585
|
if (ts.isIdentifier(expr.expression)) {
|
|
3586
|
+
if (expr.expression.text === "Array" && lowerer.isStdlibGlobal(expr.expression, "Array")) {
|
|
3587
|
+
return lowerArrayConstructor(lowerer, expr, expr.arguments);
|
|
3588
|
+
}
|
|
3584
3589
|
// A call through a compile-time util.promisify projection. execFile
|
|
3585
3590
|
// keeps its custom result helper; ordinary builtins route through
|
|
3586
3591
|
// their existing promise-module lowering.
|
|
@@ -4188,6 +4193,8 @@ export function lowerCall(lowerer, expr) {
|
|
|
4188
4193
|
// TestContext surface (t.test/t.skip/t.diagnostic), t.assert.*.
|
|
4189
4194
|
lowerer.lowerTestMethodCall(expr, expr.expression) ??
|
|
4190
4195
|
lowerer.lowerTimeoutMethodCall(expr, expr.expression) ??
|
|
4196
|
+
lowerObjectOwnPrototypeCall(lowerer, expr, expr.expression) ??
|
|
4197
|
+
lowerObjectPrototypeCall(lowerer, expr, expr.expression) ??
|
|
4191
4198
|
lowerStringPrototypeCall(lowerer, expr, expr.expression) ??
|
|
4192
4199
|
lowerStringMethodCallWithOptionalArgs(lowerer, expr, expr.expression) ??
|
|
4193
4200
|
// Typed-array/Buffer receivers and the Buffer statics — before the
|
|
@@ -4217,6 +4224,7 @@ export function lowerCall(lowerer, expr) {
|
|
|
4217
4224
|
// hasOwnProperty on a program class CONSTRUCTOR — own statics are
|
|
4218
4225
|
// compile-time-known, so a literal key folds to a constant.
|
|
4219
4226
|
lowerClassHasOwnPropertyCall(lowerer, expr, expr.expression) ??
|
|
4227
|
+
lowerObjectOwnMethodCall(lowerer, expr, expr.expression) ??
|
|
4220
4228
|
// Response constructor-object operations are unsupported in both
|
|
4221
4229
|
// tiers. Keep their SC2020 inventory contract ahead of the island
|
|
4222
4230
|
// and generic-call fallbacks (Response.json otherwise reports the
|
|
@@ -4257,6 +4265,7 @@ export function lowerCall(lowerer, expr) {
|
|
|
4257
4265
|
lowerObjectStaticCall(lowerer, expr, expr.expression) ??
|
|
4258
4266
|
lowerObjectFromEntriesCall(lowerer, expr, expr.expression) ??
|
|
4259
4267
|
lowerArrayFromCall(lowerer, expr, expr.expression) ??
|
|
4268
|
+
lowerArrayOfCall(lowerer, expr, expr.expression) ??
|
|
4260
4269
|
lowerer.lowerArrayMethodCall(expr, expr.expression) ??
|
|
4261
4270
|
// Read-only array methods (slice/map) on TUPLE receivers — the
|
|
4262
4271
|
// positions snapshot into a fresh array (the for-of stance).
|
|
@@ -4640,6 +4649,243 @@ function plainObjectCoercionReceiver(lowerer, node) {
|
|
|
4640
4649
|
return false;
|
|
4641
4650
|
return bindingNeverReassigned(lowerer, symbol, decl);
|
|
4642
4651
|
}
|
|
4652
|
+
function objectToStringTag(lowerer, type) {
|
|
4653
|
+
switch (type.kind) {
|
|
4654
|
+
case "undefinedT":
|
|
4655
|
+
case "void": return "Undefined";
|
|
4656
|
+
case "nullT": return "Null";
|
|
4657
|
+
case "bool": return "Boolean";
|
|
4658
|
+
case "f64": return "Number";
|
|
4659
|
+
case "string": return "String";
|
|
4660
|
+
case "bigint": return "BigInt";
|
|
4661
|
+
case "symbol": return "Symbol";
|
|
4662
|
+
case "array": return "Array";
|
|
4663
|
+
case "record": return "Object";
|
|
4664
|
+
case "func":
|
|
4665
|
+
case "classval": return "Function";
|
|
4666
|
+
case "date": return "Date";
|
|
4667
|
+
case "regex": return "RegExp";
|
|
4668
|
+
case "map": return "Map";
|
|
4669
|
+
case "set": return "Set";
|
|
4670
|
+
case "promise": return "Promise";
|
|
4671
|
+
case "bytes":
|
|
4672
|
+
return ({ u8: "Uint8Array", u32: "Uint32Array", i32: "Int32Array", f32: "Float32Array", f64: "Float64Array" })[type.elem];
|
|
4673
|
+
case "object":
|
|
4674
|
+
return type.className === "%Error" || lowerer.isSubclassOf(type.className, "%Error") ? "Error" : null;
|
|
4675
|
+
default: return null;
|
|
4676
|
+
}
|
|
4677
|
+
}
|
|
4678
|
+
function immediateObjectTagReceiver(lowerer, node) {
|
|
4679
|
+
let value = node;
|
|
4680
|
+
while (ts.isParenthesizedExpression(value))
|
|
4681
|
+
value = value.expression;
|
|
4682
|
+
const loc = locOf(value);
|
|
4683
|
+
if (ts.isArrayLiteralExpression(value))
|
|
4684
|
+
return { tag: "Array", effect: lowerer.lowerExpr(value) };
|
|
4685
|
+
if ((ts.isCallExpression(value) || ts.isNewExpression(value)) && ts.isIdentifier(value.expression) &&
|
|
4686
|
+
(value.expression.text === "Array" || value.expression.text === "Error") &&
|
|
4687
|
+
(value.arguments?.length ?? 0) === 0 && lowerer.isStdlibSymbol(lowerer.resolveValueSymbol(value.expression) ?? undefined)) {
|
|
4688
|
+
return { tag: value.expression.text, effect: { kind: "numLit", value: 0, type: F64, loc } };
|
|
4689
|
+
}
|
|
4690
|
+
if (ts.isNewExpression(value) && ts.isIdentifier(value.expression) &&
|
|
4691
|
+
lowerer.isStdlibSymbol(lowerer.resolveValueSymbol(value.expression) ?? undefined)) {
|
|
4692
|
+
const name = value.expression.text;
|
|
4693
|
+
const args = value.arguments ?? [];
|
|
4694
|
+
if (args.length <= 1 && !args.some(ts.isSpreadElement)) {
|
|
4695
|
+
if (name === "String") {
|
|
4696
|
+
const effect = stringWrapperToString(lowerer, value);
|
|
4697
|
+
if (effect)
|
|
4698
|
+
return { tag: "String", effect };
|
|
4699
|
+
}
|
|
4700
|
+
if (name === "Number")
|
|
4701
|
+
return {
|
|
4702
|
+
tag: "Number",
|
|
4703
|
+
effect: args.length ? lowerNumberConstructorValue(lowerer, args[0], loc) : { kind: "numLit", value: 0, type: F64, loc },
|
|
4704
|
+
};
|
|
4705
|
+
if (name === "Boolean")
|
|
4706
|
+
return {
|
|
4707
|
+
tag: "Boolean",
|
|
4708
|
+
effect: args.length ? lowerer.lowerCondition(args[0]) : { kind: "boolLit", value: false, type: BOOL, loc },
|
|
4709
|
+
};
|
|
4710
|
+
}
|
|
4711
|
+
}
|
|
4712
|
+
if ((ts.isCallExpression(value) || ts.isNewExpression(value)) &&
|
|
4713
|
+
ts.isIdentifier(value.expression) && value.expression.text === "Object" &&
|
|
4714
|
+
lowerer.isStdlibSymbol(lowerer.resolveValueSymbol(value.expression) ?? undefined)) {
|
|
4715
|
+
const args = value.arguments ?? [];
|
|
4716
|
+
if (args.some(ts.isSpreadElement))
|
|
4717
|
+
return null;
|
|
4718
|
+
const first = args[0];
|
|
4719
|
+
const inner = first && (immediateObjectTagReceiver(lowerer, first) ?? (() => {
|
|
4720
|
+
const effect = lowerer.lowerExpr(first);
|
|
4721
|
+
const tag = isUnitType(effect.type) || effect.type.kind === "void" ? "Object" : objectToStringTag(lowerer, effect.type);
|
|
4722
|
+
return tag === null ? null : { tag, effect };
|
|
4723
|
+
})());
|
|
4724
|
+
if (first && !inner)
|
|
4725
|
+
return null;
|
|
4726
|
+
const stmts = [];
|
|
4727
|
+
if (inner && !isSafeToDiscard(inner.effect))
|
|
4728
|
+
stmts.push({ kind: "exprStmt", expr: inner.effect, loc: inner.effect.loc });
|
|
4729
|
+
for (const extra of args.slice(1)) {
|
|
4730
|
+
const effect = lowerer.lowerExpr(extra);
|
|
4731
|
+
if (!isSafeToDiscard(effect))
|
|
4732
|
+
stmts.push({ kind: "exprStmt", expr: effect, loc: locOf(extra) });
|
|
4733
|
+
}
|
|
4734
|
+
return {
|
|
4735
|
+
tag: inner?.tag ?? "Object",
|
|
4736
|
+
effect: { kind: "seqExpr", stmts, result: { kind: "numLit", value: 0, type: F64, loc }, type: F64, loc },
|
|
4737
|
+
};
|
|
4738
|
+
}
|
|
4739
|
+
return null;
|
|
4740
|
+
}
|
|
4741
|
+
function lowerObjectOwnPrototypeCall(lowerer, call, access) {
|
|
4742
|
+
if (lowerer.dynamic || access.name.text !== "call" || !ts.isPropertyAccessExpression(access.expression))
|
|
4743
|
+
return null;
|
|
4744
|
+
const method = access.expression;
|
|
4745
|
+
const enumerable = method.name.text === "propertyIsEnumerable";
|
|
4746
|
+
if (method.name.text !== "hasOwnProperty" && !enumerable)
|
|
4747
|
+
return null;
|
|
4748
|
+
const prototype = method.expression;
|
|
4749
|
+
if (!ts.isPropertyAccessExpression(prototype) || prototype.name.text !== "prototype" ||
|
|
4750
|
+
!lowerer.isStdlibGlobal(prototype.expression, "Object") || !lowerer.isStdlibMember(method) ||
|
|
4751
|
+
call.arguments.some(ts.isSpreadElement))
|
|
4752
|
+
return null;
|
|
4753
|
+
return lowerObjectOwnCall(lowerer, call, call.arguments[0], call.arguments[1], call.arguments.slice(2), method.name.text);
|
|
4754
|
+
}
|
|
4755
|
+
function lowerObjectOwnMethodCall(lowerer, call, access) {
|
|
4756
|
+
const method = access.name.text;
|
|
4757
|
+
if (lowerer.dynamic || call.questionDotToken || access.questionDotToken ||
|
|
4758
|
+
(method !== "hasOwnProperty" && method !== "propertyIsEnumerable") ||
|
|
4759
|
+
!lowerer.isStdlibMember(access) || call.arguments.some(ts.isSpreadElement))
|
|
4760
|
+
return null;
|
|
4761
|
+
return lowerObjectOwnCall(lowerer, call, access.expression, call.arguments[0], call.arguments.slice(1), method);
|
|
4762
|
+
}
|
|
4763
|
+
function lowerObjectOwnCall(lowerer, call, receiverNode, keyNode, extraNodes, method) {
|
|
4764
|
+
const enumerable = method === "propertyIsEnumerable";
|
|
4765
|
+
const loc = locOf(call);
|
|
4766
|
+
const stmts = [];
|
|
4767
|
+
const receiver = receiverNode ? lowerer.lowerExpr(receiverNode) : { kind: "unitLit", unit: "undefined", type: UNDEFINED_T, loc };
|
|
4768
|
+
let receiverRef = receiver;
|
|
4769
|
+
if (isUnitType(receiver.type) || receiver.type.kind === "void") {
|
|
4770
|
+
if (!isSafeToDiscard(receiver))
|
|
4771
|
+
stmts.push({ kind: "exprStmt", expr: receiver, loc });
|
|
4772
|
+
}
|
|
4773
|
+
else {
|
|
4774
|
+
const local = lowerer.declareHiddenLocal("%ownReceiver", receiver.type);
|
|
4775
|
+
stmts.push({ kind: "varDecl", localId: local.id, init: receiver, loc });
|
|
4776
|
+
receiverRef = varRef(local.id, receiver.type, loc);
|
|
4777
|
+
}
|
|
4778
|
+
const rawKey = keyNode ? lowerer.lowerExpr(keyNode) : null;
|
|
4779
|
+
let keyRef = rawKey;
|
|
4780
|
+
if (rawKey && (isUnitType(rawKey.type) || rawKey.type.kind === "void")) {
|
|
4781
|
+
if (!isSafeToDiscard(rawKey))
|
|
4782
|
+
stmts.push({ kind: "exprStmt", expr: rawKey, loc: rawKey.loc });
|
|
4783
|
+
}
|
|
4784
|
+
else if (rawKey) {
|
|
4785
|
+
const local = lowerer.declareHiddenLocal("%ownKey", rawKey.type);
|
|
4786
|
+
stmts.push({ kind: "varDecl", localId: local.id, init: rawKey, loc: rawKey.loc });
|
|
4787
|
+
keyRef = varRef(local.id, rawKey.type, loc);
|
|
4788
|
+
}
|
|
4789
|
+
for (const extra of extraNodes) {
|
|
4790
|
+
const effect = lowerer.lowerExpr(extra);
|
|
4791
|
+
if (!isSafeToDiscard(effect))
|
|
4792
|
+
stmts.push({ kind: "exprStmt", expr: effect, loc: locOf(extra) });
|
|
4793
|
+
}
|
|
4794
|
+
let key = keyRef;
|
|
4795
|
+
if (!keyNode || rawKey?.type.kind === "undefinedT" || rawKey?.type.kind === "void") {
|
|
4796
|
+
key = { kind: "strLit", value: "undefined", type: STRING, loc };
|
|
4797
|
+
}
|
|
4798
|
+
else if (rawKey?.type.kind === "nullT") {
|
|
4799
|
+
key = { kind: "strLit", value: "null", type: STRING, loc };
|
|
4800
|
+
}
|
|
4801
|
+
else if (key && (key.type.kind === "f64" || key.type.kind === "bool" || key.type.kind === "bigint")) {
|
|
4802
|
+
key = { kind: "toString", operand: key, type: STRING, loc };
|
|
4803
|
+
}
|
|
4804
|
+
if (!key || key.type.kind !== "string") {
|
|
4805
|
+
lowerer.noLowering("Object.prototype." + method + " with this property key", keyNode ?? call);
|
|
4806
|
+
}
|
|
4807
|
+
let result;
|
|
4808
|
+
const type = receiver.type;
|
|
4809
|
+
if (isUnitType(type) || type.kind === "void") {
|
|
4810
|
+
result = nodeThrowExpr(1, "", "Cannot convert undefined or null to object", BOOL, loc);
|
|
4811
|
+
}
|
|
4812
|
+
else if (type.kind === "record") {
|
|
4813
|
+
const shape = lowerer.shapes.get(type.shapeId);
|
|
4814
|
+
if (!shape || shape.tuple || shape.indexValue || shapeHasAccessorSlots(shape)) {
|
|
4815
|
+
lowerer.noLowering("Object.prototype." + method + " over this record shape", receiverNode ?? call);
|
|
4816
|
+
}
|
|
4817
|
+
result = { kind: "call", callee: recordHasOwnHelper(lowerer, type.shapeId, loc), args: [receiverRef, key], type: BOOL, loc };
|
|
4818
|
+
}
|
|
4819
|
+
else if (type.kind === "dyn" && !enumerable) {
|
|
4820
|
+
result = { kind: "libCall", fn: "dyn.hasOwn", args: [receiverRef, key], type: BOOL, loc };
|
|
4821
|
+
}
|
|
4822
|
+
else if (type.kind === "string" || type.kind === "array") {
|
|
4823
|
+
const needleLocal = lowerer.declareHiddenLocal("%ownIndex", F64);
|
|
4824
|
+
stmts.push({ kind: "varDecl", localId: needleLocal.id, init: { kind: "libCall", fn: "num.fromString", args: [key], type: F64, loc }, loc });
|
|
4825
|
+
const needle = varRef(needleLocal.id, F64, loc);
|
|
4826
|
+
const length = type.kind === "string"
|
|
4827
|
+
? { kind: "strIntrinsic", method: "length", receiver: receiverRef, args: [], type: F64, loc }
|
|
4828
|
+
: { kind: "arrIntrinsic", method: "length", receiver: receiverRef, args: [], type: F64, loc };
|
|
4829
|
+
const inRange = { kind: "bin", op: "<", left: needle, right: length, type: BOOL, loc };
|
|
4830
|
+
const nonNegative = { kind: "bin", op: ">=", left: needle, right: { kind: "numLit", value: 0, type: F64, loc }, type: BOOL, loc };
|
|
4831
|
+
const integer = { kind: "bin", op: "===", left: needle,
|
|
4832
|
+
right: { kind: "libCall", fn: "math.floor", args: [needle], type: F64, loc }, type: BOOL, loc };
|
|
4833
|
+
const canonical = { kind: "strEq", negated: false, left: key, right: { kind: "toString", operand: needle, type: STRING, loc }, type: BOOL, loc };
|
|
4834
|
+
const present = type.kind === "string" ? { kind: "boolLit", value: true, type: BOOL, loc } :
|
|
4835
|
+
{ kind: "bin", op: "===", left: { kind: "arrIntrinsic", method: "nextPresent", receiver: receiverRef, args: [needle], type: F64, loc }, right: needle, type: BOOL, loc };
|
|
4836
|
+
const no = { kind: "boolLit", value: false, type: BOOL, loc };
|
|
4837
|
+
const indexOwn = [inRange, integer, canonical, present].reduce((left, right) => ({ kind: "logical", op: "&&", left, right, type: BOOL, loc }), nonNegative);
|
|
4838
|
+
const lengthOwn = !enumerable
|
|
4839
|
+
? { kind: "strEq", negated: false, left: key, right: { kind: "strLit", value: "length", type: STRING, loc }, type: BOOL, loc }
|
|
4840
|
+
: no;
|
|
4841
|
+
result = { kind: "logical", op: "||", left: lengthOwn, right: indexOwn, type: BOOL, loc };
|
|
4842
|
+
}
|
|
4843
|
+
else if (type.kind === "f64" || type.kind === "bool" || type.kind === "bigint" || type.kind === "symbol") {
|
|
4844
|
+
result = { kind: "boolLit", value: false, type: BOOL, loc };
|
|
4845
|
+
}
|
|
4846
|
+
else {
|
|
4847
|
+
lowerer.noLowering("Object.prototype." + method + " over a " + lowerer.fmt(type) + " receiver", receiverNode ?? call);
|
|
4848
|
+
}
|
|
4849
|
+
return { kind: "seqExpr", stmts, result, type: BOOL, loc };
|
|
4850
|
+
}
|
|
4851
|
+
function lowerObjectPrototypeCall(lowerer, call, access) {
|
|
4852
|
+
if (lowerer.dynamic || access.name.text !== "call" || !ts.isPropertyAccessExpression(access.expression))
|
|
4853
|
+
return null;
|
|
4854
|
+
const method = access.expression;
|
|
4855
|
+
const prototype = method.expression;
|
|
4856
|
+
if (method.name.text !== "toString" || !ts.isPropertyAccessExpression(prototype) ||
|
|
4857
|
+
prototype.name.text !== "prototype" || !lowerer.isStdlibGlobal(prototype.expression, "Object") ||
|
|
4858
|
+
!lowerer.isStdlibMember(method) || call.arguments.some(ts.isSpreadElement))
|
|
4859
|
+
return null;
|
|
4860
|
+
const loc = locOf(call);
|
|
4861
|
+
if (call.arguments.length === 0)
|
|
4862
|
+
return { kind: "strLit", value: "[object Undefined]", type: STRING, loc };
|
|
4863
|
+
const receiverNode = call.arguments[0];
|
|
4864
|
+
const immediate = immediateObjectTagReceiver(lowerer, receiverNode);
|
|
4865
|
+
const receiver = immediate?.effect ?? lowerer.lowerExpr(receiverNode);
|
|
4866
|
+
const tag = immediate?.tag ?? objectToStringTag(lowerer, receiver.type);
|
|
4867
|
+
if (tag === null && receiver.type.kind !== "dyn") {
|
|
4868
|
+
lowerer.noLowering("Object.prototype.toString.call over a " + lowerer.fmt(receiver.type) + " receiver", receiverNode);
|
|
4869
|
+
}
|
|
4870
|
+
const stmts = [];
|
|
4871
|
+
let ref = receiver;
|
|
4872
|
+
if (isUnitType(receiver.type) || receiver.type.kind === "void") {
|
|
4873
|
+
if (!isSafeToDiscard(receiver))
|
|
4874
|
+
stmts.push({ kind: "exprStmt", expr: receiver, loc });
|
|
4875
|
+
}
|
|
4876
|
+
else {
|
|
4877
|
+
const local = lowerer.declareHiddenLocal("%objectTagReceiver", receiver.type);
|
|
4878
|
+
stmts.push({ kind: "varDecl", localId: local.id, init: receiver, loc });
|
|
4879
|
+
ref = varRef(local.id, receiver.type, loc);
|
|
4880
|
+
}
|
|
4881
|
+
for (const extra of call.arguments.slice(1)) {
|
|
4882
|
+
stmts.push({ kind: "exprStmt", expr: lowerer.lowerExpr(extra), loc: locOf(extra) });
|
|
4883
|
+
}
|
|
4884
|
+
const result = tag === null
|
|
4885
|
+
? { kind: "libCall", fn: "dyn.objectTag", args: [ref], type: STRING, loc }
|
|
4886
|
+
: { kind: "strLit", value: "[object " + tag + "]", type: STRING, loc };
|
|
4887
|
+
return { kind: "seqExpr", stmts, result, type: STRING, loc };
|
|
4888
|
+
}
|
|
4643
4889
|
function lowerStringPrototypeCall(lowerer, call, access) {
|
|
4644
4890
|
if (access.name.text !== "call" || !ts.isPropertyAccessExpression(access.expression))
|
|
4645
4891
|
return null;
|
|
@@ -4647,21 +4893,26 @@ function lowerStringPrototypeCall(lowerer, call, access) {
|
|
|
4647
4893
|
const prototypeAccess = methodAccess.expression;
|
|
4648
4894
|
if (!ts.isPropertyAccessExpression(prototypeAccess) || prototypeAccess.name.text !== "prototype" ||
|
|
4649
4895
|
!ts.isIdentifier(prototypeAccess.expression) || prototypeAccess.expression.text !== "String" ||
|
|
4650
|
-
!Object.hasOwn(STR_METHODS, methodAccess.name.text)
|
|
4896
|
+
(!Object.hasOwn(STR_METHODS, methodAccess.name.text) && !STRING_INDEX_METHODS.has(methodAccess.name.text)) ||
|
|
4897
|
+
!lowerer.isStdlibMember(methodAccess))
|
|
4651
4898
|
return null;
|
|
4652
4899
|
if (call.arguments.some(ts.isSpreadElement))
|
|
4653
4900
|
return null;
|
|
4654
4901
|
const entry = STR_METHODS[methodAccess.name.text];
|
|
4902
|
+
const indexMethod = STRING_INDEX_METHODS.has(methodAccess.name.text) ? methodAccess.name.text : null;
|
|
4903
|
+
const resultType = indexMethod ? lowerer.withUndefinedArmOf(indexMethod === "at" ? STRING : F64) : entry?.result;
|
|
4904
|
+
if (!resultType)
|
|
4905
|
+
return null;
|
|
4655
4906
|
const method = methodAccess.name.text === "trimStart" ? "trimLeft" :
|
|
4656
4907
|
methodAccess.name.text === "trimEnd" ? "trimRight" : methodAccess.name.text;
|
|
4657
4908
|
const loc = locOf(call);
|
|
4658
|
-
const nullishError = nodeThrowExpr(1, "", `String.prototype.${method} called on null or undefined`,
|
|
4909
|
+
const nullishError = nodeThrowExpr(1, "", `String.prototype.${method} called on null or undefined`, resultType, loc);
|
|
4659
4910
|
const receiverNode = call.arguments[0];
|
|
4660
4911
|
if (!receiverNode)
|
|
4661
4912
|
return nullishError;
|
|
4662
4913
|
// Receiver coercion runs after .call has evaluated its arguments; keep
|
|
4663
4914
|
// contextual wrapper lowering on forms without later method arguments.
|
|
4664
|
-
const immediateWrapper = entry
|
|
4915
|
+
const immediateWrapper = entry?.maxArgs === 0 && call.arguments.length === 1
|
|
4665
4916
|
? immediatePrimitiveWrapperToString(lowerer, receiverNode) : null;
|
|
4666
4917
|
const receiverValue = immediateWrapper ?? lowerer.lowerExpr(receiverNode);
|
|
4667
4918
|
const receiverType = receiverValue.type;
|
|
@@ -4673,26 +4924,33 @@ function lowerStringPrototypeCall(lowerer, call, access) {
|
|
|
4673
4924
|
kind: "seqExpr",
|
|
4674
4925
|
stmts: values.filter((value) => !isSafeToDiscard(value)).map((value) => ({ kind: "exprStmt", expr: value, loc: value.loc })),
|
|
4675
4926
|
result: nullishError,
|
|
4676
|
-
type:
|
|
4927
|
+
type: resultType,
|
|
4677
4928
|
loc,
|
|
4678
4929
|
};
|
|
4679
4930
|
}
|
|
4680
|
-
const padding = entry
|
|
4931
|
+
const padding = entry?.method === "padStart" || entry?.method === "padEnd";
|
|
4681
4932
|
const scalar = receiverType.kind === "string" || receiverType.kind === "f64" ||
|
|
4682
4933
|
receiverType.kind === "bool" || receiverType.kind === "bigint" ||
|
|
4683
4934
|
(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
4935
|
// Other methods convert object receivers before lowering their arguments;
|
|
4685
4936
|
// padding uses a helper that delays conversion until every argument is ready.
|
|
4686
|
-
const objectWithoutMethodArgs = entry
|
|
4937
|
+
const objectWithoutMethodArgs = entry?.maxArgs === 0 && call.arguments.length === 1 &&
|
|
4687
4938
|
(receiverType.kind === "record" || receiverType.kind === "array" || receiverType.kind === "object");
|
|
4688
|
-
const dynObjectWithoutMethodArgs = entry
|
|
4939
|
+
const dynObjectWithoutMethodArgs = entry?.maxArgs === 0 && call.arguments.length === 1 &&
|
|
4689
4940
|
receiverType.kind === "dyn" && plainObjectCoercionReceiver(lowerer, receiverNode);
|
|
4690
4941
|
if (!scalar && !objectWithoutMethodArgs && !dynObjectWithoutMethodArgs && !(padding && receiverType.kind === "dyn")) {
|
|
4691
4942
|
return lowerer.noLowering(`String.prototype.${methodAccess.name.text}.call with ${lowerer.fmt(receiverType)} receivers`, call);
|
|
4692
4943
|
}
|
|
4944
|
+
if (indexMethod)
|
|
4945
|
+
return lowerStringIndexCall(lowerer, call, indexMethod, receiverValue, receiverNode, call.arguments.slice(1));
|
|
4946
|
+
if (!entry)
|
|
4947
|
+
return null;
|
|
4693
4948
|
if (padding) {
|
|
4694
4949
|
return lowerStringPaddingCall(lowerer, call, entry.method, receiverValue, receiverNode, call.arguments.slice(1));
|
|
4695
4950
|
}
|
|
4951
|
+
if (entry.method === "split") {
|
|
4952
|
+
return lowerStringSplitCall(lowerer, call, receiverValue, receiverNode, call.arguments.slice(1));
|
|
4953
|
+
}
|
|
4696
4954
|
const receiver = dynObjectWithoutMethodArgs
|
|
4697
4955
|
? { kind: "libCall", fn: "dyn.toStringCoerce", args: [receiverValue], type: STRING, loc }
|
|
4698
4956
|
: lowerer.ensureString(receiverValue, receiverNode);
|
|
@@ -6093,31 +6351,6 @@ export function lambdaSignature(lowerer, node) {
|
|
|
6093
6351
|
funcType.rest = true;
|
|
6094
6352
|
return { shapes, funcType };
|
|
6095
6353
|
}
|
|
6096
|
-
/** Does this function's OWN body read `arguments`? Nested plain functions
|
|
6097
|
-
* and methods have their own `arguments` (the walk skips them); arrows
|
|
6098
|
-
* see the enclosing one (the walk descends). Exported for the lowerer's
|
|
6099
|
-
* dynFallbackType: tsgo does not synthesize the `arguments` rest
|
|
6100
|
-
* parameter into inferred signatures (5.9.3 did — its param-count
|
|
6101
|
-
* mismatch was the detector), so the 7 world asks the BODY directly. */
|
|
6102
|
-
export function bodyReadsArguments(fn) {
|
|
6103
|
-
let found = false;
|
|
6104
|
-
if (fn.body === undefined)
|
|
6105
|
-
return false;
|
|
6106
|
-
// Iterative walk (walkPreorder): function bodies can hold pathologically
|
|
6107
|
-
// deep expression chains that a recursive visit would die on.
|
|
6108
|
-
ts.walkPreorder(fn.body, (n) => {
|
|
6109
|
-
if (ts.isIdentifier(n) && n.text === "arguments" && !(ts.isPropertyAccessExpression(n.parent) && n.parent.name === n)) {
|
|
6110
|
-
found = true;
|
|
6111
|
-
return "stop";
|
|
6112
|
-
}
|
|
6113
|
-
if ((ts.isFunctionExpression(n) || ts.isFunctionDeclaration(n) || ts.isMethodDeclaration(n)) &&
|
|
6114
|
-
n !== fn) {
|
|
6115
|
-
return "skip"; // own `arguments` scope
|
|
6116
|
-
}
|
|
6117
|
-
return undefined;
|
|
6118
|
-
});
|
|
6119
|
-
return found;
|
|
6120
|
-
}
|
|
6121
6354
|
/** Lifts an arrow function / function expression / nested declaration /
|
|
6122
6355
|
* object-literal shorthand method to a module-level function and yields
|
|
6123
6356
|
* the `closure` expression creating it. */
|
|
@@ -6313,9 +6546,8 @@ export function lowerLambda(lowerer, node) {
|
|
|
6313
6546
|
* becomes the catch binding, and a handler VALUE would need a
|
|
6314
6547
|
* caught-typed closure parameter, which cannot exist. finally takes
|
|
6315
6548
|
* any () => void closure (its callback sees no arguments). then takes
|
|
6316
|
-
*
|
|
6317
|
-
*
|
|
6318
|
-
* .catch); a promise-returning handler flattens through the async
|
|
6549
|
+
* a fulfillment handler and optionally a rejection handler; a
|
|
6550
|
+
* promise-returning handler flattens through the async
|
|
6319
6551
|
* return path, a receiver rejection passes through untouched (the
|
|
6320
6552
|
* wrapper's await re-throws it), and a handler throw rejects the
|
|
6321
6553
|
* result — the spec's onFulfilled rules by construction. Null for
|
|
@@ -6414,9 +6646,9 @@ export function lowerPromiseMethodCall(lowerer, call, access) {
|
|
|
6414
6646
|
};
|
|
6415
6647
|
const passthrough = call.arguments.length === 0 ||
|
|
6416
6648
|
(call.arguments.length === 1 && isAbsentHandler(call.arguments[0]));
|
|
6417
|
-
if (call.arguments.length !== 1 && !passthrough) {
|
|
6649
|
+
if (call.arguments.length !== 1 && !passthrough && !(member === "then" && call.arguments.length === 2)) {
|
|
6418
6650
|
lowerer.noLowering(`${member} with ${call.arguments.length} arguments`, call, member === "then"
|
|
6419
|
-
? "the supported form takes
|
|
6651
|
+
? "the supported form takes a fulfillment handler and an optional rejection handler"
|
|
6420
6652
|
: `the supported form takes exactly one ${member === "catch" ? "inline handler" : "callback"}`);
|
|
6421
6653
|
}
|
|
6422
6654
|
// The receiver evaluates FIRST, in the enclosing function, like JS.
|
|
@@ -6488,6 +6720,140 @@ export function lowerPromiseMethodCall(lowerer, call, access) {
|
|
|
6488
6720
|
}
|
|
6489
6721
|
}
|
|
6490
6722
|
if (member === "then") {
|
|
6723
|
+
if (call.arguments.length === 2) {
|
|
6724
|
+
const fulfilledNode = call.arguments[0];
|
|
6725
|
+
const rejectedNode = call.arguments[1];
|
|
6726
|
+
const fulfilledAbsent = isAbsentHandler(fulfilledNode);
|
|
6727
|
+
const rejectedAbsent = isAbsentHandler(rejectedNode);
|
|
6728
|
+
const resultT = lowerer.mapTypeOf(lowerer.typeOf(call));
|
|
6729
|
+
if (resultT?.kind !== "promise") {
|
|
6730
|
+
lowerer.noLowering("then with these handlers", call, "the result must be a representable promise");
|
|
6731
|
+
}
|
|
6732
|
+
const R = resultT.inner;
|
|
6733
|
+
if (!fulfilledAbsent && inner.kind === "jsval")
|
|
6734
|
+
markJsvalHandlerParams(lowerer, fulfilledNode);
|
|
6735
|
+
if (!fulfilledAbsent && inner.kind === "moduleNs")
|
|
6736
|
+
markModuleNsHandlerParams(lowerer, fulfilledNode, inner);
|
|
6737
|
+
const boxHandler = (node, allowDirect) => {
|
|
6738
|
+
const value = lowerer.lowerExpr(node);
|
|
6739
|
+
if (allowDirect && value.type.kind === "func" && value.type.params.length <= 1 &&
|
|
6740
|
+
(value.type.params.length === 0 || (inner.kind !== "void" && typeEquals(value.type.params[0], inner)))) {
|
|
6741
|
+
return { value, direct: true, returnsPromise: value.type.ret.kind === "promise" };
|
|
6742
|
+
}
|
|
6743
|
+
if (value.type.kind === "dyn")
|
|
6744
|
+
return { value, direct: false, returnsPromise: false };
|
|
6745
|
+
if (value.type.kind === "func" && value.type.params.length <= 1 &&
|
|
6746
|
+
canBoxFuncIntoDyn(value.type, (id) => lowerer.shapes.get(id), (id) => lowerer.unions.get(id))) {
|
|
6747
|
+
return { value: { kind: "dynFrom", value, type: DYN, loc }, direct: false, returnsPromise: value.type.ret.kind === "promise" };
|
|
6748
|
+
}
|
|
6749
|
+
lowerer.unsupported("SC1090", node, "then handlers that cannot be called with one value");
|
|
6750
|
+
};
|
|
6751
|
+
// Evaluate both callback expressions before the wrapper runs, in source order.
|
|
6752
|
+
const fulfilled = fulfilledAbsent ? null : boxHandler(fulfilledNode, true);
|
|
6753
|
+
const rejected = rejectedAbsent ? null : boxHandler(rejectedNode, false);
|
|
6754
|
+
const fnName = `%fn${lowerer.lambdaCounter++}_then2`;
|
|
6755
|
+
const paramTypes = [promT, ...(fulfilled ? [fulfilled.value.type] : []), ...(rejected ? [DYN] : [])];
|
|
6756
|
+
const funcType = { kind: "func", params: paramTypes, ret: resultT };
|
|
6757
|
+
const fnCtx = newFnCtx(true, null, funcType, R);
|
|
6758
|
+
fnCtx.isAsync = true;
|
|
6759
|
+
lowerer.fnStack.push(fnCtx);
|
|
6760
|
+
try {
|
|
6761
|
+
const pLocal = lowerer.declareHiddenLocal("p", promT);
|
|
6762
|
+
const fLocal = fulfilled ? lowerer.declareHiddenLocal("f", fulfilled.value.type) : null;
|
|
6763
|
+
const rLocal = rejected ? lowerer.declareHiddenLocal("r", DYN) : null;
|
|
6764
|
+
const eLocal = lowerer.declareHiddenLocal("e", CAUGHT);
|
|
6765
|
+
const vLocal = inner.kind === "void" ? null : lowerer.declareHiddenLocal("v", inner);
|
|
6766
|
+
const awaitE = {
|
|
6767
|
+
kind: "awaitExpr",
|
|
6768
|
+
value: { kind: "varRef", localId: pLocal.id, type: promT, loc },
|
|
6769
|
+
type: inner,
|
|
6770
|
+
loc,
|
|
6771
|
+
};
|
|
6772
|
+
const tryBody = vLocal
|
|
6773
|
+
? [{ kind: "varDecl", localId: vLocal.id, init: awaitE, loc }]
|
|
6774
|
+
: [{ kind: "exprStmt", expr: awaitE, loc }];
|
|
6775
|
+
const resultOf = (value, returnsPromise = false) => {
|
|
6776
|
+
const settled = value.type.kind === "promise"
|
|
6777
|
+
? { kind: "awaitExpr", value, type: value.type.inner, loc }
|
|
6778
|
+
: returnsPromise
|
|
6779
|
+
? { kind: "libCall", fn: "async.awaitDyn", args: [value], type: DYN, loc }
|
|
6780
|
+
: value;
|
|
6781
|
+
return R.kind === "void"
|
|
6782
|
+
? [{ kind: "exprStmt", expr: settled, loc }, { kind: "return", value: null, loc }]
|
|
6783
|
+
: [{ kind: "return", value: lowerer.coerceInto(call, settled, R), loc }];
|
|
6784
|
+
};
|
|
6785
|
+
const rejectedCall = rLocal ? {
|
|
6786
|
+
kind: "dynCall",
|
|
6787
|
+
callee: { kind: "varRef", localId: rLocal.id, type: DYN, loc },
|
|
6788
|
+
calleeName: jsFuncNameOf(rejectedNode) ?? "onRejected",
|
|
6789
|
+
args: [{ kind: "caughtToDyn", value: { kind: "varRef", localId: eLocal.id, type: CAUGHT, loc }, type: DYN, loc }],
|
|
6790
|
+
type: DYN,
|
|
6791
|
+
loc,
|
|
6792
|
+
} : null;
|
|
6793
|
+
const catchBody = rejectedCall
|
|
6794
|
+
? resultOf(rejectedCall, rejected?.returnsPromise)
|
|
6795
|
+
: [{ kind: "rethrow", localId: eLocal.id, loc }];
|
|
6796
|
+
const body = [{ kind: "tryCatch", tryBody, catchBody, catchLocalId: eLocal.id, finallyBody: null, loc }];
|
|
6797
|
+
// The fulfillment callback sits after the catch so a throw from it
|
|
6798
|
+
// rejects the result instead of reaching onRejected.
|
|
6799
|
+
if (fLocal) {
|
|
6800
|
+
const settled = vLocal
|
|
6801
|
+
? { kind: "varRef", localId: vLocal.id, type: inner, loc }
|
|
6802
|
+
: dynUndefinedExpr(loc);
|
|
6803
|
+
if (fulfilled.direct && fLocal.type.kind === "func") {
|
|
6804
|
+
body.push(...resultOf({
|
|
6805
|
+
kind: "callValue",
|
|
6806
|
+
callee: { kind: "varRef", localId: fLocal.id, type: fLocal.type, loc },
|
|
6807
|
+
args: fLocal.type.params.length === 0 ? [] : [settled],
|
|
6808
|
+
type: fLocal.type.ret,
|
|
6809
|
+
loc,
|
|
6810
|
+
}));
|
|
6811
|
+
}
|
|
6812
|
+
else {
|
|
6813
|
+
const arg = settled.type.kind === "dyn" ? settled
|
|
6814
|
+
: canConvertToDyn(settled.type, (id) => lowerer.shapes.get(id), (id) => lowerer.unions.get(id))
|
|
6815
|
+
? { kind: "dynFrom", value: settled, type: DYN, loc }
|
|
6816
|
+
: lowerer.unsupported("SC1090", fulfilledNode, "then fulfillment values that cannot cross the checked-dynamic boundary");
|
|
6817
|
+
body.push(...resultOf({
|
|
6818
|
+
kind: "dynCall",
|
|
6819
|
+
callee: { kind: "varRef", localId: fLocal.id, type: DYN, loc },
|
|
6820
|
+
calleeName: jsFuncNameOf(fulfilledNode) ?? "onFulfilled",
|
|
6821
|
+
args: [arg], type: DYN, loc,
|
|
6822
|
+
}, fulfilled?.returnsPromise));
|
|
6823
|
+
}
|
|
6824
|
+
}
|
|
6825
|
+
else if (vLocal) {
|
|
6826
|
+
body.push(...resultOf({ kind: "varRef", localId: vLocal.id, type: inner, loc }));
|
|
6827
|
+
}
|
|
6828
|
+
else if (R.kind === "void") {
|
|
6829
|
+
body.push({ kind: "return", value: null, loc });
|
|
6830
|
+
}
|
|
6831
|
+
else {
|
|
6832
|
+
body.push({ kind: "return", value: lowerer.coerceInto(call, dynUndefinedExpr(loc), R), loc });
|
|
6833
|
+
}
|
|
6834
|
+
const ctx = lowerer.ctx;
|
|
6835
|
+
const lifted = {
|
|
6836
|
+
name: fnName,
|
|
6837
|
+
params: [
|
|
6838
|
+
{ localId: pLocal.id, name: pLocal.name, type: promT },
|
|
6839
|
+
...(fLocal ? [{ localId: fLocal.id, name: fLocal.name, type: fLocal.type }] : []),
|
|
6840
|
+
...(rLocal ? [{ localId: rLocal.id, name: rLocal.name, type: DYN }] : []),
|
|
6841
|
+
],
|
|
6842
|
+
returnType: R,
|
|
6843
|
+
locals: ctx.locals,
|
|
6844
|
+
captures: ctx.captures,
|
|
6845
|
+
body,
|
|
6846
|
+
loc,
|
|
6847
|
+
async: true,
|
|
6848
|
+
};
|
|
6849
|
+
lowerer.liftedFns.push(lifted);
|
|
6850
|
+
const closure = { kind: "closure", fnName, captures: ctx.captureSources, type: funcType, loc };
|
|
6851
|
+
return { kind: "callValue", callee: closure, args: [receiver, ...(fulfilled ? [fulfilled.value] : []), ...(rejected ? [rejected.value] : [])], type: resultT, loc };
|
|
6852
|
+
}
|
|
6853
|
+
finally {
|
|
6854
|
+
lowerer.fnStack.pop();
|
|
6855
|
+
}
|
|
6856
|
+
}
|
|
6491
6857
|
// The settled value is an island HANDLE: an inline handler's
|
|
6492
6858
|
// unannotated parameter binds it as jsval, whatever the checker's
|
|
6493
6859
|
// contextual type spelled (a module-namespace type has no mapping —
|