@scriptc/compiler 0.0.36 → 0.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/ambient/scriptc-node-fallback.d.ts +52 -22
- package/ambient/scriptc-overrides.d.ts +0 -7
- package/ambient/scriptc.d.ts +3 -2
- package/dist/backend/build-cache.js.map +1 -1
- package/dist/backend/c/async.js +24 -2
- package/dist/backend/c/async.js.map +1 -1
- package/dist/backend/c/c-emitter.d.ts +22 -10
- package/dist/backend/c/c-emitter.js +60 -15
- package/dist/backend/c/c-emitter.js.map +1 -1
- package/dist/backend/c/exprs.js +289 -55
- package/dist/backend/c/exprs.js.map +1 -1
- package/dist/backend/c/island.js.map +1 -1
- package/dist/backend/c/may-throw.js +3 -0
- package/dist/backend/c/may-throw.js.map +1 -1
- package/dist/backend/c/stmts.d.ts +3 -5
- package/dist/backend/c/stmts.js +76 -17
- package/dist/backend/c/stmts.js.map +1 -1
- package/dist/backend/c/types.js.map +1 -1
- package/dist/backend/c/walkers.js +36 -12
- package/dist/backend/c/walkers.js.map +1 -1
- package/dist/backend/external-c.d.ts +33 -0
- package/dist/backend/external-c.js +51 -0
- package/dist/backend/external-c.js.map +1 -0
- package/dist/backend/link-plan.d.ts +37 -0
- package/dist/backend/link-plan.js +26 -0
- package/dist/backend/link-plan.js.map +1 -0
- package/dist/backend/linker.d.ts +22 -0
- package/dist/backend/linker.js +236 -0
- package/dist/backend/linker.js.map +1 -0
- package/dist/backend/llvm/blocks.js.map +1 -1
- package/dist/backend/llvm/dyn.js +63 -3
- package/dist/backend/llvm/dyn.js.map +1 -1
- package/dist/backend/llvm/emitter.js +132 -26
- package/dist/backend/llvm/emitter.js.map +1 -1
- package/dist/backend/llvm/expr-async.js +85 -3
- package/dist/backend/llvm/expr-async.js.map +1 -1
- package/dist/backend/llvm/expr-bytes.js.map +1 -1
- package/dist/backend/llvm/expr-calls.js.map +1 -1
- package/dist/backend/llvm/expr-containers.js +68 -54
- package/dist/backend/llvm/expr-containers.js.map +1 -1
- package/dist/backend/llvm/expr-context.d.ts +2 -1
- package/dist/backend/llvm/expr-control.js.map +1 -1
- package/dist/backend/llvm/expr-dispatch.js +2 -0
- package/dist/backend/llvm/expr-dispatch.js.map +1 -1
- package/dist/backend/llvm/expr-dynamic.js +6 -3
- package/dist/backend/llvm/expr-dynamic.js.map +1 -1
- package/dist/backend/llvm/expr-island.js.map +1 -1
- package/dist/backend/llvm/expr-primitives.d.ts +1 -1
- package/dist/backend/llvm/expr-primitives.js +20 -0
- package/dist/backend/llvm/expr-primitives.js.map +1 -1
- package/dist/backend/llvm/expr-records.js.map +1 -1
- package/dist/backend/llvm/expr-stream-bridges.js +245 -10
- package/dist/backend/llvm/expr-stream-bridges.js.map +1 -1
- package/dist/backend/llvm/lib-dispatch.js +2 -0
- package/dist/backend/llvm/lib-dispatch.js.map +1 -1
- package/dist/backend/llvm/lib-shared.js +18 -0
- package/dist/backend/llvm/lib-shared.js.map +1 -1
- package/dist/backend/llvm/lib-stream.js +6 -3
- package/dist/backend/llvm/lib-stream.js.map +1 -1
- package/dist/backend/llvm/split.js.map +1 -1
- package/dist/backend/llvm/unsupported.js.map +1 -1
- package/dist/backend/llvm/walkers.js +31 -4
- package/dist/backend/llvm/walkers.js.map +1 -1
- package/dist/backend/native-codegen.d.ts +8 -1
- package/dist/backend/native-codegen.js +39 -25
- package/dist/backend/native-codegen.js.map +1 -1
- package/dist/backend/native-codegen.test.js +44 -4
- package/dist/backend/native-codegen.test.js.map +1 -1
- package/dist/backend/native-link-info.d.ts +1 -1
- package/dist/backend/native-link-info.js +42 -18
- package/dist/backend/native-link-info.js.map +1 -1
- package/dist/backend/native-toolchain.js.map +1 -1
- package/dist/backend/native-toolchain.test.js +5 -1
- package/dist/backend/native-toolchain.test.js.map +1 -1
- package/dist/backend/runtime-pack-targets.test.d.ts +1 -0
- package/dist/backend/runtime-pack-targets.test.js +54 -0
- package/dist/backend/runtime-pack-targets.test.js.map +1 -0
- package/dist/backend/runtime-pack.d.ts +5 -30
- package/dist/backend/runtime-pack.js +46 -141
- package/dist/backend/runtime-pack.js.map +1 -1
- package/dist/backend/runtime-pack.test.js +49 -14
- package/dist/backend/runtime-pack.test.js.map +1 -1
- package/dist/backend/targets.d.ts +63 -17
- package/dist/backend/targets.js +251 -35
- package/dist/backend/targets.js.map +1 -1
- package/dist/backend/targets.test.js +27 -5
- package/dist/backend/targets.test.js.map +1 -1
- package/dist/backend/vendor-archives.js.map +1 -1
- package/dist/compat/fetch-profile.js +4 -2
- package/dist/compat/fetch-profile.js.map +1 -1
- package/dist/coverage/report.js.map +1 -1
- package/dist/coverage/surface-manifest.js +22 -1
- package/dist/coverage/surface-manifest.js.map +1 -1
- package/dist/diagnostics/diagnostic.d.ts +6 -5
- package/dist/diagnostics/diagnostic.js +7 -6
- package/dist/diagnostics/diagnostic.js.map +1 -1
- package/dist/diagnostics/render.js.map +1 -1
- package/dist/ffi/ffi-manifest.js.map +1 -1
- package/dist/frontend/cjs-lexer.js.map +1 -1
- package/dist/frontend/dts-paths.js.map +1 -1
- package/dist/frontend/input-tracker.d.ts +10 -0
- package/dist/frontend/input-tracker.js +26 -3
- package/dist/frontend/input-tracker.js.map +1 -1
- package/dist/frontend/input-tracker.test.js +8 -1
- package/dist/frontend/input-tracker.test.js.map +1 -1
- package/dist/frontend/lib-contract.js.map +1 -1
- package/dist/frontend/lowering/array-values.d.ts +12 -0
- package/dist/frontend/lowering/array-values.js +76 -0
- package/dist/frontend/lowering/array-values.js.map +1 -0
- package/dist/frontend/lowering/callback-arg.js.map +1 -1
- package/dist/frontend/lowering/containers/bytes.d.ts +35 -0
- package/dist/frontend/lowering/containers/bytes.js +1021 -0
- package/dist/frontend/lowering/containers/bytes.js.map +1 -0
- package/dist/frontend/lowering/containers/string-and-regexp.d.ts +17 -0
- package/dist/frontend/lowering/containers/string-and-regexp.js +345 -0
- package/dist/frontend/lowering/containers/string-and-regexp.js.map +1 -0
- package/dist/frontend/lowering/expressions/evaluation-safety.d.ts +13 -0
- package/dist/frontend/lowering/expressions/evaluation-safety.js +76 -0
- package/dist/frontend/lowering/expressions/evaluation-safety.js.map +1 -0
- package/dist/frontend/lowering/expressions/object-literals.d.ts +44 -0
- package/dist/frontend/lowering/expressions/object-literals.js +1815 -0
- package/dist/frontend/lowering/expressions/object-literals.js.map +1 -0
- package/dist/frontend/lowering/expressions/optional-chains.d.ts +31 -0
- package/dist/frontend/lowering/expressions/optional-chains.js +382 -0
- package/dist/frontend/lowering/expressions/optional-chains.js.map +1 -0
- package/dist/frontend/lowering/expressions/try-lower-expression.d.ts +7 -0
- package/dist/frontend/lowering/expressions/try-lower-expression.js +26 -0
- package/dist/frontend/lowering/expressions/try-lower-expression.js.map +1 -0
- package/dist/frontend/lowering/lib-boundary.js +7 -2
- package/dist/frontend/lowering/lib-boundary.js.map +1 -1
- package/dist/frontend/lowering/lower-array-sort.d.ts +22 -0
- package/dist/frontend/lowering/lower-array-sort.js +619 -0
- package/dist/frontend/lowering/lower-array-sort.js.map +1 -0
- package/dist/frontend/lowering/lower-assert.js.map +1 -1
- package/dist/frontend/lowering/lower-async-iteration.d.ts +7 -0
- package/dist/frontend/lowering/lower-async-iteration.js +657 -0
- package/dist/frontend/lowering/lower-async-iteration.js.map +1 -0
- package/dist/frontend/lowering/lower-builtins.d.ts +18 -0
- package/dist/frontend/lowering/lower-builtins.js +379 -48
- package/dist/frontend/lowering/lower-builtins.js.map +1 -1
- package/dist/frontend/lowering/lower-calls.d.ts +16 -8
- package/dist/frontend/lowering/lower-calls.js +667 -91
- package/dist/frontend/lowering/lower-calls.js.map +1 -1
- package/dist/frontend/lowering/lower-classes.d.ts +7 -13
- package/dist/frontend/lowering/lower-classes.js +128 -35
- package/dist/frontend/lowering/lower-classes.js.map +1 -1
- package/dist/frontend/lowering/lower-comptime.js.map +1 -1
- package/dist/frontend/lowering/lower-containers.d.ts +1 -47
- package/dist/frontend/lowering/lower-containers.js +779 -2038
- package/dist/frontend/lowering/lower-containers.js.map +1 -1
- package/dist/frontend/lowering/lower-event-emitter.js +51 -5
- package/dist/frontend/lowering/lower-event-emitter.js.map +1 -1
- package/dist/frontend/lowering/lower-exprs.d.ts +4 -78
- package/dist/frontend/lowering/lower-exprs.js +1128 -2409
- package/dist/frontend/lowering/lower-exprs.js.map +1 -1
- package/dist/frontend/lowering/lower-generators.d.ts +18 -3
- package/dist/frontend/lowering/lower-generators.js +145 -9
- package/dist/frontend/lowering/lower-generators.js.map +1 -1
- package/dist/frontend/lowering/lower-inspect.js +50 -36
- package/dist/frontend/lowering/lower-inspect.js.map +1 -1
- package/dist/frontend/lowering/lower-island.d.ts +11 -4
- package/dist/frontend/lowering/lower-island.js +47 -10
- package/dist/frontend/lowering/lower-island.js.map +1 -1
- package/dist/frontend/lowering/lower-modules.js +52 -15
- package/dist/frontend/lowering/lower-modules.js.map +1 -1
- package/dist/frontend/lowering/lower-node-module.d.ts +18 -0
- package/dist/frontend/lowering/lower-node-module.js +302 -0
- package/dist/frontend/lowering/lower-node-module.js.map +1 -0
- package/dist/frontend/lowering/lower-server.js +11 -1
- package/dist/frontend/lowering/lower-server.js.map +1 -1
- package/dist/frontend/lowering/lower-stmts.d.ts +5 -24
- package/dist/frontend/lowering/lower-stmts.js +811 -314
- package/dist/frontend/lowering/lower-stmts.js.map +1 -1
- package/dist/frontend/lowering/lower-stream.js +4 -4
- package/dist/frontend/lowering/lower-stream.js.map +1 -1
- package/dist/frontend/lowering/lowerer.d.ts +109 -34
- package/dist/frontend/lowering/lowerer.js +1225 -62
- package/dist/frontend/lowering/lowerer.js.map +1 -1
- package/dist/frontend/lowering/optional-arguments.d.ts +9 -0
- package/dist/frontend/lowering/optional-arguments.js +57 -0
- package/dist/frontend/lowering/optional-arguments.js.map +1 -0
- package/dist/frontend/lowering/surfaces.d.ts +10 -2
- package/dist/frontend/lowering/surfaces.js +15 -3
- package/dist/frontend/lowering/surfaces.js.map +1 -1
- package/dist/frontend/npm-static-declarations.d.ts +35 -0
- package/dist/frontend/npm-static-declarations.js +506 -0
- package/dist/frontend/npm-static-declarations.js.map +1 -0
- package/dist/frontend/npm-static-declarations.test.d.ts +1 -0
- package/dist/frontend/npm-static-declarations.test.js +122 -0
- package/dist/frontend/npm-static-declarations.test.js.map +1 -0
- package/dist/frontend/npm-static.d.ts +2 -0
- package/dist/frontend/npm-static.js +26 -10
- package/dist/frontend/npm-static.js.map +1 -1
- package/dist/frontend/npm.d.ts +60 -0
- package/dist/frontend/npm.js +38 -4
- package/dist/frontend/npm.js.map +1 -1
- package/dist/frontend/npm.test.d.ts +1 -0
- package/dist/frontend/npm.test.js +50 -0
- package/dist/frontend/npm.test.js.map +1 -0
- package/dist/frontend/program.d.ts +3 -2
- package/dist/frontend/program.js +495 -68
- package/dist/frontend/program.js.map +1 -1
- package/dist/frontend/resolve.d.ts +10 -2
- package/dist/frontend/resolve.js +90 -14
- package/dist/frontend/resolve.js.map +1 -1
- package/dist/frontend/resolve.test.js +4 -1
- package/dist/frontend/resolve.test.js.map +1 -1
- package/dist/frontend/runtime-resolve.d.ts +22 -0
- package/dist/frontend/runtime-resolve.js +101 -0
- package/dist/frontend/runtime-resolve.js.map +1 -0
- package/dist/frontend/runtime-resolve.test.d.ts +1 -0
- package/dist/frontend/runtime-resolve.test.js +18 -0
- package/dist/frontend/runtime-resolve.test.js.map +1 -0
- package/dist/frontend/ts7/adapter.js +12 -13
- package/dist/frontend/ts7/adapter.js.map +1 -1
- package/dist/frontend/ts7/ast.js.map +1 -1
- package/dist/frontend/ts7/census-check.js +3 -3
- package/dist/frontend/ts7/census-check.js.map +1 -1
- package/dist/frontend/ts7/checker.d.ts +3 -0
- package/dist/frontend/ts7/checker.js +12 -3
- package/dist/frontend/ts7/checker.js.map +1 -1
- package/dist/frontend/ts7/enums.js.map +1 -1
- package/dist/frontend/ts7/program-adapter.js +42 -3
- package/dist/frontend/ts7/program-adapter.js.map +1 -1
- package/dist/frontend/type-mapper.js +120 -7
- package/dist/frontend/type-mapper.js.map +1 -1
- package/dist/index.d.ts +3 -3
- package/dist/index.js +50 -28
- package/dist/index.js.map +1 -1
- package/dist/ir/analysis.js.map +1 -1
- package/dist/ir/ir.d.ts +125 -41
- package/dist/ir/ir.js +82 -31
- package/dist/ir/ir.js.map +1 -1
- package/dist/ir/serialize.d.ts +1 -1
- package/dist/ir/serialize.js +1 -1
- package/dist/ir/validate.js +127 -95
- package/dist/ir/validate.js.map +1 -1
- package/dist/library/cache-primitives.js.map +1 -1
- package/dist/library/cache-reset.test.js.map +1 -1
- package/dist/library/compiler-self-identity.js.map +1 -1
- package/dist/library/fence-eval.js +3 -1
- package/dist/library/fence-eval.js.map +1 -1
- package/dist/library/int-infer.js +12 -1
- package/dist/library/int-infer.js.map +1 -1
- package/dist/library/int-infer.test.js +4 -4
- package/dist/library/int-infer.test.js.map +1 -1
- package/dist/library/library-cache.test.js +5 -5
- package/dist/library/library-profile.js.map +1 -1
- package/dist/library/sidecar-validate.js.map +1 -1
- package/dist/library/sidecar.js.map +1 -1
- package/dist/startup-cache.d.ts +9 -1
- package/dist/startup-cache.js +16 -1
- package/dist/startup-cache.js.map +1 -1
- package/dist/wasi-paths.js.map +1 -1
- package/package.json +18 -5
- package/surface-manifest.json +64 -8
|
@@ -15,22 +15,24 @@ import { InternalCompilerError } from "../../errors.js";
|
|
|
15
15
|
*/
|
|
16
16
|
import { resolve } from "node:path";
|
|
17
17
|
import { tsgoPath } from "../dts-paths.js";
|
|
18
|
-
import { isRelativeSpecifier } from "../workspace-registry.js";
|
|
19
18
|
import * as ts from "../ts7/adapter.js";
|
|
20
19
|
import { anyOpRequiresDynamicDiag, blockedBindingUseDiag, checkerPanicDiag, componentTypeDiag, isCheckerPanic, genericSignatureTypeDiag, indexSignatureTypeDiag, intersectionTypeDiag, noLoweringDiag, overloadedSignatureTypeDiag, recordShapeMismatchDiag, requiresDynamicApiDiag, requiresDynamicPackageDiag, requiresDynamicTypeDiag, unionMismatchDiag, unsupportedDiag, unsupportedTypeDiag, } from "../../diagnostics/diagnostic.js";
|
|
21
|
-
import { arrayOf, BOOL, canAdaptDynFuncTo, canConvertToDyn, canCrossIslandBoundary, canExitIslandToType, canMarshalTypedFuncIntoIsland, DYN, F64, isJsonSafeType, isUndefinedArmedUnion, isUnitType, JSVAL, RUNTIME_ERROR_CLASSES, STRING, typeEquals, UNDEFINED_T, VOID } from "../../ir/ir.js";
|
|
20
|
+
import { arrayOf, BOOL, canAdaptDynFuncTo, canConvertToDyn, canCrossIslandBoundary, canExitIslandToType, canMarshalTypedFuncIntoIsland, DYN, DYN_HANDLE_KINDS, F64, isDynTypedRefType, isJsonSafeType, isJsonStringifySafeType, isUndefinedArmedUnion, isUnitType, JSVAL, NULL_T, RUNTIME_ERROR_CLASSES, STRING, typeEquals, UNDEFINED_T, VOID } from "../../ir/ir.js";
|
|
22
21
|
import { provenanceActive } from "../provenance-registry.js";
|
|
23
22
|
import { ambientDtsPath, canonicalBuiltinModule, cjsExportAssignmentOf, cjsExportDiscardReason, fallbackDtsPath, isCjsExportTableLiteral, isJsSourceFile, isNodeEsmFile, isNodeTypesPath, locOf, orderedImportsOf, overridesDtsPath, npmStaticDepSf7, requireSpecOf, resolveImport, workspacePackageOfPath, } from "../program.js";
|
|
24
23
|
import { containsRecord, containsUnion, describeComponentBlocker, describeRecordMemberBlocker, formatIrType, ISLAND_AMBIENT_TYPES, isUnitOnlyTsType, mapType, ShapeRegistry, typeKey, UnionRegistry, withUndefinedArm as withUndefinedArmCanonical, } from "../type-mapper.js";
|
|
25
24
|
import { boundaryIntoIslandMsg, boundaryOutOfIslandMsg, builtinConstLit, builtinModuleConstOf, builtinModulesArrayLit, builtinFenceHintOf, builtinModuleFnOf, stdlibMemberFence, isStdlibMember, isStdlibSymbol, isStdlibGlobal, stdlibGlobalMember, nodeTypesOnlySymbol } from "./surfaces.js";
|
|
26
25
|
import { splitFiles, collectProgram, collectNpmImports, collectJsonImports, moduleArtifacts, collectGlobals, declSymbolOf, defaultExportSymbolOf, lowerFileInit, lowerDefaultExport, buildMain, appendDynamicImportModules } from "./lower-modules.js";
|
|
26
|
+
import { prepareCjsModuleGraph } from "./lower-node-module.js";
|
|
27
27
|
import { registerBuiltinErrorClasses, registerBuiltinEmitterClass, registerBuiltinStreamClasses, builtinErrorInfoOf, builtinEmitterInfoOf, builtinStreamInfoOf, analyzeClassDecoration, classIteratorDrainCall, classIteratorNextCall, classIteratorOf, classIteratorOpenCall, classIteratorRestDrainCall, classMemberNameOf, classValueRef, collectClassShape, exactClassOfReceiver, collectClassShapeInner, ctorAbiEquals, findMethodOn, findStaticOn, findGenericMethodOn, findGenericStaticOn, genericClassInstanceType, isSubclassOf, inHierarchy, overrideBelow, staticShadowBelow, upcastTo, lowerClassMembers, lowerClassCtor, lowerClassExpression, lowerClassExpressionInfo, lowerClassMethodMember, lowerClassValueProperty, lowerStaticMethod, throwingSetterFn, fieldInitStmts, lowerStaticFieldInits, lowerStaticFieldRead, lowerDerivedCtorBody, superCallStmt, lowerSuperMethodCall, superThisRef, lowerSuperAccessorRead, lowerSuperAccessorWrite, inheritsBuiltinErrorCtor, inheritsBuiltinEmitterCtor, errorMessageArg, lowerNew, accessorCall } from "./lower-classes.js";
|
|
28
28
|
import { mixinCallClassInfoOf, mixinIntersectionInstanceType } from "./lower-mixins.js";
|
|
29
29
|
import { bindingNeverReassigned, bodyReadsArguments, implicitMonoFile, isThisParameter, paramShape, paramShapes, checkDefaultParamBodyType, completeArgs, wrappedUndefined, undefinedArgFor, requireExactArityValue, bodyReturnType, declaredReturnType, collectSignature, collectSignatureInner, collectGenericSignature, genericFnOf, lowerGenericCall, lowerGenericFnValue, inferTypeParamBindings, lowerGenericInstance, lowerCall, lowerFfiCall, lowerTimersMemberCall, lowerPromiseMethodCall, lowerFilterNarrowCall, isTopLevelFnSymbol, lowerNestedFunctionDecl, lambdaSignature, lowerLambda, lowerFunction, validateFfiImports } from "./lower-calls.js";
|
|
30
|
-
import { lowerArrayMethodCall,
|
|
30
|
+
import { lowerArrayMethodCall, lowerMapMethodCall, lowerMapForEachCall, buildMapForEachFn, lowerRecordOvfCaptureHelper, lowerEnvToPairsHelper, lowerSetMethodCall, lowerSetForEachCall, buildSetForEachFn } from "./lower-containers.js";
|
|
31
|
+
import { lowerBufferStaticCall, lowerBytesMethodCall, lowerBytesNew } from "./containers/bytes.js";
|
|
32
|
+
import { lowerRegexMethodCall, lowerStringMethodCall } from "./containers/string-and-regexp.js";
|
|
31
33
|
import { lowerStreamModuleCall } from "./lower-stream.js";
|
|
32
34
|
import { lowerEmitOverrideSpec } from "./lower-event-emitter.js";
|
|
33
|
-
import { builtinImportOf, createRequireBindingDecl, createRequireNamespaceDecl, createRequireSpecOf, stripTypeCasts, lowerBuiltinModuleCall, lowerFsToUnixTimestampCall, lowerFsLadderCall, lowerChildArgsArg, lowerSpawnSyncCall, lowerSpawnCall, lowerExecSyncCall, recordToEnvPairs, lowerJsonMethodCall, fencedBuiltinImportOf, lowerCryptoComposedCall, lowerUrlMethodCall, lowerSearchParamsMethodCall, lowerStatsMethodCall, lowerChildMethodCall, lowerAtomicsCall, lowerBuiltinExtraProperty, promisifiedExecFileDecl, lowerExecFileAsyncCall, execFileAsyncHelper, lowerStringDecoderMethodCall, strdecHelper, lowerReadlineMethodCall, lowerDcChannelMethodCall, lowerDcChannelProperty, lowerAlsMethodCall, lowerDcTracingChannelMethodCall, lowerDcTracingChannelProperty, lowerJsonProperty, lowerErrorCodeProperty, lowerProcessProperty, isProcessEnv, envValueType, lowerProcessEnvGet, lowerProcessMethodCall, lowerProcessOptionalMethodCall, lowerTimeoutMethodCall, envSnapshotHelper, isConsoleLog, consoleCallMember, lowerNumberStaticCall, lowerNumberStaticProperty, lowerDateCall, lowerTextCodecCall, lowerCryptoModuleCall, lowerFsConstantsProperty, lowerBuiltinConstantsProperty, builtinConstantBindingOf, builtinConstantsDestructureDecl, lowerProcessStreamProperty, lowerStringStaticCall, lowerStringLastIndexOfCall, lowerPromiseStaticCall, textCodecBindingClassOf } from "./lower-builtins.js";
|
|
35
|
+
import { builtinImportOf, createRequireBindingDecl, createRequireNamespaceDecl, createRequireSpecOf, stripTypeCasts, lowerBuiltinModuleCall, lowerTimersPromisesSetInterval, lowerFsToUnixTimestampCall, lowerFsLadderCall, lowerChildArgsArg, lowerSpawnSyncCall, lowerSpawnCall, lowerExecSyncCall, recordToEnvPairs, lowerJsonMethodCall, fencedBuiltinImportOf, lowerCryptoComposedCall, lowerUrlMethodCall, lowerSearchParamsMethodCall, lowerStatsMethodCall, lowerChildMethodCall, lowerAtomicsCall, lowerBuiltinExtraProperty, promisifiedExecFileDecl, lowerExecFileAsyncCall, execFileAsyncHelper, lowerStringDecoderMethodCall, strdecHelper, lowerReadlineMethodCall, lowerDcChannelMethodCall, lowerDcChannelProperty, lowerAlsMethodCall, lowerDcTracingChannelMethodCall, lowerDcTracingChannelProperty, lowerJsonProperty, lowerErrorCodeProperty, lowerProcessProperty, isProcessEnv, envValueType, lowerProcessEnvGet, lowerProcessMethodCall, lowerProcessOptionalMethodCall, lowerTimeoutMethodCall, envSnapshotHelper, isConsoleLog, consoleCallMember, lowerNumberStaticCall, lowerNumberStaticProperty, lowerDateCall, lowerTextCodecCall, lowerCryptoModuleCall, lowerFsConstantsProperty, lowerBuiltinConstantsProperty, builtinConstantBindingOf, builtinConstantsDestructureDecl, lowerProcessStreamProperty, lowerStringStaticCall, lowerStringLastIndexOfCall, lowerPromiseStaticCall, textCodecBindingClassOf } from "./lower-builtins.js";
|
|
34
36
|
import { fenceFetchObjectAssignment, fenceFetchObjectBinding, fenceStaticAbortControllerMemberRead, fenceStaticHeadersIteration, fenceStaticHeadersMember, fenceStaticReadableStreamMember, fenceStaticResponseMember, fenceUnsupportedFetchConstructorMember, isIslandExpr, islandFuncValueFence, islandRegexpOf, jsvalIn, requireDynamicApi, islandGlobalFnOf, lowerAbortControllerNew, lowerDynamicHeadersIteratorCall, lowerDynamicHeadersSpread, lowerDynamicImportCall, lowerFetchCall, lowerFetchElementMethodCall, lowerResponseNew, lowerStaticFetchCompanionCall, lowerStaticAbortControllerCall, lowerStaticAbortSignalListenerCall, lowerStaticReadableStreamCancelCall, lowerStaticReadableStreamControllerCall, lowerStaticReadableStreamNew, lowerStaticReadableStreamReaderCall, lowerStaticResponseCall, lowerIslandMethodCall, lowerMathProperty, npmPackageOf, npmMemberFence, npmPackageOfSymbol } from "./lower-island.js";
|
|
35
37
|
import { lowerHttpHeadersElement, lowerNetModuleCall, lowerServerMethodCall, lowerServerProperty, lowerTlsRootCertificates } from "./lower-server.js";
|
|
36
38
|
import { lowerDgramDnsModuleCall, lowerDgramMethodCall } from "./lower-dgram.js";
|
|
@@ -38,11 +40,14 @@ import { lowerNodeTestModuleCall, lowerTestDirectCall, lowerTestMethodCall, lowe
|
|
|
38
40
|
import { lowerAssertModuleCall, lowerAssertDirectCall } from "./lower-assert.js";
|
|
39
41
|
import { lowerUtilModuleCall } from "./lower-inspect.js";
|
|
40
42
|
import { lowerComptime, comptimeBakeable, rejectComptimeCaptures, comptimeValueToIr } from "./lower-comptime.js";
|
|
41
|
-
import { lowerStmts, noteBlockedBindings, isBlockedBinding, lowerScopedBlock, predeclareForwardCapture, predeclareForwardFnDecl, predeclareForwardVar,
|
|
42
|
-
import {
|
|
43
|
+
import { lowerStmts, noteBlockedBindings, isBlockedBinding, lowerScopedBlock, predeclareForwardCapture, predeclareForwardFnDecl, predeclareForwardVar, lowerStmt, lowerVarStatement, lowerDestructuringDecl, lowerDestructuringAssignParts, lowerBindingPattern, lowerJsvalBindingPattern, checkBindingElement, bindPatternTarget, isParseArgsDynCheckerType, lowerVarDeclList, lowerVarDecl, lowerSwitch, lowerTry, lowerExprStatement, lowerForOf, lowerForStatement } from "./lower-stmts.js";
|
|
44
|
+
import { lowerExpr, maybeNarrow, lowerUnitComparison, lowerNullishCoalesce, lowerCondition, ensureBool, requireTruthyUnion, eqComparableUnion, lowerIntrinsicProperty, lowerArrayLiteral, lowerElementAccess, lowerElementWrite, lowerRecordKeyRead, ensureString, lowerTemplate, lowerAsExpression, lowerPrefixUnary, lowerBinary, lowerCaughtTypeofTest, caughtRead, caughtLocalOf, caughtToString, lowerInstanceOf, lowerRegexLiteral, lowerFieldRead, lowerUnionProperty, fieldTarget, fieldGetExpr, fieldSetStmt, lowerFieldCompound, uniqueSymbolKeyOf } from "./lower-exprs.js";
|
|
45
|
+
import { finishOptionalChain, lowerOptionalChain } from "./expressions/optional-chains.js";
|
|
46
|
+
import { foldedStringKeyOf, lowerDynObjectLiteral, lowerObjectLiteral, lowerShorthandValue, rejectThisInObjectMethod } from "./expressions/object-literals.js";
|
|
43
47
|
import { lowerRecordFieldCall, lowerObjectMethodCall } from "./lower-calls.js";
|
|
44
48
|
import { fenceCrossBlockNsRef, nsPathPrefix } from "./lower-namespaces.js";
|
|
45
49
|
import { numLit, varRef } from "../../ir/build.js";
|
|
50
|
+
import { isSafeToRepeat } from "./expressions/evaluation-safety.js";
|
|
46
51
|
/** Entry function name. '%' cannot appear in a TS identifier, so a user
|
|
47
52
|
* function can never collide with it (mangling is injective per prefix). */
|
|
48
53
|
export const ENTRY_NAME = "%main";
|
|
@@ -81,7 +86,7 @@ export function newFnCtx(lifted, selfSymbol, selfType, returnType) {
|
|
|
81
86
|
* always visited. */
|
|
82
87
|
const IR_STMT_KINDS = new Set([
|
|
83
88
|
"varDecl", "assign", "exprStmt", "if", "while", "doWhile", "switch",
|
|
84
|
-
"arraySet", "forOf", "return", "fieldSet", "recordSet", "break",
|
|
89
|
+
"arraySet", "arraySetLength", "arraySetUndefined", "arrayDelete", "forOf", "return", "fieldSet", "recordSet", "break",
|
|
85
90
|
"continue", "block", "tryCatch", "throw", "rethrow", "runtimeFence",
|
|
86
91
|
]);
|
|
87
92
|
/** True when a lowered statement's OWN expressions contain island
|
|
@@ -464,6 +469,41 @@ export function dynFallbackType(lowerer, node, t) {
|
|
|
464
469
|
// the whole type's own fence.
|
|
465
470
|
return anyPiecedFuncType(lowerer, node, t);
|
|
466
471
|
}
|
|
472
|
+
// JS inference commonly spells a callback field initialized to null as
|
|
473
|
+
// `((value: any) => any) | null`. Preserve the callable arm and its
|
|
474
|
+
// checked-dynamic pieces instead of collapsing the whole slot to dyn.
|
|
475
|
+
// Besides keeping direct calls static, this leaves closure captures
|
|
476
|
+
// visible to the native cycle collector (a dyn-boxed closure edge is
|
|
477
|
+
// intentionally opaque to it).
|
|
478
|
+
if (t.isUnionType()) {
|
|
479
|
+
const arms = [];
|
|
480
|
+
let functions = 0;
|
|
481
|
+
for (const part of ts.constituentTypes(t)) {
|
|
482
|
+
if (part.flags & ts.TypeFlags.Null) {
|
|
483
|
+
arms.push(NULL_T);
|
|
484
|
+
continue;
|
|
485
|
+
}
|
|
486
|
+
if (part.flags & (ts.TypeFlags.Undefined | ts.TypeFlags.Void)) {
|
|
487
|
+
arms.push(UNDEFINED_T);
|
|
488
|
+
continue;
|
|
489
|
+
}
|
|
490
|
+
const mapped = lowerer.mapTypeOf(part) ?? jsFallbackFunctionType(lowerer, node, part);
|
|
491
|
+
if (mapped?.kind !== "func") {
|
|
492
|
+
functions = -1;
|
|
493
|
+
break;
|
|
494
|
+
}
|
|
495
|
+
functions++;
|
|
496
|
+
arms.push(mapped);
|
|
497
|
+
}
|
|
498
|
+
if (functions === 1 && arms.length > 1) {
|
|
499
|
+
const canonical = [...new Map(arms.map((arm) => [typeKey(arm), arm])).entries()]
|
|
500
|
+
.sort(([a], [b]) => a < b ? -1 : a > b ? 1 : 0)
|
|
501
|
+
.map(([, arm]) => arm);
|
|
502
|
+
return canonical.length === 1
|
|
503
|
+
? canonical[0]
|
|
504
|
+
: { kind: "union", unionId: lowerer.unions.intern(canonical) };
|
|
505
|
+
}
|
|
506
|
+
}
|
|
467
507
|
if (lowerer.checker.isArrayType(t)) {
|
|
468
508
|
const elem = lowerer.checker.getTypeArguments(t)[0];
|
|
469
509
|
const elemTainted = elem !== undefined &&
|
|
@@ -485,18 +525,23 @@ export function dynFallbackType(lowerer, node, t) {
|
|
|
485
525
|
// construct signatures, overloads, and function-with-properties shapes
|
|
486
526
|
// stay out (the whole value falls to dyn below, where every reached
|
|
487
527
|
// use meets its own fence or boxes as-is).
|
|
488
|
-
const
|
|
489
|
-
if (
|
|
490
|
-
|
|
491
|
-
const pt = lowerer.checker.getTypeOfSymbolAtLocation(p, node);
|
|
492
|
-
return lowerer.mapTypeOf(pt) ?? DYN;
|
|
493
|
-
});
|
|
494
|
-
const retT = lowerer.checker.getReturnTypeOfSignature(sig);
|
|
495
|
-
const ret = retT.flags & ts.TypeFlags.Void ? VOID : lowerer.mapTypeOf(retT) ?? DYN;
|
|
496
|
-
return { kind: "func", params, ret };
|
|
497
|
-
}
|
|
528
|
+
const fallbackFunction = jsFallbackFunctionType(lowerer, node, t);
|
|
529
|
+
if (fallbackFunction !== null)
|
|
530
|
+
return fallbackFunction;
|
|
498
531
|
return DYN;
|
|
499
532
|
}
|
|
533
|
+
function jsFallbackFunctionType(lowerer, node, t) {
|
|
534
|
+
const sig = pureSingleCallSignatureOf(lowerer, t);
|
|
535
|
+
if (!sig)
|
|
536
|
+
return null;
|
|
537
|
+
const params = sig.getParameters().map((p) => {
|
|
538
|
+
const pt = lowerer.checker.getTypeOfSymbolAtLocation(p, node);
|
|
539
|
+
return lowerer.mapTypeOf(pt) ?? DYN;
|
|
540
|
+
});
|
|
541
|
+
const retT = lowerer.checker.getReturnTypeOfSignature(sig);
|
|
542
|
+
const ret = retT.flags & ts.TypeFlags.Void ? VOID : lowerer.mapTypeOf(retT) ?? DYN;
|
|
543
|
+
return { kind: "func", params, ret };
|
|
544
|
+
}
|
|
500
545
|
/** The one call signature of a PURE function type — single signature, no
|
|
501
546
|
* properties, no construct signatures, no type parameters, no rest params
|
|
502
547
|
* (declared or synthesized from an `arguments` read). Null for every
|
|
@@ -821,12 +866,237 @@ export class Lowerer {
|
|
|
821
866
|
/** All storage slots widened for runtime absence, including slots whose
|
|
822
867
|
* current control-flow branch has temporarily narrowed the value. */
|
|
823
868
|
runtimeOptionalStorageLocals = new Set();
|
|
869
|
+
/** Globals whose checker-bare type was widened because an indexed read can
|
|
870
|
+
* carry undefined at runtime. Identifier reads keep the union tag so
|
|
871
|
+
* typeof and later boundaries observe the real value. */
|
|
872
|
+
runtimeOptionalGlobals = new Set();
|
|
873
|
+
/** Bindings whose unchecked string arithmetic can produce either a number
|
|
874
|
+
* (NaN for an absent read) or a string. Preserve that result union at bare
|
|
875
|
+
* reads instead of applying the checker-bare string narrowing adapter. */
|
|
876
|
+
runtimeOptionalArithmeticLocals = new Set();
|
|
877
|
+
runtimeOptionalArithmeticGlobals = new Set();
|
|
878
|
+
/** Binding symbols whose IR storage type was widened by the indexed-read
|
|
879
|
+
* prepass. This includes explicit annotations: unchecked TS annotations do
|
|
880
|
+
* not prove that an array property read produced a value. */
|
|
881
|
+
runtimeOptionalBindingTypes = new Map();
|
|
882
|
+
/** Concise callback/lambda returns promoted by the HOF callback prepass;
|
|
883
|
+
* arrows have no declaration symbol to key in fnSigsBySymbol. */
|
|
884
|
+
runtimeOptionalFunctionReturns = new WeakMap();
|
|
885
|
+
/** Arithmetic over an unchecked string read can answer either NaN or a
|
|
886
|
+
* string. Bindings and returns use this marker to retain that result union
|
|
887
|
+
* through checker-bare string annotations. */
|
|
888
|
+
runtimeOptionalArithmeticTypes = new WeakMap();
|
|
889
|
+
/** Record fields promoted to an undefined-armed union by an indexed-read
|
|
890
|
+
* value. The key is the concrete emitted shape and field name. */
|
|
891
|
+
runtimeOptionalFields = new Set();
|
|
892
|
+
/** Callback pattern parameters carry an optional source without making
|
|
893
|
+
* every name destructured from a present source optional. */
|
|
894
|
+
runtimeOptionalPatternTypes = new WeakMap();
|
|
895
|
+
runtimeOptionalReduceTypes = new WeakMap();
|
|
824
896
|
/** Capture entries and their origin share one mutable box. Normalize each
|
|
825
897
|
* entry to the origin so writes and flow proofs stay synchronized. */
|
|
826
898
|
runtimeOptionalRoots = new Map();
|
|
827
899
|
runtimeOptionalRootOf(local) {
|
|
828
900
|
return this.runtimeOptionalRoots.get(local) ?? local;
|
|
829
901
|
}
|
|
902
|
+
runtimeOptionalBindingType(node, fallback) {
|
|
903
|
+
const patternType = this.runtimeOptionalPatternTypes.get(node);
|
|
904
|
+
if (patternType)
|
|
905
|
+
return patternType;
|
|
906
|
+
const symbol = ts.isIdentifier(node) ? this.resolveValueSymbol(node) : this.checker.getSymbolAtLocation(node);
|
|
907
|
+
if (!symbol)
|
|
908
|
+
return fallback ?? null;
|
|
909
|
+
const known = this.runtimeOptionalBindingTypes.get(symbol);
|
|
910
|
+
if (known)
|
|
911
|
+
return known;
|
|
912
|
+
// A module-scope function alias is collected as a global before the
|
|
913
|
+
// optional-read fixed point settles. Derive its promoted function ABI
|
|
914
|
+
// directly from the aliased declaration as a final read-side hook, so
|
|
915
|
+
// the initializer cannot fall back to a narrowing adapter.
|
|
916
|
+
if (ts.isIdentifier(node) && fallback?.kind === "func") {
|
|
917
|
+
const decl = this.checker.valueDeclarationOf(symbol);
|
|
918
|
+
if (decl && ts.isVariableDeclaration(decl) && decl.initializer) {
|
|
919
|
+
let init = decl.initializer;
|
|
920
|
+
while (ts.isParenthesizedExpression(init))
|
|
921
|
+
init = init.expression;
|
|
922
|
+
if (ts.isIdentifier(init)) {
|
|
923
|
+
const source = this.resolveValueSymbol(init);
|
|
924
|
+
const sig = source ? this.fnSigsBySymbol.get(source) : undefined;
|
|
925
|
+
if (sig) {
|
|
926
|
+
return {
|
|
927
|
+
...fallback,
|
|
928
|
+
params: sig.params.filter((p) => p.mode !== "dynRest").map((p) => p.type),
|
|
929
|
+
ret: sig.returnType,
|
|
930
|
+
};
|
|
931
|
+
}
|
|
932
|
+
}
|
|
933
|
+
}
|
|
934
|
+
}
|
|
935
|
+
return fallback ?? null;
|
|
936
|
+
}
|
|
937
|
+
runtimeOptionalIdentifierValue(node) {
|
|
938
|
+
if (!ts.isIdentifier(node) || this.chainRecvByNode.has(node))
|
|
939
|
+
return null;
|
|
940
|
+
const local = this.resolveLocal(node);
|
|
941
|
+
const symbol = this.resolveValueSymbol(node);
|
|
942
|
+
const storage = local ?? (symbol ? this.globalsBySymbol.get(symbol) : undefined);
|
|
943
|
+
if (!storage || storage.type.kind !== "union" || this.armTag(storage.type.unionId, UNDEFINED_T) < 0)
|
|
944
|
+
return null;
|
|
945
|
+
const present = this.stripUndefinedArm(storage.type);
|
|
946
|
+
if (isUnitType(present))
|
|
947
|
+
return null;
|
|
948
|
+
return { value: varRef(storage.id, storage.type, locOf(node)), present, unionId: storage.type.unionId };
|
|
949
|
+
}
|
|
950
|
+
/** Recover the original optional union when a checked single-arm helper
|
|
951
|
+
* was installed before a strict property consumer could name its member.
|
|
952
|
+
* Recovery is limited to array element reads and storage roots marked by
|
|
953
|
+
* the optional-read analysis, so ordinary assertions keep their generic
|
|
954
|
+
* checked-narrow behavior. */
|
|
955
|
+
runtimeOptionalSourceValue(node, value) {
|
|
956
|
+
let origin = node;
|
|
957
|
+
while (ts.isParenthesizedExpression(origin))
|
|
958
|
+
origin = origin.expression;
|
|
959
|
+
let optionalOrigin = ts.isElementAccessExpression(origin);
|
|
960
|
+
if (ts.isIdentifier(origin)) {
|
|
961
|
+
const local = this.resolveLocal(origin);
|
|
962
|
+
const root = local ? this.runtimeOptionalRootOf(local) : null;
|
|
963
|
+
const global = this.globalOf(origin);
|
|
964
|
+
optionalOrigin =
|
|
965
|
+
(!!root && this.runtimeOptionalStorageLocals.has(root)) ||
|
|
966
|
+
(!!global && (this.isRuntimeOptionalGlobal(global) ||
|
|
967
|
+
(global.type.kind === "union" && this.armTag(global.type.unionId, UNDEFINED_T) >= 0)));
|
|
968
|
+
}
|
|
969
|
+
if (!optionalOrigin)
|
|
970
|
+
return null;
|
|
971
|
+
let source = value;
|
|
972
|
+
if (value.kind === "call" &&
|
|
973
|
+
this.checkedNarrowHelpers.has(value.callee) &&
|
|
974
|
+
value.args.length === 1 &&
|
|
975
|
+
value.args[0]?.type.kind === "union") {
|
|
976
|
+
source = value.args[0];
|
|
977
|
+
}
|
|
978
|
+
return source.type.kind === "union" && this.armTag(source.type.unionId, UNDEFINED_T) >= 0
|
|
979
|
+
? source
|
|
980
|
+
: null;
|
|
981
|
+
}
|
|
982
|
+
/** A strict property or method receiver over an array-derived optional
|
|
983
|
+
* value. Reuse a plain read directly or stabilize an effectful receiver
|
|
984
|
+
* once, throw Node's member-read TypeError for a unit arm, and extract the
|
|
985
|
+
* expected present arm otherwise. */
|
|
986
|
+
runtimeOptionalPropertyReceiver(node, value, expected, member) {
|
|
987
|
+
const source = this.runtimeOptionalSourceValue(node, value);
|
|
988
|
+
if (source?.type.kind !== "union")
|
|
989
|
+
return null;
|
|
990
|
+
const def = this.unions.get(source.type.unionId);
|
|
991
|
+
const valueTag = this.armTag(source.type.unionId, expected);
|
|
992
|
+
if (!def ||
|
|
993
|
+
valueTag < 0 ||
|
|
994
|
+
!def.arms.every((arm) => typeEquals(arm, expected) || isUnitType(arm)))
|
|
995
|
+
return null;
|
|
996
|
+
const loc = locOf(node);
|
|
997
|
+
const repeatSource = isSafeToRepeat(source);
|
|
998
|
+
const stable = repeatSource ? null : this.declareHiddenLocal("%propertyRecv", source.type);
|
|
999
|
+
const stableRef = () => repeatSource ? source : varRef(stable.id, source.type, loc);
|
|
1000
|
+
let result = {
|
|
1001
|
+
kind: "unionNarrow",
|
|
1002
|
+
unionId: source.type.unionId,
|
|
1003
|
+
tag: valueTag,
|
|
1004
|
+
value: stableRef(),
|
|
1005
|
+
type: expected,
|
|
1006
|
+
loc,
|
|
1007
|
+
};
|
|
1008
|
+
for (let i = def.arms.length - 1; i >= 0; i--) {
|
|
1009
|
+
const arm = def.arms[i];
|
|
1010
|
+
if (!arm || !isUnitType(arm))
|
|
1011
|
+
continue;
|
|
1012
|
+
const unit = arm.kind === "nullT" ? "null" : "undefined";
|
|
1013
|
+
result = {
|
|
1014
|
+
kind: "ternary",
|
|
1015
|
+
cond: {
|
|
1016
|
+
kind: "unionIsTag",
|
|
1017
|
+
unionId: source.type.unionId,
|
|
1018
|
+
tag: i,
|
|
1019
|
+
negated: false,
|
|
1020
|
+
value: stableRef(),
|
|
1021
|
+
type: BOOL,
|
|
1022
|
+
loc,
|
|
1023
|
+
},
|
|
1024
|
+
then: nodeThrowExpr(1, "", `Cannot read properties of ${unit} (reading '${member}')`, expected, loc),
|
|
1025
|
+
else_: result,
|
|
1026
|
+
type: expected,
|
|
1027
|
+
loc,
|
|
1028
|
+
};
|
|
1029
|
+
}
|
|
1030
|
+
if (stable === null)
|
|
1031
|
+
return result;
|
|
1032
|
+
return {
|
|
1033
|
+
kind: "seqExpr",
|
|
1034
|
+
stmts: [{ kind: "varDecl", localId: stable.id, init: source, loc }],
|
|
1035
|
+
result,
|
|
1036
|
+
type: expected,
|
|
1037
|
+
loc,
|
|
1038
|
+
};
|
|
1039
|
+
}
|
|
1040
|
+
runtimeOptionalFieldKey(shapeId, field) {
|
|
1041
|
+
return `${shapeId}:${field}`;
|
|
1042
|
+
}
|
|
1043
|
+
isRuntimeOptionalField(shapeId, field) {
|
|
1044
|
+
return this.runtimeOptionalFields.has(this.runtimeOptionalFieldKey(shapeId, field));
|
|
1045
|
+
}
|
|
1046
|
+
isRuntimeOptionalGlobal(global) {
|
|
1047
|
+
return this.runtimeOptionalGlobals.has(global);
|
|
1048
|
+
}
|
|
1049
|
+
isRuntimeOptionalArithmeticGlobal(global) {
|
|
1050
|
+
return this.runtimeOptionalArithmeticGlobals.has(global);
|
|
1051
|
+
}
|
|
1052
|
+
/** The union produced when an unchecked array read reaches a typed slot
|
|
1053
|
+
* whose checker type is the corresponding bare arm. */
|
|
1054
|
+
runtimeOptionalWidening(actual, expected) {
|
|
1055
|
+
if (actual.kind !== "union" || this.armTag(actual.unionId, UNDEFINED_T) < 0)
|
|
1056
|
+
return null;
|
|
1057
|
+
return typeEquals(this.stripUndefinedArm(actual), expected) ? actual : null;
|
|
1058
|
+
}
|
|
1059
|
+
runtimeOptionalType(t) {
|
|
1060
|
+
if (t.kind === "union")
|
|
1061
|
+
return this.armTag(t.unionId, UNDEFINED_T) >= 0 ? t : this.withUndefinedArmOf(t) ?? t;
|
|
1062
|
+
if (t.kind === "void" || t.kind === "dyn" || t.kind === "jsval" || t.kind === "generator")
|
|
1063
|
+
return t;
|
|
1064
|
+
return this.withUndefinedArm(t);
|
|
1065
|
+
}
|
|
1066
|
+
promoteRuntimeOptionalParameter(node, type) {
|
|
1067
|
+
const widened = this.runtimeOptionalType(type);
|
|
1068
|
+
const symbol = this.checker.getSymbolAtLocation(node);
|
|
1069
|
+
if (symbol)
|
|
1070
|
+
this.runtimeOptionalBindingTypes.set(symbol, widened);
|
|
1071
|
+
return widened;
|
|
1072
|
+
}
|
|
1073
|
+
promoteRuntimeOptionalFunctionReturn(node, type) {
|
|
1074
|
+
const widened = this.runtimeOptionalType(type);
|
|
1075
|
+
this.runtimeOptionalFunctionReturns.set(node, widened);
|
|
1076
|
+
return widened;
|
|
1077
|
+
}
|
|
1078
|
+
runtimeOptionalFunctionReturnType(node, fallback) {
|
|
1079
|
+
return this.runtimeOptionalFunctionReturns.get(node) ?? fallback;
|
|
1080
|
+
}
|
|
1081
|
+
/** Promote one field of a record storage shape to the value's runtime
|
|
1082
|
+
* type. Shapes are immutable by identity, so this interns a sibling shape
|
|
1083
|
+
* and records the field as runtime-optional for property-read narrowing. */
|
|
1084
|
+
runtimeOptionalRecordField(type, field, fieldType) {
|
|
1085
|
+
if (type.kind !== "record")
|
|
1086
|
+
return type;
|
|
1087
|
+
const shape = this.shapes.get(type.shapeId);
|
|
1088
|
+
if (!shape)
|
|
1089
|
+
return type;
|
|
1090
|
+
const existing = shape.fields.find((f) => f.name === field);
|
|
1091
|
+
if (!existing || typeEquals(existing.type, fieldType)) {
|
|
1092
|
+
if (existing)
|
|
1093
|
+
this.runtimeOptionalFields.add(this.runtimeOptionalFieldKey(type.shapeId, field));
|
|
1094
|
+
return type;
|
|
1095
|
+
}
|
|
1096
|
+
const shapeId = this.shapes.intern(shape.fields.map((f) => ({ name: f.name, type: f.name === field ? fieldType : f.type })), shape.tuple === true, shape.indexValue, shape.declaredOrder);
|
|
1097
|
+
this.runtimeOptionalFields.add(this.runtimeOptionalFieldKey(shapeId, field));
|
|
1098
|
+
return { kind: "record", shapeId };
|
|
1099
|
+
}
|
|
830
1100
|
/** Runs `fn` with the given aliased-typeof narrows applied (and restored
|
|
831
1101
|
* after) — the branch-scoping primitive. */
|
|
832
1102
|
narrowingAliases(narrows, fn) {
|
|
@@ -920,6 +1190,10 @@ export class Lowerer {
|
|
|
920
1190
|
* declaration: member reads call the getter (lower-exprs). */
|
|
921
1191
|
cjsAccessorFns = new Map();
|
|
922
1192
|
narrowHelpers = new Map();
|
|
1193
|
+
/** Exact members of narrowHelpers produced by narrowedArmHelper. This
|
|
1194
|
+
* lets property consumers recognize an earlier checked extraction by
|
|
1195
|
+
* provenance instead of relying on its generated-name prefix. */
|
|
1196
|
+
checkedNarrowHelpers = new Set();
|
|
923
1197
|
/** Interned `%iter.drain.<n>` helpers (classIteratorDrainCall): one per
|
|
924
1198
|
* receiver class — the eager drain of a class iterable's protocol into
|
|
925
1199
|
* a fresh element array, behind array/call spreads. */
|
|
@@ -1040,6 +1314,12 @@ export class Lowerer {
|
|
|
1040
1314
|
* any body lowers: import headers and inline require statements call
|
|
1041
1315
|
* dependency inits by these names. */
|
|
1042
1316
|
initNameOf = new Map();
|
|
1317
|
+
/** CommonJS module objects are scalar handles into the runtime registry.
|
|
1318
|
+
* IDs are assigned in deterministic module order before any body lowers;
|
|
1319
|
+
* the feature gate stays false for programs that never inspect the graph. */
|
|
1320
|
+
cjsModuleIdOf = new Map();
|
|
1321
|
+
cjsModuleFiles = [];
|
|
1322
|
+
cjsModuleGraphEnabled = false;
|
|
1043
1323
|
/** File → the id of its run-once guard global (a bool module global,
|
|
1044
1324
|
* false at program start). Every non-entry module gets one: its %init
|
|
1045
1325
|
* may be called from several importers/requirers, and the guard is what
|
|
@@ -1324,9 +1604,8 @@ export class Lowerer {
|
|
|
1324
1604
|
const spec = requireSpecOf(recvDecl.initializer);
|
|
1325
1605
|
const dep = spec === null
|
|
1326
1606
|
? null
|
|
1327
|
-
:
|
|
1328
|
-
|
|
1329
|
-
: npmStaticDepSf7(this.program, recvDecl.getSourceFile(), spec);
|
|
1607
|
+
: resolveImport(this.program, recvDecl.getSourceFile(), spec) ??
|
|
1608
|
+
npmStaticDepSf7(this.program, recvDecl.getSourceFile(), spec);
|
|
1330
1609
|
if (dep)
|
|
1331
1610
|
symbol = this.cjsModuleExportSymbol(dep, ident.text);
|
|
1332
1611
|
}
|
|
@@ -1462,15 +1741,15 @@ export class Lowerer {
|
|
|
1462
1741
|
return this.externalTypeSpecifierOfSymbol(this.checker.getSymbolAtLocation(value));
|
|
1463
1742
|
}
|
|
1464
1743
|
/** The source file a checker-resolved module-specifier node names. The
|
|
1465
|
-
* checker path
|
|
1466
|
-
*
|
|
1744
|
+
* checker path is preferred; resolveImport is the canonical fallback for
|
|
1745
|
+
* every project-module spelling. */
|
|
1467
1746
|
moduleSourceFileOf(from, spec) {
|
|
1468
1747
|
const moduleSymbol = this.checker.getSymbolAtLocation(spec);
|
|
1469
1748
|
for (const decl of moduleSymbol ? this.checker.declarationsOf(moduleSymbol) : []) {
|
|
1470
1749
|
if (ts.isSourceFile(decl))
|
|
1471
1750
|
return decl;
|
|
1472
1751
|
}
|
|
1473
|
-
return
|
|
1752
|
+
return resolveImport(this.program, from, spec.text);
|
|
1474
1753
|
}
|
|
1475
1754
|
/** Follow one project-module export through the checker's resolved export
|
|
1476
1755
|
* table, then recover its exact external route where alias declarations
|
|
@@ -1712,22 +1991,19 @@ export class Lowerer {
|
|
|
1712
1991
|
return null;
|
|
1713
1992
|
}
|
|
1714
1993
|
const spec = importDecl.moduleSpecifier.text;
|
|
1715
|
-
//
|
|
1716
|
-
//
|
|
1717
|
-
|
|
1718
|
-
|
|
1719
|
-
const dep = isRelativeSpecifier(spec)
|
|
1720
|
-
? resolveImport(this.program, importDecl.getSourceFile(), spec)
|
|
1721
|
-
: npmStaticDepSf7(this.program, importDecl.getSourceFile(), spec);
|
|
1994
|
+
// Project aliases/self-references resolve through the same entry point as
|
|
1995
|
+
// relative imports; an opted-in --npm-static package is the fallback.
|
|
1996
|
+
const dep = resolveImport(this.program, importDecl.getSourceFile(), spec) ??
|
|
1997
|
+
npmStaticDepSf7(this.program, importDecl.getSourceFile(), spec);
|
|
1722
1998
|
if (!dep || !isJsSourceFile(dep) || isNodeEsmFile(dep))
|
|
1723
1999
|
return null;
|
|
1724
2000
|
return dep;
|
|
1725
2001
|
}
|
|
1726
2002
|
/** True when `expr` is an identifier bound by a top-level
|
|
1727
|
-
* `const x = require("./local")` of a
|
|
1728
|
-
*
|
|
1729
|
-
* --npm-static package (its CJS entry is a program
|
|
1730
|
-
* binding is the same namespace over the same export table), or by a
|
|
2003
|
+
* `const x = require("./local")` of a project module — relative,
|
|
2004
|
+
* tsconfig-aliased, or package.json-mediated — or of a bare specifier
|
|
2005
|
+
* naming an opted-in --npm-static package (its CJS entry is a program
|
|
2006
|
+
* module, so the binding is the same namespace over the same export table), or by a
|
|
1731
2007
|
* DEFAULT import of a CommonJS JS module (`import d from "./lib.cjs"`:
|
|
1732
2008
|
* Node binds d to module.exports, the same value require answers).
|
|
1733
2009
|
* Member accesses on it resolve through the export table (property
|
|
@@ -1752,7 +2028,7 @@ export class Lowerer {
|
|
|
1752
2028
|
const spec = requireSpecOf(decl.initializer);
|
|
1753
2029
|
if (spec === null)
|
|
1754
2030
|
return false;
|
|
1755
|
-
if (
|
|
2031
|
+
if (resolveImport(this.program, decl.getSourceFile(), spec) === null &&
|
|
1756
2032
|
npmStaticDepSf7(this.program, decl.getSourceFile(), spec) === null) {
|
|
1757
2033
|
return false;
|
|
1758
2034
|
}
|
|
@@ -1972,16 +2248,15 @@ export class Lowerer {
|
|
|
1972
2248
|
* their external dependencies by name). Null for everything else
|
|
1973
2249
|
* (builtins load nothing; the rest kept its preflight fence). */
|
|
1974
2250
|
requireInitStmt(spec, node) {
|
|
1975
|
-
//
|
|
1976
|
-
// a program-module edge
|
|
1977
|
-
//
|
|
1978
|
-
//
|
|
1979
|
-
//
|
|
2251
|
+
// Project requires resolve within the program; another bare require can
|
|
2252
|
+
// be a program-module edge when it names an opted-in --npm-static package
|
|
2253
|
+
// (one package requiring another — the resolution answered its shipped
|
|
2254
|
+
// JS, the file is in the module order, and the reads alias its globals).
|
|
2255
|
+
// Without the guarded %init call at this position
|
|
1980
2256
|
// those globals stay uninitialized: the dep's module body would never
|
|
1981
2257
|
// run.
|
|
1982
|
-
const dep =
|
|
1983
|
-
|
|
1984
|
-
: npmStaticDepSf7(this.program, node.getSourceFile(), spec);
|
|
2258
|
+
const dep = resolveImport(this.program, node.getSourceFile(), spec) ??
|
|
2259
|
+
npmStaticDepSf7(this.program, node.getSourceFile(), spec);
|
|
1985
2260
|
if (!dep || dep.fileName.endsWith(".json"))
|
|
1986
2261
|
return null;
|
|
1987
2262
|
if (this.asyncInitFiles.has(dep)) {
|
|
@@ -1991,11 +2266,33 @@ export class Lowerer {
|
|
|
1991
2266
|
if (initName === undefined)
|
|
1992
2267
|
return null;
|
|
1993
2268
|
const loc = locOf(node);
|
|
1994
|
-
|
|
2269
|
+
const init = {
|
|
1995
2270
|
kind: "exprStmt",
|
|
1996
2271
|
expr: { kind: "call", callee: initName, args: [], type: VOID, loc },
|
|
1997
2272
|
loc,
|
|
1998
2273
|
};
|
|
2274
|
+
const parentModule = this.cjsModuleIdOf.get(node.getSourceFile());
|
|
2275
|
+
const childModule = this.cjsModuleIdOf.get(dep);
|
|
2276
|
+
if (parentModule === undefined || childModule === undefined)
|
|
2277
|
+
return init;
|
|
2278
|
+
return {
|
|
2279
|
+
kind: "block",
|
|
2280
|
+
body: [
|
|
2281
|
+
{
|
|
2282
|
+
kind: "exprStmt",
|
|
2283
|
+
expr: {
|
|
2284
|
+
kind: "libCall",
|
|
2285
|
+
fn: "module.link",
|
|
2286
|
+
args: [numLit(parentModule, loc), numLit(childModule, loc)],
|
|
2287
|
+
type: VOID,
|
|
2288
|
+
loc,
|
|
2289
|
+
},
|
|
2290
|
+
loc,
|
|
2291
|
+
},
|
|
2292
|
+
init,
|
|
2293
|
+
],
|
|
2294
|
+
loc,
|
|
2295
|
+
};
|
|
1999
2296
|
}
|
|
2000
2297
|
/** True when this body should lower: everything with no reachable set,
|
|
2001
2298
|
* the marked bodies in an externally-gated pass, and exactly the
|
|
@@ -2011,6 +2308,720 @@ export class Lowerer {
|
|
|
2011
2308
|
collectJsonImports(parts) {
|
|
2012
2309
|
return collectJsonImports(this, parts);
|
|
2013
2310
|
}
|
|
2311
|
+
/** Discover the unchecked-array values that cross static ABI/storage
|
|
2312
|
+
* boundaries before any function body or module initializer is emitted.
|
|
2313
|
+
* TypeScript's default indexed-access type is a useful source annotation,
|
|
2314
|
+
* but it is not a runtime proof; promoting only the affected slots keeps
|
|
2315
|
+
* the rest of the dense ABI unchanged. */
|
|
2316
|
+
analyzeRuntimeOptionalArrayReads(parts) {
|
|
2317
|
+
const optionalSymbols = new Set();
|
|
2318
|
+
const optionalReturns = new Set();
|
|
2319
|
+
const arithmeticReturns = new Map();
|
|
2320
|
+
const optionalParams = new Map();
|
|
2321
|
+
const optionalFields = new Map();
|
|
2322
|
+
const dynamicObjectEntryRows = new Set();
|
|
2323
|
+
const sourceFiles = parts.map((p) => p.sf);
|
|
2324
|
+
const fnDecls = parts.flatMap((p) => p.fnDecls);
|
|
2325
|
+
// This fixed-point scan intentionally sees deferred bodies before
|
|
2326
|
+
// reachability is known. Batch their binding queries through the
|
|
2327
|
+
// facade's panic fence, then keep lookup free of lowering side effects:
|
|
2328
|
+
// resolveValueSymbol also flushes deferred diagnostics and applies
|
|
2329
|
+
// merged-namespace fences, whose authority remains ordinary lowering.
|
|
2330
|
+
this.checker.prefetchSymbolRoots(sourceFiles);
|
|
2331
|
+
const symbolOf = (node) => {
|
|
2332
|
+
const s = this.checker.getSymbolAtLocation(node);
|
|
2333
|
+
if (!s)
|
|
2334
|
+
return null;
|
|
2335
|
+
return s.flags & ts.SymbolFlags.Alias ? this.checker.getAliasedSymbol(s) : s;
|
|
2336
|
+
};
|
|
2337
|
+
const functionDeclBySymbol = new Map();
|
|
2338
|
+
const signatureBySymbol = new Map();
|
|
2339
|
+
for (const [symbol, sig] of this.fnSigsBySymbol)
|
|
2340
|
+
signatureBySymbol.set(symbol, { params: sig.params, returnType: sig.returnType, top: sig });
|
|
2341
|
+
for (const decl of fnDecls) {
|
|
2342
|
+
const symbol = declSymbolOf(this, decl);
|
|
2343
|
+
if (symbol)
|
|
2344
|
+
functionDeclBySymbol.set(symbol, decl);
|
|
2345
|
+
}
|
|
2346
|
+
for (const sf of sourceFiles) {
|
|
2347
|
+
ts.walkPreorder(sf, (node) => {
|
|
2348
|
+
if ((ts.isMethodDeclaration(node) || ts.isGetAccessorDeclaration(node) || ts.isSetAccessorDeclaration(node)) &&
|
|
2349
|
+
node.body && node.name && (ts.isIdentifier(node.name) || ts.isStringLiteral(node.name))) {
|
|
2350
|
+
const symbol = symbolOf(node.name);
|
|
2351
|
+
if (symbol)
|
|
2352
|
+
functionDeclBySymbol.set(symbol, node);
|
|
2353
|
+
}
|
|
2354
|
+
});
|
|
2355
|
+
}
|
|
2356
|
+
for (const info of this.classes.values()) {
|
|
2357
|
+
for (const { mName, member } of this.classMethodMembers(info)) {
|
|
2358
|
+
if (!member.name || !(ts.isIdentifier(member.name) || ts.isStringLiteral(member.name)))
|
|
2359
|
+
continue;
|
|
2360
|
+
const symbol = symbolOf(member.name);
|
|
2361
|
+
const sig = info.methods.get(mName);
|
|
2362
|
+
if (symbol && sig)
|
|
2363
|
+
signatureBySymbol.set(symbol, { params: sig.params, returnType: sig.ret, method: sig });
|
|
2364
|
+
}
|
|
2365
|
+
}
|
|
2366
|
+
const peel = (node) => {
|
|
2367
|
+
let e = node;
|
|
2368
|
+
while (ts.isParenthesizedExpression(e) || ts.isAsExpression(e) || ts.isTypeAssertion(e) || ts.isNonNullExpression(e)) {
|
|
2369
|
+
e = e.expression;
|
|
2370
|
+
}
|
|
2371
|
+
return e;
|
|
2372
|
+
};
|
|
2373
|
+
const explicitlyNonNull = (node) => {
|
|
2374
|
+
let e = node;
|
|
2375
|
+
while (ts.isParenthesizedExpression(e) || ts.isAsExpression(e) || ts.isTypeAssertion(e) || ts.isNonNullExpression(e)) {
|
|
2376
|
+
if (ts.isNonNullExpression(e))
|
|
2377
|
+
return true;
|
|
2378
|
+
e = e.expression;
|
|
2379
|
+
}
|
|
2380
|
+
return false;
|
|
2381
|
+
};
|
|
2382
|
+
const isArrayRead = (node) => {
|
|
2383
|
+
const e = peel(node);
|
|
2384
|
+
if (!ts.isElementAccessExpression(e))
|
|
2385
|
+
return false;
|
|
2386
|
+
return this.mapTypeOf(this.typeOf(e.expression))?.kind === "array";
|
|
2387
|
+
};
|
|
2388
|
+
const isDynamicObjectEntryRead = (node) => {
|
|
2389
|
+
const read = peel(node);
|
|
2390
|
+
if (!ts.isElementAccessExpression(read) || !ts.isIdentifier(read.expression))
|
|
2391
|
+
return false;
|
|
2392
|
+
const source = symbolOf(read.expression);
|
|
2393
|
+
const declaration = source ? this.checker.valueDeclarationOf(source) : undefined;
|
|
2394
|
+
if (!declaration || !ts.isVariableDeclaration(declaration) || !declaration.initializer)
|
|
2395
|
+
return false;
|
|
2396
|
+
const init = peel(declaration.initializer);
|
|
2397
|
+
if (!ts.isCallExpression(init) || !ts.isPropertyAccessExpression(init.expression))
|
|
2398
|
+
return false;
|
|
2399
|
+
if (init.expression.name.text !== "entries" || !this.isStdlibGlobal(init.expression.expression, "Object"))
|
|
2400
|
+
return false;
|
|
2401
|
+
const object = init.arguments[0];
|
|
2402
|
+
const objectType = object ? this.mapTypeOf(this.typeOf(object)) : null;
|
|
2403
|
+
return objectType?.kind === "dyn" || objectType?.kind === "jsval";
|
|
2404
|
+
};
|
|
2405
|
+
const addUndefined = (t) => {
|
|
2406
|
+
if (t.kind === "union")
|
|
2407
|
+
return this.armTag(t.unionId, UNDEFINED_T) >= 0 ? t : this.withUndefinedArmOf(t) ?? t;
|
|
2408
|
+
if (t.kind === "void" || t.kind === "dyn" || t.kind === "jsval" || t.kind === "generator")
|
|
2409
|
+
return t;
|
|
2410
|
+
return this.withUndefinedArm(t);
|
|
2411
|
+
};
|
|
2412
|
+
const fieldName = (node) => node.name.text;
|
|
2413
|
+
const mayBeOptional = (node) => {
|
|
2414
|
+
// `xs[i]!` is the explicit proven-present form. Its array read keeps
|
|
2415
|
+
// the established dense bounds trap and must not promote the enclosing
|
|
2416
|
+
// function/global ABI to `T | undefined`.
|
|
2417
|
+
if (explicitlyNonNull(node))
|
|
2418
|
+
return false;
|
|
2419
|
+
const e = peel(node);
|
|
2420
|
+
if (isArrayRead(e))
|
|
2421
|
+
return true;
|
|
2422
|
+
if (ts.isCallExpression(e) && this.runtimeOptionalReduceTypes.has(e))
|
|
2423
|
+
return true;
|
|
2424
|
+
if (ts.isCallExpression(e) &&
|
|
2425
|
+
ts.isPropertyAccessExpression(e.expression) &&
|
|
2426
|
+
(e.expression.name.text === "pop" || e.expression.name.text === "shift") &&
|
|
2427
|
+
this.mapTypeOf(this.typeOf(e))?.kind === "union" &&
|
|
2428
|
+
this.armTag(this.mapTypeOf(this.typeOf(e)).unionId, UNDEFINED_T) >= 0)
|
|
2429
|
+
return true;
|
|
2430
|
+
if (ts.isIdentifier(e))
|
|
2431
|
+
return optionalSymbols.has(symbolOf(e) ?? {});
|
|
2432
|
+
if (ts.isCallExpression(e) && ts.isIdentifier(e.expression)) {
|
|
2433
|
+
return optionalReturns.has(symbolOf(e.expression) ?? {});
|
|
2434
|
+
}
|
|
2435
|
+
if (ts.isCallExpression(e) && ts.isPropertyAccessExpression(e.expression)) {
|
|
2436
|
+
return optionalReturns.has(symbolOf(e.expression.name) ?? {});
|
|
2437
|
+
}
|
|
2438
|
+
if (ts.isConditionalExpression(e)) {
|
|
2439
|
+
return mayBeOptional(e.whenTrue) || mayBeOptional(e.whenFalse);
|
|
2440
|
+
}
|
|
2441
|
+
if (ts.isBinaryExpression(e)) {
|
|
2442
|
+
const op = e.operatorToken.kind;
|
|
2443
|
+
if (op === ts.SyntaxKind.AmpersandAmpersandToken ||
|
|
2444
|
+
op === ts.SyntaxKind.BarBarToken ||
|
|
2445
|
+
op === ts.SyntaxKind.QuestionQuestionToken)
|
|
2446
|
+
return mayBeOptional(e.left) || mayBeOptional(e.right);
|
|
2447
|
+
}
|
|
2448
|
+
if (ts.isPropertyAccessExpression(e) && ts.isIdentifier(e.expression)) {
|
|
2449
|
+
const fields = optionalFields.get(symbolOf(e.expression) ?? {});
|
|
2450
|
+
return fields?.has(fieldName(e)) ?? false;
|
|
2451
|
+
}
|
|
2452
|
+
return false;
|
|
2453
|
+
};
|
|
2454
|
+
const optionalStringArithmeticType = (node) => {
|
|
2455
|
+
const e = peel(node);
|
|
2456
|
+
if (!ts.isBinaryExpression(e) || e.operatorToken.kind !== ts.SyntaxKind.PlusToken)
|
|
2457
|
+
return null;
|
|
2458
|
+
const stringArrayRead = (part) => {
|
|
2459
|
+
const t = this.mapTypeOf(this.typeOf(part));
|
|
2460
|
+
const p = peel(part);
|
|
2461
|
+
if (t?.kind !== "string" || !ts.isElementAccessExpression(p))
|
|
2462
|
+
return false;
|
|
2463
|
+
const recv = this.mapTypeOf(this.typeOf(p.expression));
|
|
2464
|
+
return recv?.kind === "array" && recv.elem.kind === "string";
|
|
2465
|
+
};
|
|
2466
|
+
const primitive = (part) => {
|
|
2467
|
+
const t = this.mapTypeOf(this.typeOf(part));
|
|
2468
|
+
return t?.kind === "f64" || t?.kind === "string";
|
|
2469
|
+
};
|
|
2470
|
+
if (!stringArrayRead(e.left) && !stringArrayRead(e.right))
|
|
2471
|
+
return null;
|
|
2472
|
+
if (!(primitive(e.left) || stringArrayRead(e.left)) || !(primitive(e.right) || stringArrayRead(e.right)))
|
|
2473
|
+
return null;
|
|
2474
|
+
return { kind: "union", unionId: this.unions.intern([F64, STRING]) };
|
|
2475
|
+
};
|
|
2476
|
+
const noteField = (decl, name) => {
|
|
2477
|
+
if (!ts.isIdentifier(decl.name))
|
|
2478
|
+
return false;
|
|
2479
|
+
const symbol = symbolOf(decl.name);
|
|
2480
|
+
if (!symbol)
|
|
2481
|
+
return false;
|
|
2482
|
+
return noteFieldSymbol(symbol, name);
|
|
2483
|
+
};
|
|
2484
|
+
const noteFieldSymbol = (symbol, name) => {
|
|
2485
|
+
const set = optionalFields.get(symbol) ?? new Set();
|
|
2486
|
+
const before = set.size;
|
|
2487
|
+
set.add(name);
|
|
2488
|
+
optionalFields.set(symbol, set);
|
|
2489
|
+
return set.size !== before;
|
|
2490
|
+
};
|
|
2491
|
+
const scanPattern = (name, init) => {
|
|
2492
|
+
if (!ts.isArrayBindingPattern(name))
|
|
2493
|
+
return false;
|
|
2494
|
+
const sourceType = this.mapTypeOf(this.typeOf(init));
|
|
2495
|
+
if (sourceType?.kind !== "array")
|
|
2496
|
+
return false;
|
|
2497
|
+
let changed = false;
|
|
2498
|
+
name.elements.forEach((el) => {
|
|
2499
|
+
if (ts.isOmittedExpression(el) || el.name === undefined || el.dotDotDotToken)
|
|
2500
|
+
return;
|
|
2501
|
+
if (ts.isIdentifier(el.name)) {
|
|
2502
|
+
const symbol = symbolOf(el.name);
|
|
2503
|
+
if (symbol && !el.initializer && !optionalSymbols.has(symbol)) {
|
|
2504
|
+
optionalSymbols.add(symbol);
|
|
2505
|
+
// Indexed flow facts can survive a mutating method call in the
|
|
2506
|
+
// checker. The array's element ABI describes every value a
|
|
2507
|
+
// position may hold after that call, so bind from that type.
|
|
2508
|
+
const mapped = sourceType.elem;
|
|
2509
|
+
if (mapped && mapped.kind !== "void" && mapped.kind !== "dyn" && mapped.kind !== "jsval") {
|
|
2510
|
+
const widened = mapped.kind === "union" ? this.withUndefinedArmOf(mapped) : this.withUndefinedArm(mapped);
|
|
2511
|
+
if (widened) {
|
|
2512
|
+
this.runtimeOptionalBindingTypes.set(symbol, widened);
|
|
2513
|
+
const global = this.globalsBySymbol.get(symbol);
|
|
2514
|
+
if (global) {
|
|
2515
|
+
global.type = widened;
|
|
2516
|
+
this.runtimeOptionalGlobals.add(global);
|
|
2517
|
+
}
|
|
2518
|
+
}
|
|
2519
|
+
}
|
|
2520
|
+
changed = true;
|
|
2521
|
+
}
|
|
2522
|
+
}
|
|
2523
|
+
});
|
|
2524
|
+
return changed;
|
|
2525
|
+
};
|
|
2526
|
+
const hofCallbackIndices = (name, hasInitialValue) => {
|
|
2527
|
+
if (name === "reduce" || name === "reduceRight")
|
|
2528
|
+
return hasInitialValue ? [1] : [0, 1];
|
|
2529
|
+
if (name === "map" || name === "filter" || name === "forEach" ||
|
|
2530
|
+
name === "find" || name === "findIndex" || name === "findLast" ||
|
|
2531
|
+
name === "findLastIndex" || name === "some" || name === "every" ||
|
|
2532
|
+
name === "flatMap")
|
|
2533
|
+
return [0];
|
|
2534
|
+
return null;
|
|
2535
|
+
};
|
|
2536
|
+
const bodyReturnsOptional = (fn) => {
|
|
2537
|
+
if (!fn.body)
|
|
2538
|
+
return false;
|
|
2539
|
+
if (!ts.isBlock(fn.body))
|
|
2540
|
+
return mayBeOptional(fn.body);
|
|
2541
|
+
let found = false;
|
|
2542
|
+
ts.walkPreorder(fn.body, (node) => {
|
|
2543
|
+
if (node !== fn.body && (ts.isFunctionDeclaration(node) || ts.isFunctionExpression(node) || ts.isArrowFunction(node) || ts.isMethodDeclaration(node)))
|
|
2544
|
+
return "skip";
|
|
2545
|
+
if (ts.isReturnStatement(node) && node.expression && mayBeOptional(node.expression))
|
|
2546
|
+
found = true;
|
|
2547
|
+
return undefined;
|
|
2548
|
+
});
|
|
2549
|
+
return found;
|
|
2550
|
+
};
|
|
2551
|
+
const promoteHofCallback = (callback, parameterIndices, seen = new Set()) => {
|
|
2552
|
+
let changed = false;
|
|
2553
|
+
const checkerTypeContainsTypeParameter = (type) => {
|
|
2554
|
+
if ((type.flags & ts.TypeFlags.TypeParameter) !== 0)
|
|
2555
|
+
return true;
|
|
2556
|
+
return type.isUnionType() && ts.constituentTypes(type).some(checkerTypeContainsTypeParameter);
|
|
2557
|
+
};
|
|
2558
|
+
const promoteNode = (fn) => {
|
|
2559
|
+
for (const parameterIndex of parameterIndices) {
|
|
2560
|
+
const parameter = fn.parameters[parameterIndex];
|
|
2561
|
+
if (!parameter)
|
|
2562
|
+
continue;
|
|
2563
|
+
const symbol = ts.isIdentifier(parameter.name) ? symbolOf(parameter.name) : null;
|
|
2564
|
+
const checkerType = this.typeOf(parameter.name);
|
|
2565
|
+
const mapped = this.mapTypeOf(checkerType);
|
|
2566
|
+
// Generic callbacks are monomorphized by their call-site binder.
|
|
2567
|
+
// During this prepass the type parameter has no static ABI yet;
|
|
2568
|
+
// asking irTypeOf to invent one poisons otherwise valid generic
|
|
2569
|
+
// value/HOF programs. Leave that parameter at its generic
|
|
2570
|
+
// signature and let the normal instantiation path specialize it.
|
|
2571
|
+
const current = mapped ??
|
|
2572
|
+
(checkerTypeContainsTypeParameter(checkerType) ? null : this.irTypeOf(parameter.name));
|
|
2573
|
+
if (!current)
|
|
2574
|
+
continue;
|
|
2575
|
+
if (!ts.isIdentifier(parameter.name)) {
|
|
2576
|
+
const widened = this.runtimeOptionalType(current);
|
|
2577
|
+
const previous = this.runtimeOptionalPatternTypes.get(parameter.name);
|
|
2578
|
+
this.runtimeOptionalPatternTypes.set(parameter.name, widened);
|
|
2579
|
+
if (!previous || !typeEquals(previous, widened))
|
|
2580
|
+
changed = true;
|
|
2581
|
+
}
|
|
2582
|
+
if (symbol) {
|
|
2583
|
+
const widened = this.runtimeOptionalType(current);
|
|
2584
|
+
const previous = this.runtimeOptionalBindingTypes.get(symbol);
|
|
2585
|
+
this.runtimeOptionalBindingTypes.set(symbol, widened);
|
|
2586
|
+
if (!previous || !typeEquals(previous, widened))
|
|
2587
|
+
changed = true;
|
|
2588
|
+
if (!optionalSymbols.has(symbol)) {
|
|
2589
|
+
optionalSymbols.add(symbol);
|
|
2590
|
+
changed = true;
|
|
2591
|
+
}
|
|
2592
|
+
}
|
|
2593
|
+
}
|
|
2594
|
+
const fnType = this.mapTypeOf(this.typeOf(fn));
|
|
2595
|
+
if (fnType?.kind === "func" && bodyReturnsOptional(fn)) {
|
|
2596
|
+
const widenedReturn = this.runtimeOptionalType(fnType.ret);
|
|
2597
|
+
const previousReturn = this.runtimeOptionalFunctionReturnType(fn, fnType.ret);
|
|
2598
|
+
this.runtimeOptionalFunctionReturns.set(fn, widenedReturn);
|
|
2599
|
+
if (!typeEquals(previousReturn, widenedReturn))
|
|
2600
|
+
changed = true;
|
|
2601
|
+
}
|
|
2602
|
+
};
|
|
2603
|
+
if (ts.isArrowFunction(callback) || ts.isFunctionExpression(callback) || ts.isFunctionDeclaration(callback) || ts.isMethodDeclaration(callback)) {
|
|
2604
|
+
promoteNode(callback);
|
|
2605
|
+
return changed;
|
|
2606
|
+
}
|
|
2607
|
+
const callbackName = ts.isIdentifier(callback)
|
|
2608
|
+
? callback
|
|
2609
|
+
: ts.isPropertyAccessExpression(callback)
|
|
2610
|
+
? callback.name
|
|
2611
|
+
: null;
|
|
2612
|
+
if (!callbackName)
|
|
2613
|
+
return false;
|
|
2614
|
+
const symbol = symbolOf(callbackName);
|
|
2615
|
+
if (!symbol || seen.has(symbol))
|
|
2616
|
+
return false;
|
|
2617
|
+
// Generic function values are pinned and monomorphized by the normal
|
|
2618
|
+
// contextual-value path when the HOF lowers its callback. Promoting
|
|
2619
|
+
// their type-parameter body here either asks for an impossible
|
|
2620
|
+
// monomorphic ABI or poisons valid generic value programs.
|
|
2621
|
+
if (this.genericFnsBySymbol.has(symbol))
|
|
2622
|
+
return false;
|
|
2623
|
+
seen.add(symbol);
|
|
2624
|
+
const declaration = this.checker.valueDeclarationOf(symbol);
|
|
2625
|
+
if (declaration && ts.isVariableDeclaration(declaration) && declaration.initializer) {
|
|
2626
|
+
if (promoteHofCallback(peel(declaration.initializer), parameterIndices, seen))
|
|
2627
|
+
changed = true;
|
|
2628
|
+
}
|
|
2629
|
+
const sig = signatureBySymbol.get(symbol);
|
|
2630
|
+
if (sig) {
|
|
2631
|
+
for (const parameterIndex of parameterIndices) {
|
|
2632
|
+
if (!sig.params[parameterIndex])
|
|
2633
|
+
continue;
|
|
2634
|
+
const before = sig.params[parameterIndex].type;
|
|
2635
|
+
sig.params[parameterIndex].type = this.runtimeOptionalType(before);
|
|
2636
|
+
if (!typeEquals(before, sig.params[parameterIndex].type))
|
|
2637
|
+
changed = true;
|
|
2638
|
+
}
|
|
2639
|
+
}
|
|
2640
|
+
const fn = functionDeclBySymbol.get(symbol);
|
|
2641
|
+
if (fn)
|
|
2642
|
+
promoteNode(fn);
|
|
2643
|
+
const valueType = this.runtimeOptionalBindingTypes.get(symbol) ?? this.mapTypeOf(this.typeOf(callback));
|
|
2644
|
+
if (valueType?.kind === "func") {
|
|
2645
|
+
const params = valueType.params.slice();
|
|
2646
|
+
for (const parameterIndex of parameterIndices) {
|
|
2647
|
+
if (params[parameterIndex])
|
|
2648
|
+
params[parameterIndex] = this.runtimeOptionalType(params[parameterIndex]);
|
|
2649
|
+
}
|
|
2650
|
+
const promoted = { ...valueType, params };
|
|
2651
|
+
const previous = this.runtimeOptionalBindingTypes.get(symbol);
|
|
2652
|
+
this.runtimeOptionalBindingTypes.set(symbol, promoted);
|
|
2653
|
+
if (!previous || !typeEquals(previous, promoted))
|
|
2654
|
+
changed = true;
|
|
2655
|
+
const global = this.globalsBySymbol.get(symbol);
|
|
2656
|
+
if (global)
|
|
2657
|
+
global.type = promoted;
|
|
2658
|
+
// A callback-typed parameter used by a native array HOF changes
|
|
2659
|
+
// the containing function's own call ABI. Record that edge in the
|
|
2660
|
+
// already-collected signature as well as on the body binding, so a
|
|
2661
|
+
// caller can promote the concrete callback value it supplies on a
|
|
2662
|
+
// later fixed-point pass (`wrapper(xs, predicate)` ->
|
|
2663
|
+
// `xs.some(predicate)`).
|
|
2664
|
+
if (declaration && ts.isParameter(declaration)) {
|
|
2665
|
+
const owner = declaration.parent;
|
|
2666
|
+
let ownerSymbol = null;
|
|
2667
|
+
if (ts.isFunctionDeclaration(owner))
|
|
2668
|
+
ownerSymbol = declSymbolOf(this, owner) ?? null;
|
|
2669
|
+
else if ((ts.isMethodDeclaration(owner) || ts.isGetAccessorDeclaration(owner) || ts.isSetAccessorDeclaration(owner)) &&
|
|
2670
|
+
owner.name)
|
|
2671
|
+
ownerSymbol = symbolOf(owner.name);
|
|
2672
|
+
const ownerSig = ownerSymbol ? signatureBySymbol.get(ownerSymbol) : undefined;
|
|
2673
|
+
const ownerIndex = ts.isFunctionLike(owner) ? owner.parameters.indexOf(declaration) : -1;
|
|
2674
|
+
const slot = ownerIndex >= 0 ? ownerSig?.params[ownerIndex] : undefined;
|
|
2675
|
+
if (slot && !typeEquals(slot.type, promoted)) {
|
|
2676
|
+
slot.type = promoted;
|
|
2677
|
+
changed = true;
|
|
2678
|
+
}
|
|
2679
|
+
}
|
|
2680
|
+
}
|
|
2681
|
+
return changed;
|
|
2682
|
+
};
|
|
2683
|
+
const callbackReturnsOptional = (callback, seen = new Set()) => {
|
|
2684
|
+
const node = peel(callback);
|
|
2685
|
+
if (ts.isArrowFunction(node) || ts.isFunctionExpression(node))
|
|
2686
|
+
return bodyReturnsOptional(node);
|
|
2687
|
+
const symbol = symbolOf(ts.isPropertyAccessExpression(node) ? node.name : node);
|
|
2688
|
+
if (!symbol || seen.has(symbol))
|
|
2689
|
+
return false;
|
|
2690
|
+
seen.add(symbol);
|
|
2691
|
+
const fn = functionDeclBySymbol.get(symbol);
|
|
2692
|
+
if (fn)
|
|
2693
|
+
return bodyReturnsOptional(fn);
|
|
2694
|
+
const decl = this.checker.valueDeclarationOf(symbol);
|
|
2695
|
+
return !!decl && ts.isVariableDeclaration(decl) && !!decl.initializer && callbackReturnsOptional(decl.initializer, seen);
|
|
2696
|
+
};
|
|
2697
|
+
const scanFile = (sf) => {
|
|
2698
|
+
let changed = false;
|
|
2699
|
+
ts.walkPreorder(sf, (node) => {
|
|
2700
|
+
if (ts.isVariableDeclaration(node)) {
|
|
2701
|
+
if (node.initializer) {
|
|
2702
|
+
if (ts.isIdentifier(node.name) && isDynamicObjectEntryRead(node.initializer)) {
|
|
2703
|
+
const symbol = symbolOf(node.name);
|
|
2704
|
+
if (symbol && !dynamicObjectEntryRows.has(symbol)) {
|
|
2705
|
+
dynamicObjectEntryRows.add(symbol);
|
|
2706
|
+
changed = true;
|
|
2707
|
+
}
|
|
2708
|
+
}
|
|
2709
|
+
else if (ts.isIdentifier(node.name) && mayBeOptional(node.initializer)) {
|
|
2710
|
+
const symbol = symbolOf(node.name);
|
|
2711
|
+
if (symbol && !optionalSymbols.has(symbol)) {
|
|
2712
|
+
optionalSymbols.add(symbol);
|
|
2713
|
+
changed = true;
|
|
2714
|
+
}
|
|
2715
|
+
}
|
|
2716
|
+
if (ts.isIdentifier(node.name)) {
|
|
2717
|
+
const valueSymbol = ts.isIdentifier(peel(node.initializer))
|
|
2718
|
+
? symbolOf(peel(node.initializer))
|
|
2719
|
+
: ts.isPropertyAccessExpression(peel(node.initializer))
|
|
2720
|
+
? symbolOf(peel(node.initializer).name)
|
|
2721
|
+
: null;
|
|
2722
|
+
const symbol = symbolOf(node.name);
|
|
2723
|
+
const sourceSig = valueSymbol ? signatureBySymbol.get(valueSymbol) : undefined;
|
|
2724
|
+
const sourceOptional = !!sourceSig &&
|
|
2725
|
+
(optionalReturns.has(valueSymbol) ||
|
|
2726
|
+
(sourceSig.returnType.kind === "union" && this.armTag(sourceSig.returnType.unionId, UNDEFINED_T) >= 0) ||
|
|
2727
|
+
sourceSig.params.some((p) => p.type.kind === "union" && this.armTag(p.type.unionId, UNDEFINED_T) >= 0));
|
|
2728
|
+
if (symbol && valueSymbol && sourceOptional) {
|
|
2729
|
+
const aliasNew = !optionalReturns.has(symbol);
|
|
2730
|
+
if (aliasNew)
|
|
2731
|
+
optionalReturns.add(symbol);
|
|
2732
|
+
if (aliasNew)
|
|
2733
|
+
changed = true;
|
|
2734
|
+
const valueType = this.mapTypeOf(this.typeOf(node.name));
|
|
2735
|
+
if (valueType?.kind === "func" && sourceSig) {
|
|
2736
|
+
const promotedFn = {
|
|
2737
|
+
...valueType,
|
|
2738
|
+
params: sourceSig.params.map((p) => p.type),
|
|
2739
|
+
ret: sourceSig.returnType,
|
|
2740
|
+
};
|
|
2741
|
+
const previous = this.runtimeOptionalBindingTypes.get(symbol);
|
|
2742
|
+
if (!previous || !typeEquals(previous, promotedFn)) {
|
|
2743
|
+
this.runtimeOptionalBindingTypes.set(symbol, promotedFn);
|
|
2744
|
+
const global = this.globalsBySymbol.get(symbol);
|
|
2745
|
+
if (global)
|
|
2746
|
+
global.type = promotedFn;
|
|
2747
|
+
changed = true;
|
|
2748
|
+
}
|
|
2749
|
+
}
|
|
2750
|
+
}
|
|
2751
|
+
}
|
|
2752
|
+
if (scanPattern(node.name, node.initializer))
|
|
2753
|
+
changed = true;
|
|
2754
|
+
if (ts.isIdentifier(node.name) && ts.isObjectLiteralExpression(peel(node.initializer))) {
|
|
2755
|
+
const object = peel(node.initializer);
|
|
2756
|
+
for (const prop of object.properties) {
|
|
2757
|
+
if (!ts.isPropertyAssignment(prop) && !ts.isShorthandPropertyAssignment(prop))
|
|
2758
|
+
continue;
|
|
2759
|
+
const name = prop.name;
|
|
2760
|
+
if (!name || !(ts.isIdentifier(name) || ts.isStringLiteral(name) || ts.isNumericLiteral(name)))
|
|
2761
|
+
continue;
|
|
2762
|
+
const value = ts.isPropertyAssignment(prop) ? prop.initializer : prop.name;
|
|
2763
|
+
if (mayBeOptional(value) && noteField(node, name.text))
|
|
2764
|
+
changed = true;
|
|
2765
|
+
}
|
|
2766
|
+
}
|
|
2767
|
+
}
|
|
2768
|
+
}
|
|
2769
|
+
if (ts.isBinaryExpression(node) &&
|
|
2770
|
+
(node.operatorToken.kind === ts.SyntaxKind.EqualsToken ||
|
|
2771
|
+
node.operatorToken.kind === ts.SyntaxKind.QuestionQuestionEqualsToken ||
|
|
2772
|
+
node.operatorToken.kind === ts.SyntaxKind.BarBarEqualsToken ||
|
|
2773
|
+
node.operatorToken.kind === ts.SyntaxKind.AmpersandAmpersandEqualsToken) &&
|
|
2774
|
+
mayBeOptional(node.right)) {
|
|
2775
|
+
if (ts.isIdentifier(node.left)) {
|
|
2776
|
+
const symbol = symbolOf(node.left);
|
|
2777
|
+
if (symbol && !optionalSymbols.has(symbol)) {
|
|
2778
|
+
optionalSymbols.add(symbol);
|
|
2779
|
+
changed = true;
|
|
2780
|
+
}
|
|
2781
|
+
}
|
|
2782
|
+
else if (ts.isPropertyAccessExpression(node.left) && ts.isIdentifier(node.left.expression)) {
|
|
2783
|
+
const symbol = symbolOf(node.left.expression);
|
|
2784
|
+
if (symbol && noteFieldSymbol(symbol, node.left.name.text))
|
|
2785
|
+
changed = true;
|
|
2786
|
+
}
|
|
2787
|
+
}
|
|
2788
|
+
if (ts.isCallExpression(node) && ts.isPropertyAccessExpression(node.expression)) {
|
|
2789
|
+
const callbackIndices = hofCallbackIndices(node.expression.name.text, node.arguments.length >= 2);
|
|
2790
|
+
const receiverNode = node.expression.expression;
|
|
2791
|
+
const receiver = this.mapTypeOf(this.typeOf(receiverNode));
|
|
2792
|
+
const callback = callbackIndices === null ? undefined : node.arguments[0];
|
|
2793
|
+
const tuple = receiver?.kind === "record" && this.shapes.get(receiver.shapeId)?.tuple === true;
|
|
2794
|
+
// A JS/evolving-any array may have acquired a precise FLOW type
|
|
2795
|
+
// at this call while its actual binding remains a checked-dynamic
|
|
2796
|
+
// array. Its callback consumes DYN values through runtime method
|
|
2797
|
+
// dispatch, so widening that callback to the native array value
|
|
2798
|
+
// ABI makes it impossible to box back into DYN. Judge a simple
|
|
2799
|
+
// binding by its declaration type, the same storage fact local
|
|
2800
|
+
// declaration lowering uses.
|
|
2801
|
+
let nativeArrayReceiver = receiver?.kind === "array" || tuple;
|
|
2802
|
+
if (nativeArrayReceiver && ts.isIdentifier(receiverNode)) {
|
|
2803
|
+
const receiverSymbol = symbolOf(receiverNode);
|
|
2804
|
+
const declaration = receiverSymbol ? this.checker.valueDeclarationOf(receiverSymbol) : undefined;
|
|
2805
|
+
if (declaration && ts.isVariableDeclaration(declaration)) {
|
|
2806
|
+
const declared = this.mapTypeOf(this.typeOf(declaration.name));
|
|
2807
|
+
if (declared?.kind !== "array" && !(declared?.kind === "record" && this.shapes.get(declared.shapeId)?.tuple === true)) {
|
|
2808
|
+
nativeArrayReceiver = false;
|
|
2809
|
+
}
|
|
2810
|
+
}
|
|
2811
|
+
}
|
|
2812
|
+
if (nativeArrayReceiver && callback && !ts.isSpreadElement(callback)) {
|
|
2813
|
+
if (promoteHofCallback(callback, callbackIndices))
|
|
2814
|
+
changed = true;
|
|
2815
|
+
const method = node.expression.name.text;
|
|
2816
|
+
if ((method === "reduce" || method === "reduceRight") &&
|
|
2817
|
+
(node.arguments.length < 2 || callbackReturnsOptional(callback))) {
|
|
2818
|
+
if (promoteHofCallback(callback, [0, 1]))
|
|
2819
|
+
changed = true;
|
|
2820
|
+
const result = this.runtimeOptionalType(this.irTypeOf(node));
|
|
2821
|
+
const previous = this.runtimeOptionalReduceTypes.get(node);
|
|
2822
|
+
if (!previous || !typeEquals(previous, result)) {
|
|
2823
|
+
this.runtimeOptionalReduceTypes.set(node, result);
|
|
2824
|
+
changed = true;
|
|
2825
|
+
}
|
|
2826
|
+
}
|
|
2827
|
+
}
|
|
2828
|
+
}
|
|
2829
|
+
if (ts.isCallExpression(node) &&
|
|
2830
|
+
(ts.isIdentifier(node.expression) || ts.isPropertyAccessExpression(node.expression))) {
|
|
2831
|
+
const calleeNode = ts.isIdentifier(node.expression) ? node.expression : node.expression.name;
|
|
2832
|
+
const symbol = symbolOf(calleeNode);
|
|
2833
|
+
if (!symbol)
|
|
2834
|
+
return;
|
|
2835
|
+
const sig = signatureBySymbol.get(symbol);
|
|
2836
|
+
if (!sig) {
|
|
2837
|
+
const decl = this.checker.valueDeclarationOf(symbol);
|
|
2838
|
+
if (decl && ts.isVariableDeclaration(decl) && decl.initializer && ts.isIdentifier(decl.initializer)) {
|
|
2839
|
+
const source = symbolOf(decl.initializer);
|
|
2840
|
+
const sourceSig = source ? signatureBySymbol.get(source) : undefined;
|
|
2841
|
+
if (sourceSig) {
|
|
2842
|
+
node.arguments.forEach((arg, i) => {
|
|
2843
|
+
if (ts.isSpreadElement(arg) || !mayBeOptional(arg) || !sourceSig.params[i])
|
|
2844
|
+
return;
|
|
2845
|
+
const before = sourceSig.params[i].type;
|
|
2846
|
+
sourceSig.params[i].type = this.runtimeOptionalType(before);
|
|
2847
|
+
if (!typeEquals(before, sourceSig.params[i].type)) {
|
|
2848
|
+
changed = true;
|
|
2849
|
+
const sourceFn = functionDeclBySymbol.get(source);
|
|
2850
|
+
const parameter = sourceFn?.parameters[i];
|
|
2851
|
+
if (parameter) {
|
|
2852
|
+
for (const bound of boundIdentifiersOf(parameter.name)) {
|
|
2853
|
+
const boundSymbol = symbolOf(bound);
|
|
2854
|
+
if (boundSymbol && !optionalSymbols.has(boundSymbol)) {
|
|
2855
|
+
optionalSymbols.add(boundSymbol);
|
|
2856
|
+
changed = true;
|
|
2857
|
+
}
|
|
2858
|
+
}
|
|
2859
|
+
}
|
|
2860
|
+
}
|
|
2861
|
+
});
|
|
2862
|
+
}
|
|
2863
|
+
}
|
|
2864
|
+
return;
|
|
2865
|
+
}
|
|
2866
|
+
node.arguments.forEach((arg, i) => {
|
|
2867
|
+
const callbackSlot = sig.params[i]?.type;
|
|
2868
|
+
if (callbackSlot?.kind === "func" && !ts.isSpreadElement(arg)) {
|
|
2869
|
+
const optionalCallbackParams = callbackSlot.params.flatMap((type, index) => type.kind === "union" && this.armTag(type.unionId, UNDEFINED_T) >= 0 ? [index] : []);
|
|
2870
|
+
if (optionalCallbackParams.length > 0 && promoteHofCallback(arg, optionalCallbackParams)) {
|
|
2871
|
+
changed = true;
|
|
2872
|
+
}
|
|
2873
|
+
}
|
|
2874
|
+
if (ts.isSpreadElement(arg) || !mayBeOptional(arg) || !sig.params[i])
|
|
2875
|
+
return;
|
|
2876
|
+
const set = optionalParams.get(symbol) ?? new Set();
|
|
2877
|
+
const before = set.size;
|
|
2878
|
+
set.add(i);
|
|
2879
|
+
optionalParams.set(symbol, set);
|
|
2880
|
+
if (set.size !== before) {
|
|
2881
|
+
changed = true;
|
|
2882
|
+
const callee = functionDeclBySymbol.get(symbol);
|
|
2883
|
+
const parameter = callee?.parameters[i];
|
|
2884
|
+
if (parameter) {
|
|
2885
|
+
for (const bound of boundIdentifiersOf(parameter.name)) {
|
|
2886
|
+
const boundSymbol = symbolOf(bound);
|
|
2887
|
+
if (boundSymbol && !optionalSymbols.has(boundSymbol)) {
|
|
2888
|
+
optionalSymbols.add(boundSymbol);
|
|
2889
|
+
changed = true;
|
|
2890
|
+
}
|
|
2891
|
+
}
|
|
2892
|
+
}
|
|
2893
|
+
}
|
|
2894
|
+
});
|
|
2895
|
+
}
|
|
2896
|
+
});
|
|
2897
|
+
return changed;
|
|
2898
|
+
};
|
|
2899
|
+
const scanReturns = (symbol, decl) => {
|
|
2900
|
+
if (!decl.body)
|
|
2901
|
+
return false;
|
|
2902
|
+
let changed = false;
|
|
2903
|
+
if (!ts.isBlock(decl.body) && mayBeOptional(decl.body) && !optionalReturns.has(symbol)) {
|
|
2904
|
+
optionalReturns.add(symbol);
|
|
2905
|
+
changed = true;
|
|
2906
|
+
}
|
|
2907
|
+
if (!ts.isBlock(decl.body)) {
|
|
2908
|
+
const arithmetic = optionalStringArithmeticType(decl.body);
|
|
2909
|
+
if (arithmetic && !arithmeticReturns.has(symbol)) {
|
|
2910
|
+
arithmeticReturns.set(symbol, arithmetic);
|
|
2911
|
+
changed = true;
|
|
2912
|
+
}
|
|
2913
|
+
}
|
|
2914
|
+
ts.walkPreorder(decl.body, (node) => {
|
|
2915
|
+
if (node !== decl.body && (ts.isFunctionDeclaration(node) || ts.isFunctionExpression(node) || ts.isArrowFunction(node) || ts.isMethodDeclaration(node)))
|
|
2916
|
+
return "skip";
|
|
2917
|
+
if (ts.isReturnStatement(node) && node.expression && mayBeOptional(node.expression) && !optionalReturns.has(symbol)) {
|
|
2918
|
+
optionalReturns.add(symbol);
|
|
2919
|
+
changed = true;
|
|
2920
|
+
}
|
|
2921
|
+
if (ts.isReturnStatement(node) && node.expression) {
|
|
2922
|
+
const arithmetic = optionalStringArithmeticType(node.expression);
|
|
2923
|
+
if (arithmetic && !arithmeticReturns.has(symbol)) {
|
|
2924
|
+
arithmeticReturns.set(symbol, arithmetic);
|
|
2925
|
+
changed = true;
|
|
2926
|
+
}
|
|
2927
|
+
}
|
|
2928
|
+
return undefined;
|
|
2929
|
+
});
|
|
2930
|
+
return changed;
|
|
2931
|
+
};
|
|
2932
|
+
let changed = true;
|
|
2933
|
+
while (changed) {
|
|
2934
|
+
changed = false;
|
|
2935
|
+
for (const sf of sourceFiles)
|
|
2936
|
+
if (scanFile(sf))
|
|
2937
|
+
changed = true;
|
|
2938
|
+
for (const [symbol, decl] of functionDeclBySymbol)
|
|
2939
|
+
if (scanReturns(symbol, decl))
|
|
2940
|
+
changed = true;
|
|
2941
|
+
}
|
|
2942
|
+
for (const [symbol, sig] of signatureBySymbol) {
|
|
2943
|
+
const params = optionalParams.get(symbol);
|
|
2944
|
+
if (params) {
|
|
2945
|
+
for (const i of params) {
|
|
2946
|
+
const shape = sig.params[i];
|
|
2947
|
+
if (shape)
|
|
2948
|
+
shape.type = addUndefined(shape.type);
|
|
2949
|
+
}
|
|
2950
|
+
}
|
|
2951
|
+
if (optionalReturns.has(symbol)) {
|
|
2952
|
+
const next = sig.returnType.kind === "promise"
|
|
2953
|
+
? { kind: "promise", inner: addUndefined(sig.returnType.inner) }
|
|
2954
|
+
: addUndefined(sig.returnType);
|
|
2955
|
+
sig.returnType = next;
|
|
2956
|
+
if (sig.top)
|
|
2957
|
+
sig.top.returnType = next;
|
|
2958
|
+
if (sig.method)
|
|
2959
|
+
sig.method.ret = next;
|
|
2960
|
+
}
|
|
2961
|
+
const arithmetic = arithmeticReturns.get(symbol);
|
|
2962
|
+
if (arithmetic && sig.returnType.kind !== "promise") {
|
|
2963
|
+
sig.returnType = arithmetic;
|
|
2964
|
+
if (sig.top)
|
|
2965
|
+
sig.top.returnType = arithmetic;
|
|
2966
|
+
if (sig.method)
|
|
2967
|
+
sig.method.ret = arithmetic;
|
|
2968
|
+
const decl = functionDeclBySymbol.get(symbol);
|
|
2969
|
+
if (decl)
|
|
2970
|
+
this.runtimeOptionalFunctionReturns.set(decl, arithmetic);
|
|
2971
|
+
}
|
|
2972
|
+
}
|
|
2973
|
+
for (const sf of sourceFiles) {
|
|
2974
|
+
ts.walkPreorder(sf, (node) => {
|
|
2975
|
+
if (!ts.isVariableDeclaration(node) || !ts.isIdentifier(node.name))
|
|
2976
|
+
return;
|
|
2977
|
+
const symbol = symbolOf(node.name);
|
|
2978
|
+
if (!symbol)
|
|
2979
|
+
return;
|
|
2980
|
+
const fields = optionalFields.get(symbol);
|
|
2981
|
+
if (!dynamicObjectEntryRows.has(symbol) && !optionalSymbols.has(symbol) && !fields)
|
|
2982
|
+
return;
|
|
2983
|
+
const current = this.mapTypeOf(this.typeOf(node.name));
|
|
2984
|
+
if (!current)
|
|
2985
|
+
return;
|
|
2986
|
+
let promoted = dynamicObjectEntryRows.has(symbol) ? DYN : current;
|
|
2987
|
+
if (!dynamicObjectEntryRows.has(symbol) && optionalSymbols.has(symbol))
|
|
2988
|
+
promoted = addUndefined(promoted);
|
|
2989
|
+
if (promoted.kind === "func" && node.initializer && ts.isIdentifier(node.initializer)) {
|
|
2990
|
+
const sourceSig = this.fnSigOf(node.initializer);
|
|
2991
|
+
if (sourceSig) {
|
|
2992
|
+
promoted = {
|
|
2993
|
+
...promoted,
|
|
2994
|
+
params: sourceSig.params.filter((p) => p.mode !== "dynRest").map((p) => p.type),
|
|
2995
|
+
ret: sourceSig.returnType,
|
|
2996
|
+
};
|
|
2997
|
+
}
|
|
2998
|
+
}
|
|
2999
|
+
if (fields && promoted.kind === "record") {
|
|
3000
|
+
for (const field of fields) {
|
|
3001
|
+
const source = promoted.kind === "record"
|
|
3002
|
+
? this.shapes.get(promoted.shapeId)?.fields.find((f) => f.name === field)
|
|
3003
|
+
: undefined;
|
|
3004
|
+
if (!source)
|
|
3005
|
+
continue;
|
|
3006
|
+
promoted = this.runtimeOptionalRecordField(promoted, field, addUndefined(source.type));
|
|
3007
|
+
}
|
|
3008
|
+
}
|
|
3009
|
+
if (!typeEquals(promoted, current)) {
|
|
3010
|
+
this.runtimeOptionalBindingTypes.set(symbol, promoted);
|
|
3011
|
+
const global = ts.isIdentifier(node.name) ? this.globalOf(node.name) : this.globalsBySymbol.get(symbol);
|
|
3012
|
+
if (global) {
|
|
3013
|
+
global.type = promoted;
|
|
3014
|
+
this.runtimeOptionalGlobals.add(global);
|
|
3015
|
+
}
|
|
3016
|
+
}
|
|
3017
|
+
else if (promoted.kind === "union" && this.armTag(promoted.unionId, UNDEFINED_T) >= 0) {
|
|
3018
|
+
const global = ts.isIdentifier(node.name) ? this.globalOf(node.name) : this.globalsBySymbol.get(symbol);
|
|
3019
|
+
if (global)
|
|
3020
|
+
this.runtimeOptionalGlobals.add(global);
|
|
3021
|
+
}
|
|
3022
|
+
});
|
|
3023
|
+
}
|
|
3024
|
+
}
|
|
2014
3025
|
run() {
|
|
2015
3026
|
const parts = this.splitFiles();
|
|
2016
3027
|
// The coverage remainder deliberately visits every body that reachable
|
|
@@ -2024,6 +3035,7 @@ export class Lowerer {
|
|
|
2024
3035
|
this.checker.prefetchSourceFile(fp.sf);
|
|
2025
3036
|
}
|
|
2026
3037
|
this.collectProgram(parts);
|
|
3038
|
+
this.analyzeRuntimeOptionalArrayReads(parts);
|
|
2027
3039
|
// Decorated classes analyze AFTER the whole collection pass: a
|
|
2028
3040
|
// decorator's return type may name a subclass declared below the
|
|
2029
3041
|
// class, and reference lowering needs each class's rebindability
|
|
@@ -2031,6 +3043,7 @@ export class Lowerer {
|
|
|
2031
3043
|
for (const info of this.classes.values())
|
|
2032
3044
|
analyzeClassDecoration(this, info);
|
|
2033
3045
|
this.prepareModuleInits(parts);
|
|
3046
|
+
prepareCjsModuleGraph(this, parts);
|
|
2034
3047
|
const functions = [];
|
|
2035
3048
|
for (const fp of parts) {
|
|
2036
3049
|
for (const decl of fp.fnDecls) {
|
|
@@ -2252,7 +3265,7 @@ export class Lowerer {
|
|
|
2252
3265
|
const module = this.diags.length > 0
|
|
2253
3266
|
? null
|
|
2254
3267
|
: {
|
|
2255
|
-
irVersion:
|
|
3268
|
+
irVersion: 8,
|
|
2256
3269
|
sourceFile: this.entry.fileName,
|
|
2257
3270
|
functions,
|
|
2258
3271
|
classes: artifacts.classes,
|
|
@@ -2395,12 +3408,14 @@ export class Lowerer {
|
|
|
2395
3408
|
// will reuse the same answers later.
|
|
2396
3409
|
this.prefetchClassCollection(parts.flatMap((fp) => fp.classDecls));
|
|
2397
3410
|
this.collectProgram(parts);
|
|
3411
|
+
this.analyzeRuntimeOptionalArrayReads(parts);
|
|
2398
3412
|
// Decorated classes analyze post-collection here too: the %init seeds
|
|
2399
3413
|
// lower the decoration calls, whose edges (decorator bodies, construct
|
|
2400
3414
|
// thunks) reachable emit must see.
|
|
2401
3415
|
for (const info of this.classes.values())
|
|
2402
3416
|
analyzeClassDecoration(this, info);
|
|
2403
3417
|
this.prepareModuleInits(parts);
|
|
3418
|
+
prepareCjsModuleGraph(this, parts);
|
|
2404
3419
|
// Every lowerable body, by emitted-function name. The names double as
|
|
2405
3420
|
// retained-function keys and are deterministic by construction
|
|
2406
3421
|
// (qualified declaration names).
|
|
@@ -3194,11 +4209,14 @@ export class Lowerer {
|
|
|
3194
4209
|
const def = this.unions.get(unionId);
|
|
3195
4210
|
return def ? def.arms.flatMap((arm, tag) => (this.isArrayValueType(arm) ? [tag] : [])) : [];
|
|
3196
4211
|
}
|
|
3197
|
-
/**
|
|
3198
|
-
*
|
|
4212
|
+
/** Checked JSON conversion excludes undefined array slots because JSON
|
|
4213
|
+
* text cannot preserve that distinction. */
|
|
3199
4214
|
jsonSafe(t) {
|
|
3200
4215
|
return isJsonSafeType(t, (id) => this.shapes.get(id), (id) => this.unions.get(id));
|
|
3201
4216
|
}
|
|
4217
|
+
jsonStringifySafe(t) {
|
|
4218
|
+
return isJsonStringifySafeType(t, (id) => this.shapes.get(id), (id) => this.unions.get(id));
|
|
4219
|
+
}
|
|
3202
4220
|
/** True when a type is a BARE undefined-armed union — the one JSON-unsafe
|
|
3203
4221
|
* shape whose rejections deserve their own wording: Node's stringify of
|
|
3204
4222
|
* bare undefined is not a string at all and JSON text never matches the
|
|
@@ -3563,10 +4581,18 @@ export class Lowerer {
|
|
|
3563
4581
|
const undefArmedOk = expected.kind === "union" &&
|
|
3564
4582
|
(this.unions
|
|
3565
4583
|
.get(expected.unionId)
|
|
3566
|
-
?.arms.every((a) => a.kind === "undefinedT" ||
|
|
4584
|
+
?.arms.every((a) => a.kind === "undefinedT" ||
|
|
4585
|
+
this.jsonSafe(a) ||
|
|
4586
|
+
isDynTypedRefType(a) ||
|
|
4587
|
+
DYN_HANDLE_KINDS.has(a.kind)) ??
|
|
3567
4588
|
false);
|
|
3568
4589
|
const bytesOk = expected.kind === "bytes" && expected.elem === "u8";
|
|
3569
4590
|
const errorOk = expected.kind === "object" && expected.className === "%Error";
|
|
4591
|
+
// Program and runtime class instances can return only from a
|
|
4592
|
+
// compiler-owned typed-reference capsule. A parsed/plain dyn object
|
|
4593
|
+
// fails the runtime check; an exact capsule unwraps the original
|
|
4594
|
+
// instance and preserves identity.
|
|
4595
|
+
const classOk = isDynTypedRefType(expected);
|
|
3570
4596
|
// ADAPTABLE function targets (the checked-dynamic function
|
|
3571
4597
|
// boundary's OUT direction — `const wrapped: F = mustCall(fn)`):
|
|
3572
4598
|
// check callable-kind, then unwrap an identical boxed signature
|
|
@@ -3574,7 +4600,8 @@ export class Lowerer {
|
|
|
3574
4600
|
// arguments/results per F.
|
|
3575
4601
|
const funcOk = expected.kind === "func" &&
|
|
3576
4602
|
canAdaptDynFuncTo(expected, (id) => this.shapes.get(id), (id) => this.unions.get(id));
|
|
3577
|
-
|
|
4603
|
+
const handleOk = DYN_HANDLE_KINDS.has(expected.kind);
|
|
4604
|
+
if (this.jsonSafe(expected) || undefArmedOk || bytesOk || errorOk || classOk || funcOk || handleOk) {
|
|
3578
4605
|
return { kind: "dynCheck", value: expr, type: expected, loc: expr.loc };
|
|
3579
4606
|
}
|
|
3580
4607
|
return expr;
|
|
@@ -5599,10 +6626,13 @@ export class Lowerer {
|
|
|
5599
6626
|
return null;
|
|
5600
6627
|
const key = `${fromId}:${tag}`;
|
|
5601
6628
|
const existing = this.narrowHelpers.get(key);
|
|
5602
|
-
if (existing)
|
|
6629
|
+
if (existing) {
|
|
6630
|
+
this.checkedNarrowHelpers.add(existing);
|
|
5603
6631
|
return existing;
|
|
6632
|
+
}
|
|
5604
6633
|
const name = `%union.narrow.${this.narrowHelpers.size}`;
|
|
5605
6634
|
this.narrowHelpers.set(key, name);
|
|
6635
|
+
this.checkedNarrowHelpers.add(name);
|
|
5606
6636
|
const fromT = { kind: "union", unionId: fromId };
|
|
5607
6637
|
const u = { kind: "varRef", localId: "u.0", type: fromT, loc };
|
|
5608
6638
|
const body = [];
|
|
@@ -7072,6 +8102,14 @@ export class Lowerer {
|
|
|
7072
8102
|
}
|
|
7073
8103
|
const local = this.declareLocal(decl.name, name, shape.type, true);
|
|
7074
8104
|
params.push({ localId: local.id, name, type: local.type });
|
|
8105
|
+
if (shape.mode === "required" &&
|
|
8106
|
+
shape.type.kind === "union" &&
|
|
8107
|
+
this.armTag(shape.type.unionId, UNDEFINED_T) >= 0 &&
|
|
8108
|
+
typeEquals(this.stripUndefinedArm(shape.type), this.mapTypeOf(this.typeOf(decl.name)) ?? VOID)) {
|
|
8109
|
+
const root = this.runtimeOptionalRootOf(local);
|
|
8110
|
+
this.runtimeOptionalLocals.add(root);
|
|
8111
|
+
this.runtimeOptionalStorageLocals.add(root);
|
|
8112
|
+
}
|
|
7075
8113
|
});
|
|
7076
8114
|
return { params, prologue };
|
|
7077
8115
|
}
|
|
@@ -7313,11 +8351,8 @@ export class Lowerer {
|
|
|
7313
8351
|
lowerScopedBlock(stmt) {
|
|
7314
8352
|
return lowerScopedBlock(this, stmt);
|
|
7315
8353
|
}
|
|
7316
|
-
/** Lowers inside a
|
|
7317
|
-
*
|
|
7318
|
-
* break/continue/return crosses. `labels` carries the construct's JS
|
|
7319
|
-
* label names when the source statement was labeled — labeled jumps
|
|
7320
|
-
* resolve against them. */
|
|
8354
|
+
/** Lowers inside a jump-target marker. `labels` carries the construct's
|
|
8355
|
+
* source label names so labeled jumps resolve against them. */
|
|
7321
8356
|
inCtl(kind, fn, labels) {
|
|
7322
8357
|
this.ctx.ctl.push(labels !== undefined && labels.length > 0 ? { kind, labels } : { kind });
|
|
7323
8358
|
try {
|
|
@@ -7338,9 +8373,6 @@ export class Lowerer {
|
|
|
7338
8373
|
this.pendingLabels = null;
|
|
7339
8374
|
return labels ?? undefined;
|
|
7340
8375
|
}
|
|
7341
|
-
rejectJumpCrossingFinally(kw, stmt, label) {
|
|
7342
|
-
return rejectJumpCrossingFinally(this, kw, stmt, label);
|
|
7343
|
-
}
|
|
7344
8376
|
lowerStmt(stmt) {
|
|
7345
8377
|
return lowerStmt(this, stmt);
|
|
7346
8378
|
}
|
|
@@ -7540,7 +8572,13 @@ export class Lowerer {
|
|
|
7540
8572
|
return lowerExprStatement(this, expr);
|
|
7541
8573
|
}
|
|
7542
8574
|
lowerForOf(stmt) {
|
|
7543
|
-
|
|
8575
|
+
const optional = this.runtimeOptionalIdentifierValue(stmt.expression);
|
|
8576
|
+
if (optional?.present.kind !== "string")
|
|
8577
|
+
return lowerForOf(this, stmt);
|
|
8578
|
+
const helper = this.narrowedArmHelper(optional.unionId, optional.present, locOf(stmt.expression));
|
|
8579
|
+
if (!helper)
|
|
8580
|
+
return lowerForOf(this, stmt);
|
|
8581
|
+
return this.withExpressionOverride(stmt.expression, { kind: "call", callee: helper, args: [optional.value], type: optional.present, loc: locOf(stmt.expression) }, () => lowerForOf(this, stmt));
|
|
7544
8582
|
}
|
|
7545
8583
|
lowerForStatement(stmt) {
|
|
7546
8584
|
return lowerForStatement(this, stmt);
|
|
@@ -7627,7 +8665,44 @@ export class Lowerer {
|
|
|
7627
8665
|
return rejectThisInObjectMethod(this, node);
|
|
7628
8666
|
}
|
|
7629
8667
|
lowerElementAccess(expr) {
|
|
7630
|
-
|
|
8668
|
+
if (ts.isPropertyAccessExpression(expr.expression) &&
|
|
8669
|
+
ts.isIdentifier(expr.expression.expression) &&
|
|
8670
|
+
expr.expression.expression.text === "require" &&
|
|
8671
|
+
expr.expression.name.text === "cache") {
|
|
8672
|
+
return lowerElementAccess(this, expr);
|
|
8673
|
+
}
|
|
8674
|
+
const receiver = this.runtimeOptionalIdentifierValue(expr.expression);
|
|
8675
|
+
const receiverValue = receiver && (receiver.present.kind === "array" || receiver.present.kind === "record")
|
|
8676
|
+
? (() => {
|
|
8677
|
+
const helper = this.narrowedArmHelper(receiver.unionId, receiver.present, locOf(expr.expression));
|
|
8678
|
+
return helper ? { kind: "call", callee: helper, args: [receiver.value], type: receiver.present, loc: locOf(expr.expression) } : null;
|
|
8679
|
+
})()
|
|
8680
|
+
: null;
|
|
8681
|
+
if (receiverValue && receiver?.present.kind === "record" && ts.isNumericLiteral(expr.argumentExpression)) {
|
|
8682
|
+
const shape = this.shapes.get(receiver.present.shapeId);
|
|
8683
|
+
const index = expr.argumentExpression.text;
|
|
8684
|
+
const field = shape?.tuple ? shape.fields.find((candidate) => candidate.name === String(Number(index))) : undefined;
|
|
8685
|
+
if (field) {
|
|
8686
|
+
const read = {
|
|
8687
|
+
kind: "recordGet",
|
|
8688
|
+
obj: receiverValue,
|
|
8689
|
+
shapeId: receiver.present.shapeId,
|
|
8690
|
+
field: field.name,
|
|
8691
|
+
type: field.type,
|
|
8692
|
+
loc: locOf(expr),
|
|
8693
|
+
};
|
|
8694
|
+
if (expr.parent && ts.isTypeOfExpression(expr.parent) &&
|
|
8695
|
+
(field.type.kind === "f64" || field.type.kind === "bool" || field.type.kind === "string")) {
|
|
8696
|
+
return { kind: "dynFrom", value: read, type: DYN, loc: locOf(expr) };
|
|
8697
|
+
}
|
|
8698
|
+
return read;
|
|
8699
|
+
}
|
|
8700
|
+
}
|
|
8701
|
+
const key = this.runtimeOptionalIdentifierValue(expr.argumentExpression);
|
|
8702
|
+
const keyValue = key?.present.kind === "string" ? this.ensureString(key.value, expr.argumentExpression) : null;
|
|
8703
|
+
const lower = () => lowerElementAccess(this, expr);
|
|
8704
|
+
const withKey = () => keyValue ? this.withExpressionOverride(expr.argumentExpression, keyValue, lower) : lower();
|
|
8705
|
+
return receiverValue ? this.withExpressionOverride(expr.expression, receiverValue, withKey) : withKey();
|
|
7631
8706
|
}
|
|
7632
8707
|
lowerRecordKeyRead(expr, shapeId, shape, includeUndefined = false) {
|
|
7633
8708
|
return lowerRecordKeyRead(this, expr, shapeId, shape, includeUndefined);
|
|
@@ -7694,7 +8769,84 @@ export class Lowerer {
|
|
|
7694
8769
|
return caughtToString(this, node);
|
|
7695
8770
|
}
|
|
7696
8771
|
lowerInstanceOf(expr, loc) {
|
|
7697
|
-
|
|
8772
|
+
const lower = () => this.withRuntimeOptionalClassValue(expr.right, () => lowerInstanceOf(this, expr, loc));
|
|
8773
|
+
if (!ts.isIdentifier(expr.right) || this.mapTypeOf(this.typeOf(expr.right))?.kind !== "classval") {
|
|
8774
|
+
return lower();
|
|
8775
|
+
}
|
|
8776
|
+
const optionalLeft = this.runtimeOptionalIdentifierValue(expr.left);
|
|
8777
|
+
const left = optionalLeft?.value ?? (ts.isElementAccessExpression(expr.left) ? this.lowerExpr(expr.left) : null);
|
|
8778
|
+
if (!left)
|
|
8779
|
+
return lower();
|
|
8780
|
+
if (left.type.kind !== "union" || this.armTag(left.type.unionId, UNDEFINED_T) < 0)
|
|
8781
|
+
return lower();
|
|
8782
|
+
const present = this.stripUndefinedArm(left.type);
|
|
8783
|
+
if (present.kind !== "object")
|
|
8784
|
+
return lower();
|
|
8785
|
+
const undefinedTag = this.armTag(left.type.unionId, UNDEFINED_T);
|
|
8786
|
+
const valueTag = this.armTag(left.type.unionId, present);
|
|
8787
|
+
if (undefinedTag < 0 || valueTag < 0)
|
|
8788
|
+
return lower();
|
|
8789
|
+
const stable = this.declareHiddenLocal("%instanceofLeft", left.type);
|
|
8790
|
+
const value = varRef(stable.id, left.type, locOf(expr.left));
|
|
8791
|
+
const optionalRight = this.runtimeOptionalIdentifierValue(expr.right);
|
|
8792
|
+
let right;
|
|
8793
|
+
if (optionalRight?.present.kind === "classval") {
|
|
8794
|
+
const helper = this.narrowedArmHelper(optionalRight.unionId, optionalRight.present, locOf(expr.right));
|
|
8795
|
+
if (!helper)
|
|
8796
|
+
return lower();
|
|
8797
|
+
right = { kind: "call", callee: helper, args: [optionalRight.value], type: optionalRight.present, loc: locOf(expr.right) };
|
|
8798
|
+
}
|
|
8799
|
+
else {
|
|
8800
|
+
right = this.lowerExpr(expr.right);
|
|
8801
|
+
}
|
|
8802
|
+
if (right.type.kind !== "classval")
|
|
8803
|
+
return lower();
|
|
8804
|
+
const stableRight = this.declareHiddenLocal("%instanceofRight", right.type);
|
|
8805
|
+
const rightValue = varRef(stableRight.id, right.type, locOf(expr.right));
|
|
8806
|
+
const body = this.withExpressionOverride(expr.left, {
|
|
8807
|
+
kind: "unionNarrow", unionId: left.type.unionId, tag: valueTag, value, type: present, loc: locOf(expr.left),
|
|
8808
|
+
}, () => this.withExpressionOverride(expr.right, rightValue, () => lowerInstanceOf(this, expr, loc)));
|
|
8809
|
+
return {
|
|
8810
|
+
kind: "seqExpr",
|
|
8811
|
+
stmts: [
|
|
8812
|
+
{ kind: "varDecl", localId: stable.id, init: left, loc: locOf(expr.left) },
|
|
8813
|
+
{ kind: "varDecl", localId: stableRight.id, init: right, loc: locOf(expr.right) },
|
|
8814
|
+
],
|
|
8815
|
+
result: {
|
|
8816
|
+
kind: "ternary",
|
|
8817
|
+
cond: { kind: "unionIsTag", unionId: left.type.unionId, tag: undefinedTag, negated: false, value, type: BOOL, loc },
|
|
8818
|
+
then: { kind: "boolLit", value: false, type: BOOL, loc },
|
|
8819
|
+
else_: body,
|
|
8820
|
+
type: BOOL,
|
|
8821
|
+
loc,
|
|
8822
|
+
},
|
|
8823
|
+
type: BOOL,
|
|
8824
|
+
loc,
|
|
8825
|
+
};
|
|
8826
|
+
}
|
|
8827
|
+
withRuntimeOptionalClassValue(node, lower) {
|
|
8828
|
+
if (this.mapTypeOf(this.typeOf(node))?.kind !== "classval")
|
|
8829
|
+
return lower();
|
|
8830
|
+
const optional = this.runtimeOptionalIdentifierValue(node);
|
|
8831
|
+
if (optional?.present.kind !== "classval")
|
|
8832
|
+
return lower();
|
|
8833
|
+
const helper = this.narrowedArmHelper(optional.unionId, optional.present, locOf(node));
|
|
8834
|
+
if (!helper)
|
|
8835
|
+
return lower();
|
|
8836
|
+
return this.withExpressionOverride(node, { kind: "call", callee: helper, args: [optional.value], type: optional.present, loc: locOf(node) }, lower);
|
|
8837
|
+
}
|
|
8838
|
+
withExpressionOverride(node, value, lower) {
|
|
8839
|
+
const previous = this.chainRecvByNode.get(node);
|
|
8840
|
+
this.chainRecvByNode.set(node, value);
|
|
8841
|
+
try {
|
|
8842
|
+
return lower();
|
|
8843
|
+
}
|
|
8844
|
+
finally {
|
|
8845
|
+
if (previous)
|
|
8846
|
+
this.chainRecvByNode.set(node, previous);
|
|
8847
|
+
else
|
|
8848
|
+
this.chainRecvByNode.delete(node);
|
|
8849
|
+
}
|
|
7698
8850
|
}
|
|
7699
8851
|
lowerCall(expr) {
|
|
7700
8852
|
// Ambient fetch (native in static builds, island-backed under
|
|
@@ -7834,7 +8986,8 @@ export class Lowerer {
|
|
|
7834
8986
|
const arg = this.lowerExpr(expr.arguments[0]);
|
|
7835
8987
|
return { kind: "jsOp", op: "construct", args: [ctor, arg], type: JSVAL, loc };
|
|
7836
8988
|
}
|
|
7837
|
-
|
|
8989
|
+
const builtin = lowerAbortControllerNew(this, expr) ?? lowerResponseNew(this, expr) ?? lowerStaticReadableStreamNew(this, expr);
|
|
8990
|
+
return builtin ?? this.withRuntimeOptionalClassValue(expr.expression, () => lowerNew(this, expr));
|
|
7838
8991
|
}
|
|
7839
8992
|
lowerFieldRead(expr) {
|
|
7840
8993
|
// `C.x` static reads first (the receiver is a CLASS, not an instance
|
|
@@ -8132,6 +9285,9 @@ export class Lowerer {
|
|
|
8132
9285
|
lowerBuiltinModuleCall(expr, bi, fn, loc) {
|
|
8133
9286
|
return lowerBuiltinModuleCall(this, expr, bi, fn, loc);
|
|
8134
9287
|
}
|
|
9288
|
+
lowerTimersPromisesSetInterval(expr, bi, loc) {
|
|
9289
|
+
return lowerTimersPromisesSetInterval(this, expr, bi, loc);
|
|
9290
|
+
}
|
|
8135
9291
|
lowerFsToUnixTimestampCall(expr, bi, loc) {
|
|
8136
9292
|
return lowerFsToUnixTimestampCall(this, expr, bi, loc);
|
|
8137
9293
|
}
|
|
@@ -8323,6 +9479,13 @@ export class Lowerer {
|
|
|
8323
9479
|
return lowerStringLastIndexOfCall(this, call, access);
|
|
8324
9480
|
}
|
|
8325
9481
|
lowerFilterNarrowCall(call, access) {
|
|
9482
|
+
// Native array filters use the state-aware HOF helper: its callback
|
|
9483
|
+
// receives present undefined values and skips holes before a narrowed
|
|
9484
|
+
// survivor is extracted. The older narrowing helper reads the payload
|
|
9485
|
+
// directly and therefore cannot represent that three-state contract.
|
|
9486
|
+
if (this.mapTypeOf(this.typeOf(access.expression))?.kind === "array" && this.isStdlibMember(access)) {
|
|
9487
|
+
return null;
|
|
9488
|
+
}
|
|
8326
9489
|
return lowerFilterNarrowCall(this, call, access);
|
|
8327
9490
|
}
|
|
8328
9491
|
lowerPromiseMethodCall(call, access) {
|