@@ -558,61 +558,54 @@ namespace ts {
558558 * and while doing so, handles traversing the structure without relying on the callstack to encode the tree structure.
559559 */
560560 export function forEachChildRecursively < T > ( rootNode : Node , cbNode : ( node : Node , parent : Node ) => T | "skip" | undefined , cbNodes ?: ( nodes : NodeArray < Node > , parent : Node ) => T | "skip" | undefined ) : T | undefined {
561-
562- const stack : Node [ ] = [ rootNode ] ;
563- while ( stack . length ) {
564- const parent = stack . pop ( ) ! ;
565- const res = visitAllPossibleChildren ( parent , gatherPossibleChildren ( parent ) ) ;
566- if ( res ) {
567- return res ;
568- }
569- }
570-
571- return ;
572-
573- function gatherPossibleChildren ( node : Node ) {
574- const children : ( Node | NodeArray < Node > ) [ ] = [ ] ;
575- forEachChild ( node , addWorkItem , addWorkItem ) ; // By using a stack above and `unshift` here, we emulate a depth-first preorder traversal
576- return children ;
577-
578- function addWorkItem ( n : Node | NodeArray < Node > ) {
579- children . unshift ( n ) ;
580- }
581- }
582-
583- function visitAllPossibleChildren ( parent : Node , children : readonly ( Node | NodeArray < Node > ) [ ] ) {
584- for ( const child of children ) {
585- if ( isArray ( child ) ) {
586- if ( cbNodes ) {
587- const res = cbNodes ( child , parent ) ;
588- if ( res ) {
589- if ( res === "skip" ) continue ;
590- return res ;
591- }
592- }
593-
594- for ( let i = child . length - 1 ; i >= 0 ; i -- ) {
595- const realChild = child [ i ] ;
596- const res = cbNode ( realChild , parent ) ;
597- if ( res ) {
598- if ( res === "skip" ) continue ;
599- return res ;
600- }
601- stack . push ( realChild ) ;
602- }
603- }
604- else {
605- stack . push ( child ) ;
606- const res = cbNode ( child , parent ) ;
561+ const queue : ( Node | NodeArray < Node > ) [ ] = gatherPossibleChildren ( rootNode ) ;
562+ const parents : Node [ ] = [ ] ; // tracks parent references for elements in queue
563+ while ( parents . length < queue . length ) {
564+ parents . push ( rootNode ) ;
565+ }
566+ while ( queue . length !== 0 ) {
567+ const current = queue . pop ( ) ! ;
568+ const parent = parents . pop ( ) ! ;
569+ if ( isArray ( current ) ) {
570+ if ( cbNodes ) {
571+ const res = cbNodes ( current , parent ) ;
607572 if ( res ) {
608573 if ( res === "skip" ) continue ;
609574 return res ;
610575 }
611576 }
577+ for ( let i = current . length - 1 ; i >= 0 ; -- i ) {
578+ queue . push ( current [ i ] ) ;
579+ parents . push ( parent ) ;
580+ }
581+ }
582+ else {
583+ const res = cbNode ( current , parent ) ;
584+ if ( res ) {
585+ if ( res === "skip" ) continue ;
586+ return res ;
587+ }
588+ if ( current . kind >= SyntaxKind . FirstNode ) {
589+ // add children in reverse order to the queue, so popping gives the first child
590+ for ( const child of gatherPossibleChildren ( current ) ) {
591+ queue . push ( child ) ;
592+ parents . push ( current ) ;
593+ }
594+ }
612595 }
613596 }
614597 }
615598
599+ function gatherPossibleChildren ( node : Node ) {
600+ const children : ( Node | NodeArray < Node > ) [ ] = [ ] ;
601+ forEachChild ( node , addWorkItem , addWorkItem ) ; // By using a stack above and `unshift` here, we emulate a depth-first preorder traversal
602+ return children ;
603+
604+ function addWorkItem ( n : Node | NodeArray < Node > ) {
605+ children . unshift ( n ) ;
606+ }
607+ }
608+
616609 export function createSourceFile ( fileName : string , sourceText : string , languageVersion : ScriptTarget , setParentNodes = false , scriptKind ?: ScriptKind ) : SourceFile {
617610 tracing . push ( tracing . Phase . Parse , "createSourceFile" , { path : fileName } , /*separateBeginAndEnd*/ true ) ;
618611 performance . mark ( "beforeParse" ) ;
0 commit comments