/// <summary> /// Allocate a field of the state machine of the given type, to serve as a temporary. /// </summary> private SynthesizedFieldSymbolBase AllocTemp(TypeSymbol type) { SynthesizedFieldSymbolBase result = null; // See if we've allocated a temp field we can reuse. Not particularly efficient, but // there should not normally be a lot of hoisted temps. for (int i = 0; i < availableFields.Count; i++) { SynthesizedFieldSymbolBase f = availableFields[i]; if (f.Type == type) { result = f; availableFields.RemoveAt(i); break; } } if ((object)result == null) { var fieldName = GeneratedNames.SpillTempName(nextTempNumber++); result = F.StateMachineField(type, fieldName, isPublic: true); } return(result); }
public static T Pop <T>(this ArrayBuilder <T> builder) { var e = builder.Peek(); builder.RemoveAt(builder.Count - 1); return(e); }
public void AssertPublicMembersThrow(ArrayBuilder <string?> builder, string?[] array) { // Public methods & properties except for IsClosed Assert.Throws <ObjectDisposedException>(() => builder.Capacity -= 1); Assert.Throws <ObjectDisposedException>(() => builder[0] = "closed"); Assert.Throws <ObjectDisposedException>(() => builder.Add("closed")); Assert.Throws <ObjectDisposedException>(() => builder.AddRange(Enumerable.Repeat("closed", 2))); Assert.Throws <ObjectDisposedException>(() => _ = builder.BinarySearch(0, 4, "element", StringComparer.Ordinal)); Assert.Throws <ObjectDisposedException>(() => _ = builder.BinarySearch("element")); Assert.Throws <ObjectDisposedException>(() => _ = builder.BinarySearch("element", StringComparer.Ordinal)); Assert.Throws <ObjectDisposedException>(() => builder.Clear()); Assert.Throws <ObjectDisposedException>(() => _ = builder.Close()); Assert.Throws <ObjectDisposedException>(() => _ = builder.CloseAndSlice()); Assert.Throws <ObjectDisposedException>(() => _ = builder.CloseAndSlice(1)); Assert.Throws <ObjectDisposedException>(() => _ = builder.CloseAndSlice(1, 1)); Assert.Throws <ObjectDisposedException>(() => _ = builder.Contains("element")); Assert.Throws <ObjectDisposedException>(() => builder.CopyTo(array)); Assert.Throws <ObjectDisposedException>(() => builder.CopyTo(0, array, 0, 4)); Assert.Throws <ObjectDisposedException>(() => builder.CopyTo(array, 0)); Assert.Throws <ObjectDisposedException>(() => builder.EnsureCapacity(12)); Assert.Throws <ObjectDisposedException>(() => _ = builder.GetEnumerator()); Assert.Throws <ObjectDisposedException>(() => _ = builder.IndexOf("element")); Assert.Throws <ObjectDisposedException>(() => _ = builder.IndexOf("element", 0)); Assert.Throws <ObjectDisposedException>(() => _ = builder.IndexOf("element", 0, 4)); Assert.Throws <ObjectDisposedException>(() => builder.Insert(0, "closed")); Assert.Throws <ObjectDisposedException>(() => builder.InsertRange(0, Enumerable.Repeat("closed", 2))); Assert.Throws <ObjectDisposedException>(() => builder.Remove("element")); Assert.Throws <ObjectDisposedException>(() => builder.RemoveAt(2)); Assert.Throws <ObjectDisposedException>(() => builder.RemoveRange(0, 3)); Assert.Throws <ObjectDisposedException>(() => _ = builder.TrimAndClose()); Assert.Throws <ObjectDisposedException>(() => builder.TrimExcess()); }
public bool Remove(T value) { if (!_set.Remove(value)) { return(false); } _elements.RemoveAt(_elements.IndexOf(value)); return(true); }
protected static SyntaxTokenList GetUpdatedDeclarationAccessibilityModifiers( ArrayBuilder <SyntaxToken> newModifierTokens, SyntaxTokenList modifiersList, Func <SyntaxToken, bool> isAccessibilityModifier) { using var _ = ArrayBuilder <SyntaxToken> .GetInstance(out var updatedModifiersList); var anyAccessModifierSeen = false; foreach (var modifier in modifiersList) { SyntaxToken newModifier; if (isAccessibilityModifier(modifier)) { if (newModifierTokens.Count == 0) { continue; } newModifier = newModifierTokens[0] .WithLeadingTrivia(modifier.LeadingTrivia) .WithTrailingTrivia(modifier.TrailingTrivia); newModifierTokens.RemoveAt(0); anyAccessModifierSeen = true; } else { if (anyAccessModifierSeen && newModifierTokens.Any()) { updatedModifiersList.AddRange(newModifierTokens); newModifierTokens.Clear(); } newModifier = modifier; } updatedModifiersList.Add(newModifier); } if (!anyAccessModifierSeen) { for (var i = newModifierTokens.Count - 1; i >= 0; i--) { updatedModifiersList.Insert(0, newModifierTokens[i]); } } else { updatedModifiersList.AddRange(newModifierTokens); } return(updatedModifiersList.ToSyntaxTokenList()); }
/// <summary> /// Combines contiguous segments with lengths that are each less than or equal to the specified segment size. /// </summary> private static void CombineSegments(ArrayBuilder <SourceText> segments, int segmentSize) { for (int i = 0; i < segments.Count - 1; i++) { if (segments[i].Length <= segmentSize) { int combinedLength = segments[i].Length; // count how many contiguous segments are reducible int count = 1; for (int j = i + 1; j < segments.Count; j++) { if (segments[j].Length <= segmentSize) { count++; combinedLength += segments[j].Length; } } // if we've got at least two, then combine them into a single text if (count > 1) { var encoding = segments[i].Encoding; var algorithm = segments[i].ChecksumAlgorithm; var writer = SourceTextWriter.Create(encoding, algorithm, combinedLength); while (count > 0) { segments[i].Write(writer); segments.RemoveAt(i); count--; } var newText = writer.ToSourceText(); segments.Insert(i, newText); } } } }
private static TBlockAnalysisData RunCore( ImmutableArray <BasicBlock> blocks, DataFlowAnalyzer <TBlockAnalysisData> analyzer, int firstBlockOrdinal, int lastBlockOrdinal, TBlockAnalysisData initialAnalysisData, ArrayBuilder <BasicBlock> unreachableBlocksToVisit, SortedSet <int> outOfRangeBlocksToVisit, PooledDictionary <ControlFlowRegion, bool> continueDispatchAfterFinally, PooledHashSet <ControlFlowRegion> dispatchedExceptionsFromRegions, CancellationToken cancellationToken) { var toVisit = new SortedSet <int>(); var firstBlock = blocks[firstBlockOrdinal]; analyzer.SetCurrentAnalysisData(firstBlock, initialAnalysisData, cancellationToken); toVisit.Add(firstBlock.Ordinal); var processedBlocks = PooledHashSet <BasicBlock> .GetInstance(); TBlockAnalysisData resultAnalysisData = default; do { cancellationToken.ThrowIfCancellationRequested(); BasicBlock current; if (toVisit.Count > 0) { var min = toVisit.Min; toVisit.Remove(min); current = blocks[min]; } else { int index; current = null; for (index = 0; index < unreachableBlocksToVisit.Count; index++) { var unreachableBlock = unreachableBlocksToVisit[index]; if (unreachableBlock.Ordinal >= firstBlockOrdinal && unreachableBlock.Ordinal <= lastBlockOrdinal) { current = unreachableBlock; break; } } if (current == null) { continue; } unreachableBlocksToVisit.RemoveAt(index); if (processedBlocks.Contains(current)) { // Already processed from a branch from another unreachable block. continue; } analyzer.SetCurrentAnalysisData(current, analyzer.GetEmptyAnalysisData(), cancellationToken); } if (current.Ordinal < firstBlockOrdinal || current.Ordinal > lastBlockOrdinal) { outOfRangeBlocksToVisit.Add(current.Ordinal); continue; } if (current.Ordinal == current.EnclosingRegion.FirstBlockOrdinal) { // We are revisiting first block of a region, so we need to again dispatch exceptions from region. dispatchedExceptionsFromRegions.Remove(current.EnclosingRegion); } TBlockAnalysisData fallThroughAnalysisData = analyzer.AnalyzeBlock(current, cancellationToken); bool fallThroughSuccessorIsReachable = true; if (current.ConditionKind != ControlFlowConditionKind.None) { TBlockAnalysisData conditionalSuccessorAnalysisData; (fallThroughAnalysisData, conditionalSuccessorAnalysisData) = analyzer.AnalyzeConditionalBranch(current, fallThroughAnalysisData, cancellationToken); bool conditionalSuccesorIsReachable = true; if (current.BranchValue.ConstantValue.HasValue && current.BranchValue.ConstantValue.Value is bool constant) { if (constant == (current.ConditionKind == ControlFlowConditionKind.WhenTrue)) { fallThroughSuccessorIsReachable = false; } else { conditionalSuccesorIsReachable = false; } } if (conditionalSuccesorIsReachable || analyzer.AnalyzeUnreachableBlocks) { FollowBranch(current, current.ConditionalSuccessor, conditionalSuccessorAnalysisData); } } else { fallThroughAnalysisData = analyzer.AnalyzeNonConditionalBranch(current, fallThroughAnalysisData, cancellationToken); } if (fallThroughSuccessorIsReachable || analyzer.AnalyzeUnreachableBlocks) { ControlFlowBranch branch = current.FallThroughSuccessor; FollowBranch(current, branch, fallThroughAnalysisData); if (current.EnclosingRegion.Kind == ControlFlowRegionKind.Finally && current.Ordinal == lastBlockOrdinal) { continueDispatchAfterFinally[current.EnclosingRegion] = branch.Semantics != ControlFlowBranchSemantics.Throw && branch.Semantics != ControlFlowBranchSemantics.Rethrow && current.FallThroughSuccessor.Semantics == ControlFlowBranchSemantics.StructuredExceptionHandling; } } if (current.Ordinal == lastBlockOrdinal) { resultAnalysisData = fallThroughAnalysisData; } // We are using very simple approach: // If try block is reachable, we should dispatch an exception from it, even if it is empty. // To simplify implementation, we dispatch exception from every reachable basic block and rely // on dispatchedExceptionsFromRegions cache to avoid doing duplicate work. DispatchException(current.EnclosingRegion); processedBlocks.Add(current); }while (toVisit.Count != 0 || unreachableBlocksToVisit.Count != 0); return(resultAnalysisData); // Local functions. void FollowBranch(BasicBlock current, ControlFlowBranch branch, TBlockAnalysisData currentAnalsisData) { if (branch == null) { return; } switch (branch.Semantics) { case ControlFlowBranchSemantics.None: case ControlFlowBranchSemantics.ProgramTermination: case ControlFlowBranchSemantics.StructuredExceptionHandling: case ControlFlowBranchSemantics.Error: Debug.Assert(branch.Destination == null); return; case ControlFlowBranchSemantics.Throw: case ControlFlowBranchSemantics.Rethrow: Debug.Assert(branch.Destination == null); StepThroughFinally(current.EnclosingRegion, destinationOrdinal: lastBlockOrdinal, ref currentAnalsisData); return; case ControlFlowBranchSemantics.Regular: case ControlFlowBranchSemantics.Return: Debug.Assert(branch.Destination != null); if (StepThroughFinally(current.EnclosingRegion, branch.Destination.Ordinal, ref currentAnalsisData)) { var destination = branch.Destination; var currentDestinationData = analyzer.GetCurrentAnalysisData(destination); var mergedAnalysisData = analyzer.Merge(currentDestinationData, currentAnalsisData, cancellationToken); // We need to analyze the destination block if both the following conditions are met: // 1. Either the current block is reachable both destination and current are non-reachable // 2. Either the new analysis data for destination has changed or destination block hasn't // been processed. if ((current.IsReachable || !destination.IsReachable) && (!analyzer.IsEqual(currentDestinationData, mergedAnalysisData) || !processedBlocks.Contains(destination))) { analyzer.SetCurrentAnalysisData(destination, mergedAnalysisData, cancellationToken); toVisit.Add(branch.Destination.Ordinal); } } return; default: throw ExceptionUtilities.UnexpectedValue(branch.Semantics); } } // Returns whether we should proceed to the destination after finallies were taken care of. bool StepThroughFinally(ControlFlowRegion region, int destinationOrdinal, ref TBlockAnalysisData currentAnalysisData) { while (!region.ContainsBlock(destinationOrdinal)) { Debug.Assert(region.Kind != ControlFlowRegionKind.Root); ControlFlowRegion enclosing = region.EnclosingRegion; if (region.Kind == ControlFlowRegionKind.Try && enclosing.Kind == ControlFlowRegionKind.TryAndFinally) { Debug.Assert(enclosing.NestedRegions[0] == region); Debug.Assert(enclosing.NestedRegions[1].Kind == ControlFlowRegionKind.Finally); if (!StepThroughSingleFinally(enclosing.NestedRegions[1], ref currentAnalysisData)) { // The point that continues dispatch is not reachable. Cancel the dispatch. return(false); } } region = enclosing; } return(true); } // Returns whether we should proceed with dispatch after finally was taken care of. bool StepThroughSingleFinally(ControlFlowRegion @finally, ref TBlockAnalysisData currentAnalysisData) { Debug.Assert(@finally.Kind == ControlFlowRegionKind.Finally); var previousAnalysisData = analyzer.GetCurrentAnalysisData(blocks[@finally.FirstBlockOrdinal]); var mergedAnalysisData = analyzer.Merge(previousAnalysisData, currentAnalysisData, cancellationToken); if (!analyzer.IsEqual(previousAnalysisData, mergedAnalysisData)) { // For simplicity, we do a complete walk of the finally/filter region in isolation // to make sure that the resume dispatch point is reachable from its beginning. // It could also be reachable through invalid branches into the finally and we don't want to consider // these cases for regular finally handling. currentAnalysisData = RunCore(blocks, analyzer, @finally.FirstBlockOrdinal, @finally.LastBlockOrdinal, mergedAnalysisData, unreachableBlocksToVisit, outOfRangeBlocksToVisit: toVisit, continueDispatchAfterFinally, dispatchedExceptionsFromRegions, cancellationToken); } if (!continueDispatchAfterFinally.TryGetValue(@finally, out bool dispatch)) { dispatch = false; continueDispatchAfterFinally.Add(@finally, false); } return(dispatch); } void DispatchException(ControlFlowRegion fromRegion) { do { if (!dispatchedExceptionsFromRegions.Add(fromRegion)) { return; } ControlFlowRegion enclosing = fromRegion.Kind == ControlFlowRegionKind.Root ? null : fromRegion.EnclosingRegion; if (fromRegion.Kind == ControlFlowRegionKind.Try) { switch (enclosing.Kind) { case ControlFlowRegionKind.TryAndFinally: Debug.Assert(enclosing.NestedRegions[0] == fromRegion); Debug.Assert(enclosing.NestedRegions[1].Kind == ControlFlowRegionKind.Finally); var currentAnalysisData = analyzer.GetCurrentAnalysisData(blocks[fromRegion.FirstBlockOrdinal]); if (!StepThroughSingleFinally(enclosing.NestedRegions[1], ref currentAnalysisData)) { // The point that continues dispatch is not reachable. Cancel the dispatch. return; } break; case ControlFlowRegionKind.TryAndCatch: Debug.Assert(enclosing.NestedRegions[0] == fromRegion); DispatchExceptionThroughCatches(enclosing, startAt: 1); break; default: throw ExceptionUtilities.UnexpectedValue(enclosing.Kind); } } else if (fromRegion.Kind == ControlFlowRegionKind.Filter) { // If filter throws, dispatch is resumed at the next catch with an original exception Debug.Assert(enclosing.Kind == ControlFlowRegionKind.FilterAndHandler); ControlFlowRegion tryAndCatch = enclosing.EnclosingRegion; Debug.Assert(tryAndCatch.Kind == ControlFlowRegionKind.TryAndCatch); int index = tryAndCatch.NestedRegions.IndexOf(enclosing, startIndex: 1); if (index > 0) { DispatchExceptionThroughCatches(tryAndCatch, startAt: index + 1); fromRegion = tryAndCatch; continue; } throw ExceptionUtilities.Unreachable; } fromRegion = enclosing; }while (fromRegion != null); } void DispatchExceptionThroughCatches(ControlFlowRegion tryAndCatch, int startAt) { // For simplicity, we do not try to figure out whether a catch clause definitely // handles all exceptions. Debug.Assert(tryAndCatch.Kind == ControlFlowRegionKind.TryAndCatch); Debug.Assert(startAt > 0); Debug.Assert(startAt <= tryAndCatch.NestedRegions.Length); for (int i = startAt; i < tryAndCatch.NestedRegions.Length; i++) { ControlFlowRegion @catch = tryAndCatch.NestedRegions[i]; switch (@catch.Kind) { case ControlFlowRegionKind.Catch: toVisit.Add(@catch.FirstBlockOrdinal); break; case ControlFlowRegionKind.FilterAndHandler: BasicBlock entryBlock = blocks[@catch.FirstBlockOrdinal]; Debug.Assert(@catch.NestedRegions[0].Kind == ControlFlowRegionKind.Filter); Debug.Assert(entryBlock.Ordinal == @catch.NestedRegions[0].FirstBlockOrdinal); toVisit.Add(entryBlock.Ordinal); break; default: throw ExceptionUtilities.UnexpectedValue(@catch.Kind); } } } }
/// <summary> /// Combines continguous segments with lengths that are each less than or equal to the specified segment size. /// </summary> private static void CombineSegments(ArrayBuilder<SourceText> segments, int segmentSize) { for (int i = 0; i < segments.Count - 1; i++) { if (segments[i].Length <= segmentSize) { int combinedLength = segments[i].Length; // count how many contiguous segments are reducible int count = 1; for (int j = i + 1; j < segments.Count; j++) { if (segments[j].Length <= segmentSize) { count++; combinedLength += segments[j].Length; } } // if we've got at least two, then combine them into a single text if (count > 1) { var encoding = segments[i].Encoding; var algorithm = segments[i].ChecksumAlgorithm; var writer = SourceTextWriter.Create(encoding, algorithm, combinedLength); while (count > 0) { segments[i].Write(writer); segments.RemoveAt(i); count--; } var newText = writer.ToSourceText(); segments.Insert(i, newText); } } } }