@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.
Files changed (256) hide show
  1. package/ambient/scriptc-node-fallback.d.ts +52 -22
  2. package/ambient/scriptc-overrides.d.ts +0 -7
  3. package/ambient/scriptc.d.ts +3 -2
  4. package/dist/backend/build-cache.js.map +1 -1
  5. package/dist/backend/c/async.js +24 -2
  6. package/dist/backend/c/async.js.map +1 -1
  7. package/dist/backend/c/c-emitter.d.ts +22 -10
  8. package/dist/backend/c/c-emitter.js +60 -15
  9. package/dist/backend/c/c-emitter.js.map +1 -1
  10. package/dist/backend/c/exprs.js +289 -55
  11. package/dist/backend/c/exprs.js.map +1 -1
  12. package/dist/backend/c/island.js.map +1 -1
  13. package/dist/backend/c/may-throw.js +3 -0
  14. package/dist/backend/c/may-throw.js.map +1 -1
  15. package/dist/backend/c/stmts.d.ts +3 -5
  16. package/dist/backend/c/stmts.js +76 -17
  17. package/dist/backend/c/stmts.js.map +1 -1
  18. package/dist/backend/c/types.js.map +1 -1
  19. package/dist/backend/c/walkers.js +36 -12
  20. package/dist/backend/c/walkers.js.map +1 -1
  21. package/dist/backend/external-c.d.ts +33 -0
  22. package/dist/backend/external-c.js +51 -0
  23. package/dist/backend/external-c.js.map +1 -0
  24. package/dist/backend/link-plan.d.ts +37 -0
  25. package/dist/backend/link-plan.js +26 -0
  26. package/dist/backend/link-plan.js.map +1 -0
  27. package/dist/backend/linker.d.ts +22 -0
  28. package/dist/backend/linker.js +236 -0
  29. package/dist/backend/linker.js.map +1 -0
  30. package/dist/backend/llvm/blocks.js.map +1 -1
  31. package/dist/backend/llvm/dyn.js +63 -3
  32. package/dist/backend/llvm/dyn.js.map +1 -1
  33. package/dist/backend/llvm/emitter.js +132 -26
  34. package/dist/backend/llvm/emitter.js.map +1 -1
  35. package/dist/backend/llvm/expr-async.js +85 -3
  36. package/dist/backend/llvm/expr-async.js.map +1 -1
  37. package/dist/backend/llvm/expr-bytes.js.map +1 -1
  38. package/dist/backend/llvm/expr-calls.js.map +1 -1
  39. package/dist/backend/llvm/expr-containers.js +68 -54
  40. package/dist/backend/llvm/expr-containers.js.map +1 -1
  41. package/dist/backend/llvm/expr-context.d.ts +2 -1
  42. package/dist/backend/llvm/expr-control.js.map +1 -1
  43. package/dist/backend/llvm/expr-dispatch.js +2 -0
  44. package/dist/backend/llvm/expr-dispatch.js.map +1 -1
  45. package/dist/backend/llvm/expr-dynamic.js +6 -3
  46. package/dist/backend/llvm/expr-dynamic.js.map +1 -1
  47. package/dist/backend/llvm/expr-island.js.map +1 -1
  48. package/dist/backend/llvm/expr-primitives.d.ts +1 -1
  49. package/dist/backend/llvm/expr-primitives.js +20 -0
  50. package/dist/backend/llvm/expr-primitives.js.map +1 -1
  51. package/dist/backend/llvm/expr-records.js.map +1 -1
  52. package/dist/backend/llvm/expr-stream-bridges.js +245 -10
  53. package/dist/backend/llvm/expr-stream-bridges.js.map +1 -1
  54. package/dist/backend/llvm/lib-dispatch.js +2 -0
  55. package/dist/backend/llvm/lib-dispatch.js.map +1 -1
  56. package/dist/backend/llvm/lib-shared.js +18 -0
  57. package/dist/backend/llvm/lib-shared.js.map +1 -1
  58. package/dist/backend/llvm/lib-stream.js +6 -3
  59. package/dist/backend/llvm/lib-stream.js.map +1 -1
  60. package/dist/backend/llvm/split.js.map +1 -1
  61. package/dist/backend/llvm/unsupported.js.map +1 -1
  62. package/dist/backend/llvm/walkers.js +31 -4
  63. package/dist/backend/llvm/walkers.js.map +1 -1
  64. package/dist/backend/native-codegen.d.ts +8 -1
  65. package/dist/backend/native-codegen.js +39 -25
  66. package/dist/backend/native-codegen.js.map +1 -1
  67. package/dist/backend/native-codegen.test.js +44 -4
  68. package/dist/backend/native-codegen.test.js.map +1 -1
  69. package/dist/backend/native-link-info.d.ts +1 -1
  70. package/dist/backend/native-link-info.js +42 -18
  71. package/dist/backend/native-link-info.js.map +1 -1
  72. package/dist/backend/native-toolchain.js.map +1 -1
  73. package/dist/backend/native-toolchain.test.js +5 -1
  74. package/dist/backend/native-toolchain.test.js.map +1 -1
  75. package/dist/backend/runtime-pack-targets.test.d.ts +1 -0
  76. package/dist/backend/runtime-pack-targets.test.js +54 -0
  77. package/dist/backend/runtime-pack-targets.test.js.map +1 -0
  78. package/dist/backend/runtime-pack.d.ts +5 -30
  79. package/dist/backend/runtime-pack.js +46 -141
  80. package/dist/backend/runtime-pack.js.map +1 -1
  81. package/dist/backend/runtime-pack.test.js +49 -14
  82. package/dist/backend/runtime-pack.test.js.map +1 -1
  83. package/dist/backend/targets.d.ts +63 -17
  84. package/dist/backend/targets.js +251 -35
  85. package/dist/backend/targets.js.map +1 -1
  86. package/dist/backend/targets.test.js +27 -5
  87. package/dist/backend/targets.test.js.map +1 -1
  88. package/dist/backend/vendor-archives.js.map +1 -1
  89. package/dist/compat/fetch-profile.js +4 -2
  90. package/dist/compat/fetch-profile.js.map +1 -1
  91. package/dist/coverage/report.js.map +1 -1
  92. package/dist/coverage/surface-manifest.js +22 -1
  93. package/dist/coverage/surface-manifest.js.map +1 -1
  94. package/dist/diagnostics/diagnostic.d.ts +6 -5
  95. package/dist/diagnostics/diagnostic.js +7 -6
  96. package/dist/diagnostics/diagnostic.js.map +1 -1
  97. package/dist/diagnostics/render.js.map +1 -1
  98. package/dist/ffi/ffi-manifest.js.map +1 -1
  99. package/dist/frontend/cjs-lexer.js.map +1 -1
  100. package/dist/frontend/dts-paths.js.map +1 -1
  101. package/dist/frontend/input-tracker.d.ts +10 -0
  102. package/dist/frontend/input-tracker.js +26 -3
  103. package/dist/frontend/input-tracker.js.map +1 -1
  104. package/dist/frontend/input-tracker.test.js +8 -1
  105. package/dist/frontend/input-tracker.test.js.map +1 -1
  106. package/dist/frontend/lib-contract.js.map +1 -1
  107. package/dist/frontend/lowering/array-values.d.ts +12 -0
  108. package/dist/frontend/lowering/array-values.js +76 -0
  109. package/dist/frontend/lowering/array-values.js.map +1 -0
  110. package/dist/frontend/lowering/callback-arg.js.map +1 -1
  111. package/dist/frontend/lowering/containers/bytes.d.ts +35 -0
  112. package/dist/frontend/lowering/containers/bytes.js +1021 -0
  113. package/dist/frontend/lowering/containers/bytes.js.map +1 -0
  114. package/dist/frontend/lowering/containers/string-and-regexp.d.ts +17 -0
  115. package/dist/frontend/lowering/containers/string-and-regexp.js +345 -0
  116. package/dist/frontend/lowering/containers/string-and-regexp.js.map +1 -0
  117. package/dist/frontend/lowering/expressions/evaluation-safety.d.ts +13 -0
  118. package/dist/frontend/lowering/expressions/evaluation-safety.js +76 -0
  119. package/dist/frontend/lowering/expressions/evaluation-safety.js.map +1 -0
  120. package/dist/frontend/lowering/expressions/object-literals.d.ts +44 -0
  121. package/dist/frontend/lowering/expressions/object-literals.js +1815 -0
  122. package/dist/frontend/lowering/expressions/object-literals.js.map +1 -0
  123. package/dist/frontend/lowering/expressions/optional-chains.d.ts +31 -0
  124. package/dist/frontend/lowering/expressions/optional-chains.js +382 -0
  125. package/dist/frontend/lowering/expressions/optional-chains.js.map +1 -0
  126. package/dist/frontend/lowering/expressions/try-lower-expression.d.ts +7 -0
  127. package/dist/frontend/lowering/expressions/try-lower-expression.js +26 -0
  128. package/dist/frontend/lowering/expressions/try-lower-expression.js.map +1 -0
  129. package/dist/frontend/lowering/lib-boundary.js +7 -2
  130. package/dist/frontend/lowering/lib-boundary.js.map +1 -1
  131. package/dist/frontend/lowering/lower-array-sort.d.ts +22 -0
  132. package/dist/frontend/lowering/lower-array-sort.js +619 -0
  133. package/dist/frontend/lowering/lower-array-sort.js.map +1 -0
  134. package/dist/frontend/lowering/lower-assert.js.map +1 -1
  135. package/dist/frontend/lowering/lower-async-iteration.d.ts +7 -0
  136. package/dist/frontend/lowering/lower-async-iteration.js +657 -0
  137. package/dist/frontend/lowering/lower-async-iteration.js.map +1 -0
  138. package/dist/frontend/lowering/lower-builtins.d.ts +18 -0
  139. package/dist/frontend/lowering/lower-builtins.js +379 -48
  140. package/dist/frontend/lowering/lower-builtins.js.map +1 -1
  141. package/dist/frontend/lowering/lower-calls.d.ts +16 -8
  142. package/dist/frontend/lowering/lower-calls.js +667 -91
  143. package/dist/frontend/lowering/lower-calls.js.map +1 -1
  144. package/dist/frontend/lowering/lower-classes.d.ts +7 -13
  145. package/dist/frontend/lowering/lower-classes.js +128 -35
  146. package/dist/frontend/lowering/lower-classes.js.map +1 -1
  147. package/dist/frontend/lowering/lower-comptime.js.map +1 -1
  148. package/dist/frontend/lowering/lower-containers.d.ts +1 -47
  149. package/dist/frontend/lowering/lower-containers.js +779 -2038
  150. package/dist/frontend/lowering/lower-containers.js.map +1 -1
  151. package/dist/frontend/lowering/lower-event-emitter.js +51 -5
  152. package/dist/frontend/lowering/lower-event-emitter.js.map +1 -1
  153. package/dist/frontend/lowering/lower-exprs.d.ts +4 -78
  154. package/dist/frontend/lowering/lower-exprs.js +1128 -2409
  155. package/dist/frontend/lowering/lower-exprs.js.map +1 -1
  156. package/dist/frontend/lowering/lower-generators.d.ts +18 -3
  157. package/dist/frontend/lowering/lower-generators.js +145 -9
  158. package/dist/frontend/lowering/lower-generators.js.map +1 -1
  159. package/dist/frontend/lowering/lower-inspect.js +50 -36
  160. package/dist/frontend/lowering/lower-inspect.js.map +1 -1
  161. package/dist/frontend/lowering/lower-island.d.ts +11 -4
  162. package/dist/frontend/lowering/lower-island.js +47 -10
  163. package/dist/frontend/lowering/lower-island.js.map +1 -1
  164. package/dist/frontend/lowering/lower-modules.js +52 -15
  165. package/dist/frontend/lowering/lower-modules.js.map +1 -1
  166. package/dist/frontend/lowering/lower-node-module.d.ts +18 -0
  167. package/dist/frontend/lowering/lower-node-module.js +302 -0
  168. package/dist/frontend/lowering/lower-node-module.js.map +1 -0
  169. package/dist/frontend/lowering/lower-server.js +11 -1
  170. package/dist/frontend/lowering/lower-server.js.map +1 -1
  171. package/dist/frontend/lowering/lower-stmts.d.ts +5 -24
  172. package/dist/frontend/lowering/lower-stmts.js +811 -314
  173. package/dist/frontend/lowering/lower-stmts.js.map +1 -1
  174. package/dist/frontend/lowering/lower-stream.js +4 -4
  175. package/dist/frontend/lowering/lower-stream.js.map +1 -1
  176. package/dist/frontend/lowering/lowerer.d.ts +109 -34
  177. package/dist/frontend/lowering/lowerer.js +1225 -62
  178. package/dist/frontend/lowering/lowerer.js.map +1 -1
  179. package/dist/frontend/lowering/optional-arguments.d.ts +9 -0
  180. package/dist/frontend/lowering/optional-arguments.js +57 -0
  181. package/dist/frontend/lowering/optional-arguments.js.map +1 -0
  182. package/dist/frontend/lowering/surfaces.d.ts +10 -2
  183. package/dist/frontend/lowering/surfaces.js +15 -3
  184. package/dist/frontend/lowering/surfaces.js.map +1 -1
  185. package/dist/frontend/npm-static-declarations.d.ts +35 -0
  186. package/dist/frontend/npm-static-declarations.js +506 -0
  187. package/dist/frontend/npm-static-declarations.js.map +1 -0
  188. package/dist/frontend/npm-static-declarations.test.d.ts +1 -0
  189. package/dist/frontend/npm-static-declarations.test.js +122 -0
  190. package/dist/frontend/npm-static-declarations.test.js.map +1 -0
  191. package/dist/frontend/npm-static.d.ts +2 -0
  192. package/dist/frontend/npm-static.js +26 -10
  193. package/dist/frontend/npm-static.js.map +1 -1
  194. package/dist/frontend/npm.d.ts +60 -0
  195. package/dist/frontend/npm.js +38 -4
  196. package/dist/frontend/npm.js.map +1 -1
  197. package/dist/frontend/npm.test.d.ts +1 -0
  198. package/dist/frontend/npm.test.js +50 -0
  199. package/dist/frontend/npm.test.js.map +1 -0
  200. package/dist/frontend/program.d.ts +3 -2
  201. package/dist/frontend/program.js +495 -68
  202. package/dist/frontend/program.js.map +1 -1
  203. package/dist/frontend/resolve.d.ts +10 -2
  204. package/dist/frontend/resolve.js +90 -14
  205. package/dist/frontend/resolve.js.map +1 -1
  206. package/dist/frontend/resolve.test.js +4 -1
  207. package/dist/frontend/resolve.test.js.map +1 -1
  208. package/dist/frontend/runtime-resolve.d.ts +22 -0
  209. package/dist/frontend/runtime-resolve.js +101 -0
  210. package/dist/frontend/runtime-resolve.js.map +1 -0
  211. package/dist/frontend/runtime-resolve.test.d.ts +1 -0
  212. package/dist/frontend/runtime-resolve.test.js +18 -0
  213. package/dist/frontend/runtime-resolve.test.js.map +1 -0
  214. package/dist/frontend/ts7/adapter.js +12 -13
  215. package/dist/frontend/ts7/adapter.js.map +1 -1
  216. package/dist/frontend/ts7/ast.js.map +1 -1
  217. package/dist/frontend/ts7/census-check.js +3 -3
  218. package/dist/frontend/ts7/census-check.js.map +1 -1
  219. package/dist/frontend/ts7/checker.d.ts +3 -0
  220. package/dist/frontend/ts7/checker.js +12 -3
  221. package/dist/frontend/ts7/checker.js.map +1 -1
  222. package/dist/frontend/ts7/enums.js.map +1 -1
  223. package/dist/frontend/ts7/program-adapter.js +42 -3
  224. package/dist/frontend/ts7/program-adapter.js.map +1 -1
  225. package/dist/frontend/type-mapper.js +120 -7
  226. package/dist/frontend/type-mapper.js.map +1 -1
  227. package/dist/index.d.ts +3 -3
  228. package/dist/index.js +50 -28
  229. package/dist/index.js.map +1 -1
  230. package/dist/ir/analysis.js.map +1 -1
  231. package/dist/ir/ir.d.ts +125 -41
  232. package/dist/ir/ir.js +82 -31
  233. package/dist/ir/ir.js.map +1 -1
  234. package/dist/ir/serialize.d.ts +1 -1
  235. package/dist/ir/serialize.js +1 -1
  236. package/dist/ir/validate.js +127 -95
  237. package/dist/ir/validate.js.map +1 -1
  238. package/dist/library/cache-primitives.js.map +1 -1
  239. package/dist/library/cache-reset.test.js.map +1 -1
  240. package/dist/library/compiler-self-identity.js.map +1 -1
  241. package/dist/library/fence-eval.js +3 -1
  242. package/dist/library/fence-eval.js.map +1 -1
  243. package/dist/library/int-infer.js +12 -1
  244. package/dist/library/int-infer.js.map +1 -1
  245. package/dist/library/int-infer.test.js +4 -4
  246. package/dist/library/int-infer.test.js.map +1 -1
  247. package/dist/library/library-cache.test.js +5 -5
  248. package/dist/library/library-profile.js.map +1 -1
  249. package/dist/library/sidecar-validate.js.map +1 -1
  250. package/dist/library/sidecar.js.map +1 -1
  251. package/dist/startup-cache.d.ts +9 -1
  252. package/dist/startup-cache.js +16 -1
  253. package/dist/startup-cache.js.map +1 -1
  254. package/dist/wasi-paths.js.map +1 -1
  255. package/package.json +18 -5
  256. 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, lowerBufferStaticCall, lowerBytesMethodCall, lowerBytesNew, lowerMapMethodCall, lowerMapForEachCall, buildMapForEachFn, lowerRecordOvfCaptureHelper, lowerEnvToPairsHelper, lowerSetMethodCall, lowerSetForEachCall, buildSetForEachFn, lowerRegexMethodCall, lowerStringMethodCall } from "./lower-containers.js";
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, rejectJumpCrossingFinally, lowerStmt, lowerVarStatement, lowerDestructuringDecl, lowerDestructuringAssignParts, lowerBindingPattern, lowerJsvalBindingPattern, checkBindingElement, bindPatternTarget, isParseArgsDynCheckerType, lowerVarDeclList, lowerVarDecl, lowerSwitch, lowerTry, lowerExprStatement, lowerForOf, lowerForStatement } from "./lower-stmts.js";
42
- import { lowerDynObjectLiteral, lowerExpr, maybeNarrow, lowerUnitComparison, lowerNullishCoalesce, lowerOptionalChain, finishOptionalChain, lowerCondition, ensureBool, requireTruthyUnion, eqComparableUnion, lowerIntrinsicProperty, lowerArrayLiteral, lowerObjectLiteral, lowerShorthandValue, rejectThisInObjectMethod, lowerElementAccess, lowerElementWrite, lowerRecordKeyRead, ensureString, lowerTemplate, lowerAsExpression, lowerPrefixUnary, lowerBinary, lowerCaughtTypeofTest, caughtRead, caughtLocalOf, caughtToString, lowerInstanceOf, lowerRegexLiteral, lowerFieldRead, lowerUnionProperty, fieldTarget, fieldGetExpr, fieldSetStmt, lowerFieldCompound, uniqueSymbolKeyOf, foldedStringKeyOf } from "./lower-exprs.js";
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 sig = pureSingleCallSignatureOf(lowerer, t);
489
- if (sig) {
490
- const params = sig.getParameters().map((p) => {
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
- : isRelativeSpecifier(spec)
1328
- ? resolveImport(this.program, recvDecl.getSourceFile(), spec)
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 covers package.json aliases as well as relative imports;
1466
- * resolveImport is the fallback for the latter. */
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 isRelativeSpecifier(spec.text) ? resolveImport(this.program, from, spec.text) : null;
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
- // Bare specifiers resolve only for opted-in --npm-static packages —
1716
- // their CJS entries take the same default-binding interop as a
1717
- // relative require; every other bare import answers null and keeps
1718
- // its own machinery.
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 RELATIVE module — the CommonJS
1728
- * namespace binding — or of a bare specifier naming an opted-in
1729
- * --npm-static package (its CJS entry is a program module, so the
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 (!isRelativeSpecifier(spec) &&
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
- // Relative requires resolve within the program; a BARE require can be
1976
- // a program-module edge too when it names an opted-in --npm-static
1977
- // package (one package requiring another — the resolution answered
1978
- // its shipped JS, the file is in the module order, and the reads
1979
- // alias its globals). Without the guarded %init call at this position
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 = isRelativeSpecifier(spec)
1983
- ? resolveImport(this.program, node.getSourceFile(), spec)
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
- return {
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: 6,
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
- /** isJsonSafeType with this Lowerer's registries — the shared fence for
3198
- * what JSON.stringify accepts and what a checked cast can validate. */
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" || this.jsonSafe(a)) ??
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
- if (this.jsonSafe(expected) || undefArmedOk || bytesOk || errorOk || funcOk) {
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 control-construct marker (loop/switch/labeled block/
7317
- * try-with-finally/finally block) so the jump fences below know what a
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
- return lowerForOf(this, stmt);
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
- return lowerElementAccess(this, expr);
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
- return lowerInstanceOf(this, expr, loc);
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
- return lowerAbortControllerNew(this, expr) ?? lowerResponseNew(this, expr) ?? lowerStaticReadableStreamNew(this, expr) ?? lowerNew(this, expr);
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) {