From 60f5c852e067e7eb7c2059412b1f7ee3b547c8e4 Mon Sep 17 00:00:00 2001 From: Chris Tate Date: Mon, 19 Oct 2026 22:31:09 -0500 Subject: [PATCH] Print holes in arrays reached through unknown as Node does A holey array converted to a checked-dynamic value keeps its holes (the conversion records them in the array's presence map), but the runtime inspector printed every element: `const u: unknown = a` for `a[2] = 1` printed `[ undefined, undefined, 1 ]`, where Node prints `[ <2 empty items>, 1 ]`, and `new Array(3)` through `unknown` printed `[ undefined, undefined, undefined ]`. The inspector now prints each run of holes as one `` entry that counts toward the 100-entry limit, as Node's formatSpecialArray does and as the typed array inspector already does. --- .../corpus/6345-inspect-dyn-holes.ts.json | 6 ++++ packages/runtime/src/scr_inspect.c | 29 ++++++++++++++----- tests/corpus/6345-inspect-dyn-holes.ts | 29 +++++++++++++++++++ .../6345-inspect-dyn-holes.ts.json | 1 + 4 files changed, 58 insertions(+), 7 deletions(-) create mode 100644 packages/compiler/test/ts7/baselines/order-parity/tests/corpus/6345-inspect-dyn-holes.ts.json create mode 100644 tests/corpus/6345-inspect-dyn-holes.ts create mode 100644 tests/harness/corpus-costs/6345-inspect-dyn-holes.ts.json diff --git a/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/6345-inspect-dyn-holes.ts.json b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/6345-inspect-dyn-holes.ts.json new file mode 100644 index 000000000..d9027a759 --- /dev/null +++ b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/6345-inspect-dyn-holes.ts.json @@ -0,0 +1,6 @@ +{ + "order": [ + "/tests/corpus/6345-inspect-dyn-holes.ts" + ], + "diags": [] +} diff --git a/packages/runtime/src/scr_inspect.c b/packages/runtime/src/scr_inspect.c index 3194528d3..e38c7eb9b 100644 --- a/packages/runtime/src/scr_inspect.c +++ b/packages/runtime/src/scr_inspect.c @@ -1362,20 +1362,35 @@ ScrStr *scr_insp_dyn(ScrDyn *d, double recurse, double depth) { case SCR_DYN_STR: return scr_insp_str(d->v.str); case SCR_DYN_ARR: { - if (d->v.arr.len == 0) return scr_str_new("[]", 2); + size_t len = d->v.arr.len; + if (len == 0) return scr_str_new("[]", 2); if (recurse > depth) return scr_str_new("[Array]", 7); scr_insp_begin(recurse + 1); - size_t shown = d->v.arr.len < 100 ? d->v.arr.len : 100; - for (size_t i = 0; i < shown; i++) { - ScrDyn *item = d->v.arr.items[i]; + /* Node's maxArrayLength (100) counts output entries; a run of holes + * is one `` entry (formatSpecialArray). */ + const unsigned char *presence = d->v.arr.presence; + size_t i = 0; + for (size_t shown = 0; i < len && shown < 100; shown++) { + if (presence && !presence[i]) { + size_t next = i + 1; + while (next < len && !presence[next]) next++; + char hole[48]; + int hn = snprintf(hole, sizeof hole, "<%zu empty item%s>", next - i, next - i == 1 ? "" : "s"); + ScrStr *s = scr_str_new(hole, (size_t)hn); + scr_insp_entry(s, false); + scr_str_release(s); + i = next; + continue; + } + ScrDyn *item = d->v.arr.items[i++]; ScrStr *s = scr_insp_dyn(item, recurse + 1, depth); scr_insp_entry(s, item->kind == SCR_DYN_NUM); scr_str_release(s); } - bool more = d->v.arr.len > 100; + bool more = i < len; if (more) { - ScrStr *m = scr_insp_more_items((double)(d->v.arr.len - 100)); - scr_insp_entry(m, d->v.arr.items[100]->kind == SCR_DYN_NUM); + ScrStr *m = scr_insp_more_items((double)(len - i)); + scr_insp_entry(m, d->v.arr.items[i]->kind == SCR_DYN_NUM); scr_str_release(m); } ScrStr *base = scr_str_new("", 0); diff --git a/tests/corpus/6345-inspect-dyn-holes.ts b/tests/corpus/6345-inspect-dyn-holes.ts new file mode 100644 index 000000000..60ecbfa73 --- /dev/null +++ b/tests/corpus/6345-inspect-dyn-holes.ts @@ -0,0 +1,29 @@ +// Holey arrays reached through `unknown` print their holes as Node does: +// runs of holes as `` entries, which count toward the +// 100-entry limit, inside nested values too. +function show(label: string, value: unknown): void { + console.log(label, value); +} +const a: number[] = []; +a[2] = 1; +show("lead", a); +const t: string[] = ["x"]; +t[4] = "y"; +t.length = 7; +show("runs", t); +const one: number[] = [1]; +one[2] = 3; +show("single", one); +const wide: number[] = []; +for (let i = 0; i < 150; i += 2) wide[i] = i; +show("wide", wide); +const dense: number[] = []; +for (let i = 0; i < 120; i++) dense[i] = i; +dense[130] = 1; +show("dense", dense); +show("nested", { rows: [a, one] }); +const made: unknown = new Array(3); +show("ctor", made); +const holes: number[] = []; +holes.length = 3; +show("length", holes); diff --git a/tests/harness/corpus-costs/6345-inspect-dyn-holes.ts.json b/tests/harness/corpus-costs/6345-inspect-dyn-holes.ts.json new file mode 100644 index 000000000..d80769f29 --- /dev/null +++ b/tests/harness/corpus-costs/6345-inspect-dyn-holes.ts.json @@ -0,0 +1 @@ +{ "plain": 2100, "san": 2300 }