@scriptc/compiler 0.2.0 → 0.2.1

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 (194) hide show
  1. package/ambient/scriptc-node-fallback.d.ts +25 -15
  2. package/dist/backend/cycle-analysis.js +1 -0
  3. package/dist/backend/cycle-analysis.js.map +1 -1
  4. package/dist/backend/llvm/classes.js +21 -5
  5. package/dist/backend/llvm/classes.js.map +1 -1
  6. package/dist/backend/llvm/dyn.d.ts +5 -0
  7. package/dist/backend/llvm/dyn.js +166 -99
  8. package/dist/backend/llvm/dyn.js.map +1 -1
  9. package/dist/backend/llvm/emitter.d.ts +2 -2
  10. package/dist/backend/llvm/emitter.js +79 -20
  11. package/dist/backend/llvm/emitter.js.map +1 -1
  12. package/dist/backend/llvm/expr-bytes.d.ts +1 -1
  13. package/dist/backend/llvm/expr-bytes.js +28 -17
  14. package/dist/backend/llvm/expr-bytes.js.map +1 -1
  15. package/dist/backend/llvm/expr-control.js +1 -1
  16. package/dist/backend/llvm/expr-control.js.map +1 -1
  17. package/dist/backend/llvm/expr-dynamic.js +18 -9
  18. package/dist/backend/llvm/expr-dynamic.js.map +1 -1
  19. package/dist/backend/llvm/expr-primitives.js +11 -1
  20. package/dist/backend/llvm/expr-primitives.js.map +1 -1
  21. package/dist/backend/llvm/expr-records.js +11 -3
  22. package/dist/backend/llvm/expr-records.js.map +1 -1
  23. package/dist/backend/llvm/expr-stream-bridges.js +125 -17
  24. package/dist/backend/llvm/expr-stream-bridges.js.map +1 -1
  25. package/dist/backend/llvm/lib-process.js +23 -3
  26. package/dist/backend/llvm/lib-process.js.map +1 -1
  27. package/dist/backend/llvm/lib-shared.js +45 -2
  28. package/dist/backend/llvm/lib-shared.js.map +1 -1
  29. package/dist/backend/llvm/shapes.js +8 -2
  30. package/dist/backend/llvm/shapes.js.map +1 -1
  31. package/dist/backend/native-toolchain.d.ts +3 -6
  32. package/dist/backend/native-toolchain.js +53 -23
  33. package/dist/backend/native-toolchain.js.map +1 -1
  34. package/dist/backend/native-toolchain.test.js +72 -0
  35. package/dist/backend/native-toolchain.test.js.map +1 -1
  36. package/dist/backend/runtime-abi.d.ts +2 -2
  37. package/dist/backend/runtime-abi.js +2 -2
  38. package/dist/backend/runtime-pack-native.js +4 -1
  39. package/dist/backend/runtime-pack-native.js.map +1 -1
  40. package/dist/backend/runtime-pack-targets.test.js +1 -1
  41. package/dist/backend/runtime-pack.test.js +1 -1
  42. package/dist/backend/targets.js +10 -6
  43. package/dist/backend/targets.js.map +1 -1
  44. package/dist/backend/targets.test.js +7 -0
  45. package/dist/backend/targets.test.js.map +1 -1
  46. package/dist/compat/fetch-profile.js +34 -4
  47. package/dist/compat/fetch-profile.js.map +1 -1
  48. package/dist/diagnostics/diagnostic.js +2 -7
  49. package/dist/diagnostics/diagnostic.js.map +1 -1
  50. package/dist/frontend/lowering/class-callbacks.d.ts +3 -3
  51. package/dist/frontend/lowering/class-callbacks.js +26 -15
  52. package/dist/frontend/lowering/class-callbacks.js.map +1 -1
  53. package/dist/frontend/lowering/class-construction.d.ts +12 -0
  54. package/dist/frontend/lowering/class-construction.js +156 -0
  55. package/dist/frontend/lowering/class-construction.js.map +1 -0
  56. package/dist/frontend/lowering/class-descriptors.d.ts +1 -0
  57. package/dist/frontend/lowering/class-descriptors.js +46 -5
  58. package/dist/frontend/lowering/class-descriptors.js.map +1 -1
  59. package/dist/frontend/lowering/class-dynamic-dispatch.d.ts +11 -1
  60. package/dist/frontend/lowering/class-dynamic-dispatch.js +403 -48
  61. package/dist/frontend/lowering/class-dynamic-dispatch.js.map +1 -1
  62. package/dist/frontend/lowering/class-dynamic-dispatch.test.js +2 -1
  63. package/dist/frontend/lowering/class-dynamic-dispatch.test.js.map +1 -1
  64. package/dist/frontend/lowering/class-instance-constructor.js +4 -1
  65. package/dist/frontend/lowering/class-instance-constructor.js.map +1 -1
  66. package/dist/frontend/lowering/class-method-values.d.ts +4 -1
  67. package/dist/frontend/lowering/class-method-values.js +72 -7
  68. package/dist/frontend/lowering/class-method-values.js.map +1 -1
  69. package/dist/frontend/lowering/class-prototypes.d.ts +5 -2
  70. package/dist/frontend/lowering/class-prototypes.js +121 -13
  71. package/dist/frontend/lowering/class-prototypes.js.map +1 -1
  72. package/dist/frontend/lowering/class-runtime-statics.d.ts +8 -0
  73. package/dist/frontend/lowering/class-runtime-statics.js +66 -0
  74. package/dist/frontend/lowering/class-runtime-statics.js.map +1 -0
  75. package/dist/frontend/lowering/class-static-data.js +2 -2
  76. package/dist/frontend/lowering/class-static-data.js.map +1 -1
  77. package/dist/frontend/lowering/containers/bytes.js +49 -13
  78. package/dist/frontend/lowering/containers/bytes.js.map +1 -1
  79. package/dist/frontend/lowering/containers/string-and-regexp.js +19 -47
  80. package/dist/frontend/lowering/containers/string-and-regexp.js.map +1 -1
  81. package/dist/frontend/lowering/error-methods.d.ts +4 -1
  82. package/dist/frontend/lowering/error-methods.js +100 -1
  83. package/dist/frontend/lowering/error-methods.js.map +1 -1
  84. package/dist/frontend/lowering/expressions/native-proxy.js +3 -1
  85. package/dist/frontend/lowering/expressions/native-proxy.js.map +1 -1
  86. package/dist/frontend/lowering/expressions/nullish-assignment.js +19 -11
  87. package/dist/frontend/lowering/expressions/nullish-assignment.js.map +1 -1
  88. package/dist/frontend/lowering/expressions/object-literals.js +38 -7
  89. package/dist/frontend/lowering/expressions/object-literals.js.map +1 -1
  90. package/dist/frontend/lowering/expressions/optional-chains.d.ts +3 -4
  91. package/dist/frontend/lowering/expressions/optional-chains.js +69 -17
  92. package/dist/frontend/lowering/expressions/optional-chains.js.map +1 -1
  93. package/dist/frontend/lowering/function-context.d.ts +11 -3
  94. package/dist/frontend/lowering/function-context.js +11 -5
  95. package/dist/frontend/lowering/function-context.js.map +1 -1
  96. package/dist/frontend/lowering/generator-dynamic-dispatch.d.ts +1 -0
  97. package/dist/frontend/lowering/generator-dynamic-dispatch.js +39 -1
  98. package/dist/frontend/lowering/generator-dynamic-dispatch.js.map +1 -1
  99. package/dist/frontend/lowering/lib-boundary.d.ts +2 -0
  100. package/dist/frontend/lowering/lib-boundary.js +54 -1
  101. package/dist/frontend/lowering/lib-boundary.js.map +1 -1
  102. package/dist/frontend/lowering/lower-builtin-values.d.ts +7 -1
  103. package/dist/frontend/lowering/lower-builtin-values.js +113 -3
  104. package/dist/frontend/lowering/lower-builtin-values.js.map +1 -1
  105. package/dist/frontend/lowering/lower-builtins.d.ts +6 -5
  106. package/dist/frontend/lowering/lower-builtins.js +156 -41
  107. package/dist/frontend/lowering/lower-builtins.js.map +1 -1
  108. package/dist/frontend/lowering/lower-calls.d.ts +8 -5
  109. package/dist/frontend/lowering/lower-calls.js +448 -83
  110. package/dist/frontend/lowering/lower-calls.js.map +1 -1
  111. package/dist/frontend/lowering/lower-classes.d.ts +13 -13
  112. package/dist/frontend/lowering/lower-classes.js +650 -176
  113. package/dist/frontend/lowering/lower-classes.js.map +1 -1
  114. package/dist/frontend/lowering/lower-containers.d.ts +9 -0
  115. package/dist/frontend/lowering/lower-containers.js +135 -36
  116. package/dist/frontend/lowering/lower-containers.js.map +1 -1
  117. package/dist/frontend/lowering/lower-dgram.js +3 -2
  118. package/dist/frontend/lowering/lower-dgram.js.map +1 -1
  119. package/dist/frontend/lowering/lower-exprs.d.ts +2 -2
  120. package/dist/frontend/lowering/lower-exprs.js +531 -126
  121. package/dist/frontend/lowering/lower-exprs.js.map +1 -1
  122. package/dist/frontend/lowering/lower-generators.js +12 -1
  123. package/dist/frontend/lowering/lower-generators.js.map +1 -1
  124. package/dist/frontend/lowering/lower-island.d.ts +2 -0
  125. package/dist/frontend/lowering/lower-island.js +32 -10
  126. package/dist/frontend/lowering/lower-island.js.map +1 -1
  127. package/dist/frontend/lowering/lower-modules.js +220 -17
  128. package/dist/frontend/lowering/lower-modules.js.map +1 -1
  129. package/dist/frontend/lowering/lower-modules.test.d.ts +1 -0
  130. package/dist/frontend/lowering/lower-modules.test.js +39 -0
  131. package/dist/frontend/lowering/lower-modules.test.js.map +1 -0
  132. package/dist/frontend/lowering/lower-stmts.d.ts +7 -3
  133. package/dist/frontend/lowering/lower-stmts.js +135 -26
  134. package/dist/frontend/lowering/lower-stmts.js.map +1 -1
  135. package/dist/frontend/lowering/lower-stream.js +1 -1
  136. package/dist/frontend/lowering/lower-stream.js.map +1 -1
  137. package/dist/frontend/lowering/lowerer.d.ts +27 -1
  138. package/dist/frontend/lowering/lowerer.js +172 -50
  139. package/dist/frontend/lowering/lowerer.js.map +1 -1
  140. package/dist/frontend/lowering/lowerer.test.js +149 -3
  141. package/dist/frontend/lowering/lowerer.test.js.map +1 -1
  142. package/dist/frontend/lowering/surfaces.d.ts +2 -0
  143. package/dist/frontend/lowering/surfaces.js +25 -8
  144. package/dist/frontend/lowering/surfaces.js.map +1 -1
  145. package/dist/frontend/lowering/text-decoder-encoding.d.ts +2 -0
  146. package/dist/frontend/lowering/text-decoder-encoding.js +5 -0
  147. package/dist/frontend/lowering/text-decoder-encoding.js.map +1 -1
  148. package/dist/frontend/lowering/unregistered-class-types.test.js +3 -3
  149. package/dist/frontend/lowering/unregistered-class-types.test.js.map +1 -1
  150. package/dist/frontend/npm-static-prune.js +116 -34
  151. package/dist/frontend/npm-static-prune.js.map +1 -1
  152. package/dist/frontend/npm-static-prune.test.js +8 -0
  153. package/dist/frontend/npm-static-prune.test.js.map +1 -1
  154. package/dist/frontend/program.d.ts +1 -1
  155. package/dist/frontend/program.js +53 -24
  156. package/dist/frontend/program.js.map +1 -1
  157. package/dist/frontend/program.test.js +18 -0
  158. package/dist/frontend/program.test.js.map +1 -1
  159. package/dist/frontend/ts7/ast.js +4 -3
  160. package/dist/frontend/ts7/ast.js.map +1 -1
  161. package/dist/frontend/ts7/semantic-json.js +5 -10
  162. package/dist/frontend/ts7/semantic-json.js.map +1 -1
  163. package/dist/frontend/type-mapper.d.ts +9 -1
  164. package/dist/frontend/type-mapper.js +90 -13
  165. package/dist/frontend/type-mapper.js.map +1 -1
  166. package/dist/frontend/type-mapper.test.js +23 -0
  167. package/dist/frontend/type-mapper.test.js.map +1 -1
  168. package/dist/frontend/union-discriminants.d.ts +1 -1
  169. package/dist/frontend/union-discriminants.js +2 -2
  170. package/dist/frontend/union-discriminants.js.map +1 -1
  171. package/dist/ir/analysis.js +2 -0
  172. package/dist/ir/analysis.js.map +1 -1
  173. package/dist/ir/ir.d.ts +52 -33
  174. package/dist/ir/ir.js +142 -42
  175. package/dist/ir/ir.js.map +1 -1
  176. package/dist/ir/ir.test.js +35 -0
  177. package/dist/ir/ir.test.js.map +1 -1
  178. package/dist/ir/serialize.js +15 -3
  179. package/dist/ir/serialize.js.map +1 -1
  180. package/dist/ir/serialize.test.js +5 -1
  181. package/dist/ir/serialize.test.js.map +1 -1
  182. package/dist/ir/validate.js +80 -11
  183. package/dist/ir/validate.js.map +1 -1
  184. package/dist/ir/validate.test.js +61 -1
  185. package/dist/ir/validate.test.js.map +1 -1
  186. package/dist/native/sanitizer.js +3 -1
  187. package/dist/native/sanitizer.js.map +1 -1
  188. package/dist/native/toolchain.d.ts +1 -1
  189. package/dist/native/toolchain.js +26 -6
  190. package/dist/native/toolchain.js.map +1 -1
  191. package/dist/native/toolchain.test.js +53 -1
  192. package/dist/native/toolchain.test.js.map +1 -1
  193. package/package.json +20 -20
  194. package/surface-manifest.json +56 -81
@@ -11,13 +11,14 @@ import * as ts from "../ts7/adapter.js";
11
11
  import { dirname } from "node:path";
12
12
  import * as posix from "node:path/posix";
13
13
  import { pathToFileURL } from "node:url";
14
+ import { captureContextArguments } from "./function-context.js";
14
15
  import { OBJECT_CALLABLE_VALUES } from "./surfaces.js";
15
16
  import { wasiGuestPath } from "../../wasi-paths.js";
16
17
  import { BIGINT_T, BYTES_ELEMENT_NAME, BOOL, CAUGHT, DYN, DYN_HANDLE_KINDS, F64, JSVAL, NULL_T, REF_TRUTHY_KINDS, REGEX, RUNTIME_ERROR_CLASSES, SEARCH_PARAMS_T, STRING, UNDEFINED_T, VOID, arrayOf, canAdaptDynFuncTo, canDynCheckTo, canBoxFuncIntoDyn, funcOf, isDynTypedRefType, isSupportedArrayElem, isUnitType, jsOpResultKind, shapeHasAccessorSlots, typeEquals, typeKey, unionContainerArmsOk } from "../../ir/ir.js";
17
18
  import { cjsClassExprWholeExportOf, cjsExportAssignmentOf, cjsExportDiscardReason, isCjsExportTableLiteral, isCjsJsFile, isJsSourceFile, isModuleExportsAccess, isNodeEsmFile, locOf } from "../program.js";
18
19
  import { ARRAY_METHODS, builtinConstLit, builtinFenceHintOf, builtinModuleConstOf, builtinModulesArrayLit, builtinModuleFnOf, COMPOUND_ASSIGN_OPS, ISLAND_SURFACE, isChildSurfaceMember, MAP_METHODS, NARROW_FIRST, SET_METHODS, STRING_INDEX_METHODS, STR_METHODS, UNSUPPORTED_EXPR, sideEffectFreeOptionValue, stdlibGlobalNameOf } from "./surfaces.js";
19
20
  import { UNSUPPORTED, blockedBindingUseDiag, requiresDynamicPackageDiag, unsupportedDiag } from "../../diagnostics/diagnostic.js";
20
- import { PoisonError, dynUndefinedExpr, jsFuncNameOf, neverTaintedJsType, nodeThrowExpr, own } from "./lowerer.js";
21
+ import { PoisonError, dynFallbackType, dynUndefinedExpr, jsFuncNameOf, neverTaintedJsType, nodeThrowExpr, own } from "./lowerer.js";
21
22
  import { lowerCollectionSpread, lowerNpmStaticSafeIndexRead, lowerSafeIndexRead, strCharsCall, tryLowerNumericIndexRead } from "./lower-containers.js";
22
23
  import { arrayValueRead, arrayValueStore, arrayValueType } from "./array-values.js";
23
24
  import { lowerOptionalStringIndex } from "./string-index.js";
@@ -29,13 +30,14 @@ import { ambientNsRootOf, ambientUndefReadType, ambientUndefVarRootOf, ambientUn
29
30
  import { expandoMemberRead, expandoWritableTarget } from "./lower-expando.js";
30
31
  import { lowerSocketInstanceOf, lowerTlsRootCertificates } from "./lower-server.js";
31
32
  import { findGenericMethodOn, lowerStaticFieldRead, staticFieldWriteTarget, storedClassValueType } from "./lower-classes.js";
33
+ import { hasRuntimeStatics } from "./class-runtime-statics.js";
32
34
  import { bindingNeverReassigned, funcTypeFromParamShapes, implicitMonoFile, lowerTaggedTemplate, nullishGenericBindingUnitOf, objLitGenericFnInfoOf, objLitGenericFnNodeOf, requireObjLitGenericReceiver } from "./lower-calls.js";
33
35
  import { mixinFnOfCallee } from "./lower-mixins.js";
34
36
  import { isConstAssertionTypeNode, isGenericCallableMemberType, isParseArgsDynTypeName, underConstAssertion, unitOnlyUnion, withUnitArm } from "../type-mapper.js";
35
37
  import { lowerYield } from "./lower-generators.js";
36
- import { errorToStringCall } from "./error-methods.js";
38
+ import { errorPropertyRead, errorPropertyWrite, errorToStringCall } from "./error-methods.js";
37
39
  import { lowerStreamProperty, lowerStreamStateProperty, streamSidesOf } from "./lower-stream.js";
38
- import { countedFor, numLit, varRef } from "../../ir/build.js";
40
+ import { countedFor, numLit, strLit, varRef } from "../../ir/build.js";
39
41
  import { unionWideningTags } from "../../ir/analysis.js";
40
42
  import { isSafeToDiscard, isSafeToMoveConditionEarlier, isSafeToRepeat } from "./expressions/evaluation-safety.js";
41
43
  import { globalSymbolKey } from "./expressions/global-symbols.js";
@@ -52,8 +54,12 @@ import { lowerClassMethodValue } from "./class-method-values.js";
52
54
  import { classInstanceOf } from "./class-dynamic-dispatch.js";
53
55
  import { lowerGlobalValue } from "./lower-global-value.js";
54
56
  import { lowerClassPrototypeData } from "./class-prototypes.js";
55
- import { lowerArrayIsArrayValue } from "./lower-builtin-values.js";
57
+ import { isClassCallback } from "./class-callbacks.js";
58
+ import { builtinPrototypeMethod, lowerArrayIsArrayValue, lowerCheckedPredicateValue, lowerNumberParserValue, lowerObjectAssignValue, lowerStringCodesValue } from "./lower-builtin-values.js";
59
+ import { lowerArrayFromValue } from "./lower-containers.js";
56
60
  import { lowerPerfHooksTypeof } from "./lower-builtins.js";
61
+ import { jsBindingHasOpenWrites } from "./lower-stmts.js";
62
+ import { checkedClassInstanceOf } from "./class-construction.js";
57
63
  /** A template piece's RAW text (String.raw's contract: escapes stay
58
64
  * characters). 7's client AST ships no rawText at runtime (the typing
59
65
  * declares it; the serialized node data omits it), so the raw span comes
@@ -704,8 +710,10 @@ function lowerExprInner(lowerer, expr) {
704
710
  // `arguments` in a variadic JS function (the rest-marked form): the
705
711
  // synthetic trailing dyn-array param — lambdaSignature marked the
706
712
  // type and lowerLambda declared the local.
707
- if (expr.text === "arguments" && lowerer.ctx.argumentsLocal) {
708
- return { kind: "varRef", localId: lowerer.ctx.argumentsLocal.id, type: DYN, loc };
713
+ if (expr.text === "arguments") {
714
+ const binding = captureContextArguments(lowerer.fnStack);
715
+ if (binding)
716
+ return { kind: "varRef", localId: binding.id, type: DYN, loc };
709
717
  }
710
718
  // A compiler-projected adapter as a VALUE: the adapter has no
711
719
  // runtime function object (its calls rewrite directly) — call it
@@ -1052,14 +1060,36 @@ function lowerExprInner(lowerer, expr) {
1052
1060
  // type-mapper.ts pins the one concrete signature `(value: string) =>
1053
1061
  // primitive`; direct calls `String(x)` never reach here — the call
1054
1062
  // lowering intercepts them with the wider static coercions).
1055
- const jsPrimitiveCtorSelector = isJsSourceFile(expr.getSourceFile()) &&
1056
- expr.parent !== undefined &&
1057
- ts.isArrowFunction(expr.parent) &&
1058
- expr.parent.body === expr;
1059
- if ((expr.text === "String" || expr.text === "Number" || expr.text === "Boolean") &&
1060
- (!isJsSourceFile(expr.getSourceFile()) || jsPrimitiveCtorSelector) &&
1061
- lowerer.isStdlibSymbol(lowerer.checker.getSymbolAtLocation(expr))) {
1062
- return primitiveCtorClosure(lowerer, expr.text, loc, jsPrimitiveCtorSelector);
1063
+ const jsPrimitiveCtorSelector = isJsSourceFile(expr.getSourceFile());
1064
+ const primitiveName = stdlibGlobalNameOf(lowerer, expr);
1065
+ if (primitiveName === "String" || primitiveName === "Number" || primitiveName === "Boolean" || primitiveName === "BigInt") {
1066
+ return primitiveCtorClosure(lowerer, primitiveName, loc, jsPrimitiveCtorSelector);
1067
+ }
1068
+ if (lowerer.isStdlibGlobal(expr, "ArrayBuffer"))
1069
+ return { kind: "libCall", fn: "arrayBuffer.constructor", args: [], type: DYN, loc };
1070
+ if (Object.values(BYTES_ELEMENT_NAME).includes(expr.text) && lowerer.isStdlibSymbol(lowerer.checker.getSymbolAtLocation(expr))) {
1071
+ return { kind: "libCall", fn: "bytes.constructor", args: [{ kind: "strLit", value: expr.text, type: STRING, loc }], type: DYN, loc };
1072
+ }
1073
+ if ((expr.text === "encodeURIComponent" || expr.text === "encodeURI" || expr.text === "decodeURIComponent" || expr.text === "decodeURI") && lowerer.isStdlibGlobal(expr, expr.text)) {
1074
+ const name = `%builtin.${expr.text}`;
1075
+ const type = funcOf([DYN], STRING);
1076
+ if (!lowerer.liftedFns.some((fn) => fn.name === name)) {
1077
+ const value = { kind: "libCall", fn: "dyn.toStringCoerce", args: [varRef("value", DYN, loc)], type: STRING, loc };
1078
+ const fn = expr.text === "encodeURIComponent" ? "str.encodeUriComponent" : expr.text === "encodeURI" ? "str.encodeUri" : expr.text === "decodeURI" ? "str.decodeUri" : "str.decodeUriComponent";
1079
+ lowerer.liftedFns.push({ name, params: [{ localId: "value", name: "value", type: DYN }],
1080
+ locals: [{ id: "value", name: "value", type: DYN, mutable: false }], returnType: STRING,
1081
+ body: [{ kind: "return", value: { kind: "libCall", fn, args: [value], type: STRING, loc }, loc }], loc });
1082
+ }
1083
+ return { kind: "closure", fnName: name, captures: [], type, loc };
1084
+ }
1085
+ if (!lowerer.dynamic && lowerer.isStdlibGlobal(expr, "crypto"))
1086
+ return { kind: "libCall", fn: "crypto.native", args: [], type: DYN, loc };
1087
+ const nativeAlias = stdlibGlobalNameOf(lowerer, expr);
1088
+ if (nativeAlias === "Buffer" && expr.text !== "Buffer") {
1089
+ return { kind: "strLit", value: "[builtin Buffer]", type: STRING, loc };
1090
+ }
1091
+ if (!lowerer.dynamic && lowerer.isStdlibGlobal(expr, "fetch")) {
1092
+ return { kind: "libCall", fn: "fetch.function", args: [], type: DYN, loc };
1063
1093
  }
1064
1094
  // The lib fence's IDENTIFIER chokepoint: the real standard library
1065
1095
  // resolves names the old minimal ambient world never declared
@@ -1260,8 +1290,13 @@ function lowerExprInner(lowerer, expr) {
1260
1290
  // `this` in plain nested functions is already a tsc error under
1261
1291
  // noImplicitThis, so the generic scope walk here is preflight-safe.
1262
1292
  const local = lowerer.resolveThis();
1263
- if (local)
1264
- return { kind: "varRef", localId: local.id, type: local.type, loc };
1293
+ if (local) {
1294
+ const value = { kind: "varRef", localId: local.id, type: local.type, loc };
1295
+ const initialized = lowerer.ctx.superInitialized;
1296
+ if (initialized)
1297
+ return { kind: "seqExpr", stmts: [{ kind: "if", cond: { kind: "unary", op: "!", operand: { kind: "varRef", localId: initialized.id, type: BOOL, loc }, type: BOOL, loc }, then: [{ kind: "exprStmt", expr: nodeThrowExpr(5, "", "Must call super constructor in derived class before accessing 'this' or returning from derived constructor", DYN, loc), loc }], else_: null, loc }], result: value, type: value.type, loc };
1298
+ return value;
1299
+ }
1265
1300
  // `this` in a plain JS FUNCTION (not a method): the AMBIENT
1266
1301
  // RECEIVER (libCall dyn.this — scr_dyn_this_get). Firing sites bind
1267
1302
  // the emitting handle around each listener call (Node calls
@@ -1456,6 +1491,12 @@ function lowerExprInner(lowerer, expr) {
1456
1491
  const prototypeData = lowerClassPrototypeData(lowerer, expr);
1457
1492
  if (prototypeData)
1458
1493
  return prototypeData;
1494
+ if (ts.isPropertyAccessExpression(expr.expression) && expr.expression.name.text === "prototype") {
1495
+ const prototype = lowerClassPrototypeData(lowerer, expr.expression);
1496
+ if (prototype)
1497
+ return { kind: "dynKeyGet", value: prototype,
1498
+ key: { kind: "strLit", value: expr.name.text, type: STRING, loc }, type: DYN, loc };
1499
+ }
1459
1500
  // `super.x`: the base chain's GETTER, called directly (super
1460
1501
  // dispatch is static in JS — never through the dynamic class).
1461
1502
  // super.method() calls are routed at the call site; a bare super
@@ -1501,6 +1542,14 @@ function lowerExprInner(lowerer, expr) {
1501
1542
  ["iterator", "asyncIterator", "toStringTag", "toPrimitive", "hasInstance", "isConcatSpreadable", "match", "matchAll", "replace", "search", "split", "species", "unscopables", "dispose", "asyncDispose"].includes(expr.name.text)) {
1502
1543
  return { kind: "libCall", fn: "sym.wellKnown", args: [{ kind: "strLit", value: expr.name.text, type: STRING, loc }], type: { kind: "symbol" }, loc };
1503
1544
  }
1545
+ if (!lowerer.dynamic && lowerer.stdlibGlobalMember(expr, "Object") === "freeze") {
1546
+ const name = "%builtin.Object.freeze";
1547
+ if (!lowerer.liftedFns.some((fn) => fn.name === name))
1548
+ lowerer.liftedFns.push({ name,
1549
+ params: [{ localId: "value", name: "value", type: DYN }], locals: [{ id: "value", name: "value", type: DYN, mutable: false }], returnType: DYN,
1550
+ body: [{ kind: "return", value: { kind: "libCall", fn: "dyn.freeze", args: [varRef("value", DYN, loc)], type: DYN, loc }, loc }], loc });
1551
+ return { kind: "closure", fnName: name, captures: [], type: funcOf([DYN], DYN), loc };
1552
+ }
1504
1553
  if (lowerer.stdlibGlobalMember(expr, "globalThis") === "console") {
1505
1554
  return { kind: "libCall", fn: "console.native", args: [], type: DYN, loc };
1506
1555
  }
@@ -1586,15 +1635,6 @@ function lowerExprInner(lowerer, expr) {
1586
1635
  }
1587
1636
  return read;
1588
1637
  }
1589
- // `m.groups` on a match result whose regex is statically known:
1590
- // the compile-time record projection over the honest slice (or
1591
- // Node's undefined when the pattern has no named groups) — see
1592
- // lowerMatchGroupsRead.
1593
- {
1594
- const g = lowerMatchGroupsRead(lowerer, expr);
1595
- if (g !== null)
1596
- return g;
1597
- }
1598
1638
  // `m.index` on a for-of-over-matchAll binding reads the companion-
1599
1639
  // index array at this iteration's cursor — always a number: every
1600
1640
  // drained row matched, so the lib's `.index` is never undefined
@@ -1612,6 +1652,25 @@ function lowerExprInner(lowerer, expr) {
1612
1652
  };
1613
1653
  }
1614
1654
  }
1655
+ if ((expr.name.text === "index" || expr.name.text === "input") && !expr.questionDotToken) {
1656
+ const represented = tryLowerExpression(lowerer, expr.expression);
1657
+ if (represented && isMatchSliceType(lowerer, represented.type) &&
1658
+ (lowerer.isStdlibMember(expr) || isJsSourceFile(expr.getSourceFile()))) {
1659
+ const recv = represented;
1660
+ const read = { kind: "dynKeyGet", value: lowerer.coerceToExpected(recv, DYN),
1661
+ key: { kind: "strLit", value: expr.name.text, type: STRING, loc }, type: DYN, loc };
1662
+ return lowerer.maybeNarrow(read, expr);
1663
+ }
1664
+ }
1665
+ // `m.groups` on a match result whose regex is statically known:
1666
+ // the compile-time record projection over the honest slice (or
1667
+ // Node's undefined when the pattern has no named groups) — see
1668
+ // lowerMatchGroupsRead.
1669
+ {
1670
+ const g = lowerMatchGroupsRead(lowerer, expr);
1671
+ if (g !== null)
1672
+ return g;
1673
+ }
1615
1674
  // `arguments.length` in a TYPED function whose signature is
1616
1675
  // FIXED-ARITY (no optional/default/rest parameters): tsc enforces
1617
1676
  // exact arity at every call site and call/apply/bind indirection is
@@ -1833,6 +1892,12 @@ function lowerExprInner(lowerer, expr) {
1833
1892
  if (canonical !== null) {
1834
1893
  if (!lowerer.dynamic && canonical === "globalThis")
1835
1894
  return lowerGlobalValue(lowerer, expr);
1895
+ if (!lowerer.dynamic && canonical === "ArrayBuffer")
1896
+ return { kind: "libCall", fn: "arrayBuffer.constructor", args: [], type: DYN, loc };
1897
+ if (!lowerer.dynamic && canonical === "fetch")
1898
+ return { kind: "libCall", fn: "fetch.function", args: [], type: DYN, loc };
1899
+ if (!lowerer.dynamic && Object.values(BYTES_ELEMENT_NAME).includes(canonical))
1900
+ return { kind: "libCall", fn: "bytes.constructor", args: [{ kind: "strLit", value: canonical, type: STRING, loc }], type: DYN, loc };
1836
1901
  return { kind: "strLit", value: `[builtin ${canonical}]`, type: STRING, loc };
1837
1902
  }
1838
1903
  }
@@ -1921,10 +1986,14 @@ function lowerExprInner(lowerer, expr) {
1921
1986
  // is an ordinary recordGet — maybeNarrow then bridges the union slot
1922
1987
  // to the checker's narrowed arm, exactly like a union field read.
1923
1988
  if (expr.name.text === "done" || expr.name.text === "value") {
1924
- const recvSym = lowerer.typeOf(expr.expression).getSymbol();
1925
- if ((recvSym?.name === "IteratorYieldResult" || recvSym?.name === "IteratorReturnResult") &&
1926
- lowerer.checker.declarationsOf(recvSym).some((d) => ts.isInterfaceDeclaration(d) && lowerer.isStdlibFile(d.getSourceFile()))) {
1989
+ const receiverType = lowerer.typeOf(expr.expression);
1990
+ const recvSym = receiverType.getAliasSymbol()?.name === "IteratorResult" ? receiverType.getAliasSymbol() : receiverType.getSymbol();
1991
+ if ((recvSym?.name === "IteratorResult" || recvSym?.name === "IteratorYieldResult" || recvSym?.name === "IteratorReturnResult") &&
1992
+ lowerer.checker.declarationsOf(recvSym).some((d) => (ts.isInterfaceDeclaration(d) || ts.isTypeAliasDeclaration(d)) && lowerer.isStdlibFile(d.getSourceFile()))) {
1927
1993
  const recv = lowerer.lowerExpr(expr.expression);
1994
+ if (recv.type.kind === "dyn")
1995
+ return lowerer.maybeNarrow({ kind: "dynKeyGet", value: recv,
1996
+ key: { kind: "strLit", value: expr.name.text, type: STRING, loc }, type: DYN, loc }, expr);
1928
1997
  if (recv.type.kind === "record") {
1929
1998
  const shape = lowerer.shapes.get(recv.type.shapeId);
1930
1999
  const field = shape?.fields.find((f) => f.name === expr.name.text);
@@ -2021,10 +2090,11 @@ function lowerExprInner(lowerer, expr) {
2021
2090
  // on undefined/null receivers unless an earlier `?.` guards the
2022
2091
  // chain. Scalar-narrowed occurrences bridge via maybeNarrow's
2023
2092
  // validated dynCheck as usual.
2024
- if (recvLowered.type.kind === "dyn") {
2093
+ if (recvLowered.type.kind === "dyn" || isJsSourceFile(expr.getSourceFile()) &&
2094
+ ["f64", "bool", "bigint", "string", "symbol"].includes(recvLowered.type.kind)) {
2025
2095
  const key = { kind: "strLit", value: expr.name.text, type: STRING, loc: locOf(expr.name) };
2026
2096
  const opt = hasOptionalChainGuard(expr.expression);
2027
- return lowerer.maybeNarrow({ kind: "dynKeyGet", key, ...(opt ? { optional: true } : {}), value: recvLowered, type: DYN, loc }, expr);
2097
+ return lowerer.maybeNarrow({ kind: "dynKeyGet", key, ...(opt ? { optional: true } : {}), value: lowerer.coerceToExpected(recvLowered, DYN), type: DYN, loc }, expr);
2028
2098
  }
2029
2099
  // The lowered receiver is a RECORD the checker spelled wider —
2030
2100
  // `s.match(re).groups.key` in a JS file: the checker says
@@ -2074,6 +2144,16 @@ function lowerExprInner(lowerer, expr) {
2074
2144
  // reports a class. Its property bag must remain reachable, while
2075
2145
  // an absent element takes the normal checked-value TypeError.
2076
2146
  const nativeReceiver = lowerer.stripUndefinedArm(recvLowered.type);
2147
+ if (ts.isPrivateIdentifier(expr.name) && recvLowered.type.kind === "object") {
2148
+ const field = lowerer.classes.get(recvLowered.type.className)?.fields.get(expr.name.text);
2149
+ if (field)
2150
+ return { kind: "fieldGet", obj: recvLowered, className: recvLowered.type.className,
2151
+ field: expr.name.text, type: field, loc };
2152
+ }
2153
+ if (nativeReceiver.kind === "object" && nativeReceiver.className === "%Error") {
2154
+ return lowerer.maybeNarrow({ kind: "dynKeyGet", value: lowerer.coerceToExpected(recvLowered, DYN),
2155
+ key: { kind: "strLit", value: expr.name.text, type: STRING, loc }, type: DYN, loc }, expr);
2156
+ }
2077
2157
  if (isDynTypedRefType(nativeReceiver)) {
2078
2158
  const info = lowerer.classes.get(nativeReceiver.className);
2079
2159
  if (info && !info.def.runtime && !info.builtinError && !info.builtinEmitter && !info.builtinStream) {
@@ -2107,7 +2187,7 @@ function moduleFileName(lowerer, sf) {
2107
2187
  : sf.fileName;
2108
2188
  }
2109
2189
  function importMetaProperty(lowerer, expr) {
2110
- if (expr.questionDotToken !== undefined || !ts.isMetaProperty(expr.expression))
2190
+ if (!ts.isMetaProperty(expr.expression))
2111
2191
  return null;
2112
2192
  const meta = expr.expression;
2113
2193
  if (meta.keywordToken !== ts.SyntaxKind.ImportKeyword || meta.name.text !== "meta")
@@ -2115,6 +2195,8 @@ function importMetaProperty(lowerer, expr) {
2115
2195
  const sf = expr.getSourceFile();
2116
2196
  const fileName = moduleFileName(lowerer, sf);
2117
2197
  switch (expr.name.text) {
2198
+ case "env":
2199
+ return dynUndefinedExpr(locOf(expr));
2118
2200
  case "url":
2119
2201
  return {
2120
2202
  kind: "strLit",
@@ -2398,7 +2480,13 @@ export function lowerTernary(lowerer, expr, expected) {
2398
2480
  thenRaw.type.kind === "func" && elseRaw.type.kind === "func" &&
2399
2481
  !typeEquals(thenRaw.type, elseRaw.type) &&
2400
2482
  lowerer.dynConvertible(thenRaw.type) && lowerer.dynConvertible(elseRaw.type);
2401
- const type = callableJoin ? DYN :
2483
+ // JavaScript inference can discard an initially-undefined field from
2484
+ // a conditional's type even after that field acquired other values.
2485
+ // A checked arm must retain its actual representation at the join.
2486
+ const checkedJsJoin = isJsSourceFile(expr.getSourceFile()) &&
2487
+ (thenRaw.type.kind === "dyn" || elseRaw.type.kind === "dyn") &&
2488
+ lowerer.dynConvertible(thenRaw.type) && lowerer.dynConvertible(elseRaw.type);
2489
+ const type = callableJoin || checkedJsJoin ? DYN :
2402
2490
  thenRaw.type.kind === "array" && typeEquals(thenRaw.type, elseRaw.type)
2403
2491
  ? thenRaw.type
2404
2492
  : dynJoin
@@ -2454,6 +2542,11 @@ export function maybeNarrow(lowerer, expr, node) {
2454
2542
  if (expr.type.kind === "dyn") {
2455
2543
  if (isJsSourceFile(node.getSourceFile()) && ts.isIdentifier(node)) {
2456
2544
  const symbol = lowerer.resolveValueSymbol(node);
2545
+ if (symbol && lowerer.checker.declarationsOf(symbol).some((decl) => ts.isVariableDeclaration(decl) && jsBindingHasOpenWrites(lowerer, decl)))
2546
+ return expr;
2547
+ if (symbol && lowerer.checker.declarationsOf(symbol).some((decl) => ts.isParameter(decl) && decl.initializer && !decl.type &&
2548
+ !/@(?:param|type)\b/.test(decl.getSourceFile().text.slice(decl.parent?.pos ?? decl.pos, decl.getStart()))))
2549
+ return expr;
2457
2550
  if (symbol && lowerer.checker.declarationsOf(symbol).some((decl) => {
2458
2551
  if (!ts.isParameter(decl) || decl.type)
2459
2552
  return false;
@@ -3003,6 +3096,10 @@ function lowerNullishPair(lowerer, expr, loc, left) {
3003
3096
  if (fresh)
3004
3097
  return { kind: "nullish", left, right: fresh, type: fresh.type, loc };
3005
3098
  const type = lowerer.irTypeOf(expr);
3099
+ if (type.kind === "dyn" && lowerer.dynConvertible(left.type)) {
3100
+ return { kind: "nullish", left: lowerer.coerceToExpected(left, DYN),
3101
+ right: lowerer.lowerExprExpecting(expr.right, DYN), type: DYN, loc };
3102
+ }
3006
3103
  if (typeEquals(type, left.type) || (rest.length === 1 && typeEquals(type, rest[0]))) {
3007
3104
  const right = lowerer.lowerExprExpecting(expr.right, type);
3008
3105
  return { kind: "nullish", left, right, type, loc };
@@ -3073,6 +3170,8 @@ export function lowerCondition(lowerer, expr) {
3073
3170
  if (op === ts.SyntaxKind.AmpersandAmpersandToken || op === ts.SyntaxKind.BarBarToken) {
3074
3171
  const isAnd = op === ts.SyntaxKind.AmpersandAmpersandToken;
3075
3172
  const left = lowerer.lowerCondition(e.left);
3173
+ if (left.kind === "boolLit" && left.value !== isAnd)
3174
+ return left;
3076
3175
  // The right operand evaluates only when the left already answered
3077
3176
  // (true for &&, false for ||) — aliased-typeof narrows the left
3078
3177
  // PROVES under that polarity hold while it lowers (`type ===
@@ -3089,6 +3188,8 @@ export function lowerCondition(lowerer, expr) {
3089
3188
  lowerer.runtimeOptionalLocals.add(lowerer.runtimeOptionalRootOf(runtimeOptionalLocal));
3090
3189
  }
3091
3190
  });
3191
+ if (left.kind === "boolLit")
3192
+ return right;
3092
3193
  return {
3093
3194
  kind: "logical",
3094
3195
  op: isAnd ? "&&" : "||",
@@ -3366,13 +3467,56 @@ export function eqComparableUnion(lowerer, unionId) {
3366
3467
  * the receiver type AND the ambient-file provenance of the member are
3367
3468
  * verified — the name alone proves nothing. */
3368
3469
  export function lowerIntrinsicProperty(lowerer, expr) {
3470
+ for (const name of ["String", "Number", "Boolean", "BigInt"]) {
3471
+ if (!lowerer.chainBlocked(expr) && lowerer.isStdlibGlobal(expr, name)) {
3472
+ return primitiveCtorClosure(lowerer, name, locOf(expr), isJsSourceFile(expr.getSourceFile()));
3473
+ }
3474
+ }
3475
+ if (lowerer.stdlibGlobalMember(expr, "Reflect") === "ownKeys") {
3476
+ const value = lowerer.lowerNativeCallableValue({
3477
+ fn: "dyn.ownKeys", params: [DYN], result: DYN, valueParams: [{ mode: "required", type: DYN }],
3478
+ }, "Reflect.ownKeys", locOf(expr));
3479
+ return { kind: "dynFrom", value, fnName: "ownKeys", type: DYN, loc: locOf(expr) };
3480
+ }
3369
3481
  if (lowerer.chainBlocked(expr))
3370
3482
  return null;
3483
+ const builtinPrototype = builtinPrototypeMethod(lowerer, expr);
3484
+ if (builtinPrototype)
3485
+ return { kind: "libCall", fn: "dyn.builtinMethod", args: [
3486
+ { kind: "strLit", value: builtinPrototype, type: STRING, loc: locOf(expr) },
3487
+ { kind: "strLit", value: expr.name.text, type: STRING, loc: locOf(expr.name) },
3488
+ ], type: DYN, loc: locOf(expr) };
3489
+ if (ts.isPropertyAccessExpression(expr.expression) &&
3490
+ lowerer.stdlibGlobalMember(expr.expression, "Array") === "prototype") {
3491
+ return { kind: "dynKeyGet", value: { kind: "libCall", fn: "dyn.arrayPrototype", args: [], type: DYN, loc: locOf(expr.expression) },
3492
+ key: { kind: "strLit", value: expr.name.text, type: STRING, loc: locOf(expr.name) }, type: DYN, loc: locOf(expr) };
3493
+ }
3494
+ if (expr.name.text === "apply" && ts.isPropertyAccessExpression(expr.expression) &&
3495
+ lowerer.stdlibGlobalMember(expr.expression, "Function") === "prototype")
3496
+ return { kind: "libCall", fn: "dyn.functionApply", args: [], type: DYN, loc: locOf(expr) };
3371
3497
  if (lowerer.stdlibGlobalMember(expr, "Array") === "prototype")
3372
3498
  return { kind: "libCall", fn: "dyn.arrayPrototype", args: [], type: DYN, loc: locOf(expr) };
3499
+ if (lowerer.stdlibGlobalMember(expr, "Object") === "prototype")
3500
+ return { kind: "libCall", fn: "dyn.objectPrototype", args: [], type: DYN, loc: locOf(expr) };
3373
3501
  if (lowerer.stdlibGlobalMember(expr, "Array") === "isArray")
3374
3502
  return lowerArrayIsArrayValue(lowerer, locOf(expr));
3503
+ if (lowerer.stdlibGlobalMember(expr, "Array") === "from")
3504
+ return lowerArrayFromValue(lowerer, locOf(expr));
3505
+ const stringMember = lowerer.stdlibGlobalMember(expr, "String");
3506
+ if (stringMember === "fromCharCode" || stringMember === "fromCodePoint")
3507
+ return lowerStringCodesValue(lowerer, stringMember, locOf(expr));
3508
+ const numberMember = lowerer.stdlibGlobalMember(expr, "Number");
3509
+ if (numberMember === "parseInt" || numberMember === "parseFloat")
3510
+ return lowerNumberParserValue(lowerer, numberMember, locOf(expr));
3511
+ if (lowerer.stdlibGlobalMember(expr, "Buffer") === "isBuffer")
3512
+ return lowerCheckedPredicateValue(lowerer, "Buffer.isBuffer", "buffer", locOf(expr));
3513
+ if (lowerer.stdlibGlobalMember(expr, "Date") === "now")
3514
+ return lowerer.lowerNativeCallableValue({
3515
+ fn: "date.now", params: [], result: F64, valueParams: [],
3516
+ }, "Date.now", locOf(expr));
3375
3517
  if (stdlibGlobalNameOf(lowerer, expr.expression) === "Object") {
3518
+ if (expr.name.text === "assign")
3519
+ return lowerObjectAssignValue(lowerer, locOf(expr));
3376
3520
  const fn = own(OBJECT_CALLABLE_VALUES, expr.name.text);
3377
3521
  if (fn)
3378
3522
  return lowerer.lowerNativeCallableValue(fn, `Object.${expr.name.text}`, locOf(expr));
@@ -3410,6 +3554,15 @@ export function lowerIntrinsicProperty(lowerer, expr) {
3410
3554
  if (kind === "child" ? !isChildSurfaceMember(lowerer, expr) : !tupleLengthOnArray && !lowerer.isStdlibMember(expr))
3411
3555
  return null;
3412
3556
  const name = expr.name.text;
3557
+ if (kind === "array" && name === "constructor" && isJsSourceFile(expr.getSourceFile())) {
3558
+ const receiver = lowerer.lowerExpr(expr.expression);
3559
+ if (receiver.type.kind === "dyn")
3560
+ return { kind: "dynKeyGet", value: receiver,
3561
+ key: { kind: "strLit", value: name, type: STRING, loc: locOf(expr) }, type: DYN, loc: locOf(expr) };
3562
+ // Ordinary arrays keep the existing Array builtin identity token.
3563
+ const result = { kind: "strLit", value: "[builtin Array]", type: STRING, loc: locOf(expr) };
3564
+ return { kind: "seqExpr", stmts: [{ kind: "exprStmt", expr: receiver, loc: locOf(expr) }], result, type: STRING, loc: locOf(expr) };
3565
+ }
3413
3566
  const strictReceiver = (expected) => {
3414
3567
  const value = lowerer.lowerExpr(expr.expression);
3415
3568
  if (value.type.kind === "dyn")
@@ -3585,6 +3738,9 @@ export function lowerIntrinsicProperty(lowerer, expr) {
3585
3738
  lowerer.noLowering(`URLSearchParams.${name}`, expr, "get, getAll, set, append, delete, has, sort, size, toString(), forEach, and for-of iteration are the supported URLSearchParams members", lowerer.checker.getSymbolAtLocation(expr.name));
3586
3739
  }
3587
3740
  if (kind === "regex") {
3741
+ if (name === "lastIndex") {
3742
+ return { kind: "regexIntrinsic", method: "lastIndex", receiver: strictReceiver({ kind: "regex" }), args: [], type: F64, loc: locOf(expr) };
3743
+ }
3588
3744
  if (name === "source" || name === "flags") {
3589
3745
  const receiver = strictReceiver({ kind: "regex" });
3590
3746
  return { kind: "regexIntrinsic", method: name, receiver, args: [], type: STRING, loc: locOf(expr) };
@@ -3604,6 +3760,12 @@ export function lowerIntrinsicProperty(lowerer, expr) {
3604
3760
  }
3605
3761
  if (kind === "bytes") {
3606
3762
  const loc = locOf(expr);
3763
+ if (name === "constructor") {
3764
+ const expected = lowerer.mapTypeOf(lowerer.typeOf(expr.expression));
3765
+ const receiver = expected?.kind === "bytes" ? strictReceiver(expected) : lowerer.lowerExpr(expr.expression);
3766
+ return { kind: "dynKeyGet", value: lowerer.coerceInto(expr.expression, receiver, DYN),
3767
+ key: { kind: "strLit", value: name, type: STRING, loc }, type: DYN, loc };
3768
+ }
3607
3769
  if (name === "length" || name === "byteLength") {
3608
3770
  const expected = lowerer.mapTypeOf(lowerer.typeOf(expr.expression));
3609
3771
  let receiver = expected?.kind === "bytes"
@@ -3674,9 +3836,10 @@ export function lowerIntrinsicProperty(lowerer, expr) {
3674
3836
  const expected = kind === "string"
3675
3837
  ? STRING
3676
3838
  : lowerer.mapTypeOf(lowerer.typeOf(expr.expression));
3677
- let receiver = expected && (expected.kind === "string" || expected.kind === "array")
3678
- ? strictReceiver(expected)
3679
- : lowerer.lowerExpr(expr.expression);
3839
+ let receiver = lowerer.lowerExpr(expr.expression);
3840
+ if (receiver.type.kind !== "dyn" && expected && (expected.kind === "string" || expected.kind === "array")) {
3841
+ receiver = lowerer.runtimeOptionalPropertyReceiver(expr.expression, receiver, expected, name) ?? receiver;
3842
+ }
3680
3843
  if (receiver.type.kind === "union" && lowerer.armTag(receiver.type.unionId, UNDEFINED_T) >= 0 && kind === "array") {
3681
3844
  const present = lowerer.stripUndefinedArm(receiver.type);
3682
3845
  const helper = present.kind === "array"
@@ -3742,6 +3905,19 @@ export function lowerArrayLiteral(lowerer, expr, expected) {
3742
3905
  // destination can use the ordinary tuple-to-array coercion afterward.
3743
3906
  const tsType = underConstAssertion(expr) ? lowerer.typeOf(expr) : ctxType ?? lowerer.typeOf(expr);
3744
3907
  let mapped = expected ?? lowerer.mapTypeOf(tsType);
3908
+ // Inferred JavaScript arrays can contain unrelated constructors with
3909
+ // structurally equal checker types. Their native class ABIs still differ.
3910
+ if (!expected && isJsSourceFile(expr.getSourceFile()) && mapped?.kind === "array" && mapped.elem.kind === "classval")
3911
+ mapped = DYN;
3912
+ // An inferred destructuring pattern supplies a contextual [any, ...]
3913
+ // tuple. Its positions do not erase the literal's inferred element
3914
+ // types; explicit destinations already arrive through expected.
3915
+ if (expected === undefined && ctxType && lowerer.checker.isTupleType(ctxType) &&
3916
+ lowerer.checker.getTypeArguments(ctxType).some((type) => (type.flags & ts.TypeFlags.Any) !== 0)) {
3917
+ const inferred = lowerer.mapTypeOf(lowerer.typeOf(expr));
3918
+ if (inferred?.kind === "array" || inferred?.kind === "record" && lowerer.shapes.get(inferred.shapeId)?.tuple)
3919
+ mapped = inferred;
3920
+ }
3745
3921
  // A JS literal whose OWN inferred type is never-tainted
3746
3922
  // (neverTaintedJsType — the evolving `const gb = []`, the mixed
3747
3923
  // command tuple `['pwd', []]`) carries no element information: route
@@ -3838,6 +4014,12 @@ export function lowerArrayLiteral(lowerer, expr, expected) {
3838
4014
  mapped = arrayOf(first.type);
3839
4015
  }
3840
4016
  }
4017
+ if (!expected && isJsSourceFile(expr.getSourceFile()) && mapped?.kind === "array") {
4018
+ const elem = mapped.elem;
4019
+ const arms = elem.kind === "union" ? lowerer.unions.get(elem.unionId)?.arms : [elem];
4020
+ if (arms?.length && arms.every((arm) => arm.kind === "classval"))
4021
+ mapped = DYN;
4022
+ }
3841
4023
  // An EMPTY literal under a CONST ASSERTION whose type queries panicked
3842
4024
  // (tsgo's readonly-[] TupleType conversion — the facade answers `any`,
3843
4025
  // which maps to nothing statically and to an island value under
@@ -3849,6 +4031,11 @@ export function lowerArrayLiteral(lowerer, expr, expected) {
3849
4031
  underConstAssertion(expr)) {
3850
4032
  mapped = arrayOf(unitOnlyUnion(lowerer.unions));
3851
4033
  }
4034
+ // Heterogeneous JavaScript object arrays must retain each literal's
4035
+ // real keys. A structural union can otherwise drop descriptor fields.
4036
+ if (!expected && isJsSourceFile(expr.getSourceFile()) && mapped?.kind === "array" && mapped.elem.kind === "union" &&
4037
+ expr.elements.some(ts.isObjectLiteralExpression))
4038
+ mapped = DYN;
3852
4039
  // A TUPLE-typed slot (`const t: [string, number] = ["a", 1]`): the
3853
4040
  // literal constructs the tuple's record shape — one positional field
3854
4041
  // per element, source order (which IS index order, so evaluation order
@@ -3904,7 +4091,7 @@ export function lowerArrayLiteral(lowerer, expr, expected) {
3904
4091
  // `unknown[]` annotation or a collapsed `(string | object)[]` maps
3905
4092
  // to DYN wholesale now (mapType's dyn-element array rule), so the
3906
4093
  // literal IS the dyn array.
3907
- if (isJsSourceFile(expr.getSourceFile()) || mapped?.kind === "dyn") {
4094
+ if (isJsSourceFile(expr.getSourceFile()) || mapped?.kind === "dyn" || lowerer.checkerAnyArray(expr)) {
3908
4095
  if (expr.elements.some(ts.isSpreadElement)) {
3909
4096
  // Native collection drains return vectors of retained dyn
3910
4097
  // elements. Reuse the ordinary spread builder, then expose the
@@ -3998,11 +4185,7 @@ export function lowerArrayLiteral(lowerer, expr, expected) {
3998
4185
  // the checker inferred a typed array from its producer. Drain once
3999
4186
  // and validate the elements before the ordinary spread copy.
4000
4187
  if (src.type.kind === "dyn" && (type.elem.kind === "dyn" || type.elem.kind === "f64" || type.elem.kind === "string" || type.elem.kind === "bool")) {
4001
- src = lowerer.coerceInto(el.expression, {
4002
- kind: "libCall", fn: "dyn.iterPack", args: [src,
4003
- { kind: "strLit", value: el.expression.getText(), type: STRING, loc: locOf(el) }],
4004
- type: DYN, loc: locOf(el),
4005
- }, type);
4188
+ src = lowerer.coerceInto(el.expression, checkedIteratorPack(lowerer, src, locOf(el)), type);
4006
4189
  }
4007
4190
  // A same-family array whose ELEMENT lifts (string[] into a
4008
4191
  // (string | symbol)[] literal — per-element wrap/width copy):
@@ -4320,7 +4503,11 @@ export function lowerElementAccess(lowerer, expr) {
4320
4503
  if (target)
4321
4504
  return lowerer.maybeNarrow(lowerer.fieldGetExpr(target, locOf(expr), expr), expr);
4322
4505
  let receiver = lowerer.lowerExpr(expr.expression);
4323
- if (receiver.type.kind === "func") {
4506
+ const classInfo = receiver.type.kind === "classval" ? lowerer.classes.get(receiver.type.className) : undefined;
4507
+ if (receiver.type.kind === "func" || receiver.type.kind === "generator" ||
4508
+ ((receiver.type.kind === "array" || receiver.type.kind === "string" || receiver.type.kind === "bytes") && lowerer.dynConvertible(receiver.type)) ||
4509
+ (receiver.type.kind === "object" && lowerer.dynConvertible(receiver.type)) || isDynTypedRefType(receiver.type) ||
4510
+ (classInfo && (classInfo.callableBase || hasRuntimeStatics(classInfo))) || ((receiver.type.kind === "map" || receiver.type.kind === "set") && lowerer.dynConvertible(receiver.type))) {
4324
4511
  const fnName = jsFuncNameOf(expr.expression);
4325
4512
  receiver = { kind: "dynFrom", value: receiver, type: DYN, loc: locOf(expr),
4326
4513
  ...(fnName !== null ? { fnName } : {}) };
@@ -4340,11 +4527,17 @@ export function lowerElementAccess(lowerer, expr) {
4340
4527
  : lowerer.mapTypeOf(lowerer.typeOf(expr.expression));
4341
4528
  if (isJsSourceFile(expr.getSourceFile())) {
4342
4529
  const receiver = tryLowerExpression(lowerer, expr.expression);
4530
+ if (receiver?.type.kind === "dyn") {
4531
+ const key = lowerer.lowerExprExpecting(expr.argumentExpression, DYN);
4532
+ const optional = hasOptionalChainGuard(expr.expression);
4533
+ return lowerer.maybeNarrow({ kind: "dynKeyGet", value: receiver, key,
4534
+ ...(optional ? { optional: true } : {}), type: DYN, loc: locOf(expr) }, expr);
4535
+ }
4343
4536
  if (receiver?.type.kind === "func" && lowerer.dynConvertible(receiver.type)) {
4344
4537
  const loc = locOf(expr);
4345
4538
  const fnName = jsFuncNameOf(expr.expression);
4346
4539
  const boxed = { kind: "dynFrom", value: receiver, type: DYN, loc, ...(fnName !== null ? { fnName } : {}) };
4347
- const key = lowerRecordPropertyKey(lowerer, lowerer.lowerExpr(expr.argumentExpression), expr.argumentExpression);
4540
+ const key = lowerer.lowerExprExpecting(expr.argumentExpression, DYN);
4348
4541
  return lowerer.maybeNarrow({ kind: "dynKeyGet", key, value: boxed, type: DYN, loc }, expr);
4349
4542
  }
4350
4543
  }
@@ -4363,7 +4556,7 @@ export function lowerElementAccess(lowerer, expr) {
4363
4556
  if (target)
4364
4557
  return lowerer.maybeNarrow(lowerer.fieldGetExpr(target, locOf(expr), expr), expr);
4365
4558
  }
4366
- if (isDynTypedRefType(receiverIr)) {
4559
+ if (receiverIr.className === "%Error" || isDynTypedRefType(receiverIr)) {
4367
4560
  const receiver = lowerer.coerceToExpected(lowerer.lowerExpr(expr.expression), DYN);
4368
4561
  const key = lowerer.lowerExprExpecting(expr.argumentExpression, DYN);
4369
4562
  return lowerer.maybeNarrow({ kind: "dynKeyGet", value: receiver, key, type: DYN, loc: locOf(expr) }, expr);
@@ -4412,6 +4605,11 @@ export function lowerElementAccess(lowerer, expr) {
4412
4605
  if (recv.type.kind === "jsval")
4413
4606
  return islandElementRead(lowerer, expr, recv);
4414
4607
  const index = lowerer.lowerExpr(expr.argumentExpression);
4608
+ if (isJsSourceFile(expr.getSourceFile()) && recv.type.kind === "bytes") {
4609
+ return { kind: "dynKeyGet", value: lowerer.coerceInto(expr.expression, recv, DYN),
4610
+ key: index.type.kind === "f64" ? { kind: "toString", operand: index, type: STRING, loc: locOf(expr) } : index,
4611
+ type: DYN, loc: locOf(expr) };
4612
+ }
4415
4613
  if (index.type.kind === "dyn" && recv.type.kind === "bytes") {
4416
4614
  return { kind: "dynKeyGet", value: lowerer.coerceInto(expr.expression, recv, DYN), key: index, type: DYN, loc: locOf(expr) };
4417
4615
  }
@@ -4509,16 +4707,21 @@ export function lowerElementAccess(lowerer, expr) {
4509
4707
  // Dispatch from that stored shape, as the array fallback below does.
4510
4708
  if (receiverIr === null && obj.type.kind === "record") {
4511
4709
  const shape = lowerer.shapes.get(obj.type.shapeId);
4710
+ const index = tupleLiteralIndex(expr.argumentExpression);
4711
+ const field = shape?.tuple && index !== null ? shape.fields.find((field) => field.name === String(index)) : null;
4712
+ if (field)
4713
+ return lowerer.maybeNarrow({ kind: "recordGet", obj, shapeId: obj.type.shapeId, field: field.name, type: field.type, loc: locOf(expr) }, expr);
4512
4714
  if (shape && !shape.tuple) {
4513
4715
  return lowerer.lowerRecordKeyRead(expr, obj.type.shapeId, shape);
4514
4716
  }
4515
4717
  }
4516
- if (obj.type.kind === "dyn" || isDynTypedRefType(obj.type)) {
4718
+ if (obj.type.kind === "dyn" || obj.type.kind === "object" && obj.type.className === "%Error" || isDynTypedRefType(obj.type)) {
4517
4719
  const rawKey = lowerer.lowerExpr(expr.argumentExpression);
4518
4720
  // Number, bool, and DYN keys stringify (ToPropertyKey) — the
4519
4721
  // dyn-keyed read `catchWarning[warning.name]` where the property
4520
4722
  // chain itself lowered dyn.
4521
- const key = rawKey.type.kind === "dyn" || rawKey.type.kind === "symbol"
4723
+ const key = rawKey.type.kind === "dyn" || rawKey.type.kind === "symbol" ||
4724
+ rawKey.type.kind === "union" && lowerer.dynConvertible(rawKey.type)
4522
4725
  ? lowerer.coerceToExpected(rawKey, DYN)
4523
4726
  : lowerRecordPropertyKey(lowerer, rawKey, expr.argumentExpression);
4524
4727
  if (key.type.kind === "string" || key.type.kind === "dyn") {
@@ -4692,6 +4895,11 @@ export function lowerElementAccess(lowerer, expr) {
4692
4895
  // is undefined, whose property lookup misses just like NaN; keep that
4693
4896
  // value instead of rejecting a checker-number index with optional IR.
4694
4897
  const index = lowerOptionalNumber(lowerer, lowerer.lowerExpr(expr.argumentExpression), locOf(expr.argumentExpression), expr.argumentExpression);
4898
+ if (arr.type.kind === "array" && (index.type.kind === "dyn" ||
4899
+ isJsSourceFile(expr.getSourceFile()) && index.type.kind !== "f64" && lowerer.dynConvertible(index.type))) {
4900
+ return { kind: "dynKeyGet", value: lowerer.coerceToExpected(arr, DYN),
4901
+ key: index, type: DYN, loc: locOf(expr) };
4902
+ }
4695
4903
  if (index.type.kind !== "f64") {
4696
4904
  lowerer.unsupported("SC1090", expr.argumentExpression, "indexing with non-number keys");
4697
4905
  }
@@ -4802,8 +5010,11 @@ export function lowerRecordKeyRead(lowerer, expr, shapeId, shape, includeUndefin
4802
5010
  // Number AND checked-dynamic keys ride the JS-exact formatter —
4803
5011
  // property keys ARE strings (o[k] is o[String(k)] in JS), and a dyn
4804
5012
  // key (agent.sockets[agent.getName(...)]) stringifies the same way.
4805
- dk = lowerRecordPropertyKey(lowerer, dk, keyNode);
4806
- if (dk.type.kind !== "string") {
5013
+ if (dk.type.kind === "union" && lowerer.dynConvertible(dk.type))
5014
+ dk = lowerer.coerceToExpected(dk, DYN);
5015
+ if (dk.type.kind !== "symbol" && dk.type.kind !== "dyn")
5016
+ dk = lowerRecordPropertyKey(lowerer, dk, keyNode);
5017
+ if (dk.type.kind !== "string" && dk.type.kind !== "symbol" && dk.type.kind !== "dyn") {
4807
5018
  lowerer.unsupported("SC1090", keyNode, "indexing records with non-string or non-number keys");
4808
5019
  }
4809
5020
  const read = { kind: "dynKeyGet", key: dk, value: obj, type: DYN, loc };
@@ -4850,6 +5061,19 @@ export function lowerRecordKeyRead(lowerer, expr, shapeId, shape, includeUndefin
4850
5061
  let key = litKey !== null
4851
5062
  ? { kind: "strLit", value: litKey, type: STRING, loc: locOf(keyNode) }
4852
5063
  : lowerer.lowerExpr(keyNode);
5064
+ // Optional string/number keys need ToPropertyKey, not a boxed copy of
5065
+ // the receiver. Keep record reads on their native storage so nested
5066
+ // references retain identity across index-signature reads.
5067
+ if (key.type.kind === "union" &&
5068
+ !lowerer.unions.get(key.type.unionId)?.arms.some((arm) => arm.kind === "symbol" || arm.kind === "dyn")) {
5069
+ key = lowerRecordPropertyKey(lowerer, key, keyNode);
5070
+ }
5071
+ if ((key.type.kind === "symbol" || key.type.kind === "dyn" || key.type.kind === "union") &&
5072
+ lowerer.dynConvertible(key.type) && lowerer.dynConvertible(obj.type)) {
5073
+ const read = { kind: "dynKeyGet", value: lowerer.coerceToExpected(obj, DYN),
5074
+ key: lowerer.coerceToExpected(key, DYN), type: DYN, loc };
5075
+ return includeUndefined ? read : lowerer.maybeNarrow(read, expr);
5076
+ }
4853
5077
  // Runtime NUMBER keys ride the JS-exact formatter (o[n] is o[String(n)]
4854
5078
  // in JS — the number-keyed-signature access path).
4855
5079
  key = lowerRecordPropertyKey(lowerer, key, keyNode);
@@ -4914,6 +5138,9 @@ export function lowerRecordKeyRead(lowerer, expr, shapeId, shape, includeUndefin
4914
5138
  * values remain real undefined values, whose JavaScript property spelling
4915
5139
  * is the string "undefined". */
4916
5140
  function lowerRecordPropertyKey(lowerer, key, node) {
5141
+ if (key.type.kind === "union" && lowerer.dynConvertible(key.type)) {
5142
+ return { kind: "libCall", fn: "dyn.toStringCoerce", args: [lowerer.coerceInto(node, key, DYN)], type: STRING, loc: key.loc };
5143
+ }
4917
5144
  if (key.type.kind === "f64" || key.type.kind === "bool" || key.type.kind === "dyn" ||
4918
5145
  lowerer.runtimeOptionalWidening(key.type, STRING) !== null) {
4919
5146
  return lowerer.ensureString(key, node);
@@ -5079,11 +5306,29 @@ export function lowerElementCompound(lowerer, expr, op) {
5079
5306
  const target = expr.left;
5080
5307
  fenceNodeModuleMutation(lowerer, target, "assignment");
5081
5308
  const loc = locOf(expr);
5082
- const receiverType = lowerer.mapTypeOf(lowerer.typeOf(target.expression));
5309
+ const checked = lowerer.lowerExpr(target.expression);
5310
+ if (checked?.type.kind === "dyn" && isJsSourceFile(expr.getSourceFile())) {
5311
+ const stmts = [];
5312
+ const save = (value, name) => {
5313
+ const local = lowerer.declareHiddenLocal(name, value.type);
5314
+ stmts.push({ kind: "varDecl", localId: local.id, init: value, loc });
5315
+ return varRef(local.id, value.type, loc);
5316
+ };
5317
+ const receiver = save(checked, "%compoundReceiver");
5318
+ const key = save(lowerer.lowerExprExpecting(target.argumentExpression, DYN), "%compoundKey");
5319
+ const old = save({ kind: "dynKeyGet", value: receiver, key, type: DYN, loc }, "%compoundOld");
5320
+ const rhs = lowerer.lowerExprExpecting(expr.right, DYN);
5321
+ const numeric = ["-", "*", "/", "%", "**"].includes(op);
5322
+ const value = save({ kind: "libCall", fn: op === "+" ? "dyn.add" : numeric ? "dyn.arithmetic" : "dyn.bitwise",
5323
+ args: op === "+" ? [old, rhs] : [old, rhs, strLit(op, loc)], type: DYN, loc }, "%compoundResult");
5324
+ stmts.push({ kind: "exprStmt", expr: { kind: "libCall", fn: "dyn.keySetComputed", args: [receiver, key, value], type: VOID, loc }, loc });
5325
+ return { kind: "seqExpr", stmts, result: value, type: DYN, loc };
5326
+ }
5327
+ const receiverType = checked.type.kind === "array" || checked.type.kind === "bytes" ? checked.type : lowerer.mapTypeOf(lowerer.typeOf(target.expression));
5083
5328
  if (receiverType?.kind !== "array" && receiverType?.kind !== "bytes") {
5084
5329
  lowerer.unsupported("SC1090", target, "compound assignment to non-array elements");
5085
5330
  }
5086
- let receiver = lowerer.lowerExpr(target.expression);
5331
+ let receiver = checked;
5087
5332
  if (receiver.type.kind === "union" && lowerer.armTag(receiver.type.unionId, UNDEFINED_T) >= 0) {
5088
5333
  const present = lowerer.stripUndefinedArm(receiver.type);
5089
5334
  const helper = present.kind === receiverType.kind
@@ -5120,6 +5365,12 @@ export function lowerElementCompound(lowerer, expr, op) {
5120
5365
  else if (valueType.kind === "f64" && numericRhs.type.kind === "f64") {
5121
5366
  computed = { kind: "bin", op, left: lowerOptionalNumber(lowerer, oldRef(), loc), right: numericRhs, type: F64, loc };
5122
5367
  }
5368
+ else if (valueType.kind === "dyn" && isJsSourceFile(expr.getSourceFile())) {
5369
+ const left = lowerer.coerceToExpected(oldRef(), DYN), right = lowerer.coerceToExpected(rhs, DYN);
5370
+ const numeric = ["-", "*", "/", "%", "**"].includes(op);
5371
+ computed = { kind: "libCall", fn: op === "+" ? "dyn.add" : numeric ? "dyn.arithmetic" : "dyn.bitwise",
5372
+ args: op === "+" ? [left, right] : [left, right, strLit(op, loc)], type: DYN, loc };
5373
+ }
5123
5374
  else {
5124
5375
  lowerer.unsupported("SC1043", expr);
5125
5376
  }
@@ -5233,12 +5484,23 @@ export function lowerElementWrite(lowerer, expr) {
5233
5484
  const receiver = lowerer.lowerExpr(target.expression);
5234
5485
  if (receiver.type.kind === "dyn")
5235
5486
  return { kind: "exprStmt", expr: lowerDynMemberAssignment(lowerer, expr, receiver), loc: locOf(expr) };
5487
+ if (isJsSourceFile(expr.getSourceFile()) && receiver.type.kind === "object" && lowerer.dynConvertible(receiver.type)) {
5488
+ return { kind: "exprStmt", expr: lowerDynMemberAssignment(lowerer, expr, lowerer.coerceToExpected(receiver, DYN)), loc: locOf(expr) };
5489
+ }
5236
5490
  lowerer.unsupported("SC1090", target, "symbol-keyed property writes outside class fields keyed by a stable module-level literal Symbol()/Symbol.for() (other static shapes have no symbol-keyed storage)");
5237
5491
  }
5238
5492
  const checkedReceiver = tryLowerExpression(lowerer, target.expression);
5239
5493
  if (checkedReceiver?.type.kind === "dyn") {
5240
5494
  return { kind: "exprStmt", expr: lowerDynMemberAssignment(lowerer, expr, checkedReceiver), loc: locOf(expr) };
5241
5495
  }
5496
+ if (checkedReceiver?.type.kind === "object" && isJsSourceFile(expr.getSourceFile())) {
5497
+ const info = lowerer.classes.get(checkedReceiver.type.className);
5498
+ const key = recordKeyLiteralText(target.argumentExpression) ?? recordKeyTypeLiteralText(lowerer, target.argumentExpression);
5499
+ if (info && key !== null && isClassCallback(lowerer, info, key)) {
5500
+ const receiver = lowerer.coerceInto(target.expression, checkedReceiver, DYN);
5501
+ return { kind: "exprStmt", expr: lowerDynMemberAssignment(lowerer, expr, receiver), loc: locOf(expr) };
5502
+ }
5503
+ }
5242
5504
  let receiverIr = lowerer.mapTypeOf(lowerer.typeOf(target.expression));
5243
5505
  if (receiverIr === null || receiverIr.kind === "dyn") {
5244
5506
  const receiver = tryLowerExpression(lowerer, target.expression);
@@ -5307,13 +5569,15 @@ export function lowerElementWrite(lowerer, expr) {
5307
5569
  return lowerer.fieldSetStmt(field, value, locOf(expr), target);
5308
5570
  }
5309
5571
  if (hasClassPayload(lowerer, receiver.type)) {
5572
+ if (receiver.type.kind === "object" && receiver.type.className === "%Error") {
5573
+ return { kind: "exprStmt", expr: lowerDynMemberAssignment(lowerer, expr, lowerer.coerceInto(target.expression, receiver, DYN)), loc: locOf(expr) };
5574
+ }
5310
5575
  lowerer.unsupported("SC1090", target, "writing undeclared properties through structural views of class instances");
5311
5576
  }
5312
5577
  }
5313
5578
  }
5314
5579
  // Typed-array element write `b[i] = v` — bytesSet: the value is an f64
5315
- // the runtime coerces JS-exactly; invalid indices trap (no appends —
5316
- // typed arrays are fixed-length; JS would ignore the write).
5580
+ // the runtime coerces JS-exactly; invalid indices are ignored.
5317
5581
  if (receiverIr?.kind === "bytes") {
5318
5582
  const recv = lowerer.lowerExpr(target.expression);
5319
5583
  // The write twin: a typed-array .d.ts surface whose VALUE is a
@@ -5499,7 +5763,7 @@ export function lowerElementWrite(lowerer, expr) {
5499
5763
  // write, anything else falls through to the fences.
5500
5764
  if (receiverIr?.kind === "dyn" || receiverIr?.kind === "object" || receiverIr === null) {
5501
5765
  const obj = receiverIr !== null ? lowerer.lowerExpr(target.expression) : tryLowerExpression(lowerer, target.expression);
5502
- if (obj !== null && (obj.type.kind === "dyn" || isDynTypedRefType(obj.type))) {
5766
+ if (obj !== null && (obj.type.kind === "dyn" || obj.type.kind === "object" && obj.type.className === "%Error" || isDynTypedRefType(obj.type))) {
5503
5767
  const loc = locOf(expr);
5504
5768
  const litKey = recordKeyLiteralText(target.argumentExpression);
5505
5769
  let key = litKey !== null
@@ -5544,6 +5808,9 @@ export function lowerElementWrite(lowerer, expr) {
5544
5808
  lowerer.unsupported("SC1090", target.expression, "assignment through a possibly missing nested array receiver");
5545
5809
  }
5546
5810
  const index = lowerOptionalNumber(lowerer, lowerer.lowerExpr(target.argumentExpression), locOf(target.argumentExpression), target.argumentExpression);
5811
+ if (index.type.kind === "dyn" && lowerer.dynConvertible(arr.type)) {
5812
+ return { kind: "exprStmt", expr: lowerDynMemberAssignment(lowerer, expr, lowerer.coerceInto(target.expression, arr, DYN)), loc: locOf(expr) };
5813
+ }
5547
5814
  if (index.type.kind !== "f64") {
5548
5815
  lowerer.unsupported("SC1090", target.argumentExpression, "indexing with non-number keys");
5549
5816
  }
@@ -5878,7 +6145,7 @@ export function lowerAsExpression(lowerer, expr) {
5878
6145
  return { kind: "jsExit", value: inner, type: target, loc: locOf(expr) };
5879
6146
  }
5880
6147
  const targetTs = lowerer.checker.getTypeFromTypeNode(expr.type);
5881
- const target = lowerer.mapTypeOf(targetTs);
6148
+ const target = lowerer.mapTypeOf(targetTs) ?? dynFallbackType(lowerer, expr.type, targetTs);
5882
6149
  if (!target)
5883
6150
  lowerer.badType(expr.type, targetTs);
5884
6151
  if (target.kind === "dyn")
@@ -6040,6 +6307,25 @@ function lowerIncDec(lowerer, expr, prefix) {
6040
6307
  // accessor-backed names keep the fence.
6041
6308
  if ((ts.isPropertyAccessExpression(expr.operand) || ts.isElementAccessExpression(expr.operand)) &&
6042
6309
  !expr.operand.questionDotToken) {
6310
+ if (ts.isPropertyAccessExpression(expr.operand) && isJsSourceFile(expr.getSourceFile())) {
6311
+ const receiver = lowerer.lowerExpr(expr.operand.expression);
6312
+ if (receiver.type.kind === "dyn") {
6313
+ const local = lowerer.declareHiddenLocal("%incrementReceiver", DYN);
6314
+ const old = lowerer.declareHiddenLocal("%incrementOld", DYN);
6315
+ const next = lowerer.declareHiddenLocal("%incrementNext", DYN);
6316
+ const value = varRef(local.id, DYN, loc);
6317
+ const key = { kind: "strLit", value: expr.operand.name.text, type: STRING, loc };
6318
+ return { kind: "seqExpr", stmts: [
6319
+ { kind: "varDecl", localId: local.id, init: receiver, loc },
6320
+ { kind: "varDecl", localId: old.id, init: { kind: "libCall", fn: "dyn.toNumeric", args: [
6321
+ { kind: "dynKeyGet", value, key, type: DYN, loc },
6322
+ ], type: DYN, loc }, loc },
6323
+ { kind: "varDecl", localId: next.id, init: { kind: "libCall", fn: "dyn.increment", args: [varRef(old.id, DYN, loc),
6324
+ { kind: "boolLit", value: op === "+", type: BOOL, loc }], type: DYN, loc }, loc },
6325
+ { kind: "exprStmt", expr: { kind: "libCall", fn: "dyn.keySet", args: [value, key, varRef(next.id, DYN, loc)], type: VOID, loc }, loc },
6326
+ ], result: varRef(prefix ? next.id : old.id, DYN, loc), type: DYN, loc };
6327
+ }
6328
+ }
6043
6329
  const target = ts.isPropertyAccessExpression(expr.operand)
6044
6330
  ? lowerer.fieldTarget(expr.operand)
6045
6331
  : symbolFieldTarget(lowerer, expr.operand);
@@ -6163,12 +6449,13 @@ export function lowerCompoundValueToTarget(lowerer, expr, compound, target) {
6163
6449
  return { kind: "bin", op: compound, left: read, right: numericRhs, type: F64, loc };
6164
6450
  }
6165
6451
  if ((target.type.kind === "dyn" || rhs.type.kind === "dyn") &&
6166
- (target.type.kind === "dyn" || target.type.kind === "f64") &&
6167
- (rhs.type.kind === "dyn" || rhs.type.kind === "f64") &&
6168
6452
  isJsSourceFile(expr.getSourceFile())) {
6169
- const checkNum = (value) => value.type.kind === "dyn" ? { kind: "dynCheck", value, type: F64, loc: value.loc } : value;
6170
- const computed = { kind: "bin", op: compound, left: checkNum(read), right: checkNum(rhs), type: F64, loc };
6171
- return target.type.kind === "dyn" ? { kind: "dynFrom", value: computed, type: DYN, loc } : computed;
6453
+ const left = lowerer.coerceInto(expr.left, read, DYN);
6454
+ const right = lowerer.coerceInto(expr.right, rhs, DYN);
6455
+ const numeric = ["-", "*", "/", "%", "**"].includes(compound);
6456
+ const computed = { kind: "libCall", fn: compound === "+" ? "dyn.add" : numeric ? "dyn.arithmetic" : "dyn.bitwise",
6457
+ args: compound === "+" ? [left, right] : [left, right, { kind: "strLit", value: compound, type: STRING, loc }], type: DYN, loc };
6458
+ return lowerer.coerceInto(expr, computed, target.type);
6172
6459
  }
6173
6460
  lowerer.unsupported("SC1043", expr);
6174
6461
  }
@@ -6293,8 +6580,30 @@ export function lowerBinary(lowerer, expr) {
6293
6580
  // key afterward, then yields the RHS without rereading the property.
6294
6581
  if (ts.isElementAccessExpression(expr.left) && !expr.left.questionDotToken) {
6295
6582
  const recv = tryLowerExpression(lowerer, expr.left.expression);
6296
- if (recv?.type.kind === "dyn") {
6297
- return lowerDynMemberAssignment(lowerer, expr, recv);
6583
+ if (recv && (recv.type.kind === "dyn" ||
6584
+ recv.type.kind === "array" && isJsSourceFile(expr.getSourceFile()) && lowerer.dynConvertible(recv.type))) {
6585
+ return lowerDynMemberAssignment(lowerer, expr, lowerer.coerceInto(expr.left.expression, recv, DYN));
6586
+ }
6587
+ if (recv?.type.kind === "array" || recv?.type.kind === "bytes") {
6588
+ const receiver = lowerer.declareHiddenLocal("%setArray", recv.type);
6589
+ const index = lowerer.lowerExpr(expr.left.argumentExpression);
6590
+ if (index.type.kind !== "f64")
6591
+ lowerer.unsupported("SC1090", expr.left.argumentExpression, "indexing with non-number keys");
6592
+ const key = lowerer.declareHiddenLocal("%setIndex", F64);
6593
+ const raw = lowerer.lowerExpr(expr.right);
6594
+ const value = lowerer.declareHiddenLocal("%setValue", raw.type);
6595
+ const result = varRef(value.id, raw.type, loc);
6596
+ const arr = varRef(receiver.id, recv.type, loc);
6597
+ const idx = varRef(key.id, F64, loc);
6598
+ const write = recv.type.kind === "array"
6599
+ ? arrayValueStore(lowerer, arr, idx, result, recv.type.elem, loc)
6600
+ : { kind: "bytesSet", arr, index: idx, value: lowerer.coerceInto(expr.right, result, F64), loc };
6601
+ return { kind: "seqExpr", stmts: [
6602
+ { kind: "varDecl", localId: receiver.id, init: recv, loc },
6603
+ { kind: "varDecl", localId: key.id, init: index, loc },
6604
+ { kind: "varDecl", localId: value.id, init: raw, loc },
6605
+ write,
6606
+ ], result, type: result.type, loc };
6298
6607
  }
6299
6608
  }
6300
6609
  // `h.k = v` on an ISLAND receiver in VALUE position: the engine
@@ -6337,32 +6646,8 @@ export function lowerBinary(lowerer, expr) {
6337
6646
  // consumer sees exactly what the checker typed.
6338
6647
  if (ts.isPropertyAccessExpression(expr.left) && !expr.left.questionDotToken) {
6339
6648
  const recv = tryLowerExpression(lowerer, expr.left.expression);
6340
- if (recv && recv.type.kind === "dyn") {
6341
- const recvTmp = lowerer.declareHiddenLocal("%setRecv", DYN);
6342
- const rhsVal = lowerer.lowerExpr(expr.right);
6343
- const valTmp = lowerer.declareHiddenLocal("%setVal", rhsVal.type);
6344
- const valRef = () => ({ kind: "varRef", localId: valTmp.id, type: rhsVal.type, loc });
6345
- const stored = lowerer.coerceToExpected(valRef(), DYN);
6346
- if (stored.type.kind !== "dyn") {
6347
- lowerer.unsupported("SC1101", expr.right, `assigning '${lowerer.fmt(rhsVal.type)}' values into a checked-dynamic member`);
6348
- }
6349
- const key = { kind: "strLit", value: expr.left.name.text, type: STRING, loc: locOf(expr.left.name) };
6350
- return {
6351
- kind: "seqExpr",
6352
- stmts: [
6353
- { kind: "varDecl", localId: recvTmp.id, init: recv, loc },
6354
- { kind: "varDecl", localId: valTmp.id, init: rhsVal, loc },
6355
- {
6356
- kind: "exprStmt",
6357
- expr: { kind: "libCall", fn: "dyn.keySet", args: [{ kind: "varRef", localId: recvTmp.id, type: DYN, loc }, key, stored], type: VOID, loc },
6358
- loc,
6359
- },
6360
- ],
6361
- result: valRef(),
6362
- type: rhsVal.type,
6363
- loc,
6364
- };
6365
- }
6649
+ if (recv && recv.type.kind === "dyn")
6650
+ return lowerDynMemberAssignment(lowerer, expr, recv);
6366
6651
  const field = lowerer.fieldTarget(expr.left);
6367
6652
  if (field) {
6368
6653
  const recvTmp = lowerer.declareHiddenLocal("%setReceiver", field.obj.type);
@@ -6476,8 +6761,8 @@ export function lowerBinary(lowerer, expr) {
6476
6761
  const scalarSide = dynSide === left ? right : left;
6477
6762
  const reference = scalarSide.type;
6478
6763
  if ((isDynTypedRefType(reference) || reference.kind === "record" || reference.kind === "array" ||
6479
- reference.kind === "bytes" || reference.kind === "func" || reference.kind === "regex" || reference.kind === "url" ||
6480
- reference.kind === "bigint" || reference.kind === "symbol" || reference.kind === "set" || reference.kind === "union" || DYN_HANDLE_KINDS.has(reference.kind)) &&
6764
+ reference.kind === "bytes" || reference.kind === "func" || reference.kind === "classval" || reference.kind === "regex" || reference.kind === "url" ||
6765
+ reference.kind === "bigint" || reference.kind === "symbol" || reference.kind === "map" || reference.kind === "set" || reference.kind === "union" || DYN_HANDLE_KINDS.has(reference.kind)) &&
6481
6766
  lowerer.dynConvertible(reference)) {
6482
6767
  const boxed = {
6483
6768
  kind: "dynFrom", value: scalarSide, type: DYN, loc: scalarSide.loc,
@@ -6524,8 +6809,11 @@ export function lowerBinary(lowerer, expr) {
6524
6809
  }
6525
6810
  // Numeric operators dispatch on ToNumeric's actual Number/BigInt
6526
6811
  // result, including values flowing through native callable properties.
6527
- if (isJsSourceFile(expr.getSourceFile()) || op === ts.SyntaxKind.PlusToken) {
6528
- const checkNum = (e) => e.type.kind === "dyn" ? { kind: "dynCheck", value: e, type: F64, loc: e.loc } : e;
6812
+ if (isJsSourceFile(expr.getSourceFile()) || op === ts.SyntaxKind.PlusToken ||
6813
+ [ts.SyntaxKind.LessThanToken, ts.SyntaxKind.LessThanEqualsToken,
6814
+ ts.SyntaxKind.GreaterThanToken, ts.SyntaxKind.GreaterThanEqualsToken].includes(op) ||
6815
+ [ts.SyntaxKind.MinusToken, ts.SyntaxKind.AsteriskToken, ts.SyntaxKind.SlashToken,
6816
+ ts.SyntaxKind.PercentToken, ts.SyntaxKind.AsteriskAsteriskToken].includes(op)) {
6529
6817
  const other = left.type.kind === "dyn" ? right : left;
6530
6818
  const NUM_BIN = {
6531
6819
  [ts.SyntaxKind.MinusToken]: "-",
@@ -6553,8 +6841,9 @@ export function lowerBinary(lowerer, expr) {
6553
6841
  return numeric ? { kind: "dynCheck", value, type: numeric, loc } : value;
6554
6842
  }
6555
6843
  }
6556
- if (cmp && (other.type.kind === "f64" || other.type.kind === "dyn")) {
6557
- return { kind: "bin", op: cmp, left: checkNum(left), right: checkNum(right), type: BOOL, loc };
6844
+ if (cmp && lowerer.dynConvertible(left.type) && lowerer.dynConvertible(right.type)) {
6845
+ return { kind: "libCall", fn: "dyn.compare", args: [lowerer.coerceToExpected(left, DYN),
6846
+ lowerer.coerceToExpected(right, DYN), { kind: "strLit", value: cmp, type: STRING, loc }], type: BOOL, loc };
6558
6847
  }
6559
6848
  // Untyped addition chooses concatenation or numeric addition only
6560
6849
  // after both operands have undergone ToPrimitive at runtime.
@@ -6986,6 +7275,10 @@ export function lowerBinary(lowerer, expr) {
6986
7275
  if (DYN_HANDLE_KINDS.has(idLeft.type.kind) && typeEquals(idLeft.type, idRight.type)) {
6987
7276
  return { kind: "bin", op: negated ? "!==" : "===", left: idLeft, right: idRight, type: BOOL, loc };
6988
7277
  }
7278
+ if (lowerer.dynConvertible(idLeft.type) && lowerer.dynConvertible(idRight.type)) {
7279
+ return { kind: "dynScalarEq", left: lowerer.coerceToExpected(idLeft, DYN),
7280
+ right: lowerer.coerceToExpected(idRight, DYN), ...(negated ? { negated: true } : {}), type: BOOL, loc };
7281
+ }
6989
7282
  lowerer.unsupported("SC1043", expr);
6990
7283
  break;
6991
7284
  }
@@ -7000,6 +7293,10 @@ export function lowerBinary(lowerer, expr) {
7000
7293
  return { kind: "bin", op: cmpOp, left: arithmeticLeft, right: arithmeticRight, type: BOOL, loc };
7001
7294
  if (bothStr)
7002
7295
  return { kind: "strCmp", op: cmpOp, left, right, type: BOOL, loc };
7296
+ if (lowerer.dynConvertible(left.type) && lowerer.dynConvertible(right.type)) {
7297
+ return { kind: "libCall", fn: "dyn.compare", args: [lowerer.coerceToExpected(left, DYN),
7298
+ lowerer.coerceToExpected(right, DYN), { kind: "strLit", value: cmpOp, type: STRING, loc }], type: BOOL, loc };
7299
+ }
7003
7300
  lowerer.unsupported("SC1043", expr);
7004
7301
  break;
7005
7302
  }
@@ -7042,6 +7339,16 @@ function lowerLogicalChain(lowerer, expr) {
7042
7339
  function lowerLogicalPair(lowerer, expr, left) {
7043
7340
  const op = expr.operatorToken.kind;
7044
7341
  const loc = locOf(expr);
7342
+ // Preserve short-circuiting during lowering too: an unreachable member
7343
+ // on a specialized value must not poison the surrounding JS statement.
7344
+ if (left.kind === "boolLit") {
7345
+ const takeRight = op === ts.SyntaxKind.AmpersandAmpersandToken ? left.value : !left.value;
7346
+ return takeRight ? lowerer.lowerExpr(expr.right) : left;
7347
+ }
7348
+ if (left.kind === "unitLit" && op === ts.SyntaxKind.AmpersandAmpersandToken)
7349
+ return left;
7350
+ if (op === ts.SyntaxKind.BarBarToken && REF_TRUTHY_KINDS.has(left.type.kind))
7351
+ return left;
7045
7352
  const right = lowerer.lowerExpr(expr.right);
7046
7353
  // A LITERAL-unit left operand (a compile-time undefined/null — the
7047
7354
  // capability-probe members: `process.features.inspector ||
@@ -7091,6 +7398,11 @@ function lowerLogicalPair(lowerer, expr, left) {
7091
7398
  type: JSVAL, loc,
7092
7399
  };
7093
7400
  }
7401
+ if (isJsSourceFile(expr.getSourceFile()) && op === ts.SyntaxKind.AmpersandAmpersandToken &&
7402
+ left.type.kind === "union" && (lowerer.unions.get(left.type.unionId)?.arms.some((arm) => arm.kind === "bool")) &&
7403
+ !typeEquals(left.type, right.type) && lowerer.dynConvertible(left.type) && lowerer.dynConvertible(right.type)) {
7404
+ return { kind: "logical", op: "&&", left: lowerer.coerceToExpected(left, DYN), right: lowerer.coerceToExpected(right, DYN), type: DYN, loc };
7405
+ }
7094
7406
  if (left.type.kind === "union" || right.type.kind === "union") {
7095
7407
  // Test the deciding operand in its own representation. The result
7096
7408
  // contains only the values that can survive short-circuit evaluation.
@@ -7970,6 +8282,8 @@ export function lowerInstanceOf(lowerer, expr, loc) {
7970
8282
  if (value.type.kind === collection)
7971
8283
  return { kind: "seqExpr", stmts: [{ kind: "exprStmt", expr: value, loc }],
7972
8284
  result: { kind: "boolLit", value: true, type: BOOL, loc }, type: BOOL, loc };
8285
+ return { kind: "libCall", fn: collection === "map" ? "dyn.nativeMapIs" : "dyn.nativeSetIs",
8286
+ args: [lowerer.coerceInto(expr.left, value, DYN)], type: BOOL, loc };
7973
8287
  }
7974
8288
  if (lowerer.isStdlibGlobal(expr.right, "WeakMap") || lowerer.isStdlibGlobal(expr.right, "WeakSet")) {
7975
8289
  if (lowerer.dynamic) {
@@ -7986,6 +8300,10 @@ export function lowerInstanceOf(lowerer, expr, loc) {
7986
8300
  const value = lowerer.coerceInto(expr.left, lowerer.lowerExpr(expr.left), DYN);
7987
8301
  return { kind: "libCall", fn: "dyn.nativeUrlIs", args: [value], type: BOOL, loc };
7988
8302
  }
8303
+ if (lowerer.isStdlibGlobal(expr.right, "DataView")) {
8304
+ const value = lowerer.coerceInto(expr.left, lowerer.lowerExpr(expr.left), DYN);
8305
+ return { kind: "libCall", fn: "dyn.dataViewIs", args: [value], type: BOOL, loc };
8306
+ }
7989
8307
  if (lowerer.isStdlibGlobal(expr.right, "ArrayBuffer")) {
7990
8308
  const value = lowerer.coerceInto(expr.left, lowerer.lowerExpr(expr.left), DYN);
7991
8309
  return { kind: "libCall", fn: "arrayBuffer.is", args: [value], type: BOOL, loc };
@@ -8040,24 +8358,29 @@ export function lowerInstanceOf(lowerer, expr, loc) {
8040
8358
  if (value.type.kind === "dyn")
8041
8359
  return { kind: "libCall", fn: date ? "dyn.nativeDateIs" : "dyn.nativeUrlIs", args: [value], type: BOOL, loc };
8042
8360
  if (value.type.kind === "union")
8043
- lowerer.unsupported("SC1090", expr, "Date or URL instanceof over a union");
8361
+ return { kind: "libCall", fn: date ? "dyn.nativeDateIs" : "dyn.nativeUrlIs",
8362
+ args: [lowerer.coerceInto(expr.left, value, DYN)], type: BOOL, loc };
8044
8363
  return { kind: "seqExpr", stmts: [{ kind: "exprStmt", expr: value, loc }],
8045
8364
  result: { kind: "boolLit", value: value.type.kind === (date ? "date" : "url"), type: BOOL, loc }, type: BOOL, loc };
8046
8365
  }
8366
+ const webBrand = ["Request", "Response", "Headers"].find((name) => lowerer.isStdlibGlobal(expr.right, name));
8367
+ if (webBrand)
8368
+ return { kind: "libCall", fn: "fetch.webIs", args: [lowerer.lowerExprExpecting(expr.left, DYN),
8369
+ { kind: "strLit", value: webBrand, type: STRING, loc }], type: BOOL, loc };
8370
+ if (lowerer.isStdlibGlobal(expr.right, "ReadableStream")) {
8371
+ const value = lowerer.lowerExprExpecting(expr.left, DYN);
8372
+ return { kind: "libCall", fn: "fetch.streamIs", args: [value], type: BOOL, loc };
8373
+ }
8047
8374
  // Unknown storage retains each numeric typed array's exact native brand.
8048
- if (ts.isIdentifier(expr.right) && !lowerer.caughtLocalOf(expr.left)) {
8375
+ if (!lowerer.caughtLocalOf(expr.left)) {
8049
8376
  const elem = Object.keys(BYTES_ELEMENT_NAME).find((kind) => lowerer.isStdlibGlobal(expr.right, BYTES_ELEMENT_NAME[kind]));
8050
8377
  if (elem !== undefined) {
8051
8378
  const left = lowerer.lowerExpr(expr.left);
8052
8379
  if (left.type.kind === "dyn") {
8053
8380
  return { kind: "dynTest", test: "bytes", bytesElem: elem, value: left, type: BOOL, loc };
8054
8381
  }
8055
- if (left.type.kind === "bytes") {
8056
- return {
8057
- kind: "seqExpr", stmts: [{ kind: "exprStmt", expr: left, loc }],
8058
- result: { kind: "boolLit", value: left.type.elem === elem, type: BOOL, loc }, type: BOOL, loc,
8059
- };
8060
- }
8382
+ return { kind: "dynTest", test: "bytes", bytesElem: elem,
8383
+ value: lowerer.coerceInto(expr.left, left, DYN), type: BOOL, loc };
8061
8384
  }
8062
8385
  }
8063
8386
  // `x instanceof RegExp` over a union with a regex arm (the
@@ -8079,9 +8402,7 @@ export function lowerInstanceOf(lowerer, expr, loc) {
8079
8402
  return { kind: "unionIsTag", unionId: left.type.unionId, tag, negated: false, value: left, type: BOOL, loc };
8080
8403
  }
8081
8404
  }
8082
- if (left.type.kind === "regex" && left.kind === "varRef" && ts.isIdentifier(expr.left)) {
8083
- return { kind: "boolLit", value: true, type: BOOL, loc };
8084
- }
8405
+ return { kind: "libCall", fn: "dyn.nativeRegexIs", args: [lowerer.coerceInto(expr.left, left, DYN)], type: BOOL, loc };
8085
8406
  }
8086
8407
  // `x instanceof X` where X is a class VALUE (a classval-typed
8087
8408
  // binding): the target is DYNAMIC — a hierarchy target reads its
@@ -8100,6 +8421,10 @@ export function lowerInstanceOf(lowerer, expr, loc) {
8100
8421
  lowerer.unsupported("SC1090", expr, "'instanceof' against a class value whose class has no lowering (the class declaration itself was rejected — see its own diagnostic)");
8101
8422
  }
8102
8423
  const left = lowerer.lowerExpr(expr.left);
8424
+ if (left.type.kind === "dyn") {
8425
+ const classValue = lowerer.lowerExpr(expr.right);
8426
+ return classInstanceOf(lowerer, left, targetInfo, loc, classValue);
8427
+ }
8103
8428
  if (left.type.kind !== "object") {
8104
8429
  lowerer.unsupported("SC1090", expr, "'instanceof' on values other than class instances (narrow union-typed values first)");
8105
8430
  }
@@ -8124,6 +8449,11 @@ export function lowerInstanceOf(lowerer, expr, loc) {
8124
8449
  }
8125
8450
  lowerer.unsupported("SC1090", expr, "statically-decided 'instanceof' on computed operands (bind the values to variables first)");
8126
8451
  }
8452
+ const constructor = tryLowerExpression(lowerer, expr.right);
8453
+ if (constructor?.type.kind === "dyn" && isJsSourceFile(expr.getSourceFile())) {
8454
+ const value = lowerer.coerceInto(expr.left, lowerer.lowerExpr(expr.left), DYN);
8455
+ return checkedClassInstanceOf(lowerer, value, constructor, loc);
8456
+ }
8127
8457
  lowerer.unsupported("SC1090", expr.right, "'instanceof' right-hand sides other than classes declared in the program");
8128
8458
  }
8129
8459
  // A catch binding on the left: the runtime test against the class's
@@ -8218,7 +8548,8 @@ export function lowerInstanceOf(lowerer, expr, loc) {
8218
8548
  }
8219
8549
  }
8220
8550
  if (left.type.kind !== "object") {
8221
- lowerer.unsupported("SC1090", expr, "'instanceof' on values other than class instances (narrow union-typed values first)");
8551
+ return { kind: "seqExpr", stmts: [{ kind: "exprStmt", expr: left, loc }],
8552
+ result: { kind: "boolLit", value: false, type: BOOL, loc }, type: BOOL, loc };
8222
8553
  }
8223
8554
  const lhsInfo = lowerer.classes.get(left.type.className);
8224
8555
  if (!lhsInfo)
@@ -8446,9 +8777,9 @@ function lowerInExpression(lowerer, expr, loc) {
8446
8777
  // `in` shares the coercion).
8447
8778
  {
8448
8779
  const probed = tryLowerExpression(lowerer, expr.right);
8449
- if (probed && (probed.type.kind === "dyn" || isDynTypedRefType(probed.type))) {
8780
+ if (probed && lowerer.dynConvertible(probed.type)) {
8450
8781
  const k = lowerer.lowerExpr(expr.left); // JS order: the key evaluates first
8451
- if (k.type.kind === "symbol" || k.type.kind === "dyn" || k.type.kind === "string" || k.type.kind === "f64") {
8782
+ if (lowerer.dynConvertible(k.type)) {
8452
8783
  const boxed = lowerer.coerceToExpected(k, DYN);
8453
8784
  const keyLocal = lowerer.declareHiddenLocal("%inKey", DYN);
8454
8785
  return { kind: "seqExpr", stmts: [{ kind: "varDecl", localId: keyLocal.id, init: boxed, loc }],
@@ -8479,7 +8810,9 @@ function lowerInExpression(lowerer, expr, loc) {
8479
8810
  return { kind: "unionIsTag", unionId: envType.unionId, tag: undefTag, negated: true, value: read, type: BOOL, loc };
8480
8811
  }
8481
8812
  let recv = lowerer.lowerExpr(expr.right);
8482
- if (isJsSourceFile(expr.getSourceFile()) && recv.type.kind === "func" && lowerer.dynConvertible(recv.type)) {
8813
+ const classInfo = recv.type.kind === "classval" ? lowerer.classes.get(recv.type.className) : undefined;
8814
+ if ((classInfo && (classInfo.callableBase || hasRuntimeStatics(classInfo))) ||
8815
+ (isJsSourceFile(expr.getSourceFile()) && recv.type.kind === "func" && lowerer.dynConvertible(recv.type))) {
8483
8816
  recv = lowerer.coerceToExpected(recv, DYN);
8484
8817
  }
8485
8818
  const siteType = lowerer.mapTypeOf(lowerer.typeOf(expr.right));
@@ -8728,8 +9061,8 @@ function lowerRuntimeKeyIn(lowerer, expr, loc) {
8728
9061
  }
8729
9062
  return { kind: "call", callee: helper, args: [keyIr, recv], type: BOOL, loc };
8730
9063
  }
8731
- /** A regex literal `/ab+c/gi` → regexLit (interned per (pattern, flags)
8732
- * by the backend). The TS parser has already syntax-checked the literal;
9064
+ /** A regex literal `/ab+c/gi` → regexLit (fresh state per evaluation,
9065
+ * shared bytecode per (pattern, flags) pair). The TS parser has already syntax-checked the literal;
8733
9066
  * what remains here is the flag-alphabet fence (d and v are
8734
9067
  * declared-valid TS flags outside this slice). Named capture groups
8735
9068
  * `(?<name>...)` and `\k<name>` backreferences compile — libregexp
@@ -8870,14 +9203,52 @@ function constInitializerOf(lowerer, ident) {
8870
9203
  * caller keeps its fence), or the groups list (possibly empty — a
8871
9204
  * traced regex WITHOUT named groups is the Node-undefined case). */
8872
9205
  export function matchResultNamedGroupsOf(lowerer, e) {
9206
+ let producer = e;
9207
+ while (ts.isParenthesizedExpression(producer) || ts.isNonNullExpression(producer))
9208
+ producer = producer.expression;
9209
+ if (ts.isIdentifier(producer)) {
9210
+ const init = constInitializerOf(lowerer, producer);
9211
+ if (init !== null)
9212
+ return matchResultNamedGroupsOf(lowerer, init);
9213
+ }
8873
9214
  const reExpr = matchProducerRegexOf(lowerer, e);
8874
9215
  if (reExpr === null)
8875
9216
  return null;
9217
+ if (ts.isCallExpression(producer) && ts.isPropertyAccessExpression(producer.expression) &&
9218
+ producer.expression.name.text === "match") {
9219
+ const flags = staticRegexFlagsOf(lowerer, reExpr);
9220
+ if (flags === null)
9221
+ return null;
9222
+ // Global String.match returns whole matches without a groups
9223
+ // property, even when the pattern contains named captures.
9224
+ if (flags.includes("g"))
9225
+ return [];
9226
+ }
8876
9227
  const pattern = staticRegexPatternOf(lowerer, reExpr);
8877
9228
  if (pattern === null)
8878
9229
  return null;
8879
9230
  return namedCaptureGroupsOfPattern(pattern);
8880
9231
  }
9232
+ function staticRegexFlagsOf(lowerer, node) {
9233
+ let expr = node;
9234
+ while (ts.isParenthesizedExpression(expr) || ts.isNonNullExpression(expr))
9235
+ expr = expr.expression;
9236
+ if (ts.isRegularExpressionLiteral(expr))
9237
+ return expr.text.slice(expr.text.lastIndexOf("/") + 1);
9238
+ if (ts.isIdentifier(expr)) {
9239
+ const init = constInitializerOf(lowerer, expr);
9240
+ return init !== null ? staticRegexFlagsOf(lowerer, init) : null;
9241
+ }
9242
+ if (ts.isNewExpression(expr) && ts.isIdentifier(expr.expression) &&
9243
+ lowerer.isStdlibGlobal(expr.expression, "RegExp")) {
9244
+ const flags = expr.arguments?.[1];
9245
+ if (!flags)
9246
+ return "";
9247
+ if (ts.isStringLiteral(flags) || ts.isNoSubstitutionTemplateLiteral(flags))
9248
+ return flags.text;
9249
+ }
9250
+ return null;
9251
+ }
8881
9252
  /** The regex EXPRESSION whose match produced `e` (see
8882
9253
  * matchResultNamedGroupsOf for the traced shapes). */
8883
9254
  function matchProducerRegexOf(lowerer, e) {
@@ -9370,7 +9741,7 @@ export function lowerUnionProperty(lowerer, expr) {
9370
9741
  // instance argument (e.g. Matrix4.makeTranslation). Preserve primitive
9371
9742
  // missing-property semantics and dispatch class data through its native
9372
9743
  // capsule instead of demanding a field shared by every union arm.
9373
- if (isJsSourceFile(expr.getSourceFile()) && def.arms.some(isDynTypedRefType) && lowerer.dynConvertible(value.type)) {
9744
+ if (isJsSourceFile(expr.getSourceFile()) && lowerer.dynConvertible(value.type)) {
9374
9745
  return { kind: "dynKeyGet", value: lowerer.coerceToExpected(value, DYN), key,
9375
9746
  ...(hasOptionalChainGuard(expr.expression) ? { optional: true } : {}), type: DYN, loc: locOf(expr) };
9376
9747
  }
@@ -9581,7 +9952,7 @@ export function fieldTarget(lowerer, access) {
9581
9952
  }
9582
9953
  const stored = ts.isIdentifier(access.expression)
9583
9954
  ? lowerer.peekLocal(access.expression)?.type ?? lowerer.globalOf(access.expression)?.type : undefined;
9584
- const receiverIr = stored?.kind === "record" ? stored : lowerer.mapTypeOf(lowerer.typeOf(access.expression));
9955
+ const receiverIr = stored?.kind === "record" || stored?.kind === "object" ? stored : lowerer.mapTypeOf(lowerer.typeOf(access.expression));
9585
9956
  if (receiverIr?.kind === "object") {
9586
9957
  return classFieldTarget(lowerer, access.expression, receiverIr, access.name.text);
9587
9958
  }
@@ -9787,6 +10158,8 @@ function symbolFieldTarget(lowerer, expr) {
9787
10158
  * tsc-clean (the property types as the setter's param), but Node yields
9788
10159
  * undefined, which these property types cannot represent. */
9789
10160
  export function fieldGetExpr(lowerer, target, loc, blame) {
10161
+ if (target.container === "class" && (target.field === "message" || target.field === "name") && (target.className === "%Error" || lowerer.isSubclassOf(target.className, "%Error")))
10162
+ return errorPropertyRead(lowerer, target.obj, target.field);
9790
10163
  if (target.container === "errorStackLimit") {
9791
10164
  return { kind: "libCall", fn: "error.stackLimitGet", args: [], type: F64, loc };
9792
10165
  }
@@ -9812,6 +10185,11 @@ export function fieldGetExpr(lowerer, target, loc, blame) {
9812
10185
  if (!getF) {
9813
10186
  lowerer.unsupported("SC1090", blame, `reading a property that has only a setter ('${target.field}' — Node would yield undefined)`);
9814
10187
  }
10188
+ if (!target.field.startsWith("#") && isJsSourceFile(blame.getSourceFile()) && lowerer.dynConvertible(getF.sig.ret)) {
10189
+ const value = { kind: "dynKeyGet", value: lowerer.coerceToExpected(target.obj, DYN),
10190
+ key: { kind: "strLit", value: target.field, type: STRING, loc }, type: DYN, loc };
10191
+ return lowerer.coerceToExpected(value, getF.sig.ret);
10192
+ }
9815
10193
  return lowerer.accessorCall(target.className, `get:${target.field}`, target.obj, [], getF.sig.ret, loc);
9816
10194
  }
9817
10195
  // Record accessor properties: the read IS a call of the %get: closure
@@ -9889,6 +10267,8 @@ export function fieldGetExpr(lowerer, target, loc, blame) {
9889
10267
  * call). A write to a getter-only property never gets here in a clean
9890
10268
  * program (tsc's TS2540 is the fence); the rejection is the backstop. */
9891
10269
  export function fieldSetStmt(lowerer, target, value, loc, blame) {
10270
+ if (target.container === "class" && (target.field === "message" || target.field === "name") && (target.className === "%Error" || lowerer.isSubclassOf(target.className, "%Error")))
10271
+ return errorPropertyWrite(lowerer, target.obj, value, target.field);
9892
10272
  if (target.container === "errorStackLimit") {
9893
10273
  return { kind: "exprStmt", expr: { kind: "libCall", fn: "error.stackLimitSet", args: [value], type: VOID, loc }, loc };
9894
10274
  }
@@ -9996,13 +10376,13 @@ export function lowerPromiseRejectCall(lowerer, call, access) {
9996
10376
  // identity survives to catch/unhandledRejection observers), and the
9997
10377
  // result is promise<dyn> when no context names a concrete one.
9998
10378
  const reason = lowerer.lowerExpr(reasonNode);
9999
- if (reason.type.kind === "dyn") {
10379
+ if (reason.type.kind === "dyn" || lowerer.dynConvertible(reason.type) && reason.type.kind !== "object") {
10000
10380
  // The result only ever REJECTS, so the inner type is unobservable:
10001
10381
  // adopt the context's promise type when one names it, else the
10002
10382
  // checker's own Promise<never> reading (promise<void> — exactly the
10003
10383
  // Error-reason form below).
10004
10384
  const resultT = type?.kind === "promise" ? type : { kind: "promise", inner: VOID };
10005
- return { kind: "intrinsic", name: "promise.reject", args: [reason], type: resultT, loc };
10385
+ return { kind: "intrinsic", name: "promise.reject", args: [lowerer.coerceInto(reasonNode, reason, DYN)], type: resultT, loc };
10006
10386
  }
10007
10387
  // An Error reason with NO context-named promise type (a bare
10008
10388
  // top-level `Promise.reject(new Error())` — the unhandled-rejection
@@ -10193,8 +10573,8 @@ function lowerStreamObjectProperty(lowerer, expr) {
10193
10573
  * Interning makes every reference under one ABI the SAME immortal closure,
10194
10574
  * so `opt.type === String` compares like JS function identity. */
10195
10575
  function primitiveCtorClosure(lowerer, name, loc, dynamicInput) {
10196
- const ret = name === "String" ? STRING : name === "Number" ? F64 : BOOL;
10197
- const input = dynamicInput ? DYN : STRING;
10576
+ const ret = name === "String" ? STRING : name === "Number" ? F64 : name === "BigInt" ? BIGINT_T : BOOL;
10577
+ const input = dynamicInput || name === "BigInt" ? DYN : STRING;
10198
10578
  const fnT = funcOf([input], ret);
10199
10579
  const key = `${name}:${dynamicInput ? "dyn" : "string"}`;
10200
10580
  let fnName = lowerer.primitiveCtorFns.get(key);
@@ -10203,16 +10583,20 @@ function primitiveCtorClosure(lowerer, name, loc, dynamicInput) {
10203
10583
  lowerer.primitiveCtorFns.set(key, fnName);
10204
10584
  const v = { kind: "varRef", localId: "v.0", type: input, loc };
10205
10585
  const value = dynamicInput
10206
- ? name === "String"
10207
- ? { kind: "libCall", fn: "dyn.toStringCoerce", args: [v], type: STRING, loc }
10208
- : name === "Number"
10209
- ? { kind: "libCall", fn: "dyn.toNumberCoerce", args: [v], type: F64, loc }
10210
- : { kind: "dynTest", test: "truthy", value: v, type: BOOL, loc }
10211
- : name === "String"
10212
- ? v
10213
- : name === "Number"
10214
- ? { kind: "libCall", fn: "num.fromString", args: [v], type: F64, loc }
10215
- : { kind: "strEq", negated: true, left: v, right: { kind: "strLit", value: "", type: STRING, loc }, type: BOOL, loc };
10586
+ ? name === "BigInt"
10587
+ ? { kind: "libCall", fn: "dyn.bigintConstructor", args: [v], type: BIGINT_T, loc }
10588
+ : name === "String"
10589
+ ? { kind: "libCall", fn: "dyn.toStringCoerce", args: [v], type: STRING, loc }
10590
+ : name === "Number"
10591
+ ? { kind: "libCall", fn: "dyn.numberConstructor", args: [v], type: F64, loc }
10592
+ : { kind: "dynTest", test: "truthy", value: v, type: BOOL, loc }
10593
+ : name === "BigInt"
10594
+ ? { kind: "libCall", fn: "dyn.bigintConstructor", args: [v], type: BIGINT_T, loc }
10595
+ : name === "String"
10596
+ ? v
10597
+ : name === "Number"
10598
+ ? { kind: "libCall", fn: "num.fromString", args: [v], type: F64, loc }
10599
+ : { kind: "strEq", negated: true, left: v, right: { kind: "strLit", value: "", type: STRING, loc }, type: BOOL, loc };
10216
10600
  const fn = {
10217
10601
  name: fnName,
10218
10602
  params: [{ localId: "v.0", name: "value", type: input }],
@@ -10225,4 +10609,25 @@ function primitiveCtorClosure(lowerer, name, loc, dynamicInput) {
10225
10609
  }
10226
10610
  return { kind: "closure", fnName, captures: [], type: fnT, loc };
10227
10611
  }
10612
+ /** Drain using checked dispatch so native class and generator iterators keep their methods. */
10613
+ function checkedIteratorPack(lowerer, source, loc) {
10614
+ const name = "%iterator.pack.checked";
10615
+ if (!lowerer.liftedFns.some((fn) => fn.name === name)) {
10616
+ const ref = (id) => ({ kind: "varRef", localId: id, type: DYN, loc });
10617
+ const property = (key) => ({ kind: "dynKeyGet", value: ref("step"), key: { kind: "strLit", value: key, type: STRING, loc }, type: DYN, loc });
10618
+ lowerer.liftedFns.push({ name, params: [{ localId: "source", name: "source", type: DYN }], returnType: DYN,
10619
+ locals: ["source", "iterator", "step", "result"].map((id) => ({ id, name: id, type: DYN, mutable: id !== "source" })),
10620
+ body: [
10621
+ { kind: "varDecl", localId: "iterator", init: { kind: "libCall", fn: "dyn.iterator", args: [ref("source"), { kind: "strLit", value: "value", type: STRING, loc }], type: DYN, loc }, loc },
10622
+ { kind: "varDecl", localId: "result", init: { kind: "dynArrLit", elems: [], type: DYN, loc }, loc },
10623
+ { kind: "while", cond: { kind: "boolLit", value: true, type: BOOL, loc }, body: [
10624
+ { kind: "varDecl", localId: "step", init: { kind: "dynInvoke", recv: ref("iterator"), method: "next", calleeName: "iterator.next", args: [], type: DYN, loc }, loc },
10625
+ { kind: "if", cond: { kind: "dynTest", test: "truthy", value: property("done"), type: BOOL, loc }, then: [{ kind: "return", value: ref("result"), loc }], else_: null, loc },
10626
+ { kind: "exprStmt", expr: { kind: "libCall", fn: "dyn.packPush", args: [ref("result"), property("value")], type: VOID, loc }, loc },
10627
+ ], loc },
10628
+ { kind: "return", value: ref("result"), loc },
10629
+ ], loc });
10630
+ }
10631
+ return { kind: "call", callee: name, args: [source], type: DYN, loc };
10632
+ }
10228
10633
  //# sourceMappingURL=lower-exprs.js.map