diff --git a/src/Compilers/Core/Portable/Operations/Operation.cs b/src/Compilers/Core/Portable/Operations/Operation.cs index 676626ccedb0f542eb4cc23967a235e3eef5cf70..9b31cd96625e27c75a239ee4a421d3136c96005d 100644 --- a/src/Compilers/Core/Portable/Operations/Operation.cs +++ b/src/Compilers/Core/Portable/Operations/Operation.cs @@ -9,6 +9,7 @@ using Microsoft.CodeAnalysis.Semantics; using Microsoft.CodeAnalysis.Text; using Roslyn.Utilities; +using Microsoft.CodeAnalysis.PooledObjects; namespace Microsoft.CodeAnalysis { @@ -147,11 +148,23 @@ public override void Accept(OperationVisitor visitor) return visitor.VisitNoneOperation(this, argument); } - public override IEnumerable Children => _getChildren().NullToEmpty(); + public override IEnumerable Children + { + get + { + foreach (var child in _getChildren().NullToEmpty().WhereNotNull()) + { + yield return Operation.SetParentOperation(child, this); + } + } + } } - private static IOperation WalkDownOperationToFindParent( - HashSet operationAlreadyProcessed, IOperation operation, TextSpan span) + private static readonly ObjectPool> s_queuePool = + new ObjectPool>(() => new Queue(), 10); + + private IOperation WalkDownOperationToFindParent( + HashSet operationAlreadyProcessed, IOperation root, TextSpan span) { void EnqueueChildOperations(Queue queue, IOperation parent) { @@ -162,96 +175,89 @@ void EnqueueChildOperations(Queue queue, IOperation parent) } } - // do we have a pool for queue? - var parentChildQueue = new Queue(); - EnqueueChildOperations(parentChildQueue, operation); + var operationQueue = s_queuePool.Allocate(); - // walk down the child operation to find parent operation - // every child returned by the queue should already have Parent operation set - IOperation child; - while ((child = parentChildQueue.Dequeue()) != null) + try { - if (!operationAlreadyProcessed.Add(child)) - { - // don't process IOperation we already processed otherwise, - // we can walk down same tree multiple times - continue; - } + EnqueueChildOperations(operationQueue, root); - if (child == operation) + // walk down the tree to find parent operation + // every operation returned by the queue should already have Parent operation set + while (operationQueue.Count > 0) { - // parent found - return child.Parent; - } + var operation = operationQueue.Dequeue(); - if (!child.Syntax.FullSpan.IntersectsWith(span)) - { - // not related node, don't walk down - continue; + if (!operationAlreadyProcessed.Add(operation)) + { + // don't process IOperation we already processed otherwise, + // we can walk down same tree multiple times + continue; + } + + if (operation == this) + { + // parent found + return operation.Parent; + } + + if (!operation.Syntax.FullSpan.IntersectsWith(span)) + { + // not related node, don't walk down + continue; + } + + // queue children so that we can do breadth first search + EnqueueChildOperations(operationQueue, operation); } - // queue children so that we can do breadth first search - EnqueueChildOperations(parentChildQueue, child); + return null; + } + finally + { + operationQueue.Clear(); + s_queuePool.Free(operationQueue); } - - return null; } private IOperation SearchParentOperation() { - // do we have a pool for hashset? - var operationAlreadyProcessed = new HashSet(); + var operationAlreadyProcessed = PooledHashSet.GetInstance(); var targetNode = Syntax; - var currentCandidate = targetNode.Parent; - while (currentCandidate != null) - { - Debug.Assert(currentCandidate.FullSpan.IntersectsWith(targetNode.FullSpan)); + // start from current node since one node can have multiple operations mapped to + var currentCandidate = targetNode; - foreach (var childNode in currentCandidate.ChildNodes()) + try + { + while (currentCandidate != null) { - if (!childNode.FullSpan.IntersectsWith(targetNode.FullSpan)) - { - // skip unrelated node - continue; - } + Debug.Assert(currentCandidate.FullSpan.IntersectsWith(targetNode.FullSpan)); - // get child operation - var childOperation = _semanticModel.GetOperationInternal(childNode); - if (childOperation != null) + // get operation + var tree = _semanticModel.GetOperationInternal(currentCandidate); + if (tree != null) { - // there is no operation for this node - continue; + // walk down operation tree to see whether this tree contains parent of this operation + var parent = WalkDownOperationToFindParent(operationAlreadyProcessed, tree, targetNode.FullSpan); + if (parent != null) + { + return parent; + } } - // record we have processed this node - if (!operationAlreadyProcessed.Add(childOperation)) - { - // we already processed this tree. no need to dig down - continue; - } - - // check easy case first - if (childOperation == this) - { - // found parent, go up the spine until we found non-null parent Operation - return currentCandidate.AncestorsAndSelf().Select(n => _semanticModel.GetOperationInternal(n)).WhereNotNull().FirstOrDefault(); - } - - // walk down child operation tree to see whether sub tree contains the given operation - var parent = WalkDownOperationToFindParent(operationAlreadyProcessed, childOperation, targetNode.FullSpan); - if (parent != null) - { - return parent; - } + // move up the tree + currentCandidate = currentCandidate.Parent; } - currentCandidate = currentCandidate.Parent; + // root node. there is no parent + return null; + } + finally + { + // put the hashset back to the pool + operationAlreadyProcessed.Free(); } - - // root node. there is no parent - return null; } } } diff --git a/src/Compilers/Core/Portable/Operations/OperationExtensions.cs b/src/Compilers/Core/Portable/Operations/OperationExtensions.cs index e1f87905f7c5fa65db8d04cfb064c5b612210b19..0598ba27b7b338a72f1b87b2da39969f8cd53ddf 100644 --- a/src/Compilers/Core/Portable/Operations/OperationExtensions.cs +++ b/src/Compilers/Core/Portable/Operations/OperationExtensions.cs @@ -1,5 +1,6 @@ // 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.Collections.Immutable; using System.Linq; @@ -27,6 +28,9 @@ public static bool HasErrors(this IOperation operation, Compilation compilation, return model.GetDiagnostics(operation.Syntax.Span, cancellationToken).Any(d => d.DefaultSeverity == DiagnosticSeverity.Error); } + private static readonly ObjectPool>> s_childEnumeratorStackPool = + new ObjectPool>>(() => new Stack>(), 10); + public static IEnumerable Descendants(this IOperation operation) { if (operation == null) @@ -46,26 +50,33 @@ public static IEnumerable DescendantsAndSelf(this IOperation operati yield return operation; - var stack = new Stack>(); + var stack = s_childEnumeratorStackPool.Allocate(); stack.Push(operation.Children.GetEnumerator()); - IEnumerator iterator; - while ((iterator = stack.Pop()) != null) + while (stack.Count > 0) { + var iterator = stack.Pop(); + if (!iterator.MoveNext()) { continue; } var current = iterator.Current; - yield return current; // push current iterator back in to the stack stack.Push(iterator); // push children iterator to the stack - stack.Push(current.Children.GetEnumerator()); + if (current != null) + { + yield return current; + stack.Push(current.Children.GetEnumerator()); + } } + + stack.Clear(); + s_childEnumeratorStackPool.Free(stack); } public static IOperation GetRootOperation(this ISymbol symbol, CancellationToken cancellationToken = default(CancellationToken))