@scriptc/compiler 0.1.3 → 0.1.4

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 (147) hide show
  1. package/ambient/scriptc-node-fallback.d.ts +200 -51
  2. package/dist/backend/c/async.d.ts +25 -0
  3. package/dist/backend/c/async.js +167 -0
  4. package/dist/backend/c/async.js.map +1 -1
  5. package/dist/backend/c/c-emitter.d.ts +10 -0
  6. package/dist/backend/c/c-emitter.js +14 -2
  7. package/dist/backend/c/c-emitter.js.map +1 -1
  8. package/dist/backend/c/exprs.js +293 -51
  9. package/dist/backend/c/exprs.js.map +1 -1
  10. package/dist/backend/c/types.js +2 -0
  11. package/dist/backend/c/types.js.map +1 -1
  12. package/dist/backend/link-plan.d.ts +2 -1
  13. package/dist/backend/link-plan.js +2 -1
  14. package/dist/backend/link-plan.js.map +1 -1
  15. package/dist/backend/llvm/emitter.js +20 -6
  16. package/dist/backend/llvm/emitter.js.map +1 -1
  17. package/dist/backend/llvm/expr-bytes.d.ts +1 -1
  18. package/dist/backend/llvm/expr-bytes.js +8 -8
  19. package/dist/backend/llvm/expr-bytes.js.map +1 -1
  20. package/dist/backend/llvm/expr-callbacks.d.ts +9 -0
  21. package/dist/backend/llvm/expr-callbacks.js +125 -0
  22. package/dist/backend/llvm/expr-callbacks.js.map +1 -1
  23. package/dist/backend/llvm/expr-context.d.ts +14 -2
  24. package/dist/backend/llvm/expr-dispatch.js +1 -0
  25. package/dist/backend/llvm/expr-dispatch.js.map +1 -1
  26. package/dist/backend/llvm/expr-primitives.d.ts +1 -1
  27. package/dist/backend/llvm/expr-primitives.js +30 -9
  28. package/dist/backend/llvm/expr-primitives.js.map +1 -1
  29. package/dist/backend/llvm/expr-primitives.test.d.ts +1 -0
  30. package/dist/backend/llvm/expr-primitives.test.js +38 -0
  31. package/dist/backend/llvm/expr-primitives.test.js.map +1 -0
  32. package/dist/backend/llvm/expr-stream-callbacks.d.ts +3 -0
  33. package/dist/backend/llvm/expr-stream-callbacks.js +69 -0
  34. package/dist/backend/llvm/expr-stream-callbacks.js.map +1 -1
  35. package/dist/backend/llvm/lib-filesystem.js +68 -10
  36. package/dist/backend/llvm/lib-filesystem.js.map +1 -1
  37. package/dist/backend/llvm/lib-network.js +18 -2
  38. package/dist/backend/llvm/lib-network.js.map +1 -1
  39. package/dist/backend/llvm/lib-process.js +156 -6
  40. package/dist/backend/llvm/lib-process.js.map +1 -1
  41. package/dist/backend/llvm/lib-shared.js +36 -0
  42. package/dist/backend/llvm/lib-shared.js.map +1 -1
  43. package/dist/backend/llvm/lib-stream.js +23 -0
  44. package/dist/backend/llvm/lib-stream.js.map +1 -1
  45. package/dist/backend/native-toolchain.d.ts +3 -0
  46. package/dist/backend/native-toolchain.js +3 -1
  47. package/dist/backend/native-toolchain.js.map +1 -1
  48. package/dist/backend/runtime-pack.test.js +28 -10
  49. package/dist/backend/runtime-pack.test.js.map +1 -1
  50. package/dist/backend/targets.d.ts +4 -0
  51. package/dist/backend/targets.js +13 -0
  52. package/dist/backend/targets.js.map +1 -1
  53. package/dist/backend/targets.test.js +8 -1
  54. package/dist/backend/targets.test.js.map +1 -1
  55. package/dist/coverage/surface-manifest.js +8 -0
  56. package/dist/coverage/surface-manifest.js.map +1 -1
  57. package/dist/diagnostics/diagnostic.js +1 -1
  58. package/dist/diagnostics/diagnostic.js.map +1 -1
  59. package/dist/executable/executable-cache.d.ts +2 -0
  60. package/dist/executable/executable-cache.js +3 -0
  61. package/dist/executable/executable-cache.js.map +1 -1
  62. package/dist/frontend/fork-target.d.ts +17 -0
  63. package/dist/frontend/fork-target.js +256 -0
  64. package/dist/frontend/fork-target.js.map +1 -0
  65. package/dist/frontend/lowering/abstract-equality.d.ts +6 -0
  66. package/dist/frontend/lowering/abstract-equality.js +250 -0
  67. package/dist/frontend/lowering/abstract-equality.js.map +1 -0
  68. package/dist/frontend/lowering/lower-assert.js +71 -2
  69. package/dist/frontend/lowering/lower-assert.js.map +1 -1
  70. package/dist/frontend/lowering/lower-builtins.d.ts +50 -23
  71. package/dist/frontend/lowering/lower-builtins.js +939 -106
  72. package/dist/frontend/lowering/lower-builtins.js.map +1 -1
  73. package/dist/frontend/lowering/lower-calls.d.ts +29 -21
  74. package/dist/frontend/lowering/lower-calls.js +573 -121
  75. package/dist/frontend/lowering/lower-calls.js.map +1 -1
  76. package/dist/frontend/lowering/lower-classes.js +39 -8
  77. package/dist/frontend/lowering/lower-classes.js.map +1 -1
  78. package/dist/frontend/lowering/lower-containers.js +150 -0
  79. package/dist/frontend/lowering/lower-containers.js.map +1 -1
  80. package/dist/frontend/lowering/lower-event-emitter.js +212 -21
  81. package/dist/frontend/lowering/lower-event-emitter.js.map +1 -1
  82. package/dist/frontend/lowering/lower-exprs.js +135 -83
  83. package/dist/frontend/lowering/lower-exprs.js.map +1 -1
  84. package/dist/frontend/lowering/lower-inspect.d.ts +4 -0
  85. package/dist/frontend/lowering/lower-inspect.js +1 -1
  86. package/dist/frontend/lowering/lower-inspect.js.map +1 -1
  87. package/dist/frontend/lowering/lower-island.d.ts +6 -3
  88. package/dist/frontend/lowering/lower-island.js +165 -6
  89. package/dist/frontend/lowering/lower-island.js.map +1 -1
  90. package/dist/frontend/lowering/lower-modules.d.ts +6 -0
  91. package/dist/frontend/lowering/lower-modules.js +184 -38
  92. package/dist/frontend/lowering/lower-modules.js.map +1 -1
  93. package/dist/frontend/lowering/lower-namespaces.js +7 -0
  94. package/dist/frontend/lowering/lower-namespaces.js.map +1 -1
  95. package/dist/frontend/lowering/lower-server.d.ts +1 -1
  96. package/dist/frontend/lowering/lower-server.js +130 -27
  97. package/dist/frontend/lowering/lower-server.js.map +1 -1
  98. package/dist/frontend/lowering/lower-stmts.js +118 -15
  99. package/dist/frontend/lowering/lower-stmts.js.map +1 -1
  100. package/dist/frontend/lowering/lower-stream.d.ts +3 -0
  101. package/dist/frontend/lowering/lower-stream.js +7 -0
  102. package/dist/frontend/lowering/lower-stream.js.map +1 -1
  103. package/dist/frontend/lowering/lowerer.d.ts +86 -9
  104. package/dist/frontend/lowering/lowerer.js +412 -72
  105. package/dist/frontend/lowering/lowerer.js.map +1 -1
  106. package/dist/frontend/lowering/surfaces.d.ts +26 -0
  107. package/dist/frontend/lowering/surfaces.js +140 -77
  108. package/dist/frontend/lowering/surfaces.js.map +1 -1
  109. package/dist/frontend/npm-static-declarations.d.ts +4 -0
  110. package/dist/frontend/npm-static-declarations.js +56 -0
  111. package/dist/frontend/npm-static-declarations.js.map +1 -1
  112. package/dist/frontend/npm-static-declarations.test.js +23 -2
  113. package/dist/frontend/npm-static-declarations.test.js.map +1 -1
  114. package/dist/frontend/npm-static.js +6 -5
  115. package/dist/frontend/npm-static.js.map +1 -1
  116. package/dist/frontend/npm.d.ts +15 -3
  117. package/dist/frontend/npm.js +80 -8
  118. package/dist/frontend/npm.js.map +1 -1
  119. package/dist/frontend/npm.test.js +76 -9
  120. package/dist/frontend/npm.test.js.map +1 -1
  121. package/dist/frontend/program.js +92 -10
  122. package/dist/frontend/program.js.map +1 -1
  123. package/dist/frontend/resolve.d.ts +6 -0
  124. package/dist/frontend/resolve.js +7 -4
  125. package/dist/frontend/resolve.js.map +1 -1
  126. package/dist/frontend/type-mapper.d.ts +5 -0
  127. package/dist/frontend/type-mapper.js +64 -24
  128. package/dist/frontend/type-mapper.js.map +1 -1
  129. package/dist/index.d.ts +5 -0
  130. package/dist/index.js +24 -3
  131. package/dist/index.js.map +1 -1
  132. package/dist/ir/ir.d.ts +93 -47
  133. package/dist/ir/ir.js +63 -4
  134. package/dist/ir/ir.js.map +1 -1
  135. package/dist/ir/ir.test.js +14 -1
  136. package/dist/ir/ir.test.js.map +1 -1
  137. package/dist/ir/serialize.d.ts +1 -1
  138. package/dist/ir/serialize.js +1 -1
  139. package/dist/ir/serialize.js.map +1 -1
  140. package/dist/ir/validate.js +165 -13
  141. package/dist/ir/validate.js.map +1 -1
  142. package/dist/library/int-infer.test.js +4 -4
  143. package/dist/library/int-infer.test.js.map +1 -1
  144. package/dist/library/library-cache.test.js +5 -5
  145. package/dist/library/library-cache.test.js.map +1 -1
  146. package/package.json +17 -17
  147. package/surface-manifest.json +132 -38
@@ -23,7 +23,7 @@ import { ambientNsRootOf, ambientUndefReadType, ambientUndefVarRootOf, ambientUn
23
23
  import { expandoMemberRead, expandoWritableTarget } from "./lower-expando.js";
24
24
  import { lowerSocketInstanceOf, lowerTlsRootCertificates } from "./lower-server.js";
25
25
  import { findGenericMethodOn, lowerStaticFieldRead } from "./lower-classes.js";
26
- import { bindingNeverReassigned, implicitMonoFile, lowerTaggedTemplate, nullishGenericBindingUnitOf, objLitGenericFnInfoOf, objLitGenericFnNodeOf, requireObjLitGenericReceiver } from "./lower-calls.js";
26
+ import { bindingNeverReassigned, funcTypeFromParamShapes, implicitMonoFile, lowerTaggedTemplate, nullishGenericBindingUnitOf, objLitGenericFnInfoOf, objLitGenericFnNodeOf, requireObjLitGenericReceiver } from "./lower-calls.js";
27
27
  import { mixinFnOfCallee } from "./lower-mixins.js";
28
28
  import { isConstAssertionTypeNode, isGenericCallableMemberType, isParseArgsDynTypeName, underConstAssertion, unitOnlyUnion } from "../type-mapper.js";
29
29
  import { lowerYield } from "./lower-generators.js";
@@ -34,6 +34,7 @@ import { hasOptionalChainGuard, isOptionalChainTail, isRequireMainFilename } fro
34
34
  import { conditionalSpreadOf, foldedStringKeyOf } from "./expressions/object-literals.js";
35
35
  import { tryLowerExpression } from "./expressions/try-lower-expression.js";
36
36
  import { fenceNodeModuleMutation, isNodeModuleValue, lowerNodeModuleIdentifier, lowerNodeModuleProperty, lowerRequireCacheElement, lowerRequireCacheHas, lowerRequireMainProperty } from "./lower-node-module.js";
37
+ import { lowerAbstractEquality } from "./abstract-equality.js";
37
38
  /** A template piece's RAW text (String.raw's contract: escapes stay
38
39
  * characters). 7's client AST ships no rawText at runtime (the typing
39
40
  * declares it; the serialized node data omits it), so the raw span comes
@@ -598,6 +599,7 @@ function lowerExprInner(lowerer, expr) {
598
599
  symbol: "symbol",
599
600
  map: "object", set: "object", promise: "object", bytes: "object",
600
601
  regexp: "object", generator: "object", classval: "function",
602
+ moduleNs: "object",
601
603
  undefinedT: "undefined", nullT: "object",
602
604
  };
603
605
  const folded = FOLD[operand.type.kind];
@@ -649,13 +651,22 @@ function lowerExprInner(lowerer, expr) {
649
651
  if (expr.text === "arguments" && lowerer.ctx.argumentsLocal) {
650
652
  return { kind: "varRef", localId: lowerer.ctx.argumentsLocal.id, type: DYN, loc };
651
653
  }
652
- // A `const x = promisify(execFile)` binding as a VALUE: the
653
- // promisified function never exists at runtime (its calls lower to
654
- // the interned async-exec helper) — call it directly.
654
+ // A compiler-projected adapter as a VALUE: the adapter has no
655
+ // runtime function object (its calls rewrite directly) — call it
656
+ // through its immutable binding instead.
655
657
  {
656
658
  const sym = lowerer.resolveValueSymbol(expr);
657
- if (sym && lowerer.promisifiedExecFile.has(sym)) {
658
- lowerer.unsupported("SC1090", expr, `a promisified execFile as a value (call '${expr.text}' directly)`);
659
+ const projection = sym ? lowerer.staticCallables.get(sym) : undefined;
660
+ if (projection?.kind === "promisified-exec-file" || projection?.kind === "promisified-builtin") {
661
+ lowerer.unsupported("SC1090", expr, `a promisified builtin as a value (call '${expr.text}' directly)`);
662
+ }
663
+ if (projection?.kind === "builtin-function") {
664
+ if (!isJsSourceFile(expr.getSourceFile())) {
665
+ const callable = lowerer.lowerBuiltinCallableValue(projection, loc);
666
+ if (callable)
667
+ return callable;
668
+ }
669
+ lowerer.unsupported("SC1090", expr, `a builtin function alias as an escaping value (call '${expr.text}' directly)`);
659
670
  }
660
671
  }
661
672
  // Union-typed bindings read through tsc's control-flow narrowing:
@@ -809,17 +820,12 @@ function lowerExprInner(lowerer, expr) {
809
820
  if (sig && lowerer.isTopLevelFnSymbol(expr)) {
810
821
  // A declared function used as a value: a zero-capture closure. The
811
822
  // backend interns one per function so `f === f` holds (JS identity).
812
- // The value's type is the completed ABI signature — exact-arity, so
813
- // optional/default/rest declarations pass the value fence first.
823
+ // The value's type is the completed ABI signature; optional/default
824
+ // slots and typed rest arrays pass the value fence first.
814
825
  lowerer.noteEdge(sig.name);
815
826
  // dynRest slots stay out of the VALUE type's param list (fn.length
816
827
  // semantics); the rest marker carries the trailing dyn-array ABI.
817
- const funcType = {
818
- kind: "func",
819
- params: sig.params.filter((p) => p.mode !== "dynRest").map((p) => p.type),
820
- ret: sig.returnType,
821
- ...(sig.params.some((p) => p.mode === "dynRest") ? { rest: true } : {}),
822
- };
828
+ const funcType = funcTypeFromParamShapes(sig.params, sig.returnType);
823
829
  lowerer.requireExactArityValue(expr, expr, sig.params, funcType);
824
830
  return { kind: "closure", fnName: sig.name, captures: [], type: funcType, loc };
825
831
  }
@@ -879,6 +885,11 @@ function lowerExprInner(lowerer, expr) {
879
885
  if (isJsSourceFile(expr.getSourceFile())) {
880
886
  return { kind: "strLit", value: `[builtin ${bi.module}.${bi.member}]`, type: STRING, loc };
881
887
  }
888
+ {
889
+ const callable = lowerer.lowerBuiltinCallableValue(bi, loc);
890
+ if (callable)
891
+ return callable;
892
+ }
882
893
  if (builtinModuleFnOf(lowerer, bi.module, bi.member)) {
883
894
  lowerer.unsupported("SC1090", expr, `library functions as values (call '${expr.text}' directly)`);
884
895
  }
@@ -889,7 +900,12 @@ function lowerExprInner(lowerer, expr) {
889
900
  // the namespace object has no representation — members lower at
890
901
  // their access sites only. The CommonJS namespace binding
891
902
  // (`const lib = require("./lib.js")`) gets the same fence.
892
- if (lowerer.builtinNamespaceModuleOf(expr) !== null || lowerer.cjsLocalModuleBindingOf(expr)) {
903
+ const builtinNamespace = lowerer.builtinNamespaceModuleOf(expr);
904
+ if (builtinNamespace !== null) {
905
+ const type = { kind: "moduleNs", moduleId: `builtin:${builtinNamespace}` };
906
+ return { kind: "moduleNsRef", moduleId: type.moduleId, type, loc };
907
+ }
908
+ if (lowerer.cjsLocalModuleBindingOf(expr)) {
893
909
  lowerer.unsupported("SC1090", expr, `module namespace objects as values (access '${expr.text}' members directly)`);
894
910
  }
895
911
  if (lowerer.islandGlobalFnOf(expr)) {
@@ -941,11 +957,14 @@ function lowerExprInner(lowerer, expr) {
941
957
  // type-mapper.ts pins the one concrete signature `(value: string) =>
942
958
  // primitive`; direct calls `String(x)` never reach here — the call
943
959
  // lowering intercepts them with the wider static coercions).
944
- // JavaScript sources keep the identity-token path below.
960
+ const jsPrimitiveCtorSelector = isJsSourceFile(expr.getSourceFile()) &&
961
+ expr.parent !== undefined &&
962
+ ts.isArrowFunction(expr.parent) &&
963
+ expr.parent.body === expr;
945
964
  if ((expr.text === "String" || expr.text === "Number" || expr.text === "Boolean") &&
946
- !isJsSourceFile(expr.getSourceFile()) &&
965
+ (!isJsSourceFile(expr.getSourceFile()) || jsPrimitiveCtorSelector) &&
947
966
  lowerer.isStdlibSymbol(lowerer.checker.getSymbolAtLocation(expr))) {
948
- return primitiveCtorClosure(lowerer, expr.text, loc);
967
+ return primitiveCtorClosure(lowerer, expr.text, loc, jsPrimitiveCtorSelector);
949
968
  }
950
969
  // The lib fence's IDENTIFIER chokepoint: the real standard library
951
970
  // resolves names the old minimal ambient world never declared
@@ -1081,6 +1100,15 @@ function lowerExprInner(lowerer, expr) {
1081
1100
  // representation (ambient namespaces compile to Node's
1082
1101
  // ReferenceError instead — the object never exists at runtime).
1083
1102
  {
1103
+ const moduleNs = lowerer.mapTypeOf(lowerer.typeOf(expr));
1104
+ if (moduleNs?.kind === "moduleNs") {
1105
+ return { kind: "moduleNsRef", moduleId: moduleNs.moduleId, type: moduleNs, loc };
1106
+ }
1107
+ const builtinNs = lowerer.builtinNamespaceModuleOf(expr);
1108
+ if (builtinNs !== null) {
1109
+ const type = { kind: "moduleNs", moduleId: `builtin:${builtinNs}` };
1110
+ return { kind: "moduleNsRef", moduleId: type.moduleId, type, loc };
1111
+ }
1084
1112
  const ns = lowerNsIdentifierValue(lowerer, expr);
1085
1113
  if (ns)
1086
1114
  return ns;
@@ -3084,10 +3112,10 @@ export function lowerIntrinsicProperty(lowerer, expr) {
3084
3112
  const receiver = lowerer.lowerExpr(expr.expression);
3085
3113
  return { kind: "libCall", fn: "child.killed", args: [receiver], type: BOOL, loc };
3086
3114
  }
3087
- if (name === "on" || name === "kill" || name === "unref") {
3115
+ if (name === "on" || name === "once" || name === "kill" || name === "unref") {
3088
3116
  lowerer.unsupported("SC1090", expr, `child methods as values (call '${name}' directly)`);
3089
3117
  }
3090
- lowerer.noLowering(`ChildProcess.${name}`, expr, "on(\"exit\" | \"error\", cb), pid, exitCode, killed, kill(signal?), and unref() are the supported ChildProcess members", lowerer.checker.getSymbolAtLocation(expr.name));
3118
+ lowerer.noLowering(`ChildProcess.${name}`, expr, "on/once(\"exit\" | \"close\" | \"error\", cb), pid, exitCode, killed, kill(signal?), and unref() are the supported ChildProcess members", lowerer.checker.getSymbolAtLocation(expr.name));
3091
3119
  }
3092
3120
  if (kind === "spawnRes") {
3093
3121
  // The spawnSync-result reads. status is the interned `number | null`
@@ -3137,7 +3165,7 @@ export function lowerIntrinsicProperty(lowerer, expr) {
3137
3165
  if (name === "isFile" || name === "isDirectory") {
3138
3166
  lowerer.unsupported("SC1090", expr, `Stats methods as values (call '${name}' directly)`);
3139
3167
  }
3140
- lowerer.noLowering(`Stats.${name}`, expr, "isFile(), isDirectory(), isSymbolicLink(), size, blocks, nlink, atimeMs, and mtimeMs are the supported Stats members", lowerer.checker.getSymbolAtLocation(expr.name));
3168
+ lowerer.noLowering(`Stats.${name}`, expr, "isFile(), isDirectory(), isSymbolicLink(), dev, ino, size, blocks, nlink, atimeMs, mtimeMs, and ctimeMs are the supported Stats members", lowerer.checker.getSymbolAtLocation(expr.name));
3141
3169
  }
3142
3170
  if (kind === "date") {
3143
3171
  const methods = new Set([
@@ -3178,22 +3206,30 @@ export function lowerIntrinsicProperty(lowerer, expr) {
3178
3206
  // serialization: lowercased hostname, `:port` appended exactly when
3179
3207
  // a non-default port is present (scr_url_host — Node-exact,
3180
3208
  // opaque-path URLs answer ""); `hostname` is the stored port-less
3181
- // host field verbatim (Node would keep IPv6 brackets here, but the
3182
- // parser rejects IPv6 hosts — documented divergence — so the getter
3183
- // never sees one).
3184
- if (name === "protocol" || name === "pathname" || name === "href" || name === "host" || name === "hostname" || name === "search") {
3209
+ // host field verbatim, including IPv6 brackets.
3210
+ if (name === "protocol" || name === "origin" || name === "username" || name === "password" || name === "pathname" || name === "href" || name === "host" || name === "hostname" || name === "port" || name === "search" || name === "hash") {
3185
3211
  const receiver = lowerer.lowerExpr(expr.expression);
3186
3212
  const fn = name === "protocol"
3187
3213
  ? "url.protocol"
3188
- : name === "pathname"
3189
- ? "url.pathname"
3190
- : name === "host"
3191
- ? "url.host"
3192
- : name === "hostname"
3193
- ? "url.hostname"
3194
- : name === "search"
3195
- ? "url.search"
3196
- : "url.href";
3214
+ : name === "origin"
3215
+ ? "url.origin"
3216
+ : name === "username"
3217
+ ? "url.username"
3218
+ : name === "password"
3219
+ ? "url.password"
3220
+ : name === "pathname"
3221
+ ? "url.pathname"
3222
+ : name === "host"
3223
+ ? "url.host"
3224
+ : name === "hostname"
3225
+ ? "url.hostname"
3226
+ : name === "port"
3227
+ ? "url.port"
3228
+ : name === "search"
3229
+ ? "url.search"
3230
+ : name === "hash"
3231
+ ? "url.hash"
3232
+ : "url.href";
3197
3233
  return { kind: "libCall", fn, args: [receiver], type: STRING, loc: locOf(expr) };
3198
3234
  }
3199
3235
  // `u.searchParams`: the LIVE cached view (one identity per URL —
@@ -3206,7 +3242,7 @@ export function lowerIntrinsicProperty(lowerer, expr) {
3206
3242
  if (name === "toString" || name === "toJSON") {
3207
3243
  lowerer.unsupported("SC1090", expr, `URL methods as values (call '${name}' directly)`);
3208
3244
  }
3209
- lowerer.noLowering(`URL.${name}`, expr, "protocol, pathname, href, host, hostname, search, searchParams, and toString() are the supported URL members", lowerer.checker.getSymbolAtLocation(expr.name));
3245
+ lowerer.noLowering(`URL.${name}`, expr, "protocol, origin, username, password, pathname, href, host, hostname, port, search, hash, searchParams, and toString() are the supported URL members", lowerer.checker.getSymbolAtLocation(expr.name));
3210
3246
  }
3211
3247
  if (kind === "searchParams") {
3212
3248
  const SEARCH_PARAMS_METHODS = new Set(["get", "getAll", "set", "append", "delete", "has", "sort", "toString", "forEach", "keys", "values", "entries"]);
@@ -5229,7 +5265,14 @@ export function lowerPrefixUnary(lowerer, expr) {
5229
5265
  if (raw.type.kind === "bigint") {
5230
5266
  return { kind: "libCall", fn: "bigint.not", args: [raw], type: BIGINT_T, loc };
5231
5267
  }
5232
- const operand = lowerOptionalNumber(lowerer, raw, loc, expr.operand);
5268
+ // A checked-dynamic value retains enough JavaScript structure to
5269
+ // run exact ToNumber, including the object valueOf/toString
5270
+ // protocol. This is the static `any`/JS-lane answer for idioms
5271
+ // such as `while (~index)`, where inference may have widened an
5272
+ // otherwise numeric slot to the checked-dynamic tree.
5273
+ const operand = raw.type.kind === "dyn"
5274
+ ? { kind: "libCall", fn: "dyn.toNumberCoerce", args: [raw], type: F64, loc }
5275
+ : lowerOptionalNumber(lowerer, raw, loc, expr.operand);
5233
5276
  if (operand.type.kind !== "f64")
5234
5277
  lowerer.unsupported("SC1043", expr);
5235
5278
  return { kind: "unary", op: "~", operand, type: F64, loc };
@@ -5518,23 +5561,10 @@ export function lowerBinary(lowerer, expr) {
5518
5561
  const nullTest = lowerLooseNullCompare(lowerer, expr, loc);
5519
5562
  if (nullTest)
5520
5563
  return nullTest;
5521
- // SAME-KIND loose equality IS strict equality (the spec's ==
5522
- // dispatches to === when both operands share a type): `typeof v ==
5523
- // 'object'`, `n != 0`, `flag == true` all lower exactly. Mixed
5524
- // kinds (where == coerces) keep the fence.
5525
- {
5526
- const negated = op === ts.SyntaxKind.ExclamationEqualsToken;
5527
- const left = lowerer.lowerExpr(expr.left);
5528
- const right = lowerer.lowerExpr(expr.right);
5529
- if (left.type.kind === "string" && right.type.kind === "string") {
5530
- return { kind: "strEq", negated, left, right, type: BOOL, loc };
5531
- }
5532
- if ((left.type.kind === "f64" && right.type.kind === "f64") ||
5533
- (left.type.kind === "bool" && right.type.kind === "bool")) {
5534
- return { kind: "bin", op: negated ? "!==" : "===", left, right, type: BOOL, loc };
5535
- }
5536
- }
5537
- lowerer.unsupported("SC1040", expr);
5564
+ const loose = lowerAbstractEquality(lowerer, lowerer.lowerExpr(expr.left), lowerer.lowerExpr(expr.right), op === ts.SyntaxKind.ExclamationEqualsToken, loc);
5565
+ if (loose)
5566
+ return loose;
5567
+ lowerer.unsupported("SC1040", expr, undefined, "object-to-primitive loose equality can call user-defined valueOf/toString methods — compare explicit primitive conversions instead");
5538
5568
  }
5539
5569
  if (op === ts.SyntaxKind.AmpersandAmpersandToken || op === ts.SyntaxKind.BarBarToken) {
5540
5570
  if (op === ts.SyntaxKind.BarBarToken) {
@@ -5558,6 +5588,15 @@ export function lowerBinary(lowerer, expr) {
5558
5588
  if (left.kind === "unitLit") {
5559
5589
  return op === ts.SyntaxKind.BarBarToken ? right : left;
5560
5590
  }
5591
+ // JS objects and symbols are always truthy. In `ref || fallback`
5592
+ // the fallback is therefore unreachable and the result is the
5593
+ // already-evaluated left value — the browser-fallback idiom used by
5594
+ // packages for `process.argv || []` and `process.env || {}`. The
5595
+ // operand itself remains in the IR, preserving reads/calls that
5596
+ // produce the reference; only the unreachable right lowering drops.
5597
+ if (op === ts.SyntaxKind.BarBarToken && REF_TRUTHY_KINDS.has(left.type.kind)) {
5598
+ return left;
5599
+ }
5561
5600
  if (left.type.kind === "dyn" || right.type.kind === "dyn") {
5562
5601
  // A checked-dynamic operand (`fn.name || '<anonymous>'` —
5563
5602
  // test/common's _mustCallInner): both sides live in the checked-dynamic tree and
@@ -6253,6 +6292,9 @@ export function lowerBinary(lowerer, expr) {
6253
6292
  if (idLeft.type.kind === "classval" && idRight.type.kind === "classval") {
6254
6293
  return { kind: "bin", op: negated ? "!==" : "===", left: idLeft, right: idRight, type: BOOL, loc };
6255
6294
  }
6295
+ if (idLeft.type.kind === "moduleNs" && idRight.type.kind === "moduleNs") {
6296
+ return { kind: "bin", op: negated ? "!==" : "===", left: idLeft, right: idRight, type: BOOL, loc };
6297
+ }
6256
6298
  if ((idLeft.type.kind === "array" ||
6257
6299
  idLeft.type.kind === "map" ||
6258
6300
  idLeft.type.kind === "set" ||
@@ -7359,10 +7401,10 @@ function lowerInExpression(lowerer, expr, loc) {
7359
7401
  // Compile-time-known STRING keys fold — literals, and the same
7360
7402
  // const/enum-literal and template folding computed property keys get
7361
7403
  // (foldedStringKeyOf); runtime-valued keys keep the fence.
7362
- // NUMERIC literal keys answer on ARRAY receivers: dense arrays hold
7363
- // exactly the indices [0, length), so `3 in xs` is a length test (and
7364
- // a negative/fractional literal is a constant miss — the receiver
7365
- // still evaluates once through its own length read).
7404
+ // NUMERIC literal keys answer on ARRAY receivers through the shared
7405
+ // presence query. Arrays retain holes independently from length, and
7406
+ // noncanonical numeric keys live in their ordinary-property table, so
7407
+ // a length comparison is not an honest `in` answer.
7366
7408
  {
7367
7409
  let kNode = expr.left;
7368
7410
  while (ts.isParenthesizedExpression(kNode))
@@ -7370,12 +7412,14 @@ function lowerInExpression(lowerer, expr, loc) {
7370
7412
  if (ts.isNumericLiteral(kNode) && lowerer.mapTypeOf(lowerer.typeOf(expr.right))?.kind === "array") {
7371
7413
  const recvArr = lowerer.lowerExpr(expr.right);
7372
7414
  if (recvArr.type.kind === "array") {
7373
- const len = { kind: "arrIntrinsic", method: "length", receiver: recvArr, args: [], type: F64, loc };
7374
7415
  const n = Number(kNode.text);
7375
- if (Number.isInteger(n) && n >= 0) {
7376
- return { kind: "bin", op: "<", left: { kind: "numLit", value: n, type: F64, loc }, right: len, type: BOOL, loc };
7377
- }
7378
- return { kind: "bin", op: "<", left: len, right: { kind: "numLit", value: 0, type: F64, loc }, type: BOOL, loc };
7416
+ return {
7417
+ kind: "arrayHas",
7418
+ arr: recvArr,
7419
+ index: { kind: "numLit", value: n, type: F64, loc },
7420
+ type: BOOL,
7421
+ loc,
7422
+ };
7379
7423
  }
7380
7424
  }
7381
7425
  }
@@ -9011,32 +9055,40 @@ function lowerStreamObjectProperty(lowerer, expr) {
9011
9055
  return lowerStreamProperty(lowerer, expr, info);
9012
9056
  }
9013
9057
  /** The primitive-constructor closure (`String`/`Number`/`Boolean` as a
9014
- * VALUE): interns one synthesized module function per constructor —
9015
- * `%builtin.String` et al. — and returns the zero-capture closure over it.
9016
- * The body IS the string-coercion the type mapping promised
9017
- * (`(value: string) => primitive`): String is identity, Number the ECMA
9018
- * StringToNumber (num.fromString — the same runtime call the direct
9019
- * `Number(s)` lowering emits), Boolean the emptiness test. Interning makes
9020
- * every reference the SAME immortal closure, so `opt.type === String`
9021
- * compares like JS function identity. */
9022
- function primitiveCtorClosure(lowerer, name, loc) {
9058
+ * VALUE): interns one synthesized module function per constructor/ABI and
9059
+ * returns the zero-capture closure over it. Typed sources use the mapped
9060
+ * `(value: string) => primitive` signature; a recognized JavaScript
9061
+ * concise-arrow selector (`() => String`, the picocolors fallback) takes
9062
+ * dyn so its ordinary coercion signature survives inference residue. The
9063
+ * dyn bodies run exact ToString/ToNumber/ToBoolean, including object hooks.
9064
+ * Interning makes every reference under one ABI the SAME immortal closure,
9065
+ * so `opt.type === String` compares like JS function identity. */
9066
+ function primitiveCtorClosure(lowerer, name, loc, dynamicInput) {
9023
9067
  const ret = name === "String" ? STRING : name === "Number" ? F64 : BOOL;
9024
- const fnT = funcOf([STRING], ret);
9025
- let fnName = lowerer.primitiveCtorFns.get(name);
9068
+ const input = dynamicInput ? DYN : STRING;
9069
+ const fnT = funcOf([input], ret);
9070
+ const key = `${name}:${dynamicInput ? "dyn" : "string"}`;
9071
+ let fnName = lowerer.primitiveCtorFns.get(key);
9026
9072
  if (!fnName) {
9027
- fnName = `%builtin.${name}`;
9028
- lowerer.primitiveCtorFns.set(name, fnName);
9029
- const s = { kind: "varRef", localId: "v.0", type: STRING, loc };
9030
- const value = name === "String"
9031
- ? s
9032
- : name === "Number"
9033
- ? { kind: "libCall", fn: "num.fromString", args: [s], type: F64, loc }
9034
- : { kind: "strEq", negated: true, left: s, right: { kind: "strLit", value: "", type: STRING, loc }, type: BOOL, loc };
9073
+ fnName = `%builtin.${name}${dynamicInput ? ".dyn" : ""}`;
9074
+ lowerer.primitiveCtorFns.set(key, fnName);
9075
+ const v = { kind: "varRef", localId: "v.0", type: input, loc };
9076
+ const value = dynamicInput
9077
+ ? name === "String"
9078
+ ? { kind: "libCall", fn: "dyn.toStringCoerce", args: [v], type: STRING, loc }
9079
+ : name === "Number"
9080
+ ? { kind: "libCall", fn: "dyn.toNumberCoerce", args: [v], type: F64, loc }
9081
+ : { kind: "dynTest", test: "truthy", value: v, type: BOOL, loc }
9082
+ : name === "String"
9083
+ ? v
9084
+ : name === "Number"
9085
+ ? { kind: "libCall", fn: "num.fromString", args: [v], type: F64, loc }
9086
+ : { kind: "strEq", negated: true, left: v, right: { kind: "strLit", value: "", type: STRING, loc }, type: BOOL, loc };
9035
9087
  const fn = {
9036
9088
  name: fnName,
9037
- params: [{ localId: "v.0", name: "value", type: STRING }],
9089
+ params: [{ localId: "v.0", name: "value", type: input }],
9038
9090
  returnType: ret,
9039
- locals: [{ id: "v.0", name: "value", type: STRING, mutable: false }],
9091
+ locals: [{ id: "v.0", name: "value", type: input, mutable: false }],
9040
9092
  body: [{ kind: "return", value, loc }],
9041
9093
  loc,
9042
9094
  };