@codeflip/embed 0.0.0-stage → 0.1.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 +373 -0
- package/README.md +87 -2
- package/dist/codeflip.js +2502 -0
- package/dist/index.d.ts +18 -0
- package/dist/index.js +4148 -0
- package/package.json +47 -3
package/dist/codeflip.js
ADDED
|
@@ -0,0 +1,2502 @@
|
|
|
1
|
+
// ../trace/src/index.ts
|
|
2
|
+
var e = {
|
|
3
|
+
c: {
|
|
4
|
+
manualMemory: !0,
|
|
5
|
+
dynamic: !1,
|
|
6
|
+
nullWord: "NULL"
|
|
7
|
+
},
|
|
8
|
+
cpp: {
|
|
9
|
+
manualMemory: !0,
|
|
10
|
+
dynamic: !1,
|
|
11
|
+
nullWord: "NULL"
|
|
12
|
+
},
|
|
13
|
+
java: {
|
|
14
|
+
manualMemory: !1,
|
|
15
|
+
dynamic: !1,
|
|
16
|
+
nullWord: "null"
|
|
17
|
+
},
|
|
18
|
+
python: {
|
|
19
|
+
manualMemory: !1,
|
|
20
|
+
dynamic: !0,
|
|
21
|
+
nullWord: "None"
|
|
22
|
+
},
|
|
23
|
+
javascript: {
|
|
24
|
+
manualMemory: !1,
|
|
25
|
+
dynamic: !0,
|
|
26
|
+
nullWord: "null"
|
|
27
|
+
},
|
|
28
|
+
typescript: {
|
|
29
|
+
manualMemory: !1,
|
|
30
|
+
dynamic: !0,
|
|
31
|
+
nullWord: "null"
|
|
32
|
+
}
|
|
33
|
+
}, t = /* @__PURE__ */ new WeakMap();
|
|
34
|
+
function n(e, n) {
|
|
35
|
+
let r = t.get(e);
|
|
36
|
+
if (!r) {
|
|
37
|
+
r = [];
|
|
38
|
+
let n = { ...e.files };
|
|
39
|
+
for (let t of e.steps) {
|
|
40
|
+
if (t.files) {
|
|
41
|
+
n = { ...n };
|
|
42
|
+
for (let [e, r] of Object.entries(t.files)) r === null ? delete n[e] : n[e] = r;
|
|
43
|
+
}
|
|
44
|
+
r.push(n);
|
|
45
|
+
}
|
|
46
|
+
t.set(e, r);
|
|
47
|
+
}
|
|
48
|
+
return r[Math.min(Math.max(n, 0), r.length - 1)] ?? { ...e.files };
|
|
49
|
+
}
|
|
50
|
+
function r(e, t) {
|
|
51
|
+
if (t <= 0) return 0;
|
|
52
|
+
let n = e.indexOf("\n", t - 1);
|
|
53
|
+
return n < 0 ? e.length : n + 1;
|
|
54
|
+
}
|
|
55
|
+
// ../renderer/src/animate.ts
|
|
56
|
+
var i = "cubic-bezier(0.16, 1, 0.3, 1)", a = 220, o = () => typeof matchMedia == "function" && matchMedia("(prefers-reduced-motion: reduce)").matches;
|
|
57
|
+
function s(e) {
|
|
58
|
+
let t = /* @__PURE__ */ new Map();
|
|
59
|
+
for (let n of e.querySelectorAll("[data-cf-anim]")) t.set(n.dataset.cfAnim, {
|
|
60
|
+
rect: n.getBoundingClientRect(),
|
|
61
|
+
el: n
|
|
62
|
+
});
|
|
63
|
+
return t;
|
|
64
|
+
}
|
|
65
|
+
function c(e, t, n) {
|
|
66
|
+
for (let e of n.querySelectorAll(".cf-exit")) e.remove();
|
|
67
|
+
let r = s(e);
|
|
68
|
+
for (let [e, { rect: n, el: a }] of r) {
|
|
69
|
+
let r = t.get(e)?.rect;
|
|
70
|
+
if (!r) {
|
|
71
|
+
a.classList.contains("cf-frame") && a.animate([{
|
|
72
|
+
opacity: 0,
|
|
73
|
+
transform: "translateY(-8px)"
|
|
74
|
+
}, {
|
|
75
|
+
opacity: 1,
|
|
76
|
+
transform: "none"
|
|
77
|
+
}], {
|
|
78
|
+
duration: 320,
|
|
79
|
+
easing: i
|
|
80
|
+
});
|
|
81
|
+
continue;
|
|
82
|
+
}
|
|
83
|
+
let o = r.left - n.left, s = r.top - n.top, c = Math.abs(r.height - n.height) > .5 || Math.abs(r.width - n.width) > .5;
|
|
84
|
+
if (Math.abs(o) < .5 && Math.abs(s) < .5 && !c) continue;
|
|
85
|
+
let l = a.style.overflow;
|
|
86
|
+
a.style.overflow = "clip";
|
|
87
|
+
let u = (c ? [...a.children].filter((e) => e instanceof HTMLElement) : []).map((e) => {
|
|
88
|
+
let t = {
|
|
89
|
+
width: e.style.width,
|
|
90
|
+
minWidth: e.style.minWidth,
|
|
91
|
+
boxSizing: e.style.boxSizing
|
|
92
|
+
}, n = `${e.getBoundingClientRect().width}px`;
|
|
93
|
+
return Object.assign(e.style, {
|
|
94
|
+
width: n,
|
|
95
|
+
minWidth: n,
|
|
96
|
+
boxSizing: "border-box"
|
|
97
|
+
}), () => Object.assign(e.style, t);
|
|
98
|
+
}), d = a.animate([{
|
|
99
|
+
transform: `translate(${o}px, ${s}px)`,
|
|
100
|
+
height: `${r.height}px`,
|
|
101
|
+
width: `${r.width}px`
|
|
102
|
+
}, {
|
|
103
|
+
transform: "none",
|
|
104
|
+
height: `${n.height}px`,
|
|
105
|
+
width: `${n.width}px`
|
|
106
|
+
}], {
|
|
107
|
+
duration: 320,
|
|
108
|
+
easing: i
|
|
109
|
+
}), f = () => {
|
|
110
|
+
a.style.overflow = l;
|
|
111
|
+
for (let e of u) e();
|
|
112
|
+
};
|
|
113
|
+
d.onfinish = f, d.oncancel = f;
|
|
114
|
+
}
|
|
115
|
+
let o = n.getBoundingClientRect();
|
|
116
|
+
for (let [e, { rect: s, el: c }] of t) {
|
|
117
|
+
if (r.has(e)) continue;
|
|
118
|
+
let t = c.cloneNode(!0);
|
|
119
|
+
for (let e of [t, ...t.querySelectorAll("*")]) e.removeAttribute("data-cf-key"), e.removeAttribute("data-cf-ptr"), e.removeAttribute("data-cf-anim"), e.removeAttribute("id");
|
|
120
|
+
t.classList.add("cf-exit"), t.setAttribute("aria-hidden", "true"), Object.assign(t.style, {
|
|
121
|
+
position: "absolute",
|
|
122
|
+
left: `${s.left - o.left + n.scrollLeft}px`,
|
|
123
|
+
top: `${s.top - o.top + n.scrollTop}px`,
|
|
124
|
+
width: `${s.width}px`,
|
|
125
|
+
height: `${s.height}px`,
|
|
126
|
+
margin: "0",
|
|
127
|
+
pointerEvents: "none",
|
|
128
|
+
overflow: "clip",
|
|
129
|
+
animation: "none"
|
|
130
|
+
}), n.append(t), t.animate([{
|
|
131
|
+
opacity: 1,
|
|
132
|
+
transform: "none"
|
|
133
|
+
}, {
|
|
134
|
+
opacity: 0,
|
|
135
|
+
transform: "scale(0.96)"
|
|
136
|
+
}], {
|
|
137
|
+
duration: a,
|
|
138
|
+
easing: i,
|
|
139
|
+
fill: "forwards"
|
|
140
|
+
}).finished.then(() => t.remove(), () => t.remove());
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
function l(e, t, n) {
|
|
144
|
+
let r = !1, i = performance.now() + e, a = () => {
|
|
145
|
+
if (!r) {
|
|
146
|
+
if (performance.now() >= i) return n();
|
|
147
|
+
t(), requestAnimationFrame(a);
|
|
148
|
+
}
|
|
149
|
+
};
|
|
150
|
+
return requestAnimationFrame(a), () => r = !0;
|
|
151
|
+
}
|
|
152
|
+
// ../renderer/src/arrows.ts
|
|
153
|
+
var u = "http://www.w3.org/2000/svg", d = 14, f = 8, p = 14, m = 6, h = 8, g = (e) => e.classList.contains("cf-cell") && !!e.previousElementSibling;
|
|
154
|
+
function _(e, t, n) {
|
|
155
|
+
let r = e.closest(".cf-heap-row"), i = e.closest(".cf-object");
|
|
156
|
+
if (!r || !i) return null;
|
|
157
|
+
if (g(e)) return r.getBoundingClientRect();
|
|
158
|
+
let a = i.getBoundingClientRect();
|
|
159
|
+
return [...r.children].some((e) => {
|
|
160
|
+
if (e === i) return !1;
|
|
161
|
+
let r = e.getBoundingClientRect();
|
|
162
|
+
return r.right + n > t && r.left < a.left;
|
|
163
|
+
}) ? r.getBoundingClientRect() : null;
|
|
164
|
+
}
|
|
165
|
+
function v(e, t, n, r) {
|
|
166
|
+
let i = e.getRootNode(), a = e.getBoundingClientRect(), o = e.scrollLeft - a.left, s = e.scrollTop - a.top;
|
|
167
|
+
t.setAttribute("width", String(e.scrollWidth)), t.setAttribute("height", String(e.scrollHeight)), t.replaceChildren();
|
|
168
|
+
let c = document.createElementNS(u, "defs");
|
|
169
|
+
c.innerHTML = "<marker id=\"cf-head\" viewBox=\"0 0 10 10\" refX=\"10\" refY=\"5\" markerWidth=\"10\" markerHeight=\"10\" markerUnits=\"userSpaceOnUse\" orient=\"auto\"><path d=\"M0 0.5L10 5L0 9.5z\" fill=\"var(--cf-arrow)\"/></marker><marker id=\"cf-head-danger\" viewBox=\"0 0 10 10\" refX=\"10\" refY=\"5\" markerWidth=\"10\" markerHeight=\"10\" markerUnits=\"userSpaceOnUse\" orient=\"auto\"><path d=\"M0 0.5L10 5L0 9.5z\" fill=\"var(--cf-danger)\"/></marker>", t.append(c);
|
|
170
|
+
let l = /* @__PURE__ */ new Map();
|
|
171
|
+
for (let t of e.querySelectorAll("[data-cf-ptr]")) {
|
|
172
|
+
let e = t.dataset.cfPtr, r = i.querySelector(`[data-cf-key="${CSS.escape(e)}"]`) ?? i.querySelector(`[data-cf-key="${CSS.escape(e.replace(/:\d+$/, ""))}"]`);
|
|
173
|
+
if (!r) continue;
|
|
174
|
+
let a = t.getBoundingClientRect(), c = (t.closest(".cf-frames") ?? t.closest(".cf-object"))?.getBoundingClientRect(), u = {
|
|
175
|
+
sx: a.left + a.width / 2 + o,
|
|
176
|
+
sy: a.top + a.height / 2 + s,
|
|
177
|
+
exitX: (c ? c.right : a.right) + o,
|
|
178
|
+
target: r,
|
|
179
|
+
changed: n.has(t.closest("[data-cf-key]")?.getAttribute("data-cf-key") ?? ""),
|
|
180
|
+
dangling: e.startsWith("ghost:"),
|
|
181
|
+
weak: t.classList.contains("cf-weak")
|
|
182
|
+
}, d = t.closest(".cf-cell"), f = d?.closest(".cf-object:not(.cf-literal)");
|
|
183
|
+
d && f && (u.cell = {
|
|
184
|
+
x: u.sx,
|
|
185
|
+
y: u.sy,
|
|
186
|
+
bottom: f.getBoundingClientRect().bottom + s
|
|
187
|
+
});
|
|
188
|
+
let p = l.get(r) ?? [];
|
|
189
|
+
p.push(u), l.set(r, p);
|
|
190
|
+
}
|
|
191
|
+
let v = [...l.values()].flat().filter((e) => e.cell), y = /* @__PURE__ */ new Map();
|
|
192
|
+
for (let e of v) y.set(e.cell.bottom, [...y.get(e.cell.bottom) ?? [], e]);
|
|
193
|
+
for (let e of y.values()) e.sort((e, t) => t.cell.x - e.cell.x), e.forEach((e, t) => {
|
|
194
|
+
let n = e.cell.bottom + 8 + t * m;
|
|
195
|
+
e.cell.next = n, e.sy = n;
|
|
196
|
+
});
|
|
197
|
+
let b = /* @__PURE__ */ new Map();
|
|
198
|
+
for (let [e, t] of l) for (let n of t) {
|
|
199
|
+
if (n.exitX >= e.getBoundingClientRect().left + o) continue;
|
|
200
|
+
let t = _(e, n.exitX, o);
|
|
201
|
+
if (!t) continue;
|
|
202
|
+
let r = e.closest(".cf-heap-row"), i = b.get(r) ?? {
|
|
203
|
+
below: [],
|
|
204
|
+
above: []
|
|
205
|
+
};
|
|
206
|
+
(n.sy < t.top + s && !g(e) ? i.above : i.below).push(n), b.set(r, i);
|
|
207
|
+
}
|
|
208
|
+
for (let { below: e, above: t } of b.values()) e.sort((e, t) => e.sy - t.sy).forEach((t, n) => t.lane = {
|
|
209
|
+
side: "below",
|
|
210
|
+
index: n,
|
|
211
|
+
count: e.length
|
|
212
|
+
}), t.sort((e, t) => t.sy - e.sy).forEach((e, n) => e.lane = {
|
|
213
|
+
side: "above",
|
|
214
|
+
index: n,
|
|
215
|
+
count: t.length
|
|
216
|
+
});
|
|
217
|
+
for (let [e, n] of l) {
|
|
218
|
+
let i = e.getBoundingClientRect(), a = i.top + s;
|
|
219
|
+
n.sort((e, t) => e.sy - t.sy);
|
|
220
|
+
let c = Math.max(0, i.height - 28), l = n.length > 1 ? Math.min(f, c / (n.length - 1)) : 0;
|
|
221
|
+
n.forEach((c, _) => {
|
|
222
|
+
let v = a + Math.min(d + _ * l, i.height / 2 + _ * l), y;
|
|
223
|
+
if (c.lane) {
|
|
224
|
+
let t = e.closest(".cf-heap-row").getBoundingClientRect(), r = c.lane.side === "below", a = c.lane.index, l = t.left + o, u = c.exitX < l ? Math.max(c.exitX + 6, l - 8 - (c.lane.count - 1 - a) * m) : c.exitX + 8 + a * m, d = r ? t.bottom + s + 7 + a * m : t.top + s - 7 - a * m, p = (e.closest(".cf-object") ?? e).getBoundingClientRect(), v = g(e), b = v ? i.left + o + i.width / 2 + (_ - (n.length - 1) / 2) * f : Math.min(p.left + o + 18 + _ * f, p.right + o - 10), x = v ? i.bottom + s + 1 : (r ? p.bottom : p.top) + s + (r ? 1 : -1), S = r ? -1 : 1, C = Math.min(h, Math.abs(d - c.sy) / 2), w = d < c.sy ? -1 : 1;
|
|
225
|
+
y = `M${c.sx} ${c.sy} H${u - C} Q${u} ${c.sy} ${u} ${c.sy + w * C} V${d - w * C} Q${u} ${d} ${u + C} ${d} H${b - h} Q${b} ${d} ${b} ${d + S * h} V${x}`;
|
|
226
|
+
} else if (i.left + o >= c.exitX + 16) {
|
|
227
|
+
let e = i.left + o - 1, t = c.exitX + 6, n = e - Math.min(p, e - c.exitX - 8), r = Math.max(16, (n - t) / 2);
|
|
228
|
+
y = `M${c.sx} ${c.sy} H${t} C${t + r} ${c.sy} ${n - r} ${v} ${n} ${v} H${e}`;
|
|
229
|
+
} else if (e.classList.contains("cf-cell") && e.nextElementSibling) {
|
|
230
|
+
let t = c.sy > i.bottom + s, r = t ? i.bottom + s + 5 : i.top + s - 5, a = i.left + o + i.width / 2 + (_ - (n.length - 1) / 2) * f, l = Math.max(c.exitX, (e.parentElement ?? e).getBoundingClientRect().right + o + p) + 28 + _ * 8, u = t ? -1 : 1;
|
|
231
|
+
y = `M${c.sx} ${c.sy} H${c.exitX} C${l} ${c.sy} ${l} ${r} ${l - 28} ${r} H${a + 4} Q${a} ${r} ${a} ${r + u * 4} V${t ? i.bottom + s + 1 : i.top + s - 1}`;
|
|
232
|
+
} else {
|
|
233
|
+
let t = (e.matches(".cf-grid th") ? e.parentElement : e).getBoundingClientRect().right + o + 1, n = t + p, r = Math.max(c.exitX, n) + 28 + _ * 8;
|
|
234
|
+
y = `M${c.sx} ${c.sy} H${c.exitX} C${r} ${c.sy} ${r} ${v} ${n} ${v} H${t}`;
|
|
235
|
+
}
|
|
236
|
+
if (c.cell) {
|
|
237
|
+
let { x: e, y: t, next: n } = c.cell;
|
|
238
|
+
if (i.top + s > n && !c.lane) {
|
|
239
|
+
let r = i.left + o + 18 + _ * f, a = r >= e ? 1 : -1, c = Math.min(h, Math.abs(r - e) / 2);
|
|
240
|
+
y = Math.abs(r - e) < 2 ? `M${e} ${t} V${i.top + s - 1}` : `M${e} ${t} V${n - c} Q${e} ${n} ${e + a * c} ${n} H${r - a * c} Q${r} ${n} ${r} ${n + c} V${i.top + s - 1}`;
|
|
241
|
+
} else y = `M${e} ${t} V${n}${y.replace(/^M\S+ \S+/, "")}`;
|
|
242
|
+
}
|
|
243
|
+
let b = document.createElementNS(u, "path");
|
|
244
|
+
b.setAttribute("d", y), c.changed && !c.dangling && !c.weak && b.setAttribute("pathLength", "1");
|
|
245
|
+
let x = c.changed && r !== void 0 ? `${Math.min(0, r - performance.now())}ms` : "";
|
|
246
|
+
b.style.animationDelay = x;
|
|
247
|
+
let S = c.changed ? r === void 0 ? " cf-arrow-pending" : " cf-arrow-new" : "";
|
|
248
|
+
b.setAttribute("class", `cf-arrow${S}${c.dangling ? " cf-arrow-dangling" : c.weak ? " cf-arrow-weak" : ""}`);
|
|
249
|
+
let C = c.dangling ? "url(#cf-head-danger)" : "url(#cf-head)";
|
|
250
|
+
if (c.changed) {
|
|
251
|
+
let e = document.createElementNS(u, "path");
|
|
252
|
+
e.setAttribute("d", y), e.setAttribute("class", `cf-arrow-head${S}`), e.setAttribute("marker-end", C), e.style.animationDelay = x, t.append(b, e);
|
|
253
|
+
} else b.setAttribute("marker-end", C), t.append(b);
|
|
254
|
+
});
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
// ../renderer/src/describe/words.ts
|
|
258
|
+
var y = (e) => /^[aeiou]/i.test(e) || /^[AEFHILMNORSX](?:[A-Z\d]|$)/.test(e) ? "an" : "a", b = (e) => `${y(e)} ${e}`, x = (e) => e.length <= 1 ? e[0] ?? "" : `${e.slice(0, -1).join(", ")} and ${e.at(-1)}`, S = (e) => e.replace(/^the /, "The "), C = (t) => e[t].nullWord, w = (e) => e.k === "ptr" && e.type.endsWith("&"), T = (e) => e === "python", E = (e) => e === "javascript" || e === "typescript", D = (e) => e === "java", O = (t) => e[t].dynamic, ee = (t) => e[t].manualMemory, k = (e) => ee(e) ? "->" : ".";
|
|
259
|
+
// ../renderer/src/describe/values.ts
|
|
260
|
+
function te(e, t) {
|
|
261
|
+
let n = (t, n) => !("truncated" in e && e.truncated) && t.length <= n && t.every((e) => e.k === "prim");
|
|
262
|
+
if (e.k === "array" && n(e.items, 6)) {
|
|
263
|
+
let n = e.items.map((e) => F(e, t)).join(", ");
|
|
264
|
+
return e.type === "Set" ? `{${n}}` : `[${n}]`;
|
|
265
|
+
}
|
|
266
|
+
return e.k === "map" && n(e.entries.flatMap((e) => [e.key, e.value]), 8) ? `{${e.entries.map((e) => `${F(e.key, t)} => ${F(e.value, t)}`).join(", ")}}` : e.k === "record" && e.type !== "function" && e.type !== "class" && n(e.fields.map((e) => e.value), 3) ? `{${e.fields.map((e) => `${e.name}: ${F(e.value, t)}`).join(", ")}}` : null;
|
|
267
|
+
}
|
|
268
|
+
function A(e, t) {
|
|
269
|
+
if (E(t)) return te(e, t);
|
|
270
|
+
if (e.k === "array" && !e.truncated && e.items.length <= 6 && e.items.every((e) => e.k === "prim")) {
|
|
271
|
+
let n = e.items.map((e) => F(e, t));
|
|
272
|
+
return e.type === "tuple" ? n.length === 1 ? `(${n[0]},)` : `(${n.join(", ")})` : e.type === "set" || e.type === "frozenset" ? n.length ? `{${n.join(", ")}}` : `${e.type}()` : `[${n.join(", ")}]`;
|
|
273
|
+
}
|
|
274
|
+
return e.k === "map" && !e.truncated && e.entries.length <= 4 && e.entries.every((e) => e.key.k === "prim" && e.value.k === "prim") ? `{${e.entries.map((e) => `${F(e.key, t)}: ${F(e.value, t)}`).join(", ")}}` : null;
|
|
275
|
+
}
|
|
276
|
+
var ne = /^(?:const )?(?:vector|map|set|multimap|multiset|unordered_(?:multi)?(?:map|set)|deque|list|forward_list|stack|queue|priority_queue)<.*>$/, j = (e, t) => t === "cpp" && ne.test(e);
|
|
277
|
+
function re(e, t, n) {
|
|
278
|
+
let r = e.match(/^(?:const )?(stack|queue|priority_queue)</)?.[1];
|
|
279
|
+
if (!r || t.k !== "array" || t.items.length === 0) return "";
|
|
280
|
+
let i = r === "stack" ? t.items.at(-1) : t.items[0];
|
|
281
|
+
return ` (${r === "queue" ? "front" : "top"}: ${F(i, n)})`;
|
|
282
|
+
}
|
|
283
|
+
var M = (e) => e.replace(/ \(\d+ owners?\)$/, "");
|
|
284
|
+
function N(e, t, n, r = !1) {
|
|
285
|
+
if (e.k === "array" && e.items.length === 0 || e.k === "map" && e.entries.length === 0) return r ? "{}" : "empty";
|
|
286
|
+
if (e.k !== "array") return F(e, t);
|
|
287
|
+
let i = e.items.map((e) => {
|
|
288
|
+
if (e.k === "prim") return e.text;
|
|
289
|
+
let i = e.k === "ptr" && e.target.kind === "heap" && j(e.type, t) ? e.target : void 0, a = i && !r ? n.heap.find((e) => e.id === i.id) : void 0;
|
|
290
|
+
return a ? N(a.value, t, n, !0) : null;
|
|
291
|
+
});
|
|
292
|
+
if (e.items.length <= 6 && !e.truncated && i.every((e) => e !== null)) return `{${i.join(", ")}}`;
|
|
293
|
+
let a = e.items.length;
|
|
294
|
+
return `${b(e.type)} with ${a}${e.truncated ? "+" : ""} ${a === 1 ? "item" : "items"}`;
|
|
295
|
+
}
|
|
296
|
+
function P(e) {
|
|
297
|
+
if (e.k !== "array" || !/char\[/.test(e.type)) return null;
|
|
298
|
+
let t = "";
|
|
299
|
+
for (let n of e.items) {
|
|
300
|
+
if (n.k !== "prim") return null;
|
|
301
|
+
if (n.text === "'\\0'") return t;
|
|
302
|
+
let e = n.text.match(/^'(.*)'$/);
|
|
303
|
+
if (!e) return null;
|
|
304
|
+
t += e[1];
|
|
305
|
+
}
|
|
306
|
+
return null;
|
|
307
|
+
}
|
|
308
|
+
function F(e, t = "c") {
|
|
309
|
+
switch (e.k) {
|
|
310
|
+
case "prim": return e.text;
|
|
311
|
+
case "uninit": return "uninitialized";
|
|
312
|
+
case "ptr": switch (e.target.kind) {
|
|
313
|
+
case "null": return C(t);
|
|
314
|
+
case "string": return `the string literal "${e.target.text}"`;
|
|
315
|
+
case "heap": return O(t) ? b(e.type) : D(t) ? `${b(e.type.replace(/<.*>$/, ""))} object` : `${b(e.type.replace(/\s*[*&]+$/, "") || "value")} on the heap`;
|
|
316
|
+
case "stack":
|
|
317
|
+
case "global": return `${e.target.name}`;
|
|
318
|
+
case "dangling": return "freed memory (dangling)";
|
|
319
|
+
default: return "an invalid address";
|
|
320
|
+
}
|
|
321
|
+
case "array": {
|
|
322
|
+
if (O(t)) return A(e, t) ?? `${b(e.type)} of ${e.items.length}${e.truncated ? "+" : ""} items`;
|
|
323
|
+
let n = P(e);
|
|
324
|
+
if (n !== null) return `"${n}"`;
|
|
325
|
+
if (e.items.length <= 6 && e.items.every((e) => e.k === "prim")) {
|
|
326
|
+
let n = e.items.map((e) => F(e, t)).join(", ");
|
|
327
|
+
return D(t) && !e.type.endsWith("]") ? `[${n}]` : `{${n}}`;
|
|
328
|
+
}
|
|
329
|
+
return `${e.type} with ${e.items.length}${e.truncated ? "+" : ""} ${e.items.length === 1 ? "item" : "items"}`;
|
|
330
|
+
}
|
|
331
|
+
case "record": return e.fields.length <= 3 && e.fields.every((e) => e.value.k === "prim") ? T(t) ? `${e.type}(${e.fields.map((e) => `${e.name}=${F(e.value, t)}`).join(", ")})` : `{${e.fields.map((e) => `${e.name}: ${F(e.value, t)}`).join(", ")}}` : e.type;
|
|
332
|
+
case "map": return O(t) ? A(e, t) ?? `${b(e.type)} of ${e.entries.length} ${E(t) ? "entries" : "items"}` : t === "cpp" && e.entries.length <= 6 && e.entries.every((e) => e.key.k === "prim" && e.value.k === "prim") ? `{${e.entries.map((e) => `${F(e.key, t)}: ${F(e.value, t)}`).join(", ")}}` : `${e.type} with ${e.entries.length} entries`;
|
|
333
|
+
case "opaque": return e.text;
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
function I(e) {
|
|
337
|
+
switch (e.k) {
|
|
338
|
+
case "prim":
|
|
339
|
+
case "opaque": return `${e.k}:${e.text}`;
|
|
340
|
+
case "uninit": return "uninit";
|
|
341
|
+
case "ptr": {
|
|
342
|
+
let t = e.target;
|
|
343
|
+
return t.kind === "null" ? "ptr:null" : t.kind === "string" ? `ptr:str:${t.text}` : `ptr:${e.address}`;
|
|
344
|
+
}
|
|
345
|
+
case "array": return `[${e.items.map(I).join(",")}]`;
|
|
346
|
+
case "record": return `{${e.fields.map((e) => `${e.name}=${I(e.value)}`).join(",")}}`;
|
|
347
|
+
case "map": return `{${e.entries.map((e) => `${I(e.key)}:${I(e.value)}`).join(",")}}`;
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
function L(e, t, n) {
|
|
351
|
+
if (e.k === "ptr" && e.target.kind === "heap") {
|
|
352
|
+
let r = e.target.id, i = t.heap.find((e) => e.id === r);
|
|
353
|
+
return O(n) ? ie(i, n) : i && j(e.type, n) ? N(i.value, n, t, !0) : R(i, i?.type ?? "value");
|
|
354
|
+
}
|
|
355
|
+
return F(e, n);
|
|
356
|
+
}
|
|
357
|
+
function ie(e, t) {
|
|
358
|
+
let n = e && A(e.value, t);
|
|
359
|
+
return n ? `the ${e.type} ${n}` : e?.value.k === "record" ? R(e, e.type) : e ? F(e.value, t) : "an object";
|
|
360
|
+
}
|
|
361
|
+
function R(e, t) {
|
|
362
|
+
t = M(t);
|
|
363
|
+
let n = e?.value.k === "record" ? e.value.fields.find((e) => e.value.k === "prim") : void 0;
|
|
364
|
+
return n ? `the ${t} with ${n.name} ${n.value.k === "prim" ? n.value.text : ""}` : /^(function|class) /.test(t) ? `the ${t}` : b(t);
|
|
365
|
+
}
|
|
366
|
+
// ../renderer/src/describe/names.ts
|
|
367
|
+
function ae(e, t = "c", n = "") {
|
|
368
|
+
let r = k(t), i = /* @__PURE__ */ new Map(), a = new Map(e.heap.map((e) => [e.id, e])), o = [], s = (e, t) => {
|
|
369
|
+
if (e.k === "ptr" && e.target.kind === "heap" && !i.has(e.target.id)) {
|
|
370
|
+
let n = a.get(e.target.id);
|
|
371
|
+
if (!n) return;
|
|
372
|
+
i.set(n.id, {
|
|
373
|
+
label: t,
|
|
374
|
+
viaRecord: n.value.k === "record"
|
|
375
|
+
}), o.push({
|
|
376
|
+
id: n.id,
|
|
377
|
+
label: t
|
|
378
|
+
});
|
|
379
|
+
}
|
|
380
|
+
}, c = (e) => !!n && RegExp(`\\b${e.name.replace(/\$/g, "\\$")}\\b`).test(n);
|
|
381
|
+
for (let t of [...e.frames].reverse()) for (let e of [...t.locals.filter(c), ...t.locals.filter((e) => !c(e))]) s(e.value, e.name);
|
|
382
|
+
for (let t of e.globals) s(t.value, t.name);
|
|
383
|
+
for (; o.length;) {
|
|
384
|
+
let { id: e, label: n } = o.shift(), i = a.get(e);
|
|
385
|
+
if (!(n.split(r === "." ? /\.|\[/ : /->|\[/).length > 4)) {
|
|
386
|
+
if (i.value.k === "record") for (let e of i.value.fields) s(e.value, `${n}${r}${e.name}`);
|
|
387
|
+
if (i.value.k === "array" && i.value.items.forEach((e, t) => s(e, `${n}[${t}]`)), i.value.k === "map" && T(t)) for (let e of i.value.entries) s(e.value, `${n}[${F(e.key, t)}]`);
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
return i;
|
|
391
|
+
}
|
|
392
|
+
function oe(e, t, n) {
|
|
393
|
+
let r = (e) => n === e || n.startsWith(`${e}.`) || n.startsWith(`${e}[`);
|
|
394
|
+
for (let [n, i] of [...e.frames].reverse().entries()) {
|
|
395
|
+
let e = i.locals.filter((e) => e.value.k === "ptr" && e.value.target.kind === "heap" && e.value.target.id === t && !r(e.name)).map((e) => n === 0 ? e.name : `${V(i.name, "java")}'s ${e.name}`);
|
|
396
|
+
if (e.length) return e;
|
|
397
|
+
}
|
|
398
|
+
return [];
|
|
399
|
+
}
|
|
400
|
+
var z = (e) => {
|
|
401
|
+
let t = e?.locals.find((e) => e.name === "this")?.value;
|
|
402
|
+
return t?.k === "ptr" && t.target.kind === "heap" ? t.target.id : void 0;
|
|
403
|
+
};
|
|
404
|
+
function se(e, t, n = []) {
|
|
405
|
+
let [r, i] = [e.at(-2), e.at(-1)];
|
|
406
|
+
if (E(t) && r && i && B(i.name, t) && /\bsuper\s*\(/.test(n[r.line - 1] ?? "")) return B(r.name, t);
|
|
407
|
+
if (!D(t) || !r || !i || !B(i.name, t)) return;
|
|
408
|
+
let a = z(i);
|
|
409
|
+
return a && z(r) === a ? B(r.name, t) : void 0;
|
|
410
|
+
}
|
|
411
|
+
function ce(e, t, n, r) {
|
|
412
|
+
let i = ae(e, r), a = (e) => e.k === "ptr" && e.target.kind === "heap" && e.target.id === t;
|
|
413
|
+
for (let t of e.heap) {
|
|
414
|
+
let e = i.get(t.id)?.label;
|
|
415
|
+
if (!(!e || e === n || e.startsWith(`${n}[`) || e.startsWith(`${n}${k(r)}`))) {
|
|
416
|
+
if (t.value.k === "array") {
|
|
417
|
+
let n = t.value.items.findIndex(a);
|
|
418
|
+
if (n >= 0) return `${e}[${n}]`;
|
|
419
|
+
} else if (t.value.k === "record") {
|
|
420
|
+
let n = t.value.fields.find((e) => a(e.value));
|
|
421
|
+
if (n) return `${e}${k(r)}${n.name}`;
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
function le(e, t) {
|
|
427
|
+
return [...e.frames.at(-1)?.locals ?? [], ...e.globals].filter((e) => e.value.k === "ptr" && e.value.target.kind === "heap" && e.value.target.id === t).map((e) => e.name);
|
|
428
|
+
}
|
|
429
|
+
var B = (e, t = "c") => T(t) ? e.match(/^(\w+)\.__init__$/)?.[1] : E(t) ? e.match(/^([\w$]+)\.constructor$/)?.[1] : e.match(/(?:^|::)(\w+)::\1$/)?.[1] ?? e.match(/(\w+)\.<init>$/)?.[1], V = (e, t = "c") => {
|
|
430
|
+
let n = B(e, t);
|
|
431
|
+
return n ? T(t) ? e : `the ${n} constructor` : (D(t) || E(t)) && /^[\w$.]+\.[\w$]+$/.test(e) ? e.split(".").pop() : e.includes("::") ? e.split("::").pop() : e;
|
|
432
|
+
};
|
|
433
|
+
function ue(e, t) {
|
|
434
|
+
if (!e.name.startsWith("lambda in ")) return null;
|
|
435
|
+
for (let n = e.line; n > 0 && n > e.line - 10; n--) {
|
|
436
|
+
let e = t[n - 1] ?? "";
|
|
437
|
+
if (!e.includes("->")) continue;
|
|
438
|
+
let r = [...e.matchAll(/\.\s*(\w+)\s*\(\s*(?:\([^()]*\)|[\w$]+)\s*->/g)].map((e) => e[1]);
|
|
439
|
+
return r.length === 1 ? {
|
|
440
|
+
name: `the lambda passed to ${r[0]}`,
|
|
441
|
+
passedTo: r[0]
|
|
442
|
+
} : { name: `the lambda on line ${n}` };
|
|
443
|
+
}
|
|
444
|
+
return { name: "the lambda" };
|
|
445
|
+
}
|
|
446
|
+
var H = (e, t, n) => (D(t) ? ue(e, n)?.name : void 0) ?? V(e.name, t), U = "the main program";
|
|
447
|
+
function de(e, t, n = "c", r = []) {
|
|
448
|
+
let i = D(n) ? ue(e.frames.at(-1), r)?.passedTo : void 0;
|
|
449
|
+
if (i) return i;
|
|
450
|
+
let a = e.frames.at(-2)?.name;
|
|
451
|
+
return a ? a === t ? `the ${V(t, n)} call that called it` : V(a, n) : O(n) ? U : "its caller";
|
|
452
|
+
}
|
|
453
|
+
// ../renderer/src/describe/diff.ts
|
|
454
|
+
var fe = (e) => e.k === "uninit" || e.k === "array" && e.items.every(fe) || e.k === "record" && e.fields.every((e) => fe(e.value));
|
|
455
|
+
function pe(e, t, n, r, i, a, o) {
|
|
456
|
+
let s = [];
|
|
457
|
+
i.forEach((i, a) => W(`${e}.${i.name}`, `${t}${n}${i.name}`, r[a]?.value, i.value, s, o));
|
|
458
|
+
let c = s.filter((e) => {
|
|
459
|
+
let r = e.label.slice(t.length + n.length).split(/[.[>-]/)[0];
|
|
460
|
+
return RegExp(`(?:\\.|->)\\s*${r}\\b`).test(o);
|
|
461
|
+
});
|
|
462
|
+
if (s.length < 2 || c.length !== 1) return void a.push(...s);
|
|
463
|
+
let [l] = c;
|
|
464
|
+
a.push({
|
|
465
|
+
...l,
|
|
466
|
+
shares: s.filter((e) => e !== l).map((e) => ({
|
|
467
|
+
label: e.label,
|
|
468
|
+
value: e.after
|
|
469
|
+
}))
|
|
470
|
+
});
|
|
471
|
+
}
|
|
472
|
+
function W(e, t, n, r, i, a = "") {
|
|
473
|
+
if (n && I(n) === I(r)) return;
|
|
474
|
+
let o = !n || n.k === "uninit";
|
|
475
|
+
if (r.k === "array" && n?.k === "array" && n.items.length === r.items.length) {
|
|
476
|
+
let o = [];
|
|
477
|
+
r.items.forEach((r, i) => W(`${e}[${i}]`, `${t}[${i}]`, n.items[i], r, o, a));
|
|
478
|
+
let s = n.items.every((e) => e.k === "uninit");
|
|
479
|
+
o.length > 2 || s && o.length > 1 || P(r) !== null && o.length > 1 ? i.push({
|
|
480
|
+
path: e,
|
|
481
|
+
label: t,
|
|
482
|
+
before: n,
|
|
483
|
+
after: r,
|
|
484
|
+
whole: !0
|
|
485
|
+
}) : i.push(...o);
|
|
486
|
+
} else if (r.k === "record" && n?.k === "record" && n.fields.length === r.fields.length) {
|
|
487
|
+
if (r.union) return pe(e, t, ".", n.fields, r.fields, i, a);
|
|
488
|
+
r.fields.forEach((r, o) => W(`${e}.${r.name}`, `${t}.${r.name}`, n.fields[o]?.value, r.value, i, a));
|
|
489
|
+
} else o && fe(r) || i.push({
|
|
490
|
+
path: e,
|
|
491
|
+
label: t,
|
|
492
|
+
before: o ? void 0 : n,
|
|
493
|
+
after: r,
|
|
494
|
+
whole: r.k === "array" || r.k === "record"
|
|
495
|
+
});
|
|
496
|
+
}
|
|
497
|
+
function me(e, t, n, r) {
|
|
498
|
+
if (n.k !== "map" || r.k !== "map") return null;
|
|
499
|
+
let i = new Map(n.entries.map((e) => [I(e.key), e.value]));
|
|
500
|
+
return n.entries.some((e) => !r.entries.some((t) => I(t.key) === I(e.key))) ? null : r.entries.flatMap((n) => {
|
|
501
|
+
let r = i.get(I(n.key));
|
|
502
|
+
return r && I(r) === I(n.value) ? [] : [{
|
|
503
|
+
path: e,
|
|
504
|
+
label: `${t}[${F(n.key, "cpp")}]`,
|
|
505
|
+
before: r,
|
|
506
|
+
after: n.value
|
|
507
|
+
}];
|
|
508
|
+
});
|
|
509
|
+
}
|
|
510
|
+
function he(e, t, n = "c", r = "") {
|
|
511
|
+
if (!e) return [];
|
|
512
|
+
let i = [], a = new Map(e.frames.map((e) => [e.id, e]));
|
|
513
|
+
for (let n of t.globals) {
|
|
514
|
+
let t = e.globals.find((e) => e.name === n.name);
|
|
515
|
+
W(`global:${n.name}`, n.name, t?.value, n.value, i, r);
|
|
516
|
+
}
|
|
517
|
+
for (let e of t.frames) {
|
|
518
|
+
let t = a.get(e.id);
|
|
519
|
+
if (t) for (let n of e.locals) {
|
|
520
|
+
let a = t.locals.find((e) => e.name === n.name);
|
|
521
|
+
W(`var:${e.id}:${n.name}`, n.name, a?.value, n.value, i, r);
|
|
522
|
+
}
|
|
523
|
+
}
|
|
524
|
+
let o = ae(t, n, r);
|
|
525
|
+
for (let a of t.heap) {
|
|
526
|
+
let t = e.heap.find((e) => e.id === a.id);
|
|
527
|
+
if (!t) continue;
|
|
528
|
+
let s = o.get(a.id), c = s ? s.label : `the ${a.type}`, l = [];
|
|
529
|
+
if (a.value.k === "record" && t.value.k === "record" && a.value.union && a.value.fields.length === t.value.fields.length) pe(`heap:${a.id}`, c, k(n), t.value.fields, a.value.fields, l, r);
|
|
530
|
+
else if (a.value.k === "record" && t.value.k === "record") a.value.fields.forEach((e, i) => W(`heap:${a.id}.${e.name}`, `${c}${k(n)}${e.name}`, t.value.fields[i]?.value, e.value, l, r));
|
|
531
|
+
else if (a.value.k === "array" && t.value.k === "array") W(`heap:${a.id}`, c, t.value, a.value, l);
|
|
532
|
+
else if (j(a.type, n)) {
|
|
533
|
+
let e = me(`heap:${a.id}`, c, t.value, a.value);
|
|
534
|
+
e ? l.push(...e) : I(t.value) !== I(a.value) && l.push({
|
|
535
|
+
path: `heap:${a.id}`,
|
|
536
|
+
label: c,
|
|
537
|
+
before: t.value,
|
|
538
|
+
after: a.value,
|
|
539
|
+
whole: !0
|
|
540
|
+
});
|
|
541
|
+
} else W(`heap:${a.id}`, s && ee(n) ? `*${c}` : c, t.value, a.value, l);
|
|
542
|
+
i.push(...l);
|
|
543
|
+
}
|
|
544
|
+
return i.filter((e) => !G(e, t));
|
|
545
|
+
}
|
|
546
|
+
function G(e, t) {
|
|
547
|
+
let n = e.before, r = e.after;
|
|
548
|
+
if (n?.k !== "opaque" || r.k !== "ptr" || r.target.kind !== "heap") return !1;
|
|
549
|
+
let i = r.target.id;
|
|
550
|
+
return t.heap.some((e) => e.id === i && e.type === n.text);
|
|
551
|
+
}
|
|
552
|
+
function ge(e) {
|
|
553
|
+
let { before: t, after: n } = e;
|
|
554
|
+
if (n.k === "array" && t?.k === "array" && n.items.length > t.items.length && t.items.every((e, t) => I(e) === I(n.items[t]))) return n.items.slice(t.items.length).map((n, r) => `${e.path}[${t.items.length + r}]`);
|
|
555
|
+
if (n.k === "map" && t?.k === "map") {
|
|
556
|
+
let r = new Map(t.entries.map((e) => [I(e.key), I(e.value)])), i = n.entries.filter((e) => r.get(I(e.key)) !== I(e.value)).map((t) => `${e.path}{${I(t.key)}}`);
|
|
557
|
+
return i.length ? i : null;
|
|
558
|
+
}
|
|
559
|
+
return null;
|
|
560
|
+
}
|
|
561
|
+
function _e(e, t) {
|
|
562
|
+
let n = /* @__PURE__ */ new Set();
|
|
563
|
+
for (let r of he(e, t)) {
|
|
564
|
+
let e = r.path.match(/^(var:[^:]+:[^.[]+|global:[^.[]+|heap:[^.[]+)/)?.[1], t = ge(r);
|
|
565
|
+
if (t) {
|
|
566
|
+
for (let e of t) n.add(e);
|
|
567
|
+
e && n.add(`${e}~`);
|
|
568
|
+
continue;
|
|
569
|
+
}
|
|
570
|
+
n.add(r.path);
|
|
571
|
+
let i = r.path.match(/^(var:[^:]+:[^.[]+|global:[^.[]+|heap:[^.[]+)/)?.[1];
|
|
572
|
+
i && i === r.path && n.add(i), i && n.add(`${i}~`);
|
|
573
|
+
}
|
|
574
|
+
return n;
|
|
575
|
+
}
|
|
576
|
+
// ../renderer/src/describe/source.ts
|
|
577
|
+
var K = /\bcatch\s*\(/;
|
|
578
|
+
function ve(e, t, n = "c") {
|
|
579
|
+
let r = e[t - 1] ?? "";
|
|
580
|
+
if (T(n)) {
|
|
581
|
+
let n = r.match(/^(\s*)(if|elif|while|for)\s+(.+):\s*(?:#.*)?$/);
|
|
582
|
+
if (!n) return null;
|
|
583
|
+
let i = n[2] === "for" ? n[3].match(/^.+?\s+in\s+(.+)$/)?.[1] : void 0;
|
|
584
|
+
if (n[2] === "for" && !i) return null;
|
|
585
|
+
let a = n[1].length, o = t;
|
|
586
|
+
for (let n = t + 1; n <= e.length; n++) {
|
|
587
|
+
let t = e[n - 1] ?? "";
|
|
588
|
+
if (t.trim() && !/^\s*#/.test(t)) {
|
|
589
|
+
if ((t.match(/^\s*/)?.[0].length ?? 0) <= a) break;
|
|
590
|
+
o = n;
|
|
591
|
+
}
|
|
592
|
+
}
|
|
593
|
+
return o === t ? null : i ? {
|
|
594
|
+
kind: "for",
|
|
595
|
+
condition: "",
|
|
596
|
+
body: [t + 1, o],
|
|
597
|
+
range: i.trim()
|
|
598
|
+
} : {
|
|
599
|
+
kind: n[2] === "while" ? "while" : "if",
|
|
600
|
+
condition: n[3].trim(),
|
|
601
|
+
body: [t + 1, o]
|
|
602
|
+
};
|
|
603
|
+
}
|
|
604
|
+
let i = r.match(/^\s*(?:\}\s*else\s+)?(if|while|for)\s*\(/);
|
|
605
|
+
if (!i) return null;
|
|
606
|
+
let a = 0, o = r.indexOf("(", i.index + i[0].length - 1), s = o + 1;
|
|
607
|
+
for (; o < r.length; o++) if (r[o] === "(") a++;
|
|
608
|
+
else if (r[o] === ")" && --a === 0) break;
|
|
609
|
+
if (a !== 0) return null;
|
|
610
|
+
let c = r.slice(s, o).trim(), l;
|
|
611
|
+
if (i[1] === "for" && !c.includes(";")) {
|
|
612
|
+
let e = E(n) ? /^(?:(?:const|let|var)\s+)?.+?\s+(?:of|in)\s+(.+)$/ : /^.*?[^:]:(?!:)\s*(.+)$/;
|
|
613
|
+
l = c.match(e)?.[1]?.trim(), c = "";
|
|
614
|
+
} else i[1] === "for" && (c = (c.split(";")[1] ?? "").trim());
|
|
615
|
+
let u = r.slice(o + 1).trim(), d;
|
|
616
|
+
if (u.startsWith("{")) {
|
|
617
|
+
let n = 0, r = t;
|
|
618
|
+
for (let i = t; i <= e.length; i++) {
|
|
619
|
+
let a = i === t ? u : e[i - 1] ?? "";
|
|
620
|
+
for (let e of a) n += e === "{" ? 1 : e === "}" ? -1 : 0;
|
|
621
|
+
if (n <= 0) {
|
|
622
|
+
r = i;
|
|
623
|
+
break;
|
|
624
|
+
}
|
|
625
|
+
}
|
|
626
|
+
d = [t + 1, Math.max(t, r - 1)];
|
|
627
|
+
} else d = u && u !== "{" ? [t, t] : [t + 1, t + 1];
|
|
628
|
+
return {
|
|
629
|
+
kind: i[1],
|
|
630
|
+
condition: c,
|
|
631
|
+
body: d,
|
|
632
|
+
...l ? { range: l } : {}
|
|
633
|
+
};
|
|
634
|
+
}
|
|
635
|
+
function ye(e, t) {
|
|
636
|
+
let n = 0;
|
|
637
|
+
for (let r = t - 1; r >= 1; r--) {
|
|
638
|
+
let t = (e[r - 1] ?? "").replace(/\/\/.*$/, "");
|
|
639
|
+
for (let i = t.length - 1; i >= 0; i--) if (t[i] === "}") n++;
|
|
640
|
+
else if (t[i] === "{" && n-- === 0) {
|
|
641
|
+
let i = /\bswitch\s*\(/.test(t) ? "switch" : /\b(?:for|while)\s*\(|\bdo\b/.test(t) ? "loop" : null;
|
|
642
|
+
if (!i) {
|
|
643
|
+
n = 0;
|
|
644
|
+
continue;
|
|
645
|
+
}
|
|
646
|
+
let a = 0;
|
|
647
|
+
for (let t = r; t <= e.length; t++) {
|
|
648
|
+
for (let n of e[t - 1] ?? "") a += n === "{" ? 1 : n === "}" ? -1 : 0;
|
|
649
|
+
if (a <= 0) return {
|
|
650
|
+
kind: i,
|
|
651
|
+
start: r,
|
|
652
|
+
end: t
|
|
653
|
+
};
|
|
654
|
+
}
|
|
655
|
+
return {
|
|
656
|
+
kind: i,
|
|
657
|
+
start: r,
|
|
658
|
+
end: e.length
|
|
659
|
+
};
|
|
660
|
+
}
|
|
661
|
+
}
|
|
662
|
+
return null;
|
|
663
|
+
}
|
|
664
|
+
var be = /^\s*(?:async\s+)?for\b/;
|
|
665
|
+
function xe(e, t, n, r = "c") {
|
|
666
|
+
let i = ve(e, t.line, r), a = r === "cpp" || D(r) || O(r) ? i?.range : void 0;
|
|
667
|
+
if (!i || !i.condition && !a) return "";
|
|
668
|
+
let [o, s] = i.body, c = o === t.line, l = D(r) && c && i.kind === "if" && /\)\s*break\s*;\s*(?:\/\/.*)?$/.test(e[t.line - 1] ?? "") ? ye(e, t.line) : null, u;
|
|
669
|
+
if (l) return u = n.line < l.start || n.line > l.end, `${i.condition} is ${u ? "true" : "false"}, ${u ? `so break leaves the ${l.kind}` : "so its body is skipped"}`;
|
|
670
|
+
if (c) {
|
|
671
|
+
if (n.event === "return" && !/\breturn\b/.test(e[t.line - 1] ?? "")) return "";
|
|
672
|
+
if (n.event === "return") u = !0;
|
|
673
|
+
else if (n.line !== t.line) u = i.kind === "if" && (he(t, n, r).length > 0 || n.stdoutLength > t.stdoutLength);
|
|
674
|
+
else if (i.kind === "if" && !O(r) && n.col !== void 0 && t.col !== void 0 && n.col > t.col) u = !0;
|
|
675
|
+
else return "";
|
|
676
|
+
} else u = n.line >= o && n.line <= s;
|
|
677
|
+
if (a && O(r)) return u ? "" : `the loop has gone through every item in ${a}, so it ends`;
|
|
678
|
+
if (a) return u ? "" : `the loop has been through every element of ${a}, so it ends`;
|
|
679
|
+
if (T(r) && i.kind === "while" && /^(True|1)$/.test(i.condition)) return u ? `while ${i.condition} keeps looping until something breaks out of it` : "";
|
|
680
|
+
if (E(r) && i.kind === "while" && i.condition === "true") return u ? "while (true) keeps looping until something breaks out of it" : "";
|
|
681
|
+
let d = i.kind === "if" ? u ? "so its body runs" : "so its body is skipped" : u ? "so the loop runs" : "so the loop ends";
|
|
682
|
+
return `${i.condition} is ${u ? "true" : "false"}, ${d}`;
|
|
683
|
+
}
|
|
684
|
+
function Se(e, t, n) {
|
|
685
|
+
let r = e.match(/^([A-Za-z_]\w*)((?:\s*(?:->|\.)\s*[A-Za-z_]\w*|\s*\[[^\]]*\])*)$/);
|
|
686
|
+
if (!r) return;
|
|
687
|
+
let i = (e) => [...t.locals, ...n.globals].find((t) => t.name === e)?.value, a = i(r[1]), o = (e) => {
|
|
688
|
+
if (e?.k !== "ptr" || e.target.kind !== "heap") return;
|
|
689
|
+
let t = e.target.id;
|
|
690
|
+
return n.heap.find((e) => e.id === t)?.value;
|
|
691
|
+
};
|
|
692
|
+
for (let [, e, t, n] of r[2].matchAll(/\s*(->|\.|\[)\s*([A-Za-z_]\w*)?([^\]]*)\]?/g)) if ((e === "->" || e === "." && a?.k === "ptr") && (a = o(a)), e === "->" || e === ".") a = a?.k === "record" ? a.fields.find((e) => e.name === t)?.value : void 0;
|
|
693
|
+
else {
|
|
694
|
+
let e = `${t ?? ""}${n ?? ""}`.trim(), r = /^\d+$/.test(e) ? Number(e) : Number(((e) => e?.k === "prim" ? e.text : NaN)(i(e))), s = a?.k === "array" ? a : o(a);
|
|
695
|
+
a = s?.k === "array" && Number.isInteger(r) ? s.items[r] : void 0;
|
|
696
|
+
}
|
|
697
|
+
return a;
|
|
698
|
+
}
|
|
699
|
+
// ../renderer/src/describe/cpp.ts
|
|
700
|
+
var q = /^(?:const )?(unique|shared)_ptr</, Ce = /^(?:const )?weak_ptr</;
|
|
701
|
+
function J(e, t) {
|
|
702
|
+
return [...e.frames.flatMap((e) => e.locals), ...e.globals].filter((e) => q.test(e.type) && e.value.k === "ptr" && e.value.target.kind === "heap" && e.value.target.id === t);
|
|
703
|
+
}
|
|
704
|
+
function we(e, t, n, r) {
|
|
705
|
+
if (r === "cpp" && Ce.test(e.after.type)) return Te(e, t);
|
|
706
|
+
let i = r === "cpp" ? e.after.type.match(q)?.[1] : void 0;
|
|
707
|
+
if (!i || e.after.k !== "ptr") return null;
|
|
708
|
+
let a = e.after.target;
|
|
709
|
+
if (a.kind === "null") {
|
|
710
|
+
let n = e.before?.k === "ptr" && e.before.target.kind === "heap" ? e.before.target.id : void 0, r = n ? t.heap.find((e) => e.id === n) : void 0, a = n && i === "shared" ? J(t, n).map((e) => e.name) : [], o = r && a.length ? ` (${x(a)} ${a.length > 1 ? "still own" : "still owns"} the ${M(r.type)})` : "";
|
|
711
|
+
return `${e.label} is now empty${o}`;
|
|
712
|
+
}
|
|
713
|
+
if (a.kind !== "heap") return null;
|
|
714
|
+
let o = t.heap.find((e) => e.id === a.id), s = M(o?.type ?? "object");
|
|
715
|
+
if (!n.heap.some((e) => e.id === a.id)) return `${e.label} now owns a new ${s}`;
|
|
716
|
+
let c = o?.value.k === "record" ? R({
|
|
717
|
+
...o,
|
|
718
|
+
type: s
|
|
719
|
+
}, s) : `the ${s}`, l = J(n, a.id).find((n) => n.name !== e.label && !J(t, a.id).some((e) => e.name === n.name));
|
|
720
|
+
if (i === "unique" || l) return `${e.label} now owns ${c}${l ? ` (moved from ${l.name})` : ""}`;
|
|
721
|
+
let u = J(t, a.id).filter((t) => t.name !== e.label).map((e) => e.name), d = u.length + 1;
|
|
722
|
+
return `${e.label} now shares the ${s}${u.length ? ` with ${x(u)}` : ""} (${d} owners)`;
|
|
723
|
+
}
|
|
724
|
+
function Te(e, t) {
|
|
725
|
+
let n = e.after;
|
|
726
|
+
if (n.k === "opaque" && n.text === "expired") return `${e.label} has expired`;
|
|
727
|
+
if (n.k !== "ptr") return null;
|
|
728
|
+
if (n.target.kind === "null") return `${e.label} is now empty`;
|
|
729
|
+
if (n.target.kind !== "heap") return null;
|
|
730
|
+
let r = n.target.id, i = J(t, r).map((e) => e.name), a = M(t.heap.find((e) => e.id === r)?.type ?? "object");
|
|
731
|
+
return `${e.label} now watches the ${a} without owning it${i.length ? ` (${x(i)} ${i.length > 1 ? "own" : "owns"} it)` : ""}`;
|
|
732
|
+
}
|
|
733
|
+
function Ee(e, t, n) {
|
|
734
|
+
if (!t || !e.path.startsWith("var:")) return null;
|
|
735
|
+
let r = e.path.split(":")[2]?.split(/[.[]/)[0];
|
|
736
|
+
for (let e of t.locals) {
|
|
737
|
+
if (e.value.k !== "ptr" || e.value.target.kind !== "stack" || e.value.target.name !== r) continue;
|
|
738
|
+
let t = e.name.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
739
|
+
if (RegExp(`\\*\\s*${t}\\b|\\b${t}\\s*(->|\\[)`).test(n) && e.name !== r) return e.name;
|
|
740
|
+
}
|
|
741
|
+
return null;
|
|
742
|
+
}
|
|
743
|
+
function De(e) {
|
|
744
|
+
let t = e.type.replace(/^std::/, "");
|
|
745
|
+
return /^std::/.test(e.type) && e.what !== void 0 ? `${b(t)} with the message ${e.what}` : e.what === void 0 ? b(t) : `the ${t} ${e.what}`;
|
|
746
|
+
}
|
|
747
|
+
var Oe = /^\s*}\s*;?\s*(?:\/\/.*)?$/;
|
|
748
|
+
function Y(e, t, n, r) {
|
|
749
|
+
let i = e.thrown, a = [i.rethrown ? `Line ${i.line} rethrows the ${i.type.replace(/^std::/, "")} it caught, so it goes on looking for another catch.` : `Line ${i.line} throws ${De(i)}.`];
|
|
750
|
+
return a.push(X(e, t, n, r, "it")), a.join(" ");
|
|
751
|
+
}
|
|
752
|
+
function X(e, t, n, r, i) {
|
|
753
|
+
let a = t.frames.filter((t) => !e.frames.some((e) => e.id === t.id)).reverse().map((e) => V(e.name, r)), o = e.frames.at(-1), s = o ? V(o.name, r) : "", c = n[e.line - 1] ?? "", l = a.length ? `${x(a)} ${a.length > 1 ? "stop" : "stops"} without returning, and ` : "", u = a.length ? ` in ${s}` : "";
|
|
754
|
+
return K.test(c) ? a.length ? `${l}the catch on line ${e.line}${u} catches ${i}.` : `The rest of the try block is skipped, and the catch on line ${e.line} catches ${i}.` : Oe.test(c) ? `${l}${l ? "on" : "On"} its way out, C++ cleans up at line ${e.line}${u}: objects in the blocks it leaves are destroyed.` : o && /~/.test(o.name) ? `On the way out, C++ runs ${V(o.name, r)} to clean up.` : `${l}${l ? "the" : "The"} program carries on at line ${e.line}${u}.`;
|
|
755
|
+
}
|
|
756
|
+
function ke(e, t, n) {
|
|
757
|
+
let r = e.steps[t], i = e.steps[t - 1];
|
|
758
|
+
if (r.event !== "step" || r.thrown) return;
|
|
759
|
+
let a = i.event !== "return" && i.frames.some((e) => !r.frames.some((t) => t.id === e.id)), o = r.line !== i.line && K.test(n[r.line - 1] ?? "");
|
|
760
|
+
if (a || o) for (let r = t - 1; r > 0; r--) {
|
|
761
|
+
let i = e.steps[r];
|
|
762
|
+
if (i.thrown) return i.thrown;
|
|
763
|
+
if (r < t - 1 && K.test(n[i.line - 1] ?? "") && i.line !== e.steps[r - 1].line) return;
|
|
764
|
+
}
|
|
765
|
+
}
|
|
766
|
+
// ../renderer/src/describe/java.ts
|
|
767
|
+
function Ae(e, t, n, r = "") {
|
|
768
|
+
let i = e.after;
|
|
769
|
+
if (i.k === "opaque" && e.before?.k === "opaque" && i.text.endsWith(" (used)") && !e.before.text.endsWith(" (used)")) return `${e.label} is used up: a stream can be used only once`;
|
|
770
|
+
if (i.k !== "ptr") return null;
|
|
771
|
+
if (i.target.kind === "null") return `${e.label} is now null: it doesn't refer to any object`;
|
|
772
|
+
if (i.target.kind !== "heap") return null;
|
|
773
|
+
let a = i.target.id, o = t.heap.find((e) => e.id === a), s = o?.type ?? "object";
|
|
774
|
+
if (!n.heap.some((e) => e.id === a)) return je(e.label, s, r) ?? Me(e.label, o) ?? `${e.label} now refers to a new ${s}`;
|
|
775
|
+
let c = oe(t, a, e.label);
|
|
776
|
+
if (c.length) return `${e.label} now refers to the same ${s} as ${x(c)}`;
|
|
777
|
+
let l = ce(t, a, e.label, "java");
|
|
778
|
+
return l ? `${e.label} now refers to the same ${s} as ${l}` : `${e.label} now refers to ${R(o, s)}`;
|
|
779
|
+
}
|
|
780
|
+
function je(e, t, n) {
|
|
781
|
+
let r = t.match(/\.(KeySet|Values|EntrySet|SubList)$/)?.[1];
|
|
782
|
+
if (!r) return null;
|
|
783
|
+
let i = n.match(/([\w.]+?)\s*\.\s*(?:keySet|values|entrySet|subList|navigableKeySet)\s*\(/)?.[1], a = {
|
|
784
|
+
KeySet: "keys",
|
|
785
|
+
Values: "values",
|
|
786
|
+
EntrySet: "entries",
|
|
787
|
+
SubList: "part"
|
|
788
|
+
}[r];
|
|
789
|
+
return `${e} now refers to ${i ? `${r === "SubList" ? "part" : `the ${a}`} of ${i}` : `${r === "SubList" ? "part" : `the ${a}`} of another collection`}, a live view: a change to either shows in both`;
|
|
790
|
+
}
|
|
791
|
+
function Me(e, t) {
|
|
792
|
+
return !t || !/^(Int|Long|Double)?Stream$/.test(t.type) || t.value.k !== "opaque" ? null : `${e} now refers to a new ${t.type} (${t.value.text}). Nothing has run yet: a stream does its work when an operation like collect or forEach asks it for elements`;
|
|
793
|
+
}
|
|
794
|
+
function Ne(e, t) {
|
|
795
|
+
let n = e.path.match(/^heap:([^.[]+)/)?.[1];
|
|
796
|
+
if (!n) return "";
|
|
797
|
+
let r = oe(t, n, e.label)[0];
|
|
798
|
+
if (!r) return "";
|
|
799
|
+
let i = e.label.match(/^[\w$]+/)?.[0] ?? e.label, a = t.heap.find((e) => e.id === n)?.type ?? "object";
|
|
800
|
+
return e.after.k === "prim" ? ` (${r} refers to the same ${a}, so ${r.replace(/^.*'s /, "")}${e.label.slice(i.length)} is ${e.after.text} too)` : ` (${r} refers to the same ${a}, so it sees the change too)`;
|
|
801
|
+
}
|
|
802
|
+
function Pe(e, t, n) {
|
|
803
|
+
let r = e.after;
|
|
804
|
+
if (r.k !== "array" || r.type.endsWith("]") || !r.items.some((e) => e.k === "ptr" && e.target.kind === "heap")) return null;
|
|
805
|
+
let i = r.items.length, a = `${e.label} now has ${i}${r.truncated ? "+" : ""} ${i === 1 ? "item" : "items"}`, o = t.frames.at(-1), s = (e) => {
|
|
806
|
+
if (e.k !== "ptr" || e.target.kind !== "heap") return F(e, "java");
|
|
807
|
+
let r = e.target.id, i = t.heap.find((e) => e.id === r), a = i?.type ?? "object";
|
|
808
|
+
if (!n.heap.some((e) => e.id === r)) return `a new ${a}`;
|
|
809
|
+
let s = o?.locals.find((e) => e.value.k === "ptr" && e.value.target.kind === "heap" && e.value.target.id === r)?.name;
|
|
810
|
+
return s ? `the same ${a} as ${s}` : R(i, a);
|
|
811
|
+
};
|
|
812
|
+
return i <= 3 && !r.truncated ? `${a}: ${x(r.items.map(s))}` : (e.before?.k === "array" ? e.before.items.length : -1) === i - 1 ? `${a}; the last one is ${s(r.items.at(-1))}` : a;
|
|
813
|
+
}
|
|
814
|
+
function Fe(e, t, n, r) {
|
|
815
|
+
let i = e.after;
|
|
816
|
+
if (!t || i.k !== "prim" || !/^(int|long|short|byte)$/.test(i.type)) return "";
|
|
817
|
+
let a = e.label.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"), o = r.match(RegExp(`(?:^|[\\s(])${a}\\s*=\\s*([\\w.]+)\\s*/\\s*([\\w.]+)\\s*;`));
|
|
818
|
+
if (!o) return "";
|
|
819
|
+
let s = (e) => {
|
|
820
|
+
if (/^\d+$/.test(e)) return Number(e);
|
|
821
|
+
let r = Se(e, t, n);
|
|
822
|
+
return r?.k === "prim" && /^-?\d+$/.test(r.text) ? Number(r.text) : NaN;
|
|
823
|
+
}, [c, l] = [s(o[1]), s(o[2])];
|
|
824
|
+
if (!Number.isFinite(c) || !Number.isFinite(l) || l === 0 || c % l === 0) return "";
|
|
825
|
+
let u = Math.abs(c / l) % 1;
|
|
826
|
+
return ` (int division drops the ${u.toFixed(3).replace(/0+$/, "").slice(1)}${Number(u.toFixed(3)) === u ? "" : "…"})`;
|
|
827
|
+
}
|
|
828
|
+
function Ie(e, t) {
|
|
829
|
+
return e.length > 3 ? `set up ${e.length} static fields` : `set the static ${e.length === 1 ? "field" : "fields"} ${x(e.map((e) => {
|
|
830
|
+
let n = e.value;
|
|
831
|
+
if (n.k !== "ptr" || n.target.kind !== "heap") return `${e.name} to ${F(n, "java")}`;
|
|
832
|
+
let r = n.target.id;
|
|
833
|
+
return `${e.name} to a new ${t.heap.find((e) => e.id === r)?.type ?? "object"}`;
|
|
834
|
+
}))}`;
|
|
835
|
+
}
|
|
836
|
+
function Le(e, t, n) {
|
|
837
|
+
let r = e[t.line - 1] ?? "", i = r.match(/^\s*switch\s*\((.+)\)\s*\{?\s*$/);
|
|
838
|
+
if (i) {
|
|
839
|
+
for (let r = n.line; r > t.line; r--) {
|
|
840
|
+
let t = e[r - 1] ?? "", n = t.match(/^\s*case\s+(.+?)\s*(?:->|:(?!:))/)?.[1];
|
|
841
|
+
if (n) return `switch (${i[1]}) matched case ${n}`;
|
|
842
|
+
if (/^\s*default\s*(?:->|:)/.test(t)) return `switch (${i[1]}) matched no case, so it runs default`;
|
|
843
|
+
}
|
|
844
|
+
return n.line > t.line ? `switch (${i[1]}) matched no case, so nothing in it runs` : "";
|
|
845
|
+
}
|
|
846
|
+
if (/^\s*(?:[^/]*[;:{}]\s*)?break\s*;\s*(?:\/\/.*)?$/.test(r) && !ve(e, t.line, "java")) {
|
|
847
|
+
let n = ye(e, t.line);
|
|
848
|
+
if (n) return `break leaves the ${n.kind}`;
|
|
849
|
+
}
|
|
850
|
+
return /^\s*}\s*(?:\/\/.*)?$/.test(r) && n.line < t.line && ye(e, t.line)?.kind === "loop" ? `the loop body ends here, so it goes back to line ${n.line}` : "";
|
|
851
|
+
}
|
|
852
|
+
// ../renderer/src/describe/python.ts
|
|
853
|
+
var Re = (e) => e.match(/^(?:function|class) ([\w<>]+)/)?.[1], ze = (e, t) => e.k === "record" ? e.fields.filter((e) => e.value.k !== "opaque").map((e) => `${e.name} = ${F(e.value, t)}`) : [];
|
|
854
|
+
function Be(e, t, n, r = "python") {
|
|
855
|
+
if (Re(e.type) !== t) return null;
|
|
856
|
+
let i = ze(e.value, r);
|
|
857
|
+
if (e.type.startsWith("class ")) return `it defined the ${e.type}${i.length ? `, with ${x(i)}` : ""}`;
|
|
858
|
+
let a = n.frames.at(-1)?.name;
|
|
859
|
+
return `it defined the ${e.type}${i.length ? `, which remembers ${x(i)}${a ? ` from ${a}` : ""}` : ""}`;
|
|
860
|
+
}
|
|
861
|
+
function Ve(e, t, n) {
|
|
862
|
+
let r = [...e.frames.at(-2)?.locals ?? [], ...e.globals];
|
|
863
|
+
for (let i of r) {
|
|
864
|
+
if (i.value.k !== "ptr" || i.value.target.kind !== "heap") continue;
|
|
865
|
+
let r = i.value.target.id, a = e.heap.find((e) => e.id === r);
|
|
866
|
+
if (a && Re(a.type) === t.name && RegExp(`\\b${i.name.replace(/\$/g, "\\$")}\\s*\\(`).test(n)) return {
|
|
867
|
+
via: i.name === t.name ? void 0 : i.name,
|
|
868
|
+
obj: a
|
|
869
|
+
};
|
|
870
|
+
}
|
|
871
|
+
return null;
|
|
872
|
+
}
|
|
873
|
+
function He(e, t) {
|
|
874
|
+
return T(t) ? /(?:^|\.)(\w+)\.__del__$/.exec(e)?.[1] : void 0;
|
|
875
|
+
}
|
|
876
|
+
var Ue = /^\s*except\b/;
|
|
877
|
+
function We(e, t, n) {
|
|
878
|
+
let r = e.message, [, i = r, a] = r.match(/^([\w.]+)(?::\s*([\s\S]*))?$/) ?? [], o = a ? ` (${a})` : "", s = `the except on line ${e.line}`;
|
|
879
|
+
if (t.frames.at(-1)?.id === e.frames.at(-1)?.id && t.message === r && Ue.test(n[t.line - 1] ?? "")) return `The except on line ${t.line} doesn't catch ${y(i)} ${i}, so Python tries ${s}.`;
|
|
880
|
+
let c = t.frames.length > e.frames.length ? t.frames.at(-1) : void 0;
|
|
881
|
+
return c ? `${c.name} raised ${i}${o} on line ${t.line}, so Python left ${c.name} and jumped to ${s}.` : `Line ${t.line} raised ${i}${o}, so Python jumped to ${s}.`;
|
|
882
|
+
}
|
|
883
|
+
function Ge(e, t, n) {
|
|
884
|
+
let r = (e[t.line - 1] ?? "").replace(/#.*$/, "").trim();
|
|
885
|
+
if (r === "break") return ["break leaves the loop"];
|
|
886
|
+
if (r === "continue") return ["continue skips the rest of the loop body and goes back to the top of the loop"];
|
|
887
|
+
let i = n.frames.at(-1);
|
|
888
|
+
if (t.frames.at(-1)?.id !== i?.id) return [];
|
|
889
|
+
let a = i ?? {
|
|
890
|
+
id: "",
|
|
891
|
+
name: "",
|
|
892
|
+
line: 0,
|
|
893
|
+
locals: []
|
|
894
|
+
}, o = r.match(/^for\s+([A-Za-z_]\w*)\s+in\s+(.+):$/), s = o && Se(o[1], a, n);
|
|
895
|
+
if (o && s) return [`${o[1]} takes the next item in ${o[2]}, which is ${L(s, n, "python")} again`];
|
|
896
|
+
let c = r.match(/^([A-Za-z_]\w*(?:\.[A-Za-z_]\w*|\[[^\]]*\])*)\s*(?:[-+*/%&|^@]|\/\/|\*\*|<<|>>)?=(?!=)/)?.[1], l = c && Se(c, a, n);
|
|
897
|
+
return l ? l.k === "ptr" && l.target.kind === "heap" ? [`${c} still refers to ${L(l, n, "python")}`] : [`${c} is still ${F(l, "python")}`] : [];
|
|
898
|
+
}
|
|
899
|
+
// ../renderer/src/describe/phrases.ts
|
|
900
|
+
function Ke(e, t, n, r) {
|
|
901
|
+
let i = new Map(t.entries.map((e) => [I(e.key), e])), a = new Set(n.entries.map((e) => I(e.key))), o = [];
|
|
902
|
+
for (let t of n.entries) {
|
|
903
|
+
let n = i.get(I(t.key));
|
|
904
|
+
n ? I(n.value) !== I(t.value) && o.push(`${e} now maps ${F(t.key, r)} to ${F(t.value, r)} (it was ${F(n.value, r)})`) : o.push(`${e} now maps ${F(t.key, r)} to ${F(t.value, r)}`);
|
|
905
|
+
}
|
|
906
|
+
for (let n of t.entries) a.has(I(n.key)) || o.push(`${F(n.key, r)} was removed from ${e}`);
|
|
907
|
+
let s = n.entries.length;
|
|
908
|
+
return o.length && o.length <= 2 ? x(o) : `${e} now has ${s} ${s === 1 ? "entry" : "entries"}`;
|
|
909
|
+
}
|
|
910
|
+
function qe(e) {
|
|
911
|
+
let t = e.after;
|
|
912
|
+
if (t.k !== "prim" || t.type !== "null" && t.type !== "undefined") return null;
|
|
913
|
+
let n = e.before?.k === "ptr" && e.before.target.kind === "heap";
|
|
914
|
+
return t.type === "null" ? `${e.label} is now null${n ? ": it doesn't refer to any object now" : ""}` : e.before ? `${e.label} changed from ${F(e.before, "javascript")} to undefined` : `${e.label} is now undefined (it has no value yet)`;
|
|
915
|
+
}
|
|
916
|
+
function Je(e) {
|
|
917
|
+
let t = e.after, n = e.before;
|
|
918
|
+
if (t.k !== "opaque" || t.type !== "file") return null;
|
|
919
|
+
let r = (e) => e.k === "opaque" ? e.text.replace(/ \((?:closed|open for [^)]*)\)$/, "") : "";
|
|
920
|
+
return n?.k === "opaque" && n.type === "file" && r(n) === r(t) && t.text.endsWith("(closed)") ? `${e.label} is now closed` : `${e.label} is now the file ${t.text}`;
|
|
921
|
+
}
|
|
922
|
+
var Ye = " (fine to compare against, but there's no element there to read)";
|
|
923
|
+
function Xe(e, t, n, r, i = "") {
|
|
924
|
+
if (e.shares?.length) {
|
|
925
|
+
let { shares: a, ...o } = e, s = a.length === 1, c = new Intl.ListFormat("en").format(a.map((e) => e.label)), l = new Intl.ListFormat("en").format(a.map((e) => F(e.value, r)));
|
|
926
|
+
return `${Xe(o, t, n, r, i)} (${c} ${s ? "uses" : "use"} the same memory, so ${s ? "it now reads" : "they now read"} ${l})`;
|
|
927
|
+
}
|
|
928
|
+
let a = e.after, o = Je(e);
|
|
929
|
+
if (o) return o;
|
|
930
|
+
let s = E(r) ? qe(e) : null;
|
|
931
|
+
if (s) return s;
|
|
932
|
+
if (O(r)) {
|
|
933
|
+
if (a.k === "ptr" && a.target.kind === "heap") {
|
|
934
|
+
let i = a.target.id, o = t.heap.find((e) => e.id === i), s = o?.type ?? "object", c = o && Be(o, e.label, n, r);
|
|
935
|
+
if (c && !n.heap.some((e) => e.id === i)) return c;
|
|
936
|
+
if ([...n.globals, ...n.frames.flatMap((e) => e.locals)].some((e) => e.value.k === "opaque" && e.value.text === s)) return `${e.label} now refers to the ${s}`;
|
|
937
|
+
if (!n.heap.some((e) => e.id === i)) {
|
|
938
|
+
if (o?.value.k === "array" && !o.value.items.length || o?.value.k === "map" && !o.value.entries.length) return `${e.label} now refers to a new empty ${s}`;
|
|
939
|
+
let t = o && A(o.value, r);
|
|
940
|
+
return `${e.label} now refers to a new ${s}${t ? ` ${t}` : ""}`;
|
|
941
|
+
}
|
|
942
|
+
let l = le(t, i).find((t) => t !== e.label);
|
|
943
|
+
return l ? `${e.label} now refers to the same ${s} as ${l}` : `${e.label} now refers to ${ie(o, r)}`;
|
|
944
|
+
}
|
|
945
|
+
if (a.k === "opaque" && !e.before && /^(function|class) /.test(a.text)) return `it defined the ${a.text}`;
|
|
946
|
+
if (a.k === "opaque" && a.type === "generator") {
|
|
947
|
+
let t = a.text.match(/^generator (\w+)/)?.[1] ?? "it";
|
|
948
|
+
return `${e.label} ${e.before ? "now" : "is now"} ${b(a.text)} (none of ${t}'s code has run yet: it starts when asked for its first value)`;
|
|
949
|
+
}
|
|
950
|
+
}
|
|
951
|
+
let c = D(r) ? Ae(e, t, n, i) ?? Pe(e, t, n) : null;
|
|
952
|
+
if (c) return c;
|
|
953
|
+
let l = we(e, t, n, r);
|
|
954
|
+
if (l) return l;
|
|
955
|
+
if (a.k === "map" && e.before?.k === "map") return Ke(e.label, e.before, a, r);
|
|
956
|
+
if (a.k === "ptr" && a.target.kind !== "null") {
|
|
957
|
+
let i = w(a) ? "refers to" : "points to", o = a.target;
|
|
958
|
+
if (o.kind === "heap") {
|
|
959
|
+
let s = t.heap.find((e) => e.id === o.id);
|
|
960
|
+
if (s && j(a.type, r)) return `${e.label} is now ${N(s.value, r, t)}${re(a.type, s.value, r)}`;
|
|
961
|
+
if (s && (j(s.type, r) || o.offset > 0) && s.value.k === "array" && s.value.items.length > 0) {
|
|
962
|
+
let n = ae(t).get(s.id)?.label, r = Math.floor(o.offset / (s.size / s.value.items.length));
|
|
963
|
+
if (n && n !== e.label && r < s.value.items.length) return `${e.label} now ${i} ${n}[${r}]`;
|
|
964
|
+
if (r === s.value.items.length) return `${e.label} now points just past the end of ${n && n !== e.label ? n : `the ${s.type}`}${Ye}`;
|
|
965
|
+
}
|
|
966
|
+
let c = !n.heap.some((e) => e.id === o.id), l = s?.type ?? "value";
|
|
967
|
+
if (s?.exception && c) return `${e.label} now ${i} the ${l} that was thrown`;
|
|
968
|
+
if (c) return `${e.label} now ${i} a new ${l}`;
|
|
969
|
+
let u = le(t, o.id).find((t) => t !== e.label) ?? ce(t, o.id, e.label, r);
|
|
970
|
+
return u && o.offset === 0 ? `${e.label} now ${i} the same ${M(l)} as ${u}` : `${e.label} now ${i} ${R(s, l)}`;
|
|
971
|
+
}
|
|
972
|
+
if ((o.kind === "stack" || o.kind === "global") && o.offset > 0) {
|
|
973
|
+
let n = (o.kind === "stack" ? t.frames.find((e) => e.id === o.frame)?.locals : t.globals)?.find((e) => e.name === o.name)?.value;
|
|
974
|
+
if (n?.k === "array" && n.items.length > 0) {
|
|
975
|
+
let t = o.size ? Math.floor(o.offset / (o.size / n.items.length)) : -1;
|
|
976
|
+
if (t >= 0 && t < n.items.length) return `${e.label} now ${i} ${o.name}[${t}]`;
|
|
977
|
+
if (t === n.items.length) return `${e.label} now points just past the end of ${o.name}${Ye}`;
|
|
978
|
+
}
|
|
979
|
+
}
|
|
980
|
+
return `${e.label} now ${i} ${F(a, r)}`;
|
|
981
|
+
}
|
|
982
|
+
return e.whole && r === "cpp" && (a.k === "array" || a.k === "map") ? `${e.label} is now ${N(a, r, t)}${re(a.type, a, r)}` : e.whole && a.k === "array" || !e.before ? `${e.label} is now ${F(a, r)}` : `${e.label} changed from ${F(e.before, r)} to ${F(a, r)}`;
|
|
983
|
+
}
|
|
984
|
+
function Ze(e, t, n, r, i, a = "python") {
|
|
985
|
+
let o = e.path.slice(5).split(/[.[]/)[0], s = t.heap.find((e) => e.id === o && e.type.startsWith("function ")), c = e.path.slice(`heap:${o}.`.length);
|
|
986
|
+
if (s && /^\w+$/.test(c)) {
|
|
987
|
+
let r = le(t, o)[0], i = Re(s.type) ?? (r ? `the function ${r} refers to` : "the function");
|
|
988
|
+
return `${Xe({
|
|
989
|
+
...e,
|
|
990
|
+
label: c
|
|
991
|
+
}, t, n, a)} (${i} keeps it in its function object, so it lasts between calls)`;
|
|
992
|
+
}
|
|
993
|
+
let l = le(t, o), u = e.label.match(/^[A-Za-z_$][\w$]*/)?.[0] ?? "";
|
|
994
|
+
if (l.length < 2 || !l.includes(u)) return Xe(e, t, n, a);
|
|
995
|
+
let d = l.find((e) => RegExp(`\\b${e}\\b`).test(r)) ?? u, f = d + e.label.slice(u.length), p = t.heap.find((e) => e.id === o)?.type ?? "object", m = [d, ...l.filter((e) => e !== d)], h = Xe({
|
|
996
|
+
...e,
|
|
997
|
+
label: f
|
|
998
|
+
}, t, n, a);
|
|
999
|
+
return i.has(o) ? h : (i.add(o), `${h} (${x(m)} ${m.length === 2 ? "both" : "all"} refer to this ${p})`);
|
|
1000
|
+
}
|
|
1001
|
+
// ../renderer/src/describe/js.ts
|
|
1002
|
+
var Qe = /\bcatch\b/;
|
|
1003
|
+
function $e(e, t) {
|
|
1004
|
+
let n = e.message, [, r = n, i] = n.match(/^([\w$.]+)(?::\s*([\s\S]*))?$/) ?? [], a = i ? ` ("${i}")` : "", o = `the catch on line ${e.line}`, s = t.frames.length > e.frames.length ? t.frames.at(-1) : void 0, c = s && V(s.name, "javascript");
|
|
1005
|
+
return c ? `${c} threw ${r}${a} on line ${t.line}, so JavaScript left ${c} and jumped to ${o}.` : `Line ${t.line} threw ${r}${a}, so JavaScript jumped to ${o}.`;
|
|
1006
|
+
}
|
|
1007
|
+
function et(e, t, n) {
|
|
1008
|
+
let r = (e[t.line - 1] ?? "").replace(/\/\/.*$/, "").trim().replace(/;$/, "");
|
|
1009
|
+
if (r === "continue") return ["continue skips the rest of the loop body and goes back to the top of the loop"];
|
|
1010
|
+
let i = r.match(/\.(then|catch|finally)\s*\(/)?.[1];
|
|
1011
|
+
if (i) return [`the callback given to .${i} waits for the Promise to settle, and then runs as a queued job`];
|
|
1012
|
+
if (/\bsetTimeout\s*\(/.test(r)) return ["setTimeout starts a timer: its callback runs once the timer is due and the code running now has finished"];
|
|
1013
|
+
if (/\bsetInterval\s*\(/.test(r)) return ["setInterval starts a repeating timer: its callback runs each time the timer is due"];
|
|
1014
|
+
if (/\bqueueMicrotask\s*\(/.test(r)) return ["the function given to queueMicrotask runs as a queued job once the code running now has finished"];
|
|
1015
|
+
let a = n.frames.at(-1);
|
|
1016
|
+
if (t.frames.at(-1)?.id !== a?.id) return [];
|
|
1017
|
+
let o = r.match(/^(?:(?:let|var)\s+)?([A-Za-z_$][\w$]*(?:\.[A-Za-z_$][\w$]*|\[[^\]]*\])*)\s*(?:[-+*/%&|^]|\*\*|<<|>>>?)?=(?![=>])/)?.[1], s = o && Se(o, a ?? {
|
|
1018
|
+
id: "",
|
|
1019
|
+
name: "",
|
|
1020
|
+
line: 0,
|
|
1021
|
+
locals: []
|
|
1022
|
+
}, n);
|
|
1023
|
+
return s ? s.k === "ptr" && s.target.kind === "heap" ? [`${o} still refers to ${L(s, n, "javascript")}`] : [`${o} is still ${F(s, "javascript")}`] : [];
|
|
1024
|
+
}
|
|
1025
|
+
function tt(e, t) {
|
|
1026
|
+
let n = e.path.match(/^heap:([^.[]+)\.(state|value|reason)$/);
|
|
1027
|
+
return n && t.heap.find((e) => e.id === n[1])?.type === "Promise" ? n[2] : void 0;
|
|
1028
|
+
}
|
|
1029
|
+
function nt(e, t) {
|
|
1030
|
+
let n = t.heap.find((t) => t.id === e);
|
|
1031
|
+
if (n?.type !== "Promise" || n.value.k !== "record") return;
|
|
1032
|
+
let r = n.value.fields.find((e) => e.name === "state")?.value, i = n.value.fields.find((e) => e.name === "value" || e.name === "reason")?.value;
|
|
1033
|
+
return {
|
|
1034
|
+
state: r?.k === "opaque" ? r.text : "pending",
|
|
1035
|
+
...i ? { value: i } : {}
|
|
1036
|
+
};
|
|
1037
|
+
}
|
|
1038
|
+
var rt = (e) => e?.k === "ptr" && e.target.kind === "heap" ? e.target.id : void 0;
|
|
1039
|
+
function it(e, t, n) {
|
|
1040
|
+
let r = e ? ae(t, "javascript").get(e)?.label : void 0;
|
|
1041
|
+
return r ? /^[\w$]+$/.test(r) ? `the Promise ${r}` : r : n;
|
|
1042
|
+
}
|
|
1043
|
+
function at(e, t, n) {
|
|
1044
|
+
let r = t ? ` with ${L(t, n, "javascript")}` : "";
|
|
1045
|
+
return e === "rejected" ? `was rejected${r}` : `resolved${r}`;
|
|
1046
|
+
}
|
|
1047
|
+
function ot(e, t) {
|
|
1048
|
+
let n = e.path.match(/^heap:([^.[]+)/)[1], r = e.label.replace(/\.state$/, ""), i = /^[\w$]+$/.test(r) ? `the Promise ${r}` : r, a = nt(n, t);
|
|
1049
|
+
return `${i} ${at(a?.state, a?.value, t)}`;
|
|
1050
|
+
}
|
|
1051
|
+
function st(e, t) {
|
|
1052
|
+
let n = e.frames.at(-2);
|
|
1053
|
+
return n ? `${V(n.name, t)} carries on` : e.queued ? "JavaScript moves on to whatever is queued next" : "the main program carries on";
|
|
1054
|
+
}
|
|
1055
|
+
function ct(e, t, n) {
|
|
1056
|
+
let r = V(t.name, n), i = t.awaiting, a = rt(i), o = nt(a, e), s = st(e, n);
|
|
1057
|
+
return o?.state === "pending" ? `${r} is waiting for ${it(a, e, "a Promise")} to settle, so it pauses here and ${s} meanwhile.` : o ? `${r} awaits ${it(a, e, "a Promise")} that has already ${o.state === "rejected" ? "been rejected" : "resolved"}. It still pauses until the code running now is done, so ${s} first.` : `${r} awaits ${i ? L(i, e, n) : "undefined"}, which isn't a Promise. It still pauses until the code running now is done, so ${s} first.`;
|
|
1058
|
+
}
|
|
1059
|
+
function lt(e, t) {
|
|
1060
|
+
for (let n = t; n >= 0 && e.steps[n].queued; n--) if (e.steps[n].job) return e.steps[n].job;
|
|
1061
|
+
}
|
|
1062
|
+
function ut(e, t, n, r) {
|
|
1063
|
+
let i = e.steps[t], a = V(n.name, r), o = lt(e, t), s = n.returnValue ? L(n.returnValue, i, r) : "undefined";
|
|
1064
|
+
if (o?.kind === "then" || o?.kind === "catch") {
|
|
1065
|
+
let e = nt(rt(n.returnValue), i) ? "follows the Promise it returns" : `resolves with ${s}`;
|
|
1066
|
+
return `${a} is finishing and returns ${s}, so the Promise that .${o.kind} made ${e}.`;
|
|
1067
|
+
}
|
|
1068
|
+
return o?.kind === "finally" ? `${a} is finishing. What it returns is not used: the Promise that .finally made settles the same way as the one before it.` : `${a} is finishing, so JavaScript moves on to whatever is queued next.`;
|
|
1069
|
+
}
|
|
1070
|
+
function dt(e, t, n, r, i) {
|
|
1071
|
+
let a = [];
|
|
1072
|
+
for (let n of t.locals) W(`var:${t.id}:${n.name}`, n.name, e.locals.find((e) => e.name === n.name)?.value, n.value, a);
|
|
1073
|
+
return a.slice(0, 3).map((e) => Xe(e, n, r, i));
|
|
1074
|
+
}
|
|
1075
|
+
function ft(e, t, n, r) {
|
|
1076
|
+
let i = e.job, a = e.frames.at(-1), o = a ? V(a.name, n) : "the callback", s = at(i.state, i.value, e), c = (a?.locals ?? []).filter((e) => e.name !== "this" && e.value.k !== "uninit").map((t) => `${t.name} = ${L(t.value, e, n)}`), l = c.length && e.event !== "await" ? ` with ${x(c)}` : "", u = t.queued ? "" : "The main program's own code is done, so JavaScript runs the jobs waiting in its queues. ", d = i.time === void 0 ? "" : ` (the program's clock is now at ${i.time} ms)`;
|
|
1077
|
+
switch (i.kind) {
|
|
1078
|
+
case "await": {
|
|
1079
|
+
let c = a && (t.paused?.find((e) => e.id === a.id) ?? t.frames.find((e) => e.id === a.id)), l = c ? ` on line ${c.line}` : "";
|
|
1080
|
+
if (i.state === "rejected") return `${u}The Promise ${o} was waiting for ${s}, so the await${l} throws that inside ${o}${e.event === "step" && e.message ? `, and JavaScript jumps to the catch on line ${e.line}` : ""}.`;
|
|
1081
|
+
let d = [...c && a ? dt(c, a, e, t, n) : [], ...r];
|
|
1082
|
+
return `${u}The Promise ${o} was waiting for ${s}, so ${o} carries on after the await${l}${d.length ? `: ${x(d)}` : ""}.`;
|
|
1083
|
+
}
|
|
1084
|
+
case "then":
|
|
1085
|
+
case "catch":
|
|
1086
|
+
case "finally": {
|
|
1087
|
+
let t = it(rt(i.promise), e, "The Promise"), n = i.kind === "then" && i.state === "rejected" ? "the rejection callback given to .then" : `the .${i.kind} callback`;
|
|
1088
|
+
return `${u}${t.charAt(0).toUpperCase()}${t.slice(1)} ${s}, so ${n} runs${l}.`;
|
|
1089
|
+
}
|
|
1090
|
+
case "thenable": return `${u}A Promise was resolved with an object that has a then method of its own, so JavaScript calls ${o} to get its value.`;
|
|
1091
|
+
case "microtask": return `${u}The function queued with queueMicrotask runs now.`;
|
|
1092
|
+
case "timer": return `${u}The ${i.delay} ms timer is due${d}, so its callback runs${l}.`;
|
|
1093
|
+
case "interval": return `${u}The interval set for every ${i.delay} ms is due${d}, so its callback runs${l}.`;
|
|
1094
|
+
}
|
|
1095
|
+
}
|
|
1096
|
+
function pt(e, t, n, r) {
|
|
1097
|
+
let i = e.steps[t], a = e.steps[t - 1], o = e.language, s = i.frames.at(-1), c = [], l = e.stdout.slice(a.stdoutLength, i.stdoutLength).trimEnd(), u = `"${l.length > 60 ? `${l.slice(0, 57)}…` : l}"`;
|
|
1098
|
+
if (a.event === "step" || a.event === "resume") {
|
|
1099
|
+
let e = r.length ? r : et(n, a, i);
|
|
1100
|
+
c.push(`${e.length ? `Line ${a.line} ran: ${x(e)}.` : `Line ${a.line} ran.`}${l ? ` It printed ${u}.` : ""}`), c.push(ft(i, a, o, []));
|
|
1101
|
+
} else l && c.push(`Meanwhile, built-in callbacks (such as console.log given to .then) printed ${u}.`), i.job.kind === "await" ? c.push(ft(i, a, o, r)) : (r.length && c.push(`${x(r).replace(/^./, (e) => e.toUpperCase())}.`), c.push(ft(i, a, o, [])));
|
|
1102
|
+
return s && i.event === "await" ? c.push("It reaches another await, so it pauses again.") : s && i.event === "return" && c.push(s.async ? "It finishes straight away." : "It has nothing to run, so it returns straight away."), c;
|
|
1103
|
+
}
|
|
1104
|
+
function mt(e) {
|
|
1105
|
+
let t = (e.paused ?? []).filter((e) => e.async).map((e) => e.name), n = t.length ? ` ${x(t)} never carried on: the Promise ${t.length > 1 ? "they were" : "it was"} waiting for never settled.` : "";
|
|
1106
|
+
return `${e.queued ? " Nothing is left in the queues, so the program has finished." : " The program has finished."}${n}`;
|
|
1107
|
+
}
|
|
1108
|
+
// ../renderer/src/describe/calls.ts
|
|
1109
|
+
function ht(e, t, n, r) {
|
|
1110
|
+
let i = e.steps[t], a = e.steps[t - 1], o = e.language, s = i.frames.at(-1), c = a.frames.at(-1), l = s.locals.find((e) => e.name === (T(o) ? "self" : "this")), u = ee(o) ? void 0 : ae(a, o), d = (e) => u && e.k === "ptr" && e.target.kind === "heap" ? u.get(e.target.id)?.label : void 0, f = s.locals.filter((e) => e.value.k !== "uninit" && e !== l).map((e) => {
|
|
1111
|
+
let t = d(e.value);
|
|
1112
|
+
if (!t || D(o) && t !== e.name && t.match(/^[\w$]+/)?.[0] === e.name) return `${e.name} = ${L(e.value, i, o)}`;
|
|
1113
|
+
t === e.name && (t = c ? `${V(c.name, o)}'s ${t}` : `the main program's ${t}`);
|
|
1114
|
+
let n = e.value.k === "ptr" && e.value.target.kind === "heap" ? e.value.target.id : "", r = i.heap.find((e) => e.id === n), a = r && D(o) && r.value.k === "array" && r.value.items.length <= 6 && r.value.items.every((e) => e.k === "prim");
|
|
1115
|
+
if (r && Re(r.type) === t) return `${e.name} = the ${r.type}`;
|
|
1116
|
+
let s = r && (a ? F(r.value, o) : A(r.value, o));
|
|
1117
|
+
return s ? `${e.name} = ${s} (the same ${r.type} as ${t})` : `${e.name} = the same ${r?.type ?? "object"} as ${t}`;
|
|
1118
|
+
}), p = f.length ? ` with ${x(f)}` : "", m = B(s.name, o), h = a.paused?.find((e) => e.id === s.id), g = be.test(n[a.line - 1] ?? "") ? `The for loop on line ${a.line}` : `Line ${a.line}`, _ = a.frames.some((e) => e.name === s.name) ? " again" : "", v = se(i.frames, o, n), y = E(o) ? s.locals.filter((e) => e.value.k === "opaque" && /^function (resolve|reject)$/.test(e.value.text)).map((e) => e.name) : [];
|
|
1119
|
+
if (h && s.closing) return `${vt(n[a.line - 1] ?? "", a.line, s.name)}: it stops where it paused on line ${h.line}, running only its cleanup code.`;
|
|
1120
|
+
if (h) return `${g} asks ${s.name} for its next value, so ${s.name} carries on from line ${h.line}, where it paused.`;
|
|
1121
|
+
if (s.generator) return `${g} asks ${s.name} for its first value, so ${s.name} starts running${p}.`;
|
|
1122
|
+
if (y.length && /\bnew\s+Promise\b/.test(n[a.line - 1] ?? "")) {
|
|
1123
|
+
let e = s.name === "(anonymous)" ? "the function given to it" : V(s.name, o);
|
|
1124
|
+
return `Line ${a.line} makes a new Promise, which calls ${e} straight away with ${x(y)}: calling ${y.length > 1 ? "one of them" : "it"} later settles the Promise.`;
|
|
1125
|
+
}
|
|
1126
|
+
if (m && v) {
|
|
1127
|
+
let e = /\bthis\s*\(/.test(n[a.line - 1] ?? "") ? "this" : "super", t = s.locals.filter((e) => e.value.k !== "uninit" && e !== l).map((e) => L(e.value, i, o)), r = e === "super" ? `the ${m} part of this ${v}` : `this ${v}`;
|
|
1128
|
+
return `Line ${a.line} ran: the ${v} constructor calls ${e}(${t.join(", ")}), so the ${m} constructor runs${p} to set up ${r}.`;
|
|
1129
|
+
}
|
|
1130
|
+
if (He(s.name, o) && e.steps.slice(0, t).some((e) => e.event === "return" && !e.frames.length)) {
|
|
1131
|
+
let e = a.globals.filter((e) => !i.globals.some((t) => t.name === e.name)).map((e) => e.name), t = e.length && e.length <= 4 ? ` (${x(e)})` : "", n = e.length ? `, so Python removes the program's names${t}` : ", so Python cleans up what is left", r = `nothing refers to the ${He(s.name, o)} any more, so Python runs its __del__ before deleting it`;
|
|
1132
|
+
return e.length ? `The program has finished${n}. Now ${r}.` : `The program has finished${n}: ${r}.`;
|
|
1133
|
+
}
|
|
1134
|
+
if (He(s.name, o)) {
|
|
1135
|
+
let e = /^\s*del\s+([\w.]+)\s*$/.exec(n[a.line - 1] ?? "")?.[1], t = r.length ? x(r) : e ? `it removed the name ${e}` : "", i = `nothing refers to the ${He(s.name, o)} any more, so Python runs its __del__ before deleting it`;
|
|
1136
|
+
return t ? `Line ${a.line} ran: ${t}. Now ${i}.` : `Line ${a.line} ran: ${i}.`;
|
|
1137
|
+
}
|
|
1138
|
+
if (m && T(o)) return `Line ${a.line} ran: it is making a new ${m}, so ${s.name} runs to set it up${p}.`;
|
|
1139
|
+
if (m) return `Line ${a.line} ran: it is making a new ${m}, so the ${m} constructor runs${p}.`;
|
|
1140
|
+
if (O(o) && (s.captured?.length || Ve(i, s, n[a.line - 1] ?? "")?.via)) {
|
|
1141
|
+
let e = Ve(i, s, n[a.line - 1] ?? ""), t = e?.via ? `${e.via} (the ${e.obj.type})` : s.name, r = e && s.captured?.length ? ze(e.obj.value, o) : [], c = r.length ? ` ${s.name} remembers ${x(r)} in its function object from earlier.` : "";
|
|
1142
|
+
return `Line ${a.line} ran and called ${t}${_}${p}.${c}`;
|
|
1143
|
+
}
|
|
1144
|
+
if (l && l.value.k === "ptr") {
|
|
1145
|
+
let e = l.value.target.kind === "stack" ? l.value.target.name : d(l.value) ?? L(l.value, i, o);
|
|
1146
|
+
return `Line ${a.line} ran and called ${V(s.name, o)} on ${e}${p}.`;
|
|
1147
|
+
}
|
|
1148
|
+
return `Line ${a.line} ran and called ${H(s, o, n)}${_}${p}.`;
|
|
1149
|
+
}
|
|
1150
|
+
function gt(e, t, n, r) {
|
|
1151
|
+
let i = e.steps[t], a = e.steps[t - 1], o = e.language, s = i.frames.at(-1), c = a.frames.at(-1), l = a.frames.length === i.frames.length ? xe(n, a, i, o) : "", u = [...r, ...l ? [l] : []], d = e.stdout.slice(a.stdoutLength, i.stdoutLength).trimEnd(), f = d ? `It printed "${d.length > 60 ? `${d.slice(0, 57)}…` : d}". ` : "", p = a.frames.at(-1)?.id === s.id ? a.line : i.line, m = u.length ? `Line ${p} ran: ${x(u)}. ${f}` : f ? `Line ${p} ran. ${f}` : "";
|
|
1152
|
+
if (i.event === "yield") {
|
|
1153
|
+
let r = s.yielded ? ` ${L(s.yielded, i, o)}` : "";
|
|
1154
|
+
return `${m}${s.name} yields${r} to ${yt(e, t, n)} and pauses, keeping its variables until it is asked for the next value.`;
|
|
1155
|
+
}
|
|
1156
|
+
if (i.event === "await") return `${a.event === "await" && c && c.id !== s.id ? `${V(c.name, o)} paused and handed back a pending Promise. ` : ""}${m}${ct(i, s, o)}`;
|
|
1157
|
+
if (s.closing) return `${m}${s.name} has finished cleaning up, so it is closed.`;
|
|
1158
|
+
if (s.generator) return `${m}${s.name} has no more values to give, so it finishes.`;
|
|
1159
|
+
if (s.async && E(o)) {
|
|
1160
|
+
let e = s.returnValue ? L(s.returnValue, i, o) : "undefined", t = nt(rt(s.returnValue), i) ? "follows the Promise it returns" : `resolves with ${e}`;
|
|
1161
|
+
return `${m}${V(s.name, o)} is finishing, so the Promise it handed back ${t}.`;
|
|
1162
|
+
}
|
|
1163
|
+
if (E(o) && i.queued && i.frames.length === 1) return `${m}${ut(e, t, s, o)}`;
|
|
1164
|
+
if (i.frames.length === 1 && D(o)) return `${m}${V(s.name, o)} is finishing, so the program ends.`;
|
|
1165
|
+
if (i.frames.length === 1 && !O(o)) {
|
|
1166
|
+
let e = i.heap.filter((e) => !j(e.type, o) && !J(i, e.id).length).length, t = e ? ` ${e} ${e === 1 ? "block" : "blocks"} on the heap ${e === 1 ? "was" : "were"} never freed.` : "", n = s.returnValue ? ` ${L(s.returnValue, i, o)}` : "";
|
|
1167
|
+
return `${m}${V(s.name, o)} is returning${n}: the program is finishing${n.trim() === "0" ? " (0 means success)" : ""}.${t}`;
|
|
1168
|
+
}
|
|
1169
|
+
let h = B(s.name, o), g = se(i.frames, o, n);
|
|
1170
|
+
if (h && g) return `${m}The ${h} constructor finished setting up the ${h} part of this ${g}.`;
|
|
1171
|
+
if (h && T(o)) return `${m}${s.name} finished setting up the new ${h}.`;
|
|
1172
|
+
let _ = He(s.name, o);
|
|
1173
|
+
if (_) return `${m}${s.name} is done, so Python deletes the ${_}.`;
|
|
1174
|
+
if (h) return `${m}The ${h} constructor finished setting up the new ${E(o) ? h : "object"}.`;
|
|
1175
|
+
let v = s.returnValue ? ` ${L(s.returnValue, i, o)}` : "", y = bt(e, t, n);
|
|
1176
|
+
return y && s.returnValue && !((e) => e.k === "opaque" && /^(function|class) /.test(e.text) || e.k === "ptr" && e.target.kind === "heap" && !!i.heap.find((t) => t.id === e.target.id)?.type.startsWith("function"))(s.returnValue) ? `${m}${S(H(s, o, n))} is finishing: ${y.expr} is ${y.values}, so it returns${v} to ${de(i, s.name, o, n)}.` : `${m}${S(H(s, o, n))} is finishing and returning${v} to ${de(i, s.name, o, n)}.`;
|
|
1177
|
+
}
|
|
1178
|
+
function _t(e, t, n, r) {
|
|
1179
|
+
let i = e.steps[t], a = e.steps[t - 1], o = e.language, s = i.frames.at(-1), c = a.frames.at(-1), l = c.returnValue ? ` ${L(c.returnValue, a, o)}` : "", u = i.event === "resume" ? i.line : i.resumedLine, d = r.length ? `, and ${x(r)}` : "", f = e.stdout.slice(a.stdoutLength, i.stdoutLength).trimEnd(), p = f ? ` It printed "${f.length > 60 ? `${f.slice(0, 57)}…` : f}".` : "", m = B(c.name, o), h = c.yielded ? ` ${L(c.yielded, a, o)}` : "", g = be.test(n[u - 1] ?? "") ? ", so the for loop ends" : "", _ = E(o) && m && s && /\bsuper\s*\(/.test(n[u - 1] ?? "") ? B(s.name, o) : void 0, v = nt(rt(c.returnValue), a) ? "follows the Promise it returned" : `resolves with${l || " undefined"}`, y = a.event === "await" ? `${V(c.name, o)} paused at its await, so the call handed back a Promise that is still pending` : `${V(c.name, o)} finished, so the Promise it handed back ${v}`, b = _ ? `the ${m} part of this ${_} is set up` : m ? `the new ${m} is ready` : a.event === "yield" ? `${c.name} yielded${h}` : c.async ? y : c.closing ? `${c.name} is closed` : He(c.name, o) ? `the ${He(c.name, o)} is deleted` : c.generator ? `${c.name} has no more values${g}` : `${H(c, o, n)} returned${l}`;
|
|
1180
|
+
return `Back in ${s ? H(s, o, n) : U} on line ${u}: ${b}${d}.${p}`;
|
|
1181
|
+
}
|
|
1182
|
+
function vt(e, t, n) {
|
|
1183
|
+
return /\.close\s*\(/.test(e) ? `Line ${t} closes ${n}` : /^\s*break\b/.test(e) ? `Line ${t} leaves the for loop early, so ${n} is closed` : /^\s*return\b/.test(e) ? `Line ${t} leaves the for loop by returning, so ${n} is closed` : `Nothing uses ${n} any more, so Python closes it`;
|
|
1184
|
+
}
|
|
1185
|
+
function yt(e, t, n) {
|
|
1186
|
+
let r = e.steps[t], i = r.frames.length - 2, a = [];
|
|
1187
|
+
for (; i >= 0 && /\byield\s*\*/.test(n[r.frames[i].line - 1] ?? ""); i--) a.push(`${r.frames[i].name}'s yield*`);
|
|
1188
|
+
let o = a.length ? ` (through ${x(a)})` : "", s = i >= 0 ? r.frames[i] : void 0, c = e.steps[t + 1], l = s ? s.line : c && (c.resumedLine ?? (c.event === "resume" ? c.line : void 0));
|
|
1189
|
+
if (l !== void 0 && be.test(n[l - 1] ?? "")) return `the for loop on line ${l}${o}`;
|
|
1190
|
+
let u = r.frames.at(-1).name, d = e.language;
|
|
1191
|
+
return s ? `${s.name === u ? `the ${V(u, d)} call that called it` : V(s.name, d)}${o}` : `${O(d) ? U : "its caller"}${o}`;
|
|
1192
|
+
}
|
|
1193
|
+
function bt(e, t, n) {
|
|
1194
|
+
let r = e.steps[t], i = r.frames.at(-1), a = n[r.line - 1] ?? "", o = e.language === "python" ? /\breturn\s+(.+?)\s*(?:#.*)?$/ : E(e.language) ? /\breturn\s+([^;]+?)\s*(?:;|\/\/|$)/ : /\breturn\s+([^;]+);/, s = a.match(o)?.[1]?.trim();
|
|
1195
|
+
if (!i || !s) return null;
|
|
1196
|
+
let c = [];
|
|
1197
|
+
for (let n = t; n > 0; n--) {
|
|
1198
|
+
let t = e.steps[n];
|
|
1199
|
+
if (!t.frames.some((e) => e.id === i.id)) break;
|
|
1200
|
+
let a = t.frames.at(-1)?.id === i.id;
|
|
1201
|
+
if (a && t.line !== r.line) break;
|
|
1202
|
+
let o = e.steps[n - 1], s = o.frames.at(-1);
|
|
1203
|
+
a && o.frames.length > t.frames.length && s?.returnValue && c.unshift(s.returnValue);
|
|
1204
|
+
}
|
|
1205
|
+
let l = [], u = 0, d = "";
|
|
1206
|
+
for (let e = 0; e < s.length;) {
|
|
1207
|
+
let t = /^[A-Za-z_]\w*\s*\(/.exec(s.slice(e)), n = s[e - 1] ?? " ";
|
|
1208
|
+
if (t && !/[\w.>]/.test(n) && !/^(sizeof)\b/.test(t[0])) {
|
|
1209
|
+
let n = 0, r = e + t[0].length - 1;
|
|
1210
|
+
for (; r < s.length; r++) if (s[r] === "(") n++;
|
|
1211
|
+
else if (s[r] === ")" && --n === 0) break;
|
|
1212
|
+
let i = c[u++];
|
|
1213
|
+
if (i?.k !== "prim") return null;
|
|
1214
|
+
l.push(i.text), d += `\u0000${l.length - 1}\u0000`, e = r + 1;
|
|
1215
|
+
} else d += s[e++];
|
|
1216
|
+
}
|
|
1217
|
+
if (u !== c.length || !/[-+*/%<>=!&|^?]/.test(d.replace(/->/g, ""))) return null;
|
|
1218
|
+
let f = 0, p = d.replace(/\b[A-Za-z_]\w*(?:\s*(?:->|\.)\s*[A-Za-z_]\w*|\s*\[[^\]]*\])*/g, (e) => {
|
|
1219
|
+
let t = Se(e, i, r);
|
|
1220
|
+
return t?.k === "prim" ? (f++, t.text) : e;
|
|
1221
|
+
}).replace(/\u0000(\d+)\u0000/g, (e, t) => l[Number(t)]);
|
|
1222
|
+
return f + u === 0 || p === s || (e.language === "cpp" || E(e.language)) && /[.>]\s*[A-Za-z_]\w*\s*\(/.test(p) ? null : {
|
|
1223
|
+
expr: s,
|
|
1224
|
+
values: p
|
|
1225
|
+
};
|
|
1226
|
+
}
|
|
1227
|
+
// ../renderer/src/describe/errors.ts
|
|
1228
|
+
var xt = (e) => e.match(/\d[\d,]*/)?.[0] ?? "many", St = (e, t) => e.replace(t, "");
|
|
1229
|
+
function Ct(e, t, n) {
|
|
1230
|
+
let r = e.message ?? "the program crashed", i = e.frames.at(-1), a = `Line ${e.line}`;
|
|
1231
|
+
switch (e.stopped) {
|
|
1232
|
+
case "steps":
|
|
1233
|
+
case "timeout": return `Paused after ${xt(r)} steps: line ${e.line} may be in a loop that never ends. Check that the loop's condition can become false.`;
|
|
1234
|
+
case "jobs": return `Paused after ${xt(r)} timer and Promise callbacks: something keeps queuing more work, such as a setInterval that is never cleared. Call clearInterval to stop an interval.`;
|
|
1235
|
+
case "stackOverflow": {
|
|
1236
|
+
let e = r.match(/\((\S+) was called/)?.[1] ?? i?.name ?? "a function";
|
|
1237
|
+
return T(n) ? `${e} kept calling itself and never returned, so Python stopped it with a RecursionError. Recursion needs a base case that stops it.` : E(n) ? `${V(e, n)} kept calling itself and never returned, so JavaScript stopped it with a RangeError. Recursion needs a base case that stops it.` : D(n) ? `${i ? V(i.name, n) : "A method"} kept calling itself and never returned. Recursion needs a base case that stops it; Java would throw a StackOverflowError.` : `${i?.name ?? "a function"} kept calling itself and never returned. Recursion needs a base case that stops it; a real program would crash with a stack overflow.`;
|
|
1238
|
+
}
|
|
1239
|
+
case "nullDeref": {
|
|
1240
|
+
let r = t[e.line - 1] ?? "", o = (i?.locals ?? []).filter((e) => e.value.k === "ptr" && e.value.target.kind === "null" && RegExp(`\\b${e.name}\\b`).test(r))[0]?.name;
|
|
1241
|
+
return o ? `${a} tried to use the memory ${o} points to, but ${o} is ${C(n)} (it points nowhere). A real program would crash here with a segmentation fault.` : `${a} used a ${C(n)} pointer (one that points nowhere). A real program would crash here with a segmentation fault.`;
|
|
1242
|
+
}
|
|
1243
|
+
case "memoryFault": return `${a} accessed memory it doesn't own (outside an array, or freed). A real program might crash here.`;
|
|
1244
|
+
case "unsupported": {
|
|
1245
|
+
let e = St(r, /^Unsupported [\w ]+: /);
|
|
1246
|
+
return D(n) ? `${a} uses ${e}, which CodeFlip can't run yet. Try writing that part with basic Java (loops, arrays, ArrayList, HashMap).` : `${a} uses ${e}, which CodeFlip can't run yet.`;
|
|
1247
|
+
}
|
|
1248
|
+
case "unhandledRejection": return `A Promise was rejected with ${St(r, /^Uncaught \(in promise\) /)} and nothing handled it (no .catch, and no try/catch around an await of it), so the program stopped.`;
|
|
1249
|
+
case "uncaught": return T(n) ? Et(e, r) : E(n) ? Ot(e, r) : D(n) ? wt(e, t) ?? `Stopped on line ${e.line}: ${r}.` : `${a} threw ${St(r, /^Uncaught exception: (?:std::)?/)} and nothing caught it, so the program stopped (C++ calls std::terminate).`;
|
|
1250
|
+
}
|
|
1251
|
+
return `Stopped on line ${e.line}: ${r}.`;
|
|
1252
|
+
}
|
|
1253
|
+
function wt(e, t) {
|
|
1254
|
+
let n = (e.message ?? "").match(/^Exception in thread "main" ([\w.$]+)(?::\s*(.*))?/);
|
|
1255
|
+
if (!n) return null;
|
|
1256
|
+
let r = n[1].split(".").pop(), i = n[2]?.trim() ?? "", a = t[e.line - 1] ?? "", o = `Line ${e.line}`, s = `so Java threw ${y(r)} ${r} and the program stopped`;
|
|
1257
|
+
if (r === "NullPointerException") {
|
|
1258
|
+
let e = i.match(/because "([^"]+)" is null/)?.[1];
|
|
1259
|
+
return e ? `${o} used ${e}, but ${e} is null (it doesn't refer to any object), ${s}.` : `${o} used a variable that is null (it doesn't refer to any object), ${s}.`;
|
|
1260
|
+
}
|
|
1261
|
+
let c = i.match(/^Index (-?\d+) out of bounds for length (\d+)/);
|
|
1262
|
+
if (c) {
|
|
1263
|
+
let [, e, t] = c, n = r.startsWith("Array") ? "array" : r.startsWith("String") ? "string" : "list", i = Number(t) === 0 ? "it is empty" : `its indexes go from 0 to ${Number(t) - 1}`;
|
|
1264
|
+
return `${o} used index ${e}, but the ${n} has ${t} ${Number(t) === 1 ? "element" : "elements"}, so ${i}. Java threw ${y(r)} ${r}.`;
|
|
1265
|
+
}
|
|
1266
|
+
return r === "ArithmeticException" && /by zero/.test(i) ? `${o} divided by zero, ${s}.` : r === "NoSuchElementException" && /\.next\w*\s*\(/.test(a) ? `${o} asked the Scanner for more input, but there was none left, ${s}. Add input under Edit code → Program input.` : r === "InputMismatchException" ? `${o} asked the Scanner for a number, but the next input wasn't one, ${s}.` : `${o} threw ${y(r)} ${r}${i ? ` ("${i}")` : ""} and nothing caught it, so the program stopped.`;
|
|
1267
|
+
}
|
|
1268
|
+
var Tt = [
|
|
1269
|
+
[/^ZeroDivisionError/, "Dividing by zero isn't allowed."],
|
|
1270
|
+
[/^NameError/, "Python doesn't know that name yet: it may be misspelled, or used before a line assigns it."],
|
|
1271
|
+
[/^IndexError/, "The index is past the end (indexes start at 0, so the last one is len - 1)."],
|
|
1272
|
+
[/^KeyError/, "That key isn't in the dictionary."]
|
|
1273
|
+
];
|
|
1274
|
+
function Et(e, t) {
|
|
1275
|
+
if (/^EOFError/.test(t) && e.inputEnded) return `Line ${e.line} called input(), but there was no input left to read, so Python raised EOFError. Add input under Edit code → Program input.`;
|
|
1276
|
+
let n = Tt.find(([e]) => e.test(t))?.[1], [, r, i] = t.match(/^([\w.]+): ([\s\S]*)$/) ?? [
|
|
1277
|
+
"",
|
|
1278
|
+
t,
|
|
1279
|
+
""
|
|
1280
|
+
], a = i ? `: ${i}${/[.?!]$/.test(i) ? "" : "."}` : ".";
|
|
1281
|
+
return `Line ${e.line} raised ${r}, so the program stopped${a}${n ? ` ${n}` : ""}`;
|
|
1282
|
+
}
|
|
1283
|
+
var Dt = [
|
|
1284
|
+
[/^ReferenceError: .* is not defined/, "JavaScript doesn't know that name: it may be misspelled, or used without being declared with let, const or var."],
|
|
1285
|
+
[/^ReferenceError: Cannot access/, "A variable declared with let or const can't be used until the line that declares it has run."],
|
|
1286
|
+
[/^TypeError: Cannot (?:read|set) properties of (undefined|null)/, "The value before the dot (or [ ]) is $1, so it has no properties."],
|
|
1287
|
+
[/^TypeError: .* is not a function/, "Only a function can be called with ( )."],
|
|
1288
|
+
[/^TypeError: Assignment to constant variable/, "A const variable can't be given a new value. Declare it with let if it needs to change."]
|
|
1289
|
+
];
|
|
1290
|
+
function Ot(e, t) {
|
|
1291
|
+
let n = `Line ${e.line}`, r = t.match(/^Uncaught ([\s\S]*)$/)?.[1];
|
|
1292
|
+
if (r) return `${n} threw ${r} and nothing caught it, so the program stopped.`;
|
|
1293
|
+
let i = Dt.map(([e, n]) => t.match(e) && n.replace("$1", t.match(e)[1] ?? "")).find(Boolean), [, a, o] = t.match(/^([\w$.]+): ([\s\S]*)$/) ?? [
|
|
1294
|
+
"",
|
|
1295
|
+
t,
|
|
1296
|
+
""
|
|
1297
|
+
], s = o ? `: ${o}${/[.?!]$/.test(o) ? "" : "."}` : ".";
|
|
1298
|
+
return `${n} threw ${y(a)} ${a}, so the program stopped${s}${i ? ` ${i}` : ""}`;
|
|
1299
|
+
}
|
|
1300
|
+
// ../renderer/src/describe/files.ts
|
|
1301
|
+
var kt = [
|
|
1302
|
+
/No such file or directory: '([^']+)'/,
|
|
1303
|
+
/([^\s"]+) \(No such file or directory\)/,
|
|
1304
|
+
/NoSuchFileException\W+(\S+)/
|
|
1305
|
+
];
|
|
1306
|
+
function At(e, t, r) {
|
|
1307
|
+
let i = e.steps[t], a = (e) => `Add ${e} under Edit code → Files.`;
|
|
1308
|
+
if (i.event === "exception") {
|
|
1309
|
+
for (let e of kt) {
|
|
1310
|
+
let t = (i.message ?? "").match(e)?.[1];
|
|
1311
|
+
if (t) return a(t);
|
|
1312
|
+
}
|
|
1313
|
+
return "";
|
|
1314
|
+
}
|
|
1315
|
+
let o = e.steps[t - 1], s = o && (r[o.line - 1] ?? "").match(/(\w+)\s*=\s*fopen\s*\(\s*"([^"]+)"\s*,\s*"r/);
|
|
1316
|
+
if (!s || n(e, t)[s[2]] !== void 0) return "";
|
|
1317
|
+
let c = [...i.frames.at(-1)?.locals ?? [], ...i.globals].find((e) => e.name === s[1])?.value;
|
|
1318
|
+
return c?.k === "ptr" && c.target.kind === "null" ? `fopen couldn't find ${s[2]}, so it returned NULL. ${a(s[2])}` : "";
|
|
1319
|
+
}
|
|
1320
|
+
function jt(e, t) {
|
|
1321
|
+
let r = e.steps[t]?.files;
|
|
1322
|
+
if (!r || t === 0) return [];
|
|
1323
|
+
let i = n(e, t - 1);
|
|
1324
|
+
return Object.entries(r).flatMap(([e, t]) => {
|
|
1325
|
+
let n = i[e];
|
|
1326
|
+
return t === null ? n === void 0 ? [] : [{
|
|
1327
|
+
name: e,
|
|
1328
|
+
kind: "deleted",
|
|
1329
|
+
added: ""
|
|
1330
|
+
}] : n === void 0 ? [{
|
|
1331
|
+
name: e,
|
|
1332
|
+
kind: "created",
|
|
1333
|
+
added: t
|
|
1334
|
+
}] : n === t ? [] : t.startsWith(n) ? [{
|
|
1335
|
+
name: e,
|
|
1336
|
+
kind: "appended",
|
|
1337
|
+
added: t.slice(n.length)
|
|
1338
|
+
}] : [{
|
|
1339
|
+
name: e,
|
|
1340
|
+
kind: "rewritten",
|
|
1341
|
+
added: t
|
|
1342
|
+
}];
|
|
1343
|
+
});
|
|
1344
|
+
}
|
|
1345
|
+
function Mt(e) {
|
|
1346
|
+
let t = e.split("\n").length - +!!e.endsWith("\n");
|
|
1347
|
+
return t === 1 && !e.endsWith("\n") && e.length <= 40 ? `"${e}"` : t === 1 ? "1 line" : `${t} lines`;
|
|
1348
|
+
}
|
|
1349
|
+
function Nt(e, t) {
|
|
1350
|
+
let r = e.steps[t];
|
|
1351
|
+
if (!r || t === 0 || !r.files && !r.fileEvents) return "";
|
|
1352
|
+
let i = n(e, t - 1), a = [], o = /* @__PURE__ */ new Map(), s = /* @__PURE__ */ new Map();
|
|
1353
|
+
for (let e of r.fileEvents ?? []) e.op === "open" ? o.set(e.file, e.mode ?? "r") : s.set(e.file, (s.get(e.file) ?? 0) + (e.lines ?? NaN));
|
|
1354
|
+
for (let [e, t] of o) {
|
|
1355
|
+
let n = i[e] === void 0;
|
|
1356
|
+
t === "r" ? a.push(`It opened ${e} for reading.`) : t === "w" ? a.push(n ? `It opened ${e} for writing, creating it.` : `It opened ${e} for writing, which empties it.`) : a.push(n ? `It opened ${e} for adding to its end, creating it.` : `It opened ${e} for adding to its end.`);
|
|
1357
|
+
}
|
|
1358
|
+
for (let [e, t] of s) a.push(Number.isNaN(t) || t === 0 ? `It read from ${e}.` : `It read ${t === 1 ? "1 line" : `${t} lines`} from ${e}.`);
|
|
1359
|
+
for (let n of jt(e, t)) o.has(n.name) && o.get(n.name) !== "r" && n.added === "" || (n.kind === "deleted" ? a.push(`It deleted ${n.name}.`) : n.kind === "created" ? a.push(n.added ? `It created ${n.name} and wrote ${Mt(n.added)} to it.` : `It created ${n.name}, empty.`) : n.kind === "appended" ? a.push(`It wrote ${Mt(n.added)} to ${n.name}.`) : a.push(n.added ? `It wrote ${n.name} anew: it now holds ${Mt(n.added)}.` : `${n.name} is now empty.`));
|
|
1360
|
+
return a.length ? `${a.join(" ")} ` : "";
|
|
1361
|
+
}
|
|
1362
|
+
// ../renderer/src/describe/heap.ts
|
|
1363
|
+
var Pt = (e, t, n) => [...e.frames.flatMap((e) => e.locals), ...e.globals].some((e) => e.name === t && e.value.k === "ptr" && e.value.target.kind === "heap" && e.value.target.id === n);
|
|
1364
|
+
function Ft(e, t, n, r) {
|
|
1365
|
+
let i = [], a = r === "cpp" ? ae(e) : void 0;
|
|
1366
|
+
if (a) {
|
|
1367
|
+
let n = (e) => [...e.frames.flatMap((e) => e.locals.map((t) => [`${e.id}:${t.name}`, t])), ...e.globals.map((e) => [e.name, e])], o = new Map(n(e));
|
|
1368
|
+
for (let [s, c] of n(t)) {
|
|
1369
|
+
let n = o.get(s)?.value;
|
|
1370
|
+
if (c.value.k !== "ptr" || c.value.target.kind !== "dangling" || n?.k !== "ptr" || n.target.kind !== "heap" || n.address !== c.value.address) continue;
|
|
1371
|
+
let l = n.target.id, u = e.heap.find((e) => e.id === l);
|
|
1372
|
+
if (!u || !j(u.type, r) || !t.heap.some((e) => e.id === l)) continue;
|
|
1373
|
+
let d = a.get(l)?.label ?? `the ${u.type}`;
|
|
1374
|
+
i.push(`${d} moved its elements to new memory as it grew, so ${c.name} is now dangling (it still holds the old address)`);
|
|
1375
|
+
}
|
|
1376
|
+
}
|
|
1377
|
+
for (let o of O(r) ? [] : e.heap) {
|
|
1378
|
+
if (t.heap.some((e) => e.id === o.id)) continue;
|
|
1379
|
+
if (D(r)) {
|
|
1380
|
+
n.some((e) => e.before?.k === "ptr" && e.before.target.kind === "heap" && e.before.target.id === o.id) && i.push(`nothing refers to ${o.value.k === "record" ? R(o, o.type) : `the ${o.type}`} any more, so Java cleans it up`);
|
|
1381
|
+
continue;
|
|
1382
|
+
}
|
|
1383
|
+
if (j(o.type, r)) {
|
|
1384
|
+
let n = e.frames.find((e) => e.locals.some((e) => e.value.k === "ptr" && e.value.target.kind === "heap" && e.value.target.id === o.id));
|
|
1385
|
+
if (n && !t.frames.some((e) => e.id === n.id)) continue;
|
|
1386
|
+
let r = a?.get(o.id)?.label;
|
|
1387
|
+
r ? /^\w+$/.test(r) ? i.push(`${r} went out of scope, so it was destroyed and its memory freed`) : i.push(`${r} was destroyed with it, freeing its memory`) : i.push(`${b(o.type)} was destroyed and its memory freed`);
|
|
1388
|
+
continue;
|
|
1389
|
+
}
|
|
1390
|
+
if (r === "cpp" && J(e, o.id).length) {
|
|
1391
|
+
let n = [...t.frames.flatMap((e) => e.locals), ...t.globals].filter((t) => t.value.k === "ptr" && t.value.target.kind === "dangling" && Pt(e, t.name, o.id)).map((e) => e.name), r = n.length ? `, leaving ${x(n)} dangling` : "";
|
|
1392
|
+
i.push(`nothing owns the ${M(o.type)} any more, so it was deleted${r}`);
|
|
1393
|
+
continue;
|
|
1394
|
+
}
|
|
1395
|
+
if (o.exception) {
|
|
1396
|
+
i.push(`the catch block is done, so the ${o.type} that was thrown is destroyed`);
|
|
1397
|
+
continue;
|
|
1398
|
+
}
|
|
1399
|
+
let s = [...t.frames.flatMap((e) => e.locals), ...t.globals].filter((e) => e.value.k === "ptr" && e.value.target.kind === "dangling" && e.value.target.id === o.id).map((e) => e.name);
|
|
1400
|
+
i.push(s.length ? `the ${o.type} was freed: ${x(s)} still ${s.length > 1 ? "hold" : "holds"} its address, but that memory is no longer yours, so ${x(s)} ${s.length > 1 ? "are" : "is"} now dangling` : `the ${o.type} on the heap was freed`);
|
|
1401
|
+
}
|
|
1402
|
+
for (let n of t.heap) {
|
|
1403
|
+
let t = e.heap.find((e) => e.id === n.id);
|
|
1404
|
+
n.unreachable && t && !t.unreachable && i.push(`the old ${n.type} is leaked (nothing points to it any more)`);
|
|
1405
|
+
}
|
|
1406
|
+
return i;
|
|
1407
|
+
}
|
|
1408
|
+
// ../renderer/src/describe/index.ts
|
|
1409
|
+
function It(e, t, n = []) {
|
|
1410
|
+
let r = e.steps[t];
|
|
1411
|
+
if (!r) return "";
|
|
1412
|
+
if (e.awaitingInput && t === e.steps.length - 1) {
|
|
1413
|
+
let i = Lt(e, t, n), a = e.stdout.slice(r.stdoutLength).trim(), o = a ? `printed "${a.length > 60 ? `${a.slice(0, 57)}…` : a}" and ` : "";
|
|
1414
|
+
return `${i ? `${i} ` : ""}${Nt(e, t)}Line ${r.line} ${o}is waiting for input: type a line in the box under Output and press Enter.`;
|
|
1415
|
+
}
|
|
1416
|
+
let i = Nt(e, t).trimEnd();
|
|
1417
|
+
return [
|
|
1418
|
+
Lt(e, t, n),
|
|
1419
|
+
i,
|
|
1420
|
+
At(e, t, n)
|
|
1421
|
+
].filter(Boolean).join(" ").trim();
|
|
1422
|
+
}
|
|
1423
|
+
function Lt(e, t, n) {
|
|
1424
|
+
let i = e.steps[t], a = t > 0 ? e.steps[t - 1] : void 0, o = e.language, s = i.frames.at(-1);
|
|
1425
|
+
if (i.event === "exception" || i.event === "limit") return Ct(i, n, o);
|
|
1426
|
+
if (a && i.thrown) return Y(i, a, n, o);
|
|
1427
|
+
let c = a && o === "cpp" ? ke(e, t, n) : void 0;
|
|
1428
|
+
if (a && c) {
|
|
1429
|
+
let e = c.type.replace(/^std::/, "");
|
|
1430
|
+
return X(i, a, n, o, c.what !== void 0 && !/^std::/.test(c.type) ? `the ${e} ${c.what}` : `the ${e}`);
|
|
1431
|
+
}
|
|
1432
|
+
if (!a) return `The program starts.${D(o) && i.globals.length ? ` Before main started, Java ${Ie(i.globals, i)}.` : ""} Next it will run line ${i.line}${s ? ` in ${V(s.name, o)}` : ""}.`;
|
|
1433
|
+
let l = [], u = i.event === "resume" || i.resumedLine !== void 0, d = n[(i.event === "resume" ? i.line : i.resumedLine ?? a.line) - 1] ?? "", f = D(o) ? i.globals.filter((e) => !a.globals.some((t) => t.name === e.name)) : [], p = he(a, i, o, d).filter((e) => !f.some((t) => e.path === `global:${t.name}`) && !(E(o) && /^var:[^:]+:this$/.test(e.path))), m = u ? i.frames.at(-1) : a.frames.at(-1), h = (() => {
|
|
1434
|
+
let e = a.frames.at(-1), t = (T(o) && e && B(e.name, o) ? e.locals.find((e) => e.name === "self")?.value : void 0) ?? e?.returnValue;
|
|
1435
|
+
if (u && t?.k === "ptr" && t.target.kind === "heap") return t.target.id;
|
|
1436
|
+
let n = e?.locals.find((e) => e.name === "this")?.value;
|
|
1437
|
+
if (u && (D(o) || E(o)) && B(e.name, o) && n?.k === "ptr" && n.target.kind === "heap") return n.target.id;
|
|
1438
|
+
})(), g = /* @__PURE__ */ new Set();
|
|
1439
|
+
if (E(o)) {
|
|
1440
|
+
let e = rt(i.job?.promise);
|
|
1441
|
+
for (let t = p.length - 1; t >= 0; t--) e && p[t].path.startsWith(`heap:${e}.`) && p.splice(t, 1);
|
|
1442
|
+
let t = p.filter((e) => tt(e, i) === "state").map((e) => e.path.replace(/\.state$/, ""));
|
|
1443
|
+
for (let e = p.length - 1; e >= 0; e--) {
|
|
1444
|
+
let n = tt(p[e], i);
|
|
1445
|
+
(n === "value" || n === "reason") && t.includes(p[e].path.replace(/\.\w+$/, "")) && p.splice(e, 1);
|
|
1446
|
+
}
|
|
1447
|
+
}
|
|
1448
|
+
let _ = p.slice(0, 3).map((e) => {
|
|
1449
|
+
if (E(o) && tt(e, i) === "state") return ot(e, i);
|
|
1450
|
+
let t = Ee(e, m, d);
|
|
1451
|
+
if (h && e.after.k === "ptr" && e.after.target.kind === "heap" && e.after.target.id === h) return j(e.after.type, o) ? `${e.label} now holds it` : `${e.label} now ${w(e.after) || !ee(o) ? "refers" : "points"} to it`;
|
|
1452
|
+
if (O(o) && e.path.startsWith("heap:")) return Ze(e, i, a, d, g, o);
|
|
1453
|
+
let n = D(o) ? Fe(e, m, a, d) + Ne(e, i) : "";
|
|
1454
|
+
return Xe(e, i, a, o, d) + (t ? ` (through ${t})` : "") + n;
|
|
1455
|
+
});
|
|
1456
|
+
p.length > 3 && _.push(`${p.length - 3} more values changed`), _.push(...Ft(a, i, p, o));
|
|
1457
|
+
let v = a.frames.at(-1);
|
|
1458
|
+
if ((i.event === "return" || i.event === "yield" || i.event === "await") && s && !(E(o) && i.job)) return gt(e, t, n, _);
|
|
1459
|
+
let y = O(o) && i.event === "return" && !s ? E(o) ? mt(i) : " The program has finished." : "";
|
|
1460
|
+
if ((i.event === "resume" || i.resumedLine !== void 0) && (s || O(o)) && v) return `${_t(e, t, n, _)}${y}`;
|
|
1461
|
+
if (E(o) && i.job) l.push(...pt(e, t, n, _));
|
|
1462
|
+
else if (s && i.frames.length > a.frames.length && !a.frames.some((e) => e.id === s.id)) l.push(ht(e, t, n, _));
|
|
1463
|
+
else if (T(o) && i.event === "step" && i.message && Ue.test(n[i.line - 1] ?? "")) l.push(We(i, a, n));
|
|
1464
|
+
else if (E(o) && i.event === "step" && i.message && Qe.test(n[i.line - 1] ?? "")) l.push($e(i, a));
|
|
1465
|
+
else if (D(o) && i.event === "step" && K.test(n[i.line - 1] ?? "")) {
|
|
1466
|
+
let e = a.frames[i.frames.length];
|
|
1467
|
+
l.push(e && !i.frames.some((t) => t.id === e.id) ? `${V(e.name, o)} threw an exception on line ${e.line}, so Java left ${V(e.name, o)} and jumped to the catch on line ${i.line}.` : `Line ${a.line} threw an exception, so Java jumped to the catch on line ${i.line}.`);
|
|
1468
|
+
} else {
|
|
1469
|
+
let e = D(o) || E(o), t = !e || v?.id === s?.id ? xe(n, a, i, o) || (e ? Le(n, a, i) : "") : "", r = [..._, ...t ? [t] : []];
|
|
1470
|
+
T(o) && !r.length && r.push(...Ge(n, a, i)), E(o) && !r.length && !(i.queued && a.event === "return") && r.push(...et(n, a, i));
|
|
1471
|
+
let c = T(o) ? Ue : E(o) ? Qe : null, u = c && a.message && c.test(n[a.line - 1] ?? "") ? a.message.match(/^[\w.$]+/)?.[0] : void 0, d = D(o) && K.test(n[a.line - 1] ?? "") ? "caught the exception" : u ? `caught the ${u}` : "ran";
|
|
1472
|
+
(!E(o) || s || !i.queued || a.event !== "return" || _.length) && l.push(r.length ? `Line ${a.line} ${d}: ${x(r)}.` : `Line ${a.line} ${d}.`);
|
|
1473
|
+
}
|
|
1474
|
+
if (f.length) {
|
|
1475
|
+
let e = f[0].name.split(".")[0];
|
|
1476
|
+
l.push(`Using ${e} for the first time, Java ${Ie(f, i).replace("the static", "its static")}.`);
|
|
1477
|
+
}
|
|
1478
|
+
let b = e.stdin ?? "", S = b.slice(a.stdinConsumed ?? 0, i.stdinConsumed ?? 0).trim(), C = e.stdout.slice(a.stdoutLength, i.stdoutLength);
|
|
1479
|
+
if (D(o)) {
|
|
1480
|
+
let e = b.slice(r(b, a.stdinConsumed ?? 0), r(b, i.stdinConsumed ?? 0)).replace(/\n?$/, "\n"), t = e.length > 1 ? C.indexOf(e) : -1;
|
|
1481
|
+
t >= 0 && (C = C.slice(0, t) + C.slice(t + e.length));
|
|
1482
|
+
}
|
|
1483
|
+
let k = C.trimEnd();
|
|
1484
|
+
O(o) && S && k.endsWith(S) && (k = k.slice(0, -S.length).trimEnd());
|
|
1485
|
+
let te = k && `"${k.length > 60 ? `${k.slice(0, 57)}…` : k}"`, A = O(o) && S && l.length === 1 ? l[0].match(/^(Line \d+ ran)(?:: (.*))?\.$/) : null;
|
|
1486
|
+
if (A) {
|
|
1487
|
+
let e = `${te ? `printed ${te} and ` : ""}read "${S}" from the input`;
|
|
1488
|
+
l[0] = `${A[1]}: it ${e}${A[2] ? `, so ${A[2]}` : ""}.`;
|
|
1489
|
+
} else k && !(E(o) && i.job) && l.push(`It printed ${te}.`), S && l.push(`It read "${S}" from the input.`);
|
|
1490
|
+
if (i.inputEnded && !a.inputEnded) {
|
|
1491
|
+
let e = n[a.line - 1] ?? "", t = (i.frames.at(-1)?.locals ?? []).filter((t) => t.value.k === "uninit" && RegExp(`&\\s*${t.name}\\b`).test(e)).map((e) => e.name), r = t.length ? `, so ${x(t)} ${t.length > 1 ? "were" : "was"} not set` : "";
|
|
1492
|
+
S ? t.length && l.push(`Then the input ran out${r}.`) : l.push(`It tried to read input, but there was none${r}. Add input under Edit code → Program input.`);
|
|
1493
|
+
}
|
|
1494
|
+
return (l.join(" ") + y).trim();
|
|
1495
|
+
}
|
|
1496
|
+
// ../renderer/src/hide.ts
|
|
1497
|
+
var Rt = /(?:#|\/\/)\s*(?:codeflip|pythontutor)_hide\s*:\s*([^\n]*)/g;
|
|
1498
|
+
function zt(e) {
|
|
1499
|
+
let t = [];
|
|
1500
|
+
for (let n of e.matchAll(Rt)) for (let e of n[1].split(/[,\s]+/)) e && t.push(e);
|
|
1501
|
+
return t;
|
|
1502
|
+
}
|
|
1503
|
+
var Bt = (e) => {
|
|
1504
|
+
let t = e.map((e) => RegExp(`^${e.replace(/[.+^${}()|[\]\\]/g, "\\$&").replace(/\*/g, ".*").replace(/\?/g, ".")}$`));
|
|
1505
|
+
return (e) => t.some((t) => t.test(e));
|
|
1506
|
+
};
|
|
1507
|
+
function Z(e, t) {
|
|
1508
|
+
if (e.k === "ptr" && e.target.kind === "heap") t.push(e.target.id);
|
|
1509
|
+
else if (e.k === "array") for (let n of e.items) Z(n, t);
|
|
1510
|
+
else if (e.k === "record") for (let n of e.fields) Z(n.value, t);
|
|
1511
|
+
else if (e.k === "map") for (let n of e.entries) Z(n.key, t), Z(n.value, t);
|
|
1512
|
+
}
|
|
1513
|
+
function Vt(t, n) {
|
|
1514
|
+
if (!n.length) return t;
|
|
1515
|
+
let r = Bt(n), i = (e) => e.filter((e) => !r(e.name)), a = (e) => ({
|
|
1516
|
+
...e,
|
|
1517
|
+
locals: i(e.locals)
|
|
1518
|
+
}), o = !e[t.language].manualMemory, s = t.steps.map((e) => {
|
|
1519
|
+
let t = e.frames.map(a), n = e.paused?.map(a), r = i(e.globals), s = e.heap;
|
|
1520
|
+
if (o) {
|
|
1521
|
+
let i = new Map(e.heap.map((e) => [e.id, e])), a = [];
|
|
1522
|
+
for (let e of [...t, ...n ?? []]) {
|
|
1523
|
+
for (let t of e.locals) Z(t.value, a);
|
|
1524
|
+
e.returnValue && Z(e.returnValue, a), e.yielded && Z(e.yielded, a), e.awaiting && Z(e.awaiting, a);
|
|
1525
|
+
}
|
|
1526
|
+
e.job?.promise && Z(e.job.promise, a), e.job?.value && Z(e.job.value, a);
|
|
1527
|
+
for (let e of r) Z(e.value, a);
|
|
1528
|
+
let o = /* @__PURE__ */ new Set();
|
|
1529
|
+
for (; a.length;) {
|
|
1530
|
+
let e = a.pop(), t = i.get(e);
|
|
1531
|
+
t && !o.has(e) && (o.add(e), Z(t.value, a));
|
|
1532
|
+
}
|
|
1533
|
+
s = e.heap.filter((e) => o.has(e.id));
|
|
1534
|
+
}
|
|
1535
|
+
return {
|
|
1536
|
+
...e,
|
|
1537
|
+
frames: t,
|
|
1538
|
+
globals: r,
|
|
1539
|
+
heap: s,
|
|
1540
|
+
...n ? { paused: n } : {}
|
|
1541
|
+
};
|
|
1542
|
+
});
|
|
1543
|
+
return {
|
|
1544
|
+
...t,
|
|
1545
|
+
steps: s
|
|
1546
|
+
};
|
|
1547
|
+
}
|
|
1548
|
+
// ../renderer/src/highlight.ts
|
|
1549
|
+
var Ht = new Set("auto break case catch char class const continue default delete do double else enum extern false final float for function if import int let long new null nullptr private protected public return short signed sizeof static struct switch this throw true try typedef union unsigned var void volatile while bool boolean byte string String extends implements interface package super instanceof template typename namespace using std const_cast include define".split(" ")), Ut = new Set("False None True and as assert async await break class continue def del elif else except finally for from global if import in is lambda nonlocal not or pass raise return try while with yield".split(" ")), Wt = new Set("break case catch class const continue debugger default delete do else export extends false finally for function if import in instanceof let new null of return static super switch this throw true try typeof undefined var void while with yield async await get set".split(" ")), Gt = /* @__PURE__ */ new Set([...Wt, ..."type interface enum implements private protected public readonly abstract declare namespace as is keyof infer satisfies any unknown never number string boolean bigint symbol object".split(" ")]), Kt = /(\/\/.*$|\/\*[\s\S]*?\*\/)|("(?:\\.|[^"\\])*"|'(?:\\.|[^'\\])*'|`(?:\\.|[^`\\])*`)|(^\s*#\s*\w+)|\b(\d+(?:\.\d+)?[fFlLuU]*|0x[\da-fA-F]+)\b|\b([A-Za-z_]\w*)\b/gm, qt = /(#.*$)|((?:\b[rRbBfFuU]{1,2})?(?:"""[\s\S]*?"""|'''[\s\S]*?'''|"(?:\\.|[^"\\])*"|'(?:\\.|[^'\\])*'))|()\b(\d[\d_]*(?:\.\d*)?(?:[eE][-+]?\d+)?j?|0[xXoObB][\da-fA-F_]+)\b|\b([A-Za-z_]\w*)\b/gm, Jt = (e) => e.replace(/[&<>]/g, (e) => e === "&" ? "&" : e === "<" ? "<" : ">");
|
|
1550
|
+
function Yt(e, t = "c") {
|
|
1551
|
+
let n = t === "python", r = t === "javascript" || t === "typescript", i = n ? Ut : t === "typescript" ? Gt : r ? Wt : Ht, a = "", o = 0;
|
|
1552
|
+
for (let t of e.matchAll(n ? qt : Kt)) {
|
|
1553
|
+
let [n, s, c, l, u, d] = t, f = s ? "comment" : c ? "string" : !r && l ? "pre" : u ? "number" : d && i.has(d) ? "keyword" : null;
|
|
1554
|
+
a += Jt(e.slice(o, t.index)), a += f ? `<span class="cf-tok-${f}">${Jt(n)}</span>` : Jt(n), o = t.index + n.length;
|
|
1555
|
+
}
|
|
1556
|
+
return a + Jt(e.slice(o));
|
|
1557
|
+
}
|
|
1558
|
+
// ../renderer/src/grid.ts
|
|
1559
|
+
var Xt = 16, Zt = (e) => e.k !== "array" && e.k !== "record" && e.k !== "map", Qt = (e, t) => e === "python" && (t.type === "set" || t.type === "frozenset") || (e === "javascript" || e === "typescript") && t.type === "Set";
|
|
1560
|
+
function $t(e) {
|
|
1561
|
+
let t = /* @__PURE__ */ new Map(), n = (e) => {
|
|
1562
|
+
e.k === "ptr" && e.target.kind === "heap" ? t.set(e.target.id, (t.get(e.target.id) ?? 0) + 1) : e.k === "array" ? e.items.forEach(n) : e.k === "record" ? e.fields.forEach((e) => n(e.value)) : e.k === "map" && e.entries.forEach((e) => (n(e.key), n(e.value)));
|
|
1563
|
+
};
|
|
1564
|
+
return e.heap.forEach((e) => n(e.value)), t;
|
|
1565
|
+
}
|
|
1566
|
+
function en(t, n, r, i, a) {
|
|
1567
|
+
if (n.k !== "array" || n.truncated || n.items.length === 0 || Qt(t.lang, n)) return null;
|
|
1568
|
+
let o = [], s = !e[t.lang].manualMemory;
|
|
1569
|
+
for (let [e, c] of n.items.entries()) {
|
|
1570
|
+
let n;
|
|
1571
|
+
if (c.k === "array" && !s) {
|
|
1572
|
+
if (n = {
|
|
1573
|
+
items: c.items,
|
|
1574
|
+
...i ? { key: `${i}:${e}` } : {},
|
|
1575
|
+
...a ? {
|
|
1576
|
+
path: `${a}[${e}]`,
|
|
1577
|
+
slot: `${a}[${e}]`
|
|
1578
|
+
} : {}
|
|
1579
|
+
}, c.truncated) return null;
|
|
1580
|
+
} else if (c.k === "ptr" && c.target.kind === "heap" && s && r) {
|
|
1581
|
+
let r = c.target.id, i = t.heap.get(r)?.value;
|
|
1582
|
+
if (i?.k !== "array" || i.truncated || Qt(t.lang, i) || t.refs.get(r) !== 1) return null;
|
|
1583
|
+
n = {
|
|
1584
|
+
items: i.items,
|
|
1585
|
+
key: `heap:${r}`,
|
|
1586
|
+
path: `heap:${r}`,
|
|
1587
|
+
...a ? { slot: `${a}[${e}]` } : {}
|
|
1588
|
+
};
|
|
1589
|
+
} else return null;
|
|
1590
|
+
if (n.items.length > Xt || !n.items.every(Zt)) return null;
|
|
1591
|
+
o.push(n);
|
|
1592
|
+
}
|
|
1593
|
+
return o.some((e) => e.items.length) ? o : null;
|
|
1594
|
+
}
|
|
1595
|
+
function tn(e, t) {
|
|
1596
|
+
let n = /* @__PURE__ */ new Set();
|
|
1597
|
+
for (let r of t.heap) for (let t of en(e, r.value, !0) ?? []) t.key?.startsWith("heap:") && n.add(t.key.slice(5));
|
|
1598
|
+
return n;
|
|
1599
|
+
}
|
|
1600
|
+
function nn(e, t) {
|
|
1601
|
+
let n = {
|
|
1602
|
+
lang: t,
|
|
1603
|
+
heap: new Map(e.heap.map((e) => [e.id, e])),
|
|
1604
|
+
refs: $t(e)
|
|
1605
|
+
}, r = (e) => en(n, e, !1) !== null || e.k === "record" && e.fields.some((e) => r(e.value));
|
|
1606
|
+
return e.heap.some((e) => en(n, e.value, !0) !== null || r(e.value)) || [...e.frames, ...e.paused ?? []].some((e) => e.locals.some((e) => r(e.value))) || e.globals.some((e) => r(e.value));
|
|
1607
|
+
}
|
|
1608
|
+
// ../renderer/src/layout.ts
|
|
1609
|
+
var rn = (e) => Number(e.match(/\d+/)?.[0] ?? 2 ** 53 - 1);
|
|
1610
|
+
function Q(e, t = []) {
|
|
1611
|
+
return e.k === "ptr" && e.target.kind === "heap" ? t.push(e.target.id) : e.k === "array" ? e.items.forEach((e) => Q(e, t)) : e.k === "record" ? e.fields.forEach((e) => Q(e.value, t)) : e.k === "map" && e.entries.forEach((e) => (Q(e.key, t), Q(e.value, t))), t;
|
|
1612
|
+
}
|
|
1613
|
+
function an(e) {
|
|
1614
|
+
let t = [];
|
|
1615
|
+
return e.map((n, r) => {
|
|
1616
|
+
let i = e[r - 1], a = new Set(n.heap.map((e) => e.id)), o = new Set((i?.heap ?? []).filter((e) => !a.has(e.id)).map((e) => e.id));
|
|
1617
|
+
t = t.map((e) => e.filter((e) => a.has(e) || o.has(e))).filter((e) => e.length > 0);
|
|
1618
|
+
let s = new Map(n.heap.map((e) => [e.id, e])), c = new Map((i?.heap ?? []).map((e) => [e.id, e])), l = (e, t) => {
|
|
1619
|
+
let n = c.get(e);
|
|
1620
|
+
return !!n && !Q(n.value).includes(t);
|
|
1621
|
+
}, u = new Set(t.flat());
|
|
1622
|
+
for (let e of [...n.heap].sort((e, t) => rn(e.id) - rn(t.id))) {
|
|
1623
|
+
if (u.has(e.id)) continue;
|
|
1624
|
+
let n = !1;
|
|
1625
|
+
for (let r of t) {
|
|
1626
|
+
let t = r.findIndex((t) => {
|
|
1627
|
+
let n = s.get(t);
|
|
1628
|
+
return n?.value.k === "record" && Q(n.value).includes(e.id);
|
|
1629
|
+
});
|
|
1630
|
+
if (t >= 0) {
|
|
1631
|
+
r.splice(t + 1, 0, e.id), n = !0;
|
|
1632
|
+
break;
|
|
1633
|
+
}
|
|
1634
|
+
}
|
|
1635
|
+
if (n ||= on(t, s, e.id), !n && e.value.k === "record") {
|
|
1636
|
+
let r = Q(e.value), i = t.find((e) => r.includes(e[0]));
|
|
1637
|
+
i && (i.unshift(e.id), n = !0);
|
|
1638
|
+
}
|
|
1639
|
+
n || t.push([e.id]), u.add(e.id);
|
|
1640
|
+
}
|
|
1641
|
+
for (let e = !0; e;) {
|
|
1642
|
+
e = !1;
|
|
1643
|
+
for (let n of t.filter((e) => e.length === 1)) {
|
|
1644
|
+
let r = n[0], i = s.get(r);
|
|
1645
|
+
if (i?.value.k !== "record") continue;
|
|
1646
|
+
let a = t.find((e) => e !== n && Q(i.value).includes(e[0])), o = t.find((e) => {
|
|
1647
|
+
let t = s.get(e.at(-1));
|
|
1648
|
+
return e !== n && t?.value.k === "record" && Q(t.value).includes(r);
|
|
1649
|
+
});
|
|
1650
|
+
if (a) a.unshift(r);
|
|
1651
|
+
else if (o) o.push(r);
|
|
1652
|
+
else continue;
|
|
1653
|
+
t = t.filter((e) => e !== n), e = !0;
|
|
1654
|
+
break;
|
|
1655
|
+
}
|
|
1656
|
+
for (let n of t.filter((e) => e.length === 1)) {
|
|
1657
|
+
let r = n[0];
|
|
1658
|
+
if ((t[t.indexOf(n) - 1] ?? []).some((e) => {
|
|
1659
|
+
let t = s.get(e);
|
|
1660
|
+
return t?.value.k === "array" && Q(t.value).includes(r);
|
|
1661
|
+
})) continue;
|
|
1662
|
+
let i = t.filter((e) => e !== n);
|
|
1663
|
+
if (on(i, s, r, (e) => l(e, r))) {
|
|
1664
|
+
t = i, e = !0;
|
|
1665
|
+
break;
|
|
1666
|
+
}
|
|
1667
|
+
}
|
|
1668
|
+
}
|
|
1669
|
+
return t.map((e) => [...e]);
|
|
1670
|
+
});
|
|
1671
|
+
}
|
|
1672
|
+
function on(e, t, n, r = () => !0) {
|
|
1673
|
+
let i = (e) => {
|
|
1674
|
+
let i = t.get(e);
|
|
1675
|
+
return r(e) && i?.value.k === "array" && Q(i.value).includes(n);
|
|
1676
|
+
}, a = e.findIndex((e) => e.some(i));
|
|
1677
|
+
if (a < 0) return !1;
|
|
1678
|
+
let o = e[a].find(i), s = Q(t.get(o).value), c = (e) => e !== o && s.includes(e), l = e[a + 1];
|
|
1679
|
+
l?.length && l.every(c) || (l = [], e.splice(a + 1, 0, l));
|
|
1680
|
+
let u = l.findIndex((e) => s.indexOf(e) > s.indexOf(n));
|
|
1681
|
+
return l.splice(u < 0 ? l.length : u, 0, n), !0;
|
|
1682
|
+
}
|
|
1683
|
+
// ../renderer/src/icons.ts
|
|
1684
|
+
var sn = (e) => `<svg viewBox="0 0 20 20" width="20" height="20" aria-hidden="true" focusable="false" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">${e}</svg>`, cn = {
|
|
1685
|
+
first: sn("<path d=\"M5 4v12\"/><path d=\"M15 5l-6 5 6 5\"/>"),
|
|
1686
|
+
back: sn("<path d=\"M12.5 5l-5 5 5 5\"/>"),
|
|
1687
|
+
next: sn("<path d=\"M7.5 5l5 5-5 5\"/>"),
|
|
1688
|
+
last: sn("<path d=\"M15 4v12\"/><path d=\"M5 5l6 5-6 5\"/>"),
|
|
1689
|
+
focusLine: "<svg viewBox=\"0 0 20 14\" width=\"20\" height=\"14\" aria-hidden=\"true\"><path d=\"M1 5h10V1l8 6-8 6V9H1z\" fill=\"currentColor\"/></svg>",
|
|
1690
|
+
nextLine: "<svg viewBox=\"0 0 20 14\" width=\"20\" height=\"14\" aria-hidden=\"true\"><path d=\"M1.75 5.75h10V2.5l6 4.5-6 4.5V8.25h-10z\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linejoin=\"round\"/></svg>",
|
|
1691
|
+
broken: sn("<path d=\"M4 10h4\"/><path d=\"M12 10h4\"/><path d=\"M9 6l2 8\"/>")
|
|
1692
|
+
}, ln = (e) => {
|
|
1693
|
+
let t = e.match(/(?:^|::)(\w+)::\1$/)?.[1] ?? e.match(/(\w+)\.<init>$/)?.[1];
|
|
1694
|
+
return t ? `${t} constructor` : e;
|
|
1695
|
+
};
|
|
1696
|
+
function un(e, t, n) {
|
|
1697
|
+
if (e?.k !== "array" || e.items.length === 0 || !n || t <= 0) return;
|
|
1698
|
+
let r = Math.floor(t / (n / e.items.length));
|
|
1699
|
+
return r < e.items.length ? r : r === e.items.length ? "end" : void 0;
|
|
1700
|
+
}
|
|
1701
|
+
var dn = 12, fn = 3, pn = 6;
|
|
1702
|
+
function $(e, t, n, r) {
|
|
1703
|
+
let i = e.createElement(t);
|
|
1704
|
+
return n && (i.className = n), r !== void 0 && (i.textContent = r), i;
|
|
1705
|
+
}
|
|
1706
|
+
function mn(e, t) {
|
|
1707
|
+
return $(e, "span", "cf-sr", t);
|
|
1708
|
+
}
|
|
1709
|
+
function hn(e, t, n) {
|
|
1710
|
+
switch (e.kind) {
|
|
1711
|
+
case "heap": {
|
|
1712
|
+
let n = t.heap.get(e.id), r = un(n?.value, e.offset, n?.size);
|
|
1713
|
+
return r === void 0 ? `heap:${e.id}` : `heap:${e.id}:${r}`;
|
|
1714
|
+
}
|
|
1715
|
+
case "stack":
|
|
1716
|
+
case "global": {
|
|
1717
|
+
let n = e.kind === "stack" ? `var:${e.frame}:${e.name}` : `global:${e.name}`, r = un(t.vars.get(n), e.offset, e.size);
|
|
1718
|
+
return r === void 0 ? n : `${n}:${r}`;
|
|
1719
|
+
}
|
|
1720
|
+
case "string": return `lit:${n}`;
|
|
1721
|
+
case "dangling": return e.id && t.ghosts.has(e.id) ? `ghost:${e.id}` : null;
|
|
1722
|
+
default: return null;
|
|
1723
|
+
}
|
|
1724
|
+
}
|
|
1725
|
+
function gn(t, n, r, i) {
|
|
1726
|
+
let { doc: a } = t;
|
|
1727
|
+
switch (n.k) {
|
|
1728
|
+
case "prim": return $(a, "span", "cf-val", n.text);
|
|
1729
|
+
case "uninit": {
|
|
1730
|
+
let n = $(a, "span", "cf-val cf-uninit", "?");
|
|
1731
|
+
return n.title = e[t.lang].dynamic ? "Not set yet: the line that gives it a value hasn't run" : "Not initialized yet: this memory still holds leftover data", n.append(mn(a, " (uninitialized)")), n;
|
|
1732
|
+
}
|
|
1733
|
+
case "opaque": return $(a, "span", "cf-val cf-opaque", n.text);
|
|
1734
|
+
case "ptr": {
|
|
1735
|
+
let r = n.target;
|
|
1736
|
+
if (r.kind === "null") return $(a, "span", "cf-val cf-null", e[t.lang].nullWord);
|
|
1737
|
+
if (r.kind === "dangling" || r.kind === "invalid") {
|
|
1738
|
+
let e = $(a, "span", "cf-val cf-bad"), i = t.quiet ? void 0 : hn(r, t, n.address);
|
|
1739
|
+
if (i || (e.innerHTML = cn.broken), e.append(r.kind === "dangling" ? "dangling" : "invalid"), i) {
|
|
1740
|
+
let t = $(a, "span", "cf-dot cf-dot-dangling");
|
|
1741
|
+
t.dataset.cfPtr = i, e.append(t);
|
|
1742
|
+
}
|
|
1743
|
+
return e.title = r.kind === "dangling" ? "Still holds the address of memory that was freed; using it is a bug" : "Points to an address that isn't valid", e;
|
|
1744
|
+
}
|
|
1745
|
+
r.kind === "string" && !t.quiet && t.literals.set(n.address, r.text);
|
|
1746
|
+
let i = t.quiet ? void 0 : hn(r, t, n.address), o = n.type.endsWith("&"), s = $(a, "span", "cf-ptr"), c = t.lang === "cpp" && /^(?:const )?weak_ptr</.test(n.type), l = $(a, "span", o ? "cf-dot cf-ref" : c ? "cf-dot cf-weak" : "cf-dot");
|
|
1747
|
+
o && (l.title = "A reference: another name for an existing variable"), c && (l.title = "A weak_ptr: it can reach the object without owning it"), t.quiet && (l.title = `When this call finished, it ${o ? "referred" : "pointed"} to ${F(n, t.lang)}`), i && (l.dataset.cfPtr = i);
|
|
1748
|
+
let u = t.lang === "java" && r.kind === "heap" ? t.heap.get(r.id) : void 0, d = F(u ? {
|
|
1749
|
+
...n,
|
|
1750
|
+
type: u.type
|
|
1751
|
+
} : n, t.lang), f = o || !e[t.lang].manualMemory ? "refers to" : "points to", p = r.kind === "stack" || r.kind === "global" ? un(t.vars.get(r.kind === "stack" ? `var:${r.frame}:${r.name}` : `global:${r.name}`), r.offset, r.size) : void 0, m = r.kind === "heap" ? t.heap.get(r.id) : void 0, h = r.kind === "heap" && m && un(m.value, r.offset, m.size) === "end" ? `points just past the end of the ${m.type} on the heap` : p === "end" ? `points just past the end of ${d}` : p === void 0 ? `${f} ${d}` : `${f} ${d}[${p}]`;
|
|
1752
|
+
return s.append(l, mn(a, h)), s;
|
|
1753
|
+
}
|
|
1754
|
+
case "array": {
|
|
1755
|
+
let e = t.grid && !(r && t.pastEnd.has(r)) ? en(t.grid, n, !1, r, i) : null;
|
|
1756
|
+
if (e) return _n(t, e);
|
|
1757
|
+
let o = $(a, "div", "cf-array");
|
|
1758
|
+
o.setAttribute("role", "list");
|
|
1759
|
+
let s = t.lang === "python" && (n.type === "set" || n.type === "frozenset") || (t.lang === "javascript" || t.lang === "typescript") && n.type === "Set";
|
|
1760
|
+
if (n.items.forEach((e, n) => {
|
|
1761
|
+
let c = i ? `${i}[${n}]` : void 0, l = c !== void 0 && (t.changed.has(c) || t.changed.has(`${c}~`)), u = $(a, "div", l ? "cf-cell cf-changed-cell" : "cf-cell");
|
|
1762
|
+
u.setAttribute("role", "listitem"), r && (u.dataset.cfKey = `${r}:${n}`), s || u.append($(a, "span", "cf-index", String(n))), u.append(gn(t, e, void 0, c)), l && u.append(mn(a, " (changed this step)")), o.append(u);
|
|
1763
|
+
}), r && !n.truncated && t.pastEnd.has(r)) {
|
|
1764
|
+
let e = $(a, "div", "cf-cell cf-cell-end");
|
|
1765
|
+
e.setAttribute("role", "listitem"), e.dataset.cfKey = `${r}:end`, e.title = "Just past the last element: a pointer can stop here, but there's nothing to read", e.append($(a, "span", "cf-index", String(n.items.length)), mn(a, "end of the array")), o.append(e);
|
|
1766
|
+
}
|
|
1767
|
+
return n.truncated && o.append($(a, "div", "cf-cell cf-more", "…")), n.items.length === 0 && o.append($(a, "span", "cf-empty", "empty")), o;
|
|
1768
|
+
}
|
|
1769
|
+
case "record": {
|
|
1770
|
+
let e = vn(t, n.fields.map((e) => [
|
|
1771
|
+
e.name,
|
|
1772
|
+
e.value,
|
|
1773
|
+
i ? `${i}.${e.name}` : void 0
|
|
1774
|
+
]), n.union ? "cf-record cf-union" : "cf-record");
|
|
1775
|
+
if (n.union) {
|
|
1776
|
+
let t = $(a, "caption", "cf-union-note", "shared memory");
|
|
1777
|
+
t.title = "A union: its members share the same bytes, so writing one changes what the others read", e.prepend(t);
|
|
1778
|
+
}
|
|
1779
|
+
return e;
|
|
1780
|
+
}
|
|
1781
|
+
case "map": return vn(t, n.entries.map((e) => [
|
|
1782
|
+
F(e.key, t.lang),
|
|
1783
|
+
e.value,
|
|
1784
|
+
i ? `${i}{${I(e.key)}}` : void 0
|
|
1785
|
+
]), "cf-record cf-map");
|
|
1786
|
+
}
|
|
1787
|
+
}
|
|
1788
|
+
function _n(e, t) {
|
|
1789
|
+
let { doc: n } = e, r = $(n, "table", "cf-grid"), i = $(n, "tr");
|
|
1790
|
+
i.append($(n, "td", "cf-grid-corner"));
|
|
1791
|
+
let a = Math.max(...t.map((e) => e.items.length));
|
|
1792
|
+
for (let e = 0; e < a; e++) {
|
|
1793
|
+
let t = $(n, "th", "cf-index", String(e));
|
|
1794
|
+
t.scope = "col", i.append(t);
|
|
1795
|
+
}
|
|
1796
|
+
let o = $(n, "thead");
|
|
1797
|
+
o.append(i);
|
|
1798
|
+
let s = $(n, "tbody");
|
|
1799
|
+
return t.forEach((t, r) => {
|
|
1800
|
+
let i = $(n, "tr"), a = t.slot !== void 0 && e.changed.has(t.slot), o = $(n, "th", a ? "cf-index cf-changed-cell" : "cf-index", String(r));
|
|
1801
|
+
o.scope = "row", t.key && (o.dataset.cfKey = t.key), i.append(o), t.items.forEach((r, o) => {
|
|
1802
|
+
let s = t.path === void 0 ? void 0 : `${t.path}[${o}]`, c = a || s !== void 0 && (e.changed.has(s) || e.changed.has(`${s}~`)), l = $(n, "td", c ? "cf-changed-cell" : void 0);
|
|
1803
|
+
t.key && (l.dataset.cfKey = `${t.key}:${o}`), l.append(gn(e, r, void 0, s)), c && l.append(mn(n, " (changed this step)")), i.append(l);
|
|
1804
|
+
}), t.items.length || i.append($(n, "td", "cf-grid-none", "empty")), s.append(i);
|
|
1805
|
+
}), r.append(o, s), r;
|
|
1806
|
+
}
|
|
1807
|
+
function vn(e, t, n) {
|
|
1808
|
+
let r = $(e.doc, "table", n);
|
|
1809
|
+
for (let [n, i, a] of t) {
|
|
1810
|
+
let t = $(e.doc, "tr"), o = $(e.doc, "th", void 0, n);
|
|
1811
|
+
o.scope = "row";
|
|
1812
|
+
let s = a !== void 0 && e.changed.has(a), c = $(e.doc, "td", s ? "cf-changed-cell" : void 0);
|
|
1813
|
+
c.append(gn(e, i, void 0, a)), s && c.append(mn(e.doc, " (changed this step)")), t.append(o, c), r.append(t);
|
|
1814
|
+
}
|
|
1815
|
+
if (t.length === 0) {
|
|
1816
|
+
let t = $(e.doc, "tr");
|
|
1817
|
+
t.append($(e.doc, "td", "cf-empty", "empty")), r.append(t);
|
|
1818
|
+
}
|
|
1819
|
+
return r;
|
|
1820
|
+
}
|
|
1821
|
+
function yn(t, n, r) {
|
|
1822
|
+
let i = $(t.doc, "table", "cf-vars");
|
|
1823
|
+
for (let a of n) {
|
|
1824
|
+
let n = r(a), o = $(t.doc, "tr"), s = t.changed.has(n);
|
|
1825
|
+
s && (o.className = "cf-changed");
|
|
1826
|
+
let c = $(t.doc, "th");
|
|
1827
|
+
c.scope = "row", c.append($(t.doc, "span", "cf-name", a.name)), !e[t.lang].dynamic && a.type && c.append($(t.doc, "span", "cf-type", a.type));
|
|
1828
|
+
let l = $(t.doc, "td");
|
|
1829
|
+
l.dataset.cfKey = n, l.append(gn(t, a.value, n, s ? void 0 : n)), s && l.append(mn(t.doc, " (changed this step)")), o.append(c, l), i.append(o);
|
|
1830
|
+
}
|
|
1831
|
+
return i;
|
|
1832
|
+
}
|
|
1833
|
+
function bn(e, t, n, r = !1, i = !1) {
|
|
1834
|
+
let a = t.returnValue || t.yielded || t.awaiting, o = r ? " cf-finished" : i ? " cf-paused" : `${n ? " cf-current" : ""}${a ? " cf-returning" : ""}`, s = $(e.doc, "section", `cf-frame${o}`), c = t.async ? `waiting at line ${t.line}` : `paused at line ${t.line}`;
|
|
1835
|
+
s.setAttribute("aria-label", `${t.name} frame${r ? ", finished" : i ? `, ${c}` : ""}`), s.dataset.cfAnim = `frame:${t.id}`;
|
|
1836
|
+
let l = $(e.doc, "h4", "cf-frame-name", ln(t.name));
|
|
1837
|
+
if (r) {
|
|
1838
|
+
let t = $(e.doc, "span", "cf-tag cf-tag-freed", "finished");
|
|
1839
|
+
t.title = "This call already returned; its variables no longer exist. Shown as they were when it returned.", l.append(t);
|
|
1840
|
+
} else if (i) {
|
|
1841
|
+
let n = $(e.doc, "span", "cf-tag cf-tag-paused", c);
|
|
1842
|
+
n.title = t.async ? "This async function is paused at await until the Promise it waits for settles. Its variables keep their values meanwhile." : "This generator handed back a value and is waiting to be asked for the next one. Its variables keep their values meanwhile.", l.append(n);
|
|
1843
|
+
}
|
|
1844
|
+
if (s.append(l), t.captured?.length) {
|
|
1845
|
+
let n = t.captured.join(", ");
|
|
1846
|
+
s.append($(e.doc, "p", "cf-empty cf-frame-hint", `Also uses ${n} from its function object.`));
|
|
1847
|
+
} else !t.locals.length && !a && s.append($(e.doc, "p", "cf-empty cf-frame-hint", "Variables appear here as their lines run."));
|
|
1848
|
+
if (t.locals.length || a) {
|
|
1849
|
+
let n = [...t.locals];
|
|
1850
|
+
t.returnValue && n.push({
|
|
1851
|
+
name: "Return value",
|
|
1852
|
+
type: "",
|
|
1853
|
+
value: t.returnValue
|
|
1854
|
+
}), t.yielded && n.push({
|
|
1855
|
+
name: "Yielded",
|
|
1856
|
+
type: "",
|
|
1857
|
+
value: t.yielded
|
|
1858
|
+
}), t.awaiting && n.push({
|
|
1859
|
+
name: "Waiting for",
|
|
1860
|
+
type: "",
|
|
1861
|
+
value: t.awaiting
|
|
1862
|
+
}), s.append(yn(r ? {
|
|
1863
|
+
...e,
|
|
1864
|
+
quiet: !0
|
|
1865
|
+
} : e, n, (e) => `var:${t.id}:${e.name}`));
|
|
1866
|
+
}
|
|
1867
|
+
return s;
|
|
1868
|
+
}
|
|
1869
|
+
var xn = (e) => Number(e.match(/\d+/)?.[0] ?? 2 ** 53 - 1);
|
|
1870
|
+
function Sn(e, t, n) {
|
|
1871
|
+
let { doc: r } = e, i = ["cf-object"];
|
|
1872
|
+
n.freed ? i.push("cf-freed") : n.isNew ? i.push("cf-new") : e.changed.has(`heap:${t.id}`) && i.push("cf-changed"), t.unreachable && !n.freed && i.push("cf-leaked"), n.newlyLeaked && i.push("cf-just-leaked");
|
|
1873
|
+
let a = $(r, "section", i.join(" "));
|
|
1874
|
+
a.dataset.cfKey = n.freed ? `ghost:${t.id}` : `heap:${t.id}`, a.dataset.cfAnim = `heap:${t.id}`;
|
|
1875
|
+
let o = n.freed ? ", freed this step" : n.isNew ? ", created this step" : t.unreachable ? ", leaked" : "";
|
|
1876
|
+
a.setAttribute("aria-label", `${t.type}${o}`);
|
|
1877
|
+
let s = $(r, "h4", "cf-object-type");
|
|
1878
|
+
if (s.append($(r, "span", "cf-object-name", t.type)), n.freed) {
|
|
1879
|
+
let e = $(r, "span", "cf-tag cf-tag-freed", "freed");
|
|
1880
|
+
e.title = "This memory was just given back with free/delete; it disappears on the next step", s.append(e);
|
|
1881
|
+
} else if (n.isNew) {
|
|
1882
|
+
let e = $(r, "span", "cf-tag cf-tag-new", "new");
|
|
1883
|
+
e.title = "Created on this step", s.append(e);
|
|
1884
|
+
}
|
|
1885
|
+
if (t.unreachable && !n.freed) {
|
|
1886
|
+
let e = $(r, "span", "cf-badge", "leaked");
|
|
1887
|
+
e.title = "Nothing points to this memory any more, and it was never freed", s.append(e);
|
|
1888
|
+
}
|
|
1889
|
+
if (a.append(s), /^(function|class) /.test(t.type) && t.value.k === "record" && !t.value.fields.length) return a;
|
|
1890
|
+
let c = n.freed ? void 0 : `heap:${t.id}`, l = e.grid && !(c && e.pastEnd.has(c)) ? en(e.grid, t.value, !0, c, c) : null;
|
|
1891
|
+
return a.append(l ? _n(e, l) : gn(e, t.value, c, c)), a;
|
|
1892
|
+
}
|
|
1893
|
+
function Cn(e, t, n) {
|
|
1894
|
+
let { doc: r } = e, i = $(r, "section", "cf-object cf-literal");
|
|
1895
|
+
i.dataset.cfKey = `lit:${t}`, i.dataset.cfAnim = `lit:${t}`, i.setAttribute("aria-label", `string literal "${n}", read-only`);
|
|
1896
|
+
let a = $(r, "h4", "cf-object-type");
|
|
1897
|
+
a.append($(r, "span", "cf-object-name", "string literal"));
|
|
1898
|
+
let o = $(r, "span", "cf-tag cf-tag-freed", "read-only");
|
|
1899
|
+
o.title = "Text written in the program itself; it can't be changed", a.append(o);
|
|
1900
|
+
let s = {
|
|
1901
|
+
k: "array",
|
|
1902
|
+
type: `char[${n.length + 1}]`,
|
|
1903
|
+
items: [...[...n].map((e) => ({
|
|
1904
|
+
k: "prim",
|
|
1905
|
+
type: "char",
|
|
1906
|
+
text: `'${e}'`
|
|
1907
|
+
})), {
|
|
1908
|
+
k: "prim",
|
|
1909
|
+
type: "char",
|
|
1910
|
+
text: "'\\0'"
|
|
1911
|
+
}]
|
|
1912
|
+
};
|
|
1913
|
+
return i.append(a, gn(e, s)), i;
|
|
1914
|
+
}
|
|
1915
|
+
function wn(t, n, r, i, a, o = {}) {
|
|
1916
|
+
let s = a && !e[r].dynamic ? a.heap.filter((e) => !n.heap.some((t) => t.id === e.id)) : [], c = {
|
|
1917
|
+
doc: t,
|
|
1918
|
+
lang: r,
|
|
1919
|
+
changed: i,
|
|
1920
|
+
heap: new Map(n.heap.map((e) => [e.id, e])),
|
|
1921
|
+
ghosts: new Set(s.map((e) => e.id)),
|
|
1922
|
+
literals: /* @__PURE__ */ new Map(),
|
|
1923
|
+
vars: new Map([...n.frames.flatMap((e) => e.locals.map((t) => [`var:${e.id}:${t.name}`, t.value])), ...n.globals.map((e) => [`global:${e.name}`, e.value])]),
|
|
1924
|
+
pastEnd: /* @__PURE__ */ new Set()
|
|
1925
|
+
};
|
|
1926
|
+
if (o.grids !== !1 && (c.grid = {
|
|
1927
|
+
lang: r,
|
|
1928
|
+
heap: c.heap,
|
|
1929
|
+
refs: $t(n)
|
|
1930
|
+
}), !e[r].dynamic) {
|
|
1931
|
+
let e = (t) => {
|
|
1932
|
+
if (t.k === "ptr") {
|
|
1933
|
+
let e = hn(t.target, c, t.address);
|
|
1934
|
+
e?.endsWith(":end") && c.pastEnd.add(e.slice(0, -4));
|
|
1935
|
+
} else t.k === "array" ? t.items.forEach(e) : t.k === "record" && t.fields.forEach((t) => e(t.value));
|
|
1936
|
+
};
|
|
1937
|
+
for (let t of c.vars.values()) e(t);
|
|
1938
|
+
for (let t of n.heap) e(t.value);
|
|
1939
|
+
}
|
|
1940
|
+
let l = $(t, "div", "cf-frames"), u = $(t, "div", "cf-static"), d = e[r].dynamic;
|
|
1941
|
+
if (n.globals.length || d) {
|
|
1942
|
+
let e = $(t, "section", "cf-frame cf-globals"), i = new Set(n.globals.map((e) => e.name.split(".")[0])), a = r === "java" ? i.size === 1 ? `Static fields of ${[...i][0]}` : "Static fields" : void 0, o = d ? "Global frame" : a ?? "Global variables";
|
|
1943
|
+
e.setAttribute("aria-label", o), e.dataset.cfAnim = "frame:globals", e.append($(t, "h4", "cf-frame-name", o)), n.globals.length ? e.append(yn(c, n.globals, (e) => `global:${e.name}`)) : e.append($(t, "p", "cf-empty cf-frame-hint", "Variables appear here as their lines run.")), d ? l.append(e) : u.append(e);
|
|
1944
|
+
}
|
|
1945
|
+
let f = new Map(n.frames.map((e) => [e.id, e])), p = o.finished, m = p ? p.order.map((e) => f.get(e) ?? p.frames.get(e)).filter((e) => !!e) : n.frames, h = n.frames.at(-1), g = m.length, _ = !o.showAllFrames && g > dn;
|
|
1946
|
+
m.forEach((e, n) => {
|
|
1947
|
+
if (_ && n >= fn && n < g - pn) {
|
|
1948
|
+
if (n === fn) {
|
|
1949
|
+
let e = g - fn - pn, n = new Set(m.slice(fn, g - pn).map((e) => e.name)), r = $(t, "div", "cf-frames-more");
|
|
1950
|
+
r.append($(t, "span", void 0, `${e} more calls to ${[...n].join(", ")}`));
|
|
1951
|
+
let i = $(t, "button", "cf-link", "Show all");
|
|
1952
|
+
i.type = "button", i.dataset.action = "show-frames", r.append(i), l.append(r);
|
|
1953
|
+
}
|
|
1954
|
+
} else l.append(bn(c, e, e === h, !f.has(e.id)));
|
|
1955
|
+
});
|
|
1956
|
+
for (let e of n.paused ?? []) l.append(bn(c, e, !1, !1, !0));
|
|
1957
|
+
let v = new Map((a?.heap ?? []).map((e) => [e.id, e])), y = c.grid ? tn(c.grid, n) : /* @__PURE__ */ new Set(), b = new Map([...n.heap.filter((e) => !y.has(e.id)).map((e) => [e.id, {
|
|
1958
|
+
h: e,
|
|
1959
|
+
freed: !1
|
|
1960
|
+
}]), ...s.map((e) => [e.id, {
|
|
1961
|
+
h: e,
|
|
1962
|
+
freed: !0
|
|
1963
|
+
}])]), x = o.layout ? o.layout.map((e) => e.filter((e) => b.has(e))).filter((e) => e.length) : [...b.keys()].sort((e, t) => xn(e) - xn(t)).map((e) => [e]), S = new Set(x.flat());
|
|
1964
|
+
for (let e of b.keys()) S.has(e) || x.push([e]);
|
|
1965
|
+
let C = $(t, "div", "cf-objects");
|
|
1966
|
+
for (let e of x) {
|
|
1967
|
+
let n = $(t, "div", "cf-heap-row");
|
|
1968
|
+
for (let t of e) {
|
|
1969
|
+
let { h: e, freed: r } = b.get(t), i = v.get(e.id);
|
|
1970
|
+
n.append(Sn(c, e, {
|
|
1971
|
+
isNew: !!a && !i && !r,
|
|
1972
|
+
freed: r,
|
|
1973
|
+
newlyLeaked: !!e.unreachable && !!i && !i.unreachable
|
|
1974
|
+
}));
|
|
1975
|
+
}
|
|
1976
|
+
C.append(n);
|
|
1977
|
+
}
|
|
1978
|
+
if (c.literals.size) {
|
|
1979
|
+
let e = $(t, "div", "cf-heap-row");
|
|
1980
|
+
for (let [t, n] of c.literals) e.append(Cn(c, t, n));
|
|
1981
|
+
u.append(e);
|
|
1982
|
+
}
|
|
1983
|
+
let w = u.childElementCount > 0;
|
|
1984
|
+
if (w) {
|
|
1985
|
+
let n = e[r].manualMemory;
|
|
1986
|
+
l.prepend($(t, "h3", "cf-subhead", n ? "Static memory (globals and string literals)" : r === "java" ? "Static fields" : "Globals"), u, $(t, "h3", "cf-subhead", n ? "Stack" : "Frames"));
|
|
1987
|
+
}
|
|
1988
|
+
return b.size === 0 && C.append($(t, "p", "cf-empty", e[r].manualMemory ? "Nothing on the heap" : "No objects")), {
|
|
1989
|
+
frames: l,
|
|
1990
|
+
objects: C,
|
|
1991
|
+
hasStatic: w
|
|
1992
|
+
};
|
|
1993
|
+
}
|
|
1994
|
+
// ../renderer/src/styles.ts
|
|
1995
|
+
var Tn = "\n:host {\n --cf-bg: #ffffff;\n --cf-fg: #1f2933;\n --cf-muted: #52606d;\n --cf-line: #e4e7eb;\n --cf-line-strong: #cbd2d9;\n --cf-accent: #2f6fde;\n --cf-accent-ink: #1d4fb8;\n --cf-accent-soft: #e8f0fe;\n /* Text selection: clearly stronger than the next-line wash so the two never blend. */\n --cf-selection: #b3cbf5;\n --cf-frame-bg: #f5f7fa;\n --cf-object-bg: #fffaeb;\n --cf-object-line: #ecdcaa;\n --cf-arrow: var(--cf-accent);\n --cf-danger: #b42318;\n --cf-danger-soft: #fef3f2;\n --cf-changed: #fff4c2;\n --cf-changed-line: #b7791f;\n --cf-radius: 8px;\n --cf-font: \"Atkinson Hyperlegible Next\", system-ui, -apple-system, \"Segoe UI\", sans-serif;\n --cf-font-code: \"Atkinson Hyperlegible Mono\", ui-monospace, \"SF Mono\", Menlo, Consolas, monospace;\n --cf-tok-keyword: #7c3aed;\n --cf-tok-string: #0f7b3e;\n --cf-tok-number: #b45309;\n --cf-tok-comment: #5f6b7a;\n --cf-tok-pre: #a21caf;\n display: block;\n color: var(--cf-fg);\n background: var(--cf-bg);\n font: 15px/1.5 var(--cf-font);\n container: cf-root / inline-size;\n}\n* { box-sizing: border-box; scrollbar-width: thin; scrollbar-color: var(--cf-line-strong) transparent; }\n::selection { background: var(--cf-selection); color: var(--cf-fg); }\n:focus-visible { outline: 2px solid var(--cf-accent); outline-offset: 2px; border-radius: 4px; }\n.cf-sr { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); white-space: nowrap; }\n\n/* Layout: one page scroll. On wide screens the code column is pinned while the\n memory diagram flows with the page, so only a long program scrolls inside its\n own panel. */\n/* The diagram can take room from the code (which wraps) down to 300px of\n code, so a linked list stays on one row: --cf-diagram-min is its widest\n row, measured once per program (visualizer.ts), so nothing shifts while\n stepping. */\n.cf-root { display: grid; grid-template-columns: minmax(300px, 4fr) minmax(min(var(--cf-diagram-min, 0px), calc(100% - 332px)), 7fr);\n gap: 32px; padding: 24px 24px 16px; align-items: start; }\n.cf-left, .cf-right { min-width: 0; }\n.cf-left { position: sticky; top: 16px; max-height: calc(100vh - 32px); display: flex; flex-direction: column; }\n/* The code, legend and caption share a header (pinned on phones, below); in\n the side column it is transparent, and the caption sits under the controls. */\n.cf-head { display: contents; }\n.cf-left > *, .cf-head > * { flex: none; }\n.cf-left .cf-code { flex: 0 1 auto; min-height: 160px; }\n.cf-left > .cf-controls { order: 1; }\n.cf-head > .cf-caption { order: 2; }\n.cf-left > .cf-input, .cf-left > .cf-files { order: 3; }\n/* Output heads the diagram column: what it printed, then the memory. */\n.cf-right > .cf-output { margin: 0 0 24px; }\n.cf-right { container: cf-memory / inline-size; }\n@container cf-root (max-width: 820px) {\n .cf-root { grid-template-columns: minmax(0, 1fr); gap: 24px; padding: 16px; }\n .cf-left { position: static; max-height: none; }\n .cf-left .cf-code { max-height: 50vh; }\n}\n/* Phones: code and caption, memory, then output, with the step controls\n pinned to the bottom. On a tall enough screen the code and caption are\n pinned to the top too, and each step scrolls what changed into the space\n between, so the line, what it did, and the diagram share one screen. */\n@container cf-root (max-width: 560px) {\n .cf-root { display: flex; flex-direction: column; align-items: stretch; gap: 20px; }\n .cf-left, .cf-right { display: contents; }\n /* Code; \"Show all code\" and the legend on one line; the caption. */\n .cf-root .cf-head { display: grid; grid-template-columns: auto minmax(0, 1fr); align-items: center; gap: 8px 16px; order: 1; }\n .cf-root .cf-code { grid-column: 1 / -1; min-height: 0; max-height: calc(5 * 26px + 20px); }\n .cf-root .cf-code.cf-expanded { max-height: none; }\n .cf-root .cf-code-toggle { display: block; padding: 0; }\n .cf-root .cf-legend { margin: 0; flex-wrap: wrap; gap: 4px 16px; justify-content: flex-end; }\n .cf-root .cf-caption { grid-column: 1 / -1; margin: 4px 0 0; }\n .cf-root .cf-memory { order: 4; grid-template-columns: minmax(0, 1fr); gap: 24px; }\n .cf-root .cf-key, .cf-root .cf-toggle { order: 4; margin-top: -12px; }\n .cf-root .cf-input { order: 5; margin-top: 0; }\n .cf-root .cf-output, .cf-root .cf-files { order: 5; margin: 0; }\n .cf-root .cf-controls { order: 6; position: sticky; bottom: 0; z-index: 3; margin: 0 -16px -16px; padding: 12px 16px;\n background: var(--cf-bg); border-top: 1px solid var(--cf-line); box-shadow: 0 -4px 12px rgb(31 41 51 / 0.06); }\n @media (min-height: 600px) {\n .cf-root .cf-head { position: sticky; top: 0; z-index: 3; margin: -16px -16px 0; padding: 12px 16px;\n background: var(--cf-bg); border-bottom: 1px solid var(--cf-line); box-shadow: 0 4px 12px rgb(31 41 51 / 0.06); }\n .cf-root .cf-code { max-height: calc(4 * 26px + 20px); }\n /* All the code: too tall to pin. */\n .cf-root .cf-head:has(.cf-expanded) { position: static; box-shadow: none; }\n }\n}\nh3 { margin: 0 0 8px; font-size: 13px; font-weight: 700; color: var(--cf-muted); letter-spacing: 0.02em; }\n\n/* Code */\n.cf-code { border: 1px solid var(--cf-line); border-radius: var(--cf-radius); overflow: auto; background: var(--cf-bg); }\n.cf-code ol { list-style: none; margin: 0; padding: 10px 0; font: 14px/26px var(--cf-font-code); }\n.cf-code li { display: grid; grid-template-columns: 28px 36px minmax(0, 1fr); align-items: start; padding-right: 16px; }\n/* Line numbers double as breakpoint toggles. */\n.cf-lineno { grid-column: 2; grid-row: 1; justify-self: end; margin-right: 6px; min-width: 24px; height: 22px; margin-top: 2px; padding: 0 4px;\n border: 0; border-radius: 999px; background: none; color: var(--cf-muted); font: inherit; line-height: 22px; text-align: right;\n font-variant-numeric: tabular-nums; cursor: pointer; }\n.cf-lineno:hover { background: var(--cf-danger-soft); color: var(--cf-danger); }\n.cf-bp .cf-lineno { background: var(--cf-danger); color: #fff; text-align: center; }\n.cf-legend .cf-l-bp .cf-link { padding: 0; font-size: 13px; }\n.cf-bp-dot { width: 10px; height: 10px; border-radius: 50%; background: var(--cf-danger); display: inline-block; }\n.cf-code .cf-marker { grid-column: 1; grid-row: 1; display: flex; justify-content: center; align-items: center; height: 26px; color: var(--cf-accent); }\n/* Long lines wrap; continuation lines stay aligned under the code, not the gutter. */\n.cf-code code { grid-column: 3; grid-row: 1; font: inherit; white-space: pre-wrap; overflow-wrap: anywhere;\n padding-left: var(--cf-hang, 2ch); text-indent: calc(-1 * var(--cf-hang, 2ch)); }\n.cf-code .cf-focus { background: var(--cf-accent-soft); }\n.cf-code .cf-next .cf-marker { color: var(--cf-muted); }\n.cf-code .cf-crash { background: var(--cf-danger-soft); }\n.cf-code .cf-crash .cf-marker { color: var(--cf-danger); }\n/* The host shows the code (live mode's editor): the panel and its toggle go. */\n.cf-codeless .cf-code, .cf-codeless .cf-code-toggle { display: none !important; }\n.cf-tok-keyword { color: var(--cf-tok-keyword); }\n.cf-tok-string { color: var(--cf-tok-string); }\n.cf-tok-number { color: var(--cf-tok-number); }\n.cf-tok-comment { color: var(--cf-tok-comment); font-style: italic; }\n.cf-tok-pre { color: var(--cf-tok-pre); }\n.cf-legend { display: flex; gap: 20px; margin: 10px 2px 0; font-size: 13px; color: var(--cf-muted); }\n.cf-legend span { display: inline-flex; align-items: center; gap: 6px; }\n.cf-legend .cf-l-focus { color: var(--cf-accent); }\n.cf-legend .cf-l-focus.cf-l-crash { color: var(--cf-danger); }\n.cf-legend [hidden] { display: none; }\n\n/* Controls */\n.cf-controls { display: grid; grid-template-columns: auto 1fr; gap: 12px 16px; align-items: center; margin-top: 20px; }\n.cf-buttons { display: flex; gap: 6px; }\n.cf-btn { display: inline-flex; align-items: center; justify-content: center; gap: 6px; height: 40px; min-width: 40px; padding: 0 10px;\n border: 1px solid var(--cf-line-strong); border-radius: var(--cf-radius); background: var(--cf-bg); color: var(--cf-fg);\n font: 600 15px var(--cf-font); cursor: pointer; transition: background-color 120ms ease-out, border-color 120ms ease-out; }\n.cf-btn:hover:not(:disabled) { background: var(--cf-frame-bg); border-color: var(--cf-muted); }\n.cf-btn:disabled { color: var(--cf-line-strong); cursor: default; }\n.cf-btn.cf-primary { background: var(--cf-accent); border-color: var(--cf-accent); color: #fff; padding: 0 16px 0 14px; }\n.cf-btn.cf-primary:hover:not(:disabled) { background: var(--cf-accent-ink); border-color: var(--cf-accent-ink); }\n.cf-btn.cf-primary:disabled { background: var(--cf-line); border-color: var(--cf-line); color: var(--cf-muted); }\n.cf-count { font-weight: 700; font-variant-numeric: tabular-nums; white-space: nowrap; justify-self: end; }\n.cf-slider { grid-column: 1 / -1; width: 100%; accent-color: var(--cf-accent); height: 24px; margin: 0; }\n\n/* Input: read part in ink with an underline, unread part muted. */\n.cf-input { margin-top: 24px; }\n.cf-input[hidden] { display: none; }\n.cf-output[hidden] { display: none; }\n.cf-input pre { margin: 0; padding: 10px 12px; border: 1px solid var(--cf-line); border-radius: var(--cf-radius); font: 14px/1.5 var(--cf-font-code); white-space: pre-wrap; background: var(--cf-frame-bg); }\n.cf-in-read { color: var(--cf-fg); text-decoration: underline; text-decoration-color: var(--cf-accent); text-underline-offset: 4px; text-decoration-thickness: 2px; }\n.cf-in-rest { color: var(--cf-muted); }\n\n/* Output */\n.cf-output { margin-top: 24px; }\n.cf-output pre { margin: 0; min-height: 64px; max-height: 160px; overflow: auto; padding: 10px 12px; border: 1px solid var(--cf-line);\n border-radius: var(--cf-radius); font: 14px/1.5 var(--cf-font-code); white-space: pre-wrap; background: var(--cf-frame-bg); }\n.cf-output pre:empty::before { content: \"Nothing printed yet\"; color: var(--cf-muted); font-family: var(--cf-font); }\n/* Files: each file's text at this step; what this step wrote is underlined, like input read. */\n.cf-files { margin-top: 24px; }\n.cf-files[hidden] { display: none; }\n.cf-files-head { display: flex; align-items: baseline; justify-content: space-between; gap: 12px; }\n.cf-files-head h3 { overflow-wrap: anywhere; }\n.cf-files-head .cf-link { padding: 0; font-size: 13px; }\n/* A tab per file; a dot marks one this step changed. */\n.cf-file-tabs { display: flex; flex-wrap: wrap; gap: 4px; margin-bottom: -1px; }\n.cf-file-tabs[hidden] { display: none; }\n.cf-file-tab { display: inline-flex; align-items: center; gap: 6px; min-height: 32px; padding: 4px 10px; border: 1px solid var(--cf-line);\n border-bottom: 0; border-radius: var(--cf-radius) var(--cf-radius) 0 0; background: var(--cf-bg); color: var(--cf-muted);\n font: 600 13px var(--cf-font-code); cursor: pointer; }\n.cf-file-tab[aria-selected=\"true\"] { background: var(--cf-frame-bg); color: var(--cf-fg); }\n.cf-file-tab.cf-file-changed::after { content: \"\"; width: 7px; height: 7px; border-radius: 50%; background: var(--cf-accent); }\n.cf-file-tabs:not([hidden]) + .cf-file-text { border-top-left-radius: 0; }\n.cf-files pre { margin: 0; max-height: 160px; overflow: auto; padding: 10px 12px; border: 1px solid var(--cf-line); border-radius: var(--cf-radius);\n font: 14px/1.5 var(--cf-font-code); white-space: pre-wrap; background: var(--cf-frame-bg); }\n.cf-files pre:empty::before { content: \"Empty\"; color: var(--cf-muted); font-family: var(--cf-font); }\n.cf-files mark { background: none; color: var(--cf-fg); text-decoration: underline; text-decoration-color: var(--cf-accent); text-underline-offset: 4px; text-decoration-thickness: 2px; }\n/* The program is waiting for a line of input: type it like at a terminal. */\n.cf-prompt { display: flex; flex-wrap: wrap; align-items: center; gap: 8px 12px; margin-top: 8px; }\n.cf-prompt[hidden] { display: none; }\n.cf-prompt-input { flex: 1 1 160px; min-width: 0; height: 40px; padding: 0 12px; border: 2px solid var(--cf-accent); border-radius: var(--cf-radius);\n font: 14px var(--cf-font-code); color: inherit; background: var(--cf-bg); }\n.cf-prompt-input:focus-visible { outline: 2px solid var(--cf-accent); outline-offset: 2px; }\n\n/* Caption */\n/* Sits right under the step controls: you read what happened where you clicked,\n and its length never moves the diagram. At least two lines are reserved,\n or the longest caption in the trace (measured in visualizer.ts), so\n nothing around it jumps. */\n.cf-caption { margin: 16px 0 0; padding: 12px 16px; border-radius: var(--cf-radius); background: var(--cf-accent-soft); font-size: 16px;\n min-height: max(calc(3em + 24px), var(--cf-caption-min, 0px)); }\n.cf-caption.cf-error { background: var(--cf-danger-soft); color: var(--cf-danger); font-weight: 600; }\n\n/* Memory */\n/* The diagram never scrolls on its own: values wrap to the space they have, and\n Frames/Objects stack when the diagram itself (not the page) gets narrow. */\n.cf-memory { position: relative; display: grid; grid-template-columns: fit-content(62%) minmax(180px, 1fr); gap: 40px; align-items: start; padding: 2px 24px 8px 2px; }\n@container cf-memory (max-width: 600px) { .cf-memory { grid-template-columns: minmax(0, 1fr); gap: 24px; } }\n.cf-arrows { position: absolute; left: 0; top: 0; pointer-events: none; overflow: visible; }\n.cf-arrow { fill: none; stroke: var(--cf-arrow); stroke-width: 2; }\n.cf-frames, .cf-objects { display: flex; flex-direction: column; gap: 16px; min-width: 0; }\n/* Room between heap rows for arrows that run beside a row (see arrows.ts). */\n.cf-objects { gap: 24px; }\n/* Heap rows: linked structures run left to right along their pointers. */\n.cf-heap-row { display: flex; flex-wrap: wrap; align-items: flex-start; gap: 16px 20px; }\n.cf-subhead { margin: 8px 0 -8px; }\n.cf-subhead:first-child { margin-top: 0; }\n.cf-static { display: flex; flex-direction: column; gap: 16px; }\n.cf-measure { position: absolute; left: -10000px; top: 0; width: max-content; visibility: hidden; pointer-events: none; }\n/* Unstretched, so a table's name column measures the names alone. */\n.cf-measure .cf-vars { width: auto; }\n.cf-measure .cf-heap-row { flex-wrap: nowrap; width: max-content; }\n/* Cumulative mode: finished calls stay, quiet and clearly over. */\n.cf-frame.cf-finished { background: var(--cf-bg); border-style: dashed; border-color: var(--cf-line-strong); }\n.cf-frame.cf-finished .cf-frame-name { color: var(--cf-muted); }\n.cf-frame.cf-finished td { color: var(--cf-muted); }\n.cf-frame.cf-finished .cf-dot:not(.cf-ref) { background: var(--cf-muted); }\n.cf-frame.cf-finished .cf-dot.cf-ref { box-shadow: inset 0 0 0 2px var(--cf-muted); }\n.cf-toggle { display: inline-flex; align-items: center; gap: 8px; margin: 14px 2px 0; font-size: 14px; color: var(--cf-muted); cursor: pointer; }\n.cf-toggle[hidden] { display: none; }\n.cf-toggle input { width: 16px; height: 16px; margin: 0; accent-color: var(--cf-accent); }\n.cf-key { display: flex; flex-wrap: wrap; gap: 6px 20px; margin: 12px 2px 0; font-size: 13px; color: var(--cf-muted); }\n.cf-key[hidden], .cf-key [hidden] { display: none; }\n.cf-key > span { display: inline-flex; align-items: center; gap: 6px; }\n.cf-key .cf-dot-dangling { margin-left: 0; }\n/* The hint wraps to the frame; it never makes the Stack column wider. */\n.cf-frame-hint { font-size: 13px; contain: inline-size; }\n.cf-frames-more { display: flex; align-items: center; justify-content: space-between; gap: 12px; padding: 8px 12px; border: 1px dashed var(--cf-line-strong); border-radius: var(--cf-radius); color: var(--cf-muted); font-size: 14px; }\n.cf-link { border: 0; background: none; padding: 4px 0; color: var(--cf-accent-ink); font: 600 14px var(--cf-font); text-decoration: underline; text-underline-offset: 3px; cursor: pointer; }\n.cf-code-toggle { display: none; }\n.cf-frame { background: var(--cf-frame-bg); border: 1px solid var(--cf-line); border-radius: var(--cf-radius); padding: 10px 12px 12px; }\n.cf-frame.cf-current { border-color: var(--cf-accent); box-shadow: 0 1px 3px rgb(31 41 51 / 0.08), 0 0 0 1px var(--cf-accent); }\n.cf-frame.cf-returning { border-style: dashed; }\n/* A paused generator: off the call stack, quieter, but its variables are live. */\n.cf-frame.cf-paused { background: var(--cf-bg); border-style: dashed; border-color: var(--cf-line-strong); }\n.cf-frame.cf-paused .cf-frame-name { color: var(--cf-muted); }\n.cf-tag-paused { color: var(--cf-muted); background: var(--cf-frame-bg); border: 1px solid var(--cf-line-strong); }\n.cf-frame-name, .cf-object-type { margin: 0 0 8px; font-size: 15px; font-weight: 700; display: flex; flex-wrap: wrap; align-items: center; gap: 4px 8px; }\n.cf-tag, .cf-badge { white-space: nowrap; }\n.cf-object-type { font-size: 13px; color: var(--cf-muted); }\n.cf-object-type:last-child { margin-bottom: 0; }\n.cf-vars, .cf-record { border-collapse: collapse; width: 100%; }\n.cf-vars th, .cf-record th { text-align: right; font-weight: 400; padding: 5px 10px 5px 0; vertical-align: middle; white-space: nowrap; }\n.cf-record th { font-family: var(--cf-font-code); font-size: 14px; }\n/* One name-column width for every frame (measured in visualizer.ts). */\n.cf-vars > tr > th, .cf-vars > tbody > tr > th { box-sizing: border-box; width: var(--cf-names-width, auto); }\n.cf-vars td, .cf-record td { background: var(--cf-bg); border: 1px solid var(--cf-line); padding: 5px 10px; min-width: 56px; font-family: var(--cf-font-code); font-size: 14px; }\n/* Changed this step: outlined and bold, with the fill only as reinforcement. */\n.cf-vars tr.cf-changed td { background: var(--cf-changed); outline: 2px solid var(--cf-changed-line); outline-offset: -2px; }\n.cf-vars tr.cf-changed td .cf-val { font-weight: 700; }\n.cf-name { display: block; font-family: var(--cf-font-code); font-size: 14px; }\n.cf-type { display: block; font-size: 12px; color: var(--cf-muted); }\n.cf-object { background: var(--cf-object-bg); border: 1px solid var(--cf-object-line); border-radius: var(--cf-radius); padding: 8px 8px 8px; width: max-content; max-width: 100%; }\n.cf-object.cf-changed { outline: 2px solid var(--cf-changed-line); outline-offset: 2px; }\n.cf-object.cf-leaked { border-style: dashed; border-color: var(--cf-danger); }\n.cf-object .cf-record td { background: var(--cf-bg); min-width: 44px; }\n.cf-object .cf-record th { padding-right: 8px; }\n/* Created / freed this step. The text tag is the cue; motion only reinforces it. */\n.cf-tag { font-size: 12px; font-weight: 700; border-radius: 999px; padding: 0 8px; }\n.cf-tag-new { color: var(--cf-accent-ink); background: var(--cf-accent-soft); }\n.cf-tag-freed { color: var(--cf-muted); background: var(--cf-frame-bg); border: 1px solid var(--cf-line-strong); }\n.cf-object.cf-new { outline: 2px solid var(--cf-accent); outline-offset: 2px; }\n/* Freed: a dashed, uncoloured ghost. Text stays at full contrast (no opacity). */\n.cf-object.cf-freed { background: var(--cf-bg); border-style: dashed; border-color: var(--cf-line-strong); }\n.cf-object.cf-freed .cf-val, .cf-object.cf-freed td, .cf-object.cf-freed .cf-cell { color: var(--cf-muted); background: var(--cf-bg); }\n.cf-object.cf-freed .cf-object-name { text-decoration: line-through; text-decoration-color: var(--cf-muted); }\n.cf-badge { font-size: 12px; font-weight: 700; color: var(--cf-danger); border: 1px solid currentColor; border-radius: 999px; padding: 0 8px; }\n/* An array stays on one row while it fits. A long one wraps, with a gap\n between rows and every cell keeping its index, so the rows read as one\n sequence rather than a 2-D grid. Nothing scrolls sideways. */\n.cf-array { display: flex; flex-wrap: wrap; row-gap: 8px; max-width: 100%; padding: 1px 0 0 1px; }\n.cf-cell { display: flex; flex-direction: column; align-items: center; min-width: 40px; border: 1px solid var(--cf-line-strong); margin: -1px 0 0 -1px; background: var(--cf-bg); padding: 0 6px 4px; }\n.cf-cell .cf-val { overflow-wrap: normal; white-space: nowrap; }\n.cf-cell-end { border-style: dashed; border-color: var(--cf-line); background: transparent; min-height: 2.6em; }\n/* An array of arrays as a grid: numbered rows under each other. Declared\n after .cf-vars td so a grid inside a variable keeps its own cells. */\n.cf-union-note { caption-side: top; text-align: left; white-space: nowrap; font-size: 12px; color: var(--cf-muted); padding: 0 0 2px; font-family: var(--cf-font); }\n.cf-grid { border-collapse: collapse; margin: 1px 0 0 1px; }\n.cf-grid td { background: var(--cf-bg); border: 1px solid var(--cf-line-strong); padding: 2px 8px; min-width: 40px; text-align: center; font-family: var(--cf-font-code); font-size: 14px; }\n.cf-grid th.cf-index { font-weight: 400; padding: 0 6px 2px; text-align: center; }\n.cf-grid tbody th.cf-index { text-align: right; padding: 0 6px 0 0; }\n.cf-grid td.cf-grid-corner { border: none; background: transparent; min-width: 0; padding: 0; }\n.cf-grid td.cf-grid-none { border: none; background: transparent; color: var(--cf-muted); font-family: var(--cf-font); text-align: left; }\n.cf-index { font-size: 12px; color: var(--cf-muted); font-family: var(--cf-font); }\n.cf-val { font-family: var(--cf-font-code); }\n/* Only text values may wrap; numbers and identifiers never break mid-value. */\n.cf-str, .cf-opaque { overflow-wrap: anywhere; }\n.cf-uninit { color: var(--cf-muted); text-decoration: underline dotted; text-underline-offset: 3px; }\n.cf-null { color: var(--cf-muted); }\n.cf-str { color: var(--cf-tok-string); }\n.cf-bad { display: inline-flex; align-items: center; gap: 4px; color: var(--cf-danger); font-family: var(--cf-font); font-weight: 700; font-size: 13px; }\n.cf-ptr { display: inline-flex; align-items: center; min-height: 18px; }\n.cf-dot { width: 10px; height: 10px; border-radius: 50%; background: var(--cf-arrow); display: inline-block; }\n/* A reference is an alias: hollow dot. A dangling pointer: brick dot. */\n.cf-dot.cf-ref { background: var(--cf-bg); box-shadow: inset 0 0 0 2px var(--cf-arrow); }\n.cf-dot.cf-dot-dangling { background: var(--cf-danger); margin-left: 6px; }\n.cf-arrow-dangling { stroke: var(--cf-danger); stroke-dasharray: 5 4; }\n/* A weak_ptr: a dashed arrow in the usual colour (it can reach the object, but doesn't own it). */\n.cf-arrow-weak { stroke-dasharray: 6 4; }\n.cf-key .cf-dash { width: 22px; height: 0; border-top: 2px dashed var(--cf-arrow); display: inline-block; }\n/* A changed pointer waits, head included, until the boxes stop moving. */\n.cf-arrow-pending { visibility: hidden; }\n.cf-arrow-head { fill: none; stroke: none; }\n.cf-literal { background: var(--cf-frame-bg); border-color: var(--cf-line-strong); }\n/* Only the element that changed is outlined, not the whole array or struct. */\n.cf-changed-cell { background: var(--cf-changed) !important; outline: 2px solid var(--cf-changed-line); outline-offset: -2px; }\n.cf-changed-cell .cf-val { font-weight: 700; }\n.cf-empty { color: var(--cf-muted); font-size: 14px; margin: 0; }\n\n@media (prefers-reduced-motion: no-preference) {\n .cf-changed td, .cf-object.cf-changed { transition: background-color 600ms cubic-bezier(0.16, 1, 0.3, 1); }\n /* pathLength=\"1\" on every arrow, so the dash covers arrows of any length. */\n .cf-arrow-new { stroke-dasharray: 1; stroke-dashoffset: 1; animation: cf-draw 420ms cubic-bezier(0.16, 1, 0.3, 1) forwards; }\n @keyframes cf-draw { to { stroke-dashoffset: 0; } }\n /* The head lands with the line: the draw is front-loaded, nearly done by 40%. */\n .cf-arrow-head.cf-arrow-new { stroke-dasharray: none; animation: cf-head-in 420ms linear both; }\n /* Dashed arrows can't draw in with a dash: they fade in with their heads. */\n .cf-arrow-weak.cf-arrow-new { stroke-dasharray: 6 4; stroke-dashoffset: 0; animation: cf-head-in 420ms linear both; }\n @keyframes cf-head-in { 0%, 38% { opacity: 0; } 55% { opacity: 1; } }\n /* A new object grows in from its slot; the arrow then draws to it. */\n .cf-object.cf-new { animation: cf-appear 360ms cubic-bezier(0.16, 1, 0.3, 1) both; }\n @keyframes cf-appear { from { opacity: 0; transform: translateY(-6px) scale(0.96); outline-color: transparent; } }\n /* A freed object fades back and shrinks slightly: it is going away. */\n .cf-object.cf-freed { animation: cf-release 420ms cubic-bezier(0.16, 1, 0.3, 1) both; }\n @keyframes cf-release { from { background: var(--cf-object-bg); border-color: var(--cf-object-line); transform: scale(1.02); } }\n /* The leaked badge pops in on the step the last pointer left. */\n .cf-just-leaked .cf-badge { animation: cf-pop 360ms cubic-bezier(0.16, 1, 0.3, 1) both; }\n @keyframes cf-pop { from { transform: scale(0.6); opacity: 0; } }\n}\n", En = /(?:#|\/\/)\s*break\s*$/, Dn = (e) => e.split("\n").flatMap((e, t) => En.test(e) ? [t + 1] : []), On = (e) => e.event === "exception" || e.event === "limit", kn = `
|
|
1996
|
+
<div class="cf-root">
|
|
1997
|
+
<div class="cf-left">
|
|
1998
|
+
<div class="cf-head">
|
|
1999
|
+
<div class="cf-code" role="region" aria-label="Program code" tabindex="0"><ol></ol></div>
|
|
2000
|
+
<button class="cf-link cf-code-toggle" type="button" data-action="toggle-code">Show all code</button>
|
|
2001
|
+
<div class="cf-legend">
|
|
2002
|
+
<span class="cf-l-focus">${cn.focusLine} <span class="cf-l-focus-text">line that just ran</span></span>
|
|
2003
|
+
<span class="cf-l-next">${cn.nextLine} next line to run</span>
|
|
2004
|
+
<span class="cf-l-bp" hidden><span class="cf-bp-dot"></span> breakpoint: Next and Back stop here <button class="cf-link" type="button" data-action="clear-breakpoints">Clear</button></span>
|
|
2005
|
+
</div>
|
|
2006
|
+
<p class="cf-caption" aria-live="polite"></p>
|
|
2007
|
+
</div>
|
|
2008
|
+
<div class="cf-controls" role="group" aria-label="Step through the program">
|
|
2009
|
+
<div class="cf-buttons">
|
|
2010
|
+
<button class="cf-btn" data-go="first" aria-label="First step" title="First step (Home)">${cn.first}</button>
|
|
2011
|
+
<button class="cf-btn" data-go="back" aria-label="Previous step" title="Previous step (←)">${cn.back}</button>
|
|
2012
|
+
<button class="cf-btn cf-primary" data-go="next" title="Next step (→)">Next ${cn.next}</button>
|
|
2013
|
+
<button class="cf-btn" data-go="last" aria-label="Last step" title="Last step (End)">${cn.last}</button>
|
|
2014
|
+
</div>
|
|
2015
|
+
<span class="cf-count" aria-hidden="true"></span>
|
|
2016
|
+
<input class="cf-slider" type="range" min="0" value="0" aria-label="Step">
|
|
2017
|
+
</div>
|
|
2018
|
+
<div class="cf-input" hidden>
|
|
2019
|
+
<h3 id="cf-in-label">Input</h3>
|
|
2020
|
+
<pre aria-labelledby="cf-in-label"><span class="cf-in-read"></span><span class="cf-in-rest"></span></pre>
|
|
2021
|
+
</div>
|
|
2022
|
+
<div class="cf-files" hidden>
|
|
2023
|
+
<div class="cf-files-head">
|
|
2024
|
+
<h3 id="cf-files-label">Files</h3>
|
|
2025
|
+
<button class="cf-link" type="button" data-action="copy-file">Copy</button>
|
|
2026
|
+
</div>
|
|
2027
|
+
<div class="cf-file-tabs" role="tablist" aria-labelledby="cf-files-label"></div>
|
|
2028
|
+
<pre class="cf-file-text" role="tabpanel" tabindex="0"></pre>
|
|
2029
|
+
</div>
|
|
2030
|
+
</div>
|
|
2031
|
+
<div class="cf-right">
|
|
2032
|
+
<div class="cf-output"><h3 id="cf-out-label">Output</h3><pre aria-labelledby="cf-out-label"></pre>
|
|
2033
|
+
<form class="cf-prompt" hidden>
|
|
2034
|
+
<input type="text" class="cf-prompt-input" aria-label="Input for the program" autocomplete="off" spellcheck="false">
|
|
2035
|
+
<button class="cf-btn cf-primary" type="submit">Enter</button>
|
|
2036
|
+
<button class="cf-link" type="button" data-action="end-input" title="Let the program find that there is no more input (end of file)">No more input</button>
|
|
2037
|
+
</form>
|
|
2038
|
+
</div>
|
|
2039
|
+
<div class="cf-memory">
|
|
2040
|
+
<div><h3 class="cf-frames-title">Frames</h3><div class="cf-frames-slot"></div></div>
|
|
2041
|
+
<div><h3 class="cf-objects-title">Objects</h3><div class="cf-objects-slot"></div></div>
|
|
2042
|
+
<svg class="cf-arrows" aria-hidden="true"></svg>
|
|
2043
|
+
</div>
|
|
2044
|
+
<label class="cf-toggle" hidden><input type="checkbox" data-action="cumulative"> Keep finished calls on screen</label>
|
|
2045
|
+
<label class="cf-toggle" hidden><input type="checkbox" data-action="grids"> Draw 2D arrays as grids</label>
|
|
2046
|
+
<p class="cf-key" aria-hidden="true">
|
|
2047
|
+
<span class="cf-k-ptr"><span class="cf-dot"></span> pointer: holds an address</span>
|
|
2048
|
+
<span class="cf-k-ref"><span class="cf-dot cf-ref"></span> reference: another name for an existing variable</span>
|
|
2049
|
+
<span class="cf-k-dangling"><span class="cf-dot cf-dot-dangling"></span> dangling: still holds the address of freed memory</span>
|
|
2050
|
+
<span class="cf-k-weak"><span class="cf-dash"></span> weak_ptr: can reach the object, but doesn't own it</span>
|
|
2051
|
+
</p>
|
|
2052
|
+
</div>
|
|
2053
|
+
</div>`;
|
|
2054
|
+
function An(t, r) {
|
|
2055
|
+
let a = t.shadowRoot ?? t.attachShadow({ mode: "open" }), u = document.createElement("style");
|
|
2056
|
+
u.textContent = Tn, a.replaceChildren(u), a.append(document.createRange().createContextualFragment(kn));
|
|
2057
|
+
let d = (e) => a.querySelector(e), f = d(".cf-code ol"), p = d(".cf-slider"), m = d(".cf-count"), h = d(".cf-output pre"), g = d(".cf-prompt"), _ = d(".cf-prompt-input"), y = d(".cf-input"), b = d(".cf-in-read"), x = d(".cf-in-rest"), S = d(".cf-files"), C = d(".cf-file-tabs"), w = d(".cf-file-text"), T = d("[data-action=copy-file]"), E, D = d(".cf-caption"), O = d(".cf-l-focus-text"), ee = d(".cf-l-next"), k = d(".cf-memory"), te = d(".cf-frames-slot"), A = d(".cf-objects-slot"), ne = d(".cf-arrows"), j = d(".cf-key"), re = d("[data-action=cumulative]"), M = d("[data-action=grids]"), N = Object.fromEntries([...a.querySelectorAll("[data-go]")].map((e) => [e.dataset.go, e])), P = Vt(r.trace, zt(r.source)), F = an(P.steps), I = !1, L = !!r.cumulative;
|
|
2058
|
+
re.checked = L;
|
|
2059
|
+
let ie = () => P.steps.some((e, t) => t > 0 && e.frames.length < P.steps[t - 1].frames.length), R = r.grids !== !1;
|
|
2060
|
+
M.checked = R;
|
|
2061
|
+
let ae = () => {
|
|
2062
|
+
re.parentElement.hidden = !ie(), M.parentElement.hidden = !P.steps.some((e) => nn(e, P.language));
|
|
2063
|
+
}, oe = (e) => {
|
|
2064
|
+
let t = [], n = /* @__PURE__ */ new Map();
|
|
2065
|
+
for (let r = 0; r <= e; r++) for (let e of P.steps[r].frames) n.has(e.id) || t.push(e.id), n.set(e.id, e);
|
|
2066
|
+
for (let t of [...P.steps[e].frames, ...P.steps[e].paused ?? []]) n.delete(t.id);
|
|
2067
|
+
return {
|
|
2068
|
+
frames: n,
|
|
2069
|
+
order: t
|
|
2070
|
+
};
|
|
2071
|
+
}, z = d(".cf-code"), se = d(".cf-root");
|
|
2072
|
+
se.classList.toggle("cf-codeless", r.showCode === !1);
|
|
2073
|
+
let ce = d(".cf-frames-title"), le = d(".cf-objects-title"), B = () => {
|
|
2074
|
+
let t = e[P.language].manualMemory;
|
|
2075
|
+
ce.textContent = t ? "Stack" : "Frames", le.textContent = t ? "Heap" : "Objects";
|
|
2076
|
+
};
|
|
2077
|
+
B();
|
|
2078
|
+
let V = (e, t = !1) => {
|
|
2079
|
+
if (!(e instanceof HTMLElement)) return;
|
|
2080
|
+
let n = e.getBoundingClientRect().top - z.getBoundingClientRect().top + z.scrollTop, r = n + e.offsetHeight, i = z.clientHeight;
|
|
2081
|
+
t ? z.scrollTop = n - (i - e.offsetHeight) / 2 : n < z.scrollTop ? z.scrollTop = n - 8 : r > z.scrollTop + i && (z.scrollTop = r - i + 8);
|
|
2082
|
+
}, ue = () => {
|
|
2083
|
+
let e = z.clientHeight > 0 && z.clientHeight < 200 && !z.classList.contains("cf-expanded");
|
|
2084
|
+
V(f.querySelector("li.cf-next"), e), V(f.querySelector("li.cf-focus"), e);
|
|
2085
|
+
}, H = new ResizeObserver(() => ue());
|
|
2086
|
+
H.observe(z), H.observe(f);
|
|
2087
|
+
let U = 0, de = /* @__PURE__ */ new Set(), fe = !1, pe = d(".cf-head"), W = d(".cf-controls"), me = () => {
|
|
2088
|
+
if (getComputedStyle(pe).position !== "sticky") return;
|
|
2089
|
+
let e = k.querySelector(".cf-changed-cell, .cf-changed, .cf-object.cf-new");
|
|
2090
|
+
if (!e) return;
|
|
2091
|
+
let t = e.getBoundingClientRect(), n = pe.getBoundingClientRect().bottom + 12, r = W.getBoundingClientRect().top - 12, i = t.top < n ? t.top - n : t.bottom > r ? Math.min(t.bottom - r, t.top - n) : 0;
|
|
2092
|
+
Math.abs(i) > 1 && window.scrollBy({
|
|
2093
|
+
top: i,
|
|
2094
|
+
behavior: o() ? "auto" : "smooth"
|
|
2095
|
+
});
|
|
2096
|
+
}, he = [], G = new Set(r.breakpoints ?? []), ge = d(".cf-l-bp"), K = () => {
|
|
2097
|
+
[...f.children].forEach((e, t) => {
|
|
2098
|
+
let n = G.has(t + 1);
|
|
2099
|
+
e.classList.toggle("cf-bp", n), e.querySelector(".cf-lineno")?.setAttribute("aria-pressed", String(n));
|
|
2100
|
+
}), ge.hidden = G.size === 0, N.next.title = G.size ? "Next breakpoint (→; Shift+→ for one step)" : "Next step (→)", N.back.title = G.size ? "Previous breakpoint (←; Shift+← for one step)" : "Previous step (←)";
|
|
2101
|
+
}, ve = (e, t = !1) => {
|
|
2102
|
+
if (t || !G.size) return U + e;
|
|
2103
|
+
for (let t = U + e; t >= 0 && t < P.steps.length; t += e) {
|
|
2104
|
+
let e = P.steps[t];
|
|
2105
|
+
if (e.event === "step" && G.has(e.line)) return t;
|
|
2106
|
+
}
|
|
2107
|
+
return e > 0 ? P.steps.length - 1 : 0;
|
|
2108
|
+
}, ye = (e) => {
|
|
2109
|
+
he = e.split("\n");
|
|
2110
|
+
let t = e.replace(/\n$/, "").split("\n").length;
|
|
2111
|
+
G = new Set([...G].filter((e) => e <= t));
|
|
2112
|
+
for (let t of Dn(e)) G.add(t);
|
|
2113
|
+
f.replaceChildren(...e.replace(/\n$/, "").split("\n").map((e, t) => {
|
|
2114
|
+
let n = document.createElement("li");
|
|
2115
|
+
n.innerHTML = `<span class="cf-marker"></span><button class="cf-lineno" type="button" tabindex="-1" aria-pressed="false" aria-label="Breakpoint on line ${t + 1}" title="Click to stop here when stepping">${t + 1}</button><code>${Yt(e, P.language) || " "}</code>`;
|
|
2116
|
+
let r = (e.match(/^[ \t]*/)?.[0] ?? "").replace(/\t/g, " ").length;
|
|
2117
|
+
return n.style.setProperty("--cf-hang", `${r + 2}ch`), n;
|
|
2118
|
+
})), K();
|
|
2119
|
+
}, be, xe = () => v(k, ne, de, be), Se = () => {}, q = 0, Ce = 0, J = 0, we = () => {
|
|
2120
|
+
let e = document.createElement("div");
|
|
2121
|
+
e.className = "cf-measure", e.style.setProperty("--cf-names-width", "auto"), k.firstElementChild.append(e);
|
|
2122
|
+
let t = P.steps.length, n = Math.max(1, Math.ceil(t / 300)), r = document.createElement("div");
|
|
2123
|
+
r.className = "cf-measure", k.firstElementChild.append(r);
|
|
2124
|
+
let i = 0;
|
|
2125
|
+
J = 0;
|
|
2126
|
+
for (let a = 0; a < t; a += n) {
|
|
2127
|
+
let t = P.steps[a], { frames: n, objects: o } = wn(document, t, P.language, /* @__PURE__ */ new Set(), P.steps[a - 1], {
|
|
2128
|
+
layout: F[a],
|
|
2129
|
+
showAllFrames: I,
|
|
2130
|
+
grids: R,
|
|
2131
|
+
...L ? { finished: oe(a) } : {}
|
|
2132
|
+
});
|
|
2133
|
+
e.replaceChildren(n), i = Math.max(i, e.getBoundingClientRect().width);
|
|
2134
|
+
for (let t of e.querySelectorAll(".cf-vars > tr > th, .cf-vars > tbody > tr > th")) Ce = Math.max(Ce, t.getBoundingClientRect().width);
|
|
2135
|
+
r.replaceChildren(o);
|
|
2136
|
+
for (let e of r.querySelectorAll(".cf-heap-row")) {
|
|
2137
|
+
let t = [...e.children].filter((e) => e instanceof HTMLElement && e.offsetParent !== null);
|
|
2138
|
+
if (t.length < 2) continue;
|
|
2139
|
+
let n = parseFloat(getComputedStyle(e).columnGap) || 0, r = t.reduce((e, t) => e + Math.min(360, t.getBoundingClientRect().width), n * (t.length - 1));
|
|
2140
|
+
J = Math.max(J, r);
|
|
2141
|
+
}
|
|
2142
|
+
}
|
|
2143
|
+
e.remove(), r.remove(), q = i;
|
|
2144
|
+
}, Te = () => {
|
|
2145
|
+
let e = te.parentElement;
|
|
2146
|
+
q || (Ce = 0, we()), e.style.minWidth = `min(${Math.ceil(q)}px, 62cqi)`, e.style.setProperty("--cf-names-width", `${Math.ceil(Ce)}px`);
|
|
2147
|
+
let t = getComputedStyle(k), n = (parseFloat(t.columnGap) || 0) + (parseFloat(t.paddingLeft) || 0) + (parseFloat(t.paddingRight) || 0);
|
|
2148
|
+
se.style.setProperty("--cf-diagram-min", J ? `${Math.ceil(q + J + n)}px` : "0px");
|
|
2149
|
+
}, Ee = 0, De = () => {
|
|
2150
|
+
let e = D.getBoundingClientRect().width;
|
|
2151
|
+
if (!e || Math.abs(e - Ee) < 1) return;
|
|
2152
|
+
Ee = e;
|
|
2153
|
+
let t = P.steps.map((e, t) => ({
|
|
2154
|
+
text: It(P, t, he),
|
|
2155
|
+
error: On(e)
|
|
2156
|
+
})).sort((e, t) => t.text.length - e.text.length).slice(0, 8), n = document.createElement("p");
|
|
2157
|
+
n.setAttribute("aria-hidden", "true"), n.style.cssText = `position: absolute; visibility: hidden; pointer-events: none; min-height: 0; width: ${e}px`, D.after(n);
|
|
2158
|
+
let r = 0;
|
|
2159
|
+
for (let { text: e, error: i } of t) n.className = i ? "cf-caption cf-error" : "cf-caption", n.textContent = e, r = Math.max(r, n.getBoundingClientRect().height);
|
|
2160
|
+
let i = getComputedStyle(n), a = 4 * parseFloat(i.lineHeight) + parseFloat(i.paddingTop) + parseFloat(i.paddingBottom);
|
|
2161
|
+
n.remove(), D.style.setProperty("--cf-caption-min", r ? `${Math.ceil(Math.min(r, a || r))}px` : "0px");
|
|
2162
|
+
}, Oe = new ResizeObserver(() => De());
|
|
2163
|
+
Oe.observe(D);
|
|
2164
|
+
let Y = () => {
|
|
2165
|
+
let e = P.steps[U], t = P.steps.length;
|
|
2166
|
+
if (!e) return;
|
|
2167
|
+
let n = P.steps[U - 1], a = (e) => f.querySelector(`li.${e} .cf-marker svg`)?.getBoundingClientRect().top, u = !o(), v = {
|
|
2168
|
+
"cf-focus": a("cf-focus"),
|
|
2169
|
+
"cf-next": a("cf-next")
|
|
2170
|
+
};
|
|
2171
|
+
for (let e of f.querySelectorAll("li.cf-focus, li.cf-next")) e.classList.remove("cf-focus", "cf-next", "cf-crash"), e.removeAttribute("aria-current"), e.querySelector(".cf-marker").innerHTML = "";
|
|
2172
|
+
let S = (e, t, n) => {
|
|
2173
|
+
let r = f.children[e - 1];
|
|
2174
|
+
if (r) return r.classList.add(...t), r.querySelector(".cf-marker").innerHTML = n, r;
|
|
2175
|
+
}, C = On(e), w = e.event === "return" && !!n && n.frames.at(-1)?.id !== e.frames.at(-1)?.id, T = C ? e.line : n ? e.event === "resume" || w ? e.line : e.resumedLine ?? n.line : null, E = !C && e.line !== T ? e.line : null;
|
|
2176
|
+
if (E !== null && S(E, ["cf-next"], cn.nextLine), r.onLines?.({
|
|
2177
|
+
focus: T,
|
|
2178
|
+
next: E,
|
|
2179
|
+
crashed: C
|
|
2180
|
+
}), ((T === null ? void 0 : S(T, C ? ["cf-focus", "cf-crash"] : ["cf-focus"], cn.focusLine)) ?? f.querySelector("li.cf-next"))?.setAttribute("aria-current", "step"), O.textContent = e.event === "limit" ? "line where it was stopped" : C ? "line that crashed" : "line that just ran", O.parentElement.classList.toggle("cf-l-crash", C), O.parentElement.hidden = T === null, ee.hidden = !f.querySelector("li.cf-next"), ue(), u) for (let [e, t] of Object.entries(v)) {
|
|
2181
|
+
let n = f.querySelector(`li.${e} .cf-marker svg`);
|
|
2182
|
+
if (t === void 0 || !n) continue;
|
|
2183
|
+
let r = t - n.getBoundingClientRect().top;
|
|
2184
|
+
Math.abs(r) > .5 && n.animate([{ transform: `translateY(${r}px)` }, { transform: "none" }], {
|
|
2185
|
+
duration: 240,
|
|
2186
|
+
easing: i
|
|
2187
|
+
});
|
|
2188
|
+
}
|
|
2189
|
+
let ne = z.clientHeight > 0 && z.clientHeight < 200 && !z.classList.contains("cf-expanded"), re = f.querySelector("li.cf-next");
|
|
2190
|
+
if (ne && re) {
|
|
2191
|
+
let e = re.getBoundingClientRect().top - z.getBoundingClientRect().top;
|
|
2192
|
+
e >= 0 && e + re.offsetHeight <= z.clientHeight || (ee.hidden = !0);
|
|
2193
|
+
}
|
|
2194
|
+
let M = k.isConnected && !o() && te.childElementCount > 0, ie = M ? s(k) : null;
|
|
2195
|
+
de = _e(n, e);
|
|
2196
|
+
let { frames: ae, objects: se, hasStatic: le } = wn(document, e, P.language, de, n, {
|
|
2197
|
+
layout: F[U],
|
|
2198
|
+
showAllFrames: I,
|
|
2199
|
+
grids: R,
|
|
2200
|
+
...L ? { finished: oe(U) } : {}
|
|
2201
|
+
});
|
|
2202
|
+
te.replaceChildren(ae), A.replaceChildren(se), ce.hidden = le;
|
|
2203
|
+
let B = !!k.querySelector(".cf-dot.cf-ref"), V = !!k.querySelector(".cf-dot-dangling"), H = !!k.querySelector(".cf-dot.cf-weak");
|
|
2204
|
+
d(".cf-k-ptr").hidden = !B, d(".cf-k-ref").hidden = !B, d(".cf-k-dangling").hidden = !V, d(".cf-k-weak").hidden = !H, j.hidden = !B && !V && !H, Te(), fe && me(), fe = !0;
|
|
2205
|
+
let pe = It(P, U, he);
|
|
2206
|
+
M && D.textContent !== pe && D.animate([{ opacity: .35 }, { opacity: 1 }], {
|
|
2207
|
+
duration: 200,
|
|
2208
|
+
easing: "ease-out"
|
|
2209
|
+
}), D.textContent = pe, D.classList.toggle("cf-error", C);
|
|
2210
|
+
let W = !!P.awaitingInput && U === t - 1;
|
|
2211
|
+
h.textContent = W ? P.stdout : P.stdout.slice(0, e.stdoutLength), r.hideEmptyOutput && (h.parentElement.hidden = !h.textContent);
|
|
2212
|
+
let G = W && !!r.onInput;
|
|
2213
|
+
if (g.hidden === G) {
|
|
2214
|
+
g.hidden = !G;
|
|
2215
|
+
for (let e of g.elements) e.disabled = !1;
|
|
2216
|
+
G && _.focus({ preventScroll: !0 });
|
|
2217
|
+
}
|
|
2218
|
+
let ge = P.stdin ?? "";
|
|
2219
|
+
y.hidden = !ge && !P.steps.some((e) => e.inputEnded);
|
|
2220
|
+
let K = e.stdinConsumed ?? 0;
|
|
2221
|
+
b.textContent = ge.slice(0, K), x.textContent = ge.slice(K), b.title = "Already read by the program", x.title = "Not read yet", ge || (x.textContent = "(no input given)"), h.scrollTop = h.scrollHeight, Ae(), p.max = String(t - 1), p.value = String(U), p.setAttribute("aria-valuetext", `Step ${U + 1} of ${t}, line ${e.line}`), m.textContent = `Step ${U + 1} of ${t}`, N.first.disabled = N.back.disabled = U === 0, N.next.disabled = N.last.disabled = U === t - 1, Se(), ie ? (c(k, ie, k), be = void 0, Se = l(320, xe, () => {
|
|
2222
|
+
be = performance.now(), xe();
|
|
2223
|
+
})) : (be = performance.now(), requestAnimationFrame(xe)), r.onStep?.(U);
|
|
2224
|
+
}, X = (e) => {
|
|
2225
|
+
let t = Math.max(0, Math.min(P.steps.length - 1, e));
|
|
2226
|
+
if (t === U && f.children.length) return Y();
|
|
2227
|
+
U = t, Y();
|
|
2228
|
+
}, ke = (e) => {
|
|
2229
|
+
let t = e.target.closest("[data-action]")?.dataset.action;
|
|
2230
|
+
if (t === "cumulative") return L = re.checked, q = 0, r.onCumulative?.(L), Y();
|
|
2231
|
+
if (t === "grids") return R = M.checked, q = 0, r.onGrids?.(R), Y();
|
|
2232
|
+
if (t === "show-frames") return I = !0, q = 0, Y();
|
|
2233
|
+
if (t === "end-input") {
|
|
2234
|
+
Me(null);
|
|
2235
|
+
return;
|
|
2236
|
+
}
|
|
2237
|
+
if (t === "copy-file") {
|
|
2238
|
+
let e = w.textContent ?? "", t = (e) => {
|
|
2239
|
+
T.textContent = e, setTimeout(() => T.textContent = "Copy", 1500);
|
|
2240
|
+
}, n = () => {
|
|
2241
|
+
getSelection()?.selectAllChildren(w), t("Press Ctrl+C");
|
|
2242
|
+
};
|
|
2243
|
+
navigator.clipboard ? navigator.clipboard.writeText(e).then(() => t("Copied"), n) : n();
|
|
2244
|
+
return;
|
|
2245
|
+
}
|
|
2246
|
+
let n = e.target.closest(".cf-file-tab");
|
|
2247
|
+
if (n) return je(n.dataset.file);
|
|
2248
|
+
if (t === "clear-breakpoints") {
|
|
2249
|
+
G.clear(), K(), r.onBreakpoints?.([]);
|
|
2250
|
+
return;
|
|
2251
|
+
}
|
|
2252
|
+
let i = e.target.closest(".cf-lineno");
|
|
2253
|
+
if (i) {
|
|
2254
|
+
let e = Number(i.textContent);
|
|
2255
|
+
G.delete(e) || G.add(e), K(), r.onBreakpoints?.([...G].sort((e, t) => e - t));
|
|
2256
|
+
return;
|
|
2257
|
+
}
|
|
2258
|
+
if (t === "toggle-code") {
|
|
2259
|
+
let t = z.classList.toggle("cf-expanded");
|
|
2260
|
+
e.target.textContent = t ? "Show less code" : "Show all code";
|
|
2261
|
+
return;
|
|
2262
|
+
}
|
|
2263
|
+
let a = e.target.closest("[data-go]")?.dataset.go;
|
|
2264
|
+
a === "first" ? X(0) : a === "back" ? X(ve(-1, e.shiftKey)) : a === "next" ? X(ve(1, e.shiftKey)) : a === "last" && X(P.steps.length - 1);
|
|
2265
|
+
};
|
|
2266
|
+
function Ae(e = !0) {
|
|
2267
|
+
let t = Object.keys(P.files ?? {}).length > 0 || P.steps.some((e) => e.files);
|
|
2268
|
+
if (S.hidden = !t, !t) return;
|
|
2269
|
+
let r = n(P, U), i = Object.keys(r), a = new Map(jt(P, U).map((e) => [e.name, e])), o = [...a.keys(), ...(P.steps[U]?.fileEvents ?? []).map((e) => e.file)].filter((e) => e in r);
|
|
2270
|
+
e && o.length && (E = o[0]), E && E in r || (E = i[0]), C.replaceChildren(...i.map((e) => {
|
|
2271
|
+
let t = document.createElement("button");
|
|
2272
|
+
t.type = "button", t.className = "cf-file-tab", t.setAttribute("role", "tab"), t.dataset.file = e, t.textContent = e;
|
|
2273
|
+
let n = e === E;
|
|
2274
|
+
return t.setAttribute("aria-selected", String(n)), t.tabIndex = n ? 0 : -1, a.has(e) && (t.classList.add("cf-file-changed"), t.title = "Changed on this step"), t;
|
|
2275
|
+
})), C.hidden = i.length < 2 && !a.size;
|
|
2276
|
+
let s = E;
|
|
2277
|
+
w.setAttribute("aria-label", s ?? ""), T.setAttribute("aria-label", `Copy ${s}`);
|
|
2278
|
+
let c = s ? r[s] : "", l = s && a.get(s)?.added || "";
|
|
2279
|
+
if (w.replaceChildren(c.slice(0, c.length - l.length)), l) {
|
|
2280
|
+
let e = document.createElement("mark");
|
|
2281
|
+
e.textContent = l, e.title = "Written on this step", w.append(e);
|
|
2282
|
+
}
|
|
2283
|
+
d("#cf-files-label").textContent = i.length === 1 && C.hidden ? `File: ${i[0]}` : "Files";
|
|
2284
|
+
}
|
|
2285
|
+
let je = (e, t = !1) => {
|
|
2286
|
+
E = e, Ae(!1), t && C.querySelector(`[data-file="${CSS.escape(e)}"]`)?.focus();
|
|
2287
|
+
}, Me = (e) => {
|
|
2288
|
+
if (!(g.hidden || _.disabled)) {
|
|
2289
|
+
for (let e of g.elements) e.disabled = !0;
|
|
2290
|
+
r.onInput?.(e);
|
|
2291
|
+
}
|
|
2292
|
+
}, Ne = (e) => {
|
|
2293
|
+
e.preventDefault(), Me(_.value), _.value = "";
|
|
2294
|
+
}, Pe = (e) => {
|
|
2295
|
+
if (e.target === p || e.target === _ || e.altKey || e.ctrlKey || e.metaKey) return;
|
|
2296
|
+
let t = e.target.closest?.(".cf-file-tab");
|
|
2297
|
+
if (t && [
|
|
2298
|
+
"ArrowLeft",
|
|
2299
|
+
"ArrowRight",
|
|
2300
|
+
"Home",
|
|
2301
|
+
"End"
|
|
2302
|
+
].includes(e.key)) {
|
|
2303
|
+
let n = [...C.querySelectorAll(".cf-file-tab")], r = n.indexOf(t), i = e.key === "Home" ? 0 : e.key === "End" ? n.length - 1 : (r + (e.key === "ArrowRight" ? 1 : -1) + n.length) % n.length;
|
|
2304
|
+
e.preventDefault(), je(n[i].dataset.file, !0);
|
|
2305
|
+
return;
|
|
2306
|
+
}
|
|
2307
|
+
let n = {
|
|
2308
|
+
ArrowRight: () => X(ve(1, e.shiftKey)),
|
|
2309
|
+
ArrowLeft: () => X(ve(-1, e.shiftKey)),
|
|
2310
|
+
Home: () => X(0),
|
|
2311
|
+
End: () => X(P.steps.length - 1)
|
|
2312
|
+
}[e.key];
|
|
2313
|
+
n && (e.preventDefault(), n());
|
|
2314
|
+
};
|
|
2315
|
+
a.addEventListener("click", ke), a.addEventListener("keydown", Pe), p.addEventListener("input", () => X(Number(p.value))), g.addEventListener("submit", Ne), k.addEventListener("scroll", xe, { passive: !0 });
|
|
2316
|
+
let Fe = new ResizeObserver(() => {
|
|
2317
|
+
!q && k.offsetParent && Te(), requestAnimationFrame(xe);
|
|
2318
|
+
});
|
|
2319
|
+
return Fe.observe(k), document.fonts?.ready.then(() => {
|
|
2320
|
+
Ee = 0, De(), q = 0, Te(), xe();
|
|
2321
|
+
}), ye(r.source), ae(), U = Math.max(0, Math.min(P.steps.length - 1, r.step ?? 0)), Y(), {
|
|
2322
|
+
get step() {
|
|
2323
|
+
return U;
|
|
2324
|
+
},
|
|
2325
|
+
get length() {
|
|
2326
|
+
return P.steps.length;
|
|
2327
|
+
},
|
|
2328
|
+
go: X,
|
|
2329
|
+
setBreakpoints(e) {
|
|
2330
|
+
G = new Set(e), K();
|
|
2331
|
+
},
|
|
2332
|
+
setCumulative(e) {
|
|
2333
|
+
L = re.checked = e, q = 0, Y();
|
|
2334
|
+
},
|
|
2335
|
+
setGrids(e) {
|
|
2336
|
+
R = M.checked = e, q = 0, Y();
|
|
2337
|
+
},
|
|
2338
|
+
setTrace(e, t, n = 0) {
|
|
2339
|
+
P = Vt(e, zt(t)), q = 0, F = an(e.steps), I = !1, B(), ae(), ye(t), Ee = 0, De(), U = Math.max(0, Math.min(e.steps.length - 1, n)), g.hidden = !0, Y();
|
|
2340
|
+
},
|
|
2341
|
+
destroy() {
|
|
2342
|
+
Se(), Fe.disconnect(), H.disconnect(), Oe.disconnect(), a.removeEventListener("click", ke), a.removeEventListener("keydown", Pe), g.removeEventListener("submit", Ne), a.replaceChildren();
|
|
2343
|
+
}
|
|
2344
|
+
};
|
|
2345
|
+
}
|
|
2346
|
+
// ../renderer/src/compile-errors.ts
|
|
2347
|
+
var jn = {};
|
|
2348
|
+
for (let [e, t] of Object.entries({
|
|
2349
|
+
"stdio.h": "printf scanf puts putchar getchar gets fgets fopen fclose fprintf sprintf snprintf sscanf perror",
|
|
2350
|
+
"stdlib.h": "malloc calloc realloc free exit atoi atof atol strtol rand srand abs qsort",
|
|
2351
|
+
"string.h": "strlen strcpy strncpy strcmp strncmp strcat strncat strchr strstr strdup memcpy memmove memset memcmp",
|
|
2352
|
+
"math.h": "sqrt pow fabs floor ceil round sin cos tan log log10 exp fmod",
|
|
2353
|
+
"ctype.h": "isdigit isalpha isalnum isspace isupper islower toupper tolower",
|
|
2354
|
+
"stdbool.h": "bool true false",
|
|
2355
|
+
"stddef.h": "NULL size_t",
|
|
2356
|
+
"time.h": "time clock",
|
|
2357
|
+
"assert.h": "assert"
|
|
2358
|
+
})) for (let n of t.split(" ")) jn[n] = e;
|
|
2359
|
+
new Set("ArrayList List LinkedList HashMap Map TreeMap HashSet Set TreeSet Scanner Arrays Collections Random Stack Queue Deque ArrayDeque Iterator".split(" "));
|
|
2360
|
+
// src/index.ts
|
|
2361
|
+
var Mn = (() => {
|
|
2362
|
+
try {
|
|
2363
|
+
let e = new URL(import.meta.url);
|
|
2364
|
+
return e.pathname.endsWith("/codeflip.js") ? e.origin : "https://codeflip.dev";
|
|
2365
|
+
} catch {
|
|
2366
|
+
return "https://codeflip.dev";
|
|
2367
|
+
}
|
|
2368
|
+
})(), Nn = "\n:host { display: block; }\n:host([hidden]) { display: none; }\niframe { display: block; width: 100%; height: 480px; border: 1px solid #e4e7eb; border-radius: 8px; background: #fff; }\n.error { margin: 0; padding: 12px 16px; border-radius: 8px; background: #fef3f2; color: #b42318; font: 600 15px/1.5 system-ui, sans-serif; }\n", Pn = class extends HTMLElement {
|
|
2369
|
+
static observedAttributes = [
|
|
2370
|
+
"lang",
|
|
2371
|
+
"stdin",
|
|
2372
|
+
"files",
|
|
2373
|
+
"cumulative",
|
|
2374
|
+
"grid",
|
|
2375
|
+
"example",
|
|
2376
|
+
"link",
|
|
2377
|
+
"breakpoints",
|
|
2378
|
+
"host",
|
|
2379
|
+
"trace-src"
|
|
2380
|
+
];
|
|
2381
|
+
#e = null;
|
|
2382
|
+
#t = null;
|
|
2383
|
+
#n = 0;
|
|
2384
|
+
#r = !1;
|
|
2385
|
+
#i = (e) => this.#m(e);
|
|
2386
|
+
#a = !1;
|
|
2387
|
+
#o = new MutationObserver(() => this.#c());
|
|
2388
|
+
get step() {
|
|
2389
|
+
return this.#t?.step ?? this.#n;
|
|
2390
|
+
}
|
|
2391
|
+
set step(e) {
|
|
2392
|
+
this.#n = e, this.#t ? this.#t.go(e) : this.#e?.contentWindow?.postMessage({
|
|
2393
|
+
type: "code-flip:go",
|
|
2394
|
+
step: e
|
|
2395
|
+
}, "*");
|
|
2396
|
+
}
|
|
2397
|
+
#s() {
|
|
2398
|
+
return (this.getAttribute("breakpoints") ?? "").split(/[,\s]+/).map(Number).filter((e) => Number.isInteger(e) && e > 0);
|
|
2399
|
+
}
|
|
2400
|
+
get source() {
|
|
2401
|
+
return In(this.querySelector("script[type='text/plain']")?.textContent ?? this.textContent ?? "");
|
|
2402
|
+
}
|
|
2403
|
+
connectedCallback() {
|
|
2404
|
+
this.#r = !0, this.#n = this.getAttribute("step") === "end" ? Infinity : Math.max(0, (Number(this.getAttribute("step")) || 1) - 1), window.addEventListener("message", this.#i), this.#o.observe(this, {
|
|
2405
|
+
childList: !0,
|
|
2406
|
+
subtree: !0,
|
|
2407
|
+
characterData: !0
|
|
2408
|
+
}), document.readyState === "loading" ? document.addEventListener("DOMContentLoaded", () => this.#c(), { once: !0 }) : this.#c();
|
|
2409
|
+
}
|
|
2410
|
+
disconnectedCallback() {
|
|
2411
|
+
this.#r = !1, this.#o.disconnect(), window.removeEventListener("message", this.#i), this.#t?.destroy(), this.#t = null, this.#e = null;
|
|
2412
|
+
}
|
|
2413
|
+
attributeChangedCallback() {
|
|
2414
|
+
this.#r && this.#c();
|
|
2415
|
+
}
|
|
2416
|
+
#c() {
|
|
2417
|
+
this.#a || (this.#a = !0, queueMicrotask(() => {
|
|
2418
|
+
this.#a = !1, this.#r && document.readyState !== "loading" && this.#u();
|
|
2419
|
+
}));
|
|
2420
|
+
}
|
|
2421
|
+
#l() {
|
|
2422
|
+
let e = this.shadowRoot ?? this.attachShadow({ mode: "open" });
|
|
2423
|
+
return e.replaceChildren(Object.assign(document.createElement("style"), { textContent: Nn })), e;
|
|
2424
|
+
}
|
|
2425
|
+
#u() {
|
|
2426
|
+
this.#t?.destroy(), this.#t = null;
|
|
2427
|
+
let e = this.getAttribute("trace-src");
|
|
2428
|
+
e ? this.#f(e) : this.#d();
|
|
2429
|
+
}
|
|
2430
|
+
#d() {
|
|
2431
|
+
let e = (this.getAttribute("host") ?? Mn).replace(/\/$/, ""), t = new URLSearchParams(), n = this.getAttribute("example"), r = this.getAttribute("link");
|
|
2432
|
+
r || n ? n && !r && t.set("example", n) : (t.set("code", this.source), t.set("lang", this.getAttribute("lang") ?? "c"));
|
|
2433
|
+
let i = this.getAttribute("stdin");
|
|
2434
|
+
i && t.set("stdin", i);
|
|
2435
|
+
let a = this.getAttribute("files");
|
|
2436
|
+
a !== null && (Fn(a) ? t.set("files", a) : console.warn("<code-flip>: files must be a JSON object of file name to text, like files='{\"in.txt\": \"1 2\\n\"}'")), this.#n && t.set("step", this.#n === Infinity ? "end" : String(this.#n + 1)), this.hasAttribute("cumulative") && this.getAttribute("cumulative") !== "false" && t.set("cumulative", "1"), this.getAttribute("grid") === "false" && t.set("grid", "0"), this.#s().length && t.set("bp", this.#s().join(","));
|
|
2437
|
+
let o = document.createElement("iframe");
|
|
2438
|
+
o.src = r ? `${e}/embed/${encodeURIComponent(r)}#${t}` : `${e}/embed#${t}`, o.title = this.getAttribute("title") ?? "Step-by-step visualization of a program", o.loading = "lazy", this.#e = o, this.#l().append(o);
|
|
2439
|
+
}
|
|
2440
|
+
async #f(e) {
|
|
2441
|
+
let t = this.#l(), n;
|
|
2442
|
+
try {
|
|
2443
|
+
let t = await fetch(e);
|
|
2444
|
+
if (!t.ok) throw Error(`${t.status}`);
|
|
2445
|
+
n = await t.json();
|
|
2446
|
+
} catch {
|
|
2447
|
+
return this.#p(t, `Couldn't load the trace from ${e}.`);
|
|
2448
|
+
}
|
|
2449
|
+
let r = document.createElement("div");
|
|
2450
|
+
t.append(r), this.#t = An(r, {
|
|
2451
|
+
trace: n,
|
|
2452
|
+
source: this.source,
|
|
2453
|
+
step: this.#n,
|
|
2454
|
+
cumulative: this.hasAttribute("cumulative") && this.getAttribute("cumulative") !== "false",
|
|
2455
|
+
grids: this.getAttribute("grid") !== "false",
|
|
2456
|
+
breakpoints: this.#s(),
|
|
2457
|
+
onStep: (e) => this.#h("code-flip-step", {
|
|
2458
|
+
step: e,
|
|
2459
|
+
steps: n.steps.length
|
|
2460
|
+
})
|
|
2461
|
+
}), this.#h("code-flip-ready", { steps: n.steps.length });
|
|
2462
|
+
}
|
|
2463
|
+
#p(e, t) {
|
|
2464
|
+
e.append(Object.assign(document.createElement("p"), {
|
|
2465
|
+
className: "error",
|
|
2466
|
+
textContent: t
|
|
2467
|
+
})), this.#h("code-flip-error", { message: t });
|
|
2468
|
+
}
|
|
2469
|
+
#m(e) {
|
|
2470
|
+
if (!this.#e || e.source !== this.#e.contentWindow) return;
|
|
2471
|
+
let t = e.data;
|
|
2472
|
+
t?.type === "code-flip:resize" && typeof t.height == "number" ? this.#e.style.height = `${t.height + 2}px` : t?.type === "code-flip:ready" ? this.#h("code-flip-ready", { steps: t.steps }) : t?.type === "code-flip:step" ? (this.#n = t.step ?? 0, this.#h("code-flip-step", {
|
|
2473
|
+
step: t.step,
|
|
2474
|
+
steps: t.steps
|
|
2475
|
+
})) : t?.type === "code-flip:error" && this.#h("code-flip-error", { message: t.message });
|
|
2476
|
+
}
|
|
2477
|
+
#h(e, t) {
|
|
2478
|
+
this.dispatchEvent(new CustomEvent(e, {
|
|
2479
|
+
detail: t,
|
|
2480
|
+
bubbles: !0,
|
|
2481
|
+
composed: !0
|
|
2482
|
+
}));
|
|
2483
|
+
}
|
|
2484
|
+
};
|
|
2485
|
+
function Fn(e) {
|
|
2486
|
+
try {
|
|
2487
|
+
let t = JSON.parse(e);
|
|
2488
|
+
return typeof t == "object" && !!t && !Array.isArray(t) && Object.values(t).every((e) => typeof e == "string");
|
|
2489
|
+
} catch {
|
|
2490
|
+
return !1;
|
|
2491
|
+
}
|
|
2492
|
+
}
|
|
2493
|
+
function In(e) {
|
|
2494
|
+
let t = e.replace(/^\s*\n/, "").replace(/\s+$/, "").split("\n"), n = Math.min(...t.filter((e) => e.trim()).map((e) => e.match(/^[ \t]*/)[0].length));
|
|
2495
|
+
return `${t.map((e) => e.slice(Number.isFinite(n) ? n : 0)).join("\n")}\n`;
|
|
2496
|
+
}
|
|
2497
|
+
function Ln(e = "code-flip") {
|
|
2498
|
+
customElements.get(e) || customElements.define(e, class extends Pn {});
|
|
2499
|
+
}
|
|
2500
|
+
// src/define.ts
|
|
2501
|
+
Ln();
|
|
2502
|
+
export { Pn as CodeFlipElement, Ln as defineCodeFlip };
|