From af19b77ca55175c6239089bcb64e31c6b09cdf0f Mon Sep 17 00:00:00 2001 From: Klaus Meinhardt Date: Tue, 12 Jan 2021 21:59:55 +0100 Subject: [PATCH] fix forEachChildRecursively --- src/compiler/parser.ts | 85 +++++++++++++++++++----------------------- 1 file changed, 39 insertions(+), 46 deletions(-) diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts index 6e6f1dcc19777..cd61ad8ec807d 100644 --- a/src/compiler/parser.ts +++ b/src/compiler/parser.ts @@ -558,61 +558,54 @@ namespace ts { * and while doing so, handles traversing the structure without relying on the callstack to encode the tree structure. */ export function forEachChildRecursively(rootNode: Node, cbNode: (node: Node, parent: Node) => T | "skip" | undefined, cbNodes?: (nodes: NodeArray, parent: Node) => T | "skip" | undefined): T | undefined { - - const stack: Node[] = [rootNode]; - while (stack.length) { - const parent = stack.pop()!; - const res = visitAllPossibleChildren(parent, gatherPossibleChildren(parent)); - if (res) { - return res; - } - } - - return; - - function gatherPossibleChildren(node: Node) { - const children: (Node | NodeArray)[] = []; - forEachChild(node, addWorkItem, addWorkItem); // By using a stack above and `unshift` here, we emulate a depth-first preorder traversal - return children; - - function addWorkItem(n: Node | NodeArray) { - children.unshift(n); - } - } - - function visitAllPossibleChildren(parent: Node, children: readonly (Node | NodeArray)[]) { - for (const child of children) { - if (isArray(child)) { - if (cbNodes) { - const res = cbNodes(child, parent); - if (res) { - if (res === "skip") continue; - return res; - } - } - - for (let i = child.length - 1; i >= 0; i--) { - const realChild = child[i]; - const res = cbNode(realChild, parent); - if (res) { - if (res === "skip") continue; - return res; - } - stack.push(realChild); - } - } - else { - stack.push(child); - const res = cbNode(child, parent); + const queue: (Node | NodeArray)[] = gatherPossibleChildren(rootNode); + const parents: Node[] = []; // tracks parent references for elements in queue + while (parents.length < queue.length) { + parents.push(rootNode); + } + while (queue.length !== 0) { + const current = queue.pop()!; + const parent = parents.pop()!; + if (isArray(current)) { + if (cbNodes) { + const res = cbNodes(current, parent); if (res) { if (res === "skip") continue; return res; } } + for (let i = current.length - 1; i >= 0; --i) { + queue.push(current[i]); + parents.push(parent); + } + } + else { + const res = cbNode(current, parent); + if (res) { + if (res === "skip") continue; + return res; + } + if (current.kind >= SyntaxKind.FirstNode) { + // add children in reverse order to the queue, so popping gives the first child + for (const child of gatherPossibleChildren(current)) { + queue.push(child); + parents.push(current); + } + } } } } + function gatherPossibleChildren(node: Node) { + const children: (Node | NodeArray)[] = []; + forEachChild(node, addWorkItem, addWorkItem); // By using a stack above and `unshift` here, we emulate a depth-first preorder traversal + return children; + + function addWorkItem(n: Node | NodeArray) { + children.unshift(n); + } + } + export function createSourceFile(fileName: string, sourceText: string, languageVersion: ScriptTarget, setParentNodes = false, scriptKind?: ScriptKind): SourceFile { tracing.push(tracing.Phase.Parse, "createSourceFile", { path: fileName }, /*separateBeginAndEnd*/ true); performance.mark("beforeParse");