@scriptc/compiler 0.1.1 → 0.1.2
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 +45 -17
- package/ambient/scriptc.d.ts +2 -1
- package/dist/backend/build-cache.js.map +1 -1
- package/dist/backend/c/async.js +24 -2
- package/dist/backend/c/async.js.map +1 -1
- package/dist/backend/c/c-emitter.d.ts +22 -10
- package/dist/backend/c/c-emitter.js +60 -15
- package/dist/backend/c/c-emitter.js.map +1 -1
- package/dist/backend/c/exprs.js +220 -10
- package/dist/backend/c/exprs.js.map +1 -1
- package/dist/backend/c/island.js.map +1 -1
- package/dist/backend/c/may-throw.js.map +1 -1
- package/dist/backend/c/stmts.d.ts +3 -5
- package/dist/backend/c/stmts.js +58 -17
- package/dist/backend/c/stmts.js.map +1 -1
- package/dist/backend/c/types.js.map +1 -1
- package/dist/backend/c/walkers.js +31 -5
- package/dist/backend/c/walkers.js.map +1 -1
- package/dist/backend/external-c.js.map +1 -1
- package/dist/backend/linker.js.map +1 -1
- package/dist/backend/llvm/blocks.js.map +1 -1
- package/dist/backend/llvm/dyn.js +56 -3
- package/dist/backend/llvm/dyn.js.map +1 -1
- package/dist/backend/llvm/emitter.js +98 -19
- package/dist/backend/llvm/emitter.js.map +1 -1
- package/dist/backend/llvm/expr-async.js +85 -3
- package/dist/backend/llvm/expr-async.js.map +1 -1
- package/dist/backend/llvm/expr-bytes.js.map +1 -1
- package/dist/backend/llvm/expr-calls.js.map +1 -1
- package/dist/backend/llvm/expr-containers.js.map +1 -1
- package/dist/backend/llvm/expr-context.d.ts +1 -0
- package/dist/backend/llvm/expr-control.js.map +1 -1
- package/dist/backend/llvm/expr-dispatch.js.map +1 -1
- package/dist/backend/llvm/expr-dynamic.js +6 -3
- package/dist/backend/llvm/expr-dynamic.js.map +1 -1
- package/dist/backend/llvm/expr-island.js.map +1 -1
- package/dist/backend/llvm/expr-primitives.js.map +1 -1
- package/dist/backend/llvm/expr-records.js.map +1 -1
- package/dist/backend/llvm/expr-stream-bridges.js +220 -9
- package/dist/backend/llvm/expr-stream-bridges.js.map +1 -1
- package/dist/backend/llvm/lib-dispatch.js +2 -0
- package/dist/backend/llvm/lib-dispatch.js.map +1 -1
- package/dist/backend/llvm/lib-shared.js +18 -0
- package/dist/backend/llvm/lib-shared.js.map +1 -1
- package/dist/backend/llvm/lib-stream.js +6 -3
- package/dist/backend/llvm/lib-stream.js.map +1 -1
- package/dist/backend/llvm/split.js.map +1 -1
- package/dist/backend/llvm/unsupported.js.map +1 -1
- package/dist/backend/llvm/walkers.js.map +1 -1
- package/dist/backend/native-codegen.js.map +1 -1
- package/dist/backend/native-codegen.test.js +2 -0
- package/dist/backend/native-codegen.test.js.map +1 -1
- package/dist/backend/native-toolchain.js.map +1 -1
- package/dist/backend/native-toolchain.test.js.map +1 -1
- package/dist/backend/runtime-pack.js.map +1 -1
- package/dist/backend/targets.js.map +1 -1
- package/dist/backend/vendor-archives.js.map +1 -1
- package/dist/compat/fetch-profile.js +4 -2
- package/dist/compat/fetch-profile.js.map +1 -1
- package/dist/coverage/report.js.map +1 -1
- package/dist/coverage/surface-manifest.js +12 -0
- package/dist/coverage/surface-manifest.js.map +1 -1
- package/dist/diagnostics/diagnostic.d.ts +6 -5
- package/dist/diagnostics/diagnostic.js +7 -6
- package/dist/diagnostics/diagnostic.js.map +1 -1
- package/dist/diagnostics/render.js.map +1 -1
- package/dist/ffi/ffi-manifest.js.map +1 -1
- package/dist/frontend/cjs-lexer.js.map +1 -1
- package/dist/frontend/dts-paths.js.map +1 -1
- package/dist/frontend/input-tracker.d.ts +10 -0
- package/dist/frontend/input-tracker.js +26 -3
- package/dist/frontend/input-tracker.js.map +1 -1
- package/dist/frontend/input-tracker.test.js +8 -1
- package/dist/frontend/input-tracker.test.js.map +1 -1
- package/dist/frontend/lib-contract.js.map +1 -1
- package/dist/frontend/lowering/array-values.d.ts +4 -0
- package/dist/frontend/lowering/array-values.js +18 -2
- package/dist/frontend/lowering/array-values.js.map +1 -1
- package/dist/frontend/lowering/callback-arg.js.map +1 -1
- package/dist/frontend/lowering/containers/bytes.d.ts +35 -0
- package/dist/frontend/lowering/containers/bytes.js +1021 -0
- package/dist/frontend/lowering/containers/bytes.js.map +1 -0
- package/dist/frontend/lowering/containers/string-and-regexp.d.ts +17 -0
- package/dist/frontend/lowering/containers/string-and-regexp.js +345 -0
- package/dist/frontend/lowering/containers/string-and-regexp.js.map +1 -0
- package/dist/frontend/lowering/expressions/evaluation-safety.d.ts +13 -0
- package/dist/frontend/lowering/expressions/evaluation-safety.js +76 -0
- package/dist/frontend/lowering/expressions/evaluation-safety.js.map +1 -0
- package/dist/frontend/lowering/expressions/object-literals.d.ts +44 -0
- package/dist/frontend/lowering/expressions/object-literals.js +1815 -0
- package/dist/frontend/lowering/expressions/object-literals.js.map +1 -0
- package/dist/frontend/lowering/expressions/optional-chains.d.ts +31 -0
- package/dist/frontend/lowering/expressions/optional-chains.js +382 -0
- package/dist/frontend/lowering/expressions/optional-chains.js.map +1 -0
- package/dist/frontend/lowering/expressions/try-lower-expression.d.ts +7 -0
- package/dist/frontend/lowering/expressions/try-lower-expression.js +26 -0
- package/dist/frontend/lowering/expressions/try-lower-expression.js.map +1 -0
- package/dist/frontend/lowering/lib-boundary.js +7 -2
- package/dist/frontend/lowering/lib-boundary.js.map +1 -1
- package/dist/frontend/lowering/lower-assert.js.map +1 -1
- package/dist/frontend/lowering/lower-async-iteration.d.ts +7 -0
- package/dist/frontend/lowering/lower-async-iteration.js +657 -0
- package/dist/frontend/lowering/lower-async-iteration.js.map +1 -0
- package/dist/frontend/lowering/lower-builtins.d.ts +18 -0
- package/dist/frontend/lowering/lower-builtins.js +219 -48
- package/dist/frontend/lowering/lower-builtins.js.map +1 -1
- package/dist/frontend/lowering/lower-calls.d.ts +13 -4
- package/dist/frontend/lowering/lower-calls.js +407 -64
- package/dist/frontend/lowering/lower-calls.js.map +1 -1
- package/dist/frontend/lowering/lower-classes.d.ts +7 -13
- package/dist/frontend/lowering/lower-classes.js +128 -35
- package/dist/frontend/lowering/lower-classes.js.map +1 -1
- package/dist/frontend/lowering/lower-comptime.js.map +1 -1
- package/dist/frontend/lowering/lower-containers.d.ts +1 -47
- package/dist/frontend/lowering/lower-containers.js +135 -1467
- package/dist/frontend/lowering/lower-containers.js.map +1 -1
- package/dist/frontend/lowering/lower-event-emitter.js +51 -5
- package/dist/frontend/lowering/lower-event-emitter.js.map +1 -1
- package/dist/frontend/lowering/lower-exprs.d.ts +0 -78
- package/dist/frontend/lowering/lower-exprs.js +402 -2351
- package/dist/frontend/lowering/lower-exprs.js.map +1 -1
- package/dist/frontend/lowering/lower-generators.d.ts +18 -3
- package/dist/frontend/lowering/lower-generators.js +145 -9
- package/dist/frontend/lowering/lower-generators.js.map +1 -1
- package/dist/frontend/lowering/lower-inspect.js +3 -3
- package/dist/frontend/lowering/lower-inspect.js.map +1 -1
- package/dist/frontend/lowering/lower-island.d.ts +6 -0
- package/dist/frontend/lowering/lower-island.js +37 -5
- package/dist/frontend/lowering/lower-island.js.map +1 -1
- package/dist/frontend/lowering/lower-modules.js +52 -15
- package/dist/frontend/lowering/lower-modules.js.map +1 -1
- package/dist/frontend/lowering/lower-node-module.d.ts +18 -0
- package/dist/frontend/lowering/lower-node-module.js +302 -0
- package/dist/frontend/lowering/lower-node-module.js.map +1 -0
- package/dist/frontend/lowering/lower-server.js +1 -1
- package/dist/frontend/lowering/lower-server.js.map +1 -1
- package/dist/frontend/lowering/lower-stmts.d.ts +5 -24
- package/dist/frontend/lowering/lower-stmts.js +519 -253
- package/dist/frontend/lowering/lower-stmts.js.map +1 -1
- package/dist/frontend/lowering/lower-stream.js +4 -4
- package/dist/frontend/lowering/lower-stream.js.map +1 -1
- package/dist/frontend/lowering/lowerer.d.ts +37 -34
- package/dist/frontend/lowering/lowerer.js +143 -57
- package/dist/frontend/lowering/lowerer.js.map +1 -1
- package/dist/frontend/lowering/optional-arguments.d.ts +9 -0
- package/dist/frontend/lowering/optional-arguments.js +57 -0
- package/dist/frontend/lowering/optional-arguments.js.map +1 -0
- package/dist/frontend/npm-static-declarations.d.ts +35 -0
- package/dist/frontend/npm-static-declarations.js +506 -0
- package/dist/frontend/npm-static-declarations.js.map +1 -0
- package/dist/frontend/npm-static-declarations.test.d.ts +1 -0
- package/dist/frontend/npm-static-declarations.test.js +122 -0
- package/dist/frontend/npm-static-declarations.test.js.map +1 -0
- package/dist/frontend/npm-static.d.ts +2 -0
- package/dist/frontend/npm-static.js +26 -10
- package/dist/frontend/npm-static.js.map +1 -1
- package/dist/frontend/npm.d.ts +60 -0
- package/dist/frontend/npm.js +38 -4
- package/dist/frontend/npm.js.map +1 -1
- package/dist/frontend/npm.test.d.ts +1 -0
- package/dist/frontend/npm.test.js +50 -0
- package/dist/frontend/npm.test.js.map +1 -0
- package/dist/frontend/program.d.ts +3 -2
- package/dist/frontend/program.js +495 -68
- package/dist/frontend/program.js.map +1 -1
- package/dist/frontend/resolve.d.ts +10 -2
- package/dist/frontend/resolve.js +90 -14
- package/dist/frontend/resolve.js.map +1 -1
- package/dist/frontend/resolve.test.js +4 -1
- package/dist/frontend/resolve.test.js.map +1 -1
- package/dist/frontend/runtime-resolve.d.ts +22 -0
- package/dist/frontend/runtime-resolve.js +101 -0
- package/dist/frontend/runtime-resolve.js.map +1 -0
- package/dist/frontend/runtime-resolve.test.d.ts +1 -0
- package/dist/frontend/runtime-resolve.test.js +18 -0
- package/dist/frontend/runtime-resolve.test.js.map +1 -0
- package/dist/frontend/ts7/adapter.js +12 -13
- package/dist/frontend/ts7/adapter.js.map +1 -1
- package/dist/frontend/ts7/ast.js.map +1 -1
- package/dist/frontend/ts7/census-check.js +3 -3
- package/dist/frontend/ts7/census-check.js.map +1 -1
- package/dist/frontend/ts7/checker.d.ts +3 -0
- package/dist/frontend/ts7/checker.js +12 -3
- package/dist/frontend/ts7/checker.js.map +1 -1
- package/dist/frontend/ts7/enums.js.map +1 -1
- package/dist/frontend/ts7/program-adapter.js +42 -3
- package/dist/frontend/ts7/program-adapter.js.map +1 -1
- package/dist/frontend/type-mapper.js +120 -7
- package/dist/frontend/type-mapper.js.map +1 -1
- package/dist/index.js +5 -5
- package/dist/index.js.map +1 -1
- package/dist/ir/analysis.js.map +1 -1
- package/dist/ir/ir.d.ts +60 -30
- package/dist/ir/ir.js +57 -6
- package/dist/ir/ir.js.map +1 -1
- package/dist/ir/validate.js +44 -59
- package/dist/ir/validate.js.map +1 -1
- package/dist/library/cache-primitives.js.map +1 -1
- package/dist/library/cache-reset.test.js.map +1 -1
- package/dist/library/compiler-self-identity.js.map +1 -1
- package/dist/library/int-infer.js.map +1 -1
- package/dist/library/int-infer.test.js.map +1 -1
- package/dist/library/library-profile.js.map +1 -1
- package/dist/library/sidecar-validate.js.map +1 -1
- package/dist/library/sidecar.js.map +1 -1
- package/dist/startup-cache.js.map +1 -1
- package/dist/wasi-paths.js.map +1 -1
- package/package.json +18 -18
- package/surface-manifest.json +60 -4
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import { InternalCompilerError } from "../../errors.js";
|
|
2
2
|
/* Statement lowering: the statement dispatch (lowerStmt), variable
|
|
3
3
|
* declarations including destructuring patterns, scoped blocks, control
|
|
4
|
-
* flow (if/while/for/for-of/do, switch, try/catch,
|
|
5
|
-
*
|
|
4
|
+
* flow (if/while/for/for-of/do, switch, try/catch, abrupt completions),
|
|
5
|
+
* explicit resource management, and blocked-binding poisoning. */
|
|
6
6
|
import * as ts from "../ts7/adapter.js";
|
|
7
7
|
import { arrayValueRead, arrayValueStore, arrayValueType } from "./array-values.js";
|
|
8
|
-
import { lowerForOfGenerator, lowerYieldStarStatement } from "./lower-generators.js";
|
|
8
|
+
import { lowerForAwaitGenerator, lowerForOfGenerator, lowerYieldStarStatement } from "./lower-generators.js";
|
|
9
|
+
import { lowerForAwaitBuiltin } from "./lower-async-iteration.js";
|
|
9
10
|
import { BOOL, BYTES_U8, CAUGHT, DYN, F64, JSVAL, STRING, UNDEFINED_T, VOID, arrayOf, isUnitType, shapeHasAccessorSlots, typeEquals } from "../../ir/ir.js";
|
|
10
11
|
import { PoisonError, boundIdentifiersOf, dynFallbackType, dynUndefinedExpr, importCallHandleType, neverTaintedJsType, stmtUsesIsland, uncheckedOverloadHandleCall } from "./lowerer.js";
|
|
11
12
|
import { enforceLibBoundary } from "./lib-boundary.js";
|
|
@@ -15,20 +16,262 @@ import { isProvenanceSourceFile } from "../provenance-registry.js";
|
|
|
15
16
|
import { ambientUndefVarRootOf, lowerImportEquals, nsUndefRead, nsWritableTarget, trapDeclRootOf } from "./lower-namespaces.js";
|
|
16
17
|
import { expandoWritableTarget, lowerExpandoAssignStmt } from "./lower-expando.js";
|
|
17
18
|
import { lowerForOfArrayIter, lowerForOfMap, lowerForOfSearchParams, lowerForOfSet, lowerSafeIndexRead, objectIterOverIndexShape, strCharsCall } from "./lower-containers.js";
|
|
18
|
-
import { bindingContextualGenericFnNodeOf, bindingGenericFnAliasInfoOf, bindingGenericFnInfoOf, bindingGenericFnNodeOf, deadUnmappableBinding, implicitLocalFnInfoOf, implicitLocalFnNodeOf, nullishExprUnitOf, nullishGenericBindingUnitOf, recordKeysArrayCall } from "./lower-calls.js";
|
|
19
|
+
import { bindingContextualGenericFnNodeOf, bindingGenericFnAliasInfoOf, bindingGenericFnInfoOf, bindingGenericFnNodeOf, deadUnmappableBinding, implicitLocalFnInfoOf, implicitLocalFnNodeOf, implicitMethodCallInfersReturn, nullishExprUnitOf, nullishGenericBindingUnitOf, recordKeysArrayCall } from "./lower-calls.js";
|
|
19
20
|
import { isMixinFnBinding, mixinResultBindingClassOf } from "./lower-mixins.js";
|
|
20
21
|
import { genericIfaceBindingKeepsClass } from "./lower-classes.js";
|
|
21
|
-
import { lowerStreamUnderscoreAssign, streamClassAliasDecl
|
|
22
|
+
import { lowerStreamUnderscoreAssign, streamClassAliasDecl } from "./lower-stream.js";
|
|
22
23
|
import { lowerHttpResPropertyAssignment, lowerHttpServerTimeoutAssignment, lowerServerCloseOverrideAssignment } from "./lower-server.js";
|
|
23
24
|
import { builtinMemberRequireDecl, builtinNamespaceDestructureModuleOf, createRequireBindingDecl, createRequireCalleeFileOf, createRequireNamespaceDecl, textCodecBindingDecl } from "./lower-builtins.js";
|
|
24
25
|
import { lowerEnumDeclaration } from "./lower-enums.js";
|
|
25
|
-
import { abstractPropertyDeclOf, aliasTypeofNarrows, isMatchSliceType, lowerAbsenceProbe, lowerGroupsProjection, lowerOptionalNumber, matchResultNamedGroupsOf,
|
|
26
|
+
import { abstractPropertyDeclOf, aliasTypeofNarrows, isMatchSliceType, lowerAbsenceProbe, lowerGroupsProjection, lowerOptionalNumber, matchResultNamedGroupsOf, runtimeOptionalTrueIds, symbolFieldInfo, withRuntimeOptionalNarrowed } from "./lower-exprs.js";
|
|
27
|
+
import { isSafeToRepeat } from "./expressions/evaluation-safety.js";
|
|
28
|
+
import { tryLowerExpression } from "./expressions/try-lower-expression.js";
|
|
26
29
|
import { checkerPanicDiag, isCheckerPanic, requiresDynamicDiag } from "../../diagnostics/diagnostic.js";
|
|
27
30
|
import { isParseArgsDynTypeName, isUnitOnlyTsType, unitOnlyUnion } from "../type-mapper.js";
|
|
28
31
|
import { canonicalBuiltinModule } from "../builtin-modules.js";
|
|
29
32
|
import { isRelativeSpecifier } from "../workspace-registry.js";
|
|
30
33
|
import { probeNodeRequireRefusal } from "../npm.js";
|
|
31
34
|
import { numLit, varRef } from "../../ir/build.js";
|
|
35
|
+
import { constituentTypes } from "../ts7/checker.js";
|
|
36
|
+
import { npmStaticPackageOfPath } from "../npm-static.js";
|
|
37
|
+
import { fenceNodeModuleMutation } from "./lower-node-module.js";
|
|
38
|
+
/** An opted package's directly-returned empty array may take the function's
|
|
39
|
+
* explicit static return element type. This is the common JS accumulator
|
|
40
|
+
* shape (`const result = []; result.push(value); return result`) and is
|
|
41
|
+
* narrower than treating every evolving array in the function alike. */
|
|
42
|
+
function npmStaticReturnedEmptyArrayType(lowerer, decl) {
|
|
43
|
+
if (!ts.isIdentifier(decl.name) ||
|
|
44
|
+
decl.initializer === undefined ||
|
|
45
|
+
!ts.isArrayLiteralExpression(decl.initializer) ||
|
|
46
|
+
decl.initializer.elements.length !== 0 ||
|
|
47
|
+
!isJsSourceFile(decl.getSourceFile()) ||
|
|
48
|
+
npmStaticPackageOfPath(decl.getSourceFile().fileName) === null ||
|
|
49
|
+
lowerer.ctx.returnType.kind !== "array") {
|
|
50
|
+
return null;
|
|
51
|
+
}
|
|
52
|
+
const symbol = lowerer.checker.getSymbolAtLocation(decl.name);
|
|
53
|
+
if (symbol === undefined)
|
|
54
|
+
return null;
|
|
55
|
+
let owner = decl.parent;
|
|
56
|
+
while (owner !== undefined && !ts.isFunctionLike(owner))
|
|
57
|
+
owner = owner.parent;
|
|
58
|
+
const ownerBody = owner !== undefined && "body" in owner ? owner.body : undefined;
|
|
59
|
+
if (ownerBody === undefined || !ts.isBlock(ownerBody))
|
|
60
|
+
return null;
|
|
61
|
+
let returned = false;
|
|
62
|
+
ts.walkPreorder(ownerBody, (node) => {
|
|
63
|
+
if (node !== ownerBody && ts.isFunctionLike(node))
|
|
64
|
+
return "skip";
|
|
65
|
+
if (!ts.isReturnStatement(node) || node.expression === undefined)
|
|
66
|
+
return undefined;
|
|
67
|
+
let expression = node.expression;
|
|
68
|
+
while (ts.isParenthesizedExpression(expression))
|
|
69
|
+
expression = expression.expression;
|
|
70
|
+
if (ts.isIdentifier(expression) && lowerer.checker.getSymbolAtLocation(expression) === symbol) {
|
|
71
|
+
returned = true;
|
|
72
|
+
return "stop";
|
|
73
|
+
}
|
|
74
|
+
return undefined;
|
|
75
|
+
});
|
|
76
|
+
return returned ? lowerer.ctx.returnType : null;
|
|
77
|
+
}
|
|
78
|
+
/** A JS loop cursor in opted package source can be initialized with `T`
|
|
79
|
+
* and later assigned a nullable self link (`cursor = cursor.parent`). Keep
|
|
80
|
+
* that optional value in the local so the next loop condition, rather than
|
|
81
|
+
* the assignment, narrows it. */
|
|
82
|
+
function npmStaticOptionalSelfWriteType(lowerer, decl, initial) {
|
|
83
|
+
if (!ts.isIdentifier(decl.name) ||
|
|
84
|
+
!isJsSourceFile(decl.getSourceFile()) ||
|
|
85
|
+
npmStaticPackageOfPath(decl.getSourceFile().fileName) === null ||
|
|
86
|
+
initial.kind === "union") {
|
|
87
|
+
return null;
|
|
88
|
+
}
|
|
89
|
+
const symbol = lowerer.checker.getSymbolAtLocation(decl.name);
|
|
90
|
+
if (symbol === undefined)
|
|
91
|
+
return null;
|
|
92
|
+
let owner = decl.parent;
|
|
93
|
+
while (owner !== undefined && !ts.isFunctionLike(owner))
|
|
94
|
+
owner = owner.parent;
|
|
95
|
+
const ownerBody = owner !== undefined && "body" in owner ? owner.body : undefined;
|
|
96
|
+
if (ownerBody === undefined || !ts.isBlock(ownerBody))
|
|
97
|
+
return null;
|
|
98
|
+
let candidate = null;
|
|
99
|
+
let invalid = false;
|
|
100
|
+
ts.walkPreorder(ownerBody, (node) => {
|
|
101
|
+
if (node !== ownerBody && ts.isFunctionLike(node))
|
|
102
|
+
return "skip";
|
|
103
|
+
if (ts.isBinaryExpression(node)) {
|
|
104
|
+
let left = node.left;
|
|
105
|
+
while (ts.isParenthesizedExpression(left))
|
|
106
|
+
left = left.expression;
|
|
107
|
+
if (ts.isIdentifier(left) && lowerer.checker.getSymbolAtLocation(left) === symbol) {
|
|
108
|
+
if (node.operatorToken.kind !== ts.SyntaxKind.EqualsToken) {
|
|
109
|
+
invalid = true;
|
|
110
|
+
return "stop";
|
|
111
|
+
}
|
|
112
|
+
const written = lowerer.mapTypeOf(lowerer.typeOf(node.right));
|
|
113
|
+
if (written !== null && typeEquals(written, initial))
|
|
114
|
+
return undefined;
|
|
115
|
+
const arms = written?.kind === "union" ? lowerer.unions.get(written.unionId)?.arms : undefined;
|
|
116
|
+
if (written?.kind !== "union" ||
|
|
117
|
+
arms === undefined ||
|
|
118
|
+
!arms.some((arm) => typeEquals(arm, initial)) ||
|
|
119
|
+
!arms.every((arm) => typeEquals(arm, initial) || isUnitType(arm)) ||
|
|
120
|
+
(candidate !== null && !typeEquals(candidate, written))) {
|
|
121
|
+
invalid = true;
|
|
122
|
+
return "stop";
|
|
123
|
+
}
|
|
124
|
+
candidate = written;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
if ((ts.isPrefixUnaryExpression(node) || ts.isPostfixUnaryExpression(node)) &&
|
|
128
|
+
ts.isIdentifier(node.operand) &&
|
|
129
|
+
lowerer.checker.getSymbolAtLocation(node.operand) === symbol) {
|
|
130
|
+
invalid = true;
|
|
131
|
+
return "stop";
|
|
132
|
+
}
|
|
133
|
+
return undefined;
|
|
134
|
+
});
|
|
135
|
+
return invalid ? null : candidate;
|
|
136
|
+
}
|
|
137
|
+
function isAwaitUsing(list) {
|
|
138
|
+
return (list.flags & ts.NodeFlags.AwaitUsing) === ts.NodeFlags.AwaitUsing;
|
|
139
|
+
}
|
|
140
|
+
function stdlibTypeNamed(lowerer, node, name) {
|
|
141
|
+
const visit = (t) => {
|
|
142
|
+
if (t.flags & ts.TypeFlags.Union)
|
|
143
|
+
return constituentTypes(t).some(visit);
|
|
144
|
+
const sym = t.getAliasSymbol() ?? t.getSymbol();
|
|
145
|
+
return sym?.name === name && lowerer.checker.declarationsOf(sym).some((d) => lowerer.isStdlibFile(d.getSourceFile()));
|
|
146
|
+
};
|
|
147
|
+
return visit(lowerer.typeOf(node));
|
|
148
|
+
}
|
|
149
|
+
function cleanupCall(lowerer, node, value, awaited) {
|
|
150
|
+
const loc = locOf(node);
|
|
151
|
+
if (value.type.kind === "object") {
|
|
152
|
+
const info = lowerer.classes.get(value.type.className);
|
|
153
|
+
if (!info)
|
|
154
|
+
lowerer.flushDeferredClass(value.type.className);
|
|
155
|
+
const asyncFound = awaited && info ? lowerer.findMethodOn(info, "sym:asyncDispose") : null;
|
|
156
|
+
const syncFound = info ? lowerer.findMethodOn(info, "sym:dispose") : null;
|
|
157
|
+
const member = asyncFound ? "sym:asyncDispose" : syncFound ? "sym:dispose" : null;
|
|
158
|
+
const found = asyncFound ?? syncFound;
|
|
159
|
+
if (!info || !member || !found)
|
|
160
|
+
return null;
|
|
161
|
+
if (found.sig.params.length !== 0) {
|
|
162
|
+
lowerer.unsupported("SC1090", node, `${member === "sym:asyncDispose" ? "[Symbol.asyncDispose]" : "[Symbol.dispose]"} methods with parameters`);
|
|
163
|
+
}
|
|
164
|
+
return {
|
|
165
|
+
call: lowerer.accessorCall(value.type.className, member, value, [], found.sig.ret, loc),
|
|
166
|
+
async: member === "sym:asyncDispose",
|
|
167
|
+
};
|
|
168
|
+
}
|
|
169
|
+
if (value.type.kind === "fileHandle") {
|
|
170
|
+
if (!awaited)
|
|
171
|
+
lowerer.unsupported("SC1090", node, "synchronous 'using' with an async-only FileHandle (use 'await using')");
|
|
172
|
+
return {
|
|
173
|
+
call: { kind: "libCall", fn: "fileHandle.close", args: [value], type: { kind: "promise", inner: VOID }, loc },
|
|
174
|
+
async: true,
|
|
175
|
+
};
|
|
176
|
+
}
|
|
177
|
+
if (value.type.kind === "child") {
|
|
178
|
+
return {
|
|
179
|
+
call: {
|
|
180
|
+
kind: "libCall",
|
|
181
|
+
fn: "child.kill",
|
|
182
|
+
args: [value, { kind: "strLit", value: "SIGTERM", type: STRING, loc }],
|
|
183
|
+
type: BOOL,
|
|
184
|
+
loc,
|
|
185
|
+
},
|
|
186
|
+
async: false,
|
|
187
|
+
};
|
|
188
|
+
}
|
|
189
|
+
if (value.type.kind === "f64") {
|
|
190
|
+
if (stdlibTypeNamed(lowerer, node, "Timeout")) {
|
|
191
|
+
return { call: { kind: "libCall", fn: "timers.clearTimeout", args: [value], type: VOID, loc }, async: false };
|
|
192
|
+
}
|
|
193
|
+
if (stdlibTypeNamed(lowerer, node, "Immediate")) {
|
|
194
|
+
return { call: { kind: "libCall", fn: "timers.clearImmediate", args: [value], type: VOID, loc }, async: false };
|
|
195
|
+
}
|
|
196
|
+
if (stdlibTypeNamed(lowerer, node, "Interface")) {
|
|
197
|
+
return { call: { kind: "libCall", fn: "rl.close", args: [value], type: VOID, loc }, async: false };
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
return null;
|
|
201
|
+
}
|
|
202
|
+
function cleanupForValue(lowerer, node, value, awaited) {
|
|
203
|
+
const loc = locOf(node);
|
|
204
|
+
if (isUnitType(value.type))
|
|
205
|
+
return [];
|
|
206
|
+
if (value.type.kind === "union") {
|
|
207
|
+
const def = lowerer.unions.get(value.type.unionId);
|
|
208
|
+
if (!def)
|
|
209
|
+
throw new InternalCompilerError(`lowerer bug: unknown using union ${value.type.unionId}`);
|
|
210
|
+
const out = [];
|
|
211
|
+
for (let tag = 0; tag < def.arms.length; tag++) {
|
|
212
|
+
const arm = def.arms[tag];
|
|
213
|
+
if (isUnitType(arm))
|
|
214
|
+
continue;
|
|
215
|
+
const narrowed = { kind: "unionNarrow", unionId: value.type.unionId, tag, value, type: arm, loc };
|
|
216
|
+
const body = cleanupForValue(lowerer, node, narrowed, awaited);
|
|
217
|
+
out.push({
|
|
218
|
+
kind: "if",
|
|
219
|
+
cond: { kind: "unionIsTag", unionId: value.type.unionId, tag, negated: false, value, type: BOOL, loc },
|
|
220
|
+
then: body,
|
|
221
|
+
else_: null,
|
|
222
|
+
loc,
|
|
223
|
+
});
|
|
224
|
+
}
|
|
225
|
+
return out;
|
|
226
|
+
}
|
|
227
|
+
const cleanup = cleanupCall(lowerer, node, value, awaited);
|
|
228
|
+
if (!cleanup) {
|
|
229
|
+
lowerer.unsupported("SC1090", node, `disposing values of type '${lowerer.fmt(value.type)}' (use a class with a zero-parameter [Symbol.dispose] or [Symbol.asyncDispose] method, or a supported Node resource)`);
|
|
230
|
+
}
|
|
231
|
+
const call = cleanup.call;
|
|
232
|
+
if (cleanup.async) {
|
|
233
|
+
if (call.type.kind !== "promise" || call.type.inner.kind !== "void") {
|
|
234
|
+
lowerer.unsupported("SC1090", node, "[Symbol.asyncDispose] methods whose result is not Promise<void>");
|
|
235
|
+
}
|
|
236
|
+
return [{ kind: "exprStmt", expr: { kind: "awaitExpr", value: call, type: VOID, loc }, loc }];
|
|
237
|
+
}
|
|
238
|
+
const out = [{ kind: "exprStmt", expr: call, loc }];
|
|
239
|
+
if (awaited)
|
|
240
|
+
out.push({ kind: "exprStmt", expr: { kind: "libCall", fn: "async.hop", args: [], type: VOID, loc }, loc });
|
|
241
|
+
return out;
|
|
242
|
+
}
|
|
243
|
+
function lowerUsingStatement(lowerer, stmt) {
|
|
244
|
+
if (ts.isSourceFile(stmt.parent)) {
|
|
245
|
+
lowerer.unsupported("SC1090", stmt, "top-level 'using' declarations (wrap the resource lifetime in a block or function)");
|
|
246
|
+
}
|
|
247
|
+
if (ts.isCaseClause(stmt.parent) || ts.isDefaultClause(stmt.parent)) {
|
|
248
|
+
lowerer.unsupported("SC1090", stmt, "'using' declarations directly in switch clauses (wrap the clause body in a block)");
|
|
249
|
+
}
|
|
250
|
+
const awaited = isAwaitUsing(stmt.declarationList);
|
|
251
|
+
if (awaited && !lowerer.ctx.isAsync) {
|
|
252
|
+
lowerer.unsupported("SC1090", stmt, "'await using' outside an async function or top-level-await module");
|
|
253
|
+
}
|
|
254
|
+
const registrations = [];
|
|
255
|
+
for (const decl of stmt.declarationList.declarations) {
|
|
256
|
+
if (!ts.isIdentifier(decl.name))
|
|
257
|
+
lowerer.unsupported("SC1031", decl.name);
|
|
258
|
+
if (decl.initializer === undefined)
|
|
259
|
+
lowerer.unsupported("SC1090", decl, "a 'using' declaration without an initializer");
|
|
260
|
+
const acquire = lowerer.lowerVarDecl(decl, false);
|
|
261
|
+
if (!acquire)
|
|
262
|
+
throw new InternalCompilerError("lowerer bug: using declaration emitted no acquisition");
|
|
263
|
+
const symbol = lowerer.checker.getSymbolAtLocation(decl.name);
|
|
264
|
+
if (!symbol)
|
|
265
|
+
throw new InternalCompilerError("lowerer bug: using declaration has no symbol");
|
|
266
|
+
const binding = lowerer.bindingIn(lowerer.ctx, symbol) ?? lowerer.globalsBySymbol.get(symbol) ?? null;
|
|
267
|
+
if (!binding)
|
|
268
|
+
throw new InternalCompilerError("lowerer bug: using declaration has no runtime binding");
|
|
269
|
+
const value = varRef(binding.id, binding.type, locOf(decl.name));
|
|
270
|
+
const cleanup = cleanupForValue(lowerer, decl.name, value, awaited);
|
|
271
|
+
registrations.push({ acquire, cleanup, loc: locOf(decl) });
|
|
272
|
+
}
|
|
273
|
+
return registrations;
|
|
274
|
+
}
|
|
32
275
|
/** `const X = /* @__PURE__ *\/ makeX()` at a module's top level: every
|
|
33
276
|
* declarator initialized by a call (or `new`) carrying the bundler PURE
|
|
34
277
|
* annotation in its leading trivia — the shape --provenance-sources may
|
|
@@ -113,6 +356,7 @@ export function provenanceElidedConstDecl(lowerer, decl) {
|
|
|
113
356
|
* `let a = 1, b = a + 1;`) but is counted once. */
|
|
114
357
|
export function lowerStmts(lowerer, stmts) {
|
|
115
358
|
const out = [];
|
|
359
|
+
const usingMarkers = [];
|
|
116
360
|
// JAVASCRIPT sources defer their compile fences to runtime (the
|
|
117
361
|
// JS-input design: no annotations exist to change what lowers, so a
|
|
118
362
|
// statement whose construct has no static lowering compiles to a
|
|
@@ -144,7 +388,25 @@ export function lowerStmts(lowerer, stmts) {
|
|
|
144
388
|
}
|
|
145
389
|
const diagsBefore = lowerer.diags.length;
|
|
146
390
|
try {
|
|
147
|
-
|
|
391
|
+
let lowered;
|
|
392
|
+
let appended = false;
|
|
393
|
+
if (ts.isVariableStatement(stmt) &&
|
|
394
|
+
(stmt.declarationList.flags & ts.NodeFlags.Using) !== 0) {
|
|
395
|
+
const registrations = lowerUsingStatement(lowerer, stmt);
|
|
396
|
+
const accounting = [];
|
|
397
|
+
for (const registration of registrations) {
|
|
398
|
+
out.push(registration.acquire);
|
|
399
|
+
accounting.push(registration.acquire, ...registration.cleanup);
|
|
400
|
+
if (registration.cleanup.length > 0) {
|
|
401
|
+
usingMarkers.push({ start: out.length, cleanup: registration.cleanup, loc: registration.loc });
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
lowered = accounting;
|
|
405
|
+
appended = true;
|
|
406
|
+
}
|
|
407
|
+
else {
|
|
408
|
+
lowered = lowerer.lowerStmt(stmt);
|
|
409
|
+
}
|
|
148
410
|
// The lib-boundary chokepoint (lib-boundary.ts): checked-dynamic
|
|
149
411
|
// arguments that reached builtin-call slots without the checked
|
|
150
412
|
// coercion get their dynCheck here, and the uncoercible ones
|
|
@@ -153,10 +415,12 @@ export function lowerStmts(lowerer, stmts) {
|
|
|
153
415
|
// deferred rejection.
|
|
154
416
|
if (lowered)
|
|
155
417
|
enforceLibBoundary(lowerer, lowered);
|
|
156
|
-
if (
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
418
|
+
if (!appended) {
|
|
419
|
+
if (Array.isArray(lowered))
|
|
420
|
+
out.push(...lowered);
|
|
421
|
+
else if (lowered)
|
|
422
|
+
out.push(lowered);
|
|
423
|
+
}
|
|
160
424
|
// Island accounting (--dynamic coverage): a statement that lowered
|
|
161
425
|
// but carries island constructs compiles DYNAMICALLY — its work
|
|
162
426
|
// runs in the embedded engine, and the report says so instead of
|
|
@@ -215,6 +479,23 @@ export function lowerStmts(lowerer, stmts) {
|
|
|
215
479
|
}
|
|
216
480
|
}
|
|
217
481
|
}
|
|
482
|
+
// Each acquisition protects the remainder of its lexical statement
|
|
483
|
+
// list. Folding from the last registration outward produces the
|
|
484
|
+
// spec's LIFO nesting and ensures a later initializer failure only
|
|
485
|
+
// disposes resources acquired before it.
|
|
486
|
+
for (let i = usingMarkers.length - 1; i >= 0; i--) {
|
|
487
|
+
const marker = usingMarkers[i];
|
|
488
|
+
const body = out.splice(marker.start);
|
|
489
|
+
out.push({
|
|
490
|
+
kind: "tryCatch",
|
|
491
|
+
tryBody: body,
|
|
492
|
+
catchBody: null,
|
|
493
|
+
catchLocalId: null,
|
|
494
|
+
finallyBody: marker.cleanup,
|
|
495
|
+
suppressFinallyErrors: true,
|
|
496
|
+
loc: marker.loc,
|
|
497
|
+
});
|
|
498
|
+
}
|
|
218
499
|
}
|
|
219
500
|
finally {
|
|
220
501
|
lowerer.activeStmtLists.pop();
|
|
@@ -589,44 +870,6 @@ export function lowerScopedBlock(lowerer, stmt) {
|
|
|
589
870
|
lowerer.scopes.pop();
|
|
590
871
|
}
|
|
591
872
|
}
|
|
592
|
-
/** The finally fences. `return` crossing OUT of a try/catch body guarded
|
|
593
|
-
* by a finally is SUPPORTED now (the backend's pending-return path runs
|
|
594
|
-
* every crossed finally inner-to-outer before the function returns, the
|
|
595
|
-
* value snapshotted first — Node-exact); what stays rejected is (a) any
|
|
596
|
-
* jump out of a finally BLOCK itself (a return there would REPLACE a
|
|
597
|
-
* pending completion — clearing a still-propagating exception or
|
|
598
|
-
* abandoning a pending return, a model the emitter doesn't implement),
|
|
599
|
-
* and (b) break/continue crossing a try-with-finally (their targets bind
|
|
600
|
-
* inside the function; the pending-action plumbing exists only for
|
|
601
|
-
* return, the shape real code needs). Rejected, not miscompiled. Plain
|
|
602
|
-
* try/catch is transparent to jumps (it never appears in ctl). A LABELED
|
|
603
|
-
* jump walks outward past every construct (labeled targets can be
|
|
604
|
-
* arbitrarily far out) until the entry carrying its label, applying the
|
|
605
|
-
* same finally fences to everything crossed on the way. */
|
|
606
|
-
export function rejectJumpCrossingFinally(lowerer, kw, stmt, label) {
|
|
607
|
-
const ctl = lowerer.ctx.ctl;
|
|
608
|
-
for (let i = ctl.length - 1; i >= 0; i--) {
|
|
609
|
-
const c = ctl[i];
|
|
610
|
-
if (c.kind === "finallyBlock") {
|
|
611
|
-
lowerer.unsupported("SC1090", stmt, `'${kw}' out of a 'finally' block (it would replace the pending completion — restructure so the finally only cleans up)`);
|
|
612
|
-
}
|
|
613
|
-
if (c.kind === "tryFinally") {
|
|
614
|
-
if (kw !== "return") {
|
|
615
|
-
lowerer.unsupported("SC1090", stmt, `'${kw}' crossing a 'finally' block`);
|
|
616
|
-
}
|
|
617
|
-
continue; // return runs the finally on the way out — supported
|
|
618
|
-
}
|
|
619
|
-
if (kw === "return")
|
|
620
|
-
continue; // return crosses every construct
|
|
621
|
-
if (label !== undefined) {
|
|
622
|
-
if (c.labels?.includes(label))
|
|
623
|
-
return; // the labeled target — binds here
|
|
624
|
-
continue; // an inner construct the labeled jump exits
|
|
625
|
-
}
|
|
626
|
-
if (c.kind === "loop" || (kw === "break" && c.kind === "switch"))
|
|
627
|
-
return; // binds inside the region
|
|
628
|
-
}
|
|
629
|
-
}
|
|
630
873
|
/** `lbl: stmt` — labeled statements. A label chain collapses to one name
|
|
631
874
|
* list (`a: b: while ...` — both names target the same loop, JS-exact).
|
|
632
875
|
* Loops and switches take the labels DIRECTLY (a labeled continue needs
|
|
@@ -720,7 +963,6 @@ export function lowerStmt(lowerer, stmt) {
|
|
|
720
963
|
if (ts.isLabeledStatement(stmt))
|
|
721
964
|
return lowerLabeled(lowerer, stmt);
|
|
722
965
|
if (ts.isReturnStatement(stmt)) {
|
|
723
|
-
lowerer.rejectJumpCrossingFinally("return", stmt);
|
|
724
966
|
// Implicit-any instance RETURN INFERENCE (lower-calls'
|
|
725
967
|
// resolveInferredReturn): the value lowers BARE and the statement
|
|
726
968
|
// records itself — the post-pass settles the instance's return type
|
|
@@ -784,10 +1026,8 @@ export function lowerStmt(lowerer, stmt) {
|
|
|
784
1026
|
if (!target || (kw === "continue" && target.kind !== "loop")) {
|
|
785
1027
|
lowerer.unsupported("SC1050", stmt, `'${kw} ${label}' targeting this statement form (the labeled statement lowers through a desugar that has no label point)`);
|
|
786
1028
|
}
|
|
787
|
-
lowerer.rejectJumpCrossingFinally(kw, stmt, label);
|
|
788
1029
|
return { kind: kw, label, loc: locOf(stmt) };
|
|
789
1030
|
}
|
|
790
|
-
lowerer.rejectJumpCrossingFinally(kw, stmt);
|
|
791
1031
|
// tsc rejects break outside loops/switches and continue outside loops,
|
|
792
1032
|
// so the context is always valid here (the validator re-checks).
|
|
793
1033
|
return { kind: kw, loc: locOf(stmt) };
|
|
@@ -2904,6 +3144,95 @@ export function lowerVarDeclList(lowerer, list) {
|
|
|
2904
3144
|
throw new InternalCompilerError("lowerer bug: for-init declarator resolved to a global");
|
|
2905
3145
|
return lowered;
|
|
2906
3146
|
}
|
|
3147
|
+
/** A single static type for an initializer-less JavaScript `let` whose
|
|
3148
|
+
* later plain writes all agree. An evolving JS local reads as `undefined`
|
|
3149
|
+
* until control flow proves a write, so a scalar slot is sound only when
|
|
3150
|
+
* every ordinary use already has that concrete flow type. Any closure
|
|
3151
|
+
* capture stays checked-dynamic: it can observe the local before an outer
|
|
3152
|
+
* assignment, and the checker deliberately leaves captured evolving lets
|
|
3153
|
+
* broad. */
|
|
3154
|
+
function inferredEvolvingLetType(lowerer, decl) {
|
|
3155
|
+
if (!ts.isIdentifier(decl.name))
|
|
3156
|
+
return null;
|
|
3157
|
+
const symbol = lowerer.checker.getSymbolAtLocation(decl.name);
|
|
3158
|
+
const scope = decl.parent.parent.parent;
|
|
3159
|
+
if (symbol === undefined || (!ts.isBlock(scope) && !ts.isSourceFile(scope)))
|
|
3160
|
+
return null;
|
|
3161
|
+
const isPlainAssignmentTarget = (node) => {
|
|
3162
|
+
let target = node;
|
|
3163
|
+
while (ts.isParenthesizedExpression(target.parent) && target.parent.expression === target) {
|
|
3164
|
+
target = target.parent;
|
|
3165
|
+
}
|
|
3166
|
+
const parent = target.parent;
|
|
3167
|
+
return ts.isBinaryExpression(parent) &&
|
|
3168
|
+
parent.operatorToken.kind === ts.SyntaxKind.EqualsToken &&
|
|
3169
|
+
parent.left === target;
|
|
3170
|
+
};
|
|
3171
|
+
const captures = (fn) => {
|
|
3172
|
+
let captured = false;
|
|
3173
|
+
ts.walkPreorder(fn, (node) => {
|
|
3174
|
+
if (node !== fn &&
|
|
3175
|
+
ts.isIdentifier(node) &&
|
|
3176
|
+
lowerer.checker.getSymbolAtLocation(node) === symbol) {
|
|
3177
|
+
captured = true;
|
|
3178
|
+
return "stop";
|
|
3179
|
+
}
|
|
3180
|
+
return undefined;
|
|
3181
|
+
});
|
|
3182
|
+
return captured;
|
|
3183
|
+
};
|
|
3184
|
+
const writes = [];
|
|
3185
|
+
const reads = [];
|
|
3186
|
+
let invalid = false;
|
|
3187
|
+
ts.walkPreorder(scope, (node) => {
|
|
3188
|
+
if (node !== scope && ts.isFunctionLike(node)) {
|
|
3189
|
+
if (captures(node))
|
|
3190
|
+
invalid = true;
|
|
3191
|
+
return "skip";
|
|
3192
|
+
}
|
|
3193
|
+
if (ts.isBinaryExpression(node)) {
|
|
3194
|
+
let left = node.left;
|
|
3195
|
+
while (ts.isParenthesizedExpression(left))
|
|
3196
|
+
left = left.expression;
|
|
3197
|
+
if (ts.isIdentifier(left) && lowerer.checker.getSymbolAtLocation(left) === symbol) {
|
|
3198
|
+
if (node.operatorToken.kind !== ts.SyntaxKind.EqualsToken) {
|
|
3199
|
+
invalid = true;
|
|
3200
|
+
return "stop";
|
|
3201
|
+
}
|
|
3202
|
+
if (ts.isCallExpression(node.right) && implicitMethodCallInfersReturn(lowerer, node.right)) {
|
|
3203
|
+
invalid = true;
|
|
3204
|
+
return "stop";
|
|
3205
|
+
}
|
|
3206
|
+
const mapped = lowerer.mapTypeOf(lowerer.typeOf(node.right));
|
|
3207
|
+
if (!mapped || mapped.kind === "dyn" || mapped.kind === "jsval" || mapped.kind === "void" || isUnitType(mapped)) {
|
|
3208
|
+
invalid = true;
|
|
3209
|
+
return "stop";
|
|
3210
|
+
}
|
|
3211
|
+
writes.push(mapped);
|
|
3212
|
+
}
|
|
3213
|
+
}
|
|
3214
|
+
if ((ts.isPrefixUnaryExpression(node) || ts.isPostfixUnaryExpression(node)) &&
|
|
3215
|
+
ts.isIdentifier(node.operand) &&
|
|
3216
|
+
lowerer.checker.getSymbolAtLocation(node.operand) === symbol) {
|
|
3217
|
+
invalid = true;
|
|
3218
|
+
return "stop";
|
|
3219
|
+
}
|
|
3220
|
+
if (ts.isIdentifier(node) &&
|
|
3221
|
+
node !== decl.name &&
|
|
3222
|
+
lowerer.checker.getSymbolAtLocation(node) === symbol &&
|
|
3223
|
+
!isPlainAssignmentTarget(node)) {
|
|
3224
|
+
reads.push(node);
|
|
3225
|
+
}
|
|
3226
|
+
return undefined;
|
|
3227
|
+
});
|
|
3228
|
+
const first = writes[0];
|
|
3229
|
+
if (invalid || first === undefined || !writes.every((write) => typeEquals(write, first)))
|
|
3230
|
+
return null;
|
|
3231
|
+
return reads.every((read) => {
|
|
3232
|
+
const mapped = lowerer.mapTypeOf(lowerer.typeOf(read));
|
|
3233
|
+
return mapped !== null && typeEquals(mapped, first);
|
|
3234
|
+
}) ? first : null;
|
|
3235
|
+
}
|
|
2907
3236
|
export function lowerVarDecl(lowerer, decl, isLet) {
|
|
2908
3237
|
// --provenance-sources: an elided pure-annotated dead const emits
|
|
2909
3238
|
// nothing (collectGlobals registered no global by the same test —
|
|
@@ -3163,6 +3492,9 @@ export function lowerVarDecl(lowerer, decl, isLet) {
|
|
|
3163
3492
|
// immediately (the type covers the unassigned state), so they
|
|
3164
3493
|
// initialize to the interned undefined arm like an omitted optional.
|
|
3165
3494
|
let type = lowerer.irTypeOf(decl.name);
|
|
3495
|
+
if (type.kind === "dyn" && isLet && isJsSourceFile(decl.getSourceFile())) {
|
|
3496
|
+
type = inferredEvolvingLetType(lowerer, decl) ?? type;
|
|
3497
|
+
}
|
|
3166
3498
|
// `var v: void;` / `let x: undefined;` — a unit-only binding rides
|
|
3167
3499
|
// the unit-only union (its undefined arm is the unassigned state,
|
|
3168
3500
|
// which is also the only state).
|
|
@@ -3233,6 +3565,12 @@ export function lowerVarDecl(lowerer, decl, isLet) {
|
|
|
3233
3565
|
(lowerer.dynamic && init.type.kind === "dyn" && jsvalFlavoredType(lowerer.mapTypeOf(lowerer.typeOf(decl.name)) ?? DYN) ? DYN : null) ??
|
|
3234
3566
|
(bindingTainted ? null : lowerer.mapTypeOf(lowerer.typeOf(decl.name))) ??
|
|
3235
3567
|
(init.type.kind === "dyn" ? DYN : null);
|
|
3568
|
+
if (lowerer.implicitParamTypes !== null &&
|
|
3569
|
+
init.type.kind === "dyn" &&
|
|
3570
|
+
isJsSourceFile(decl.getSourceFile()) &&
|
|
3571
|
+
npmStaticPackageOfPath(decl.getSourceFile().fileName) !== null) {
|
|
3572
|
+
type = DYN;
|
|
3573
|
+
}
|
|
3236
3574
|
// A JS `let x = {}`: TS's empty-object-literal type admits ANY later
|
|
3237
3575
|
// non-nullish assignment (`envs = {}`, later `envs =
|
|
3238
3576
|
// Object.fromEntries(...)` — tsc accepts every such write, since
|
|
@@ -3245,6 +3583,11 @@ export function lowerVarDecl(lowerer, decl, isLet) {
|
|
|
3245
3583
|
if (shape && shape.fields.length === 0 && !shape.indexValue && !shape.tuple)
|
|
3246
3584
|
type = DYN;
|
|
3247
3585
|
}
|
|
3586
|
+
const returnedEmptyArrayType = npmStaticReturnedEmptyArrayType(lowerer, decl);
|
|
3587
|
+
if (returnedEmptyArrayType !== null) {
|
|
3588
|
+
type = returnedEmptyArrayType;
|
|
3589
|
+
init = lowerer.lowerExprExpecting(decl.initializer, returnedEmptyArrayType);
|
|
3590
|
+
}
|
|
3248
3591
|
// A JS `const leaked = [];` (the evolving-array idiom — test/common's
|
|
3249
3592
|
// leak ledger): tsc types the binding by its LATER pushes, but this
|
|
3250
3593
|
// frontend answers the declaration name with the uninhabited never[],
|
|
@@ -3254,7 +3597,9 @@ export function lowerVarDecl(lowerer, decl, isLet) {
|
|
|
3254
3597
|
// checked-dynamic instead (the `let x = {}` stance, and lower-modules'
|
|
3255
3598
|
// file-scope evolving-array rule): pushes ride the dyn array, typed
|
|
3256
3599
|
// exits dynCheck.
|
|
3257
|
-
if (
|
|
3600
|
+
if (returnedEmptyArrayType === null &&
|
|
3601
|
+
(type === null || type.kind === "array") &&
|
|
3602
|
+
isJsSourceFile(decl.getSourceFile())) {
|
|
3258
3603
|
let initExpr = decl.initializer;
|
|
3259
3604
|
while (ts.isParenthesizedExpression(initExpr))
|
|
3260
3605
|
initExpr = initExpr.expression;
|
|
@@ -3316,14 +3661,15 @@ export function lowerVarDecl(lowerer, decl, isLet) {
|
|
|
3316
3661
|
// their tuple record likewise). Const only — an evolving-`any`
|
|
3317
3662
|
// `let` may be reassigned a different shape later; genuine `any`
|
|
3318
3663
|
// only — every other unmappable keeps its own diagnostic.
|
|
3319
|
-
|
|
3320
|
-
|
|
3664
|
+
const checkerAny = (lowerer.typeOf(decl.name).flags & ts.TypeFlags.Any) !== 0;
|
|
3665
|
+
if ((type === null || (type.kind === "dyn" && checkerAny)) && !isLet &&
|
|
3666
|
+
(checkerAny ||
|
|
3321
3667
|
(lowerer.checkerAnyArray(decl.name) && lowerer.isArrayValueType(init.type)) ||
|
|
3322
3668
|
// JS declarations carry no annotations: an unmappable inferred
|
|
3323
3669
|
// type (a union with a fence-folded arm — the typeof-'bigint'
|
|
3324
3670
|
// dual-mode ternary) adopts the initializer's static type, the
|
|
3325
3671
|
// same rule genuine `any` consts take.
|
|
3326
|
-
isJsSourceFile(decl.getSourceFile())) &&
|
|
3672
|
+
(type === null && isJsSourceFile(decl.getSourceFile()))) &&
|
|
3327
3673
|
init.type.kind !== "void" && init.type.kind !== "caught" && init.type.kind !== "jsval" &&
|
|
3328
3674
|
!isUnitType(init.type)) {
|
|
3329
3675
|
type = init.type;
|
|
@@ -3338,6 +3684,7 @@ export function lowerVarDecl(lowerer, decl, isLet) {
|
|
|
3338
3684
|
}
|
|
3339
3685
|
if (!type)
|
|
3340
3686
|
lowerer.badType(decl.name, lowerer.typeOf(decl.name));
|
|
3687
|
+
let settledType = type;
|
|
3341
3688
|
const arithmeticType = decl.initializer ? lowerer.runtimeOptionalArithmeticTypes.get(decl.initializer) : undefined;
|
|
3342
3689
|
const isStringArithmeticUnion = (t) => {
|
|
3343
3690
|
if (t.kind !== "union")
|
|
@@ -3346,35 +3693,42 @@ export function lowerVarDecl(lowerer, decl, isLet) {
|
|
|
3346
3693
|
return !!arms && arms.length === 2 &&
|
|
3347
3694
|
arms.some((a) => a.kind === "f64") && arms.some((a) => a.kind === "string");
|
|
3348
3695
|
};
|
|
3349
|
-
const runtimeStringArithmetic =
|
|
3696
|
+
const runtimeStringArithmetic = settledType.kind === "string" &&
|
|
3350
3697
|
((arithmeticType && typeEquals(arithmeticType, init.type)) || isStringArithmeticUnion(init.type));
|
|
3351
3698
|
if (runtimeStringArithmetic) {
|
|
3352
|
-
|
|
3699
|
+
settledType = init.type;
|
|
3353
3700
|
}
|
|
3354
|
-
|
|
3701
|
+
const optionalSelfWriteType = isLet
|
|
3702
|
+
? npmStaticOptionalSelfWriteType(lowerer, decl, settledType)
|
|
3703
|
+
: null;
|
|
3704
|
+
settledType = lowerer.runtimeOptionalBindingType(decl.name, settledType);
|
|
3705
|
+
if (optionalSelfWriteType !== null)
|
|
3706
|
+
settledType = optionalSelfWriteType;
|
|
3707
|
+
if (returnedEmptyArrayType !== null)
|
|
3708
|
+
settledType = returnedEmptyArrayType;
|
|
3355
3709
|
// `const x: void = undefined` / `let y: undefined = undefined`: the
|
|
3356
3710
|
// unit-only union — the initializer's unit literal wraps into its arm
|
|
3357
3711
|
// like any optional completion. Non-literal void initializers (a
|
|
3358
3712
|
// void CALL's result) keep their fence at the coercion below.
|
|
3359
|
-
if (
|
|
3360
|
-
|
|
3713
|
+
if (settledType.kind === "void" && isUnitOnlyTsType(lowerer.typeOf(decl.name))) {
|
|
3714
|
+
settledType = unitOnlyUnion(lowerer.unions);
|
|
3361
3715
|
}
|
|
3362
|
-
if (
|
|
3716
|
+
if (settledType.kind === "void")
|
|
3363
3717
|
lowerer.badType(decl.name, lowerer.typeOf(decl.name));
|
|
3364
3718
|
// A PACKAGE promise stored in a local stays an island HANDLE — the
|
|
3365
3719
|
// engine's promise has no static promise value to coerce into. Awaits
|
|
3366
3720
|
// and .catch/.finally chains on the local bridge per use site (each
|
|
3367
3721
|
// bridge is an independent observer of the same settlement).
|
|
3368
|
-
if (init.type.kind === "jsval" &&
|
|
3369
|
-
|
|
3722
|
+
if (init.type.kind === "jsval" && settledType.kind === "promise")
|
|
3723
|
+
settledType = JSVAL;
|
|
3370
3724
|
// An island value under an `any[]`-declared LOCAL spelling stays an
|
|
3371
3725
|
// island HANDLE too (the runtime-world rule): the jsval-element-array
|
|
3372
3726
|
// exit is a CALL-boundary conversion (the loadPlugins param ABI) —
|
|
3373
3727
|
// converting a local would strand the engine's own prototypes
|
|
3374
3728
|
// (join/map/... on a native handle-element array), where the handle
|
|
3375
3729
|
// routes every use engine-side.
|
|
3376
|
-
if (init.type.kind === "jsval" &&
|
|
3377
|
-
|
|
3730
|
+
if (init.type.kind === "jsval" && settledType.kind === "array" && settledType.elem.kind === "jsval") {
|
|
3731
|
+
settledType = JSVAL;
|
|
3378
3732
|
}
|
|
3379
3733
|
// A STATIC array of island HANDLES under an unannotated declared type
|
|
3380
3734
|
// spelling evolved elements (`const kept = fns.filter(...)` over an
|
|
@@ -3387,8 +3741,8 @@ export function lowerVarDecl(lowerer, decl, isLet) {
|
|
|
3387
3741
|
// validated-boundary fence.
|
|
3388
3742
|
if (!decl.type &&
|
|
3389
3743
|
init.type.kind === "array" && init.type.elem.kind === "jsval" &&
|
|
3390
|
-
|
|
3391
|
-
|
|
3744
|
+
settledType.kind === "array" && settledType.elem.kind !== "jsval") {
|
|
3745
|
+
settledType = init.type;
|
|
3392
3746
|
}
|
|
3393
3747
|
// `const runner = options.runner || defaultRunner`: the checker types
|
|
3394
3748
|
// the || as the union/merge of two structurally-compatible function
|
|
@@ -3398,13 +3752,13 @@ export function lowerVarDecl(lowerer, decl, isLet) {
|
|
|
3398
3752
|
// when the parameters agree and the declared return either contains
|
|
3399
3753
|
// the init's return as a union arm or is the opaque spawnRes the
|
|
3400
3754
|
// adapter converts (nothing coerces INTO either declared form).
|
|
3401
|
-
if (
|
|
3755
|
+
if (settledType.kind === "func" && init.type.kind === "func" && !typeEquals(settledType, init.type)) {
|
|
3402
3756
|
const initFn = init.type;
|
|
3403
|
-
const paramsAgree =
|
|
3404
|
-
|
|
3405
|
-
const retUnionArm =
|
|
3406
|
-
if (paramsAgree && (retUnionArm || lowerer.spawnResFnAdapterPlan(
|
|
3407
|
-
|
|
3757
|
+
const paramsAgree = settledType.params.length === initFn.params.length &&
|
|
3758
|
+
settledType.params.every((p, i) => typeEquals(p, initFn.params[i]));
|
|
3759
|
+
const retUnionArm = settledType.ret.kind === "union" && lowerer.armTag(settledType.ret.unionId, initFn.ret) >= 0;
|
|
3760
|
+
if (paramsAgree && (retUnionArm || lowerer.spawnResFnAdapterPlan(settledType, initFn) !== null)) {
|
|
3761
|
+
settledType = init.type;
|
|
3408
3762
|
}
|
|
3409
3763
|
}
|
|
3410
3764
|
// The downstream twin: `const result = runner(cmd, args)` where the
|
|
@@ -3413,12 +3767,12 @@ export function lowerVarDecl(lowerer, decl, isLet) {
|
|
|
3413
3767
|
// can narrow (spawnRes has no discriminant). The VALUE is statically
|
|
3414
3768
|
// the record arm, so the local adopts it; flows into the union slot
|
|
3415
3769
|
// re-wrap via the ordinary arm coercion.
|
|
3416
|
-
if (
|
|
3417
|
-
const arms = lowerer.unions.get(
|
|
3770
|
+
if (settledType.kind === "union" && init.type.kind === "record") {
|
|
3771
|
+
const arms = lowerer.unions.get(settledType.unionId)?.arms ?? [];
|
|
3418
3772
|
if (arms.length === 2 &&
|
|
3419
3773
|
arms.some((a) => a.kind === "spawnRes") &&
|
|
3420
3774
|
arms.some((a) => typeEquals(a, init.type))) {
|
|
3421
|
-
|
|
3775
|
+
settledType = init.type;
|
|
3422
3776
|
}
|
|
3423
3777
|
}
|
|
3424
3778
|
// `const r: Repo = new MemRepo()` over an all-generic-method
|
|
@@ -3426,8 +3780,8 @@ export function lowerVarDecl(lowerer, decl, isLet) {
|
|
|
3426
3780
|
// (the record shape maps empty and the width copy would drop the
|
|
3427
3781
|
// class the generic-method calls monomorphize against) — see
|
|
3428
3782
|
// genericIfaceBindingKeepsClass.
|
|
3429
|
-
if (
|
|
3430
|
-
|
|
3783
|
+
if (settledType.kind === "record" && init.type.kind === "object" && genericIfaceBindingKeepsClass(lowerer, decl, settledType)) {
|
|
3784
|
+
settledType = init.type;
|
|
3431
3785
|
}
|
|
3432
3786
|
// A TDZ box minted DURING this very initializer (a callback inside it
|
|
3433
3787
|
// captured this const — predeclareForwardCapture's current-statement
|
|
@@ -3442,8 +3796,8 @@ export function lowerVarDecl(lowerer, decl, isLet) {
|
|
|
3442
3796
|
}
|
|
3443
3797
|
// Slot coercion: `const r: A | B = bValue;` wraps implicitly; width
|
|
3444
3798
|
// subtyping (`const p: {a: number} = wider;`) is rejected, not coerced.
|
|
3445
|
-
init = lowerer.coerceInto(decl.initializer, init,
|
|
3446
|
-
const local = lowerer.declareLocal(decl.name, decl.name.text,
|
|
3799
|
+
init = lowerer.coerceInto(decl.initializer, init, settledType);
|
|
3800
|
+
const local = lowerer.declareLocal(decl.name, decl.name.text, settledType, isLet);
|
|
3447
3801
|
if (runtimeStringArithmetic && isStringArithmeticUnion(init.type)) {
|
|
3448
3802
|
lowerer.runtimeOptionalArithmeticLocals.add(lowerer.runtimeOptionalRootOf(local));
|
|
3449
3803
|
}
|
|
@@ -3545,9 +3899,8 @@ export function lowerSwitch(lowerer, stmt) {
|
|
|
3545
3899
|
const labels = lowerer.takeLabels();
|
|
3546
3900
|
const disc = lowerer.lowerExpr(stmt.expression);
|
|
3547
3901
|
const dk = disc.type.kind;
|
|
3548
|
-
if (dk === "dyn")
|
|
3549
|
-
lowerer
|
|
3550
|
-
}
|
|
3902
|
+
if (dk === "dyn")
|
|
3903
|
+
return lowerDynSwitch(lowerer, stmt, disc);
|
|
3551
3904
|
if (dk === "union")
|
|
3552
3905
|
return lowerUnionSwitch(lowerer, stmt, disc);
|
|
3553
3906
|
if (dk !== "f64" && dk !== "string" && dk !== "bool") {
|
|
@@ -3596,19 +3949,51 @@ export function lowerSwitch(lowerer, stmt) {
|
|
|
3596
3949
|
* is last).
|
|
3597
3950
|
* Case bodies share ONE lexical scope, exactly like the real switch. */
|
|
3598
3951
|
function lowerUnionSwitch(lowerer, stmt, disc) {
|
|
3599
|
-
const loc = locOf(stmt);
|
|
3600
3952
|
if (disc.type.kind !== "union")
|
|
3601
3953
|
throw new InternalCompilerError("lowerer bug: non-union disc");
|
|
3602
3954
|
const unionType = disc.type;
|
|
3603
3955
|
if (!lowerer.eqComparableUnion(unionType.unionId)) {
|
|
3604
3956
|
lowerer.unsupported("SC1090", stmt.expression, "switch on a union whose arms do not have static equality");
|
|
3605
3957
|
}
|
|
3958
|
+
return lowerBranchSwitch(lowerer, stmt, disc, (stableDisc, test, expression) => {
|
|
3959
|
+
const unitTest = test.kind === "unitLit"
|
|
3960
|
+
? lowerer.lowerUnitComparison(stableDisc, test, false, locOf(expression))
|
|
3961
|
+
: null;
|
|
3962
|
+
return unitTest ?? {
|
|
3963
|
+
kind: "unionEq",
|
|
3964
|
+
unionId: unionType.unionId,
|
|
3965
|
+
negated: false,
|
|
3966
|
+
sameValue: false,
|
|
3967
|
+
left: stableDisc,
|
|
3968
|
+
right: lowerer.coerceInto(expression, test, unionType),
|
|
3969
|
+
type: BOOL,
|
|
3970
|
+
loc: locOf(expression),
|
|
3971
|
+
};
|
|
3972
|
+
});
|
|
3973
|
+
}
|
|
3974
|
+
/** A checked-dynamic switch whose tests have native strict-equality
|
|
3975
|
+
* answers. Scalar tests use dynScalarEq; null/undefined use dyn kind tests.
|
|
3976
|
+
* Other case values retain the existing unknown-switch fence. */
|
|
3977
|
+
function lowerDynSwitch(lowerer, stmt, disc) {
|
|
3978
|
+
return lowerBranchSwitch(lowerer, stmt, disc, (stableDisc, test, expression) => {
|
|
3979
|
+
if (test.kind === "unitLit") {
|
|
3980
|
+
return { kind: "dynTest", test: test.unit, value: stableDisc, type: BOOL, loc: locOf(expression) };
|
|
3981
|
+
}
|
|
3982
|
+
if (test.type.kind !== "string" && test.type.kind !== "f64" &&
|
|
3983
|
+
test.type.kind !== "bool" && test.type.kind !== "dyn") {
|
|
3984
|
+
lowerer.unsupported("SC1100", expression, "switch case values that cannot compare strictly with 'unknown'");
|
|
3985
|
+
}
|
|
3986
|
+
return { kind: "dynScalarEq", left: stableDisc, right: test, type: BOOL, loc: locOf(expression) };
|
|
3987
|
+
});
|
|
3988
|
+
}
|
|
3989
|
+
function lowerBranchSwitch(lowerer, stmt, disc, compare) {
|
|
3990
|
+
const loc = locOf(stmt);
|
|
3606
3991
|
const prefix = [];
|
|
3607
3992
|
let stableDisc = disc;
|
|
3608
|
-
if (!
|
|
3609
|
-
const temp = lowerer.declareHiddenLocal("%switch",
|
|
3993
|
+
if (!isSafeToRepeat(disc)) {
|
|
3994
|
+
const temp = lowerer.declareHiddenLocal("%switch", disc.type);
|
|
3610
3995
|
prefix.push({ kind: "varDecl", localId: temp.id, init: disc, loc });
|
|
3611
|
-
stableDisc = { kind: "varRef", localId: temp.id, type:
|
|
3996
|
+
stableDisc = { kind: "varRef", localId: temp.id, type: disc.type, loc };
|
|
3612
3997
|
}
|
|
3613
3998
|
const clauses = stmt.caseBlock.clauses;
|
|
3614
3999
|
// An unlabeled break at a clause's END exits the switch — the chain's
|
|
@@ -3667,7 +4052,7 @@ function lowerUnionSwitch(lowerer, stmt, disc) {
|
|
|
3667
4052
|
// final else, so matching them changes nothing when pure).
|
|
3668
4053
|
if (test.kind !== "strLit" && test.kind !== "numLit" &&
|
|
3669
4054
|
test.kind !== "boolLit" && test.kind !== "unitLit" &&
|
|
3670
|
-
!
|
|
4055
|
+
!isSafeToRepeat(test)) {
|
|
3671
4056
|
lowerer.unsupported("SC1090", clause.expression, "effectful case tests in a union-typed switch (bind the test value to a const first)");
|
|
3672
4057
|
}
|
|
3673
4058
|
// A unit-literal test takes the unit-comparison lowering: a tag
|
|
@@ -3675,19 +4060,7 @@ function lowerUnionSwitch(lowerer, stmt, disc) {
|
|
|
3675
4060
|
// lacks it (`case null:` on a `number | undefined` — legal TS,
|
|
3676
4061
|
// never matches; coercing the literal into the union would hit
|
|
3677
4062
|
// the stranded-arm trap and throw where JS just skips the case).
|
|
3678
|
-
|
|
3679
|
-
? lowerer.lowerUnitComparison(stableDisc, test, false, locOf(clause.expression))
|
|
3680
|
-
: null;
|
|
3681
|
-
pendingTests.push(unitTest ?? {
|
|
3682
|
-
kind: "unionEq",
|
|
3683
|
-
unionId: unionType.unionId,
|
|
3684
|
-
negated: false,
|
|
3685
|
-
sameValue: false,
|
|
3686
|
-
left: stableDisc,
|
|
3687
|
-
right: lowerer.coerceInto(clause.expression, test, unionType),
|
|
3688
|
-
type: BOOL,
|
|
3689
|
-
loc: locOf(clause.expression),
|
|
3690
|
-
});
|
|
4063
|
+
pendingTests.push(compare(stableDisc, test, clause.expression));
|
|
3691
4064
|
}
|
|
3692
4065
|
const isDefault = ts.isDefaultClause(clause);
|
|
3693
4066
|
if (clause.statements.length === 0 && !isDefault && i < clauses.length - 1) {
|
|
@@ -3730,7 +4103,7 @@ function lowerUnionSwitch(lowerer, stmt, disc) {
|
|
|
3730
4103
|
lowerer.scopes.pop();
|
|
3731
4104
|
}
|
|
3732
4105
|
}
|
|
3733
|
-
/** try/catch/finally
|
|
4106
|
+
/** try/catch/finally:
|
|
3734
4107
|
* - `catch { }` (bindingless) discards the thrown value on entry.
|
|
3735
4108
|
* - `catch (e)` binds it as a CAUGHT local — a snapshot of the exception
|
|
3736
4109
|
* cell, deliberately narrower than dyn: the supported uses are the
|
|
@@ -3741,23 +4114,14 @@ function lowerUnionSwitch(lowerer, stmt, disc) {
|
|
|
3741
4114
|
* assignment are fenced (SC1063 and friends); destructuring patterns
|
|
3742
4115
|
* are SC1062. The `: any` / `: unknown` annotations tsc admits both
|
|
3743
4116
|
* lower the same way (tsc narrows either through the tests).
|
|
3744
|
-
* - `finally` runs on normal completion,
|
|
3745
|
-
*
|
|
3746
|
-
*
|
|
3747
|
-
*
|
|
3748
|
-
* break/continue crossing a try-with-finally stay rejected, as does
|
|
3749
|
-
* any jump out of the finally body itself (it would replace a pending
|
|
3750
|
-
* completion — rejectJumpCrossingFinally). Plain try/catch places no
|
|
3751
|
-
* restriction on jumps.
|
|
4117
|
+
* - `finally` runs on normal completion, exception paths, and every
|
|
4118
|
+
* abrupt completion that crosses it. Return values are snapshotted
|
|
4119
|
+
* before cleanup; an abrupt completion inside the finally replaces the
|
|
4120
|
+
* pending one, matching JavaScript completion-record semantics.
|
|
3752
4121
|
* Each block is its own lexical scope (the binding lives in a scope
|
|
3753
4122
|
* WRAPPING the catch block, like the spec's catch environment). */
|
|
3754
4123
|
export function lowerTry(lowerer, stmt) {
|
|
3755
|
-
const
|
|
3756
|
-
// try/catch bodies are jump-fenced only when a finally guards them.
|
|
3757
|
-
const lowerGuarded = (block) => hasFinally
|
|
3758
|
-
? lowerer.inCtl("tryFinally", () => lowerer.lowerScopedBlock(block))
|
|
3759
|
-
: lowerer.lowerScopedBlock(block);
|
|
3760
|
-
const tryBody = lowerGuarded(stmt.tryBlock);
|
|
4124
|
+
const tryBody = lowerer.lowerScopedBlock(stmt.tryBlock);
|
|
3761
4125
|
let catchBody = null;
|
|
3762
4126
|
let catchLocalId = null;
|
|
3763
4127
|
if (stmt.catchClause) {
|
|
@@ -3770,19 +4134,17 @@ export function lowerTry(lowerer, stmt) {
|
|
|
3770
4134
|
try {
|
|
3771
4135
|
const local = lowerer.declareLocal(vd.name, vd.name.text, CAUGHT, false);
|
|
3772
4136
|
catchLocalId = local.id;
|
|
3773
|
-
catchBody =
|
|
4137
|
+
catchBody = lowerer.lowerScopedBlock(stmt.catchClause.block);
|
|
3774
4138
|
}
|
|
3775
4139
|
finally {
|
|
3776
4140
|
lowerer.scopes.pop();
|
|
3777
4141
|
}
|
|
3778
4142
|
}
|
|
3779
4143
|
else {
|
|
3780
|
-
catchBody =
|
|
4144
|
+
catchBody = lowerer.lowerScopedBlock(stmt.catchClause.block);
|
|
3781
4145
|
}
|
|
3782
4146
|
}
|
|
3783
|
-
const finallyBody = stmt.finallyBlock
|
|
3784
|
-
? lowerer.inCtl("finallyBlock", () => lowerer.lowerScopedBlock(stmt.finallyBlock))
|
|
3785
|
-
: null;
|
|
4147
|
+
const finallyBody = stmt.finallyBlock ? lowerer.lowerScopedBlock(stmt.finallyBlock) : null;
|
|
3786
4148
|
return {
|
|
3787
4149
|
kind: "tryCatch",
|
|
3788
4150
|
tryBody,
|
|
@@ -3806,6 +4168,7 @@ function lowerDeleteStatement(lowerer, expr) {
|
|
|
3806
4168
|
if (!ts.isElementAccessExpression(target) && !ts.isPropertyAccessExpression(target)) {
|
|
3807
4169
|
lowerer.unsupported("SC1090", expr, "'delete' of non-property expressions");
|
|
3808
4170
|
}
|
|
4171
|
+
fenceNodeModuleMutation(lowerer, target, "delete");
|
|
3809
4172
|
const lowerKey = () => {
|
|
3810
4173
|
if (ts.isPropertyAccessExpression(target)) {
|
|
3811
4174
|
return { kind: "strLit", value: target.name.text, type: STRING, loc: locOf(target.name) };
|
|
@@ -4299,6 +4662,7 @@ export function lowerExprStatement(lowerer, expr) {
|
|
|
4299
4662
|
if (ts.isElementAccessExpression(expr.left))
|
|
4300
4663
|
return lowerer.lowerElementWrite(expr);
|
|
4301
4664
|
if (ts.isPropertyAccessExpression(expr.left)) {
|
|
4665
|
+
fenceNodeModuleMutation(lowerer, expr.left, "assignment");
|
|
4302
4666
|
// Native arrays have a writable length property. The runtime grows
|
|
4303
4667
|
// by creating holes and truncates with RC-aware removal; keep this
|
|
4304
4668
|
// as a statement operation so invalid lengths can use the normal
|
|
@@ -4451,10 +4815,10 @@ export function lowerExprStatement(lowerer, expr) {
|
|
|
4451
4815
|
// chain): the dyn keyed write — the value converts INTO dyn
|
|
4452
4816
|
// (closures box), the runtime sets the member on OBJ receivers
|
|
4453
4817
|
// and throws Node's TypeErrors on the rest. The receiver is
|
|
4454
|
-
// PROBED (
|
|
4818
|
+
// PROBED (tryLowerExpression) and claimed exactly when it lowers to
|
|
4455
4819
|
// dyn; typed receivers keep their own lowerings and fences.
|
|
4456
4820
|
if (!expr.left.questionDotToken) {
|
|
4457
|
-
const recv =
|
|
4821
|
+
const recv = tryLowerExpression(lowerer, expr.left.expression);
|
|
4458
4822
|
if (recv && recv.type.kind === "dyn") {
|
|
4459
4823
|
const loc = locOf(expr);
|
|
4460
4824
|
const key = { kind: "strLit", value: expr.left.name.text, type: STRING, loc: locOf(expr.left.name) };
|
|
@@ -5810,29 +6174,27 @@ export function lowerForOf(lowerer, stmt) {
|
|
|
5810
6174
|
// desugars drop them — a labeled jump naming those loops fences at the
|
|
5811
6175
|
// jump site (no label point exists in their desugared shape).
|
|
5812
6176
|
const labels = lowerer.takeLabels();
|
|
5813
|
-
// `for await` is ASYNC ITERATION, not the shipped async/await.
|
|
5814
|
-
// async
|
|
5815
|
-
//
|
|
5816
|
-
// named specifically (the bare SC1070 text would claim async/await
|
|
5817
|
-
// itself is missing).
|
|
6177
|
+
// `for await` is ASYNC ITERATION, not the shipped async/await. Typed
|
|
6178
|
+
// async generators use the generic request protocol; process.stdin
|
|
6179
|
+
// and readable streams retain their dedicated next-chunk paths.
|
|
5818
6180
|
if (stmt.awaitModifier) {
|
|
5819
|
-
if (ts.isPropertyAccessExpression(stmt.expression) &&
|
|
5820
|
-
lowerer.stdlibGlobalMember(stmt.expression, "process") === "stdin") {
|
|
5821
|
-
return lowerForAwaitStdin(lowerer, stmt);
|
|
5822
|
-
}
|
|
5823
|
-
// Readable streams (the readableAsyncIterator surface): the mapped
|
|
5824
|
-
// receiver class roots at a readable-sided stream class.
|
|
5825
6181
|
{
|
|
5826
|
-
const
|
|
5827
|
-
if (
|
|
5828
|
-
const
|
|
5829
|
-
|
|
5830
|
-
|
|
5831
|
-
return lowerForAwaitReadable(lowerer, stmt, recvT);
|
|
6182
|
+
const genT = lowerer.mapTypeOf(lowerer.typeOf(stmt.expression));
|
|
6183
|
+
if (genT?.kind === "generator" && genT.async) {
|
|
6184
|
+
const iterable = lowerer.lowerExpr(stmt.expression);
|
|
6185
|
+
if (iterable.type.kind === "generator" && iterable.type.async) {
|
|
6186
|
+
return lowerForAwaitGenerator(lowerer, stmt, iterable, labels);
|
|
5832
6187
|
}
|
|
5833
6188
|
}
|
|
5834
6189
|
}
|
|
5835
|
-
|
|
6190
|
+
if (ts.isPropertyAccessExpression(stmt.expression) &&
|
|
6191
|
+
lowerer.stdlibGlobalMember(stmt.expression, "process") === "stdin") {
|
|
6192
|
+
return lowerForAwaitStdin(lowerer, stmt);
|
|
6193
|
+
}
|
|
6194
|
+
const builtin = lowerForAwaitBuiltin(lowerer, stmt);
|
|
6195
|
+
if (builtin)
|
|
6196
|
+
return builtin;
|
|
6197
|
+
lowerer.unsupported("SC1070", stmt, "'for await' over this value (supported: typed async generators, represented class async iterators, process.stdin, and Node/Web readable streams)");
|
|
5836
6198
|
}
|
|
5837
6199
|
lowerer.fenceStaticHeadersIteration(stmt.expression);
|
|
5838
6200
|
// A stored numeric value iterator declared in this function keeps its
|
|
@@ -6051,7 +6413,7 @@ export function lowerForOf(lowerer, stmt) {
|
|
|
6051
6413
|
elemT = { kind: "union", unionId: lowerer.unions.intern(arms) };
|
|
6052
6414
|
}
|
|
6053
6415
|
}
|
|
6054
|
-
if (elemT !== null &&
|
|
6416
|
+
if (elemT !== null && isSafeToRepeat(iterable)) {
|
|
6055
6417
|
const loc = locOf(stmt.expression);
|
|
6056
6418
|
const shapeId = iterable.type.shapeId;
|
|
6057
6419
|
const snapshotElem = elemT;
|
|
@@ -6161,9 +6523,7 @@ export function lowerForOf(lowerer, stmt) {
|
|
|
6161
6523
|
lowerer.unsupported("SC1090", stmt.initializer, "for-of over a pre-declared variable (declare the loop variable in the loop: for (const x of ...))");
|
|
6162
6524
|
}
|
|
6163
6525
|
const list = stmt.initializer;
|
|
6164
|
-
|
|
6165
|
-
lowerer.unsupported("SC1090", list, "'using' declarations (dispose-at-scope-exit semantics)");
|
|
6166
|
-
}
|
|
6526
|
+
const resourceLoop = (list.flags & ts.NodeFlags.Using) !== 0;
|
|
6167
6527
|
const isLet = (list.flags & ts.NodeFlags.Let) !== 0 || (list.flags & ts.NodeFlags.BlockScoped) === 0;
|
|
6168
6528
|
const decl = list.declarations[0]; // the grammar allows exactly one
|
|
6169
6529
|
// `for (const [k, v] of pairs)` — a destructuring loop variable: the
|
|
@@ -6216,7 +6576,19 @@ export function lowerForOf(lowerer, stmt) {
|
|
|
6216
6576
|
lowerer.runtimeOptionalStorageLocals.add(root);
|
|
6217
6577
|
}
|
|
6218
6578
|
const body = lowerer.inCtl("loop", () => lowerer.lowerScopedBlock(stmt.statement), labels);
|
|
6219
|
-
|
|
6579
|
+
if (!resourceLoop)
|
|
6580
|
+
return forValues(local.id, body);
|
|
6581
|
+
const resource = varRef(local.id, local.type, locOf(decl.name));
|
|
6582
|
+
const cleanup = cleanupForValue(lowerer, decl.name, resource, isAwaitUsing(list));
|
|
6583
|
+
return forValues(local.id, [{
|
|
6584
|
+
kind: "tryCatch",
|
|
6585
|
+
tryBody: body,
|
|
6586
|
+
catchBody: null,
|
|
6587
|
+
catchLocalId: null,
|
|
6588
|
+
finallyBody: cleanup,
|
|
6589
|
+
suppressFinallyErrors: true,
|
|
6590
|
+
loc: locOf(decl),
|
|
6591
|
+
}]);
|
|
6220
6592
|
}
|
|
6221
6593
|
finally {
|
|
6222
6594
|
lowerer.scopes.pop();
|
|
@@ -7372,112 +7744,6 @@ function lowerForAwaitStdin(lowerer, stmt) {
|
|
|
7372
7744
|
lowerer.scopes.pop();
|
|
7373
7745
|
}
|
|
7374
7746
|
}
|
|
7375
|
-
/** `for await (const chunk of readable)` — the stream async iterator
|
|
7376
|
-
* (the stdin desugar's sibling): every pass awaits the runtime's
|
|
7377
|
-
* next-chunk promise — buffered content, or a REJECTION carrying the
|
|
7378
|
-
* stream's error (the await rethrows it, Node's iterator contract) —
|
|
7379
|
-
* and exits on the EOF sentinel. Chunks are Buffers in typed code
|
|
7380
|
-
* (encoded streams fence at the runtime entry); in the JS lane the
|
|
7381
|
-
* chunk is checked-dynamic and boxes by runtime tag (dyn strings once
|
|
7382
|
-
* an encoding applies, dyn undefined as the sentinel — chunks are
|
|
7383
|
-
* never undefined). Early exit leaves the stream alive (Node's
|
|
7384
|
-
* iterator return() would destroy it — a documented divergence). */
|
|
7385
|
-
function lowerForAwaitReadable(lowerer, stmt, recvT) {
|
|
7386
|
-
if (!lowerer.ctx.isAsync) {
|
|
7387
|
-
lowerer.unsupported("SC1090", stmt, "top-level 'for await' (await outside async functions)");
|
|
7388
|
-
}
|
|
7389
|
-
if (!ts.isVariableDeclarationList(stmt.initializer)) {
|
|
7390
|
-
lowerer.unsupported("SC1090", stmt.initializer, "for-await over a pre-declared variable (declare the loop variable in the loop: for await (const chunk of ...))");
|
|
7391
|
-
}
|
|
7392
|
-
const list = stmt.initializer;
|
|
7393
|
-
const isConst = (list.flags & ts.NodeFlags.Const) !== 0;
|
|
7394
|
-
const isLet = (list.flags & ts.NodeFlags.Let) !== 0;
|
|
7395
|
-
// `for await (var chunk of ...)`: the chunk binding is per-await
|
|
7396
|
-
// machinery (a fresh value each pass, fiber-parked in between) —
|
|
7397
|
-
// threading it through a shared hoisted slot has no user yet.
|
|
7398
|
-
if (!isConst && !isLet)
|
|
7399
|
-
lowerer.unsupported("SC1030", list, "'var' loop bindings in 'for await' (use const)");
|
|
7400
|
-
const decl = list.declarations[0];
|
|
7401
|
-
if (!ts.isIdentifier(decl.name))
|
|
7402
|
-
lowerer.unsupported("SC1031", decl.name);
|
|
7403
|
-
const loc = locOf(stmt);
|
|
7404
|
-
// The chunk is CHECKED-DYNAMIC in both lanes (the shim types the
|
|
7405
|
-
// iterator's element as any): the runtime boxes by tag — Buffers
|
|
7406
|
-
// normally, strings once an encoding applies — and the dyn surface
|
|
7407
|
-
// carries the common consumptions (.length, [i], toString, +=,
|
|
7408
|
-
// typeof). A statically-typed chunk would have to pick one side of
|
|
7409
|
-
// the encoding question at compile time.
|
|
7410
|
-
const dynLane = true;
|
|
7411
|
-
const chunkT = DYN;
|
|
7412
|
-
const promiseT = { kind: "promise", inner: chunkT };
|
|
7413
|
-
lowerer.scopes.push(new Map());
|
|
7414
|
-
try {
|
|
7415
|
-
// The receiver evaluates ONCE, before the loop.
|
|
7416
|
-
const recvLocal = lowerer.declareHiddenLocal("%faStream", recvT);
|
|
7417
|
-
const recvDecl = {
|
|
7418
|
-
kind: "varDecl",
|
|
7419
|
-
localId: recvLocal.id,
|
|
7420
|
-
init: lowerer.lowerExpr(stmt.expression),
|
|
7421
|
-
loc,
|
|
7422
|
-
};
|
|
7423
|
-
const p = lowerer.declareHiddenLocal("%streamNext", promiseT);
|
|
7424
|
-
const chunk = lowerer.declareLocal(decl.name, decl.name.text, chunkT, isLet);
|
|
7425
|
-
const body = lowerer.inCtl("loop", () => lowerer.lowerScopedBlock(stmt.statement));
|
|
7426
|
-
const chunkRef = { kind: "varRef", localId: chunk.id, type: chunkT, loc };
|
|
7427
|
-
const eofCond = dynLane
|
|
7428
|
-
? { kind: "dynTest", test: "undefined", value: chunkRef, type: BOOL, loc }
|
|
7429
|
-
: {
|
|
7430
|
-
kind: "bin",
|
|
7431
|
-
op: "===",
|
|
7432
|
-
left: { kind: "bytesIntrinsic", method: "length", receiver: chunkRef, args: [], type: F64, loc },
|
|
7433
|
-
right: { kind: "numLit", value: 0, type: F64, loc },
|
|
7434
|
-
type: BOOL,
|
|
7435
|
-
loc,
|
|
7436
|
-
};
|
|
7437
|
-
const head = [
|
|
7438
|
-
{
|
|
7439
|
-
kind: "varDecl",
|
|
7440
|
-
localId: p.id,
|
|
7441
|
-
init: {
|
|
7442
|
-
kind: "libCall",
|
|
7443
|
-
fn: dynLane ? "readable.nextChunkDyn" : "readable.nextChunk",
|
|
7444
|
-
args: [{ kind: "varRef", localId: recvLocal.id, type: recvT, loc }],
|
|
7445
|
-
type: promiseT,
|
|
7446
|
-
loc,
|
|
7447
|
-
},
|
|
7448
|
-
loc,
|
|
7449
|
-
},
|
|
7450
|
-
{
|
|
7451
|
-
kind: "varDecl",
|
|
7452
|
-
localId: chunk.id,
|
|
7453
|
-
init: {
|
|
7454
|
-
kind: "awaitExpr",
|
|
7455
|
-
value: { kind: "varRef", localId: p.id, type: promiseT, loc },
|
|
7456
|
-
type: chunkT,
|
|
7457
|
-
loc,
|
|
7458
|
-
},
|
|
7459
|
-
loc,
|
|
7460
|
-
},
|
|
7461
|
-
{ kind: "if", cond: eofCond, then: [{ kind: "break", loc }], else_: null, loc },
|
|
7462
|
-
];
|
|
7463
|
-
return {
|
|
7464
|
-
kind: "block",
|
|
7465
|
-
body: [
|
|
7466
|
-
recvDecl,
|
|
7467
|
-
{
|
|
7468
|
-
kind: "while",
|
|
7469
|
-
cond: { kind: "boolLit", value: true, type: BOOL, loc },
|
|
7470
|
-
body: [...head, ...body],
|
|
7471
|
-
loc,
|
|
7472
|
-
},
|
|
7473
|
-
],
|
|
7474
|
-
loc,
|
|
7475
|
-
};
|
|
7476
|
-
}
|
|
7477
|
-
finally {
|
|
7478
|
-
lowerer.scopes.pop();
|
|
7479
|
-
}
|
|
7480
|
-
}
|
|
7481
7747
|
export function lowerForStatement(lowerer, stmt) {
|
|
7482
7748
|
const labels = lowerer.takeLabels();
|
|
7483
7749
|
lowerer.scopes.push(new Map());
|