@scriptc/compiler 0.2.0 → 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 +84 -19
- package/dist/backend/cycle-analysis.js +1 -0
- package/dist/backend/cycle-analysis.js.map +1 -1
- package/dist/backend/llvm/classes.js +21 -5
- package/dist/backend/llvm/classes.js.map +1 -1
- package/dist/backend/llvm/dyn.d.ts +8 -0
- package/dist/backend/llvm/dyn.js +247 -108
- package/dist/backend/llvm/dyn.js.map +1 -1
- package/dist/backend/llvm/emitter.d.ts +6 -3
- package/dist/backend/llvm/emitter.js +99 -25
- 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-bytes.d.ts +1 -1
- package/dist/backend/llvm/expr-bytes.js +28 -17
- package/dist/backend/llvm/expr-bytes.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-control.js +1 -1
- package/dist/backend/llvm/expr-control.js.map +1 -1
- package/dist/backend/llvm/expr-dynamic.js +47 -13
- package/dist/backend/llvm/expr-dynamic.js.map +1 -1
- package/dist/backend/llvm/expr-primitives.js +11 -1
- package/dist/backend/llvm/expr-primitives.js.map +1 -1
- package/dist/backend/llvm/expr-records.js +11 -3
- package/dist/backend/llvm/expr-records.js.map +1 -1
- package/dist/backend/llvm/expr-stream-bridges.js +187 -18
- 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-process.js +23 -3
- package/dist/backend/llvm/lib-process.js.map +1 -1
- package/dist/backend/llvm/lib-shared.js +80 -3
- package/dist/backend/llvm/lib-shared.js.map +1 -1
- package/dist/backend/llvm/shapes.js +8 -2
- package/dist/backend/llvm/shapes.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/native-toolchain.d.ts +3 -6
- package/dist/backend/native-toolchain.js +53 -23
- package/dist/backend/native-toolchain.js.map +1 -1
- package/dist/backend/native-toolchain.test.js +72 -0
- package/dist/backend/native-toolchain.test.js.map +1 -1
- package/dist/backend/runtime-abi.d.ts +2 -2
- package/dist/backend/runtime-abi.js +2 -2
- package/dist/backend/runtime-pack-native.js +4 -1
- package/dist/backend/runtime-pack-native.js.map +1 -1
- package/dist/backend/runtime-pack-targets.test.js +1 -1
- package/dist/backend/runtime-pack.test.js +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/backend/targets.js +10 -6
- package/dist/backend/targets.js.map +1 -1
- package/dist/backend/targets.test.js +7 -0
- package/dist/backend/targets.test.js.map +1 -1
- package/dist/compat/fetch-profile.js +34 -4
- package/dist/compat/fetch-profile.js.map +1 -1
- package/dist/coverage/surface-manifest.js +5 -3
- package/dist/coverage/surface-manifest.js.map +1 -1
- package/dist/diagnostics/diagnostic.js +2 -7
- package/dist/diagnostics/diagnostic.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 +4 -4
- package/dist/frontend/lowering/class-callbacks.js +29 -17
- package/dist/frontend/lowering/class-callbacks.js.map +1 -1
- package/dist/frontend/lowering/class-construction.d.ts +14 -0
- package/dist/frontend/lowering/class-construction.js +185 -0
- package/dist/frontend/lowering/class-construction.js.map +1 -0
- package/dist/frontend/lowering/class-descriptors.d.ts +2 -1
- package/dist/frontend/lowering/class-descriptors.js +58 -10
- package/dist/frontend/lowering/class-descriptors.js.map +1 -1
- package/dist/frontend/lowering/class-dynamic-dispatch.d.ts +36 -1
- package/dist/frontend/lowering/class-dynamic-dispatch.js +965 -91
- package/dist/frontend/lowering/class-dynamic-dispatch.js.map +1 -1
- package/dist/frontend/lowering/class-dynamic-dispatch.test.js +66 -3
- 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 +15 -42
- package/dist/frontend/lowering/class-instance-constructor.js.map +1 -1
- package/dist/frontend/lowering/class-method-values.d.ts +20 -1
- package/dist/frontend/lowering/class-method-values.js +120 -24
- package/dist/frontend/lowering/class-method-values.js.map +1 -1
- package/dist/frontend/lowering/class-prototypes.d.ts +10 -3
- package/dist/frontend/lowering/class-prototypes.js +188 -21
- package/dist/frontend/lowering/class-prototypes.js.map +1 -1
- package/dist/frontend/lowering/class-runtime-statics.d.ts +8 -0
- package/dist/frontend/lowering/class-runtime-statics.js +84 -0
- package/dist/frontend/lowering/class-runtime-statics.js.map +1 -0
- package/dist/frontend/lowering/class-static-data.d.ts +1 -0
- package/dist/frontend/lowering/class-static-data.js +6 -4
- package/dist/frontend/lowering/class-static-data.js.map +1 -1
- package/dist/frontend/lowering/containers/bytes.js +66 -17
- package/dist/frontend/lowering/containers/bytes.js.map +1 -1
- package/dist/frontend/lowering/containers/string-and-regexp.js +24 -47
- 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/error-methods.d.ts +4 -1
- package/dist/frontend/lowering/error-methods.js +100 -1
- package/dist/frontend/lowering/error-methods.js.map +1 -1
- package/dist/frontend/lowering/expressions/native-proxy.js +3 -1
- package/dist/frontend/lowering/expressions/native-proxy.js.map +1 -1
- package/dist/frontend/lowering/expressions/nullish-assignment.js +19 -11
- package/dist/frontend/lowering/expressions/nullish-assignment.js.map +1 -1
- package/dist/frontend/lowering/expressions/object-literals.js +68 -7
- package/dist/frontend/lowering/expressions/object-literals.js.map +1 -1
- package/dist/frontend/lowering/expressions/optional-chains.d.ts +3 -4
- package/dist/frontend/lowering/expressions/optional-chains.js +72 -20
- package/dist/frontend/lowering/expressions/optional-chains.js.map +1 -1
- package/dist/frontend/lowering/function-context.d.ts +11 -3
- package/dist/frontend/lowering/function-context.js +11 -5
- package/dist/frontend/lowering/function-context.js.map +1 -1
- package/dist/frontend/lowering/generator-dynamic-dispatch.d.ts +1 -0
- package/dist/frontend/lowering/generator-dynamic-dispatch.js +39 -1
- package/dist/frontend/lowering/generator-dynamic-dispatch.js.map +1 -1
- package/dist/frontend/lowering/lib-boundary.d.ts +2 -0
- package/dist/frontend/lowering/lib-boundary.js +54 -1
- package/dist/frontend/lowering/lib-boundary.js.map +1 -1
- package/dist/frontend/lowering/lower-builtin-values.d.ts +7 -1
- package/dist/frontend/lowering/lower-builtin-values.js +113 -3
- package/dist/frontend/lowering/lower-builtin-values.js.map +1 -1
- package/dist/frontend/lowering/lower-builtins.d.ts +10 -14
- package/dist/frontend/lowering/lower-builtins.js +443 -88
- package/dist/frontend/lowering/lower-builtins.js.map +1 -1
- package/dist/frontend/lowering/lower-calls.d.ts +8 -5
- package/dist/frontend/lowering/lower-calls.js +717 -142
- package/dist/frontend/lowering/lower-calls.js.map +1 -1
- package/dist/frontend/lowering/lower-classes.d.ts +18 -13
- package/dist/frontend/lowering/lower-classes.js +1076 -188
- package/dist/frontend/lowering/lower-classes.js.map +1 -1
- package/dist/frontend/lowering/lower-containers.d.ts +9 -0
- package/dist/frontend/lowering/lower-containers.js +172 -51
- package/dist/frontend/lowering/lower-containers.js.map +1 -1
- package/dist/frontend/lowering/lower-dgram.js +3 -2
- package/dist/frontend/lowering/lower-dgram.js.map +1 -1
- package/dist/frontend/lowering/lower-exprs.d.ts +12 -8
- package/dist/frontend/lowering/lower-exprs.js +802 -232
- package/dist/frontend/lowering/lower-exprs.js.map +1 -1
- package/dist/frontend/lowering/lower-generators.js +17 -2
- 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.d.ts +2 -0
- package/dist/frontend/lowering/lower-island.js +36 -10
- 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 +380 -73
- package/dist/frontend/lowering/lower-modules.js.map +1 -1
- package/dist/frontend/lowering/lower-modules.test.d.ts +1 -0
- package/dist/frontend/lowering/lower-modules.test.js +107 -0
- package/dist/frontend/lowering/lower-modules.test.js.map +1 -0
- package/dist/frontend/lowering/lower-stmts.d.ts +7 -3
- package/dist/frontend/lowering/lower-stmts.js +221 -47
- 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 +3 -1
- 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 +53 -10
- package/dist/frontend/lowering/lowerer.js +390 -77
- package/dist/frontend/lowering/lowerer.js.map +1 -1
- package/dist/frontend/lowering/lowerer.test.js +187 -3
- 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 +3 -0
- package/dist/frontend/lowering/surfaces.js +48 -19
- package/dist/frontend/lowering/surfaces.js.map +1 -1
- package/dist/frontend/lowering/text-decoder-encoding.d.ts +2 -0
- package/dist/frontend/lowering/text-decoder-encoding.js +8 -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/lowering/unregistered-class-types.test.js +3 -3
- package/dist/frontend/lowering/unregistered-class-types.test.js.map +1 -1
- package/dist/frontend/npm-static-prune.js +116 -34
- package/dist/frontend/npm-static-prune.js.map +1 -1
- package/dist/frontend/npm-static-prune.test.js +8 -0
- package/dist/frontend/npm-static-prune.test.js.map +1 -1
- package/dist/frontend/program.d.ts +1 -1
- package/dist/frontend/program.js +57 -26
- package/dist/frontend/program.js.map +1 -1
- package/dist/frontend/program.test.js +18 -0
- package/dist/frontend/program.test.js.map +1 -1
- package/dist/frontend/ts7/ast.js +4 -3
- package/dist/frontend/ts7/ast.js.map +1 -1
- package/dist/frontend/ts7/semantic-json.js +5 -10
- package/dist/frontend/ts7/semantic-json.js.map +1 -1
- package/dist/frontend/type-mapper.d.ts +9 -1
- package/dist/frontend/type-mapper.js +169 -23
- package/dist/frontend/type-mapper.js.map +1 -1
- package/dist/frontend/type-mapper.test.js +23 -0
- package/dist/frontend/type-mapper.test.js.map +1 -1
- package/dist/frontend/union-discriminants.d.ts +1 -1
- package/dist/frontend/union-discriminants.js +24 -9
- 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/analysis.js +2 -0
- package/dist/ir/analysis.js.map +1 -1
- package/dist/ir/ir.d.ts +74 -41
- package/dist/ir/ir.js +167 -43
- package/dist/ir/ir.js.map +1 -1
- package/dist/ir/ir.test.js +35 -0
- package/dist/ir/ir.test.js.map +1 -1
- package/dist/ir/serialize.js +35 -3
- package/dist/ir/serialize.js.map +1 -1
- package/dist/ir/serialize.test.js +26 -1
- 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 +136 -16
- package/dist/ir/validate.js.map +1 -1
- package/dist/ir/validate.test.js +77 -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/dist/native/sanitizer.js +3 -1
- package/dist/native/sanitizer.js.map +1 -1
- package/dist/native/toolchain.d.ts +1 -1
- package/dist/native/toolchain.js +26 -6
- package/dist/native/toolchain.js.map +1 -1
- package/dist/native/toolchain.test.js +53 -1
- package/dist/native/toolchain.test.js.map +1 -1
- package/package.json +20 -20
- package/surface-manifest.json +170 -93
|
@@ -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
|
|
@@ -11,13 +12,14 @@ import * as ts from "../ts7/adapter.js";
|
|
|
11
12
|
import { dirname } from "node:path";
|
|
12
13
|
import * as posix from "node:path/posix";
|
|
13
14
|
import { pathToFileURL } from "node:url";
|
|
15
|
+
import { captureContextArguments } from "./function-context.js";
|
|
14
16
|
import { OBJECT_CALLABLE_VALUES } from "./surfaces.js";
|
|
15
17
|
import { wasiGuestPath } from "../../wasi-paths.js";
|
|
16
18
|
import { BIGINT_T, BYTES_ELEMENT_NAME, BOOL, CAUGHT, DYN, DYN_HANDLE_KINDS, F64, JSVAL, NULL_T, REF_TRUTHY_KINDS, REGEX, RUNTIME_ERROR_CLASSES, SEARCH_PARAMS_T, STRING, UNDEFINED_T, VOID, arrayOf, canAdaptDynFuncTo, canDynCheckTo, canBoxFuncIntoDyn, funcOf, isDynTypedRefType, isSupportedArrayElem, isUnitType, jsOpResultKind, shapeHasAccessorSlots, typeEquals, typeKey, unionContainerArmsOk } from "../../ir/ir.js";
|
|
17
19
|
import { cjsClassExprWholeExportOf, cjsExportAssignmentOf, cjsExportDiscardReason, isCjsExportTableLiteral, isCjsJsFile, isJsSourceFile, isModuleExportsAccess, isNodeEsmFile, locOf } from "../program.js";
|
|
18
20
|
import { ARRAY_METHODS, builtinConstLit, builtinFenceHintOf, builtinModuleConstOf, builtinModulesArrayLit, builtinModuleFnOf, COMPOUND_ASSIGN_OPS, ISLAND_SURFACE, isChildSurfaceMember, MAP_METHODS, NARROW_FIRST, SET_METHODS, STRING_INDEX_METHODS, STR_METHODS, UNSUPPORTED_EXPR, sideEffectFreeOptionValue, stdlibGlobalNameOf } from "./surfaces.js";
|
|
19
21
|
import { UNSUPPORTED, blockedBindingUseDiag, requiresDynamicPackageDiag, unsupportedDiag } from "../../diagnostics/diagnostic.js";
|
|
20
|
-
import { PoisonError, dynUndefinedExpr, jsFuncNameOf, neverTaintedJsType, nodeThrowExpr, own } from "./lowerer.js";
|
|
22
|
+
import { PoisonError, dynFallbackType, dynUndefinedExpr, jsFuncNameOf, neverTaintedJsType, nodeThrowExpr, own } from "./lowerer.js";
|
|
21
23
|
import { lowerCollectionSpread, lowerNpmStaticSafeIndexRead, lowerSafeIndexRead, strCharsCall, tryLowerNumericIndexRead } from "./lower-containers.js";
|
|
22
24
|
import { arrayValueRead, arrayValueStore, arrayValueType } from "./array-values.js";
|
|
23
25
|
import { lowerOptionalStringIndex } from "./string-index.js";
|
|
@@ -29,13 +31,14 @@ import { ambientNsRootOf, ambientUndefReadType, ambientUndefVarRootOf, ambientUn
|
|
|
29
31
|
import { expandoMemberRead, expandoWritableTarget } from "./lower-expando.js";
|
|
30
32
|
import { lowerSocketInstanceOf, lowerTlsRootCertificates } from "./lower-server.js";
|
|
31
33
|
import { findGenericMethodOn, lowerStaticFieldRead, staticFieldWriteTarget, storedClassValueType } from "./lower-classes.js";
|
|
34
|
+
import { hasRuntimeStatics } from "./class-runtime-statics.js";
|
|
32
35
|
import { bindingNeverReassigned, funcTypeFromParamShapes, implicitMonoFile, lowerTaggedTemplate, nullishGenericBindingUnitOf, objLitGenericFnInfoOf, objLitGenericFnNodeOf, requireObjLitGenericReceiver } from "./lower-calls.js";
|
|
33
36
|
import { mixinFnOfCallee } from "./lower-mixins.js";
|
|
34
37
|
import { isConstAssertionTypeNode, isGenericCallableMemberType, isParseArgsDynTypeName, underConstAssertion, unitOnlyUnion, withUnitArm } from "../type-mapper.js";
|
|
35
38
|
import { lowerYield } from "./lower-generators.js";
|
|
36
|
-
import { errorToStringCall } from "./error-methods.js";
|
|
39
|
+
import { errorPropertyRead, errorPropertyWrite, errorToStringCall } from "./error-methods.js";
|
|
37
40
|
import { lowerStreamProperty, lowerStreamStateProperty, streamSidesOf } from "./lower-stream.js";
|
|
38
|
-
import { countedFor, numLit, varRef } from "../../ir/build.js";
|
|
41
|
+
import { countedFor, numLit, strLit, varRef } from "../../ir/build.js";
|
|
39
42
|
import { unionWideningTags } from "../../ir/analysis.js";
|
|
40
43
|
import { isSafeToDiscard, isSafeToMoveConditionEarlier, isSafeToRepeat } from "./expressions/evaluation-safety.js";
|
|
41
44
|
import { globalSymbolKey } from "./expressions/global-symbols.js";
|
|
@@ -51,9 +54,14 @@ import { classSymbolKeyOf } from "./symbol-fields.js";
|
|
|
51
54
|
import { lowerClassMethodValue } from "./class-method-values.js";
|
|
52
55
|
import { classInstanceOf } from "./class-dynamic-dispatch.js";
|
|
53
56
|
import { lowerGlobalValue } from "./lower-global-value.js";
|
|
54
|
-
import { lowerClassPrototypeData } from "./class-prototypes.js";
|
|
55
|
-
import {
|
|
57
|
+
import { lowerClassPrototypeData, lowerClassPrototypeComputedAssignment } from "./class-prototypes.js";
|
|
58
|
+
import { isClassCallback } from "./class-callbacks.js";
|
|
59
|
+
import { builtinPrototypeMethod, lowerArrayIsArrayValue, lowerCheckedPredicateValue, lowerNumberParserValue, lowerObjectAssignValue, lowerStringCodesValue } from "./lower-builtin-values.js";
|
|
60
|
+
import { lowerArrayFromValue } from "./lower-containers.js";
|
|
56
61
|
import { lowerPerfHooksTypeof } from "./lower-builtins.js";
|
|
62
|
+
import { jsBindingHasOpenWrites } from "./lower-stmts.js";
|
|
63
|
+
import { checkedClassInstanceOf } from "./class-construction.js";
|
|
64
|
+
import { lowerModuleNamespaceElement } from "./module-namespace-elements.js";
|
|
57
65
|
/** A template piece's RAW text (String.raw's contract: escapes stay
|
|
58
66
|
* characters). 7's client AST ships no rawText at runtime (the typing
|
|
59
67
|
* declares it; the serialized node data omits it), so the raw span comes
|
|
@@ -580,6 +588,8 @@ function lowerExprInner(lowerer, expr) {
|
|
|
580
588
|
if (ts.isTypeOfExpression(expr)) {
|
|
581
589
|
if (lowerer.isStdlibGlobal(expr.expression, "process"))
|
|
582
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 };
|
|
583
593
|
const performance = lowerPerfHooksTypeof(lowerer, expr.expression);
|
|
584
594
|
if (performance)
|
|
585
595
|
return performance;
|
|
@@ -704,8 +714,10 @@ function lowerExprInner(lowerer, expr) {
|
|
|
704
714
|
// `arguments` in a variadic JS function (the rest-marked form): the
|
|
705
715
|
// synthetic trailing dyn-array param — lambdaSignature marked the
|
|
706
716
|
// type and lowerLambda declared the local.
|
|
707
|
-
if (expr.text === "arguments"
|
|
708
|
-
|
|
717
|
+
if (expr.text === "arguments") {
|
|
718
|
+
const binding = captureContextArguments(lowerer.fnStack);
|
|
719
|
+
if (binding)
|
|
720
|
+
return { kind: "varRef", localId: binding.id, type: DYN, loc };
|
|
709
721
|
}
|
|
710
722
|
// A compiler-projected adapter as a VALUE: the adapter has no
|
|
711
723
|
// runtime function object (its calls rewrite directly) — call it
|
|
@@ -769,7 +781,7 @@ function lowerExprInner(lowerer, expr) {
|
|
|
769
781
|
if (use?.kind === "element") {
|
|
770
782
|
const key = runtimeOptionalElementKey(use.access.argumentExpression);
|
|
771
783
|
if (key === null) {
|
|
772
|
-
|
|
784
|
+
return varRef(local.id, local.type, loc);
|
|
773
785
|
}
|
|
774
786
|
return runtimeOptionalReceiverRead(lowerer, expr, local, key, loc);
|
|
775
787
|
}
|
|
@@ -1052,14 +1064,38 @@ function lowerExprInner(lowerer, expr) {
|
|
|
1052
1064
|
// type-mapper.ts pins the one concrete signature `(value: string) =>
|
|
1053
1065
|
// primitive`; direct calls `String(x)` never reach here — the call
|
|
1054
1066
|
// lowering intercepts them with the wider static coercions).
|
|
1055
|
-
const jsPrimitiveCtorSelector = isJsSourceFile(expr.getSourceFile())
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1067
|
+
const jsPrimitiveCtorSelector = isJsSourceFile(expr.getSourceFile());
|
|
1068
|
+
const primitiveName = stdlibGlobalNameOf(lowerer, expr);
|
|
1069
|
+
if (primitiveName === "String" || primitiveName === "Number" || primitiveName === "Boolean" || primitiveName === "BigInt") {
|
|
1070
|
+
return primitiveCtorClosure(lowerer, primitiveName, loc, jsPrimitiveCtorSelector);
|
|
1071
|
+
}
|
|
1072
|
+
if (lowerer.isStdlibGlobal(expr, "ArrayBuffer"))
|
|
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 };
|
|
1076
|
+
if (Object.values(BYTES_ELEMENT_NAME).includes(expr.text) && lowerer.isStdlibSymbol(lowerer.checker.getSymbolAtLocation(expr))) {
|
|
1077
|
+
return { kind: "libCall", fn: "bytes.constructor", args: [{ kind: "strLit", value: expr.text, type: STRING, loc }], type: DYN, loc };
|
|
1078
|
+
}
|
|
1079
|
+
if ((expr.text === "encodeURIComponent" || expr.text === "encodeURI" || expr.text === "decodeURIComponent" || expr.text === "decodeURI") && lowerer.isStdlibGlobal(expr, expr.text)) {
|
|
1080
|
+
const name = `%builtin.${expr.text}`;
|
|
1081
|
+
const type = funcOf([DYN], STRING);
|
|
1082
|
+
if (!lowerer.liftedFns.some((fn) => fn.name === name)) {
|
|
1083
|
+
const value = { kind: "libCall", fn: "dyn.toStringCoerce", args: [varRef("value", DYN, loc)], type: STRING, loc };
|
|
1084
|
+
const fn = expr.text === "encodeURIComponent" ? "str.encodeUriComponent" : expr.text === "encodeURI" ? "str.encodeUri" : expr.text === "decodeURI" ? "str.decodeUri" : "str.decodeUriComponent";
|
|
1085
|
+
lowerer.liftedFns.push({ name, params: [{ localId: "value", name: "value", type: DYN }],
|
|
1086
|
+
locals: [{ id: "value", name: "value", type: DYN, mutable: false }], returnType: STRING,
|
|
1087
|
+
body: [{ kind: "return", value: { kind: "libCall", fn, args: [value], type: STRING, loc }, loc }], loc });
|
|
1088
|
+
}
|
|
1089
|
+
return { kind: "closure", fnName: name, captures: [], type, loc };
|
|
1090
|
+
}
|
|
1091
|
+
if (!lowerer.dynamic && lowerer.isStdlibGlobal(expr, "crypto"))
|
|
1092
|
+
return { kind: "libCall", fn: "crypto.native", args: [], type: DYN, loc };
|
|
1093
|
+
const nativeAlias = stdlibGlobalNameOf(lowerer, expr);
|
|
1094
|
+
if (nativeAlias === "Buffer" && expr.text !== "Buffer") {
|
|
1095
|
+
return { kind: "strLit", value: "[builtin Buffer]", type: STRING, loc };
|
|
1096
|
+
}
|
|
1097
|
+
if (!lowerer.dynamic && lowerer.isStdlibGlobal(expr, "fetch")) {
|
|
1098
|
+
return { kind: "libCall", fn: "fetch.function", args: [], type: DYN, loc };
|
|
1063
1099
|
}
|
|
1064
1100
|
// The lib fence's IDENTIFIER chokepoint: the real standard library
|
|
1065
1101
|
// resolves names the old minimal ambient world never declared
|
|
@@ -1260,8 +1296,13 @@ function lowerExprInner(lowerer, expr) {
|
|
|
1260
1296
|
// `this` in plain nested functions is already a tsc error under
|
|
1261
1297
|
// noImplicitThis, so the generic scope walk here is preflight-safe.
|
|
1262
1298
|
const local = lowerer.resolveThis();
|
|
1263
|
-
if (local)
|
|
1264
|
-
|
|
1299
|
+
if (local) {
|
|
1300
|
+
const value = { kind: "varRef", localId: local.id, type: local.type, loc };
|
|
1301
|
+
const initialized = lowerer.ctx.superInitialized;
|
|
1302
|
+
if (initialized)
|
|
1303
|
+
return { kind: "seqExpr", stmts: [{ kind: "if", cond: { kind: "unary", op: "!", operand: { kind: "varRef", localId: initialized.id, type: BOOL, loc }, type: BOOL, loc }, then: [{ kind: "exprStmt", expr: nodeThrowExpr(5, "", "Must call super constructor in derived class before accessing 'this' or returning from derived constructor", DYN, loc), loc }], else_: null, loc }], result: value, type: value.type, loc };
|
|
1304
|
+
return value;
|
|
1305
|
+
}
|
|
1265
1306
|
// `this` in a plain JS FUNCTION (not a method): the AMBIENT
|
|
1266
1307
|
// RECEIVER (libCall dyn.this — scr_dyn_this_get). Firing sites bind
|
|
1267
1308
|
// the emitting handle around each listener call (Node calls
|
|
@@ -1456,6 +1497,12 @@ function lowerExprInner(lowerer, expr) {
|
|
|
1456
1497
|
const prototypeData = lowerClassPrototypeData(lowerer, expr);
|
|
1457
1498
|
if (prototypeData)
|
|
1458
1499
|
return prototypeData;
|
|
1500
|
+
if (ts.isPropertyAccessExpression(expr.expression) && expr.expression.name.text === "prototype") {
|
|
1501
|
+
const prototype = lowerClassPrototypeData(lowerer, expr.expression);
|
|
1502
|
+
if (prototype)
|
|
1503
|
+
return { kind: "dynKeyGet", value: prototype,
|
|
1504
|
+
key: { kind: "strLit", value: expr.name.text, type: STRING, loc }, type: DYN, loc };
|
|
1505
|
+
}
|
|
1459
1506
|
// `super.x`: the base chain's GETTER, called directly (super
|
|
1460
1507
|
// dispatch is static in JS — never through the dynamic class).
|
|
1461
1508
|
// super.method() calls are routed at the call site; a bare super
|
|
@@ -1501,6 +1548,14 @@ function lowerExprInner(lowerer, expr) {
|
|
|
1501
1548
|
["iterator", "asyncIterator", "toStringTag", "toPrimitive", "hasInstance", "isConcatSpreadable", "match", "matchAll", "replace", "search", "split", "species", "unscopables", "dispose", "asyncDispose"].includes(expr.name.text)) {
|
|
1502
1549
|
return { kind: "libCall", fn: "sym.wellKnown", args: [{ kind: "strLit", value: expr.name.text, type: STRING, loc }], type: { kind: "symbol" }, loc };
|
|
1503
1550
|
}
|
|
1551
|
+
if (!lowerer.dynamic && lowerer.stdlibGlobalMember(expr, "Object") === "freeze") {
|
|
1552
|
+
const name = "%builtin.Object.freeze";
|
|
1553
|
+
if (!lowerer.liftedFns.some((fn) => fn.name === name))
|
|
1554
|
+
lowerer.liftedFns.push({ name,
|
|
1555
|
+
params: [{ localId: "value", name: "value", type: DYN }], locals: [{ id: "value", name: "value", type: DYN, mutable: false }], returnType: DYN,
|
|
1556
|
+
body: [{ kind: "return", value: { kind: "libCall", fn: "dyn.freeze", args: [varRef("value", DYN, loc)], type: DYN, loc }, loc }], loc });
|
|
1557
|
+
return { kind: "closure", fnName: name, captures: [], type: funcOf([DYN], DYN), loc };
|
|
1558
|
+
}
|
|
1504
1559
|
if (lowerer.stdlibGlobalMember(expr, "globalThis") === "console") {
|
|
1505
1560
|
return { kind: "libCall", fn: "console.native", args: [], type: DYN, loc };
|
|
1506
1561
|
}
|
|
@@ -1586,15 +1641,6 @@ function lowerExprInner(lowerer, expr) {
|
|
|
1586
1641
|
}
|
|
1587
1642
|
return read;
|
|
1588
1643
|
}
|
|
1589
|
-
// `m.groups` on a match result whose regex is statically known:
|
|
1590
|
-
// the compile-time record projection over the honest slice (or
|
|
1591
|
-
// Node's undefined when the pattern has no named groups) — see
|
|
1592
|
-
// lowerMatchGroupsRead.
|
|
1593
|
-
{
|
|
1594
|
-
const g = lowerMatchGroupsRead(lowerer, expr);
|
|
1595
|
-
if (g !== null)
|
|
1596
|
-
return g;
|
|
1597
|
-
}
|
|
1598
1644
|
// `m.index` on a for-of-over-matchAll binding reads the companion-
|
|
1599
1645
|
// index array at this iteration's cursor — always a number: every
|
|
1600
1646
|
// drained row matched, so the lib's `.index` is never undefined
|
|
@@ -1612,6 +1658,25 @@ function lowerExprInner(lowerer, expr) {
|
|
|
1612
1658
|
};
|
|
1613
1659
|
}
|
|
1614
1660
|
}
|
|
1661
|
+
if ((expr.name.text === "index" || expr.name.text === "input") && !expr.questionDotToken) {
|
|
1662
|
+
const represented = tryLowerExpression(lowerer, expr.expression);
|
|
1663
|
+
if (represented && isMatchSliceType(lowerer, represented.type) &&
|
|
1664
|
+
(lowerer.isStdlibMember(expr) || isJsSourceFile(expr.getSourceFile()))) {
|
|
1665
|
+
const recv = represented;
|
|
1666
|
+
const read = { kind: "dynKeyGet", value: lowerer.coerceToExpected(recv, DYN),
|
|
1667
|
+
key: { kind: "strLit", value: expr.name.text, type: STRING, loc }, type: DYN, loc };
|
|
1668
|
+
return lowerer.maybeNarrow(read, expr);
|
|
1669
|
+
}
|
|
1670
|
+
}
|
|
1671
|
+
// `m.groups` on a match result whose regex is statically known:
|
|
1672
|
+
// the compile-time record projection over the honest slice (or
|
|
1673
|
+
// Node's undefined when the pattern has no named groups) — see
|
|
1674
|
+
// lowerMatchGroupsRead.
|
|
1675
|
+
{
|
|
1676
|
+
const g = lowerMatchGroupsRead(lowerer, expr);
|
|
1677
|
+
if (g !== null)
|
|
1678
|
+
return g;
|
|
1679
|
+
}
|
|
1615
1680
|
// `arguments.length` in a TYPED function whose signature is
|
|
1616
1681
|
// FIXED-ARITY (no optional/default/rest parameters): tsc enforces
|
|
1617
1682
|
// exact arity at every call site and call/apply/bind indirection is
|
|
@@ -1833,6 +1898,12 @@ function lowerExprInner(lowerer, expr) {
|
|
|
1833
1898
|
if (canonical !== null) {
|
|
1834
1899
|
if (!lowerer.dynamic && canonical === "globalThis")
|
|
1835
1900
|
return lowerGlobalValue(lowerer, expr);
|
|
1901
|
+
if (!lowerer.dynamic && canonical === "ArrayBuffer")
|
|
1902
|
+
return { kind: "libCall", fn: "arrayBuffer.constructor", args: [], type: DYN, loc };
|
|
1903
|
+
if (!lowerer.dynamic && canonical === "fetch")
|
|
1904
|
+
return { kind: "libCall", fn: "fetch.function", args: [], type: DYN, loc };
|
|
1905
|
+
if (!lowerer.dynamic && Object.values(BYTES_ELEMENT_NAME).includes(canonical))
|
|
1906
|
+
return { kind: "libCall", fn: "bytes.constructor", args: [{ kind: "strLit", value: canonical, type: STRING, loc }], type: DYN, loc };
|
|
1836
1907
|
return { kind: "strLit", value: `[builtin ${canonical}]`, type: STRING, loc };
|
|
1837
1908
|
}
|
|
1838
1909
|
}
|
|
@@ -1876,6 +1947,13 @@ function lowerExprInner(lowerer, expr) {
|
|
|
1876
1947
|
: { kind: "strIntrinsic", method: "length", receiver: recv, args: [], type: F64, loc };
|
|
1877
1948
|
}
|
|
1878
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
|
+
}
|
|
1879
1957
|
// `f.name` / `f.length` / own properties on a FUNCTION-typed JS
|
|
1880
1958
|
// value (the mustCall wrapper's function-instance members): read
|
|
1881
1959
|
// through the dyn box — the closure's own-property table answers
|
|
@@ -1921,10 +1999,14 @@ function lowerExprInner(lowerer, expr) {
|
|
|
1921
1999
|
// is an ordinary recordGet — maybeNarrow then bridges the union slot
|
|
1922
2000
|
// to the checker's narrowed arm, exactly like a union field read.
|
|
1923
2001
|
if (expr.name.text === "done" || expr.name.text === "value") {
|
|
1924
|
-
const
|
|
1925
|
-
|
|
1926
|
-
|
|
2002
|
+
const receiverType = lowerer.typeOf(expr.expression);
|
|
2003
|
+
const recvSym = receiverType.getAliasSymbol()?.name === "IteratorResult" ? receiverType.getAliasSymbol() : receiverType.getSymbol();
|
|
2004
|
+
if ((recvSym?.name === "IteratorResult" || recvSym?.name === "IteratorYieldResult" || recvSym?.name === "IteratorReturnResult") &&
|
|
2005
|
+
lowerer.checker.declarationsOf(recvSym).some((d) => (ts.isInterfaceDeclaration(d) || ts.isTypeAliasDeclaration(d)) && lowerer.isStdlibFile(d.getSourceFile()))) {
|
|
1927
2006
|
const recv = lowerer.lowerExpr(expr.expression);
|
|
2007
|
+
if (recv.type.kind === "dyn")
|
|
2008
|
+
return lowerer.maybeNarrow({ kind: "dynKeyGet", value: recv,
|
|
2009
|
+
key: { kind: "strLit", value: expr.name.text, type: STRING, loc }, type: DYN, loc }, expr);
|
|
1928
2010
|
if (recv.type.kind === "record") {
|
|
1929
2011
|
const shape = lowerer.shapes.get(recv.type.shapeId);
|
|
1930
2012
|
const field = shape?.fields.find((f) => f.name === expr.name.text);
|
|
@@ -2021,10 +2103,12 @@ function lowerExprInner(lowerer, expr) {
|
|
|
2021
2103
|
// on undefined/null receivers unless an earlier `?.` guards the
|
|
2022
2104
|
// chain. Scalar-narrowed occurrences bridge via maybeNarrow's
|
|
2023
2105
|
// validated dynCheck as usual.
|
|
2024
|
-
if (recvLowered.type.kind === "dyn")
|
|
2106
|
+
if (recvLowered.type.kind === "dyn" || isJsSourceFile(expr.getSourceFile()) &&
|
|
2107
|
+
(["f64", "bool", "bigint", "string", "symbol"].includes(recvLowered.type.kind) ||
|
|
2108
|
+
recvLowered.type.kind === "union" && lowerer.dynConvertible(recvLowered.type))) {
|
|
2025
2109
|
const key = { kind: "strLit", value: expr.name.text, type: STRING, loc: locOf(expr.name) };
|
|
2026
2110
|
const opt = hasOptionalChainGuard(expr.expression);
|
|
2027
|
-
return lowerer.maybeNarrow({ kind: "dynKeyGet", key, ...(opt ? { optional: true } : {}), value: recvLowered, type: DYN, loc }, expr);
|
|
2111
|
+
return lowerer.maybeNarrow({ kind: "dynKeyGet", key, ...(opt ? { optional: true } : {}), value: lowerer.coerceToExpected(recvLowered, DYN), type: DYN, loc }, expr);
|
|
2028
2112
|
}
|
|
2029
2113
|
// The lowered receiver is a RECORD the checker spelled wider —
|
|
2030
2114
|
// `s.match(re).groups.key` in a JS file: the checker says
|
|
@@ -2074,6 +2158,16 @@ function lowerExprInner(lowerer, expr) {
|
|
|
2074
2158
|
// reports a class. Its property bag must remain reachable, while
|
|
2075
2159
|
// an absent element takes the normal checked-value TypeError.
|
|
2076
2160
|
const nativeReceiver = lowerer.stripUndefinedArm(recvLowered.type);
|
|
2161
|
+
if (ts.isPrivateIdentifier(expr.name) && recvLowered.type.kind === "object") {
|
|
2162
|
+
const field = lowerer.classes.get(recvLowered.type.className)?.fields.get(expr.name.text);
|
|
2163
|
+
if (field)
|
|
2164
|
+
return { kind: "fieldGet", obj: recvLowered, className: recvLowered.type.className,
|
|
2165
|
+
field: expr.name.text, type: field, loc };
|
|
2166
|
+
}
|
|
2167
|
+
if (nativeReceiver.kind === "object" && nativeReceiver.className === "%Error") {
|
|
2168
|
+
return lowerer.maybeNarrow({ kind: "dynKeyGet", value: lowerer.coerceToExpected(recvLowered, DYN),
|
|
2169
|
+
key: { kind: "strLit", value: expr.name.text, type: STRING, loc }, type: DYN, loc }, expr);
|
|
2170
|
+
}
|
|
2077
2171
|
if (isDynTypedRefType(nativeReceiver)) {
|
|
2078
2172
|
const info = lowerer.classes.get(nativeReceiver.className);
|
|
2079
2173
|
if (info && !info.def.runtime && !info.builtinError && !info.builtinEmitter && !info.builtinStream) {
|
|
@@ -2107,7 +2201,7 @@ function moduleFileName(lowerer, sf) {
|
|
|
2107
2201
|
: sf.fileName;
|
|
2108
2202
|
}
|
|
2109
2203
|
function importMetaProperty(lowerer, expr) {
|
|
2110
|
-
if (
|
|
2204
|
+
if (!ts.isMetaProperty(expr.expression))
|
|
2111
2205
|
return null;
|
|
2112
2206
|
const meta = expr.expression;
|
|
2113
2207
|
if (meta.keywordToken !== ts.SyntaxKind.ImportKeyword || meta.name.text !== "meta")
|
|
@@ -2115,6 +2209,8 @@ function importMetaProperty(lowerer, expr) {
|
|
|
2115
2209
|
const sf = expr.getSourceFile();
|
|
2116
2210
|
const fileName = moduleFileName(lowerer, sf);
|
|
2117
2211
|
switch (expr.name.text) {
|
|
2212
|
+
case "env":
|
|
2213
|
+
return dynUndefinedExpr(locOf(expr));
|
|
2118
2214
|
case "url":
|
|
2119
2215
|
return {
|
|
2120
2216
|
kind: "strLit",
|
|
@@ -2398,7 +2494,13 @@ export function lowerTernary(lowerer, expr, expected) {
|
|
|
2398
2494
|
thenRaw.type.kind === "func" && elseRaw.type.kind === "func" &&
|
|
2399
2495
|
!typeEquals(thenRaw.type, elseRaw.type) &&
|
|
2400
2496
|
lowerer.dynConvertible(thenRaw.type) && lowerer.dynConvertible(elseRaw.type);
|
|
2401
|
-
|
|
2497
|
+
// JavaScript inference can discard an initially-undefined field from
|
|
2498
|
+
// a conditional's type even after that field acquired other values.
|
|
2499
|
+
// A checked arm must retain its actual representation at the join.
|
|
2500
|
+
const checkedJsJoin = isJsSourceFile(expr.getSourceFile()) &&
|
|
2501
|
+
(thenRaw.type.kind === "dyn" || elseRaw.type.kind === "dyn") &&
|
|
2502
|
+
lowerer.dynConvertible(thenRaw.type) && lowerer.dynConvertible(elseRaw.type);
|
|
2503
|
+
const type = callableJoin || checkedJsJoin ? DYN :
|
|
2402
2504
|
thenRaw.type.kind === "array" && typeEquals(thenRaw.type, elseRaw.type)
|
|
2403
2505
|
? thenRaw.type
|
|
2404
2506
|
: dynJoin
|
|
@@ -2452,8 +2554,57 @@ export function maybeNarrow(lowerer, expr, node) {
|
|
|
2452
2554
|
// instead of misreading the payload (the dyn boundary's usual stance).
|
|
2453
2555
|
// Object/array narrowings stay dyn-typed and keep their fences.
|
|
2454
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
|
+
}
|
|
2455
2596
|
if (isJsSourceFile(node.getSourceFile()) && ts.isIdentifier(node)) {
|
|
2456
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;
|
|
2603
|
+
if (symbol && lowerer.checker.declarationsOf(symbol).some((decl) => ts.isVariableDeclaration(decl) && jsBindingHasOpenWrites(lowerer, decl)))
|
|
2604
|
+
return expr;
|
|
2605
|
+
if (symbol && lowerer.checker.declarationsOf(symbol).some((decl) => ts.isParameter(decl) && decl.initializer && !decl.type &&
|
|
2606
|
+
!/@(?:param|type)\b/.test(decl.getSourceFile().text.slice(decl.parent?.pos ?? decl.pos, decl.getStart()))))
|
|
2607
|
+
return expr;
|
|
2457
2608
|
if (symbol && lowerer.checker.declarationsOf(symbol).some((decl) => {
|
|
2458
2609
|
if (!ts.isParameter(decl) || decl.type)
|
|
2459
2610
|
return false;
|
|
@@ -2512,6 +2663,19 @@ export function maybeNarrow(lowerer, expr, node) {
|
|
|
2512
2663
|
}
|
|
2513
2664
|
if (expr.type.kind !== "union")
|
|
2514
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
|
+
}
|
|
2515
2679
|
if (ts.isIdentifier(node)) {
|
|
2516
2680
|
const symbol = lowerer.resolveValueSymbol(node);
|
|
2517
2681
|
if (symbol && lowerer.unstableSwitchBindings.has(symbol))
|
|
@@ -3003,6 +3167,10 @@ function lowerNullishPair(lowerer, expr, loc, left) {
|
|
|
3003
3167
|
if (fresh)
|
|
3004
3168
|
return { kind: "nullish", left, right: fresh, type: fresh.type, loc };
|
|
3005
3169
|
const type = lowerer.irTypeOf(expr);
|
|
3170
|
+
if (type.kind === "dyn" && lowerer.dynConvertible(left.type)) {
|
|
3171
|
+
return { kind: "nullish", left: lowerer.coerceToExpected(left, DYN),
|
|
3172
|
+
right: lowerer.lowerExprExpecting(expr.right, DYN), type: DYN, loc };
|
|
3173
|
+
}
|
|
3006
3174
|
if (typeEquals(type, left.type) || (rest.length === 1 && typeEquals(type, rest[0]))) {
|
|
3007
3175
|
const right = lowerer.lowerExprExpecting(expr.right, type);
|
|
3008
3176
|
return { kind: "nullish", left, right, type, loc };
|
|
@@ -3073,6 +3241,8 @@ export function lowerCondition(lowerer, expr) {
|
|
|
3073
3241
|
if (op === ts.SyntaxKind.AmpersandAmpersandToken || op === ts.SyntaxKind.BarBarToken) {
|
|
3074
3242
|
const isAnd = op === ts.SyntaxKind.AmpersandAmpersandToken;
|
|
3075
3243
|
const left = lowerer.lowerCondition(e.left);
|
|
3244
|
+
if (left.kind === "boolLit" && left.value !== isAnd)
|
|
3245
|
+
return left;
|
|
3076
3246
|
// The right operand evaluates only when the left already answered
|
|
3077
3247
|
// (true for &&, false for ||) — aliased-typeof narrows the left
|
|
3078
3248
|
// PROVES under that polarity hold while it lowers (`type ===
|
|
@@ -3089,6 +3259,8 @@ export function lowerCondition(lowerer, expr) {
|
|
|
3089
3259
|
lowerer.runtimeOptionalLocals.add(lowerer.runtimeOptionalRootOf(runtimeOptionalLocal));
|
|
3090
3260
|
}
|
|
3091
3261
|
});
|
|
3262
|
+
if (left.kind === "boolLit")
|
|
3263
|
+
return right;
|
|
3092
3264
|
return {
|
|
3093
3265
|
kind: "logical",
|
|
3094
3266
|
op: isAnd ? "&&" : "||",
|
|
@@ -3366,13 +3538,56 @@ export function eqComparableUnion(lowerer, unionId) {
|
|
|
3366
3538
|
* the receiver type AND the ambient-file provenance of the member are
|
|
3367
3539
|
* verified — the name alone proves nothing. */
|
|
3368
3540
|
export function lowerIntrinsicProperty(lowerer, expr) {
|
|
3541
|
+
for (const name of ["String", "Number", "Boolean", "BigInt"]) {
|
|
3542
|
+
if (!lowerer.chainBlocked(expr) && lowerer.isStdlibGlobal(expr, name)) {
|
|
3543
|
+
return primitiveCtorClosure(lowerer, name, locOf(expr), isJsSourceFile(expr.getSourceFile()));
|
|
3544
|
+
}
|
|
3545
|
+
}
|
|
3546
|
+
if (lowerer.stdlibGlobalMember(expr, "Reflect") === "ownKeys") {
|
|
3547
|
+
const value = lowerer.lowerNativeCallableValue({
|
|
3548
|
+
fn: "dyn.ownKeys", params: [DYN], result: DYN, valueParams: [{ mode: "required", type: DYN }],
|
|
3549
|
+
}, "Reflect.ownKeys", locOf(expr));
|
|
3550
|
+
return { kind: "dynFrom", value, fnName: "ownKeys", type: DYN, loc: locOf(expr) };
|
|
3551
|
+
}
|
|
3369
3552
|
if (lowerer.chainBlocked(expr))
|
|
3370
3553
|
return null;
|
|
3554
|
+
const builtinPrototype = builtinPrototypeMethod(lowerer, expr);
|
|
3555
|
+
if (builtinPrototype)
|
|
3556
|
+
return { kind: "libCall", fn: "dyn.builtinMethod", args: [
|
|
3557
|
+
{ kind: "strLit", value: builtinPrototype, type: STRING, loc: locOf(expr) },
|
|
3558
|
+
{ kind: "strLit", value: expr.name.text, type: STRING, loc: locOf(expr.name) },
|
|
3559
|
+
], type: DYN, loc: locOf(expr) };
|
|
3560
|
+
if (ts.isPropertyAccessExpression(expr.expression) &&
|
|
3561
|
+
lowerer.stdlibGlobalMember(expr.expression, "Array") === "prototype") {
|
|
3562
|
+
return { kind: "dynKeyGet", value: { kind: "libCall", fn: "dyn.arrayPrototype", args: [], type: DYN, loc: locOf(expr.expression) },
|
|
3563
|
+
key: { kind: "strLit", value: expr.name.text, type: STRING, loc: locOf(expr.name) }, type: DYN, loc: locOf(expr) };
|
|
3564
|
+
}
|
|
3565
|
+
if (expr.name.text === "apply" && ts.isPropertyAccessExpression(expr.expression) &&
|
|
3566
|
+
lowerer.stdlibGlobalMember(expr.expression, "Function") === "prototype")
|
|
3567
|
+
return { kind: "libCall", fn: "dyn.functionApply", args: [], type: DYN, loc: locOf(expr) };
|
|
3371
3568
|
if (lowerer.stdlibGlobalMember(expr, "Array") === "prototype")
|
|
3372
3569
|
return { kind: "libCall", fn: "dyn.arrayPrototype", args: [], type: DYN, loc: locOf(expr) };
|
|
3570
|
+
if (lowerer.stdlibGlobalMember(expr, "Object") === "prototype")
|
|
3571
|
+
return { kind: "libCall", fn: "dyn.objectPrototype", args: [], type: DYN, loc: locOf(expr) };
|
|
3373
3572
|
if (lowerer.stdlibGlobalMember(expr, "Array") === "isArray")
|
|
3374
3573
|
return lowerArrayIsArrayValue(lowerer, locOf(expr));
|
|
3574
|
+
if (lowerer.stdlibGlobalMember(expr, "Array") === "from")
|
|
3575
|
+
return lowerArrayFromValue(lowerer, locOf(expr));
|
|
3576
|
+
const stringMember = lowerer.stdlibGlobalMember(expr, "String");
|
|
3577
|
+
if (stringMember === "fromCharCode" || stringMember === "fromCodePoint")
|
|
3578
|
+
return lowerStringCodesValue(lowerer, stringMember, locOf(expr));
|
|
3579
|
+
const numberMember = lowerer.stdlibGlobalMember(expr, "Number");
|
|
3580
|
+
if (numberMember === "parseInt" || numberMember === "parseFloat")
|
|
3581
|
+
return lowerNumberParserValue(lowerer, numberMember, locOf(expr));
|
|
3582
|
+
if (lowerer.stdlibGlobalMember(expr, "Buffer") === "isBuffer")
|
|
3583
|
+
return lowerCheckedPredicateValue(lowerer, "Buffer.isBuffer", "buffer", locOf(expr));
|
|
3584
|
+
if (lowerer.stdlibGlobalMember(expr, "Date") === "now")
|
|
3585
|
+
return lowerer.lowerNativeCallableValue({
|
|
3586
|
+
fn: "date.now", params: [], result: F64, valueParams: [],
|
|
3587
|
+
}, "Date.now", locOf(expr));
|
|
3375
3588
|
if (stdlibGlobalNameOf(lowerer, expr.expression) === "Object") {
|
|
3589
|
+
if (expr.name.text === "assign")
|
|
3590
|
+
return lowerObjectAssignValue(lowerer, locOf(expr));
|
|
3376
3591
|
const fn = own(OBJECT_CALLABLE_VALUES, expr.name.text);
|
|
3377
3592
|
if (fn)
|
|
3378
3593
|
return lowerer.lowerNativeCallableValue(fn, `Object.${expr.name.text}`, locOf(expr));
|
|
@@ -3410,10 +3625,21 @@ export function lowerIntrinsicProperty(lowerer, expr) {
|
|
|
3410
3625
|
if (kind === "child" ? !isChildSurfaceMember(lowerer, expr) : !tupleLengthOnArray && !lowerer.isStdlibMember(expr))
|
|
3411
3626
|
return null;
|
|
3412
3627
|
const name = expr.name.text;
|
|
3628
|
+
if (kind === "array" && name === "constructor" && isJsSourceFile(expr.getSourceFile())) {
|
|
3629
|
+
const receiver = lowerer.lowerExpr(expr.expression);
|
|
3630
|
+
if (receiver.type.kind === "dyn")
|
|
3631
|
+
return { kind: "dynKeyGet", value: receiver,
|
|
3632
|
+
key: { kind: "strLit", value: name, type: STRING, loc: locOf(expr) }, type: DYN, 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) };
|
|
3635
|
+
}
|
|
3413
3636
|
const strictReceiver = (expected) => {
|
|
3414
3637
|
const value = lowerer.lowerExpr(expr.expression);
|
|
3415
|
-
if (value.type.kind === "dyn")
|
|
3638
|
+
if (value.type.kind === "dyn") {
|
|
3639
|
+
if (isJsSourceFile(expr.getSourceFile()) && name === "length")
|
|
3640
|
+
return value;
|
|
3416
3641
|
return lowerer.coerceInto(expr.expression, value, expected);
|
|
3642
|
+
}
|
|
3417
3643
|
return lowerer.runtimeOptionalPropertyReceiver(expr.expression, value, expected, name) ?? value;
|
|
3418
3644
|
};
|
|
3419
3645
|
if (kind === "child") {
|
|
@@ -3517,7 +3743,7 @@ export function lowerIntrinsicProperty(lowerer, expr) {
|
|
|
3517
3743
|
// (the child.stdout pattern); a checker-narrowed read extracts
|
|
3518
3744
|
// through maybeNarrow like any union member.
|
|
3519
3745
|
if (name === "description") {
|
|
3520
|
-
const receiver = lowerer.lowerExpr(expr.expression);
|
|
3746
|
+
const receiver = lowerer.coerceToExpected(lowerer.lowerExpr(expr.expression), SYMBOL_T);
|
|
3521
3747
|
const type = { kind: "union", unionId: lowerer.unions.intern([STRING, UNDEFINED_T]) };
|
|
3522
3748
|
return { kind: "libCall", fn: "sym.desc", args: [receiver], type, loc };
|
|
3523
3749
|
}
|
|
@@ -3585,6 +3811,9 @@ export function lowerIntrinsicProperty(lowerer, expr) {
|
|
|
3585
3811
|
lowerer.noLowering(`URLSearchParams.${name}`, expr, "get, getAll, set, append, delete, has, sort, size, toString(), forEach, and for-of iteration are the supported URLSearchParams members", lowerer.checker.getSymbolAtLocation(expr.name));
|
|
3586
3812
|
}
|
|
3587
3813
|
if (kind === "regex") {
|
|
3814
|
+
if (name === "lastIndex") {
|
|
3815
|
+
return { kind: "regexIntrinsic", method: "lastIndex", receiver: strictReceiver({ kind: "regex" }), args: [], type: F64, loc: locOf(expr) };
|
|
3816
|
+
}
|
|
3588
3817
|
if (name === "source" || name === "flags") {
|
|
3589
3818
|
const receiver = strictReceiver({ kind: "regex" });
|
|
3590
3819
|
return { kind: "regexIntrinsic", method: name, receiver, args: [], type: STRING, loc: locOf(expr) };
|
|
@@ -3604,11 +3833,20 @@ export function lowerIntrinsicProperty(lowerer, expr) {
|
|
|
3604
3833
|
}
|
|
3605
3834
|
if (kind === "bytes") {
|
|
3606
3835
|
const loc = locOf(expr);
|
|
3836
|
+
if (name === "constructor") {
|
|
3837
|
+
const expected = lowerer.mapTypeOf(lowerer.typeOf(expr.expression));
|
|
3838
|
+
const receiver = expected?.kind === "bytes" ? strictReceiver(expected) : lowerer.lowerExpr(expr.expression);
|
|
3839
|
+
return { kind: "dynKeyGet", value: lowerer.coerceInto(expr.expression, receiver, DYN),
|
|
3840
|
+
key: { kind: "strLit", value: name, type: STRING, loc }, type: DYN, loc };
|
|
3841
|
+
}
|
|
3607
3842
|
if (name === "length" || name === "byteLength") {
|
|
3608
3843
|
const expected = lowerer.mapTypeOf(lowerer.typeOf(expr.expression));
|
|
3609
3844
|
let receiver = expected?.kind === "bytes"
|
|
3610
3845
|
? strictReceiver(expected)
|
|
3611
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) };
|
|
3612
3850
|
if (receiver.type.kind === "union" && lowerer.armTag(receiver.type.unionId, UNDEFINED_T) >= 0) {
|
|
3613
3851
|
const present = lowerer.stripUndefinedArm(receiver.type);
|
|
3614
3852
|
const helper = present.kind === "bytes"
|
|
@@ -3674,9 +3912,10 @@ export function lowerIntrinsicProperty(lowerer, expr) {
|
|
|
3674
3912
|
const expected = kind === "string"
|
|
3675
3913
|
? STRING
|
|
3676
3914
|
: lowerer.mapTypeOf(lowerer.typeOf(expr.expression));
|
|
3677
|
-
let receiver =
|
|
3678
|
-
|
|
3679
|
-
|
|
3915
|
+
let receiver = lowerer.lowerExpr(expr.expression);
|
|
3916
|
+
if (receiver.type.kind !== "dyn" && expected && (expected.kind === "string" || expected.kind === "array")) {
|
|
3917
|
+
receiver = lowerer.runtimeOptionalPropertyReceiver(expr.expression, receiver, expected, name) ?? receiver;
|
|
3918
|
+
}
|
|
3680
3919
|
if (receiver.type.kind === "union" && lowerer.armTag(receiver.type.unionId, UNDEFINED_T) >= 0 && kind === "array") {
|
|
3681
3920
|
const present = lowerer.stripUndefinedArm(receiver.type);
|
|
3682
3921
|
const helper = present.kind === "array"
|
|
@@ -3742,6 +3981,19 @@ export function lowerArrayLiteral(lowerer, expr, expected) {
|
|
|
3742
3981
|
// destination can use the ordinary tuple-to-array coercion afterward.
|
|
3743
3982
|
const tsType = underConstAssertion(expr) ? lowerer.typeOf(expr) : ctxType ?? lowerer.typeOf(expr);
|
|
3744
3983
|
let mapped = expected ?? lowerer.mapTypeOf(tsType);
|
|
3984
|
+
// Inferred JavaScript arrays can contain unrelated constructors with
|
|
3985
|
+
// structurally equal checker types. Their native class ABIs still differ.
|
|
3986
|
+
if (!expected && isJsSourceFile(expr.getSourceFile()) && mapped?.kind === "array" && mapped.elem.kind === "classval")
|
|
3987
|
+
mapped = DYN;
|
|
3988
|
+
// An inferred destructuring pattern supplies a contextual [any, ...]
|
|
3989
|
+
// tuple. Its positions do not erase the literal's inferred element
|
|
3990
|
+
// types; explicit destinations already arrive through expected.
|
|
3991
|
+
if (expected === undefined && ctxType && lowerer.checker.isTupleType(ctxType) &&
|
|
3992
|
+
lowerer.checker.getTypeArguments(ctxType).some((type) => (type.flags & ts.TypeFlags.Any) !== 0)) {
|
|
3993
|
+
const inferred = lowerer.mapTypeOf(lowerer.typeOf(expr));
|
|
3994
|
+
if (inferred?.kind === "array" || inferred?.kind === "record" && lowerer.shapes.get(inferred.shapeId)?.tuple)
|
|
3995
|
+
mapped = inferred;
|
|
3996
|
+
}
|
|
3745
3997
|
// A JS literal whose OWN inferred type is never-tainted
|
|
3746
3998
|
// (neverTaintedJsType — the evolving `const gb = []`, the mixed
|
|
3747
3999
|
// command tuple `['pwd', []]`) carries no element information: route
|
|
@@ -3838,6 +4090,14 @@ export function lowerArrayLiteral(lowerer, expr, expected) {
|
|
|
3838
4090
|
mapped = arrayOf(first.type);
|
|
3839
4091
|
}
|
|
3840
4092
|
}
|
|
4093
|
+
if (!expected && isJsSourceFile(expr.getSourceFile()) && mapped?.kind === "array") {
|
|
4094
|
+
const elem = mapped.elem;
|
|
4095
|
+
const arms = elem.kind === "union" ? lowerer.unions.get(elem.unionId)?.arms : [elem];
|
|
4096
|
+
if (arms?.length && arms.every((arm) => arm.kind === "classval"))
|
|
4097
|
+
mapped = DYN;
|
|
4098
|
+
}
|
|
4099
|
+
if (!expected && isJsSourceFile(expr.getSourceFile()) && expr.elements.some((element) => ts.isSpreadElement(element) && tryLowerExpression(lowerer, element.expression)?.type.kind === "dyn"))
|
|
4100
|
+
mapped = DYN;
|
|
3841
4101
|
// An EMPTY literal under a CONST ASSERTION whose type queries panicked
|
|
3842
4102
|
// (tsgo's readonly-[] TupleType conversion — the facade answers `any`,
|
|
3843
4103
|
// which maps to nothing statically and to an island value under
|
|
@@ -3849,6 +4109,11 @@ export function lowerArrayLiteral(lowerer, expr, expected) {
|
|
|
3849
4109
|
underConstAssertion(expr)) {
|
|
3850
4110
|
mapped = arrayOf(unitOnlyUnion(lowerer.unions));
|
|
3851
4111
|
}
|
|
4112
|
+
// Heterogeneous JavaScript object arrays must retain each literal's
|
|
4113
|
+
// real keys. A structural union can otherwise drop descriptor fields.
|
|
4114
|
+
if (!expected && isJsSourceFile(expr.getSourceFile()) && mapped?.kind === "array" && mapped.elem.kind === "union" &&
|
|
4115
|
+
expr.elements.some(ts.isObjectLiteralExpression))
|
|
4116
|
+
mapped = DYN;
|
|
3852
4117
|
// A TUPLE-typed slot (`const t: [string, number] = ["a", 1]`): the
|
|
3853
4118
|
// literal constructs the tuple's record shape — one positional field
|
|
3854
4119
|
// per element, source order (which IS index order, so evaluation order
|
|
@@ -3904,7 +4169,7 @@ export function lowerArrayLiteral(lowerer, expr, expected) {
|
|
|
3904
4169
|
// `unknown[]` annotation or a collapsed `(string | object)[]` maps
|
|
3905
4170
|
// to DYN wholesale now (mapType's dyn-element array rule), so the
|
|
3906
4171
|
// literal IS the dyn array.
|
|
3907
|
-
if (isJsSourceFile(expr.getSourceFile()) || mapped?.kind === "dyn") {
|
|
4172
|
+
if (isJsSourceFile(expr.getSourceFile()) || mapped?.kind === "dyn" || lowerer.checkerAnyArray(expr)) {
|
|
3908
4173
|
if (expr.elements.some(ts.isSpreadElement)) {
|
|
3909
4174
|
// Native collection drains return vectors of retained dyn
|
|
3910
4175
|
// elements. Reuse the ordinary spread builder, then expose the
|
|
@@ -3998,11 +4263,7 @@ export function lowerArrayLiteral(lowerer, expr, expected) {
|
|
|
3998
4263
|
// the checker inferred a typed array from its producer. Drain once
|
|
3999
4264
|
// and validate the elements before the ordinary spread copy.
|
|
4000
4265
|
if (src.type.kind === "dyn" && (type.elem.kind === "dyn" || type.elem.kind === "f64" || type.elem.kind === "string" || type.elem.kind === "bool")) {
|
|
4001
|
-
src = lowerer.coerceInto(el.expression,
|
|
4002
|
-
kind: "libCall", fn: "dyn.iterPack", args: [src,
|
|
4003
|
-
{ kind: "strLit", value: el.expression.getText(), type: STRING, loc: locOf(el) }],
|
|
4004
|
-
type: DYN, loc: locOf(el),
|
|
4005
|
-
}, type);
|
|
4266
|
+
src = lowerer.coerceInto(el.expression, checkedIteratorPack(lowerer, src, locOf(el)), type);
|
|
4006
4267
|
}
|
|
4007
4268
|
// A same-family array whose ELEMENT lifts (string[] into a
|
|
4008
4269
|
// (string | symbol)[] literal — per-element wrap/width copy):
|
|
@@ -4255,6 +4516,9 @@ export function lowerElementAccess(lowerer, expr) {
|
|
|
4255
4516
|
const cachedModule = lowerRequireCacheElement(lowerer, expr);
|
|
4256
4517
|
if (cachedModule)
|
|
4257
4518
|
return cachedModule;
|
|
4519
|
+
const namespaceElement = lowerModuleNamespaceElement(lowerer, expr);
|
|
4520
|
+
if (namespaceElement)
|
|
4521
|
+
return namespaceElement;
|
|
4258
4522
|
// Native Web handles use the same supported surface for bracket and dot
|
|
4259
4523
|
// spellings. Fence a statically-known unsupported key before the generic
|
|
4260
4524
|
// checked-dynamic element-read path can turn it into a runtime missing
|
|
@@ -4320,7 +4584,11 @@ export function lowerElementAccess(lowerer, expr) {
|
|
|
4320
4584
|
if (target)
|
|
4321
4585
|
return lowerer.maybeNarrow(lowerer.fieldGetExpr(target, locOf(expr), expr), expr);
|
|
4322
4586
|
let receiver = lowerer.lowerExpr(expr.expression);
|
|
4323
|
-
|
|
4587
|
+
const classInfo = receiver.type.kind === "classval" ? lowerer.classes.get(receiver.type.className) : undefined;
|
|
4588
|
+
if (receiver.type.kind === "func" || receiver.type.kind === "generator" ||
|
|
4589
|
+
((receiver.type.kind === "array" || receiver.type.kind === "string" || receiver.type.kind === "bytes") && lowerer.dynConvertible(receiver.type)) ||
|
|
4590
|
+
(receiver.type.kind === "object" && lowerer.dynConvertible(receiver.type)) || isDynTypedRefType(receiver.type) ||
|
|
4591
|
+
(classInfo && (classInfo.callableBase || hasRuntimeStatics(classInfo))) || ((receiver.type.kind === "map" || receiver.type.kind === "set") && lowerer.dynConvertible(receiver.type))) {
|
|
4324
4592
|
const fnName = jsFuncNameOf(expr.expression);
|
|
4325
4593
|
receiver = { kind: "dynFrom", value: receiver, type: DYN, loc: locOf(expr),
|
|
4326
4594
|
...(fnName !== null ? { fnName } : {}) };
|
|
@@ -4340,13 +4608,26 @@ export function lowerElementAccess(lowerer, expr) {
|
|
|
4340
4608
|
: lowerer.mapTypeOf(lowerer.typeOf(expr.expression));
|
|
4341
4609
|
if (isJsSourceFile(expr.getSourceFile())) {
|
|
4342
4610
|
const receiver = tryLowerExpression(lowerer, expr.expression);
|
|
4611
|
+
if (receiver?.type.kind === "dyn") {
|
|
4612
|
+
const key = lowerer.lowerExprExpecting(expr.argumentExpression, DYN);
|
|
4613
|
+
const optional = hasOptionalChainGuard(expr.expression);
|
|
4614
|
+
return lowerer.maybeNarrow({ kind: "dynKeyGet", value: receiver, key,
|
|
4615
|
+
...(optional ? { optional: true } : {}), type: DYN, loc: locOf(expr) }, expr);
|
|
4616
|
+
}
|
|
4343
4617
|
if (receiver?.type.kind === "func" && lowerer.dynConvertible(receiver.type)) {
|
|
4344
4618
|
const loc = locOf(expr);
|
|
4345
4619
|
const fnName = jsFuncNameOf(expr.expression);
|
|
4346
4620
|
const boxed = { kind: "dynFrom", value: receiver, type: DYN, loc, ...(fnName !== null ? { fnName } : {}) };
|
|
4347
|
-
const key =
|
|
4621
|
+
const key = lowerer.lowerExprExpecting(expr.argumentExpression, DYN);
|
|
4348
4622
|
return lowerer.maybeNarrow({ kind: "dynKeyGet", key, value: boxed, type: DYN, loc }, expr);
|
|
4349
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
|
+
}
|
|
4350
4631
|
}
|
|
4351
4632
|
// A readonly tuple union under Array.isArray is checker-typed as an
|
|
4352
4633
|
// intersection with any[], whose structural mapping is not the tuple
|
|
@@ -4363,7 +4644,7 @@ export function lowerElementAccess(lowerer, expr) {
|
|
|
4363
4644
|
if (target)
|
|
4364
4645
|
return lowerer.maybeNarrow(lowerer.fieldGetExpr(target, locOf(expr), expr), expr);
|
|
4365
4646
|
}
|
|
4366
|
-
if (isDynTypedRefType(receiverIr)) {
|
|
4647
|
+
if (receiverIr.className === "%Error" || isDynTypedRefType(receiverIr)) {
|
|
4367
4648
|
const receiver = lowerer.coerceToExpected(lowerer.lowerExpr(expr.expression), DYN);
|
|
4368
4649
|
const key = lowerer.lowerExprExpecting(expr.argumentExpression, DYN);
|
|
4369
4650
|
return lowerer.maybeNarrow({ kind: "dynKeyGet", value: receiver, key, type: DYN, loc: locOf(expr) }, expr);
|
|
@@ -4412,6 +4693,11 @@ export function lowerElementAccess(lowerer, expr) {
|
|
|
4412
4693
|
if (recv.type.kind === "jsval")
|
|
4413
4694
|
return islandElementRead(lowerer, expr, recv);
|
|
4414
4695
|
const index = lowerer.lowerExpr(expr.argumentExpression);
|
|
4696
|
+
if (isJsSourceFile(expr.getSourceFile()) && recv.type.kind === "bytes") {
|
|
4697
|
+
return { kind: "dynKeyGet", value: lowerer.coerceInto(expr.expression, recv, DYN),
|
|
4698
|
+
key: index.type.kind === "f64" ? { kind: "toString", operand: index, type: STRING, loc: locOf(expr) } : index,
|
|
4699
|
+
type: DYN, loc: locOf(expr) };
|
|
4700
|
+
}
|
|
4415
4701
|
if (index.type.kind === "dyn" && recv.type.kind === "bytes") {
|
|
4416
4702
|
return { kind: "dynKeyGet", value: lowerer.coerceInto(expr.expression, recv, DYN), key: index, type: DYN, loc: locOf(expr) };
|
|
4417
4703
|
}
|
|
@@ -4509,16 +4795,21 @@ export function lowerElementAccess(lowerer, expr) {
|
|
|
4509
4795
|
// Dispatch from that stored shape, as the array fallback below does.
|
|
4510
4796
|
if (receiverIr === null && obj.type.kind === "record") {
|
|
4511
4797
|
const shape = lowerer.shapes.get(obj.type.shapeId);
|
|
4798
|
+
const index = tupleLiteralIndex(expr.argumentExpression);
|
|
4799
|
+
const field = shape?.tuple && index !== null ? shape.fields.find((field) => field.name === String(index)) : null;
|
|
4800
|
+
if (field)
|
|
4801
|
+
return lowerer.maybeNarrow({ kind: "recordGet", obj, shapeId: obj.type.shapeId, field: field.name, type: field.type, loc: locOf(expr) }, expr);
|
|
4512
4802
|
if (shape && !shape.tuple) {
|
|
4513
4803
|
return lowerer.lowerRecordKeyRead(expr, obj.type.shapeId, shape);
|
|
4514
4804
|
}
|
|
4515
4805
|
}
|
|
4516
|
-
if (obj.type.kind === "dyn" || isDynTypedRefType(obj.type)) {
|
|
4806
|
+
if (obj.type.kind === "dyn" || obj.type.kind === "object" && obj.type.className === "%Error" || isDynTypedRefType(obj.type)) {
|
|
4517
4807
|
const rawKey = lowerer.lowerExpr(expr.argumentExpression);
|
|
4518
4808
|
// Number, bool, and DYN keys stringify (ToPropertyKey) — the
|
|
4519
4809
|
// dyn-keyed read `catchWarning[warning.name]` where the property
|
|
4520
4810
|
// chain itself lowered dyn.
|
|
4521
|
-
const key = rawKey.type.kind === "dyn" || rawKey.type.kind === "symbol"
|
|
4811
|
+
const key = rawKey.type.kind === "dyn" || rawKey.type.kind === "symbol" ||
|
|
4812
|
+
rawKey.type.kind === "union" && lowerer.dynConvertible(rawKey.type)
|
|
4522
4813
|
? lowerer.coerceToExpected(rawKey, DYN)
|
|
4523
4814
|
: lowerRecordPropertyKey(lowerer, rawKey, expr.argumentExpression);
|
|
4524
4815
|
if (key.type.kind === "string" || key.type.kind === "dyn") {
|
|
@@ -4571,6 +4862,11 @@ export function lowerElementAccess(lowerer, expr) {
|
|
|
4571
4862
|
if (receiverIr?.kind !== "array") {
|
|
4572
4863
|
{
|
|
4573
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
|
+
}
|
|
4574
4870
|
if (receiver?.type.kind === "func" &&
|
|
4575
4871
|
canBoxFuncIntoDyn(receiver.type, (id) => lowerer.shapes.get(id), (id) => lowerer.unions.get(id))) {
|
|
4576
4872
|
const key = lowerRecordPropertyKey(lowerer, lowerer.lowerExpr(expr.argumentExpression), expr.argumentExpression);
|
|
@@ -4692,6 +4988,11 @@ export function lowerElementAccess(lowerer, expr) {
|
|
|
4692
4988
|
// is undefined, whose property lookup misses just like NaN; keep that
|
|
4693
4989
|
// value instead of rejecting a checker-number index with optional IR.
|
|
4694
4990
|
const index = lowerOptionalNumber(lowerer, lowerer.lowerExpr(expr.argumentExpression), locOf(expr.argumentExpression), expr.argumentExpression);
|
|
4991
|
+
if (arr.type.kind === "array" && (index.type.kind === "dyn" ||
|
|
4992
|
+
isJsSourceFile(expr.getSourceFile()) && index.type.kind !== "f64" && lowerer.dynConvertible(index.type))) {
|
|
4993
|
+
return { kind: "dynKeyGet", value: lowerer.coerceToExpected(arr, DYN),
|
|
4994
|
+
key: index, type: DYN, loc: locOf(expr) };
|
|
4995
|
+
}
|
|
4695
4996
|
if (index.type.kind !== "f64") {
|
|
4696
4997
|
lowerer.unsupported("SC1090", expr.argumentExpression, "indexing with non-number keys");
|
|
4697
4998
|
}
|
|
@@ -4802,8 +5103,11 @@ export function lowerRecordKeyRead(lowerer, expr, shapeId, shape, includeUndefin
|
|
|
4802
5103
|
// Number AND checked-dynamic keys ride the JS-exact formatter —
|
|
4803
5104
|
// property keys ARE strings (o[k] is o[String(k)] in JS), and a dyn
|
|
4804
5105
|
// key (agent.sockets[agent.getName(...)]) stringifies the same way.
|
|
4805
|
-
dk
|
|
4806
|
-
|
|
5106
|
+
if (dk.type.kind === "union" && lowerer.dynConvertible(dk.type))
|
|
5107
|
+
dk = lowerer.coerceToExpected(dk, DYN);
|
|
5108
|
+
if (dk.type.kind !== "symbol" && dk.type.kind !== "dyn")
|
|
5109
|
+
dk = lowerRecordPropertyKey(lowerer, dk, keyNode);
|
|
5110
|
+
if (dk.type.kind !== "string" && dk.type.kind !== "symbol" && dk.type.kind !== "dyn") {
|
|
4807
5111
|
lowerer.unsupported("SC1090", keyNode, "indexing records with non-string or non-number keys");
|
|
4808
5112
|
}
|
|
4809
5113
|
const read = { kind: "dynKeyGet", key: dk, value: obj, type: DYN, loc };
|
|
@@ -4850,6 +5154,19 @@ export function lowerRecordKeyRead(lowerer, expr, shapeId, shape, includeUndefin
|
|
|
4850
5154
|
let key = litKey !== null
|
|
4851
5155
|
? { kind: "strLit", value: litKey, type: STRING, loc: locOf(keyNode) }
|
|
4852
5156
|
: lowerer.lowerExpr(keyNode);
|
|
5157
|
+
// Optional string/number keys need ToPropertyKey, not a boxed copy of
|
|
5158
|
+
// the receiver. Keep record reads on their native storage so nested
|
|
5159
|
+
// references retain identity across index-signature reads.
|
|
5160
|
+
if (key.type.kind === "union" &&
|
|
5161
|
+
!lowerer.unions.get(key.type.unionId)?.arms.some((arm) => arm.kind === "symbol" || arm.kind === "dyn")) {
|
|
5162
|
+
key = lowerRecordPropertyKey(lowerer, key, keyNode);
|
|
5163
|
+
}
|
|
5164
|
+
if ((key.type.kind === "symbol" || key.type.kind === "dyn" || key.type.kind === "union") &&
|
|
5165
|
+
lowerer.dynConvertible(key.type) && lowerer.dynConvertible(obj.type)) {
|
|
5166
|
+
const read = { kind: "dynKeyGet", value: lowerer.coerceToExpected(obj, DYN),
|
|
5167
|
+
key: lowerer.coerceToExpected(key, DYN), type: DYN, loc };
|
|
5168
|
+
return includeUndefined ? read : lowerer.maybeNarrow(read, expr);
|
|
5169
|
+
}
|
|
4853
5170
|
// Runtime NUMBER keys ride the JS-exact formatter (o[n] is o[String(n)]
|
|
4854
5171
|
// in JS — the number-keyed-signature access path).
|
|
4855
5172
|
key = lowerRecordPropertyKey(lowerer, key, keyNode);
|
|
@@ -4914,6 +5231,9 @@ export function lowerRecordKeyRead(lowerer, expr, shapeId, shape, includeUndefin
|
|
|
4914
5231
|
* values remain real undefined values, whose JavaScript property spelling
|
|
4915
5232
|
* is the string "undefined". */
|
|
4916
5233
|
function lowerRecordPropertyKey(lowerer, key, node) {
|
|
5234
|
+
if (key.type.kind === "union" && lowerer.dynConvertible(key.type)) {
|
|
5235
|
+
return { kind: "libCall", fn: "dyn.toStringCoerce", args: [lowerer.coerceInto(node, key, DYN)], type: STRING, loc: key.loc };
|
|
5236
|
+
}
|
|
4917
5237
|
if (key.type.kind === "f64" || key.type.kind === "bool" || key.type.kind === "dyn" ||
|
|
4918
5238
|
lowerer.runtimeOptionalWidening(key.type, STRING) !== null) {
|
|
4919
5239
|
return lowerer.ensureString(key, node);
|
|
@@ -5079,11 +5399,29 @@ export function lowerElementCompound(lowerer, expr, op) {
|
|
|
5079
5399
|
const target = expr.left;
|
|
5080
5400
|
fenceNodeModuleMutation(lowerer, target, "assignment");
|
|
5081
5401
|
const loc = locOf(expr);
|
|
5082
|
-
const
|
|
5402
|
+
const checked = lowerer.lowerExpr(target.expression);
|
|
5403
|
+
if (checked?.type.kind === "dyn" && isJsSourceFile(expr.getSourceFile())) {
|
|
5404
|
+
const stmts = [];
|
|
5405
|
+
const save = (value, name) => {
|
|
5406
|
+
const local = lowerer.declareHiddenLocal(name, value.type);
|
|
5407
|
+
stmts.push({ kind: "varDecl", localId: local.id, init: value, loc });
|
|
5408
|
+
return varRef(local.id, value.type, loc);
|
|
5409
|
+
};
|
|
5410
|
+
const receiver = save(checked, "%compoundReceiver");
|
|
5411
|
+
const key = save(lowerer.lowerExprExpecting(target.argumentExpression, DYN), "%compoundKey");
|
|
5412
|
+
const old = save({ kind: "dynKeyGet", value: receiver, key, type: DYN, loc }, "%compoundOld");
|
|
5413
|
+
const rhs = lowerer.lowerExprExpecting(expr.right, DYN);
|
|
5414
|
+
const numeric = ["-", "*", "/", "%", "**"].includes(op);
|
|
5415
|
+
const value = save({ kind: "libCall", fn: op === "+" ? "dyn.add" : numeric ? "dyn.arithmetic" : "dyn.bitwise",
|
|
5416
|
+
args: op === "+" ? [old, rhs] : [old, rhs, strLit(op, loc)], type: DYN, loc }, "%compoundResult");
|
|
5417
|
+
stmts.push({ kind: "exprStmt", expr: { kind: "libCall", fn: "dyn.keySetComputed", args: [receiver, key, value], type: VOID, loc }, loc });
|
|
5418
|
+
return { kind: "seqExpr", stmts, result: value, type: DYN, loc };
|
|
5419
|
+
}
|
|
5420
|
+
const receiverType = checked.type.kind === "array" || checked.type.kind === "bytes" ? checked.type : lowerer.mapTypeOf(lowerer.typeOf(target.expression));
|
|
5083
5421
|
if (receiverType?.kind !== "array" && receiverType?.kind !== "bytes") {
|
|
5084
5422
|
lowerer.unsupported("SC1090", target, "compound assignment to non-array elements");
|
|
5085
5423
|
}
|
|
5086
|
-
let receiver =
|
|
5424
|
+
let receiver = checked;
|
|
5087
5425
|
if (receiver.type.kind === "union" && lowerer.armTag(receiver.type.unionId, UNDEFINED_T) >= 0) {
|
|
5088
5426
|
const present = lowerer.stripUndefinedArm(receiver.type);
|
|
5089
5427
|
const helper = present.kind === receiverType.kind
|
|
@@ -5108,7 +5446,9 @@ export function lowerElementCompound(lowerer, expr, op) {
|
|
|
5108
5446
|
const oldLocal = lowerer.declareHiddenLocal("%compoundOld", oldValue.type);
|
|
5109
5447
|
const oldRef = () => varRef(oldLocal.id, oldValue.type, loc);
|
|
5110
5448
|
const rhs = lowerer.lowerExpr(expr.right);
|
|
5111
|
-
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);
|
|
5112
5452
|
const elementType = receiver.type.kind === "array" ? receiver.type.elem : F64;
|
|
5113
5453
|
const valueType = elementType.kind === "union" && lowerer.armTag(elementType.unionId, UNDEFINED_T) >= 0
|
|
5114
5454
|
? lowerer.stripUndefinedArm(elementType)
|
|
@@ -5120,6 +5460,12 @@ export function lowerElementCompound(lowerer, expr, op) {
|
|
|
5120
5460
|
else if (valueType.kind === "f64" && numericRhs.type.kind === "f64") {
|
|
5121
5461
|
computed = { kind: "bin", op, left: lowerOptionalNumber(lowerer, oldRef(), loc), right: numericRhs, type: F64, loc };
|
|
5122
5462
|
}
|
|
5463
|
+
else if (valueType.kind === "dyn" && isJsSourceFile(expr.getSourceFile())) {
|
|
5464
|
+
const left = lowerer.coerceToExpected(oldRef(), DYN), right = lowerer.coerceToExpected(rhs, DYN);
|
|
5465
|
+
const numeric = ["-", "*", "/", "%", "**"].includes(op);
|
|
5466
|
+
computed = { kind: "libCall", fn: op === "+" ? "dyn.add" : numeric ? "dyn.arithmetic" : "dyn.bitwise",
|
|
5467
|
+
args: op === "+" ? [left, right] : [left, right, strLit(op, loc)], type: DYN, loc };
|
|
5468
|
+
}
|
|
5123
5469
|
else {
|
|
5124
5470
|
lowerer.unsupported("SC1043", expr);
|
|
5125
5471
|
}
|
|
@@ -5181,6 +5527,18 @@ export function lowerNativeFunctionAssignment(lowerer, expr) {
|
|
|
5181
5527
|
(!ts.isPropertyAccessExpression(target) && !ts.isElementAccessExpression(target)) ||
|
|
5182
5528
|
target.questionDotToken)
|
|
5183
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;
|
|
5184
5542
|
const receiver = tryLowerExpression(lowerer, target.expression);
|
|
5185
5543
|
if (receiver?.type.kind !== "func" ||
|
|
5186
5544
|
!canBoxFuncIntoDyn(receiver.type, (id) => lowerer.shapes.get(id), (id) => lowerer.unions.get(id)))
|
|
@@ -5194,6 +5552,9 @@ export function lowerNativeFunctionAssignment(lowerer, expr) {
|
|
|
5194
5552
|
return lowerDynMemberAssignment(lowerer, expr, boxed);
|
|
5195
5553
|
}
|
|
5196
5554
|
export function lowerElementWrite(lowerer, expr) {
|
|
5555
|
+
const prototype = lowerClassPrototypeComputedAssignment(lowerer, expr);
|
|
5556
|
+
if (prototype)
|
|
5557
|
+
return { kind: "exprStmt", expr: prototype, loc: locOf(expr) };
|
|
5197
5558
|
const target = expr.left;
|
|
5198
5559
|
fenceNodeModuleMutation(lowerer, target, "assignment");
|
|
5199
5560
|
const globalKey = globalSymbolKey(lowerer, target.expression, target.argumentExpression);
|
|
@@ -5233,13 +5594,30 @@ export function lowerElementWrite(lowerer, expr) {
|
|
|
5233
5594
|
const receiver = lowerer.lowerExpr(target.expression);
|
|
5234
5595
|
if (receiver.type.kind === "dyn")
|
|
5235
5596
|
return { kind: "exprStmt", expr: lowerDynMemberAssignment(lowerer, expr, receiver), loc: locOf(expr) };
|
|
5597
|
+
if (isJsSourceFile(expr.getSourceFile()) && receiver.type.kind === "object" && lowerer.dynConvertible(receiver.type)) {
|
|
5598
|
+
return { kind: "exprStmt", expr: lowerDynMemberAssignment(lowerer, expr, lowerer.coerceToExpected(receiver, DYN)), loc: locOf(expr) };
|
|
5599
|
+
}
|
|
5236
5600
|
lowerer.unsupported("SC1090", target, "symbol-keyed property writes outside class fields keyed by a stable module-level literal Symbol()/Symbol.for() (other static shapes have no symbol-keyed storage)");
|
|
5237
5601
|
}
|
|
5238
5602
|
const checkedReceiver = tryLowerExpression(lowerer, target.expression);
|
|
5239
5603
|
if (checkedReceiver?.type.kind === "dyn") {
|
|
5240
5604
|
return { kind: "exprStmt", expr: lowerDynMemberAssignment(lowerer, expr, checkedReceiver), loc: locOf(expr) };
|
|
5241
5605
|
}
|
|
5606
|
+
if (checkedReceiver?.type.kind === "object" && isJsSourceFile(expr.getSourceFile())) {
|
|
5607
|
+
const info = lowerer.classes.get(checkedReceiver.type.className);
|
|
5608
|
+
const key = recordKeyLiteralText(target.argumentExpression) ?? recordKeyTypeLiteralText(lowerer, target.argumentExpression);
|
|
5609
|
+
if (info && key !== null && isClassCallback(lowerer, info, key)) {
|
|
5610
|
+
const receiver = lowerer.coerceInto(target.expression, checkedReceiver, DYN);
|
|
5611
|
+
return { kind: "exprStmt", expr: lowerDynMemberAssignment(lowerer, expr, receiver), loc: locOf(expr) };
|
|
5612
|
+
}
|
|
5613
|
+
}
|
|
5242
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
|
+
}
|
|
5243
5621
|
if (receiverIr === null || receiverIr.kind === "dyn") {
|
|
5244
5622
|
const receiver = tryLowerExpression(lowerer, target.expression);
|
|
5245
5623
|
if (receiver?.type.kind === "array")
|
|
@@ -5307,13 +5685,15 @@ export function lowerElementWrite(lowerer, expr) {
|
|
|
5307
5685
|
return lowerer.fieldSetStmt(field, value, locOf(expr), target);
|
|
5308
5686
|
}
|
|
5309
5687
|
if (hasClassPayload(lowerer, receiver.type)) {
|
|
5688
|
+
if (receiver.type.kind === "object" && receiver.type.className === "%Error") {
|
|
5689
|
+
return { kind: "exprStmt", expr: lowerDynMemberAssignment(lowerer, expr, lowerer.coerceInto(target.expression, receiver, DYN)), loc: locOf(expr) };
|
|
5690
|
+
}
|
|
5310
5691
|
lowerer.unsupported("SC1090", target, "writing undeclared properties through structural views of class instances");
|
|
5311
5692
|
}
|
|
5312
5693
|
}
|
|
5313
5694
|
}
|
|
5314
5695
|
// Typed-array element write `b[i] = v` — bytesSet: the value is an f64
|
|
5315
|
-
// the runtime coerces JS-exactly; invalid indices
|
|
5316
|
-
// typed arrays are fixed-length; JS would ignore the write).
|
|
5696
|
+
// the runtime coerces JS-exactly; invalid indices are ignored.
|
|
5317
5697
|
if (receiverIr?.kind === "bytes") {
|
|
5318
5698
|
const recv = lowerer.lowerExpr(target.expression);
|
|
5319
5699
|
// The write twin: a typed-array .d.ts surface whose VALUE is a
|
|
@@ -5499,7 +5879,7 @@ export function lowerElementWrite(lowerer, expr) {
|
|
|
5499
5879
|
// write, anything else falls through to the fences.
|
|
5500
5880
|
if (receiverIr?.kind === "dyn" || receiverIr?.kind === "object" || receiverIr === null) {
|
|
5501
5881
|
const obj = receiverIr !== null ? lowerer.lowerExpr(target.expression) : tryLowerExpression(lowerer, target.expression);
|
|
5502
|
-
if (obj !== null && (obj.type.kind === "dyn" || isDynTypedRefType(obj.type))) {
|
|
5882
|
+
if (obj !== null && (obj.type.kind === "dyn" || obj.type.kind === "object" && obj.type.className === "%Error" || isDynTypedRefType(obj.type))) {
|
|
5503
5883
|
const loc = locOf(expr);
|
|
5504
5884
|
const litKey = recordKeyLiteralText(target.argumentExpression);
|
|
5505
5885
|
let key = litKey !== null
|
|
@@ -5544,6 +5924,9 @@ export function lowerElementWrite(lowerer, expr) {
|
|
|
5544
5924
|
lowerer.unsupported("SC1090", target.expression, "assignment through a possibly missing nested array receiver");
|
|
5545
5925
|
}
|
|
5546
5926
|
const index = lowerOptionalNumber(lowerer, lowerer.lowerExpr(target.argumentExpression), locOf(target.argumentExpression), target.argumentExpression);
|
|
5927
|
+
if (index.type.kind === "dyn" && lowerer.dynConvertible(arr.type)) {
|
|
5928
|
+
return { kind: "exprStmt", expr: lowerDynMemberAssignment(lowerer, expr, lowerer.coerceInto(target.expression, arr, DYN)), loc: locOf(expr) };
|
|
5929
|
+
}
|
|
5547
5930
|
if (index.type.kind !== "f64") {
|
|
5548
5931
|
lowerer.unsupported("SC1090", target.argumentExpression, "indexing with non-number keys");
|
|
5549
5932
|
}
|
|
@@ -5878,7 +6261,7 @@ export function lowerAsExpression(lowerer, expr) {
|
|
|
5878
6261
|
return { kind: "jsExit", value: inner, type: target, loc: locOf(expr) };
|
|
5879
6262
|
}
|
|
5880
6263
|
const targetTs = lowerer.checker.getTypeFromTypeNode(expr.type);
|
|
5881
|
-
const target = lowerer.mapTypeOf(targetTs);
|
|
6264
|
+
const target = lowerer.mapTypeOf(targetTs) ?? dynFallbackType(lowerer, expr.type, targetTs);
|
|
5882
6265
|
if (!target)
|
|
5883
6266
|
lowerer.badType(expr.type, targetTs);
|
|
5884
6267
|
if (target.kind === "dyn")
|
|
@@ -6011,59 +6394,86 @@ export function lowerPrefixUnary(lowerer, expr) {
|
|
|
6011
6394
|
}
|
|
6012
6395
|
lowerer.unsupported("SC1090", expr, `syntax '${ts.syntaxKindName(expr.kind)}'`);
|
|
6013
6396
|
}
|
|
6014
|
-
/**
|
|
6015
|
-
|
|
6016
|
-
|
|
6017
|
-
* backend reads/writes through the box); record fields and array
|
|
6018
|
-
* elements keep the fence in value position (statement-position `obj.f++`
|
|
6019
|
-
* still desugars through the compound-field path). */
|
|
6020
|
-
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) {
|
|
6021
6400
|
const loc = locOf(expr);
|
|
6022
6401
|
const op = expr.operator === ts.SyntaxKind.PlusPlusToken ? "+" : "-";
|
|
6023
6402
|
if (!ts.isIdentifier(expr.operand)) {
|
|
6024
|
-
|
|
6025
|
-
|
|
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);
|
|
6026
6410
|
if (target) {
|
|
6027
6411
|
if (target.type.kind !== "f64")
|
|
6028
6412
|
lowerer.unsupported("SC1043", expr);
|
|
6029
6413
|
return { kind: "incDec", op, prefix, localId: target.id, type: F64, loc };
|
|
6030
6414
|
}
|
|
6031
6415
|
}
|
|
6032
|
-
|
|
6033
|
-
|
|
6034
|
-
|
|
6035
|
-
|
|
6036
|
-
|
|
6037
|
-
|
|
6038
|
-
|
|
6039
|
-
|
|
6040
|
-
|
|
6041
|
-
|
|
6042
|
-
|
|
6043
|
-
|
|
6044
|
-
|
|
6045
|
-
:
|
|
6046
|
-
|
|
6047
|
-
|
|
6048
|
-
|
|
6049
|
-
|
|
6050
|
-
|
|
6051
|
-
|
|
6052
|
-
|
|
6053
|
-
|
|
6054
|
-
|
|
6055
|
-
|
|
6056
|
-
|
|
6057
|
-
|
|
6058
|
-
|
|
6059
|
-
|
|
6060
|
-
|
|
6061
|
-
|
|
6062
|
-
|
|
6063
|
-
|
|
6064
|
-
|
|
6065
|
-
|
|
6066
|
-
|
|
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");
|
|
6067
6477
|
}
|
|
6068
6478
|
const target = lowerer.resolveWritable(expr.operand);
|
|
6069
6479
|
if (!target) {
|
|
@@ -6143,6 +6553,7 @@ export function lowerCompoundValueToTarget(lowerer, expr, compound, target) {
|
|
|
6143
6553
|
const loc = locOf(expr);
|
|
6144
6554
|
const read = { kind: "varRef", localId: target.id, type: target.type, loc: locOf(expr.left) };
|
|
6145
6555
|
const rhs = lowerer.lowerExpr(expr.right);
|
|
6556
|
+
const numericRead = lowerOptionalNumber(lowerer, read, loc);
|
|
6146
6557
|
const numericRhs = lowerOptionalNumber(lowerer, rhs, loc);
|
|
6147
6558
|
if (target.type.kind === "jsval" || rhs.type.kind === "jsval") {
|
|
6148
6559
|
const JS_COMPOUND = { "+": "add", "-": "sub", "*": "mul", "/": "div", "%": "mod", "**": "pow" };
|
|
@@ -6159,16 +6570,17 @@ export function lowerCompoundValueToTarget(lowerer, expr, compound, target) {
|
|
|
6159
6570
|
if (compound === "+" && target.type.kind === "string") {
|
|
6160
6571
|
return { kind: "strConcat", left: read, right: lowerer.ensureString(rhs, expr.right), type: STRING, loc };
|
|
6161
6572
|
}
|
|
6162
|
-
if (
|
|
6163
|
-
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);
|
|
6164
6575
|
}
|
|
6165
6576
|
if ((target.type.kind === "dyn" || rhs.type.kind === "dyn") &&
|
|
6166
|
-
(target.type.kind === "dyn" || target.type.kind === "f64") &&
|
|
6167
|
-
(rhs.type.kind === "dyn" || rhs.type.kind === "f64") &&
|
|
6168
6577
|
isJsSourceFile(expr.getSourceFile())) {
|
|
6169
|
-
const
|
|
6170
|
-
const
|
|
6171
|
-
|
|
6578
|
+
const left = lowerer.coerceInto(expr.left, read, DYN);
|
|
6579
|
+
const right = lowerer.coerceInto(expr.right, rhs, DYN);
|
|
6580
|
+
const numeric = ["-", "*", "/", "%", "**"].includes(compound);
|
|
6581
|
+
const computed = { kind: "libCall", fn: compound === "+" ? "dyn.add" : numeric ? "dyn.arithmetic" : "dyn.bitwise",
|
|
6582
|
+
args: compound === "+" ? [left, right] : [left, right, { kind: "strLit", value: compound, type: STRING, loc }], type: DYN, loc };
|
|
6583
|
+
return lowerer.coerceInto(expr, computed, target.type);
|
|
6172
6584
|
}
|
|
6173
6585
|
lowerer.unsupported("SC1043", expr);
|
|
6174
6586
|
}
|
|
@@ -6180,6 +6592,9 @@ export function lowerBinary(lowerer, expr) {
|
|
|
6180
6592
|
return cacheHas;
|
|
6181
6593
|
if (op === ts.SyntaxKind.EqualsToken || (op >= ts.SyntaxKind.FirstCompoundAssignment && op <= ts.SyntaxKind.LastCompoundAssignment)) {
|
|
6182
6594
|
if (op === ts.SyntaxKind.EqualsToken) {
|
|
6595
|
+
const prototypeWrite = lowerClassPrototypeComputedAssignment(lowerer, expr);
|
|
6596
|
+
if (prototypeWrite)
|
|
6597
|
+
return prototypeWrite;
|
|
6183
6598
|
const callableWrite = lowerNativeFunctionAssignment(lowerer, expr);
|
|
6184
6599
|
if (callableWrite)
|
|
6185
6600
|
return callableWrite;
|
|
@@ -6293,8 +6708,30 @@ export function lowerBinary(lowerer, expr) {
|
|
|
6293
6708
|
// key afterward, then yields the RHS without rereading the property.
|
|
6294
6709
|
if (ts.isElementAccessExpression(expr.left) && !expr.left.questionDotToken) {
|
|
6295
6710
|
const recv = tryLowerExpression(lowerer, expr.left.expression);
|
|
6296
|
-
if (recv
|
|
6297
|
-
|
|
6711
|
+
if (recv && (recv.type.kind === "dyn" ||
|
|
6712
|
+
recv.type.kind === "array" && isJsSourceFile(expr.getSourceFile()) && lowerer.dynConvertible(recv.type))) {
|
|
6713
|
+
return lowerDynMemberAssignment(lowerer, expr, lowerer.coerceInto(expr.left.expression, recv, DYN));
|
|
6714
|
+
}
|
|
6715
|
+
if (recv?.type.kind === "array" || recv?.type.kind === "bytes") {
|
|
6716
|
+
const receiver = lowerer.declareHiddenLocal("%setArray", recv.type);
|
|
6717
|
+
const index = lowerer.lowerExpr(expr.left.argumentExpression);
|
|
6718
|
+
if (index.type.kind !== "f64")
|
|
6719
|
+
lowerer.unsupported("SC1090", expr.left.argumentExpression, "indexing with non-number keys");
|
|
6720
|
+
const key = lowerer.declareHiddenLocal("%setIndex", F64);
|
|
6721
|
+
const raw = lowerer.lowerExpr(expr.right);
|
|
6722
|
+
const value = lowerer.declareHiddenLocal("%setValue", raw.type);
|
|
6723
|
+
const result = varRef(value.id, raw.type, loc);
|
|
6724
|
+
const arr = varRef(receiver.id, recv.type, loc);
|
|
6725
|
+
const idx = varRef(key.id, F64, loc);
|
|
6726
|
+
const write = recv.type.kind === "array"
|
|
6727
|
+
? arrayValueStore(lowerer, arr, idx, result, recv.type.elem, loc)
|
|
6728
|
+
: { kind: "bytesSet", arr, index: idx, value: lowerer.coerceInto(expr.right, result, F64), loc };
|
|
6729
|
+
return { kind: "seqExpr", stmts: [
|
|
6730
|
+
{ kind: "varDecl", localId: receiver.id, init: recv, loc },
|
|
6731
|
+
{ kind: "varDecl", localId: key.id, init: index, loc },
|
|
6732
|
+
{ kind: "varDecl", localId: value.id, init: raw, loc },
|
|
6733
|
+
write,
|
|
6734
|
+
], result, type: result.type, loc };
|
|
6298
6735
|
}
|
|
6299
6736
|
}
|
|
6300
6737
|
// `h.k = v` on an ISLAND receiver in VALUE position: the engine
|
|
@@ -6337,32 +6774,8 @@ export function lowerBinary(lowerer, expr) {
|
|
|
6337
6774
|
// consumer sees exactly what the checker typed.
|
|
6338
6775
|
if (ts.isPropertyAccessExpression(expr.left) && !expr.left.questionDotToken) {
|
|
6339
6776
|
const recv = tryLowerExpression(lowerer, expr.left.expression);
|
|
6340
|
-
if (recv && recv.type.kind === "dyn")
|
|
6341
|
-
|
|
6342
|
-
const rhsVal = lowerer.lowerExpr(expr.right);
|
|
6343
|
-
const valTmp = lowerer.declareHiddenLocal("%setVal", rhsVal.type);
|
|
6344
|
-
const valRef = () => ({ kind: "varRef", localId: valTmp.id, type: rhsVal.type, loc });
|
|
6345
|
-
const stored = lowerer.coerceToExpected(valRef(), DYN);
|
|
6346
|
-
if (stored.type.kind !== "dyn") {
|
|
6347
|
-
lowerer.unsupported("SC1101", expr.right, `assigning '${lowerer.fmt(rhsVal.type)}' values into a checked-dynamic member`);
|
|
6348
|
-
}
|
|
6349
|
-
const key = { kind: "strLit", value: expr.left.name.text, type: STRING, loc: locOf(expr.left.name) };
|
|
6350
|
-
return {
|
|
6351
|
-
kind: "seqExpr",
|
|
6352
|
-
stmts: [
|
|
6353
|
-
{ kind: "varDecl", localId: recvTmp.id, init: recv, loc },
|
|
6354
|
-
{ kind: "varDecl", localId: valTmp.id, init: rhsVal, loc },
|
|
6355
|
-
{
|
|
6356
|
-
kind: "exprStmt",
|
|
6357
|
-
expr: { kind: "libCall", fn: "dyn.keySet", args: [{ kind: "varRef", localId: recvTmp.id, type: DYN, loc }, key, stored], type: VOID, loc },
|
|
6358
|
-
loc,
|
|
6359
|
-
},
|
|
6360
|
-
],
|
|
6361
|
-
result: valRef(),
|
|
6362
|
-
type: rhsVal.type,
|
|
6363
|
-
loc,
|
|
6364
|
-
};
|
|
6365
|
-
}
|
|
6777
|
+
if (recv && recv.type.kind === "dyn")
|
|
6778
|
+
return lowerDynMemberAssignment(lowerer, expr, recv);
|
|
6366
6779
|
const field = lowerer.fieldTarget(expr.left);
|
|
6367
6780
|
if (field) {
|
|
6368
6781
|
const recvTmp = lowerer.declareHiddenLocal("%setReceiver", field.obj.type);
|
|
@@ -6446,8 +6859,14 @@ export function lowerBinary(lowerer, expr) {
|
|
|
6446
6859
|
}
|
|
6447
6860
|
const strictEquality = op === ts.SyntaxKind.EqualsEqualsEqualsToken ||
|
|
6448
6861
|
op === ts.SyntaxKind.ExclamationEqualsEqualsToken;
|
|
6449
|
-
|
|
6450
|
-
|
|
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
|
+
}
|
|
6451
6870
|
if (left.type.kind === "dyn" || right.type.kind === "dyn") {
|
|
6452
6871
|
// The narrowing unit comparisons ARE answerable on unknown: `v ===
|
|
6453
6872
|
// undefined` / `v !== null` test the dyn node's kind directly, and
|
|
@@ -6475,9 +6894,9 @@ export function lowerBinary(lowerer, expr) {
|
|
|
6475
6894
|
const dynSide = left.type.kind === "dyn" ? left : right;
|
|
6476
6895
|
const scalarSide = dynSide === left ? right : left;
|
|
6477
6896
|
const reference = scalarSide.type;
|
|
6478
|
-
if ((
|
|
6479
|
-
reference.kind === "bytes" || reference.kind === "func" || reference.kind === "regex" || reference.kind === "url" ||
|
|
6480
|
-
reference.kind === "bigint" || reference.kind === "symbol" || reference.kind === "set" || reference.kind === "union" || DYN_HANDLE_KINDS.has(reference.kind)) &&
|
|
6897
|
+
if ((reference.kind === "object" || reference.kind === "record" || reference.kind === "array" ||
|
|
6898
|
+
reference.kind === "bytes" || reference.kind === "func" || reference.kind === "classval" || reference.kind === "regex" || reference.kind === "url" ||
|
|
6899
|
+
reference.kind === "bigint" || reference.kind === "symbol" || reference.kind === "map" || reference.kind === "set" || reference.kind === "union" || DYN_HANDLE_KINDS.has(reference.kind)) &&
|
|
6481
6900
|
lowerer.dynConvertible(reference)) {
|
|
6482
6901
|
const boxed = {
|
|
6483
6902
|
kind: "dynFrom", value: scalarSide, type: DYN, loc: scalarSide.loc,
|
|
@@ -6524,8 +6943,11 @@ export function lowerBinary(lowerer, expr) {
|
|
|
6524
6943
|
}
|
|
6525
6944
|
// Numeric operators dispatch on ToNumeric's actual Number/BigInt
|
|
6526
6945
|
// result, including values flowing through native callable properties.
|
|
6527
|
-
if (isJsSourceFile(expr.getSourceFile()) || op === ts.SyntaxKind.PlusToken
|
|
6528
|
-
|
|
6946
|
+
if (isJsSourceFile(expr.getSourceFile()) || op === ts.SyntaxKind.PlusToken ||
|
|
6947
|
+
[ts.SyntaxKind.LessThanToken, ts.SyntaxKind.LessThanEqualsToken,
|
|
6948
|
+
ts.SyntaxKind.GreaterThanToken, ts.SyntaxKind.GreaterThanEqualsToken].includes(op) ||
|
|
6949
|
+
[ts.SyntaxKind.MinusToken, ts.SyntaxKind.AsteriskToken, ts.SyntaxKind.SlashToken,
|
|
6950
|
+
ts.SyntaxKind.PercentToken, ts.SyntaxKind.AsteriskAsteriskToken].includes(op)) {
|
|
6529
6951
|
const other = left.type.kind === "dyn" ? right : left;
|
|
6530
6952
|
const NUM_BIN = {
|
|
6531
6953
|
[ts.SyntaxKind.MinusToken]: "-",
|
|
@@ -6553,8 +6975,9 @@ export function lowerBinary(lowerer, expr) {
|
|
|
6553
6975
|
return numeric ? { kind: "dynCheck", value, type: numeric, loc } : value;
|
|
6554
6976
|
}
|
|
6555
6977
|
}
|
|
6556
|
-
if (cmp && (
|
|
6557
|
-
return { kind: "
|
|
6978
|
+
if (cmp && lowerer.dynConvertible(left.type) && lowerer.dynConvertible(right.type)) {
|
|
6979
|
+
return { kind: "libCall", fn: "dyn.compare", args: [lowerer.coerceToExpected(left, DYN),
|
|
6980
|
+
lowerer.coerceToExpected(right, DYN), { kind: "strLit", value: cmp, type: STRING, loc }], type: BOOL, loc };
|
|
6558
6981
|
}
|
|
6559
6982
|
// Untyped addition chooses concatenation or numeric addition only
|
|
6560
6983
|
// after both operands have undergone ToPrimitive at runtime.
|
|
@@ -6986,6 +7409,10 @@ export function lowerBinary(lowerer, expr) {
|
|
|
6986
7409
|
if (DYN_HANDLE_KINDS.has(idLeft.type.kind) && typeEquals(idLeft.type, idRight.type)) {
|
|
6987
7410
|
return { kind: "bin", op: negated ? "!==" : "===", left: idLeft, right: idRight, type: BOOL, loc };
|
|
6988
7411
|
}
|
|
7412
|
+
if (lowerer.dynConvertible(idLeft.type) && lowerer.dynConvertible(idRight.type)) {
|
|
7413
|
+
return { kind: "dynScalarEq", left: lowerer.coerceToExpected(idLeft, DYN),
|
|
7414
|
+
right: lowerer.coerceToExpected(idRight, DYN), ...(negated ? { negated: true } : {}), type: BOOL, loc };
|
|
7415
|
+
}
|
|
6989
7416
|
lowerer.unsupported("SC1043", expr);
|
|
6990
7417
|
break;
|
|
6991
7418
|
}
|
|
@@ -7000,6 +7427,10 @@ export function lowerBinary(lowerer, expr) {
|
|
|
7000
7427
|
return { kind: "bin", op: cmpOp, left: arithmeticLeft, right: arithmeticRight, type: BOOL, loc };
|
|
7001
7428
|
if (bothStr)
|
|
7002
7429
|
return { kind: "strCmp", op: cmpOp, left, right, type: BOOL, loc };
|
|
7430
|
+
if (lowerer.dynConvertible(left.type) && lowerer.dynConvertible(right.type)) {
|
|
7431
|
+
return { kind: "libCall", fn: "dyn.compare", args: [lowerer.coerceToExpected(left, DYN),
|
|
7432
|
+
lowerer.coerceToExpected(right, DYN), { kind: "strLit", value: cmpOp, type: STRING, loc }], type: BOOL, loc };
|
|
7433
|
+
}
|
|
7003
7434
|
lowerer.unsupported("SC1043", expr);
|
|
7004
7435
|
break;
|
|
7005
7436
|
}
|
|
@@ -7042,6 +7473,16 @@ function lowerLogicalChain(lowerer, expr) {
|
|
|
7042
7473
|
function lowerLogicalPair(lowerer, expr, left) {
|
|
7043
7474
|
const op = expr.operatorToken.kind;
|
|
7044
7475
|
const loc = locOf(expr);
|
|
7476
|
+
// Preserve short-circuiting during lowering too: an unreachable member
|
|
7477
|
+
// on a specialized value must not poison the surrounding JS statement.
|
|
7478
|
+
if (left.kind === "boolLit") {
|
|
7479
|
+
const takeRight = op === ts.SyntaxKind.AmpersandAmpersandToken ? left.value : !left.value;
|
|
7480
|
+
return takeRight ? lowerer.lowerExpr(expr.right) : left;
|
|
7481
|
+
}
|
|
7482
|
+
if (left.kind === "unitLit" && op === ts.SyntaxKind.AmpersandAmpersandToken)
|
|
7483
|
+
return left;
|
|
7484
|
+
if (op === ts.SyntaxKind.BarBarToken && REF_TRUTHY_KINDS.has(left.type.kind))
|
|
7485
|
+
return left;
|
|
7045
7486
|
const right = lowerer.lowerExpr(expr.right);
|
|
7046
7487
|
// A LITERAL-unit left operand (a compile-time undefined/null — the
|
|
7047
7488
|
// capability-probe members: `process.features.inspector ||
|
|
@@ -7091,6 +7532,11 @@ function lowerLogicalPair(lowerer, expr, left) {
|
|
|
7091
7532
|
type: JSVAL, loc,
|
|
7092
7533
|
};
|
|
7093
7534
|
}
|
|
7535
|
+
if (isJsSourceFile(expr.getSourceFile()) && op === ts.SyntaxKind.AmpersandAmpersandToken &&
|
|
7536
|
+
left.type.kind === "union" && (lowerer.unions.get(left.type.unionId)?.arms.some((arm) => arm.kind === "bool")) &&
|
|
7537
|
+
!typeEquals(left.type, right.type) && lowerer.dynConvertible(left.type) && lowerer.dynConvertible(right.type)) {
|
|
7538
|
+
return { kind: "logical", op: "&&", left: lowerer.coerceToExpected(left, DYN), right: lowerer.coerceToExpected(right, DYN), type: DYN, loc };
|
|
7539
|
+
}
|
|
7094
7540
|
if (left.type.kind === "union" || right.type.kind === "union") {
|
|
7095
7541
|
// Test the deciding operand in its own representation. The result
|
|
7096
7542
|
// contains only the values that can survive short-circuit evaluation.
|
|
@@ -7664,6 +8110,8 @@ function lowerDynTypeofTest(lowerer, expr, loc) {
|
|
|
7664
8110
|
for (const [a, b] of operandPairs(expr)) {
|
|
7665
8111
|
if (!ts.isTypeOfExpression(a))
|
|
7666
8112
|
continue;
|
|
8113
|
+
if (lowerer.isStdlibGlobal(a.expression, "fetch"))
|
|
8114
|
+
continue;
|
|
7667
8115
|
// Non-literal comparisons use the ordinary typeof string value,
|
|
7668
8116
|
// preserving both operands' evaluation order without a narrowing test.
|
|
7669
8117
|
if (!ts.isStringLiteral(b))
|
|
@@ -7970,6 +8418,8 @@ export function lowerInstanceOf(lowerer, expr, loc) {
|
|
|
7970
8418
|
if (value.type.kind === collection)
|
|
7971
8419
|
return { kind: "seqExpr", stmts: [{ kind: "exprStmt", expr: value, loc }],
|
|
7972
8420
|
result: { kind: "boolLit", value: true, type: BOOL, loc }, type: BOOL, loc };
|
|
8421
|
+
return { kind: "libCall", fn: collection === "map" ? "dyn.nativeMapIs" : "dyn.nativeSetIs",
|
|
8422
|
+
args: [lowerer.coerceInto(expr.left, value, DYN)], type: BOOL, loc };
|
|
7973
8423
|
}
|
|
7974
8424
|
if (lowerer.isStdlibGlobal(expr.right, "WeakMap") || lowerer.isStdlibGlobal(expr.right, "WeakSet")) {
|
|
7975
8425
|
if (lowerer.dynamic) {
|
|
@@ -7986,6 +8436,10 @@ export function lowerInstanceOf(lowerer, expr, loc) {
|
|
|
7986
8436
|
const value = lowerer.coerceInto(expr.left, lowerer.lowerExpr(expr.left), DYN);
|
|
7987
8437
|
return { kind: "libCall", fn: "dyn.nativeUrlIs", args: [value], type: BOOL, loc };
|
|
7988
8438
|
}
|
|
8439
|
+
if (lowerer.isStdlibGlobal(expr.right, "DataView")) {
|
|
8440
|
+
const value = lowerer.coerceInto(expr.left, lowerer.lowerExpr(expr.left), DYN);
|
|
8441
|
+
return { kind: "libCall", fn: "dyn.dataViewIs", args: [value], type: BOOL, loc };
|
|
8442
|
+
}
|
|
7989
8443
|
if (lowerer.isStdlibGlobal(expr.right, "ArrayBuffer")) {
|
|
7990
8444
|
const value = lowerer.coerceInto(expr.left, lowerer.lowerExpr(expr.left), DYN);
|
|
7991
8445
|
return { kind: "libCall", fn: "arrayBuffer.is", args: [value], type: BOOL, loc };
|
|
@@ -8040,24 +8494,29 @@ export function lowerInstanceOf(lowerer, expr, loc) {
|
|
|
8040
8494
|
if (value.type.kind === "dyn")
|
|
8041
8495
|
return { kind: "libCall", fn: date ? "dyn.nativeDateIs" : "dyn.nativeUrlIs", args: [value], type: BOOL, loc };
|
|
8042
8496
|
if (value.type.kind === "union")
|
|
8043
|
-
|
|
8497
|
+
return { kind: "libCall", fn: date ? "dyn.nativeDateIs" : "dyn.nativeUrlIs",
|
|
8498
|
+
args: [lowerer.coerceInto(expr.left, value, DYN)], type: BOOL, loc };
|
|
8044
8499
|
return { kind: "seqExpr", stmts: [{ kind: "exprStmt", expr: value, loc }],
|
|
8045
8500
|
result: { kind: "boolLit", value: value.type.kind === (date ? "date" : "url"), type: BOOL, loc }, type: BOOL, loc };
|
|
8046
8501
|
}
|
|
8502
|
+
const webBrand = ["Request", "Response", "Headers"].find((name) => lowerer.isStdlibGlobal(expr.right, name));
|
|
8503
|
+
if (webBrand)
|
|
8504
|
+
return { kind: "libCall", fn: "fetch.webIs", args: [lowerer.lowerExprExpecting(expr.left, DYN),
|
|
8505
|
+
{ kind: "strLit", value: webBrand, type: STRING, loc }], type: BOOL, loc };
|
|
8506
|
+
if (lowerer.isStdlibGlobal(expr.right, "ReadableStream")) {
|
|
8507
|
+
const value = lowerer.lowerExprExpecting(expr.left, DYN);
|
|
8508
|
+
return { kind: "libCall", fn: "fetch.streamIs", args: [value], type: BOOL, loc };
|
|
8509
|
+
}
|
|
8047
8510
|
// Unknown storage retains each numeric typed array's exact native brand.
|
|
8048
|
-
if (
|
|
8511
|
+
if (!lowerer.caughtLocalOf(expr.left)) {
|
|
8049
8512
|
const elem = Object.keys(BYTES_ELEMENT_NAME).find((kind) => lowerer.isStdlibGlobal(expr.right, BYTES_ELEMENT_NAME[kind]));
|
|
8050
8513
|
if (elem !== undefined) {
|
|
8051
8514
|
const left = lowerer.lowerExpr(expr.left);
|
|
8052
8515
|
if (left.type.kind === "dyn") {
|
|
8053
8516
|
return { kind: "dynTest", test: "bytes", bytesElem: elem, value: left, type: BOOL, loc };
|
|
8054
8517
|
}
|
|
8055
|
-
|
|
8056
|
-
|
|
8057
|
-
kind: "seqExpr", stmts: [{ kind: "exprStmt", expr: left, loc }],
|
|
8058
|
-
result: { kind: "boolLit", value: left.type.elem === elem, type: BOOL, loc }, type: BOOL, loc,
|
|
8059
|
-
};
|
|
8060
|
-
}
|
|
8518
|
+
return { kind: "dynTest", test: "bytes", bytesElem: elem,
|
|
8519
|
+
value: lowerer.coerceInto(expr.left, left, DYN), type: BOOL, loc };
|
|
8061
8520
|
}
|
|
8062
8521
|
}
|
|
8063
8522
|
// `x instanceof RegExp` over a union with a regex arm (the
|
|
@@ -8079,9 +8538,7 @@ export function lowerInstanceOf(lowerer, expr, loc) {
|
|
|
8079
8538
|
return { kind: "unionIsTag", unionId: left.type.unionId, tag, negated: false, value: left, type: BOOL, loc };
|
|
8080
8539
|
}
|
|
8081
8540
|
}
|
|
8082
|
-
|
|
8083
|
-
return { kind: "boolLit", value: true, type: BOOL, loc };
|
|
8084
|
-
}
|
|
8541
|
+
return { kind: "libCall", fn: "dyn.nativeRegexIs", args: [lowerer.coerceInto(expr.left, left, DYN)], type: BOOL, loc };
|
|
8085
8542
|
}
|
|
8086
8543
|
// `x instanceof X` where X is a class VALUE (a classval-typed
|
|
8087
8544
|
// binding): the target is DYNAMIC — a hierarchy target reads its
|
|
@@ -8100,6 +8557,10 @@ export function lowerInstanceOf(lowerer, expr, loc) {
|
|
|
8100
8557
|
lowerer.unsupported("SC1090", expr, "'instanceof' against a class value whose class has no lowering (the class declaration itself was rejected — see its own diagnostic)");
|
|
8101
8558
|
}
|
|
8102
8559
|
const left = lowerer.lowerExpr(expr.left);
|
|
8560
|
+
if (left.type.kind === "dyn") {
|
|
8561
|
+
const classValue = lowerer.lowerExpr(expr.right);
|
|
8562
|
+
return classInstanceOf(lowerer, left, targetInfo, loc, classValue);
|
|
8563
|
+
}
|
|
8103
8564
|
if (left.type.kind !== "object") {
|
|
8104
8565
|
lowerer.unsupported("SC1090", expr, "'instanceof' on values other than class instances (narrow union-typed values first)");
|
|
8105
8566
|
}
|
|
@@ -8124,6 +8585,11 @@ export function lowerInstanceOf(lowerer, expr, loc) {
|
|
|
8124
8585
|
}
|
|
8125
8586
|
lowerer.unsupported("SC1090", expr, "statically-decided 'instanceof' on computed operands (bind the values to variables first)");
|
|
8126
8587
|
}
|
|
8588
|
+
const constructor = tryLowerExpression(lowerer, expr.right);
|
|
8589
|
+
if (constructor?.type.kind === "dyn" && isJsSourceFile(expr.getSourceFile())) {
|
|
8590
|
+
const value = lowerer.coerceInto(expr.left, lowerer.lowerExpr(expr.left), DYN);
|
|
8591
|
+
return checkedClassInstanceOf(lowerer, value, constructor, loc);
|
|
8592
|
+
}
|
|
8127
8593
|
lowerer.unsupported("SC1090", expr.right, "'instanceof' right-hand sides other than classes declared in the program");
|
|
8128
8594
|
}
|
|
8129
8595
|
// A catch binding on the left: the runtime test against the class's
|
|
@@ -8218,7 +8684,8 @@ export function lowerInstanceOf(lowerer, expr, loc) {
|
|
|
8218
8684
|
}
|
|
8219
8685
|
}
|
|
8220
8686
|
if (left.type.kind !== "object") {
|
|
8221
|
-
|
|
8687
|
+
return { kind: "seqExpr", stmts: [{ kind: "exprStmt", expr: left, loc }],
|
|
8688
|
+
result: { kind: "boolLit", value: false, type: BOOL, loc }, type: BOOL, loc };
|
|
8222
8689
|
}
|
|
8223
8690
|
const lhsInfo = lowerer.classes.get(left.type.className);
|
|
8224
8691
|
if (!lhsInfo)
|
|
@@ -8446,9 +8913,9 @@ function lowerInExpression(lowerer, expr, loc) {
|
|
|
8446
8913
|
// `in` shares the coercion).
|
|
8447
8914
|
{
|
|
8448
8915
|
const probed = tryLowerExpression(lowerer, expr.right);
|
|
8449
|
-
if (probed &&
|
|
8916
|
+
if (probed && lowerer.dynConvertible(probed.type)) {
|
|
8450
8917
|
const k = lowerer.lowerExpr(expr.left); // JS order: the key evaluates first
|
|
8451
|
-
if (
|
|
8918
|
+
if (lowerer.dynConvertible(k.type)) {
|
|
8452
8919
|
const boxed = lowerer.coerceToExpected(k, DYN);
|
|
8453
8920
|
const keyLocal = lowerer.declareHiddenLocal("%inKey", DYN);
|
|
8454
8921
|
return { kind: "seqExpr", stmts: [{ kind: "varDecl", localId: keyLocal.id, init: boxed, loc }],
|
|
@@ -8479,7 +8946,9 @@ function lowerInExpression(lowerer, expr, loc) {
|
|
|
8479
8946
|
return { kind: "unionIsTag", unionId: envType.unionId, tag: undefTag, negated: true, value: read, type: BOOL, loc };
|
|
8480
8947
|
}
|
|
8481
8948
|
let recv = lowerer.lowerExpr(expr.right);
|
|
8482
|
-
|
|
8949
|
+
const classInfo = recv.type.kind === "classval" ? lowerer.classes.get(recv.type.className) : undefined;
|
|
8950
|
+
if ((classInfo && (classInfo.callableBase || hasRuntimeStatics(classInfo))) ||
|
|
8951
|
+
(isJsSourceFile(expr.getSourceFile()) && recv.type.kind === "func" && lowerer.dynConvertible(recv.type))) {
|
|
8483
8952
|
recv = lowerer.coerceToExpected(recv, DYN);
|
|
8484
8953
|
}
|
|
8485
8954
|
const siteType = lowerer.mapTypeOf(lowerer.typeOf(expr.right));
|
|
@@ -8728,8 +9197,8 @@ function lowerRuntimeKeyIn(lowerer, expr, loc) {
|
|
|
8728
9197
|
}
|
|
8729
9198
|
return { kind: "call", callee: helper, args: [keyIr, recv], type: BOOL, loc };
|
|
8730
9199
|
}
|
|
8731
|
-
/** A regex literal `/ab+c/gi` → regexLit (
|
|
8732
|
-
*
|
|
9200
|
+
/** A regex literal `/ab+c/gi` → regexLit (fresh state per evaluation,
|
|
9201
|
+
* shared bytecode per (pattern, flags) pair). The TS parser has already syntax-checked the literal;
|
|
8733
9202
|
* what remains here is the flag-alphabet fence (d and v are
|
|
8734
9203
|
* declared-valid TS flags outside this slice). Named capture groups
|
|
8735
9204
|
* `(?<name>...)` and `\k<name>` backreferences compile — libregexp
|
|
@@ -8870,14 +9339,52 @@ function constInitializerOf(lowerer, ident) {
|
|
|
8870
9339
|
* caller keeps its fence), or the groups list (possibly empty — a
|
|
8871
9340
|
* traced regex WITHOUT named groups is the Node-undefined case). */
|
|
8872
9341
|
export function matchResultNamedGroupsOf(lowerer, e) {
|
|
9342
|
+
let producer = e;
|
|
9343
|
+
while (ts.isParenthesizedExpression(producer) || ts.isNonNullExpression(producer))
|
|
9344
|
+
producer = producer.expression;
|
|
9345
|
+
if (ts.isIdentifier(producer)) {
|
|
9346
|
+
const init = constInitializerOf(lowerer, producer);
|
|
9347
|
+
if (init !== null)
|
|
9348
|
+
return matchResultNamedGroupsOf(lowerer, init);
|
|
9349
|
+
}
|
|
8873
9350
|
const reExpr = matchProducerRegexOf(lowerer, e);
|
|
8874
9351
|
if (reExpr === null)
|
|
8875
9352
|
return null;
|
|
9353
|
+
if (ts.isCallExpression(producer) && ts.isPropertyAccessExpression(producer.expression) &&
|
|
9354
|
+
producer.expression.name.text === "match") {
|
|
9355
|
+
const flags = staticRegexFlagsOf(lowerer, reExpr);
|
|
9356
|
+
if (flags === null)
|
|
9357
|
+
return null;
|
|
9358
|
+
// Global String.match returns whole matches without a groups
|
|
9359
|
+
// property, even when the pattern contains named captures.
|
|
9360
|
+
if (flags.includes("g"))
|
|
9361
|
+
return [];
|
|
9362
|
+
}
|
|
8876
9363
|
const pattern = staticRegexPatternOf(lowerer, reExpr);
|
|
8877
9364
|
if (pattern === null)
|
|
8878
9365
|
return null;
|
|
8879
9366
|
return namedCaptureGroupsOfPattern(pattern);
|
|
8880
9367
|
}
|
|
9368
|
+
function staticRegexFlagsOf(lowerer, node) {
|
|
9369
|
+
let expr = node;
|
|
9370
|
+
while (ts.isParenthesizedExpression(expr) || ts.isNonNullExpression(expr))
|
|
9371
|
+
expr = expr.expression;
|
|
9372
|
+
if (ts.isRegularExpressionLiteral(expr))
|
|
9373
|
+
return expr.text.slice(expr.text.lastIndexOf("/") + 1);
|
|
9374
|
+
if (ts.isIdentifier(expr)) {
|
|
9375
|
+
const init = constInitializerOf(lowerer, expr);
|
|
9376
|
+
return init !== null ? staticRegexFlagsOf(lowerer, init) : null;
|
|
9377
|
+
}
|
|
9378
|
+
if (ts.isNewExpression(expr) && ts.isIdentifier(expr.expression) &&
|
|
9379
|
+
lowerer.isStdlibGlobal(expr.expression, "RegExp")) {
|
|
9380
|
+
const flags = expr.arguments?.[1];
|
|
9381
|
+
if (!flags)
|
|
9382
|
+
return "";
|
|
9383
|
+
if (ts.isStringLiteral(flags) || ts.isNoSubstitutionTemplateLiteral(flags))
|
|
9384
|
+
return flags.text;
|
|
9385
|
+
}
|
|
9386
|
+
return null;
|
|
9387
|
+
}
|
|
8881
9388
|
/** The regex EXPRESSION whose match produced `e` (see
|
|
8882
9389
|
* matchResultNamedGroupsOf for the traced shapes). */
|
|
8883
9390
|
function matchProducerRegexOf(lowerer, e) {
|
|
@@ -8967,11 +9474,10 @@ function matchAllRegexOf(lowerer, e) {
|
|
|
8967
9474
|
* member fence);
|
|
8968
9475
|
* - a nullable receiver (the JS `s.match(re).groups` idiom) throws
|
|
8969
9476
|
* Node's exact TypeError on the unit arms;
|
|
8970
|
-
* - every declared name is a key (nonparticipating groups hold
|
|
8971
|
-
*
|
|
9477
|
+
* - every declared name is a key (nonparticipating groups hold undefined,
|
|
9478
|
+
* exactly the slice elements they project);
|
|
8972
9479
|
* - ES2025 duplicate names (distinct alternatives) project the first
|
|
8973
|
-
* participating occurrence
|
|
8974
|
-
* participating-empty "" falls through to slots that are "" anyway;
|
|
9480
|
+
* participating occurrence, including a participating empty string;
|
|
8975
9481
|
* - key order is group declaration order (declaredOrder), Node's own.
|
|
8976
9482
|
* The record has no prototype — Node's groups object is null-prototype,
|
|
8977
9483
|
* observable only through util.inspect's "[Object: null prototype]"
|
|
@@ -9048,12 +9554,8 @@ function matchSliceUnionArms(lowerer, unionId) {
|
|
|
9048
9554
|
}
|
|
9049
9555
|
return { arrTag, units };
|
|
9050
9556
|
}
|
|
9051
|
-
/** The groups-record projection
|
|
9052
|
-
*
|
|
9053
|
-
* record with no declared fields — holding one OVERFLOW entry per
|
|
9054
|
-
* group name in declaration order, so every downstream consumer
|
|
9055
|
-
* (recordKeyGet reads, Object.keys, JSON, the `| undefined` union
|
|
9056
|
-
* 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
|
|
9057
9559
|
* (index list, receiver representation) builds it; see
|
|
9058
9560
|
* lowerMatchGroupsRead for the semantics the body encodes. */
|
|
9059
9561
|
export function lowerGroupsProjection(lowerer, recv, groups, loc) {
|
|
@@ -9071,7 +9573,8 @@ export function lowerGroupsProjection(lowerer, recv, groups, loc) {
|
|
|
9071
9573
|
order.push(g.name);
|
|
9072
9574
|
}
|
|
9073
9575
|
}
|
|
9074
|
-
const
|
|
9576
|
+
const valueType = arrayValueType(lowerer, STRING);
|
|
9577
|
+
const shapeId = lowerer.shapes.intern([], false, valueType);
|
|
9075
9578
|
const recordT = { kind: "record", shapeId };
|
|
9076
9579
|
const recvKey = recv.type.kind === "union" ? recv.type.unionId : "arr";
|
|
9077
9580
|
const key = `regexgroups:${shapeId}:${groups.map((g) => `${g.name}=${g.index}`).join(",")}:${recvKey}`;
|
|
@@ -9113,7 +9616,7 @@ export function lowerGroupsProjection(lowerer, recv, groups, loc) {
|
|
|
9113
9616
|
body.push({ kind: "varDecl", localId: "arr.0", init: narrowed, loc });
|
|
9114
9617
|
mRef = { kind: "varRef", localId: "arr.0", type: arrayOf(STRING), loc };
|
|
9115
9618
|
}
|
|
9116
|
-
const elem = (index) => (
|
|
9619
|
+
const elem = (index) => arrayValueRead(lowerer, mRef, numLit(index, loc), STRING, loc);
|
|
9117
9620
|
const values = new Map();
|
|
9118
9621
|
order.forEach((name, i) => {
|
|
9119
9622
|
const idxs = indicesByName.get(name);
|
|
@@ -9121,16 +9624,16 @@ export function lowerGroupsProjection(lowerer, recv, groups, loc) {
|
|
|
9121
9624
|
values.set(name, elem(idxs[0]));
|
|
9122
9625
|
return;
|
|
9123
9626
|
}
|
|
9124
|
-
// Duplicates: the first
|
|
9125
|
-
//
|
|
9627
|
+
// Duplicates: the first participating occurrence. Empty strings
|
|
9628
|
+
// participate; undefined means this alternative did not match.
|
|
9126
9629
|
const id = `g${i}.0`;
|
|
9127
|
-
locals.push({ id, name: `g${i}`, type:
|
|
9630
|
+
locals.push({ id, name: `g${i}`, type: valueType, mutable: true });
|
|
9128
9631
|
body.push({ kind: "varDecl", localId: id, init: elem(idxs[0]), loc });
|
|
9129
|
-
const gRef = { kind: "varRef", localId: id, type:
|
|
9632
|
+
const gRef = { kind: "varRef", localId: id, type: valueType, loc };
|
|
9130
9633
|
for (const idx of idxs.slice(1)) {
|
|
9131
9634
|
body.push({
|
|
9132
9635
|
kind: "if",
|
|
9133
|
-
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 },
|
|
9134
9637
|
then: [{ kind: "assign", localId: id, value: elem(idx), loc }],
|
|
9135
9638
|
else_: null,
|
|
9136
9639
|
loc,
|
|
@@ -9224,6 +9727,15 @@ export function lowerFieldRead(lowerer, expr) {
|
|
|
9224
9727
|
if (receiverIr?.kind === "record") {
|
|
9225
9728
|
const shape = lowerer.shapes.get(receiverIr.shapeId);
|
|
9226
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
|
+
}
|
|
9227
9739
|
lowerer.unsupported("SC1090", expr, `dot access to index-signature keys (spell it r["${expr.name.text}"] — brackets are the index-signature form)`);
|
|
9228
9740
|
}
|
|
9229
9741
|
}
|
|
@@ -9370,7 +9882,7 @@ export function lowerUnionProperty(lowerer, expr) {
|
|
|
9370
9882
|
// instance argument (e.g. Matrix4.makeTranslation). Preserve primitive
|
|
9371
9883
|
// missing-property semantics and dispatch class data through its native
|
|
9372
9884
|
// capsule instead of demanding a field shared by every union arm.
|
|
9373
|
-
if (isJsSourceFile(expr.getSourceFile()) &&
|
|
9885
|
+
if (isJsSourceFile(expr.getSourceFile()) && lowerer.dynConvertible(value.type)) {
|
|
9374
9886
|
return { kind: "dynKeyGet", value: lowerer.coerceToExpected(value, DYN), key,
|
|
9375
9887
|
...(hasOptionalChainGuard(expr.expression) ? { optional: true } : {}), type: DYN, loc: locOf(expr) };
|
|
9376
9888
|
}
|
|
@@ -9504,6 +10016,10 @@ function lowerUnionKeyedRead(lowerer, expr, unionId, value, key, literalField) {
|
|
|
9504
10016
|
* or accessor dispatch. Resolve the property from the represented class;
|
|
9505
10017
|
* the optional checker view must not turn its getter into a record read. */
|
|
9506
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
|
+
}
|
|
9507
10023
|
const info = lowerer.classes.get(receiverType.className);
|
|
9508
10024
|
if (!info) {
|
|
9509
10025
|
// A receiver typed as a class whose collection deferred: the
|
|
@@ -9513,14 +10029,14 @@ function classFieldTarget(lowerer, receiverNode, receiverType, fieldName, saved)
|
|
|
9513
10029
|
}
|
|
9514
10030
|
const fieldType = info.fields.get(fieldName);
|
|
9515
10031
|
const lowerObjectReceiver = () => {
|
|
9516
|
-
let obj =
|
|
10032
|
+
let obj = represented;
|
|
9517
10033
|
if (obj.type.kind === "union") {
|
|
9518
10034
|
const helper = lowerer.narrowedArmHelper(obj.type.unionId, receiverType, locOf(receiverNode));
|
|
9519
10035
|
obj = helper
|
|
9520
10036
|
? { kind: "call", callee: helper, args: [obj], type: receiverType, loc: locOf(receiverNode) }
|
|
9521
10037
|
: lowerer.maybeNarrow(obj, receiverNode);
|
|
9522
10038
|
}
|
|
9523
|
-
return
|
|
10039
|
+
return lowerer.coerceInto(receiverNode, obj, receiverType);
|
|
9524
10040
|
};
|
|
9525
10041
|
if (fieldName === "cause" && receiverType.className !== "%DOMException") {
|
|
9526
10042
|
let root = info;
|
|
@@ -9532,6 +10048,11 @@ function classFieldTarget(lowerer, receiverNode, receiverType, fieldName, saved)
|
|
|
9532
10048
|
}
|
|
9533
10049
|
if (fieldType) {
|
|
9534
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
|
+
}
|
|
9535
10056
|
return { container: "class", obj, className: receiverType.className, field: fieldName, fieldType };
|
|
9536
10057
|
}
|
|
9537
10058
|
// Accessor property: either half declared anywhere on the chain
|
|
@@ -9549,6 +10070,12 @@ function classFieldTarget(lowerer, receiverNode, receiverType, fieldName, saved)
|
|
|
9549
10070
|
fieldType: getF ? getF.sig.ret : setF.sig.params[0].type,
|
|
9550
10071
|
};
|
|
9551
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
|
+
}
|
|
9552
10079
|
return null;
|
|
9553
10080
|
}
|
|
9554
10081
|
/** A structural assertion can leave the value in its original class
|
|
@@ -9574,6 +10101,8 @@ function representedClassFieldTarget(lowerer, receiverNode, fieldName, value) {
|
|
|
9574
10101
|
* receivers) a declared accessor property. Returns the pieces of a
|
|
9575
10102
|
* fieldSet/recordSet/accessor-call (minus value/kind) or null. */
|
|
9576
10103
|
export function fieldTarget(lowerer, access) {
|
|
10104
|
+
if (lowerer.cjsLocalModuleBindingOf(access.expression))
|
|
10105
|
+
return null;
|
|
9577
10106
|
if (lowerer.chainBlocked(access))
|
|
9578
10107
|
return null;
|
|
9579
10108
|
if (access.name.text === "stackTraceLimit" && lowerer.isStdlibGlobal(access.expression, "Error")) {
|
|
@@ -9581,7 +10110,7 @@ export function fieldTarget(lowerer, access) {
|
|
|
9581
10110
|
}
|
|
9582
10111
|
const stored = ts.isIdentifier(access.expression)
|
|
9583
10112
|
? lowerer.peekLocal(access.expression)?.type ?? lowerer.globalOf(access.expression)?.type : undefined;
|
|
9584
|
-
const receiverIr = stored?.kind === "record" ? stored : lowerer.mapTypeOf(lowerer.typeOf(access.expression));
|
|
10113
|
+
const receiverIr = stored?.kind === "record" || stored?.kind === "object" ? stored : lowerer.mapTypeOf(lowerer.typeOf(access.expression));
|
|
9585
10114
|
if (receiverIr?.kind === "object") {
|
|
9586
10115
|
return classFieldTarget(lowerer, access.expression, receiverIr, access.name.text);
|
|
9587
10116
|
}
|
|
@@ -9688,8 +10217,17 @@ export function fieldTarget(lowerer, access) {
|
|
|
9688
10217
|
return declaredArms.every((a) => ivArms.some((b) => typeEquals(a, b)));
|
|
9689
10218
|
};
|
|
9690
10219
|
if (!nameSym || nameSym.name === ts.InternalSymbolName.Index || canonicalized()) {
|
|
9691
|
-
|
|
9692
|
-
|
|
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 };
|
|
9693
10231
|
}
|
|
9694
10232
|
}
|
|
9695
10233
|
return null;
|
|
@@ -9787,6 +10325,13 @@ function symbolFieldTarget(lowerer, expr) {
|
|
|
9787
10325
|
* tsc-clean (the property types as the setter's param), but Node yields
|
|
9788
10326
|
* undefined, which these property types cannot represent. */
|
|
9789
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
|
+
};
|
|
10333
|
+
if (target.container === "class" && (target.field === "message" || target.field === "name") && (target.className === "%Error" || lowerer.isSubclassOf(target.className, "%Error")))
|
|
10334
|
+
return errorPropertyRead(lowerer, target.obj, target.field);
|
|
9790
10335
|
if (target.container === "errorStackLimit") {
|
|
9791
10336
|
return { kind: "libCall", fn: "error.stackLimitGet", args: [], type: F64, loc };
|
|
9792
10337
|
}
|
|
@@ -9812,6 +10357,11 @@ export function fieldGetExpr(lowerer, target, loc, blame) {
|
|
|
9812
10357
|
if (!getF) {
|
|
9813
10358
|
lowerer.unsupported("SC1090", blame, `reading a property that has only a setter ('${target.field}' — Node would yield undefined)`);
|
|
9814
10359
|
}
|
|
10360
|
+
if (!target.field.startsWith("#") && isJsSourceFile(blame.getSourceFile()) && lowerer.dynConvertible(getF.sig.ret)) {
|
|
10361
|
+
const value = { kind: "dynKeyGet", value: lowerer.coerceToExpected(target.obj, DYN),
|
|
10362
|
+
key: { kind: "strLit", value: target.field, type: STRING, loc }, type: DYN, loc };
|
|
10363
|
+
return lowerer.coerceToExpected(value, getF.sig.ret);
|
|
10364
|
+
}
|
|
9815
10365
|
return lowerer.accessorCall(target.className, `get:${target.field}`, target.obj, [], getF.sig.ret, loc);
|
|
9816
10366
|
}
|
|
9817
10367
|
// Record accessor properties: the read IS a call of the %get: closure
|
|
@@ -9889,6 +10439,13 @@ export function fieldGetExpr(lowerer, target, loc, blame) {
|
|
|
9889
10439
|
* call). A write to a getter-only property never gets here in a clean
|
|
9890
10440
|
* program (tsc's TS2540 is the fence); the rejection is the backstop. */
|
|
9891
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
|
+
};
|
|
10447
|
+
if (target.container === "class" && (target.field === "message" || target.field === "name") && (target.className === "%Error" || lowerer.isSubclassOf(target.className, "%Error")))
|
|
10448
|
+
return errorPropertyWrite(lowerer, target.obj, value, target.field);
|
|
9892
10449
|
if (target.container === "errorStackLimit") {
|
|
9893
10450
|
return { kind: "exprStmt", expr: { kind: "libCall", fn: "error.stackLimitSet", args: [value], type: VOID, loc }, loc };
|
|
9894
10451
|
}
|
|
@@ -9996,13 +10553,13 @@ export function lowerPromiseRejectCall(lowerer, call, access) {
|
|
|
9996
10553
|
// identity survives to catch/unhandledRejection observers), and the
|
|
9997
10554
|
// result is promise<dyn> when no context names a concrete one.
|
|
9998
10555
|
const reason = lowerer.lowerExpr(reasonNode);
|
|
9999
|
-
if (reason.type.kind === "dyn") {
|
|
10556
|
+
if (reason.type.kind === "dyn" || lowerer.dynConvertible(reason.type) && reason.type.kind !== "object") {
|
|
10000
10557
|
// The result only ever REJECTS, so the inner type is unobservable:
|
|
10001
10558
|
// adopt the context's promise type when one names it, else the
|
|
10002
10559
|
// checker's own Promise<never> reading (promise<void> — exactly the
|
|
10003
10560
|
// Error-reason form below).
|
|
10004
10561
|
const resultT = type?.kind === "promise" ? type : { kind: "promise", inner: VOID };
|
|
10005
|
-
return { kind: "intrinsic", name: "promise.reject", args: [reason], type: resultT, loc };
|
|
10562
|
+
return { kind: "intrinsic", name: "promise.reject", args: [lowerer.coerceInto(reasonNode, reason, DYN)], type: resultT, loc };
|
|
10006
10563
|
}
|
|
10007
10564
|
// An Error reason with NO context-named promise type (a bare
|
|
10008
10565
|
// top-level `Promise.reject(new Error())` — the unhandled-rejection
|
|
@@ -10054,7 +10611,7 @@ export function lowerPromiseAllTupleCall(lowerer, call, access) {
|
|
|
10054
10611
|
const loc = locOf(call);
|
|
10055
10612
|
const inner = first.inner;
|
|
10056
10613
|
const entryT = { kind: "promise", inner };
|
|
10057
|
-
const elems = argNode.elements.map((el) => lowerer.
|
|
10614
|
+
const elems = argNode.elements.map((el) => lowerer.lowerExprExpecting(el, entryT));
|
|
10058
10615
|
const entries = { kind: "arrayLit", elems, type: arrayOf(entryT), loc };
|
|
10059
10616
|
const type = inner.kind === "void"
|
|
10060
10617
|
? { kind: "promise", inner: VOID }
|
|
@@ -10086,7 +10643,10 @@ function lowerFieldCompoundValue(lowerer, access, op, rhsNode, loc) {
|
|
|
10086
10643
|
// NaN — SEMANTICS.md), combine, write back (dyn.keySet). The
|
|
10087
10644
|
// receiver and member read are saved before evaluating the RHS.
|
|
10088
10645
|
if (ts.isPropertyAccessExpression(access)) {
|
|
10089
|
-
|
|
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);
|
|
10090
10650
|
if (probed?.type.kind === "dyn") {
|
|
10091
10651
|
const receiver = save(probed, "%compoundReceiver");
|
|
10092
10652
|
const key = { kind: "strLit", value: access.name.text, type: STRING, loc: locOf(access.name) };
|
|
@@ -10095,6 +10655,14 @@ function lowerFieldCompoundValue(lowerer, access, op, rhsNode, loc) {
|
|
|
10095
10655
|
const rhs = save(rhsNode
|
|
10096
10656
|
? lowerer.lowerExpr(rhsNode)
|
|
10097
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
|
+
}
|
|
10098
10666
|
const numericRhs = rhs.type.kind === "dyn"
|
|
10099
10667
|
? { kind: "dynCheck", value: rhs, type: F64, loc: rhs.loc }
|
|
10100
10668
|
: lowerOptionalNumber(lowerer, rhs, loc);
|
|
@@ -10132,35 +10700,12 @@ function lowerFieldCompoundValue(lowerer, access, op, rhsNode, loc) {
|
|
|
10132
10700
|
value = { kind: "bin", op, left: read, right: numericRhs, type: F64, loc };
|
|
10133
10701
|
}
|
|
10134
10702
|
else if (target.fieldType.kind === "dyn" &&
|
|
10135
|
-
isJsSourceFile(access.getSourceFile())
|
|
10136
|
-
|
|
10137
|
-
|
|
10138
|
-
|
|
10139
|
-
|
|
10140
|
-
|
|
10141
|
-
// ToNumber; SEMANTICS.md) and computes natively; the result boxes
|
|
10142
|
-
// back into the field's dyn slot. `+=` with a string RHS is the
|
|
10143
|
-
// string-context read: check to string, concat, box back.
|
|
10144
|
-
const checkNum = (e) => e.type.kind === "dyn" ? { kind: "dynCheck", value: e, type: F64, loc: e.loc } : e;
|
|
10145
|
-
if (op === "+" && rhs.type.kind === "string") {
|
|
10146
|
-
const concat = {
|
|
10147
|
-
kind: "strConcat",
|
|
10148
|
-
// String-context read: JS's String(unknown) over the field —
|
|
10149
|
-
// the JS-exact dyn walker, never a checked cast.
|
|
10150
|
-
left: { kind: "toString", operand: read, type: STRING, loc },
|
|
10151
|
-
right: rhs,
|
|
10152
|
-
type: STRING,
|
|
10153
|
-
loc,
|
|
10154
|
-
};
|
|
10155
|
-
value = { kind: "dynFrom", value: concat, type: DYN, loc };
|
|
10156
|
-
}
|
|
10157
|
-
else if (rhs.type.kind === "f64" || rhs.type.kind === "dyn") {
|
|
10158
|
-
const bin = { kind: "bin", op, left: checkNum(read), right: checkNum(rhs), type: F64, loc };
|
|
10159
|
-
value = { kind: "dynFrom", value: bin, type: DYN, loc };
|
|
10160
|
-
}
|
|
10161
|
-
else {
|
|
10162
|
-
lowerer.unsupported("SC1043", access);
|
|
10163
|
-
}
|
|
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 };
|
|
10164
10709
|
}
|
|
10165
10710
|
else {
|
|
10166
10711
|
lowerer.unsupported("SC1043", access);
|
|
@@ -10193,8 +10738,8 @@ function lowerStreamObjectProperty(lowerer, expr) {
|
|
|
10193
10738
|
* Interning makes every reference under one ABI the SAME immortal closure,
|
|
10194
10739
|
* so `opt.type === String` compares like JS function identity. */
|
|
10195
10740
|
function primitiveCtorClosure(lowerer, name, loc, dynamicInput) {
|
|
10196
|
-
const ret = name === "String" ? STRING : name === "Number" ? F64 : BOOL;
|
|
10197
|
-
const input = dynamicInput ? DYN : STRING;
|
|
10741
|
+
const ret = name === "String" ? STRING : name === "Number" ? F64 : name === "BigInt" ? BIGINT_T : BOOL;
|
|
10742
|
+
const input = dynamicInput || name === "BigInt" ? DYN : STRING;
|
|
10198
10743
|
const fnT = funcOf([input], ret);
|
|
10199
10744
|
const key = `${name}:${dynamicInput ? "dyn" : "string"}`;
|
|
10200
10745
|
let fnName = lowerer.primitiveCtorFns.get(key);
|
|
@@ -10203,16 +10748,20 @@ function primitiveCtorClosure(lowerer, name, loc, dynamicInput) {
|
|
|
10203
10748
|
lowerer.primitiveCtorFns.set(key, fnName);
|
|
10204
10749
|
const v = { kind: "varRef", localId: "v.0", type: input, loc };
|
|
10205
10750
|
const value = dynamicInput
|
|
10206
|
-
? name === "
|
|
10207
|
-
? { kind: "libCall", fn: "dyn.
|
|
10208
|
-
: name === "
|
|
10209
|
-
? { kind: "libCall", fn: "dyn.
|
|
10210
|
-
:
|
|
10211
|
-
|
|
10212
|
-
|
|
10213
|
-
|
|
10214
|
-
|
|
10215
|
-
|
|
10751
|
+
? name === "BigInt"
|
|
10752
|
+
? { kind: "libCall", fn: "dyn.bigintConstructor", args: [v], type: BIGINT_T, loc }
|
|
10753
|
+
: name === "String"
|
|
10754
|
+
? { kind: "libCall", fn: "dyn.toStringCoerce", args: [v], type: STRING, loc }
|
|
10755
|
+
: name === "Number"
|
|
10756
|
+
? { kind: "libCall", fn: "dyn.numberConstructor", args: [v], type: F64, loc }
|
|
10757
|
+
: { kind: "dynTest", test: "truthy", value: v, type: BOOL, loc }
|
|
10758
|
+
: name === "BigInt"
|
|
10759
|
+
? { kind: "libCall", fn: "dyn.bigintConstructor", args: [v], type: BIGINT_T, loc }
|
|
10760
|
+
: name === "String"
|
|
10761
|
+
? v
|
|
10762
|
+
: name === "Number"
|
|
10763
|
+
? { kind: "libCall", fn: "num.fromString", args: [v], type: F64, loc }
|
|
10764
|
+
: { kind: "strEq", negated: true, left: v, right: { kind: "strLit", value: "", type: STRING, loc }, type: BOOL, loc };
|
|
10216
10765
|
const fn = {
|
|
10217
10766
|
name: fnName,
|
|
10218
10767
|
params: [{ localId: "v.0", name: "value", type: input }],
|
|
@@ -10225,4 +10774,25 @@ function primitiveCtorClosure(lowerer, name, loc, dynamicInput) {
|
|
|
10225
10774
|
}
|
|
10226
10775
|
return { kind: "closure", fnName, captures: [], type: fnT, loc };
|
|
10227
10776
|
}
|
|
10777
|
+
/** Drain using checked dispatch so native class and generator iterators keep their methods. */
|
|
10778
|
+
function checkedIteratorPack(lowerer, source, loc) {
|
|
10779
|
+
const name = "%iterator.pack.checked";
|
|
10780
|
+
if (!lowerer.liftedFns.some((fn) => fn.name === name)) {
|
|
10781
|
+
const ref = (id) => ({ kind: "varRef", localId: id, type: DYN, loc });
|
|
10782
|
+
const property = (key) => ({ kind: "dynKeyGet", value: ref("step"), key: { kind: "strLit", value: key, type: STRING, loc }, type: DYN, loc });
|
|
10783
|
+
lowerer.liftedFns.push({ name, params: [{ localId: "source", name: "source", type: DYN }], returnType: DYN,
|
|
10784
|
+
locals: ["source", "iterator", "step", "result"].map((id) => ({ id, name: id, type: DYN, mutable: id !== "source" })),
|
|
10785
|
+
body: [
|
|
10786
|
+
{ kind: "varDecl", localId: "iterator", init: { kind: "libCall", fn: "dyn.iterator", args: [ref("source"), { kind: "strLit", value: "value", type: STRING, loc }], type: DYN, loc }, loc },
|
|
10787
|
+
{ kind: "varDecl", localId: "result", init: { kind: "dynArrLit", elems: [], type: DYN, loc }, loc },
|
|
10788
|
+
{ kind: "while", cond: { kind: "boolLit", value: true, type: BOOL, loc }, body: [
|
|
10789
|
+
{ kind: "varDecl", localId: "step", init: { kind: "dynInvoke", recv: ref("iterator"), method: "next", calleeName: "iterator.next", args: [], type: DYN, loc }, loc },
|
|
10790
|
+
{ kind: "if", cond: { kind: "dynTest", test: "truthy", value: property("done"), type: BOOL, loc }, then: [{ kind: "return", value: ref("result"), loc }], else_: null, loc },
|
|
10791
|
+
{ kind: "exprStmt", expr: { kind: "libCall", fn: "dyn.packPush", args: [ref("result"), property("value")], type: VOID, loc }, loc },
|
|
10792
|
+
], loc },
|
|
10793
|
+
{ kind: "return", value: ref("result"), loc },
|
|
10794
|
+
], loc });
|
|
10795
|
+
}
|
|
10796
|
+
return { kind: "call", callee: name, args: [source], type: DYN, loc };
|
|
10797
|
+
}
|
|
10228
10798
|
//# sourceMappingURL=lower-exprs.js.map
|