@scriptc/compiler 0.0.0 → 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (206) hide show
  1. package/LICENSE +202 -0
  2. package/ambient/package.json +3 -0
  3. package/ambient/scriptc-node-fallback.d.ts +3001 -0
  4. package/ambient/scriptc-overrides.d.ts +95 -0
  5. package/ambient/scriptc.d.ts +74 -0
  6. package/dist/backend/cc.d.ts +169 -0
  7. package/dist/backend/cc.js +897 -0
  8. package/dist/backend/cc.js.map +1 -0
  9. package/dist/backend/emission/emit-async.d.ts +145 -0
  10. package/dist/backend/emission/emit-async.js +996 -0
  11. package/dist/backend/emission/emit-async.js.map +1 -0
  12. package/dist/backend/emission/emit-exprs.d.ts +3 -0
  13. package/dist/backend/emission/emit-exprs.js +5947 -0
  14. package/dist/backend/emission/emit-exprs.js.map +1 -0
  15. package/dist/backend/emission/emit-island.d.ts +45 -0
  16. package/dist/backend/emission/emit-island.js +271 -0
  17. package/dist/backend/emission/emit-island.js.map +1 -0
  18. package/dist/backend/emission/emit-shapes.d.ts +142 -0
  19. package/dist/backend/emission/emit-shapes.js +575 -0
  20. package/dist/backend/emission/emit-shapes.js.map +1 -0
  21. package/dist/backend/emission/emit-stmts.d.ts +78 -0
  22. package/dist/backend/emission/emit-stmts.js +960 -0
  23. package/dist/backend/emission/emit-stmts.js.map +1 -0
  24. package/dist/backend/emission/emit-types.d.ts +65 -0
  25. package/dist/backend/emission/emit-types.js +652 -0
  26. package/dist/backend/emission/emit-types.js.map +1 -0
  27. package/dist/backend/emission/emit-walkers.d.ts +154 -0
  28. package/dist/backend/emission/emit-walkers.js +1587 -0
  29. package/dist/backend/emission/emit-walkers.js.map +1 -0
  30. package/dist/backend/emission/emitter.d.ts +452 -0
  31. package/dist/backend/emission/emitter.js +1249 -0
  32. package/dist/backend/emission/emitter.js.map +1 -0
  33. package/dist/backend/emission/may-throw.d.ts +15 -0
  34. package/dist/backend/emission/may-throw.js +250 -0
  35. package/dist/backend/emission/may-throw.js.map +1 -0
  36. package/dist/backend/llvm/blocks.d.ts +21 -0
  37. package/dist/backend/llvm/blocks.js +68 -0
  38. package/dist/backend/llvm/blocks.js.map +1 -0
  39. package/dist/backend/llvm/classes.d.ts +84 -0
  40. package/dist/backend/llvm/classes.js +438 -0
  41. package/dist/backend/llvm/classes.js.map +1 -0
  42. package/dist/backend/llvm/dyn.d.ts +123 -0
  43. package/dist/backend/llvm/dyn.js +2624 -0
  44. package/dist/backend/llvm/dyn.js.map +1 -0
  45. package/dist/backend/llvm/emitter.d.ts +3 -0
  46. package/dist/backend/llvm/emitter.js +10087 -0
  47. package/dist/backend/llvm/emitter.js.map +1 -0
  48. package/dist/backend/llvm/shapes.d.ts +78 -0
  49. package/dist/backend/llvm/shapes.js +754 -0
  50. package/dist/backend/llvm/shapes.js.map +1 -0
  51. package/dist/backend/llvm/unsupported.d.ts +6 -0
  52. package/dist/backend/llvm/unsupported.js +12 -0
  53. package/dist/backend/llvm/unsupported.js.map +1 -0
  54. package/dist/backend/llvm/walkers.d.ts +58 -0
  55. package/dist/backend/llvm/walkers.js +793 -0
  56. package/dist/backend/llvm/walkers.js.map +1 -0
  57. package/dist/backend/mangle.d.ts +124 -0
  58. package/dist/backend/mangle.js +232 -0
  59. package/dist/backend/mangle.js.map +1 -0
  60. package/dist/coverage/report.d.ts +64 -0
  61. package/dist/coverage/report.js +226 -0
  62. package/dist/coverage/report.js.map +1 -0
  63. package/dist/diagnostics/diagnostic.d.ts +140 -0
  64. package/dist/diagnostics/diagnostic.js +458 -0
  65. package/dist/diagnostics/diagnostic.js.map +1 -0
  66. package/dist/diagnostics/render.d.ts +11 -0
  67. package/dist/diagnostics/render.js +58 -0
  68. package/dist/diagnostics/render.js.map +1 -0
  69. package/dist/frontend/cjs-lexer.d.ts +20 -0
  70. package/dist/frontend/cjs-lexer.js +813 -0
  71. package/dist/frontend/cjs-lexer.js.map +1 -0
  72. package/dist/frontend/lowering/http2-constants.d.ts +1 -0
  73. package/dist/frontend/lowering/http2-constants.js +251 -0
  74. package/dist/frontend/lowering/http2-constants.js.map +1 -0
  75. package/dist/frontend/lowering/lib-boundary.d.ts +6 -0
  76. package/dist/frontend/lowering/lib-boundary.js +143 -0
  77. package/dist/frontend/lowering/lib-boundary.js.map +1 -0
  78. package/dist/frontend/lowering/lower-assert.d.ts +18 -0
  79. package/dist/frontend/lowering/lower-assert.js +1269 -0
  80. package/dist/frontend/lowering/lower-assert.js.map +1 -0
  81. package/dist/frontend/lowering/lower-builtins.d.ts +511 -0
  82. package/dist/frontend/lowering/lower-builtins.js +5249 -0
  83. package/dist/frontend/lowering/lower-builtins.js.map +1 -0
  84. package/dist/frontend/lowering/lower-calls.d.ts +581 -0
  85. package/dist/frontend/lowering/lower-calls.js +6281 -0
  86. package/dist/frontend/lowering/lower-calls.js.map +1 -0
  87. package/dist/frontend/lowering/lower-classes.d.ts +727 -0
  88. package/dist/frontend/lowering/lower-classes.js +4701 -0
  89. package/dist/frontend/lowering/lower-classes.js.map +1 -0
  90. package/dist/frontend/lowering/lower-comptime.d.ts +53 -0
  91. package/dist/frontend/lowering/lower-comptime.js +244 -0
  92. package/dist/frontend/lowering/lower-comptime.js.map +1 -0
  93. package/dist/frontend/lowering/lower-containers.d.ts +483 -0
  94. package/dist/frontend/lowering/lower-containers.js +6281 -0
  95. package/dist/frontend/lowering/lower-containers.js.map +1 -0
  96. package/dist/frontend/lowering/lower-dgram.d.ts +20 -0
  97. package/dist/frontend/lowering/lower-dgram.js +319 -0
  98. package/dist/frontend/lowering/lower-dgram.js.map +1 -0
  99. package/dist/frontend/lowering/lower-emitter.d.ts +10 -0
  100. package/dist/frontend/lowering/lower-emitter.js +688 -0
  101. package/dist/frontend/lowering/lower-emitter.js.map +1 -0
  102. package/dist/frontend/lowering/lower-enums.d.ts +22 -0
  103. package/dist/frontend/lowering/lower-enums.js +235 -0
  104. package/dist/frontend/lowering/lower-enums.js.map +1 -0
  105. package/dist/frontend/lowering/lower-expando.d.ts +35 -0
  106. package/dist/frontend/lowering/lower-expando.js +276 -0
  107. package/dist/frontend/lowering/lower-expando.js.map +1 -0
  108. package/dist/frontend/lowering/lower-exprs.d.ts +524 -0
  109. package/dist/frontend/lowering/lower-exprs.js +8099 -0
  110. package/dist/frontend/lowering/lower-exprs.js.map +1 -0
  111. package/dist/frontend/lowering/lower-generators.d.ts +50 -0
  112. package/dist/frontend/lowering/lower-generators.js +421 -0
  113. package/dist/frontend/lowering/lower-generators.js.map +1 -0
  114. package/dist/frontend/lowering/lower-inspect.d.ts +20 -0
  115. package/dist/frontend/lowering/lower-inspect.js +1069 -0
  116. package/dist/frontend/lowering/lower-inspect.js.map +1 -0
  117. package/dist/frontend/lowering/lower-island.d.ts +108 -0
  118. package/dist/frontend/lowering/lower-island.js +650 -0
  119. package/dist/frontend/lowering/lower-island.js.map +1 -0
  120. package/dist/frontend/lowering/lower-mixins.d.ts +96 -0
  121. package/dist/frontend/lowering/lower-mixins.js +534 -0
  122. package/dist/frontend/lowering/lower-mixins.js.map +1 -0
  123. package/dist/frontend/lowering/lower-modules.d.ts +135 -0
  124. package/dist/frontend/lowering/lower-modules.js +1613 -0
  125. package/dist/frontend/lowering/lower-modules.js.map +1 -0
  126. package/dist/frontend/lowering/lower-namespaces.d.ts +168 -0
  127. package/dist/frontend/lowering/lower-namespaces.js +706 -0
  128. package/dist/frontend/lowering/lower-namespaces.js.map +1 -0
  129. package/dist/frontend/lowering/lower-server.d.ts +90 -0
  130. package/dist/frontend/lowering/lower-server.js +3471 -0
  131. package/dist/frontend/lowering/lower-server.js.map +1 -0
  132. package/dist/frontend/lowering/lower-stmts.d.ts +361 -0
  133. package/dist/frontend/lowering/lower-stmts.js +5341 -0
  134. package/dist/frontend/lowering/lower-stmts.js.map +1 -0
  135. package/dist/frontend/lowering/lower-stream.d.ts +88 -0
  136. package/dist/frontend/lowering/lower-stream.js +1533 -0
  137. package/dist/frontend/lowering/lower-stream.js.map +1 -0
  138. package/dist/frontend/lowering/lower-test.d.ts +27 -0
  139. package/dist/frontend/lowering/lower-test.js +353 -0
  140. package/dist/frontend/lowering/lower-test.js.map +1 -0
  141. package/dist/frontend/lowering/lowerer.d.ts +1748 -0
  142. package/dist/frontend/lowering/lowerer.js +6256 -0
  143. package/dist/frontend/lowering/lowerer.js.map +1 -0
  144. package/dist/frontend/lowering/surfaces.d.ts +272 -0
  145. package/dist/frontend/lowering/surfaces.js +1096 -0
  146. package/dist/frontend/lowering/surfaces.js.map +1 -0
  147. package/dist/frontend/npm-static.d.ts +55 -0
  148. package/dist/frontend/npm-static.js +300 -0
  149. package/dist/frontend/npm-static.js.map +1 -0
  150. package/dist/frontend/npm.d.ts +330 -0
  151. package/dist/frontend/npm.js +1259 -0
  152. package/dist/frontend/npm.js.map +1 -0
  153. package/dist/frontend/program.d.ts +186 -0
  154. package/dist/frontend/program.js +2172 -0
  155. package/dist/frontend/program.js.map +1 -0
  156. package/dist/frontend/provenance-registry.d.ts +48 -0
  157. package/dist/frontend/provenance-registry.js +87 -0
  158. package/dist/frontend/provenance-registry.js.map +1 -0
  159. package/dist/frontend/provenance.d.ts +6 -0
  160. package/dist/frontend/provenance.js +459 -0
  161. package/dist/frontend/provenance.js.map +1 -0
  162. package/dist/frontend/resolve.d.ts +56 -0
  163. package/dist/frontend/resolve.js +682 -0
  164. package/dist/frontend/resolve.js.map +1 -0
  165. package/dist/frontend/shared.d.ts +60 -0
  166. package/dist/frontend/shared.js +232 -0
  167. package/dist/frontend/shared.js.map +1 -0
  168. package/dist/frontend/ts7/adapter.d.ts +7 -0
  169. package/dist/frontend/ts7/adapter.js +54 -0
  170. package/dist/frontend/ts7/adapter.js.map +1 -0
  171. package/dist/frontend/ts7/ast.d.ts +50 -0
  172. package/dist/frontend/ts7/ast.js +211 -0
  173. package/dist/frontend/ts7/ast.js.map +1 -0
  174. package/dist/frontend/ts7/census-check.d.ts +1 -0
  175. package/dist/frontend/ts7/census-check.js +12 -0
  176. package/dist/frontend/ts7/census-check.js.map +1 -0
  177. package/dist/frontend/ts7/checker.d.ts +140 -0
  178. package/dist/frontend/ts7/checker.js +544 -0
  179. package/dist/frontend/ts7/checker.js.map +1 -0
  180. package/dist/frontend/ts7/enums.d.ts +18 -0
  181. package/dist/frontend/ts7/enums.js +43 -0
  182. package/dist/frontend/ts7/enums.js.map +1 -0
  183. package/dist/frontend/ts7/program.d.ts +99 -0
  184. package/dist/frontend/ts7/program.js +278 -0
  185. package/dist/frontend/ts7/program.js.map +1 -0
  186. package/dist/frontend/ts7/world-check.d.ts +3 -0
  187. package/dist/frontend/ts7/world-check.js +34 -0
  188. package/dist/frontend/ts7/world-check.js.map +1 -0
  189. package/dist/frontend/types.d.ts +205 -0
  190. package/dist/frontend/types.js +2485 -0
  191. package/dist/frontend/types.js.map +1 -0
  192. package/dist/index.d.ts +85 -0
  193. package/dist/index.js +424 -0
  194. package/dist/index.js.map +1 -0
  195. package/dist/ir/nodes.d.ts +4334 -0
  196. package/dist/ir/nodes.js +1906 -0
  197. package/dist/ir/nodes.js.map +1 -0
  198. package/dist/ir/serialize.d.ts +4 -0
  199. package/dist/ir/serialize.js +34 -0
  200. package/dist/ir/serialize.js.map +1 -0
  201. package/dist/ir/validate.d.ts +36 -0
  202. package/dist/ir/validate.js +4884 -0
  203. package/dist/ir/validate.js.map +1 -0
  204. package/package.json +30 -6
  205. package/README.md +0 -3
  206. package/index.js +0 -2
@@ -0,0 +1,1069 @@
1
+ /* The node:util inspect/format lowering (a spoke module like
2
+ * lower-assert.ts): STATIC util.inspect — one synthesized traversal
3
+ * helper per argument type (the deepStrictEqual precedent, interned per
4
+ * typeKey), producing Node v24's byte-exact DEFAULT-options rendering —
5
+ * and util.format/formatWithOptions over compile-time format strings.
6
+ *
7
+ * The division of labor: the synthesized helpers own the TRAVERSAL the
8
+ * static type dictates (fields, elements, entries, union arms, depth
9
+ * bookkeeping); scr_inspect.c owns everything the type cannot know —
10
+ * scalar formatting (-0, the quoting ladder, string splitting), Buffer
11
+ * hex, and the layout engine (frames, break-length, grid grouping).
12
+ * Checked-dynamic values render ENTIRELY in the runtime (the dyn DOM
13
+ * carries its own shape); island `any` values render their scalar kinds
14
+ * and throw catchably on composites (the runtime tag is all there is).
15
+ *
16
+ * Options fence honestly, never approximate: depth takes any numeric
17
+ * literal (null/Infinity only over non-recursive types — a cycle would
18
+ * render forever), colors:false / compact:3 / breakLength:80 are the
19
+ * accepted no-op spellings, and everything else names itself in the
20
+ * diagnostic. Values whose rendering would need runtime type identity
21
+ * the static type lacks fence the same way: class hierarchies (a
22
+ * subclass's name and extra fields are dynamic), function values inside
23
+ * composites (no runtime name exists), Uint8Array-vs-Buffer ambiguity.
24
+ *
25
+ * Known-divergence corners (SEMANTICS.md): errors render the STACKLESS
26
+ * `[Name: message]` form (compiled binaries carry no JS stack), cyclic
27
+ * runtime data renders depth placeholders instead of `<ref *1>`/
28
+ * `[Circular *1]`, and record/class property order follows declaration
29
+ * order (SEMANTICS.md 36's existing stance). */
30
+ import * as ts from "../ts7/adapter.js";
31
+ import { isJsSourceFile } from "../program.js";
32
+ import { BOOL, DYN, F64, RUNTIME_ERROR_CLASSES, STRING, shapeHasAccessorSlots, typeKey } from "../../ir/nodes.js";
33
+ import { pureReemittable } from "./lower-exprs.js";
34
+ /* ── IR construction shorthand ───────────────────────────────────────── */
35
+ const str = (value, loc) => ({ kind: "strLit", value, type: STRING, loc });
36
+ const num = (value, loc) => ({ kind: "numLit", value, type: F64, loc });
37
+ const boolLit = (value, loc) => ({ kind: "boolLit", value, type: BOOL, loc });
38
+ function concatAll(parts, loc) {
39
+ if (parts.length === 0)
40
+ return str("", loc);
41
+ let out = parts[0];
42
+ for (let i = 1; i < parts.length; i++) {
43
+ out = { kind: "strConcat", left: out, right: parts[i], type: STRING, loc };
44
+ }
45
+ return out;
46
+ }
47
+ /* ── property-name rendering (compile-time keys) ─────────────────────── */
48
+ /** strEscape's quote ladder over a COMPILE-TIME string (record field
49
+ * names, format-string chunks never pass through here — only keys).
50
+ * Mirrors scr_inspect.c's insp_quote_into byte-for-byte. */
51
+ export function inspectQuote(s) {
52
+ const hasSingle = s.includes("'");
53
+ const quote = !hasSingle ? "'" : !s.includes('"') ? '"' : !s.includes("`") && !s.includes("${") ? "`" : "'";
54
+ let body = "";
55
+ for (let i = 0; i < s.length; i++) {
56
+ const c = s.charCodeAt(i);
57
+ if (c < 0x20) {
58
+ const meta = ["\\b", "\\t", "\\n", undefined, "\\f", "\\r"][c - 8];
59
+ body += c >= 8 && c <= 13 && meta !== undefined ? meta : `\\x${c.toString(16).toUpperCase().padStart(2, "0")}`;
60
+ }
61
+ else if (c === 0x27 && quote === "'") {
62
+ body += "\\'";
63
+ }
64
+ else if (c === 0x5c) {
65
+ body += "\\\\";
66
+ }
67
+ else if (c >= 0x7f && c <= 0x9f) {
68
+ body += `\\x${c.toString(16).toUpperCase().padStart(2, "0")}`;
69
+ }
70
+ else if (c >= 0xd800 && c <= 0xdfff) {
71
+ const next = i + 1 < s.length ? s.charCodeAt(i + 1) : 0;
72
+ if (c <= 0xdbff && next >= 0xdc00 && next <= 0xdfff) {
73
+ body += s[i] + s[i + 1];
74
+ i++;
75
+ }
76
+ else {
77
+ body += `\\u${c.toString(16)}`;
78
+ }
79
+ }
80
+ else {
81
+ body += s[i];
82
+ }
83
+ }
84
+ return quote + body + quote;
85
+ }
86
+ /** formatProperty's key rendering: bare identifiers stay bare
87
+ * ('__proto__' excepted), everything else quotes. */
88
+ function inspectKey(name) {
89
+ if (name === "__proto__")
90
+ return "['__proto__']";
91
+ return /^[a-zA-Z_][a-zA-Z_0-9]*$/.test(name) ? name : inspectQuote(name);
92
+ }
93
+ /* ── type support ────────────────────────────────────────────────────── */
94
+ /** The error-hierarchy gate: `info`'s instances render through the
95
+ * runtime's name/message/code slots, which subclasses inherit — sound
96
+ * only when NO descendant adds declared fields (they would be own
97
+ * properties Node prints). */
98
+ function errorRenderable(info) {
99
+ // The builtin classes' name/message/code live in runtime slots (the
100
+ // ScrError prefix insp.error reads), not IR fields; only USER-declared
101
+ // fields are own properties the runtime rendering would miss.
102
+ if (!info.builtinError && info.def.fields.length > 0)
103
+ return false;
104
+ return info.subclasses.every((s) => errorRenderable(s));
105
+ }
106
+ function isErrorClass(L, className) {
107
+ if (RUNTIME_ERROR_CLASSES.has(className))
108
+ return true;
109
+ return L.isSubclassOf(className, "%Error");
110
+ }
111
+ /** inspUnsupportedReason: the deepUnsupportedReason twin. Fences name the
112
+ * FIRST unsupported constituent. `visiting` terminates recursive shapes
113
+ * and records their existence (the depth-null gate). */
114
+ function inspectSupport(L, t, visiting, out) {
115
+ switch (t.kind) {
116
+ case "f64":
117
+ case "string":
118
+ case "bool":
119
+ case "undefinedT":
120
+ case "nullT":
121
+ case "regex":
122
+ case "symbol":
123
+ case "dyn":
124
+ case "jsval":
125
+ return null;
126
+ case "array":
127
+ return inspectSupport(L, t.elem, visiting, out);
128
+ case "bytes":
129
+ return "typed arrays inside composites have no inspect lowering yet (Buffer's <Buffer ..> form vs Uint8Array's is not recorded in the static type here)";
130
+ case "record": {
131
+ if (visiting.has(t.shapeId)) {
132
+ out.recursive = true;
133
+ return null;
134
+ }
135
+ visiting.add(t.shapeId);
136
+ const shape = L.shapes.get(t.shapeId);
137
+ if (!shape)
138
+ return "this record shape has no inspect lowering";
139
+ // Accessor-carrying shapes: Node prints the accessor names as
140
+ // `x: [Getter]` / `[Setter]` / `[Getter/Setter]` in insertion order
141
+ // — a position the static field walk does not track (accessor slots
142
+ // live outside declaredOrder), so the render fences by name.
143
+ if (shapeHasAccessorSlots(shape)) {
144
+ return "shapes carrying get/set accessor properties have no inspect lowering yet (Node prints the accessor names as [Getter]/[Setter] in insertion order — read the properties explicitly)";
145
+ }
146
+ if (shape.indexValue !== undefined) {
147
+ // PURE index-signature shapes (Record<string, V> — no declared
148
+ // fields) render like the dyn DOM's objects: a runtime key walk
149
+ // (recordOvfKeys), each key through insp.key's bare-or-quoted
150
+ // ladder. Hybrids keep the fence — JS orders integer keys first
151
+ // ACROSS the declared and overflow stores, an interleave the
152
+ // declared-then-overflow walk cannot honor.
153
+ if (shape.fields.length > 0) {
154
+ return "index-signature records with declared fields have no inspect lowering yet (JS interleaves integer keys across the declared and dynamic key sets)";
155
+ }
156
+ const why = inspectSupport(L, shape.indexValue, visiting, out);
157
+ if (why !== null)
158
+ return why;
159
+ visiting.delete(t.shapeId);
160
+ return null;
161
+ }
162
+ for (const f of shape.fields) {
163
+ const why = inspectSupport(L, f.type, visiting, out);
164
+ if (why !== null)
165
+ return why;
166
+ }
167
+ visiting.delete(t.shapeId);
168
+ return null;
169
+ }
170
+ case "map": {
171
+ const keyWhy = inspectSupport(L, t.key, visiting, out);
172
+ if (keyWhy !== null)
173
+ return keyWhy;
174
+ return inspectSupport(L, t.value, visiting, out);
175
+ }
176
+ case "set":
177
+ return inspectSupport(L, t.elem, visiting, out);
178
+ case "union": {
179
+ const def = L.unions.get(t.unionId);
180
+ if (!def)
181
+ return "this union has no inspect lowering";
182
+ if (visiting.has(t.unionId)) {
183
+ out.recursive = true;
184
+ return null;
185
+ }
186
+ visiting.add(t.unionId);
187
+ for (const arm of def.arms) {
188
+ const why = inspectSupport(L, arm, visiting, out);
189
+ if (why !== null)
190
+ return why;
191
+ }
192
+ visiting.delete(t.unionId);
193
+ return null;
194
+ }
195
+ case "func":
196
+ return "function values have no inspect lowering (no runtime name exists — Node prints '[Function: name]'; pass the function's declared identifier directly to util.inspect for the baked form)";
197
+ case "object": {
198
+ const info = L.classes.get(t.className);
199
+ if (!info)
200
+ return `class '${t.className}' has no inspect lowering`;
201
+ if (isErrorClass(L, t.className)) {
202
+ return errorRenderable(info)
203
+ ? null
204
+ : "error subclasses with declared fields have no inspect lowering yet (the extra own properties are runtime-dynamic)";
205
+ }
206
+ if (info.subclasses.length > 0) {
207
+ return `inspect of '${t.className}' values is not lowered (the class has subclasses — the runtime value's constructor name and fields are dynamic)`;
208
+ }
209
+ if (visiting.has(t.className)) {
210
+ out.recursive = true;
211
+ return null;
212
+ }
213
+ visiting.add(t.className);
214
+ for (const f of info.def.fields) {
215
+ const why = inspectSupport(L, f.type, visiting, out);
216
+ if (why !== null)
217
+ return why;
218
+ }
219
+ visiting.delete(t.className);
220
+ return null;
221
+ }
222
+ default:
223
+ return `'${L.fmt(t)}' values have no inspect lowering`;
224
+ }
225
+ }
226
+ /* ── the per-type rendering expression ───────────────────────────────── */
227
+ /** True when a runtime value of `t` at index expr `v` answers
228
+ * `typeof v === 'number'` — the grid-grouping order flag. */
229
+ function isNumberFlag(L, t, v, loc) {
230
+ if (t.kind === "f64")
231
+ return boolLit(true, loc);
232
+ if (t.kind === "union") {
233
+ const def = L.unions.get(t.unionId);
234
+ const tag = def ? def.arms.findIndex((a) => a.kind === "f64") : -1;
235
+ if (tag >= 0) {
236
+ return { kind: "unionIsTag", unionId: t.unionId, tag, negated: false, value: v(), type: BOOL, loc };
237
+ }
238
+ }
239
+ return boolLit(false, loc);
240
+ }
241
+ /** The rendering of one value of type `t` at runtime depth `recurse`
242
+ * with the depth budget `depth` — a direct scalar libCall or a call of
243
+ * the interned per-type helper. */
244
+ function inspectExpr(L, t, value, recurse, depth, loc) {
245
+ switch (t.kind) {
246
+ case "f64":
247
+ return { kind: "libCall", fn: "insp.f64", args: [value], type: STRING, loc };
248
+ case "string":
249
+ return { kind: "libCall", fn: "insp.str", args: [value], type: STRING, loc };
250
+ case "bool":
251
+ return { kind: "toString", operand: value, type: STRING, loc };
252
+ case "undefinedT":
253
+ return str("undefined", loc);
254
+ case "nullT":
255
+ return str("null", loc);
256
+ case "regex":
257
+ return { kind: "libCall", fn: "insp.regex", args: [value], type: STRING, loc };
258
+ case "symbol":
259
+ // inspect(sym) IS Symbol.prototype.toString's text ("Symbol(foo)")
260
+ // — Node prints it unquoted at every depth.
261
+ return { kind: "libCall", fn: "sym.toString", args: [value], type: STRING, loc };
262
+ case "bytes":
263
+ return { kind: "libCall", fn: "insp.buffer", args: [value], type: STRING, loc };
264
+ case "dyn":
265
+ return { kind: "libCall", fn: "insp.dyn", args: [value, recurse, depth], type: STRING, loc };
266
+ case "jsval":
267
+ return { kind: "libCall", fn: "insp.jsval", args: [value, recurse, depth], type: STRING, loc };
268
+ case "object":
269
+ if (isErrorClass(L, t.className)) {
270
+ return { kind: "libCall", fn: "insp.error", args: [value, recurse, depth], type: STRING, loc };
271
+ }
272
+ return { kind: "call", callee: inspectHelper(L, t, loc), args: [value, recurse, depth], type: STRING, loc };
273
+ default:
274
+ return { kind: "call", callee: inspectHelper(L, t, loc), args: [value, recurse, depth], type: STRING, loc };
275
+ }
276
+ }
277
+ /* ── the interned per-type helper ────────────────────────────────────── */
278
+ /** `%util.insp.<n>(v, r, d): string` — formatValue over the static type:
279
+ * empty composites answer their literals (before the depth check, Node's
280
+ * order), the depth budget answers `[Array]`/`[Object]`/`[Map]`/
281
+ * `[Set]`/`[ClassName]`, and non-empty composites drive the runtime
282
+ * frame engine (begin / entry / end). Registered in the cache BEFORE the
283
+ * body builds, so recursive shapes call themselves by name. */
284
+ function inspectHelper(L, t, loc) {
285
+ const key = `insp:${typeKey(t)}`;
286
+ const existing = L.inspectHelpers.get(key);
287
+ if (existing)
288
+ return existing;
289
+ const name = `%util.insp.${L.inspectHelpers.size}`;
290
+ L.inspectHelpers.set(key, name);
291
+ const ref = (localId, type) => ({ kind: "varRef", localId, type, loc });
292
+ const v = () => ref("v.0", t);
293
+ const r = () => ref("r.0", F64);
294
+ const d = () => ref("d.0", F64);
295
+ const rPlus1 = () => ({ kind: "bin", op: "+", left: r(), right: num(1, loc), type: F64, loc });
296
+ const ret = (value) => ({ kind: "return", value, loc });
297
+ const exprStmt = (expr) => ({ kind: "exprStmt", expr, loc });
298
+ const begin = () => exprStmt({ kind: "libCall", fn: "insp.begin", args: [rPlus1()], type: { kind: "void" }, loc });
299
+ const entry = (s, isNum) => exprStmt({ kind: "libCall", fn: "insp.entry", args: [s, isNum], type: { kind: "void" }, loc });
300
+ const end = (base, b0, b1, arrayExtras, trailingMore) => ({
301
+ kind: "libCall",
302
+ fn: "insp.end",
303
+ args: [base, b0, b1, rPlus1(), boolLit(arrayExtras, loc), trailingMore],
304
+ type: STRING,
305
+ loc,
306
+ });
307
+ /** if (r > d) return "<placeholder>"; */
308
+ const depthGate = (placeholder) => ({
309
+ kind: "if",
310
+ cond: { kind: "bin", op: ">", left: r(), right: d(), type: BOOL, loc },
311
+ then: [ret(str(placeholder, loc))],
312
+ else_: null,
313
+ loc,
314
+ });
315
+ const child = (elemT, value) => inspectExpr(L, elemT, value, rPlus1(), d(), loc);
316
+ const locals = [
317
+ { id: "v.0", name: "v", type: t, mutable: false },
318
+ { id: "r.0", name: "r", type: F64, mutable: false },
319
+ { id: "d.0", name: "d", type: F64, mutable: false },
320
+ ];
321
+ let body;
322
+ switch (t.kind) {
323
+ case "array": {
324
+ const len = () => ({ kind: "arrIntrinsic", method: "length", receiver: v(), args: [], type: F64, loc });
325
+ const at = (i) => ({ kind: "arrayGet", arr: v(), index: i, type: t.elem, loc });
326
+ locals.push({ id: "n.0", name: "n", type: F64, mutable: false }, { id: "s.0", name: "s", type: F64, mutable: false }, { id: "i.0", name: "i", type: F64, mutable: true });
327
+ const n = () => ref("n.0", F64);
328
+ const shown = () => ref("s.0", F64);
329
+ const i = () => ref("i.0", F64);
330
+ const hasMore = () => ({ kind: "bin", op: ">", left: n(), right: num(100, loc), type: BOOL, loc });
331
+ body = [
332
+ { kind: "varDecl", localId: "n.0", init: len(), loc },
333
+ {
334
+ kind: "if",
335
+ cond: { kind: "bin", op: "===", left: n(), right: num(0, loc), type: BOOL, loc },
336
+ then: [ret(str("[]", loc))],
337
+ else_: null,
338
+ loc,
339
+ },
340
+ depthGate("[Array]"),
341
+ { kind: "varDecl", localId: "s.0", init: { kind: "ternary", cond: hasMore(), then: num(100, loc), else_: n(), type: F64, loc }, loc },
342
+ begin(),
343
+ {
344
+ kind: "for",
345
+ init: { kind: "varDecl", localId: "i.0", init: num(0, loc), loc },
346
+ cond: { kind: "bin", op: "<", left: i(), right: shown(), type: BOOL, loc },
347
+ update: { kind: "assign", localId: "i.0", value: { kind: "bin", op: "+", left: i(), right: num(1, loc), type: F64, loc }, loc },
348
+ body: [entry(child(t.elem, at(i())), isNumberFlag(L, t.elem, () => at(i()), loc))],
349
+ loc,
350
+ },
351
+ {
352
+ kind: "if",
353
+ cond: hasMore(),
354
+ then: [
355
+ entry({
356
+ kind: "libCall",
357
+ fn: "insp.moreItems",
358
+ args: [{ kind: "bin", op: "-", left: n(), right: num(100, loc), type: F64, loc }],
359
+ type: STRING,
360
+ loc,
361
+ }, isNumberFlag(L, t.elem, () => at(num(100, loc)), loc)),
362
+ ],
363
+ else_: null,
364
+ loc,
365
+ },
366
+ ret(end(str("", loc), str("[", loc), str("]", loc), true, hasMore())),
367
+ ];
368
+ break;
369
+ }
370
+ case "record": {
371
+ const shape = L.shapes.get(t.shapeId);
372
+ if (!shape)
373
+ throw new Error(`inspect of unknown shape ${t.shapeId}`);
374
+ const get = (field, type) => ({ kind: "recordGet", obj: v(), shapeId: t.shapeId, field, type, loc });
375
+ if (shape.tuple) {
376
+ // Tuples ARE arrays to Node: bracket form, array-extras grouping.
377
+ if (shape.fields.length === 0) {
378
+ body = [ret(str("[]", loc))];
379
+ break;
380
+ }
381
+ body = [depthGate("[Array]"), begin()];
382
+ for (const f of shape.fields) {
383
+ body.push(entry(child(f.type, get(f.name, f.type)), isNumberFlag(L, f.type, () => get(f.name, f.type), loc)));
384
+ }
385
+ body.push(ret(end(str("", loc), str("[", loc), str("]", loc), true, boolLit(false, loc))));
386
+ break;
387
+ }
388
+ if (shape.indexValue && shape.fields.length === 0) {
389
+ // The PURE index-signature shape (Record<string, V>): a runtime
390
+ // key walk in insertion order (recordOvfKeys — SEMANTICS.md 36's
391
+ // key-order stance), each key rendered through insp.key's
392
+ // bare-or-quoted ladder, each value read back through the keyed
393
+ // get. No 100-entry cap: Node truncates arrays and iterables,
394
+ // never plain objects.
395
+ const iv = shape.indexValue;
396
+ const ksT = { kind: "array", elem: STRING };
397
+ locals.push({ id: "ks.0", name: "ks", type: ksT, mutable: false }, { id: "n.0", name: "n", type: F64, mutable: false }, { id: "i.0", name: "i", type: F64, mutable: true }, { id: "k.0", name: "k", type: STRING, mutable: false });
398
+ const ks = () => ref("ks.0", ksT);
399
+ const n = () => ref("n.0", F64);
400
+ const i = () => ref("i.0", F64);
401
+ const k = () => ref("k.0", STRING);
402
+ const keyedRead = () => ({ kind: "recordKeyGet", obj: v(), shapeId: t.shapeId, key: k(), overflowOnly: true, type: iv, loc });
403
+ body = [
404
+ { kind: "varDecl", localId: "ks.0", init: { kind: "recordOvfKeys", obj: v(), shapeId: t.shapeId, type: ksT, loc }, loc },
405
+ { kind: "varDecl", localId: "n.0", init: { kind: "arrIntrinsic", method: "length", receiver: ks(), args: [], type: F64, loc }, loc },
406
+ {
407
+ kind: "if",
408
+ cond: { kind: "bin", op: "===", left: n(), right: num(0, loc), type: BOOL, loc },
409
+ then: [ret(str("{}", loc))],
410
+ else_: null,
411
+ loc,
412
+ },
413
+ depthGate("[Object]"),
414
+ begin(),
415
+ {
416
+ kind: "for",
417
+ init: { kind: "varDecl", localId: "i.0", init: num(0, loc), loc },
418
+ cond: { kind: "bin", op: "<", left: i(), right: n(), type: BOOL, loc },
419
+ update: { kind: "assign", localId: "i.0", value: { kind: "bin", op: "+", left: i(), right: num(1, loc), type: F64, loc }, loc },
420
+ body: [
421
+ { kind: "varDecl", localId: "k.0", init: { kind: "arrayGet", arr: ks(), index: i(), type: STRING, loc }, loc },
422
+ entry(concatAll([
423
+ { kind: "libCall", fn: "insp.key", args: [k()], type: STRING, loc },
424
+ str(": ", loc),
425
+ child(iv, keyedRead()),
426
+ ], loc), boolLit(false, loc)),
427
+ ],
428
+ loc,
429
+ },
430
+ ret(end(str("", loc), str("{", loc), str("}", loc), false, boolLit(false, loc))),
431
+ ];
432
+ break;
433
+ }
434
+ if (shape.fields.length === 0) {
435
+ body = [ret(str("{}", loc))];
436
+ break;
437
+ }
438
+ // Object.keys' declared order (SEMANTICS.md 36's stance).
439
+ const order = shape.declaredOrder ?? shape.fields.map((f) => f.name);
440
+ const byName = new Map(shape.fields.map((f) => [f.name, f.type]));
441
+ body = [depthGate("[Object]"), begin()];
442
+ for (const fname of order) {
443
+ const ft = byName.get(fname);
444
+ if (!ft)
445
+ continue;
446
+ body.push(entry(concatAll([str(`${inspectKey(fname)}: `, loc), child(ft, get(fname, ft))], loc), boolLit(false, loc)));
447
+ }
448
+ body.push(ret(end(str("", loc), str("{", loc), str("}", loc), false, boolLit(false, loc))));
449
+ break;
450
+ }
451
+ case "map":
452
+ case "set": {
453
+ const isMap = t.kind === "map";
454
+ const mi = (method, args, type) => isMap
455
+ ? { kind: "mapIntrinsic", method: method, receiver: v(), args, type, loc }
456
+ : { kind: "setIntrinsic", method: method, receiver: v(), args, type, loc };
457
+ locals.push({ id: "n.0", name: "n", type: F64, mutable: false }, { id: "i.0", name: "i", type: F64, mutable: true }, { id: "c.0", name: "c", type: F64, mutable: true });
458
+ const n = () => ref("n.0", F64);
459
+ const i = () => ref("i.0", F64);
460
+ const c = () => ref("c.0", F64);
461
+ const label = isMap ? "Map" : "Set";
462
+ const keyT = isMap ? t.key : t.elem;
463
+ const hasMore = () => ({ kind: "bin", op: ">", left: n(), right: num(100, loc), type: BOOL, loc });
464
+ const entryValue = () => {
465
+ const k = child(keyT, mi("iterKey", [i()], keyT));
466
+ if (!isMap)
467
+ return k;
468
+ const valueT = t.value;
469
+ return concatAll([k, str(" => ", loc), child(valueT, mi("iterValue", [i()], valueT))], loc);
470
+ };
471
+ body = [
472
+ { kind: "varDecl", localId: "n.0", init: mi("size", [], F64), loc },
473
+ {
474
+ kind: "if",
475
+ cond: { kind: "bin", op: "===", left: n(), right: num(0, loc), type: BOOL, loc },
476
+ then: [ret(str(`${label}(0) {}`, loc))],
477
+ else_: null,
478
+ loc,
479
+ },
480
+ depthGate(`[${label}]`),
481
+ begin(),
482
+ { kind: "varDecl", localId: "c.0", init: num(0, loc), loc },
483
+ {
484
+ kind: "for",
485
+ init: { kind: "varDecl", localId: "i.0", init: num(0, loc), loc },
486
+ cond: { kind: "bin", op: "<", left: i(), right: mi("iterCount", [], F64), type: BOOL, loc },
487
+ update: { kind: "assign", localId: "i.0", value: { kind: "bin", op: "+", left: i(), right: num(1, loc), type: F64, loc }, loc },
488
+ body: [
489
+ {
490
+ kind: "if",
491
+ cond: { kind: "unary", op: "!", operand: mi("iterLive", [i()], BOOL), type: BOOL, loc },
492
+ then: [{ kind: "continue", loc }],
493
+ else_: null,
494
+ loc,
495
+ },
496
+ {
497
+ kind: "if",
498
+ cond: { kind: "bin", op: "===", left: c(), right: num(100, loc), type: BOOL, loc },
499
+ then: [{ kind: "break", loc }],
500
+ else_: null,
501
+ loc,
502
+ },
503
+ entry(entryValue(), boolLit(false, loc)),
504
+ { kind: "assign", localId: "c.0", value: { kind: "bin", op: "+", left: c(), right: num(1, loc), type: F64, loc }, loc },
505
+ ],
506
+ loc,
507
+ },
508
+ {
509
+ kind: "if",
510
+ cond: hasMore(),
511
+ then: [
512
+ entry({
513
+ kind: "libCall",
514
+ fn: "insp.moreItems",
515
+ args: [{ kind: "bin", op: "-", left: n(), right: num(100, loc), type: F64, loc }],
516
+ type: STRING,
517
+ loc,
518
+ }, boolLit(false, loc)),
519
+ ],
520
+ else_: null,
521
+ loc,
522
+ },
523
+ ret(end(str("", loc), concatAll([str(`${label}(`, loc), { kind: "toString", operand: n(), type: STRING, loc }, str(") {", loc)], loc), str("}", loc), false, hasMore())),
524
+ ];
525
+ break;
526
+ }
527
+ case "union": {
528
+ const def = L.unions.get(t.unionId);
529
+ if (!def)
530
+ throw new Error(`inspect of unknown union ${t.unionId}`);
531
+ body = [];
532
+ def.arms.forEach((arm, tag) => {
533
+ // The union wrapper is not a nesting level: arms render at the
534
+ // SAME recursion depth (formatValue dispatches on the value).
535
+ const narrowed = { kind: "unionNarrow", unionId: t.unionId, tag, value: v(), type: arm, loc };
536
+ body.push({
537
+ kind: "if",
538
+ cond: { kind: "unionIsTag", unionId: t.unionId, tag, negated: false, value: v(), type: BOOL, loc },
539
+ then: [ret(inspectExpr(L, arm, narrowed, r(), d(), loc))],
540
+ else_: null,
541
+ loc,
542
+ });
543
+ });
544
+ body.push(ret(str("", loc))); // unreachable: some arm always matches
545
+ break;
546
+ }
547
+ case "object": {
548
+ const info = L.classes.get(t.className);
549
+ if (!info)
550
+ throw new Error(`inspect of unknown class ${t.className}`);
551
+ // Node prints the class's OWN name — the declaration's, never the
552
+ // IR name's module qualifier (`m0.Timer` is the frontend's spelling
553
+ // for a class declared in a non-entry module).
554
+ const display = info.decl?.name?.text ?? info.def.name.replace(/^%/, "");
555
+ if (info.def.fields.length === 0) {
556
+ body = [ret(str(`${display} {}`, loc))];
557
+ break;
558
+ }
559
+ const get = (field, type) => ({ kind: "fieldGet", obj: v(), className: t.className, field, type, loc });
560
+ body = [depthGate(`[${display}]`), begin()];
561
+ // def.fields carries layout order: the base chain first, then own —
562
+ // exactly the own-property insertion order of a constructor that
563
+ // assigns in declaration order (SEMANTICS.md 36's stance). SYMBOL-
564
+ // keyed fields render LAST ([[OwnPropertyKeys]] lists all string
565
+ // keys before all symbol keys — Node's inspect order) and their
566
+ // layout name IS Node's key spelling (`Symbol(limit)`), printed
567
+ // verbatim, never quoted.
568
+ const symNames = new Set(info.symbolFields?.values() ?? []);
569
+ const ordered = [
570
+ ...info.def.fields.filter((f) => !symNames.has(f.name)),
571
+ ...info.def.fields.filter((f) => symNames.has(f.name)),
572
+ ];
573
+ for (const f of ordered) {
574
+ const key = symNames.has(f.name) ? f.name : inspectKey(f.name);
575
+ body.push(entry(concatAll([str(`${key}: `, loc), child(f.type, get(f.name, f.type))], loc), boolLit(false, loc)));
576
+ }
577
+ body.push(ret(end(str("", loc), str(`${display} {`, loc), str("}", loc), false, boolLit(false, loc))));
578
+ break;
579
+ }
580
+ default:
581
+ throw new Error(`inspect helper over unexpected type ${typeKey(t)}`);
582
+ }
583
+ L.liftedFns.push({
584
+ name,
585
+ params: [
586
+ { localId: "v.0", name: "v", type: t },
587
+ { localId: "r.0", name: "r", type: F64 },
588
+ { localId: "d.0", name: "d", type: F64 },
589
+ ],
590
+ returnType: STRING,
591
+ locals,
592
+ body,
593
+ loc,
594
+ });
595
+ return name;
596
+ }
597
+ /* ── the callsite: options, direct function/class names ──────────────── */
598
+ /** The parsed options literal: the resolved depth (numeric; Infinity for
599
+ * `null`), with every non-default knob fenced by name. */
600
+ function parseInspectOptions(L, node, recursive) {
601
+ if (!node || (ts.isIdentifier(node) && node.text === "undefined"))
602
+ return 2;
603
+ if (!ts.isObjectLiteralExpression(node)) {
604
+ L.noLowering("util.inspect with a non-literal options argument", node, "the options must be an object literal ({ depth, colors: false, ... }) so the knobs resolve at compile time");
605
+ }
606
+ let depth = 2;
607
+ for (const prop of node.properties) {
608
+ if (!ts.isPropertyAssignment(prop) || !ts.isIdentifier(prop.name)) {
609
+ L.noLowering("util.inspect options in this form", prop, "plain `key: literal` entries are the lowered options");
610
+ }
611
+ const key = prop.name.text;
612
+ const value = prop.initializer;
613
+ const numeric = () => {
614
+ if (ts.isNumericLiteral(value))
615
+ return Number(value.text);
616
+ if (ts.isPrefixUnaryExpression(value) && value.operator === ts.SyntaxKind.MinusToken && ts.isNumericLiteral(value.operand)) {
617
+ return -Number(value.operand.text);
618
+ }
619
+ return null;
620
+ };
621
+ if (key === "depth") {
622
+ if (value.kind === ts.SyntaxKind.NullKeyword || (ts.isIdentifier(value) && value.text === "Infinity")) {
623
+ if (recursive) {
624
+ L.noLowering("util.inspect with unbounded depth over a recursive type", value, "cyclic runtime data would render forever — pass a numeric depth");
625
+ }
626
+ depth = Infinity;
627
+ }
628
+ else if (!(ts.isIdentifier(value) && value.text === "undefined")) {
629
+ const n = numeric();
630
+ if (n === null) {
631
+ L.noLowering("util.inspect with a non-literal depth option", value, "depth must be a numeric literal (or null)");
632
+ }
633
+ depth = n;
634
+ }
635
+ }
636
+ else if (key === "colors") {
637
+ if (value.kind !== ts.SyntaxKind.FalseKeyword) {
638
+ L.noLowering("util.inspect with colors: true", value, "ANSI styling has no lowering — only colors: false");
639
+ }
640
+ }
641
+ else if (key === "compact") {
642
+ if (numeric() !== 3) {
643
+ L.noLowering("util.inspect with a non-default compact option", value, "only the default compact: 3 is lowered");
644
+ }
645
+ }
646
+ else if (key === "breakLength") {
647
+ if (numeric() !== 80) {
648
+ L.noLowering("util.inspect with a non-default breakLength option", value, "only the default breakLength: 80 is lowered");
649
+ }
650
+ }
651
+ else {
652
+ L.noLowering(`util.inspect with the '${key}' option`, prop, "the lowered options are depth (numeric literal or null), colors: false, compact: 3, and breakLength: 80");
653
+ }
654
+ }
655
+ return depth;
656
+ }
657
+ /** `util.inspect(fn)` / `util.inspect(Cls)` over a DIRECT identifier
658
+ * naming a declared function or class: the name is compile-time truth,
659
+ * so `[Function: name]` / `[class X extends Y]` bake as literals. Null
660
+ * when the identifier is not such a declaration. */
661
+ function directCallableInspect(L, node, loc) {
662
+ if (!ts.isIdentifier(node))
663
+ return null;
664
+ const sym = L.resolveValueSymbol(node);
665
+ if (!sym)
666
+ return null;
667
+ const cls = L.classBySymbol.get(sym) ?? L.builtinErrorInfoOf(sym);
668
+ // A rebindable decorated name is not compile-time truth — the binding
669
+ // holds the decoration result; no literal folds.
670
+ if (cls?.classDecorators?.valueGlobalId !== undefined)
671
+ return null;
672
+ if (cls) {
673
+ if (cls.builtinError) {
674
+ // Node's Error constructors are native functions, not classes.
675
+ return str(`[Function: ${cls.def.name.replace(/^%/, "")}]`, loc);
676
+ }
677
+ const base = cls.base ? ` extends ${cls.base.def.name.replace(/^%/, "")}` : "";
678
+ return str(`[class ${cls.def.name}${base}]`, loc);
679
+ }
680
+ const decl = L.checker.declarationsOf(sym)[0];
681
+ if (!decl)
682
+ return null;
683
+ if (ts.isFunctionDeclaration(decl) && decl.name) {
684
+ return str(`[Function: ${decl.name.text}]`, loc);
685
+ }
686
+ if (ts.isVariableDeclaration(decl) &&
687
+ ts.isIdentifier(decl.name) &&
688
+ decl.initializer &&
689
+ (ts.isArrowFunction(decl.initializer) || ts.isFunctionExpression(decl.initializer))) {
690
+ // JS name inference: the binding names the function value — unless
691
+ // the function expression carries its OWN name, which wins.
692
+ const own = ts.isFunctionExpression(decl.initializer) ? decl.initializer.name?.text : undefined;
693
+ return str(`[Function: ${own ?? decl.name.text}]`, loc);
694
+ }
695
+ return null;
696
+ }
697
+ /** util.inspect(value[, options]) — the spoke's inspect entry. */
698
+ function lowerInspectCall(L, expr, loc) {
699
+ if (expr.arguments.length < 1 || expr.arguments.length > 2) {
700
+ L.noLowering(`util.inspect with ${expr.arguments.length} arguments`, expr, "the supported forms are inspect(value) and inspect(value, options)");
701
+ }
702
+ const valueNode = expr.arguments[0];
703
+ // Inline function/class literals and direct declared names render as
704
+ // their baked forms; other function-typed values fence below.
705
+ const direct = directCallableInspect(L, valueNode, loc);
706
+ if (direct) {
707
+ parseInspectOptions(L, expr.arguments[1], false);
708
+ return direct;
709
+ }
710
+ if (ts.isArrowFunction(valueNode) || ts.isFunctionExpression(valueNode)) {
711
+ parseInspectOptions(L, expr.arguments[1], false);
712
+ return str(valueNode.kind === ts.SyntaxKind.FunctionExpression && valueNode.name ? `[Function: ${valueNode.name.text}]` : "[Function (anonymous)]", loc);
713
+ }
714
+ const value = L.lowerExpr(valueNode);
715
+ if (value.type.kind === "bytes") {
716
+ // One runtime representation serves Buffer AND Uint8Array; their
717
+ // renderings differ (<Buffer aa> vs Uint8Array(1) [ 170 ]), so the
718
+ // TOP-LEVEL argument's checker type picks — and only Buffer lowers.
719
+ // Nested bytes fence in inspectSupport (no checker type survives
720
+ // into shapes).
721
+ const tname = L.checker.typeToString(L.checker.getBaseTypeOfLiteralType(L.typeOf(valueNode)));
722
+ const isBuffer = tname === "Buffer" || tname === "NonSharedBuffer" || tname.startsWith("Buffer<") || tname.startsWith("NonSharedBuffer<");
723
+ if (value.type.elem !== "u8" || !isBuffer) {
724
+ L.noLowering(`util.inspect of '${tname}' values`, valueNode, "Buffer's <Buffer ..> form is the lowered typed-array rendering; other typed arrays fence");
725
+ }
726
+ const bufDepth = parseInspectOptions(L, expr.arguments[1], false);
727
+ void bufDepth; // Buffers render fully at any depth (custom inspect)
728
+ return inspectExpr(L, value.type, value, num(0, loc), num(2, loc), loc);
729
+ }
730
+ const walk = { recursive: false };
731
+ const reason = inspectSupport(L, value.type, new Set(), walk);
732
+ if (reason !== null) {
733
+ L.noLowering(`util.inspect of '${L.fmt(value.type)}' values`, valueNode, reason);
734
+ }
735
+ const depth = parseInspectOptions(L, expr.arguments[1], walk.recursive);
736
+ return inspectExpr(L, value.type, value, num(0, loc), num(depth, loc), loc);
737
+ }
738
+ /* ── util.format / util.formatWithOptions ────────────────────────────── */
739
+ /** One argument as format's %s conversion (Node: numbers via
740
+ * formatNumber, strings verbatim, objects through inspect at the given
741
+ * depth). Null = fence with the given reason. */
742
+ function formatSArg(L, node, depth, loc) {
743
+ const value = L.lowerExpr(node);
744
+ const t = value.type;
745
+ if (t.kind === "string")
746
+ return value;
747
+ if (t.kind === "f64")
748
+ return { kind: "libCall", fn: "insp.f64", args: [value], type: STRING, loc };
749
+ if (t.kind === "bool")
750
+ return { kind: "toString", operand: value, type: STRING, loc };
751
+ if (t.kind === "undefinedT")
752
+ return str("undefined", loc);
753
+ if (t.kind === "nullT")
754
+ return str("null", loc);
755
+ // %s of a symbol prints inspect's text ("Symbol(foo)") — String(sym)'s
756
+ // answer too, one runtime call either way.
757
+ if (t.kind === "symbol")
758
+ return { kind: "libCall", fn: "sym.toString", args: [value], type: STRING, loc };
759
+ if (t.kind === "dyn") {
760
+ return { kind: "libCall", fn: "insp.dynS", args: [value, num(depth, loc)], type: STRING, loc };
761
+ }
762
+ if (t.kind === "object" && !isErrorClass(L, t.className)) {
763
+ // hasBuiltInToString: a class with its OWN toString goes through
764
+ // String(arg) in Node, not inspect — call it explicitly instead.
765
+ for (let info = L.classes.get(t.className) ?? null; info; info = info.base) {
766
+ if (info.methods.has("toString")) {
767
+ L.noLowering("util.format %s over a class with its own toString", node, "Node calls the override (String(arg)) — call it explicitly");
768
+ }
769
+ }
770
+ }
771
+ if (t.kind === "bytes") {
772
+ L.noLowering(`util.format %s of typed-array values`, node, "pass util.inspect(buf) explicitly");
773
+ }
774
+ const walk = { recursive: false };
775
+ const reason = inspectSupport(L, t, new Set(), walk);
776
+ if (reason !== null)
777
+ L.noLowering(`util.format %s of '${L.fmt(t)}' values`, node, reason);
778
+ return inspectExpr(L, t, value, num(0, loc), num(depth, loc), loc);
779
+ }
780
+ /** %O (depth 2) / %o (showHidden, depth 4) — inspect with the spec's
781
+ * option deltas; %o fences over arrays (showHidden adds their hidden
782
+ * [length] entry, which has no lowering). */
783
+ function formatOArg(L, node, depth, loc) {
784
+ const value = L.lowerExpr(node);
785
+ if (value.type.kind === "bytes") {
786
+ L.noLowering(`util.format %${depth === 4 ? "o" : "O"} of typed-array values`, node, "pass util.inspect(buf) explicitly");
787
+ }
788
+ const walk = { recursive: false };
789
+ const reason = inspectSupport(L, value.type, new Set(), walk);
790
+ if (reason !== null)
791
+ L.noLowering(`util.format %${depth === 4 ? "o" : "O"} of '${L.fmt(value.type)}' values`, node, reason);
792
+ if (depth === 4 && typeTreeHasArray(L, value.type, new Set())) {
793
+ L.noLowering("util.format %o over arrays", node, "%o renders with showHidden (arrays gain a hidden [length] entry) — use %O for the default rendering");
794
+ }
795
+ return inspectExpr(L, value.type, value, num(0, loc), num(depth, loc), loc);
796
+ }
797
+ /** util.format(...) / util.formatWithOptions({}, ...): the compile-time
798
+ * %-substitution over a literal format string (formatWithOptionsInternal
799
+ * ported exactly — the arg cursor, the args-exhausted guard, %c's
800
+ * consume-and-drop), with per-static-type conversions. Non-literal
801
+ * format strings lower only in the substitution-free shapes. */
802
+ export function lowerFormatCall(L, expr, loc, withOptions) {
803
+ const argNodes = [...expr.arguments];
804
+ if (withOptions) {
805
+ const opts = argNodes.shift();
806
+ if (!opts || !ts.isObjectLiteralExpression(opts) || opts.properties.length > 0) {
807
+ L.noLowering("util.formatWithOptions with a non-empty options literal", opts ?? expr, "only the empty literal {} (the defaults — exactly util.format) is lowered");
808
+ }
809
+ }
810
+ if (argNodes.length === 0)
811
+ return str("", loc);
812
+ const first = argNodes[0];
813
+ const firstIsLiteral = ts.isStringLiteral(first) || ts.isNoSubstitutionTemplateLiteral(first);
814
+ /** The rest-args tail: ` ` + (strings verbatim, everything else
815
+ * inspected at the default depth). */
816
+ const restArg = (node) => formatSArg(L, node, 2, loc);
817
+ if (!firstIsLiteral) {
818
+ const firstT = L.mapTypeOf(L.typeOf(first));
819
+ if (firstT?.kind === "string" && argNodes.length > 1) {
820
+ L.noLowering("util.format with a runtime format string and further arguments", first, "the %-substitution positions are runtime-dependent — use a string literal");
821
+ }
822
+ // No substitutions possible: every argument joins with spaces.
823
+ const parts = [];
824
+ for (let i = 0; i < argNodes.length; i++) {
825
+ if (i > 0)
826
+ parts.push(str(" ", loc));
827
+ parts.push(restArg(argNodes[i]));
828
+ }
829
+ return concatAll(parts, loc);
830
+ }
831
+ const fmt = first.text;
832
+ // Node's early return: a lone string first argument passes VERBATIM —
833
+ // no substitution runs, "%%" stays "%%".
834
+ if (argNodes.length === 1)
835
+ return str(fmt, loc);
836
+ const parts = [];
837
+ let a = 0; // the arg cursor over argNodes (0 = the format string)
838
+ let lastPos = 0;
839
+ const args = argNodes;
840
+ const convert = (spec, node) => {
841
+ switch (spec) {
842
+ case 115: // %s
843
+ return formatSArg(L, node, 0, loc);
844
+ case 100: {
845
+ // %d — Number(arg) formatted: numbers as-is, booleans 1/0;
846
+ // the full ToNumber string grammar has no static lowering.
847
+ const value = L.lowerExpr(node);
848
+ if (value.type.kind === "f64")
849
+ return { kind: "libCall", fn: "insp.f64", args: [value], type: STRING, loc };
850
+ if (value.type.kind === "bool") {
851
+ return {
852
+ kind: "libCall",
853
+ fn: "insp.f64",
854
+ args: [{ kind: "ternary", cond: value, then: num(1, loc), else_: num(0, loc), type: F64, loc }],
855
+ type: STRING,
856
+ loc,
857
+ };
858
+ }
859
+ // A checked-dynamic argument (`console.log('... actual %d.',
860
+ // context.actual)` — test/common's exit report): VALIDATE it as a
861
+ // number (dynCheck) and format. A non-number DOM value throws the
862
+ // catchable TypeError where Node would print its ToNumber (NaN
863
+ // for objects) — loud, never a silent wrong answer (SEMANTICS.md).
864
+ if (value.type.kind === "dyn") {
865
+ return {
866
+ kind: "libCall",
867
+ fn: "insp.f64",
868
+ args: [{ kind: "dynCheck", value, type: F64, loc }],
869
+ type: STRING,
870
+ loc,
871
+ };
872
+ }
873
+ L.noLowering(`util.format %d of '${L.fmt(value.type)}' values`, node, "numbers and booleans lower; ToNumber over other types has no static lowering");
874
+ break;
875
+ }
876
+ case 105: {
877
+ // %i — parseInt(ToString(arg)): the spec-exact composition.
878
+ const value = L.lowerExpr(node);
879
+ if (value.type.kind === "f64" || value.type.kind === "bool" || value.type.kind === "string") {
880
+ const text = value.type.kind === "string" ? value : { kind: "toString", operand: value, type: STRING, loc };
881
+ const parsed = { kind: "libCall", fn: "num.parseInt", args: [text, num(0, loc)], type: F64, loc };
882
+ return { kind: "libCall", fn: "insp.f64", args: [parsed], type: STRING, loc };
883
+ }
884
+ L.noLowering(`util.format %i of '${L.fmt(value.type)}' values`, node);
885
+ break;
886
+ }
887
+ case 106: {
888
+ // %j — JSON.stringify; undefined-valued args print "undefined"
889
+ // (Node appends the non-string result of tryStringify).
890
+ const value = L.lowerExpr(node);
891
+ if (value.type.kind === "undefinedT")
892
+ return str("undefined", loc);
893
+ // JSON.stringify(sym) is undefined in Node — %j prints the
894
+ // "undefined" text. Folding drops the operand, so only
895
+ // side-effect-free reads compose (the typeof-fold stance).
896
+ if (value.type.kind === "symbol") {
897
+ if (!pureReemittable(value)) {
898
+ L.noLowering(`util.format %j of computed symbol values`, node, "bind the symbol to a const first (the %j text is always \"undefined\")");
899
+ }
900
+ return str("undefined", loc);
901
+ }
902
+ // A checked-dynamic argument (`console.error('headers: %j',
903
+ // headers)` — the suite's http logging idiom): the runtime DOM
904
+ // walk stringifies JS-exactly (root undefined/function prints
905
+ // "undefined"; a handle in the tree throws the loud fence).
906
+ if (value.type.kind === "dyn") {
907
+ return { kind: "libCall", fn: "insp.jsonDyn", args: [value], type: STRING, loc };
908
+ }
909
+ if (!L.jsonSafe(value.type)) {
910
+ L.noLowering(`util.format %j of '${L.fmt(value.type)}' values`, node, "only JSON-safe static types lower");
911
+ }
912
+ return { kind: "jsonStringify", value, type: STRING, loc };
913
+ }
914
+ case 79: // %O — inspect at the defaults
915
+ return formatOArg(L, node, 2, loc);
916
+ case 111: // %o — showHidden semantics; depth 4
917
+ return formatOArg(L, node, 4, loc);
918
+ }
919
+ throw new Error("unreachable format spec");
920
+ };
921
+ for (let i = 0; i < fmt.length - 1; i++) {
922
+ if (fmt.charCodeAt(i) === 37 /* % */) {
923
+ const nextChar = fmt.charCodeAt(++i);
924
+ if (a + 1 !== args.length) {
925
+ switch (nextChar) {
926
+ case 115: // s
927
+ case 106: // j
928
+ case 100: // d
929
+ case 79: // O
930
+ case 111: // o
931
+ case 105: { // i
932
+ const node = args[++a];
933
+ if (lastPos !== i - 1)
934
+ parts.push(str(fmt.slice(lastPos, i - 1), loc));
935
+ parts.push(convert(nextChar, node));
936
+ lastPos = i + 1;
937
+ continue;
938
+ }
939
+ case 102: // f — parseFloat's full grammar has no static lowering
940
+ L.noLowering("util.format %f", expr, "parseFloat has no static lowering (it runs with --dynamic)");
941
+ break;
942
+ case 99: // c — consumes its argument, contributes nothing
943
+ a += 1;
944
+ if (lastPos !== i - 1)
945
+ parts.push(str(fmt.slice(lastPos, i - 1), loc));
946
+ lastPos = i + 1;
947
+ continue;
948
+ case 37: // %%
949
+ parts.push(str(fmt.slice(lastPos, i), loc));
950
+ lastPos = i + 1;
951
+ continue;
952
+ default:
953
+ continue;
954
+ }
955
+ }
956
+ else if (nextChar === 37) {
957
+ parts.push(str(fmt.slice(lastPos, i), loc));
958
+ lastPos = i + 1;
959
+ }
960
+ }
961
+ }
962
+ if (lastPos !== 0) {
963
+ a++;
964
+ if (lastPos < fmt.length)
965
+ parts.push(str(fmt.slice(lastPos), loc));
966
+ }
967
+ else {
968
+ // No substitution touched the format string: it joins verbatim, and
969
+ // the cursor advances past it.
970
+ parts.push(str(fmt, loc));
971
+ a++;
972
+ }
973
+ for (; a < args.length; a++) {
974
+ parts.push(str(" ", loc));
975
+ parts.push(restArg(args[a]));
976
+ }
977
+ return concatAll(parts, loc);
978
+ }
979
+ /** Arrays anywhere in the tree — the %o (showHidden) gate. */
980
+ function typeTreeHasArray(L, t, visiting) {
981
+ switch (t.kind) {
982
+ case "array":
983
+ return true;
984
+ case "record": {
985
+ if (visiting.has(t.shapeId))
986
+ return false;
987
+ visiting.add(t.shapeId);
988
+ const shape = L.shapes.get(t.shapeId);
989
+ if (shape?.tuple)
990
+ return true;
991
+ return (shape?.fields ?? []).some((f) => typeTreeHasArray(L, f.type, visiting));
992
+ }
993
+ case "map":
994
+ return typeTreeHasArray(L, t.key, visiting) || typeTreeHasArray(L, t.value, visiting);
995
+ case "set":
996
+ return typeTreeHasArray(L, t.elem, visiting);
997
+ case "union": {
998
+ if (visiting.has(t.unionId))
999
+ return false;
1000
+ visiting.add(t.unionId);
1001
+ return (L.unions.get(t.unionId)?.arms ?? []).some((a) => typeTreeHasArray(L, a, visiting));
1002
+ }
1003
+ case "object": {
1004
+ if (visiting.has(t.className))
1005
+ return false;
1006
+ visiting.add(t.className);
1007
+ return (L.classes.get(t.className)?.def.fields ?? []).some((f) => typeTreeHasArray(L, f.type, visiting));
1008
+ }
1009
+ case "bytes":
1010
+ return true;
1011
+ default:
1012
+ return false;
1013
+ }
1014
+ }
1015
+ /* ── the module dispatch ─────────────────────────────────────────────── */
1016
+ /** The spoke's entry, called from both the named-import and namespace/
1017
+ * default-import call paths. Null for other modules and members (the
1018
+ * module tables' fence takes over). */
1019
+ export function lowerUtilModuleCall(L, expr, bi, loc) {
1020
+ if (bi.module !== "util")
1021
+ return null;
1022
+ switch (bi.member) {
1023
+ case "inspect":
1024
+ return lowerInspectCall(L, expr, loc);
1025
+ case "format":
1026
+ return lowerFormatCall(L, expr, loc, false);
1027
+ case "formatWithOptions":
1028
+ return lowerFormatCall(L, expr, loc, true);
1029
+ case "getCallSites":
1030
+ return lowerGetCallSitesCall(L, expr, loc);
1031
+ default:
1032
+ return null;
1033
+ }
1034
+ }
1035
+ /** util.getCallSites() in a JS source: compiled binaries keep no runtime
1036
+ * call stacks, so the honest STATIC answer is a fixed-shape placeholder —
1037
+ * a fresh DOM array of three call-site records whose scriptName is the
1038
+ * CALL SITE's own file (compile-time-knowable) and whose line/column are
1039
+ * 0 (SEMANTICS.md; the fields exist so consumers like test/common's
1040
+ * mustNotCall — `callSite.scriptName`:`callSite.lineNumber` in the
1041
+ * failure text — read a printable value instead of throwing). Argument
1042
+ * forms and TypeScript keep the SC2020 fence: a typed consumer asserting
1043
+ * real frames must fail loudly at compile time, not read placeholders. */
1044
+ function lowerGetCallSitesCall(L, expr, loc) {
1045
+ if (expr.arguments.length !== 0)
1046
+ return null;
1047
+ if (!isJsSourceFile(expr.getSourceFile()))
1048
+ return null;
1049
+ const frame = () => {
1050
+ const field = (name, value) => ({
1051
+ key: { kind: "strLit", value: name, type: STRING, loc },
1052
+ value: { kind: "dynFrom", value, type: DYN, loc },
1053
+ });
1054
+ return {
1055
+ kind: "dynObjLit",
1056
+ fields: [
1057
+ field("functionName", { kind: "strLit", value: "", type: STRING, loc }),
1058
+ field("scriptName", { kind: "strLit", value: expr.getSourceFile().fileName, type: STRING, loc }),
1059
+ field("scriptId", { kind: "strLit", value: "0", type: STRING, loc }),
1060
+ field("lineNumber", { kind: "numLit", value: 0, type: F64, loc }),
1061
+ field("column", { kind: "numLit", value: 0, type: F64, loc }),
1062
+ ],
1063
+ type: DYN,
1064
+ loc,
1065
+ };
1066
+ };
1067
+ return { kind: "dynArrLit", elems: [frame(), frame(), frame()], type: DYN, loc };
1068
+ }
1069
+ //# sourceMappingURL=lower-inspect.js.map