mirror of https://github.com/icsharpcode/ILSpy.git
				
				
			
			You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
					
					
						
							660 lines
						
					
					
						
							21 KiB
						
					
					
				
			
		
		
	
	
							660 lines
						
					
					
						
							21 KiB
						
					
					
				#nullable enable | 
						|
// Copyright (c) 2014 Daniel Grunwald | 
						|
//  | 
						|
// Permission is hereby granted, free of charge, to any person obtaining a copy of this | 
						|
// software and associated documentation files (the "Software"), to deal in the Software | 
						|
// without restriction, including without limitation the rights to use, copy, modify, merge, | 
						|
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons | 
						|
// to whom the Software is furnished to do so, subject to the following conditions: | 
						|
//  | 
						|
// The above copyright notice and this permission notice shall be included in all copies or | 
						|
// substantial portions of the Software. | 
						|
//  | 
						|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, | 
						|
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR | 
						|
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE | 
						|
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR | 
						|
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER | 
						|
// DEALINGS IN THE SOFTWARE. | 
						|
 | 
						|
using System; | 
						|
using System.Collections.Generic; | 
						|
using System.Diagnostics; | 
						|
 | 
						|
using ICSharpCode.Decompiler.TypeSystem; | 
						|
 | 
						|
namespace ICSharpCode.Decompiler.IL | 
						|
{ | 
						|
	public enum VariableKind | 
						|
	{ | 
						|
		/// <summary> | 
						|
		/// A local variable. | 
						|
		/// </summary> | 
						|
		Local, | 
						|
		/// <summary> | 
						|
		/// A pinned local variable (not associated with a pinned region) | 
						|
		/// </summary> | 
						|
		PinnedLocal, | 
						|
		/// <summary> | 
						|
		/// A pinned local variable (associated with a pinned region) | 
						|
		/// </summary> | 
						|
		PinnedRegionLocal, | 
						|
		/// <summary> | 
						|
		/// A local variable used as using-resource variable. | 
						|
		/// </summary> | 
						|
		UsingLocal, | 
						|
		/// <summary> | 
						|
		/// A local variable used as foreach variable. | 
						|
		/// </summary> | 
						|
		ForeachLocal, | 
						|
		/// <summary> | 
						|
		/// A local variable used inside an array, collection or | 
						|
		/// object initializer block to denote the object being initialized. | 
						|
		/// </summary> | 
						|
		InitializerTarget, | 
						|
		/// <summary> | 
						|
		/// A parameter. | 
						|
		/// </summary> | 
						|
		Parameter, | 
						|
		/// <summary> | 
						|
		/// Variable created for exception handler. | 
						|
		/// </summary> | 
						|
		ExceptionStackSlot, | 
						|
		/// <summary> | 
						|
		/// Local variable used in a catch block. | 
						|
		/// </summary> | 
						|
		ExceptionLocal, | 
						|
		/// <summary> | 
						|
		/// Variable created from stack slot. | 
						|
		/// </summary> | 
						|
		StackSlot, | 
						|
		/// <summary> | 
						|
		/// Variable in BlockKind.CallWithNamedArgs | 
						|
		/// </summary> | 
						|
		NamedArgument, | 
						|
		/// <summary> | 
						|
		/// Local variable that holds the display class used for lambdas within this function. | 
						|
		/// </summary> | 
						|
		DisplayClassLocal, | 
						|
		/// <summary> | 
						|
		/// Local variable declared within a pattern match. | 
						|
		/// </summary> | 
						|
		PatternLocal, | 
						|
		/// <summary> | 
						|
		/// Temporary variable declared in a deconstruction init section. | 
						|
		/// </summary> | 
						|
		DeconstructionInitTemporary, | 
						|
	} | 
						|
 | 
						|
	static class VariableKindExtensions | 
						|
	{ | 
						|
		public static bool IsThis(this ILVariable v) | 
						|
		{ | 
						|
			return v.Kind == VariableKind.Parameter && v.Index < 0; | 
						|
		} | 
						|
 | 
						|
		public static bool IsLocal(this VariableKind kind) | 
						|
		{ | 
						|
			switch (kind) | 
						|
			{ | 
						|
				case VariableKind.Local: | 
						|
				case VariableKind.ExceptionLocal: | 
						|
				case VariableKind.ForeachLocal: | 
						|
				case VariableKind.UsingLocal: | 
						|
				case VariableKind.PatternLocal: | 
						|
				case VariableKind.PinnedLocal: | 
						|
				case VariableKind.PinnedRegionLocal: | 
						|
				case VariableKind.DisplayClassLocal: | 
						|
					return true; | 
						|
				default: | 
						|
					return false; | 
						|
			} | 
						|
		} | 
						|
	} | 
						|
 | 
						|
	[DebuggerDisplay("{Name} : {Type}")] | 
						|
	public class ILVariable | 
						|
	{ | 
						|
		VariableKind kind; | 
						|
 | 
						|
		public VariableKind Kind { | 
						|
			get { | 
						|
				return kind; | 
						|
			} | 
						|
			internal set { | 
						|
				if (kind == VariableKind.Parameter) | 
						|
					throw new InvalidOperationException("Kind=Parameter cannot be changed!"); | 
						|
				if (Index != null && value.IsLocal() && !kind.IsLocal()) | 
						|
				{ | 
						|
					// For variables, Index has different meaning than for stack slots, | 
						|
					// so we need to reset it to null. | 
						|
					// StackSlot -> ForeachLocal can happen sometimes (e.g. PST.TransformForeachOnArray) | 
						|
					Index = null; | 
						|
				} | 
						|
				kind = value; | 
						|
			} | 
						|
		} | 
						|
 | 
						|
		public readonly StackType StackType; | 
						|
 | 
						|
		IType type; | 
						|
		public IType Type { | 
						|
			get { | 
						|
				return type; | 
						|
			} | 
						|
			internal set { | 
						|
				if (value.GetStackType() != StackType) | 
						|
					throw new ArgumentException($"Expected stack-type: {StackType} may not be changed. Found: {value.GetStackType()}"); | 
						|
				type = value; | 
						|
			} | 
						|
		} | 
						|
 | 
						|
		/// <summary> | 
						|
		/// This variable is either a C# 7 'in' parameter or must be declared as 'ref readonly'. | 
						|
		/// </summary> | 
						|
		public bool IsRefReadOnly { get; internal set; } | 
						|
 | 
						|
		/// <summary> | 
						|
		/// The index of the local variable or parameter (depending on Kind) | 
						|
		///  | 
						|
		/// For VariableKinds with "Local" in the name: | 
						|
		///  * if non-null, the Index refers to the LocalVariableSignature. | 
						|
		///  * index may be null for variables that used to be fields (captured by lambda/async) | 
						|
		/// For Parameters, the Index refers to the method's list of parameters. | 
						|
		///   The special "this" parameter has index -1. | 
						|
		/// For ExceptionStackSlot, the index is the IL offset of the exception handler. | 
						|
		/// For other kinds, the index has no meaning, and is usually null. | 
						|
		/// </summary> | 
						|
		public int? Index { get; private set; } | 
						|
 | 
						|
		[Conditional("DEBUG")] | 
						|
		internal void CheckInvariant() | 
						|
		{ | 
						|
			switch (kind) | 
						|
			{ | 
						|
				case VariableKind.Local: | 
						|
				case VariableKind.ForeachLocal: | 
						|
				case VariableKind.PatternLocal: | 
						|
				case VariableKind.PinnedLocal: | 
						|
				case VariableKind.PinnedRegionLocal: | 
						|
				case VariableKind.UsingLocal: | 
						|
				case VariableKind.ExceptionLocal: | 
						|
				case VariableKind.DisplayClassLocal: | 
						|
					// in range of LocalVariableSignature | 
						|
					Debug.Assert(Index == null || Index >= 0); | 
						|
					break; | 
						|
				case VariableKind.Parameter: | 
						|
					// -1 for the "this" parameter | 
						|
					Debug.Assert(Index >= -1); | 
						|
					Debug.Assert(Function == null || Index < Function.Parameters.Count); | 
						|
					break; | 
						|
				case VariableKind.ExceptionStackSlot: | 
						|
					Debug.Assert(Index >= 0); | 
						|
					break; | 
						|
			} | 
						|
		} | 
						|
 | 
						|
		public string? Name { get; set; } | 
						|
 | 
						|
		public bool HasGeneratedName { get; set; } | 
						|
 | 
						|
		/// <summary> | 
						|
		/// Gets the function in which this variable is declared. | 
						|
		/// </summary> | 
						|
		/// <remarks> | 
						|
		/// This property is set automatically when the variable is added to the <c>ILFunction.Variables</c> collection. | 
						|
		/// </remarks> | 
						|
		public ILFunction? Function { get; internal set; } | 
						|
 | 
						|
		/// <summary> | 
						|
		/// Gets the block container in which this variable is captured. | 
						|
		/// For captured variables declared inside the loop, the capture scope is the BlockContainer of the loop. | 
						|
		/// For captured variables declared outside of the loop, the capture scope is the BlockContainer of the parent function. | 
						|
		/// </summary> | 
						|
		/// <remarks> | 
						|
		/// This property returns null for variables that are not captured. | 
						|
		/// </remarks> | 
						|
		public BlockContainer? CaptureScope { get; internal set; } | 
						|
 | 
						|
		/// <summary> | 
						|
		/// Gets the index of this variable within the <c>Function.Variables</c> collection. | 
						|
		/// </summary> | 
						|
		/// <remarks> | 
						|
		/// This property is set automatically when the variable is added to the <c>VariableScope.Variables</c> collection. | 
						|
		/// It may change if an item with a lower index is removed from the collection. | 
						|
		/// </remarks> | 
						|
		public int IndexInFunction { get; internal set; } | 
						|
 | 
						|
		/// <summary> | 
						|
		/// Number of ldloc instructions referencing this variable. | 
						|
		/// </summary> | 
						|
		/// <remarks> | 
						|
		/// This variable is automatically updated when adding/removing ldloc instructions from the ILAst. | 
						|
		/// </remarks> | 
						|
		public int LoadCount => LoadInstructions.Count; | 
						|
 | 
						|
		readonly List<LdLoc> loadInstructions = new List<LdLoc>(); | 
						|
 | 
						|
		/// <summary> | 
						|
		/// List of ldloc instructions referencing this variable. | 
						|
		/// </summary> | 
						|
		/// <remarks> | 
						|
		/// This list is automatically updated when adding/removing ldloc instructions from the ILAst. | 
						|
		/// </remarks> | 
						|
		public IReadOnlyList<LdLoc> LoadInstructions => loadInstructions; | 
						|
 | 
						|
		/// <summary> | 
						|
		/// Number of store instructions referencing this variable, | 
						|
		/// plus 1 if HasInitialValue. | 
						|
		///  | 
						|
		/// Stores are: | 
						|
		/// <list type="item"> | 
						|
		/// <item>stloc</item> | 
						|
		/// <item>TryCatchHandler (assigning the exception variable)</item> | 
						|
		/// <item>PinnedRegion (assigning the pointer variable)</item> | 
						|
		/// <item>initial values (<see cref="UsesInitialValue"/>)</item> | 
						|
		/// </list> | 
						|
		/// </summary> | 
						|
		/// <remarks> | 
						|
		/// This variable is automatically updated when adding/removing stores instructions from the ILAst. | 
						|
		/// </remarks> | 
						|
		public int StoreCount => (usesInitialValue ? 1 : 0) + StoreInstructions.Count; | 
						|
 | 
						|
		readonly List<IStoreInstruction> storeInstructions = new List<IStoreInstruction>(); | 
						|
 | 
						|
		/// <summary> | 
						|
		/// List of store instructions referencing this variable. | 
						|
		///  | 
						|
		/// Stores are: | 
						|
		/// <list type="item"> | 
						|
		/// <item>stloc</item> | 
						|
		/// <item>TryCatchHandler (assigning the exception variable)</item> | 
						|
		/// <item>PinnedRegion (assigning the pointer variable)</item> | 
						|
		/// <item>initial values (<see cref="UsesInitialValue"/>) -- however, there is no instruction for | 
						|
		///       the initial value, so it is not contained in the store list.</item> | 
						|
		/// </list> | 
						|
		/// </summary> | 
						|
		/// <remarks> | 
						|
		/// This list is automatically updated when adding/removing stores instructions from the ILAst. | 
						|
		/// </remarks> | 
						|
		public IReadOnlyList<IStoreInstruction> StoreInstructions => storeInstructions; | 
						|
 | 
						|
		/// <summary> | 
						|
		/// Number of ldloca instructions referencing this variable. | 
						|
		/// </summary> | 
						|
		/// <remarks> | 
						|
		/// This variable is automatically updated when adding/removing ldloca instructions from the ILAst. | 
						|
		/// </remarks> | 
						|
		public int AddressCount => AddressInstructions.Count; | 
						|
 | 
						|
		readonly List<LdLoca> addressInstructions = new List<LdLoca>(); | 
						|
 | 
						|
		/// <summary> | 
						|
		/// List of ldloca instructions referencing this variable. | 
						|
		/// </summary> | 
						|
		/// <remarks> | 
						|
		/// This list is automatically updated when adding/removing ldloca instructions from the ILAst. | 
						|
		/// </remarks> | 
						|
		public IReadOnlyList<LdLoca> AddressInstructions => addressInstructions; | 
						|
 | 
						|
		internal void AddLoadInstruction(LdLoc inst) => inst.IndexInLoadInstructionList = AddInstruction(loadInstructions, inst); | 
						|
		internal void AddStoreInstruction(IStoreInstruction inst) => inst.IndexInStoreInstructionList = AddInstruction(storeInstructions, inst); | 
						|
		internal void AddAddressInstruction(LdLoca inst) => inst.IndexInAddressInstructionList = AddInstruction(addressInstructions, inst); | 
						|
 | 
						|
		internal void RemoveLoadInstruction(LdLoc inst) => RemoveInstruction(loadInstructions, inst.IndexInLoadInstructionList, inst); | 
						|
		internal void RemoveStoreInstruction(IStoreInstruction inst) => RemoveInstruction(storeInstructions, inst.IndexInStoreInstructionList, inst); | 
						|
		internal void RemoveAddressInstruction(LdLoca inst) => RemoveInstruction(addressInstructions, inst.IndexInAddressInstructionList, inst); | 
						|
 | 
						|
		int AddInstruction<T>(List<T> list, T inst) where T : class, IInstructionWithVariableOperand | 
						|
		{ | 
						|
			list.Add(inst); | 
						|
			return list.Count - 1; | 
						|
		} | 
						|
 | 
						|
		void RemoveInstruction<T>(List<T> list, int index, T? inst) where T : class, IInstructionWithVariableOperand | 
						|
		{ | 
						|
			Debug.Assert(list[index] == inst); | 
						|
			int indexToMove = list.Count - 1; | 
						|
			list[index] = list[indexToMove]; | 
						|
			list[index].IndexInVariableInstructionMapping = index; | 
						|
			list.RemoveAt(indexToMove); | 
						|
		} | 
						|
 | 
						|
		bool initialValueIsInitialized; | 
						|
 | 
						|
		/// <summary> | 
						|
		/// Gets/Sets whether the variable's initial value is initialized. | 
						|
		/// This is always <c>true</c> for parameters (incl. <c>this</c>). | 
						|
		///  | 
						|
		/// Normal variables have an initial value if the function uses ".locals init". | 
						|
		/// </summary> | 
						|
		public bool InitialValueIsInitialized { | 
						|
			get { return initialValueIsInitialized; } | 
						|
			set { | 
						|
				if (Kind == VariableKind.Parameter && !value) | 
						|
					throw new InvalidOperationException("Cannot remove InitialValueIsInitialized from parameters"); | 
						|
				initialValueIsInitialized = value; | 
						|
			} | 
						|
		} | 
						|
 | 
						|
		bool usesInitialValue; | 
						|
 | 
						|
		/// <summary> | 
						|
		/// Gets/Sets whether the initial value of the variable is used. | 
						|
		/// This is always <c>true</c> for parameters (incl. <c>this</c>). | 
						|
		///  | 
						|
		/// Normal variables use the initial value, if no explicit initialization is done. | 
						|
		/// </summary> | 
						|
		/// <remarks> | 
						|
		/// The following table shows the relationship between <see cref="InitialValueIsInitialized"/> | 
						|
		/// and <see cref="UsesInitialValue"/>. | 
						|
		/// <list type="table"> | 
						|
		/// <listheader> | 
						|
		/// <term><see cref="InitialValueIsInitialized"/></term> | 
						|
		/// <term><see cref="UsesInitialValue"/></term> | 
						|
		/// <term>Meaning</term> | 
						|
		/// </listheader> | 
						|
		/// <item> | 
						|
		/// <term><see langword="true" /></term> | 
						|
		/// <term><see langword="true" /></term> | 
						|
		/// <term>This variable's initial value is zero-initialized (<c>.locals init</c>) and the initial value is used. | 
						|
		/// From C#'s point of view a the value <c>default(T)</c> is assigned at the site of declaration.</term> | 
						|
		/// </item> | 
						|
		/// <item> | 
						|
		/// <term><see langword="true" /></term> | 
						|
		/// <term><see langword="false" /></term> | 
						|
		/// <term>This variable's initial value is zero-initialized (<c>.locals init</c>) and the initial value is not used. | 
						|
		/// From C#'s point of view no implicit initialization occurs, because the code assigns a value | 
						|
		/// explicitly, before the variable is first read.</term> | 
						|
		/// </item> | 
						|
		/// <item> | 
						|
		/// <term><see langword="false" /></term> | 
						|
		/// <term><see langword="true" /></term> | 
						|
		/// <term>This variable's initial value is uninitialized (<c>.locals</c> without <c>init</c>) and the | 
						|
		/// initial value is used. | 
						|
		/// From C#'s point of view a call to <code>System.Runtime.CompilerServices.Unsafe.SkipInit(out T)</code> | 
						|
		/// is generated after the declaration.</term> | 
						|
		/// </item> | 
						|
		/// <item> | 
						|
		/// <term><see langword="false" /></term> | 
						|
		/// <term><see langword="false" /></term> | 
						|
		/// <term>This variable's initial value is uninitialized (<c>.locals</c> without <c>init</c>) and the | 
						|
		/// initial value is not used. | 
						|
		/// From C#'s point of view no implicit initialization occurs, because the code assigns a value | 
						|
		/// explicitly, before the variable is first read.</term> | 
						|
		/// </item> | 
						|
		/// </list> | 
						|
		/// </remarks> | 
						|
		public bool UsesInitialValue { | 
						|
			get { return usesInitialValue; } | 
						|
			set { | 
						|
				if (Kind == VariableKind.Parameter && !value) | 
						|
					throw new InvalidOperationException("Cannot remove UsesInitialValue from parameters"); | 
						|
				usesInitialValue = value; | 
						|
			} | 
						|
		} | 
						|
 | 
						|
		[Obsolete("Use 'UsesInitialValue' instead.")] | 
						|
		public bool HasInitialValue { | 
						|
			get => UsesInitialValue; | 
						|
			set => UsesInitialValue = value; | 
						|
		} | 
						|
 | 
						|
		/// <summary> | 
						|
		/// Gets whether the variable is in SSA form: | 
						|
		/// There is exactly 1 store, and every load sees the value from that store. | 
						|
		/// </summary> | 
						|
		/// <remarks> | 
						|
		/// Note: the single store is not necessary a store instruction, it might also | 
						|
		/// be the use of the implicit initial value. | 
						|
		/// For example: for parameters, IsSingleDefinition will only return true if | 
						|
		/// the parameter is never assigned to within the function. | 
						|
		/// </remarks> | 
						|
		public bool IsSingleDefinition { | 
						|
			get { | 
						|
				return StoreCount == 1 && AddressCount == 0; | 
						|
			} | 
						|
		} | 
						|
 | 
						|
		/// <summary> | 
						|
		/// Gets whether the variable is dead - unused. | 
						|
		/// </summary> | 
						|
		public bool IsDead { | 
						|
			get { | 
						|
				return StoreInstructions.Count == 0 | 
						|
					&& LoadCount == 0 | 
						|
					&& AddressCount == 0; | 
						|
			} | 
						|
		} | 
						|
 | 
						|
		/// <summary> | 
						|
		/// The field which was converted to a local variable. | 
						|
		/// Set when the variable is from a 'yield return' or 'async' state machine. | 
						|
		/// </summary> | 
						|
		public IField? StateMachineField; | 
						|
 | 
						|
		/// <summary> | 
						|
		/// If enabled, remove dead stores to this variable as if the "Remove dead code" option is enabled. | 
						|
		/// </summary> | 
						|
		internal bool RemoveIfRedundant; | 
						|
 | 
						|
		private bool hasNullCheck; | 
						|
 | 
						|
		/// <summary> | 
						|
		/// Gets/sets whether a parameter has an auto-generated null check, i.e., the !! modifier. | 
						|
		/// Returns false for all variables except parameters. | 
						|
		/// </summary> | 
						|
		public bool HasNullCheck { | 
						|
			get => hasNullCheck; | 
						|
			set { | 
						|
				if (Kind != VariableKind.Parameter && value) | 
						|
					throw new InvalidOperationException("Cannot set HasNullCheck on local variables!"); | 
						|
				hasNullCheck = value; | 
						|
			} | 
						|
		} | 
						|
 | 
						|
		public ILVariable(VariableKind kind, IType type, int? index = null) | 
						|
		{ | 
						|
			if (type == null) | 
						|
				throw new ArgumentNullException(nameof(type)); | 
						|
			this.Kind = kind; | 
						|
			this.type = type; | 
						|
			this.StackType = type.GetStackType(); | 
						|
			this.Index = index; | 
						|
			if (kind == VariableKind.Parameter) | 
						|
			{ | 
						|
				this.InitialValueIsInitialized = true; | 
						|
				this.UsesInitialValue = true; | 
						|
			} | 
						|
			CheckInvariant(); | 
						|
		} | 
						|
 | 
						|
		public ILVariable(VariableKind kind, IType type, StackType stackType, int? index = null) | 
						|
		{ | 
						|
			if (type == null) | 
						|
				throw new ArgumentNullException(nameof(type)); | 
						|
			this.Kind = kind; | 
						|
			this.type = type; | 
						|
			this.StackType = stackType; | 
						|
			this.Index = index; | 
						|
			if (kind == VariableKind.Parameter) | 
						|
			{ | 
						|
				this.InitialValueIsInitialized = true; | 
						|
				this.UsesInitialValue = true; | 
						|
			} | 
						|
			CheckInvariant(); | 
						|
		} | 
						|
 | 
						|
		public override string? ToString() | 
						|
		{ | 
						|
			return Name; | 
						|
		} | 
						|
 | 
						|
		internal void WriteDefinitionTo(ITextOutput output) | 
						|
		{ | 
						|
			if (IsRefReadOnly) | 
						|
			{ | 
						|
				output.Write("readonly "); | 
						|
			} | 
						|
			switch (Kind) | 
						|
			{ | 
						|
				case VariableKind.Local: | 
						|
					output.Write("local "); | 
						|
					break; | 
						|
				case VariableKind.PinnedLocal: | 
						|
					output.Write("pinned local "); | 
						|
					break; | 
						|
				case VariableKind.PinnedRegionLocal: | 
						|
					output.Write("PinnedRegion local "); | 
						|
					break; | 
						|
				case VariableKind.Parameter: | 
						|
					output.Write("param "); | 
						|
					break; | 
						|
				case VariableKind.ExceptionLocal: | 
						|
					output.Write("exception local "); | 
						|
					break; | 
						|
				case VariableKind.ExceptionStackSlot: | 
						|
					output.Write("exception stack "); | 
						|
					break; | 
						|
				case VariableKind.StackSlot: | 
						|
					output.Write("stack "); | 
						|
					break; | 
						|
				case VariableKind.InitializerTarget: | 
						|
					output.Write("initializer "); | 
						|
					break; | 
						|
				case VariableKind.ForeachLocal: | 
						|
					output.Write("foreach "); | 
						|
					break; | 
						|
				case VariableKind.UsingLocal: | 
						|
					output.Write("using "); | 
						|
					break; | 
						|
				case VariableKind.NamedArgument: | 
						|
					output.Write("named_arg "); | 
						|
					break; | 
						|
				case VariableKind.DisplayClassLocal: | 
						|
					output.Write("display_class local "); | 
						|
					break; | 
						|
				case VariableKind.PatternLocal: | 
						|
					output.Write("pattern local "); | 
						|
					break; | 
						|
				case VariableKind.DeconstructionInitTemporary: | 
						|
					output.Write("deconstruction init temporary "); | 
						|
					break; | 
						|
				default: | 
						|
					throw new ArgumentOutOfRangeException(); | 
						|
			} | 
						|
			output.WriteLocalReference(this.Name, this, isDefinition: true); | 
						|
			output.Write(" : "); | 
						|
			Type.WriteTo(output); | 
						|
			output.Write('('); | 
						|
			if (Kind == VariableKind.Parameter || Kind == VariableKind.Local || Kind == VariableKind.PinnedLocal || Kind == VariableKind.PinnedRegionLocal) | 
						|
			{ | 
						|
				output.Write("Index={0}, ", Index); | 
						|
			} | 
						|
			output.Write("LoadCount={0}, AddressCount={1}, StoreCount={2})", LoadCount, AddressCount, StoreCount); | 
						|
			if (Kind != VariableKind.Parameter) | 
						|
			{ | 
						|
				if (initialValueIsInitialized) | 
						|
				{ | 
						|
					output.Write(" init"); | 
						|
				} | 
						|
				else | 
						|
				{ | 
						|
					output.Write(" uninit"); | 
						|
				} | 
						|
				if (usesInitialValue) | 
						|
				{ | 
						|
					output.Write(" used"); | 
						|
				} | 
						|
				else | 
						|
				{ | 
						|
					output.Write(" unused"); | 
						|
				} | 
						|
			} | 
						|
			if (CaptureScope != null) | 
						|
			{ | 
						|
				output.Write(" captured in "); | 
						|
				output.WriteLocalReference(CaptureScope.EntryPoint?.Label, CaptureScope); | 
						|
			} | 
						|
			if (StateMachineField != null) | 
						|
			{ | 
						|
				output.Write(" from state-machine"); | 
						|
			} | 
						|
		} | 
						|
 | 
						|
		internal void WriteTo(ITextOutput output) | 
						|
		{ | 
						|
			output.WriteLocalReference(this.Name, this); | 
						|
		} | 
						|
 | 
						|
		/// <summary> | 
						|
		/// Gets whether this variable occurs within the specified instruction. | 
						|
		/// </summary> | 
						|
		internal bool IsUsedWithin(ILInstruction inst) | 
						|
		{ | 
						|
			if (inst is IInstructionWithVariableOperand iwvo && iwvo.Variable == this) | 
						|
			{ | 
						|
				return true; | 
						|
			} | 
						|
			foreach (var child in inst.Children) | 
						|
			{ | 
						|
				if (IsUsedWithin(child)) | 
						|
					return true; | 
						|
			} | 
						|
			return false; | 
						|
		} | 
						|
	} | 
						|
 | 
						|
	public interface IInstructionWithVariableOperand | 
						|
	{ | 
						|
		ILVariable Variable { get; set; } | 
						|
		int IndexInVariableInstructionMapping { get; set; } | 
						|
	} | 
						|
 | 
						|
	public interface IStoreInstruction : IInstructionWithVariableOperand | 
						|
	{ | 
						|
		int IndexInStoreInstructionList { get; set; } | 
						|
	} | 
						|
 | 
						|
	interface ILoadInstruction : IInstructionWithVariableOperand | 
						|
	{ | 
						|
		int IndexInLoadInstructionList { get; set; } | 
						|
	} | 
						|
 | 
						|
	interface IAddressInstruction : IInstructionWithVariableOperand | 
						|
	{ | 
						|
		int IndexInAddressInstructionList { get; set; } | 
						|
	} | 
						|
 | 
						|
	public class ILVariableEqualityComparer : IEqualityComparer<ILVariable> | 
						|
	{ | 
						|
		public static readonly ILVariableEqualityComparer Instance = new ILVariableEqualityComparer(); | 
						|
 | 
						|
		public bool Equals(ILVariable? x, ILVariable? y) | 
						|
		{ | 
						|
			if (x == y) | 
						|
				return true; | 
						|
			if (x == null || y == null) | 
						|
				return false; | 
						|
			if (x.Kind == VariableKind.StackSlot || y.Kind == VariableKind.StackSlot) | 
						|
				return false; | 
						|
			if (x.Kind == VariableKind.PatternLocal || y.Kind == VariableKind.PatternLocal) | 
						|
				return false; | 
						|
			if (!(x.Function == y.Function && x.Kind == y.Kind)) | 
						|
				return false; | 
						|
			if (x.Index != null) | 
						|
				return x.Index == y.Index; | 
						|
			else if (x.StateMachineField != null) | 
						|
				return x.StateMachineField.Equals(y.StateMachineField); | 
						|
			else | 
						|
				return false; | 
						|
		} | 
						|
 | 
						|
		public int GetHashCode(ILVariable obj) | 
						|
		{ | 
						|
			if (obj.Kind == VariableKind.StackSlot) | 
						|
				return obj.GetHashCode(); | 
						|
			return (obj.Function, obj.Kind, obj.Index).GetHashCode(); | 
						|
		} | 
						|
	} | 
						|
}
 | 
						|
 |