mirror of
https://github.com/vercel-labs/scriptc.git
synced 2026-10-22 05:59:31 +08:00
`i in arr` with a non-literal number key boxed the typed array into the checked-dynamic tree and materialized every element per evaluation, so `for (...) if (i in arr)` loops were quadratic (1e4 elements: 5.3 s vs Node 0.7 ms). A number key over an array receiver now lowers to arrayHas (scr_arr_has), the query the literal fold and Object.hasOwn already use; the key is stabilized first to keep JS evaluation order. `k in dict` with a runtime string key over an index-signature record snapshotted and scanned every overflow key per probe (1e4 keys: 436 ms, 1e5: timeout). The helper now ends in recordOvfHas (scr_map_has_str).
75 lines
2.6 KiB
TypeScript
75 lines
2.6 KiB
TypeScript
// `i in arr` with a RUNTIME numeric key over a typed array answers with the
|
|
// array's own slot/property presence query (holes, length, ordinary numeric
|
|
// properties) instead of materializing the array per probe. The large loop
|
|
// at the end used to be quadratic.
|
|
const dense: number[] = [10, 20, 30];
|
|
const probes: number[] = [0, 1, 2, 3, -1, 1.5, NaN, Infinity, -Infinity, -0, 4294967294, 4294967295];
|
|
console.log(probes.map((k) => k in dense).join(","));
|
|
|
|
// Holes from `new Array(n)` and from writes past the end.
|
|
const holes = new Array<number>(6);
|
|
holes[1] = 1;
|
|
holes[4] = 4;
|
|
const present: number[] = [];
|
|
for (let i = -1; i < 8; i++) if (i in holes) present.push(i);
|
|
console.log(present.join(","), holes.length);
|
|
|
|
// Length truncation and pop remove presence; fill restores it.
|
|
const shrink: number[] = [1, 2, 3, 4, 5];
|
|
shrink.length = 2;
|
|
shrink.pop();
|
|
let alive = 0;
|
|
for (let i = 0; i < 5; i++) if (i in shrink) alive++;
|
|
console.log(alive, shrink.length);
|
|
shrink.length = 4;
|
|
console.log([0, 1, 2, 3].map((i) => i in shrink).join(","));
|
|
shrink.fill(9);
|
|
console.log([0, 1, 2, 3].map((i) => i in shrink).join(","));
|
|
|
|
// Ordinary (noncanonical) numeric property keys live beside the slots.
|
|
const withProps: number[] = [1, 2];
|
|
withProps[-1] = 5;
|
|
withProps[1.5] = 6;
|
|
console.log(-1 in withProps, 1.5 in withProps, -2 in withProps, 2.5 in withProps, withProps.length);
|
|
|
|
// String and object element arrays share the same query.
|
|
const names: string[] = ["a", "b"];
|
|
const objs: { v: number }[] = [{ v: 1 }];
|
|
let idx = 1;
|
|
console.log(idx in names, idx in objs, !(idx in objs));
|
|
|
|
// A key read from another array (number after the non-null assertion).
|
|
const keys: number[] = [0, 5, 2];
|
|
console.log(keys.map((_, j) => keys[j]! in dense).join(","));
|
|
|
|
// Sparse indices past the dense cutoff.
|
|
const sparse: number[] = [];
|
|
sparse[3000000] = 1;
|
|
sparse[5] = 2;
|
|
console.log(5 in sparse, 6 in sparse, 3000000 in sparse, 2999999 in sparse, sparse.length);
|
|
|
|
// JS evaluates the key before the receiver, and both exactly once.
|
|
const trace: string[] = [];
|
|
function key(k: number): number {
|
|
trace.push("key");
|
|
return k;
|
|
}
|
|
function recv(): number[] {
|
|
trace.push("recv");
|
|
return dense;
|
|
}
|
|
console.log(key(1) in recv(), key(7) in recv(), trace.join(","));
|
|
|
|
// Reassigning the receiver inside the key expression: the receiver is read
|
|
// after the key ran.
|
|
let swap: number[] = [1];
|
|
let j = 0;
|
|
console.log((swap = [1, 2, 3], j = 2, j) in swap);
|
|
|
|
// Large loop: linear in the array size.
|
|
const big: number[] = [];
|
|
for (let i = 0; i < 300000; i++) if (i % 3 !== 0) big[i] = i;
|
|
let count = 0;
|
|
for (let i = 0; i < 600000; i++) if (i in big) count++;
|
|
console.log(count, big.length);
|