@scriptc/compiler 0.1.4 → 0.1.5

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 (150) 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 +264 -21
  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 +32 -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 +12 -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-shared.js +64 -0
  36. package/dist/backend/llvm/lib-shared.js.map +1 -1
  37. package/dist/backend/native-toolchain.d.ts +2 -0
  38. package/dist/backend/native-toolchain.js +2 -1
  39. package/dist/backend/native-toolchain.js.map +1 -1
  40. package/dist/backend/targets.d.ts +3 -0
  41. package/dist/backend/targets.js +13 -0
  42. package/dist/backend/targets.js.map +1 -1
  43. package/dist/coverage/surface-manifest.js +6 -4
  44. package/dist/coverage/surface-manifest.js.map +1 -1
  45. package/dist/executable/executable-cache.d.ts +2 -0
  46. package/dist/executable/executable-cache.js +3 -1
  47. package/dist/executable/executable-cache.js.map +1 -1
  48. package/dist/executable/executable-cache.test.js +3 -3
  49. package/dist/executable/executable-cache.test.js.map +1 -1
  50. package/dist/frontend/lowering/containers/bytes.d.ts +1 -1
  51. package/dist/frontend/lowering/containers/bytes.js +6 -5
  52. package/dist/frontend/lowering/containers/bytes.js.map +1 -1
  53. package/dist/frontend/lowering/containers/string-and-regexp.d.ts +7 -6
  54. package/dist/frontend/lowering/containers/string-and-regexp.js +237 -28
  55. package/dist/frontend/lowering/containers/string-and-regexp.js.map +1 -1
  56. package/dist/frontend/lowering/expressions/object-literals.js +8 -0
  57. package/dist/frontend/lowering/expressions/object-literals.js.map +1 -1
  58. package/dist/frontend/lowering/lower-builtins.d.ts +4 -5
  59. package/dist/frontend/lowering/lower-builtins.js +18 -10
  60. package/dist/frontend/lowering/lower-builtins.js.map +1 -1
  61. package/dist/frontend/lowering/lower-calls.d.ts +3 -2
  62. package/dist/frontend/lowering/lower-calls.js +265 -88
  63. package/dist/frontend/lowering/lower-calls.js.map +1 -1
  64. package/dist/frontend/lowering/lower-classes.js +2 -2
  65. package/dist/frontend/lowering/lower-classes.js.map +1 -1
  66. package/dist/frontend/lowering/lower-containers.d.ts +7 -9
  67. package/dist/frontend/lowering/lower-containers.js +143 -51
  68. package/dist/frontend/lowering/lower-containers.js.map +1 -1
  69. package/dist/frontend/lowering/lower-exprs.d.ts +12 -0
  70. package/dist/frontend/lowering/lower-exprs.js +128 -10
  71. package/dist/frontend/lowering/lower-exprs.js.map +1 -1
  72. package/dist/frontend/lowering/lower-island.js +17 -6
  73. package/dist/frontend/lowering/lower-island.js.map +1 -1
  74. package/dist/frontend/lowering/lower-server.d.ts +2 -4
  75. package/dist/frontend/lowering/lower-server.js +288 -32
  76. package/dist/frontend/lowering/lower-server.js.map +1 -1
  77. package/dist/frontend/lowering/lower-stmts.js +4 -4
  78. package/dist/frontend/lowering/lower-stmts.js.map +1 -1
  79. package/dist/frontend/lowering/lowerer.js +37 -2
  80. package/dist/frontend/lowering/lowerer.js.map +1 -1
  81. package/dist/frontend/lowering/optional-arguments.d.ts +9 -1
  82. package/dist/frontend/lowering/optional-arguments.js +67 -1
  83. package/dist/frontend/lowering/optional-arguments.js.map +1 -1
  84. package/dist/frontend/lowering/pairs-snapshot.d.ts +3 -1
  85. package/dist/frontend/lowering/pairs-snapshot.js +12 -4
  86. package/dist/frontend/lowering/pairs-snapshot.js.map +1 -1
  87. package/dist/frontend/lowering/surfaces.d.ts +8 -14
  88. package/dist/frontend/lowering/surfaces.js +37 -27
  89. package/dist/frontend/lowering/surfaces.js.map +1 -1
  90. package/dist/frontend/npm-static-prune.d.ts +6 -0
  91. package/dist/frontend/npm-static-prune.js +203 -0
  92. package/dist/frontend/npm-static-prune.js.map +1 -0
  93. package/dist/frontend/npm-static-prune.test.d.ts +1 -0
  94. package/dist/frontend/npm-static-prune.test.js +48 -0
  95. package/dist/frontend/npm-static-prune.test.js.map +1 -0
  96. package/dist/frontend/npm.js +7 -6
  97. package/dist/frontend/npm.js.map +1 -1
  98. package/dist/frontend/program.js +15 -4
  99. package/dist/frontend/program.js.map +1 -1
  100. package/dist/frontend/provenance.js +8 -3
  101. package/dist/frontend/provenance.js.map +1 -1
  102. package/dist/frontend/type-mapper.js +16 -5
  103. package/dist/frontend/type-mapper.js.map +1 -1
  104. package/dist/index.d.ts +3 -3
  105. package/dist/index.js +13 -41
  106. package/dist/index.js.map +1 -1
  107. package/dist/ir/analysis.d.ts +5 -3
  108. package/dist/ir/analysis.js +25 -11
  109. package/dist/ir/analysis.js.map +1 -1
  110. package/dist/ir/analysis.test.js +50 -2
  111. package/dist/ir/analysis.test.js.map +1 -1
  112. package/dist/ir/constant-tables.d.ts +17 -0
  113. package/dist/ir/constant-tables.js +99 -0
  114. package/dist/ir/constant-tables.js.map +1 -0
  115. package/dist/ir/constant-tables.test.d.ts +1 -0
  116. package/dist/ir/constant-tables.test.js +78 -0
  117. package/dist/ir/constant-tables.test.js.map +1 -0
  118. package/dist/ir/integer-ranges.d.ts +13 -0
  119. package/dist/ir/integer-ranges.js +122 -0
  120. package/dist/ir/integer-ranges.js.map +1 -0
  121. package/dist/ir/integer-ranges.test.d.ts +1 -0
  122. package/dist/ir/integer-ranges.test.js +63 -0
  123. package/dist/ir/integer-ranges.test.js.map +1 -0
  124. package/dist/ir/ir.d.ts +42 -34
  125. package/dist/ir/ir.js +7 -1
  126. package/dist/ir/ir.js.map +1 -1
  127. package/dist/ir/ir.test.js +6 -0
  128. package/dist/ir/ir.test.js.map +1 -1
  129. package/dist/ir/scalar-records.d.ts +8 -0
  130. package/dist/ir/scalar-records.js +216 -0
  131. package/dist/ir/scalar-records.js.map +1 -0
  132. package/dist/ir/scalar-records.test.d.ts +1 -0
  133. package/dist/ir/scalar-records.test.js +142 -0
  134. package/dist/ir/scalar-records.test.js.map +1 -0
  135. package/dist/ir/validate.js +92 -8
  136. package/dist/ir/validate.js.map +1 -1
  137. package/dist/ir/validate.test.d.ts +1 -0
  138. package/dist/ir/validate.test.js +31 -0
  139. package/dist/ir/validate.test.js.map +1 -0
  140. package/dist/library/cache-primitives.d.ts +1 -1
  141. package/dist/library/cache-primitives.js +3 -3
  142. package/dist/library/cache-primitives.js.map +1 -1
  143. package/dist/library/fence-eval.js +11 -4
  144. package/dist/library/fence-eval.js.map +1 -1
  145. package/dist/library/library-cache.js +0 -1
  146. package/dist/library/library-cache.js.map +1 -1
  147. package/dist/library/library-cache.test.js +3 -3
  148. package/dist/library/library-cache.test.js.map +1 -1
  149. package/package.json +17 -17
  150. package/surface-manifest.json +55 -42
@@ -9,8 +9,9 @@ import { type ClassInfo } from "./lower-classes.js";
9
9
  * `omittable` params (declared `x?: T` or `x: T = e`) may be omitted by a
10
10
  * trailing-suffix call, and the frontend appends the interned undefined arm;
11
11
  * `rest` (always last) receives the surplus arguments packed into one array
12
- * literal at each call site. */
13
- export type ParamMode = "required" | "omittable" | "rest" | "dynRest" | "islandRest";
12
+ * literal at each call site. `arguments` is a hidden slot containing every
13
+ * actual argument, including those bound to named parameters. */
14
+ export type ParamMode = "required" | "omittable" | "rest" | "dynRest" | "islandRest" | "arguments";
14
15
  /** One parameter of a signature, as call sites and callee prologues see it.
15
16
  * `type` is the ABI type — what the emitted C parameter carries: the
16
17
  * checker's `T | undefined` union for `x?: T`, a synthesized `T | undefined`
@@ -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, 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
16
  import { dynStringReceiver, lowerArrayFromCall, 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, lowerStringMethodCall, lowerStringPaddingCall } 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.
@@ -4175,11 +4188,12 @@ export function lowerCall(lowerer, expr) {
4175
4188
  // TestContext surface (t.test/t.skip/t.diagnostic), t.assert.*.
4176
4189
  lowerer.lowerTestMethodCall(expr, expr.expression) ??
4177
4190
  lowerer.lowerTimeoutMethodCall(expr, expr.expression) ??
4191
+ lowerStringPrototypeCall(lowerer, expr, expr.expression) ??
4178
4192
  lowerStringMethodCallWithOptionalArgs(lowerer, expr, expr.expression) ??
4179
4193
  // Typed-array/Buffer receivers and the Buffer statics — before the
4180
4194
  // island path (bytes never cross the boundary).
4181
4195
  lowerer.lowerBytesMethodCall(expr, expr.expression) ??
4182
- lowerBufferStaticCallWithOptionalArg(lowerer, expr, expr.expression) ??
4196
+ lowerBufferStaticCallWithNarrowedArg(lowerer, expr, expr.expression) ??
4183
4197
  // URL.revokeObjectURL's zero-argument contract (the one-argument
4184
4198
  // form keeps the fence — createObjectURL does too).
4185
4199
  lowerUrlStaticCall(lowerer, expr, expr.expression) ??
@@ -4454,22 +4468,29 @@ function lowerRegexMethodCallWithOptionalArg(lowerer, call, access) {
4454
4468
  function optionalCallValue(lowerer, node) {
4455
4469
  return lowerer.runtimeOptionalIdentifierValue(node)?.value ?? lowerAbsenceProbe(lowerer, node);
4456
4470
  }
4457
- function lowerBufferStaticCallWithOptionalArg(lowerer, call, access) {
4471
+ function lowerBufferStaticCallWithNarrowedArg(lowerer, call, access) {
4458
4472
  const lowered = lowerer.lowerBufferStaticCall(call, access);
4459
4473
  if (lowered?.kind !== "bytesNew" || !lowered.source || lowered.source.type.kind !== "union")
4460
4474
  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;
4475
+ // A callback can store a wider union than the checker sees at this use
4476
+ // (including an added undefined arm). Extract the exact arm proven by
4477
+ // control-flow narrowing, not merely the union with undefined removed.
4478
+ const narrowed = lowerer.mapTypeOf(lowerer.typeOf(call.arguments[0]));
4479
+ if (!narrowed ||
4480
+ !(narrowed.kind === "f64" ||
4481
+ (narrowed.kind === "array" && narrowed.elem.kind === "f64") ||
4482
+ (narrowed.kind === "bytes" && narrowed.elem === "u8")) ||
4483
+ lowerer.armTag(lowered.source.type.unionId, narrowed) < 0) {
4484
+ lowerer.unsupported("SC1090", call.arguments[0], "a Buffer constructor argument whose narrowed type is not a stored source arm");
4485
+ }
4486
+ const helper = lowerer.narrowedArmHelper(lowered.source.type.unionId, narrowed, lowered.source.loc);
4487
+ if (!helper) {
4488
+ lowerer.unsupported("SC1090", call.arguments[0], "a Buffer constructor argument whose narrowed type is not a stored source arm");
4489
+ }
4490
+ return {
4491
+ ...lowered,
4492
+ source: { kind: "call", callee: helper, args: [lowered.source], type: narrowed, loc: lowered.source.loc },
4493
+ };
4473
4494
  }
4474
4495
  function lowerOptionalNumberDefault(lowerer, value, dflt, loc) {
4475
4496
  const widened = lowerer.runtimeOptionalWidening(value.type, F64);
@@ -4509,14 +4530,14 @@ function lowerOptionalNumberDefault(lowerer, value, dflt, loc) {
4509
4530
  }
4510
4531
  return { kind: "call", callee: helper, args: [value], type: F64, loc };
4511
4532
  }
4512
- function lowerStringMethodCallWithOptionalArgs(lowerer, call, access) {
4513
- const lowered = lowerer.lowerStringMethodCall(call, access);
4533
+ function lowerStringMethodCallWithOptionalArgs(lowerer, call, access, receiver, argumentNodes = call.arguments) {
4534
+ const lowered = lowerStringMethodCall(lowerer, call, access, receiver, argumentNodes);
4514
4535
  if (lowered?.kind !== "strIntrinsic")
4515
4536
  return lowered;
4516
4537
  const args = [...lowered.args];
4517
4538
  let changed = false;
4518
4539
  const zeroDefaultArgs = lowered.method === "charCodeAt" || lowered.method === "charAt" ||
4519
- lowered.method === "repeat" || lowered.method === "padStart" || lowered.method === "padEnd"
4540
+ lowered.method === "repeat"
4520
4541
  ? [0]
4521
4542
  : lowered.method === "slice" || lowered.method === "substring"
4522
4543
  ? [0]
@@ -4537,11 +4558,170 @@ function lowerStringMethodCallWithOptionalArgs(lowerer, call, access) {
4537
4558
  lowered.method === "startsWith" || lowered.method === "endsWith") &&
4538
4559
  args[0]?.type.kind === "union" &&
4539
4560
  lowerer.runtimeOptionalWidening(args[0].type, STRING) !== null) {
4540
- args[0] = lowerer.ensureString(args[0], call.arguments[0]);
4561
+ args[0] = lowerer.ensureString(args[0], argumentNodes[0]);
4541
4562
  changed = true;
4542
4563
  }
4543
4564
  return changed ? { ...lowered, args } : lowered;
4544
4565
  }
4566
+ function lowerNumberConstructorValue(lowerer, argNode, loc) {
4567
+ const undefinedArg = lowerStaticallyUndefinedArgument(lowerer, argNode);
4568
+ if (undefinedArg) {
4569
+ return defaultAfterUndefined(undefinedArg, {
4570
+ kind: "bin", op: "/", left: { kind: "numLit", value: 0, type: F64, loc },
4571
+ right: { kind: "numLit", value: 0, type: F64, loc }, type: F64, loc,
4572
+ });
4573
+ }
4574
+ const arg = lowerer.lowerExpr(argNode);
4575
+ if (arg.type.kind === "nullT")
4576
+ return defaultAfterUndefined(arg, { kind: "numLit", value: 0, type: F64, loc });
4577
+ if (arg.type.kind === "bigint")
4578
+ return { kind: "libCall", fn: "bigint.toF64", args: [arg], type: F64, loc };
4579
+ if (arg.type.kind === "f64")
4580
+ return arg;
4581
+ if (arg.type.kind === "bool") {
4582
+ return {
4583
+ kind: "ternary",
4584
+ cond: arg,
4585
+ then: { kind: "numLit", value: 1, type: F64, loc },
4586
+ else_: { kind: "numLit", value: 0, type: F64, loc },
4587
+ type: F64,
4588
+ loc,
4589
+ };
4590
+ }
4591
+ if (arg.type.kind === "string")
4592
+ return { kind: "libCall", fn: "num.fromString", args: [arg], type: F64, loc };
4593
+ const optionalNumber = lowerOptionalStringNumber(lowerer, arg, loc);
4594
+ if (optionalNumber)
4595
+ return optionalNumber;
4596
+ const scalarUnionNumber = lowerScalarUnionNumber(lowerer, arg, argNode, loc);
4597
+ if (scalarUnionNumber)
4598
+ return scalarUnionNumber;
4599
+ return lowerer.noLowering(`Number of ${lowerer.fmt(arg.type)} values`, argNode, arg.type.kind === "union"
4600
+ ? "unions of numbers, booleans, strings, null, and undefined lower — narrow other arms first"
4601
+ : undefined);
4602
+ }
4603
+ function immediatePrimitiveWrapperToString(lowerer, node) {
4604
+ const stringValue = stringWrapperToString(lowerer, node);
4605
+ if (stringValue)
4606
+ return stringValue;
4607
+ let wrapped = node;
4608
+ while (ts.isParenthesizedExpression(wrapped))
4609
+ wrapped = wrapped.expression;
4610
+ if (!ts.isNewExpression(wrapped) || !ts.isIdentifier(wrapped.expression))
4611
+ return null;
4612
+ const name = wrapped.expression.text;
4613
+ if ((name !== "Boolean" && name !== "Number") ||
4614
+ !lowerer.isStdlibSymbol(lowerer.resolveValueSymbol(wrapped.expression) ?? undefined))
4615
+ return null;
4616
+ const args = wrapped.arguments ?? [];
4617
+ if (args.length > 1 || args.some(ts.isSpreadElement))
4618
+ return null;
4619
+ const loc = locOf(wrapped);
4620
+ const primitive = name === "Boolean"
4621
+ ? args.length ? lowerer.lowerCondition(args[0]) : { kind: "boolLit", value: false, type: BOOL, loc }
4622
+ : args.length ? lowerNumberConstructorValue(lowerer, args[0], loc) : { kind: "numLit", value: 0, type: F64, loc };
4623
+ return lowerer.ensureString(primitive, wrapped);
4624
+ }
4625
+ function plainObjectCoercionReceiver(lowerer, node) {
4626
+ const plainLiteral = (literal) => literal.properties.every((prop) => ts.isPropertyAssignment(prop) &&
4627
+ (ts.isIdentifier(prop.name) || ts.isStringLiteral(prop.name) || ts.isNumericLiteral(prop.name)) &&
4628
+ prop.name.text !== "__proto__");
4629
+ let value = node;
4630
+ while (ts.isParenthesizedExpression(value))
4631
+ value = value.expression;
4632
+ if (ts.isObjectLiteralExpression(value))
4633
+ return plainLiteral(value);
4634
+ if (!ts.isIdentifier(value))
4635
+ return false;
4636
+ const symbol = lowerer.resolveValueSymbol(value);
4637
+ const decl = symbol ? lowerer.checker.valueDeclarationOf(symbol) : undefined;
4638
+ if (!symbol || !decl || !ts.isVariableDeclaration(decl) || !decl.initializer ||
4639
+ !ts.isObjectLiteralExpression(decl.initializer) || !plainLiteral(decl.initializer))
4640
+ return false;
4641
+ return bindingNeverReassigned(lowerer, symbol, decl);
4642
+ }
4643
+ function lowerStringPrototypeCall(lowerer, call, access) {
4644
+ if (access.name.text !== "call" || !ts.isPropertyAccessExpression(access.expression))
4645
+ return null;
4646
+ const methodAccess = access.expression;
4647
+ const prototypeAccess = methodAccess.expression;
4648
+ if (!ts.isPropertyAccessExpression(prototypeAccess) || prototypeAccess.name.text !== "prototype" ||
4649
+ !ts.isIdentifier(prototypeAccess.expression) || prototypeAccess.expression.text !== "String" ||
4650
+ !Object.hasOwn(STR_METHODS, methodAccess.name.text) || !lowerer.isStdlibMember(methodAccess))
4651
+ return null;
4652
+ if (call.arguments.some(ts.isSpreadElement))
4653
+ return null;
4654
+ const entry = STR_METHODS[methodAccess.name.text];
4655
+ const method = methodAccess.name.text === "trimStart" ? "trimLeft" :
4656
+ methodAccess.name.text === "trimEnd" ? "trimRight" : methodAccess.name.text;
4657
+ const loc = locOf(call);
4658
+ const nullishError = nodeThrowExpr(1, "", `String.prototype.${method} called on null or undefined`, entry.result, loc);
4659
+ const receiverNode = call.arguments[0];
4660
+ if (!receiverNode)
4661
+ return nullishError;
4662
+ // Receiver coercion runs after .call has evaluated its arguments; keep
4663
+ // contextual wrapper lowering on forms without later method arguments.
4664
+ const immediateWrapper = entry.maxArgs === 0 && call.arguments.length === 1
4665
+ ? immediatePrimitiveWrapperToString(lowerer, receiverNode) : null;
4666
+ const receiverValue = immediateWrapper ?? lowerer.lowerExpr(receiverNode);
4667
+ const receiverType = receiverValue.type;
4668
+ const nullish = isUnitType(receiverType) || receiverType.kind === "void" ||
4669
+ (receiverType.kind === "union" && (lowerer.unions.get(receiverType.unionId)?.arms.every((arm) => isUnitType(arm) || arm.kind === "void") ?? false));
4670
+ if (nullish) {
4671
+ const values = [receiverValue, ...call.arguments.slice(1).map((arg) => lowerer.lowerExpr(arg))];
4672
+ return {
4673
+ kind: "seqExpr",
4674
+ stmts: values.filter((value) => !isSafeToDiscard(value)).map((value) => ({ kind: "exprStmt", expr: value, loc: value.loc })),
4675
+ result: nullishError,
4676
+ type: entry.result,
4677
+ loc,
4678
+ };
4679
+ }
4680
+ const padding = entry.method === "padStart" || entry.method === "padEnd";
4681
+ const scalar = receiverType.kind === "string" || receiverType.kind === "f64" ||
4682
+ receiverType.kind === "bool" || receiverType.kind === "bigint" ||
4683
+ (receiverType.kind === "union" && (lowerer.unions.get(receiverType.unionId)?.arms.every((arm) => arm.kind === "string" || arm.kind === "f64" || arm.kind === "bool" || arm.kind === "bigint") ?? false));
4684
+ // Other methods convert object receivers before lowering their arguments;
4685
+ // padding uses a helper that delays conversion until every argument is ready.
4686
+ const objectWithoutMethodArgs = entry.maxArgs === 0 && call.arguments.length === 1 &&
4687
+ (receiverType.kind === "record" || receiverType.kind === "array" || receiverType.kind === "object");
4688
+ const dynObjectWithoutMethodArgs = entry.maxArgs === 0 && call.arguments.length === 1 &&
4689
+ receiverType.kind === "dyn" && plainObjectCoercionReceiver(lowerer, receiverNode);
4690
+ if (!scalar && !objectWithoutMethodArgs && !dynObjectWithoutMethodArgs && !(padding && receiverType.kind === "dyn")) {
4691
+ return lowerer.noLowering(`String.prototype.${methodAccess.name.text}.call with ${lowerer.fmt(receiverType)} receivers`, call);
4692
+ }
4693
+ if (padding) {
4694
+ return lowerStringPaddingCall(lowerer, call, entry.method, receiverValue, receiverNode, call.arguments.slice(1));
4695
+ }
4696
+ const receiver = dynObjectWithoutMethodArgs
4697
+ ? { kind: "libCall", fn: "dyn.toStringCoerce", args: [receiverValue], type: STRING, loc }
4698
+ : lowerer.ensureString(receiverValue, receiverNode);
4699
+ return lowerStringMethodCallWithOptionalArgs(lowerer, call, methodAccess, () => receiver, call.arguments.slice(1));
4700
+ }
4701
+ function lowerScalarUnionNumber(lowerer, arg, node, loc) {
4702
+ 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)))
4703
+ return null;
4704
+ const key = `number.scalar:${arg.type.unionId}`;
4705
+ let helper = lowerer.widthHelpers.get(key);
4706
+ if (!helper) {
4707
+ helper = `%number.scalar.${lowerer.widthHelpers.size}`;
4708
+ lowerer.widthHelpers.set(key, helper);
4709
+ const value = varRef("value.0", arg.type, loc);
4710
+ const nan = {
4711
+ kind: "bin", op: "/", left: { kind: "numLit", value: 0, type: F64, loc },
4712
+ right: { kind: "numLit", value: 0, type: F64, loc }, type: F64, loc,
4713
+ };
4714
+ lowerer.liftedFns.push({
4715
+ name: helper,
4716
+ params: [{ localId: "value.0", name: "value", type: arg.type }],
4717
+ returnType: F64,
4718
+ locals: [{ id: "value.0", name: "value", type: arg.type, mutable: false }],
4719
+ body: [{ kind: "return", value: positionNumber(lowerer, value, nan, node, "Number argument"), loc }],
4720
+ loc,
4721
+ });
4722
+ }
4723
+ return { kind: "call", callee: helper, args: [arg], type: F64, loc };
4724
+ }
4545
4725
  function lowerOptionalStringNumber(lowerer, arg, loc, fn = "num.fromString", label = "number") {
4546
4726
  if (arg.type.kind !== "union")
4547
4727
  return null;
@@ -5893,26 +6073,23 @@ export function lambdaSignature(lowerer, node) {
5893
6073
  ret = VOID;
5894
6074
  }
5895
6075
  }
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) &&
6076
+ // A plain JS function reading `arguments` receives one hidden dyn
6077
+ // array. With named parameters, the array contains every actual
6078
+ // argument and only ES modules are admitted: sloppy-mode arguments
6079
+ // alias named parameters. Arrows inherit the enclosing `arguments`.
6080
+ const hasRest = shapes.some((s) => s.mode === "rest" || s.mode === "dynRest" || s.mode === "islandRest");
6081
+ const usesArguments = !hasRest &&
6082
+ (ts.isFunctionExpression(node) || ts.isFunctionDeclaration(node)) &&
5909
6083
  isJsSourceFile(node.getSourceFile()) &&
5910
6084
  bodyReadsArguments(node);
5911
6085
  if (usesArguments && node.parameters.length > 0) {
5912
- lowerer.unsupported("SC1090", node, "'arguments' in functions with declared parameters (use a rest parameter: (...args))");
6086
+ if (!isNodeEsmFile(node.getSourceFile())) {
6087
+ lowerer.unsupported("SC1090", node, "parameterized 'arguments' outside an ES module (sloppy-mode parameter aliases)");
6088
+ }
6089
+ shapes.push({ type: DYN, mode: "arguments" });
5913
6090
  }
5914
6091
  const funcType = funcTypeFromParamShapes(shapes, ret);
5915
- if (usesArguments && !hasDynRest)
6092
+ if (usesArguments)
5916
6093
  funcType.rest = true;
5917
6094
  return { shapes, funcType };
5918
6095
  }
@@ -5988,9 +6165,9 @@ export function lowerLambda(lowerer, node) {
5988
6165
  lowerer.fnStack.push(fnCtx);
5989
6166
  try {
5990
6167
  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).
6168
+ // The synthetic trailing dyn-array param carries `arguments`.
6169
+ // Parameterized functions receive all actual arguments; zero-param
6170
+ // functions receive the same array through their rest ABI.
5994
6171
  if (funcType.rest && !shapes.some((s) => s.mode === "dynRest" || s.mode === "islandRest")) {
5995
6172
  const argsLocal = lowerer.declareHiddenLocal("%arguments", DYN);
5996
6173
  params.push({ localId: argsLocal.id, name: "%arguments", type: DYN });
@@ -7072,9 +7249,10 @@ function lowerBigIntMethodCall(lowerer, call, access) {
7072
7249
  if (name === "valueOf" && call.arguments.length === 0)
7073
7250
  return receiver;
7074
7251
  if (name === "toString" && call.arguments.length <= 1) {
7252
+ const defaultRadix = { kind: "numLit", value: 10, type: F64, loc };
7075
7253
  const radix = call.arguments[0]
7076
- ? lowerer.lowerExprExpecting(call.arguments[0], F64)
7077
- : { kind: "numLit", value: 10, type: F64, loc };
7254
+ ? lowerOptionalArgument(lowerer, call.arguments[0], F64, defaultRadix)
7255
+ : defaultRadix;
7078
7256
  return { kind: "libCall", fn: "bigint.toString", args: [receiver, radix], type: STRING, loc };
7079
7257
  }
7080
7258
  return null;
@@ -8092,10 +8270,9 @@ export function lowerFunction(lowerer, decl) {
8092
8270
  lowerer.fnStack.push(ctx);
8093
8271
  try {
8094
8272
  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") {
8273
+ // The synthetic `arguments` slot follows the declared parameters;
8274
+ // reads resolve to this dyn array.
8275
+ if (sig.params.length > decl.parameters.length && (sig.params[sig.params.length - 1].mode === "dynRest" || sig.params[sig.params.length - 1].mode === "arguments")) {
8099
8276
  const argsLocal = lowerer.declareHiddenLocal("%arguments", DYN);
8100
8277
  params.push({ localId: argsLocal.id, name: "%arguments", type: DYN });
8101
8278
  ctx.argumentsLocal = argsLocal;