@scriptc/compiler 0.1.4 → 0.1.6

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 (158) hide show
  1. package/ambient/scriptc-node-fallback.d.ts +75 -11
  2. package/dist/backend/c/c-emitter.d.ts +5 -1
  3. package/dist/backend/c/c-emitter.js +22 -9
  4. package/dist/backend/c/c-emitter.js.map +1 -1
  5. package/dist/backend/c/exprs.d.ts +2 -2
  6. package/dist/backend/c/exprs.js +298 -27
  7. package/dist/backend/c/exprs.js.map +1 -1
  8. package/dist/backend/c/stmts.js +5 -3
  9. package/dist/backend/c/stmts.js.map +1 -1
  10. package/dist/backend/c/types.d.ts +2 -3
  11. package/dist/backend/c/types.js +8 -1
  12. package/dist/backend/c/types.js.map +1 -1
  13. package/dist/backend/c/walkers.js +1 -1
  14. package/dist/backend/c/walkers.js.map +1 -1
  15. package/dist/backend/link-plan.d.ts +1 -0
  16. package/dist/backend/link-plan.js +2 -1
  17. package/dist/backend/link-plan.js.map +1 -1
  18. package/dist/backend/llvm/dyn.js +1 -1
  19. package/dist/backend/llvm/dyn.js.map +1 -1
  20. package/dist/backend/llvm/emitter.js +25 -9
  21. package/dist/backend/llvm/emitter.js.map +1 -1
  22. package/dist/backend/llvm/expr-bytes.d.ts +1 -2
  23. package/dist/backend/llvm/expr-bytes.js +22 -17
  24. package/dist/backend/llvm/expr-bytes.js.map +1 -1
  25. package/dist/backend/llvm/expr-containers.d.ts +1 -0
  26. package/dist/backend/llvm/expr-containers.js +58 -4
  27. package/dist/backend/llvm/expr-containers.js.map +1 -1
  28. package/dist/backend/llvm/expr-context.d.ts +6 -2
  29. package/dist/backend/llvm/expr-primitives.js +22 -7
  30. package/dist/backend/llvm/expr-primitives.js.map +1 -1
  31. package/dist/backend/llvm/lib-filesystem.js +20 -0
  32. package/dist/backend/llvm/lib-filesystem.js.map +1 -1
  33. package/dist/backend/llvm/lib-network.js +53 -10
  34. package/dist/backend/llvm/lib-network.js.map +1 -1
  35. package/dist/backend/llvm/lib-process.js +4 -3
  36. package/dist/backend/llvm/lib-process.js.map +1 -1
  37. package/dist/backend/llvm/lib-shared.js +65 -0
  38. package/dist/backend/llvm/lib-shared.js.map +1 -1
  39. package/dist/backend/native-toolchain.d.ts +2 -0
  40. package/dist/backend/native-toolchain.js +2 -1
  41. package/dist/backend/native-toolchain.js.map +1 -1
  42. package/dist/backend/targets.d.ts +3 -0
  43. package/dist/backend/targets.js +13 -0
  44. package/dist/backend/targets.js.map +1 -1
  45. package/dist/coverage/surface-manifest.js +12 -5
  46. package/dist/coverage/surface-manifest.js.map +1 -1
  47. package/dist/executable/executable-cache.d.ts +2 -0
  48. package/dist/executable/executable-cache.js +3 -1
  49. package/dist/executable/executable-cache.js.map +1 -1
  50. package/dist/executable/executable-cache.test.js +3 -3
  51. package/dist/executable/executable-cache.test.js.map +1 -1
  52. package/dist/frontend/lowering/array-indexed-mutation.d.ts +8 -0
  53. package/dist/frontend/lowering/array-indexed-mutation.js +176 -0
  54. package/dist/frontend/lowering/array-indexed-mutation.js.map +1 -0
  55. package/dist/frontend/lowering/containers/bytes.d.ts +1 -1
  56. package/dist/frontend/lowering/containers/bytes.js +6 -5
  57. package/dist/frontend/lowering/containers/bytes.js.map +1 -1
  58. package/dist/frontend/lowering/containers/string-and-regexp.d.ts +14 -6
  59. package/dist/frontend/lowering/containers/string-and-regexp.js +420 -34
  60. package/dist/frontend/lowering/containers/string-and-regexp.js.map +1 -1
  61. package/dist/frontend/lowering/expressions/object-literals.js +8 -0
  62. package/dist/frontend/lowering/expressions/object-literals.js.map +1 -1
  63. package/dist/frontend/lowering/indexed-comparison.d.ts +6 -0
  64. package/dist/frontend/lowering/indexed-comparison.js +30 -0
  65. package/dist/frontend/lowering/indexed-comparison.js.map +1 -0
  66. package/dist/frontend/lowering/lower-builtins.d.ts +5 -6
  67. package/dist/frontend/lowering/lower-builtins.js +24 -16
  68. package/dist/frontend/lowering/lower-builtins.js.map +1 -1
  69. package/dist/frontend/lowering/lower-calls.d.ts +3 -2
  70. package/dist/frontend/lowering/lower-calls.js +522 -89
  71. package/dist/frontend/lowering/lower-calls.js.map +1 -1
  72. package/dist/frontend/lowering/lower-classes.js +5 -29
  73. package/dist/frontend/lowering/lower-classes.js.map +1 -1
  74. package/dist/frontend/lowering/lower-containers.d.ts +15 -11
  75. package/dist/frontend/lowering/lower-containers.js +588 -146
  76. package/dist/frontend/lowering/lower-containers.js.map +1 -1
  77. package/dist/frontend/lowering/lower-exprs.d.ts +12 -0
  78. package/dist/frontend/lowering/lower-exprs.js +145 -11
  79. package/dist/frontend/lowering/lower-exprs.js.map +1 -1
  80. package/dist/frontend/lowering/lower-island.js +17 -6
  81. package/dist/frontend/lowering/lower-island.js.map +1 -1
  82. package/dist/frontend/lowering/lower-server.d.ts +2 -4
  83. package/dist/frontend/lowering/lower-server.js +288 -32
  84. package/dist/frontend/lowering/lower-server.js.map +1 -1
  85. package/dist/frontend/lowering/lower-stmts.js +4 -4
  86. package/dist/frontend/lowering/lower-stmts.js.map +1 -1
  87. package/dist/frontend/lowering/lowerer.js +37 -2
  88. package/dist/frontend/lowering/lowerer.js.map +1 -1
  89. package/dist/frontend/lowering/optional-arguments.d.ts +9 -1
  90. package/dist/frontend/lowering/optional-arguments.js +67 -1
  91. package/dist/frontend/lowering/optional-arguments.js.map +1 -1
  92. package/dist/frontend/lowering/pairs-snapshot.d.ts +3 -1
  93. package/dist/frontend/lowering/pairs-snapshot.js +12 -4
  94. package/dist/frontend/lowering/pairs-snapshot.js.map +1 -1
  95. package/dist/frontend/lowering/surfaces.d.ts +10 -14
  96. package/dist/frontend/lowering/surfaces.js +50 -28
  97. package/dist/frontend/lowering/surfaces.js.map +1 -1
  98. package/dist/frontend/npm-static-prune.d.ts +6 -0
  99. package/dist/frontend/npm-static-prune.js +203 -0
  100. package/dist/frontend/npm-static-prune.js.map +1 -0
  101. package/dist/frontend/npm-static-prune.test.d.ts +1 -0
  102. package/dist/frontend/npm-static-prune.test.js +48 -0
  103. package/dist/frontend/npm-static-prune.test.js.map +1 -0
  104. package/dist/frontend/npm.js +7 -6
  105. package/dist/frontend/npm.js.map +1 -1
  106. package/dist/frontend/program.js +15 -4
  107. package/dist/frontend/program.js.map +1 -1
  108. package/dist/frontend/provenance.js +8 -3
  109. package/dist/frontend/provenance.js.map +1 -1
  110. package/dist/frontend/type-mapper.js +16 -5
  111. package/dist/frontend/type-mapper.js.map +1 -1
  112. package/dist/index.d.ts +3 -3
  113. package/dist/index.js +13 -41
  114. package/dist/index.js.map +1 -1
  115. package/dist/ir/analysis.d.ts +5 -3
  116. package/dist/ir/analysis.js +25 -11
  117. package/dist/ir/analysis.js.map +1 -1
  118. package/dist/ir/analysis.test.js +50 -2
  119. package/dist/ir/analysis.test.js.map +1 -1
  120. package/dist/ir/constant-tables.d.ts +17 -0
  121. package/dist/ir/constant-tables.js +99 -0
  122. package/dist/ir/constant-tables.js.map +1 -0
  123. package/dist/ir/constant-tables.test.d.ts +1 -0
  124. package/dist/ir/constant-tables.test.js +78 -0
  125. package/dist/ir/constant-tables.test.js.map +1 -0
  126. package/dist/ir/integer-ranges.d.ts +13 -0
  127. package/dist/ir/integer-ranges.js +122 -0
  128. package/dist/ir/integer-ranges.js.map +1 -0
  129. package/dist/ir/integer-ranges.test.d.ts +1 -0
  130. package/dist/ir/integer-ranges.test.js +63 -0
  131. package/dist/ir/integer-ranges.test.js.map +1 -0
  132. package/dist/ir/ir.d.ts +56 -35
  133. package/dist/ir/ir.js +9 -1
  134. package/dist/ir/ir.js.map +1 -1
  135. package/dist/ir/ir.test.js +6 -0
  136. package/dist/ir/ir.test.js.map +1 -1
  137. package/dist/ir/scalar-records.d.ts +8 -0
  138. package/dist/ir/scalar-records.js +216 -0
  139. package/dist/ir/scalar-records.js.map +1 -0
  140. package/dist/ir/scalar-records.test.d.ts +1 -0
  141. package/dist/ir/scalar-records.test.js +142 -0
  142. package/dist/ir/scalar-records.test.js.map +1 -0
  143. package/dist/ir/validate.js +135 -34
  144. package/dist/ir/validate.js.map +1 -1
  145. package/dist/ir/validate.test.d.ts +1 -0
  146. package/dist/ir/validate.test.js +48 -0
  147. package/dist/ir/validate.test.js.map +1 -0
  148. package/dist/library/cache-primitives.d.ts +1 -1
  149. package/dist/library/cache-primitives.js +3 -3
  150. package/dist/library/cache-primitives.js.map +1 -1
  151. package/dist/library/fence-eval.js +11 -4
  152. package/dist/library/fence-eval.js.map +1 -1
  153. package/dist/library/library-cache.js +0 -1
  154. package/dist/library/library-cache.js.map +1 -1
  155. package/dist/library/library-cache.test.js +3 -3
  156. package/dist/library/library-cache.test.js.map +1 -1
  157. package/package.json +17 -17
  158. package/surface-manifest.json +90 -45
@@ -6,18 +6,18 @@ import { InternalCompilerError } from "../../errors.js";
6
6
  import * as ts from "../ts7/adapter.js";
7
7
  import { lowerGenMethodCall } from "./lower-generators.js";
8
8
  import { BIGINT_T, BOOL, CAUGHT, DYN, F64, JSVAL, STRING, SYMBOL_T, UNDEFINED_T, VOID, arrayOf, canBoxFuncIntoDyn, canConvertToDyn, canDynCheckTo, canMarshalTypedFuncIntoIsland, ffiClassType, ffiSourceParamTypes, funcOf, isFfiCallbackParam, isFfiContextParam, isFfiReleaseParam, isUnitType, shapeHasAccessorSlots, typeEquals } from "../../ir/ir.js";
9
- import { isJsSourceFile, locOf } from "../program.js";
9
+ import { isJsSourceFile, isNodeEsmFile, locOf } from "../program.js";
10
10
  import { genResultRecord, isGenericCallableMemberType, typeKey } from "../type-mapper.js";
11
11
  import { PoisonError, dynFallbackType, dynUndefinedExpr, importCallHandleType, jsFuncNameOf, newFnCtx, nodeThrowExpr, staticImportNamespaceType } from "./lowerer.js";
12
12
  import { enforceLibBoundary } from "./lib-boundary.js";
13
- import { NARROW_FIRST, builtinFenceHintOf, builtinModuleFnOf } from "./surfaces.js";
13
+ import { NARROW_FIRST, STRING_INDEX_METHODS, STR_METHODS, builtinFenceHintOf, builtinModuleFnOf } from "./surfaces.js";
14
14
  import { ffiBindingDiag, ffiSignatureDiag, libCallbackDiag, requiresDynamicDiag } from "../../diagnostics/diagnostic.js";
15
15
  import { mixinFnShapeOf } from "./lower-mixins.js";
16
- import { dynStringReceiver, lowerArrayFromCall, lowerDynArrayFilterCall, lowerDynArrayFlatMapCall, lowerGroupByStaticCall, lowerIteratorHelperCall, lowerObjectAssignIndexShape, lowerObjectFromEntriesCall, lowerObjectIterOverIndexShape, lowerTupleReadMethodCall } from "./lower-containers.js";
16
+ import { dynStringReceiver, lowerArrayConstructor, lowerArrayFromCall, lowerArrayOfCall, lowerDynArrayFilterCall, lowerDynArrayFlatMapCall, lowerGroupByStaticCall, lowerIteratorHelperCall, lowerObjectAssignIndexShape, lowerObjectFromEntriesCall, lowerObjectIterOverIndexShape, lowerTupleReadMethodCall } from "./lower-containers.js";
17
17
  import { bufEncoding } from "./containers/bytes.js";
18
- import { lowerRegexMethodCall, lowerStringMethodCall } from "./containers/string-and-regexp.js";
18
+ import { lowerRegexMethodCall, lowerStringIndexCall, lowerStringMethodCall, lowerStringPaddingCall, lowerStringSplitCall } from "./containers/string-and-regexp.js";
19
19
  import { lowerChildStreamMethodCall, lowerChildWriterMethodCall, lowerCreateRequireCall, lowerCryptoHashMethodCall, lowerDirentMethodCall, lowerFileHandleMethodCall, lowerImportMetaResolveCall, lowerNodeModuleCall, lowerPerfHooksCall, lowerProcStreamMethodCall, lowerReflectApplyCall, lowerRequireResolveCall, lowerWatcherMethodCall } from "./lower-builtins.js";
20
- import { lowerAbsenceProbe, lowerPromiseAllTupleCall, lowerPromiseRejectCall, templateRawTextOf } from "./lower-exprs.js";
20
+ import { lowerAbsenceProbe, lowerPromiseAllTupleCall, lowerPromiseRejectCall, stringWrapperToString, templateRawTextOf } from "./lower-exprs.js";
21
21
  import { isSafeToDiscard } from "./expressions/evaluation-safety.js";
22
22
  import { tryLowerExpression } from "./expressions/try-lower-expression.js";
23
23
  import { httpClientFnBindingOf, isStreamUndefCallExpr, lowerCompatReqStreamOptionalCall, lowerHttpClientFnCall } from "./lower-server.js";
@@ -32,6 +32,7 @@ import { npmStaticPackageOfPath } from "../npm-static.js";
32
32
  import { countedFor, varRef } from "../../ir/build.js";
33
33
  import { rejectStaticThis } from "./static-this.js";
34
34
  import { fenceNodeModuleMutationCall, lowerRequireCacheKeys } from "./lower-node-module.js";
35
+ import { defaultAfterUndefined, lowerOptionalArgument, lowerStaticallyUndefinedArgument, positionNumber } from "./optional-arguments.js";
35
36
  /** The one func-type projection of completed parameter shapes. Typed rest
36
37
  * parameters keep their packed array as the trailing native ABI slot and
37
38
  * mark the value variadic so indirect call sites run completeArgs before
@@ -40,12 +41,14 @@ import { fenceNodeModuleMutationCall, lowerRequireCacheKeys } from "./lower-node
40
41
  export function funcTypeFromParamShapes(shapes, ret) {
41
42
  const typedRest = shapes.some((shape) => shape.mode === "rest");
42
43
  const dynRest = shapes.some((shape) => shape.mode === "dynRest");
44
+ const argumentsAll = shapes.some((shape) => shape.mode === "arguments");
43
45
  const islandRest = shapes.some((shape) => shape.mode === "islandRest");
44
46
  return {
45
47
  kind: "func",
46
- params: shapes.filter((shape) => shape.mode !== "dynRest").map((shape) => shape.type),
48
+ params: shapes.filter((shape) => shape.mode !== "dynRest" && shape.mode !== "arguments").map((shape) => shape.type),
47
49
  ret,
48
- ...(typedRest || dynRest || islandRest ? { rest: true } : {}),
50
+ ...(typedRest || dynRest || islandRest || argumentsAll ? { rest: true } : {}),
51
+ ...(argumentsAll ? { argumentsAll: true } : {}),
49
52
  ...(typedRest
50
53
  ? { restAbi: "typed" }
51
54
  : islandRest
@@ -382,8 +385,42 @@ leading) {
382
385
  });
383
386
  });
384
387
  }
385
- const restAt = shapes.findIndex((s) => s.mode === "rest" || s.mode === "dynRest" || s.mode === "islandRest");
388
+ const restAt = shapes.findIndex((s) => s.mode === "rest" || s.mode === "dynRest" || s.mode === "islandRest" || s.mode === "arguments");
386
389
  const positional = restAt >= 0 ? shapes.slice(0, restAt) : [...shapes];
390
+ if (restAt >= 0 && shapes[restAt].mode === "arguments") {
391
+ // JS `arguments` contains every supplied value, including fixed
392
+ // parameters. Store each call expression once, in source order, so
393
+ // both the native parameter slots and the full array read it.
394
+ const stmts = [];
395
+ const passed = sources.map((source, i) => {
396
+ if (!isIr(source) && ts.isSpreadElement(source)) {
397
+ lowerer.unsupported("SC1090", source, "spread arguments into fixed parameter positions (a spread can only fill a rest parameter)");
398
+ }
399
+ const slotType = i < restAt ? positional[i].type : DYN;
400
+ const value = isIr(source)
401
+ ? lowerer.coerceInto(blame, source.ir, slotType)
402
+ : lowerer.lowerExprExpecting(source, slotType);
403
+ const saved = lowerer.declareHiddenLocal("%callArg", slotType);
404
+ stmts.push({ kind: "varDecl", localId: saved.id, init: value, loc: value.loc });
405
+ return { kind: "varRef", localId: saved.id, type: slotType, loc: value.loc };
406
+ });
407
+ const fixed = positional.map((shape, i) => {
408
+ if (i < passed.length)
409
+ return passed[i];
410
+ if (shape.mode === "omittable")
411
+ return shape.callDefault ?? lowerer.undefinedArgFor(shape.type, loc, blame);
412
+ if (shape.type.kind === "dyn")
413
+ return { kind: "dynFrom", value: { kind: "unitLit", unit: "undefined", type: UNDEFINED_T, loc }, type: DYN, loc };
414
+ lowerer.unsupported("SC1090", blame, "this call form");
415
+ });
416
+ const elems = passed.map((value) => lowerer.coerceInto(blame, value, DYN));
417
+ const full = { kind: "dynArrLit", elems, type: DYN, loc };
418
+ const out = [...fixed, full];
419
+ if (stmts.length > 0) {
420
+ out[0] = { kind: "seqExpr", stmts, result: out[0], type: out[0].type, loc };
421
+ }
422
+ return out;
423
+ }
387
424
  const out = positional.map((shape, i) => {
388
425
  const src = sources[i];
389
426
  if (isIr(src))
@@ -617,6 +654,7 @@ export function requireExactArityValue(lowerer, blame, contextual, shapes, funcT
617
654
  // tier's undefined (omittedArgFor).
618
655
  if (shapes.every((s) => s.mode === "required" ||
619
656
  s.mode === "dynRest" ||
657
+ s.mode === "arguments" ||
620
658
  s.mode === "islandRest" ||
621
659
  (s.mode === "omittable" && (s.type.kind === "dyn" || s.type.kind === "jsval")))) {
622
660
  return;
@@ -915,18 +953,16 @@ export function collectSignatureInner(lowerer, decl) {
915
953
  }
916
954
  }
917
955
  const params = lowerer.paramShapes(decl.parameters);
918
- // The VARIADIC `arguments` form on a DECLARED function: same rule as
919
- // lambdas (lambdaSignature) — zero declared params, the body reads
920
- // `arguments`, a synthetic trailing dynRest shape carries the call's
921
- // arguments (completeArgs packs direct calls; the boxed thunk packs
922
- // indirect ones; lowerFunction declares the `arguments` local).
923
- if (!params.some((sh) => sh.mode === "dynRest") &&
956
+ // A declared function reading `arguments` receives a hidden dyn array.
957
+ // The zero-parameter form uses the existing rest pack; named parameters
958
+ // use the full argument pack for both direct and boxed calls.
959
+ if (!params.some((sh) => sh.mode === "rest" || sh.mode === "dynRest" || sh.mode === "islandRest") &&
924
960
  isJsSourceFile(decl.getSourceFile()) &&
925
961
  bodyReadsArguments(decl)) {
926
- if (decl.parameters.length > 0) {
927
- lowerer.unsupported("SC1090", decl, "'arguments' in functions with declared parameters (use a rest parameter: (...args))");
962
+ if (decl.parameters.length > 0 && !isNodeEsmFile(decl.getSourceFile())) {
963
+ lowerer.unsupported("SC1090", decl, "parameterized 'arguments' outside an ES module (sloppy-mode parameter aliases)");
928
964
  }
929
- params.push({ type: DYN, mode: "dynRest" });
965
+ params.push({ type: DYN, mode: decl.parameters.length > 0 ? "arguments" : "dynRest" });
930
966
  }
931
967
  const nameBlame = decl.name ?? decl;
932
968
  const returnType = lowerer.declaredReturnType(decl, nameBlame);
@@ -3339,13 +3375,12 @@ export function lowerCall(lowerer, expr) {
3339
3375
  // String(x) is exactly the template-literal ToString, Boolean(x) is
3340
3376
  // exactly the condition ToBoolean (union arms included), Number(x) is
3341
3377
  // ToNumber where it lowers exactly: numbers pass through, booleans
3342
- // become 1/0, and strings run the runtime's ECMA-exact
3343
- // StringToNumber (num.fromString — the full StringNumericLiteral
3344
- // grammar, scr_string.c). Other argument types (unions included —
3345
- // narrow first) keep the fence.
3378
+ // become 1/0, null becomes 0, and undefined becomes NaN. Strings use
3379
+ // the runtime's StringToNumber parser (num.fromString — the full
3380
+ // StringNumericLiteral grammar in scr_string.c).
3346
3381
  // Provenance-checked like setTimeout; zero-arg forms are the JS
3347
- // constants ("", false, 0). `new String(...)` (wrapper objects) stays
3348
- // on the SC2020 fence.
3382
+ // constants ("", false, 0). Wrapper objects stay fenced except where
3383
+ // the receiver is immediately converted by a direct String method call.
3349
3384
  if (ts.isIdentifier(expr.expression) &&
3350
3385
  expr.expression.text === "BigInt" &&
3351
3386
  lowerer.isStdlibSymbol(lowerer.resolveValueSymbol(expr.expression) ?? undefined)) {
@@ -3406,33 +3441,11 @@ export function lowerCall(lowerer, expr) {
3406
3441
  // bool) that a value lowering of the `&&` would fence on.
3407
3442
  if (name === "Boolean")
3408
3443
  return lowerer.lowerCondition(argNode);
3444
+ if (name === "Number")
3445
+ return lowerNumberConstructorValue(lowerer, argNode, loc);
3409
3446
  const arg = lowerer.lowerExpr(argNode);
3410
3447
  if (name === "String")
3411
3448
  return lowerer.ensureString(arg, argNode);
3412
- if (name === "Number" && arg.type.kind === "bigint") {
3413
- return { kind: "libCall", fn: "bigint.toF64", args: [arg], type: F64, loc };
3414
- }
3415
- if (arg.type.kind === "f64")
3416
- return arg;
3417
- if (arg.type.kind === "bool") {
3418
- return {
3419
- kind: "ternary",
3420
- cond: arg,
3421
- then: { kind: "numLit", value: 1, type: F64, loc },
3422
- else_: { kind: "numLit", value: 0, type: F64, loc },
3423
- type: F64,
3424
- loc,
3425
- };
3426
- }
3427
- if (arg.type.kind === "string") {
3428
- return { kind: "libCall", fn: "num.fromString", args: [arg], type: F64, loc };
3429
- }
3430
- const optionalNumber = lowerOptionalStringNumber(lowerer, arg, loc);
3431
- if (optionalNumber)
3432
- return optionalNumber;
3433
- lowerer.noLowering(`Number of ${lowerer.fmt(arg.type)} values`, argNode, arg.type.kind === "union"
3434
- ? "numbers, booleans, and strings lower (the full ToNumber string grammar included) — narrow the union first"
3435
- : undefined);
3436
3449
  }
3437
3450
  // __island_eval: the internal island testing hook (eval in the embedded
3438
3451
  // engine, String(result) back). Provenance-checked like setTimeout.
@@ -3568,6 +3581,9 @@ export function lowerCall(lowerer, expr) {
3568
3581
  // supported form is fenced here per site. Members with no lowering at
3569
3582
  // all (fs.watch, os.cpus, ...) fence with the module-qualified name.
3570
3583
  if (ts.isIdentifier(expr.expression)) {
3584
+ if (expr.expression.text === "Array" && lowerer.isStdlibGlobal(expr.expression, "Array")) {
3585
+ return lowerArrayConstructor(lowerer, expr, expr.arguments);
3586
+ }
3571
3587
  // A call through a compile-time util.promisify projection. execFile
3572
3588
  // keeps its custom result helper; ordinary builtins route through
3573
3589
  // their existing promise-module lowering.
@@ -4175,11 +4191,14 @@ export function lowerCall(lowerer, expr) {
4175
4191
  // TestContext surface (t.test/t.skip/t.diagnostic), t.assert.*.
4176
4192
  lowerer.lowerTestMethodCall(expr, expr.expression) ??
4177
4193
  lowerer.lowerTimeoutMethodCall(expr, expr.expression) ??
4194
+ lowerObjectOwnPrototypeCall(lowerer, expr, expr.expression) ??
4195
+ lowerObjectPrototypeCall(lowerer, expr, expr.expression) ??
4196
+ lowerStringPrototypeCall(lowerer, expr, expr.expression) ??
4178
4197
  lowerStringMethodCallWithOptionalArgs(lowerer, expr, expr.expression) ??
4179
4198
  // Typed-array/Buffer receivers and the Buffer statics — before the
4180
4199
  // island path (bytes never cross the boundary).
4181
4200
  lowerer.lowerBytesMethodCall(expr, expr.expression) ??
4182
- lowerBufferStaticCallWithOptionalArg(lowerer, expr, expr.expression) ??
4201
+ lowerBufferStaticCallWithNarrowedArg(lowerer, expr, expr.expression) ??
4183
4202
  // URL.revokeObjectURL's zero-argument contract (the one-argument
4184
4203
  // form keeps the fence — createObjectURL does too).
4185
4204
  lowerUrlStaticCall(lowerer, expr, expr.expression) ??
@@ -4203,6 +4222,7 @@ export function lowerCall(lowerer, expr) {
4203
4222
  // hasOwnProperty on a program class CONSTRUCTOR — own statics are
4204
4223
  // compile-time-known, so a literal key folds to a constant.
4205
4224
  lowerClassHasOwnPropertyCall(lowerer, expr, expr.expression) ??
4225
+ lowerObjectOwnMethodCall(lowerer, expr, expr.expression) ??
4206
4226
  // Response constructor-object operations are unsupported in both
4207
4227
  // tiers. Keep their SC2020 inventory contract ahead of the island
4208
4228
  // and generic-call fallbacks (Response.json otherwise reports the
@@ -4243,6 +4263,7 @@ export function lowerCall(lowerer, expr) {
4243
4263
  lowerObjectStaticCall(lowerer, expr, expr.expression) ??
4244
4264
  lowerObjectFromEntriesCall(lowerer, expr, expr.expression) ??
4245
4265
  lowerArrayFromCall(lowerer, expr, expr.expression) ??
4266
+ lowerArrayOfCall(lowerer, expr, expr.expression) ??
4246
4267
  lowerer.lowerArrayMethodCall(expr, expr.expression) ??
4247
4268
  // Read-only array methods (slice/map) on TUPLE receivers — the
4248
4269
  // positions snapshot into a fresh array (the for-of stance).
@@ -4454,22 +4475,29 @@ function lowerRegexMethodCallWithOptionalArg(lowerer, call, access) {
4454
4475
  function optionalCallValue(lowerer, node) {
4455
4476
  return lowerer.runtimeOptionalIdentifierValue(node)?.value ?? lowerAbsenceProbe(lowerer, node);
4456
4477
  }
4457
- function lowerBufferStaticCallWithOptionalArg(lowerer, call, access) {
4478
+ function lowerBufferStaticCallWithNarrowedArg(lowerer, call, access) {
4458
4479
  const lowered = lowerer.lowerBufferStaticCall(call, access);
4459
4480
  if (lowered?.kind !== "bytesNew" || !lowered.source || lowered.source.type.kind !== "union")
4460
4481
  return lowered;
4461
- const present = lowerer.stripUndefinedArm(lowered.source.type);
4462
- const validSource = (present.kind === "array" && present.elem.kind === "f64") ||
4463
- (present.kind === "bytes" && present.elem === "u8");
4464
- if (!validSource)
4465
- return lowered;
4466
- const helper = lowerer.narrowedArmHelper(lowered.source.type.unionId, present, lowered.source.loc);
4467
- return helper
4468
- ? {
4469
- ...lowered,
4470
- source: { kind: "call", callee: helper, args: [lowered.source], type: present, loc: lowered.source.loc },
4471
- }
4472
- : lowered;
4482
+ // A callback can store a wider union than the checker sees at this use
4483
+ // (including an added undefined arm). Extract the exact arm proven by
4484
+ // control-flow narrowing, not merely the union with undefined removed.
4485
+ const narrowed = lowerer.mapTypeOf(lowerer.typeOf(call.arguments[0]));
4486
+ if (!narrowed ||
4487
+ !(narrowed.kind === "f64" ||
4488
+ (narrowed.kind === "array" && narrowed.elem.kind === "f64") ||
4489
+ (narrowed.kind === "bytes" && narrowed.elem === "u8")) ||
4490
+ lowerer.armTag(lowered.source.type.unionId, narrowed) < 0) {
4491
+ lowerer.unsupported("SC1090", call.arguments[0], "a Buffer constructor argument whose narrowed type is not a stored source arm");
4492
+ }
4493
+ const helper = lowerer.narrowedArmHelper(lowered.source.type.unionId, narrowed, lowered.source.loc);
4494
+ if (!helper) {
4495
+ lowerer.unsupported("SC1090", call.arguments[0], "a Buffer constructor argument whose narrowed type is not a stored source arm");
4496
+ }
4497
+ return {
4498
+ ...lowered,
4499
+ source: { kind: "call", callee: helper, args: [lowered.source], type: narrowed, loc: lowered.source.loc },
4500
+ };
4473
4501
  }
4474
4502
  function lowerOptionalNumberDefault(lowerer, value, dflt, loc) {
4475
4503
  const widened = lowerer.runtimeOptionalWidening(value.type, F64);
@@ -4509,14 +4537,14 @@ function lowerOptionalNumberDefault(lowerer, value, dflt, loc) {
4509
4537
  }
4510
4538
  return { kind: "call", callee: helper, args: [value], type: F64, loc };
4511
4539
  }
4512
- function lowerStringMethodCallWithOptionalArgs(lowerer, call, access) {
4513
- const lowered = lowerer.lowerStringMethodCall(call, access);
4540
+ function lowerStringMethodCallWithOptionalArgs(lowerer, call, access, receiver, argumentNodes = call.arguments) {
4541
+ const lowered = lowerStringMethodCall(lowerer, call, access, receiver, argumentNodes);
4514
4542
  if (lowered?.kind !== "strIntrinsic")
4515
4543
  return lowered;
4516
4544
  const args = [...lowered.args];
4517
4545
  let changed = false;
4518
4546
  const zeroDefaultArgs = lowered.method === "charCodeAt" || lowered.method === "charAt" ||
4519
- lowered.method === "repeat" || lowered.method === "padStart" || lowered.method === "padEnd"
4547
+ lowered.method === "repeat"
4520
4548
  ? [0]
4521
4549
  : lowered.method === "slice" || lowered.method === "substring"
4522
4550
  ? [0]
@@ -4537,11 +4565,419 @@ function lowerStringMethodCallWithOptionalArgs(lowerer, call, access) {
4537
4565
  lowered.method === "startsWith" || lowered.method === "endsWith") &&
4538
4566
  args[0]?.type.kind === "union" &&
4539
4567
  lowerer.runtimeOptionalWidening(args[0].type, STRING) !== null) {
4540
- args[0] = lowerer.ensureString(args[0], call.arguments[0]);
4568
+ args[0] = lowerer.ensureString(args[0], argumentNodes[0]);
4541
4569
  changed = true;
4542
4570
  }
4543
4571
  return changed ? { ...lowered, args } : lowered;
4544
4572
  }
4573
+ function lowerNumberConstructorValue(lowerer, argNode, loc) {
4574
+ const undefinedArg = lowerStaticallyUndefinedArgument(lowerer, argNode);
4575
+ if (undefinedArg) {
4576
+ return defaultAfterUndefined(undefinedArg, {
4577
+ kind: "bin", op: "/", left: { kind: "numLit", value: 0, type: F64, loc },
4578
+ right: { kind: "numLit", value: 0, type: F64, loc }, type: F64, loc,
4579
+ });
4580
+ }
4581
+ const arg = lowerer.lowerExpr(argNode);
4582
+ if (arg.type.kind === "nullT")
4583
+ return defaultAfterUndefined(arg, { kind: "numLit", value: 0, type: F64, loc });
4584
+ if (arg.type.kind === "bigint")
4585
+ return { kind: "libCall", fn: "bigint.toF64", args: [arg], type: F64, loc };
4586
+ if (arg.type.kind === "f64")
4587
+ return arg;
4588
+ if (arg.type.kind === "bool") {
4589
+ return {
4590
+ kind: "ternary",
4591
+ cond: arg,
4592
+ then: { kind: "numLit", value: 1, type: F64, loc },
4593
+ else_: { kind: "numLit", value: 0, type: F64, loc },
4594
+ type: F64,
4595
+ loc,
4596
+ };
4597
+ }
4598
+ if (arg.type.kind === "string")
4599
+ return { kind: "libCall", fn: "num.fromString", args: [arg], type: F64, loc };
4600
+ const optionalNumber = lowerOptionalStringNumber(lowerer, arg, loc);
4601
+ if (optionalNumber)
4602
+ return optionalNumber;
4603
+ const scalarUnionNumber = lowerScalarUnionNumber(lowerer, arg, argNode, loc);
4604
+ if (scalarUnionNumber)
4605
+ return scalarUnionNumber;
4606
+ return lowerer.noLowering(`Number of ${lowerer.fmt(arg.type)} values`, argNode, arg.type.kind === "union"
4607
+ ? "unions of numbers, booleans, strings, null, and undefined lower — narrow other arms first"
4608
+ : undefined);
4609
+ }
4610
+ function immediatePrimitiveWrapperToString(lowerer, node) {
4611
+ const stringValue = stringWrapperToString(lowerer, node);
4612
+ if (stringValue)
4613
+ return stringValue;
4614
+ let wrapped = node;
4615
+ while (ts.isParenthesizedExpression(wrapped))
4616
+ wrapped = wrapped.expression;
4617
+ if (!ts.isNewExpression(wrapped) || !ts.isIdentifier(wrapped.expression))
4618
+ return null;
4619
+ const name = wrapped.expression.text;
4620
+ if ((name !== "Boolean" && name !== "Number") ||
4621
+ !lowerer.isStdlibSymbol(lowerer.resolveValueSymbol(wrapped.expression) ?? undefined))
4622
+ return null;
4623
+ const args = wrapped.arguments ?? [];
4624
+ if (args.length > 1 || args.some(ts.isSpreadElement))
4625
+ return null;
4626
+ const loc = locOf(wrapped);
4627
+ const primitive = name === "Boolean"
4628
+ ? args.length ? lowerer.lowerCondition(args[0]) : { kind: "boolLit", value: false, type: BOOL, loc }
4629
+ : args.length ? lowerNumberConstructorValue(lowerer, args[0], loc) : { kind: "numLit", value: 0, type: F64, loc };
4630
+ return lowerer.ensureString(primitive, wrapped);
4631
+ }
4632
+ function plainObjectCoercionReceiver(lowerer, node) {
4633
+ const plainLiteral = (literal) => literal.properties.every((prop) => ts.isPropertyAssignment(prop) &&
4634
+ (ts.isIdentifier(prop.name) || ts.isStringLiteral(prop.name) || ts.isNumericLiteral(prop.name)) &&
4635
+ prop.name.text !== "__proto__");
4636
+ let value = node;
4637
+ while (ts.isParenthesizedExpression(value))
4638
+ value = value.expression;
4639
+ if (ts.isObjectLiteralExpression(value))
4640
+ return plainLiteral(value);
4641
+ if (!ts.isIdentifier(value))
4642
+ return false;
4643
+ const symbol = lowerer.resolveValueSymbol(value);
4644
+ const decl = symbol ? lowerer.checker.valueDeclarationOf(symbol) : undefined;
4645
+ if (!symbol || !decl || !ts.isVariableDeclaration(decl) || !decl.initializer ||
4646
+ !ts.isObjectLiteralExpression(decl.initializer) || !plainLiteral(decl.initializer))
4647
+ return false;
4648
+ return bindingNeverReassigned(lowerer, symbol, decl);
4649
+ }
4650
+ function objectToStringTag(lowerer, type) {
4651
+ switch (type.kind) {
4652
+ case "undefinedT":
4653
+ case "void": return "Undefined";
4654
+ case "nullT": return "Null";
4655
+ case "bool": return "Boolean";
4656
+ case "f64": return "Number";
4657
+ case "string": return "String";
4658
+ case "bigint": return "BigInt";
4659
+ case "symbol": return "Symbol";
4660
+ case "array": return "Array";
4661
+ case "record": return "Object";
4662
+ case "func":
4663
+ case "classval": return "Function";
4664
+ case "date": return "Date";
4665
+ case "regex": return "RegExp";
4666
+ case "map": return "Map";
4667
+ case "set": return "Set";
4668
+ case "promise": return "Promise";
4669
+ case "bytes":
4670
+ return ({ u8: "Uint8Array", u32: "Uint32Array", i32: "Int32Array", f32: "Float32Array", f64: "Float64Array" })[type.elem];
4671
+ case "object":
4672
+ return type.className === "%Error" || lowerer.isSubclassOf(type.className, "%Error") ? "Error" : null;
4673
+ default: return null;
4674
+ }
4675
+ }
4676
+ function immediateObjectTagReceiver(lowerer, node) {
4677
+ let value = node;
4678
+ while (ts.isParenthesizedExpression(value))
4679
+ value = value.expression;
4680
+ const loc = locOf(value);
4681
+ if (ts.isArrayLiteralExpression(value))
4682
+ return { tag: "Array", effect: lowerer.lowerExpr(value) };
4683
+ if ((ts.isCallExpression(value) || ts.isNewExpression(value)) && ts.isIdentifier(value.expression) &&
4684
+ (value.expression.text === "Array" || value.expression.text === "Error") &&
4685
+ (value.arguments?.length ?? 0) === 0 && lowerer.isStdlibSymbol(lowerer.resolveValueSymbol(value.expression) ?? undefined)) {
4686
+ return { tag: value.expression.text, effect: { kind: "numLit", value: 0, type: F64, loc } };
4687
+ }
4688
+ if (ts.isNewExpression(value) && ts.isIdentifier(value.expression) &&
4689
+ lowerer.isStdlibSymbol(lowerer.resolveValueSymbol(value.expression) ?? undefined)) {
4690
+ const name = value.expression.text;
4691
+ const args = value.arguments ?? [];
4692
+ if (args.length <= 1 && !args.some(ts.isSpreadElement)) {
4693
+ if (name === "String") {
4694
+ const effect = stringWrapperToString(lowerer, value);
4695
+ if (effect)
4696
+ return { tag: "String", effect };
4697
+ }
4698
+ if (name === "Number")
4699
+ return {
4700
+ tag: "Number",
4701
+ effect: args.length ? lowerNumberConstructorValue(lowerer, args[0], loc) : { kind: "numLit", value: 0, type: F64, loc },
4702
+ };
4703
+ if (name === "Boolean")
4704
+ return {
4705
+ tag: "Boolean",
4706
+ effect: args.length ? lowerer.lowerCondition(args[0]) : { kind: "boolLit", value: false, type: BOOL, loc },
4707
+ };
4708
+ }
4709
+ }
4710
+ if ((ts.isCallExpression(value) || ts.isNewExpression(value)) &&
4711
+ ts.isIdentifier(value.expression) && value.expression.text === "Object" &&
4712
+ lowerer.isStdlibSymbol(lowerer.resolveValueSymbol(value.expression) ?? undefined)) {
4713
+ const args = value.arguments ?? [];
4714
+ if (args.some(ts.isSpreadElement))
4715
+ return null;
4716
+ const first = args[0];
4717
+ const inner = first && (immediateObjectTagReceiver(lowerer, first) ?? (() => {
4718
+ const effect = lowerer.lowerExpr(first);
4719
+ const tag = isUnitType(effect.type) || effect.type.kind === "void" ? "Object" : objectToStringTag(lowerer, effect.type);
4720
+ return tag === null ? null : { tag, effect };
4721
+ })());
4722
+ if (first && !inner)
4723
+ return null;
4724
+ const stmts = [];
4725
+ if (inner && !isSafeToDiscard(inner.effect))
4726
+ stmts.push({ kind: "exprStmt", expr: inner.effect, loc: inner.effect.loc });
4727
+ for (const extra of args.slice(1)) {
4728
+ const effect = lowerer.lowerExpr(extra);
4729
+ if (!isSafeToDiscard(effect))
4730
+ stmts.push({ kind: "exprStmt", expr: effect, loc: locOf(extra) });
4731
+ }
4732
+ return {
4733
+ tag: inner?.tag ?? "Object",
4734
+ effect: { kind: "seqExpr", stmts, result: { kind: "numLit", value: 0, type: F64, loc }, type: F64, loc },
4735
+ };
4736
+ }
4737
+ return null;
4738
+ }
4739
+ function lowerObjectOwnPrototypeCall(lowerer, call, access) {
4740
+ if (lowerer.dynamic || access.name.text !== "call" || !ts.isPropertyAccessExpression(access.expression))
4741
+ return null;
4742
+ const method = access.expression;
4743
+ const enumerable = method.name.text === "propertyIsEnumerable";
4744
+ if (method.name.text !== "hasOwnProperty" && !enumerable)
4745
+ return null;
4746
+ const prototype = method.expression;
4747
+ if (!ts.isPropertyAccessExpression(prototype) || prototype.name.text !== "prototype" ||
4748
+ !lowerer.isStdlibGlobal(prototype.expression, "Object") || !lowerer.isStdlibMember(method) ||
4749
+ call.arguments.some(ts.isSpreadElement))
4750
+ return null;
4751
+ return lowerObjectOwnCall(lowerer, call, call.arguments[0], call.arguments[1], call.arguments.slice(2), method.name.text);
4752
+ }
4753
+ function lowerObjectOwnMethodCall(lowerer, call, access) {
4754
+ const method = access.name.text;
4755
+ if (lowerer.dynamic || call.questionDotToken || access.questionDotToken ||
4756
+ (method !== "hasOwnProperty" && method !== "propertyIsEnumerable") ||
4757
+ !lowerer.isStdlibMember(access) || call.arguments.some(ts.isSpreadElement))
4758
+ return null;
4759
+ return lowerObjectOwnCall(lowerer, call, access.expression, call.arguments[0], call.arguments.slice(1), method);
4760
+ }
4761
+ function lowerObjectOwnCall(lowerer, call, receiverNode, keyNode, extraNodes, method) {
4762
+ const enumerable = method === "propertyIsEnumerable";
4763
+ const loc = locOf(call);
4764
+ const stmts = [];
4765
+ const receiver = receiverNode ? lowerer.lowerExpr(receiverNode) : { kind: "unitLit", unit: "undefined", type: UNDEFINED_T, loc };
4766
+ let receiverRef = receiver;
4767
+ if (isUnitType(receiver.type) || receiver.type.kind === "void") {
4768
+ if (!isSafeToDiscard(receiver))
4769
+ stmts.push({ kind: "exprStmt", expr: receiver, loc });
4770
+ }
4771
+ else {
4772
+ const local = lowerer.declareHiddenLocal("%ownReceiver", receiver.type);
4773
+ stmts.push({ kind: "varDecl", localId: local.id, init: receiver, loc });
4774
+ receiverRef = varRef(local.id, receiver.type, loc);
4775
+ }
4776
+ const rawKey = keyNode ? lowerer.lowerExpr(keyNode) : null;
4777
+ let keyRef = rawKey;
4778
+ if (rawKey && (isUnitType(rawKey.type) || rawKey.type.kind === "void")) {
4779
+ if (!isSafeToDiscard(rawKey))
4780
+ stmts.push({ kind: "exprStmt", expr: rawKey, loc: rawKey.loc });
4781
+ }
4782
+ else if (rawKey) {
4783
+ const local = lowerer.declareHiddenLocal("%ownKey", rawKey.type);
4784
+ stmts.push({ kind: "varDecl", localId: local.id, init: rawKey, loc: rawKey.loc });
4785
+ keyRef = varRef(local.id, rawKey.type, loc);
4786
+ }
4787
+ for (const extra of extraNodes) {
4788
+ const effect = lowerer.lowerExpr(extra);
4789
+ if (!isSafeToDiscard(effect))
4790
+ stmts.push({ kind: "exprStmt", expr: effect, loc: locOf(extra) });
4791
+ }
4792
+ let key = keyRef;
4793
+ if (!keyNode || rawKey?.type.kind === "undefinedT" || rawKey?.type.kind === "void") {
4794
+ key = { kind: "strLit", value: "undefined", type: STRING, loc };
4795
+ }
4796
+ else if (rawKey?.type.kind === "nullT") {
4797
+ key = { kind: "strLit", value: "null", type: STRING, loc };
4798
+ }
4799
+ else if (key && (key.type.kind === "f64" || key.type.kind === "bool" || key.type.kind === "bigint")) {
4800
+ key = { kind: "toString", operand: key, type: STRING, loc };
4801
+ }
4802
+ if (!key || key.type.kind !== "string") {
4803
+ lowerer.noLowering("Object.prototype." + method + " with this property key", keyNode ?? call);
4804
+ }
4805
+ let result;
4806
+ const type = receiver.type;
4807
+ if (isUnitType(type) || type.kind === "void") {
4808
+ result = nodeThrowExpr(1, "", "Cannot convert undefined or null to object", BOOL, loc);
4809
+ }
4810
+ else if (type.kind === "record") {
4811
+ const shape = lowerer.shapes.get(type.shapeId);
4812
+ if (!shape || shape.tuple || shape.indexValue || shapeHasAccessorSlots(shape)) {
4813
+ lowerer.noLowering("Object.prototype." + method + " over this record shape", receiverNode ?? call);
4814
+ }
4815
+ result = { kind: "call", callee: recordHasOwnHelper(lowerer, type.shapeId, loc), args: [receiverRef, key], type: BOOL, loc };
4816
+ }
4817
+ else if (type.kind === "dyn" && !enumerable) {
4818
+ result = { kind: "libCall", fn: "dyn.hasOwn", args: [receiverRef, key], type: BOOL, loc };
4819
+ }
4820
+ else if (type.kind === "string" || type.kind === "array") {
4821
+ const needleLocal = lowerer.declareHiddenLocal("%ownIndex", F64);
4822
+ stmts.push({ kind: "varDecl", localId: needleLocal.id, init: { kind: "libCall", fn: "num.fromString", args: [key], type: F64, loc }, loc });
4823
+ const needle = varRef(needleLocal.id, F64, loc);
4824
+ const length = type.kind === "string"
4825
+ ? { kind: "strIntrinsic", method: "length", receiver: receiverRef, args: [], type: F64, loc }
4826
+ : { kind: "arrIntrinsic", method: "length", receiver: receiverRef, args: [], type: F64, loc };
4827
+ const inRange = { kind: "bin", op: "<", left: needle, right: length, type: BOOL, loc };
4828
+ const nonNegative = { kind: "bin", op: ">=", left: needle, right: { kind: "numLit", value: 0, type: F64, loc }, type: BOOL, loc };
4829
+ const integer = { kind: "bin", op: "===", left: needle,
4830
+ right: { kind: "libCall", fn: "math.floor", args: [needle], type: F64, loc }, type: BOOL, loc };
4831
+ const canonical = { kind: "strEq", negated: false, left: key, right: { kind: "toString", operand: needle, type: STRING, loc }, type: BOOL, loc };
4832
+ const present = type.kind === "string" ? { kind: "boolLit", value: true, type: BOOL, loc } :
4833
+ { kind: "bin", op: "===", left: { kind: "arrIntrinsic", method: "nextPresent", receiver: receiverRef, args: [needle], type: F64, loc }, right: needle, type: BOOL, loc };
4834
+ const no = { kind: "boolLit", value: false, type: BOOL, loc };
4835
+ const indexOwn = [inRange, integer, canonical, present].reduce((left, right) => ({ kind: "logical", op: "&&", left, right, type: BOOL, loc }), nonNegative);
4836
+ const lengthOwn = !enumerable
4837
+ ? { kind: "strEq", negated: false, left: key, right: { kind: "strLit", value: "length", type: STRING, loc }, type: BOOL, loc }
4838
+ : no;
4839
+ result = { kind: "logical", op: "||", left: lengthOwn, right: indexOwn, type: BOOL, loc };
4840
+ }
4841
+ else if (type.kind === "f64" || type.kind === "bool" || type.kind === "bigint" || type.kind === "symbol") {
4842
+ result = { kind: "boolLit", value: false, type: BOOL, loc };
4843
+ }
4844
+ else {
4845
+ lowerer.noLowering("Object.prototype." + method + " over a " + lowerer.fmt(type) + " receiver", receiverNode ?? call);
4846
+ }
4847
+ return { kind: "seqExpr", stmts, result, type: BOOL, loc };
4848
+ }
4849
+ function lowerObjectPrototypeCall(lowerer, call, access) {
4850
+ if (lowerer.dynamic || access.name.text !== "call" || !ts.isPropertyAccessExpression(access.expression))
4851
+ return null;
4852
+ const method = access.expression;
4853
+ const prototype = method.expression;
4854
+ if (method.name.text !== "toString" || !ts.isPropertyAccessExpression(prototype) ||
4855
+ prototype.name.text !== "prototype" || !lowerer.isStdlibGlobal(prototype.expression, "Object") ||
4856
+ !lowerer.isStdlibMember(method) || call.arguments.some(ts.isSpreadElement))
4857
+ return null;
4858
+ const loc = locOf(call);
4859
+ if (call.arguments.length === 0)
4860
+ return { kind: "strLit", value: "[object Undefined]", type: STRING, loc };
4861
+ const receiverNode = call.arguments[0];
4862
+ const immediate = immediateObjectTagReceiver(lowerer, receiverNode);
4863
+ const receiver = immediate?.effect ?? lowerer.lowerExpr(receiverNode);
4864
+ const tag = immediate?.tag ?? objectToStringTag(lowerer, receiver.type);
4865
+ if (tag === null && receiver.type.kind !== "dyn") {
4866
+ lowerer.noLowering("Object.prototype.toString.call over a " + lowerer.fmt(receiver.type) + " receiver", receiverNode);
4867
+ }
4868
+ const stmts = [];
4869
+ let ref = receiver;
4870
+ if (isUnitType(receiver.type) || receiver.type.kind === "void") {
4871
+ if (!isSafeToDiscard(receiver))
4872
+ stmts.push({ kind: "exprStmt", expr: receiver, loc });
4873
+ }
4874
+ else {
4875
+ const local = lowerer.declareHiddenLocal("%objectTagReceiver", receiver.type);
4876
+ stmts.push({ kind: "varDecl", localId: local.id, init: receiver, loc });
4877
+ ref = varRef(local.id, receiver.type, loc);
4878
+ }
4879
+ for (const extra of call.arguments.slice(1)) {
4880
+ stmts.push({ kind: "exprStmt", expr: lowerer.lowerExpr(extra), loc: locOf(extra) });
4881
+ }
4882
+ const result = tag === null
4883
+ ? { kind: "libCall", fn: "dyn.objectTag", args: [ref], type: STRING, loc }
4884
+ : { kind: "strLit", value: "[object " + tag + "]", type: STRING, loc };
4885
+ return { kind: "seqExpr", stmts, result, type: STRING, loc };
4886
+ }
4887
+ function lowerStringPrototypeCall(lowerer, call, access) {
4888
+ if (access.name.text !== "call" || !ts.isPropertyAccessExpression(access.expression))
4889
+ return null;
4890
+ const methodAccess = access.expression;
4891
+ const prototypeAccess = methodAccess.expression;
4892
+ if (!ts.isPropertyAccessExpression(prototypeAccess) || prototypeAccess.name.text !== "prototype" ||
4893
+ !ts.isIdentifier(prototypeAccess.expression) || prototypeAccess.expression.text !== "String" ||
4894
+ (!Object.hasOwn(STR_METHODS, methodAccess.name.text) && !STRING_INDEX_METHODS.has(methodAccess.name.text)) ||
4895
+ !lowerer.isStdlibMember(methodAccess))
4896
+ return null;
4897
+ if (call.arguments.some(ts.isSpreadElement))
4898
+ return null;
4899
+ const entry = STR_METHODS[methodAccess.name.text];
4900
+ const indexMethod = STRING_INDEX_METHODS.has(methodAccess.name.text) ? methodAccess.name.text : null;
4901
+ const resultType = indexMethod ? lowerer.withUndefinedArmOf(indexMethod === "at" ? STRING : F64) : entry?.result;
4902
+ if (!resultType)
4903
+ return null;
4904
+ const method = methodAccess.name.text === "trimStart" ? "trimLeft" :
4905
+ methodAccess.name.text === "trimEnd" ? "trimRight" : methodAccess.name.text;
4906
+ const loc = locOf(call);
4907
+ const nullishError = nodeThrowExpr(1, "", `String.prototype.${method} called on null or undefined`, resultType, loc);
4908
+ const receiverNode = call.arguments[0];
4909
+ if (!receiverNode)
4910
+ return nullishError;
4911
+ // Receiver coercion runs after .call has evaluated its arguments; keep
4912
+ // contextual wrapper lowering on forms without later method arguments.
4913
+ const immediateWrapper = entry?.maxArgs === 0 && call.arguments.length === 1
4914
+ ? immediatePrimitiveWrapperToString(lowerer, receiverNode) : null;
4915
+ const receiverValue = immediateWrapper ?? lowerer.lowerExpr(receiverNode);
4916
+ const receiverType = receiverValue.type;
4917
+ const nullish = isUnitType(receiverType) || receiverType.kind === "void" ||
4918
+ (receiverType.kind === "union" && (lowerer.unions.get(receiverType.unionId)?.arms.every((arm) => isUnitType(arm) || arm.kind === "void") ?? false));
4919
+ if (nullish) {
4920
+ const values = [receiverValue, ...call.arguments.slice(1).map((arg) => lowerer.lowerExpr(arg))];
4921
+ return {
4922
+ kind: "seqExpr",
4923
+ stmts: values.filter((value) => !isSafeToDiscard(value)).map((value) => ({ kind: "exprStmt", expr: value, loc: value.loc })),
4924
+ result: nullishError,
4925
+ type: resultType,
4926
+ loc,
4927
+ };
4928
+ }
4929
+ const padding = entry?.method === "padStart" || entry?.method === "padEnd";
4930
+ const scalar = receiverType.kind === "string" || receiverType.kind === "f64" ||
4931
+ receiverType.kind === "bool" || receiverType.kind === "bigint" ||
4932
+ (receiverType.kind === "union" && (lowerer.unions.get(receiverType.unionId)?.arms.every((arm) => arm.kind === "string" || arm.kind === "f64" || arm.kind === "bool" || arm.kind === "bigint") ?? false));
4933
+ // Other methods convert object receivers before lowering their arguments;
4934
+ // padding uses a helper that delays conversion until every argument is ready.
4935
+ const objectWithoutMethodArgs = entry?.maxArgs === 0 && call.arguments.length === 1 &&
4936
+ (receiverType.kind === "record" || receiverType.kind === "array" || receiverType.kind === "object");
4937
+ const dynObjectWithoutMethodArgs = entry?.maxArgs === 0 && call.arguments.length === 1 &&
4938
+ receiverType.kind === "dyn" && plainObjectCoercionReceiver(lowerer, receiverNode);
4939
+ if (!scalar && !objectWithoutMethodArgs && !dynObjectWithoutMethodArgs && !(padding && receiverType.kind === "dyn")) {
4940
+ return lowerer.noLowering(`String.prototype.${methodAccess.name.text}.call with ${lowerer.fmt(receiverType)} receivers`, call);
4941
+ }
4942
+ if (indexMethod)
4943
+ return lowerStringIndexCall(lowerer, call, indexMethod, receiverValue, receiverNode, call.arguments.slice(1));
4944
+ if (!entry)
4945
+ return null;
4946
+ if (padding) {
4947
+ return lowerStringPaddingCall(lowerer, call, entry.method, receiverValue, receiverNode, call.arguments.slice(1));
4948
+ }
4949
+ if (entry.method === "split") {
4950
+ return lowerStringSplitCall(lowerer, call, receiverValue, receiverNode, call.arguments.slice(1));
4951
+ }
4952
+ const receiver = dynObjectWithoutMethodArgs
4953
+ ? { kind: "libCall", fn: "dyn.toStringCoerce", args: [receiverValue], type: STRING, loc }
4954
+ : lowerer.ensureString(receiverValue, receiverNode);
4955
+ return lowerStringMethodCallWithOptionalArgs(lowerer, call, methodAccess, () => receiver, call.arguments.slice(1));
4956
+ }
4957
+ function lowerScalarUnionNumber(lowerer, arg, node, loc) {
4958
+ if (arg.type.kind !== "union" || !lowerer.unions.get(arg.type.unionId)?.arms.every((arm) => arm.kind === "f64" || arm.kind === "string" || arm.kind === "bool" || isUnitType(arm)))
4959
+ return null;
4960
+ const key = `number.scalar:${arg.type.unionId}`;
4961
+ let helper = lowerer.widthHelpers.get(key);
4962
+ if (!helper) {
4963
+ helper = `%number.scalar.${lowerer.widthHelpers.size}`;
4964
+ lowerer.widthHelpers.set(key, helper);
4965
+ const value = varRef("value.0", arg.type, loc);
4966
+ const nan = {
4967
+ kind: "bin", op: "/", left: { kind: "numLit", value: 0, type: F64, loc },
4968
+ right: { kind: "numLit", value: 0, type: F64, loc }, type: F64, loc,
4969
+ };
4970
+ lowerer.liftedFns.push({
4971
+ name: helper,
4972
+ params: [{ localId: "value.0", name: "value", type: arg.type }],
4973
+ returnType: F64,
4974
+ locals: [{ id: "value.0", name: "value", type: arg.type, mutable: false }],
4975
+ body: [{ kind: "return", value: positionNumber(lowerer, value, nan, node, "Number argument"), loc }],
4976
+ loc,
4977
+ });
4978
+ }
4979
+ return { kind: "call", callee: helper, args: [arg], type: F64, loc };
4980
+ }
4545
4981
  function lowerOptionalStringNumber(lowerer, arg, loc, fn = "num.fromString", label = "number") {
4546
4982
  if (arg.type.kind !== "union")
4547
4983
  return null;
@@ -5893,26 +6329,23 @@ export function lambdaSignature(lowerer, node) {
5893
6329
  ret = VOID;
5894
6330
  }
5895
6331
  }
5896
- // VARIADIC JS functions: a dynRest param (above), or a plain function
5897
- // whose body reads `arguments` (test/common's mustCall wrapper —
5898
- // `function() { ...; return fn.apply(this, arguments); }`). Both mark
5899
- // the func type `rest`: the lifted body takes one trailing dyn-array
5900
- // param, filled by the boxed call thunk with the call's arguments
5901
- // from index params.length on. `arguments` is only claimed in
5902
- // ZERO-param functions (there it IS the surplus array); alongside
5903
- // declared params the alias story has no model — the fence says to
5904
- // use a rest parameter. Arrows never claim it (JS: an arrow's
5905
- // `arguments` is the enclosing function's).
5906
- const hasDynRest = shapes.some((s) => s.mode === "dynRest");
5907
- const usesArguments = !hasDynRest &&
5908
- !ts.isArrowFunction(node) &&
6332
+ // A plain JS function reading `arguments` receives one hidden dyn
6333
+ // array. With named parameters, the array contains every actual
6334
+ // argument and only ES modules are admitted: sloppy-mode arguments
6335
+ // alias named parameters. Arrows inherit the enclosing `arguments`.
6336
+ const hasRest = shapes.some((s) => s.mode === "rest" || s.mode === "dynRest" || s.mode === "islandRest");
6337
+ const usesArguments = !hasRest &&
6338
+ (ts.isFunctionExpression(node) || ts.isFunctionDeclaration(node)) &&
5909
6339
  isJsSourceFile(node.getSourceFile()) &&
5910
6340
  bodyReadsArguments(node);
5911
6341
  if (usesArguments && node.parameters.length > 0) {
5912
- lowerer.unsupported("SC1090", node, "'arguments' in functions with declared parameters (use a rest parameter: (...args))");
6342
+ if (!isNodeEsmFile(node.getSourceFile())) {
6343
+ lowerer.unsupported("SC1090", node, "parameterized 'arguments' outside an ES module (sloppy-mode parameter aliases)");
6344
+ }
6345
+ shapes.push({ type: DYN, mode: "arguments" });
5913
6346
  }
5914
6347
  const funcType = funcTypeFromParamShapes(shapes, ret);
5915
- if (usesArguments && !hasDynRest)
6348
+ if (usesArguments)
5916
6349
  funcType.rest = true;
5917
6350
  return { shapes, funcType };
5918
6351
  }
@@ -5988,9 +6421,9 @@ export function lowerLambda(lowerer, node) {
5988
6421
  lowerer.fnStack.push(fnCtx);
5989
6422
  try {
5990
6423
  const { params, prologue } = lowerer.declareParams(node.parameters, shapes);
5991
- // The VARIADIC `arguments` form (rest-marked with no declared rest
5992
- // param): a synthetic trailing dyn-array param carries the call's
5993
- // arguments; `arguments` reads resolve to it (identifier lowering).
6424
+ // The synthetic trailing dyn-array param carries `arguments`.
6425
+ // Parameterized functions receive all actual arguments; zero-param
6426
+ // functions receive the same array through their rest ABI.
5994
6427
  if (funcType.rest && !shapes.some((s) => s.mode === "dynRest" || s.mode === "islandRest")) {
5995
6428
  const argsLocal = lowerer.declareHiddenLocal("%arguments", DYN);
5996
6429
  params.push({ localId: argsLocal.id, name: "%arguments", type: DYN });
@@ -7072,9 +7505,10 @@ function lowerBigIntMethodCall(lowerer, call, access) {
7072
7505
  if (name === "valueOf" && call.arguments.length === 0)
7073
7506
  return receiver;
7074
7507
  if (name === "toString" && call.arguments.length <= 1) {
7508
+ const defaultRadix = { kind: "numLit", value: 10, type: F64, loc };
7075
7509
  const radix = call.arguments[0]
7076
- ? lowerer.lowerExprExpecting(call.arguments[0], F64)
7077
- : { kind: "numLit", value: 10, type: F64, loc };
7510
+ ? lowerOptionalArgument(lowerer, call.arguments[0], F64, defaultRadix)
7511
+ : defaultRadix;
7078
7512
  return { kind: "libCall", fn: "bigint.toString", args: [receiver, radix], type: STRING, loc };
7079
7513
  }
7080
7514
  return null;
@@ -8092,10 +8526,9 @@ export function lowerFunction(lowerer, decl) {
8092
8526
  lowerer.fnStack.push(ctx);
8093
8527
  try {
8094
8528
  const { params, prologue } = lowerer.declareParams(decl.parameters, sig.params);
8095
- // The synthetic `arguments` slot (a dynRest shape BEYOND the declared
8096
- // parameters — collectSignatureInner appended it): one trailing
8097
- // dyn-array param, resolved by `arguments` reads.
8098
- if (sig.params.length > decl.parameters.length && sig.params[sig.params.length - 1].mode === "dynRest") {
8529
+ // The synthetic `arguments` slot follows the declared parameters;
8530
+ // reads resolve to this dyn array.
8531
+ if (sig.params.length > decl.parameters.length && (sig.params[sig.params.length - 1].mode === "dynRest" || sig.params[sig.params.length - 1].mode === "arguments")) {
8099
8532
  const argsLocal = lowerer.declareHiddenLocal("%arguments", DYN);
8100
8533
  params.push({ localId: argsLocal.id, name: "%arguments", type: DYN });
8101
8534
  ctx.argumentsLocal = argsLocal;