visaljs 0.0.9 → 0.0.10

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.
package/cli.js CHANGED
@@ -1,13 +1,15 @@
1
1
  #!/usr/bin/env node
2
2
  import { createRequire, isBuiltin } from "node:module";
3
+ import { Worker, isMainThread, parentPort, workerData } from "node:worker_threads";
3
4
  import { existsSync, mkdirSync, readFileSync, readdirSync, realpathSync, renameSync, rmSync, statSync, writeFileSync } from "node:fs";
4
5
  import { basename, delimiter, dirname, extname, isAbsolute, join, normalize, relative, resolve, sep } from "node:path";
5
6
  import { parseArgs, promisify } from "node:util";
7
+ import { getHeapStatistics } from "node:v8";
6
8
  import ts from "typescript";
7
9
  import { RolldownMagicString, rolldown } from "rolldown";
8
10
  import { execFile, execFileSync, spawn, spawnSync } from "node:child_process";
9
11
  import { createHash } from "node:crypto";
10
- import { availableParallelism, homedir, tmpdir, totalmem } from "node:os";
12
+ import { availableParallelism, homedir, tmpdir } from "node:os";
11
13
  //#region \0rolldown/runtime.js
12
14
  var __defProp$1 = Object.defineProperty;
13
15
  var __exportAll = (all, no_symbols) => {
@@ -98,6 +100,19 @@ function hoistHostImports(body) {
98
100
  function parseModule(text, file, map, entry, hostPrimitives = {}) {
99
101
  return parse(text, file, true, map, entry, hostPrimitives);
100
102
  }
103
+ /** A static unit as one module: its types ignored, its files' top levels one after another.
104
+ * Its functions can't be printed (Function.prototype.toString): their source is in many files. */
105
+ function convertStaticUnit(unit, hostPrimitives) {
106
+ const programs = unit.files.map((sf) => new Converter(sf, true, void 0, resolve(sf.fileName), hostPrimitives, unit).program());
107
+ return {
108
+ ...programs[0],
109
+ static: true,
110
+ body: hoistHostImports(programs.flatMap((p) => p.body)),
111
+ exportedNames: programs.flatMap((p) => p.exportedNames),
112
+ source: "",
113
+ unsupported: programs.flatMap((p) => p.unsupported)
114
+ };
115
+ }
101
116
  function parse(text, file, module, map, entry, hostPrimitives = {}) {
102
117
  const setExternalModuleIndicator = (s) => {
103
118
  s.externalModuleIndicator = module ? s : void 0;
@@ -113,15 +128,26 @@ function parse(text, file, module, map, entry, hostPrimitives = {}) {
113
128
  }
114
129
  return new Converter(sf, module, map, resolve(entry ?? file), hostPrimitives).program();
115
130
  }
116
- function locAt(sf, pos, map) {
131
+ function locAt(sf, pos, map, file = normalize(sf.fileName)) {
117
132
  const { line, character } = sf.getLineAndCharacterOfPosition(pos);
118
133
  const loc = {
119
- file: normalize(sf.fileName),
134
+ file,
120
135
  line: line + 1,
121
136
  column: character + 1
122
137
  };
123
138
  return map?.original(loc.line, loc.column) ?? loc;
124
139
  }
140
+ /** A numeric literal written as a floating-point one (1.0, 1e3): a double to static code, as in C. */
141
+ function isFloatWritten(raw) {
142
+ return /[.eE]/.test(raw) && !/^0[xXbBoO]/.test(raw);
143
+ }
144
+ /** `import type …`, or an import of types only (`import { type a }`). */
145
+ function typeOnly(node) {
146
+ const clause = node.importClause;
147
+ if (!clause || clause.isTypeOnly) return clause !== void 0;
148
+ const bindings = clause.namedBindings;
149
+ return !clause.name && bindings !== void 0 && ts.isNamedImports(bindings) && bindings.elements.every((e) => e.isTypeOnly);
150
+ }
125
151
  /** TypeScript rejects `a?.#x` (still parsed as usual); ES2022 allows it. */
126
152
  const PRIVATE_IN_OPTIONAL_CHAIN = 18030;
127
153
  /** Readable names for constructs the compiler doesn't handle. */
@@ -192,12 +218,15 @@ var Converter = class {
192
218
  map;
193
219
  entry;
194
220
  hostPrimitives;
195
- constructor(sf, module, map, entry, hostPrimitives) {
221
+ /** Converting a static unit's TypeScript: types are skipped, not errors. */
222
+ unit;
223
+ constructor(sf, module, map, entry, hostPrimitives, unit) {
196
224
  this.sf = sf;
197
225
  this.module = module;
198
226
  this.map = map;
199
227
  this.entry = entry;
200
228
  this.hostPrimitives = hostPrimitives;
229
+ this.unit = unit;
201
230
  }
202
231
  /** The absolute path of the original file `node` comes from (for import.meta and import()). */
203
232
  originalFile(node) {
@@ -205,7 +234,7 @@ var Converter = class {
205
234
  return this.map?.originalSource(line + 1, character + 1) ?? resolve(this.sf.fileName);
206
235
  }
207
236
  loc(node) {
208
- return locAt(this.sf, node.getStart(this.sf), this.map);
237
+ return locAt(this.sf, node.getStart(this.sf), this.map, this.unit?.fileName(this.sf));
209
238
  }
210
239
  fail(kind, detail, node) {
211
240
  if (kind === "NotSupported") throw new Deferred(detail, node);
@@ -234,6 +263,7 @@ var Converter = class {
234
263
  }
235
264
  /** TypeScript-only syntax the parser accepts in .js files: a JS syntax error. */
236
265
  noTypes(node) {
266
+ if (this.unit) return;
237
267
  if (node.type) this.fail("SyntaxError", "type annotations are TypeScript, not JavaScript", node.type);
238
268
  if (node.typeParameters || node.typeArguments) this.fail("SyntaxError", "type arguments are TypeScript, not JavaScript", node);
239
269
  }
@@ -258,7 +288,7 @@ var Converter = class {
258
288
  return {
259
289
  type: "Program",
260
290
  loc: {
261
- file: normalize(this.sf.fileName),
291
+ file: this.unit?.fileName(this.sf) ?? normalize(this.sf.fileName),
262
292
  line: 1,
263
293
  column: 1
264
294
  },
@@ -282,10 +312,12 @@ var Converter = class {
282
312
  }
283
313
  identifier(node) {
284
314
  if (!ts.isIdentifier(node)) this.fail("SyntaxError", "identifier expected", node);
315
+ const renamed = this.unit?.rename(node);
285
316
  const id = {
286
317
  type: "Identifier",
287
318
  loc: this.loc(node),
288
- name: node.text
319
+ name: renamed ?? node.text,
320
+ ...renamed ? { original: node.text } : {}
289
321
  };
290
322
  if (this.map) {
291
323
  const { line, character } = this.sf.getLineAndCharacterOfPosition(node.getStart(this.sf));
@@ -456,11 +488,12 @@ var Converter = class {
456
488
  kind: flags === ts.NodeFlags.Let ? "let" : flags === ts.NodeFlags.Const ? "const" : "var",
457
489
  declarations: list.declarations.map((d) => {
458
490
  this.noTypes(d);
459
- if (d.exclamationToken) this.fail("SyntaxError", "definite assignment assertions are TypeScript", d);
491
+ if (d.exclamationToken && !this.unit) this.fail("SyntaxError", "definite assignment assertions are TypeScript", d);
492
+ const fn = d.initializer && (ts.isArrowFunction(d.initializer) || ts.isFunctionExpression(d.initializer)) ? d.initializer : void 0;
460
493
  return {
461
494
  type: "VariableDeclarator",
462
495
  loc: this.loc(d),
463
- id: this.bindingPattern(d.name),
496
+ id: this.declaredInt(this.bindingPattern(d.name), d.type, fn?.type),
464
497
  init: d.initializer ? this.expression(d.initializer) : null
465
498
  };
466
499
  })
@@ -511,8 +544,16 @@ var Converter = class {
511
544
  return this.orUnsupported(node, () => {
512
545
  const loc = this.loc(node);
513
546
  if (ts.isImportDeclaration(node) && ts.isStringLiteral(node.moduleSpecifier) && node.moduleSpecifier.text === "visaljs:host") return this.hostImport(node);
547
+ if (this.unit && ts.isImportDeclaration(node) && (this.unit.linked(node) || typeOnly(node))) return {
548
+ type: "EmptyStatement",
549
+ loc
550
+ };
514
551
  if (ts.isImportDeclaration(node) || ts.isImportEqualsDeclaration(node)) this.fail("NotSupported", "import declarations: the bundler resolves them", node);
515
552
  if (ts.isExportDeclaration(node)) {
553
+ if (node.isTypeOnly && this.unit) return {
554
+ type: "EmptyStatement",
555
+ loc
556
+ };
516
557
  if (node.isTypeOnly) this.fail("SyntaxError", "type-only exports are TypeScript", node);
517
558
  if (node.moduleSpecifier) this.fail("NotSupported", "export … from: the bundler resolves them", node);
518
559
  const clause = node.exportClause;
@@ -606,11 +647,12 @@ var Converter = class {
606
647
  return {
607
648
  type: "FunctionDeclaration",
608
649
  loc,
609
- id: f.name ? this.identifier(f.name) : {
650
+ id: f.name ? this.declaredInt(this.identifier(f.name), void 0, f.type) : {
610
651
  type: "Identifier",
611
652
  loc,
612
653
  name: "*default*"
613
654
  },
655
+ ...this.returnsInt(f),
614
656
  ...this.parameters(f.parameters),
615
657
  source: this.range(f),
616
658
  body: this.block(f.body),
@@ -796,6 +838,11 @@ var Converter = class {
796
838
  }
797
839
  case ts.SyntaxKind.InterfaceDeclaration:
798
840
  case ts.SyntaxKind.TypeAliasDeclaration:
841
+ if (this.unit) return {
842
+ type: "EmptyStatement",
843
+ loc
844
+ };
845
+ return this.fail("SyntaxError", "TypeScript declarations are not JavaScript", node);
799
846
  case ts.SyntaxKind.EnumDeclaration:
800
847
  case ts.SyntaxKind.ModuleDeclaration: return this.fail("SyntaxError", "TypeScript declarations are not JavaScript", node);
801
848
  default: return this.unsupported(node);
@@ -845,14 +892,14 @@ var Converter = class {
845
892
  let rest = null;
846
893
  list.forEach((p, i) => {
847
894
  this.noTypes(p);
848
- if (p.questionToken) this.fail("SyntaxError", "optional parameters are TypeScript", p);
895
+ if (p.questionToken && !this.unit) this.fail("SyntaxError", "optional parameters are TypeScript", p);
849
896
  if (p.dotDotDotToken) {
850
897
  if (i !== list.length - 1) this.fail("SyntaxError", "A rest parameter must be last in a parameter list", p);
851
898
  if (p.initializer) this.fail("SyntaxError", "Rest parameter may not have a default initializer", p);
852
899
  rest = this.bindingPattern(p.name);
853
900
  return;
854
901
  }
855
- params.push(this.bindingPattern(p.name));
902
+ params.push(this.declaredInt(this.bindingPattern(p.name), p.type));
856
903
  defaults.push(p.initializer ? this.expression(p.initializer) : null);
857
904
  });
858
905
  return {
@@ -879,6 +926,7 @@ var Converter = class {
879
926
  type: "FunctionExpression",
880
927
  loc: this.loc(f),
881
928
  id: !method && !ts.isConstructorDeclaration(f) && f.name && ts.isIdentifier(f.name) ? this.identifier(f.name) : null,
929
+ ...this.returnsInt(f),
882
930
  ...this.parameters(f.parameters),
883
931
  source: this.range(cls?.ctor && cls.node ? cls.node : f),
884
932
  body: this.block(f.body),
@@ -1187,6 +1235,7 @@ var Converter = class {
1187
1235
  type: "FunctionExpression",
1188
1236
  loc,
1189
1237
  id: null,
1238
+ ...this.returnsInt(f),
1190
1239
  ...this.parameters(f.parameters),
1191
1240
  source: this.range(f),
1192
1241
  body,
@@ -1201,6 +1250,23 @@ var Converter = class {
1201
1250
  ...this.asyncFlag(f)
1202
1251
  };
1203
1252
  }
1253
+ /** Static code: the integers a declaration names, on its identifier: its type's, and (a
1254
+ * function's name, or a const holding a function) its function's return type's. */
1255
+ declaredInt(id, type, returns) {
1256
+ if (!this.unit || id.type !== "Identifier") return id;
1257
+ const int = this.unit.intKind(type);
1258
+ const returnsInt = this.unit.intKind(returns);
1259
+ return {
1260
+ ...id,
1261
+ ...int ? { int } : {},
1262
+ ...returnsInt ? { returnsInt } : {}
1263
+ };
1264
+ }
1265
+ /** Static code: the integer a function's declared return type names. */
1266
+ returnsInt(f) {
1267
+ const int = this.unit?.intKind(f.type);
1268
+ return int ? { returnsInt: int } : {};
1269
+ }
1204
1270
  /** `async`: async functions and async generators. */
1205
1271
  asyncFlag(f) {
1206
1272
  return f.modifiers?.some((m) => m.kind === ts.SyntaxKind.AsyncKeyword) ? { async: true } : {};
@@ -1225,6 +1291,7 @@ var Converter = class {
1225
1291
  }
1226
1292
  chainLink(node) {
1227
1293
  if (!ts.isOptionalChain(node)) return this.expression(node);
1294
+ if (ts.isNonNullExpression(node) && this.unit) return this.chainLink(node.expression);
1228
1295
  if (ts.isNonNullExpression(node)) this.fail("SyntaxError", "non-null assertions are TypeScript, not JavaScript", node);
1229
1296
  const optional = node.questionDotToken ? { optional: true } : {};
1230
1297
  const loc = this.loc(node);
@@ -1389,7 +1456,11 @@ var Converter = class {
1389
1456
  type: "Property",
1390
1457
  kind: "init",
1391
1458
  loc,
1392
- key: this.identifier(p.name),
1459
+ key: {
1460
+ type: "Identifier",
1461
+ loc: this.loc(p.name),
1462
+ name: p.name.text
1463
+ },
1393
1464
  computed: false,
1394
1465
  value: this.identifier(p.name),
1395
1466
  isProto: false
@@ -1412,7 +1483,8 @@ var Converter = class {
1412
1483
  case ts.SyntaxKind.NumericLiteral: return {
1413
1484
  type: "Literal",
1414
1485
  loc,
1415
- value: Number(node.text)
1486
+ value: Number(node.text),
1487
+ ...this.unit && isFloatWritten(node.getText(this.sf)) ? { float: true } : {}
1416
1488
  };
1417
1489
  case ts.SyntaxKind.StringLiteral:
1418
1490
  case ts.SyntaxKind.NoSubstitutionTemplateLiteral: return {
@@ -1691,16 +1763,30 @@ var Converter = class {
1691
1763
  case ts.SyntaxKind.AsExpression:
1692
1764
  case ts.SyntaxKind.SatisfiesExpression:
1693
1765
  case ts.SyntaxKind.NonNullExpression:
1694
- case ts.SyntaxKind.TypeAssertionExpression: return this.fail("SyntaxError", "type assertions are TypeScript, not JavaScript", node);
1766
+ case ts.SyntaxKind.TypeAssertionExpression:
1767
+ if (this.unit) return this.expression(node.expression);
1768
+ return this.fail("SyntaxError", "type assertions are TypeScript, not JavaScript", node);
1695
1769
  default: return this.unsupported(node);
1696
1770
  }
1697
1771
  }
1698
1772
  };
1699
1773
  //#endregion
1700
1774
  //#region compiler/src/frontend/sourcemap.ts
1701
- const DIGIT = new Map([..."ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"].map((c, i) => [c, i]));
1775
+ const FIELDS = 5;
1776
+ const GEN_COLUMN = 0;
1777
+ const SOURCE = 1;
1778
+ const LINE = 2;
1779
+ const COLUMN = 3;
1780
+ const NAME = 4;
1781
+ const BASE64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
1782
+ const DIGIT = (/* @__PURE__ */ new Int8Array(128)).fill(-1);
1783
+ for (let i = 0; i < 64; i++) DIGIT[BASE64.charCodeAt(i)] = i;
1784
+ const COMMA = 44;
1785
+ const SEMICOLON = 59;
1702
1786
  var SourceMap = class {
1703
- lines = [];
1787
+ segments = new Int32Array(1024 * FIELDS);
1788
+ /** Line n's segments: from lineStarts[n] to lineStarts[n + 1] (segment indexes). */
1789
+ lineStarts = [0];
1704
1790
  sources;
1705
1791
  rawSources;
1706
1792
  names;
@@ -1710,93 +1796,114 @@ var SourceMap = class {
1710
1796
  this.rawSources = raw.sources.map((s) => s ?? "");
1711
1797
  this.sources = this.rawSources.map(resolveSource);
1712
1798
  this.names = raw.names ?? [];
1713
- let source = 0, line = 0, column = 0, name = 0;
1714
- for (const text of raw.mappings.split(";")) {
1715
- const segments = [];
1716
- let genColumn = 0;
1717
- for (const seg of text.split(",")) {
1718
- if (seg === "") continue;
1719
- const f = decodeVlq(seg);
1720
- genColumn += f[0];
1721
- if (f.length < 4) {
1722
- segments.push([
1723
- genColumn,
1724
- -1,
1725
- 0,
1726
- 0,
1727
- -1
1728
- ]);
1729
- continue;
1799
+ const mappings = raw.mappings;
1800
+ const fields = [
1801
+ 0,
1802
+ 0,
1803
+ 0,
1804
+ 0,
1805
+ 0
1806
+ ];
1807
+ let count = 0, lineStart = 0, genColumn = 0, source = 0, line = 0, column = 0, name = 0;
1808
+ for (let i = 0; i <= mappings.length; i++) {
1809
+ const c = i < mappings.length ? mappings.charCodeAt(i) : SEMICOLON;
1810
+ if (c !== COMMA && c !== SEMICOLON) continue;
1811
+ const n = decodeVlq(mappings, lineStart, i, fields);
1812
+ if (n > 0) {
1813
+ genColumn += fields[0];
1814
+ if (n >= 4) {
1815
+ source += fields[1];
1816
+ line += fields[2];
1817
+ column += fields[3];
1818
+ if (n >= 5) name += fields[4];
1730
1819
  }
1731
- source += f[1];
1732
- line += f[2];
1733
- column += f[3];
1734
- if (f.length >= 5) name += f[4];
1735
- segments.push([
1736
- genColumn,
1737
- source,
1738
- line,
1739
- column,
1740
- f.length >= 5 ? name : -1
1741
- ]);
1820
+ if ((count + 1) * FIELDS > this.segments.length) {
1821
+ const grown = new Int32Array(this.segments.length * 2);
1822
+ grown.set(this.segments);
1823
+ this.segments = grown;
1824
+ }
1825
+ const at = count++ * FIELDS;
1826
+ this.segments[at + GEN_COLUMN] = genColumn;
1827
+ this.segments[at + SOURCE] = n >= 4 ? source : -1;
1828
+ this.segments[at + LINE] = n >= 4 ? line : 0;
1829
+ this.segments[at + COLUMN] = n >= 4 ? column : 0;
1830
+ this.segments[at + NAME] = n >= 5 ? name : -1;
1831
+ }
1832
+ lineStart = i + 1;
1833
+ if (c === SEMICOLON) {
1834
+ this.sortLine(this.lineStarts[this.lineStarts.length - 1], count);
1835
+ this.lineStarts.push(count);
1836
+ genColumn = 0;
1742
1837
  }
1743
- segments.sort((a, b) => a[0] - b[0]);
1744
- this.lines.push(segments);
1745
1838
  }
1839
+ this.segments = this.segments.slice(0, count * FIELDS);
1746
1840
  }
1747
1841
  /** The original location of a 1-based bundle position: the segment at or before it on
1748
1842
  * its line (else the line's first), or null where the bundler generated the code. */
1749
1843
  original(line, column) {
1750
- const seg = this.segmentAt(line, column, false);
1751
- if (!seg || seg[1] < 0) return null;
1844
+ const at = this.segmentAt(line, column, false);
1845
+ if (at < 0 || this.segments[at + SOURCE] < 0) return null;
1752
1846
  return {
1753
- file: this.sources[seg[1]],
1754
- line: seg[2] + 1,
1755
- column: seg[3] + 1
1847
+ file: this.sources[this.segments[at + SOURCE]],
1848
+ line: this.segments[at + LINE] + 1,
1849
+ column: this.segments[at + COLUMN] + 1
1756
1850
  };
1757
1851
  }
1758
1852
  /** The original file of a 1-based bundle position, as the map's `sources` names it (absolute
1759
1853
  * paths from the driver), or null. */
1760
1854
  originalSource(line, column) {
1761
- const seg = this.segmentAt(line, column, false);
1762
- return seg && seg[1] >= 0 ? this.rawSources[seg[1]] : null;
1855
+ const at = this.segmentAt(line, column, false);
1856
+ return at >= 0 && this.segments[at + SOURCE] >= 0 ? this.rawSources[this.segments[at + SOURCE]] : null;
1763
1857
  }
1764
1858
  /** The original name of the identifier starting exactly at a 1-based bundle position. */
1765
1859
  originalName(line, column) {
1766
- const seg = this.segmentAt(line, column, true);
1767
- return seg && seg[4] >= 0 ? this.names[seg[4]] : void 0;
1860
+ const at = this.segmentAt(line, column, true);
1861
+ return at >= 0 && this.segments[at + NAME] >= 0 ? this.names[this.segments[at + NAME]] : void 0;
1768
1862
  }
1863
+ /** The offset in `segments` of the segment for a 1-based position, or -1. */
1769
1864
  segmentAt(line, column, exact) {
1770
- const segments = this.lines[line - 1];
1771
- if (!segments || segments.length === 0) return void 0;
1865
+ if (line < 1 || line >= this.lineStarts.length) return -1;
1866
+ const first = this.lineStarts[line - 1], last = this.lineStarts[line] - 1;
1867
+ if (last < first) return -1;
1772
1868
  const col = column - 1;
1773
- let lo = 0, hi = segments.length - 1, found = -1;
1869
+ let lo = first, hi = last, found = -1;
1774
1870
  while (lo <= hi) {
1775
1871
  const mid = lo + hi >> 1;
1776
- if (segments[mid][0] <= col) {
1872
+ if (this.segments[mid * FIELDS + GEN_COLUMN] <= col) {
1777
1873
  found = mid;
1778
1874
  lo = mid + 1;
1779
1875
  } else hi = mid - 1;
1780
1876
  }
1781
- if (exact) return found >= 0 && segments[found][0] === col ? segments[found] : void 0;
1782
- return segments[found >= 0 ? found : 0];
1877
+ if (exact) return found >= 0 && this.segments[found * FIELDS + GEN_COLUMN] === col ? found * FIELDS : -1;
1878
+ return (found >= 0 ? found : first) * FIELDS;
1879
+ }
1880
+ /** Segments `from` to `to` in generated column order (maps almost always have them so). */
1881
+ sortLine(from, to) {
1882
+ let sorted = true;
1883
+ for (let s = from + 1; s < to && sorted; s++) sorted = this.segments[(s - 1) * FIELDS] <= this.segments[s * FIELDS];
1884
+ if (sorted) return;
1885
+ const line = [...Array(to - from).keys()].map((k) => this.segments.slice((from + k) * FIELDS, (from + k + 1) * FIELDS));
1886
+ line.sort((a, b) => a[GEN_COLUMN] - b[GEN_COLUMN]);
1887
+ line.forEach((seg, k) => this.segments.set(seg, (from + k) * FIELDS));
1783
1888
  }
1784
1889
  };
1785
- function decodeVlq(s) {
1786
- const out = [];
1787
- let value = 0, shift = 0;
1788
- for (const c of s) {
1789
- const digit = DIGIT.get(c);
1790
- if (digit === void 0) throw new Error(`invalid source map mapping '${s}'`);
1890
+ /** Decodes the VLQ fields of mappings[from, to) into `out`; returns how many there are. */
1891
+ function decodeVlq(mappings, from, to, out) {
1892
+ let n = 0, value = 0, shift = 0;
1893
+ for (let i = from; i < to; i++) {
1894
+ const c = mappings.charCodeAt(i);
1895
+ const digit = c < 128 ? DIGIT[c] : -1;
1896
+ if (digit < 0) throw new Error(`invalid source map mapping '${mappings.slice(from, to)}'`);
1791
1897
  value += (digit & 31) << shift;
1792
1898
  if (digit & 32) shift += 5;
1793
1899
  else {
1794
- out.push(value & 1 ? -(value >>> 1) : value >>> 1);
1900
+ if (n < out.length) out[n] = value & 1 ? -(value >>> 1) : value >>> 1;
1901
+ n++;
1795
1902
  value = 0;
1796
1903
  shift = 0;
1797
1904
  }
1798
1905
  }
1799
- return out;
1906
+ return n;
1800
1907
  }
1801
1908
  //#endregion
1802
1909
  //#region compiler/src/sema/analyze.ts
@@ -2676,6 +2783,134 @@ var Resolver = class {
2676
2783
  }
2677
2784
  };
2678
2785
  //#endregion
2786
+ //#region ir/src/types.ts
2787
+ const T = {
2788
+ None: 0,
2789
+ Undefined: 1,
2790
+ Null: 2,
2791
+ Boolean: 4,
2792
+ Int32: 8,
2793
+ Double: 16,
2794
+ String: 32,
2795
+ Symbol: 64,
2796
+ BigInt: 128,
2797
+ Object: 256,
2798
+ Empty: 512,
2799
+ Int64: 1024
2800
+ };
2801
+ const Number_ = T.Int32 | T.Double | T.Int64;
2802
+ const Primitive = T.Undefined | T.Null | T.Boolean | Number_ | T.String | T.Symbol | T.BigInt;
2803
+ const Any = Primitive | T.Object;
2804
+ /** Nothing known: any JS value, or EMPTY (what an uninitialized binding reads). */
2805
+ const Unknown = Any | T.Empty;
2806
+ function isSubtype(a, b) {
2807
+ return (a & ~b) === 0;
2808
+ }
2809
+ const NAMED$1 = [
2810
+ [Any | T.Empty, "Any|Empty"],
2811
+ [Any, "Any"],
2812
+ [Primitive, "Primitive"],
2813
+ [Number_, "Number"]
2814
+ ];
2815
+ const BITS = [
2816
+ [T.Undefined, "Undefined"],
2817
+ [T.Null, "Null"],
2818
+ [T.Boolean, "Boolean"],
2819
+ [T.Int32, "Int32"],
2820
+ [T.Double, "Double"],
2821
+ [T.String, "String"],
2822
+ [T.Symbol, "Symbol"],
2823
+ [T.BigInt, "BigInt"],
2824
+ [T.Object, "Object"],
2825
+ [T.Empty, "Empty"],
2826
+ [T.Int64, "Int64"]
2827
+ ];
2828
+ function typeName(t) {
2829
+ for (const [bits, name] of NAMED$1) if (t === bits) return name;
2830
+ if (t === T.None) return "None";
2831
+ const parts = [];
2832
+ let rest = t;
2833
+ if ((rest & Number_) === Number_) {
2834
+ parts.push("Number");
2835
+ rest &= ~Number_;
2836
+ }
2837
+ for (const [bit, name] of BITS) if (rest & bit) parts.push(name);
2838
+ return parts.join("|");
2839
+ }
2840
+ //#endregion
2841
+ //#region ir/src/views.ts
2842
+ /** Each kind's constructor, and its JSTypedArrayType in C. */
2843
+ const VIEWS = {
2844
+ i8: {
2845
+ ctor: "Int8Array",
2846
+ c: "JS_TA_INT8"
2847
+ },
2848
+ u8: {
2849
+ ctor: "Uint8Array",
2850
+ c: "JS_TA_UINT8"
2851
+ },
2852
+ i32: {
2853
+ ctor: "Int32Array",
2854
+ c: "JS_TA_INT32"
2855
+ },
2856
+ f64: {
2857
+ ctor: "Float64Array",
2858
+ c: "JS_TA_FLOAT64"
2859
+ }
2860
+ };
2861
+ const BY_CTOR = new Map(Object.entries(VIEWS).map(([kind, v]) => [v.ctor, kind]));
2862
+ /** The kind whose constructor is named `ctor`. */
2863
+ function viewOf(ctor) {
2864
+ return BY_CTOR.get(ctor);
2865
+ }
2866
+ //#endregion
2867
+ //#region ir/src/static.ts
2868
+ function isStatic(fn) {
2869
+ return fn.dialect === "static";
2870
+ }
2871
+ /** By the name the source writes: tsc erases `int` and `i32` to number, so they are known by name. */
2872
+ const STATIC_TYPES = {
2873
+ boolean: {
2874
+ type: T.Boolean,
2875
+ words: "boolean"
2876
+ },
2877
+ number: {
2878
+ type: T.Int32 | T.Double,
2879
+ words: "number"
2880
+ },
2881
+ int: {
2882
+ type: T.Int64,
2883
+ int: "i64",
2884
+ words: "number"
2885
+ },
2886
+ i32: {
2887
+ type: T.Int32,
2888
+ int: "i32",
2889
+ words: "i32"
2890
+ },
2891
+ string: {
2892
+ type: T.String,
2893
+ words: "string"
2894
+ },
2895
+ undefined: {
2896
+ type: T.Undefined,
2897
+ words: "undefined"
2898
+ },
2899
+ null: {
2900
+ type: T.Null,
2901
+ words: "null"
2902
+ }
2903
+ };
2904
+ /** A declared parameter's type as ERR_INVALID_ARG_TYPE words it: "of type string or undefined",
2905
+ * "an instance of Float64Array". */
2906
+ function expectedText(t, view) {
2907
+ if (view) return `an instance of ${VIEWS[view].ctor}`;
2908
+ const parts = Object.values(STATIC_TYPES).filter((s) => isSubtype(s.type, t));
2909
+ const widest = parts.filter((s) => !parts.some((o) => o !== s && isSubtype(s.type, o.type)));
2910
+ const names = [...new Set(widest.map((s) => s.words))];
2911
+ return `of type ${names.length > 2 ? `${names.slice(0, -1).join(", ")}, or ${names.at(-1)}` : names.join(" or ")}`;
2912
+ }
2913
+ //#endregion
2679
2914
  //#region ir/src/ir.ts
2680
2915
  const pure = (arity) => ({
2681
2916
  arity,
@@ -2866,6 +3101,29 @@ const OPS = {
2866
3101
  ...pure(1),
2867
3102
  allocates: false
2868
3103
  },
3104
+ char_code_at: {
3105
+ ...pure(2),
3106
+ allocates: true
3107
+ },
3108
+ math: pure(1),
3109
+ int_add: pure(2),
3110
+ int_sub: pure(2),
3111
+ int_mul: pure(2),
3112
+ to_i64: pure(1),
3113
+ to_i32: pure(1),
3114
+ int_div: {
3115
+ ...pure(2),
3116
+ mayThrow: true
3117
+ },
3118
+ int_mod: {
3119
+ ...pure(2),
3120
+ mayThrow: true
3121
+ },
3122
+ i64: pure(2),
3123
+ identical: pure(2),
3124
+ strict_eq_atom: pure(2),
3125
+ is_type: pure(1),
3126
+ strict_eq_num: pure(2),
2869
3127
  in: generic(2),
2870
3128
  instanceof: generic(2),
2871
3129
  to_property_key: generic(1),
@@ -3136,6 +3394,45 @@ const OPS = {
3136
3394
  call_runtime: call,
3137
3395
  take_exception: pure(0)
3138
3396
  };
3397
+ /** What a typed parameter of type `t` takes: an int any Number (the function converts it, as C). */
3398
+ function accepts(t) {
3399
+ return isSubtype(t, T.Int64) ? Number_ : t;
3400
+ }
3401
+ /** Whether a direct call of a typed function ("use static") may take its typed entry: each
3402
+ * declared parameter's argument proven of its type (`type`: the types; inference passes the ones
3403
+ * it is inferring). Otherwise the call goes through the checked entry, which throws a TypeError
3404
+ * for an argument that isn't: tsc lets typed code pass a number to an `int`, so a check remains. */
3405
+ function declaredCallFits(i, callee, type = (v) => v.type) {
3406
+ const declared = callee.meta?.declared;
3407
+ if (!declared) return true;
3408
+ return declared.every((t, k) => {
3409
+ if (t === void 0) return true;
3410
+ const o = i.operands[k];
3411
+ const a = o ? type(o) & ~T.Empty : T.Undefined;
3412
+ return a !== T.None && isSubtype(a, accepts(t));
3413
+ });
3414
+ }
3415
+ /** Whether a typed export's call takes its typed entry: as many arguments as it declares, each
3416
+ * proven of its declared type (inference, unboxing and C emission all ask this). A typed array
3417
+ * never is: the type lattice doesn't tell one from any other object. */
3418
+ function typedCallFits(i, type = (v) => v.type) {
3419
+ const tc = i.typedCall;
3420
+ if (!tc || i.operands.length - 2 !== tc.params.length || tc.params.some((p) => p.view)) return false;
3421
+ return i.operands.slice(2).every((o, k) => {
3422
+ const t = type(o) & ~T.Empty;
3423
+ return t !== T.None && isSubtype(t, accepts(tc.params[k].type));
3424
+ });
3425
+ }
3426
+ /** Whether i reads a string's length: its own, read-only property, an int32; reading it runs no code. */
3427
+ function isStringLength(i) {
3428
+ const s = i.operands[0];
3429
+ return i.op === "get_prop" && i.key !== void 0 && "name" in i.key && i.key.name === "length" && s !== void 0 && s.type !== T.None && isSubtype(s.type, T.String);
3430
+ }
3431
+ /** Whether i reads a Float64Array's element in typed code: a double, past the end undefined as
3432
+ * a number (NaN), as its type, number, says. */
3433
+ function readsDouble(i) {
3434
+ return i.taView === "f64" && (i.op === "get_index" || i.op === "get_elem") && isStatic(i.block.fn);
3435
+ }
3139
3436
  /** The key for `o.name` or `{ 1: v }`: canonical array indices (0 … 2^32 - 2) become index keys. */
3140
3437
  function propKey(v) {
3141
3438
  const s = String(v);
@@ -3165,59 +3462,6 @@ function isExceptionalEdge(from, to) {
3165
3462
  return from.handler === to && !normalSuccessors(from).includes(to);
3166
3463
  }
3167
3464
  //#endregion
3168
- //#region ir/src/types.ts
3169
- const T = {
3170
- None: 0,
3171
- Undefined: 1,
3172
- Null: 2,
3173
- Boolean: 4,
3174
- Int32: 8,
3175
- Double: 16,
3176
- String: 32,
3177
- Symbol: 64,
3178
- BigInt: 128,
3179
- Object: 256,
3180
- Empty: 512
3181
- };
3182
- const Number_ = T.Int32 | T.Double;
3183
- const Primitive = T.Undefined | T.Null | T.Boolean | Number_ | T.String | T.Symbol | T.BigInt;
3184
- const Any = Primitive | T.Object;
3185
- /** Nothing known: any JS value, or EMPTY (what an uninitialized binding reads). */
3186
- const Unknown = Any | T.Empty;
3187
- function isSubtype(a, b) {
3188
- return (a & ~b) === 0;
3189
- }
3190
- const NAMED = [
3191
- [Any | T.Empty, "Any|Empty"],
3192
- [Any, "Any"],
3193
- [Primitive, "Primitive"],
3194
- [Number_, "Number"]
3195
- ];
3196
- const BITS = [
3197
- [T.Undefined, "Undefined"],
3198
- [T.Null, "Null"],
3199
- [T.Boolean, "Boolean"],
3200
- [T.Int32, "Int32"],
3201
- [T.Double, "Double"],
3202
- [T.String, "String"],
3203
- [T.Symbol, "Symbol"],
3204
- [T.BigInt, "BigInt"],
3205
- [T.Object, "Object"],
3206
- [T.Empty, "Empty"]
3207
- ];
3208
- function typeName(t) {
3209
- for (const [bits, name] of NAMED) if (t === bits) return name;
3210
- if (t === T.None) return "None";
3211
- const parts = [];
3212
- let rest = t;
3213
- if ((rest & Number_) === Number_) {
3214
- parts.push("Number");
3215
- rest &= ~Number_;
3216
- }
3217
- for (const [bit, name] of BITS) if (rest & bit) parts.push(name);
3218
- return parts.join("|");
3219
- }
3220
- //#endregion
3221
3465
  //#region ir/src/builder.ts
3222
3466
  var FunctionBuilder = class {
3223
3467
  fn;
@@ -3528,7 +3772,7 @@ function printFunction(fn) {
3528
3772
  for (const phi of b.phis) lines.push(` ${def(phi)}phi ${phi.incoming.map((i) => `[${bb(i.block)}: ${name(i.value)}]`).join(", ")}`);
3529
3773
  for (const i of b.instrs) {
3530
3774
  if (i.op === "param") continue;
3531
- lines.push(` ${def(i)}${instr(i)}${i.numberFirst ? " ?number" : ""}${i.builtin ? ` ?${i.builtin}` : ""}${i.mayThrow ? " !mayThrow" : ""}`);
3775
+ lines.push(` ${def(i)}${instr(i)}${i.numberFirst ? " ?number" : ""}${i.builtin ? ` ?${i.builtin}` : ""}${i.keyedSite ? " ?keyed" : ""}${i.mayThrow ? " !mayThrow" : ""}`);
3532
3776
  }
3533
3777
  if (b.term) lines.push(` ${term(b.term)}`);
3534
3778
  }
@@ -3561,6 +3805,11 @@ function printFunction(fn) {
3561
3805
  case "call_runtime": return `${i.op} ${i.target}(${args})`;
3562
3806
  case "construct_direct": return `construct_direct ${i.target}(${args})`;
3563
3807
  case "same_function": return `same_function ${args}, ${i.target}`;
3808
+ case "i64": return `i64 ${i.target} ${args}`;
3809
+ case "is_type": return `${i.index ? "!" : ""}is_type ${args}, ${JSON.stringify(i.target)}`;
3810
+ case "identical":
3811
+ case "strict_eq_atom":
3812
+ case "strict_eq_num": return `${i.index ? "!" : ""}${i.op} ${args}`;
3564
3813
  case "new_instance": return `new_instance ${args}, ${i.index} slots`;
3565
3814
  case "get_prop":
3566
3815
  case "set_prop":
@@ -3706,12 +3955,15 @@ function verify(fn) {
3706
3955
  boxed: Any | T.Empty,
3707
3956
  f64: Number_,
3708
3957
  i32: T.Int32,
3958
+ i64: T.Int64 | T.Int32,
3709
3959
  bool: T.Boolean
3710
3960
  };
3711
3961
  const reprFits = (repr, t) => isSubtype(t, REPR_TYPE[repr]);
3962
+ const int64 = (t) => (t & T.Int64) !== 0 && isSubtype(t, T.Int64 | T.Int32);
3712
3963
  for (const i of [...fn.params, ...fn.blocks.flatMap((b) => [...b.phis, ...b.instrs])]) {
3713
3964
  if (!reprFits(i.repr, i.type)) bad(`%${i.id} ${i.op} is ${i.repr} but its type is ${typeName(i.type)}`);
3714
- if (i.repr !== "boxed" && (i.mayThrow && i.op !== "call_direct" || !OPS[i.op].result)) bad(`%${i.id} ${i.op} is ${i.repr} but can throw or has no result`);
3965
+ if (!isStatic(fn) && int64(i.type)) bad(`%${i.id} ${i.op} is an int64 outside static code`);
3966
+ if (i.repr !== "boxed" && (i.mayThrow && i.op !== "call_direct" && i.op !== "int_div" && i.op !== "int_mod" && !readsDouble(i) && !typedCallFits(i) || !OPS[i.op].result)) bad(`%${i.id} ${i.op} is ${i.repr} but can throw or has no result`);
3715
3967
  }
3716
3968
  const ret = fn.returnRepr ?? "boxed";
3717
3969
  for (const b of fn.blocks) if (b.term?.kind === "return" && !reprFits(ret, b.term.value.type)) bad(`bb${b.id} returns ${typeName(b.term.value.type)} from a function returning ${ret}`);
@@ -3809,6 +4061,21 @@ function hasSpread(items) {
3809
4061
  function notSupported(detail, node) {
3810
4062
  throw new CompileError("NotSupported", detail, node.loc);
3811
4063
  }
4064
+ /** Static code's integer operators: an `i64` (+ - * & | ^ << >> >>>; / and % by a constant other
4065
+ * than 0), or int_div and int_mod. */
4066
+ const INT_OPERATORS = /* @__PURE__ */ new Set([
4067
+ "+",
4068
+ "-",
4069
+ "*",
4070
+ "/",
4071
+ "%",
4072
+ "&",
4073
+ "|",
4074
+ "^",
4075
+ "<<",
4076
+ ">>",
4077
+ ">>>"
4078
+ ]);
3812
4079
  /** A function's `name` from its binding: a module's anonymous default export is "default"
3813
4080
  * (§16.2.3.7), though its hidden binding is `*default*`. */
3814
4081
  function functionName(binding) {
@@ -3859,7 +4126,26 @@ function lower(program, info) {
3859
4126
  const captured = [...scope.bindings.values()].filter((b) => b.captured);
3860
4127
  if (captured.length) envs.set(scope, new Map(captured.map((b, i) => [b, i])));
3861
4128
  }
3862
- const printAssigned = [...info.references.values()].some((r) => r.id.name === "print" && r.write && !r.binding);
4129
+ const declaredFunctions = /* @__PURE__ */ new Map();
4130
+ const ownNames = /* @__PURE__ */ new Map();
4131
+ for (const fn of info.functions.values()) {
4132
+ const node = fn.node;
4133
+ if (node.type === "FunctionDeclaration") {
4134
+ const binding = info.declarations.get(node.id);
4135
+ if (!declaredFunctions.has(binding)) declaredFunctions.set(binding, fn);
4136
+ } else if (node.type === "FunctionExpression" && node.id) ownNames.set(info.declarations.get(node.id), fn);
4137
+ }
4138
+ const classesById = /* @__PURE__ */ new Map();
4139
+ for (const sc of info.scopes.values()) {
4140
+ const cls = sc.node;
4141
+ if (sc.kind === "class" && cls.id && !classesById.has(cls.id)) classesById.set(cls.id, cls);
4142
+ }
4143
+ let printAssigned = false;
4144
+ const readsOwnName = /* @__PURE__ */ new Set();
4145
+ for (const r of info.references.values()) {
4146
+ if (r.id.name === "print" && r.write && !r.binding) printAssigned = true;
4147
+ if (r.binding && ownNames.get(r.binding) === r.func) readsOwnName.add(r.func);
4148
+ }
3863
4149
  const m = {
3864
4150
  info,
3865
4151
  slots,
@@ -3868,15 +4154,25 @@ function lower(program, info) {
3868
4154
  inferredNames: /* @__PURE__ */ new Map(),
3869
4155
  printAssigned,
3870
4156
  globals,
3871
- importMetaSlots: /* @__PURE__ */ new Map()
4157
+ importMetaSlots: /* @__PURE__ */ new Map(),
4158
+ declaredFunctions,
4159
+ classesById,
4160
+ readsOwnName,
4161
+ static: program.static === true
3872
4162
  };
3873
4163
  const main = new FunctionLowering(m, info.program).run();
3874
4164
  const functions = [];
3875
- for (const fn of info.functions.values()) if (fn !== info.program) functions.push(new FunctionLowering(m, fn).run());
3876
- for (const f of functions) {
3877
- const node = [...ids].find(([, id]) => id === f.name)[0];
4165
+ const nodes = [];
4166
+ for (const fn of info.functions.values()) {
4167
+ if (fn === info.program) continue;
4168
+ functions.push(new FunctionLowering(m, fn).run());
4169
+ nodes.push(fn.node);
4170
+ }
4171
+ functions.forEach((f, i) => {
4172
+ const node = nodes[i];
3878
4173
  if (node.type === "FunctionExpression" && !node.id) f.meta.jsName = m.inferredNames.get(node) ?? "";
3879
- }
4174
+ });
4175
+ if (m.static) for (const fn of [main, ...functions]) fn.dialect = "static";
3880
4176
  return {
3881
4177
  main,
3882
4178
  functions,
@@ -3947,8 +4243,7 @@ var FunctionLowering = class {
3947
4243
  this.b = new FunctionBuilder("main", [], fn.strict, node.loc);
3948
4244
  return;
3949
4245
  }
3950
- const self = node.type === "FunctionExpression" && node.id ? this.info.declarations.get(node.id) : null;
3951
- const readsSelf = self !== null && [...this.info.references.values()].some((r) => r.binding === self && r.func === fn);
4246
+ const readsSelf = (node.type === "FunctionExpression" && node.id ? this.info.declarations.get(node.id) : null) !== null && m.readsOwnName.has(fn);
3952
4247
  const takesArguments = fn.argumentsBinding !== null;
3953
4248
  const mappedArguments = takesArguments && !fn.strict && fn.simpleParams;
3954
4249
  const classCtor = node.type === "FunctionExpression" ? node.classCtor : null;
@@ -4085,7 +4380,6 @@ var FunctionLowering = class {
4085
4380
  name: "arguments"
4086
4381
  });
4087
4382
  }
4088
- /** A parameter with a default: the argument, or the default when it's undefined. */
4089
4383
  /** After gen_start or async_start: the resume function's first block. */
4090
4384
  startBody() {
4091
4385
  const b = this.b;
@@ -4474,6 +4768,7 @@ var FunctionLowering = class {
4474
4768
  return true;
4475
4769
  }
4476
4770
  write(binding, value) {
4771
+ if (this.m.static && binding.node.int && value.op !== "empty") value = this.toInt(value, binding.node.int);
4477
4772
  if (this.isGlobalVar(binding)) {
4478
4773
  this.b.emit("set_prop", [this.b.emit("global_this", []), value], { key: propKey(binding.name) });
4479
4774
  return;
@@ -4516,6 +4811,35 @@ var FunctionLowering = class {
4516
4811
  });
4517
4812
  return ref.tdz ? b.emit("check_tdz", [v], { name: binding.name }) : v;
4518
4813
  }
4814
+ /** Static code: whether `e` is an integer, as C's types say: an int binding, an integer
4815
+ * literal, a call of a function declared to return one, or integer arithmetic of those. A
4816
+ * `number` is a double, whatever values it holds. */
4817
+ isInt(e) {
4818
+ if (!this.m.static) return false;
4819
+ const declared = (id) => this.info.references.get(id)?.binding?.node;
4820
+ switch (e.type) {
4821
+ case "Literal": return typeof e.value === "number" && Number.isInteger(e.value) && !e.float;
4822
+ case "Identifier": return declared(e)?.int !== void 0;
4823
+ case "UnaryExpression": return (e.operator === "-" || e.operator === "~") && this.isInt(e.argument);
4824
+ case "UpdateExpression": return this.isInt(e.argument);
4825
+ case "BinaryExpression": return INT_OPERATORS.has(e.operator) && this.isInt(e.left) && this.isInt(e.right);
4826
+ case "AssignmentExpression": return e.target.type === "Identifier" && this.isInt(e.target);
4827
+ case "ConditionalExpression": return this.isInt(e.consequent) && this.isInt(e.alternate);
4828
+ case "CallExpression": return e.callee.type === "Identifier" && declared(e.callee)?.returnsInt !== void 0;
4829
+ default: return false;
4830
+ }
4831
+ }
4832
+ /** Static code's integer arithmetic, C's: / and % by a constant other than 0 can't throw. */
4833
+ int64(operator, left, right) {
4834
+ if ((operator === "/" || operator === "%") && !(right.op === "const" && right.lit.value !== 0)) return this.b.emit(operator === "/" ? "int_div" : "int_mod", [left, right]);
4835
+ return this.b.emit("i64", [left, right], { target: operator });
4836
+ }
4837
+ /** Static code: `value` converted to an integer, as C converts what is stored in one. */
4838
+ toInt(value, kind) {
4839
+ const op = kind === "i32" ? "to_i32" : "to_i64";
4840
+ if (value.op === op || kind === "i64" && (value.op === "i64" || value.op === "int_div" || value.op === "int_mod")) return value;
4841
+ return this.b.emit(op, [value]);
4842
+ }
4519
4843
  /** Assigns to an identifier (not a declaration's initialization). */
4520
4844
  assign(id, value) {
4521
4845
  const ref = this.info.references.get(id);
@@ -4607,6 +4931,8 @@ var FunctionLowering = class {
4607
4931
  case "ReturnStatement": {
4608
4932
  let value = s.argument ? this.expression(s.argument) : b.const(void 0);
4609
4933
  if (s.argument && this.asyncGenerator) value = b.emit("await", [value]);
4934
+ const returnsInt = this.fn.node.type === "Program" ? void 0 : this.fn.node.returnsInt;
4935
+ if (this.m.static && returnsInt) value = this.toInt(value, returnsInt);
4610
4936
  if (this.derivedCtor) value = s.argument ? b.emit("derived_return", [value, this.loadBinding(this.fn.thisBinding)]) : this.readThis();
4611
4937
  b.writeVariable(this.retval, value);
4612
4938
  return this.transfer({ kind: "return" }, this.frames.length);
@@ -5103,6 +5429,7 @@ var FunctionLowering = class {
5103
5429
  if (e.operator === "delete") return this.delete(e);
5104
5430
  const v = e.operator === "typeof" && e.argument.type === "Identifier" ? this.read(this.info.references.get(e.argument), e.argument, true) : this.expression(e.argument);
5105
5431
  b.loc = e.loc;
5432
+ if ((e.operator === "-" || e.operator === "~") && this.isInt(e.argument)) return e.operator === "-" ? this.int64("-", b.const(0), v) : this.int64("^", v, b.const(-1));
5106
5433
  switch (e.operator) {
5107
5434
  case "-": return b.emit("neg", [v]);
5108
5435
  case "+": return b.emit("to_number", [v]);
@@ -5117,6 +5444,11 @@ var FunctionLowering = class {
5117
5444
  const target = this.targetRef(e.argument, true);
5118
5445
  const old = this.readTarget(target);
5119
5446
  b.loc = e.loc;
5447
+ if (this.isInt(e.argument)) {
5448
+ const updated = this.int64(e.operator === "++" ? "+" : "-", old, b.const(1));
5449
+ this.writeTarget(target, updated);
5450
+ return e.prefix ? updated : old;
5451
+ }
5120
5452
  const num = b.emit("to_numeric", [old]);
5121
5453
  const updated = b.emit(e.operator === "++" ? "inc" : "dec", [num]);
5122
5454
  this.writeTarget(target, updated);
@@ -5131,6 +5463,7 @@ var FunctionLowering = class {
5131
5463
  const left = this.expression(e.left);
5132
5464
  const right = this.expression(e.right);
5133
5465
  b.loc = e.loc;
5466
+ if (this.isInt(e)) return this.int64(e.operator, left, right);
5134
5467
  return b.emit(BINARY[e.operator], [left, right]);
5135
5468
  }
5136
5469
  case "LogicalExpression": return this.logical(e.operator, () => this.expression(e.left), () => this.expression(e.right));
@@ -5400,7 +5733,8 @@ var FunctionLowering = class {
5400
5733
  const old = this.readTarget(target);
5401
5734
  const rhs = this.expression(e.value);
5402
5735
  b.loc = e.loc;
5403
- const value = b.emit(BINARY[e.operator.slice(0, -1)], [old, rhs]);
5736
+ const operator = e.operator.slice(0, -1);
5737
+ const value = INT_OPERATORS.has(operator) && this.isInt(e.target) && this.isInt(e.value) ? this.int64(operator, old, rhs) : b.emit(BINARY[operator], [old, rhs]);
5404
5738
  this.writeTarget(target, value);
5405
5739
  return value;
5406
5740
  }
@@ -5476,8 +5810,6 @@ var FunctionLowering = class {
5476
5810
  value
5477
5811
  ]);
5478
5812
  }
5479
- /** An assignment target, evaluated once. `readFirst`: a compound assignment or update,
5480
- * which converts a computed key once for both the read and the write. */
5481
5813
  /** A binding's current value, without a TDZ check. */
5482
5814
  loadBinding(binding) {
5483
5815
  if (this.isLocal(binding)) return this.b.readVariable(binding);
@@ -5626,6 +5958,8 @@ var FunctionLowering = class {
5626
5958
  this.exitScope(scope);
5627
5959
  return ctor;
5628
5960
  }
5961
+ /** An assignment target, evaluated once. `readFirst`: a compound assignment or update,
5962
+ * which converts a computed key once for both the read and the write. */
5629
5963
  targetRef(t, readFirst) {
5630
5964
  if (t.type === "Identifier") return { id: t };
5631
5965
  const ref = this.memberRef(t);
@@ -5746,7 +6080,7 @@ var FunctionLowering = class {
5746
6080
  }
5747
6081
  return this.genericCall(this.expression(callee), b.const(void 0), e, calleeText(callee));
5748
6082
  }
5749
- const target = [...this.info.functions.values()].find((f) => f.node.type === "FunctionDeclaration" && this.info.declarations.get(f.node.id) === binding);
6083
+ const target = this.m.declaredFunctions.get(binding);
5750
6084
  if (binding.scope !== this.info.program.scope || binding.reassigned || !target || hasSpread(e.arguments) || target.argumentsBinding || target.generator) return this.genericCall(this.expression(callee), b.const(void 0), e, calleeText(callee));
5751
6085
  const decl = target.node;
5752
6086
  const args = e.arguments.map((a) => this.expression(a));
@@ -5768,9 +6102,9 @@ var FunctionLowering = class {
5768
6102
  const binding = this.info.references.get(e.callee)?.binding;
5769
6103
  if (!binding || binding.reassigned) return null;
5770
6104
  let ctor;
5771
- if (binding.kind === "function") ctor = [...this.info.functions.values()].find((f) => f.node.type === "FunctionDeclaration" && this.info.declarations.get(f.node.id) === binding)?.node;
6105
+ if (binding.kind === "function") ctor = this.m.declaredFunctions.get(binding)?.node;
5772
6106
  else if (binding.kind === "class" || binding.kind === "const" && binding.scope.kind === "class") {
5773
- const cls = [...this.info.scopes.values()].find((sc) => sc.kind === "class" && sc.node.id === binding.node)?.node;
6107
+ const cls = this.m.classesById.get(binding.node);
5774
6108
  if (cls && !cls.superClass) ctor = cls.ctor;
5775
6109
  } else {
5776
6110
  const init = this.info.initializers.get(binding);
@@ -6032,8 +6366,7 @@ const RUNTIME = {
6032
6366
  function argArray(args) {
6033
6367
  return args.length ? `(const JSValue[]){ ${args.join(", ")} }` : "NULL";
6034
6368
  }
6035
- /** A C identifier fragment from a JS name. */
6036
- function ident(name) {
6369
+ function ident$1(name) {
6037
6370
  let out = "";
6038
6371
  for (const ch of name) out += /[A-Za-z0-9_]/.test(ch) ? ch : `_u${ch.codePointAt(0).toString(16)}_`;
6039
6372
  return out;
@@ -6218,12 +6551,14 @@ const C_TYPE = {
6218
6551
  boxed: "JSValue",
6219
6552
  f64: "double",
6220
6553
  i32: "int32_t",
6554
+ i64: "int64_t",
6221
6555
  bool: "bool"
6222
6556
  };
6223
6557
  const C_ZERO = {
6224
6558
  boxed: "JS_UNDEFINED",
6225
6559
  f64: "0",
6226
6560
  i32: "0",
6561
+ i64: "0",
6227
6562
  bool: "false"
6228
6563
  };
6229
6564
  /** What a direct entry returns after a throw: unboxed, any value, the pending
@@ -6232,17 +6567,50 @@ const C_FAIL = {
6232
6567
  boxed: "JS_EXCEPTION",
6233
6568
  f64: "0",
6234
6569
  i32: "0",
6570
+ i64: "0",
6235
6571
  bool: "false"
6236
6572
  };
6237
6573
  /** A C expression of repr `from` as repr `to`, when the type allows it (an int32_t only ever
6238
6574
  * holds int32s, so converting to one can't lose anything). */
6239
6575
  function convert(expr, from, to) {
6240
6576
  if (from === to) return expr;
6577
+ if (from === "i64") return to === "boxed" ? `js_number((double)(${expr}))` : to === "f64" ? `(double)(${expr})` : to === "i32" ? `(int32_t)(${expr})` : `((${expr}) != 0)`;
6578
+ if (to === "i64") return from === "i32" ? `(int64_t)(${expr})` : from === "f64" ? `js_f64_to_i64(${expr})` : from === "bool" ? `(int64_t)(${expr})` : `js_value_to_i64(${expr})`;
6241
6579
  if (from === "i32") return to === "boxed" ? `js_int32(${expr})` : `(double)(${expr})`;
6242
6580
  if (to === "boxed") return from === "bool" ? `js_bool(${expr})` : `js_number(${expr})`;
6243
6581
  if (to === "i32") return from === "f64" ? `(int32_t)(${expr})` : `js_as_int32_value(${expr})`;
6244
6582
  return to === "bool" ? `js_as_bool(${expr})` : `js_as_number(${expr})`;
6245
6583
  }
6584
+ /** The narrowest repr that holds every value of a type: an int32_t, an int64_t, a double, or boxed. */
6585
+ function reprOf(t) {
6586
+ if (isSubtype(t, T.Int32)) return "i32";
6587
+ if (isSubtype(t, T.Int64 | T.Int32)) return "i64";
6588
+ return isSubtype(t, Number_) ? "f64" : "boxed";
6589
+ }
6590
+ /** The operators on proven Numbers (specialize's num_* forms): their C is unboxed() either way. */
6591
+ const NUM_OPS = /* @__PURE__ */ new Set([
6592
+ "num_add",
6593
+ "num_sub",
6594
+ "num_mul",
6595
+ "num_div",
6596
+ "num_mod",
6597
+ "num_neg",
6598
+ "num_inc",
6599
+ "num_dec",
6600
+ "num_lt",
6601
+ "num_le",
6602
+ "num_gt",
6603
+ "num_ge",
6604
+ "num_eq",
6605
+ "num_ne",
6606
+ "num_bitand",
6607
+ "num_bitor",
6608
+ "num_bitxor",
6609
+ "num_bitnot",
6610
+ "num_shl",
6611
+ "num_sar",
6612
+ "num_shr"
6613
+ ]);
6246
6614
  /** Number comparisons: a C bool from doubles or int32s. */
6247
6615
  const BOOL_OPS = /* @__PURE__ */ new Set([
6248
6616
  "num_lt",
@@ -6252,7 +6620,6 @@ const BOOL_OPS = /* @__PURE__ */ new Set([
6252
6620
  "num_eq",
6253
6621
  "num_ne"
6254
6622
  ]);
6255
- /** A Number as a C double literal. */
6256
6623
  function cDouble(d) {
6257
6624
  if (Number.isNaN(d)) return "NAN";
6258
6625
  if (d === Infinity) return "INFINITY";
@@ -6261,9 +6628,47 @@ function cDouble(d) {
6261
6628
  if (Number.isSafeInteger(d)) return `${d}.0`;
6262
6629
  return hexFloat(d);
6263
6630
  }
6264
- /** Inline caches: one per *_prop site with a named key, in mod_ics[]. */
6631
+ /** A charCodeAt whose index inference proved in the string: its result an int32. */
6632
+ const inString = (i) => isSubtype(i.type, T.Int32) && i.type !== T.None;
6633
+ /** Static code's integer operator on int64_ts, as C's: wrapping (as unsigned), shifts mod 64,
6634
+ * INT64_MIN / -1 wrapping too (C leaves it undefined). */
6635
+ function int64Op(op, x, y) {
6636
+ const wrap = (o) => `(int64_t)((uint64_t)${x} ${o} (uint64_t)${y})`;
6637
+ switch (op) {
6638
+ case "+":
6639
+ case "-":
6640
+ case "*": return wrap(op);
6641
+ case "/": return `(${y} == -1 ? (int64_t)(0 - (uint64_t)${x}) : ${x} / ${y})`;
6642
+ case "%": return `(${y} == -1 ? INT64_C(0) : ${x} % ${y})`;
6643
+ case "&":
6644
+ case "|":
6645
+ case "^": return `(${x} ${op} ${y})`;
6646
+ case "<<": return `(int64_t)((uint64_t)${x} << (${y} & 63))`;
6647
+ case ">>": return `(${x} >> (${y} & 63))`;
6648
+ case ">>>": return `(int64_t)((uint64_t)${x} >> (${y} & 63))`;
6649
+ default: throw new Error(`i64 ${op}`);
6650
+ }
6651
+ }
6652
+ /** `typeof v === name` (or v == null, name "nullish") as a C condition: js_typeof's cases. */
6653
+ function isTypeTest(name, v) {
6654
+ switch (name) {
6655
+ case "nullish": return `js_is_nullish(${v})`;
6656
+ case "undefined": return `js_is_undefined(${v})`;
6657
+ case "boolean": return `js_is_bool(${v})`;
6658
+ case "number": return `js_is_number(${v})`;
6659
+ case "string": return `js_is_string(${v})`;
6660
+ case "symbol": return `js_is_symbol(${v})`;
6661
+ case "bigint": return `js_is_bigint(${v})`;
6662
+ case "function": return `js_is_callable(${v})`;
6663
+ case "object": return `(js_is_null(${v}) || (js_is_object(${v}) && !js_is_callable(${v})))`;
6664
+ default: return "false";
6665
+ }
6666
+ }
6667
+ /** Inline caches: one per *_prop site with a named key, in mod_ics[]; a keyed site's key, in
6668
+ * mod_keys[] (GC roots). */
6265
6669
  var Caches = class {
6266
6670
  count = 0;
6671
+ keys = 0;
6267
6672
  };
6268
6673
  function constant(v, strings) {
6269
6674
  if (v === void 0) return "JS_UNDEFINED";
@@ -6278,10 +6683,10 @@ function constant(v, strings) {
6278
6683
  return `js_double(${hexFloat(v)})`;
6279
6684
  }
6280
6685
  function cFunctionName(fn, isMain, prefix = "") {
6281
- return isMain ? `${prefix}js_script` : `${prefix}jsf_${ident(fn.name)}`;
6686
+ return isMain ? `${prefix}js_script` : `${prefix}jsf_${ident$1(fn.name)}`;
6282
6687
  }
6283
6688
  function infoName(fnName, prefix) {
6284
- return `${prefix}jsfi_${ident(fnName)}`;
6689
+ return `${prefix}jsfi_${ident$1(fnName)}`;
6285
6690
  }
6286
6691
  /** A function's `this` parameter in its direct entry (after the environment and the
6287
6692
  * function object), when it takes one. */
@@ -6300,6 +6705,20 @@ function instanceSlots(fn) {
6300
6705
  for (const b of fn.blocks) for (const i of b.instrs) if (addsToThis$1(fn, i)) keys.add(i.key.name);
6301
6706
  return keys.size ? Math.min(keys.size, 16) : 4;
6302
6707
  }
6708
+ /** A C test that `v` (evaluated more than once) has one of the kinds in `t`. */
6709
+ function typeTest(t, v) {
6710
+ const tests = [];
6711
+ if (t & T.Undefined) tests.push(`js_is_undefined(${v})`);
6712
+ if (t & T.Null) tests.push(`js_is_null(${v})`);
6713
+ if (t & T.Boolean) tests.push(`js_is_bool(${v})`);
6714
+ if ((t & Number_) === Number_ || t & (T.Double | T.Int64)) tests.push(`js_is_number(${v})`);
6715
+ else if (t & T.Int32) tests.push(`js_is_int32(${v})`);
6716
+ if (t & T.String) tests.push(`js_is_string(${v})`);
6717
+ if (t & T.Symbol) tests.push(`js_is_symbol(${v})`);
6718
+ if (t & T.BigInt) tests.push(`js_is_bigint(${v})`);
6719
+ if (t & T.Object) tests.push(`js_is_object(${v})`);
6720
+ return tests.length ? tests.join(" || ") : "false";
6721
+ }
6303
6722
  /** The generic entry: unpacks argv into the direct entry's parameters. */
6304
6723
  function genericEntry(fn, prefix) {
6305
6724
  const meta = fn.meta;
@@ -6324,9 +6743,17 @@ function genericEntry(fn, prefix) {
6324
6743
  ].filter((p) => !(p === "callee" && (meta.takesCallee || meta.takesEnv)) && !(p === "this_val" && meta.takesThis) && !((p === "argc" || p === "argv") && usesArgv) && !(p === "new_target" && meta.takesNewTarget));
6325
6744
  const call = `${direct}(${["ctx", ...args.map((a, k) => convert(a, "boxed", fn.params[k].repr))].join(", ")})`;
6326
6745
  const ret = fn.returnRepr ?? "boxed";
6746
+ const hidden = fn.params.length - declared - Number(meta.hasRest);
6747
+ const checks = (meta.declared ?? []).flatMap((t, k) => {
6748
+ if (t === void 0) return [];
6749
+ const v = `(argc > ${k - hidden} ? argv[${k - hidden}] : JS_UNDEFINED)`;
6750
+ const view = meta.declaredViews?.[k];
6751
+ return [` if (!(${view ? `js_ta_is(ctx, ${v}, ${VIEWS[view].c})` : typeTest(t, v)})) return js_throw_invalid_arg_type(ctx, ${cString(fn.params[k].name ?? `argument ${k - hidden + 1}`)}, ${cString(expectedText(t, view))}, ${v});`];
6752
+ });
6327
6753
  return [
6328
- `static JSValue ${direct}__generic(JSContext *ctx, JSValue callee, JSValue this_val, uint32_t argc, const JSValue *argv, JSValue new_target) {`,
6754
+ `${meta.declared ? "" : "static "}JSValue ${direct}__generic(JSContext *ctx, JSValue callee, JSValue this_val, uint32_t argc, const JSValue *argv, JSValue new_target) {`,
6329
6755
  ...unused.length ? [` ${unused.map((u) => `(void)${u};`).join(" ")}`] : [],
6756
+ ...checks,
6330
6757
  ...ret === "boxed" ? [` return ${call};`] : [` ${C_TYPE[ret]} r = ${call};`, ` return js_has_exception(ctx) ? JS_EXCEPTION : ${convert("r", ret, "boxed")};`],
6331
6758
  "}"
6332
6759
  ].join("\n");
@@ -6361,6 +6788,8 @@ function functionInfo(fn, strings, embedSource, shared = false) {
6361
6788
  var Hosts = class {
6362
6789
  table;
6363
6790
  used = /* @__PURE__ */ new Set();
6791
+ /** Typed entries of a typed unit this module calls: their prototypes. */
6792
+ typed = /* @__PURE__ */ new Map();
6364
6793
  constructor(table) {
6365
6794
  this.table = table;
6366
6795
  }
@@ -6371,7 +6800,7 @@ var Hosts = class {
6371
6800
  return host;
6372
6801
  }
6373
6802
  decls() {
6374
- return [...this.used].sort().map((c) => `JSValue ${c}(JSContext *, JSValue, JSValue, uint32_t, const JSValue *, JSValue); /* host primitive */`);
6803
+ return [...[...this.used].sort().map((c) => `JSValue ${c}(JSContext *, JSValue, JSValue, uint32_t, const JSValue *, JSValue); /* host primitive */`), ...[...this.typed.values()].sort()];
6375
6804
  }
6376
6805
  };
6377
6806
  var FunctionEmitter = class {
@@ -6388,6 +6817,9 @@ var FunctionEmitter = class {
6388
6817
  /** Values something needs: instructions and terminators, and the phis feeding them. */
6389
6818
  used = /* @__PURE__ */ new Set();
6390
6819
  labels = /* @__PURE__ */ new Set();
6820
+ /** An exception leaves the function: through one shared return, which GCC compiles faster than
6821
+ * a return at each check (4% of a large program's compile time). */
6822
+ exits = false;
6391
6823
  lines = [];
6392
6824
  lastLoc = null;
6393
6825
  constructor(fn, cname, strings, caches, hosts, callees) {
@@ -6459,9 +6891,8 @@ var FunctionEmitter = class {
6459
6891
  v(i) {
6460
6892
  return this.as(i, "boxed");
6461
6893
  }
6462
- /** A value's C variable. */
6463
6894
  name(i) {
6464
- return `v${i.id}${i.name ? `_${ident(i.name)}` : ""}`;
6895
+ return `v${i.id}${i.name ? `_${ident$1(i.name)}` : ""}`;
6465
6896
  }
6466
6897
  /** A value as a C expression in representation `repr` (the optimizer's unbox pass
6467
6898
  * proved the type, so unboxing can't fail). */
@@ -6470,6 +6901,7 @@ var FunctionEmitter = class {
6470
6901
  const value = i.lit.value;
6471
6902
  if (repr === "f64") return cDouble(value);
6472
6903
  if (repr === "i32") return value === -2147483648 ? "INT32_MIN" : `${value}`;
6904
+ if (repr === "i64") return Number.isSafeInteger(value) ? `INT64_C(${value})` : `js_f64_to_i64(${cDouble(value)})`;
6473
6905
  if (repr === "bool") return value ? "true" : "false";
6474
6906
  return constant(value, this.strings);
6475
6907
  }
@@ -6478,9 +6910,21 @@ var FunctionEmitter = class {
6478
6910
  }
6479
6911
  /** A num_* operator on unboxed operands: plain C. Doubles; int32s where both operands are
6480
6912
  * (an int32 result is one inference proved can't overflow); bitwise ones through ToInt32. */
6481
- unboxed(i) {
6913
+ unboxed(i, repr = i.repr) {
6482
6914
  const isInt = (o) => isSubtype(o.type, T.Int32);
6483
- if (i.operands.every(isInt) && (i.repr === "i32" || BOOL_OPS.has(i.op))) {
6915
+ const isLong = (o) => isSubtype(o.type, T.Int64 | T.Int32);
6916
+ if (BOOL_OPS.has(i.op) && i.operands.every(isLong) && i.operands.some((o) => (o.type & T.Int64) !== 0)) {
6917
+ const [x, y] = i.operands.map((o) => this.as(o, "i64"));
6918
+ switch (i.op) {
6919
+ case "num_lt": return `${x} < ${y}`;
6920
+ case "num_le": return `${x} <= ${y}`;
6921
+ case "num_gt": return `${x} > ${y}`;
6922
+ case "num_ge": return `${x} >= ${y}`;
6923
+ case "num_eq": return `${x} == ${y}`;
6924
+ case "num_ne": return `${x} != ${y}`;
6925
+ }
6926
+ }
6927
+ if (i.operands.every(isInt) && (repr === "i32" || BOOL_OPS.has(i.op))) {
6484
6928
  const [x, y] = i.operands.map((o) => this.as(o, "i32"));
6485
6929
  switch (i.op) {
6486
6930
  case "num_add": return `${x} + ${y}`;
@@ -6497,7 +6941,7 @@ var FunctionEmitter = class {
6497
6941
  }
6498
6942
  const [a, b] = i.operands.map((o) => this.as(o, "f64"));
6499
6943
  const [ia, ib] = i.operands.map((o) => isInt(o) ? this.as(o, "i32") : `js_f64_to_int32(${this.as(o, "f64")})`);
6500
- const bits = (expr) => i.repr === "i32" ? expr : `(double)(${expr})`;
6944
+ const bits = (expr) => repr === "i32" ? expr : `(double)(${expr})`;
6501
6945
  switch (i.op) {
6502
6946
  case "num_add": return `${a} + ${b}`;
6503
6947
  case "num_sub": return `${a} - ${b}`;
@@ -6554,7 +6998,6 @@ var FunctionEmitter = class {
6554
6998
  this.labels.add(to);
6555
6999
  return [...this.copies(from, to), `goto bb${to.id};`].join(" ");
6556
7000
  }
6557
- /** Where an exception raised in `b` goes. */
6558
7001
  /** `new F(…)` of a constructor of this module. When F's value is that
6559
7002
  * function (made in this realm), `this` comes from js_new_instance and the constructor's direct
6560
7003
  * entry runs; an object it returns wins, as in [[Construct]] (§10.2.2). Anything else, through
@@ -6571,7 +7014,7 @@ var FunctionEmitter = class {
6571
7014
  ...meta.takesNewTarget ? ["c"] : []
6572
7015
  ];
6573
7016
  const passed = i.operands.slice(1).map((o, k) => this.as(o, callee.params[hidden.length + k]?.repr ?? "boxed"));
6574
- const call = `${this.strings.prefix}jsf_${ident(callee.name)}(ctx${[...hidden, ...passed].map((a) => `, ${a}`).join("")})`;
7017
+ const call = `${this.strings.prefix}jsf_${ident$1(callee.name)}(ctx${[...hidden, ...passed].map((a) => `, ${a}`).join("")})`;
6575
7018
  const ret = callee.returnRepr ?? "boxed";
6576
7019
  const result = out ? `${out} = ` : "";
6577
7020
  this.lines.push(` { JSValue c = ${args[0]};`, ` if (JS_LIKELY(js_is_function(c) && js_as_function(c)->info == &${infoName(callee.name, this.strings.prefix)} && js_as_function(c)->realm == ctx)) {`, ` JSValue t = js_new_instance(ctx, c, ${instanceSlots(callee)}, ${this.cache()});`, ` if (JS_UNLIKELY(js_is_exception(t))) { ${fail} }`, ...ret === "boxed" ? [
@@ -6586,6 +7029,72 @@ var FunctionEmitter = class {
6586
7029
  ...out ? [` ${result}t;`] : []
6587
7030
  ], " } else {", ` JSValue r = js_construct(ctx, c, ${args.length - 1}, ${argArray(args.slice(1))}, JS_UNDEFINED, ${cString(i.text ?? "")});`, ` if (JS_UNLIKELY(js_is_exception(r))) { ${fail} }`, ...out ? [` ${result}r;`] : [" js_discard(r);"], " } }");
6588
7031
  }
7032
+ /** An access of a typed array's view (the typed-arrays pass): the variant for its key's repr
7033
+ * (_l an int64_t, _i an int32_t, _d a double); a set with `strict`, of a double unboxed. A
7034
+ * double read (readsDouble) stores into `d`. */
7035
+ viewAccess(i, args, strict) {
7036
+ const key = i.operands[1];
7037
+ const [suffix, k] = key.repr === "i64" ? ["_l", this.as(key, "i64")] : key.repr === "i32" ? ["_i", this.as(key, "i32")] : key.repr === "f64" ? ["_d", this.as(key, "f64")] : ["", args[1]];
7038
+ const view = `&${this.name(i.operands[0])}_view`;
7039
+ if (strict === void 0) return readsDouble(i) ? `js_tav_num_f64${suffix}(ctx, ${view}, ${args[0]}, ${k}, &d)` : `js_tav_get_${i.taView}${suffix}(ctx, ${view}, ${args[0]}, ${k})`;
7040
+ const value = i.operands[2];
7041
+ return i.taView === "f64" && value.repr === "f64" ? `js_tav_put_f64${suffix}(ctx, ${view}, ${args[0]}, ${k}, ${this.as(value, "f64")}, ${strict})` : `js_tav_set_${i.taView}${suffix}(ctx, ${view}, ${args[0]}, ${k}, ${args[2]}, ${strict})`;
7042
+ }
7043
+ /** A double read of a Float64Array's view: false from the access is an exception. */
7044
+ readDouble(i, args) {
7045
+ this.lines.push(` { double d; if (JS_UNLIKELY(!${this.viewAccess(i, args)})) { ${this.onException(i.block)} }`, this.hasVar(i) ? ` ${this.name(i)} = ${convert("d", "f64", i.repr)}; }` : " }");
7046
+ }
7047
+ /** A call returning a JSValue: its exception checked, its result into i's variable. */
7048
+ boxedCall(i, call) {
7049
+ const out = this.hasVar(i) ? this.name(i) : null;
7050
+ this.lines.push(` { JSValue r = ${call};`, ` if (JS_UNLIKELY(js_is_exception(r))) { ${this.onException(i.block)} }`, ...out ? [` ${out} = ${convert("r", "boxed", i.repr)}; }`] : [" js_discard(r); }"]);
7051
+ }
7052
+ /** The string parameters whose units this function reads at indices inference proved in them:
7053
+ * their units taken once, on entry (strings don't change, and the GC doesn't move cells; a
7054
+ * rope not yet flattened has none, and each read takes the slow path). */
7055
+ stringViewList = null;
7056
+ stringViews() {
7057
+ this.stringViewList ??= new Set(this.fn.blocks.flatMap((b) => b.instrs).filter((i) => i.op === "char_code_at" && inString(i) && i.operands[0].op === "param").map((i) => i.operands[0]));
7058
+ return this.stringViewList;
7059
+ }
7060
+ /** The typed arrays this function holds views of (the typed-arrays pass). */
7061
+ viewList = null;
7062
+ views() {
7063
+ this.viewList ??= [.../* @__PURE__ */ new Set([...this.fn.params, ...this.fn.blocks.flatMap((b) => b.instrs)])].filter((v) => v.taView && (v.op === "param" || v.op === "new"));
7064
+ return this.viewList;
7065
+ }
7066
+ /** A call of a typed unit's export: its typed entry when the arguments are proven of the
7067
+ * declared types (as many as it declares); else, for as many arguments as it declares, the
7068
+ * typed entry when C's tests of them pass; else its checked entry, called directly. */
7069
+ typedCall(i, tc, args) {
7070
+ const operands = i.operands.slice(2);
7071
+ const fits = typedCallFits(i);
7072
+ if (!fits && operands.length !== tc.params.length) {
7073
+ this.genericCall(i, tc, args);
7074
+ return;
7075
+ }
7076
+ this.hosts.typed.set(tc.direct, `${C_TYPE[tc.returnRepr]} ${tc.direct}(JSContext *${tc.params.map((p) => `, ${C_TYPE[p.repr]}`).join("")}); /* typed entry */`);
7077
+ const tests = fits ? [] : operands.flatMap((o, k) => {
7078
+ const { type, view } = tc.params[k];
7079
+ if (view) return [`js_ta_is(ctx, ${args[k + 2]}, ${VIEWS[view].c})`];
7080
+ return isSubtype(o.type & ~T.Empty, type) ? [] : [`(${typeTest(type, args[k + 2])})`];
7081
+ });
7082
+ const call = `${tc.direct}(ctx${operands.map((o, k) => `, ${tests.length ? convert(args[k + 2], "boxed", tc.params[k].repr) : this.as(o, tc.params[k].repr)}`).join("")})`;
7083
+ if (tests.length) this.lines.push(` if (${tests.join(" && ")}) {`);
7084
+ if (tc.returnRepr === "boxed") this.boxedCall(i, call);
7085
+ else {
7086
+ const out = this.hasVar(i) ? this.name(i) : null;
7087
+ this.lines.push(` { ${C_TYPE[tc.returnRepr]} r = ${call};`, ` if (JS_UNLIKELY(js_has_exception(ctx))) { ${this.onException(i.block)} }`, ...out ? [` ${out} = ${convert("r", tc.returnRepr, i.repr)}; }`] : [" (void)r; }"]);
7088
+ }
7089
+ if (!tests.length) return;
7090
+ this.lines.push(" } else {");
7091
+ this.genericCall(i, tc, args);
7092
+ this.lines.push(" }");
7093
+ }
7094
+ genericCall(i, tc, args) {
7095
+ this.hosts.used.add(tc.generic);
7096
+ this.boxedCall(i, `${tc.generic}(ctx, JS_UNDEFINED, ${args[1]}, ${args.length - 2}, ${argArray(args.slice(2))}, JS_UNDEFINED)`);
7097
+ }
6589
7098
  /** The values this function calls, or checks as an inlined call's callee. */
6590
7099
  calledValues = null;
6591
7100
  called() {
@@ -6596,11 +7105,17 @@ var FunctionEmitter = class {
6596
7105
  return this.calledValues;
6597
7106
  }
6598
7107
  onException(b) {
6599
- return b.handler ? this.goto(b, b.handler) : `return ${C_FAIL[this.fn.returnRepr ?? "boxed"]};`;
7108
+ if (b.handler) return this.goto(b, b.handler);
7109
+ if (this.gen) return `return ${C_FAIL[this.fn.returnRepr ?? "boxed"]};`;
7110
+ this.exits = true;
7111
+ return "goto exc_;";
6600
7112
  }
6601
7113
  key(i) {
6602
7114
  return keyValue(i.key, this.strings);
6603
7115
  }
7116
+ keyedCache() {
7117
+ return `&${this.strings.prefix}mod_keys[${this.caches.keys++}], ${this.cache()}`;
7118
+ }
6604
7119
  cache() {
6605
7120
  return `&${this.strings.prefix}mod_ics[${this.caches.count++}]`;
6606
7121
  }
@@ -6675,7 +7190,7 @@ var FunctionEmitter = class {
6675
7190
  expr = `js_new_arguments(ctx, ${args[0]}, JS_UNDEFINED, JS_UNDEFINED, NULL, 0)`;
6676
7191
  break;
6677
7192
  }
6678
- const table = `${this.strings.prefix}jsam_${ident(this.fn.name)}`;
7193
+ const table = `${this.strings.prefix}jsam_${ident$1(this.fn.name)}`;
6679
7194
  const entries = i.table.length ? i.table.map((t) => t < 0 ? "UINT32_MAX" : String(t)) : ["UINT32_MAX"];
6680
7195
  this.strings.decls.push(datum("uint32_t", `${table}[]`, `{ ${entries.join(", ")} }`));
6681
7196
  expr = `js_new_arguments(ctx, ${args[0]}, ${args[1]}, ${args[2]}, ${table}, ${i.table.length})`;
@@ -6727,7 +7242,7 @@ var FunctionEmitter = class {
6727
7242
  const length = "name" in i.key && i.key.name === "length";
6728
7243
  const str = i.operands[0].type !== T.None && isSubtype(i.operands[0].type, T.String);
6729
7244
  if (str && length) {
6730
- expr = `js_int32((int32_t)js_string_length(${args[0]}))`;
7245
+ expr = convert(`(int32_t)js_string_length(${this.as(i.operands[0], "boxed")})`, "i32", i.repr);
6731
7246
  break;
6732
7247
  }
6733
7248
  const get = str ? "js_get_string_prop" : length ? "js_get_length" : this.called().has(i) ? "js_get_method" : "js_get_prop";
@@ -6735,19 +7250,37 @@ var FunctionEmitter = class {
6735
7250
  break;
6736
7251
  }
6737
7252
  case "get_elem":
6738
- expr = `js_get_elem(ctx, ${args[0]}, ${args[1]})`;
7253
+ if (readsDouble(i)) return this.readDouble(i, args);
7254
+ if (i.taView) {
7255
+ expr = this.viewAccess(i, args);
7256
+ break;
7257
+ }
7258
+ expr = i.keyedSite ? `js_get_keyed(ctx, ${args[0]}, ${args[1]}, ${this.keyedCache()})` : `js_get_elem(ctx, ${args[0]}, ${args[1]})`;
6739
7259
  break;
6740
7260
  case "get_index":
6741
- expr = i.operands[1].repr === "f64" ? `js_get_index_f64(ctx, ${args[0]}, ${this.as(i.operands[1], "f64")})` : `js_get_index(ctx, ${args[0]}, ${args[1]})`;
7261
+ if (readsDouble(i)) return this.readDouble(i, args);
7262
+ if (i.taView) {
7263
+ expr = this.viewAccess(i, args);
7264
+ break;
7265
+ }
7266
+ expr = i.operands[1].repr === "f64" || i.operands[1].repr === "i64" ? `js_get_index_ta_f64(ctx, ${args[0]}, ${this.as(i.operands[1], "f64")})` : `js_get_index_ta(ctx, ${args[0]}, ${args[1]})`;
6742
7267
  break;
6743
7268
  case "set_prop":
6744
7269
  status = "name" in i.key ? `${i.addsProp || addsToThis$1(this.fn, i) ? "js_set_prop_add" : "js_set_prop"}(ctx, ${args[0]}, ${this.key(i)}, ${args[1]}, ${strict}, ${this.cache()})` : `js_set_value(ctx, ${args[0]}, ${this.key(i)}, ${args[1]}, ${strict})`;
6745
7270
  break;
6746
7271
  case "set_elem":
6747
- status = `js_set_elem(ctx, ${args[0]}, ${args[1]}, ${args[2]}, ${strict})`;
7272
+ if (i.taView) {
7273
+ status = this.viewAccess(i, args, strict);
7274
+ break;
7275
+ }
7276
+ status = i.keyedSite ? `js_set_keyed(ctx, ${args[0]}, ${args[1]}, ${args[2]}, ${strict}, ${this.keyedCache()})` : `js_set_elem(ctx, ${args[0]}, ${args[1]}, ${args[2]}, ${strict})`;
6748
7277
  break;
6749
7278
  case "set_index":
6750
- status = i.operands[1].repr === "f64" ? `js_set_index_f64(ctx, ${args[0]}, ${this.as(i.operands[1], "f64")}, ${args[2]}, ${strict})` : `js_set_index(ctx, ${args[0]}, ${args[1]}, ${args[2]}, ${strict})`;
7279
+ if (i.taView) {
7280
+ status = this.viewAccess(i, args, strict);
7281
+ break;
7282
+ }
7283
+ status = i.operands[1].repr === "f64" || i.operands[1].repr === "i64" ? `js_set_index_ta_f64(ctx, ${args[0]}, ${this.as(i.operands[1], "f64")}, ${args[2]}, ${strict})` : `js_set_index_ta(ctx, ${args[0]}, ${args[1]}, ${args[2]}, ${strict})`;
6751
7284
  break;
6752
7285
  case "delete_prop":
6753
7286
  expr = `js_delete_value(ctx, ${args[0]}, ${this.key(i)}, ${strict})`;
@@ -6851,6 +7384,18 @@ var FunctionEmitter = class {
6851
7384
  expr = `js_construct_spread(ctx, ${args[0]}, ${args[1]}, ${cString(i.target)})`;
6852
7385
  break;
6853
7386
  case "call":
7387
+ if (i.typedCall) {
7388
+ this.typedCall(i, i.typedCall, args);
7389
+ return;
7390
+ }
7391
+ if (i.builtin === "apply") {
7392
+ expr = `js_call_apply(ctx, ${args[0]}, ${args[1]}, ${args[2] ?? "JS_UNDEFINED"}, ${args[3] ?? "JS_UNDEFINED"}, ${cString(i.target)})`;
7393
+ break;
7394
+ }
7395
+ if (i.builtin === "call") {
7396
+ expr = `js_call_call(ctx, ${args[0]}, ${args[1]}, ${args.length - 2}, ${argArray(args.slice(2))}, ${cString(i.target)})`;
7397
+ break;
7398
+ }
6854
7399
  if (i.builtin) {
6855
7400
  expr = `js_call_${i.builtin}(ctx, ${args[0]}, ${args[1]}, ${args[2]}, ${cString(i.target)})`;
6856
7401
  break;
@@ -6859,6 +7404,10 @@ var FunctionEmitter = class {
6859
7404
  break;
6860
7405
  case "new":
6861
7406
  expr = `js_construct(ctx, ${args[0]}, ${args.length - 1}, ${argArray(args.slice(1))}, JS_UNDEFINED, ${cString(i.target)})`;
7407
+ if (i.taView && this.hasVar(i)) {
7408
+ this.lines.push(` ${this.name(i)} = ${expr};`, ` if (JS_UNLIKELY(js_is_exception(${this.name(i)}))) { ${this.onException(i.block)} }`, ` ${this.name(i)}_view = js_ta_view(ctx, ${this.name(i)}, ${VIEWS[i.taView].c});`);
7409
+ return;
7410
+ }
6862
7411
  break;
6863
7412
  case "check_tdz":
6864
7413
  expr = `js_check_tdz(ctx, ${args[0]}, ${cString(i.name ?? "variable")})`;
@@ -6866,6 +7415,65 @@ var FunctionEmitter = class {
6866
7415
  case "construct_direct":
6867
7416
  this.constructDirect(i, args);
6868
7417
  return;
7418
+ case "math": {
7419
+ const f = {
7420
+ sqrt: "sqrt",
7421
+ abs: "fabs",
7422
+ floor: "floor",
7423
+ ceil: "ceil",
7424
+ trunc: "trunc"
7425
+ }[i.target];
7426
+ expr = convert(`${f}(${this.as(i.operands[0], "f64")})`, "f64", i.repr);
7427
+ break;
7428
+ }
7429
+ case "int_div":
7430
+ case "int_mod": {
7431
+ const [x, y] = i.operands.map((o) => this.as(o, "i64"));
7432
+ this.lines.push(` if (JS_UNLIKELY(${y} == 0)) { js_discard(js_throw_range_error(ctx, "Division by zero")); ${this.onException(i.block)} }`);
7433
+ if (this.hasVar(i)) this.lines.push(` ${this.name(i)} = ${convert(int64Op(i.op === "int_div" ? "/" : "%", x, y), "i64", i.repr)};`);
7434
+ return;
7435
+ }
7436
+ case "i64":
7437
+ expr = convert(int64Op(i.target, this.as(i.operands[0], "i64"), this.as(i.operands[1], "i64")), "i64", i.repr);
7438
+ break;
7439
+ case "to_i32": {
7440
+ const o = i.operands[0];
7441
+ const from = reprOf(o.type);
7442
+ expr = convert(from === "i32" ? this.as(o, "i32") : from === "i64" ? `(int32_t)(uint32_t)${this.as(o, "i64")}` : from === "f64" ? `js_f64_to_int32(${this.as(o, "f64")})` : `(int32_t)(uint32_t)js_value_to_i64(${this.as(o, "boxed")})`, "i32", i.repr);
7443
+ break;
7444
+ }
7445
+ case "to_i64": {
7446
+ const o = i.operands[0];
7447
+ const from = reprOf(o.type);
7448
+ expr = convert(convert(this.as(o, from), from, "i64"), "i64", i.repr);
7449
+ break;
7450
+ }
7451
+ case "int_add":
7452
+ case "int_sub":
7453
+ case "int_mul": {
7454
+ const [x, y] = i.operands.map((o) => `(uint32_t)${this.as(o, "i32")}`);
7455
+ expr = convert(`(int32_t)(${x} ${i.op === "int_add" ? "+" : i.op === "int_sub" ? "-" : "*"} ${y})`, "i32", i.repr);
7456
+ break;
7457
+ }
7458
+ case "char_code_at": {
7459
+ if (!inString(i)) {
7460
+ expr = convert(`js_string_char_code_at(ctx, ${args[0]}, ${this.as(i.operands[1], "f64")})`, "f64", i.repr);
7461
+ break;
7462
+ }
7463
+ const s = i.operands[0];
7464
+ const k = `(uint32_t)${this.as(i.operands[1], "i32")}`;
7465
+ expr = convert(this.stringViews().has(s) ? `(${this.name(s)}_units.length ? (int32_t)js_view_at(${this.name(s)}_units, ${k}) : js_string_unit_at(ctx, ${args[0]}, ${k}))` : `js_string_unit_at(ctx, ${args[0]}, ${k})`, "i32", i.repr);
7466
+ break;
7467
+ }
7468
+ case "identical":
7469
+ case "strict_eq_atom":
7470
+ case "strict_eq_num":
7471
+ case "is_type": {
7472
+ const test = i.op === "identical" ? `js_same_bits(${args[0]}, ${args[1]})` : i.op === "strict_eq_num" ? `js_strict_eq_num(${this.v(i.operands[0])}, ${cDouble(i.operands[1].lit.value)})` : i.op === "strict_eq_atom" ? `js_strict_eq_atom(ctx, ${args[0]}, ${args[1]})` : isTypeTest(i.target, args[0]);
7473
+ const cond = i.index ? `!${test}` : test;
7474
+ expr = i.repr === "bool" ? `(${cond})` : `js_bool(${cond})`;
7475
+ break;
7476
+ }
6869
7477
  case "same_function": {
6870
7478
  const cond = `js_is_function(${args[0]}) && js_as_function(${args[0]})->info == &${infoName(i.target, this.strings.prefix)} && js_as_function(${args[0]})->realm == ctx`;
6871
7479
  expr = i.repr === "bool" ? `(${cond})` : `js_bool(${cond})`;
@@ -6882,8 +7490,13 @@ var FunctionEmitter = class {
6882
7490
  break;
6883
7491
  case "call_direct": {
6884
7492
  const callee = this.callees.get(i.target);
7493
+ if (callee?.meta?.declared && !declaredCallFits(i, callee)) {
7494
+ const hidden = callee.meta.declared.findIndex((t) => t !== void 0);
7495
+ this.boxedCall(i, `${this.strings.prefix}jsf_${ident$1(i.target)}__generic(ctx, JS_UNDEFINED, JS_UNDEFINED, ${i.operands.length - hidden}, ${argArray(i.operands.slice(hidden).map((o) => this.as(o, "boxed")))}, JS_UNDEFINED)`);
7496
+ return;
7497
+ }
6885
7498
  const passed = i.operands.map((o, k) => this.as(o, callee?.params[k]?.repr ?? "boxed"));
6886
- const call = `${this.strings.prefix}jsf_${ident(i.target)}(ctx${passed.map((a) => `, ${a}`).join("")})`;
7499
+ const call = `${this.strings.prefix}jsf_${ident$1(i.target)}(ctx${passed.map((a) => `, ${a}`).join("")})`;
6887
7500
  const ret = callee?.returnRepr ?? "boxed";
6888
7501
  if (ret === "boxed") {
6889
7502
  const result = this.hasVar(i) ? this.name(i) : "r";
@@ -6896,6 +7509,12 @@ var FunctionEmitter = class {
6896
7509
  if (i.mayThrow) this.lines.push(` if (JS_UNLIKELY(js_has_exception(ctx))) { ${this.onException(i.block)} }`);
6897
7510
  return;
6898
7511
  }
7512
+ case "not": {
7513
+ const o = i.operands[0];
7514
+ const bool = o.type !== T.None && isSubtype(o.type, T.Boolean);
7515
+ expr = i.repr === "boxed" && !bool ? `js_not(${args[0]})` : convert(`!(${bool ? this.as(o, "bool") : `js_truthy(${args[0]})`})`, "bool", i.repr);
7516
+ break;
7517
+ }
6899
7518
  case "call_runtime": {
6900
7519
  const r = RUNTIME[i.target];
6901
7520
  if (!r) throw new Error(`unknown runtime function ${i.target}`);
@@ -6907,12 +7526,18 @@ var FunctionEmitter = class {
6907
7526
  expr = this.unboxed(i);
6908
7527
  break;
6909
7528
  }
7529
+ if (NUM_OPS.has(i.op)) {
7530
+ const raw = BOOL_OPS.has(i.op) ? "bool" : reprOf(i.type);
7531
+ expr = convert(this.unboxed(i, raw), raw, "boxed");
7532
+ break;
7533
+ }
6910
7534
  const [fn, withCtx] = CALLS[i.op];
6911
7535
  expr = `${fn}${i.numberFirst ? "_fast" : ""}(${[...withCtx ? ["ctx"] : [], ...args].join(", ")})`;
6912
7536
  }
6913
7537
  }
6914
7538
  if (status !== null) {
6915
7539
  this.lines.push(` if (JS_UNLIKELY(${status} < 0)) { ${this.onException(i.block)} }`);
7540
+ this.retakeViews(i);
6916
7541
  return;
6917
7542
  }
6918
7543
  expr = expr;
@@ -6921,6 +7546,14 @@ var FunctionEmitter = class {
6921
7546
  if (i.mayThrow) this.lines.push(` if (JS_UNLIKELY(js_is_exception(${this.name(i)}))) { ${this.onException(i.block)} }`);
6922
7547
  } else if (i.mayThrow) this.lines.push(` if (JS_UNLIKELY(js_is_exception(${expr}))) { ${this.onException(i.block)} }`);
6923
7548
  else if (i.op === "take_exception" || !OPS[i.op].result || OPS[i.op].callsUser) this.lines.push(` js_discard(${expr});`);
7549
+ this.retakeViews(i);
7550
+ }
7551
+ /** After an operation that may run code (a call, a getter, valueOf), a parameter's view again:
7552
+ * that code may have detached or resized its buffer. A view's own accesses take it again on
7553
+ * their slow paths. */
7554
+ retakeViews(i) {
7555
+ if (!OPS[i.op].callsUser || !i.mayThrow || i.taView) return;
7556
+ for (const p of this.views()) if (p.op === "param") this.lines.push(` ${this.name(p)}_view = js_ta_view(ctx, ${this.name(p)}, ${VIEWS[p.taView].c});`);
6924
7557
  }
6925
7558
  terminator(b) {
6926
7559
  const t = b.term;
@@ -7016,8 +7649,13 @@ var FunctionEmitter = class {
7016
7649
  const signature = this.prototype().slice(0, -1);
7017
7650
  const body = this.emitBlocks(fn.blocks, null);
7018
7651
  const locals = [];
7019
- for (const b of fn.blocks) for (const i of [...b.phis, ...b.instrs]) if (this.hasVar(i)) locals.push(` ${C_TYPE[i.repr]} ${this.name(i)} = ${C_ZERO[i.repr]};`);
7652
+ for (const b of fn.blocks) for (const i of [...b.phis, ...b.instrs]) if (this.hasVar(i)) {
7653
+ locals.push(` ${C_TYPE[i.repr]} ${this.name(i)} = ${C_ZERO[i.repr]};`);
7654
+ if (i.op === "new" && i.taView) locals.push(` JSTAView ${this.name(i)}_view = { NULL, 0 };`);
7655
+ }
7020
7656
  const unusedParams = fn.params.filter((p) => !this.used.has(p)).map((p) => ` (void)${this.name(p)};`);
7657
+ for (const p of this.views().filter((v) => v.op === "param")) locals.push(` JSTAView ${this.name(p)}_view = js_ta_view(ctx, ${this.name(p)}, ${VIEWS[p.taView].c});`);
7658
+ for (const p of this.stringViews()) locals.push(` JSStrView ${this.name(p)}_units = { NULL, 0, true }; (void)js_string_units(${this.name(p)}, &${this.name(p)}_units);`);
7021
7659
  const calls = fn.blocks.some((b) => b.instrs.some((i) => OPS[i.op].callsUser));
7022
7660
  const usesCtx = calls || body.some((l) => /\bctx\b/.test(l));
7023
7661
  const ret = fn.returnRepr ?? "boxed";
@@ -7028,11 +7666,12 @@ var FunctionEmitter = class {
7028
7666
  ...unusedParams,
7029
7667
  ...calls ? [ret === "boxed" ? " JS_CHECK_STACK(ctx);" : ` JS_CHECK_STACK_OR(ctx, ${C_FAIL[ret]});`] : [],
7030
7668
  ...body,
7669
+ ...this.exits ? [`exc_: return ${C_FAIL[ret]};`] : [],
7031
7670
  "}"
7032
7671
  ].join("\n");
7033
7672
  }
7034
7673
  linkage() {
7035
- return this.shared ? "JS_MAYBE_UNUSED " : "static JS_MAYBE_UNUSED ";
7674
+ return this.shared || this.fn.meta?.declared ? "JS_MAYBE_UNUSED " : "static JS_MAYBE_UNUSED ";
7036
7675
  }
7037
7676
  /** The entry's prototype: in program.h when it's shared, else in the function's own file. */
7038
7677
  prototype() {
@@ -7048,13 +7687,60 @@ var FunctionEmitter = class {
7048
7687
  function emitProgram(m, options = {}) {
7049
7688
  return emitModule(m, "", null, options);
7050
7689
  }
7051
- function prepare(m, p, options) {
7052
- const strings = new Strings(p, options.regexps, options.harness?.atoms);
7053
- const caches = new Caches();
7054
- const hosts = new Hosts(options.hostPrimitives ?? {});
7055
- const callees = new Map(m.functions.map((fn) => [fn.name, fn]));
7056
- return {
7057
- strings,
7690
+ /** A test262 harness file, compiled once and linked into the test host: its tables
7691
+ * and `<prefix>js_script` are exported, `<prefix>register(rt)` registers the tables, and the
7692
+ * units run the script. `atoms`: the atoms the harness files before it in the same C file define. */
7693
+ function emitHarnessScript(m, prefix, atoms, options = {}) {
7694
+ const e = prepare(m, prefix, {
7695
+ ...options,
7696
+ harness: {
7697
+ atoms,
7698
+ register: "",
7699
+ scripts: []
7700
+ }
7701
+ });
7702
+ e.strings.externalInFile = true;
7703
+ e.main.shared = true;
7704
+ const emitters = [...e.functions, e.main];
7705
+ const bodies = emitters.map((f) => f.emit());
7706
+ const infos = m.functions.map((fn) => functionInfo(fn, e.strings, e.embedSource));
7707
+ const nGlobals = Math.max(1, m.globals.length);
7708
+ const cells = e.strings.cells;
7709
+ return {
7710
+ c: [
7711
+ GENERATED,
7712
+ "#include <stdlib.h>",
7713
+ "#include \"js_runtime.h\"",
7714
+ "",
7715
+ ...e.hosts.used.size ? [...e.hosts.decls(), ""] : [],
7716
+ ...dataLines(m, prefix, `${prefix}register`, options, e, ""),
7717
+ "",
7718
+ ...emitters.flatMap((f) => [f.prototype(), ...f.resumePrototype()]),
7719
+ ...m.functions.map((fn) => genericPrototype(fn, prefix)),
7720
+ "",
7721
+ ...infos,
7722
+ ...infos.length ? [""] : [],
7723
+ ...bodies.flatMap((b) => [b, ""]),
7724
+ ...m.functions.flatMap((fn) => [genericEntry(fn, prefix), ""]),
7725
+ `void ${prefix}register(JSRuntime *rt) {`,
7726
+ ...cells.length ? [` js_register_static_atoms(rt, ${prefix}mod_atoms, ${cells.length});`] : [],
7727
+ ...e.caches.count ? [` js_register_prop_caches(rt, ${prefix}mod_ics, ${e.caches.count});`] : [],
7728
+ ...e.caches.keys ? [` js_register_roots(rt, ${prefix}mod_keys, ${e.caches.keys}); /* zeroed: EMPTY, no key yet */`] : [],
7729
+ ` for (size_t i = 0; i < ${nGlobals}; i++) ${prefix}mod_globals[i] = JS_UNDEFINED;`,
7730
+ ` js_register_roots(rt, ${prefix}mod_globals, ${nGlobals});`,
7731
+ "}",
7732
+ ""
7733
+ ].join("\n"),
7734
+ atoms: e.strings.atoms
7735
+ };
7736
+ }
7737
+ function prepare(m, p, options) {
7738
+ const strings = new Strings(p, options.regexps, options.harness?.atoms);
7739
+ const caches = new Caches();
7740
+ const hosts = new Hosts(options.hostPrimitives ?? {});
7741
+ const callees = new Map(m.functions.map((fn) => [fn.name, fn]));
7742
+ return {
7743
+ strings,
7058
7744
  caches,
7059
7745
  hosts,
7060
7746
  functions: m.functions.map((fn) => new FunctionEmitter(fn, cFunctionName(fn, false, p), strings, caches, hosts, callees)),
@@ -7063,7 +7749,7 @@ function prepare(m, p, options) {
7063
7749
  };
7064
7750
  }
7065
7751
  function genericPrototype(fn, p) {
7066
- return `static JSValue ${cFunctionName(fn, false, p)}__generic(JSContext *ctx, JSValue callee, JSValue this_val, uint32_t argc, const JSValue *argv, JSValue new_target);`;
7752
+ return `${fn.meta.declared ? "" : "static "}JSValue ${cFunctionName(fn, false, p)}__generic(JSContext *ctx, JSValue callee, JSValue this_val, uint32_t argc, const JSValue *argv, JSValue new_target);`;
7067
7753
  }
7068
7754
  /** The module's data, once every function is emitted: globals, literals, caches, tables.
7069
7755
  * `storage`: "static " in one file; "" in a split program's program.c, which defines them for
@@ -7079,6 +7765,7 @@ function dataLines(m, p, entry, options, e, storage) {
7079
7765
  ...e.embedSource ? [sourceCell(m.source, p).define(storage)] : [],
7080
7766
  ...atoms.length ? [`static const JSString *const ${p}mod_atoms[] = { ${atoms.map((a) => `(const JSString *)&${a}`).join(", ")} };`] : [],
7081
7767
  ...e.caches.count ? [`${storage}JSPropCache ${p}mod_ics[${e.caches.count}];`] : [],
7768
+ ...e.caches.keys ? [`${storage}JSValue ${p}mod_keys[${e.caches.keys}];`] : [],
7082
7769
  ...options.components?.length && !entry ? [`static const JSComponent *const ${p}mod_components[] = { ${options.components.map((c) => `&${c}`).join(", ")} };`] : [],
7083
7770
  ...options.core && !entry ? [
7084
7771
  `static const JSCoreEntry ${p}mod_core[] = {`,
@@ -7096,6 +7783,7 @@ function mainLines(m, p, entry, options, e) {
7096
7783
  " JSRuntime *rt = js_runtime_new();",
7097
7784
  ...atoms.length ? [` js_register_static_atoms(rt, ${p}mod_atoms, ${atoms.length});`] : [],
7098
7785
  ...e.caches.count ? [` js_register_prop_caches(rt, ${p}mod_ics, ${e.caches.count});`] : [],
7786
+ ...e.caches.keys ? [` js_register_roots(rt, ${p}mod_keys, ${e.caches.keys}); /* zeroed: EMPTY, no key yet */`] : [],
7099
7787
  ...options.harness?.scripts.length ? [` void ${options.harness.register}(JSRuntime *rt);`, ` ${options.harness.register}(rt); /* the harness's tables */`] : [],
7100
7788
  options.core && !entry ? ` js_use_core(rt, ${p}mod_core, ${options.core.length}); /* the core methods it names */` : " js_use_all_core(rt); /* every core method */",
7101
7789
  " JSContext *ctx = js_context_new(rt);",
@@ -7166,17 +7854,20 @@ function partition(fns, size) {
7166
7854
  if (used.length <= 1) return null;
7167
7855
  return new Map(fns.map((fn, i) => [fn, used.indexOf(raw[i]) + 1]));
7168
7856
  }
7169
- /** The program's C files. A program under `options.partSize` instructions is
7170
- * program.c alone, as emitProgram makes it. A bigger one is program.h (what the files
7171
- * share), program.c (the data and main()) and program_1.c … program_N.c (the functions),
7172
- * which compile in parallel. A function another file calls, or makes, isn't static. */
7173
- function emitProgramFiles(m, options = {}) {
7857
+ /** The program's C files: program.c alone under `options.partSize` instructions, else
7858
+ * program_1.c … program_N.c (the functions, compiled in parallel), program.h and program.c (the
7859
+ * data and main()). Yielded as each is made, so a caller that writes each away never holds a
7860
+ * large program's C (hundreds of megabytes) at once. */
7861
+ function* emitProgramFiles(m, options = {}) {
7174
7862
  const fns = [...m.functions, m.main];
7175
7863
  const parts = options.partSize ? partition(fns, options.partSize) : null;
7176
- if (!parts) return [{
7177
- name: "program.c",
7178
- text: emitProgram(m, options)
7179
- }];
7864
+ if (!parts) {
7865
+ yield {
7866
+ name: "program.c",
7867
+ text: emitProgram(m, options)
7868
+ };
7869
+ return;
7870
+ }
7180
7871
  const e = prepare(m, "", options);
7181
7872
  const emitters = new Map(fns.map((fn, i) => [fn, i < m.functions.length ? e.functions[i] : e.main]));
7182
7873
  const byName = new Map(m.functions.map((fn) => [fn.name, fn]));
@@ -7189,46 +7880,15 @@ function emitProgramFiles(m, options = {}) {
7189
7880
  if (i.op === "call_direct" || i.op === "construct_direct") emitters.get(target).shared = true;
7190
7881
  if (i.op !== "call_direct") sharedInfos.add(target);
7191
7882
  }
7192
- const bodies = new Map(fns.map((fn) => [fn, emitters.get(fn).emit()]));
7193
- const infos = new Map(m.functions.map((fn) => [fn, functionInfo(fn, e.strings, e.embedSource, sharedInfos.has(fn))]));
7194
- const header = [
7195
- GENERATED,
7196
- "/* What the program's files share: program.c defines the data, program_N.c the functions. */",
7197
- "#include <stdlib.h>",
7198
- "#include \"js_runtime.h\"",
7199
- "",
7200
- ...e.hosts.used.size ? [...e.hosts.decls(), ""] : [],
7201
- "#ifndef VISALJS_PROGRAM_DATA /* program.c has the definitions */",
7202
- `extern JSValue mod_globals[${Math.max(1, m.globals.length)}];`,
7203
- ...e.caches.count ? [`extern JSPropCache mod_ics[${e.caches.count}];`] : [],
7204
- ...e.strings.decls.map((d) => d.declare),
7205
- ...e.embedSource ? [sourceCell(m.source, "").declare] : [],
7206
- "#endif",
7207
- "",
7208
- ...fns.filter((fn) => emitters.get(fn).shared).map((fn) => emitters.get(fn).prototype()),
7209
- ...[...sharedInfos].map((fn) => `extern const JSFunctionInfo ${infoName(fn.name, "")};`),
7210
- ""
7211
- ];
7212
- const data = [
7213
- GENERATED,
7214
- "#define VISALJS_PROGRAM_DATA",
7215
- "#include \"program.h\"",
7216
- "",
7217
- ...dataLines(m, "", null, options, e, ""),
7218
- "",
7219
- ...mainLines(m, "", null, options, e)
7220
- ];
7221
- const files = [{
7222
- name: "program.h",
7223
- text: header.join("\n")
7224
- }, {
7225
- name: "program.c",
7226
- text: data.join("\n")
7227
- }];
7228
- const count = Math.max(...parts.values());
7229
- for (let k = 1; k <= count; k++) {
7230
- const own = fns.filter((fn) => parts.get(fn) === k);
7883
+ const byPart = /* @__PURE__ */ new Map();
7884
+ for (const fn of fns) {
7885
+ const k = parts.get(fn);
7886
+ if (!byPart.has(k)) byPart.set(k, []);
7887
+ byPart.get(k).push(fn);
7888
+ }
7889
+ for (const [k, own] of byPart) {
7231
7890
  const made = own.filter((fn) => fn !== m.main);
7891
+ const bodies = own.map((fn) => emitters.get(fn).emit());
7232
7892
  const text = [
7233
7893
  GENERATED,
7234
7894
  "#include \"program.h\"",
@@ -7239,17 +7899,50 @@ function emitProgramFiles(m, options = {}) {
7239
7899
  }),
7240
7900
  ...made.map((fn) => genericPrototype(fn, "")),
7241
7901
  "",
7242
- ...made.map((fn) => infos.get(fn)),
7902
+ ...made.map((fn) => functionInfo(fn, e.strings, e.embedSource, sharedInfos.has(fn))),
7243
7903
  ...made.length ? [""] : [],
7244
- ...own.flatMap((fn) => [bodies.get(fn), ""]),
7904
+ ...bodies.flatMap((body) => [body, ""]),
7245
7905
  ...made.flatMap((fn) => [genericEntry(fn, ""), ""])
7246
7906
  ];
7247
- files.push({
7907
+ yield {
7248
7908
  name: `program_${k}.c`,
7249
7909
  text: text.join("\n")
7250
- });
7910
+ };
7251
7911
  }
7252
- return files;
7912
+ yield {
7913
+ name: "program.h",
7914
+ text: [
7915
+ GENERATED,
7916
+ "/* What the program's files share: program.c defines the data, program_N.c the functions. */",
7917
+ "#include <stdlib.h>",
7918
+ "#include \"js_runtime.h\"",
7919
+ "",
7920
+ ...e.hosts.used.size ? [...e.hosts.decls(), ""] : [],
7921
+ "#ifndef VISALJS_PROGRAM_DATA /* program.c has the definitions */",
7922
+ `extern JSValue mod_globals[${Math.max(1, m.globals.length)}];`,
7923
+ ...e.caches.count ? [`extern JSPropCache mod_ics[${e.caches.count}];`] : [],
7924
+ ...e.caches.keys ? [`extern JSValue mod_keys[${e.caches.keys}];`] : [],
7925
+ ...e.strings.decls.map((d) => d.declare),
7926
+ ...e.embedSource ? [sourceCell(m.source, "").declare] : [],
7927
+ "#endif",
7928
+ "",
7929
+ ...fns.filter((fn) => emitters.get(fn).shared).map((fn) => emitters.get(fn).prototype()),
7930
+ ...[...sharedInfos].map((fn) => `extern const JSFunctionInfo ${infoName(fn.name, "")};`),
7931
+ ""
7932
+ ].join("\n")
7933
+ };
7934
+ yield {
7935
+ name: "program.c",
7936
+ text: [
7937
+ GENERATED,
7938
+ "#define VISALJS_PROGRAM_DATA",
7939
+ "#include \"program.h\"",
7940
+ "",
7941
+ ...dataLines(m, "", null, options, e, ""),
7942
+ "",
7943
+ ...mainLines(m, "", null, options, e)
7944
+ ].join("\n")
7945
+ };
7253
7946
  }
7254
7947
  //#endregion
7255
7948
  //#region runtime/components.ts
@@ -7795,7 +8488,7 @@ function pauses(fn) {
7795
8488
  * optimizer's to choose. */
7796
8489
  function closedFunctions(m) {
7797
8490
  const byName = new Map(m.functions.map((fn) => [fn.name, fn]));
7798
- const closed = new Set(m.functions.filter((fn) => fn.meta && !pauses(fn)));
8491
+ const closed = new Set(m.functions.filter((fn) => fn.meta && !fn.meta.external && !pauses(fn)));
7799
8492
  for (const fn of functionsOf(m)) for (const b of fn.blocks) for (const i of b.instrs) {
7800
8493
  if (i.op !== "make_function" && i.op !== "call_direct") continue;
7801
8494
  const target = byName.get(i.target);
@@ -7817,6 +8510,35 @@ function callers(m) {
7817
8510
  return result;
7818
8511
  }
7819
8512
  //#endregion
8513
+ //#region optimizer/src/constant-keys.ts
8514
+ const constantKeys = {
8515
+ name: "constant-keys",
8516
+ run(m, count) {
8517
+ const stores = /* @__PURE__ */ new Map();
8518
+ for (const fn of functionsOf(m)) for (const b of fn.blocks) for (const i of b.instrs) if (i.op === "store_global" && i.operands[0].op !== "empty") stores.set(i.index, (stores.get(i.index) ?? 0) + 1);
8519
+ const storedOnce = (key) => {
8520
+ const load = key.op === "check_tdz" ? key.operands[0] : key;
8521
+ return load.op === "load_global" && stores.get(load.index) === 1;
8522
+ };
8523
+ for (const fn of functionsOf(m)) for (const b of fn.blocks) for (const i of b.instrs) {
8524
+ if (i.op !== "get_elem" && i.op !== "set_elem") continue;
8525
+ const key = i.operands[1];
8526
+ if (key.op === "const" && typeof key.lit.value === "string") {
8527
+ const k = propKey(key.lit.value);
8528
+ if (!("name" in k)) continue;
8529
+ i.op = i.op === "get_elem" ? "get_prop" : "set_prop";
8530
+ i.key = k;
8531
+ i.operands.splice(1, 1);
8532
+ count("named", "element access", 1, "element accesses");
8533
+ } else if (storedOnce(key)) {
8534
+ i.keyedSite = true;
8535
+ count("cached", "keyed site");
8536
+ }
8537
+ }
8538
+ return m;
8539
+ }
8540
+ };
8541
+ //#endregion
7820
8542
  //#region optimizer/src/dce.ts
7821
8543
  /** Operations with no effect but their result. */
7822
8544
  const REMOVABLE = /* @__PURE__ */ new Set([
@@ -7864,7 +8586,15 @@ const REMOVABLE = /* @__PURE__ */ new Set([
7864
8586
  "host_function",
7865
8587
  "same_function",
7866
8588
  "function_env",
7867
- "construct_result"
8589
+ "construct_result",
8590
+ "char_code_at",
8591
+ "math",
8592
+ "int_add",
8593
+ "int_sub",
8594
+ "int_mul",
8595
+ "to_i64",
8596
+ "to_i32",
8597
+ "i64"
7868
8598
  ]);
7869
8599
  const dce = {
7870
8600
  name: "dce",
@@ -7902,6 +8632,45 @@ function dceFunction(fn) {
7902
8632
  return dead.size;
7903
8633
  }
7904
8634
  //#endregion
8635
+ //#region optimizer/src/typed-arrays.ts
8636
+ const typedArrays = {
8637
+ name: "typed-arrays",
8638
+ run(m, count) {
8639
+ for (const fn of functionsOf(m)) {
8640
+ if (pauses(fn)) continue;
8641
+ const made = /* @__PURE__ */ new Map();
8642
+ fn.meta?.declaredViews?.forEach((kind, k) => {
8643
+ if (kind) made.set(fn.params[k], kind);
8644
+ });
8645
+ for (const b of fn.blocks) for (const i of b.instrs) {
8646
+ const ctor = i.operands[0];
8647
+ const kind = i.op === "new" && i.operands.length === 2 && ctor?.op === "global_name" ? viewOf(ctor.target) : void 0;
8648
+ if (kind) made.set(i, kind);
8649
+ }
8650
+ if (!made.size) continue;
8651
+ const accesses = /* @__PURE__ */ new Map();
8652
+ const escape = (v) => made.delete(v);
8653
+ for (const b of fn.blocks) {
8654
+ for (const phi of b.phis) for (const inc of phi.incoming) escape(inc.value);
8655
+ for (const i of b.instrs) i.operands.forEach((o, k) => {
8656
+ if (!made.has(o)) return;
8657
+ if ((i.op === "get_elem" || i.op === "set_elem") && k === 0 && i.operands[1] !== o && i.operands[2] !== o) accesses.set(o, [...accesses.get(o) ?? [], i]);
8658
+ else escape(o);
8659
+ });
8660
+ const t = b.term;
8661
+ if (t?.kind === "branch") escape(t.cond);
8662
+ if (t?.kind === "return" || t?.kind === "throw") escape(t.value);
8663
+ }
8664
+ for (const [array, kind] of made) {
8665
+ array.taView = kind;
8666
+ for (const a of accesses.get(array) ?? []) a.taView = kind;
8667
+ count("viewed", "typed array");
8668
+ }
8669
+ }
8670
+ return m;
8671
+ }
8672
+ };
8673
+ //#endregion
7905
8674
  //#region optimizer/src/elide-arguments.ts
7906
8675
  const elideArguments = {
7907
8676
  name: "elide-arguments",
@@ -7937,6 +8706,285 @@ function elide(fn) {
7937
8706
  return candidates.size;
7938
8707
  }
7939
8708
  //#endregion
8709
+ //#region optimizer/src/specialize.ts
8710
+ const NUMBER_FORM = {
8711
+ add: "num_add",
8712
+ sub: "num_sub",
8713
+ mul: "num_mul",
8714
+ div: "num_div",
8715
+ mod: "num_mod",
8716
+ neg: "num_neg",
8717
+ inc: "num_inc",
8718
+ dec: "num_dec",
8719
+ lt: "num_lt",
8720
+ le: "num_le",
8721
+ gt: "num_gt",
8722
+ ge: "num_ge",
8723
+ eq: "num_eq",
8724
+ ne: "num_ne",
8725
+ strict_eq: "num_eq",
8726
+ strict_ne: "num_ne",
8727
+ bitand: "num_bitand",
8728
+ bitor: "num_bitor",
8729
+ bitxor: "num_bitxor",
8730
+ bitnot: "num_bitnot",
8731
+ shl: "num_shl",
8732
+ sar: "num_sar",
8733
+ shr: "num_shr"
8734
+ };
8735
+ /** The generic operators with a Number path (the rest of NUMBER_FORM's, and ToNumeric). */
8736
+ const NUMBER_FIRST = /* @__PURE__ */ new Set([
8737
+ "add",
8738
+ "sub",
8739
+ "mul",
8740
+ "div",
8741
+ "mod",
8742
+ "neg",
8743
+ "inc",
8744
+ "dec",
8745
+ "lt",
8746
+ "le",
8747
+ "gt",
8748
+ "ge",
8749
+ "eq",
8750
+ "ne",
8751
+ "bitand",
8752
+ "bitor",
8753
+ "bitxor",
8754
+ "bitnot",
8755
+ "shl",
8756
+ "sar",
8757
+ "shr",
8758
+ "to_numeric"
8759
+ ]);
8760
+ /** Math functions of one Number whose C form generated code may call inline. */
8761
+ const MATH_INLINE = /* @__PURE__ */ new Set([
8762
+ "sqrt",
8763
+ "abs",
8764
+ "floor",
8765
+ "ceil",
8766
+ "trunc"
8767
+ ]);
8768
+ /** The built-in a call may run inline, if any: `Math.sqrt(x)` …, `s.charCodeAt(i)`,
8769
+ * `String.fromCharCode(c)` with one argument, `f.apply(…)` and `f.call(…)` (named by its C
8770
+ * helper's suffix). */
8771
+ function inlineBuiltin(i) {
8772
+ const callee = i.op === "call" ? i.operands[0] : null;
8773
+ if (callee?.op !== "get_prop" || !("name" in callee.key)) return null;
8774
+ const name = callee.key.name;
8775
+ if ((name === "apply" && i.operands.length <= 4 || name === "call") && i.operands[1] === callee.operands[0]) return name;
8776
+ if (i.operands.length !== 3) return null;
8777
+ const global = (g) => callee.operands[0].op === "global_name" && callee.operands[0].target === g;
8778
+ if (MATH_INLINE.has(name) && global("Math")) return `math_${name}`;
8779
+ if (name === "fromCharCode" && global("String")) return "from_char_code";
8780
+ if (name === "charCodeAt" && i.operands[1] === callee.operands[0]) return "char_code_at";
8781
+ return null;
8782
+ }
8783
+ /** What typed code ("use static", which trusts the built-ins to be the standard ones) makes of a
8784
+ * built-in call: s.charCodeAt(i) of a string, or Math's sqrt/abs/floor/ceil/trunc of a Number,
8785
+ * the C helper's suffix; null for anything else. `type`: the values' types. */
8786
+ function trustedBuiltin(i, type) {
8787
+ if (!isStatic(i.block.fn)) return null;
8788
+ const name = inlineBuiltin(i);
8789
+ if (name === "char_code_at") return isSubtype(type(i.operands[1]), T.String) ? name : null;
8790
+ if (name?.startsWith("math_")) return isSubtype(type(i.operands[2]), Number_) ? name : null;
8791
+ return null;
8792
+ }
8793
+ /** The bitwise operators: ToInt32 (ToUint32) of their operands first. */
8794
+ const TRUNCATING = /* @__PURE__ */ new Set([
8795
+ "num_bitor",
8796
+ "num_bitand",
8797
+ "num_bitxor",
8798
+ "num_shl",
8799
+ "num_sar",
8800
+ "num_shr"
8801
+ ]);
8802
+ /** Wrapping int32 arithmetic (`(a + b) | 0` of int32s), by the Number operator. */
8803
+ const INT_FORM = {
8804
+ num_add: "int_add",
8805
+ num_sub: "int_sub",
8806
+ num_mul: "int_mul",
8807
+ num_inc: "int_add",
8808
+ num_dec: "int_sub"
8809
+ };
8810
+ /** Whether the Number operator `x` of int32s gives an exact double, whose low 32 bits are then
8811
+ * the wrapping int32 operation's: a sum always; a product only when it stays within 2^53, as
8812
+ * against a constant of at most 2^22 (`h * 16777619 >>> 0`, FNV's prime, rounds). */
8813
+ function exactInDouble(x) {
8814
+ if (x.op !== "num_mul") return true;
8815
+ return x.operands.some((o) => o.op === "const" && Math.abs(o.lit.value) <= 2 ** 22);
8816
+ }
8817
+ /** A constant 1 before `i`, for x++ as x + 1. */
8818
+ function one(i) {
8819
+ const b = i.block;
8820
+ const c = {
8821
+ id: nextId(b.fn),
8822
+ op: "const",
8823
+ operands: [],
8824
+ type: T.Int32,
8825
+ repr: "boxed",
8826
+ mayThrow: false,
8827
+ block: b,
8828
+ loc: i.loc,
8829
+ lit: { value: 1 }
8830
+ };
8831
+ b.instrs.splice(b.instrs.indexOf(i), 0, c);
8832
+ return c;
8833
+ }
8834
+ function nextId(fn) {
8835
+ return 1 + Math.max(0, ...fn.params.map((p) => p.id), ...fn.blocks.flatMap((b) => [...b.phis, ...b.instrs].map((i) => i.id)));
8836
+ }
8837
+ /** Kinds of value whose === compares contents, not bits: two Numbers (1 and 1.0 boxed
8838
+ * differently, NaN), two strings (equal units in two cells), two BigInts. */
8839
+ const BY_CONTENT = Number_ | T.String | T.BigInt;
8840
+ const NONE = Symbol("no literal");
8841
+ /** A comparison js_strict_equals (js_eq) needn't make: `typeof x === "function"`, `x == null`,
8842
+ * `x === "literal"`, and === where the operands can't both be Numbers, strings or BigInts
8843
+ * (`x === undefined`, objects): a C bool test of boxed values. Rewrites `i`; false: kept. */
8844
+ function simplerComparison(i) {
8845
+ const strict = i.op === "strict_eq" || i.op === "strict_ne";
8846
+ if (!strict && i.op !== "eq" && i.op !== "ne") return false;
8847
+ const negated = i.op === "strict_ne" || i.op === "ne" ? 1 : 0;
8848
+ const literal = (v) => v.op === "const" ? v.lit.value : NONE;
8849
+ for (const [x, y] of [[i.operands[0], i.operands[1]], [i.operands[1], i.operands[0]]]) {
8850
+ const lit = literal(y);
8851
+ if (x.op === "typeof" && typeof lit === "string") {
8852
+ Object.assign(i, {
8853
+ op: "is_type",
8854
+ operands: [x.operands[0]],
8855
+ target: lit,
8856
+ index: negated
8857
+ });
8858
+ return true;
8859
+ }
8860
+ if (!strict && (lit === null || lit === void 0)) {
8861
+ Object.assign(i, {
8862
+ op: "is_type",
8863
+ operands: [x],
8864
+ target: "nullish",
8865
+ index: negated
8866
+ });
8867
+ return true;
8868
+ }
8869
+ if (strict && typeof lit === "string") {
8870
+ Object.assign(i, {
8871
+ op: "strict_eq_atom",
8872
+ operands: [x, y],
8873
+ index: negated
8874
+ });
8875
+ return true;
8876
+ }
8877
+ if (strict && typeof lit === "number" && !Number.isNaN(lit) && !(x.op === "const" && typeof x.lit.value === "number")) {
8878
+ Object.assign(i, {
8879
+ op: "strict_eq_num",
8880
+ operands: [x, y],
8881
+ index: negated
8882
+ });
8883
+ return true;
8884
+ }
8885
+ }
8886
+ if (strict && (i.operands[0].type & i.operands[1].type & BY_CONTENT) === 0) {
8887
+ Object.assign(i, {
8888
+ op: "identical",
8889
+ index: negated
8890
+ });
8891
+ return true;
8892
+ }
8893
+ return false;
8894
+ }
8895
+ const INDEX_FORM = {
8896
+ get_elem: "get_index",
8897
+ set_elem: "set_index"
8898
+ };
8899
+ const specialize = {
8900
+ name: "specialize",
8901
+ run(m, count) {
8902
+ for (const fn of functionsOf(m)) {
8903
+ let edges = false;
8904
+ const depth = loopDepths(fn);
8905
+ const uses = useCounts(fn);
8906
+ const unread = /* @__PURE__ */ new Set();
8907
+ for (const b of fn.blocks) for (const i of b.instrs) {
8908
+ const trusted = trustedBuiltin(i, (v) => v.type);
8909
+ if (trusted) {
8910
+ edges ||= i.mayThrow;
8911
+ clearMayThrow(i);
8912
+ const method = i.operands[0];
8913
+ if (method.mayThrow) edges = true;
8914
+ if (uses.get(method) === 1) {
8915
+ unread.add(method);
8916
+ const holder = method.operands[0];
8917
+ if (trusted !== "char_code_at" && holder.op === "global_name" && uses.get(holder) === 2) {
8918
+ unread.add(holder);
8919
+ edges = true;
8920
+ }
8921
+ }
8922
+ if (trusted === "char_code_at") {
8923
+ i.op = "char_code_at";
8924
+ i.operands = [i.operands[1], i.operands[2]];
8925
+ } else {
8926
+ i.op = "math";
8927
+ i.target = trusted.slice(5);
8928
+ i.operands = [i.operands[2]];
8929
+ }
8930
+ count("inlined", "built-in call");
8931
+ continue;
8932
+ }
8933
+ if (isStringLength(i) && i.mayThrow) {
8934
+ edges = true;
8935
+ clearMayThrow(i);
8936
+ count("inlined", "string length");
8937
+ continue;
8938
+ }
8939
+ const builtin = inlineBuiltin(i);
8940
+ if (builtin) {
8941
+ i.builtin = builtin;
8942
+ count("inlined", "built-in call");
8943
+ continue;
8944
+ }
8945
+ const index = INDEX_FORM[i.op];
8946
+ if (index && isSubtype(i.operands[1].type, Number_)) {
8947
+ i.op = index;
8948
+ count("inlined", "element access", 1, "element accesses");
8949
+ continue;
8950
+ }
8951
+ const form = NUMBER_FORM[i.op];
8952
+ const numbers = i.operands.every((o) => isSubtype(o.type, Number_) && o.type !== T.None);
8953
+ if (!numbers && simplerComparison(i)) {
8954
+ edges ||= i.mayThrow;
8955
+ clearMayThrow(i);
8956
+ count("simplified", "comparison");
8957
+ continue;
8958
+ }
8959
+ if (!form || !numbers) {
8960
+ if (NUMBER_FIRST.has(i.op) && depth.get(b) > 0 && i.operands.every((o) => o.type & Number_)) {
8961
+ i.numberFirst = true;
8962
+ count("added", "number fast path");
8963
+ }
8964
+ continue;
8965
+ }
8966
+ edges ||= i.mayThrow;
8967
+ clearMayThrow(i);
8968
+ i.op = form;
8969
+ count("inlined", "number operator");
8970
+ if (TRUNCATING.has(form)) {
8971
+ for (const x of i.operands) if (INT_FORM[x.op] && uses.get(x) === 1 && x.operands.every((o) => isSubtype(o.type, T.Int32)) && exactInDouble(x)) {
8972
+ if (x.op === "num_inc" || x.op === "num_dec") x.operands = [x.operands[0], one(x)];
8973
+ x.op = INT_FORM[x.op];
8974
+ count("wrapped", "int operation");
8975
+ }
8976
+ }
8977
+ }
8978
+ if (unread.size) {
8979
+ for (const v of unread) clearMayThrow(v);
8980
+ removeInstrs(fn, unread);
8981
+ }
8982
+ if (edges) removeUnreachable(fn);
8983
+ }
8984
+ return m;
8985
+ }
8986
+ };
8987
+ //#endregion
7940
8988
  //#region optimizer/src/infer-types.ts
7941
8989
  const inferTypes = {
7942
8990
  name: "infer-types",
@@ -7965,7 +9013,8 @@ function inferModule(m) {
7965
9013
  };
7966
9014
  for (const fn of m.functions) {
7967
9015
  if (!pauses(fn)) sig.returns.set(fn.name, T.None);
7968
- for (const p of fn.params) sig.params.set(p, closed.has(fn) ? T.None : Unknown & ~T.Empty);
9016
+ const declared = fn.meta?.declared;
9017
+ fn.params.forEach((p, k) => sig.params.set(p, declared?.[k] ?? (closed.has(fn) ? T.None : Unknown & ~T.Empty)));
7969
9018
  }
7970
9019
  const byName = new Map(m.functions.map((fn) => [fn.name, fn]));
7971
9020
  const callerMap = callers(m);
@@ -7984,7 +9033,7 @@ function inferModule(m) {
7984
9033
  }
7985
9034
  for (const b of fn.blocks) for (const i of b.instrs) {
7986
9035
  const callee = i.op === "call_direct" ? byName.get(i.target) : void 0;
7987
- if (!callee || !closed.has(callee)) continue;
9036
+ if (!callee || !closed.has(callee) || callee.meta?.declared) continue;
7988
9037
  callee.params.forEach((p, k) => {
7989
9038
  const old = sig.params.get(p);
7990
9039
  const next = old | i.operands[k].type & ~T.Empty;
@@ -8044,7 +9093,11 @@ const BOOLEAN = /* @__PURE__ */ new Set([
8044
9093
  "delete_prop",
8045
9094
  "delete_elem",
8046
9095
  "private_in",
8047
- "same_function"
9096
+ "same_function",
9097
+ "identical",
9098
+ "strict_eq_atom",
9099
+ "is_type",
9100
+ "strict_eq_num"
8048
9101
  ]);
8049
9102
  const OBJECT = /* @__PURE__ */ new Set([
8050
9103
  "new_object",
@@ -8096,9 +9149,8 @@ function bounds(fn, type) {
8096
9149
  while ((v.op === "to_numeric" || v.op === "to_number" || v.op === "check_tdz") && isSubtype(type(v), Number_)) v = v.operands[0];
8097
9150
  return v;
8098
9151
  };
8099
- const bound = (x, at, dir) => {
9152
+ const proves = (x, at, holds) => {
8100
9153
  idom ??= dominators(fn);
8101
- if (!int32(x)) return false;
8102
9154
  for (let a = at;;) {
8103
9155
  const p = a.preds.length === 1 ? a.preds[0] : null;
8104
9156
  const term = p?.term;
@@ -8108,12 +9160,7 @@ function bounds(fn, type) {
8108
9160
  if (rel && (l === x || r === x)) {
8109
9161
  if (term.else === a) rel = NEGATED[rel];
8110
9162
  if (r === x) rel = FLIPPED[rel];
8111
- const other = l === x ? r : l;
8112
- const lit = other.op === "const" && typeof other.lit.value === "number" ? other.lit.value : null;
8113
- if (int32(other)) {
8114
- if (dir === "up" && (rel === "<" || rel === "<=" && lit !== null && lit < 2147483647)) return true;
8115
- if (dir === "down" && (rel === ">" || rel === ">=" && lit !== null && lit > -2147483648)) return true;
8116
- }
9163
+ if (holds(rel, l === x ? r : l)) return true;
8117
9164
  }
8118
9165
  }
8119
9166
  const up = idom.get(a);
@@ -8121,7 +9168,30 @@ function bounds(fn, type) {
8121
9168
  a = up;
8122
9169
  }
8123
9170
  };
8124
- return Object.assign(bound, { root });
9171
+ const bound = (x, at, dir) => int32(x) && proves(x, at, (rel, other) => {
9172
+ const lit = other.op === "const" && typeof other.lit.value === "number" ? other.lit.value : null;
9173
+ if (!int32(other)) return false;
9174
+ if (dir === "up") return rel === "<" || rel === "<=" && lit !== null && lit < 2147483647;
9175
+ return rel === ">" || rel === ">=" && lit !== null && lit > -2147483648;
9176
+ });
9177
+ const nonNegative = (x, seen = /* @__PURE__ */ new Set()) => {
9178
+ x = root(x);
9179
+ if (seen.has(x)) return true;
9180
+ if (x.op === "const") return typeof x.lit.value === "number" && x.lit.value >= 0;
9181
+ if (x.op === "phi") return seen.add(x) && x.incoming.every((inc) => nonNegative(inc.value, seen));
9182
+ if (x.op === "inc" || x.op === "num_inc") return nonNegative(x.operands[0], seen);
9183
+ return false;
9184
+ };
9185
+ const lengthOf = (v) => {
9186
+ v = root(v);
9187
+ const s = v.operands[0];
9188
+ return v.op === "get_prop" && v.key && "name" in v.key && v.key.name === "length" && s && isSubtype(type(s) & ~T.Empty, T.String) && type(s) !== T.None ? root(s) : void 0;
9189
+ };
9190
+ const inString = (s, x, at) => int32(x) && nonNegative(x) && proves(root(x), at, (rel, other) => rel === "<" && lengthOf(other) === root(s));
9191
+ return Object.assign(bound, {
9192
+ root,
9193
+ inString
9194
+ });
8125
9195
  }
8126
9196
  /** x + 1 or x - 1 (`i++`, `i += 1`, `i--`) of an int32 x the branches bound: an int32. */
8127
9197
  function step(i, bound) {
@@ -8157,7 +9227,7 @@ function transfer(i, type, sig, bound = null) {
8157
9227
  case "bigint": return T.BigInt;
8158
9228
  case "switch_string": return T.Int32;
8159
9229
  case "to_number": return Number_;
8160
- case "to_numeric": return numeric(t(a));
9230
+ case "to_numeric": return isSubtype(t(a), Number_) && t(a) !== T.None ? t(a) : numeric(t(a));
8161
9231
  case "add": {
8162
9232
  const ta = t(a), tb = t(b);
8163
9233
  if (ta === T.String || tb === T.String) return T.String;
@@ -8201,6 +9271,27 @@ function transfer(i, type, sig, bound = null) {
8201
9271
  case "num_ne": return T.Boolean;
8202
9272
  case "get_prop": return i.key && "name" in i.key && i.key.name === "length" && isSubtype(t(a), T.String) ? T.Int32 : Unknown;
8203
9273
  case "call_direct": return sig?.returns.get(i.target) ?? Unknown;
9274
+ case "get_elem":
9275
+ case "get_index":
9276
+ if (readsDouble(i)) return T.Int32 | T.Double;
9277
+ if (i.taView && isStatic(i.block.fn)) return T.Int32 | T.Undefined;
9278
+ return Unknown;
9279
+ case "char_code_at":
9280
+ case "math": return Number_;
9281
+ case "int_add":
9282
+ case "int_sub":
9283
+ case "int_mul": return T.Int32;
9284
+ case "to_i64":
9285
+ case "i64":
9286
+ case "int_div":
9287
+ case "int_mod": return T.Int64;
9288
+ case "to_i32": return T.Int32;
9289
+ case "call": {
9290
+ const trusted = trustedBuiltin(i, t);
9291
+ if (trusted === "char_code_at" && bound?.inString(b, i.operands[2], i.block)) return T.Int32;
9292
+ if (trusted) return Number_;
9293
+ return i.typedCall ? i.typedCall.returns : Unknown;
9294
+ }
8204
9295
  default: return Unknown;
8205
9296
  }
8206
9297
  }
@@ -8230,8 +9321,9 @@ function inferFunction(fn, sig = null) {
8230
9321
  }
8231
9322
  //#endregion
8232
9323
  //#region optimizer/src/inline.ts
8233
- /** A callee's largest size, in instructions. */
8234
- const INLINE_SIZE = 40;
9324
+ /** A callee's largest size, in instructions. 40 made a large program's C 10% bigger than 20 did
9325
+ * (each copy is C to compile) and ran no faster. */
9326
+ const INLINE_SIZE = 20;
8235
9327
  /** A caller's largest size it grows to, in instructions. */
8236
9328
  const CALLER_SIZE = 3e3;
8237
9329
  const ROUNDS = 2;
@@ -8776,123 +9868,18 @@ function foldBranch(b) {
8776
9868
  return true;
8777
9869
  }
8778
9870
  //#endregion
8779
- //#region optimizer/src/specialize.ts
8780
- /** Each generic operator's Number form. */
8781
- const NUMBER_FORM = {
8782
- add: "num_add",
8783
- sub: "num_sub",
8784
- mul: "num_mul",
8785
- div: "num_div",
8786
- mod: "num_mod",
8787
- neg: "num_neg",
8788
- inc: "num_inc",
8789
- dec: "num_dec",
8790
- lt: "num_lt",
8791
- le: "num_le",
8792
- gt: "num_gt",
8793
- ge: "num_ge",
8794
- eq: "num_eq",
8795
- ne: "num_ne",
8796
- strict_eq: "num_eq",
8797
- strict_ne: "num_ne",
8798
- bitand: "num_bitand",
8799
- bitor: "num_bitor",
8800
- bitxor: "num_bitxor",
8801
- bitnot: "num_bitnot",
8802
- shl: "num_shl",
8803
- sar: "num_sar",
8804
- shr: "num_shr"
8805
- };
8806
- /** The generic operators with a Number path (the rest of NUMBER_FORM's, and ToNumeric). */
8807
- const NUMBER_FIRST = /* @__PURE__ */ new Set([
8808
- "add",
8809
- "sub",
8810
- "mul",
8811
- "div",
8812
- "mod",
8813
- "neg",
8814
- "inc",
8815
- "dec",
8816
- "lt",
8817
- "le",
8818
- "gt",
8819
- "ge",
8820
- "eq",
8821
- "ne",
8822
- "bitand",
8823
- "bitor",
8824
- "bitxor",
8825
- "bitnot",
8826
- "shl",
8827
- "sar",
8828
- "shr",
8829
- "to_numeric"
8830
- ]);
8831
- /** Math functions of one Number whose C form generated code may call inline. */
8832
- const MATH_INLINE = /* @__PURE__ */ new Set([
8833
- "sqrt",
8834
- "abs",
8835
- "floor",
8836
- "ceil",
8837
- "trunc"
8838
- ]);
8839
- /** The built-in a one-argument call may run inline, if any: `Math.sqrt(x)` …,
8840
- * `s.charCodeAt(i)`, `String.fromCharCode(c)` (named by its C helper's suffix). */
8841
- function inlineBuiltin(i) {
8842
- const callee = i.op === "call" && i.operands.length === 3 ? i.operands[0] : null;
8843
- if (callee?.op !== "get_prop" || !("name" in callee.key)) return null;
8844
- const name = callee.key.name;
8845
- const global = (g) => callee.operands[0].op === "global_name" && callee.operands[0].target === g;
8846
- if (MATH_INLINE.has(name) && global("Math")) return `math_${name}`;
8847
- if (name === "fromCharCode" && global("String")) return "from_char_code";
8848
- if (name === "charCodeAt" && i.operands[1] === callee.operands[0]) return "char_code_at";
8849
- return null;
8850
- }
8851
- /** Element access's form for a Number key. */
8852
- const INDEX_FORM = {
8853
- get_elem: "get_index",
8854
- set_elem: "set_index"
8855
- };
8856
- const specialize = {
8857
- name: "specialize",
8858
- run(m, count) {
8859
- for (const fn of functionsOf(m)) {
8860
- let edges = false;
8861
- const depth = loopDepths(fn);
8862
- for (const b of fn.blocks) for (const i of b.instrs) {
8863
- const builtin = inlineBuiltin(i);
8864
- if (builtin) {
8865
- i.builtin = builtin;
8866
- count("inlined", "built-in call");
8867
- continue;
8868
- }
8869
- const index = INDEX_FORM[i.op];
8870
- if (index && isSubtype(i.operands[1].type, Number_)) {
8871
- i.op = index;
8872
- count("inlined", "element access", 1, "element accesses");
8873
- continue;
8874
- }
8875
- const form = NUMBER_FORM[i.op];
8876
- if (!form || !i.operands.every((o) => isSubtype(o.type, Number_))) {
8877
- if (NUMBER_FIRST.has(i.op) && depth.get(b) > 0 && i.operands.every((o) => o.type & Number_)) {
8878
- i.numberFirst = true;
8879
- count("added", "number fast path");
8880
- }
8881
- continue;
8882
- }
8883
- edges ||= i.mayThrow;
8884
- clearMayThrow(i);
8885
- i.op = form;
8886
- count("inlined", "number operator");
8887
- }
8888
- if (edges) removeUnreachable(fn);
8889
- }
8890
- return m;
8891
- }
8892
- };
8893
- //#endregion
8894
9871
  //#region optimizer/src/unbox.ts
8895
9872
  const F64 = /* @__PURE__ */ new Set([
9873
+ "char_code_at",
9874
+ "math",
9875
+ "int_add",
9876
+ "int_sub",
9877
+ "int_mul",
9878
+ "to_i64",
9879
+ "to_i32",
9880
+ "int_div",
9881
+ "int_mod",
9882
+ "i64",
8896
9883
  "num_add",
8897
9884
  "num_sub",
8898
9885
  "num_mul",
@@ -8917,11 +9904,21 @@ const BOOL = /* @__PURE__ */ new Set([
8917
9904
  "num_eq",
8918
9905
  "num_ne"
8919
9906
  ]);
9907
+ /** Boolean tests of boxed operands: their result is a C bool, their operands stay boxed. */
9908
+ const TESTS = /* @__PURE__ */ new Set([
9909
+ "same_function",
9910
+ "identical",
9911
+ "strict_eq_atom",
9912
+ "is_type",
9913
+ "strict_eq_num"
9914
+ ]);
9915
+ const isBool = (t) => t !== T.None && isSubtype(t, T.Boolean);
8920
9916
  /** How the report names each unboxed repr. */
8921
9917
  const REPR_NOUN = {
8922
9918
  boxed: "",
8923
9919
  f64: "double",
8924
9920
  i32: "int32",
9921
+ i64: "int64",
8925
9922
  bool: "bool"
8926
9923
  };
8927
9924
  /** Boxing a double for a use, against unboxing a JSValue (per use, before loop weights). */
@@ -8942,6 +9939,7 @@ const unbox = {
8942
9939
  function unboxedRepr(t) {
8943
9940
  if (t === T.None) return null;
8944
9941
  if (isSubtype(t, T.Int32)) return "i32";
9942
+ if (isSubtype(t, T.Int64 | T.Int32)) return "i64";
8945
9943
  if (isSubtype(t, Number_)) return "f64";
8946
9944
  if (isSubtype(t, T.Boolean)) return "bool";
8947
9945
  return null;
@@ -8950,7 +9948,7 @@ function unboxModule(m) {
8950
9948
  const closed = closedFunctions(m);
8951
9949
  const byName = new Map(m.functions.map((fn) => [fn.name, fn]));
8952
9950
  const fns = functionsOf(m).filter((fn) => !pauses(fn));
8953
- for (const fn of closed) {
9951
+ for (const fn of m.functions.filter((fn) => closed.has(fn) || fn.meta?.declared && !pauses(fn))) {
8954
9952
  const repr = unboxedRepr(fn.blocks.flatMap((b) => b.term?.kind === "return" ? [b.term.value] : []).reduce((u, v) => u | v.type, T.None));
8955
9953
  if (repr) fn.returnRepr = repr;
8956
9954
  }
@@ -8988,26 +9986,34 @@ function unboxModule(m) {
8988
9986
  index: t.kind === "return" ? 1 : 0
8989
9987
  });
8990
9988
  }
8991
- const params = closed.has(fn) ? fn.params : [];
9989
+ const declared = fn.meta?.declared;
9990
+ const params = closed.has(fn) ? fn.params : declared ? fn.params.filter((_, k) => declared[k] !== void 0) : [];
8992
9991
  for (const v of [...params, ...fn.blocks.flatMap((b) => b.phis)]) {
8993
9992
  const repr = unboxedRepr(v.type);
8994
9993
  if (repr === "bool") v.repr = "bool";
8995
9994
  else if (repr) candidates.push(v);
8996
9995
  }
8997
- for (const b of fn.blocks) for (const i of b.instrs) if (F64.has(i.op)) candidates.push(i);
8998
- else if (BOOL.has(i.op) || i.op === "same_function") i.repr = "bool";
8999
- else if (i.op === "call_direct") i.repr = byName.get(i.target)?.returnRepr ?? "boxed";
9996
+ for (const b of fn.blocks) for (const i of b.instrs) if (F64.has(i.op) || readsDouble(i) || isStringLength(i)) candidates.push(i);
9997
+ else if (BOOL.has(i.op) || TESTS.has(i.op) || i.op === "not") i.repr = "bool";
9998
+ else if (i.op === "call_direct") {
9999
+ const callee = byName.get(i.target);
10000
+ i.repr = callee && declaredCallFits(i, callee) ? callee.returnRepr ?? "boxed" : "boxed";
10001
+ } else if (typedCallFits(i)) i.repr = i.typedCall.returnRepr === "i64" ? "f64" : i.typedCall.returnRepr;
9000
10002
  }
9001
- for (const c of candidates) c.repr = isSubtype(c.type, T.Int32) ? "i32" : "f64";
10003
+ for (const c of candidates) c.repr = isSubtype(c.type, T.Int32) ? "i32" : isSubtype(c.type, T.Int64 | T.Int32) ? "i64" : "f64";
9002
10004
  const wants = (u) => {
9003
10005
  if (!u.user) return u.index === 1 ? u.block.fn.returnRepr ?? "boxed" : "boxed";
9004
10006
  if (u.user.op === "phi") return u.user.repr;
9005
- if (F64.has(u.user.op) || BOOL.has(u.user.op)) return u.user.repr === "boxed" ? "boxed" : "f64";
10007
+ if (u.user.op === "not") return isBool(u.user.operands[0].type) ? "bool" : "boxed";
10008
+ if (F64.has(u.user.op) || BOOL.has(u.user.op)) return "f64";
9006
10009
  if ((u.user.op === "get_index" || u.user.op === "set_index") && u.index === 1) return "f64";
10010
+ if ((u.user.op === "set_index" || u.user.op === "set_elem") && u.index === 2 && u.user.taView === "f64") return "f64";
9007
10011
  if (u.user.op === "call_direct") {
9008
10012
  const callee = byName.get(u.user.target);
9009
- return callee && closed.has(callee) ? callee.params[u.index].repr : "boxed";
10013
+ if (callee?.meta?.declared && !declaredCallFits(u.user, callee)) return "boxed";
10014
+ return callee && (closed.has(callee) || callee.meta?.declared?.[u.index] !== void 0) ? callee.params[u.index].repr : "boxed";
9010
10015
  }
10016
+ if (u.index >= 2 && typedCallFits(u.user)) return u.user.typedCall.params[u.index - 2].repr;
9011
10017
  return "boxed";
9012
10018
  };
9013
10019
  for (let changed = true; changed;) {
@@ -9032,6 +10038,8 @@ const DEFAULT_PASSES = [
9032
10038
  promoteGlobals,
9033
10039
  inline,
9034
10040
  dce,
10041
+ constantKeys,
10042
+ typedArrays,
9035
10043
  inferTypes,
9036
10044
  simplify,
9037
10045
  specialize,
@@ -10299,6 +11307,10 @@ const primitives = {
10299
11307
  c: "js_host_timer_unref",
10300
11308
  length: 2
10301
11309
  },
11310
+ loopNow: {
11311
+ c: "js_host_loop_now",
11312
+ length: 0
11313
+ },
10302
11314
  timerStart: {
10303
11315
  c: "js_host_timer_start",
10304
11316
  length: 4
@@ -11255,9 +12267,259 @@ function hostPrimitives(hosts) {
11255
12267
  return Object.assign({}, ...hosts.map((h) => h.primitives));
11256
12268
  }
11257
12269
  //#endregion
12270
+ //#region cli/src/static/types.ts
12271
+ const TYPES_FILE = resolve(import.meta.dirname, "..", "..", "types", "visaljs.d.ts");
12272
+ /** The integer a type annotation names: "visaljs"'s `int` or `i32`, through imports and aliases. */
12273
+ function intKind(checker, node) {
12274
+ if (!node || !ts.isTypeReferenceNode(node)) return void 0;
12275
+ let symbol = checker.getSymbolAtLocation(node.typeName);
12276
+ if (symbol && symbol.flags & ts.SymbolFlags.Alias) symbol = checker.getAliasedSymbol(symbol);
12277
+ const declaration = symbol?.declarations?.[0];
12278
+ if (!symbol || !declaration || !ts.isTypeAliasDeclaration(declaration)) return void 0;
12279
+ if (resolve(declaration.getSourceFile().fileName) !== TYPES_FILE) return intKind(checker, declaration.type);
12280
+ return Object.hasOwn(STATIC_TYPES, symbol.name) ? STATIC_TYPES[symbol.name].int : void 0;
12281
+ }
12282
+ /** What a parameter of each integer takes: an int any Number (an int64_t, converted by the
12283
+ * caller as C would); an i32 any Number too, converted to it on entry. */
12284
+ const INT_PARAMETERS = {
12285
+ i64: STATIC_TYPES.int.type,
12286
+ i32: STATIC_TYPES.number.type
12287
+ };
12288
+ /** The declared parameter types of a file's top-level functions, by where each starts: each a
12289
+ * type static code takes, or an error. */
12290
+ function declaredSignatures(checker, sf, out) {
12291
+ for (const st of sf.statements) {
12292
+ if (!ts.isFunctionDeclaration(st) || !st.name) continue;
12293
+ const types = st.parameters.map((p) => {
12294
+ const kind = intKind(checker, p.type);
12295
+ if (kind) return INT_PARAMETERS[kind];
12296
+ const type = checker.getTypeAtLocation(p);
12297
+ const t = irType(type);
12298
+ if (t === null) throw compileError(p, `parameter '${p.name.getText(sf)}' of '${st.name.text}': ${checker.typeToString(type)} isn't a static type yet`);
12299
+ return t;
12300
+ });
12301
+ const views = st.parameters.map((p) => viewOf(checker.getTypeAtLocation(p).getSymbol()?.name ?? ""));
12302
+ out.set(locKey(st), {
12303
+ types,
12304
+ views
12305
+ });
12306
+ }
12307
+ }
12308
+ /** tsc's primitive types (and their literals), by the name STATIC_TYPES knows them by. */
12309
+ const PRIMITIVE_FLAGS = [
12310
+ [ts.TypeFlags.Number | ts.TypeFlags.NumberLiteral, "number"],
12311
+ [ts.TypeFlags.String | ts.TypeFlags.StringLiteral, "string"],
12312
+ [ts.TypeFlags.Boolean | ts.TypeFlags.BooleanLiteral, "boolean"],
12313
+ [ts.TypeFlags.Undefined, "undefined"],
12314
+ [ts.TypeFlags.Null, "null"]
12315
+ ];
12316
+ /** A TypeScript type as the IR's, if static code takes it. */
12317
+ function irType(t) {
12318
+ if (t.isUnion()) {
12319
+ let u = T.None;
12320
+ for (const part of t.types) {
12321
+ const p = irType(part);
12322
+ if (p === null) return null;
12323
+ u |= p;
12324
+ }
12325
+ return u;
12326
+ }
12327
+ const name = PRIMITIVE_FLAGS.find(([flags]) => t.flags & flags)?.[1];
12328
+ if (name) return STATIC_TYPES[name].type;
12329
+ return viewOf(t.getSymbol()?.name ?? "") ? T.Object : null;
12330
+ }
12331
+ //#endregion
12332
+ //#region cli/src/static/link.ts
12333
+ /** A file's name in locations, as the program's: relative to the working directory. */
12334
+ function fileName(sf) {
12335
+ return normalize(relative(process.cwd(), sf.fileName));
12336
+ }
12337
+ /** A node's position as the adapter writes a location: the key that finds its IR function. */
12338
+ function locKey(node) {
12339
+ const sf = node.getSourceFile();
12340
+ const { line, character } = sf.getLineAndCharacterOfPosition(node.getStart(sf));
12341
+ return `${fileName(sf)}:${line + 1}:${character + 1}`;
12342
+ }
12343
+ function compileError(node, detail) {
12344
+ const sf = node.getSourceFile();
12345
+ const { line, character } = sf.getLineAndCharacterOfPosition(node.getStart(sf));
12346
+ return new CompileError("SyntaxError", detail, {
12347
+ file: fileName(sf),
12348
+ line: line + 1,
12349
+ column: character + 1
12350
+ });
12351
+ }
12352
+ /** The file an import names, when it is one of the unit's. */
12353
+ function importedFile(checker, node, files) {
12354
+ const symbol = node.moduleSpecifier ? checker.getSymbolAtLocation(node.moduleSpecifier) : void 0;
12355
+ const file = symbol?.valueDeclaration && ts.isSourceFile(symbol.valueDeclaration) ? resolve(symbol.valueDeclaration.fileName) : void 0;
12356
+ return file && files.has(file) ? file : void 0;
12357
+ }
12358
+ /** The names a file's top level declares, with their symbols. */
12359
+ function topLevel(checker, sf) {
12360
+ const out = /* @__PURE__ */ new Map();
12361
+ const add = (name) => {
12362
+ const symbol = checker.getSymbolAtLocation(name);
12363
+ if (symbol) out.set(name.text, symbol);
12364
+ };
12365
+ const addBinding = (b) => {
12366
+ if (ts.isIdentifier(b)) add(b);
12367
+ else for (const e of b.elements) if (!ts.isOmittedExpression(e)) addBinding(e.name);
12368
+ };
12369
+ for (const st of sf.statements) if ((ts.isFunctionDeclaration(st) || ts.isClassDeclaration(st)) && st.name) add(st.name);
12370
+ else if (ts.isVariableStatement(st)) for (const d of st.declarationList.declarations) addBinding(d.name);
12371
+ else if (ts.isImportDeclaration(st) && ts.isStringLiteral(st.moduleSpecifier) && st.moduleSpecifier.text === "visaljs:host") {
12372
+ const bindings = st.importClause?.namedBindings;
12373
+ if (bindings && ts.isNamedImports(bindings)) for (const e of bindings.elements) add(e.name);
12374
+ }
12375
+ return out;
12376
+ }
12377
+ /** The files in dependency order (an imported file before its importers); a cycle is an error. */
12378
+ function ordered(checker, sources, files) {
12379
+ const byFile = new Map(sources.map((sf) => [resolve(sf.fileName), sf]));
12380
+ const out = [];
12381
+ const state = /* @__PURE__ */ new Map();
12382
+ const visit = (sf, from) => {
12383
+ if (state.get(sf) === "done") return;
12384
+ if (state.get(sf) === "visiting") throw compileError(from, `static files import each other in a cycle (${sf.fileName}): their top levels would run in no order`);
12385
+ state.set(sf, "visiting");
12386
+ for (const st of sf.statements) if (ts.isImportDeclaration(st) || ts.isExportDeclaration(st)) {
12387
+ const file = importedFile(checker, st, files);
12388
+ if (file) visit(byFile.get(file), st);
12389
+ }
12390
+ state.set(sf, "done");
12391
+ out.push(sf);
12392
+ };
12393
+ for (const sf of sources) visit(sf, void 0);
12394
+ return out;
12395
+ }
12396
+ /** Links the unit's files (checked by tsc, and by the subset). */
12397
+ function linkUnit(program, files, hostPrimitives) {
12398
+ const checker = program.getTypeChecker();
12399
+ const fileSet = new Set(files.map((f) => resolve(f)));
12400
+ const sources = ordered(checker, files.map((f) => program.getSourceFile(f)), fileSet);
12401
+ const renamed = /* @__PURE__ */ new Map();
12402
+ const seen = /* @__PURE__ */ new Set();
12403
+ sources.forEach((sf, k) => {
12404
+ for (const [name, symbol] of topLevel(checker, sf)) {
12405
+ if (seen.has(name)) renamed.set(symbol, `${name}$${k}`);
12406
+ seen.add(name);
12407
+ }
12408
+ });
12409
+ const declarationOf = (id) => {
12410
+ const symbol = ts.isShorthandPropertyAssignment(id.parent) && id.parent.name === id ? checker.getShorthandAssignmentValueSymbol(id.parent) : checker.getSymbolAtLocation(id);
12411
+ if (!symbol) return void 0;
12412
+ if (renamed.has(symbol) || !(symbol.flags & ts.SymbolFlags.Alias)) return symbol;
12413
+ return checker.getAliasedSymbol(symbol);
12414
+ };
12415
+ const unit = {
12416
+ files: sources,
12417
+ rename: (id) => {
12418
+ const symbol = declarationOf(id);
12419
+ return symbol ? renamed.get(symbol) : void 0;
12420
+ },
12421
+ linked: (node) => importedFile(checker, node, fileSet) !== void 0,
12422
+ fileName,
12423
+ intKind: (type) => intKind(checker, type)
12424
+ };
12425
+ const exports = /* @__PURE__ */ new Map();
12426
+ const exportedFrom = /* @__PURE__ */ new Map();
12427
+ const exportFunction = (name, at, declaration) => {
12428
+ if (exportedFrom.has(name)) throw compileError(at, `typed unit: '${name}' is exported by ${exportedFrom.get(name)} and ${at.getSourceFile().fileName}`);
12429
+ if (Object.hasOwn(hostPrimitives, name)) throw compileError(at, `typed unit: export '${name}' is a host primitive's name`);
12430
+ if (!declaration || !ts.isFunctionDeclaration(declaration)) throw compileError(at, `typed unit: export '${name}' is not a function declaration whose parameters are all number, string or boolean`);
12431
+ exportedFrom.set(name, at.getSourceFile().fileName);
12432
+ exports.set(name, locKey(declaration));
12433
+ };
12434
+ for (const sf of sources) for (const { name, at } of exportsOf(sf)) exportFunction(name, at, ts.isExportSpecifier(at) ? checker.getExportSpecifierLocalTargetSymbol(at)?.valueDeclaration : ts.isFunctionDeclaration(at.parent) ? at.parent : void 0);
12435
+ return {
12436
+ ast: convertStaticUnit(unit, hostPrimitives),
12437
+ exports
12438
+ };
12439
+ }
12440
+ /** A static file's exported values, by name: where each is named (a function's name, a
12441
+ * variable's declaration, an export specifier). Read from the syntax alone, for the program's
12442
+ * bundle too (boundary.ts). */
12443
+ function exportsOf(sf) {
12444
+ const out = [];
12445
+ for (const st of sf.statements) {
12446
+ const exported = ts.canHaveModifiers(st) && ts.getModifiers(st)?.some((m) => m.kind === ts.SyntaxKind.ExportKeyword);
12447
+ if (exported && ts.isFunctionDeclaration(st) && st.name) out.push({
12448
+ name: st.name.text,
12449
+ at: st.name
12450
+ });
12451
+ else if (exported && ts.isVariableStatement(st)) for (const d of st.declarationList.declarations) out.push({
12452
+ name: d.name.getText(sf),
12453
+ at: d
12454
+ });
12455
+ else if (ts.isExportDeclaration(st) && !st.isTypeOnly && !st.moduleSpecifier && st.exportClause && ts.isNamedExports(st.exportClause)) {
12456
+ for (const spec of st.exportClause.elements) if (!spec.isTypeOnly) out.push({
12457
+ name: spec.name.text,
12458
+ at: spec
12459
+ });
12460
+ }
12461
+ }
12462
+ return out;
12463
+ }
12464
+ //#endregion
12465
+ //#region cli/src/static/boundary.ts
12466
+ /** A static file's module in the program's bundle: the file's path, then this suffix (messages
12467
+ * name the file). */
12468
+ const STATIC_MODULE = "?static";
12469
+ const isStaticModule = (id) => id.endsWith(STATIC_MODULE);
12470
+ /** A file that starts with the "use static" directive (after comments). */
12471
+ const DIRECTIVE = /^(?:\s|\/\/[^\n]*\n|\/\*[\s\S]*?\*\/)*(["'])use static\1/;
12472
+ function isStaticFile(file) {
12473
+ return /\.[cm]?tsx?$/.test(file) && DIRECTIVE.test(readFileSync(file, "utf8"));
12474
+ }
12475
+ /** The program's bundle: a static file's import resolves to its module of host primitives;
12476
+ * `found` collects the static files. */
12477
+ function staticImports(found) {
12478
+ return {
12479
+ name: "visaljs-static-imports",
12480
+ async resolveId(source, importer, { kind }) {
12481
+ if (source === "visaljs:host" || !importer || kind !== "import-statement") return null;
12482
+ const r = await this.resolve(source, importer, { skipSelf: true });
12483
+ if (!r || r.external || !isStaticFile(r.id)) return null;
12484
+ found.add(r.id);
12485
+ return r.id + STATIC_MODULE;
12486
+ },
12487
+ load(id) {
12488
+ if (!isStaticModule(id)) return null;
12489
+ const file = id.slice(0, -7);
12490
+ const names = exportsOf(ts.createSourceFile(file, readFileSync(file, "utf8"), ts.ScriptTarget.Latest, true, ts.ScriptKind.TS)).map((e) => e.name);
12491
+ return names.length ? `export { ${names.join(", ")} } from ${JSON.stringify(HOST_MODULE)};\n` : "";
12492
+ }
12493
+ };
12494
+ }
12495
+ /** The program's calls of static exports: a call whose callee is the export's host import (the
12496
+ * host function, or the module binding one store sets to it). Before optimizing. */
12497
+ function annotateTypedCalls(m, calls) {
12498
+ const all = [m.main, ...m.functions];
12499
+ const slots = /* @__PURE__ */ new Map();
12500
+ const stores = /* @__PURE__ */ new Map();
12501
+ for (const fn of all) for (const b of fn.blocks) for (const i of b.instrs) {
12502
+ if (i.op !== "store_global" || i.operands[0].op === "empty") continue;
12503
+ stores.set(i.index, (stores.get(i.index) ?? 0) + 1);
12504
+ const v = i.operands[0];
12505
+ if (v.op === "host_function" && Object.hasOwn(calls, v.target)) slots.set(i.index, v.target);
12506
+ }
12507
+ const target = (callee) => {
12508
+ const v = callee.op === "check_tdz" ? callee.operands[0] : callee;
12509
+ if (v.op === "host_function") return Object.hasOwn(calls, v.target) ? v.target : void 0;
12510
+ if (v.op === "load_global" && stores.get(v.index) === 1) return slots.get(v.index);
12511
+ };
12512
+ for (const fn of all) for (const b of fn.blocks) for (const i of b.instrs) {
12513
+ const name = i.op === "call" ? target(i.operands[0]) : void 0;
12514
+ if (name) i.typedCall = calls[name];
12515
+ }
12516
+ }
12517
+ //#endregion
11258
12518
  //#region cli/src/bundle.ts
11259
12519
  /** A bundling failure, already formatted as `file:line:col: Kind: message` lines. */
11260
12520
  var BundleError = class extends Error {};
12521
+ /** Bundles `entry` into one ES module, `node:*` imports resolved to the host packages' modules.
12522
+ * Warnings go to `warn`, with their location ("file:line:col: ", or ""). */
11261
12523
  async function bundle(entry, hosts, warn, extras = {}) {
11262
12524
  const rollup = (implicit, warn, required) => rollupOnce(entry, hosts, implicit, warn, required, extras);
11263
12525
  const b = await rollup([], warn, []);
@@ -11273,11 +12535,8 @@ function programCode(code, cwd, hosts) {
11273
12535
  const preludes = hosts.map((h) => h.preludeDir + sep);
11274
12536
  return code.replace(/^\/\/#region (.*)\n[^]*?^\/\/#endregion$/gm, (region, path) => preludes.some((dir) => resolve(cwd, path.replace(/^(?:\0|\\0)visaljs-runtime:/, "")).startsWith(dir)) ? "" : region);
11275
12537
  }
11276
- /** The packages a bundle names in a call the bundler couldn't follow as a require() (and those
11277
- * `given`):
11278
- * ``a(`react-dom`)``, where `a` is a bundle's createRequire(import.meta.url) (Vite's, for the
11279
- * packages it left external). Only in code that calls createRequire; a call of a plain name with
11280
- * one string, naming a package (not a built-in) that resolves from the entry. A call that isn't a
12538
+ /** `given`, and the packages a bundle names in ``a(`react-dom`)``, where `a` is a createRequire()
12539
+ * the bundler couldn't follow (Vite's, for the packages it left external). A call that isn't a
11281
12540
  * require() but names an installed package bundles it needlessly: the build lists them. */
11282
12541
  function requiredPackages(code, entry, given) {
11283
12542
  if (!/\bcreateRequire\s*\(/.test(code)) return [];
@@ -11307,11 +12566,12 @@ async function rollupOnce(entry, hosts, implicit, warn, required, extras) {
11307
12566
  cwd: dirname(resolve(entry)),
11308
12567
  platform: "node",
11309
12568
  logLevel: "warn",
11310
- onLog: (_level, log) => log.code !== "EVAL" && warn(logText(log), logWhere(log)),
12569
+ onLog: (_level, log) => log.code !== "EVAL" && !(log.code === "MODULE_LEVEL_DIRECTIVE" && log.message.includes("\"use static\"")) && warn(logText(log), logWhere(log)),
11311
12570
  ...extras.nodeEnv === void 0 ? {} : { transform: { define: { "process.env.NODE_ENV": JSON.stringify(extras.nodeEnv) } } },
11312
12571
  plugins: [
11313
12572
  hostModules(hosts),
11314
12573
  commonJS(),
12574
+ ...ignoredModules$1(extras.ignored ?? []),
11315
12575
  missingRequires(warn),
11316
12576
  depdWrappers(),
11317
12577
  implicitEntry(entry, implicit),
@@ -11345,9 +12605,8 @@ async function rollupOnce(entry, hosts, implicit, warn, required, extras) {
11345
12605
  throw new BundleError(errors.map((log) => formatLog(log, log.code === "PARSE_ERROR" ? "SyntaxError" : "error")).join("\n"));
11346
12606
  }
11347
12607
  }
11348
- /** Host modules: `node:fs` (or `fs`) resolves to its host package's prelude
11349
- * module, which is bundled like the program's own code; prelude modules may import the host
11350
- * primitives. */
12608
+ /** `node:fs` (or `fs`) resolves to its host package's prelude module, bundled like the program's
12609
+ * code; only prelude modules may import the host primitives. */
11351
12610
  function hostModules(hosts) {
11352
12611
  const modules = hostModules$1(hosts);
11353
12612
  const preludes = hosts.map((h) => h.preludeDir + sep);
@@ -11355,7 +12614,7 @@ function hostModules(hosts) {
11355
12614
  name: "visaljs-host",
11356
12615
  resolveId(source, importer, { kind }) {
11357
12616
  if (source === "visaljs:host") {
11358
- if (importer && (preludes.some((dir) => importer.startsWith(dir)) || importer.startsWith(RUNTIME_COPY))) return {
12617
+ if (importer && (preludes.some((dir) => importer.startsWith(dir)) || importer.startsWith(RUNTIME_COPY) || isStaticModule(importer))) return {
11359
12618
  id: source,
11360
12619
  external: true
11361
12620
  };
@@ -11386,14 +12645,11 @@ module.exports = m;
11386
12645
  }
11387
12646
  };
11388
12647
  }
11389
- /** CommonJS modules, as Node decides it: `.cjs`, or `.js` without module syntax outside a
11390
- * `"type": "module"` package. Node runs one as sloppy code unless it says "use strict", though the
11391
- * bundle is an ES module (strict): the bundler wraps each in a function, which gets the compiler's
11392
- * COMMONJS_DIRECTIVE, so it's sloppy; one that says "use strict" (a directive the bundler drops)
11393
- * runs in a strict function of its own. One that names `__filename` or `__dirname`
11394
- * declares them at its top as the original file's absolute path and directory at build time, as
11395
- * `import.meta`'s `filename` and `dirname` are. ES modules keep them undefined (code tests
11396
- * `typeof __dirname` to tell which it is). */
12648
+ /** CommonJS modules (as Node decides: `.cjs`, or `.js` without module syntax outside a `"type":
12649
+ * "module"` package) run sloppy, though the bundle is strict: each wrapper gets COMMONJS_DIRECTIVE,
12650
+ * and one saying "use strict" (a directive the bundler drops) gets a strict function of its own.
12651
+ * `__filename` and `__dirname` are the file's at build time; ES modules keep them undefined (code
12652
+ * tests `typeof __dirname`). */
11397
12653
  function commonJS() {
11398
12654
  const types = /* @__PURE__ */ new Map();
11399
12655
  const packageType = (dir) => {
@@ -11451,15 +12707,14 @@ function commonJS() {
11451
12707
  }
11452
12708
  return marked ? {
11453
12709
  code: s.toString(),
11454
- map: s.generateMap({ hires: true }).toString()
12710
+ map: s.generateMap({ hires: "boundary" }).toString()
11455
12711
  } : null;
11456
12712
  }
11457
12713
  };
11458
12714
  }
11459
- /** A require() of a file that isn't there, or of a native addon (a .node file), throws when it's
11460
- * reached, as in Node, instead of failing the build: packages require() files their published
11461
- * build leaves out (a framework's dev server, in a branch production never takes: Next.js's
11462
- * standalone output), and addons they fall back from (sharp, in a try). A warning says where. */
12715
+ /** A require() of a missing file or a native addon throws when reached, as in Node, instead of
12716
+ * failing the build: packages require() files their published build leaves out (Next.js's dev
12717
+ * server) and addons they fall back from (sharp, in a try). */
11463
12718
  function missingRequires(warn) {
11464
12719
  return {
11465
12720
  name: "visaljs-missing-requires",
@@ -11481,10 +12736,59 @@ function missingRequires(warn) {
11481
12736
  };
11482
12737
  }
11483
12738
  const MISSING = "\0visaljs-missing:";
11484
- /** depd (deprecation warnings: Express 4, body-parser, http-errors, Next.js's send) wraps a
11485
- * deprecated function with new Function, to give the wrapper the function's arity: a closure
11486
- * instead, whose length is 0. new Function needs eval; depd's call is the same everywhere it's
11487
- * bundled or vendored: `new Function("fn", "log", "deprecate", "message", "site", body)(…)`. */
12739
+ /** Stand-ins for the `ignored` modules. A "call" one whose export names can't be read is bundled
12740
+ * instead. */
12741
+ function ignoredModules$1(ignored) {
12742
+ if (!ignored.length) return [];
12743
+ const matching = (id) => {
12744
+ const path = id.replaceAll("\\", "/");
12745
+ return ignored.find((m) => /\.[cm]?js$/.test(m.module) ? path.endsWith(`/node_modules/${m.module}`) : path.includes(`/node_modules/${m.module}/`));
12746
+ };
12747
+ return [{
12748
+ name: "visaljs-ignored",
12749
+ async resolveId(source, importer, { kind }) {
12750
+ if (kind !== "require-call" || !importer || source.startsWith("\0")) return null;
12751
+ const resolved = await this.resolve(source, importer, { skipSelf: true });
12752
+ const m = resolved && !resolved.external ? matching(resolved.id) : void 0;
12753
+ if (!resolved || !m) return null;
12754
+ const code = m.stub === "call" ? readFileSync(resolved.id, "utf8") : "";
12755
+ const names = m.stub === "call" ? exportNames(code) : [];
12756
+ if (m.stub === "call" && !names.length) return null;
12757
+ return IGNORED + JSON.stringify({
12758
+ module: m.module,
12759
+ reason: m.reason,
12760
+ code: m.code ?? "MODULE_NOT_FOUND",
12761
+ names,
12762
+ esModule: code.includes("Object.defineProperty(exports, \"__esModule\"")
12763
+ });
12764
+ },
12765
+ load(id) {
12766
+ if (!id.startsWith(IGNORED)) return null;
12767
+ const { module, reason, code, names, esModule } = JSON.parse(id.slice(IGNORED.length));
12768
+ if (!names.length) {
12769
+ const message = code === "MODULE_NOT_FOUND" ? `Cannot find module '${module}' (${reason})` : `${module}: ${reason}`;
12770
+ return `throw Object.assign(new Error(${JSON.stringify(message)}), { code: ${JSON.stringify(code)} });\n`;
12771
+ }
12772
+ const message = JSON.stringify(`${module} isn't in the compiled program (${reason})`);
12773
+ return (esModule ? "Object.defineProperty(exports, \"__esModule\", { value: true });\n" : "") + names.map((n) => `exports.${n} = function () { throw new Error(${message}); };\n`).join("");
12774
+ }
12775
+ }];
12776
+ }
12777
+ const IGNORED = "\0visaljs-ignored:";
12778
+ /** A CommonJS module's export names, as compilers write them: `exports.a = …`,
12779
+ * `Object.defineProperty(exports, "a", …)`, and TypeScript's and SWC's `_export(exports, { a: …, b: … })`. */
12780
+ function exportNames(code) {
12781
+ const names = /* @__PURE__ */ new Set();
12782
+ for (const m of code.matchAll(/\bexports\.([A-Za-z_$][\w$]*)\s*=(?!=)/g)) names.add(m[1]);
12783
+ for (const m of code.matchAll(/\bObject\.defineProperty\(exports, "([A-Za-z_$][\w$]*)"/g)) names.add(m[1]);
12784
+ const block = /\b_export\(exports, \{\n([\s\S]*?)\n\}\);/.exec(code)?.[1];
12785
+ if (block) for (const m of block.matchAll(/^ {4}([A-Za-z_$][\w$]*): function\(\) \{/gm)) names.add(m[1]);
12786
+ names.delete("__esModule");
12787
+ return [...names];
12788
+ }
12789
+ /** depd (Express 4, body-parser, Next.js's send) wraps deprecated functions with new Function,
12790
+ * which needs eval, for their arity: a closure instead (length 0). Its call is the same wherever
12791
+ * it's bundled or vendored. */
11488
12792
  function depdWrappers() {
11489
12793
  const call = /\bnew Function\((?=\s*(["'])fn\1\s*,\s*\1log\1\s*,\s*\1deprecate\1\s*,\s*\1message\1\s*,\s*\1site\1\s*,)/g;
11490
12794
  const helper = "function __visaljs_depdWrapper() { return function (fn, log, deprecate, message, site) { return function () { log.call(deprecate, message, site); return fn.apply(this, arguments); }; }; }\n";
@@ -11572,10 +12876,10 @@ function gcCheckFlags() {
11572
12876
  if (mode === "stress") return ["-DJS_GC_STRESS=1"];
11573
12877
  throw new Error(`VISALJS_GC_CHECKS=${mode}: expected verify or stress`);
11574
12878
  }
11575
- /** $CC alone, else each of the usual C compilers on the PATH that runs, in order of preference:
11576
- * a build falls back to the next when one fails. Names for the same program (cc, a link to
11577
- * gcc) count once. Last comes Zig's clang, which brings its own libc headers and linker: a C
11578
- * compiler for a machine whose own toolchain is missing or broken. */
12879
+ /** $CC alone, else each usual C compiler on the PATH that runs, in order of preference: a build
12880
+ * falls back to the next. Clang first: it compiles a large program's C in half GCC's time and
12881
+ * memory, and its code ran faster (7% fewer instructions per HTTP request). Zig's clang comes
12882
+ * last: it brings its own libc headers and linker. */
11579
12883
  function findCompilers() {
11580
12884
  if (process.env.CC) {
11581
12885
  if (!compilerVersion(process.env.CC)) throw new Error(`$CC (${process.env.CC}) doesn't run`);
@@ -11583,8 +12887,8 @@ function findCompilers() {
11583
12887
  }
11584
12888
  const found = /* @__PURE__ */ new Map();
11585
12889
  for (const cc of [
11586
- "cc",
11587
12890
  "clang",
12891
+ "cc",
11588
12892
  "gcc",
11589
12893
  "zig cc"
11590
12894
  ]) {
@@ -11616,7 +12920,6 @@ function command(cc) {
11616
12920
  const [program = cc, ...args] = cc.trim().split(/\s+/);
11617
12921
  return [program, ...args];
11618
12922
  }
11619
- /** Runs the compiler with `args`, synchronously (execFileSync's options and result). */
11620
12923
  function runCompiler(cc, args, options) {
11621
12924
  const [program, ...prefix] = command(cc);
11622
12925
  return execFileSync(program, [...prefix, ...args], options);
@@ -11745,13 +13048,9 @@ function compileCommand(cc, src, common) {
11745
13048
  ...cxx(common)
11746
13049
  ];
11747
13050
  }
11748
- /** Prebuilt libraries (cli/scripts/prebuilt.ts): each platform's third-party libraries, built
11749
- * ahead with Zig, so a first build doesn't compile them. The published package has them in
11750
- * prebuilt/<target>/ (hostTarget), beside runtime/ and node/, as <name>-<prebuiltKey>.a: the key
11751
- * is the library's sources and flags, not the compiler's, so any C compiler for the platform
11752
- * links them. They're -O2 builds, used when the build is one (not --debug or VISALJS_GC_CHECKS)
11753
- * and the compiler targets this machine. VISALJS_PREBUILT names another directory of them (a
11754
- * cross-compiler's target's, or build/prebuilt/<target> in this repository), or 0 for none. */
13051
+ /** A prebuilt library (cli/scripts/prebuilt.ts, built with Zig): prebuilt/<target>/<name>-<prebuiltKey>.a.
13052
+ * The key leaves out the compiler, so any C compiler for the platform links it. Only for -O2 builds
13053
+ * whose compiler targets this machine; VISALJS_PREBUILT names another directory, or 0 for none. */
11755
13054
  function prebuiltArchive(tc, lib) {
11756
13055
  if (tc.opt.join(" ") !== PREBUILT_OPT.join(" ")) return void 0;
11757
13056
  const env = process.env.VISALJS_PREBUILT;
@@ -11762,9 +13061,9 @@ function prebuiltArchive(tc, lib) {
11762
13061
  }
11763
13062
  [...COMMON.filter((f) => f !== "-g")];
11764
13063
  const PREBUILT_OPT = ["-O2"];
11765
- /** A prebuilt library's key: its sources' and headers' paths (from the directory holding runtime/
11766
- * and node/: the same in this repository and in the published package) and contents, and its flags (their paths the same way). PREBUILT_COMMON and PREBUILT_OPT are
11767
- * the format's: changing them changes its version, the key's first line. */
13064
+ /** A prebuilt library's key: its files' paths (from runtime/'s parent: the same in this repository
13065
+ * and the package) and contents, and its flags. Changing PREBUILT_COMMON or PREBUILT_OPT bumps
13066
+ * the version, the key's first line. */
11768
13067
  function prebuiltKey(lib) {
11769
13068
  const root = dirname(RUNTIME_DIR);
11770
13069
  const portable = (s) => s.split(root).join("<root>").replaceAll("\\", "/");
@@ -11778,13 +13077,13 @@ function hostTarget() {
11778
13077
  const musl = process.platform === "linux" && !process.report.getReport().header?.glibcVersionRuntime;
11779
13078
  return `${process.platform}-${process.arch}${musl ? "-musl" : ""}`;
11780
13079
  }
11781
- let installed;
13080
+ let installed$1;
11782
13081
  function installedPrebuilt() {
11783
- if (installed === void 0) {
13082
+ if (installed$1 === void 0) {
11784
13083
  const dir = join(dirname(RUNTIME_DIR), "prebuilt", hostTarget());
11785
- installed = existsSync(dir) ? dir : "";
13084
+ installed$1 = existsSync(dir) ? dir : "";
11786
13085
  }
11787
- return installed;
13086
+ return installed$1;
11788
13087
  }
11789
13088
  /** Runs the compiler commands, as many at once as there are processors (in a child Node process:
11790
13089
  * the driver's API is synchronous; the commands on its stdin). Throws when one fails, after its output. */
@@ -11861,17 +13160,14 @@ function programArchives(tc, hosts, building) {
11861
13160
  const libraries = [...hosts.flatMap((h) => h.libraries(RUNTIME_INCLUDE)).filter((l) => l.sources.length), ...runtimeLibraries()];
11862
13161
  return [...libraries.map((l) => libraryArchive(tc, l, building)), ...libraries.flatMap((l) => l.link ?? []).map((l) => `-l${l}`)];
11863
13162
  }
11864
- /** Compiles the program's C files and links them with `archives` (programArchives). The
11865
- * linker takes only what the program reaches: a program without I/O links no libuv. The
11866
- * libraries keep their debug info (-g); stripping, when asked, happens at this link.
11867
- *
11868
- * One file compiles and links in one command. A split program's files compile to
11869
- * objects, `jobs` at once, then link; `compiled` hears as each one is done. Asynchronous: the
11870
- * build's report keeps drawing while the compilers run. */
13163
+ /** Compiles the program's C files and links them with `archives` (programArchives), stripping at
13164
+ * this link when asked. A split program's files compile `jobs` at once; `compiled` hears as each
13165
+ * is done. Asynchronous, so the build's report keeps drawing. */
11871
13166
  async function compileProgram(tc, cFiles, output, archives, { extraFlags = [], jobs = compileJobs(), compiled } = {}) {
11872
13167
  const flags = [
11873
13168
  ...COMMON,
11874
13169
  ...tc.opt,
13170
+ ...tc.strip ? ["-g0"] : [],
11875
13171
  ...extraFlags
11876
13172
  ];
11877
13173
  const link = [
@@ -11918,10 +13214,11 @@ async function compileProgram(tc, cFiles, output, archives, { extraFlags = [], j
11918
13214
  ...link
11919
13215
  ]);
11920
13216
  }
11921
- /** How many of a split program's files compile at once: one per processor, and one per 1.5 GB
11922
- * of memory (a file of the split needs up to about 1 GB at -O2). */
11923
- function compileJobs() {
11924
- return Math.max(1, Math.min(availableParallelism(), Math.floor(totalmem() / 15e8)));
13217
+ /** How many of a split program's files compile at once: one per processor and per `perJob` bytes
13218
+ * of available memory (a cgroup's limit counts). A file needs up to about 1.1 GB at -O2 with
13219
+ * debug info, 0.55 GB without. */
13220
+ function compileJobs(perJob = 15e8) {
13221
+ return Math.max(1, Math.min(availableParallelism(), Math.floor(process.availableMemory() / perJob)));
11925
13222
  }
11926
13223
  /** Runs the compiler; its stderr passes through (but for notes the program can't act on), and its
11927
13224
  * tail is kept to recognize a broken toolchain. Rejects when it fails. */
@@ -12104,8 +13401,244 @@ ${readFileSync(VISALJS_LICENSE, "utf8").trimEnd()}
12104
13401
  ${sections.join("\n")}`;
12105
13402
  }
12106
13403
  //#endregion
12107
- //#region cli/src/entry.ts
12108
- var EntryError = class extends Error {};
13404
+ //#region cli/src/static/subset.ts
13405
+ const K = ts.SyntaxKind;
13406
+ const STATEMENTS = /* @__PURE__ */ new Set([
13407
+ K.FunctionDeclaration,
13408
+ K.VariableStatement,
13409
+ K.VariableDeclarationList,
13410
+ K.VariableDeclaration,
13411
+ K.Block,
13412
+ K.IfStatement,
13413
+ K.ForStatement,
13414
+ K.ForOfStatement,
13415
+ K.WhileStatement,
13416
+ K.DoStatement,
13417
+ K.ReturnStatement,
13418
+ K.BreakStatement,
13419
+ K.ContinueStatement,
13420
+ K.ExpressionStatement,
13421
+ K.SwitchStatement,
13422
+ K.CaseBlock,
13423
+ K.CaseClause,
13424
+ K.DefaultClause,
13425
+ K.ThrowStatement,
13426
+ K.TryStatement,
13427
+ K.CatchClause,
13428
+ K.EmptyStatement,
13429
+ K.LabeledStatement,
13430
+ K.TypeAliasDeclaration,
13431
+ K.InterfaceDeclaration
13432
+ ]);
13433
+ const EXPRESSIONS = /* @__PURE__ */ new Set([
13434
+ K.Identifier,
13435
+ K.NumericLiteral,
13436
+ K.StringLiteral,
13437
+ K.NoSubstitutionTemplateLiteral,
13438
+ K.TemplateExpression,
13439
+ K.TemplateHead,
13440
+ K.TemplateMiddle,
13441
+ K.TemplateTail,
13442
+ K.TemplateSpan,
13443
+ K.TrueKeyword,
13444
+ K.FalseKeyword,
13445
+ K.NullKeyword,
13446
+ K.BinaryExpression,
13447
+ K.PrefixUnaryExpression,
13448
+ K.PostfixUnaryExpression,
13449
+ K.ConditionalExpression,
13450
+ K.CallExpression,
13451
+ K.NewExpression,
13452
+ K.PropertyAccessExpression,
13453
+ K.ElementAccessExpression,
13454
+ K.ArrayLiteralExpression,
13455
+ K.ObjectLiteralExpression,
13456
+ K.PropertyAssignment,
13457
+ K.ShorthandPropertyAssignment,
13458
+ K.ParenthesizedExpression,
13459
+ K.ArrowFunction,
13460
+ K.FunctionExpression,
13461
+ K.TypeOfExpression,
13462
+ K.NonNullExpression,
13463
+ K.AsExpression,
13464
+ K.SatisfiesExpression,
13465
+ K.Parameter
13466
+ ]);
13467
+ /** What isn't allowed yet, in words, for the error. */
13468
+ const NAMED = {
13469
+ [K.ClassDeclaration]: "classes",
13470
+ [K.ClassExpression]: "classes",
13471
+ [K.ThisKeyword]: "`this`",
13472
+ [K.SuperKeyword]: "`super`",
13473
+ [K.EnumDeclaration]: "enums",
13474
+ [K.ModuleDeclaration]: "namespaces",
13475
+ [K.Decorator]: "decorators",
13476
+ [K.YieldExpression]: "generators",
13477
+ [K.AwaitExpression]: "async functions",
13478
+ [K.DeleteExpression]: "`delete`",
13479
+ [K.ForInStatement]: "for…in",
13480
+ [K.SpreadElement]: "spread",
13481
+ [K.SpreadAssignment]: "spread",
13482
+ [K.ObjectBindingPattern]: "destructuring",
13483
+ [K.ArrayBindingPattern]: "destructuring",
13484
+ [K.TaggedTemplateExpression]: "tagged templates",
13485
+ [K.RegularExpressionLiteral]: "regular expressions",
13486
+ [K.BigIntLiteral]: "BigInt",
13487
+ [K.VoidExpression]: "`void`",
13488
+ [K.MetaProperty]: "import.meta and new.target",
13489
+ [K.ImportKeyword]: "import()",
13490
+ [K.MethodDeclaration]: "methods",
13491
+ [K.GetAccessor]: "getters",
13492
+ [K.SetAccessor]: "setters",
13493
+ [K.WithStatement]: "`with`",
13494
+ [K.DebuggerStatement]: "`debugger`",
13495
+ [K.ExportAssignment]: "default exports"
13496
+ };
13497
+ /** A module's top level: declarations only (top-level code runs, in an order across files). */
13498
+ const TOP_LEVEL = /* @__PURE__ */ new Set([
13499
+ K.FunctionDeclaration,
13500
+ K.VariableStatement,
13501
+ K.TypeAliasDeclaration,
13502
+ K.InterfaceDeclaration,
13503
+ K.ImportDeclaration,
13504
+ K.ExportDeclaration,
13505
+ K.EmptyStatement
13506
+ ]);
13507
+ const notYet = (what) => `static code doesn't have ${what} yet`;
13508
+ /** Checks the unit's files against the subset; throws the first error. */
13509
+ function checkSubset(program, files) {
13510
+ const checker = program.getTypeChecker();
13511
+ const fileSet = new Set(files.map((f) => resolve(f)));
13512
+ for (const file of files) program.getSourceFile(file).statements.forEach((st, k) => {
13513
+ if (k === 0 && ts.isExpressionStatement(st) && ts.isStringLiteral(st.expression)) return;
13514
+ if (NAMED[st.kind]) throw compileError(st, notYet(NAMED[st.kind]));
13515
+ if (!TOP_LEVEL.has(st.kind)) throw compileError(st, "a static file only declares: top-level statements would run code, in no order across files");
13516
+ if (ts.isImportDeclaration(st)) return checkImport(checker, st, fileSet);
13517
+ if (ts.isExportDeclaration(st)) {
13518
+ if (st.moduleSpecifier) throw compileError(st, notYet("re-exports (export … from)"));
13519
+ return;
13520
+ }
13521
+ visit(st);
13522
+ });
13523
+ }
13524
+ function checkImport(checker, st, files) {
13525
+ const specifier = st.moduleSpecifier.text;
13526
+ if (specifier === "visaljs:host" || specifier === "visaljs" || importedFile(checker, st, files)) {
13527
+ if (st.importClause?.name || st.importClause?.namedBindings && ts.isNamespaceImport(st.importClause.namedBindings)) throw compileError(st, notYet("default and namespace imports"));
13528
+ return;
13529
+ }
13530
+ throw compileError(st.moduleSpecifier, `static code imports only static files, "visaljs" and ${HOST_MODULE}: '${specifier}' isn't one`);
13531
+ }
13532
+ /** A type: anything tsc takes but `any`. */
13533
+ function visitType(node) {
13534
+ if (node.kind === K.AnyKeyword) throw compileError(node, "`any` isn't a static type: name the type");
13535
+ ts.forEachChild(node, visitType);
13536
+ }
13537
+ function visit(node) {
13538
+ if (ts.isTypeNode(node)) return visitType(node);
13539
+ if (NAMED[node.kind]) throw compileError(node, notYet(NAMED[node.kind]));
13540
+ if (ts.isToken(node) || ts.isModifier(node) || ts.isTypeParameterDeclaration(node)) return;
13541
+ if (ts.isFunctionLike(node) && (node.asteriskToken || ts.getModifiers(node)?.some((m) => m.kind === K.AsyncKeyword))) throw compileError(node, notYet("generators and async functions"));
13542
+ if (ts.isInterfaceDeclaration(node) || ts.isTypeAliasDeclaration(node)) return visitType(node);
13543
+ if (!STATEMENTS.has(node.kind) && !EXPRESSIONS.has(node.kind)) throw compileError(node, notYet(ts.SyntaxKind[node.kind]));
13544
+ ts.forEachChild(node, visit);
13545
+ }
13546
+ /** A function's name in C, as the emitter makes it (its ident). */
13547
+ function ident(name) {
13548
+ let out = "";
13549
+ for (const ch of name) out += /[A-Za-z0-9_]/.test(ch) ? ch : `_u${ch.codePointAt(0).toString(16)}_`;
13550
+ return out;
13551
+ }
13552
+ /** Compiles the typed files as one unit. */
13553
+ function buildTypedUnit(files, primitives) {
13554
+ const program = ts.createProgram([...files, TYPES_FILE], {
13555
+ strict: true,
13556
+ noEmit: true,
13557
+ target: ts.ScriptTarget.ESNext,
13558
+ module: ts.ModuleKind.ESNext,
13559
+ moduleResolution: ts.ModuleResolutionKind.Bundler,
13560
+ allowImportingTsExtensions: true,
13561
+ skipLibCheck: true
13562
+ });
13563
+ checkSubset(program, files);
13564
+ const errors = ts.getPreEmitDiagnostics(program).filter((d) => d.category === ts.DiagnosticCategory.Error && files.includes(d.file?.fileName ?? ""));
13565
+ if (errors.length) throw new Error(ts.formatDiagnostics(errors, {
13566
+ getCanonicalFileName: (f) => f,
13567
+ getCurrentDirectory: () => process.cwd(),
13568
+ getNewLine: () => "\n"
13569
+ }));
13570
+ const checker = program.getTypeChecker();
13571
+ const signatures = /* @__PURE__ */ new Map();
13572
+ for (const file of files) declaredSignatures(checker, program.getSourceFile(file), signatures);
13573
+ const { ast, exports: exported } = linkUnit(program, files, primitives);
13574
+ const ir = lower(ast, analyze(ast));
13575
+ const at = (fn) => fn.loc ? `${fn.loc.file}:${fn.loc.line}:${fn.loc.column}` : "";
13576
+ for (const fn of ir.functions) {
13577
+ const meta = fn.meta;
13578
+ const sig = signatures.get(at(fn));
13579
+ if (!sig) continue;
13580
+ const hidden = fn.params.length - sig.types.length;
13581
+ meta.declared = fn.params.map((_, k) => k < hidden ? void 0 : sig.types[k - hidden]);
13582
+ meta.declaredViews = fn.params.map((_, k) => k < hidden ? void 0 : sig.views[k - hidden]);
13583
+ meta.external = true;
13584
+ }
13585
+ const exports = {};
13586
+ const exportedFns = /* @__PURE__ */ new Map();
13587
+ for (const [name, loc] of exported) {
13588
+ const fn = ir.functions.find((f) => at(f) === loc);
13589
+ exportedFns.set(name, fn.name);
13590
+ exports[name] = {
13591
+ c: `vt_jsf_${ident(fn.name)}__generic`,
13592
+ length: fn.meta.length
13593
+ };
13594
+ }
13595
+ const report = [];
13596
+ const optimized = optimize(ir, void 0, report);
13597
+ const calls = {};
13598
+ for (const [name, fnName] of exportedFns) {
13599
+ const fn = optimized.functions.find((f) => f.name === fnName);
13600
+ const declared = fn.meta.declared;
13601
+ if (declared.some((t) => t === void 0)) continue;
13602
+ let returns = T.None;
13603
+ for (const b of fn.blocks) if (b.term?.kind === "return") returns |= b.term.value.type & ~T.Empty;
13604
+ calls[name] = {
13605
+ direct: `vt_jsf_${ident(fn.name)}`,
13606
+ generic: exports[name].c,
13607
+ params: fn.params.map((p, k) => ({
13608
+ type: declared[k],
13609
+ repr: p.repr,
13610
+ ...fn.meta.declaredViews?.[k] ? { view: fn.meta.declaredViews[k] } : {}
13611
+ })),
13612
+ returns: returns === T.None ? T.Undefined : returns & T.Int64 ? returns & ~T.Int64 | T.Int32 | T.Double : returns,
13613
+ returnRepr: fn.returnRepr ?? "boxed"
13614
+ };
13615
+ }
13616
+ const unit = emitHarnessScript(optimized, "vt_", /* @__PURE__ */ new Map(), { hostPrimitives: primitives });
13617
+ return {
13618
+ c: unit.c,
13619
+ atoms: new Map(unit.atoms),
13620
+ exports,
13621
+ calls,
13622
+ report,
13623
+ names: [...programNames(ast)]
13624
+ };
13625
+ }
13626
+ //#endregion
13627
+ //#region cli/src/pipeline/errors.ts
13628
+ /** An integration's failure, as the message to print. */
13629
+ var IntegrationError = class extends Error {};
13630
+ /** A command `ctx.run` ran that failed: its output, for the integration to look into. */
13631
+ var CommandError = class extends IntegrationError {
13632
+ output;
13633
+ constructor(message, output) {
13634
+ super(message);
13635
+ this.output = output;
13636
+ }
13637
+ };
13638
+ //#endregion
13639
+ //#region cli/src/integrations/project.ts
13640
+ /** The project names no entry, or several. */
13641
+ var EntryError = class extends IntegrationError {};
12109
13642
  const NODE_VALUE_OPTIONS = /* @__PURE__ */ new Set([
12110
13643
  "-r",
12111
13644
  "--require",
@@ -12203,18 +13736,31 @@ function projectEntry(root) {
12203
13736
  };
12204
13737
  throw new EntryError(`no entry file: checked ${existsSync(pkgFile) ? `package.json's "bin", "scripts.start" and "main", and index.js` : "a package.json and index.js"}${missing.length ? ` (${missing.join("; ")})` : ""}`);
12205
13738
  }
13739
+ const project = {
13740
+ id: "project",
13741
+ name: "package.json",
13742
+ provides: "entry",
13743
+ detect: () => true,
13744
+ hooks: { prepare(ctx) {
13745
+ const entry = projectEntry(ctx.root);
13746
+ ctx.entryFile = entry.file;
13747
+ ctx.output = join(ctx.root, entry.name);
13748
+ ctx.note(`Entry: ${relative(ctx.root, entry.file).replaceAll("\\", "/")} (${entry.from})`, "📄");
13749
+ if (entry.ignoredArgs.length) ctx.note(`its arguments (${entry.ignoredArgs.join(" ")}) aren't built in: give them when you run the binary`);
13750
+ } }
13751
+ };
12206
13752
  //#endregion
12207
- //#region cli/src/frameworks/error.ts
12208
- /** A framework's failure, as the message to print. */
12209
- var FrameworkError = class extends Error {};
12210
- //#endregion
12211
- //#region cli/src/frameworks/project.ts
13753
+ //#region cli/src/pipeline/packages.ts
12212
13754
  /** The project's package.json, or {} without one. */
12213
13755
  function packageJson(root) {
12214
13756
  const file = join(root, "package.json");
12215
- return existsSync(file) ? JSON.parse(readFileSync(file, "utf8")) : {};
13757
+ if (!existsSync(file)) return {};
13758
+ try {
13759
+ return JSON.parse(readFileSync(file, "utf8"));
13760
+ } catch (e) {
13761
+ throw new IntegrationError(`cannot read ${file}: ${e.message}`);
13762
+ }
12216
13763
  }
12217
- /** The project's package.json's dependencies and devDependencies. */
12218
13764
  function dependencies(root) {
12219
13765
  const pkg = packageJson(root);
12220
13766
  return {
@@ -12222,25 +13768,36 @@ function dependencies(root) {
12222
13768
  ...pkg.dependencies
12223
13769
  };
12224
13770
  }
13771
+ /** Whether `name` is installed for the project in `root`: in node_modules here or in a directory
13772
+ * above (a workspace's), as Node finds it, or in pnpm's store there (node_modules/.pnpm/<name>@…),
13773
+ * where a dependency's dependencies are. */
13774
+ function installed(root, name) {
13775
+ const store = `${name.replace("/", "+")}@`;
13776
+ for (let dir = root;; dir = dirname(dir)) {
13777
+ const modules = join(dir, "node_modules");
13778
+ if (existsSync(join(modules, name, "package.json"))) return true;
13779
+ const pnpm = join(modules, ".pnpm");
13780
+ if (existsSync(pnpm) && readdirSync(pnpm).some((d) => d.startsWith(store))) return true;
13781
+ if (dirname(dir) === dir) return false;
13782
+ }
13783
+ }
12225
13784
  /** The script a package installed in the project runs as `name` (its package.json's "bin"). */
12226
13785
  function packageBin(root, pkg, name) {
12227
13786
  let manifest;
12228
13787
  try {
12229
13788
  manifest = createRequire(join(root, "package.json")).resolve(`${pkg}/package.json`);
12230
13789
  } catch {
12231
- throw new FrameworkError(`${pkg} isn't installed in ${root}: run npm install first`);
13790
+ throw new IntegrationError(`${pkg} isn't installed in ${root}: run npm install first`);
12232
13791
  }
12233
13792
  const { bin } = JSON.parse(readFileSync(manifest, "utf8"));
12234
13793
  const script = typeof bin === "string" ? bin : bin?.[name];
12235
- if (!script) throw new FrameworkError(`${pkg} has no ${name} command`);
13794
+ if (!script) throw new IntegrationError(`${pkg} has no ${name} command`);
12236
13795
  return join(dirname(manifest), script);
12237
13796
  }
12238
13797
  //#endregion
12239
- //#region cli/src/frameworks/next.ts
13798
+ //#region cli/src/integrations/next.ts
12240
13799
  const TABLE = "\0visaljs-next-table";
12241
13800
  const UNTRACED = "\0visaljs-next-untraced:";
12242
- const UNCALLED_PREFIX = "\0visaljs-next-uncalled:";
12243
- /** next build's config, as the standalone server.js gets it, and its build directory (.next). */
12244
13801
  function serverConfig(root) {
12245
13802
  for (const distDir of [".next", ...configuredDistDir(root)]) {
12246
13803
  const file = join(root, distDir, "required-server-files.json");
@@ -12252,10 +13809,9 @@ function serverConfig(root) {
12252
13809
  };
12253
13810
  }
12254
13811
  }
12255
- if (existsSync(join(root, "out", "index.html")) || existsSync(join(root, "out", "404.html"))) throw new FrameworkError(`${root} is a static export (output: "export" in next.config): it has no server to compile. Serve out/ with any static file server, or remove output: "export" to compile the Next.js server`);
12256
- throw new FrameworkError(`${root} has no Next.js build (no .next/required-server-files.json): run \`next build\`, or build without --no-framework-build`);
13812
+ if (existsSync(join(root, "out", "index.html")) || existsSync(join(root, "out", "404.html"))) throw new IntegrationError(`${root} is a static export (output: "export" in next.config): it has no server to compile. Serve out/ with any static file server, or remove output: "export" to compile the Next.js server`);
13813
+ throw new IntegrationError(`${root} has no Next.js build (no .next/required-server-files.json): run \`next build\`, or build without --no-framework-build`);
12257
13814
  }
12258
- /** Whether next build used Turbopack (its default since Next.js 16): its chunks' runtime is there. */
12259
13815
  function isTurbopackBuild(root, distDir) {
12260
13816
  const chunks = join(root, distDir, "server", "chunks");
12261
13817
  return existsSync(join(chunks, "[turbopack]_runtime.js")) || existsSync(join(chunks, "ssr", "[turbopack]_runtime.js"));
@@ -12263,7 +13819,7 @@ function isTurbopackBuild(root, distDir) {
12263
13819
  function configuredDistDir(root) {
12264
13820
  return readdirSync(root).filter((d) => d !== ".next" && existsSync(join(root, d, "required-server-files.json")));
12265
13821
  }
12266
- /** Every file under `dir`, by its path relative to it, with "/". */
13822
+ /** Every file under `dir`, relative to it, with "/". */
12267
13823
  function files(dir, under = "") {
12268
13824
  const out = [];
12269
13825
  for (const entry of readdirSync(join(dir, under))) {
@@ -12273,9 +13829,6 @@ function files(dir, under = "") {
12273
13829
  }
12274
13830
  return out;
12275
13831
  }
12276
- /** The table's module (CommonJS: each entry is required when it's first loaded): .next/server's
12277
- * modules by their path under .next, and its client reference manifests as functions of the
12278
- * context Next evaluates them in. */
12279
13832
  function tableModule(root, distDir) {
12280
13833
  const build = join(root, distDir);
12281
13834
  const server = files(join(build, "server")).filter((f) => f.endsWith(".js")).map((f) => `server/${f}`);
@@ -12284,7 +13837,6 @@ function tableModule(root, distDir) {
12284
13837
  const manifests = server.filter(manifest).map((f) => ` ${JSON.stringify(f)}: function (globalThis) { var process = globalThis.process;\n${readFileSync(join(build, f), "utf8")}\n},`);
12285
13838
  return `"use strict";\nexports.modules = {\n${modules.join("\n")}\n};\nexports.manifests = {\n${manifests.join("\n")}\n};\n`;
12286
13839
  }
12287
- /** The files next build traced as the server's and its routes' (their .nft.json files): absolute paths. */
12288
13840
  function tracedFiles(root, distDir) {
12289
13841
  const build = join(root, distDir);
12290
13842
  const traces = [...files(build, "").filter((f) => !f.startsWith("standalone/") && !f.startsWith("cache/") && f.endsWith(".nft.json"))];
@@ -12295,9 +13847,9 @@ function tracedFiles(root, distDir) {
12295
13847
  }
12296
13848
  return traced;
12297
13849
  }
12298
- /** process.env's variables Next's server reads to pick a route runtime, as this build's server has
12299
- * them: NEXT_RUNTIME (only the edge runtime sets it), TURBOPACK (next.js sets it for a Turbopack
12300
- * build) and __NEXT_EXPERIMENTAL_REACT (as next build defines it in the code it compiles). */
13850
+ /** The variables Next picks a route runtime by, as this build's server has them: NEXT_RUNTIME (only
13851
+ * the edge runtime sets it), TURBOPACK (set for a Turbopack build), __NEXT_EXPERIMENTAL_REACT (as
13852
+ * next build defines it). */
12301
13853
  function runtimeVariables(root, config) {
12302
13854
  let experimental;
12303
13855
  try {
@@ -12312,25 +13864,106 @@ function runtimeVariables(root, config) {
12312
13864
  ...experimental ? {} : { __NEXT_EXPERIMENTAL_REACT: "undefined" }
12313
13865
  };
12314
13866
  }
12315
- /** Whether the build has middleware or edge functions: the edge sandbox runs them. */
13867
+ function hasPagesRoutes(root, distDir) {
13868
+ const file = join(root, distDir, "server", "pages-manifest.json");
13869
+ if (!existsSync(file)) return true;
13870
+ return Object.values(JSON.parse(readFileSync(file, "utf8"))).some((f) => f.endsWith(".js"));
13871
+ }
12316
13872
  function hasMiddleware(root, distDir) {
12317
13873
  const file = join(root, distDir, "server", "middleware-manifest.json");
12318
13874
  if (!existsSync(file)) return true;
12319
13875
  const { middleware = {}, functions = {} } = JSON.parse(readFileSync(file, "utf8"));
12320
13876
  return Object.keys(middleware).length > 0 || Object.keys(functions).length > 0;
12321
13877
  }
12322
- const DEV_ONLY = [
12323
- ["next/dist/server/next.js", "./dev/next-dev-server"],
12324
- ["next/dist/server/lib/router-server.js", "./router-utils/setup-dev-bundler"],
12325
- ["next/dist/client/components/app-router.js", "../dev/hot-reloader/app/hot-reloader-app"],
12326
- ["next/dist/server/lib/router-server.js", "../../telemetry/storage"]
12327
- ];
12328
- const UNCALLED = [[
12329
- "next/dist/server/config.js",
12330
- "../build/next-config-ts/transpile-config",
12331
- ["transpileConfig"]
12332
- ]];
13878
+ function ignoredModules(root, distDir) {
13879
+ const dev = "Next's dev mode: visaljs builds its production server";
13880
+ return [
13881
+ {
13882
+ module: "next/dist/server/dev/next-dev-server.js",
13883
+ reason: dev
13884
+ },
13885
+ {
13886
+ module: "next/dist/server/lib/router-utils/setup-dev-bundler.js",
13887
+ reason: dev
13888
+ },
13889
+ {
13890
+ module: "next/dist/telemetry/storage.js",
13891
+ reason: dev
13892
+ },
13893
+ {
13894
+ module: "next/dist/client/dev/hot-reloader/app/hot-reloader-app.js",
13895
+ reason: dev
13896
+ },
13897
+ {
13898
+ module: "next/dist/server/next-typescript.js",
13899
+ reason: "Next's TypeScript plugin for editors"
13900
+ },
13901
+ {
13902
+ module: "next/dist/build/next-config-ts/transpile-config.js",
13903
+ stub: "call",
13904
+ reason: "the server gets the config next build saved"
13905
+ },
13906
+ {
13907
+ module: "next/dist/build/swc/index.js",
13908
+ stub: "call",
13909
+ reason: "SWC is a native addon, which visaljs can't load"
13910
+ },
13911
+ ...hasPagesRoutes(root, distDir) ? [] : [{
13912
+ module: "next/dist/server/route-modules/pages/module.compiled.js",
13913
+ reason: "the Pages Router's renderer, and this build has no Pages routes"
13914
+ }],
13915
+ ...hasMiddleware(root, distDir) ? [] : [{
13916
+ module: "next/dist/server/web/sandbox/index.js",
13917
+ reason: "the edge sandbox runs middleware, which this build has none of"
13918
+ }]
13919
+ ];
13920
+ }
12333
13921
  const RUNTIME_PICKERS = /[\\/]next[\\/]dist[\\/]server[\\/]route-modules[\\/](?:[\w-]+[\\/]module\.compiled|app-page[\\/]vendored[\\/]\w+[\\/]entrypoints)\.js$/;
13922
+ function constants(root, config) {
13923
+ const testProxy = config.experimental?.testProxy;
13924
+ const runners = testRunners(root);
13925
+ return [
13926
+ ...runners ? [{
13927
+ file: /[\\/]next[\\/]dist[\\/]server[\\/]config-schema\.js$/,
13928
+ find: "const _nexttest = require(\"../cli/next-test\");",
13929
+ replace: `const _nexttest = { SUPPORTED_TEST_RUNNERS_LIST: ${runners} };`
13930
+ }] : [],
13931
+ {
13932
+ file: /[\\/]next[\\/]dist[\\/]server[\\/]lib[\\/]start-server\.js$/,
13933
+ find: "const { dir, isDev, hostname,",
13934
+ replace: "const isDev = false, { dir, hostname,"
13935
+ },
13936
+ ...testProxy ? [] : [
13937
+ {
13938
+ file: /[\\/]next[\\/]dist[\\/]server[\\/]next-server\.js$/,
13939
+ find: "this.serverOptions.experimentalTestProxy",
13940
+ replace: "false"
13941
+ },
13942
+ {
13943
+ file: /[\\/]next[\\/]dist[\\/]server[\\/]lib[\\/]router-server\.js$/,
13944
+ find: "config.experimental.testProxy",
13945
+ replace: "false"
13946
+ },
13947
+ {
13948
+ file: /[\\/]next[\\/]dist[\\/]server[\\/]web[\\/]adapter\.js$/,
13949
+ find: "process.env.NEXT_PRIVATE_TEST_PROXY === 'true'",
13950
+ replace: "false"
13951
+ }
13952
+ ]
13953
+ ].map((c) => ({
13954
+ ...c,
13955
+ missing: "skip"
13956
+ }));
13957
+ }
13958
+ function testRunners(root) {
13959
+ try {
13960
+ const file = createRequire(join(root, "package.json")).resolve("next/dist/cli/next-test.js");
13961
+ const list = /\bconst SUPPORTED_TEST_RUNNERS_LIST = (\[[\s\w'",]*\]);/.exec(readFileSync(file, "utf8"))?.[1];
13962
+ return list && JSON.stringify(JSON.parse(list.replaceAll("'", "\"").replace(/,\s*\]$/, "]")));
13963
+ } catch {
13964
+ return;
13965
+ }
13966
+ }
12334
13967
  const LOADS = [
12335
13968
  {
12336
13969
  file: /[\\/]next[\\/]dist[\\/]server[\\/]require\.js$/,
@@ -12348,42 +13981,8 @@ const LOADS = [
12348
13981
  replace: "globalThis.__visaljs_next.evaluate(path, contextObject, () => (0, _vm.runInNewContext)(content, contextObject))"
12349
13982
  }
12350
13983
  ];
12351
- const next = {
12352
- name: "Next.js",
12353
- detect: (root) => "next" in dependencies(root) && !("vinext" in dependencies(root)),
12354
- async build(root) {
12355
- const bin = packageBin(root, "next", "next");
12356
- const standalone = join(root, ".next", "standalone");
12357
- const hadStandalone = existsSync(standalone);
12358
- try {
12359
- const env = {
12360
- ...process.env,
12361
- NEXT_PRIVATE_STANDALONE: "1",
12362
- NEXT_TELEMETRY_DISABLED: "1",
12363
- FORCE_COLOR: "0"
12364
- };
12365
- await promisify(execFile)(process.execPath, [bin, "build"], {
12366
- cwd: root,
12367
- maxBuffer: 64 << 20,
12368
- env
12369
- });
12370
- } catch (e) {
12371
- const { stdout = "", stderr = "" } = e;
12372
- throw new FrameworkError(`next build failed in ${root}:\n${`${stdout}${stderr}`.trimEnd().split("\n").slice(-30).join("\n")}`);
12373
- } finally {
12374
- if (!hadStandalone) rmSync(standalone, {
12375
- recursive: true,
12376
- force: true
12377
- });
12378
- }
12379
- return "Built the app with next build, as a standalone build (.next/)";
12380
- },
12381
- entry(root) {
12382
- const { config, distDir } = serverConfig(root);
12383
- if (!existsSync(join(root, distDir, "server", "app")) && !existsSync(join(root, distDir, "server", "pages"))) throw new FrameworkError(`${root}'s ${distDir}/server has no app/ or pages/: this Next.js build isn't supported yet`);
12384
- if (config.output !== "standalone") throw new FrameworkError(`${root}'s ${distDir} isn't a standalone build, whose traces visaljs bundles from: build without --no-framework-build (visaljs runs next build as one, without changing next.config), or set output: "standalone"`);
12385
- if (!isTurbopackBuild(root, distDir)) throw new FrameworkError(`${root}'s ${distDir} was built with webpack (next build --webpack): visaljs compiles Turbopack builds, next build's default, for now. Build without --webpack`);
12386
- return `import { modules, manifests } from ${JSON.stringify(TABLE)};
13984
+ function entrySource(config, distDir) {
13985
+ return `import { modules, manifests } from ${JSON.stringify(TABLE)};
12387
13986
  const config = ${JSON.stringify(config)};
12388
13987
  const key = (path) => {
12389
13988
  const p = String(path).replaceAll("\\\\", "/");
@@ -12418,67 +14017,102 @@ await startServer({
12418
14017
  process.exit(1);
12419
14018
  });
12420
14019
  `;
12421
- },
12422
- plugins(root) {
12423
- const { config, distDir } = serverConfig(root);
12424
- const traced = tracedFiles(root, distDir);
12425
- const variables = runtimeVariables(root, config);
12426
- const read = new RegExp(`\\bprocess\\.env\\.(${Object.keys(variables).join("|")})\\b(?!\\s*(?:=[^=]|\\?\\?=|\\|\\|=|&&=))`, "g");
12427
- const sandbox = !hasMiddleware(root, distDir);
12428
- const packages = join(root, "node_modules") + sep;
12429
- return [{
12430
- name: "visaljs-next",
12431
- async resolveId(source, importer, { kind }) {
12432
- if (source === TABLE) return source;
12433
- if (kind !== "require-call" || source.startsWith("\0") || !importer?.startsWith(packages)) return null;
12434
- const from = importer.replaceAll("\\", "/");
12435
- if (DEV_ONLY.some(([file, s]) => s === source && from.endsWith(`/node_modules/${file}`))) return UNTRACED + source;
12436
- const uncalled = UNCALLED.find(([file, s]) => s === source && from.endsWith(`/node_modules/${file}`));
12437
- if (uncalled) return UNCALLED_PREFIX + JSON.stringify([source, uncalled[2]]);
12438
- if (sandbox && source === "./web/sandbox" && /[\\/]next[\\/]dist[\\/]server[\\/]next-server\.js$/.test(importer)) return UNTRACED + source;
12439
- const resolved = await this.resolve(source, importer, { skipSelf: true });
12440
- if (!resolved || resolved.external || !resolved.id.startsWith(packages) || traced.has(resolved.id)) return resolved;
12441
- return UNTRACED + source;
12442
- },
12443
- load(id) {
12444
- if (id === TABLE) return tableModule(root, distDir);
12445
- if (id.startsWith(UNCALLED_PREFIX)) {
12446
- const [source, names] = JSON.parse(id.slice(23));
12447
- const message = JSON.stringify(`${source} isn't in the compiled server: visaljs builds Next's production server, which doesn't call it`);
12448
- return names.map((n) => `exports.${n} = function () { throw new Error(${message}); };\n`).join("");
12449
- }
12450
- if (!id.startsWith(UNTRACED)) return null;
12451
- const message = `Cannot find module '${id.slice(23)}' (next build didn't trace it as the server's)`;
12452
- return `throw Object.assign(new Error(${JSON.stringify(message)}), { code: "MODULE_NOT_FOUND" });\n`;
12453
- },
12454
- transform: {
12455
- filter: { id: /(?:require|load-manifest\.external|_runtime|module\.compiled|entrypoints)\.js$/ },
12456
- handler(code, id) {
12457
- if (RUNTIME_PICKERS.test(id)) return {
12458
- code: code.replace(read, (_, name) => variables[name]),
12459
- map: null
12460
- };
12461
- const load = LOADS.find((l) => l.file.test(id));
12462
- if (!load) return null;
12463
- if (!code.includes(load.find)) this.error(`${relative(root, id)} no longer loads the build as visaljs expects (${load.find}): this Next.js version isn't supported yet`);
12464
- return {
12465
- code: code.replaceAll(load.find, load.replace),
12466
- map: null
12467
- };
12468
- }
14020
+ }
14021
+ function buildModules(root, distDir) {
14022
+ const traced = tracedFiles(root, distDir);
14023
+ const packages = join(root, "node_modules") + sep;
14024
+ return {
14025
+ name: "visaljs-next",
14026
+ async resolveId(source, importer, { kind }) {
14027
+ if (source === TABLE) return source;
14028
+ if (kind !== "require-call" || source.startsWith("\0") || !importer?.startsWith(packages)) return null;
14029
+ const resolved = await this.resolve(source, importer, { skipSelf: true });
14030
+ if (!resolved || resolved.external || !resolved.id.startsWith(packages) || traced.has(resolved.id)) return resolved;
14031
+ return UNTRACED + source;
14032
+ },
14033
+ load(id) {
14034
+ if (id === TABLE) return tableModule(root, distDir);
14035
+ if (!id.startsWith(UNTRACED)) return null;
14036
+ const message = `Cannot find module '${id.slice(23)}' (next build didn't trace it as the server's)`;
14037
+ return `throw Object.assign(new Error(${JSON.stringify(message)}), { code: "MODULE_NOT_FOUND" });\n`;
14038
+ }
14039
+ };
14040
+ }
14041
+ const next = {
14042
+ id: "next",
14043
+ name: "Next.js",
14044
+ provides: "entry",
14045
+ detect: (project) => project.packages.has("next"),
14046
+ hooks: {
14047
+ async frameworkBuild(ctx) {
14048
+ const bin = packageBin(ctx.root, "next", "next");
14049
+ const standalone = join(ctx.root, ".next", "standalone");
14050
+ const hadStandalone = existsSync(standalone);
14051
+ try {
14052
+ await ctx.run([bin, "build"], { env: {
14053
+ NEXT_PRIVATE_STANDALONE: "1",
14054
+ NEXT_TELEMETRY_DISABLED: "1"
14055
+ } });
14056
+ } finally {
14057
+ if (!hadStandalone) rmSync(standalone, {
14058
+ recursive: true,
14059
+ force: true
14060
+ });
12469
14061
  }
12470
- }];
12471
- },
12472
- notes(root, output) {
12473
- const { distDir } = serverConfig(root);
12474
- const inside = relative(root, output);
12475
- const bin = !inside.startsWith("..") && !isAbsolute(inside) ? `./${inside.replaceAll("\\", "/")}` : output;
12476
- const project = relative(process.cwd(), root);
12477
- return [`Run it in the project: it serves ${distDir}/ and public/ from the working directory, as \`next start\` does: ${project && project !== "." ? `cd ${project.startsWith("..") ? root : project} && ` : ""}PORT=3000 ${bin}`, `To deploy it, copy ${basename(output)}, ${distDir}/ (without ${distDir}/cache) and public/ to the server, and run it there the same way`];
14062
+ return "Built the app with next build, as a standalone build (.next/)";
14063
+ },
14064
+ prepare(ctx) {
14065
+ const { root } = ctx;
14066
+ const { config, distDir } = serverConfig(root);
14067
+ if (!existsSync(join(root, distDir, "server", "app")) && !existsSync(join(root, distDir, "server", "pages"))) throw new IntegrationError(`${root}'s ${distDir}/server has no app/ or pages/: this Next.js build isn't supported yet`);
14068
+ if (config.output !== "standalone") throw new IntegrationError(`${root}'s ${distDir} isn't a standalone build, whose traces visaljs bundles from: build without --no-framework-build (visaljs runs next build as one, without changing next.config), or set output: "standalone"`);
14069
+ if (!isTurbopackBuild(root, distDir)) throw new IntegrationError(`${root}'s ${distDir} was built with webpack (next build --webpack): visaljs compiles Turbopack builds, next build's default, for now. Build without --webpack`);
14070
+ ctx.entrySource = entrySource(config, distDir);
14071
+ return {
14072
+ config,
14073
+ distDir
14074
+ };
14075
+ },
14076
+ bundle(ctx, { config, distDir }) {
14077
+ for (const m of ignoredModules(ctx.root, distDir)) ctx.ignore(m);
14078
+ const variables = runtimeVariables(ctx.root, config);
14079
+ const read = new RegExp(`\\bprocess\\.env\\.(${Object.keys(variables).join("|")})\\b(?!\\s*(?:=[^=]|\\?\\?=|\\|\\|=|&&=))`, "g");
14080
+ ctx.rewrite({
14081
+ file: RUNTIME_PICKERS,
14082
+ find: read,
14083
+ replace: (_, name) => variables[name],
14084
+ missing: "skip"
14085
+ });
14086
+ for (const r of [...constants(ctx.root, config), ...LOADS]) ctx.rewrite(r);
14087
+ ctx.plugin(buildModules(ctx.root, distDir));
14088
+ },
14089
+ done(ctx, { distDir }) {
14090
+ const { root, output } = ctx;
14091
+ const inside = relative(root, output);
14092
+ const bin = !inside.startsWith("..") && !isAbsolute(inside) ? `./${inside.replaceAll("\\", "/")}` : output;
14093
+ const project = relative(process.cwd(), root);
14094
+ const cd = project && project !== "." ? `cd ${project.startsWith("..") ? root : project} && ` : "";
14095
+ ctx.note(`Run it in the project: it serves ${distDir}/ and public/ from the working directory, as \`next start\` does: ${cd}PORT=3000 ${bin}`);
14096
+ ctx.note(`To deploy it, copy ${basename(output)}, ${distDir}/ (without ${distDir}/cache) and public/ to the server, and run it there the same way`);
14097
+ }
12478
14098
  }
12479
14099
  };
12480
14100
  //#endregion
12481
- //#region cli/src/frameworks/vinext.ts
14101
+ //#region cli/src/integrations/sharp.ts
14102
+ const sharp = {
14103
+ id: "sharp",
14104
+ name: "sharp",
14105
+ detect: (project) => project.installed("sharp"),
14106
+ hooks: { bundle(ctx) {
14107
+ ctx.ignore({
14108
+ module: "sharp",
14109
+ reason: "sharp is a native addon, which visaljs can't load",
14110
+ code: "ERR_DLOPEN_FAILED"
14111
+ });
14112
+ } }
14113
+ };
14114
+ //#endregion
14115
+ //#region cli/src/integrations/vinext.ts
12482
14116
  /** The Vite config file and mode the project's build script gives `vite build` (-c, -m), if any. */
12483
14117
  function buildScriptOptions(root) {
12484
14118
  const words = packageJson(root).scripts?.build?.split(/\s+/) ?? [];
@@ -12498,7 +14132,6 @@ const WORKERS_FILES = [
12498
14132
  "wrangler.toml",
12499
14133
  "cloudflare.config.ts"
12500
14134
  ];
12501
- /** Whether the project is made for Cloudflare Workers: its own build makes a Worker. */
12502
14135
  function isWorkersProject(root) {
12503
14136
  return WORKERS_FILES.some((f) => existsSync(join(root, f))) || "@cloudflare/vite-plugin" in dependencies(root);
12504
14137
  }
@@ -12520,81 +14153,262 @@ const flat = async (plugins) => (await Promise.all((plugins ?? []).map(async (p)
12520
14153
  const plugins = (await flat(loaded?.config.plugins)).filter((p) => !p.name?.startsWith("vite-plugin-cloudflare"));
12521
14154
  export default { ...loaded?.config, root, plugins };
12522
14155
  `;
14156
+ const BUILD_IMPORT = {
14157
+ file: /[\\/]vinext[\\/]dist[\\/]server[\\/]prod-server\.js$/,
14158
+ find: "import(resolveServerEntryImportUrl(entryPath))"
14159
+ };
12523
14160
  const MARKER = join("dist", "server", "vinext-server.json");
12524
14161
  const BUILD_ENTRIES = ["dist/server/index.js", "dist/server/entry.js"];
12525
14162
  //#endregion
12526
- //#region cli/src/frameworks/index.ts
12527
- const FRAMEWORKS = [{
12528
- name: "vinext",
12529
- detect: (root) => "vinext" in dependencies(root),
12530
- async build(root) {
12531
- const vite = packageBin(root, "vite", "vite");
12532
- const { config, mode } = buildScriptOptions(root);
12533
- const workers = isWorkersProject(root);
12534
- const args = [vite, "build"];
12535
- if (mode) args.push("--mode", mode);
12536
- const env = {
12537
- ...process.env,
12538
- FORCE_COLOR: "0"
14163
+ //#region cli/src/integrations/index.ts
14164
+ const INTEGRATIONS = [
14165
+ {
14166
+ id: "vinext",
14167
+ name: "vinext",
14168
+ provides: "entry",
14169
+ overrides: ["next"],
14170
+ detect: (project) => project.packages.has("vinext"),
14171
+ hooks: {
14172
+ async frameworkBuild(ctx) {
14173
+ const { root } = ctx;
14174
+ const vite = packageBin(root, "vite", "vite");
14175
+ const { config, mode } = buildScriptOptions(root);
14176
+ const workers = isWorkersProject(root);
14177
+ const args = [vite, "build"];
14178
+ if (mode) args.push("--mode", mode);
14179
+ const env = {};
14180
+ const wrapper = join(root, "node_modules", ".visaljs", "vite.config.mjs");
14181
+ if (workers) {
14182
+ mkdirSync(dirname(wrapper), { recursive: true });
14183
+ writeFileSync(wrapper, WORKERS_CONFIG);
14184
+ args.push("--config", wrapper);
14185
+ if (config) env.VISALJS_VITE_CONFIG = join(root, config);
14186
+ if (mode) env.VISALJS_VITE_MODE = mode;
14187
+ } else if (config) args.push("--config", config);
14188
+ try {
14189
+ await ctx.run(args, { env });
14190
+ } catch (e) {
14191
+ if (!(e instanceof CommandError)) throw e;
14192
+ const cloudflare = /failed to resolve import "(cloudflare:[\w-]+)" from "([^"]+)"/.exec(e.output);
14193
+ if (workers && cloudflare) throw new IntegrationError(`${relative(root, cloudflare[2])} imports ${cloudflare[1]}: Cloudflare Workers' own APIs (bindings: env, KV, D1, …) have no Node equivalent, so visaljs can't build this app for Node`);
14194
+ throw e;
14195
+ } finally {
14196
+ if (workers) rmSync(dirname(wrapper), {
14197
+ recursive: true,
14198
+ force: true
14199
+ });
14200
+ }
14201
+ return workers ? "Built the app for Node with vite build, without its Cloudflare plugin (dist/)" : "Built the app with vite build (dist/)";
14202
+ },
14203
+ prepare(ctx) {
14204
+ const { root } = ctx;
14205
+ if (!existsSync(join(root, MARKER))) throw new IntegrationError(`${root} has no vinext build (no ${MARKER}): run \`vite build\`, or build without --no-framework-build`);
14206
+ if (!BUILD_ENTRIES.some((f) => existsSync(join(root, f)))) throw new IntegrationError(isWorkersProject(root) ? `${root}'s dist/ is a Cloudflare Workers build, which has no Node server: build without --no-framework-build, and visaljs builds the app for Node` : `${root}'s vinext build has no Node server (${BUILD_ENTRIES.join(", ")}): this vinext setup isn't supported yet`);
14207
+ ctx.entrySource = "import { startProdServer } from \"vinext/server/prod-server\";\nawait startProdServer();\n";
14208
+ },
14209
+ bundle(ctx) {
14210
+ const { root } = ctx;
14211
+ const choose = BUILD_ENTRIES.filter((f) => existsSync(join(root, f))).map((f) => `entryPath.replaceAll("\\\\", "/").endsWith(${JSON.stringify("/" + f)}) ? import(${JSON.stringify(join(root, f))}) : `).join("");
14212
+ ctx.rewrite({
14213
+ ...BUILD_IMPORT,
14214
+ replace: `(${choose}Promise.reject(new Error("visaljs: the build has no " + entryPath)))`
14215
+ });
14216
+ const externals = join(root, "dist", "server", "vinext-externals.json");
14217
+ if (existsSync(externals)) for (const name of JSON.parse(readFileSync(externals, "utf8"))) ctx.require(name);
14218
+ },
14219
+ done(ctx) {
14220
+ ctx.note("It serves ./dist under the working directory, as `vinext start` does: run it from the project");
14221
+ if (isWorkersProject(ctx.root)) ctx.note("Built for Node, not Workers: the project's own build still makes its Worker");
14222
+ }
14223
+ }
14224
+ },
14225
+ next,
14226
+ sharp
14227
+ ];
14228
+ /** The entry when the project uses no framework. */
14229
+ const FALLBACK = project;
14230
+ //#endregion
14231
+ //#region cli/src/pipeline/index.ts
14232
+ /** The stages before bundling, for the project in `root` (absolute). `output` is -o's. */
14233
+ async function runPipeline(root, options) {
14234
+ const { flags, report } = options;
14235
+ const project = {
14236
+ root,
14237
+ pkg: packageJson(root),
14238
+ packages: new Set(Object.keys(dependencies(root))),
14239
+ installed: (name) => installed(root, name),
14240
+ flags
14241
+ };
14242
+ const detected = (options.integrations ?? INTEGRATIONS).filter((i) => i.detect(project));
14243
+ const replaced = new Set(detected.flatMap((i) => i.overrides ?? []));
14244
+ const kept = detected.filter((i) => !replaced.has(i.id));
14245
+ const frameworks = kept.filter((i) => i.provides === "entry");
14246
+ if (frameworks.length > 1) throw new IntegrationError(`the project uses ${frameworks.map((f) => f.name).join(" and ")}: visaljs builds one framework's server`);
14247
+ const framework = frameworks[0] ?? options.fallback ?? FALLBACK;
14248
+ if (frameworks.length) report.info(` 🧭 A ${framework.name} app: compiling its production server`);
14249
+ const used = [framework, ...kept.filter((i) => i !== framework)];
14250
+ const open = (fields) => {
14251
+ let closed = false;
14252
+ const guard = () => {
14253
+ if (closed) throw new Error("an integration's context was used after its stage was over");
12539
14254
  };
12540
- const wrapper = join(root, "node_modules", ".visaljs", "vite.config.mjs");
12541
- if (workers) {
12542
- mkdirSync(dirname(wrapper), { recursive: true });
12543
- writeFileSync(wrapper, WORKERS_CONFIG);
12544
- args.push("--config", wrapper);
12545
- if (config) env.VISALJS_VITE_CONFIG = join(root, config);
12546
- if (mode) env.VISALJS_VITE_MODE = mode;
12547
- } else if (config) args.push("--config", config);
12548
- try {
12549
- await promisify(execFile)(process.execPath, args, {
12550
- cwd: root,
12551
- maxBuffer: 64 << 20,
12552
- env
12553
- });
12554
- } catch (e) {
12555
- const { stdout = "", stderr = "" } = e;
12556
- const cloudflare = /failed to resolve import "(cloudflare:[\w-]+)" from "([^"]+)"/.exec(stdout + stderr);
12557
- if (workers && cloudflare) throw new FrameworkError(`${relative(root, cloudflare[2])} imports ${cloudflare[1]}: Cloudflare Workers' own APIs (bindings: env, KV, D1, …) have no Node equivalent, so visaljs can't build this app for Node`);
12558
- throw new FrameworkError(`vite build failed in ${root}:\n${`${stdout}${stderr}`.trimEnd().split("\n").slice(-30).join("\n")}`);
12559
- } finally {
12560
- if (workers) rmSync(dirname(wrapper), {
12561
- recursive: true,
12562
- force: true
12563
- });
14255
+ const context = {
14256
+ ...project,
14257
+ note(text, icon) {
14258
+ guard();
14259
+ report.info(icon ? ` ${icon} ${text}` : ` • ${text}`);
14260
+ },
14261
+ warn(message) {
14262
+ guard();
14263
+ report.warn(message);
14264
+ }
14265
+ };
14266
+ return {
14267
+ ctx: Object.defineProperties(context, Object.getOwnPropertyDescriptors(fields(guard))),
14268
+ close: () => closed = true
14269
+ };
14270
+ };
14271
+ if (!flags.skipFrameworkBuild) for (const i of used) {
14272
+ const hook = i.hooks.frameworkBuild;
14273
+ if (!hook) continue;
14274
+ const { ctx, close } = open((guard) => ({ async run(args, { env } = {}) {
14275
+ guard();
14276
+ try {
14277
+ return await promisify(execFile)(process.execPath, args, {
14278
+ cwd: root,
14279
+ maxBuffer: 64 << 20,
14280
+ env: {
14281
+ ...process.env,
14282
+ FORCE_COLOR: "0",
14283
+ ...env
14284
+ }
14285
+ });
14286
+ } catch (e) {
14287
+ const { stdout = "", stderr = "" } = e;
14288
+ const output = `${stdout}${stderr}`;
14289
+ throw new CommandError(`${[basename(args[0] ?? "").replace(/\.[cm]?js$/, ""), args[1]].filter(Boolean).join(" ")} failed in ${root}:\n${output.trimEnd().split("\n").slice(-30).join("\n")}`, output);
14290
+ }
14291
+ } }));
14292
+ await report.step("🏗️ ", `Building the ${i.name} app`, () => hook(ctx), (what) => what || `Built the ${i.name} app`);
14293
+ close();
14294
+ }
14295
+ const decided = {};
14296
+ const states = /* @__PURE__ */ new Map();
14297
+ for (const i of used) {
14298
+ const hook = i.hooks.prepare;
14299
+ if (!hook) continue;
14300
+ const decide = (guard) => {
14301
+ guard();
14302
+ if (i !== framework) throw new Error(`${i.name} sets the entry or the output, which only the integration that provides the entry sets`);
14303
+ };
14304
+ const { ctx, close } = open((guard) => ({
14305
+ get entryFile() {
14306
+ return decided.entryFile;
14307
+ },
14308
+ set entryFile(file) {
14309
+ decide(guard);
14310
+ decided.entryFile = file;
14311
+ },
14312
+ get entrySource() {
14313
+ return decided.entrySource;
14314
+ },
14315
+ set entrySource(source) {
14316
+ decide(guard);
14317
+ decided.entrySource = source;
14318
+ },
14319
+ get output() {
14320
+ return decided.output;
14321
+ },
14322
+ set output(file) {
14323
+ decide(guard);
14324
+ decided.output = file;
14325
+ }
14326
+ }));
14327
+ states.set(i, await hook(ctx));
14328
+ close();
14329
+ }
14330
+ const entry = decided.entryFile ?? join(root, "visaljs-entry.js");
14331
+ const output = options.output ?? decided.output ?? join(root, basename(root));
14332
+ const rewrites = [];
14333
+ const plugins = [];
14334
+ const requires = [];
14335
+ const ignored = [];
14336
+ for (const i of used) {
14337
+ const hook = i.hooks.bundle;
14338
+ if (!hook) continue;
14339
+ const { ctx, close } = open((guard) => ({
14340
+ entry,
14341
+ output,
14342
+ rewrite(r) {
14343
+ guard();
14344
+ rewrites.push({
14345
+ ...r,
14346
+ by: i.name
14347
+ });
14348
+ },
14349
+ plugin(p) {
14350
+ guard();
14351
+ plugins.push(p);
14352
+ },
14353
+ require(name) {
14354
+ guard();
14355
+ requires.push(name);
14356
+ },
14357
+ ignore(m) {
14358
+ guard();
14359
+ ignored.push(m);
14360
+ }
14361
+ }));
14362
+ await hook(ctx, states.get(i));
14363
+ close();
14364
+ }
14365
+ return {
14366
+ entry,
14367
+ output,
14368
+ extras: {
14369
+ ...decided.entrySource !== void 0 && { source: decided.entrySource },
14370
+ plugins: [...rewritePlugin(root, rewrites), ...plugins],
14371
+ requires,
14372
+ ignored
14373
+ },
14374
+ done(binary) {
14375
+ for (const i of used) {
14376
+ const hook = i.hooks.done;
14377
+ if (!hook) continue;
14378
+ const { ctx, close } = open(() => ({
14379
+ entry,
14380
+ output: binary
14381
+ }));
14382
+ hook(ctx, states.get(i));
14383
+ close();
14384
+ }
12564
14385
  }
12565
- return workers ? "Built the app for Node with vite build, without its Cloudflare plugin (dist/)" : "Built the app with vite build (dist/)";
12566
- },
12567
- entry(root) {
12568
- if (!existsSync(join(root, MARKER))) throw new FrameworkError(`${root} has no vinext build (no ${MARKER}): run \`vite build\`, or build without --no-framework-build`);
12569
- if (!BUILD_ENTRIES.some((f) => existsSync(join(root, f)))) throw new FrameworkError(isWorkersProject(root) ? `${root}'s dist/ is a Cloudflare Workers build, which has no Node server: build without --no-framework-build, and visaljs builds the app for Node` : `${root}'s vinext build has no Node server (${BUILD_ENTRIES.join(", ")}): this vinext setup isn't supported yet`);
12570
- return "import { startProdServer } from \"vinext/server/prod-server\";\nawait startProdServer();\n";
12571
- },
12572
- plugins(root) {
12573
- const choose = BUILD_ENTRIES.filter((f) => existsSync(join(root, f))).map((f) => `entryPath.replaceAll("\\\\", "/").endsWith(${JSON.stringify("/" + f)}) ? import(${JSON.stringify(join(root, f))}) : `).join("");
12574
- return [{
12575
- name: "visaljs-vinext",
12576
- transform: {
12577
- filter: { id: /[\\/]vinext[\\/]dist[\\/]server[\\/]prod-server\.js$/ },
12578
- handler(code) {
12579
- const call = "import(resolveServerEntryImportUrl(entryPath))";
12580
- if (!code.includes(call)) this.error("vinext's prod-server.js no longer imports its build as visaljs expects: this vinext version isn't supported yet");
12581
- return {
12582
- code: code.replace(call, `(${choose}Promise.reject(new Error("visaljs: the build has no " + entryPath)))`),
12583
- map: null
12584
- };
14386
+ };
14387
+ }
14388
+ function rewritePlugin(root, rewrites) {
14389
+ if (!rewrites.length) return [];
14390
+ return [{
14391
+ name: "visaljs-rewrites",
14392
+ transform: {
14393
+ filter: { id: rewrites.map((r) => r.file) },
14394
+ handler(code, id) {
14395
+ let text = code;
14396
+ for (const r of rewrites) {
14397
+ if (!r.file.test(id)) continue;
14398
+ if (typeof r.find === "string" ? !text.includes(r.find) : text.search(r.find) < 0) {
14399
+ if (r.missing === "skip") continue;
14400
+ this.error(`${relative(root, id)} no longer has the code visaljs rewrites (${String(r.find)}): this ${r.by} version isn't supported yet`);
14401
+ }
14402
+ const replace = typeof r.replace === "string" ? () => r.replace : r.replace;
14403
+ text = typeof r.find === "string" ? text.replaceAll(r.find, replace) : text.replace(r.find, replace);
12585
14404
  }
14405
+ return text === code ? null : {
14406
+ code: text,
14407
+ map: null
14408
+ };
12586
14409
  }
12587
- }];
12588
- },
12589
- requires(root) {
12590
- const file = join(root, "dist", "server", "vinext-externals.json");
12591
- return existsSync(file) ? JSON.parse(readFileSync(file, "utf8")) : [];
12592
- },
12593
- notes: (root) => ["It serves ./dist under the working directory, as `vinext start` does: run it from the project", ...isWorkersProject(root) ? ["Built for Node, not Workers: the project's own build still makes its Worker"] : []]
12594
- }, next];
12595
- /** The framework of the project in `root`, or undefined. */
12596
- function detectFramework(root) {
12597
- return FRAMEWORKS.find((f) => f.detect(root));
14410
+ }
14411
+ }];
12598
14412
  }
12599
14413
  //#endregion
12600
14414
  //#region cli/src/regexp.ts
@@ -12708,6 +14522,10 @@ var Report = class {
12708
14522
  this.#write(`${text}\n`);
12709
14523
  if (this.#live && this.#running) this.#draw();
12710
14524
  }
14525
+ /** A line of the report, as `note`, but only when reporting (not --quiet). */
14526
+ info(text) {
14527
+ if (this.#style) this.note(text);
14528
+ }
12711
14529
  /** A warning, after its location (`where`, if any): printed even when quiet, its label in yellow. */
12712
14530
  warn(message, where = "") {
12713
14531
  this.note(`${where}${this.#paint("1;33", "warning:")} ${message}`);
@@ -12763,7 +14581,6 @@ function lineCount(text) {
12763
14581
  const VERSION = "0.0.7";
12764
14582
  /** A program's C splits into files of about this many IR instructions. */
12765
14583
  const PART_SIZE = 2e4;
12766
- /** A TypeScript file, by its extension: the bundler strips its types. */
12767
14584
  const TYPESCRIPT = /\.(?:[cm]?ts|tsx)$/;
12768
14585
  const USAGE = `usage: visaljs build [entry.js | entry.ts | project directory] [options]
12769
14586
  visaljs licenses print the licenses to ship with a binary you distribute
@@ -12785,6 +14602,7 @@ options:
12785
14602
  --debug build without optimization (-O0), keeping symbols and debug info
12786
14603
  --symbols keep symbols and debug info in the optimized build (for profilers such as perf)
12787
14604
  --no-optimize skip the IR optimizer (to compare, or to rule it out)
14605
+ --no-static compile "use static" files as plain TypeScript (to compare)
12788
14606
  -j, --jobs <n> compile at most n C files at once (default: one per processor, memory permitting)
12789
14607
  --embed-source embed the source text: Function.prototype.toString returns it
12790
14608
  --all-builtins install every built-in, not only those the program names
@@ -12818,6 +14636,7 @@ async function main(argv, io) {
12818
14636
  debug: { type: "boolean" },
12819
14637
  symbols: { type: "boolean" },
12820
14638
  "no-optimize": { type: "boolean" },
14639
+ "no-static": { type: "boolean" },
12821
14640
  jobs: {
12822
14641
  type: "string",
12823
14642
  short: "j"
@@ -12875,33 +14694,30 @@ async function main(argv, io) {
12875
14694
  }
12876
14695
  }
12877
14696
  async function build(entry, options, io) {
12878
- let framework;
12879
- let project;
12880
- let source = "";
14697
+ const dump = options["dump-ast"] === true || options["dump-ir"] === true;
14698
+ const style = options.quiet || dump ? void 0 : io.report;
14699
+ const report = new Report(io.stderr, style);
14700
+ const binary = !dump && !options["emit-c"];
14701
+ const hosts = DEFAULT_HOSTS;
14702
+ let primitives = hostPrimitives(hosts);
14703
+ const nodeEnv = options["node-env"] === "" ? void 0 : options["node-env"] ?? "production";
14704
+ let pipeline;
12881
14705
  if (statSync(entry, { throwIfNoEntry: false })?.isDirectory()) {
12882
- const root = resolve(entry);
12883
- const fw = detectFramework(root);
12884
- if (fw) {
12885
- framework = {
12886
- fw,
12887
- root
12888
- };
12889
- entry = join(root, "visaljs-entry.js");
12890
- } else {
12891
- try {
12892
- project = {
12893
- ...projectEntry(root),
12894
- root
12895
- };
12896
- } catch (e) {
12897
- if (!(e instanceof EntryError)) throw e;
12898
- io.stderr(`visaljs: ${entry === "." ? "the current directory" : entry}: ${e.message}: give an entry file\n`);
12899
- return 1;
12900
- }
12901
- entry = project.file;
14706
+ try {
14707
+ pipeline = await runPipeline(resolve(entry), {
14708
+ flags: { skipFrameworkBuild: options["no-framework-build"] === true },
14709
+ output: options.output,
14710
+ report
14711
+ });
14712
+ } catch (e) {
14713
+ if (!(e instanceof IntegrationError)) throw e;
14714
+ io.stderr(e instanceof EntryError ? `visaljs: ${entry === "." ? "the current directory" : entry}: ${e.message}: give an entry file\n` : `visaljs: ${e.message}\n`);
14715
+ return 1;
12902
14716
  }
14717
+ entry = pipeline.entry;
12903
14718
  }
12904
- if (!framework) try {
14719
+ let source = pipeline?.extras.source;
14720
+ if (source === void 0) try {
12905
14721
  source = readFileSync(entry, "utf8");
12906
14722
  } catch (e) {
12907
14723
  io.stderr(`visaljs: cannot read ${entry}: ${e.message}\n`);
@@ -12911,35 +14727,6 @@ async function build(entry, options, io) {
12911
14727
  io.stderr(`visaljs: ${entry} is TypeScript: it's compiled through the bundler, without ${options.script ? "--script" : "--no-bundle"}\n`);
12912
14728
  return 1;
12913
14729
  }
12914
- const dump = options["dump-ast"] === true || options["dump-ir"] === true;
12915
- const style = options.quiet || dump ? void 0 : io.report;
12916
- const report = new Report(io.stderr, style);
12917
- const binary = !dump && !options["emit-c"];
12918
- const hosts = DEFAULT_HOSTS;
12919
- const primitives = hostPrimitives(hosts);
12920
- let frameworkExtras = {};
12921
- const nodeEnv = options["node-env"] === "" ? void 0 : options["node-env"] ?? "production";
12922
- if (project && style) {
12923
- report.note(` 📄 Entry: ${relative(project.root, project.file).replaceAll("\\", "/")} (${project.from})`);
12924
- if (project.ignoredArgs.length) report.note(` • its arguments (${project.ignoredArgs.join(" ")}) aren't built in: give them when you run the binary`);
12925
- }
12926
- if (framework) {
12927
- const { fw, root } = framework;
12928
- if (style) report.note(` 🧭 A ${fw.name} app: compiling its production server`);
12929
- try {
12930
- if (fw.build && options["no-framework-build"] !== true) await report.step("🏗️ ", `Building the ${fw.name} app`, () => fw.build(root), (what) => what);
12931
- source = fw.entry(root);
12932
- } catch (e) {
12933
- if (!(e instanceof FrameworkError)) throw e;
12934
- io.stderr(`visaljs: ${e.message}\n`);
12935
- return 1;
12936
- }
12937
- frameworkExtras = {
12938
- source,
12939
- plugins: fw.plugins?.(root) ?? [],
12940
- requires: fw.requires?.(root) ?? []
12941
- };
12942
- }
12943
14730
  let compilers;
12944
14731
  if (binary) try {
12945
14732
  compilers = await report.step("🔍", "Detecting C compilers", findCompilers, detected);
@@ -12947,122 +14734,295 @@ async function build(entry, options, io) {
12947
14734
  io.stderr(`visaljs: ${e.message}\n`);
12948
14735
  return 1;
12949
14736
  }
12950
- const script = options.script === true;
12951
- const bundled = !script && options["no-bundle"] !== true;
14737
+ const bundled = !(options.script === true) && options["no-bundle"] !== true;
12952
14738
  let map;
14739
+ const typedFiles = /* @__PURE__ */ new Set();
14740
+ let typed;
12953
14741
  if (bundled) try {
12954
14742
  const b = await report.step("📦", "Rolling the modules into a single file", () => bundle(entry, hosts, (message, where) => report.warn(message, where), {
12955
- ...frameworkExtras,
14743
+ ...pipeline?.extras,
12956
14744
  nodeEnv,
12957
- allFeatures: options["all-features"] === true
14745
+ allFeatures: options["all-features"] === true,
14746
+ plugins: [...pipeline?.extras.plugins ?? [], ...options["no-static"] ? [] : [staticImports(typedFiles)]]
12958
14747
  }), (b) => `Rolled ${count(b.modules, "module")} into a single file (${size(Buffer.byteLength(b.code))})` + (b.required.length ? `; for a require() at run time: ${b.required.join(", ")}` : ""));
12959
14748
  source = b.code;
12960
- if (b.map) map = new SourceMap(b.map, displayPath);
14749
+ if (b.map) map = b.map;
14750
+ if (typedFiles.size) {
14751
+ const files = [...typedFiles];
14752
+ typed = await report.step("🧷", "Compiling the typed files", () => buildTypedUnit(files, primitives), (u) => [`Compiled ${count(files.length, "typed file")}: ${count(Object.keys(u.exports).length, "export")}`, ...formatReport(u.report).map((line) => ` • ${line}`)].join("\n"));
14753
+ primitives = {
14754
+ ...primitives,
14755
+ ...typed.exports
14756
+ };
14757
+ }
12961
14758
  } catch (e) {
12962
14759
  if (!(e instanceof BundleError)) throw e;
12963
14760
  io.stderr(`${e.message}\n`);
12964
14761
  return 1;
12965
14762
  }
12966
- const jobs = options.jobs === void 0 ? compileJobs() : Number(options.jobs);
12967
- if (!Number.isInteger(jobs) || jobs < 1) {
14763
+ if (options.jobs !== void 0 && (!Number.isInteger(Number(options.jobs)) || Number(options.jobs) < 1)) {
12968
14764
  io.stderr(`visaljs: --jobs takes a positive whole number, not '${options.jobs}'\n`);
12969
14765
  return 2;
12970
14766
  }
12971
- let files;
14767
+ const emitDir = options["emit-c"];
14768
+ let output = "";
14769
+ if (binary) {
14770
+ output = pipeline?.output ?? options.output ?? basename(entry).replace(/\.(?:[cm]?[jt]s|[jt]sx)$/, "");
14771
+ if (process.platform === "win32" && !extname(output)) output += ".exe";
14772
+ if (resolve(output) === resolve(entry) || statSync(output, { throwIfNoEntry: false })?.isDirectory()) {
14773
+ io.stderr(`visaljs: the binary would be ${displayPath(resolve(output))}, which is ${resolve(output) === resolve(entry) ? "the entry file" : "a directory"}: give another with -o\n`);
14774
+ return 1;
14775
+ }
14776
+ }
14777
+ const dir = emitDir ?? (binary ? scratchDir() : "");
12972
14778
  try {
12973
- const ast = await report.step("🌳", "Parsing the JavaScript", () => script ? parseScript(source, entry) : parseModule(source, map ? "bundle.js" : entry, map, resolve(entry), primitives), () => `Parsed ${count(lineCount(source), "line")} of JavaScript`);
12974
- if (options["dump-ast"]) {
12975
- io.stdout(JSON.stringify(ast, null, 2) + "\n");
14779
+ const typedLink = typed ? {
14780
+ atoms: [...typed.atoms],
14781
+ prefix: "vt_",
14782
+ names: typed.names,
14783
+ calls: typed.calls
14784
+ } : void 0;
14785
+ const c = await compileInWorker({
14786
+ source,
14787
+ entry,
14788
+ map,
14789
+ options,
14790
+ primitives,
14791
+ compilers,
14792
+ dir,
14793
+ emitDir,
14794
+ typed: typedLink
14795
+ }, report);
14796
+ if ("compileError" in c) {
14797
+ io.stderr(`${c.compileError}\n`);
14798
+ return 1;
14799
+ }
14800
+ compilers = c.compilers;
14801
+ if (typeof c.result === "string") {
14802
+ io.stdout(c.result);
12976
14803
  return 0;
12977
14804
  }
12978
- const info = analyze(ast);
12979
- const evalWarn = evalWarning(evalCalls(ast, info), options.verbose === true);
12980
- if (evalWarn) report.warn(evalWarn);
12981
- const literals = regexpLiterals(ast);
12982
- const regexps = literals.length ? await report.step("🧩", "Compiling the regular expressions", () => withCompiler(compilers ??= findCompilers(), report, (cc) => compileRegExps(cc, literals)), () => `Compiled ${count(literals.length, "regular expression")}`) : /* @__PURE__ */ new Map();
12983
- const unsupported = firstUnsupported(ast);
12984
- if (unsupported) throw unsupported;
12985
- const passes = [];
12986
- const ir = await report.step("🔧", "Converting to IR and optimizing", () => optimize(lower(ast, info), options["no-optimize"] ? [] : void 0, passes), (ir) => [`Converted ${count(ir.functions.length + 1, "function")} to IR and optimized them`, ...formatReport(passes).map((line) => ` • ${line}`)].join("\n"));
12987
- if (options["dump-ir"]) {
12988
- io.stdout(printModule(ir));
14805
+ const written = c.result;
14806
+ if (emitDir) {
14807
+ if (bundled) writeFileSync(join(emitDir, "bundle.js"), source);
14808
+ if (typed) writeFileSync(join(emitDir, "typed.c"), typed.c);
14809
+ report.finish("📂", (time) => `Wrote ${written.length > 1 ? `${written.length} files to ${displayPath(resolve(emitDir))}` : displayPath(resolve(emitDir, "program.c"))} in ${time}`);
12989
14810
  return 0;
12990
14811
  }
12991
- const names = programNames(ast);
12992
- const all = options["all-builtins"] === true;
12993
- files = await report.step("📝", "Emitting C code", () => emitProgramFiles(ir, {
12994
- embedSource: options["embed-source"] === true,
12995
- components: (all ? COMPONENTS : reachableComponents(names)).map((c) => c.symbol),
12996
- core: all ? void 0 : namedCore(names),
12997
- regexps,
12998
- hostPrimitives: primitives,
12999
- partSize: Number(process.env.VISALJS_PART_SIZE) || PART_SIZE
13000
- }), (files) => `Emitted ${count(files.reduce((n, f) => n + lineCount(f.text), 0), "line")} of C code (${size(files.reduce((n, f) => n + Buffer.byteLength(f.text), 0))}${files.length > 1 ? `, in ${files.length} files` : ""})`);
13001
- } catch (e) {
13002
- if (!(e instanceof CompileError)) throw e;
13003
- io.stderr(`${e.message}\n`);
13004
- return 1;
13005
- }
13006
- if (options["emit-c"]) {
13007
- mkdirSync(options["emit-c"], { recursive: true });
13008
- for (const f of files) writeFileSync(join(options["emit-c"], f.name), f.text);
13009
- if (bundled) writeFileSync(join(options["emit-c"], "bundle.js"), source);
13010
- report.finish("📂", (time) => `Wrote ${files.length > 1 ? `${files.length} files to ${displayPath(resolve(options["emit-c"]))}` : displayPath(resolve(options["emit-c"], "program.c"))} in ${time}`);
13011
- return 0;
13012
- }
13013
- let output = options.output ?? (framework ? join(framework.root, basename(framework.root)) : project ? join(project.root, project.name) : basename(entry).replace(/\.(?:[cm]?[jt]s|[jt]sx)$/, ""));
13014
- if (process.platform === "win32" && !extname(output)) output += ".exe";
13015
- if (resolve(output) === resolve(entry) || statSync(output, { throwIfNoEntry: false })?.isDirectory()) {
13016
- io.stderr(`visaljs: the binary would be ${displayPath(resolve(output))}, which is ${resolve(output) === resolve(entry) ? "the entry file" : "a directory"}: give another with -o\n`);
13017
- return 1;
13018
- }
13019
- const opt = [...options.debug ? ["-O0"] : ["-O2"], ...gcCheckFlags()];
13020
- const dir = scratchDir();
13021
- try {
13022
- for (const f of files) writeFileSync(join(dir, f.name), f.text);
13023
- const cFiles = files.filter((f) => f.name.endsWith(".c")).map((f) => join(dir, f.name));
13024
- await withCompiler(compilers, report, async (cc) => {
13025
- const tc = {
13026
- cc,
13027
- opt,
13028
- strip: !options.debug && !options.symbols
13029
- };
13030
- const built = [];
13031
- const archives = await report.step("📚", "Preparing the runtime libraries", () => programArchives(tc, hosts, (name) => {
13032
- built.push(`lib${name}`);
13033
- report.relabel(`Building lib${name} with ${describeCompiler(cc)} (first build only: it's cached after)`);
13034
- }), () => built.length ? `Built ${built.join(", ")} (cached for next time)` : "Runtime libraries ready (cached ⚡)");
13035
- const compiling = `Compiling with ${describeCompiler(cc)}`;
13036
- const parallel = cFiles.length > 1 ? `${cFiles.length} files, ${Math.min(jobs, cFiles.length)} at a time` : "";
13037
- await report.step("🔨", parallel ? `${compiling} (${parallel})` : compiling, () => compileProgram(tc, cFiles, output, archives, {
13038
- jobs,
13039
- compiled: (done, total) => report.relabel(done < total ? `${compiling} (${done}/${total} files, ${Math.min(jobs, total)} at a time)` : `Linking with ${describeCompiler(cc)}`)
13040
- }), () => `Compiled the binary with ${describeCompiler(cc)} (${[...opt, ...parallel ? [parallel] : []].join(", ")}, ${tc.strip ? "stripped" : "with debug info"})`);
13041
- });
13042
- } catch (e) {
13043
- io.stderr(`visaljs: C compilation failed: ${e.message}\n`);
13044
- return 1;
14812
+ const cFiles = written.filter((name) => name.endsWith(".c")).map((name) => join(dir, name));
14813
+ if (typed) {
14814
+ writeFileSync(join(dir, "typed.c"), typed.c);
14815
+ cFiles.push(join(dir, "typed.c"));
14816
+ }
14817
+ const opt = [...options.debug ? ["-O0"] : ["-O2"], ...gcCheckFlags()];
14818
+ const strip = !options.debug && !options.symbols;
14819
+ const jobs = options.jobs === void 0 ? compileJobs(strip ? 8e8 : 15e8) : Number(options.jobs);
14820
+ try {
14821
+ await withCompiler(compilers, report, async (cc) => {
14822
+ const tc = {
14823
+ cc,
14824
+ opt,
14825
+ strip
14826
+ };
14827
+ const built = [];
14828
+ const archives = await report.step("📚", "Preparing the runtime libraries", () => programArchives(tc, hosts, (name) => {
14829
+ built.push(`lib${name}`);
14830
+ report.relabel(`Building lib${name} with ${describeCompiler(cc)} (first build only: it's cached after)`);
14831
+ }), () => built.length ? `Built ${built.join(", ")} (cached for next time)` : "Runtime libraries ready (cached ⚡)");
14832
+ const compiling = `Compiling with ${describeCompiler(cc)}`;
14833
+ const parallel = cFiles.length > 1 ? `${cFiles.length} files, ${Math.min(jobs, cFiles.length)} at a time` : "";
14834
+ await report.step("🔨", parallel ? `${compiling} (${parallel})` : compiling, () => compileProgram(tc, cFiles, output, archives, {
14835
+ jobs,
14836
+ compiled: (done, total) => report.relabel(done < total ? `${compiling} (${done}/${total} files, ${Math.min(jobs, total)} at a time)` : `Linking with ${describeCompiler(cc)}`)
14837
+ }), () => `Compiled the binary with ${describeCompiler(cc)} (${[...opt, ...parallel ? [parallel] : []].join(", ")}, ${tc.strip ? "stripped" : "with debug info"})`);
14838
+ });
14839
+ } catch (e) {
14840
+ io.stderr(`visaljs: C compilation failed: ${e.message}\n`);
14841
+ return 1;
14842
+ }
13045
14843
  } finally {
13046
- rmSync(dir, {
14844
+ if (binary) rmSync(dir, {
13047
14845
  recursive: true,
13048
14846
  force: true
13049
14847
  });
13050
14848
  }
13051
14849
  const bytes = statSync(output).size;
13052
14850
  report.finish("🎉", (time) => `Built ${displayPath(resolve(output))} in ${time}: ${size(bytes)}, ${sizeQuip(bytes)}`);
13053
- if (framework?.fw.notes && style) for (const note of framework.fw.notes(framework.root, resolve(output))) report.note(` • ${note}`);
14851
+ pipeline?.done(resolve(output));
13054
14852
  return 0;
13055
14853
  }
13056
- /** "Detected GCC 13.3.0", else the compilers numbered in the order they're tried, under the
13057
- * step's line (indented to its text). */
14854
+ const WORKER = "visaljs-compile";
14855
+ /** compileToC in a worker thread, so its heap (gigabytes for a large program) goes back to the
14856
+ * system for the C compilers when it ends. The worker is this module (bundled: cli.js). */
14857
+ function compileInWorker(job, report) {
14858
+ const heap = Math.floor(getHeapStatistics().heap_size_limit / 2 ** 20);
14859
+ const worker = new Worker(new URL(import.meta.url), {
14860
+ workerData: { [WORKER]: job },
14861
+ resourceLimits: { maxOldGenerationSizeMb: heap }
14862
+ });
14863
+ return new Promise((resolve, reject) => {
14864
+ let step;
14865
+ let result;
14866
+ worker.on("message", (m) => {
14867
+ switch (m.kind) {
14868
+ case "step":
14869
+ report.step(m.icon, m.label, () => new Promise((done, fail) => step = {
14870
+ done,
14871
+ fail: () => fail(new Error(m.label))
14872
+ }), (text) => text).catch(() => {});
14873
+ break;
14874
+ case "done":
14875
+ step?.done(m.text);
14876
+ break;
14877
+ case "fail":
14878
+ step?.fail();
14879
+ break;
14880
+ case "relabel":
14881
+ report.relabel(m.label);
14882
+ break;
14883
+ case "note":
14884
+ report.note(m.text);
14885
+ break;
14886
+ case "warn":
14887
+ report.warn(m.message, m.where);
14888
+ break;
14889
+ case "result":
14890
+ result = m.result;
14891
+ worker.terminate();
14892
+ break;
14893
+ case "crash": reject(Object.assign(new Error(m.stack.split("\n")[0]), { stack: m.stack }));
14894
+ }
14895
+ });
14896
+ worker.on("error", reject);
14897
+ worker.on("exit", () => result ? resolve(result) : reject(/* @__PURE__ */ new Error("the compiler's worker stopped without a result")));
14898
+ });
14899
+ }
14900
+ async function runCompileJob(job) {
14901
+ const post = (m) => parentPort.postMessage(m);
14902
+ const reporter = {
14903
+ async step(icon, label, run, done) {
14904
+ post({
14905
+ kind: "step",
14906
+ icon,
14907
+ label
14908
+ });
14909
+ try {
14910
+ const result = await run();
14911
+ post({
14912
+ kind: "done",
14913
+ text: done(result)
14914
+ });
14915
+ return result;
14916
+ } catch (e) {
14917
+ post({ kind: "fail" });
14918
+ throw e;
14919
+ }
14920
+ },
14921
+ relabel: (label) => post({
14922
+ kind: "relabel",
14923
+ label
14924
+ }),
14925
+ note: (text) => post({
14926
+ kind: "note",
14927
+ text
14928
+ }),
14929
+ warn: (message, where = "") => post({
14930
+ kind: "warn",
14931
+ message,
14932
+ where
14933
+ })
14934
+ };
14935
+ let compilers = job.compilers;
14936
+ try {
14937
+ const map = job.map === void 0 ? void 0 : new SourceMap(job.map, displayPath);
14938
+ post({
14939
+ kind: "result",
14940
+ result: {
14941
+ result: await compileToC(job.source, job.entry, map, job.options, reporter, job.primitives, () => compilers ??= findCompilers(), job.typed, (f) => {
14942
+ if (job.emitDir) mkdirSync(job.emitDir, { recursive: true });
14943
+ writeFileSync(join(job.dir, f.name), f.text);
14944
+ }),
14945
+ compilers
14946
+ }
14947
+ });
14948
+ } catch (e) {
14949
+ if (e instanceof CompileError) post({
14950
+ kind: "result",
14951
+ result: { compileError: e.message }
14952
+ });
14953
+ else post({
14954
+ kind: "crash",
14955
+ stack: e.stack ?? String(e)
14956
+ });
14957
+ }
14958
+ }
14959
+ if (!isMainThread && workerData?.[WORKER]) await runCompileJob(workerData[WORKER]);
14960
+ /** The program's C files' names, or the text --dump-ast or --dump-ir prints. Its own function, as
14961
+ * is toIR, so the syntax tree and then the IR become garbage: a large program's take gigabytes. */
14962
+ async function compileToC(source, entry, map, options, report, primitives, compilers, typed, write) {
14963
+ const program = await toIR(source, entry, map, options, report, primitives, compilers, typed?.calls);
14964
+ if (typeof program === "string") return program;
14965
+ const { ir, regexps } = program;
14966
+ const names = /* @__PURE__ */ new Set([...program.names, ...typed?.names ?? []]);
14967
+ const all = options["all-builtins"] === true;
14968
+ return (await report.step("📝", "Emitting C code", () => {
14969
+ const files = emitProgramFiles(ir, {
14970
+ embedSource: options["embed-source"] === true,
14971
+ components: (all ? COMPONENTS : reachableComponents(names)).map((c) => c.symbol),
14972
+ core: all ? void 0 : namedCore(names),
14973
+ regexps,
14974
+ hostPrimitives: primitives,
14975
+ ...typed ? { harness: {
14976
+ atoms: new Map(typed.atoms),
14977
+ register: `${typed.prefix}register`,
14978
+ scripts: [typed.prefix]
14979
+ } } : {},
14980
+ partSize: Number(process.env.VISALJS_PART_SIZE) || PART_SIZE
14981
+ });
14982
+ const out = {
14983
+ names: [],
14984
+ lines: 0,
14985
+ bytes: 0
14986
+ };
14987
+ for (const f of files) {
14988
+ write(f);
14989
+ out.names.push(f.name);
14990
+ out.lines += lineCount(f.text);
14991
+ out.bytes += Buffer.byteLength(f.text);
14992
+ }
14993
+ return out;
14994
+ }, ({ names, lines, bytes }) => `Emitted ${count(lines, "line")} of C code (${size(bytes)}${names.length > 1 ? `, in ${names.length} files` : ""})`)).names;
14995
+ }
14996
+ async function toIR(source, entry, map, options, report, primitives, compilers, typedCalls) {
14997
+ const ast = await report.step("🌳", "Parsing the JavaScript", () => options.script ? parseScript(source, entry) : parseModule(source, map ? "bundle.js" : entry, map, resolve(entry), primitives), () => `Parsed ${count(lineCount(source), "line")} of JavaScript`);
14998
+ if (options["dump-ast"]) return JSON.stringify(ast, null, 2) + "\n";
14999
+ const info = analyze(ast);
15000
+ const evalWarn = evalWarning(evalCalls(ast, info), options.verbose === true);
15001
+ if (evalWarn) report.warn(evalWarn);
15002
+ const literals = regexpLiterals(ast);
15003
+ const regexps = literals.length ? await report.step("🧩", "Compiling the regular expressions", () => withCompiler(compilers(), report, (cc) => compileRegExps(cc, literals)), () => `Compiled ${count(literals.length, "regular expression")}`) : /* @__PURE__ */ new Map();
15004
+ const unsupported = firstUnsupported(ast);
15005
+ if (unsupported) throw unsupported;
15006
+ const passes = [];
15007
+ const ir = await report.step("🔧", "Converting to IR and optimizing", () => {
15008
+ const lowered = lower(ast, info);
15009
+ if (typedCalls) annotateTypedCalls(lowered, typedCalls);
15010
+ return optimize(lowered, options["no-optimize"] ? [] : void 0, passes);
15011
+ }, (ir) => [`Converted ${count(ir.functions.length + 1, "function")} to IR and optimized them`, ...formatReport(passes).map((line) => ` • ${line}`)].join("\n"));
15012
+ if (options["dump-ir"]) return printModule(ir);
15013
+ return {
15014
+ ir,
15015
+ names: programNames(ast),
15016
+ regexps
15017
+ };
15018
+ }
13058
15019
  function detected(compilers) {
13059
15020
  const names = compilers.map(describeCompiler);
13060
15021
  if (names.length === 1) return `Detected ${names[0]}${process.env.CC ? " (from $CC)" : ""}`;
13061
15022
  return [`Detected ${count(names.length, "C compiler")} (tried in this order, falling back to the next if one fails):`, ...names.map((name, i) => ` ${i + 1}. ${name}`)].join("\n");
13062
15023
  }
13063
- /** Runs `run` with each compiler in turn until one succeeds. A failure that isn't the
13064
- * program's (not a CompileError) is noted and moves on to the next; the compilers that failed
13065
- * are dropped, so later steps start with the one that worked. The last failure is rethrown. */
15024
+ /** Runs `run` with each compiler until one succeeds. A failure that isn't the program's (not a
15025
+ * CompileError) moves on to the next, dropping the failed one from `compilers` for later steps. */
13066
15026
  async function withCompiler(compilers, report, run) {
13067
15027
  for (;;) {
13068
15028
  const cc = compilers[0];
@@ -13077,8 +15037,7 @@ async function withCompiler(compilers, report, run) {
13077
15037
  }
13078
15038
  }
13079
15039
  }
13080
- /** How diagnostics and #line name an original file: relative to the working directory when
13081
- * it's inside it, else absolute. Code the bundler generated keeps its own name. */
15040
+ /** How diagnostics and #line name an original file. */
13082
15041
  function displayPath(source) {
13083
15042
  if (source.startsWith("\0")) return source.slice(1);
13084
15043
  const rel = relative(process.cwd(), source);
@@ -13086,14 +15045,16 @@ function displayPath(source) {
13086
15045
  }
13087
15046
  //#endregion
13088
15047
  //#region cli/src/cli.ts
13089
- const tty = process.stderr.isTTY === true;
13090
- process.exitCode = await main(process.argv.slice(2), {
13091
- stdout: (text) => process.stdout.write(text),
13092
- stderr: (text) => process.stderr.write(text),
13093
- report: {
13094
- live: tty,
13095
- color: tty && !process.env.NO_COLOR && process.env.TERM !== "dumb"
13096
- }
13097
- });
15048
+ if (isMainThread) {
15049
+ const tty = process.stderr.isTTY === true;
15050
+ process.exitCode = await main(process.argv.slice(2), {
15051
+ stdout: (text) => process.stdout.write(text),
15052
+ stderr: (text) => process.stderr.write(text),
15053
+ report: {
15054
+ live: tty,
15055
+ color: tty && !process.env.NO_COLOR && process.env.TERM !== "dumb"
15056
+ }
15057
+ });
15058
+ }
13098
15059
  //#endregion
13099
15060
  export {};