File: src\tools\illink\src\ILLink.Shared\DataFlow\IControlFlowGraph.cs
Web Access
Project: src\src\tools\illink\src\ILLink.RoslynAnalyzer\ILLink.RoslynAnalyzer.csproj (ILLink.RoslynAnalyzer)
// Copyright (c) .NET Foundation and contributors. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
 
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Diagnostics.CodeAnalysis;
 
// This is needed due to NativeAOT which doesn't enable nullable globally yet
#nullable enable
 
namespace ILLink.Shared.DataFlow
{
	public enum RegionKind
	{
		Try,
		Catch,
		Filter,
		Finally
	}
 
	public enum ConditionKind
	{
		Unconditional,
		WhenFalse,
		WhenTrue,
		Unknown
	}
 
	public interface IRegion<TRegion> : IEquatable<TRegion>
	{
		RegionKind Kind { get; }
	}
 
	public interface IBlock<TBlock> : IEquatable<TBlock>
	{
	}
 
	public interface IControlFlowGraph<TBlock, TRegion>
		where TBlock : struct, IBlock<TBlock>
		where TRegion : IRegion<TRegion>
	{
 
		// Represents an edge in the control flow graph, from a source to a destination basic block.
		public readonly struct ControlFlowBranch : IEquatable<ControlFlowBranch>
		{
			public readonly TBlock Source;
 
			// Might be null in a 'throw' branch.
			public readonly TBlock? Destination;
 
			public readonly ConditionKind ConditionKind;
 
			// The finally regions exited when control flows through this edge.
			// For example:
			//
			// try {
			//     try {
			//         Source();
			//     }
			//     finally {}
			// } finally {}
			// Target();
			//
			// There will be an edge in the CRFG from the block that calls
			// Source() to the block that calls Target(), which exits both
			// finally regions.
			public readonly ImmutableArray<TRegion> FinallyRegions;
 
			public ControlFlowBranch (TBlock source, TBlock? destination, ImmutableArray<TRegion> finallyRegions, ConditionKind conditionKind)
			{
				Source = source;
				Destination = destination;
				FinallyRegions = finallyRegions;
				ConditionKind = conditionKind;
			}
 
			public bool Equals (ControlFlowBranch other)
			{
				if (!Source.Equals (other.Source))
					return false;
 
				if (ConditionKind != other.ConditionKind)
					return false;
 
				if (Destination == null)
					return other.Destination == null;
 
				return Destination.Equals (other.Destination);
			}
 
			public override bool Equals (object? obj)
			{
				return obj is ControlFlowBranch other && Equals (other);
			}
 
			public override int GetHashCode ()
			{
				return HashUtils.Combine (
					Source.GetHashCode (),
					Destination?.GetHashCode () ?? typeof (ControlFlowBranch).GetHashCode (),
					ConditionKind.GetHashCode ());
			}
		}
 
		IEnumerable<TBlock> Blocks { get; }
 
		TBlock Entry { get; }
 
		// This does not include predecessor edges for exceptional control flow into
		// catch regions or finally regions. It also doesn't include edges for non-exceptional
		// control flow from try -> finally or from catch -> finally.
		IEnumerable<ControlFlowBranch> GetPredecessors (TBlock block);
 
		IEnumerable<ControlFlowBranch> GetSuccessors (TBlock block);
 
		bool TryGetEnclosingTryOrCatchOrFilter (TBlock block, [NotNullWhen (true)] out TRegion? tryOrCatchOrFilterRegion);
 
		bool TryGetEnclosingTryOrCatchOrFilter (TRegion region, [NotNullWhen (true)] out TRegion? tryOrCatchOrFilterRegion);
 
		bool TryGetEnclosingFinally (TBlock block, [NotNullWhen (true)] out TRegion? region);
 
		TRegion GetCorrespondingTry (TRegion cathOrFilterOrFinallyRegion);
 
		IEnumerable<TRegion> GetPreviousFilters (TRegion catchOrFilterRegion);
 
		bool HasFilter (TRegion catchRegion);
 
		TBlock FirstBlock (TRegion region);
 
		TBlock LastBlock (TRegion region);
	}
}