// 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.Immutable; using System.Diagnostics; using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.Operations { public partial class ControlFlowGraph { /// /// Defines kinds of regions that can be present in a /// public enum RegionKind { /// /// A root region encapsulating all s in a /// Root, /// /// Region with the only purpose to represent the life-time of locals. /// PROTOTYPE(dataflow): We should clearly explain what "life-time" refers to here, or use a different term. /// Locals, /// /// Region representing a try region. For example, /// Try, /// /// Region representing /// Filter, /// /// Region representing /// Catch, /// /// Region representing a union of a and the corresponding catch regions. /// Doesn't contain any s directly. /// FilterAndHandler, /// /// Region representing a union of a and all corresponding catch /// and regions. Doesn't contain any s directly. /// TryAndCatch, /// /// Region representing /// Finally, /// /// Region representing a union of a and corresponding finally /// region. Doesn't contain any s directly. /// /// An that has a set of and a /// at the same time is mapped to a region with region inside its region. /// TryAndFinally, /// /// Region representing the initialization for a VB Static local variable. This region will only be executed /// the first time a function is called. /// StaticLocalInitializer, } /// /// Encapsulates information about regions of s in a . /// Regions can overlap, but never cross each other boundaries. /// public sealed class Region { /// /// Region's kind /// public RegionKind Kind { get; } /// /// Enclosing region. Null for /// public Region Enclosing { get; private set; } /// /// Target exception type for , , /// /// public ITypeSymbol ExceptionType { get; } /// /// Ordinal () of the first within the region. /// public int FirstBlockOrdinal { get; } /// /// Ordinal () of the last within the region. /// public int LastBlockOrdinal { get; } /// /// Regions within this region /// public ImmutableArray Regions { get; } /// /// Locals for which this region represent the life-time. /// public ImmutableArray Locals { get; } internal Region(RegionKind kind, int firstBlockOrdinal, int lastBlockOrdinal, ImmutableArray regions = default, ImmutableArray locals = default, ITypeSymbol exceptionType = null) { Debug.Assert(firstBlockOrdinal >= 0); Debug.Assert(lastBlockOrdinal >= firstBlockOrdinal); Kind = kind; FirstBlockOrdinal = firstBlockOrdinal; LastBlockOrdinal = lastBlockOrdinal; ExceptionType = exceptionType; Locals = locals.NullToEmpty(); Regions = regions.NullToEmpty(); foreach(Region r in Regions) { Debug.Assert(r.Enclosing == null && r.Kind != RegionKind.Root); r.Enclosing = this; } #if DEBUG int previousLast; switch (kind) { case RegionKind.TryAndFinally: case RegionKind.FilterAndHandler: Debug.Assert(Regions.Length == 2); Debug.Assert(Regions[0].Kind == (kind == RegionKind.TryAndFinally ? RegionKind.Try : RegionKind.Filter)); Debug.Assert(Regions[1].Kind == (kind == RegionKind.TryAndFinally ? RegionKind.Finally : RegionKind.Catch)); Debug.Assert(Regions[0].FirstBlockOrdinal == firstBlockOrdinal); Debug.Assert(Regions[1].LastBlockOrdinal == lastBlockOrdinal); Debug.Assert(Regions[0].LastBlockOrdinal + 1 == Regions[1].FirstBlockOrdinal); break; case RegionKind.TryAndCatch: Debug.Assert(Regions.Length >= 2); Debug.Assert(Regions[0].Kind == RegionKind.Try); Debug.Assert(Regions[0].FirstBlockOrdinal == firstBlockOrdinal); previousLast = Regions[0].LastBlockOrdinal; for (int i = 1; i < Regions.Length; i++) { Region r = Regions[i]; Debug.Assert(previousLast + 1 == r.FirstBlockOrdinal); previousLast = r.LastBlockOrdinal; Debug.Assert(r.Kind == RegionKind.FilterAndHandler || r.Kind == RegionKind.Catch); } Debug.Assert(previousLast == lastBlockOrdinal); break; case RegionKind.Root: case RegionKind.Locals: case RegionKind.Try: case RegionKind.Filter: case RegionKind.Catch: case RegionKind.Finally: case RegionKind.StaticLocalInitializer: previousLast = firstBlockOrdinal - 1; foreach (Region r in Regions) { Debug.Assert(previousLast < r.FirstBlockOrdinal); previousLast = r.LastBlockOrdinal; } Debug.Assert(previousLast <= lastBlockOrdinal); break; default: throw ExceptionUtilities.UnexpectedValue(kind); } #endif } internal bool ContainsBlock(int destinationOrdinal) { return FirstBlockOrdinal <= destinationOrdinal && LastBlockOrdinal >= destinationOrdinal; } } } }