diff --git a/src/Compilers/Core/Portable/Syntax/AbstractSyntaxNavigator.cs b/src/Compilers/Core/Portable/Syntax/AbstractSyntaxNavigator.cs index 0bd0e84cdeb5ed2a9506bbf13c2c4279f583ae4c..e2801988977953047a1592c2b206860420661511 100644 --- a/src/Compilers/Core/Portable/Syntax/AbstractSyntaxNavigator.cs +++ b/src/Compilers/Core/Portable/Syntax/AbstractSyntaxNavigator.cs @@ -1,7 +1,9 @@ // Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; +using System.Collections.Generic; using System.Diagnostics; +using Roslyn.Utilities; namespace Microsoft.CodeAnalysis { @@ -51,38 +53,95 @@ internal SyntaxToken GetNextToken(SyntaxToken current, Func p return GetNextToken(current, predicate, stepInto != null, stepInto); } + private static readonly ObjectPool> s_childEnumeratorStackPool + = new ObjectPool>(() => new Stack(), 10); + internal SyntaxToken GetFirstToken(SyntaxNode current, Func predicate, Func stepInto) { - foreach (var child in current.ChildNodesAndTokens()) + var stack = s_childEnumeratorStackPool.Allocate(); + try { - var token = child.IsToken - ? GetFirstToken(child.AsToken(), predicate, stepInto) - : GetFirstToken(child.AsNode(), predicate, stepInto); + stack.Push(current.ChildNodesAndTokens().GetEnumerator()); - if (token.RawKind != None) + while (stack.Count > 0) { - return token; + var en = stack.Pop(); + if (en.MoveNext()) + { + var child = en.Current; + + if (child.IsToken) + { + var token = GetFirstToken(child.AsToken(), predicate, stepInto); + if (token.RawKind != None) + { + return token; + } + } + + // push this enumerator back, not done yet + stack.Push(en); + + if (child.IsNode) + { + stack.Push(child.AsNode().ChildNodesAndTokens().GetEnumerator()); + } + } } - } - return default(SyntaxToken); + return default(SyntaxToken); + } + finally + { + stack.Clear(); + s_childEnumeratorStackPool.Free(stack); + } } + private static readonly ObjectPool> s_childReversedEnumeratorStackPool + = new ObjectPool>(() => new Stack(), 10); + internal SyntaxToken GetLastToken(SyntaxNode current, Func predicate, Func stepInto) { - foreach (var child in current.ChildNodesAndTokens().Reverse()) + var stack = s_childReversedEnumeratorStackPool.Allocate(); + try { - var token = child.IsToken - ? GetLastToken(child.AsToken(), predicate, stepInto) - : GetLastToken(child.AsNode(), predicate, stepInto); + stack.Push(current.ChildNodesAndTokens().Reverse().GetEnumerator()); - if (token.RawKind != None) + while (stack.Count > 0) { - return token; + var en = stack.Pop(); + + if (en.MoveNext()) + { + var child = en.Current; + + if (child.IsToken) + { + var token = GetLastToken(child.AsToken(), predicate, stepInto); + if (token.RawKind != None) + { + return token; + } + } + + // push this enumerator back, not done yet + stack.Push(en); + + if (child.IsNode) + { + stack.Push(child.AsNode().ChildNodesAndTokens().Reverse().GetEnumerator()); + } + } } - } - return default(SyntaxToken); + return default(SyntaxToken); + } + finally + { + stack.Clear(); + s_childReversedEnumeratorStackPool.Free(stack); + } } private SyntaxToken GetFirstToken(