@scriptc/compiler 0.2.1 → 0.2.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (193) hide show
  1. package/ambient/scriptc-node-fallback.d.ts +59 -4
  2. package/dist/backend/llvm/dyn.d.ts +3 -0
  3. package/dist/backend/llvm/dyn.js +84 -12
  4. package/dist/backend/llvm/dyn.js.map +1 -1
  5. package/dist/backend/llvm/emitter.d.ts +4 -1
  6. package/dist/backend/llvm/emitter.js +20 -5
  7. package/dist/backend/llvm/emitter.js.map +1 -1
  8. package/dist/backend/llvm/emitter.test.js +9 -1
  9. package/dist/backend/llvm/emitter.test.js.map +1 -1
  10. package/dist/backend/llvm/expr-containers.js +5 -0
  11. package/dist/backend/llvm/expr-containers.js.map +1 -1
  12. package/dist/backend/llvm/expr-dynamic.js +30 -5
  13. package/dist/backend/llvm/expr-dynamic.js.map +1 -1
  14. package/dist/backend/llvm/expr-stream-bridges.js +70 -9
  15. package/dist/backend/llvm/expr-stream-bridges.js.map +1 -1
  16. package/dist/backend/llvm/expr-stream-callbacks.d.ts +2 -2
  17. package/dist/backend/llvm/expr-stream-callbacks.js +7 -57
  18. package/dist/backend/llvm/expr-stream-callbacks.js.map +1 -1
  19. package/dist/backend/llvm/lib-dispatch.js +41 -3
  20. package/dist/backend/llvm/lib-dispatch.js.map +1 -1
  21. package/dist/backend/llvm/lib-filesystem.js +12 -9
  22. package/dist/backend/llvm/lib-filesystem.js.map +1 -1
  23. package/dist/backend/llvm/lib-shared.js +35 -1
  24. package/dist/backend/llvm/lib-shared.js.map +1 -1
  25. package/dist/backend/may-throw.js +4 -0
  26. package/dist/backend/may-throw.js.map +1 -1
  27. package/dist/backend/native-codegen.d.ts +1 -1
  28. package/dist/backend/native-codegen.js +4 -3
  29. package/dist/backend/native-codegen.js.map +1 -1
  30. package/dist/backend/native-codegen.test.js +11 -0
  31. package/dist/backend/native-codegen.test.js.map +1 -1
  32. package/dist/backend/target-diagnostics.d.ts +5 -0
  33. package/dist/backend/target-diagnostics.js +72 -1
  34. package/dist/backend/target-diagnostics.js.map +1 -1
  35. package/dist/backend/target-diagnostics.test.d.ts +1 -0
  36. package/dist/backend/target-diagnostics.test.js +68 -0
  37. package/dist/backend/target-diagnostics.test.js.map +1 -0
  38. package/dist/coverage/surface-manifest.js +5 -3
  39. package/dist/coverage/surface-manifest.js.map +1 -1
  40. package/dist/executable/executable-cache.d.ts +7 -3
  41. package/dist/executable/executable-cache.js +47 -11
  42. package/dist/executable/executable-cache.js.map +1 -1
  43. package/dist/executable/executable-cache.test.js +109 -2
  44. package/dist/executable/executable-cache.test.js.map +1 -1
  45. package/dist/executable/prepare.js +2 -1
  46. package/dist/executable/prepare.js.map +1 -1
  47. package/dist/frontend/builtin-modules.d.ts +1 -1
  48. package/dist/frontend/builtin-modules.js +1 -1
  49. package/dist/frontend/builtin-modules.js.map +1 -1
  50. package/dist/frontend/function-completion.d.ts +6 -0
  51. package/dist/frontend/function-completion.js +50 -0
  52. package/dist/frontend/function-completion.js.map +1 -0
  53. package/dist/frontend/lowering/abstract-equality.js +5 -1
  54. package/dist/frontend/lowering/abstract-equality.js.map +1 -1
  55. package/dist/frontend/lowering/checked-iterable-spread.d.ts +4 -0
  56. package/dist/frontend/lowering/checked-iterable-spread.js +30 -0
  57. package/dist/frontend/lowering/checked-iterable-spread.js.map +1 -0
  58. package/dist/frontend/lowering/checked-promise-all.d.ts +5 -0
  59. package/dist/frontend/lowering/checked-promise-all.js +31 -0
  60. package/dist/frontend/lowering/checked-promise-all.js.map +1 -0
  61. package/dist/frontend/lowering/class-callbacks.d.ts +1 -1
  62. package/dist/frontend/lowering/class-callbacks.js +7 -6
  63. package/dist/frontend/lowering/class-callbacks.js.map +1 -1
  64. package/dist/frontend/lowering/class-construction.d.ts +2 -0
  65. package/dist/frontend/lowering/class-construction.js +34 -5
  66. package/dist/frontend/lowering/class-construction.js.map +1 -1
  67. package/dist/frontend/lowering/class-descriptors.d.ts +1 -1
  68. package/dist/frontend/lowering/class-descriptors.js +12 -5
  69. package/dist/frontend/lowering/class-descriptors.js.map +1 -1
  70. package/dist/frontend/lowering/class-dynamic-dispatch.d.ts +25 -0
  71. package/dist/frontend/lowering/class-dynamic-dispatch.js +597 -78
  72. package/dist/frontend/lowering/class-dynamic-dispatch.js.map +1 -1
  73. package/dist/frontend/lowering/class-dynamic-dispatch.test.js +64 -2
  74. package/dist/frontend/lowering/class-dynamic-dispatch.test.js.map +1 -1
  75. package/dist/frontend/lowering/class-instance-constructor.d.ts +1 -1
  76. package/dist/frontend/lowering/class-instance-constructor.js +11 -41
  77. package/dist/frontend/lowering/class-instance-constructor.js.map +1 -1
  78. package/dist/frontend/lowering/class-method-values.d.ts +16 -0
  79. package/dist/frontend/lowering/class-method-values.js +60 -29
  80. package/dist/frontend/lowering/class-method-values.js.map +1 -1
  81. package/dist/frontend/lowering/class-prototypes.d.ts +5 -1
  82. package/dist/frontend/lowering/class-prototypes.js +69 -10
  83. package/dist/frontend/lowering/class-prototypes.js.map +1 -1
  84. package/dist/frontend/lowering/class-runtime-statics.d.ts +4 -4
  85. package/dist/frontend/lowering/class-runtime-statics.js +26 -8
  86. package/dist/frontend/lowering/class-runtime-statics.js.map +1 -1
  87. package/dist/frontend/lowering/class-static-data.d.ts +1 -0
  88. package/dist/frontend/lowering/class-static-data.js +4 -2
  89. package/dist/frontend/lowering/class-static-data.js.map +1 -1
  90. package/dist/frontend/lowering/containers/bytes.js +17 -4
  91. package/dist/frontend/lowering/containers/bytes.js.map +1 -1
  92. package/dist/frontend/lowering/containers/string-and-regexp.js +6 -1
  93. package/dist/frontend/lowering/containers/string-and-regexp.js.map +1 -1
  94. package/dist/frontend/lowering/deferred-module-initializers.d.ts +13 -0
  95. package/dist/frontend/lowering/deferred-module-initializers.js +410 -0
  96. package/dist/frontend/lowering/deferred-module-initializers.js.map +1 -0
  97. package/dist/frontend/lowering/deferred-module-initializers.test.d.ts +1 -0
  98. package/dist/frontend/lowering/deferred-module-initializers.test.js +92 -0
  99. package/dist/frontend/lowering/deferred-module-initializers.test.js.map +1 -0
  100. package/dist/frontend/lowering/expressions/object-literals.js +31 -1
  101. package/dist/frontend/lowering/expressions/object-literals.js.map +1 -1
  102. package/dist/frontend/lowering/expressions/optional-chains.js +3 -3
  103. package/dist/frontend/lowering/expressions/optional-chains.js.map +1 -1
  104. package/dist/frontend/lowering/lower-builtins.d.ts +4 -9
  105. package/dist/frontend/lowering/lower-builtins.js +291 -51
  106. package/dist/frontend/lowering/lower-builtins.js.map +1 -1
  107. package/dist/frontend/lowering/lower-calls.js +283 -73
  108. package/dist/frontend/lowering/lower-calls.js.map +1 -1
  109. package/dist/frontend/lowering/lower-classes.d.ts +5 -0
  110. package/dist/frontend/lowering/lower-classes.js +439 -25
  111. package/dist/frontend/lowering/lower-classes.js.map +1 -1
  112. package/dist/frontend/lowering/lower-containers.d.ts +1 -1
  113. package/dist/frontend/lowering/lower-containers.js +39 -17
  114. package/dist/frontend/lowering/lower-containers.js.map +1 -1
  115. package/dist/frontend/lowering/lower-exprs.d.ts +10 -6
  116. package/dist/frontend/lowering/lower-exprs.js +294 -129
  117. package/dist/frontend/lowering/lower-exprs.js.map +1 -1
  118. package/dist/frontend/lowering/lower-generators.js +5 -1
  119. package/dist/frontend/lowering/lower-generators.js.map +1 -1
  120. package/dist/frontend/lowering/lower-inspect.js +15 -1
  121. package/dist/frontend/lowering/lower-inspect.js.map +1 -1
  122. package/dist/frontend/lowering/lower-island.js +4 -0
  123. package/dist/frontend/lowering/lower-island.js.map +1 -1
  124. package/dist/frontend/lowering/lower-island.test.d.ts +1 -0
  125. package/dist/frontend/lowering/lower-island.test.js +51 -0
  126. package/dist/frontend/lowering/lower-island.test.js.map +1 -0
  127. package/dist/frontend/lowering/lower-modules.d.ts +1 -3
  128. package/dist/frontend/lowering/lower-modules.js +186 -82
  129. package/dist/frontend/lowering/lower-modules.js.map +1 -1
  130. package/dist/frontend/lowering/lower-modules.test.js +69 -1
  131. package/dist/frontend/lowering/lower-modules.test.js.map +1 -1
  132. package/dist/frontend/lowering/lower-stmts.js +100 -35
  133. package/dist/frontend/lowering/lower-stmts.js.map +1 -1
  134. package/dist/frontend/lowering/lower-stmts.test.d.ts +1 -0
  135. package/dist/frontend/lowering/lower-stmts.test.js +77 -0
  136. package/dist/frontend/lowering/lower-stmts.test.js.map +1 -0
  137. package/dist/frontend/lowering/lower-stream.js +2 -0
  138. package/dist/frontend/lowering/lower-stream.js.map +1 -1
  139. package/dist/frontend/lowering/lower-url.d.ts +2 -1
  140. package/dist/frontend/lowering/lower-url.js +32 -9
  141. package/dist/frontend/lowering/lower-url.js.map +1 -1
  142. package/dist/frontend/lowering/lowerer.d.ts +26 -9
  143. package/dist/frontend/lowering/lowerer.js +218 -27
  144. package/dist/frontend/lowering/lowerer.js.map +1 -1
  145. package/dist/frontend/lowering/lowerer.test.js +38 -0
  146. package/dist/frontend/lowering/lowerer.test.js.map +1 -1
  147. package/dist/frontend/lowering/module-namespace-elements.d.ts +7 -0
  148. package/dist/frontend/lowering/module-namespace-elements.js +54 -0
  149. package/dist/frontend/lowering/module-namespace-elements.js.map +1 -0
  150. package/dist/frontend/lowering/object-factory-new.d.ts +4 -3
  151. package/dist/frontend/lowering/object-factory-new.js +27 -14
  152. package/dist/frontend/lowering/object-factory-new.js.map +1 -1
  153. package/dist/frontend/lowering/surfaces.d.ts +1 -0
  154. package/dist/frontend/lowering/surfaces.js +24 -12
  155. package/dist/frontend/lowering/surfaces.js.map +1 -1
  156. package/dist/frontend/lowering/text-decoder-encoding.js +3 -0
  157. package/dist/frontend/lowering/text-decoder-encoding.js.map +1 -1
  158. package/dist/frontend/lowering/text-decoder-encoding.test.js +2 -1
  159. package/dist/frontend/lowering/text-decoder-encoding.test.js.map +1 -1
  160. package/dist/frontend/program.js +4 -2
  161. package/dist/frontend/program.js.map +1 -1
  162. package/dist/frontend/type-mapper.js +83 -14
  163. package/dist/frontend/type-mapper.js.map +1 -1
  164. package/dist/frontend/union-discriminants.js +22 -7
  165. package/dist/frontend/union-discriminants.js.map +1 -1
  166. package/dist/index.js +16 -10
  167. package/dist/index.js.map +1 -1
  168. package/dist/ir/ir.d.ts +27 -13
  169. package/dist/ir/ir.js +26 -2
  170. package/dist/ir/ir.js.map +1 -1
  171. package/dist/ir/serialize.js +21 -1
  172. package/dist/ir/serialize.js.map +1 -1
  173. package/dist/ir/serialize.test.js +21 -0
  174. package/dist/ir/serialize.test.js.map +1 -1
  175. package/dist/ir/traverse.d.ts +0 -3
  176. package/dist/ir/traverse.js +33 -20
  177. package/dist/ir/traverse.js.map +1 -1
  178. package/dist/ir/validate.js +56 -5
  179. package/dist/ir/validate.js.map +1 -1
  180. package/dist/ir/validate.test.js +17 -1
  181. package/dist/ir/validate.test.js.map +1 -1
  182. package/dist/library/compiler-self-identity.js +1 -1
  183. package/dist/library/compiler-self-identity.test.js +2 -2
  184. package/dist/library/compiler-self-identity.test.js.map +1 -1
  185. package/dist/library/int-infer.d.ts +2 -2
  186. package/dist/library/int-infer.js +10 -7
  187. package/dist/library/int-infer.js.map +1 -1
  188. package/dist/library/int-infer.test.js +44 -0
  189. package/dist/library/int-infer.test.js.map +1 -1
  190. package/dist/library/prepare.js +2 -1
  191. package/dist/library/prepare.js.map +1 -1
  192. package/package.json +20 -20
  193. package/surface-manifest.json +115 -13
@@ -10,7 +10,7 @@ import { bodyReadsArguments } from "../arguments-usage.js";
10
10
  import { lowerGenMethodCall } from "./lower-generators.js";
11
11
  import { lowerClassSymbolMethodCall } from "./symbol-methods.js";
12
12
  import { errorToStringCall } from "./error-methods.js";
13
- import { lowerClassPrototypeAssign } from "./class-prototypes.js";
13
+ import { lowerClassPrototypeAssign, lowerClassPrototypeDescriptors } from "./class-prototypes.js";
14
14
  import { BYTES_ELEMENT_NAME, BIGINT_T, BOOL, CAUGHT, DYN, F64, JSVAL, NULL_T, STRING, SYMBOL_T, UNDEFINED_T, VOID, arrayOf, canBoxFuncIntoDyn, canConvertToDyn, canDynCheckTo, canMarshalTypedFuncIntoIsland, ffiClassType, ffiSourceParamTypes, funcOf, isDynTypedRefType, isFfiCallbackParam, isFfiContextParam, isFfiReleaseParam, isUnitType, shapeHasAccessorSlots, typeEquals } from "../../ir/ir.js";
15
15
  import { isJsSourceFile, isNodeEsmFile, locOf, requireSpecOf } from "../program.js";
16
16
  import { genResultRecord, isGenericCallableMemberType, jsOpenObjectType, typeKey } from "../type-mapper.js";
@@ -45,6 +45,9 @@ import { fenceSymbolFieldCopy } from "./symbol-fields.js";
45
45
  import { lowerClassDataDescriptor, lowerClassDescriptorRead } from "./class-descriptors.js";
46
46
  import { classStaticDataFor } from "./class-static-data.js";
47
47
  import { jsBindingHasOpenWrites } from "./lower-stmts.js";
48
+ import { functionCanReturnUndefined } from "../function-completion.js";
49
+ import { checkedIterableSpread } from "./checked-iterable-spread.js";
50
+ import { lowerUrlFactory } from "./lower-url.js";
48
51
  export { bodyReadsArguments };
49
52
  /** The one func-type projection of completed parameter shapes. Typed rest
50
53
  * parameters keep their packed array as the trailing native ABI slot and
@@ -125,6 +128,23 @@ export const MAX_GENERIC_INSTANCES = 100;
125
128
  * - `...xs: T[]`: the ABI type is the array; call sites pack the surplus.
126
129
  */
127
130
  export function paramShape(lowerer, param) {
131
+ if (isJsSourceFile(param.getSourceFile()) && ts.isSetAccessorDeclaration(param.parent))
132
+ return { type: DYN, mode: "required" };
133
+ if (isJsSourceFile(param.getSourceFile()) && ts.isConstructorDeclaration(param.parent) && !param.dotDotDotToken) {
134
+ // Reflective JavaScript construction can supply values outside the
135
+ // constructor's documented usual types. Retain the actual arguments
136
+ // and apply checked conversions only at operations that need them.
137
+ return param.initializer ? { type: DYN, mode: "omittable", bodyType: DYN }
138
+ : { type: DYN, mode: param.questionToken ? "omittable" : "required" };
139
+ }
140
+ const executor = param.parent;
141
+ const construction = executor?.parent;
142
+ const nativeExecutor = executor && (ts.isArrowFunction(executor) || ts.isFunctionExpression(executor)) && ts.isNewExpression(construction) &&
143
+ construction.arguments?.[0] === executor && lowerer.isStdlibGlobal(construction.expression, "Promise");
144
+ if (!nativeExecutor && isJsSourceFile(param.getSourceFile()) && !param.type && !param.dotDotDotToken && !ts.isSetAccessorDeclaration(param.parent) &&
145
+ !/@(?:param|type)\b/.test(param.getSourceFile().text.slice(param.parent?.pos ?? param.pos, param.getStart()))) {
146
+ return param.initializer ? { type: DYN, mode: "omittable", bodyType: DYN } : { type: DYN, mode: "required" };
147
+ }
128
148
  if (param.initializer && isJsSourceFile(param.getSourceFile()) && !param.type &&
129
149
  !/@(?:param|type)\b/.test(param.getSourceFile().text.slice(param.parent?.pos ?? param.pos, param.getStart()))) {
130
150
  return { type: DYN, mode: "omittable", bodyType: DYN };
@@ -219,9 +239,39 @@ export function paramShape(lowerer, param) {
219
239
  }
220
240
  if (param.initializer) {
221
241
  const raw = lowerer.irTypeOf(param.name);
242
+ // An optional JS array parameter is a live caller-owned buffer,
243
+ // including typed arrays passed through checked callbacks. Extracting
244
+ // a native array here would copy it and discard element writes.
245
+ if (isJsSourceFile(param.getSourceFile()) && !param.type &&
246
+ (raw.kind === "array" || raw.kind === "union" && lowerer.unions.get(raw.unionId)?.arms.some((arm) => arm.kind === "array"))) {
247
+ return { type: DYN, mode: "omittable", bodyType: DYN };
248
+ }
222
249
  return defaultParameterShape(lowerer, param, raw);
223
250
  }
224
- const type = lowerer.runtimeOptionalBindingType(param.name, lowerer.irTypeOf(param.name));
251
+ let type = lowerer.runtimeOptionalBindingType(param.name, lowerer.irTypeOf(param.name));
252
+ const body = param.parent?.body;
253
+ if (isJsSourceFile(param.getSourceFile()) && body &&
254
+ type.kind !== "dyn" && type.kind !== "jsval") {
255
+ const symbol = lowerer.checker.getSymbolAtLocation(param.name);
256
+ let acceptsUndefined = false;
257
+ ts.walkPreorder(body, (node) => {
258
+ if (acceptsUndefined || !ts.isBinaryExpression(node))
259
+ return;
260
+ const op = node.operatorToken.kind;
261
+ if (op !== ts.SyntaxKind.EqualsEqualsEqualsToken && op !== ts.SyntaxKind.ExclamationEqualsEqualsToken &&
262
+ op !== ts.SyntaxKind.EqualsEqualsToken && op !== ts.SyntaxKind.ExclamationEqualsToken)
263
+ return;
264
+ const checks = (value, absent) => ts.isIdentifier(value) && ts.isIdentifier(absent) && absent.text === "undefined" &&
265
+ (lowerer.typeOf(absent).flags & ts.TypeFlags.Undefined) !== 0 &&
266
+ lowerer.checker.getSymbolAtLocation(value) === symbol;
267
+ acceptsUndefined = checks(node.left, node.right) || checks(node.right, node.left);
268
+ });
269
+ // A JavaScript body that handles an absent argument defines a wider
270
+ // contract than its JSDoc's usual value. Checked callers must deliver
271
+ // undefined to that branch rather than reject it at the ABI boundary.
272
+ if (acceptsUndefined)
273
+ type = lowerer.promoteRuntimeOptionalParameter(param.name, type);
274
+ }
225
275
  if (type.kind === "void") {
226
276
  return { type: DYN, mode: param.questionToken ? "omittable" : "required" };
227
277
  }
@@ -368,7 +418,7 @@ leading) {
368
418
  // before conversion to the dense checked-dynamic argument pack.
369
419
  value = { kind: "arrayLit", elems: [value], spreads: [0], type: value.type, loc: value.loc };
370
420
  }
371
- value = lowerer.coerceInto(arg.expression, value, DYN);
421
+ value = checkedIterableSpread(lowerer, value, arg.expression.getText(), loc);
372
422
  if (spreads === 1 && index === argNodes.length - 1) {
373
423
  stmts.push({ kind: "exprStmt", expr: {
374
424
  kind: "libCall", fn: "dyn.packPushSpread", args: [ref(), value, { kind: "strLit", value: arg.expression.getText(), type: STRING, loc }], type: VOID, loc,
@@ -892,6 +942,8 @@ function producesConstructor(lowerer, type) {
892
942
  return false;
893
943
  }
894
944
  export function declaredReturnType(lowerer, decl, blame) {
945
+ if (isJsSourceFile(decl.getSourceFile()) && ts.isGetAccessorDeclaration(decl))
946
+ return DYN;
895
947
  const sig = lowerer.checker.getSignatureFromDeclaration(decl);
896
948
  if (!sig)
897
949
  lowerer.unsupported("SC1090", decl, "this function form");
@@ -906,11 +958,36 @@ export function declaredReturnType(lowerer, decl, blame) {
906
958
  // (`.action(() => { throw ... })`). `never` VALUES stay unmapped.
907
959
  if (retTsType.flags & ts.TypeFlags.Never)
908
960
  return VOID;
909
- const mappedReturn = lowerer.mapTypeOf(retTsType);
910
- if (isJsSourceFile(decl.getSourceFile()) && mappedReturn?.kind === "array" && mappedReturn.elem.kind === "f64")
961
+ let mappedReturn = lowerer.mapTypeOf(retTsType);
962
+ if (isJsSourceFile(decl.getSourceFile()) && decl.modifiers?.some((modifier) => modifier.kind === ts.SyntaxKind.AsyncKeyword) &&
963
+ !decl.asteriskToken && mappedReturn?.kind !== "promise") {
964
+ const fallback = dynFallbackType(lowerer, decl, retTsType);
965
+ return fallback?.kind === "promise" ? fallback : { kind: "promise", inner: DYN };
966
+ }
967
+ if (mappedReturn && isJsSourceFile(decl.getSourceFile()) && functionCanReturnUndefined(decl))
968
+ mappedReturn = lowerer.withUndefinedArmOf(mappedReturn) ?? mappedReturn;
969
+ if (isJsSourceFile(decl.getSourceFile()) && mappedReturn?.kind === "void") {
970
+ let returnsValue = false;
971
+ const scan = (node) => {
972
+ if (node !== decl && ts.isFunctionLike(node))
973
+ return;
974
+ if (ts.isReturnStatement(node) && node.expression)
975
+ returnsValue = true;
976
+ node.forEachChild(scan);
977
+ };
978
+ scan(decl);
979
+ if (returnsValue)
980
+ return DYN;
981
+ }
982
+ if (isJsSourceFile(decl.getSourceFile()) && mappedReturn?.kind === "array")
983
+ return DYN;
984
+ if (isJsSourceFile(decl.getSourceFile()) && !decl.type && !hasExplicitJsDocReturn(decl) && mappedReturn?.kind === "record")
911
985
  return DYN;
912
986
  if (isJsSourceFile(decl.getSourceFile()) && !decl.type && !hasExplicitJsDocReturn(decl) && mappedReturn?.kind === "date")
913
987
  return DYN;
988
+ if (isJsSourceFile(decl.getSourceFile()) && !decl.type && !hasExplicitJsDocReturn(decl) &&
989
+ mappedReturn?.kind === "union" && lowerer.unions.get(mappedReturn.unionId)?.arms.some((arm) => arm.kind === "func"))
990
+ return DYN;
914
991
  // JS inference assigns Number to arithmetic over any, although the
915
992
  // runtime operands can both be BigInts. Preserve that native result.
916
993
  if (isJsSourceFile(decl.getSourceFile()) && !decl.type && !hasExplicitJsDocReturn(decl) &&
@@ -971,7 +1048,7 @@ export function declaredReturnType(lowerer, decl, blame) {
971
1048
  // Native-only members (such as Int32Array layout offsets) cannot cross
972
1049
  // that boundary; their factories keep the native record ABI.
973
1050
  const asyncReturn = decl.modifiers?.some((modifier) => modifier.kind === ts.SyntaxKind.AsyncKeyword) && mappedReturn?.kind === "promise";
974
- const recordReturn = asyncReturn ? mappedReturn.inner : mappedReturn;
1051
+ const recordReturn = asyncReturn && mappedReturn?.kind === "promise" ? mappedReturn.inner : mappedReturn;
975
1052
  const recordArms = recordReturn?.kind === "union" ? lowerer.unions.get(recordReturn.unionId)?.arms : recordReturn ? [recordReturn] : undefined;
976
1053
  const inferredRecordReturn = recordArms?.some((arm) => arm.kind === "record" || arm.kind === "object") &&
977
1054
  recordArms.every((arm) => isUnitType(arm) || arm.kind === "record" || arm.kind === "object" && !hasExplicitJsDocReturn(decl));
@@ -985,7 +1062,7 @@ export function declaredReturnType(lowerer, decl, blame) {
985
1062
  })) {
986
1063
  return asyncReturn ? { kind: "promise", inner: DYN } : DYN;
987
1064
  }
988
- const returnType = lowerer.mapTypeOf(retTsType);
1065
+ const returnType = mappedReturn;
989
1066
  if (!returnType) {
990
1067
  // JS inference residue (an `any` return, an unmappable union): the
991
1068
  // checked-dynamic fallback, exactly the declaration story in
@@ -2097,6 +2174,12 @@ function implicitDeclaredReturn(lowerer, info) {
2097
2174
  if (retTs.flags & (ts.TypeFlags.Null | ts.TypeFlags.Undefined))
2098
2175
  return DYN;
2099
2176
  if (lowerer.checker.isArrayType(retTs)) {
2177
+ // Inferred JavaScript arrays can hold values selected through a
2178
+ // mutable constructor registry. The checker may retain only one
2179
+ // constructor's element type; settle the result from the lowered
2180
+ // container instead of checking every element against that guess.
2181
+ if (!hasExplicitJsDocReturn(info.decl))
2182
+ return null;
2100
2183
  if (info.decl.parameters.some((param, index) => info.implicitParams?.[index] &&
2101
2184
  lowerer.mapTypeOf(lowerer.typeOf(param.name))?.kind === "array"))
2102
2185
  return null;
@@ -2110,6 +2193,12 @@ function implicitDeclaredReturn(lowerer, info) {
2110
2193
  return null;
2111
2194
  }
2112
2195
  const mapped = lowerer.mapTypeOf(retTs);
2196
+ // Mixed inferred results can select a closure or a class instance.
2197
+ // Their callable ABI is settled by the lowered body, including native
2198
+ // classes whose reflected calls return checked values.
2199
+ if (mapped?.kind === "union" && !hasExplicitJsDocReturn(info.decl) &&
2200
+ lowerer.unions.get(mapped.unionId)?.arms.some((arm) => arm.kind === "func"))
2201
+ return null;
2113
2202
  if (mapped?.kind === "symbol" && !hasExplicitJsDocReturn(info.decl))
2114
2203
  return null;
2115
2204
  if (mapped?.kind === "date" && !hasExplicitJsDocReturn(info.decl))
@@ -2206,12 +2295,25 @@ function storedImplicitArgumentType(lowerer, arg) {
2206
2295
  return DYN;
2207
2296
  if (ts.isIdentifier(arg))
2208
2297
  return lowerer.peekLocal(arg)?.type ?? lowerer.globalOf(arg)?.type ?? null;
2298
+ if (isJsSourceFile(arg.getSourceFile()) &&
2299
+ (ts.isPropertyAccessExpression(arg) || ts.isElementAccessExpression(arg))) {
2300
+ // Mutable JS members can retain an initializer-only checker type (for
2301
+ // example a linked-list slot initially null). Specialize to the value
2302
+ // we actually represent, rather than checking it back into that type.
2303
+ const represented = tryLowerExpression(lowerer, arg);
2304
+ if (represented)
2305
+ return represented.type;
2306
+ }
2307
+ if (isJsSourceFile(arg.getSourceFile()) && (ts.isCallExpression(arg) || ts.isConditionalExpression(arg) ||
2308
+ ts.isBinaryExpression(arg) && (arg.operatorToken.kind === ts.SyntaxKind.BarBarToken ||
2309
+ arg.operatorToken.kind === ts.SyntaxKind.AmpersandAmpersandToken || arg.operatorToken.kind === ts.SyntaxKind.QuestionQuestionToken))) {
2310
+ // Calls and fallback expressions can retain checked storage wider than
2311
+ // the checker's inference (for example Object.assign on a JS instance).
2312
+ const represented = tryLowerExpression(lowerer, arg);
2313
+ if (represented?.type.kind === "dyn" || represented?.type.kind === "func")
2314
+ return represented.type;
2315
+ }
2209
2316
  if (ts.isCallExpression(arg) && ts.isIdentifier(arg.expression)) {
2210
- if (isJsSourceFile(arg.getSourceFile())) {
2211
- const represented = tryLowerExpression(lowerer, arg);
2212
- if (represented?.type.kind === "dyn" || represented?.type.kind === "func")
2213
- return represented.type;
2214
- }
2215
2317
  const callee = storedImplicitArgumentType(lowerer, arg.expression);
2216
2318
  if (callee?.kind === "dyn")
2217
2319
  return DYN;
@@ -3852,6 +3954,7 @@ export function lowerCall(lowerer, expr) {
3852
3954
  lowerer.fenceStaticReadableStreamMember(access, "call");
3853
3955
  const source = tryLowerExpression(lowerer, access.expression);
3854
3956
  if (source && (source.type.kind === "dyn" || source.type.kind === "func" ||
3957
+ source.type.kind === "union" && isJsSourceFile(expr.getSourceFile()) && lowerer.dynConvertible(source.type) ||
3855
3958
  (source.type.kind === "map" || source.type.kind === "set") && lowerer.dynConvertible(source.type) || isDynTypedRefType(source.type) &&
3856
3959
  lowerer.foldedStringKeyOf(access.argumentExpression) === null &&
3857
3960
  lowerer.mapTypeOf(lowerer.typeOf(access.argumentExpression))?.kind !== "symbol")) {
@@ -4238,6 +4341,9 @@ export function lowerCall(lowerer, expr) {
4238
4341
  if (name === "parseFloat" && probed?.type.kind === "string") {
4239
4342
  return { kind: "libCall", fn: "num.parseFloat", args: [probed], type: F64, loc };
4240
4343
  }
4344
+ if (name === "parseFloat" && probed?.type.kind === "dyn") {
4345
+ return { kind: "libCall", fn: "num.parseFloat", args: [{ kind: "libCall", fn: "dyn.toStringCoerce", args: [probed], type: STRING, loc }], type: F64, loc };
4346
+ }
4241
4347
  if (name === "isFinite" && probed?.type.kind === "f64") {
4242
4348
  return { kind: "libCall", fn: "number.isFinite", args: [probed], type: BOOL, loc };
4243
4349
  }
@@ -4565,6 +4671,7 @@ export function lowerCall(lowerer, expr) {
4565
4671
  // Before the island path: regex-argument replace/replaceAll/split
4566
4672
  // lower STATICALLY; only the string-pattern overloads are island.
4567
4673
  lowerRegexMethodCallWithOptionalArg(lowerer, expr, expr.expression) ??
4674
+ lowerUrlStaticCall(lowerer, expr, expr.expression) ??
4568
4675
  lowerer.lowerUrlMethodCall(expr, expr.expression) ??
4569
4676
  lowerer.lowerSearchParamsMethodCall(expr, expr.expression) ??
4570
4677
  lowerer.lowerStatsMethodCall(expr, expr.expression) ??
@@ -4615,9 +4722,6 @@ export function lowerCall(lowerer, expr) {
4615
4722
  lowerer.lowerBytesMethodCall(expr, expr.expression) ??
4616
4723
  lowerBytesStaticCall(lowerer, expr, expr.expression) ??
4617
4724
  lowerBufferStaticCallWithNarrowedArg(lowerer, expr, expr.expression) ??
4618
- // URL.revokeObjectURL's zero-argument contract (the one-argument
4619
- // form keeps the fence — createObjectURL does too).
4620
- lowerUrlStaticCall(lowerer, expr, expr.expression) ??
4621
4725
  // Readable.from — the stream classes' one static (before the
4622
4726
  // stdlib chokepoint claims the member).
4623
4727
  lowerStreamStaticCall(lowerer, expr, expr.expression) ??
@@ -5760,7 +5864,7 @@ function lowerSpreadArgsCall(lowerer, expr, callee, loc) {
5760
5864
  // The spelling rides along for V8's nullish spread-call
5761
5865
  // TypeError ("v is not iterable (cannot read property ...)").
5762
5866
  spreads.push({ arg: args.length, what: p.spreadOf.getText() });
5763
- args.push(lowerer.coerceInto(p.spreadOf, p.v, DYN));
5867
+ args.push(checkedIterableSpread(lowerer, p.v, p.spreadOf.getText(), loc));
5764
5868
  }
5765
5869
  else {
5766
5870
  args.push(lowerer.coerceInto(p.node, p.v, DYN));
@@ -5770,7 +5874,7 @@ function lowerSpreadArgsCall(lowerer, expr, callee, loc) {
5770
5874
  return { kind: "dynCall", callee: boxed, calleeName, args, spreads, type: DYN, loc };
5771
5875
  }
5772
5876
  if (spreadParts.length > 0 &&
5773
- spreadParts.every((p) => p.v.type.kind === "jsval") &&
5877
+ (spreadParts.every((p) => p.v.type.kind === "jsval") || callee.type.kind === "func" && callee.type.restAbi === "jsval") &&
5774
5878
  (callee.type.kind === "jsval" || callee.type.kind === "func")) {
5775
5879
  if (spreadParts.length !== 1 || parts[parts.length - 1].spreadOf === null) {
5776
5880
  lowerer.unsupported("SC1090", expr, "spread arguments before positional arguments in island calls (one trailing spread after the fixed arguments is the supported form)");
@@ -5783,7 +5887,7 @@ function lowerSpreadArgsCall(lowerer, expr, callee, loc) {
5783
5887
  const last = parts[parts.length - 1];
5784
5888
  // The spelling rides in `name` for V8's nullish spread-call
5785
5889
  // TypeError ("v is not iterable (cannot read property ...)").
5786
- return { kind: "jsOp", op: "callSpread", name: last.spreadOf.getText(), args: [f, preArr, last.v], type: JSVAL, loc };
5890
+ return { kind: "jsOp", op: "callSpread", name: last.spreadOf.getText(), args: [f, preArr, lowerer.coerceInto(last.spreadOf, last.v, JSVAL)], type: JSVAL, loc };
5787
5891
  }
5788
5892
  // No lane fits (a spread source outside both tiers, a callee neither
5789
5893
  // boxable nor marshalable, mixed dyn/jsval spreads): fence HERE — the
@@ -5835,12 +5939,21 @@ function canCompleteRuntimeSpread(lowerer, shapes) {
5835
5939
  function lowerDynReceiverMethodCall(lowerer, call, access) {
5836
5940
  if (lowerer.chainBlocked(call, access))
5837
5941
  return null;
5942
+ if (stdlibGlobalNameOf(lowerer, access.expression) !== null)
5943
+ return null;
5838
5944
  // Only checker-untyped receivers: `any`/`unknown`, or the `any[]` an
5839
5945
  // Array.isArray guard narrows them to (the value is STILL the checked-dynamic tree
5840
5946
  // array — scalar narrowings bridge through maybeNarrow's dynCheck and
5841
5947
  // take the ordinary typed paths, but there is no static home for an
5842
5948
  // any-elemented array). Typed receivers keep their own lowerings.
5843
5949
  const recvTs = lowerer.typeOf(access.expression);
5950
+ const recvSymbol = recvTs.getSymbol();
5951
+ if (recvSymbol && ["MapIterator", "SetIterator"].includes(recvSymbol.name) &&
5952
+ lowerer.isStdlibSymbol(recvSymbol) &&
5953
+ ["map", "filter", "take", "drop", "flatMap", "toArray", "forEach", "reduce", "some", "every", "find"].includes(access.name.text) &&
5954
+ lowerer.isStdlibMember(access)) {
5955
+ lowerer.noLowering(`collection iterator .${access.name.text}()`, call, "stored Map/Set iterators support .next() and iteration; iterator helper methods have no lowering");
5956
+ }
5844
5957
  const arrayReceiver = lowerer.checker.isArrayType(recvTs);
5845
5958
  const anyArray = arrayReceiver &&
5846
5959
  ((lowerer.checker.getTypeArguments(recvTs)[0]?.flags ?? 0) &
@@ -5860,7 +5973,7 @@ function lowerDynReceiverMethodCall(lowerer, call, access) {
5860
5973
  !["call", "apply", "bind", "toString", "valueOf", "constructor"].includes(access.name.text) &&
5861
5974
  canBoxFuncIntoDyn(probed.type, (id) => lowerer.shapes.get(id), (id) => lowerer.unions.get(id))) {
5862
5975
  const loc = locOf(call);
5863
- const fnName = jsFuncNameOf(access.expression);
5976
+ const fnName = jsFuncNameOf(access.expression, lowerer);
5864
5977
  const boxed = { kind: "dynFrom", value: probed, type: DYN, loc, ...(fnName !== null ? { fnName } : {}) };
5865
5978
  const local = lowerer.declareHiddenLocal("%callableReceiver", DYN);
5866
5979
  const receiver = { kind: "varRef", localId: local.id, type: DYN, loc };
@@ -5879,18 +5992,24 @@ function lowerDynReceiverMethodCall(lowerer, call, access) {
5879
5992
  }
5880
5993
  if (probed?.type.kind === "classval") {
5881
5994
  const info = lowerer.classes.get(probed.type.className);
5882
- if (info && (info.callableBase || hasRuntimeStatics(info)))
5995
+ if (info)
5883
5996
  return lowerStaticMethodCall(lowerer, call, access);
5884
5997
  }
5885
- if (probed?.type.kind !== "dyn")
5886
- return null;
5887
- recv = probed;
5998
+ if (probed?.type.kind === "union" && isJsSourceFile(call.getSourceFile()) &&
5999
+ lowerer.unions.get(probed.type.unionId)?.arms.every((arm) => isUnitType(arm) || arm.kind === "object" || arm.kind === "record" || arm.kind === "func") && lowerer.dynConvertible(probed.type)) {
6000
+ recv = lowerer.coerceToExpected(probed, DYN);
6001
+ }
6002
+ else {
6003
+ if (probed?.type.kind !== "dyn")
6004
+ return null;
6005
+ recv = probed;
6006
+ }
5888
6007
  }
5889
6008
  // A factory may return a constructor more precise than its inferred
5890
6009
  // checker result. Runtime static members live on that constructor.
5891
6010
  if (recv.type.kind === "classval") {
5892
6011
  const info = lowerer.classes.get(recv.type.className);
5893
- if (info && (info.callableBase || hasRuntimeStatics(info)))
6012
+ if (info)
5894
6013
  return lowerStaticMethodCall(lowerer, call, access);
5895
6014
  }
5896
6015
  // A checker-`any` receiver that already lowered to a real STRING (the
@@ -5906,6 +6025,25 @@ function lowerDynReceiverMethodCall(lowerer, call, access) {
5906
6025
  }
5907
6026
  if (recv.type.kind !== "dyn")
5908
6027
  return null;
6028
+ if (access.name.text === "normalize" && call.arguments.length <= 1 && !call.arguments.some(ts.isSpreadElement)) {
6029
+ const loc = locOf(call);
6030
+ const hasArgument = call.arguments.length === 1;
6031
+ const name = `%dyn.string.normalize:${hasArgument ? 1 : 0}`;
6032
+ if (!lowerer.liftedFns.some((fn) => fn.name === name)) {
6033
+ const value = varRef("receiver", DYN, loc);
6034
+ const argument = varRef("form", DYN, loc);
6035
+ const form = { kind: "ternary", cond: { kind: "dynTest", value: argument, test: "undefined", type: BOOL, loc },
6036
+ then: { kind: "strLit", value: "NFC", type: STRING, loc },
6037
+ else_: { kind: "libCall", fn: "dyn.toStringCoerce", args: [argument], type: STRING, loc }, type: STRING, loc };
6038
+ const result = { kind: "strIntrinsic", method: "normalize", receiver: { kind: "dynCheck", value, type: STRING, loc }, args: [form], type: STRING, loc };
6039
+ lowerer.liftedFns.push({ name, params: [{ localId: "receiver", name: "receiver", type: DYN }, { localId: "form", name: "form", type: DYN }],
6040
+ locals: [{ id: "receiver", name: "receiver", type: DYN, mutable: false }, { id: "form", name: "form", type: DYN, mutable: false }], returnType: DYN, loc,
6041
+ body: [{ kind: "if", cond: { kind: "dynTest", value, test: "string", type: BOOL, loc }, then: [
6042
+ { kind: "return", value: { kind: "dynFrom", value: result, type: DYN, loc }, loc },
6043
+ ], else_: null, loc }, { kind: "return", value: { kind: "dynInvoke", recv: value, method: "normalize", calleeName: access.getText(), args: hasArgument ? [argument] : [], type: DYN, loc }, loc }] });
6044
+ }
6045
+ return { kind: "call", callee: name, args: [recv, call.arguments[0] ? lowerer.lowerExprExpecting(call.arguments[0], DYN) : dynUndefinedExpr(loc)], type: DYN, loc };
6046
+ }
5909
6047
  if (lowerer.mapTypeOf(recvTs)?.kind === "classval") {
5910
6048
  return lowerStaticMethodCall(lowerer, call, access);
5911
6049
  }
@@ -5965,6 +6103,9 @@ function lowerDynReceiverMethodCall(lowerer, call, access) {
5965
6103
  // with the original receiver rather than a materialized property view.
5966
6104
  const callbackProperty = (() => {
5967
6105
  const prop = lowerer.checker.getPropertyOfType(recvTs, access.name.text);
6106
+ const mapped = lowerer.mapTypeOf(recvTs);
6107
+ if (mapped?.kind === "union" && lowerer.unions.get(mapped.unionId)?.arms.every((arm) => arm.kind === "object" && lowerer.classes.get(arm.className)?.fields.has(access.name.text)))
6108
+ return true;
5968
6109
  return prop !== undefined && lowerer.checker.declarationsOf(prop).some((decl) => ts.isPropertyDeclaration(decl) || ts.isPropertySignature(decl));
5969
6110
  })();
5970
6111
  const dispatched = callbackProperty ? null : lowerDynDispatchMethodCall(lowerer, call, access, recv, arrayReceiver);
@@ -6002,18 +6143,24 @@ function lowerDynReceiverMethodCall(lowerer, call, access) {
6002
6143
  type: DYN,
6003
6144
  loc: locOf(access),
6004
6145
  };
6146
+ const calleeLocal = lowerer.declareHiddenLocal("%dynCallCallee", DYN);
6147
+ const callee = varRef(calleeLocal.id, DYN, loc);
6148
+ const stmts = [
6149
+ { kind: "varDecl", localId: receiverLocal.id, init: recv, loc },
6150
+ { kind: "varDecl", localId: calleeLocal.id, init: member, loc },
6151
+ ];
6005
6152
  if (call.arguments.some(ts.isSpreadElement)) {
6006
- const spread = lowerSpreadArgsCall(lowerer, call, member, loc);
6153
+ const spread = lowerSpreadArgsCall(lowerer, call, callee, loc);
6007
6154
  if (spread?.kind === "dynCall")
6008
6155
  return { kind: "seqExpr",
6009
- stmts: [{ kind: "varDecl", localId: receiverLocal.id, init: recv, loc }],
6156
+ stmts,
6010
6157
  result: { ...spread, receiver }, type: DYN, loc };
6011
6158
  lowerer.unsupported("SC1090", call, "spread arguments in calls through 'unknown' values");
6012
6159
  }
6013
6160
  const args = call.arguments.map((a) => lowerer.lowerExprExpecting(a, DYN));
6014
6161
  return {
6015
- kind: "seqExpr", stmts: [{ kind: "varDecl", localId: receiverLocal.id, init: recv, loc }],
6016
- result: { kind: "dynCall", callee: member, receiver, calleeName: access.getText(), args, type: DYN, loc }, type: DYN, loc,
6162
+ kind: "seqExpr", stmts,
6163
+ result: { kind: "dynCall", callee, receiver, calleeName: access.getText(), args, type: DYN, loc }, type: DYN, loc,
6017
6164
  };
6018
6165
  }
6019
6166
  /** Prototype method names of the checked-dynamic tree-representable kinds (String, Array,
@@ -6041,6 +6188,7 @@ const DYN_PROTO_METHOD_NAMES = new Set([
6041
6188
  * is-not-a-function where the kind's prototype lacks the name, and
6042
6189
  * fences LOUDLY on real-but-unimplemented pairs. */
6043
6190
  const DYN_DISPATCH_METHODS = new Set([
6191
+ "toFixed", "toExponential",
6044
6192
  "segment", "containing", "resolvedOptions", "test", "exec",
6045
6193
  "apply", "bind", "call",
6046
6194
  "push", "pop", "shift", "unshift", "slice", "at",
@@ -6539,6 +6687,15 @@ function lowerPrimitiveProtoCall(lowerer, call, recv, name, memberSym) {
6539
6687
  if (!lowerer.isStdlibSymbol(memberSym))
6540
6688
  return null;
6541
6689
  const recvKind = lowerer.mapTypeOf(lowerer.typeOf(recv))?.kind;
6690
+ if ((name === "toFixed" || name === "toExponential") && call.arguments.length <= 1 &&
6691
+ !call.arguments.some(ts.isSpreadElement) && isJsSourceFile(call.getSourceFile())) {
6692
+ const operand = lowerer.lowerExpr(recv);
6693
+ if (operand.type.kind === "dyn" || operand.type.kind === "union" && lowerer.dynConvertible(operand.type))
6694
+ return {
6695
+ kind: "dynInvoke", recv: lowerer.coerceToExpected(operand, DYN), method: name, calleeName: `${recv.getText()}.${name}`,
6696
+ args: call.arguments.map((argument) => lowerer.lowerExprExpecting(argument, DYN)), type: DYN, loc: locOf(call),
6697
+ };
6698
+ }
6542
6699
  if (recvKind !== "f64" && recvKind !== "bool" && recvKind !== "string")
6543
6700
  return null;
6544
6701
  const loc = locOf(call);
@@ -7019,6 +7176,17 @@ export function lowerNestedFunctionDecl(lowerer, stmt) {
7019
7176
  throw new InternalCompilerError("lowerer bug: nested function declaration has no active statement list");
7020
7177
  }
7021
7178
  const init = lowerer.lowerLambda(stmt);
7179
+ // Body inference may settle a JS return differently from the initial
7180
+ // checker signature. Publish that same ABI on the hoisted binding and
7181
+ // every mutable capture of it before callers are lowered.
7182
+ if (init.type.kind === "func" && !typeEquals(local.type, init.type)) {
7183
+ local.type = init.type;
7184
+ for (const fn of lowerer.liftedFns)
7185
+ for (const capture of fn.captures ?? []) {
7186
+ if (capture.localId === local.id)
7187
+ capture.type = init.type;
7188
+ }
7189
+ }
7022
7190
  return { kind: "assign", localId: local.id, value: init, loc: locOf(stmt) };
7023
7191
  }
7024
7192
  /** Signature checks + param shapes + IR func type for any lambda-like
@@ -7183,6 +7351,13 @@ export function lowerLambda(lowerer, node) {
7183
7351
  const signature = lowerer.lambdaSignature(node);
7184
7352
  const { shapes } = signature;
7185
7353
  let { funcType } = signature;
7354
+ // Reflected JS methods receive the caller's actual `this`, which may
7355
+ // be a Proxy or a descriptor-created object rather than the class.
7356
+ // Fluent methods must return that receiver without a class cast.
7357
+ if (ts.isMethodDeclaration(node) && isJsSourceFile(node.getSourceFile()) &&
7358
+ !node.type && !node.asteriskToken && !hasExplicitJsDocReturn(node) && returnsOnlyThis(node)) {
7359
+ funcType = { ...funcType, ret: funcType.ret.kind === "promise" ? { ...funcType.ret, inner: DYN } : DYN };
7360
+ }
7186
7361
  // A lambda IS a value: the completed-ABI rule applies at birth. The
7187
7362
  // contextual (target) type decides — `(x?: number) => void` may flow
7188
7363
  // into a slot annotated `(x: number | undefined) => void` (same ABI
@@ -7792,7 +7967,7 @@ export function lowerPromiseMethodCall(lowerer, call, access) {
7792
7967
  value: {
7793
7968
  kind: "dynCall",
7794
7969
  callee: { kind: "varRef", localId: cbLocal.id, type: DYN, loc },
7795
- calleeName: jsFuncNameOf(call.arguments[0]) ?? "onFulfilled",
7970
+ calleeName: jsFuncNameOf(call.arguments[0], lowerer) ?? "onFulfilled",
7796
7971
  args: handlerArgs,
7797
7972
  type: DYN,
7798
7973
  loc,
@@ -7825,7 +8000,8 @@ export function lowerPromiseMethodCall(lowerer, call, access) {
7825
8000
  lowerer.unsupported("SC1090", call.arguments[0], "then handlers with more than one parameter (the two-argument onRejected form has no lowering — chain .catch(...) instead)");
7826
8001
  }
7827
8002
  const param = cb.type.params[0];
7828
- if (param !== undefined && !typeEquals(param, inner) && !(inner.kind === "void" && param.kind === "dyn")) {
8003
+ if (param !== undefined && !typeEquals(param, inner) &&
8004
+ !(param.kind === "dyn" && (inner.kind === "void" || lowerer.dynConvertible(inner)))) {
7829
8005
  lowerer.unsupported("SC1090", call.arguments[0], `then handlers whose parameter is not the settled value's type (expected '${lowerer.fmt(inner)}', got '${lowerer.fmt(param)}')`);
7830
8006
  }
7831
8007
  const resultT = lowerer.mapTypeOf(lowerer.typeOf(call));
@@ -7855,7 +8031,7 @@ export function lowerPromiseMethodCall(lowerer, call, access) {
7855
8031
  if (param !== undefined && inner.kind !== "void") {
7856
8032
  const vLocal = lowerer.declareHiddenLocal("v", inner);
7857
8033
  body.push({ kind: "varDecl", localId: vLocal.id, init: awaitE, loc });
7858
- handlerArgs = [{ kind: "varRef", localId: vLocal.id, type: inner, loc }];
8034
+ handlerArgs = [lowerer.coerceToExpected({ kind: "varRef", localId: vLocal.id, type: inner, loc }, param)];
7859
8035
  }
7860
8036
  else {
7861
8037
  body.push({ kind: "exprStmt", expr: awaitE, loc });
@@ -7916,6 +8092,14 @@ export function lowerPromiseMethodCall(lowerer, call, access) {
7916
8092
  }
7917
8093
  if (member === "finally") {
7918
8094
  const cb = lowerer.lowerExpr(call.arguments[0]);
8095
+ if (lowerer.dynConvertible(promT) && lowerer.dynConvertible(cb.type)) {
8096
+ const result = {
8097
+ kind: "dynInvoke", recv: lowerer.coerceToExpected(receiver, DYN),
8098
+ method: "finally", calleeName: access.getText(),
8099
+ args: [lowerer.coerceToExpected(cb, DYN)], type: DYN, loc,
8100
+ };
8101
+ return lowerer.coerceToExpected(result, promT);
8102
+ }
7919
8103
  if (cb.type.kind !== "func" || cb.type.params.length !== 0 || cb.type.ret.kind !== "void") {
7920
8104
  lowerer.unsupported("SC1090", call.arguments[0], "finally callbacks with parameters or a return value (use () => { ... })");
7921
8105
  }
@@ -7992,7 +8176,8 @@ export function lowerPromiseMethodCall(lowerer, call, access) {
7992
8176
  // receiver, passes fulfillments through as dyn, and on rejection
7993
8177
  // calls the boxed handler with caughtToDyn's identity-preserving
7994
8178
  // snapshot (the dyn-then desugar's catch twin).
7995
- if (member === "catch" && inner.kind === "dyn") {
8179
+ if (member === "catch" && (inner.kind === "dyn" || isJsSourceFile(call.getSourceFile()) &&
8180
+ (inner.kind === "void" || lowerer.dynConvertible(inner)))) {
7996
8181
  let cb = lowerer.lowerExpr(call.arguments[0]);
7997
8182
  if (cb.type.kind === "func" &&
7998
8183
  canBoxFuncIntoDyn(cb.type, (id) => lowerer.shapes.get(id), (id) => lowerer.unions.get(id))) {
@@ -8009,20 +8194,11 @@ export function lowerPromiseMethodCall(lowerer, call, access) {
8009
8194
  const pLocal = lowerer.declareHiddenLocal("p", promT);
8010
8195
  const cbLocal = lowerer.declareHiddenLocal("cb", DYN);
8011
8196
  const eLocal = lowerer.declareHiddenLocal("e", CAUGHT);
8012
- const vLocal = lowerer.declareHiddenLocal("v", DYN);
8197
+ const vLocal = inner.kind === "void" ? null : lowerer.declareHiddenLocal("v", inner);
8198
+ const awaited = { kind: "awaitExpr", value: { kind: "varRef", localId: pLocal.id, type: promT, loc }, type: inner, loc };
8013
8199
  const tryBody = [
8014
- {
8015
- kind: "varDecl",
8016
- localId: vLocal.id,
8017
- init: {
8018
- kind: "awaitExpr",
8019
- value: { kind: "varRef", localId: pLocal.id, type: promT, loc },
8020
- type: DYN,
8021
- loc,
8022
- },
8023
- loc,
8024
- },
8025
- { kind: "return", value: { kind: "varRef", localId: vLocal.id, type: DYN, loc }, loc },
8200
+ vLocal ? { kind: "varDecl", localId: vLocal.id, init: awaited, loc } : { kind: "exprStmt", expr: awaited, loc },
8201
+ { kind: "return", value: vLocal ? lowerer.coerceToExpected(varRef(vLocal.id, inner, loc), DYN) : dynUndefinedExpr(loc), loc },
8026
8202
  ];
8027
8203
  const catchBody = [
8028
8204
  {
@@ -8030,7 +8206,7 @@ export function lowerPromiseMethodCall(lowerer, call, access) {
8030
8206
  value: {
8031
8207
  kind: "dynCall",
8032
8208
  callee: { kind: "varRef", localId: cbLocal.id, type: DYN, loc },
8033
- calleeName: jsFuncNameOf(call.arguments[0]) ?? "onRejected",
8209
+ calleeName: jsFuncNameOf(call.arguments[0], lowerer) ?? "onRejected",
8034
8210
  args: [
8035
8211
  {
8036
8212
  kind: "caughtToDyn",
@@ -8833,7 +9009,7 @@ function lowerObjectStaticCall(lowerer, call, access) {
8833
9009
  !call.arguments.some(ts.isSpreadElement) && !lowerer.dynamic) {
8834
9010
  let object = lowerer.lowerExpr(call.arguments[0]);
8835
9011
  if (object.type.kind === "func") {
8836
- const fnName = jsFuncNameOf(call.arguments[0]);
9012
+ const fnName = jsFuncNameOf(call.arguments[0], lowerer);
8837
9013
  object = { kind: "dynFrom", value: object, type: DYN, loc: locOf(call),
8838
9014
  ...(fnName !== null ? { fnName } : {}) };
8839
9015
  }
@@ -8841,8 +9017,15 @@ function lowerObjectStaticCall(lowerer, call, access) {
8841
9017
  object = lowerer.coerceInto(call.arguments[0], object, DYN);
8842
9018
  if (object.type.kind === "dyn") {
8843
9019
  const loc = locOf(call);
8844
- if (member === "getPrototypeOf")
8845
- return { kind: "libCall", fn: "dyn.getPrototype", args: [object], type: DYN, loc };
9020
+ if (member === "getPrototypeOf") {
9021
+ let parent = call.parent;
9022
+ while (parent && ts.isParenthesizedExpression(parent))
9023
+ parent = parent.parent;
9024
+ const identityOnly = !!parent && ts.isBinaryExpression(parent) &&
9025
+ [ts.SyntaxKind.EqualsEqualsEqualsToken, ts.SyntaxKind.ExclamationEqualsEqualsToken].includes(parent.operatorToken.kind);
9026
+ return { kind: "libCall", fn: "dyn.getPrototype", args: [object], type: DYN, loc,
9027
+ ...(identityOnly ? { prototypeIdentityOnly: true } : {}) };
9028
+ }
8846
9029
  const prototype = lowerer.lowerExprExpecting(call.arguments[1], DYN);
8847
9030
  if (prototype.type.kind === "dyn")
8848
9031
  return { kind: "libCall", fn: "dyn.setPrototype", args: [object, prototype], type: DYN, loc };
@@ -9024,7 +9207,7 @@ function lowerObjectStaticCall(lowerer, call, access) {
9024
9207
  if (isJsSourceFile(call.getSourceFile()) && call.arguments.length > 0 && !call.arguments.some(ts.isSpreadElement)) {
9025
9208
  const target = tryLowerExpression(lowerer, call.arguments[0]);
9026
9209
  if (target && ((target.type.kind === "func" && lowerer.dynConvertible(target.type)) ||
9027
- (target.type.kind === "object" && lowerer.errorHierarchyClassOf(target.type.className)))) {
9210
+ (target.type.kind === "object" && lowerer.dynConvertible(target.type)))) {
9028
9211
  const loc = locOf(call);
9029
9212
  return { kind: "libCall", fn: "dyn.assignAll", args: [lowerer.coerceToExpected(target, DYN),
9030
9213
  { kind: "dynArrLit", elems: call.arguments.slice(1).map((arg) => lowerer.lowerExprExpecting(arg, DYN)), type: DYN, loc },
@@ -9226,7 +9409,7 @@ function lowerObjectStaticCall(lowerer, call, access) {
9226
9409
  target = lowerer.coerceToExpected(target, DYN);
9227
9410
  }
9228
9411
  if (target?.type.kind === "func" && lowerer.dynConvertible(target.type)) {
9229
- const fnName = jsFuncNameOf(call.arguments[0]);
9412
+ const fnName = jsFuncNameOf(call.arguments[0], lowerer);
9230
9413
  target = { kind: "dynFrom", value: target, type: DYN, loc: locOf(call.arguments[0]), ...(fnName !== null ? { fnName } : {}) };
9231
9414
  }
9232
9415
  const descriptorPrimitive = member === "getOwnPropertyDescriptor" && target &&
@@ -9255,6 +9438,9 @@ function lowerObjectStaticCall(lowerer, call, access) {
9255
9438
  // static shapes have no property table to extend.
9256
9439
  if (member === "defineProperties" && call.arguments.length === 2 &&
9257
9440
  !call.arguments.some((a) => ts.isSpreadElement(a))) {
9441
+ const prototype = lowerClassPrototypeDescriptors(lowerer, call, member);
9442
+ if (prototype)
9443
+ return prototype;
9258
9444
  let target = tryLowerExpression(lowerer, call.arguments[0]);
9259
9445
  if (target) {
9260
9446
  const native = lowerClassDataDescriptor(lowerer, call, member, target);
@@ -9267,7 +9453,7 @@ function lowerObjectStaticCall(lowerer, call, access) {
9267
9453
  // wrapper returned later reads the same table.
9268
9454
  if (target && target.type.kind === "func" &&
9269
9455
  canBoxFuncIntoDyn(target.type, (id) => lowerer.shapes.get(id), (id) => lowerer.unions.get(id))) {
9270
- const fnName = jsFuncNameOf(call.arguments[0]);
9456
+ const fnName = jsFuncNameOf(call.arguments[0], lowerer);
9271
9457
  target = { kind: "dynFrom", value: target, type: DYN, loc: locOf(call.arguments[0]), ...(fnName !== null ? { fnName } : {}) };
9272
9458
  }
9273
9459
  if (target && isUnitType(target.type)) {
@@ -9322,7 +9508,8 @@ function lowerObjectStaticCall(lowerer, call, access) {
9322
9508
  let inner = argNode;
9323
9509
  while (ts.isParenthesizedExpression(inner) || ts.isAsExpression(inner))
9324
9510
  inner = inner.expression;
9325
- const value = lowerer.lowerExpr(argNode);
9511
+ const value = isJsSourceFile(call.getSourceFile()) && (ts.isObjectLiteralExpression(inner) || ts.isArrayLiteralExpression(inner))
9512
+ ? lowerer.lowerExprExpecting(argNode, DYN) : lowerer.lowerExpr(argNode);
9326
9513
  if (value.type.kind === "dyn")
9327
9514
  return { kind: "libCall", fn: "dyn.freeze", args: [value], type: DYN, loc: locOf(call) };
9328
9515
  if (ts.isObjectLiteralExpression(inner) || ts.isArrayLiteralExpression(inner)) {
@@ -9458,14 +9645,11 @@ function lowerObjectStaticCall(lowerer, call, access) {
9458
9645
  }
9459
9646
  }
9460
9647
  let argIr = lowerer.mapTypeOf(lowerer.typeOf(argNode));
9461
- // JS: an unmappable CHECKER type over a value that lowered to a real
9462
- // record (the narrowed export-table literal) — the lowered value's
9463
- // shape is the honest dispatch key, exactly the identity-Set stance.
9464
- if (argIr === null && isJsSourceFile(argNode.getSourceFile())) {
9465
- const probed = tryLowerExpression(lowerer, argNode);
9466
- if (probed?.type.kind === "record")
9467
- argIr = probed.type;
9468
- }
9648
+ // Inferred records can carry runtime-optional index values that the
9649
+ // checker omits (named regex captures). Enumerate the represented shape.
9650
+ const represented = tryLowerExpression(lowerer, argNode);
9651
+ if (represented?.type.kind === "record")
9652
+ argIr = represented.type;
9469
9653
  if (argIr?.kind !== "record")
9470
9654
  return null; // Maps, classes, arrays → the SC2020 fence
9471
9655
  const shape = lowerer.shapes.get(argIr.shapeId);
@@ -9857,6 +10041,13 @@ export function lowerRecordFieldCall(lowerer, call, access) {
9857
10041
  return { kind: "callValue", callee, args, type: callee.type.ret, loc: locOf(call) };
9858
10042
  }
9859
10043
  const target = lowerer.fieldTarget(access);
10044
+ const loc = locOf(call);
10045
+ const receiverLocal = target && isJsSourceFile(call.getSourceFile())
10046
+ ? lowerer.declareHiddenLocal("%callReceiver", target.obj.type) : null;
10047
+ const init = target && receiverLocal
10048
+ ? { kind: "varDecl", localId: receiverLocal.id, init: target.obj, loc } : null;
10049
+ if (target && receiverLocal)
10050
+ target.obj = varRef(receiverLocal.id, receiverLocal.type, loc);
9860
10051
  let callee = target ? lowerer.fieldGetExpr(target, locOf(access), access) : null;
9861
10052
  if (!callee)
9862
10053
  return null;
@@ -9865,10 +10056,24 @@ export function lowerRecordFieldCall(lowerer, call, access) {
9865
10056
  // `.bold` (the chalk shape).
9866
10057
  if (callee.type.kind === "record")
9867
10058
  callee = lowerer.hybridCallUnwrap(callee);
10059
+ // Inferred JavaScript option records can retain checked callables in
10060
+ // fields even when the checker only describes those members as any.
10061
+ if (callee.type.kind === "dyn" && isJsSourceFile(call.getSourceFile())) {
10062
+ const receiver = target && receiverLocal ? lowerer.coerceToExpected(target.obj, DYN) : undefined;
10063
+ const spread = call.arguments.some(ts.isSpreadElement) ? lowerSpreadArgsCall(lowerer, call, callee, loc) : null;
10064
+ if (spread && spread.kind !== "dynCall")
10065
+ lowerer.unsupported("SC1090", call, "checked record property call spread arguments");
10066
+ const result = spread ? (receiver ? { ...spread, receiver } : spread) : {
10067
+ kind: "dynCall", callee, ...(receiver ? { receiver } : {}), calleeName: access.getText(),
10068
+ args: call.arguments.map((arg) => lowerer.lowerExprExpecting(arg, DYN)), type: DYN, loc,
10069
+ };
10070
+ return init ? { kind: "seqExpr", stmts: [init], result, type: result.type, loc } : result;
10071
+ }
9868
10072
  if (callee.type.kind !== "func")
9869
10073
  lowerer.badType(access, lowerer.typeOf(access));
9870
10074
  const args = completeFuncValueArgs(lowerer, call, callee.type, locOf(call));
9871
- return { kind: "callValue", callee, args, type: callee.type.ret, loc: locOf(call) };
10075
+ const result = { kind: "callValue", callee, ...(target && receiverLocal ? { receiver: lowerer.coerceToExpected(target.obj, DYN) } : {}), args, type: callee.type.ret, loc };
10076
+ return init ? { kind: "seqExpr", stmts: [init], result, type: result.type, loc } : result;
9872
10077
  }
9873
10078
  /** The function-like node behind an object-literal generic-method member:
9874
10079
  * the MethodDeclaration itself (`{ m<T>(x: T) {...} }`) or a generic
@@ -10526,7 +10731,8 @@ export function requireObjLitGenericReceiver(lowerer, blame, recvExpr, literal,
10526
10731
  * direct `call` of the instance with the receiver unevaluated. Claims
10527
10732
  * every call whose member is generic-callable — lowering it or fencing
10528
10733
  * with a named message. */
10529
- /** URL.revokeObjectURL() with NO argument: Node's ERR_MISSING_ARGS
10734
+ /** The URL factories use the constructor's conversion order and parser.
10735
+ * URL.revokeObjectURL() with NO argument: Node's ERR_MISSING_ARGS
10530
10736
  * throws before the registry lookup, so the zero-argument contract is
10531
10737
  * exact without any blob machinery. The one-argument form (Node's
10532
10738
  * silent no-op for unregistered ids) and createObjectURL keep their
@@ -10534,12 +10740,15 @@ export function requireObjLitGenericReceiver(lowerer, blame, recvExpr, literal,
10534
10740
  function lowerUrlStaticCall(lowerer, call, callee) {
10535
10741
  if (!ts.isPropertyAccessExpression(callee) || callee.questionDotToken !== undefined)
10536
10742
  return null;
10537
- if (!ts.isIdentifier(callee.expression) || callee.expression.text !== "URL")
10538
- return null;
10539
- if (callee.name.text !== "revokeObjectURL" || call.arguments.length !== 0)
10743
+ if (!ts.isIdentifier(callee.expression))
10540
10744
  return null;
10541
10745
  const sym = lowerer.resolveValueSymbol(callee.expression);
10542
- if (!sym || !lowerer.isStdlibSymbol(sym))
10746
+ if (!sym || !lowerer.isStdlibSymbol(sym) || sym.name !== "URL")
10747
+ return null;
10748
+ if (callee.name.text === "canParse" || callee.name.text === "parse") {
10749
+ return lowerUrlFactory(lowerer, call, callee.name.text);
10750
+ }
10751
+ if (callee.name.text !== "revokeObjectURL" || call.arguments.length !== 0)
10543
10752
  return null;
10544
10753
  return nodeThrowExpr(1, "ERR_MISSING_ARGS", 'The "url" argument must be specified', VOID, locOf(call));
10545
10754
  }
@@ -10820,9 +11029,11 @@ export function lowerObjectMethodCall(lowerer, call, access) {
10820
11029
  // type still belongs to the unspecialized body (plane.normal.dot()).
10821
11030
  // Use the lowered receiver's representation and retain its evaluation.
10822
11031
  const probe = mappedReceiver?.kind !== "object" && (lowerer.implicitParamTypes !== null || ts.isNewExpression(access.expression) ||
10823
- isJsSourceFile(call.getSourceFile()) && ts.isCallExpression(access.expression))
10824
- ? tryLowerExpression(lowerer, access.expression) : null;
10825
- const specializedReceiver = probe?.type.kind === "object" ? probe : null;
11032
+ isJsSourceFile(call.getSourceFile()))
11033
+ ? ts.isNewExpression(access.expression) ? lowerer.lowerExpr(access.expression) : tryLowerExpression(lowerer, access.expression)
11034
+ : null;
11035
+ const specializedReceiver = probe?.type.kind === "object" || probe?.type.kind === "union" &&
11036
+ lowerer.unions.get(probe.type.unionId)?.arms.some((arm) => arm.kind === "object") ? probe : null;
10826
11037
  if (specializedReceiver)
10827
11038
  mappedReceiver = specializedReceiver.type;
10828
11039
  if (mappedReceiver?.kind === "union") {
@@ -10833,8 +11044,7 @@ export function lowerObjectMethodCall(lowerer, call, access) {
10833
11044
  const arms = lowerer.unions.get(mappedReceiver.unionId)?.arms;
10834
11045
  if (isJsSourceFile(call.getSourceFile()) && arms?.every((arm) => arm.kind === "object" &&
10835
11046
  !lowerer.classes.get(arm.className)?.def.runtime) && lowerer.dynConvertible(mappedReceiver)) {
10836
- const receiver = lowerer.coerceInto(access.expression, lowerer.lowerExpr(access.expression), DYN);
10837
- return lowerDynDispatchMethodCall(lowerer, call, access, receiver, false);
11047
+ return lowerDynReceiverMethodCall(lowerer, call, access);
10838
11048
  }
10839
11049
  }
10840
11050
  const receiverIr = mappedReceiver?.kind === "object"
@@ -10982,7 +11192,7 @@ export function lowerObjectMethodCall(lowerer, call, access) {
10982
11192
  const fallback = virtual
10983
11193
  ? { kind: "virtualCall", className: info.def.name, method, args: [lowerer.upcastTo(value, info.def.name), ...args], type: found.sig.ret, loc }
10984
11194
  : { kind: "call", callee: `%${found.declarer.def.name}.${method}`, args: [lowerer.upcastTo(value, found.declarer.def.name), ...args], type: found.sig.ret, loc };
10985
- const result = classCallbackCall(lowerer, value, method, call.arguments.map((arg) => lowerer.lowerExprExpecting(arg, DYN)), fallback, loc);
11195
+ const result = classCallbackCall(lowerer, value, method, call.arguments.map((arg) => lowerer.lowerExprExpecting(arg, DYN)), fallback, loc, access.getText());
10986
11196
  return { kind: "seqExpr", stmts: [{ kind: "varDecl", localId: local.id, init: receiver, loc }], result, type: result.type, loc };
10987
11197
  }
10988
11198
  if (found.declarer.builtinError) {