migrate majority of parser to slim ast node

This commit is contained in:
Ron Buckton 2024-09-18 14:01:41 -04:00
parent 85e91cb62b
commit 648e4a912d
No known key found for this signature in database
8 changed files with 2533 additions and 2115 deletions

View File

@ -22,6 +22,7 @@ export * from "../factory/nodeTests.js";
export * from "../factory/nodeChildren.js";
export * from "../factory/utilities.js";
export * from "../factory/utilitiesPublic.js";
export * from "../forEachChild.js";
export * from "../parser.js";
export * from "../commandLineParser.js";
export * from "../moduleNameResolver.js";

View File

@ -90,21 +90,19 @@ export class AstNodeExtraFields {
original: AstNode | undefined = undefined;
emitNode: EmitNode | undefined = undefined;
modifierFlagsCache: ts.ModifierFlags = ModifierFlags.None;
transformFlags: TransformFlags | undefined = undefined;
__pos: number | undefined = undefined;
__end: number | undefined = undefined;
transformFlags: TransformFlags = (-1 as TransformFlags);
__pos: number = -1;
__end: number = -1;
}
let astNodeCloneCore: (node: AstNode) => AstNode;
let astNodeShadowCore: (node: AstNode) => AstNode;
let astNodeMaybeExtra: (node: AstNode) => AstNodeExtraFields | undefined;
/** @internal */
export class AstNode<N extends Node<SyntaxKind, AstData> = Node<SyntaxKind, AstData>> {
static {
astNodeCloneCore = node => node.cloneCore();
astNodeShadowCore = node => node.shadowCore();
astNodeMaybeExtra = node => node._extra;
}
private _node: N | undefined = undefined;
@ -115,11 +113,14 @@ export class AstNode<N extends Node<SyntaxKind, AstData> = Node<SyntaxKind, AstD
readonly data: N["data"] = undefined!;
parent: AstNode<NonNullable<N["parent"]>> | undefined = undefined;
flags: NodeFlags;
flags: NodeFlags = NodeFlags.None;
pos = -1;
end = -1;
constructor(kind: N["kind"], data: N["data"], nodeConstructor: NodeConstructor<N>, flags: ts.NodeFlags = NodeFlags.None) {
// catch any excess properties assigned to the Node
Object.preventExtensions(this);
this.kind = kind;
this.data = data;
this.flags = flags;
@ -149,23 +150,51 @@ export class AstNode<N extends Node<SyntaxKind, AstData> = Node<SyntaxKind, AstD
this.extra.emitNode = value;
}
get transformFlags(): ts.TransformFlags {
const transformFlags = this._extra?.transformFlags;
if (transformFlags === undefined) {
this.extra.transformFlags = TransformFlags.None;
return this.extra.transformFlags = this.data.computeTransformFlags(this);
const extra = this.extra;
if (extra.transformFlags === (-1 as TransformFlags)) {
extra.transformFlags = this.data.computeTransformFlags(this);
}
return transformFlags;
return extra.transformFlags;
}
set transformFlags(value) {
this.extra.transformFlags = value;
}
get modifierFlagsCache(): ts.ModifierFlags {
return this._extra?.modifierFlagsCache ?? ModifierFlags.None;
}
set modifierFlagsCache(value) {
this.extra.modifierFlagsCache = value;
}
get __pos(): number | undefined { // eslint-disable-line @typescript-eslint/naming-convention
const pos = this._extra?.__pos;
return pos === -1 ? undefined : pos;
}
set __pos(value) { // eslint-disable-line @typescript-eslint/naming-convention
if (value === undefined) {
if (this._extra !== undefined) {
this._extra.__pos = -1;
}
}
else {
if (value < 0) throw new RangeError();
this.extra.__pos = value;
}
}
get __end(): number | undefined { // eslint-disable-line @typescript-eslint/naming-convention
const end = this._extra?.__end;
return end === -1 ? undefined : end;
}
set __end(value) { // eslint-disable-line @typescript-eslint/naming-convention
if (value === undefined) {
if (this._extra !== undefined) {
this._extra.__end = -1;
}
}
else {
if (value < 0) throw new RangeError();
this.extra.__end = value;
}
}
/*private*/ get extra(): AstNodeExtraFields {
return this._extra ??= new AstNodeExtraFields();
@ -958,13 +987,13 @@ export class AstTypeScriptNodeData extends AstData {
/** @internal */
export class Node<K extends SyntaxKind = SyntaxKind, T extends AstData = AstData> implements ts.Node {
readonly ast: AstNode<Node<K, T>>;
readonly ast: AstNode<Node<K, T>> = undefined!;
constructor(ast: AstNode<Node<K, T>>) {
this.ast = ast;
// catch any excess properties assigned to the Node
Object.preventExtensions(this);
this.ast = ast;
}
get kind(): K {
@ -1028,16 +1057,16 @@ export class Node<K extends SyntaxKind = SyntaxKind, T extends AstData = AstData
this.ast.emitNode = value;
}
get __pos(): number | undefined { // eslint-disable-line @typescript-eslint/naming-convention
return astNodeMaybeExtra(this.ast)?.__pos;
return this.ast.__pos;
}
set __pos(value) { // eslint-disable-line @typescript-eslint/naming-convention
this.ast.extra.__pos = value;
this.ast.__pos = value;
}
get __end(): number | undefined { // eslint-disable-line @typescript-eslint/naming-convention
return astNodeMaybeExtra(this.ast)?.__end;
return this.ast.__end;
}
set __end(value) { // eslint-disable-line @typescript-eslint/naming-convention
this.ast.extra.__end = value;
this.ast.__end = value;
}
private assertHasRealPosition(message?: string) {
@ -1258,6 +1287,9 @@ export class AstNodeArray<N extends AstNode> {
private _extra: AstNodeArrayExtraFields<N> | undefined = undefined;
constructor(items: readonly N[], hasTrailingComma = false) {
// catch any excess properties assigned to the NodeArray
Object.preventExtensions(this);
this.items = items;
this.hasTrailingComma = hasTrailingComma;
}
@ -1273,7 +1305,9 @@ export class AstNodeArray<N extends AstNode> {
return this._extra?.hasTrailingComma ?? false;
}
set hasTrailingComma(value) {
this.extra.hasTrailingComma = value;
if (value !== this.hasTrailingComma) {
this.extra.hasTrailingComma = value;
}
}
get transformFlags(): ts.TransformFlags {
@ -1335,7 +1369,7 @@ export class NodeArray<N extends Node> extends Array<N> implements ts.NodeArray<
get __pos(): number | undefined { // eslint-disable-line @typescript-eslint/naming-convention
return astNodeArrayMaybeExtra(this.ast)?.__pos;
}
}
set __pos(value) { // eslint-disable-line @typescript-eslint/naming-convention
this.ast.extra.__pos = value;
}
@ -1565,8 +1599,57 @@ export interface PrimaryExpression extends MemberExpression {
export type AstPrimaryExpression = AstNode<PrimaryExpression>;
/** @internal */
export interface LiteralExpression extends PrimaryExpression {
export interface KeywordExpression<TKind extends KeywordSyntaxKind = KeywordSyntaxKind> extends PrimaryExpression, KeywordToken<TKind> {
readonly ast: AstKeywordExpression<TKind>;
readonly kind: TKind;
}
/** @internal */
export type AstKeywordExpression<TKind extends KeywordSyntaxKind = KeywordSyntaxKind> = AstNode<KeywordExpression<TKind>>;
/** @internal */
export interface LiteralLikeNode extends Node {
readonly ast: AstLiteralLikeNode;
readonly data: AstLiteralLikeNodeData;
text: string;
isUnterminated?: boolean | undefined;
hasExtendedUnicodeEscape?: boolean | undefined;
}
/** @internal */
export type AstLiteralLikeNode = AstNode<LiteralLikeNode>;
/** @internal */
export interface AstLiteralLikeNodeData extends AstData {
text: string;
isUnterminated?: boolean | undefined;
hasExtendedUnicodeEscape?: boolean | undefined;
}
/** @internal */
export interface TemplateLiteralLikeNode extends LiteralLikeNode {
readonly ast: AstTemplateLiteralLikeNode;
readonly data: AstTemplateLiteralLikeNodeData;
rawText?: string | undefined;
/** @internal */
templateFlags?: TokenFlags | undefined;
}
/** @internal */
export interface AstTemplateLiteralLikeNodeData extends AstLiteralLikeNodeData {
rawText?: string | undefined;
/** @internal */
templateFlags?: TokenFlags | undefined;
}
/** @internal */
export type AstTemplateLiteralLikeNode = AstNode<TemplateLiteralLikeNode>;
/** @internal */
export interface LiteralExpression extends PrimaryExpression, LiteralLikeNode {
readonly ast: AstLiteralExpression;
readonly data: AstLiteralExpressionData;
_literalExpressionBrand: any;
get text(): string;
@ -1576,6 +1659,10 @@ export interface LiteralExpression extends PrimaryExpression {
/** @internal */
export type AstLiteralExpression = AstNode<LiteralExpression>;
/** @internal */
export interface AstLiteralExpressionData extends AstLiteralLikeNodeData {
}
/** @internal */
export class Token<K extends TokenSyntaxKind = TokenSyntaxKind, T extends AstTokenData = AstTokenData> extends Node<K, T> implements ts.Token<K> {
public override getChildCount(_sourceFile?: ts.SourceFile): number {
@ -1668,7 +1755,7 @@ export class AstEndOfFileTokenData extends AstTokenData {
}
/** @internal */
export class ThisExpression extends Token<SyntaxKind.ThisKeyword, AstThisExpressionData> implements PrimaryExpression, FlowContainer, ts.ThisExpression {
export class ThisExpression extends Token<SyntaxKind.ThisKeyword, AstThisExpressionData> implements KeywordExpression<SyntaxKind.ThisKeyword>, FlowContainer, ts.ThisExpression {
declare readonly ast: AstThisExpression;
declare _primaryExpressionBrand: any;
@ -1693,7 +1780,7 @@ export class AstThisExpressionData extends AstTokenData {
}
/** @internal */
export class SuperExpression extends Token<SyntaxKind.SuperKeyword, AstSuperExpressionData> implements PrimaryExpression, FlowContainer, ts.SuperExpression {
export class SuperExpression extends Token<SyntaxKind.SuperKeyword, AstSuperExpressionData> implements KeywordExpression<SyntaxKind.SuperKeyword>, FlowContainer, ts.SuperExpression {
declare readonly ast: AstSuperExpression;
declare _primaryExpressionBrand: any;
@ -1718,7 +1805,7 @@ export class AstSuperExpressionData extends AstTokenData {
}
/** @internal */
export class ImportExpression extends Token<SyntaxKind.ImportKeyword> implements PrimaryExpression, ts.ImportExpression {
export class ImportExpression extends Token<SyntaxKind.ImportKeyword> implements KeywordExpression<SyntaxKind.ImportKeyword>, ts.ImportExpression {
declare readonly ast: AstImportExpression;
declare _primaryExpressionBrand: any;
@ -1730,7 +1817,7 @@ export class ImportExpression extends Token<SyntaxKind.ImportKeyword> implements
}
/** @internal */
export class NullLiteral extends Token<SyntaxKind.NullKeyword> implements PrimaryExpression, ts.NullLiteral {
export class NullLiteral extends Token<SyntaxKind.NullKeyword> implements KeywordExpression<SyntaxKind.NullKeyword>, ts.NullLiteral {
declare readonly ast: AstNullLiteral;
declare _primaryExpressionBrand: any;
@ -1742,7 +1829,7 @@ export class NullLiteral extends Token<SyntaxKind.NullKeyword> implements Primar
}
/** @internal */
export class TrueLiteral extends Token<SyntaxKind.TrueKeyword> implements PrimaryExpression, ts.TrueLiteral {
export class TrueLiteral extends Token<SyntaxKind.TrueKeyword> implements KeywordExpression<SyntaxKind.TrueKeyword>, ts.TrueLiteral {
declare readonly ast: AstTrueLiteral;
declare _primaryExpressionBrand: any;
@ -1754,7 +1841,7 @@ export class TrueLiteral extends Token<SyntaxKind.TrueKeyword> implements Primar
}
/** @internal */
export class FalseLiteral extends Token<SyntaxKind.FalseKeyword> implements PrimaryExpression, ts.FalseLiteral {
export class FalseLiteral extends Token<SyntaxKind.FalseKeyword> implements KeywordExpression<SyntaxKind.FalseKeyword>, ts.FalseLiteral {
declare readonly ast: AstFalseLiteral;
declare _primaryExpressionBrand: any;
@ -4445,6 +4532,14 @@ export class ThisTypeNode extends Node<SyntaxKind.ThisType, AstThisTypeNodeData>
export class AstThisTypeNodeData extends AstTypeScriptNodeData {
}
/** @internal */
export type FunctionOrConstructorTypeNode =
| FunctionTypeNode
| ConstructorTypeNode;
/** @internal */
export type AstFunctionOrConstructorTypeNode = AstNodeOneOf<FunctionOrConstructorTypeNode>;
/** @internal */
export class FunctionTypeNode extends Node<SyntaxKind.FunctionType, AstFunctionTypeNodeData> implements JSDocContainer, Declaration, LocalsContainer, ts.FunctionTypeNode {
declare readonly ast: AstFunctionTypeNode;
@ -4518,11 +4613,11 @@ export class FunctionTypeNode extends Node<SyntaxKind.FunctionType, AstFunctionT
/** @internal */
export class AstFunctionTypeNodeData extends AstTypeScriptNodeData {
modifiers: AstNodeArray<AstModifier> | undefined = undefined; // initialized by parser (grammar error)
typeParameters: AstNodeArray<AstTypeParameterDeclaration> | undefined = undefined;
parameters: AstNodeArray<AstParameterDeclaration> = undefined!;
type: AstTypeNode = undefined!;
typeArguments: AstNodeArray<AstTypeNode> | undefined = undefined; // quick info
modifiers: AstNodeArray<AstModifier> | undefined = undefined; // initialized by parser (grammar error)
jsDoc: JSDocArray | undefined = undefined; // initialized by parser (JSDocContainer)
symbol: Symbol = undefined!; // initialized by binder (Declaration)
@ -4883,6 +4978,14 @@ export class AstRestTypeNodeData extends AstTypeScriptNodeData {
type: AstTypeNode = undefined!;
}
/** @internal */
export type UnionOrIntersectionTypeNode =
| UnionTypeNode
| IntersectionTypeNode;
/** @internal */
export type AstUnionOrIntersectionTypeNode = AstNodeOneOf<UnionOrIntersectionTypeNode>;
/** @internal */
export class UnionTypeNode extends Node<SyntaxKind.UnionType, AstUnionTypeNodeData> implements ts.UnionTypeNode {
declare readonly ast: AstUnionTypeNode;
@ -7944,7 +8047,7 @@ export class AstJsxExpressionData extends AstData {
}
/** @internal */
export class JsxText extends Token<SyntaxKind.JsxText, AstJsxTextData> implements ts.JsxText {
export class JsxText extends Token<SyntaxKind.JsxText, AstJsxTextData> implements ts.JsxText, LiteralLikeNode {
declare readonly ast: AstJsxText;
override get parent() {
@ -13533,6 +13636,69 @@ export type AstCommaListExpression = AstNode<CommaListExpression>;
/** @internal */
export type AstSyntheticReferenceExpression = AstNode<SyntheticReferenceExpression>;
// JSON
/** @internal */
export interface JsonMinusNumericLiteral extends PrefixUnaryExpression {
readonly ast: AstJsonMinusNumericLiteral;
readonly data: AstJsonMinusNumericLiteralData;
readonly operator: SyntaxKind.MinusToken;
readonly operand: NumericLiteral;
}
/** @internal */
export interface AstJsonMinusNumericLiteralData extends AstPrefixUnaryExpressionData {
operator: SyntaxKind.MinusToken;
operand: AstNumericLiteral;
}
/** @internal */
export type AstJsonMinusNumericLiteral = AstNode<JsonMinusNumericLiteral>;
/** @internal */
export type JsonObjectExpression =
| ObjectLiteralExpression
| ArrayLiteralExpression
| JsonMinusNumericLiteral
| NumericLiteral
| StringLiteral
| BooleanLiteral
| NullLiteral;
/** @internal */
export type AstJsonObjectExpression = AstNodeOneOf<JsonObjectExpression>;
/** @internal */
export interface JsonObjectExpressionStatement extends ExpressionStatement {
readonly ast: AstJsonObjectExpressionStatement;
readonly data: AstJsonObjectExpressionStatementData;
readonly expression: JsonObjectExpression;
}
/** @internal */
export type AstJsonObjectExpressionStatement = AstNode<JsonObjectExpressionStatement>;
/** @internal */
export interface AstJsonObjectExpressionStatementData extends AstExpressionStatementData {
expression: AstJsonObjectExpression;
}
/** @internal */
export interface JsonSourceFile extends SourceFile {
readonly ast: AstJsonSourceFile;
readonly data: AstJsonSourceFileData;
readonly statements: NodeArray<JsonObjectExpressionStatement>;
}
/** @internal */
export type AstJsonSourceFile = AstNode<JsonSourceFile>;
/** @internal */
export interface AstJsonSourceFileData extends AstSourceFileData {
statements: AstNodeArray<AstJsonObjectExpressionStatement>;
}
function propagateNameFlags(node: AstPropertyName | AstBindingPattern | AstNoSubstitutionTemplateLiteral | undefined) {
return node?.kind === SyntaxKind.Identifier ? propagateIdentifierNameFlags(node) : propagateChildFlags(node);
}

View File

@ -347,6 +347,7 @@ import {
SyntaxKind,
TokenFlags,
TokenSyntaxKind,
TransformFlags,
Type,
} from "../_namespaces/ts.js";
@ -5482,7 +5483,7 @@ export function createAstNodeFactory(flags: NodeFactoryFlags, onFinishNode?: (no
node.data.typeReferenceDirectives = typeReferences;
node.data.hasNoDefaultLib = hasNoDefaultLib;
node.data.libReferenceDirectives = libReferences;
node.extra.transformFlags = undefined;
node.extra.transformFlags = -1 as TransformFlags;
return finish(node);
}

File diff suppressed because it is too large Load Diff

1018
src/compiler/forEachChild.ts Normal file

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -777,7 +777,7 @@ export function usingSingleLineStringWriter(action: (writer: EmitTextWriter) =>
}
/** @internal */
export function getFullWidth(node: Node): number {
export function getFullWidth(node: Node | ast.AstNode): number {
return node.end - node.pos;
}
@ -933,8 +933,8 @@ export function hasChangesInResolutions<K, V>(
// Returns true if this node contains a parse error anywhere underneath it.
/** @internal */
export function containsParseError(node: Node): boolean {
aggregateChildData(node);
export function containsParseError(node: Node | ast.AstNode): boolean {
aggregateChildData(node instanceof ast.AstNode ? node.node : node); // TODO(rbuckton): do not instantiate node
return (node.flags & NodeFlags.ThisNodeOrAnySubNodesHasError) !== 0;
}
@ -1058,7 +1058,7 @@ export function isFileLevelUniqueName(sourceFile: SourceFile, name: string, hasG
// However, this node will be 'missing' in the sense that no actual source-code/tokens are
// contained within it.
/** @internal */
export function nodeIsMissing(node: Node | undefined): boolean {
export function nodeIsMissing(node: Node | ast.AstNode | undefined): boolean {
if (node === undefined) {
return true;
}
@ -1067,7 +1067,7 @@ export function nodeIsMissing(node: Node | undefined): boolean {
}
/** @internal */
export function nodeIsPresent(node: Node | undefined): boolean {
export function nodeIsPresent(node: Node | ast.AstNode | undefined): boolean {
return !nodeIsMissing(node);
}
@ -1279,8 +1279,10 @@ export function getSourceTextOfNodeFromSourceFile(sourceFile: SourceFile, node:
return getTextOfNodeFromSourceText(sourceFile.text, node, includeTrivia);
}
function isJSDocTypeExpressionOrChild(node: Node): boolean {
return !!findAncestor(node, isJSDocTypeExpression);
function isJSDocTypeExpressionOrChild(node: Node | ast.AstNode): boolean {
return node instanceof ast.AstNode ?
!!findAncestor(node, ast.isAstJSDocTypeExpression) :
!!findAncestor(node, isJSDocTypeExpression);
}
/** @internal */
@ -1310,7 +1312,7 @@ export function moduleExportNameIsDefault(node: ModuleExportName): boolean {
}
/** @internal */
export function getTextOfNodeFromSourceText(sourceText: string, node: Node, includeTrivia = false): string {
export function getTextOfNodeFromSourceText(sourceText: string, node: Node | ast.AstNode, includeTrivia = false): string {
if (nodeIsMissing(node)) {
return "";
}
@ -2608,7 +2610,7 @@ export function getLeadingCommentRangesOfNode(node: Node, sourceFileOfNode: Sour
}
/** @internal */
export function getJSDocCommentRanges(node: Node, text: string): CommentRange[] | undefined {
export function getJSDocCommentRanges(node: Node | ast.AstNode, text: string): CommentRange[] | undefined {
const commentRanges = (node.kind === SyntaxKind.Parameter ||
node.kind === SyntaxKind.TypeParameter ||
node.kind === SyntaxKind.FunctionExpression ||
@ -4390,7 +4392,10 @@ export function canHaveFlowNode(node: Node): node is HasFlowNode {
}
/** @internal */
export function canHaveJSDoc(node: Node): node is HasJSDoc {
export function canHaveJSDoc(node: Node): node is HasJSDoc;
/** @internal */
export function canHaveJSDoc(node: ast.AstNode): node is ast.AstHasJSDoc;
export function canHaveJSDoc(node: Node | ast.AstNode) {
switch (node.kind) {
case SyntaxKind.ArrowFunction:
case SyntaxKind.BinaryExpression:
@ -7167,7 +7172,7 @@ export function getSyntacticModifierFlagsNoCache(node: Node): ModifierFlags {
}
/** @internal */
export function modifiersToFlags(modifiers: readonly ModifierLike[] | undefined): ModifierFlags {
export function modifiersToFlags(modifiers: readonly ModifierLike[] | readonly ast.AstModifierLike[] | undefined): ModifierFlags {
let flags = ModifierFlags.None;
if (modifiers) {
for (const modifier of modifiers) {
@ -10419,11 +10424,11 @@ export function setNodeFlags<T extends Node>(node: T | undefined, newFlags: Node
*
* @internal
*/
export function setParent<T extends Node>(child: T, parent: T["parent"] | undefined): T;
export function setParent<T extends Node | ast.AstNode>(child: T, parent: T["parent"] | undefined): T;
/** @internal */
export function setParent<T extends Node>(child: T | undefined, parent: T["parent"] | undefined): T | undefined;
export function setParent<T extends Node | ast.AstNode>(child: T | undefined, parent: T["parent"] | undefined): T | undefined;
/** @internal */
export function setParent<T extends Node>(child: T | undefined, parent: T["parent"] | undefined): T | undefined {
export function setParent<T extends Node | ast.AstNode>(child: T | undefined, parent: T["parent"] | undefined): T | undefined {
if (child && parent) {
(child as Mutable<T>).parent = parent;
}

View File

@ -769,6 +769,18 @@ export function getOriginalNode<T extends Node>(node: Node | undefined, nodeTest
return nodeTest(node) ? node : undefined;
}
interface AncestorTraversable<T extends AncestorTraversable<T>> {
parent: T | undefined;
}
/**
* Iterates through the parent chain of a node and performs the callback on each parent until the callback
* returns a truthy value, then returns that value.
* If no such value is found, it applies the callback until the parent pointer is undefined or the callback returns "quit"
* At that point findAncestor returns undefined.
* @internal
*/
export function findAncestor<T extends ast.AstNode>(node: ast.AstNode | undefined, callback: (element: ast.AstNode) => element is T): T | undefined;
/**
* Iterates through the parent chain of a node and performs the callback on each parent until the callback
* returns a truthy value, then returns that value.
@ -776,8 +788,10 @@ export function getOriginalNode<T extends Node>(node: Node | undefined, nodeTest
* At that point findAncestor returns undefined.
*/
export function findAncestor<T extends Node>(node: Node | undefined, callback: (element: Node) => element is T): T | undefined;
/** @internal */
export function findAncestor(node: ast.AstNode | undefined, callback: (element: ast.AstNode) => boolean | "quit"): ast.AstNode | undefined;
export function findAncestor(node: Node | undefined, callback: (element: Node) => boolean | "quit"): Node | undefined;
export function findAncestor(node: Node | undefined, callback: (element: Node) => boolean | "quit"): Node | undefined {
export function findAncestor<T extends AncestorTraversable<T>>(node: T | T | undefined, callback: (element: T) => boolean | "quit"): T | undefined {
while (node) {
const result = callback(node);
if (result === "quit") {