@scriptc/compiler 0.0.9 → 0.0.11

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 (97) hide show
  1. package/ambient/scriptc-node-fallback.d.ts +54 -9
  2. package/dist/backend/emission/emit-exprs.js +170 -14
  3. package/dist/backend/emission/emit-exprs.js.map +1 -1
  4. package/dist/backend/emission/emit-walkers.js +29 -2
  5. package/dist/backend/emission/emit-walkers.js.map +1 -1
  6. package/dist/backend/emission/emitter.js +25 -1
  7. package/dist/backend/emission/emitter.js.map +1 -1
  8. package/dist/backend/llvm/dyn.d.ts +1 -0
  9. package/dist/backend/llvm/dyn.js +83 -1
  10. package/dist/backend/llvm/dyn.js.map +1 -1
  11. package/dist/backend/llvm/emitter.js +210 -13
  12. package/dist/backend/llvm/emitter.js.map +1 -1
  13. package/dist/coverage/surface-manifest.js +16 -2
  14. package/dist/coverage/surface-manifest.js.map +1 -1
  15. package/dist/diagnostics/diagnostic.d.ts +41 -14
  16. package/dist/diagnostics/diagnostic.js +79 -27
  17. package/dist/diagnostics/diagnostic.js.map +1 -1
  18. package/dist/diagnostics/render.js +8 -0
  19. package/dist/diagnostics/render.js.map +1 -1
  20. package/dist/frontend/lowering/lib-boundary.js +11 -1
  21. package/dist/frontend/lowering/lib-boundary.js.map +1 -1
  22. package/dist/frontend/lowering/lower-builtins.d.ts +69 -0
  23. package/dist/frontend/lowering/lower-builtins.js +380 -4
  24. package/dist/frontend/lowering/lower-builtins.js.map +1 -1
  25. package/dist/frontend/lowering/lower-calls.d.ts +41 -0
  26. package/dist/frontend/lowering/lower-calls.js +559 -11
  27. package/dist/frontend/lowering/lower-calls.js.map +1 -1
  28. package/dist/frontend/lowering/lower-classes.d.ts +7 -0
  29. package/dist/frontend/lowering/lower-classes.js +51 -1
  30. package/dist/frontend/lowering/lower-classes.js.map +1 -1
  31. package/dist/frontend/lowering/lower-containers.d.ts +21 -0
  32. package/dist/frontend/lowering/lower-containers.js +262 -17
  33. package/dist/frontend/lowering/lower-containers.js.map +1 -1
  34. package/dist/frontend/lowering/lower-dgram.js +92 -17
  35. package/dist/frontend/lowering/lower-dgram.js.map +1 -1
  36. package/dist/frontend/lowering/lower-emitter.d.ts +70 -3
  37. package/dist/frontend/lowering/lower-emitter.js +436 -29
  38. package/dist/frontend/lowering/lower-emitter.js.map +1 -1
  39. package/dist/frontend/lowering/lower-exprs.d.ts +80 -5
  40. package/dist/frontend/lowering/lower-exprs.js +651 -23
  41. package/dist/frontend/lowering/lower-exprs.js.map +1 -1
  42. package/dist/frontend/lowering/lower-modules.d.ts +8 -5
  43. package/dist/frontend/lowering/lower-modules.js +144 -19
  44. package/dist/frontend/lowering/lower-modules.js.map +1 -1
  45. package/dist/frontend/lowering/lower-namespaces.d.ts +39 -11
  46. package/dist/frontend/lowering/lower-namespaces.js +166 -24
  47. package/dist/frontend/lowering/lower-namespaces.js.map +1 -1
  48. package/dist/frontend/lowering/lower-server.js +211 -4
  49. package/dist/frontend/lowering/lower-server.js.map +1 -1
  50. package/dist/frontend/lowering/lower-stmts.d.ts +1 -5
  51. package/dist/frontend/lowering/lower-stmts.js +293 -10
  52. package/dist/frontend/lowering/lower-stmts.js.map +1 -1
  53. package/dist/frontend/lowering/lower-stream.js +178 -10
  54. package/dist/frontend/lowering/lower-stream.js.map +1 -1
  55. package/dist/frontend/lowering/lowerer.d.ts +53 -1
  56. package/dist/frontend/lowering/lowerer.js +210 -19
  57. package/dist/frontend/lowering/lowerer.js.map +1 -1
  58. package/dist/frontend/lowering/surfaces.d.ts +44 -0
  59. package/dist/frontend/lowering/surfaces.js +234 -3
  60. package/dist/frontend/lowering/surfaces.js.map +1 -1
  61. package/dist/frontend/npm.d.ts +12 -0
  62. package/dist/frontend/npm.js +30 -0
  63. package/dist/frontend/npm.js.map +1 -1
  64. package/dist/frontend/program.d.ts +25 -0
  65. package/dist/frontend/program.js +144 -135
  66. package/dist/frontend/program.js.map +1 -1
  67. package/dist/frontend/shared.d.ts +1 -1
  68. package/dist/frontend/shared.js +1 -1
  69. package/dist/frontend/shared.js.map +1 -1
  70. package/dist/frontend/types.d.ts +13 -0
  71. package/dist/frontend/types.js +84 -7
  72. package/dist/frontend/types.js.map +1 -1
  73. package/dist/index.d.ts +2 -1
  74. package/dist/index.js +157 -12
  75. package/dist/index.js.map +1 -1
  76. package/dist/ir/nodes.d.ts +163 -13
  77. package/dist/ir/nodes.js +65 -6
  78. package/dist/ir/nodes.js.map +1 -1
  79. package/dist/ir/validate.js +64 -5
  80. package/dist/ir/validate.js.map +1 -1
  81. package/dist/library/fence-eval.d.ts +77 -0
  82. package/dist/library/fence-eval.js +395 -0
  83. package/dist/library/fence-eval.js.map +1 -0
  84. package/dist/library/int-infer.d.ts +119 -0
  85. package/dist/library/int-infer.js +1301 -0
  86. package/dist/library/int-infer.js.map +1 -0
  87. package/dist/library/int-infer.test.d.ts +1 -0
  88. package/dist/library/int-infer.test.js +279 -0
  89. package/dist/library/int-infer.test.js.map +1 -0
  90. package/dist/library/profile.d.ts +43 -6
  91. package/dist/library/profile.js +179 -31
  92. package/dist/library/profile.js.map +1 -1
  93. package/dist/library/sidecar.d.ts +23 -0
  94. package/dist/library/sidecar.js +150 -16
  95. package/dist/library/sidecar.js.map +1 -1
  96. package/package.json +2 -2
  97. package/surface-manifest.json +208 -10
@@ -274,6 +274,23 @@ export type ForOfIterProjection = "keys" | "values" | "entries";
274
274
  * enter/exit bracketing: the pair list compacts immediately (no
275
275
  * tombstones), so there is no iteration depth to keep exact. */
276
276
  export declare function lowerForOfSearchParams(L: Lowerer, stmt: ts.ForOfStatement, iterable: IrExpr, proj?: ForOfIterProjection): IrStmt;
277
+ /** for-of over an ARRAY's keys()/entries() projections consumed directly
278
+ * by the head (`for (const [index, line] of lines.entries())` — the
279
+ * dominant formatter idiom): the LIVE index walk — the length re-reads
280
+ * every pass, exactly the array iterator's contract (elements appended
281
+ * mid-walk are visited; a shrink ends the loop early) — yielding the
282
+ * index (`keys`, a number) or the [index, element] pair (`entries`).
283
+ * `values` never lands here: lowerForOf's receiver unwrap owns it. A
284
+ * `[i, v]` head of plain identifiers binds straight from the reads (the
285
+ * tuple never materializes); identifier heads and the remaining
286
+ * patterns bind through the checker's own element type — the interned
287
+ * [number, T] tuple record. Stored iterator OBJECTS keep their fence
288
+ * (only the direct for-of position unwraps). */
289
+ export declare function lowerForOfArrayIter(L: Lowerer, stmt: ts.ForOfStatement, iterable: IrExpr & {
290
+ type: IrType & {
291
+ kind: "array";
292
+ };
293
+ }, proj: "keys" | "entries"): IrStmt;
277
294
  export declare function lowerForOfMap(L: Lowerer, stmt: ts.ForOfStatement, iterable: IrExpr, mapT: IrType & {
278
295
  kind: "map";
279
296
  }, proj?: ForOfIterProjection): IrStmt;
@@ -327,6 +344,10 @@ export declare function lowerBytesNew(L: Lowerer, expr: ts.NewExpression, symbol
327
344
  * else the lib declares (fill, indexOf, reverse, ...) falls through to
328
345
  * the SC2020 member fence. Null when this isn't a bytes method call. */
329
346
  export declare function lowerBytesMethodCall(L: Lowerer, call: ts.CallExpression, access: ts.PropertyAccessExpression): IrExpr | null;
347
+ /** A literal encoding's canonical name, or undefined for a spelling Node
348
+ * does not know — the ladder callers (stream options) turn unknown
349
+ * literals into Node's runtime ERR_UNKNOWN_ENCODING throw. */
350
+ export declare function knownBufEncoding(name: string): string | undefined;
330
351
  /** The literal encoding argument of a Buffer surface, normalized — or a
331
352
  * fence when it isn't a literal alias Node knows. */
332
353
  export declare function bufEncoding(L: Lowerer, what: string, encNode: ts.Expression): string;
@@ -2649,23 +2649,27 @@ export function lowerIteratorHelperCall(L, call, access) {
2649
2649
  const terminal = access.name.text;
2650
2650
  if (!ITER_TERMINALS.has(terminal))
2651
2651
  return null;
2652
- // Walk receiver: stage* ← values() ← array-typed source.
2652
+ // Walk receiver: stage* ← values()/entries() ← array-typed source
2653
+ // (entries seeds the chain with [index, element] pairs — the
2654
+ // checker's own tuple type).
2653
2655
  const stages = [];
2654
2656
  let cur = access.expression;
2655
2657
  let source = null;
2658
+ let srcProj = "values";
2656
2659
  for (;;) {
2657
2660
  if (!ts.isCallExpression(cur) || !ts.isPropertyAccessExpression(cur.expression))
2658
2661
  return null;
2659
2662
  if (cur.questionDotToken || cur.expression.questionDotToken)
2660
2663
  return null;
2661
2664
  const name = cur.expression.name.text;
2662
- if (name === "values" && cur.arguments.length === 0) {
2665
+ if ((name === "values" || name === "entries") && cur.arguments.length === 0) {
2663
2666
  const recvIr = L.mapTypeOf(L.typeOf(cur.expression.expression));
2664
2667
  if (recvIr?.kind !== "array")
2665
2668
  return null;
2666
2669
  if (!L.isStdlibMember(cur.expression))
2667
2670
  return null;
2668
2671
  source = cur.expression.expression;
2672
+ srcProj = name;
2669
2673
  break;
2670
2674
  }
2671
2675
  if (!ITER_STAGES.has(name) || cur.arguments.length !== 1 || ts.isSpreadElement(cur.arguments[0]))
@@ -2687,7 +2691,12 @@ export function lowerIteratorHelperCall(L, call, access) {
2687
2691
  const items = L.lowerExpr(source);
2688
2692
  if (items.type.kind !== "array")
2689
2693
  return null;
2690
- let elem = items.type.elem;
2694
+ // The entries() seed: the interned [number, T] tuple — the same shape
2695
+ // the checker's own [number, T] maps to, so stage callbacks typed
2696
+ // against the lib's pairs intern equal.
2697
+ let elem = srcProj === "entries"
2698
+ ? { kind: "record", shapeId: L.shapes.intern([{ name: "0", type: F64 }, { name: "1", type: items.type.elem }], true) }
2699
+ : items.type.elem;
2691
2700
  const stageIrs = [];
2692
2701
  for (const s of stages) {
2693
2702
  if (s.name === "take" || s.name === "drop") {
@@ -2774,12 +2783,12 @@ export function lowerIteratorHelperCall(L, call, access) {
2774
2783
  L.noLowering(`.${terminal} at this result type`, call, "the chain's element and result types must be representable — annotate the callbacks' types");
2775
2784
  }
2776
2785
  const stageKeys = stageIrs.map((s) => (s.kind === "take" || s.kind === "drop" ? s.kind : `${s.kind}:${typeKey(s.fn.type)}`));
2777
- const key = `iter:${typeKey(items.type)}:${stageKeys.join(",")}:${terminal}:${termArgs.map((a) => typeKey(a.type)).join(",")}`;
2786
+ const key = `iter:${srcProj}:${typeKey(items.type)}:${stageKeys.join(",")}:${terminal}:${termArgs.map((a) => typeKey(a.type)).join(",")}`;
2778
2787
  let helper = L.arrHofHelpers.get(key);
2779
2788
  if (!helper) {
2780
2789
  helper = `%iter.${terminal}.${L.arrHofHelpers.size}`;
2781
2790
  L.arrHofHelpers.set(key, helper);
2782
- L.liftedFns.push(buildIterChainFn(L, helper, items.type, stageIrs, terminal, termArgs.map((a) => a.type), resultT, loc));
2791
+ L.liftedFns.push(buildIterChainFn(L, helper, items.type, srcProj, stageIrs, terminal, termArgs.map((a) => a.type), resultT, loc));
2783
2792
  }
2784
2793
  const budgetOrFn = stageIrs.map((s) => (s.kind === "take" || s.kind === "drop" ? s.budget : s.fn));
2785
2794
  return { kind: "call", callee: helper, args: [items, ...budgetOrFn, ...termArgs], type: resultT, loc };
@@ -2862,7 +2871,7 @@ function internBudgetChecker(L, loc) {
2862
2871
  * source order, flatMap as an inner loop over its returned array.
2863
2872
  * Pre-loop, a zero take budget marks done immediately — Node pulls
2864
2873
  * nothing at all through a take(0). */
2865
- function buildIterChainFn(L, name, itemsT, stages, terminal, termArgTs, resultT, loc) {
2874
+ function buildIterChainFn(L, name, itemsT, srcProj, stages, terminal, termArgTs, resultT, loc) {
2866
2875
  const ref = (localId, type) => ({ kind: "varRef", localId, type, loc });
2867
2876
  const num = (value) => ({ kind: "numLit", value, type: F64, loc });
2868
2877
  const boolLit = (value) => ({ kind: "boolLit", value, type: BOOL, loc });
@@ -2883,7 +2892,12 @@ function buildIterChainFn(L, name, itemsT, stages, terminal, termArgTs, resultT,
2883
2892
  const doneId = addLocal("done", BOOL, true);
2884
2893
  const done = () => ref(doneId, BOOL);
2885
2894
  const setDone = { kind: "assign", localId: doneId, value: boolLit(true), loc };
2886
- let elem = itemsT.elem;
2895
+ // The entries() seed builds each pass's [index, element] pair — the
2896
+ // same interned tuple the caller computed its stage types against.
2897
+ const seedT = srcProj === "entries"
2898
+ ? { kind: "record", shapeId: L.shapes.intern([{ name: "0", type: F64 }, { name: "1", type: itemsT.elem }], true) }
2899
+ : itemsT.elem;
2900
+ let elem = seedT;
2887
2901
  const slots = [];
2888
2902
  for (const s of stages) {
2889
2903
  if (s.kind === "take" || s.kind === "drop") {
@@ -3128,7 +3142,19 @@ function buildIterChainFn(L, name, itemsT, stages, terminal, termArgTs, resultT,
3128
3142
  }
3129
3143
  }
3130
3144
  const iId = addLocal("i", F64, true);
3131
- const vId = addLocal("v", itemsT.elem, false);
3145
+ const vId = addLocal("v", seedT, false);
3146
+ const elemRead = () => ({ kind: "arrayGet", arr: ref("items.0", itemsT), index: ref(iId, F64), type: itemsT.elem, loc });
3147
+ const seedInit = srcProj === "entries"
3148
+ ? {
3149
+ kind: "recordLit",
3150
+ fields: [
3151
+ { name: "0", value: ref(iId, F64) },
3152
+ { name: "1", value: elemRead() },
3153
+ ],
3154
+ type: seedT,
3155
+ loc,
3156
+ }
3157
+ : elemRead();
3132
3158
  const loop = {
3133
3159
  kind: "for",
3134
3160
  init: { kind: "varDecl", localId: iId, init: num(0), loc },
@@ -3142,8 +3168,8 @@ function buildIterChainFn(L, name, itemsT, stages, terminal, termArgTs, resultT,
3142
3168
  },
3143
3169
  update: { kind: "assign", localId: iId, value: { kind: "bin", op: "+", left: ref(iId, F64), right: num(1), type: F64, loc }, loc },
3144
3170
  body: [
3145
- { kind: "varDecl", localId: vId, init: { kind: "arrayGet", arr: ref("items.0", itemsT), index: ref(iId, F64), type: itemsT.elem, loc }, loc },
3146
- ...emit(ref(vId, itemsT.elem)),
3171
+ { kind: "varDecl", localId: vId, init: seedInit, loc },
3172
+ ...emit(ref(vId, seedT)),
3147
3173
  ],
3148
3174
  loc,
3149
3175
  };
@@ -3436,6 +3462,143 @@ export function lowerForOfSearchParams(L, stmt, iterable, proj) {
3436
3462
  L.scopes.pop();
3437
3463
  }
3438
3464
  }
3465
+ /** for-of over an ARRAY's keys()/entries() projections consumed directly
3466
+ * by the head (`for (const [index, line] of lines.entries())` — the
3467
+ * dominant formatter idiom): the LIVE index walk — the length re-reads
3468
+ * every pass, exactly the array iterator's contract (elements appended
3469
+ * mid-walk are visited; a shrink ends the loop early) — yielding the
3470
+ * index (`keys`, a number) or the [index, element] pair (`entries`).
3471
+ * `values` never lands here: lowerForOf's receiver unwrap owns it. A
3472
+ * `[i, v]` head of plain identifiers binds straight from the reads (the
3473
+ * tuple never materializes); identifier heads and the remaining
3474
+ * patterns bind through the checker's own element type — the interned
3475
+ * [number, T] tuple record. Stored iterator OBJECTS keep their fence
3476
+ * (only the direct for-of position unwraps). */
3477
+ export function lowerForOfArrayIter(L, stmt, iterable, proj) {
3478
+ const loc = locOf(stmt);
3479
+ const arrT = iterable.type;
3480
+ const elemT = arrT.elem;
3481
+ const yieldsPair = proj === "entries";
3482
+ if (!ts.isVariableDeclarationList(stmt.initializer)) {
3483
+ L.unsupported("SC1090", stmt.initializer, "for-of over a pre-declared variable (declare the loop variable in the loop: for (const x of ...))");
3484
+ }
3485
+ const list = stmt.initializer;
3486
+ if ((list.flags & ts.NodeFlags.Using) !== 0) {
3487
+ L.unsupported("SC1090", list, "'using' declarations (dispose-at-scope-exit semantics)");
3488
+ }
3489
+ const isVar = (list.flags & ts.NodeFlags.BlockScoped) === 0;
3490
+ const isLet = (list.flags & ts.NodeFlags.Let) !== 0 || isVar;
3491
+ const decl = list.declarations[0]; // the grammar allows exactly one
3492
+ L.scopes.push(new Map());
3493
+ try {
3494
+ const arr = L.declareHiddenLocal("%arof", arrT);
3495
+ const i = L.declareHiddenLocal("%iterof", F64);
3496
+ i.mutable = true;
3497
+ const ref = (localId, type) => ({ kind: "varRef", localId, type, loc });
3498
+ const num = (value) => ({ kind: "numLit", value, type: F64, loc });
3499
+ const keyRead = () => ref(i.id, F64);
3500
+ const valRead = () => ({ kind: "arrayGet", arr: ref(arr.id, arrT), index: ref(i.id, F64), type: elemT, loc });
3501
+ const binds = [];
3502
+ const isPlainIdent = (el) => ts.isBindingElement(el) && el.name !== undefined && ts.isIdentifier(el.name) && !el.initializer && !el.dotDotDotToken;
3503
+ if (yieldsPair &&
3504
+ !isVar && // var pattern names assign hoisted slots — the generic desugar below owns them
3505
+ ts.isArrayBindingPattern(decl.name) &&
3506
+ decl.name.elements.length >= 1 && decl.name.elements.length <= 2 &&
3507
+ decl.name.elements.every(isPlainIdent)) {
3508
+ // `for (const [i, v] of xs.entries())` — bind straight from the
3509
+ // reads; the tuple never exists. `[i]` alone reads only the index.
3510
+ const els = decl.name.elements;
3511
+ const kLocal = L.declareLocal(els[0].name, els[0].name.text, F64, isLet);
3512
+ binds.push({ kind: "varDecl", localId: kLocal.id, init: keyRead(), loc });
3513
+ if (els[1]) {
3514
+ const vLocal = L.declareLocal(els[1].name, els[1].name.text, elemT, isLet);
3515
+ binds.push({ kind: "varDecl", localId: vLocal.id, init: valRead(), loc });
3516
+ }
3517
+ }
3518
+ else {
3519
+ // Identifier heads and the remaining patterns bind through the
3520
+ // checker's own element type — [number, T] for pair yields,
3521
+ // number otherwise — the Map desugar's exact shape.
3522
+ const declT = L.mapTypeOf(L.checker.getTypeAtLocation(decl.name));
3523
+ if (yieldsPair) {
3524
+ const shape = declT?.kind === "record" ? L.shapes.get(declT.shapeId) : null;
3525
+ if (declT?.kind !== "record" || !shape?.tuple || shape.fields.length !== 2 ||
3526
+ shape.fields.find((f) => f.name === "0")?.type.kind !== "f64" ||
3527
+ !typeEquals(shape.fields.find((f) => f.name === "1")?.type ?? F64, elemT)) {
3528
+ L.badType(decl.name, L.checker.getTypeAtLocation(decl.name)); // defensive: the lib declares [number, T]
3529
+ }
3530
+ }
3531
+ else if (declT?.kind !== "f64") {
3532
+ L.badType(decl.name, L.checker.getTypeAtLocation(decl.name)); // defensive: the lib declares number
3533
+ }
3534
+ const elemInit = yieldsPair
3535
+ ? {
3536
+ kind: "recordLit",
3537
+ fields: [
3538
+ { name: "0", value: keyRead() },
3539
+ { name: "1", value: valRead() },
3540
+ ],
3541
+ type: declT,
3542
+ loc,
3543
+ }
3544
+ : keyRead();
3545
+ if (ts.isIdentifier(decl.name)) {
3546
+ const varTarget = forOfVarTarget(L, decl);
3547
+ if (varTarget) {
3548
+ // `for (var x of ...)`: one function-scoped binding, assigned
3549
+ // per pass — closures made in the loop share it, and the value
3550
+ // persists after the loop (both Node-exact for var).
3551
+ const tmp = L.declareHiddenLocal("%vof", declT);
3552
+ binds.push({ kind: "varDecl", localId: tmp.id, init: elemInit, loc });
3553
+ binds.push({
3554
+ kind: "assign",
3555
+ localId: varTarget.id,
3556
+ value: L.coerceInto(decl.name, { kind: "varRef", localId: tmp.id, type: declT, loc }, varTarget.type),
3557
+ loc,
3558
+ });
3559
+ }
3560
+ else {
3561
+ const local = L.declareLocal(decl.name, decl.name.text, declT, isLet);
3562
+ binds.push({ kind: "varDecl", localId: local.id, init: elemInit, loc });
3563
+ }
3564
+ }
3565
+ else {
3566
+ const tmp = L.declareHiddenLocal("%destr", declT);
3567
+ binds.push({ kind: "varDecl", localId: tmp.id, init: elemInit, loc });
3568
+ L.lowerBindingPattern(decl.name, () => ({ kind: "varRef", localId: tmp.id, type: declT, loc }), declT, isLet, binds);
3569
+ }
3570
+ }
3571
+ const body = L.inCtl("loop", () => L.lowerScopedBlock(stmt.statement));
3572
+ const loop = {
3573
+ kind: "for",
3574
+ init: { kind: "varDecl", localId: i.id, init: num(0), loc },
3575
+ cond: {
3576
+ kind: "bin",
3577
+ op: "<",
3578
+ left: ref(i.id, F64),
3579
+ right: { kind: "arrIntrinsic", method: "length", receiver: ref(arr.id, arrT), args: [], type: F64, loc },
3580
+ type: BOOL,
3581
+ loc,
3582
+ },
3583
+ update: {
3584
+ kind: "assign",
3585
+ localId: i.id,
3586
+ value: { kind: "bin", op: "+", left: ref(i.id, F64), right: num(1), type: F64, loc },
3587
+ loc,
3588
+ },
3589
+ body: [...binds, ...body],
3590
+ loc,
3591
+ };
3592
+ return {
3593
+ kind: "block",
3594
+ body: [{ kind: "varDecl", localId: arr.id, init: iterable, loc }, loop],
3595
+ loc,
3596
+ };
3597
+ }
3598
+ finally {
3599
+ L.scopes.pop();
3600
+ }
3601
+ }
3439
3602
  export function lowerForOfMap(L, stmt, iterable, mapT, proj) {
3440
3603
  return lowerForOfMapOrSet(L, stmt, iterable, mapT, proj);
3441
3604
  }
@@ -3841,9 +4004,26 @@ export function lowerRegexMethodCall(L, call, access, dynReceiver) {
3841
4004
  // [whole match, ...captures] or the null arm. The g-flag match returns
3842
4005
  // EVERY match (a different shape) and /y is stateful — both fence at
3843
4006
  // compile time on literal arguments (values reaching the runtime with
3844
- // those flags abort, the test() stance). `.index`/`.input`/`.groups`
3845
- // reads on the result fence per member (array-typed value).
3846
- if (receiverKind === "string" && name === "match") {
4007
+ // those flags abort, the test() stance). `.index`/`.input` reads on
4008
+ // the result fence per member (array-typed value); `.groups` reads
4009
+ // desugar at their access sites when the regex is statically known
4010
+ // (lowerMatchGroupsRead).
4011
+ // `s.match(re)` also claims a NULLABLE string receiver (string + unit
4012
+ // arms — `process.versions.openssl.match(...)`, the Dict<string>
4013
+ // member the node suite's crypto helper reads): the checked
4014
+ // extraction narrows to the string arm and a unit value throws the
4015
+ // catchable TypeError at the read, where Node's own member read
4016
+ // throws — tsc only admits the spelling in JS sources.
4017
+ const nullableStringRecv = receiverKind === "union" &&
4018
+ name === "match" &&
4019
+ (() => {
4020
+ const t = L.mapTypeOf(L.typeOf(access.expression));
4021
+ if (t?.kind !== "union")
4022
+ return false;
4023
+ const arms = L.unions.get(t.unionId)?.arms ?? [];
4024
+ return arms.some((a) => a.kind === "string") && arms.every((a) => a.kind === "string" || isUnitType(a));
4025
+ })();
4026
+ if ((receiverKind === "string" || nullableStringRecv) && name === "match") {
3847
4027
  const arg0 = call.arguments[0];
3848
4028
  if (!arg0 || L.mapTypeOf(L.typeOf(arg0))?.kind !== "regex")
3849
4029
  return null; // string-pattern match: the SC2020 fence
@@ -3858,7 +4038,7 @@ export function lowerRegexMethodCall(L, call, access, dynReceiver) {
3858
4038
  L.unsupported("SC1120", call, "'.match()' with the 'g' or 'y' flag (an every-match array is a different shape — use replaceAll/split, or test() per position)");
3859
4039
  }
3860
4040
  }
3861
- const receiver = lowerReceiver();
4041
+ const receiver = nullableStringRecv ? L.lowerExprExpecting(access.expression, STRING) : lowerReceiver();
3862
4042
  const re = L.lowerExpr(arg0);
3863
4043
  // RegExpMatchArray | null maps to the string[] | null union by
3864
4044
  // itself; intern it directly when the checker's spelling doesn't —
@@ -4232,8 +4412,30 @@ export function lowerBytesMethodCall(L, call, access) {
4232
4412
  }
4233
4413
  const receiver = L.lowerExpr(access.expression);
4234
4414
  const args = call.arguments.map((a) => L.lowerExprExpecting(a, F64));
4235
- // subarray COPIES here (no views exist) — SEMANTICS.md documents it.
4236
- return { kind: "bytesIntrinsic", method: "slice", receiver, args, type: receiverIr, loc };
4415
+ // subarray is a VIEW (TypedArray.prototype.subarray aliases), and
4416
+ // Buffer's slice() is subarray's deprecated Node alias — resolved by
4417
+ // where the member is declared, the toString discipline below. Only
4418
+ // the plain typed arrays' slice() copies (JS-exact).
4419
+ const declaredOnBuffer = name === "slice" &&
4420
+ (() => {
4421
+ const nameSym = L.checker.getSymbolAtLocation(access.name);
4422
+ return nameSym !== undefined && L.checker.declarationsOf(nameSym).some((d) => ts.isInterfaceDeclaration(d.parent) && d.parent.name.text === "Buffer");
4423
+ })();
4424
+ const method = name === "subarray" || declaredOnBuffer ? "subarray" : "slice";
4425
+ return { kind: "bytesIntrinsic", method, receiver, args, type: receiverIr, loc };
4426
+ }
4427
+ if (name === "fill" && receiverIr.elem !== "u8") {
4428
+ // TypedArray.prototype.fill on the non-u8 kinds: per-element value
4429
+ // coercion, slice-clamped relative indices, never throws. u8
4430
+ // receivers keep the Buffer fill family (string patterns, throwing
4431
+ // offset validation) — lowerBufferInstanceMethod's surface.
4432
+ if (nArgs < 1 || nArgs > 3) {
4433
+ L.noLowering(`.fill with ${nArgs} arguments on typed arrays`, call);
4434
+ }
4435
+ const receiver = L.lowerExpr(access.expression);
4436
+ const v = L.lowerExprExpecting(call.arguments[0], F64);
4437
+ const idx = call.arguments.slice(1).map((a) => L.lowerExprExpecting(a, F64));
4438
+ return { kind: "bytesIntrinsic", method: "fillElem", receiver, args: [v, ...idx], type: receiverIr, loc };
4237
4439
  }
4238
4440
  if (name === "set") {
4239
4441
  if (nArgs < 1 || nArgs > 2) {
@@ -4408,6 +4610,12 @@ const BUF_ENCODINGS = {
4408
4610
  ucs2: "utf16le",
4409
4611
  "ucs-2": "utf16le",
4410
4612
  };
4613
+ /** A literal encoding's canonical name, or undefined for a spelling Node
4614
+ * does not know — the ladder callers (stream options) turn unknown
4615
+ * literals into Node's runtime ERR_UNKNOWN_ENCODING throw. */
4616
+ export function knownBufEncoding(name) {
4617
+ return own(BUF_ENCODINGS, name);
4618
+ }
4411
4619
  /** The literal encoding argument of a Buffer surface, normalized — or a
4412
4620
  * fence when it isn't a literal alias Node knows. */
4413
4621
  export function bufEncoding(L, what, encNode) {
@@ -4690,6 +4898,22 @@ export function lowerBufferStaticCall(L, call, access) {
4690
4898
  const loc = locOf(call);
4691
4899
  const args = call.arguments;
4692
4900
  if (member === "from") {
4901
+ // Buffer.from(x.buffer[, byteOffset[, length]]): a u8 VIEW sharing
4902
+ // x's storage (Node shares the ArrayBuffer; length is in BYTES).
4903
+ // The first argument must be the SYNTACTIC `.buffer` of a typed
4904
+ // array — the DataView peel — and the construction rides the same
4905
+ // dataViewNew intrinsic (offset/length validation throws Node's
4906
+ // DataView-shaped RangeErrors; valid indices behave exactly).
4907
+ if (args.length >= 1 && args.length <= 3 && !args.some(ts.isSpreadElement) &&
4908
+ ts.isPropertyAccessExpression(args[0]) && args[0].name.text === "buffer") {
4909
+ const srcNode = args[0].expression;
4910
+ const srcIr = L.mapTypeOf(L.typeOf(srcNode));
4911
+ if (srcIr?.kind === "bytes") {
4912
+ const receiver = L.lowerExpr(srcNode);
4913
+ const idxArgs = args.slice(1).map((a) => L.lowerExprExpecting(a, F64));
4914
+ return { kind: "bytesIntrinsic", method: "dataViewNew", receiver, args: idxArgs, type: BYTES_U8, loc };
4915
+ }
4916
+ }
4693
4917
  if (args.length >= 1 && args.length <= 2 && !ts.isSpreadElement(args[0])) {
4694
4918
  const argNode = args[0];
4695
4919
  if (ts.isArrayLiteralExpression(argNode) && !argNode.elements.some(ts.isSpreadElement)) {
@@ -4719,7 +4943,8 @@ export function lowerBufferStaticCall(L, call, access) {
4719
4943
  }
4720
4944
  }
4721
4945
  L.noLowering("Buffer.from with this argument shape", call, "supported: Buffer.from(string, encoding?) with a literal encoding, Buffer.from(u8Array) — a copy — " +
4722
- "or Buffer.from(number[]); ArrayBuffers do not exist here (narrow unions first)");
4946
+ "Buffer.from(number[]), or Buffer.from(x.buffer, byteOffset?, length?) — a view sharing x's " +
4947
+ "storage (no free-standing ArrayBuffer value exists; narrow unions first)");
4723
4948
  }
4724
4949
  if (member === "alloc" || member === "allocUnsafe") {
4725
4950
  const maxArgs = member === "alloc" ? 3 : 1;
@@ -6305,6 +6530,26 @@ export function lowerDynArrayFlatMapCall(L, call, access, recv) {
6305
6530
  const { fnArg, arity } = hofCallbackArg(L, argNode, [DYN], DYN);
6306
6531
  const ret = fnArg.type.ret;
6307
6532
  if (ret.kind !== "array") {
6533
+ // A DYNAMIC-returning callback (`plugins.flatMap((plugin) =>
6534
+ // plugin.languages ?? [])` — the getSupportInfo shape, where the
6535
+ // callback's members ride the DOM): the call dispatches on the
6536
+ // RECEIVER's runtime kind (dynInvoke) with the callback boxed —
6537
+ // a wrapped ISLAND receiver runs the ENGINE's own JS-exact
6538
+ // Array.prototype.flatMap (the routed-ops lane), a native DOM
6539
+ // array runs the runtime's flatMap, and non-arrays throw Node's
6540
+ // TypeError. The result stays dyn, checked per use.
6541
+ if (ret.kind === "dyn" || ret.kind === "jsval") {
6542
+ const boxed = { kind: "dynFrom", value: fnArg, type: DYN, loc };
6543
+ return {
6544
+ kind: "dynInvoke",
6545
+ recv,
6546
+ method: "flatMap",
6547
+ calleeName: access.getText(),
6548
+ args: [boxed],
6549
+ type: DYN,
6550
+ loc,
6551
+ };
6552
+ }
6308
6553
  L.unsupported("SC1090", call, `'.flatMap' on 'unknown' receivers whose callback returns '${L.fmt(ret)}' ` +
6309
6554
  `(only callbacks returning a typed ARRAY compile — the results concatenate ` +
6310
6555
  `without validation; return one-element/empty arrays instead of bare values)`);