Decode an AST list's position only when it is read

Every read of an AST list field built an AstNodeList whose pos and end
were decoded from the wire first, though most readers only use `nodes`.
The list now keeps the wire file and the list's index, and pos and end
decode when they are read. The allocation per read stays the same size,
and a decoded file still holds no list objects.
This commit is contained in:
Chris Tate
2026-10-20 03:59:25 -05:00
parent 35e566855f
commit 1ccda45f7d
+13 -4
View File
@@ -19,13 +19,22 @@ import { SyntaxKind, NodeFlags } from "./enums.js";
/** A list of child nodes with the list's own position, the shape of tsc-ts's
* NodeList. The file keeps the nodes array, not this object: each read of a
* list field returns a new AstNodeList over the same array, so `nodes` keeps
* its identity and a decoded file holds no list objects. */
* its identity and a decoded file holds no list objects. The position is
* decoded from the wire when it is read; most readers only use `nodes`. */
export class AstNodeList<T extends AstNode = AstNode> {
constructor(
readonly pos: number,
readonly end: number,
private readonly wire: AstWireFile,
private readonly index: number,
readonly nodes: readonly T[],
) {}
get pos(): number {
return this.wire.pos(this.index) >>> 0;
}
get end(): number {
return this.wire.end(this.index) >>> 0;
}
}
/** One immutable AST response owns one object per node and one array per
@@ -83,7 +92,7 @@ export class AstFile {
this.lists[index] = nodes;
this.materialized?.();
}
return new AstNodeList(this.wire.pos(index) >>> 0, this.wire.end(index) >>> 0, nodes);
return new AstNodeList(this.wire, index, nodes);
}
namedChild(node: AstNode, name: string): number {