@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.
- package/LICENSE +202 -0
- package/ambient/package.json +3 -0
- package/ambient/scriptc-node-fallback.d.ts +3001 -0
- package/ambient/scriptc-overrides.d.ts +95 -0
- package/ambient/scriptc.d.ts +74 -0
- package/dist/backend/cc.d.ts +169 -0
- package/dist/backend/cc.js +897 -0
- package/dist/backend/cc.js.map +1 -0
- package/dist/backend/emission/emit-async.d.ts +145 -0
- package/dist/backend/emission/emit-async.js +996 -0
- package/dist/backend/emission/emit-async.js.map +1 -0
- package/dist/backend/emission/emit-exprs.d.ts +3 -0
- package/dist/backend/emission/emit-exprs.js +5947 -0
- package/dist/backend/emission/emit-exprs.js.map +1 -0
- package/dist/backend/emission/emit-island.d.ts +45 -0
- package/dist/backend/emission/emit-island.js +271 -0
- package/dist/backend/emission/emit-island.js.map +1 -0
- package/dist/backend/emission/emit-shapes.d.ts +142 -0
- package/dist/backend/emission/emit-shapes.js +575 -0
- package/dist/backend/emission/emit-shapes.js.map +1 -0
- package/dist/backend/emission/emit-stmts.d.ts +78 -0
- package/dist/backend/emission/emit-stmts.js +960 -0
- package/dist/backend/emission/emit-stmts.js.map +1 -0
- package/dist/backend/emission/emit-types.d.ts +65 -0
- package/dist/backend/emission/emit-types.js +652 -0
- package/dist/backend/emission/emit-types.js.map +1 -0
- package/dist/backend/emission/emit-walkers.d.ts +154 -0
- package/dist/backend/emission/emit-walkers.js +1587 -0
- package/dist/backend/emission/emit-walkers.js.map +1 -0
- package/dist/backend/emission/emitter.d.ts +452 -0
- package/dist/backend/emission/emitter.js +1249 -0
- package/dist/backend/emission/emitter.js.map +1 -0
- package/dist/backend/emission/may-throw.d.ts +15 -0
- package/dist/backend/emission/may-throw.js +250 -0
- package/dist/backend/emission/may-throw.js.map +1 -0
- package/dist/backend/llvm/blocks.d.ts +21 -0
- package/dist/backend/llvm/blocks.js +68 -0
- package/dist/backend/llvm/blocks.js.map +1 -0
- package/dist/backend/llvm/classes.d.ts +84 -0
- package/dist/backend/llvm/classes.js +438 -0
- package/dist/backend/llvm/classes.js.map +1 -0
- package/dist/backend/llvm/dyn.d.ts +123 -0
- package/dist/backend/llvm/dyn.js +2624 -0
- package/dist/backend/llvm/dyn.js.map +1 -0
- package/dist/backend/llvm/emitter.d.ts +3 -0
- package/dist/backend/llvm/emitter.js +10087 -0
- package/dist/backend/llvm/emitter.js.map +1 -0
- package/dist/backend/llvm/shapes.d.ts +78 -0
- package/dist/backend/llvm/shapes.js +754 -0
- package/dist/backend/llvm/shapes.js.map +1 -0
- package/dist/backend/llvm/unsupported.d.ts +6 -0
- package/dist/backend/llvm/unsupported.js +12 -0
- package/dist/backend/llvm/unsupported.js.map +1 -0
- package/dist/backend/llvm/walkers.d.ts +58 -0
- package/dist/backend/llvm/walkers.js +793 -0
- package/dist/backend/llvm/walkers.js.map +1 -0
- package/dist/backend/mangle.d.ts +124 -0
- package/dist/backend/mangle.js +232 -0
- package/dist/backend/mangle.js.map +1 -0
- package/dist/coverage/report.d.ts +64 -0
- package/dist/coverage/report.js +226 -0
- package/dist/coverage/report.js.map +1 -0
- package/dist/diagnostics/diagnostic.d.ts +140 -0
- package/dist/diagnostics/diagnostic.js +458 -0
- package/dist/diagnostics/diagnostic.js.map +1 -0
- package/dist/diagnostics/render.d.ts +11 -0
- package/dist/diagnostics/render.js +58 -0
- package/dist/diagnostics/render.js.map +1 -0
- package/dist/frontend/cjs-lexer.d.ts +20 -0
- package/dist/frontend/cjs-lexer.js +813 -0
- package/dist/frontend/cjs-lexer.js.map +1 -0
- package/dist/frontend/lowering/http2-constants.d.ts +1 -0
- package/dist/frontend/lowering/http2-constants.js +251 -0
- package/dist/frontend/lowering/http2-constants.js.map +1 -0
- package/dist/frontend/lowering/lib-boundary.d.ts +6 -0
- package/dist/frontend/lowering/lib-boundary.js +143 -0
- package/dist/frontend/lowering/lib-boundary.js.map +1 -0
- package/dist/frontend/lowering/lower-assert.d.ts +18 -0
- package/dist/frontend/lowering/lower-assert.js +1269 -0
- package/dist/frontend/lowering/lower-assert.js.map +1 -0
- package/dist/frontend/lowering/lower-builtins.d.ts +511 -0
- package/dist/frontend/lowering/lower-builtins.js +5249 -0
- package/dist/frontend/lowering/lower-builtins.js.map +1 -0
- package/dist/frontend/lowering/lower-calls.d.ts +581 -0
- package/dist/frontend/lowering/lower-calls.js +6281 -0
- package/dist/frontend/lowering/lower-calls.js.map +1 -0
- package/dist/frontend/lowering/lower-classes.d.ts +727 -0
- package/dist/frontend/lowering/lower-classes.js +4701 -0
- package/dist/frontend/lowering/lower-classes.js.map +1 -0
- package/dist/frontend/lowering/lower-comptime.d.ts +53 -0
- package/dist/frontend/lowering/lower-comptime.js +244 -0
- package/dist/frontend/lowering/lower-comptime.js.map +1 -0
- package/dist/frontend/lowering/lower-containers.d.ts +483 -0
- package/dist/frontend/lowering/lower-containers.js +6281 -0
- package/dist/frontend/lowering/lower-containers.js.map +1 -0
- package/dist/frontend/lowering/lower-dgram.d.ts +20 -0
- package/dist/frontend/lowering/lower-dgram.js +319 -0
- package/dist/frontend/lowering/lower-dgram.js.map +1 -0
- package/dist/frontend/lowering/lower-emitter.d.ts +10 -0
- package/dist/frontend/lowering/lower-emitter.js +688 -0
- package/dist/frontend/lowering/lower-emitter.js.map +1 -0
- package/dist/frontend/lowering/lower-enums.d.ts +22 -0
- package/dist/frontend/lowering/lower-enums.js +235 -0
- package/dist/frontend/lowering/lower-enums.js.map +1 -0
- package/dist/frontend/lowering/lower-expando.d.ts +35 -0
- package/dist/frontend/lowering/lower-expando.js +276 -0
- package/dist/frontend/lowering/lower-expando.js.map +1 -0
- package/dist/frontend/lowering/lower-exprs.d.ts +524 -0
- package/dist/frontend/lowering/lower-exprs.js +8099 -0
- package/dist/frontend/lowering/lower-exprs.js.map +1 -0
- package/dist/frontend/lowering/lower-generators.d.ts +50 -0
- package/dist/frontend/lowering/lower-generators.js +421 -0
- package/dist/frontend/lowering/lower-generators.js.map +1 -0
- package/dist/frontend/lowering/lower-inspect.d.ts +20 -0
- package/dist/frontend/lowering/lower-inspect.js +1069 -0
- package/dist/frontend/lowering/lower-inspect.js.map +1 -0
- package/dist/frontend/lowering/lower-island.d.ts +108 -0
- package/dist/frontend/lowering/lower-island.js +650 -0
- package/dist/frontend/lowering/lower-island.js.map +1 -0
- package/dist/frontend/lowering/lower-mixins.d.ts +96 -0
- package/dist/frontend/lowering/lower-mixins.js +534 -0
- package/dist/frontend/lowering/lower-mixins.js.map +1 -0
- package/dist/frontend/lowering/lower-modules.d.ts +135 -0
- package/dist/frontend/lowering/lower-modules.js +1613 -0
- package/dist/frontend/lowering/lower-modules.js.map +1 -0
- package/dist/frontend/lowering/lower-namespaces.d.ts +168 -0
- package/dist/frontend/lowering/lower-namespaces.js +706 -0
- package/dist/frontend/lowering/lower-namespaces.js.map +1 -0
- package/dist/frontend/lowering/lower-server.d.ts +90 -0
- package/dist/frontend/lowering/lower-server.js +3471 -0
- package/dist/frontend/lowering/lower-server.js.map +1 -0
- package/dist/frontend/lowering/lower-stmts.d.ts +361 -0
- package/dist/frontend/lowering/lower-stmts.js +5341 -0
- package/dist/frontend/lowering/lower-stmts.js.map +1 -0
- package/dist/frontend/lowering/lower-stream.d.ts +88 -0
- package/dist/frontend/lowering/lower-stream.js +1533 -0
- package/dist/frontend/lowering/lower-stream.js.map +1 -0
- package/dist/frontend/lowering/lower-test.d.ts +27 -0
- package/dist/frontend/lowering/lower-test.js +353 -0
- package/dist/frontend/lowering/lower-test.js.map +1 -0
- package/dist/frontend/lowering/lowerer.d.ts +1748 -0
- package/dist/frontend/lowering/lowerer.js +6256 -0
- package/dist/frontend/lowering/lowerer.js.map +1 -0
- package/dist/frontend/lowering/surfaces.d.ts +272 -0
- package/dist/frontend/lowering/surfaces.js +1096 -0
- package/dist/frontend/lowering/surfaces.js.map +1 -0
- package/dist/frontend/npm-static.d.ts +55 -0
- package/dist/frontend/npm-static.js +300 -0
- package/dist/frontend/npm-static.js.map +1 -0
- package/dist/frontend/npm.d.ts +330 -0
- package/dist/frontend/npm.js +1259 -0
- package/dist/frontend/npm.js.map +1 -0
- package/dist/frontend/program.d.ts +186 -0
- package/dist/frontend/program.js +2172 -0
- package/dist/frontend/program.js.map +1 -0
- package/dist/frontend/provenance-registry.d.ts +48 -0
- package/dist/frontend/provenance-registry.js +87 -0
- package/dist/frontend/provenance-registry.js.map +1 -0
- package/dist/frontend/provenance.d.ts +6 -0
- package/dist/frontend/provenance.js +459 -0
- package/dist/frontend/provenance.js.map +1 -0
- package/dist/frontend/resolve.d.ts +56 -0
- package/dist/frontend/resolve.js +682 -0
- package/dist/frontend/resolve.js.map +1 -0
- package/dist/frontend/shared.d.ts +60 -0
- package/dist/frontend/shared.js +232 -0
- package/dist/frontend/shared.js.map +1 -0
- package/dist/frontend/ts7/adapter.d.ts +7 -0
- package/dist/frontend/ts7/adapter.js +54 -0
- package/dist/frontend/ts7/adapter.js.map +1 -0
- package/dist/frontend/ts7/ast.d.ts +50 -0
- package/dist/frontend/ts7/ast.js +211 -0
- package/dist/frontend/ts7/ast.js.map +1 -0
- package/dist/frontend/ts7/census-check.d.ts +1 -0
- package/dist/frontend/ts7/census-check.js +12 -0
- package/dist/frontend/ts7/census-check.js.map +1 -0
- package/dist/frontend/ts7/checker.d.ts +140 -0
- package/dist/frontend/ts7/checker.js +544 -0
- package/dist/frontend/ts7/checker.js.map +1 -0
- package/dist/frontend/ts7/enums.d.ts +18 -0
- package/dist/frontend/ts7/enums.js +43 -0
- package/dist/frontend/ts7/enums.js.map +1 -0
- package/dist/frontend/ts7/program.d.ts +99 -0
- package/dist/frontend/ts7/program.js +278 -0
- package/dist/frontend/ts7/program.js.map +1 -0
- package/dist/frontend/ts7/world-check.d.ts +3 -0
- package/dist/frontend/ts7/world-check.js +34 -0
- package/dist/frontend/ts7/world-check.js.map +1 -0
- package/dist/frontend/types.d.ts +205 -0
- package/dist/frontend/types.js +2485 -0
- package/dist/frontend/types.js.map +1 -0
- package/dist/index.d.ts +85 -0
- package/dist/index.js +424 -0
- package/dist/index.js.map +1 -0
- package/dist/ir/nodes.d.ts +4334 -0
- package/dist/ir/nodes.js +1906 -0
- package/dist/ir/nodes.js.map +1 -0
- package/dist/ir/serialize.d.ts +4 -0
- package/dist/ir/serialize.js +34 -0
- package/dist/ir/serialize.js.map +1 -0
- package/dist/ir/validate.d.ts +36 -0
- package/dist/ir/validate.js +4884 -0
- package/dist/ir/validate.js.map +1 -0
- package/package.json +30 -6
- package/README.md +0 -3
- 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
|