@scriptc/compiler 0.2.1 → 0.2.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 +59 -4
- package/dist/backend/llvm/dyn.d.ts +3 -0
- package/dist/backend/llvm/dyn.js +84 -12
- package/dist/backend/llvm/dyn.js.map +1 -1
- package/dist/backend/llvm/emitter.d.ts +4 -1
- package/dist/backend/llvm/emitter.js +20 -5
- package/dist/backend/llvm/emitter.js.map +1 -1
- package/dist/backend/llvm/emitter.test.js +9 -1
- package/dist/backend/llvm/emitter.test.js.map +1 -1
- package/dist/backend/llvm/expr-containers.js +5 -0
- package/dist/backend/llvm/expr-containers.js.map +1 -1
- package/dist/backend/llvm/expr-dynamic.js +30 -5
- package/dist/backend/llvm/expr-dynamic.js.map +1 -1
- package/dist/backend/llvm/expr-stream-bridges.js +70 -9
- package/dist/backend/llvm/expr-stream-bridges.js.map +1 -1
- package/dist/backend/llvm/expr-stream-callbacks.d.ts +2 -2
- package/dist/backend/llvm/expr-stream-callbacks.js +7 -57
- package/dist/backend/llvm/expr-stream-callbacks.js.map +1 -1
- package/dist/backend/llvm/lib-dispatch.js +41 -3
- package/dist/backend/llvm/lib-dispatch.js.map +1 -1
- package/dist/backend/llvm/lib-filesystem.js +12 -9
- package/dist/backend/llvm/lib-filesystem.js.map +1 -1
- package/dist/backend/llvm/lib-shared.js +35 -1
- package/dist/backend/llvm/lib-shared.js.map +1 -1
- package/dist/backend/may-throw.js +4 -0
- package/dist/backend/may-throw.js.map +1 -1
- package/dist/backend/native-codegen.d.ts +1 -1
- package/dist/backend/native-codegen.js +4 -3
- package/dist/backend/native-codegen.js.map +1 -1
- package/dist/backend/native-codegen.test.js +11 -0
- package/dist/backend/native-codegen.test.js.map +1 -1
- package/dist/backend/target-diagnostics.d.ts +5 -0
- package/dist/backend/target-diagnostics.js +72 -1
- package/dist/backend/target-diagnostics.js.map +1 -1
- package/dist/backend/target-diagnostics.test.d.ts +1 -0
- package/dist/backend/target-diagnostics.test.js +68 -0
- package/dist/backend/target-diagnostics.test.js.map +1 -0
- package/dist/coverage/surface-manifest.js +5 -3
- package/dist/coverage/surface-manifest.js.map +1 -1
- package/dist/executable/executable-cache.d.ts +7 -3
- package/dist/executable/executable-cache.js +47 -11
- package/dist/executable/executable-cache.js.map +1 -1
- package/dist/executable/executable-cache.test.js +109 -2
- package/dist/executable/executable-cache.test.js.map +1 -1
- package/dist/executable/prepare.js +2 -1
- package/dist/executable/prepare.js.map +1 -1
- package/dist/frontend/builtin-modules.d.ts +1 -1
- package/dist/frontend/builtin-modules.js +1 -1
- package/dist/frontend/builtin-modules.js.map +1 -1
- package/dist/frontend/function-completion.d.ts +6 -0
- package/dist/frontend/function-completion.js +50 -0
- package/dist/frontend/function-completion.js.map +1 -0
- package/dist/frontend/lowering/abstract-equality.js +5 -1
- package/dist/frontend/lowering/abstract-equality.js.map +1 -1
- package/dist/frontend/lowering/checked-iterable-spread.d.ts +4 -0
- package/dist/frontend/lowering/checked-iterable-spread.js +30 -0
- package/dist/frontend/lowering/checked-iterable-spread.js.map +1 -0
- package/dist/frontend/lowering/checked-promise-all.d.ts +5 -0
- package/dist/frontend/lowering/checked-promise-all.js +31 -0
- package/dist/frontend/lowering/checked-promise-all.js.map +1 -0
- package/dist/frontend/lowering/class-callbacks.d.ts +1 -1
- package/dist/frontend/lowering/class-callbacks.js +7 -6
- package/dist/frontend/lowering/class-callbacks.js.map +1 -1
- package/dist/frontend/lowering/class-construction.d.ts +2 -0
- package/dist/frontend/lowering/class-construction.js +34 -5
- package/dist/frontend/lowering/class-construction.js.map +1 -1
- package/dist/frontend/lowering/class-descriptors.d.ts +1 -1
- package/dist/frontend/lowering/class-descriptors.js +12 -5
- package/dist/frontend/lowering/class-descriptors.js.map +1 -1
- package/dist/frontend/lowering/class-dynamic-dispatch.d.ts +25 -0
- package/dist/frontend/lowering/class-dynamic-dispatch.js +597 -78
- package/dist/frontend/lowering/class-dynamic-dispatch.js.map +1 -1
- package/dist/frontend/lowering/class-dynamic-dispatch.test.js +64 -2
- package/dist/frontend/lowering/class-dynamic-dispatch.test.js.map +1 -1
- package/dist/frontend/lowering/class-instance-constructor.d.ts +1 -1
- package/dist/frontend/lowering/class-instance-constructor.js +11 -41
- package/dist/frontend/lowering/class-instance-constructor.js.map +1 -1
- package/dist/frontend/lowering/class-method-values.d.ts +16 -0
- package/dist/frontend/lowering/class-method-values.js +60 -29
- package/dist/frontend/lowering/class-method-values.js.map +1 -1
- package/dist/frontend/lowering/class-prototypes.d.ts +5 -1
- package/dist/frontend/lowering/class-prototypes.js +69 -10
- package/dist/frontend/lowering/class-prototypes.js.map +1 -1
- package/dist/frontend/lowering/class-runtime-statics.d.ts +4 -4
- package/dist/frontend/lowering/class-runtime-statics.js +26 -8
- package/dist/frontend/lowering/class-runtime-statics.js.map +1 -1
- package/dist/frontend/lowering/class-static-data.d.ts +1 -0
- package/dist/frontend/lowering/class-static-data.js +4 -2
- package/dist/frontend/lowering/class-static-data.js.map +1 -1
- package/dist/frontend/lowering/containers/bytes.js +17 -4
- package/dist/frontend/lowering/containers/bytes.js.map +1 -1
- package/dist/frontend/lowering/containers/string-and-regexp.js +6 -1
- package/dist/frontend/lowering/containers/string-and-regexp.js.map +1 -1
- package/dist/frontend/lowering/deferred-module-initializers.d.ts +13 -0
- package/dist/frontend/lowering/deferred-module-initializers.js +410 -0
- package/dist/frontend/lowering/deferred-module-initializers.js.map +1 -0
- package/dist/frontend/lowering/deferred-module-initializers.test.d.ts +1 -0
- package/dist/frontend/lowering/deferred-module-initializers.test.js +92 -0
- package/dist/frontend/lowering/deferred-module-initializers.test.js.map +1 -0
- package/dist/frontend/lowering/expressions/object-literals.js +31 -1
- package/dist/frontend/lowering/expressions/object-literals.js.map +1 -1
- package/dist/frontend/lowering/expressions/optional-chains.js +3 -3
- package/dist/frontend/lowering/expressions/optional-chains.js.map +1 -1
- package/dist/frontend/lowering/lower-builtins.d.ts +4 -9
- package/dist/frontend/lowering/lower-builtins.js +291 -51
- package/dist/frontend/lowering/lower-builtins.js.map +1 -1
- package/dist/frontend/lowering/lower-calls.js +283 -73
- package/dist/frontend/lowering/lower-calls.js.map +1 -1
- package/dist/frontend/lowering/lower-classes.d.ts +5 -0
- package/dist/frontend/lowering/lower-classes.js +439 -25
- package/dist/frontend/lowering/lower-classes.js.map +1 -1
- package/dist/frontend/lowering/lower-containers.d.ts +1 -1
- package/dist/frontend/lowering/lower-containers.js +39 -17
- package/dist/frontend/lowering/lower-containers.js.map +1 -1
- package/dist/frontend/lowering/lower-exprs.d.ts +10 -6
- package/dist/frontend/lowering/lower-exprs.js +294 -129
- package/dist/frontend/lowering/lower-exprs.js.map +1 -1
- package/dist/frontend/lowering/lower-generators.js +5 -1
- package/dist/frontend/lowering/lower-generators.js.map +1 -1
- package/dist/frontend/lowering/lower-inspect.js +15 -1
- package/dist/frontend/lowering/lower-inspect.js.map +1 -1
- package/dist/frontend/lowering/lower-island.js +4 -0
- package/dist/frontend/lowering/lower-island.js.map +1 -1
- package/dist/frontend/lowering/lower-island.test.d.ts +1 -0
- package/dist/frontend/lowering/lower-island.test.js +51 -0
- package/dist/frontend/lowering/lower-island.test.js.map +1 -0
- package/dist/frontend/lowering/lower-modules.d.ts +1 -3
- package/dist/frontend/lowering/lower-modules.js +186 -82
- package/dist/frontend/lowering/lower-modules.js.map +1 -1
- package/dist/frontend/lowering/lower-modules.test.js +69 -1
- package/dist/frontend/lowering/lower-modules.test.js.map +1 -1
- package/dist/frontend/lowering/lower-stmts.js +100 -35
- package/dist/frontend/lowering/lower-stmts.js.map +1 -1
- package/dist/frontend/lowering/lower-stmts.test.d.ts +1 -0
- package/dist/frontend/lowering/lower-stmts.test.js +77 -0
- package/dist/frontend/lowering/lower-stmts.test.js.map +1 -0
- package/dist/frontend/lowering/lower-stream.js +2 -0
- package/dist/frontend/lowering/lower-stream.js.map +1 -1
- package/dist/frontend/lowering/lower-url.d.ts +2 -1
- package/dist/frontend/lowering/lower-url.js +32 -9
- package/dist/frontend/lowering/lower-url.js.map +1 -1
- package/dist/frontend/lowering/lowerer.d.ts +26 -9
- package/dist/frontend/lowering/lowerer.js +218 -27
- package/dist/frontend/lowering/lowerer.js.map +1 -1
- package/dist/frontend/lowering/lowerer.test.js +38 -0
- package/dist/frontend/lowering/lowerer.test.js.map +1 -1
- package/dist/frontend/lowering/module-namespace-elements.d.ts +7 -0
- package/dist/frontend/lowering/module-namespace-elements.js +54 -0
- package/dist/frontend/lowering/module-namespace-elements.js.map +1 -0
- package/dist/frontend/lowering/object-factory-new.d.ts +4 -3
- package/dist/frontend/lowering/object-factory-new.js +27 -14
- package/dist/frontend/lowering/object-factory-new.js.map +1 -1
- package/dist/frontend/lowering/surfaces.d.ts +1 -0
- package/dist/frontend/lowering/surfaces.js +24 -12
- package/dist/frontend/lowering/surfaces.js.map +1 -1
- package/dist/frontend/lowering/text-decoder-encoding.js +3 -0
- package/dist/frontend/lowering/text-decoder-encoding.js.map +1 -1
- package/dist/frontend/lowering/text-decoder-encoding.test.js +2 -1
- package/dist/frontend/lowering/text-decoder-encoding.test.js.map +1 -1
- package/dist/frontend/program.js +4 -2
- package/dist/frontend/program.js.map +1 -1
- package/dist/frontend/type-mapper.js +83 -14
- package/dist/frontend/type-mapper.js.map +1 -1
- package/dist/frontend/union-discriminants.js +22 -7
- package/dist/frontend/union-discriminants.js.map +1 -1
- package/dist/index.js +16 -10
- package/dist/index.js.map +1 -1
- package/dist/ir/ir.d.ts +27 -13
- package/dist/ir/ir.js +26 -2
- package/dist/ir/ir.js.map +1 -1
- package/dist/ir/serialize.js +21 -1
- package/dist/ir/serialize.js.map +1 -1
- package/dist/ir/serialize.test.js +21 -0
- package/dist/ir/serialize.test.js.map +1 -1
- package/dist/ir/traverse.d.ts +0 -3
- package/dist/ir/traverse.js +33 -20
- package/dist/ir/traverse.js.map +1 -1
- package/dist/ir/validate.js +56 -5
- package/dist/ir/validate.js.map +1 -1
- package/dist/ir/validate.test.js +17 -1
- package/dist/ir/validate.test.js.map +1 -1
- package/dist/library/compiler-self-identity.js +1 -1
- package/dist/library/compiler-self-identity.test.js +2 -2
- package/dist/library/compiler-self-identity.test.js.map +1 -1
- package/dist/library/int-infer.d.ts +2 -2
- package/dist/library/int-infer.js +10 -7
- package/dist/library/int-infer.js.map +1 -1
- package/dist/library/int-infer.test.js +44 -0
- package/dist/library/int-infer.test.js.map +1 -1
- package/dist/library/prepare.js +2 -1
- package/dist/library/prepare.js.map +1 -1
- package/package.json +20 -20
- package/surface-manifest.json +115 -13
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { InternalCompilerError } from "../../errors.js";
|
|
2
|
+
import { SYMBOL_T } from "../../ir/ir.js";
|
|
2
3
|
import { literalValues } from "../literal-values.js";
|
|
3
4
|
import { literalUnionArm } from "../union-discriminants.js";
|
|
4
5
|
/* Expression lowering: the expression dispatch (lowerExpr), literals
|
|
@@ -53,13 +54,14 @@ import { classSymbolKeyOf } from "./symbol-fields.js";
|
|
|
53
54
|
import { lowerClassMethodValue } from "./class-method-values.js";
|
|
54
55
|
import { classInstanceOf } from "./class-dynamic-dispatch.js";
|
|
55
56
|
import { lowerGlobalValue } from "./lower-global-value.js";
|
|
56
|
-
import { lowerClassPrototypeData } from "./class-prototypes.js";
|
|
57
|
+
import { lowerClassPrototypeData, lowerClassPrototypeComputedAssignment } from "./class-prototypes.js";
|
|
57
58
|
import { isClassCallback } from "./class-callbacks.js";
|
|
58
59
|
import { builtinPrototypeMethod, lowerArrayIsArrayValue, lowerCheckedPredicateValue, lowerNumberParserValue, lowerObjectAssignValue, lowerStringCodesValue } from "./lower-builtin-values.js";
|
|
59
60
|
import { lowerArrayFromValue } from "./lower-containers.js";
|
|
60
61
|
import { lowerPerfHooksTypeof } from "./lower-builtins.js";
|
|
61
62
|
import { jsBindingHasOpenWrites } from "./lower-stmts.js";
|
|
62
63
|
import { checkedClassInstanceOf } from "./class-construction.js";
|
|
64
|
+
import { lowerModuleNamespaceElement } from "./module-namespace-elements.js";
|
|
63
65
|
/** A template piece's RAW text (String.raw's contract: escapes stay
|
|
64
66
|
* characters). 7's client AST ships no rawText at runtime (the typing
|
|
65
67
|
* declares it; the serialized node data omits it), so the raw span comes
|
|
@@ -586,6 +588,8 @@ function lowerExprInner(lowerer, expr) {
|
|
|
586
588
|
if (ts.isTypeOfExpression(expr)) {
|
|
587
589
|
if (lowerer.isStdlibGlobal(expr.expression, "process"))
|
|
588
590
|
return { kind: "strLit", value: "object", type: STRING, loc };
|
|
591
|
+
if (lowerer.isStdlibGlobal(expr.expression, "fetch"))
|
|
592
|
+
return { kind: "strLit", value: "function", type: STRING, loc };
|
|
589
593
|
const performance = lowerPerfHooksTypeof(lowerer, expr.expression);
|
|
590
594
|
if (performance)
|
|
591
595
|
return performance;
|
|
@@ -777,7 +781,7 @@ function lowerExprInner(lowerer, expr) {
|
|
|
777
781
|
if (use?.kind === "element") {
|
|
778
782
|
const key = runtimeOptionalElementKey(use.access.argumentExpression);
|
|
779
783
|
if (key === null) {
|
|
780
|
-
|
|
784
|
+
return varRef(local.id, local.type, loc);
|
|
781
785
|
}
|
|
782
786
|
return runtimeOptionalReceiverRead(lowerer, expr, local, key, loc);
|
|
783
787
|
}
|
|
@@ -1067,6 +1071,8 @@ function lowerExprInner(lowerer, expr) {
|
|
|
1067
1071
|
}
|
|
1068
1072
|
if (lowerer.isStdlibGlobal(expr, "ArrayBuffer"))
|
|
1069
1073
|
return { kind: "libCall", fn: "arrayBuffer.constructor", args: [], type: DYN, loc };
|
|
1074
|
+
if (isJsSourceFile(expr.getSourceFile()) && lowerer.isStdlibGlobal(expr, "Array"))
|
|
1075
|
+
return { kind: "libCall", fn: "dyn.arrayConstructor", args: [], type: DYN, loc };
|
|
1070
1076
|
if (Object.values(BYTES_ELEMENT_NAME).includes(expr.text) && lowerer.isStdlibSymbol(lowerer.checker.getSymbolAtLocation(expr))) {
|
|
1071
1077
|
return { kind: "libCall", fn: "bytes.constructor", args: [{ kind: "strLit", value: expr.text, type: STRING, loc }], type: DYN, loc };
|
|
1072
1078
|
}
|
|
@@ -1941,6 +1947,13 @@ function lowerExprInner(lowerer, expr) {
|
|
|
1941
1947
|
: { kind: "strIntrinsic", method: "length", receiver: recv, args: [], type: F64, loc };
|
|
1942
1948
|
}
|
|
1943
1949
|
}
|
|
1950
|
+
if (isJsSourceFile(expr.getSourceFile()) && expr.name.text === "constructor") {
|
|
1951
|
+
const receiver = tryLowerExpression(lowerer, expr.expression);
|
|
1952
|
+
if (receiver?.type.kind === "dyn" || receiver?.type.kind === "object" && !lowerer.classes.get(receiver.type.className)?.fields.has("constructor")) {
|
|
1953
|
+
return { kind: "dynKeyGet", value: lowerer.coerceToExpected(receiver, DYN),
|
|
1954
|
+
key: { kind: "strLit", value: "constructor", type: STRING, loc }, type: DYN, loc };
|
|
1955
|
+
}
|
|
1956
|
+
}
|
|
1944
1957
|
// `f.name` / `f.length` / own properties on a FUNCTION-typed JS
|
|
1945
1958
|
// value (the mustCall wrapper's function-instance members): read
|
|
1946
1959
|
// through the dyn box — the closure's own-property table answers
|
|
@@ -2091,7 +2104,8 @@ function lowerExprInner(lowerer, expr) {
|
|
|
2091
2104
|
// chain. Scalar-narrowed occurrences bridge via maybeNarrow's
|
|
2092
2105
|
// validated dynCheck as usual.
|
|
2093
2106
|
if (recvLowered.type.kind === "dyn" || isJsSourceFile(expr.getSourceFile()) &&
|
|
2094
|
-
["f64", "bool", "bigint", "string", "symbol"].includes(recvLowered.type.kind)
|
|
2107
|
+
(["f64", "bool", "bigint", "string", "symbol"].includes(recvLowered.type.kind) ||
|
|
2108
|
+
recvLowered.type.kind === "union" && lowerer.dynConvertible(recvLowered.type))) {
|
|
2095
2109
|
const key = { kind: "strLit", value: expr.name.text, type: STRING, loc: locOf(expr.name) };
|
|
2096
2110
|
const opt = hasOptionalChainGuard(expr.expression);
|
|
2097
2111
|
return lowerer.maybeNarrow({ kind: "dynKeyGet", key, ...(opt ? { optional: true } : {}), value: lowerer.coerceToExpected(recvLowered, DYN), type: DYN, loc }, expr);
|
|
@@ -2540,8 +2554,52 @@ export function maybeNarrow(lowerer, expr, node) {
|
|
|
2540
2554
|
// instead of misreading the payload (the dyn boundary's usual stance).
|
|
2541
2555
|
// Object/array narrowings stay dyn-typed and keep their fences.
|
|
2542
2556
|
if (expr.type.kind === "dyn") {
|
|
2557
|
+
// An inferred JS member's storage may be deliberately wider than its
|
|
2558
|
+
// declaration (prototype-selected buffers, null-initialized scratch
|
|
2559
|
+
// objects). Extract at the consuming operation, not at the read.
|
|
2560
|
+
if (isJsSourceFile(node.getSourceFile()) && ts.isPropertyAccessExpression(node)) {
|
|
2561
|
+
const receiver = lowerer.mapTypeOf(lowerer.typeOf(node.expression));
|
|
2562
|
+
if (receiver?.kind === "object") {
|
|
2563
|
+
const info = lowerer.classes.get(receiver.className);
|
|
2564
|
+
if (info?.fields.get(node.name.text)?.kind === "dyn" || lowerer.findMethodOn(info ?? null, `get:${node.name.text}`)?.sig.ret.kind === "dyn")
|
|
2565
|
+
return expr;
|
|
2566
|
+
}
|
|
2567
|
+
if (receiver?.kind === "classval") {
|
|
2568
|
+
const info = lowerer.classes.get(receiver.className);
|
|
2569
|
+
if (info && hasRuntimeStatics(info))
|
|
2570
|
+
return expr;
|
|
2571
|
+
}
|
|
2572
|
+
}
|
|
2573
|
+
if (isJsSourceFile(node.getSourceFile()) &&
|
|
2574
|
+
(ts.isPropertyAccessExpression(node) || ts.isElementAccessExpression(node))) {
|
|
2575
|
+
let receiver = node.expression;
|
|
2576
|
+
while (ts.isPropertyAccessExpression(receiver) || ts.isElementAccessExpression(receiver) || ts.isParenthesizedExpression(receiver)) {
|
|
2577
|
+
if (ts.isPropertyAccessExpression(receiver)) {
|
|
2578
|
+
const owner = lowerer.mapTypeOf(lowerer.typeOf(receiver.expression));
|
|
2579
|
+
if (owner?.kind === "object" && lowerer.classes.get(owner.className)?.fields.get(receiver.name.text)?.kind === "dyn")
|
|
2580
|
+
return expr;
|
|
2581
|
+
}
|
|
2582
|
+
receiver = receiver.expression;
|
|
2583
|
+
}
|
|
2584
|
+
if (receiver.kind === ts.SyntaxKind.ThisKeyword && lowerer.resolveThis()?.type.kind === "dyn")
|
|
2585
|
+
return expr;
|
|
2586
|
+
if (ts.isIdentifier(receiver)) {
|
|
2587
|
+
const stored = lowerer.peekLocal(receiver)?.type ?? lowerer.globalOf(receiver)?.type;
|
|
2588
|
+
if (stored?.kind === "dyn")
|
|
2589
|
+
return expr;
|
|
2590
|
+
const symbol = lowerer.resolveValueSymbol(receiver);
|
|
2591
|
+
const bound = symbol && lowerer.implicitParamTypes?.get(symbol);
|
|
2592
|
+
if (bound && lowerer.mapTypeOf(bound)?.kind === "dyn")
|
|
2593
|
+
return expr;
|
|
2594
|
+
}
|
|
2595
|
+
}
|
|
2543
2596
|
if (isJsSourceFile(node.getSourceFile()) && ts.isIdentifier(node)) {
|
|
2544
2597
|
const symbol = lowerer.resolveValueSymbol(node);
|
|
2598
|
+
if (symbol && lowerer.checker.declarationsOf(symbol).some((decl) => ts.isParameter(decl) &&
|
|
2599
|
+
ts.isMethodDeclaration(decl.parent) && lowerer.checkedVirtualJsMethods.has(decl.parent)))
|
|
2600
|
+
return expr;
|
|
2601
|
+
if (symbol && lowerer.checker.declarationsOf(symbol).some((decl) => ts.isParameter(decl) && ts.isConstructorDeclaration(decl.parent)))
|
|
2602
|
+
return expr;
|
|
2545
2603
|
if (symbol && lowerer.checker.declarationsOf(symbol).some((decl) => ts.isVariableDeclaration(decl) && jsBindingHasOpenWrites(lowerer, decl)))
|
|
2546
2604
|
return expr;
|
|
2547
2605
|
if (symbol && lowerer.checker.declarationsOf(symbol).some((decl) => ts.isParameter(decl) && decl.initializer && !decl.type &&
|
|
@@ -2605,6 +2663,19 @@ export function maybeNarrow(lowerer, expr, node) {
|
|
|
2605
2663
|
}
|
|
2606
2664
|
if (expr.type.kind !== "union")
|
|
2607
2665
|
return expr;
|
|
2666
|
+
// Represented fields and index values can include undefined even when
|
|
2667
|
+
// checker inference or lib.d.ts declares strings (RegExp captures).
|
|
2668
|
+
// Only a declaration that represents the optional arm can provide a
|
|
2669
|
+
// trustworthy flow narrowing; otherwise consumers must check it.
|
|
2670
|
+
if ((expr.kind === "recordGet" || expr.kind === "recordKeyGet") &&
|
|
2671
|
+
(ts.isPropertyAccessExpression(node) || ts.isElementAccessExpression(node))) {
|
|
2672
|
+
const receiver = lowerer.mapTypeOf(lowerer.typeOf(node.expression));
|
|
2673
|
+
const shape = receiver?.kind === "record" ? lowerer.shapes.get(receiver.shapeId) : undefined;
|
|
2674
|
+
const declared = expr.kind === "recordGet" ? shape?.fields.find((field) => field.name === expr.field)?.type : shape?.indexValue;
|
|
2675
|
+
if (lowerer.armTag(expr.type.unionId, UNDEFINED_T) >= 0 &&
|
|
2676
|
+
declared && !(declared.kind === "union" && lowerer.armTag(declared.unionId, UNDEFINED_T) >= 0))
|
|
2677
|
+
return expr;
|
|
2678
|
+
}
|
|
2608
2679
|
if (ts.isIdentifier(node)) {
|
|
2609
2680
|
const symbol = lowerer.resolveValueSymbol(node);
|
|
2610
2681
|
if (symbol && lowerer.unstableSwitchBindings.has(symbol))
|
|
@@ -3559,14 +3630,16 @@ export function lowerIntrinsicProperty(lowerer, expr) {
|
|
|
3559
3630
|
if (receiver.type.kind === "dyn")
|
|
3560
3631
|
return { kind: "dynKeyGet", value: receiver,
|
|
3561
3632
|
key: { kind: "strLit", value: name, type: STRING, loc: locOf(expr) }, type: DYN, loc: locOf(expr) };
|
|
3562
|
-
|
|
3563
|
-
|
|
3564
|
-
return { kind: "seqExpr", stmts: [{ kind: "exprStmt", expr: receiver, loc: locOf(expr) }], result, type: STRING, loc: locOf(expr) };
|
|
3633
|
+
const result = { kind: "libCall", fn: "dyn.arrayConstructor", args: [], type: DYN, loc: locOf(expr) };
|
|
3634
|
+
return { kind: "seqExpr", stmts: [{ kind: "exprStmt", expr: receiver, loc: locOf(expr) }], result, type: DYN, loc: locOf(expr) };
|
|
3565
3635
|
}
|
|
3566
3636
|
const strictReceiver = (expected) => {
|
|
3567
3637
|
const value = lowerer.lowerExpr(expr.expression);
|
|
3568
|
-
if (value.type.kind === "dyn")
|
|
3638
|
+
if (value.type.kind === "dyn") {
|
|
3639
|
+
if (isJsSourceFile(expr.getSourceFile()) && name === "length")
|
|
3640
|
+
return value;
|
|
3569
3641
|
return lowerer.coerceInto(expr.expression, value, expected);
|
|
3642
|
+
}
|
|
3570
3643
|
return lowerer.runtimeOptionalPropertyReceiver(expr.expression, value, expected, name) ?? value;
|
|
3571
3644
|
};
|
|
3572
3645
|
if (kind === "child") {
|
|
@@ -3670,7 +3743,7 @@ export function lowerIntrinsicProperty(lowerer, expr) {
|
|
|
3670
3743
|
// (the child.stdout pattern); a checker-narrowed read extracts
|
|
3671
3744
|
// through maybeNarrow like any union member.
|
|
3672
3745
|
if (name === "description") {
|
|
3673
|
-
const receiver = lowerer.lowerExpr(expr.expression);
|
|
3746
|
+
const receiver = lowerer.coerceToExpected(lowerer.lowerExpr(expr.expression), SYMBOL_T);
|
|
3674
3747
|
const type = { kind: "union", unionId: lowerer.unions.intern([STRING, UNDEFINED_T]) };
|
|
3675
3748
|
return { kind: "libCall", fn: "sym.desc", args: [receiver], type, loc };
|
|
3676
3749
|
}
|
|
@@ -3771,6 +3844,9 @@ export function lowerIntrinsicProperty(lowerer, expr) {
|
|
|
3771
3844
|
let receiver = expected?.kind === "bytes"
|
|
3772
3845
|
? strictReceiver(expected)
|
|
3773
3846
|
: lowerer.lowerExpr(expr.expression);
|
|
3847
|
+
if (receiver.type.kind === "dyn")
|
|
3848
|
+
return { kind: "dynCheck", value: { kind: "dynKeyGet", value: receiver,
|
|
3849
|
+
key: { kind: "strLit", value: name, type: STRING, loc: locOf(expr) }, type: DYN, loc: locOf(expr) }, type: F64, loc: locOf(expr) };
|
|
3774
3850
|
if (receiver.type.kind === "union" && lowerer.armTag(receiver.type.unionId, UNDEFINED_T) >= 0) {
|
|
3775
3851
|
const present = lowerer.stripUndefinedArm(receiver.type);
|
|
3776
3852
|
const helper = present.kind === "bytes"
|
|
@@ -4020,6 +4096,8 @@ export function lowerArrayLiteral(lowerer, expr, expected) {
|
|
|
4020
4096
|
if (arms?.length && arms.every((arm) => arm.kind === "classval"))
|
|
4021
4097
|
mapped = DYN;
|
|
4022
4098
|
}
|
|
4099
|
+
if (!expected && isJsSourceFile(expr.getSourceFile()) && expr.elements.some((element) => ts.isSpreadElement(element) && tryLowerExpression(lowerer, element.expression)?.type.kind === "dyn"))
|
|
4100
|
+
mapped = DYN;
|
|
4023
4101
|
// An EMPTY literal under a CONST ASSERTION whose type queries panicked
|
|
4024
4102
|
// (tsgo's readonly-[] TupleType conversion — the facade answers `any`,
|
|
4025
4103
|
// which maps to nothing statically and to an island value under
|
|
@@ -4438,6 +4516,9 @@ export function lowerElementAccess(lowerer, expr) {
|
|
|
4438
4516
|
const cachedModule = lowerRequireCacheElement(lowerer, expr);
|
|
4439
4517
|
if (cachedModule)
|
|
4440
4518
|
return cachedModule;
|
|
4519
|
+
const namespaceElement = lowerModuleNamespaceElement(lowerer, expr);
|
|
4520
|
+
if (namespaceElement)
|
|
4521
|
+
return namespaceElement;
|
|
4441
4522
|
// Native Web handles use the same supported surface for bracket and dot
|
|
4442
4523
|
// spellings. Fence a statically-known unsupported key before the generic
|
|
4443
4524
|
// checked-dynamic element-read path can turn it into a runtime missing
|
|
@@ -4540,6 +4621,13 @@ export function lowerElementAccess(lowerer, expr) {
|
|
|
4540
4621
|
const key = lowerer.lowerExprExpecting(expr.argumentExpression, DYN);
|
|
4541
4622
|
return lowerer.maybeNarrow({ kind: "dynKeyGet", key, value: boxed, type: DYN, loc }, expr);
|
|
4542
4623
|
}
|
|
4624
|
+
if (receiver?.type.kind === "array" || receiver?.type.kind === "bytes")
|
|
4625
|
+
receiverIr = receiver.type;
|
|
4626
|
+
if (receiver?.type.kind === "union") {
|
|
4627
|
+
const present = lowerer.stripUndefinedArm(receiver.type);
|
|
4628
|
+
if (present.kind === "array" || present.kind === "bytes")
|
|
4629
|
+
receiverIr = present;
|
|
4630
|
+
}
|
|
4543
4631
|
}
|
|
4544
4632
|
// A readonly tuple union under Array.isArray is checker-typed as an
|
|
4545
4633
|
// intersection with any[], whose structural mapping is not the tuple
|
|
@@ -4774,6 +4862,11 @@ export function lowerElementAccess(lowerer, expr) {
|
|
|
4774
4862
|
if (receiverIr?.kind !== "array") {
|
|
4775
4863
|
{
|
|
4776
4864
|
const receiver = tryLowerExpression(lowerer, expr.expression);
|
|
4865
|
+
if (receiver?.type.kind === "dyn" || receiver && isJsSourceFile(expr.getSourceFile()) &&
|
|
4866
|
+
(receiver.type.kind === "union" || receiver.type.kind === "bytes") && lowerer.dynConvertible(receiver.type)) {
|
|
4867
|
+
const key = lowerRecordPropertyKey(lowerer, lowerer.lowerExpr(expr.argumentExpression), expr.argumentExpression);
|
|
4868
|
+
return lowerer.maybeNarrow({ kind: "dynKeyGet", value: lowerer.coerceInto(expr.expression, receiver, DYN), key, type: DYN, loc: locOf(expr) }, expr);
|
|
4869
|
+
}
|
|
4777
4870
|
if (receiver?.type.kind === "func" &&
|
|
4778
4871
|
canBoxFuncIntoDyn(receiver.type, (id) => lowerer.shapes.get(id), (id) => lowerer.unions.get(id))) {
|
|
4779
4872
|
const key = lowerRecordPropertyKey(lowerer, lowerer.lowerExpr(expr.argumentExpression), expr.argumentExpression);
|
|
@@ -5353,7 +5446,9 @@ export function lowerElementCompound(lowerer, expr, op) {
|
|
|
5353
5446
|
const oldLocal = lowerer.declareHiddenLocal("%compoundOld", oldValue.type);
|
|
5354
5447
|
const oldRef = () => varRef(oldLocal.id, oldValue.type, loc);
|
|
5355
5448
|
const rhs = lowerer.lowerExpr(expr.right);
|
|
5356
|
-
const numericRhs =
|
|
5449
|
+
const numericRhs = rhs.type.kind === "dyn" && isJsSourceFile(expr.getSourceFile()) && receiver.type.kind === "bytes"
|
|
5450
|
+
? { kind: "libCall", fn: "dyn.toNumberCoerce", args: [rhs], type: F64, loc }
|
|
5451
|
+
: lowerOptionalNumber(lowerer, rhs, loc);
|
|
5357
5452
|
const elementType = receiver.type.kind === "array" ? receiver.type.elem : F64;
|
|
5358
5453
|
const valueType = elementType.kind === "union" && lowerer.armTag(elementType.unionId, UNDEFINED_T) >= 0
|
|
5359
5454
|
? lowerer.stripUndefinedArm(elementType)
|
|
@@ -5432,6 +5527,18 @@ export function lowerNativeFunctionAssignment(lowerer, expr) {
|
|
|
5432
5527
|
(!ts.isPropertyAccessExpression(target) && !ts.isElementAccessExpression(target)) ||
|
|
5433
5528
|
target.questionDotToken)
|
|
5434
5529
|
return null;
|
|
5530
|
+
// Checked constructors share the callable's live property descriptors.
|
|
5531
|
+
const classType = storedClassValueType(lowerer, target.expression);
|
|
5532
|
+
const staticClass = classType?.kind === "classval" ? lowerer.classes.get(classType.className) : undefined;
|
|
5533
|
+
if (staticClass && hasRuntimeStatics(staticClass)) {
|
|
5534
|
+
return lowerDynMemberAssignment(lowerer, expr, lowerer.lowerExprExpecting(target.expression, DYN));
|
|
5535
|
+
}
|
|
5536
|
+
// Other class statics and prototype tables have their own paths.
|
|
5537
|
+
// Probing them as ordinary functions could materialize unused methods.
|
|
5538
|
+
if (lowerer.exactClassOfReceiver(target.expression) ||
|
|
5539
|
+
ts.isPropertyAccessExpression(target.expression) && target.expression.name.text === "prototype" &&
|
|
5540
|
+
lowerer.exactClassOfReceiver(target.expression.expression))
|
|
5541
|
+
return null;
|
|
5435
5542
|
const receiver = tryLowerExpression(lowerer, target.expression);
|
|
5436
5543
|
if (receiver?.type.kind !== "func" ||
|
|
5437
5544
|
!canBoxFuncIntoDyn(receiver.type, (id) => lowerer.shapes.get(id), (id) => lowerer.unions.get(id)))
|
|
@@ -5445,6 +5552,9 @@ export function lowerNativeFunctionAssignment(lowerer, expr) {
|
|
|
5445
5552
|
return lowerDynMemberAssignment(lowerer, expr, boxed);
|
|
5446
5553
|
}
|
|
5447
5554
|
export function lowerElementWrite(lowerer, expr) {
|
|
5555
|
+
const prototype = lowerClassPrototypeComputedAssignment(lowerer, expr);
|
|
5556
|
+
if (prototype)
|
|
5557
|
+
return { kind: "exprStmt", expr: prototype, loc: locOf(expr) };
|
|
5448
5558
|
const target = expr.left;
|
|
5449
5559
|
fenceNodeModuleMutation(lowerer, target, "assignment");
|
|
5450
5560
|
const globalKey = globalSymbolKey(lowerer, target.expression, target.argumentExpression);
|
|
@@ -5502,6 +5612,12 @@ export function lowerElementWrite(lowerer, expr) {
|
|
|
5502
5612
|
}
|
|
5503
5613
|
}
|
|
5504
5614
|
let receiverIr = lowerer.mapTypeOf(lowerer.typeOf(target.expression));
|
|
5615
|
+
if (isJsSourceFile(expr.getSourceFile()) && checkedReceiver) {
|
|
5616
|
+
const represented = checkedReceiver.type.kind === "union"
|
|
5617
|
+
? lowerer.stripUndefinedArm(checkedReceiver.type) : checkedReceiver.type;
|
|
5618
|
+
if (represented.kind === "array" || represented.kind === "bytes")
|
|
5619
|
+
receiverIr = represented;
|
|
5620
|
+
}
|
|
5505
5621
|
if (receiverIr === null || receiverIr.kind === "dyn") {
|
|
5506
5622
|
const receiver = tryLowerExpression(lowerer, target.expression);
|
|
5507
5623
|
if (receiver?.type.kind === "array")
|
|
@@ -6278,78 +6394,86 @@ export function lowerPrefixUnary(lowerer, expr) {
|
|
|
6278
6394
|
}
|
|
6279
6395
|
lowerer.unsupported("SC1090", expr, `syntax '${ts.syntaxKindName(expr.kind)}'`);
|
|
6280
6396
|
}
|
|
6281
|
-
/**
|
|
6282
|
-
|
|
6283
|
-
|
|
6284
|
-
* backend reads/writes through the box); record fields and array
|
|
6285
|
-
* elements keep the fence in value position (statement-position `obj.f++`
|
|
6286
|
-
* still desugars through the compound-field path). */
|
|
6287
|
-
function lowerIncDec(lowerer, expr, prefix) {
|
|
6397
|
+
/** Member updates evaluate the reference once, convert its old value with
|
|
6398
|
+
* ToNumeric, store the new value, then yield the old or new numeric value. */
|
|
6399
|
+
export function lowerIncDec(lowerer, expr, prefix) {
|
|
6288
6400
|
const loc = locOf(expr);
|
|
6289
6401
|
const op = expr.operator === ts.SyntaxKind.PlusPlusToken ? "+" : "-";
|
|
6290
6402
|
if (!ts.isIdentifier(expr.operand)) {
|
|
6291
|
-
|
|
6292
|
-
|
|
6403
|
+
const access = expr.operand;
|
|
6404
|
+
if (!ts.isPropertyAccessExpression(access) && !ts.isElementAccessExpression(access) || access.questionDotToken) {
|
|
6405
|
+
lowerer.unsupported("SC1045", expr, "increment/decrement of this target");
|
|
6406
|
+
}
|
|
6407
|
+
fenceNodeModuleMutation(lowerer, access, "assignment");
|
|
6408
|
+
if (ts.isPropertyAccessExpression(access)) {
|
|
6409
|
+
const target = staticFieldWriteTarget(lowerer, access);
|
|
6293
6410
|
if (target) {
|
|
6294
6411
|
if (target.type.kind !== "f64")
|
|
6295
6412
|
lowerer.unsupported("SC1043", expr);
|
|
6296
6413
|
return { kind: "incDec", op, prefix, localId: target.id, type: F64, loc };
|
|
6297
6414
|
}
|
|
6298
6415
|
}
|
|
6299
|
-
|
|
6300
|
-
|
|
6301
|
-
|
|
6302
|
-
|
|
6303
|
-
|
|
6304
|
-
|
|
6305
|
-
|
|
6306
|
-
|
|
6307
|
-
|
|
6308
|
-
|
|
6309
|
-
|
|
6310
|
-
|
|
6311
|
-
|
|
6312
|
-
|
|
6313
|
-
|
|
6314
|
-
|
|
6315
|
-
|
|
6316
|
-
|
|
6317
|
-
|
|
6318
|
-
|
|
6319
|
-
|
|
6320
|
-
|
|
6321
|
-
|
|
6322
|
-
|
|
6323
|
-
|
|
6324
|
-
|
|
6325
|
-
|
|
6326
|
-
|
|
6327
|
-
|
|
6328
|
-
|
|
6329
|
-
const
|
|
6330
|
-
? lowerer.
|
|
6331
|
-
:
|
|
6332
|
-
|
|
6333
|
-
|
|
6334
|
-
|
|
6335
|
-
|
|
6336
|
-
|
|
6337
|
-
|
|
6338
|
-
|
|
6339
|
-
|
|
6340
|
-
|
|
6341
|
-
|
|
6342
|
-
|
|
6343
|
-
|
|
6344
|
-
|
|
6345
|
-
|
|
6346
|
-
|
|
6347
|
-
|
|
6348
|
-
|
|
6349
|
-
|
|
6350
|
-
|
|
6351
|
-
|
|
6352
|
-
|
|
6416
|
+
const stmts = [];
|
|
6417
|
+
const save = (value, name) => {
|
|
6418
|
+
const local = lowerer.declareHiddenLocal(name, value.type);
|
|
6419
|
+
stmts.push({ kind: "varDecl", localId: local.id, init: value, loc });
|
|
6420
|
+
return varRef(local.id, value.type, loc);
|
|
6421
|
+
};
|
|
6422
|
+
const finish = (read, write) => {
|
|
6423
|
+
const old = save(read.type.kind === "f64" ? read : {
|
|
6424
|
+
kind: "libCall", fn: "dyn.toNumeric", args: [lowerer.coerceInto(access, read, DYN)], type: DYN, loc,
|
|
6425
|
+
}, "%incrementOld");
|
|
6426
|
+
const next = save(old.type.kind === "f64" ? {
|
|
6427
|
+
kind: "bin", op, left: old, right: { kind: "numLit", value: 1, type: F64, loc }, type: F64, loc,
|
|
6428
|
+
} : {
|
|
6429
|
+
kind: "libCall", fn: "dyn.increment", args: [old, { kind: "boolLit", value: op === "+", type: BOOL, loc }], type: DYN, loc,
|
|
6430
|
+
}, "%incrementNext");
|
|
6431
|
+
stmts.push(write(next));
|
|
6432
|
+
const result = prefix ? next : old;
|
|
6433
|
+
return { kind: "seqExpr", stmts, result, type: result.type, loc };
|
|
6434
|
+
};
|
|
6435
|
+
const raw = lowerer.lowerExpr(access.expression);
|
|
6436
|
+
// Keep native array/byte storage: boxing a native array can create a
|
|
6437
|
+
// checked view, which is not a substitute for updating its slot.
|
|
6438
|
+
if (ts.isElementAccessExpression(access) && (raw.type.kind === "array" || raw.type.kind === "bytes")) {
|
|
6439
|
+
const receiver = save(raw, "%incrementArray");
|
|
6440
|
+
const rawIndex = lowerer.lowerExpr(access.argumentExpression);
|
|
6441
|
+
const index = save(rawIndex.type.kind === "dyn" && isJsSourceFile(expr.getSourceFile())
|
|
6442
|
+
? { kind: "libCall", fn: "dyn.toNumberCoerce", args: [rawIndex], type: F64, loc } : rawIndex, "%incrementIndex");
|
|
6443
|
+
if (index.type.kind !== "f64")
|
|
6444
|
+
lowerer.unsupported("SC1090", access, "incrementing non-number array keys");
|
|
6445
|
+
const type = raw.type;
|
|
6446
|
+
const read = type.kind === "array"
|
|
6447
|
+
? arrayValueRead(lowerer, receiver, index, type.elem, loc)
|
|
6448
|
+
: { kind: "bytesIntrinsic", method: "get", receiver, args: [index], type: F64, loc };
|
|
6449
|
+
return finish(read, (value) => type.kind === "array"
|
|
6450
|
+
? arrayValueStore(lowerer, receiver, index, lowerer.coerceInto(access, value, type.elem), type.elem, loc)
|
|
6451
|
+
: { kind: "bytesSet", arr: receiver, index, value: lowerer.coerceInto(access, value, F64), loc });
|
|
6452
|
+
}
|
|
6453
|
+
if (isJsSourceFile(expr.getSourceFile()) && raw.type.kind === "dyn") {
|
|
6454
|
+
const receiver = save(raw, "%incrementReceiver");
|
|
6455
|
+
const key = save(ts.isElementAccessExpression(access)
|
|
6456
|
+
? lowerer.lowerExprExpecting(access.argumentExpression, DYN)
|
|
6457
|
+
: lowerer.coerceInto(access, { kind: "strLit", value: access.name.text, type: STRING, loc }, DYN), "%incrementKey");
|
|
6458
|
+
return finish({ kind: "dynKeyGet", value: receiver, key, type: DYN, loc }, (value) => ({
|
|
6459
|
+
kind: "exprStmt", expr: { kind: "libCall", fn: "dyn.keySetComputed", args: [receiver, key, lowerer.coerceInto(access, value, DYN)], type: VOID, loc }, loc,
|
|
6460
|
+
}));
|
|
6461
|
+
}
|
|
6462
|
+
const target = ts.isPropertyAccessExpression(access) ? lowerer.fieldTarget(access) : symbolFieldTarget(lowerer, access);
|
|
6463
|
+
if (target && lowerer.dynConvertible(target.fieldType)) {
|
|
6464
|
+
target.obj = save(target.obj, "%incrementReceiver");
|
|
6465
|
+
return finish(lowerer.fieldGetExpr(target, loc, access), (value) => lowerer.fieldSetStmt(target, lowerer.coerceInto(access, value, target.fieldType), loc, access));
|
|
6466
|
+
}
|
|
6467
|
+
if (isJsSourceFile(expr.getSourceFile()) && lowerer.dynConvertible(raw.type)) {
|
|
6468
|
+
const receiver = save(lowerer.coerceInto(access.expression, raw, DYN), "%incrementReceiver");
|
|
6469
|
+
const key = save(ts.isElementAccessExpression(access)
|
|
6470
|
+
? lowerer.lowerExprExpecting(access.argumentExpression, DYN)
|
|
6471
|
+
: lowerer.coerceInto(access, { kind: "strLit", value: access.name.text, type: STRING, loc }, DYN), "%incrementKey");
|
|
6472
|
+
return finish({ kind: "dynKeyGet", value: receiver, key, type: DYN, loc }, (value) => ({
|
|
6473
|
+
kind: "exprStmt", expr: { kind: "libCall", fn: "dyn.keySetComputed", args: [receiver, key, lowerer.coerceInto(access, value, DYN)], type: VOID, loc }, loc,
|
|
6474
|
+
}));
|
|
6475
|
+
}
|
|
6476
|
+
lowerer.unsupported("SC1045", expr, "increment/decrement of this member target");
|
|
6353
6477
|
}
|
|
6354
6478
|
const target = lowerer.resolveWritable(expr.operand);
|
|
6355
6479
|
if (!target) {
|
|
@@ -6429,6 +6553,7 @@ export function lowerCompoundValueToTarget(lowerer, expr, compound, target) {
|
|
|
6429
6553
|
const loc = locOf(expr);
|
|
6430
6554
|
const read = { kind: "varRef", localId: target.id, type: target.type, loc: locOf(expr.left) };
|
|
6431
6555
|
const rhs = lowerer.lowerExpr(expr.right);
|
|
6556
|
+
const numericRead = lowerOptionalNumber(lowerer, read, loc);
|
|
6432
6557
|
const numericRhs = lowerOptionalNumber(lowerer, rhs, loc);
|
|
6433
6558
|
if (target.type.kind === "jsval" || rhs.type.kind === "jsval") {
|
|
6434
6559
|
const JS_COMPOUND = { "+": "add", "-": "sub", "*": "mul", "/": "div", "%": "mod", "**": "pow" };
|
|
@@ -6445,8 +6570,8 @@ export function lowerCompoundValueToTarget(lowerer, expr, compound, target) {
|
|
|
6445
6570
|
if (compound === "+" && target.type.kind === "string") {
|
|
6446
6571
|
return { kind: "strConcat", left: read, right: lowerer.ensureString(rhs, expr.right), type: STRING, loc };
|
|
6447
6572
|
}
|
|
6448
|
-
if (
|
|
6449
|
-
return { kind: "bin", op: compound, left:
|
|
6573
|
+
if (numericRead.type.kind === "f64" && numericRhs.type.kind === "f64") {
|
|
6574
|
+
return lowerer.coerceInto(expr, { kind: "bin", op: compound, left: numericRead, right: numericRhs, type: F64, loc }, target.type);
|
|
6450
6575
|
}
|
|
6451
6576
|
if ((target.type.kind === "dyn" || rhs.type.kind === "dyn") &&
|
|
6452
6577
|
isJsSourceFile(expr.getSourceFile())) {
|
|
@@ -6467,6 +6592,9 @@ export function lowerBinary(lowerer, expr) {
|
|
|
6467
6592
|
return cacheHas;
|
|
6468
6593
|
if (op === ts.SyntaxKind.EqualsToken || (op >= ts.SyntaxKind.FirstCompoundAssignment && op <= ts.SyntaxKind.LastCompoundAssignment)) {
|
|
6469
6594
|
if (op === ts.SyntaxKind.EqualsToken) {
|
|
6595
|
+
const prototypeWrite = lowerClassPrototypeComputedAssignment(lowerer, expr);
|
|
6596
|
+
if (prototypeWrite)
|
|
6597
|
+
return prototypeWrite;
|
|
6470
6598
|
const callableWrite = lowerNativeFunctionAssignment(lowerer, expr);
|
|
6471
6599
|
if (callableWrite)
|
|
6472
6600
|
return callableWrite;
|
|
@@ -6731,8 +6859,14 @@ export function lowerBinary(lowerer, expr) {
|
|
|
6731
6859
|
}
|
|
6732
6860
|
const strictEquality = op === ts.SyntaxKind.EqualsEqualsEqualsToken ||
|
|
6733
6861
|
op === ts.SyntaxKind.ExclamationEqualsEqualsToken;
|
|
6734
|
-
|
|
6735
|
-
|
|
6862
|
+
let left = strictEquality ? lowerAbsenceProbe(lowerer, expr.left) ?? lowerer.lowerExpr(expr.left) : lowerer.lowerExpr(expr.left);
|
|
6863
|
+
let right = strictEquality ? lowerAbsenceProbe(lowerer, expr.right) ?? lowerer.lowerExpr(expr.right) : lowerer.lowerExpr(expr.right);
|
|
6864
|
+
if (strictEquality && (left.type.kind === "caught" || right.type.kind === "caught")) {
|
|
6865
|
+
if (left.type.kind === "caught")
|
|
6866
|
+
left = { kind: "caughtToDyn", value: left, type: DYN, loc: left.loc };
|
|
6867
|
+
if (right.type.kind === "caught")
|
|
6868
|
+
right = { kind: "caughtToDyn", value: right, type: DYN, loc: right.loc };
|
|
6869
|
+
}
|
|
6736
6870
|
if (left.type.kind === "dyn" || right.type.kind === "dyn") {
|
|
6737
6871
|
// The narrowing unit comparisons ARE answerable on unknown: `v ===
|
|
6738
6872
|
// undefined` / `v !== null` test the dyn node's kind directly, and
|
|
@@ -6760,7 +6894,7 @@ export function lowerBinary(lowerer, expr) {
|
|
|
6760
6894
|
const dynSide = left.type.kind === "dyn" ? left : right;
|
|
6761
6895
|
const scalarSide = dynSide === left ? right : left;
|
|
6762
6896
|
const reference = scalarSide.type;
|
|
6763
|
-
if ((
|
|
6897
|
+
if ((reference.kind === "object" || reference.kind === "record" || reference.kind === "array" ||
|
|
6764
6898
|
reference.kind === "bytes" || reference.kind === "func" || reference.kind === "classval" || reference.kind === "regex" || reference.kind === "url" ||
|
|
6765
6899
|
reference.kind === "bigint" || reference.kind === "symbol" || reference.kind === "map" || reference.kind === "set" || reference.kind === "union" || DYN_HANDLE_KINDS.has(reference.kind)) &&
|
|
6766
6900
|
lowerer.dynConvertible(reference)) {
|
|
@@ -7976,6 +8110,8 @@ function lowerDynTypeofTest(lowerer, expr, loc) {
|
|
|
7976
8110
|
for (const [a, b] of operandPairs(expr)) {
|
|
7977
8111
|
if (!ts.isTypeOfExpression(a))
|
|
7978
8112
|
continue;
|
|
8113
|
+
if (lowerer.isStdlibGlobal(a.expression, "fetch"))
|
|
8114
|
+
continue;
|
|
7979
8115
|
// Non-literal comparisons use the ordinary typeof string value,
|
|
7980
8116
|
// preserving both operands' evaluation order without a narrowing test.
|
|
7981
8117
|
if (!ts.isStringLiteral(b))
|
|
@@ -9338,11 +9474,10 @@ function matchAllRegexOf(lowerer, e) {
|
|
|
9338
9474
|
* member fence);
|
|
9339
9475
|
* - a nullable receiver (the JS `s.match(re).groups` idiom) throws
|
|
9340
9476
|
* Node's exact TypeError on the unit arms;
|
|
9341
|
-
* - every declared name is a key (nonparticipating groups hold
|
|
9342
|
-
*
|
|
9477
|
+
* - every declared name is a key (nonparticipating groups hold undefined,
|
|
9478
|
+
* exactly the slice elements they project);
|
|
9343
9479
|
* - ES2025 duplicate names (distinct alternatives) project the first
|
|
9344
|
-
* participating occurrence
|
|
9345
|
-
* participating-empty "" falls through to slots that are "" anyway;
|
|
9480
|
+
* participating occurrence, including a participating empty string;
|
|
9346
9481
|
* - key order is group declaration order (declaredOrder), Node's own.
|
|
9347
9482
|
* The record has no prototype — Node's groups object is null-prototype,
|
|
9348
9483
|
* observable only through util.inspect's "[Object: null prototype]"
|
|
@@ -9419,12 +9554,8 @@ function matchSliceUnionArms(lowerer, unionId) {
|
|
|
9419
9554
|
}
|
|
9420
9555
|
return { arrTag, units };
|
|
9421
9556
|
}
|
|
9422
|
-
/** The groups-record projection
|
|
9423
|
-
*
|
|
9424
|
-
* record with no declared fields — holding one OVERFLOW entry per
|
|
9425
|
-
* group name in declaration order, so every downstream consumer
|
|
9426
|
-
* (recordKeyGet reads, Object.keys, JSON, the `| undefined` union
|
|
9427
|
-
* wrap) meets exactly the type tsc reported. One lifted helper per
|
|
9557
|
+
/** The groups-record projection holds string | undefined values, including
|
|
9558
|
+
* an own overflow entry for every nonparticipating capture. One lifted helper per
|
|
9428
9559
|
* (index list, receiver representation) builds it; see
|
|
9429
9560
|
* lowerMatchGroupsRead for the semantics the body encodes. */
|
|
9430
9561
|
export function lowerGroupsProjection(lowerer, recv, groups, loc) {
|
|
@@ -9442,7 +9573,8 @@ export function lowerGroupsProjection(lowerer, recv, groups, loc) {
|
|
|
9442
9573
|
order.push(g.name);
|
|
9443
9574
|
}
|
|
9444
9575
|
}
|
|
9445
|
-
const
|
|
9576
|
+
const valueType = arrayValueType(lowerer, STRING);
|
|
9577
|
+
const shapeId = lowerer.shapes.intern([], false, valueType);
|
|
9446
9578
|
const recordT = { kind: "record", shapeId };
|
|
9447
9579
|
const recvKey = recv.type.kind === "union" ? recv.type.unionId : "arr";
|
|
9448
9580
|
const key = `regexgroups:${shapeId}:${groups.map((g) => `${g.name}=${g.index}`).join(",")}:${recvKey}`;
|
|
@@ -9484,7 +9616,7 @@ export function lowerGroupsProjection(lowerer, recv, groups, loc) {
|
|
|
9484
9616
|
body.push({ kind: "varDecl", localId: "arr.0", init: narrowed, loc });
|
|
9485
9617
|
mRef = { kind: "varRef", localId: "arr.0", type: arrayOf(STRING), loc };
|
|
9486
9618
|
}
|
|
9487
|
-
const elem = (index) => (
|
|
9619
|
+
const elem = (index) => arrayValueRead(lowerer, mRef, numLit(index, loc), STRING, loc);
|
|
9488
9620
|
const values = new Map();
|
|
9489
9621
|
order.forEach((name, i) => {
|
|
9490
9622
|
const idxs = indicesByName.get(name);
|
|
@@ -9492,16 +9624,16 @@ export function lowerGroupsProjection(lowerer, recv, groups, loc) {
|
|
|
9492
9624
|
values.set(name, elem(idxs[0]));
|
|
9493
9625
|
return;
|
|
9494
9626
|
}
|
|
9495
|
-
// Duplicates: the first
|
|
9496
|
-
//
|
|
9627
|
+
// Duplicates: the first participating occurrence. Empty strings
|
|
9628
|
+
// participate; undefined means this alternative did not match.
|
|
9497
9629
|
const id = `g${i}.0`;
|
|
9498
|
-
locals.push({ id, name: `g${i}`, type:
|
|
9630
|
+
locals.push({ id, name: `g${i}`, type: valueType, mutable: true });
|
|
9499
9631
|
body.push({ kind: "varDecl", localId: id, init: elem(idxs[0]), loc });
|
|
9500
|
-
const gRef = { kind: "varRef", localId: id, type:
|
|
9632
|
+
const gRef = { kind: "varRef", localId: id, type: valueType, loc };
|
|
9501
9633
|
for (const idx of idxs.slice(1)) {
|
|
9502
9634
|
body.push({
|
|
9503
9635
|
kind: "if",
|
|
9504
|
-
cond: { kind: "
|
|
9636
|
+
cond: { kind: "unionIsTag", unionId: valueType.kind === "union" ? valueType.unionId : "", tag: valueType.kind === "union" ? lowerer.armTag(valueType.unionId, UNDEFINED_T) : -1, negated: false, value: gRef, type: BOOL, loc },
|
|
9505
9637
|
then: [{ kind: "assign", localId: id, value: elem(idx), loc }],
|
|
9506
9638
|
else_: null,
|
|
9507
9639
|
loc,
|
|
@@ -9595,6 +9727,15 @@ export function lowerFieldRead(lowerer, expr) {
|
|
|
9595
9727
|
if (receiverIr?.kind === "record") {
|
|
9596
9728
|
const shape = lowerer.shapes.get(receiverIr.shapeId);
|
|
9597
9729
|
if (shape?.indexValue && !shape.fields.some((f) => f.name === expr.name.text)) {
|
|
9730
|
+
const actual = tryLowerExpression(lowerer, expr.expression);
|
|
9731
|
+
if (actual?.type.kind === "dyn")
|
|
9732
|
+
return { kind: "dynKeyGet", value: actual,
|
|
9733
|
+
key: { kind: "strLit", value: expr.name.text, type: STRING, loc: locOf(expr.name) }, type: DYN, loc: locOf(expr) };
|
|
9734
|
+
if (actual?.type.kind === "record") {
|
|
9735
|
+
const field = lowerer.shapes.get(actual.type.shapeId)?.fields.find((field) => field.name === expr.name.text);
|
|
9736
|
+
if (field)
|
|
9737
|
+
return { kind: "recordGet", obj: actual, shapeId: actual.type.shapeId, field: field.name, type: field.type, loc: locOf(expr) };
|
|
9738
|
+
}
|
|
9598
9739
|
lowerer.unsupported("SC1090", expr, `dot access to index-signature keys (spell it r["${expr.name.text}"] — brackets are the index-signature form)`);
|
|
9599
9740
|
}
|
|
9600
9741
|
}
|
|
@@ -9875,6 +10016,10 @@ function lowerUnionKeyedRead(lowerer, expr, unionId, value, key, literalField) {
|
|
|
9875
10016
|
* or accessor dispatch. Resolve the property from the represented class;
|
|
9876
10017
|
* the optional checker view must not turn its getter into a record read. */
|
|
9877
10018
|
function classFieldTarget(lowerer, receiverNode, receiverType, fieldName, saved) {
|
|
10019
|
+
const represented = saved ?? lowerer.lowerExpr(receiverNode);
|
|
10020
|
+
if (represented.type.kind === "dyn" && isJsSourceFile(receiverNode.getSourceFile()) && !fieldName.startsWith("#")) {
|
|
10021
|
+
return { container: "dynamic", obj: represented, field: fieldName, fieldType: DYN };
|
|
10022
|
+
}
|
|
9878
10023
|
const info = lowerer.classes.get(receiverType.className);
|
|
9879
10024
|
if (!info) {
|
|
9880
10025
|
// A receiver typed as a class whose collection deferred: the
|
|
@@ -9884,14 +10029,14 @@ function classFieldTarget(lowerer, receiverNode, receiverType, fieldName, saved)
|
|
|
9884
10029
|
}
|
|
9885
10030
|
const fieldType = info.fields.get(fieldName);
|
|
9886
10031
|
const lowerObjectReceiver = () => {
|
|
9887
|
-
let obj =
|
|
10032
|
+
let obj = represented;
|
|
9888
10033
|
if (obj.type.kind === "union") {
|
|
9889
10034
|
const helper = lowerer.narrowedArmHelper(obj.type.unionId, receiverType, locOf(receiverNode));
|
|
9890
10035
|
obj = helper
|
|
9891
10036
|
? { kind: "call", callee: helper, args: [obj], type: receiverType, loc: locOf(receiverNode) }
|
|
9892
10037
|
: lowerer.maybeNarrow(obj, receiverNode);
|
|
9893
10038
|
}
|
|
9894
|
-
return
|
|
10039
|
+
return lowerer.coerceInto(receiverNode, obj, receiverType);
|
|
9895
10040
|
};
|
|
9896
10041
|
if (fieldName === "cause" && receiverType.className !== "%DOMException") {
|
|
9897
10042
|
let root = info;
|
|
@@ -9903,6 +10048,11 @@ function classFieldTarget(lowerer, receiverNode, receiverType, fieldName, saved)
|
|
|
9903
10048
|
}
|
|
9904
10049
|
if (fieldType) {
|
|
9905
10050
|
const obj = lowerObjectReceiver();
|
|
10051
|
+
if (isJsSourceFile(receiverNode.getSourceFile()) && !fieldName.startsWith("#") &&
|
|
10052
|
+
[...lowerer.classes.values()].some((candidate) => lowerer.isSubclassOf(candidate.def.name, receiverType.className) &&
|
|
10053
|
+
(candidate.methods.has(`get:${fieldName}`) || candidate.methods.has(`set:${fieldName}`)))) {
|
|
10054
|
+
return { container: "dynamic", obj: lowerer.coerceToExpected(obj, DYN), field: fieldName, fieldType: DYN };
|
|
10055
|
+
}
|
|
9906
10056
|
return { container: "class", obj, className: receiverType.className, field: fieldName, fieldType };
|
|
9907
10057
|
}
|
|
9908
10058
|
// Accessor property: either half declared anywhere on the chain
|
|
@@ -9920,6 +10070,12 @@ function classFieldTarget(lowerer, receiverNode, receiverType, fieldName, saved)
|
|
|
9920
10070
|
fieldType: getF ? getF.sig.ret : setF.sig.params[0].type,
|
|
9921
10071
|
};
|
|
9922
10072
|
}
|
|
10073
|
+
if (info.decl && isJsSourceFile(info.decl.getSourceFile())) {
|
|
10074
|
+
const property = lowerer.checker.getPropertyOfType(lowerer.checker.getTypeAtLocation(receiverNode), fieldName);
|
|
10075
|
+
if (property && lowerer.checker.declarationsOf(property).some((declaration) => ts.isPropertyAccessExpression(declaration) || ts.isBinaryExpression(declaration) || ts.isElementAccessExpression(declaration))) {
|
|
10076
|
+
return { container: "dynamic", obj: lowerer.coerceToExpected(lowerObjectReceiver(), DYN), field: fieldName, fieldType: DYN };
|
|
10077
|
+
}
|
|
10078
|
+
}
|
|
9923
10079
|
return null;
|
|
9924
10080
|
}
|
|
9925
10081
|
/** A structural assertion can leave the value in its original class
|
|
@@ -9945,6 +10101,8 @@ function representedClassFieldTarget(lowerer, receiverNode, fieldName, value) {
|
|
|
9945
10101
|
* receivers) a declared accessor property. Returns the pieces of a
|
|
9946
10102
|
* fieldSet/recordSet/accessor-call (minus value/kind) or null. */
|
|
9947
10103
|
export function fieldTarget(lowerer, access) {
|
|
10104
|
+
if (lowerer.cjsLocalModuleBindingOf(access.expression))
|
|
10105
|
+
return null;
|
|
9948
10106
|
if (lowerer.chainBlocked(access))
|
|
9949
10107
|
return null;
|
|
9950
10108
|
if (access.name.text === "stackTraceLimit" && lowerer.isStdlibGlobal(access.expression, "Error")) {
|
|
@@ -10059,8 +10217,17 @@ export function fieldTarget(lowerer, access) {
|
|
|
10059
10217
|
return declaredArms.every((a) => ivArms.some((b) => typeEquals(a, b)));
|
|
10060
10218
|
};
|
|
10061
10219
|
if (!nameSym || nameSym.name === ts.InternalSymbolName.Index || canonicalized()) {
|
|
10062
|
-
|
|
10063
|
-
|
|
10220
|
+
let obj = lowerer.lowerExpr(access.expression);
|
|
10221
|
+
if (obj.type.kind === "union" && lowerer.armTag(obj.type.unionId, UNDEFINED_T) >= 0) {
|
|
10222
|
+
const present = lowerer.stripUndefinedArm(obj.type);
|
|
10223
|
+
obj = present.kind === "record" ? lowerer.runtimeOptionalPropertyReceiver(access.expression, obj, present, access.name.text) ?? obj : lowerer.maybeNarrow(obj, access.expression);
|
|
10224
|
+
}
|
|
10225
|
+
if (obj.type.kind !== "record")
|
|
10226
|
+
return null;
|
|
10227
|
+
const actualShape = lowerer.shapes.get(obj.type.shapeId);
|
|
10228
|
+
if (!actualShape?.indexValue)
|
|
10229
|
+
return null;
|
|
10230
|
+
return { container: "recordOvf", obj, shapeId: obj.type.shapeId, field: access.name.text, fieldType: actualShape.indexValue };
|
|
10064
10231
|
}
|
|
10065
10232
|
}
|
|
10066
10233
|
return null;
|
|
@@ -10158,6 +10325,11 @@ function symbolFieldTarget(lowerer, expr) {
|
|
|
10158
10325
|
* tsc-clean (the property types as the setter's param), but Node yields
|
|
10159
10326
|
* undefined, which these property types cannot represent. */
|
|
10160
10327
|
export function fieldGetExpr(lowerer, target, loc, blame) {
|
|
10328
|
+
if (target.container === "dynamic")
|
|
10329
|
+
return {
|
|
10330
|
+
kind: "dynKeyGet", value: target.obj,
|
|
10331
|
+
key: { kind: "strLit", value: target.field, type: STRING, loc }, type: DYN, loc,
|
|
10332
|
+
};
|
|
10161
10333
|
if (target.container === "class" && (target.field === "message" || target.field === "name") && (target.className === "%Error" || lowerer.isSubclassOf(target.className, "%Error")))
|
|
10162
10334
|
return errorPropertyRead(lowerer, target.obj, target.field);
|
|
10163
10335
|
if (target.container === "errorStackLimit") {
|
|
@@ -10267,6 +10439,11 @@ export function fieldGetExpr(lowerer, target, loc, blame) {
|
|
|
10267
10439
|
* call). A write to a getter-only property never gets here in a clean
|
|
10268
10440
|
* program (tsc's TS2540 is the fence); the rejection is the backstop. */
|
|
10269
10441
|
export function fieldSetStmt(lowerer, target, value, loc, blame) {
|
|
10442
|
+
if (target.container === "dynamic")
|
|
10443
|
+
return {
|
|
10444
|
+
kind: "exprStmt", expr: { kind: "libCall", fn: "dyn.keySet",
|
|
10445
|
+
args: [target.obj, { kind: "strLit", value: target.field, type: STRING, loc }, lowerer.coerceToExpected(value, DYN)], type: VOID, loc }, loc,
|
|
10446
|
+
};
|
|
10270
10447
|
if (target.container === "class" && (target.field === "message" || target.field === "name") && (target.className === "%Error" || lowerer.isSubclassOf(target.className, "%Error")))
|
|
10271
10448
|
return errorPropertyWrite(lowerer, target.obj, value, target.field);
|
|
10272
10449
|
if (target.container === "errorStackLimit") {
|
|
@@ -10434,7 +10611,7 @@ export function lowerPromiseAllTupleCall(lowerer, call, access) {
|
|
|
10434
10611
|
const loc = locOf(call);
|
|
10435
10612
|
const inner = first.inner;
|
|
10436
10613
|
const entryT = { kind: "promise", inner };
|
|
10437
|
-
const elems = argNode.elements.map((el) => lowerer.
|
|
10614
|
+
const elems = argNode.elements.map((el) => lowerer.lowerExprExpecting(el, entryT));
|
|
10438
10615
|
const entries = { kind: "arrayLit", elems, type: arrayOf(entryT), loc };
|
|
10439
10616
|
const type = inner.kind === "void"
|
|
10440
10617
|
? { kind: "promise", inner: VOID }
|
|
@@ -10466,7 +10643,10 @@ function lowerFieldCompoundValue(lowerer, access, op, rhsNode, loc) {
|
|
|
10466
10643
|
// NaN — SEMANTICS.md), combine, write back (dyn.keySet). The
|
|
10467
10644
|
// receiver and member read are saved before evaluating the RHS.
|
|
10468
10645
|
if (ts.isPropertyAccessExpression(access)) {
|
|
10469
|
-
|
|
10646
|
+
let probed = tryLowerExpression(lowerer, access.expression);
|
|
10647
|
+
const staticClass = probed?.type.kind === "classval" ? lowerer.classes.get(probed.type.className) : undefined;
|
|
10648
|
+
if (probed && staticClass && hasRuntimeStatics(staticClass))
|
|
10649
|
+
probed = lowerer.coerceToExpected(probed, DYN);
|
|
10470
10650
|
if (probed?.type.kind === "dyn") {
|
|
10471
10651
|
const receiver = save(probed, "%compoundReceiver");
|
|
10472
10652
|
const key = { kind: "strLit", value: access.name.text, type: STRING, loc: locOf(access.name) };
|
|
@@ -10475,6 +10655,14 @@ function lowerFieldCompoundValue(lowerer, access, op, rhsNode, loc) {
|
|
|
10475
10655
|
const rhs = save(rhsNode
|
|
10476
10656
|
? lowerer.lowerExpr(rhsNode)
|
|
10477
10657
|
: { kind: "numLit", value: 1, type: F64, loc }, "%compoundRhs");
|
|
10658
|
+
if (isJsSourceFile(access.getSourceFile()) && lowerer.dynConvertible(rhs.type)) {
|
|
10659
|
+
const numeric = ["-", "*", "/", "%", "**"].includes(op);
|
|
10660
|
+
const right = lowerer.coerceToExpected(rhs, DYN);
|
|
10661
|
+
const value = save({ kind: "libCall", fn: op === "+" ? "dyn.add" : numeric ? "dyn.arithmetic" : "dyn.bitwise",
|
|
10662
|
+
args: op === "+" ? [read, right] : [read, right, { kind: "strLit", value: op, type: STRING, loc }], type: DYN, loc }, "%compoundResult");
|
|
10663
|
+
body.push({ kind: "exprStmt", expr: { kind: "libCall", fn: "dyn.keySet", args: [receiver, key, value], type: VOID, loc }, loc });
|
|
10664
|
+
return { kind: "seqExpr", stmts: body, result: value, type: DYN, loc };
|
|
10665
|
+
}
|
|
10478
10666
|
const numericRhs = rhs.type.kind === "dyn"
|
|
10479
10667
|
? { kind: "dynCheck", value: rhs, type: F64, loc: rhs.loc }
|
|
10480
10668
|
: lowerOptionalNumber(lowerer, rhs, loc);
|
|
@@ -10512,35 +10700,12 @@ function lowerFieldCompoundValue(lowerer, access, op, rhsNode, loc) {
|
|
|
10512
10700
|
value = { kind: "bin", op, left: read, right: numericRhs, type: F64, loc };
|
|
10513
10701
|
}
|
|
10514
10702
|
else if (target.fieldType.kind === "dyn" &&
|
|
10515
|
-
isJsSourceFile(access.getSourceFile())
|
|
10516
|
-
|
|
10517
|
-
|
|
10518
|
-
|
|
10519
|
-
|
|
10520
|
-
|
|
10521
|
-
// ToNumber; SEMANTICS.md) and computes natively; the result boxes
|
|
10522
|
-
// back into the field's dyn slot. `+=` with a string RHS is the
|
|
10523
|
-
// string-context read: check to string, concat, box back.
|
|
10524
|
-
const checkNum = (e) => e.type.kind === "dyn" ? { kind: "dynCheck", value: e, type: F64, loc: e.loc } : e;
|
|
10525
|
-
if (op === "+" && rhs.type.kind === "string") {
|
|
10526
|
-
const concat = {
|
|
10527
|
-
kind: "strConcat",
|
|
10528
|
-
// String-context read: JS's String(unknown) over the field —
|
|
10529
|
-
// the JS-exact dyn walker, never a checked cast.
|
|
10530
|
-
left: { kind: "toString", operand: read, type: STRING, loc },
|
|
10531
|
-
right: rhs,
|
|
10532
|
-
type: STRING,
|
|
10533
|
-
loc,
|
|
10534
|
-
};
|
|
10535
|
-
value = { kind: "dynFrom", value: concat, type: DYN, loc };
|
|
10536
|
-
}
|
|
10537
|
-
else if (rhs.type.kind === "f64" || rhs.type.kind === "dyn") {
|
|
10538
|
-
const bin = { kind: "bin", op, left: checkNum(read), right: checkNum(rhs), type: F64, loc };
|
|
10539
|
-
value = { kind: "dynFrom", value: bin, type: DYN, loc };
|
|
10540
|
-
}
|
|
10541
|
-
else {
|
|
10542
|
-
lowerer.unsupported("SC1043", access);
|
|
10543
|
-
}
|
|
10703
|
+
isJsSourceFile(access.getSourceFile())) {
|
|
10704
|
+
const numeric = ["-", "*", "/", "%", "**"].includes(op);
|
|
10705
|
+
const left = lowerer.coerceToExpected(read, DYN);
|
|
10706
|
+
const right = lowerer.coerceToExpected(rhs, DYN);
|
|
10707
|
+
value = { kind: "libCall", fn: op === "+" ? "dyn.add" : numeric ? "dyn.arithmetic" : "dyn.bitwise",
|
|
10708
|
+
args: op === "+" ? [left, right] : [left, right, { kind: "strLit", value: op, type: STRING, loc }], type: DYN, loc };
|
|
10544
10709
|
}
|
|
10545
10710
|
else {
|
|
10546
10711
|
lowerer.unsupported("SC1043", access);
|