提交 ffaac1a3 编写于 作者: P Pharring

Reduce memory used by the DescendantNode/Trivia/Token iterators.

Eliminated the StrongBox-es in favor of hand-rolled stacks of value-types.
Eliminated the UnionStack`2 and UnionStack`3 types.
Introduced dedicated methods on ChildSyntaxList.Enumerator for combining "MoveNext" and "Current" into one method call. (changeset 1219622)
上级 7b1e3fe9
......@@ -268,8 +268,6 @@
<Compile Include="InternalUtilities\TextKeyedCache.cs" />
<Compile Include="InternalUtilities\ThreadSafeFlagOperations.cs" />
<Compile Include="InternalUtilities\ThreeState.cs" />
<Compile Include="InternalUtilities\UnionStack`2.cs" />
<Compile Include="InternalUtilities\UnionStack`3.cs" />
<Compile Include="InternalUtilities\ValueTuple.cs" />
<Compile Include="InternalUtilities\ValueTuple`2.cs" />
<Compile Include="InternalUtilities\ValueTuple`3.cs" />
......@@ -541,6 +539,7 @@
<Compile Include="Syntax\SyntaxList`1.cs" />
<Compile Include="Syntax\SyntaxList`1.Enumerator.cs" />
<Compile Include="Syntax\SyntaxNode.cs" />
<Compile Include="Syntax\SyntaxNode.Iterators.cs" />
<Compile Include="Syntax\SyntaxNodeExtensions.cs" />
<Compile Include="Syntax\SyntaxNodeExtensions_Tracking.cs" />
<Compile Include="Syntax\SyntaxNodeLocationComparer.cs" />
......
// Copyright (c) Microsoft Open Technologies, Inc. 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.Diagnostics;
using Microsoft.CodeAnalysis;
namespace Roslyn.Utilities
{
internal class UnionStack<T0, T1>
where T0 : struct
where T1 : struct
{
private ArrayBuilder<T0> stack0 = ArrayBuilder<T0>.GetInstance();
private ArrayBuilder<T1> stack1 = ArrayBuilder<T1>.GetInstance();
private ArrayBuilder<Discriminator> discriminatorStack = ArrayBuilder<Discriminator>.GetInstance();
public enum Discriminator
{
Invalid,
Value0,
Value1
}
public void Push(T0 value)
{
stack0.Push(value);
discriminatorStack.Push(Discriminator.Value0);
}
public void Push(ref T0 value)
{
stack0.Push(value);
discriminatorStack.Push(Discriminator.Value0);
}
public void Push(T1 value)
{
stack1.Push(value);
discriminatorStack.Push(Discriminator.Value1);
}
public void Push(ref T1 value)
{
stack1.Push(value);
discriminatorStack.Push(Discriminator.Value1);
}
public bool TryPeek(out Discriminator discriminator)
{
if (discriminatorStack.Count == 0)
{
discriminator = Discriminator.Invalid;
return false;
}
discriminator = discriminatorStack.Peek();
return true;
}
public T0 PopValue0()
{
var discriminator = discriminatorStack.Pop();
Debug.Assert(discriminator == Discriminator.Value0);
return stack0.Pop();
}
public T1 PopValue1()
{
var discriminator = discriminatorStack.Pop();
Debug.Assert(discriminator == Discriminator.Value1);
return stack1.Pop();
}
public void FreePooledObjects()
{
stack0.Free();
stack1.Free();
discriminatorStack.Free();
}
}
}
\ No newline at end of file
// Copyright (c) Microsoft Open Technologies, Inc. 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.Diagnostics;
using Microsoft.CodeAnalysis;
namespace Roslyn.Utilities
{
internal class UnionStack<T0, T1, T2>
where T0 : struct
where T1 : struct
where T2 : struct
{
private ArrayBuilder<T0> stack0 = ArrayBuilder<T0>.GetInstance();
private ArrayBuilder<T1> stack1 = ArrayBuilder<T1>.GetInstance();
private ArrayBuilder<T2> stack2 = ArrayBuilder<T2>.GetInstance();
public enum Discriminator : byte
{
Invalid,
Value0,
Value1,
Value2
}
private ArrayBuilder<Discriminator> discriminatorStack = ArrayBuilder<Discriminator>.GetInstance();
public void Push(ref T0 value)
{
stack0.Push(value);
discriminatorStack.Push(Discriminator.Value0);
}
public void Push(T0 value)
{
stack0.Push(value);
discriminatorStack.Push(Discriminator.Value0);
}
public void Push(ref T1 value)
{
stack1.Push(value);
discriminatorStack.Push(Discriminator.Value1);
}
public void Push(T1 value)
{
stack1.Push(value);
discriminatorStack.Push(Discriminator.Value1);
}
public void Push(ref T2 value)
{
stack2.Push(value);
discriminatorStack.Push(Discriminator.Value2);
}
public void Push(T2 value)
{
stack2.Push(value);
discriminatorStack.Push(Discriminator.Value2);
}
public bool TryPeekDiscriminator(out Discriminator discriminator)
{
if (discriminatorStack.Count == 0)
{
discriminator = Discriminator.Invalid;
return false;
}
discriminator = discriminatorStack.Peek();
return true;
}
public T0 PopValue0()
{
var discriminator = discriminatorStack.Pop();
Debug.Assert(discriminator == Discriminator.Value0);
return stack0.Pop();
}
public T1 PopValue1()
{
var discriminator = discriminatorStack.Pop();
Debug.Assert(discriminator == Discriminator.Value1);
return stack1.Pop();
}
public T2 PopValue2()
{
var discriminator = discriminatorStack.Pop();
Debug.Assert(discriminator == Discriminator.Value2);
return stack2.Pop();
}
public void FreePooledObjects()
{
stack0.Free();
stack1.Free();
stack2.Free();
discriminatorStack.Free();
}
}
}
\ No newline at end of file
......@@ -52,6 +52,31 @@ public void Reset()
{
this.childIndex = -1;
}
internal bool TryMoveNextAndGetCurrent(ref SyntaxNodeOrToken current)
{
if (!MoveNext())
{
return false;
}
current = ItemInternal(node, this.childIndex);
return true;
}
internal SyntaxNode TryMoveNextAndGetCurrentAsNode()
{
while (MoveNext())
{
var nodeValue = ItemInternalAsNode(node, this.childIndex);
if (nodeValue != null)
{
return nodeValue;
}
}
return null;
}
}
private class EnumeratorImpl : IEnumerator<SyntaxNodeOrToken>
......
......@@ -158,6 +158,54 @@ internal static SyntaxNodeOrToken ItemInternal(SyntaxNode node, int index)
return new SyntaxNodeOrToken(node, greenChild, position, index);
}
/// <summary>
/// internal indexer that does not verify index.
/// Used when caller has already ensured that index is within bounds.
/// </summary>
internal static SyntaxNode ItemInternalAsNode(SyntaxNode node, int index)
{
GreenNode greenChild;
var green = node.Green;
var idx = index;
var slotIndex = 0;
// find a slot that contains the node or its parent list (if node is in a list)
// we will be skipping whole slots here so we will not loop for long
// the max possible number of slots is 11 (TypeDeclarationSyntax)
// and typically much less than that
//
// at the end of this loop we will have
// 1) slot index - slotIdx
// 2) if the slot is a list, node index in the list - idx
while (true)
{
greenChild = green.GetSlot(slotIndex);
if (greenChild != null)
{
int currentOccupancy = Occupancy(greenChild);
if (idx < currentOccupancy)
{
break;
}
idx -= currentOccupancy;
}
slotIndex++;
}
// get node that represents this slot
var red = node.GetNodeSlot(slotIndex);
if (greenChild.IsList && red != null)
{
// it is a red list of nodes (separated or not), most common case
return red.GetNodeSlot(idx);
}
// this is a single node or token
return red;
}
// for debugging
private SyntaxNodeOrToken[] Nodes
{
......
......@@ -5,23 +5,18 @@
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Threading;
using Microsoft.CodeAnalysis.Text;
using Roslyn.Utilities;
namespace Microsoft.CodeAnalysis
{
using BoxedEnumerator = StrongBox<ChildSyntaxList.Enumerator>;
using Stack2 = UnionStack<ChildSyntaxList.Enumerator, SyntaxTriviaList.Enumerator>;
using Stack3 = UnionStack<ChildSyntaxList.Enumerator, SyntaxTriviaList.Enumerator, SyntaxNodeOrToken>;
/// <summary>
/// Represents a non-terminal node in the syntax tree. This is the language agnostic equivalent of <see
/// cref="T:Microsoft.CodeAnalysis.CSharp.SyntaxNode"/> and <see cref="T:Microsoft.CodeAnalysis.VisualBasic.SyntaxNode"/>.
/// </summary>
[DebuggerDisplay("{GetDebuggerDisplay(), nq}")]
public abstract class SyntaxNode
public abstract partial class SyntaxNode
{
private readonly GreenNode green;
private readonly SyntaxNode parent;
......@@ -766,244 +761,6 @@ public IEnumerable<SyntaxNodeOrToken> DescendantNodesAndTokensAndSelf(TextSpan s
return DescendantNodesAndTokensImpl(span, descendIntoChildren, descendIntoTrivia, includeSelf: true);
}
private IEnumerable<SyntaxNode> DescendantNodesImpl(TextSpan span, Func<SyntaxNode, bool> descendIntoChildren, bool descendIntoTrivia, bool includeSelf)
{
return descendIntoTrivia
? DescendantNodesAndTokensImpl(span, descendIntoChildren, descendIntoTrivia, includeSelf).Where(e => e.IsNode).Select(e => e.AsNode())
: DescendantNodesOnly(span, descendIntoChildren, includeSelf);
}
private IEnumerable<SyntaxNodeOrToken> DescendantNodesAndTokensImpl(TextSpan span, Func<SyntaxNode, bool> descendIntoChildren, bool descendIntoTrivia, bool includeSelf)
{
return descendIntoTrivia
? DescendantNodesAndTokensIntoTrivia(span, descendIntoChildren, includeSelf)
: DescendantNodesAndTokensOnly(span, descendIntoChildren, includeSelf);
}
private static bool IsInSpan(TextSpan span, TextSpan childSpan)
{
return childSpan.OverlapsWith(span)
// special case for zero-width tokens (OverlapsWith never returns true for these)
|| (childSpan.Length == 0 && childSpan.IntersectsWith(span));
}
private IEnumerable<SyntaxNode> DescendantNodesOnly(TextSpan span, Func<SyntaxNode, bool> descendIntoChildren, bool includeSelf)
{
if (includeSelf && IsInSpan(span, this.FullSpan))
{
yield return this;
}
if (descendIntoChildren != null && !descendIntoChildren(this))
{
yield break;
}
// Keep around the strongboxes we use so that don't have to allocate unnecessarily. Use
// strong boxes so we can mutate the enumerators (which are structs) without having to
// pop them off the stack and push them back on. This saves about 10% of the time when
// enumerating the tokens in a tree.
var boxes = new Queue<BoxedEnumerator>();
var stack = new Stack<BoxedEnumerator>();
stack.Push(new BoxedEnumerator(this.ChildNodesAndTokens().GetEnumerator()));
while (stack.Count > 0)
{
var enumerator = stack.Peek();
if (!enumerator.Value.MoveNext())
{
var box = stack.Pop();
box.Value = default(ChildSyntaxList.Enumerator);
boxes.Enqueue(box);
}
else
{
var nodeValue = enumerator.Value.Current.AsNode();
if (nodeValue != null && IsInSpan(span, nodeValue.FullSpan))
{
yield return nodeValue;
if (descendIntoChildren == null || descendIntoChildren(nodeValue))
{
var box = boxes.Count == 0
? new BoxedEnumerator()
: boxes.Dequeue();
box.Value = nodeValue.ChildNodesAndTokens().GetEnumerator();
stack.Push(box);
}
}
}
}
}
private IEnumerable<SyntaxNodeOrToken> DescendantNodesAndTokensOnly(TextSpan span, Func<SyntaxNode, bool> descendIntoChildren, bool includeSelf)
{
if (includeSelf && IsInSpan(span, this.FullSpan))
{
yield return this;
}
if (descendIntoChildren != null && !descendIntoChildren(this))
{
yield break;
}
// Keep around the strongboxes we use so that don't have to allocate unnecessarily. Use
// strong boxes so we can mutate the enumerators (which are structs) without having to
// pop them off the stack and push them back on. This saves about 10% of the time when
// enumerating the tokens in a tree.
var boxes = new Queue<BoxedEnumerator>();
var stack = new Stack<BoxedEnumerator>();
stack.Push(new BoxedEnumerator(this.ChildNodesAndTokens().GetEnumerator()));
while (stack.Count > 0)
{
var enumerator = stack.Peek();
if (!enumerator.Value.MoveNext())
{
var box = stack.Pop();
box.Value = default(ChildSyntaxList.Enumerator);
boxes.Enqueue(box);
}
else
{
var value = enumerator.Value.Current;
if (IsInSpan(span, value.FullSpan))
{
yield return value;
var nodeValue = value.AsNode();
if (nodeValue != null)
{
if (descendIntoChildren == null || descendIntoChildren(nodeValue))
{
var box = boxes.Count == 0
? new BoxedEnumerator()
: boxes.Dequeue();
box.Value = value.AsNode().ChildNodesAndTokens().GetEnumerator();
stack.Push(box);
}
}
}
}
}
}
private IEnumerable<SyntaxNodeOrToken> DescendantNodesAndTokensIntoTrivia(TextSpan span, Func<SyntaxNode, bool> descendIntoChildren, bool includeSelf)
{
if (includeSelf && IsInSpan(span, this.FullSpan))
{
yield return this;
}
if (descendIntoChildren != null && !descendIntoChildren(this))
{
yield break;
}
var stack = new Stack3();
try
{
stack.Push(this.ChildNodesAndTokens().GetEnumerator());
Stack3.Discriminator discriminator;
while (stack.TryPeekDiscriminator(out discriminator))
{
switch (discriminator)
{
case Stack3.Discriminator.Value0: // child nodes & tokens
{
var enumerator = stack.PopValue0();
if (enumerator.MoveNext())
{
var value = enumerator.Current;
stack.Push(ref enumerator);
if (IsInSpan(span, value.FullSpan))
{
if (value.IsNode)
{
// parent nodes come before children (prefix document order)
yield return value;
var nodeValue = value.AsNode();
if (descendIntoChildren == null || descendIntoChildren(nodeValue))
{
stack.Push(value.AsNode().ChildNodesAndTokens().GetEnumerator());
}
}
else if (value.IsToken)
{
var token = value.AsToken();
// only look through trivia if this node has structured trivia
if (token.HasStructuredTrivia)
{
// trailing trivia comes last
if (token.HasTrailingTrivia)
{
stack.Push(token.TrailingTrivia.GetEnumerator());
}
// tokens come between leading and trailing trivia
stack.Push(ref value);
// leading trivia comes first
if (token.HasLeadingTrivia)
{
stack.Push(token.LeadingTrivia.GetEnumerator());
}
}
else
{
// no structure trivia, so just yield this token now
yield return value;
}
}
}
}
break;
}
case Stack3.Discriminator.Value1: // trivia
{
// yield structure nodes and enumerate their children
var enumerator = stack.PopValue1();
if (enumerator.MoveNext())
{
var trivia = enumerator.Current;
stack.Push(ref enumerator);
if (trivia.HasStructure && IsInSpan(span, trivia.FullSpan))
{
var structureNode = trivia.GetStructure();
// parent nodes come before children (prefix document order)
yield return structureNode;
if (descendIntoChildren == null || descendIntoChildren(structureNode))
{
stack.Push(structureNode.ChildNodesAndTokens().GetEnumerator());
}
}
}
break;
}
case Stack3.Discriminator.Value2: // single node or token
yield return stack.PopValue2();
break;
}
}
}
finally
{
stack.FreePooledObjects();
}
}
/// <summary>
/// Finds the node with the smallest <see cref="FullSpan"/> that contains <paramref name="span"/>.
/// <paramref name="getInnermostNodeForTie"/> is used to determine the behavior in case of a tie (i.e. a node having the same span as its parent).
......@@ -1143,7 +900,7 @@ public SyntaxTrivia FindTrivia(int position, bool findInsideTrivia = false)
/// </summary>
public IEnumerable<SyntaxTrivia> DescendantTrivia(Func<SyntaxNode, bool> descendIntoChildren = null, bool descendIntoTrivia = false)
{
return DescendantTrivia(this.FullSpan, descendIntoChildren, descendIntoTrivia);
return DescendantTriviaImpl(this.FullSpan, descendIntoChildren, descendIntoTrivia);
}
/// <summary>
......@@ -1151,161 +908,9 @@ public IEnumerable<SyntaxTrivia> DescendantTrivia(Func<SyntaxNode, bool> descend
/// </summary>
public IEnumerable<SyntaxTrivia> DescendantTrivia(TextSpan span, Func<SyntaxNode, bool> descendIntoChildren = null, bool descendIntoTrivia = false)
{
if (descendIntoTrivia)
{
return DescendantTriviaIntoTrivia(span, descendIntoChildren);
}
else
{
return DescendantTriviaOnly(span, descendIntoChildren);
}
return DescendantTriviaImpl(span, descendIntoChildren, descendIntoTrivia);
}
private IEnumerable<SyntaxTrivia> DescendantTriviaOnly(TextSpan span, Func<SyntaxNode, bool> descendIntoChildren)
{
if (descendIntoChildren != null && !descendIntoChildren(this))
{
yield break;
}
var stack = new Stack<ChildSyntaxList.Enumerator>();
stack.Push(this.ChildNodesAndTokens().GetEnumerator());
while (stack.Count > 0)
{
var enumerator = stack.Pop();
if (enumerator.MoveNext())
{
var value = enumerator.Current;
stack.Push(enumerator);
if (IsInSpan(span, value.FullSpan))
{
if (value.IsNode)
{
var nodeValue = value.AsNode();
if (descendIntoChildren == null || descendIntoChildren(nodeValue))
{
stack.Push(value.AsNode().ChildNodesAndTokens().GetEnumerator());
}
}
else if (value.IsToken)
{
var token = value.AsToken();
foreach (var trivia in token.LeadingTrivia)
{
if (IsInSpan(span, trivia.FullSpan))
{
yield return trivia;
}
}
foreach (var trivia in token.TrailingTrivia)
{
if (IsInSpan(span, trivia.FullSpan))
{
yield return trivia;
}
}
}
}
}
}
}
private IEnumerable<SyntaxTrivia> DescendantTriviaIntoTrivia(TextSpan span, Func<SyntaxNode, bool> descendIntoChildren)
{
if (descendIntoChildren != null && !descendIntoChildren(this))
{
yield break;
}
var stack = new Stack2();
try
{
stack.Push(this.ChildNodesAndTokens().GetEnumerator());
Stack2.Discriminator discriminator;
while (stack.TryPeek(out discriminator))
{
switch (discriminator)
{
case Stack2.Discriminator.Value0: // child nodes & tokens
{
var enumerator = stack.PopValue0();
if (enumerator.MoveNext())
{
var value = enumerator.Current;
stack.Push(ref enumerator);
if (IsInSpan(span, value.FullSpan))
{
if (value.IsNode)
{
var nodeValue = value.AsNode();
if (descendIntoChildren == null || descendIntoChildren(nodeValue))
{
stack.Push(value.AsNode().ChildNodesAndTokens().GetEnumerator());
}
}
else if (value.IsToken)
{
var token = value.AsToken();
if (token.HasTrailingTrivia)
{
stack.Push(token.TrailingTrivia.GetEnumerator());
}
if (token.HasLeadingTrivia)
{
stack.Push(token.LeadingTrivia.GetEnumerator());
}
}
}
}
break;
}
case Stack2.Discriminator.Value1: // trivia
{
var enumerator = stack.PopValue1();
// yield structure nodes and enumerate their children
if (enumerator.MoveNext())
{
var trivia = enumerator.Current;
stack.Push(ref enumerator);
if (IsInSpan(span, trivia.FullSpan))
{
yield return trivia;
}
if (trivia.HasStructure)
{
var structureNode = trivia.GetStructure();
if (descendIntoChildren == null || descendIntoChildren(structureNode))
{
stack.Push(structureNode.ChildNodesAndTokens().GetEnumerator());
}
}
}
break;
}
}
}
}
finally
{
stack.FreePooledObjects();
}
}
#endregion
#region Annotations
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册