Files
scriptc/tests/corpus/4610-array-ext-dyn-refresh.ts
T
Malte Ubl 1a14263b5f Move rarely used array storage out of line
Every ScrArr carried sixteen words, seven of them (sparse slots, their
length and capacity, noncanonical numeric properties with length and
capacity, and match-result metadata) used only by sparse arrays, arrays
with numeric non-index properties and regex match results. They now live in
a ScrArrExt the array points at, allocated on first use: a dense array
shrinks from 128 to 80 bytes, and a cycle-capable one (with its 16-byte
collector header) from 144 to 96.

Runtime readers go through a shared empty ext; writers allocate it. The
backend mirror loses the seven fields, tagged-template arrays are emitted
with a NULL ext, live stream commits swap the ext pointer (which swapped all
seven fields before), and the cached typed-view refresh swaps sparse slots
and properties through scr_arr_swap_indexed_ext, keeping each array's
metadata as before. The program-object ABI marker moves to
scr_runtime_abi_v11.

A corpus program (4610-array-ext-dyn-refresh.ts, with its order baseline) pins numeric non-index properties of a dynamically typed array through its refreshed typed view, sparse slots written directly and through a live reference, truncation, and regex match metadata read directly, through `any` and after a copy.
2026-10-11 09:38:27 -07:00

45 lines
1.7 KiB
TypeScript

// Sparse slots, noncanonical numeric properties and regex match metadata
// live in an array's out-of-line storage: they keep their values through
// typed views of dynamically typed arrays, writes through live references,
// truncation and copies.
function asNumbers(value: any): number[] {
return value as number[];
}
const parsed: any = JSON.parse("[1, 2, 3]");
const first = asNumbers(parsed);
console.log("first", first.length, first[0], first[2]);
parsed[-1] = 9;
parsed["1.5"] = 11;
parsed.push(4);
const second = asNumbers(parsed);
console.log("properties", second.length, second[3], parsed[-1], parsed["1.5"], "-1" in parsed, Object.keys(parsed).length);
delete parsed[-1];
parsed.push(5);
const third = asNumbers(parsed);
console.log("third", third.length, third[4], parsed[-1], "-1" in parsed, "1.5" in parsed);
const sparse: number[] = [1, 2, 3];
sparse[4_000_000] = 7;
console.log("sparse", sparse.length, sparse[4_000_000], 3_999_999 in sparse, 4_000_000 in sparse);
sparse.length = 3;
console.log("sparse truncated", sparse.length, sparse[4_000_000], 4_000_000 in sparse);
const typed: number[] = [5, 6];
typed[3_000_000] = 1;
const viaAny: any = typed;
viaAny[3_000_001] = 2;
console.log("typed", typed.length, typed[3_000_000], typed[3_000_001], 3_000_001 in typed);
typed.length = 2;
console.log("typed truncated", typed.length, typed[3_000_000], viaAny.length);
const match = /(b)(c)?/.exec("abd")!;
console.log("match", match.index, match.input, match.length, match[1], match[2]);
const loose: any = match;
console.log("match via any", loose.index, loose.input, loose[1], loose.length);
const copy = match.slice();
console.log("copy", copy.length, copy[0], copy[1]);