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.
		
		
		
		
		
			
		
			
				
					
					
						
							4556 lines
						
					
					
						
							177 KiB
						
					
					
				
			
		
		
	
	
							4556 lines
						
					
					
						
							177 KiB
						
					
					
				// Copyright (c) 2014-2020 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.Collections.Immutable; | 
						|
using System.Diagnostics; | 
						|
using System.Linq; | 
						|
using System.Reflection.Metadata; | 
						|
using System.Runtime.CompilerServices; | 
						|
using System.Threading; | 
						|
 | 
						|
using ICSharpCode.Decompiler.CSharp.Resolver; | 
						|
using ICSharpCode.Decompiler.CSharp.Syntax; | 
						|
using ICSharpCode.Decompiler.CSharp.Transforms; | 
						|
using ICSharpCode.Decompiler.CSharp.TypeSystem; | 
						|
using ICSharpCode.Decompiler.IL; | 
						|
using ICSharpCode.Decompiler.IL.Transforms; | 
						|
using ICSharpCode.Decompiler.Semantics; | 
						|
using ICSharpCode.Decompiler.TypeSystem; | 
						|
using ICSharpCode.Decompiler.TypeSystem.Implementation; | 
						|
using ICSharpCode.Decompiler.Util; | 
						|
 | 
						|
using ExpressionType = System.Linq.Expressions.ExpressionType; | 
						|
using PrimitiveType = ICSharpCode.Decompiler.CSharp.Syntax.PrimitiveType; | 
						|
 | 
						|
namespace ICSharpCode.Decompiler.CSharp | 
						|
{ | 
						|
	/// <summary> | 
						|
	/// Translates from ILAst to C# expressions. | 
						|
	/// </summary> | 
						|
	/// <remarks> | 
						|
	/// Every translated expression must have: | 
						|
	/// * an ILInstruction annotation | 
						|
	/// * a ResolveResult annotation | 
						|
	/// Post-condition for Translate() calls: | 
						|
	///   * The type of the ResolveResult must match the StackType of the corresponding ILInstruction, | 
						|
	///     except that the width of integer types does not need to match (I4, I and I8 count as the same stack type here) | 
						|
	///   * Evaluating the resulting C# expression shall produce the same side effects as evaluating the ILInstruction. | 
						|
	///   * If the IL instruction has <c>ResultType == StackType.Void</c>, the C# expression may evaluate to an arbitrary type and value. | 
						|
	///   * Otherwise, evaluating the resulting C# expression shall produce a similar value as evaluating the ILInstruction. | 
						|
	///      * If the IL instruction evaluates to an integer stack type (I4, I, or I8), | 
						|
	///        the C# type of the resulting expression shall also be an integer (or enum/pointer/char/bool) type. | 
						|
	///        * If sizeof(C# type) == sizeof(IL stack type), the values must be the same. | 
						|
	///        * If sizeof(C# type) > sizeof(IL stack type), the C# value truncated to the width of the IL stack type must equal the IL value. | 
						|
	///        * If sizeof(C# type) < sizeof(IL stack type), the C# value (sign/zero-)extended to the width of the IL stack type | 
						|
	///          must equal the IL value. | 
						|
	///          Whether sign or zero extension is used depends on the sign of the C# type (as determined by <c>IType.GetSign()</c>). | 
						|
	///      * If the IL instruction is a lifted nullable operation, and the underlying operation evaluates to an integer stack type, | 
						|
	///        the C# type of the resulting expression shall be Nullable{T}, where T is an integer type (as above). | 
						|
	///        The C# value shall be null iff the IL-level value evaluates to null, and otherwise the values shall correspond | 
						|
	///        as with non-lifted integer operations. | 
						|
	///      * If the IL instruction evaluates to a managed reference (Ref) created by starting tracking of an unmanaged reference, | 
						|
	///        the C# instruction may evaluate to any integral/enum/pointer type that when converted to pointer type | 
						|
	///        is equivalent to the managed reference. | 
						|
	///      * Otherwise, the C# type of the resulting expression shall match the IL stack type, | 
						|
	///        and the evaluated values shall be the same. | 
						|
	/// </remarks> | 
						|
	sealed class ExpressionBuilder : ILVisitor<TranslationContext, TranslatedExpression> | 
						|
	{ | 
						|
		internal readonly StatementBuilder statementBuilder; | 
						|
		readonly IDecompilerTypeSystem typeSystem; | 
						|
		internal readonly ITypeResolveContext decompilationContext; | 
						|
		internal readonly ILFunction currentFunction; | 
						|
		internal readonly ICompilation compilation; | 
						|
		internal readonly CSharpResolver resolver; | 
						|
		internal readonly TypeSystemAstBuilder astBuilder; | 
						|
		internal readonly TypeInference typeInference; | 
						|
		internal readonly DecompilerSettings settings; | 
						|
		readonly CancellationToken cancellationToken; | 
						|
 | 
						|
		public ExpressionBuilder(StatementBuilder statementBuilder, IDecompilerTypeSystem typeSystem, ITypeResolveContext decompilationContext, ILFunction currentFunction, DecompilerSettings settings, CancellationToken cancellationToken) | 
						|
		{ | 
						|
			Debug.Assert(decompilationContext != null); | 
						|
			this.statementBuilder = statementBuilder; | 
						|
			this.typeSystem = typeSystem; | 
						|
			this.decompilationContext = decompilationContext; | 
						|
			this.currentFunction = currentFunction; | 
						|
			this.settings = settings; | 
						|
			this.cancellationToken = cancellationToken; | 
						|
			this.compilation = decompilationContext.Compilation; | 
						|
			this.resolver = new CSharpResolver(new CSharpTypeResolveContext(compilation.MainModule, null, decompilationContext.CurrentTypeDefinition, decompilationContext.CurrentMember)); | 
						|
			this.astBuilder = new TypeSystemAstBuilder(resolver); | 
						|
			this.astBuilder.AlwaysUseShortTypeNames = true; | 
						|
			this.astBuilder.AddResolveResultAnnotations = true; | 
						|
			this.astBuilder.ShowAttributes = true; | 
						|
			this.astBuilder.UseNullableSpecifierForValueTypes = settings.LiftNullables; | 
						|
			this.astBuilder.AlwaysUseGlobal = settings.AlwaysUseGlobal; | 
						|
			this.typeInference = new TypeInference(compilation) { Algorithm = TypeInferenceAlgorithm.Improved }; | 
						|
		} | 
						|
 | 
						|
		public AstType ConvertType(IType type) | 
						|
		{ | 
						|
			var astType = astBuilder.ConvertType(type); | 
						|
			Debug.Assert(astType.Annotation<TypeResolveResult>() != null); | 
						|
			return astType; | 
						|
		} | 
						|
 | 
						|
		public ExpressionWithResolveResult ConvertConstantValue(ResolveResult rr, bool allowImplicitConversion = false) | 
						|
		{ | 
						|
			var expr = astBuilder.ConvertConstantValue(rr); | 
						|
			if (!allowImplicitConversion) | 
						|
			{ | 
						|
				if (expr is NullReferenceExpression && rr.Type.Kind != TypeKind.Null) | 
						|
				{ | 
						|
					expr = new CastExpression(ConvertType(rr.Type), expr); | 
						|
				} | 
						|
				else if (rr.Type.IsCSharpSmallIntegerType()) | 
						|
				{ | 
						|
					expr = new CastExpression(new PrimitiveType(KnownTypeReference.GetCSharpNameByTypeCode(rr.Type.GetDefinition().KnownTypeCode)), expr); | 
						|
					// Note: no unchecked annotation necessary, because the constant was folded to be in-range | 
						|
				} | 
						|
				else if (rr.Type.IsCSharpNativeIntegerType()) | 
						|
				{ | 
						|
					expr = new CastExpression(new PrimitiveType(rr.Type.Name), expr); | 
						|
					// Note: no unchecked annotation necessary, because the rr wouldn't be a constant if the value wasn't in-range on 32bit | 
						|
				} | 
						|
			} | 
						|
			var exprRR = expr.Annotation<ResolveResult>(); | 
						|
			if (exprRR == null) | 
						|
			{ | 
						|
				exprRR = rr; | 
						|
				expr.AddAnnotation(rr); | 
						|
			} | 
						|
			return new ExpressionWithResolveResult(expr, exprRR); | 
						|
		} | 
						|
 | 
						|
		public ExpressionWithResolveResult ConvertConstantValue(ResolveResult rr, | 
						|
			bool allowImplicitConversion = false, bool displayAsHex = false) | 
						|
		{ | 
						|
			astBuilder.PrintIntegralValuesAsHex = displayAsHex; | 
						|
			try | 
						|
			{ | 
						|
				return ConvertConstantValue(rr, allowImplicitConversion); | 
						|
			} | 
						|
			finally | 
						|
			{ | 
						|
				astBuilder.PrintIntegralValuesAsHex = false; | 
						|
			} | 
						|
		} | 
						|
 | 
						|
		public TranslatedExpression Translate(ILInstruction inst, IType typeHint = null) | 
						|
		{ | 
						|
			Debug.Assert(inst != null); | 
						|
			cancellationToken.ThrowIfCancellationRequested(); | 
						|
			TranslationContext context = new TranslationContext { | 
						|
				TypeHint = typeHint ?? SpecialType.UnknownType | 
						|
			}; | 
						|
			var cexpr = inst.AcceptVisitor(this, context); | 
						|
#if DEBUG | 
						|
			if (inst.ResultType != StackType.Void && cexpr.Type.Kind != TypeKind.Unknown && inst.ResultType != StackType.Unknown && cexpr.Type.Kind != TypeKind.None) | 
						|
			{ | 
						|
				// Validate the Translate post-condition (documented at beginning of this file): | 
						|
				if (inst.ResultType.IsIntegerType()) | 
						|
				{ | 
						|
					Debug.Assert(cexpr.Type.GetStackType().IsIntegerType(), "IL instructions of integer type must convert into C# expressions of integer type"); | 
						|
					Debug.Assert(cexpr.Type.GetSign() != Sign.None, "Must have a sign specified for zero/sign-extension"); | 
						|
				} | 
						|
				else if (inst is ILiftableInstruction liftable && liftable.IsLifted) | 
						|
				{ | 
						|
					if (liftable.UnderlyingResultType != StackType.Unknown) | 
						|
					{ | 
						|
						Debug.Assert(NullableType.IsNullable(cexpr.Type)); | 
						|
						IType underlying = NullableType.GetUnderlyingType(cexpr.Type); | 
						|
						if (liftable.UnderlyingResultType.IsIntegerType()) | 
						|
						{ | 
						|
							Debug.Assert(underlying.GetStackType().IsIntegerType(), "IL instructions of integer type must convert into C# expressions of integer type"); | 
						|
							Debug.Assert(underlying.GetSign() != Sign.None, "Must have a sign specified for zero/sign-extension"); | 
						|
						} | 
						|
						else | 
						|
						{ | 
						|
							Debug.Assert(underlying.GetStackType() == liftable.UnderlyingResultType); | 
						|
						} | 
						|
					} | 
						|
				} | 
						|
				else if (inst.ResultType == StackType.Ref) | 
						|
				{ | 
						|
					Debug.Assert(cexpr.Type.GetStackType() == StackType.Ref || cexpr.Type.GetStackType().IsIntegerType()); | 
						|
				} | 
						|
				else | 
						|
				{ | 
						|
					Debug.Assert(cexpr.Type.GetStackType() == inst.ResultType); | 
						|
				} | 
						|
			} | 
						|
#endif | 
						|
			return cexpr; | 
						|
		} | 
						|
 | 
						|
		public TranslatedExpression TranslateCondition(ILInstruction condition, bool negate = false) | 
						|
		{ | 
						|
			Debug.Assert(condition.ResultType == StackType.I4); | 
						|
			var expr = Translate(condition, compilation.FindType(KnownTypeCode.Boolean)); | 
						|
			if (expr.Type.GetStackType().GetSize() > 4) | 
						|
			{ | 
						|
				expr = expr.ConvertTo(FindType(StackType.I4, expr.Type.GetSign()), this); | 
						|
			} | 
						|
			return expr.ConvertToBoolean(this, negate); | 
						|
		} | 
						|
 | 
						|
		internal ExpressionWithResolveResult ConvertVariable(ILVariable variable) | 
						|
		{ | 
						|
			Expression expr; | 
						|
			if (variable.Kind == VariableKind.Parameter && variable.Index < 0) | 
						|
				expr = new ThisReferenceExpression(); | 
						|
			else | 
						|
				expr = new IdentifierExpression(variable.Name); | 
						|
			if (variable.Type.Kind == TypeKind.ByReference) | 
						|
			{ | 
						|
				// When loading a by-ref parameter, use 'ref paramName'. | 
						|
				// We'll strip away the 'ref' when dereferencing. | 
						|
 | 
						|
				// Ensure that the IdentifierExpression itself also gets a resolve result, as that might | 
						|
				// get used after the 'ref' is stripped away: | 
						|
				var elementType = ((ByReferenceType)variable.Type).ElementType; | 
						|
				var elementRR = new ILVariableResolveResult(variable, elementType); | 
						|
				expr.WithRR(elementRR); | 
						|
 | 
						|
				expr = new DirectionExpression(FieldDirection.Ref, expr); | 
						|
				return expr.WithRR(new ByReferenceResolveResult(elementRR, ReferenceKind.Ref)); | 
						|
			} | 
						|
			else | 
						|
			{ | 
						|
				return expr.WithRR(new ILVariableResolveResult(variable, variable.Type)); | 
						|
			} | 
						|
		} | 
						|
 | 
						|
		internal bool HidesVariableWithName(string name) | 
						|
		{ | 
						|
			return HidesVariableWithName(currentFunction, name); | 
						|
		} | 
						|
 | 
						|
		internal static bool HidesVariableWithName(ILFunction currentFunction, string name) | 
						|
		{ | 
						|
			return currentFunction.Ancestors.OfType<ILFunction>().Any(HidesVariableOrNestedFunction); | 
						|
 | 
						|
			bool HidesVariableOrNestedFunction(ILFunction function) | 
						|
			{ | 
						|
				foreach (var v in function.Variables) | 
						|
				{ | 
						|
					if (v.Name == name) | 
						|
						return true; | 
						|
				} | 
						|
 | 
						|
				foreach (var f in function.LocalFunctions) | 
						|
				{ | 
						|
					if (f.Name == name) | 
						|
						return true; | 
						|
				} | 
						|
 | 
						|
				return false; | 
						|
			} | 
						|
		} | 
						|
 | 
						|
		internal ILFunction ResolveLocalFunction(IMethod method) | 
						|
		{ | 
						|
			Debug.Assert(method.IsLocalFunction); | 
						|
			method = (IMethod)((IMethod)method.MemberDefinition).ReducedFrom.MemberDefinition; | 
						|
			foreach (var parent in currentFunction.Ancestors.OfType<ILFunction>()) | 
						|
			{ | 
						|
				var definition = parent.LocalFunctions.FirstOrDefault(f => f.Method.MemberDefinition.Equals(method)); | 
						|
				if (definition != null) | 
						|
				{ | 
						|
					return definition; | 
						|
				} | 
						|
			} | 
						|
			return null; | 
						|
		} | 
						|
 | 
						|
		bool RequiresQualifier(IMember member, TranslatedExpression target) | 
						|
		{ | 
						|
			if (settings.AlwaysQualifyMemberReferences || HidesVariableWithName(member.Name)) | 
						|
				return true; | 
						|
			if (member.IsStatic) | 
						|
				return !IsCurrentOrContainingType(member.DeclaringTypeDefinition); | 
						|
			return !(target.Expression is ThisReferenceExpression || target.Expression is BaseReferenceExpression); | 
						|
		} | 
						|
 | 
						|
		ExpressionWithResolveResult ConvertField(IField field, ILInstruction targetInstruction = null) | 
						|
		{ | 
						|
			var target = TranslateTarget(targetInstruction, | 
						|
				nonVirtualInvocation: true, | 
						|
				memberStatic: field.IsStatic, | 
						|
				memberDeclaringType: field.DeclaringType); | 
						|
			bool requireTarget; | 
						|
			// If this is a reference to the backing field of an automatic property and we're going to transform automatic properties | 
						|
			// in PatternStatementTransform, then we have to do the "requires qualifier"-check based on the property instead of the field. | 
						|
			// It is easier to solve this special case here than in PatternStatementTransform, because here we perform all resolver checks. | 
						|
			// It feels a bit hacky, though. | 
						|
			if (settings.AutomaticProperties | 
						|
				&& PatternStatementTransform.IsBackingFieldOfAutomaticProperty(field, out var property) | 
						|
				&& decompilationContext.CurrentMember != property | 
						|
				&& (property.CanSet || settings.GetterOnlyAutomaticProperties)) | 
						|
			{ | 
						|
				requireTarget = RequiresQualifier(property, target); | 
						|
			} | 
						|
			else | 
						|
			{ | 
						|
				requireTarget = RequiresQualifier(field, target); | 
						|
			} | 
						|
			bool targetCasted = false; | 
						|
			var targetResolveResult = requireTarget ? target.ResolveResult : null; | 
						|
 | 
						|
			bool IsAmbiguousAccess(out MemberResolveResult result) | 
						|
			{ | 
						|
				if (targetResolveResult == null) | 
						|
				{ | 
						|
					result = resolver.ResolveSimpleName(field.Name, EmptyList<IType>.Instance, isInvocationTarget: false) as MemberResolveResult; | 
						|
				} | 
						|
				else | 
						|
				{ | 
						|
					var lookup = new MemberLookup(resolver.CurrentTypeDefinition, resolver.CurrentTypeDefinition.ParentModule); | 
						|
					result = lookup.Lookup(target.ResolveResult, field.Name, EmptyList<IType>.Instance, isInvocation: false) as MemberResolveResult; | 
						|
				} | 
						|
				return result == null || result.IsError || !result.Member.Equals(field, NormalizeTypeVisitor.TypeErasure); | 
						|
			} | 
						|
 | 
						|
			MemberResolveResult mrr; | 
						|
			while (IsAmbiguousAccess(out mrr)) | 
						|
			{ | 
						|
				if (!requireTarget) | 
						|
				{ | 
						|
					requireTarget = true; | 
						|
					targetResolveResult = target.ResolveResult; | 
						|
				} | 
						|
				else if (!targetCasted) | 
						|
				{ | 
						|
					targetCasted = true; | 
						|
					target = target.ConvertTo(field.DeclaringType, this); | 
						|
					targetResolveResult = target.ResolveResult; | 
						|
				} | 
						|
				else | 
						|
				{ | 
						|
					// the field reference is still ambiguous, however, mrr might refer to a different member, | 
						|
					// e.g., in the case of auto events, their backing fields have the same name. | 
						|
					// "this.Event" is ambiguous, but should refer to the field, not the event. | 
						|
					mrr = null; | 
						|
					break; | 
						|
				} | 
						|
			} | 
						|
 | 
						|
			if (mrr == null) | 
						|
			{ | 
						|
				mrr = new MemberResolveResult(target.ResolveResult, field); | 
						|
			} | 
						|
 | 
						|
			var expr = requireTarget | 
						|
				? new MemberReferenceExpression(target, field.Name).WithRR(mrr) | 
						|
				: new IdentifierExpression(field.Name).WithRR(mrr); | 
						|
 | 
						|
			if (field.Type.Kind == TypeKind.ByReference) | 
						|
			{ | 
						|
				expr = new DirectionExpression(FieldDirection.Ref, expr) | 
						|
					.WithRR(new ByReferenceResolveResult(mrr, ReferenceKind.Ref)); | 
						|
			} | 
						|
 | 
						|
			return expr; | 
						|
		} | 
						|
 | 
						|
		TranslatedExpression IsType(IsInst inst) | 
						|
		{ | 
						|
			var arg = Translate(inst.Argument); | 
						|
			arg = UnwrapBoxingConversion(arg); | 
						|
			return new IsExpression(arg.Expression, ConvertType(inst.Type)) | 
						|
				.WithILInstruction(inst) | 
						|
				.WithRR(new TypeIsResolveResult(arg.ResolveResult, inst.Type, compilation.FindType(TypeCode.Boolean))); | 
						|
		} | 
						|
 | 
						|
		protected internal override TranslatedExpression VisitIsInst(IsInst inst, TranslationContext context) | 
						|
		{ | 
						|
			var arg = Translate(inst.Argument); | 
						|
			if (inst.Type.IsReferenceType != true) | 
						|
			{ | 
						|
				// isinst with a value type results in an expression of "boxed value type", | 
						|
				// which is not supported in C#. | 
						|
				// It's also not supported for unconstrained generic types. | 
						|
				// Note that several other instructions special-case isinst arguments: | 
						|
				//  unbox.any T(isinst T(expr)) ==> "expr as T" for nullable value types and class-constrained generic types | 
						|
				//  comp(isinst T(expr) != null) ==> "expr is T" | 
						|
				//  on block level (StatementBuilder.VisitIsInst) => "expr is T" | 
						|
				if (SemanticHelper.IsPure(inst.Argument.Flags)) | 
						|
				{ | 
						|
					// We can emulate isinst using | 
						|
					//   expr is T ? expr : null | 
						|
					return new ConditionalExpression( | 
						|
						new IsExpression(arg, ConvertType(inst.Type)).WithILInstruction(inst), | 
						|
						arg.Expression.Clone(), | 
						|
						new NullReferenceExpression() | 
						|
					).WithoutILInstruction().WithRR(new ResolveResult(arg.Type)); | 
						|
				} | 
						|
				else | 
						|
				{ | 
						|
					return ErrorExpression("isinst with value type is only supported in some contexts"); | 
						|
				} | 
						|
			} | 
						|
			arg = UnwrapBoxingConversion(arg); | 
						|
			return new AsExpression(arg.Expression, ConvertType(inst.Type)) | 
						|
				.WithILInstruction(inst) | 
						|
				.WithRR(new ConversionResolveResult(inst.Type, arg.ResolveResult, Conversion.TryCast)); | 
						|
		} | 
						|
 | 
						|
		internal static TranslatedExpression UnwrapBoxingConversion(TranslatedExpression arg) | 
						|
		{ | 
						|
			if (arg.Expression is CastExpression cast | 
						|
					&& arg.Type.IsKnownType(KnownTypeCode.Object) | 
						|
					&& arg.ResolveResult is ConversionResolveResult crr | 
						|
					&& crr.Conversion.IsBoxingConversion) | 
						|
			{ | 
						|
				// When 'is' or 'as' is used with a value type or type parameter, | 
						|
				// the C# compiler implicitly boxes the input. | 
						|
				arg = arg.UnwrapChild(cast.Expression); | 
						|
			} | 
						|
 | 
						|
			return arg; | 
						|
		} | 
						|
 | 
						|
		protected internal override TranslatedExpression VisitNewObj(NewObj inst, TranslationContext context) | 
						|
		{ | 
						|
			var type = inst.Method.DeclaringType; | 
						|
			if (type.IsKnownType(KnownTypeCode.SpanOfT) || type.IsKnownType(KnownTypeCode.ReadOnlySpanOfT)) | 
						|
			{ | 
						|
				if (inst.Arguments.Count == 2 && inst.Arguments[0] is Block b && b.Kind == BlockKind.StackAllocInitializer) | 
						|
				{ | 
						|
					return TranslateStackAllocInitializer(b, type.TypeArguments[0]); | 
						|
				} | 
						|
			} | 
						|
			return new CallBuilder(this, typeSystem, settings).Build(inst); | 
						|
		} | 
						|
 | 
						|
		protected internal override TranslatedExpression VisitLdVirtDelegate(LdVirtDelegate inst, TranslationContext context) | 
						|
		{ | 
						|
			return new CallBuilder(this, typeSystem, settings).Build(inst); | 
						|
		} | 
						|
 | 
						|
		protected internal override TranslatedExpression VisitNewArr(NewArr inst, TranslationContext context) | 
						|
		{ | 
						|
			var dimensions = inst.Indices.Count; | 
						|
			var args = inst.Indices.Select(arg => TranslateArrayIndex(arg)).ToArray(); | 
						|
			var expr = new ArrayCreateExpression { Type = ConvertType(inst.Type) }; | 
						|
			if (expr.Type is ComposedType ct) | 
						|
			{ | 
						|
				// change "new (int[,])[10] to new int[10][,]" | 
						|
				ct.ArraySpecifiers.MoveTo(expr.AdditionalArraySpecifiers); | 
						|
			} | 
						|
			expr.Arguments.AddRange(args.Select(arg => arg.Expression)); | 
						|
			return expr.WithILInstruction(inst) | 
						|
				.WithRR(new ArrayCreateResolveResult(new ArrayType(compilation, inst.Type, dimensions), args.Select(a => a.ResolveResult).ToList(), Empty<ResolveResult>.Array)); | 
						|
		} | 
						|
 | 
						|
		protected internal override TranslatedExpression VisitLocAlloc(LocAlloc inst, TranslationContext context) | 
						|
		{ | 
						|
			return TranslateLocAlloc(inst, context.TypeHint, out var elementType) | 
						|
				.WithILInstruction(inst).WithRR(new ResolveResult(new PointerType(elementType))); | 
						|
		} | 
						|
 | 
						|
		protected internal override TranslatedExpression VisitLocAllocSpan(LocAllocSpan inst, TranslationContext context) | 
						|
		{ | 
						|
			return TranslateLocAllocSpan(inst, context.TypeHint, out _) | 
						|
				.WithILInstruction(inst).WithRR(new ResolveResult(inst.Type)); | 
						|
		} | 
						|
 | 
						|
		StackAllocExpression TranslateLocAllocSpan(LocAllocSpan inst, IType typeHint, out IType elementType) | 
						|
		{ | 
						|
			elementType = inst.Type.TypeArguments[0]; | 
						|
			TranslatedExpression countExpression = Translate(inst.Argument) | 
						|
				.ConvertTo(compilation.FindType(KnownTypeCode.Int32), this); | 
						|
			return new StackAllocExpression { | 
						|
				Type = ConvertType(elementType), | 
						|
				CountExpression = countExpression | 
						|
			}; | 
						|
		} | 
						|
 | 
						|
		StackAllocExpression TranslateLocAlloc(LocAlloc inst, IType typeHint, out IType elementType) | 
						|
		{ | 
						|
			TranslatedExpression countExpression; | 
						|
			PointerType pointerType; | 
						|
			if (inst.Argument.MatchBinaryNumericInstruction(BinaryNumericOperator.Mul, out var left, out var right) | 
						|
				&& right.UnwrapConv(ConversionKind.SignExtend).UnwrapConv(ConversionKind.ZeroExtend).MatchSizeOf(out elementType)) | 
						|
			{ | 
						|
				// Determine the element type from the sizeof | 
						|
				countExpression = Translate(left.UnwrapConv(ConversionKind.ZeroExtend)); | 
						|
				pointerType = new PointerType(elementType); | 
						|
			} | 
						|
			else | 
						|
			{ | 
						|
				// Determine the element type from the expected pointer type in this context | 
						|
				pointerType = typeHint as PointerType; | 
						|
				if (pointerType != null && GetPointerArithmeticOffset( | 
						|
						inst.Argument, Translate(inst.Argument), | 
						|
						pointerType.ElementType, checkForOverflow: true, | 
						|
						unwrapZeroExtension: true | 
						|
					) is TranslatedExpression offset) | 
						|
				{ | 
						|
					countExpression = offset; | 
						|
					elementType = pointerType.ElementType; | 
						|
				} | 
						|
				else | 
						|
				{ | 
						|
					elementType = compilation.FindType(KnownTypeCode.Byte); | 
						|
					pointerType = new PointerType(elementType); | 
						|
					countExpression = Translate(inst.Argument); | 
						|
				} | 
						|
			} | 
						|
			countExpression = countExpression.ConvertTo(compilation.FindType(KnownTypeCode.Int32), this); | 
						|
			return new StackAllocExpression { | 
						|
				Type = ConvertType(elementType), | 
						|
				CountExpression = countExpression | 
						|
			}; | 
						|
		} | 
						|
 | 
						|
		protected internal override TranslatedExpression VisitLdcI4(LdcI4 inst, TranslationContext context) | 
						|
		{ | 
						|
			ResolveResult rr; | 
						|
			if (context.TypeHint.GetSign() == Sign.Unsigned) | 
						|
			{ | 
						|
				rr = new ConstantResolveResult( | 
						|
					compilation.FindType(KnownTypeCode.UInt32), | 
						|
					unchecked((uint)inst.Value) | 
						|
				); | 
						|
			} | 
						|
			else | 
						|
			{ | 
						|
				rr = new ConstantResolveResult( | 
						|
					compilation.FindType(KnownTypeCode.Int32), | 
						|
					inst.Value | 
						|
				); | 
						|
			} | 
						|
			rr = AdjustConstantToType(rr, context.TypeHint); | 
						|
			return ConvertConstantValue( | 
						|
				rr, | 
						|
				allowImplicitConversion: true, | 
						|
				ShouldDisplayAsHex(inst.Value, rr.Type, inst.Parent) | 
						|
			).WithILInstruction(inst); | 
						|
		} | 
						|
 | 
						|
		protected internal override TranslatedExpression VisitLdcI8(LdcI8 inst, TranslationContext context) | 
						|
		{ | 
						|
			ResolveResult rr; | 
						|
			if (context.TypeHint.GetSign() == Sign.Unsigned) | 
						|
			{ | 
						|
				rr = new ConstantResolveResult( | 
						|
					compilation.FindType(KnownTypeCode.UInt64), | 
						|
					unchecked((ulong)inst.Value) | 
						|
				); | 
						|
			} | 
						|
			else | 
						|
			{ | 
						|
				rr = new ConstantResolveResult( | 
						|
					compilation.FindType(KnownTypeCode.Int64), | 
						|
					inst.Value | 
						|
				); | 
						|
			} | 
						|
			rr = AdjustConstantToType(rr, context.TypeHint); | 
						|
			return ConvertConstantValue( | 
						|
				rr, | 
						|
				allowImplicitConversion: true, | 
						|
				ShouldDisplayAsHex(inst.Value, rr.Type, inst.Parent) | 
						|
			).WithILInstruction(inst); | 
						|
		} | 
						|
 | 
						|
		private bool ShouldDisplayAsHex(long value, IType type, ILInstruction parent) | 
						|
		{ | 
						|
			if (parent is Conv conv) | 
						|
				parent = conv.Parent; | 
						|
			if (value >= 0 && value <= 9) | 
						|
				return false; | 
						|
			if (value < 0 && type.GetSign() == Sign.Signed) | 
						|
				return false; | 
						|
			switch (parent) | 
						|
			{ | 
						|
				case BinaryNumericInstruction bni: | 
						|
					if (bni.Operator == BinaryNumericOperator.BitAnd | 
						|
						|| bni.Operator == BinaryNumericOperator.BitOr | 
						|
						|| bni.Operator == BinaryNumericOperator.BitXor) | 
						|
						return true; | 
						|
					break; | 
						|
			} | 
						|
			return false; | 
						|
		} | 
						|
 | 
						|
		protected internal override TranslatedExpression VisitLdcF4(LdcF4 inst, TranslationContext context) | 
						|
		{ | 
						|
			var expr = astBuilder.ConvertConstantValue(compilation.FindType(KnownTypeCode.Single), inst.Value); | 
						|
			return new TranslatedExpression(expr.WithILInstruction(inst)); | 
						|
		} | 
						|
 | 
						|
		protected internal override TranslatedExpression VisitLdcF8(LdcF8 inst, TranslationContext context) | 
						|
		{ | 
						|
			var expr = astBuilder.ConvertConstantValue(compilation.FindType(KnownTypeCode.Double), inst.Value); | 
						|
			return new TranslatedExpression(expr.WithILInstruction(inst)); | 
						|
		} | 
						|
 | 
						|
		protected internal override TranslatedExpression VisitLdcDecimal(LdcDecimal inst, TranslationContext context) | 
						|
		{ | 
						|
			var expr = astBuilder.ConvertConstantValue(compilation.FindType(KnownTypeCode.Decimal), inst.Value); | 
						|
			return new TranslatedExpression(expr.WithILInstruction(inst)); | 
						|
		} | 
						|
 | 
						|
		protected internal override TranslatedExpression VisitLdStr(LdStr inst, TranslationContext context) | 
						|
		{ | 
						|
			return new PrimitiveExpression(inst.Value) | 
						|
				.WithILInstruction(inst) | 
						|
				.WithRR(new ConstantResolveResult(compilation.FindType(KnownTypeCode.String), inst.Value)); | 
						|
		} | 
						|
 | 
						|
		protected internal override TranslatedExpression VisitLdNull(LdNull inst, TranslationContext context) | 
						|
		{ | 
						|
			return GetDefaultValueExpression(SpecialType.NullType).WithILInstruction(inst); | 
						|
		} | 
						|
 | 
						|
		protected internal override TranslatedExpression VisitDefaultValue(DefaultValue inst, TranslationContext context) | 
						|
		{ | 
						|
			return GetDefaultValueExpression(inst.Type).WithILInstruction(inst); | 
						|
		} | 
						|
 | 
						|
		internal ExpressionWithResolveResult GetDefaultValueExpression(IType type) | 
						|
		{ | 
						|
			Expression expr; | 
						|
			IType constantType; | 
						|
			object constantValue; | 
						|
			if (type.IsReferenceType == true || type.IsKnownType(KnownTypeCode.NullableOfT)) | 
						|
			{ | 
						|
				expr = new NullReferenceExpression(); | 
						|
				constantType = SpecialType.NullType; | 
						|
				constantValue = null; | 
						|
			} | 
						|
			else | 
						|
			{ | 
						|
				expr = new DefaultValueExpression(ConvertType(type)); | 
						|
				constantType = type; | 
						|
				constantValue = CSharpResolver.GetDefaultValue(type); | 
						|
			} | 
						|
			return expr.WithRR(new ConstantResolveResult(constantType, constantValue)); | 
						|
		} | 
						|
 | 
						|
		protected internal override TranslatedExpression VisitSizeOf(SizeOf inst, TranslationContext context) | 
						|
		{ | 
						|
			if (inst.Type.IsUnmanagedType(allowGenerics: settings.IntroduceUnmanagedConstraint)) | 
						|
			{ | 
						|
				return new SizeOfExpression(ConvertType(inst.Type)) | 
						|
					.WithILInstruction(inst) | 
						|
					.WithRR(new SizeOfResolveResult(compilation.FindType(KnownTypeCode.Int32), inst.Type, null)); | 
						|
			} | 
						|
			else | 
						|
			{ | 
						|
				return CallUnsafeIntrinsic( | 
						|
					name: "SizeOf", | 
						|
					arguments: Array.Empty<Expression>(), | 
						|
					returnType: compilation.FindType(KnownTypeCode.Int32), | 
						|
					inst: inst, | 
						|
					typeArguments: new[] { inst.Type } | 
						|
				); | 
						|
			} | 
						|
		} | 
						|
 | 
						|
		protected internal override TranslatedExpression VisitLdTypeToken(LdTypeToken inst, TranslationContext context) | 
						|
		{ | 
						|
			var typeofExpr = new TypeOfExpression(ConvertType(inst.Type)) | 
						|
				.WithRR(new TypeOfResolveResult(compilation.FindType(KnownTypeCode.Type), inst.Type)); | 
						|
			return new MemberReferenceExpression(typeofExpr, "TypeHandle") | 
						|
				.WithILInstruction(inst) | 
						|
				.WithRR(new TypeOfResolveResult(compilation.FindType(new TopLevelTypeName("System", "RuntimeTypeHandle")), inst.Type)); | 
						|
		} | 
						|
 | 
						|
		protected internal override TranslatedExpression VisitBitNot(BitNot inst, TranslationContext context) | 
						|
		{ | 
						|
			var argument = Translate(inst.Argument); | 
						|
			var argUType = NullableType.GetUnderlyingType(argument.Type); | 
						|
 | 
						|
			if (argUType.GetStackType().GetSize() < inst.UnderlyingResultType.GetSize() | 
						|
				|| argUType.Kind == TypeKind.Enum && argUType.IsSmallIntegerType() | 
						|
				|| (argUType.GetStackType() == StackType.I && !argUType.IsCSharpNativeIntegerType()) | 
						|
				|| argUType.IsKnownType(KnownTypeCode.Boolean) | 
						|
				|| argUType.IsKnownType(KnownTypeCode.Char)) | 
						|
			{ | 
						|
				// Argument is undersized (even after implicit integral promotion to I4) | 
						|
				// -> we need to perform sign/zero-extension before the BitNot. | 
						|
				// Same if the argument is an enum based on a small integer type | 
						|
				// (those don't undergo numeric promotion in C# the way non-enum small integer types do). | 
						|
				// Same if the type is one that does not support ~ (IntPtr, bool and char). | 
						|
				Sign sign = context.TypeHint.GetSign(); | 
						|
				if (sign == Sign.None) | 
						|
				{ | 
						|
					sign = argUType.GetSign(); | 
						|
				} | 
						|
				IType targetType = FindArithmeticType(inst.UnderlyingResultType, sign); | 
						|
				if (inst.IsLifted) | 
						|
				{ | 
						|
					targetType = NullableType.Create(compilation, targetType); | 
						|
				} | 
						|
				argument = argument.ConvertTo(targetType, this); | 
						|
			} | 
						|
 | 
						|
			return new UnaryOperatorExpression(UnaryOperatorType.BitNot, argument) | 
						|
				.WithRR(resolver.ResolveUnaryOperator(UnaryOperatorType.BitNot, argument.ResolveResult)) | 
						|
				.WithILInstruction(inst); | 
						|
		} | 
						|
 | 
						|
		internal ExpressionWithResolveResult LogicNot(TranslatedExpression expr) | 
						|
		{ | 
						|
			// "!expr" implicitly converts to bool so we can remove the cast; | 
						|
			// but only if doing so wouldn't cause us to call a user-defined "operator !" | 
						|
			expr = expr.UnwrapImplicitBoolConversion(type => !type.GetMethods(m => m.IsOperator && m.Name == "op_LogicalNot").Any()); | 
						|
			return new UnaryOperatorExpression(UnaryOperatorType.Not, expr.Expression) | 
						|
				.WithRR(new OperatorResolveResult(compilation.FindType(KnownTypeCode.Boolean), ExpressionType.Not, expr.ResolveResult)); | 
						|
		} | 
						|
 | 
						|
		readonly HashSet<ILVariable> loadedVariablesSet = new HashSet<ILVariable>(); | 
						|
 | 
						|
		protected internal override TranslatedExpression VisitLdLoc(LdLoc inst, TranslationContext context) | 
						|
		{ | 
						|
			if (inst.Variable.Kind == VariableKind.StackSlot && inst.Variable.IsSingleDefinition) | 
						|
			{ | 
						|
				loadedVariablesSet.Add(inst.Variable); | 
						|
			} | 
						|
			return ConvertVariable(inst.Variable).WithILInstruction(inst); | 
						|
		} | 
						|
 | 
						|
		protected internal override TranslatedExpression VisitLdLoca(LdLoca inst, TranslationContext context) | 
						|
		{ | 
						|
			var expr = ConvertVariable(inst.Variable).WithILInstruction(inst); | 
						|
			// Note that we put the instruction on the IdentifierExpression instead of the DirectionExpression, | 
						|
			// because the DirectionExpression might get removed by dereferencing instructions such as LdObj | 
						|
			return new DirectionExpression(FieldDirection.Ref, expr.Expression) | 
						|
				.WithoutILInstruction() | 
						|
				.WithRR(new ByReferenceResolveResult(expr.ResolveResult, ReferenceKind.Ref)); | 
						|
		} | 
						|
 | 
						|
		protected internal override TranslatedExpression VisitStLoc(StLoc inst, TranslationContext context) | 
						|
		{ | 
						|
			var translatedValue = Translate(inst.Value, typeHint: inst.Variable.Type); | 
						|
			if (inst.Variable.Kind == VariableKind.StackSlot && !loadedVariablesSet.Contains(inst.Variable)) | 
						|
			{ | 
						|
				// Stack slots in the ILAst have inaccurate types (e.g. System.Object for StackType.O) | 
						|
				// so we should replace them with more accurate types where possible: | 
						|
				if (CanUseTypeForStackSlot(inst.Variable, translatedValue.Type) | 
						|
						&& inst.Variable.StackType == translatedValue.Type.GetStackType() | 
						|
						&& translatedValue.Type.Kind != TypeKind.Null) | 
						|
				{ | 
						|
					inst.Variable.Type = translatedValue.Type; | 
						|
				} | 
						|
				else if (inst.Value.MatchDefaultValue(out var type) && IsOtherValueType(type)) | 
						|
				{ | 
						|
					inst.Variable.Type = type; | 
						|
				} | 
						|
			} | 
						|
			var lhs = ConvertVariable(inst.Variable).WithoutILInstruction(); | 
						|
			if (lhs.Expression is DirectionExpression dirExpr && lhs.ResolveResult is ByReferenceResolveResult lhsRefRR) | 
						|
			{ | 
						|
				// ref (re-)assignment, emit "ref (a = ref b)". | 
						|
				lhs = lhs.UnwrapChild(dirExpr.Expression); | 
						|
				translatedValue = translatedValue.ConvertTo(lhsRefRR.Type, this, allowImplicitConversion: true); | 
						|
				var assign = new AssignmentExpression(lhs.Expression, translatedValue.Expression) | 
						|
					.WithRR(new OperatorResolveResult(lhs.Type, ExpressionType.Assign, lhsRefRR, translatedValue.ResolveResult)); | 
						|
				return new DirectionExpression(FieldDirection.Ref, assign) | 
						|
					.WithoutILInstruction().WithRR(lhsRefRR); | 
						|
			} | 
						|
			else | 
						|
			{ | 
						|
				return Assignment(lhs, translatedValue).WithILInstruction(inst); | 
						|
			} | 
						|
 | 
						|
			bool CanUseTypeForStackSlot(ILVariable v, IType type) | 
						|
			{ | 
						|
				return v.IsSingleDefinition | 
						|
					|| IsOtherValueType(type) | 
						|
					|| v.StackType == StackType.Ref | 
						|
					|| AllStoresUseConsistentType(v.StoreInstructions, type); | 
						|
			} | 
						|
 | 
						|
			bool IsOtherValueType(IType type) | 
						|
			{ | 
						|
				return type.IsReferenceType == false && type.GetStackType() == StackType.O; | 
						|
			} | 
						|
 | 
						|
			bool AllStoresUseConsistentType(IReadOnlyList<IStoreInstruction> storeInstructions, IType expectedType) | 
						|
			{ | 
						|
				expectedType = expectedType.AcceptVisitor(NormalizeTypeVisitor.TypeErasure); | 
						|
				foreach (var store in storeInstructions) | 
						|
				{ | 
						|
					if (!(store is StLoc stloc)) | 
						|
						return false; | 
						|
					IType type = stloc.Value.InferType(compilation).AcceptVisitor(NormalizeTypeVisitor.TypeErasure); | 
						|
					if (!type.Equals(expectedType)) | 
						|
						return false; | 
						|
				} | 
						|
				return true; | 
						|
			} | 
						|
		} | 
						|
 | 
						|
		protected internal override TranslatedExpression VisitComp(Comp inst, TranslationContext context) | 
						|
		{ | 
						|
			if (inst.LiftingKind == ComparisonLiftingKind.ThreeValuedLogic) | 
						|
			{ | 
						|
				if (inst.Kind == ComparisonKind.Equality && inst.Right.MatchLdcI4(0)) | 
						|
				{ | 
						|
					// lifted logic.not | 
						|
					var targetType = NullableType.Create(compilation, compilation.FindType(KnownTypeCode.Boolean)); | 
						|
					var arg = Translate(inst.Left, targetType).ConvertTo(targetType, this); | 
						|
					return new UnaryOperatorExpression(UnaryOperatorType.Not, arg.Expression) | 
						|
						.WithRR(new OperatorResolveResult(targetType, ExpressionType.Not, arg.ResolveResult)) | 
						|
						.WithILInstruction(inst); | 
						|
				} | 
						|
				return ErrorExpression("Nullable comparisons with three-valued-logic not supported in C#"); | 
						|
			} | 
						|
			if (inst.InputType == StackType.Ref) | 
						|
			{ | 
						|
				// Reference comparison using Unsafe intrinsics | 
						|
				Debug.Assert(!inst.IsLifted); | 
						|
				(string methodName, bool negate) = inst.Kind switch { | 
						|
					ComparisonKind.Equality => ("AreSame", false), | 
						|
					ComparisonKind.Inequality => ("AreSame", true), | 
						|
					ComparisonKind.LessThan => ("IsAddressLessThan", false), | 
						|
					ComparisonKind.LessThanOrEqual => ("IsAddressGreaterThan", true), | 
						|
					ComparisonKind.GreaterThan => ("IsAddressGreaterThan", false), | 
						|
					ComparisonKind.GreaterThanOrEqual => ("IsAddressLessThan", true), | 
						|
					_ => throw new InvalidOperationException("Invalid ComparisonKind") | 
						|
				}; | 
						|
				var left = Translate(inst.Left); | 
						|
				var right = Translate(inst.Right); | 
						|
				if (left.Type.Kind != TypeKind.ByReference || !NormalizeTypeVisitor.TypeErasure.EquivalentTypes(left.Type, right.Type)) | 
						|
				{ | 
						|
					IType commonRefType = new ByReferenceType(compilation.FindType(KnownTypeCode.Byte)); | 
						|
					left = left.ConvertTo(commonRefType, this); | 
						|
					right = right.ConvertTo(commonRefType, this); | 
						|
				} | 
						|
				IType boolType = compilation.FindType(KnownTypeCode.Boolean); | 
						|
				TranslatedExpression expr = CallUnsafeIntrinsic( | 
						|
					name: methodName, | 
						|
					arguments: new Expression[] { left, right }, | 
						|
					returnType: boolType, | 
						|
					inst: inst | 
						|
				); | 
						|
				if (negate) | 
						|
				{ | 
						|
					expr = new UnaryOperatorExpression(UnaryOperatorType.Not, expr) | 
						|
						.WithoutILInstruction().WithRR(new ResolveResult(boolType)); | 
						|
				} | 
						|
				return expr; | 
						|
			} | 
						|
			if (inst.Kind.IsEqualityOrInequality()) | 
						|
			{ | 
						|
				var result = TranslateCeq(inst, out bool negateOutput); | 
						|
				if (negateOutput) | 
						|
					return LogicNot(result).WithILInstruction(inst); | 
						|
				else | 
						|
					return result; | 
						|
			} | 
						|
			else | 
						|
			{ | 
						|
				return TranslateComp(inst); | 
						|
			} | 
						|
		} | 
						|
 | 
						|
		/// <summary> | 
						|
		/// Translates the equality comparison between left and right. | 
						|
		/// </summary> | 
						|
		TranslatedExpression TranslateCeq(Comp inst, out bool negateOutput) | 
						|
		{ | 
						|
			Debug.Assert(inst.Kind.IsEqualityOrInequality()); | 
						|
			// Translate '(e as T) == null' to '!(e is T)'. | 
						|
			// This is necessary for correctness when T is a value type. | 
						|
			if (inst.Left.OpCode == OpCode.IsInst && inst.Right.OpCode == OpCode.LdNull) | 
						|
			{ | 
						|
				negateOutput = inst.Kind == ComparisonKind.Equality; | 
						|
				return IsType((IsInst)inst.Left); | 
						|
			} | 
						|
			else if (inst.Right.OpCode == OpCode.IsInst && inst.Left.OpCode == OpCode.LdNull) | 
						|
			{ | 
						|
				negateOutput = inst.Kind == ComparisonKind.Equality; | 
						|
				return IsType((IsInst)inst.Right); | 
						|
			} | 
						|
 | 
						|
			var left = Translate(inst.Left); | 
						|
			var right = Translate(inst.Right); | 
						|
 | 
						|
			// Remove redundant bool comparisons | 
						|
			if (left.Type.IsKnownType(KnownTypeCode.Boolean)) | 
						|
			{ | 
						|
				if (inst.Right.MatchLdcI4(0)) | 
						|
				{ | 
						|
					// 'b == 0' => '!b' | 
						|
					// 'b != 0' => 'b' | 
						|
					negateOutput = inst.Kind == ComparisonKind.Equality; | 
						|
					return left; | 
						|
				} | 
						|
				if (inst.Right.MatchLdcI4(1)) | 
						|
				{ | 
						|
					// 'b == 1' => 'b' | 
						|
					// 'b != 1' => '!b' | 
						|
					negateOutput = inst.Kind == ComparisonKind.Inequality; | 
						|
					return left; | 
						|
				} | 
						|
			} | 
						|
			else if (right.Type.IsKnownType(KnownTypeCode.Boolean)) | 
						|
			{ | 
						|
				if (inst.Left.MatchLdcI4(0)) | 
						|
				{ | 
						|
					// '0 == b' => '!b' | 
						|
					// '0 != b' => 'b' | 
						|
					negateOutput = inst.Kind == ComparisonKind.Equality; | 
						|
					return right; | 
						|
				} | 
						|
				if (inst.Left.MatchLdcI4(1)) | 
						|
				{ | 
						|
					// '1 == b' => 'b' | 
						|
					// '1 != b' => '!b' | 
						|
					negateOutput = inst.Kind == ComparisonKind.Inequality; | 
						|
					return right; | 
						|
				} | 
						|
			} | 
						|
			// Handle comparisons between unsafe pointers and null: | 
						|
			if (left.Type.Kind == TypeKind.Pointer && inst.Right.MatchLdcI(0)) | 
						|
			{ | 
						|
				negateOutput = false; | 
						|
				right = new NullReferenceExpression().WithRR(new ConstantResolveResult(SpecialType.NullType, null)) | 
						|
					.WithILInstruction(inst.Right); | 
						|
				return CreateBuiltinBinaryOperator(left, inst.Kind.ToBinaryOperatorType(), right) | 
						|
					.WithILInstruction(inst); | 
						|
			} | 
						|
			else if (right.Type.Kind == TypeKind.Pointer && inst.Left.MatchLdcI(0)) | 
						|
			{ | 
						|
				negateOutput = false; | 
						|
				left = new NullReferenceExpression().WithRR(new ConstantResolveResult(SpecialType.NullType, null)) | 
						|
					.WithILInstruction(inst.Left); | 
						|
				return CreateBuiltinBinaryOperator(left, inst.Kind.ToBinaryOperatorType(), right) | 
						|
					.WithILInstruction(inst); | 
						|
			} | 
						|
 | 
						|
			// Special case comparisons with enum and char literals | 
						|
			left = TryUniteEqualityOperandType(left, right); | 
						|
			right = TryUniteEqualityOperandType(right, left); | 
						|
 | 
						|
			if (IsSpecialCasedReferenceComparisonWithNull(left, right)) | 
						|
			{ | 
						|
				// When comparing a string/delegate with null, the C# compiler generates a reference comparison. | 
						|
				negateOutput = false; | 
						|
				return CreateBuiltinBinaryOperator(left, inst.Kind.ToBinaryOperatorType(), right) | 
						|
					.WithILInstruction(inst); | 
						|
			} | 
						|
 | 
						|
			OperatorResolveResult rr = resolver.ResolveBinaryOperator(inst.Kind.ToBinaryOperatorType(), left.ResolveResult, right.ResolveResult) as OperatorResolveResult; | 
						|
			if (rr == null || rr.IsError || rr.UserDefinedOperatorMethod != null | 
						|
				|| NullableType.GetUnderlyingType(rr.Operands[0].Type).GetStackType() != inst.InputType | 
						|
				|| !rr.Type.IsKnownType(KnownTypeCode.Boolean)) | 
						|
			{ | 
						|
				IType targetType; | 
						|
				if (inst.InputType == StackType.O) | 
						|
				{ | 
						|
					targetType = compilation.FindType(KnownTypeCode.Object); | 
						|
				} | 
						|
				else | 
						|
				{ | 
						|
					var leftUType = NullableType.GetUnderlyingType(left.Type); | 
						|
					var rightUType = NullableType.GetUnderlyingType(right.Type); | 
						|
					if (leftUType.GetStackType() == inst.InputType && !leftUType.IsSmallIntegerType()) | 
						|
					{ | 
						|
						targetType = leftUType; | 
						|
					} | 
						|
					else if (rightUType.GetStackType() == inst.InputType && !rightUType.IsSmallIntegerType()) | 
						|
					{ | 
						|
						targetType = rightUType; | 
						|
					} | 
						|
					else | 
						|
					{ | 
						|
						targetType = FindType(inst.InputType, leftUType.GetSign()); | 
						|
					} | 
						|
				} | 
						|
				if (inst.IsLifted) | 
						|
				{ | 
						|
					targetType = NullableType.Create(compilation, targetType); | 
						|
				} | 
						|
				if (targetType.Equals(left.Type)) | 
						|
				{ | 
						|
					right = right.ConvertTo(targetType, this); | 
						|
				} | 
						|
				else | 
						|
				{ | 
						|
					left = left.ConvertTo(targetType, this); | 
						|
				} | 
						|
				rr = resolver.ResolveBinaryOperator(inst.Kind.ToBinaryOperatorType(), | 
						|
					left.ResolveResult, right.ResolveResult) as OperatorResolveResult; | 
						|
				if (rr == null || rr.IsError || rr.UserDefinedOperatorMethod != null | 
						|
					|| NullableType.GetUnderlyingType(rr.Operands[0].Type).GetStackType() != inst.InputType | 
						|
					|| !rr.Type.IsKnownType(KnownTypeCode.Boolean)) | 
						|
				{ | 
						|
					// If converting one input wasn't sufficient, convert both: | 
						|
					left = left.ConvertTo(targetType, this); | 
						|
					right = right.ConvertTo(targetType, this); | 
						|
					rr = new OperatorResolveResult( | 
						|
						compilation.FindType(KnownTypeCode.Boolean), | 
						|
						BinaryOperatorExpression.GetLinqNodeType(inst.Kind.ToBinaryOperatorType(), false), | 
						|
						left.ResolveResult, right.ResolveResult); | 
						|
				} | 
						|
			} | 
						|
			negateOutput = false; | 
						|
			return new BinaryOperatorExpression(left.Expression, inst.Kind.ToBinaryOperatorType(), right.Expression) | 
						|
				.WithILInstruction(inst) | 
						|
				.WithRR(rr); | 
						|
		} | 
						|
 | 
						|
		TranslatedExpression TryUniteEqualityOperandType(TranslatedExpression left, TranslatedExpression right) | 
						|
		{ | 
						|
			// Special case for enum flag check "(enum & EnumType.SomeValue) == 0" | 
						|
			// so that the const 0 value is printed as 0 integer and not as enum type, e.g. EnumType.None | 
						|
			if (left.ResolveResult.IsCompileTimeConstant && | 
						|
				left.ResolveResult.Type.IsCSharpPrimitiveIntegerType() && | 
						|
				(left.ResolveResult.ConstantValue as int?) == 0 && | 
						|
				NullableType.GetUnderlyingType(right.Type).Kind == TypeKind.Enum && | 
						|
				right.Expression is BinaryOperatorExpression binaryExpr && | 
						|
				binaryExpr.Operator == BinaryOperatorType.BitwiseAnd) | 
						|
			{ | 
						|
				return AdjustConstantExpressionToType(left, compilation.FindType(KnownTypeCode.Int32)); | 
						|
			} | 
						|
			else | 
						|
				return AdjustConstantExpressionToType(left, right.Type); | 
						|
		} | 
						|
 | 
						|
		bool IsSpecialCasedReferenceComparisonWithNull(TranslatedExpression lhs, TranslatedExpression rhs) | 
						|
		{ | 
						|
			if (lhs.Type.Kind == TypeKind.Null) | 
						|
				ExtensionMethods.Swap(ref lhs, ref rhs); | 
						|
			return rhs.Type.Kind == TypeKind.Null | 
						|
				&& (lhs.Type.Kind == TypeKind.Delegate || lhs.Type.IsKnownType(KnownTypeCode.String)) | 
						|
				&& lhs.Type.GetDefinition() != decompilationContext.CurrentTypeDefinition; | 
						|
		} | 
						|
 | 
						|
		ExpressionWithResolveResult CreateBuiltinBinaryOperator( | 
						|
			TranslatedExpression left, BinaryOperatorType type, TranslatedExpression right, | 
						|
			bool checkForOverflow = false) | 
						|
		{ | 
						|
			return new BinaryOperatorExpression(left.Expression, type, right.Expression) | 
						|
			.WithRR(new OperatorResolveResult( | 
						|
				compilation.FindType(KnownTypeCode.Boolean), | 
						|
				BinaryOperatorExpression.GetLinqNodeType(type, checkForOverflow), | 
						|
				left.ResolveResult, right.ResolveResult)); | 
						|
		} | 
						|
 | 
						|
		/// <summary> | 
						|
		/// Handle Comp instruction, operators other than equality/inequality. | 
						|
		/// </summary> | 
						|
		TranslatedExpression TranslateComp(Comp inst) | 
						|
		{ | 
						|
			var op = inst.Kind.ToBinaryOperatorType(); | 
						|
			var left = Translate(inst.Left); | 
						|
			var right = Translate(inst.Right); | 
						|
 | 
						|
			if (left.Type.Kind == TypeKind.Pointer && right.Type.Kind == TypeKind.Pointer) | 
						|
			{ | 
						|
				return CreateBuiltinBinaryOperator(left, op, right) | 
						|
					.WithILInstruction(inst); | 
						|
			} | 
						|
 | 
						|
			left = PrepareArithmeticArgument(left, inst.InputType, inst.Sign, inst.IsLifted); | 
						|
			right = PrepareArithmeticArgument(right, inst.InputType, inst.Sign, inst.IsLifted); | 
						|
 | 
						|
			// Special case comparisons with enum and char literals | 
						|
			left = AdjustConstantExpressionToType(left, right.Type); | 
						|
			right = AdjustConstantExpressionToType(right, left.Type); | 
						|
 | 
						|
			// attempt comparison without any additional casts | 
						|
			var rr = resolver.ResolveBinaryOperator(inst.Kind.ToBinaryOperatorType(), left.ResolveResult, right.ResolveResult) | 
						|
				as OperatorResolveResult; | 
						|
			if (rr != null && !rr.IsError) | 
						|
			{ | 
						|
				IType compUType = NullableType.GetUnderlyingType(rr.Operands[0].Type); | 
						|
				if (compUType.GetSign() == inst.Sign && compUType.GetStackType() == inst.InputType) | 
						|
				{ | 
						|
					return new BinaryOperatorExpression(left.Expression, op, right.Expression) | 
						|
						.WithILInstruction(inst) | 
						|
						.WithRR(rr); | 
						|
				} | 
						|
			} | 
						|
 | 
						|
			if (inst.InputType.IsIntegerType()) | 
						|
			{ | 
						|
				// Ensure the inputs have the correct sign: | 
						|
				IType inputType = FindArithmeticType(inst.InputType, inst.Sign); | 
						|
				if (inst.IsLifted) | 
						|
				{ | 
						|
					inputType = NullableType.Create(compilation, inputType); | 
						|
				} | 
						|
				left = left.ConvertTo(inputType, this); | 
						|
				right = right.ConvertTo(inputType, this); | 
						|
			} | 
						|
			return new BinaryOperatorExpression(left.Expression, op, right.Expression) | 
						|
				.WithILInstruction(inst) | 
						|
				.WithRR(new OperatorResolveResult(compilation.FindType(TypeCode.Boolean), | 
						|
												  BinaryOperatorExpression.GetLinqNodeType(op, false), | 
						|
												  left.ResolveResult, right.ResolveResult)); | 
						|
		} | 
						|
 | 
						|
		protected internal override TranslatedExpression VisitThreeValuedBoolAnd(ThreeValuedBoolAnd inst, TranslationContext context) | 
						|
		{ | 
						|
			return HandleThreeValuedLogic(inst, BinaryOperatorType.BitwiseAnd, ExpressionType.And); | 
						|
		} | 
						|
 | 
						|
		protected internal override TranslatedExpression VisitThreeValuedBoolOr(ThreeValuedBoolOr inst, TranslationContext context) | 
						|
		{ | 
						|
			return HandleThreeValuedLogic(inst, BinaryOperatorType.BitwiseOr, ExpressionType.Or); | 
						|
		} | 
						|
 | 
						|
		TranslatedExpression HandleThreeValuedLogic(BinaryInstruction inst, BinaryOperatorType op, ExpressionType eop) | 
						|
		{ | 
						|
			var left = Translate(inst.Left); | 
						|
			var right = Translate(inst.Right); | 
						|
			IType boolType = compilation.FindType(KnownTypeCode.Boolean); | 
						|
			IType nullableBoolType = NullableType.Create(compilation, boolType); | 
						|
			if (NullableType.IsNullable(left.Type)) | 
						|
			{ | 
						|
				left = left.ConvertTo(nullableBoolType, this); | 
						|
				if (NullableType.IsNullable(right.Type)) | 
						|
				{ | 
						|
					right = right.ConvertTo(nullableBoolType, this); | 
						|
				} | 
						|
				else | 
						|
				{ | 
						|
					right = right.ConvertTo(boolType, this); | 
						|
				} | 
						|
			} | 
						|
			else | 
						|
			{ | 
						|
				left = left.ConvertTo(boolType, this); | 
						|
				right = right.ConvertTo(nullableBoolType, this); | 
						|
			} | 
						|
			return new BinaryOperatorExpression(left.Expression, op, right.Expression) | 
						|
				.WithRR(new OperatorResolveResult(nullableBoolType, eop, null, true, new[] { left.ResolveResult, right.ResolveResult })) | 
						|
				.WithILInstruction(inst); | 
						|
		} | 
						|
 | 
						|
		protected internal override TranslatedExpression VisitThrow(Throw inst, TranslationContext context) | 
						|
		{ | 
						|
			return new ThrowExpression(Translate(inst.Argument)) | 
						|
				.WithILInstruction(inst) | 
						|
				.WithRR(new ThrowResolveResult()); | 
						|
		} | 
						|
 | 
						|
		protected internal override TranslatedExpression VisitUserDefinedLogicOperator(UserDefinedLogicOperator inst, TranslationContext context) | 
						|
		{ | 
						|
			var left = Translate(inst.Left, inst.Method.Parameters[0].Type).ConvertTo(inst.Method.Parameters[0].Type, this); | 
						|
			var right = Translate(inst.Right, inst.Method.Parameters[1].Type).ConvertTo(inst.Method.Parameters[1].Type, this); | 
						|
			BinaryOperatorType op; | 
						|
			if (inst.Method.Name == "op_BitwiseAnd") | 
						|
			{ | 
						|
				op = BinaryOperatorType.ConditionalAnd; | 
						|
			} | 
						|
			else if (inst.Method.Name == "op_BitwiseOr") | 
						|
			{ | 
						|
				op = BinaryOperatorType.ConditionalOr; | 
						|
			} | 
						|
			else | 
						|
			{ | 
						|
				throw new InvalidOperationException("Invalid method name"); | 
						|
			} | 
						|
			return new BinaryOperatorExpression(left.Expression, op, right.Expression) | 
						|
				.WithRR(new InvocationResolveResult(null, inst.Method, new ResolveResult[] { left.ResolveResult, right.ResolveResult })) | 
						|
				.WithILInstruction(inst); | 
						|
		} | 
						|
 | 
						|
		ExpressionWithResolveResult Assignment(TranslatedExpression left, TranslatedExpression right) | 
						|
		{ | 
						|
			right = right.ConvertTo(left.Type, this, allowImplicitConversion: true); | 
						|
			return new AssignmentExpression(left.Expression, right.Expression) | 
						|
				.WithRR(new OperatorResolveResult(left.Type, ExpressionType.Assign, left.ResolveResult, right.ResolveResult)); | 
						|
		} | 
						|
 | 
						|
		protected internal override TranslatedExpression VisitBinaryNumericInstruction(BinaryNumericInstruction inst, TranslationContext context) | 
						|
		{ | 
						|
			switch (inst.Operator) | 
						|
			{ | 
						|
				case BinaryNumericOperator.Add: | 
						|
					return HandleBinaryNumeric(inst, BinaryOperatorType.Add, context); | 
						|
				case BinaryNumericOperator.Sub: | 
						|
					return HandleBinaryNumeric(inst, BinaryOperatorType.Subtract, context); | 
						|
				case BinaryNumericOperator.Mul: | 
						|
					return HandleBinaryNumeric(inst, BinaryOperatorType.Multiply, context); | 
						|
				case BinaryNumericOperator.Div: | 
						|
					return HandlePointerSubtraction(inst) | 
						|
						?? HandleBinaryNumeric(inst, BinaryOperatorType.Divide, context); | 
						|
				case BinaryNumericOperator.Rem: | 
						|
					return HandleBinaryNumeric(inst, BinaryOperatorType.Modulus, context); | 
						|
				case BinaryNumericOperator.BitAnd: | 
						|
					return HandleBinaryNumeric(inst, BinaryOperatorType.BitwiseAnd, context); | 
						|
				case BinaryNumericOperator.BitOr: | 
						|
					return HandleBinaryNumeric(inst, BinaryOperatorType.BitwiseOr, context); | 
						|
				case BinaryNumericOperator.BitXor: | 
						|
					return HandleBinaryNumeric(inst, BinaryOperatorType.ExclusiveOr, context); | 
						|
				case BinaryNumericOperator.ShiftLeft: | 
						|
					return HandleShift(inst, BinaryOperatorType.ShiftLeft); | 
						|
				case BinaryNumericOperator.ShiftRight: | 
						|
					return HandleShift(inst, BinaryOperatorType.ShiftRight); | 
						|
				default: | 
						|
					throw new ArgumentOutOfRangeException(); | 
						|
			} | 
						|
		} | 
						|
 | 
						|
		/// <summary> | 
						|
		/// Translates pointer arithmetic: | 
						|
		///   ptr + int | 
						|
		///   int + ptr | 
						|
		///   ptr - int | 
						|
		/// Returns null if 'inst' is not performing pointer arithmetic. | 
						|
		/// 'ptr - ptr' is not handled here, but in HandlePointerSubtraction()! | 
						|
		/// </summary> | 
						|
		TranslatedExpression? HandlePointerArithmetic(BinaryNumericInstruction inst, TranslatedExpression left, TranslatedExpression right) | 
						|
		{ | 
						|
			if (!(inst.Operator == BinaryNumericOperator.Add || inst.Operator == BinaryNumericOperator.Sub)) | 
						|
				return null; | 
						|
			if (inst.CheckForOverflow || inst.IsLifted) | 
						|
				return null; | 
						|
			if (!(inst.LeftInputType == StackType.I && inst.RightInputType == StackType.I)) | 
						|
				return null; | 
						|
			PointerType pointerType; | 
						|
			ILInstruction byteOffsetInst; | 
						|
			TranslatedExpression byteOffsetExpr; | 
						|
			if (left.Type.Kind == TypeKind.Pointer) | 
						|
			{ | 
						|
				byteOffsetInst = inst.Right; | 
						|
				byteOffsetExpr = right; | 
						|
				pointerType = (PointerType)left.Type; | 
						|
			} | 
						|
			else if (right.Type.Kind == TypeKind.Pointer) | 
						|
			{ | 
						|
				if (inst.Operator != BinaryNumericOperator.Add) | 
						|
					return null; | 
						|
				byteOffsetInst = inst.Left; | 
						|
				byteOffsetExpr = left; | 
						|
				pointerType = (PointerType)right.Type; | 
						|
			} | 
						|
			else | 
						|
			{ | 
						|
				return null; | 
						|
			} | 
						|
			TranslatedExpression offsetExpr = GetPointerArithmeticOffset(byteOffsetInst, byteOffsetExpr, pointerType.ElementType, inst.CheckForOverflow) | 
						|
				?? FallBackToBytePointer(); | 
						|
 | 
						|
			if (left.Type.Kind == TypeKind.Pointer) | 
						|
			{ | 
						|
				Debug.Assert(inst.Operator == BinaryNumericOperator.Add || inst.Operator == BinaryNumericOperator.Sub); | 
						|
				left = left.ConvertTo(pointerType, this); | 
						|
				right = offsetExpr; | 
						|
			} | 
						|
			else | 
						|
			{ | 
						|
				Debug.Assert(inst.Operator == BinaryNumericOperator.Add); | 
						|
				Debug.Assert(right.Type.Kind == TypeKind.Pointer); | 
						|
				left = offsetExpr; | 
						|
				right = right.ConvertTo(pointerType, this); | 
						|
			} | 
						|
			var operatorType = inst.Operator == BinaryNumericOperator.Add ? BinaryOperatorType.Add : BinaryOperatorType.Subtract; | 
						|
			return new BinaryOperatorExpression(left, operatorType, right) | 
						|
				.WithILInstruction(inst) | 
						|
				.WithRR(new OperatorResolveResult( | 
						|
					pointerType, BinaryOperatorExpression.GetLinqNodeType(operatorType, inst.CheckForOverflow), | 
						|
					left.ResolveResult, right.ResolveResult)); | 
						|
 | 
						|
			TranslatedExpression FallBackToBytePointer() | 
						|
			{ | 
						|
				pointerType = new PointerType(compilation.FindType(KnownTypeCode.Byte)); | 
						|
				return EnsureIntegerType(byteOffsetExpr); | 
						|
			} | 
						|
		} | 
						|
 | 
						|
		/// <summary> | 
						|
		/// Translates pointer arithmetic with managed pointers: | 
						|
		///   ref + int | 
						|
		///   int + ref | 
						|
		///   ref - int | 
						|
		///   ref - ref | 
						|
		/// </summary> | 
						|
		TranslatedExpression? HandleManagedPointerArithmetic(BinaryNumericInstruction inst, TranslatedExpression left, TranslatedExpression right) | 
						|
		{ | 
						|
			if (!(inst.Operator == BinaryNumericOperator.Add || inst.Operator == BinaryNumericOperator.Sub)) | 
						|
				return null; | 
						|
			if (inst.CheckForOverflow || inst.IsLifted) | 
						|
				return null; | 
						|
			if (inst.Operator == BinaryNumericOperator.Sub && inst.LeftInputType == StackType.Ref && inst.RightInputType == StackType.Ref) | 
						|
			{ | 
						|
				// ref - ref => i | 
						|
				return CallUnsafeIntrinsic("ByteOffset", new[] {  | 
						|
					// ByteOffset() expects the parameters the wrong way around, so order using named arguments | 
						|
					new NamedArgumentExpression("target", left.Expression), | 
						|
					new NamedArgumentExpression("origin", right.Expression) | 
						|
				}, compilation.FindType(KnownTypeCode.IntPtr), inst); | 
						|
			} | 
						|
			if (inst.LeftInputType == StackType.Ref && inst.RightInputType.IsIntegerType()) | 
						|
			{ | 
						|
				// ref [+-] int | 
						|
				var brt = left.Type as ByReferenceType; | 
						|
				if (brt == null) | 
						|
				{ | 
						|
					brt = GetReferenceType(left.Type); | 
						|
					left = left.ConvertTo(brt, this); | 
						|
				} | 
						|
				string name = (inst.Operator == BinaryNumericOperator.Sub ? "Subtract" : "Add"); | 
						|
				ILInstruction offsetInst = PointerArithmeticOffset.Detect(inst.Right, brt?.ElementType, inst.CheckForOverflow); | 
						|
				if (offsetInst != null) | 
						|
				{ | 
						|
					if (settings.FixedBuffers && inst.Operator == BinaryNumericOperator.Add && inst.Left is LdFlda ldFlda | 
						|
						&& ldFlda.Target is LdFlda nestedLdFlda && CSharpDecompiler.IsFixedField(nestedLdFlda.Field, out var elementType, out _)) | 
						|
					{ | 
						|
						Expression fieldAccess = ConvertField(nestedLdFlda.Field, nestedLdFlda.Target); | 
						|
						var mrr = (MemberResolveResult)fieldAccess.GetResolveResult(); | 
						|
						fieldAccess.RemoveAnnotations<ResolveResult>(); | 
						|
						var result = fieldAccess.WithRR(new MemberResolveResult(mrr.TargetResult, mrr.Member, new PointerType(elementType))) | 
						|
							.WithILInstruction(inst); | 
						|
						right = TranslateArrayIndex(offsetInst); | 
						|
						TranslatedExpression expr = new IndexerExpression(result.Expression, right.Expression) | 
						|
							.WithILInstruction(inst) | 
						|
							.WithRR(new ResolveResult(elementType)); | 
						|
						return new DirectionExpression(FieldDirection.Ref, expr) | 
						|
							.WithoutILInstruction().WithRR(new ByReferenceResolveResult(expr.ResolveResult, ReferenceKind.Ref)); | 
						|
					} | 
						|
					right = Translate(offsetInst); | 
						|
					right = ConvertArrayIndex(right, inst.RightInputType, allowIntPtr: true); | 
						|
					return CallUnsafeIntrinsic(name, new[] { left.Expression, right.Expression }, brt, inst); | 
						|
				} | 
						|
				else | 
						|
				{ | 
						|
					right = ConvertArrayIndex(right, inst.RightInputType, allowIntPtr: true); | 
						|
					return CallUnsafeIntrinsic(name + "ByteOffset", new[] { left.Expression, right.Expression }, brt, inst); | 
						|
				} | 
						|
			} | 
						|
 | 
						|
			if (inst.LeftInputType == StackType.I && inst.RightInputType == StackType.Ref | 
						|
				&& inst.Operator == BinaryNumericOperator.Add) | 
						|
			{ | 
						|
				// int + ref | 
						|
				var brt = right.Type as ByReferenceType; | 
						|
				if (brt == null) | 
						|
				{ | 
						|
					brt = GetReferenceType(right.Type); | 
						|
					right = right.ConvertTo(brt, this); | 
						|
				} | 
						|
				ILInstruction offsetInst = PointerArithmeticOffset.Detect(inst.Left, brt.ElementType, inst.CheckForOverflow); | 
						|
				if (offsetInst != null) | 
						|
				{ | 
						|
					left = Translate(offsetInst); | 
						|
					left = ConvertArrayIndex(left, inst.LeftInputType, allowIntPtr: true); | 
						|
					return CallUnsafeIntrinsic("Add", new[] { | 
						|
						new NamedArgumentExpression("elementOffset", left), | 
						|
						new NamedArgumentExpression("source", right) | 
						|
					}, brt, inst); | 
						|
				} | 
						|
				else | 
						|
				{ | 
						|
					left = ConvertArrayIndex(left, inst.LeftInputType, allowIntPtr: true); | 
						|
					return CallUnsafeIntrinsic("AddByteOffset", new[] { | 
						|
						new NamedArgumentExpression("byteOffset", left.Expression), | 
						|
						new NamedArgumentExpression("source", right) | 
						|
					}, brt, inst); | 
						|
				} | 
						|
			} | 
						|
			return null; | 
						|
 | 
						|
			ByReferenceType GetReferenceType(IType type) | 
						|
			{ | 
						|
				if (type is PointerType pt) | 
						|
				{ | 
						|
					return new ByReferenceType(pt.ElementType); | 
						|
				} | 
						|
				else | 
						|
				{ | 
						|
					return new ByReferenceType(compilation.FindType(KnownTypeCode.Byte)); | 
						|
				} | 
						|
			} | 
						|
		} | 
						|
 | 
						|
		internal TranslatedExpression CallUnsafeIntrinsic(string name, Expression[] arguments, IType returnType, ILInstruction inst = null, IEnumerable<IType> typeArguments = null) | 
						|
		{ | 
						|
			var target = new MemberReferenceExpression { | 
						|
				Target = new TypeReferenceExpression(astBuilder.ConvertType(compilation.FindType(KnownTypeCode.Unsafe))), | 
						|
				MemberName = name | 
						|
			}; | 
						|
			if (typeArguments != null) | 
						|
			{ | 
						|
				target.TypeArguments.AddRange(typeArguments.Select(astBuilder.ConvertType)); | 
						|
			} | 
						|
			var invocationExpr = new InvocationExpression(target, arguments); | 
						|
			var invocation = inst != null ? invocationExpr.WithILInstruction(inst) : invocationExpr.WithoutILInstruction(); | 
						|
			if (returnType is ByReferenceType brt) | 
						|
			{ | 
						|
				return WrapInRef(invocation.WithRR(new ResolveResult(brt.ElementType)), brt); | 
						|
			} | 
						|
			else | 
						|
			{ | 
						|
				return invocation.WithRR(new ResolveResult(returnType)); | 
						|
			} | 
						|
		} | 
						|
 | 
						|
		TranslatedExpression EnsureIntegerType(TranslatedExpression expr) | 
						|
		{ | 
						|
			if (!expr.Type.IsCSharpPrimitiveIntegerType() && !expr.Type.IsCSharpNativeIntegerType()) | 
						|
			{ | 
						|
				// pointer arithmetic accepts all primitive integer types, but no enums etc. | 
						|
				expr = expr.ConvertTo(FindArithmeticType(expr.Type.GetStackType(), expr.Type.GetSign()), this); | 
						|
			} | 
						|
			return expr; | 
						|
		} | 
						|
 | 
						|
		TranslatedExpression? GetPointerArithmeticOffset(ILInstruction byteOffsetInst, TranslatedExpression byteOffsetExpr, | 
						|
			IType pointerElementType, bool checkForOverflow, bool unwrapZeroExtension = false) | 
						|
		{ | 
						|
			var countOffsetInst = PointerArithmeticOffset.Detect(byteOffsetInst, pointerElementType, | 
						|
				checkForOverflow: checkForOverflow, | 
						|
				unwrapZeroExtension: unwrapZeroExtension); | 
						|
			if (countOffsetInst == null) | 
						|
			{ | 
						|
				return null; | 
						|
			} | 
						|
			if (countOffsetInst == byteOffsetInst) | 
						|
			{ | 
						|
				return EnsureIntegerType(byteOffsetExpr); | 
						|
			} | 
						|
			else | 
						|
			{ | 
						|
				TranslatedExpression expr = Translate(countOffsetInst); | 
						|
				// Keep original ILInstruction as annotation | 
						|
				expr.Expression.RemoveAnnotations<ILInstruction>(); | 
						|
				return EnsureIntegerType(expr.WithILInstruction(byteOffsetInst)); | 
						|
			} | 
						|
		} | 
						|
 | 
						|
		/// <summary> | 
						|
		/// Called for divisions, detect and handles the code pattern: | 
						|
		///   div(sub(a, b), sizeof(T)) | 
						|
		/// when a,b are of type T*. | 
						|
		/// This is what the C# compiler generates for pointer subtraction. | 
						|
		/// </summary> | 
						|
		TranslatedExpression? HandlePointerSubtraction(BinaryNumericInstruction inst) | 
						|
		{ | 
						|
			Debug.Assert(inst.Operator == BinaryNumericOperator.Div); | 
						|
			if (inst.CheckForOverflow || inst.LeftInputType != StackType.I) | 
						|
				return null; | 
						|
			if (!(inst.Left is BinaryNumericInstruction sub && sub.Operator == BinaryNumericOperator.Sub)) | 
						|
				return null; | 
						|
			if (sub.CheckForOverflow) | 
						|
				return null; | 
						|
			// First, attempt to parse the 'sizeof' on the RHS | 
						|
			IType elementType; | 
						|
			if (inst.Right.MatchLdcI(out long elementSize)) | 
						|
			{ | 
						|
				elementType = null; | 
						|
				// OK, might be pointer subtraction if the element size matches | 
						|
			} | 
						|
			else if (inst.Right.UnwrapConv(ConversionKind.SignExtend).MatchSizeOf(out elementType)) | 
						|
			{ | 
						|
				// OK, might be pointer subtraction if the element type matches | 
						|
			} | 
						|
			else | 
						|
			{ | 
						|
				return null; | 
						|
			} | 
						|
			var left = Translate(sub.Left); | 
						|
			var right = Translate(sub.Right); | 
						|
			IType pointerType; | 
						|
			if (IsMatchingPointerType(left.Type)) | 
						|
			{ | 
						|
				pointerType = left.Type; | 
						|
			} | 
						|
			else if (IsMatchingPointerType(right.Type)) | 
						|
			{ | 
						|
				pointerType = right.Type; | 
						|
			} | 
						|
			else if (elementSize == 1 && left.Type.Kind == TypeKind.Pointer && right.Type.Kind == TypeKind.Pointer) | 
						|
			{ | 
						|
				// two pointers (neither matching), we're dividing by 1 (debug builds only), | 
						|
				// -> subtract two byte pointers | 
						|
				pointerType = new PointerType(compilation.FindType(KnownTypeCode.Byte)); | 
						|
			} | 
						|
			else | 
						|
			{ | 
						|
				// neither is a matching pointer type | 
						|
				// -> not a pointer subtraction after all | 
						|
				return null; | 
						|
			} | 
						|
			// We got a pointer subtraction. | 
						|
			left = left.ConvertTo(pointerType, this); | 
						|
			right = right.ConvertTo(pointerType, this); | 
						|
			var rr = new OperatorResolveResult( | 
						|
				compilation.FindType(KnownTypeCode.Int64), | 
						|
				ExpressionType.Subtract, | 
						|
				left.ResolveResult, right.ResolveResult | 
						|
			); | 
						|
			var result = new BinaryOperatorExpression( | 
						|
				left.Expression, BinaryOperatorType.Subtract, right.Expression | 
						|
			).WithILInstruction(new[] { inst, sub }) | 
						|
			 .WithRR(rr); | 
						|
			return result; | 
						|
 | 
						|
			bool IsMatchingPointerType(IType type) | 
						|
			{ | 
						|
				if (type is PointerType pt) | 
						|
				{ | 
						|
					if (elementType != null) | 
						|
						return elementType.Equals(pt.ElementType); | 
						|
					else if (elementSize > 0) | 
						|
						return PointerArithmeticOffset.ComputeSizeOf(pt.ElementType) == elementSize; | 
						|
				} | 
						|
				return false; | 
						|
			} | 
						|
		} | 
						|
 | 
						|
		TranslatedExpression HandleBinaryNumeric(BinaryNumericInstruction inst, BinaryOperatorType op, TranslationContext context) | 
						|
		{ | 
						|
			var resolverWithOverflowCheck = resolver.WithCheckForOverflow(inst.CheckForOverflow); | 
						|
			var left = Translate(inst.Left, op.IsBitwise() ? context.TypeHint : null); | 
						|
			var right = Translate(inst.Right, op.IsBitwise() ? context.TypeHint : null); | 
						|
 | 
						|
			if (inst.UnderlyingResultType == StackType.Ref) | 
						|
			{ | 
						|
				var ptrResult = HandleManagedPointerArithmetic(inst, left, right); | 
						|
				if (ptrResult != null) | 
						|
					return ptrResult.Value; | 
						|
			} | 
						|
			if (left.Type.Kind == TypeKind.Pointer || right.Type.Kind == TypeKind.Pointer) | 
						|
			{ | 
						|
				var ptrResult = HandlePointerArithmetic(inst, left, right); | 
						|
				if (ptrResult != null) | 
						|
					return ptrResult.Value; | 
						|
			} | 
						|
 | 
						|
			left = PrepareArithmeticArgument(left, inst.LeftInputType, inst.Sign, inst.IsLifted); | 
						|
			right = PrepareArithmeticArgument(right, inst.RightInputType, inst.Sign, inst.IsLifted); | 
						|
 | 
						|
			if (op == BinaryOperatorType.Subtract && inst.Left.MatchLdcI(0)) | 
						|
			{ | 
						|
				IType rightUType = NullableType.GetUnderlyingType(right.Type); | 
						|
				if (rightUType.IsKnownType(KnownTypeCode.Int32) || rightUType.IsKnownType(KnownTypeCode.Int64) | 
						|
					|| rightUType.IsCSharpSmallIntegerType() || rightUType.Kind == TypeKind.NInt) | 
						|
				{ | 
						|
					// unary minus is supported on signed int, nint and long, and on the small integer types (since they promote to int) | 
						|
					var uoe = new UnaryOperatorExpression(UnaryOperatorType.Minus, right.Expression); | 
						|
					uoe.AddAnnotation(inst.CheckForOverflow ? AddCheckedBlocks.CheckedAnnotation : AddCheckedBlocks.UncheckedAnnotation); | 
						|
					var resultType = FindArithmeticType(inst.RightInputType, Sign.Signed); | 
						|
					if (inst.IsLifted) | 
						|
						resultType = NullableType.Create(compilation, resultType); | 
						|
					return uoe.WithILInstruction(inst).WithRR(new OperatorResolveResult( | 
						|
						resultType, | 
						|
						inst.CheckForOverflow ? ExpressionType.NegateChecked : ExpressionType.Negate, | 
						|
						right.ResolveResult)); | 
						|
				} | 
						|
			} | 
						|
			if (op.IsBitwise() | 
						|
				&& left.Type.IsKnownType(KnownTypeCode.Boolean) | 
						|
				&& right.Type.IsKnownType(KnownTypeCode.Boolean) | 
						|
				&& SemanticHelper.IsPure(inst.Right.Flags)) | 
						|
			{ | 
						|
				// Undo the C# compiler's optimization of "a && b" to "a & b". | 
						|
				if (op == BinaryOperatorType.BitwiseAnd) | 
						|
				{ | 
						|
					op = BinaryOperatorType.ConditionalAnd; | 
						|
				} | 
						|
				else if (op == BinaryOperatorType.BitwiseOr) | 
						|
				{ | 
						|
					op = BinaryOperatorType.ConditionalOr; | 
						|
				} | 
						|
			} | 
						|
 | 
						|
			if (op.IsBitwise() && (left.Type.Kind == TypeKind.Enum || right.Type.Kind == TypeKind.Enum)) | 
						|
			{ | 
						|
				left = AdjustConstantExpressionToType(left, right.Type); | 
						|
				right = AdjustConstantExpressionToType(right, left.Type); | 
						|
			} | 
						|
 | 
						|
			var rr = resolverWithOverflowCheck.ResolveBinaryOperator(op, left.ResolveResult, right.ResolveResult); | 
						|
			if (rr.IsError || NullableType.GetUnderlyingType(rr.Type).GetStackType() != inst.UnderlyingResultType | 
						|
				|| !IsCompatibleWithSign(left.Type, inst.Sign) || !IsCompatibleWithSign(right.Type, inst.Sign)) | 
						|
			{ | 
						|
				// Left and right operands are incompatible, so convert them to a common type | 
						|
				Sign sign = inst.Sign; | 
						|
				if (sign == Sign.None) | 
						|
				{ | 
						|
					// If the sign doesn't matter, try to use the same sign as expected by the context | 
						|
					sign = context.TypeHint.GetSign(); | 
						|
					if (sign == Sign.None) | 
						|
					{ | 
						|
						sign = op.IsBitwise() ? Sign.Unsigned : Sign.Signed; | 
						|
					} | 
						|
				} | 
						|
				IType targetType = FindArithmeticType(inst.UnderlyingResultType, sign); | 
						|
				left = left.ConvertTo(NullableType.IsNullable(left.Type) ? NullableType.Create(compilation, targetType) : targetType, this); | 
						|
				right = right.ConvertTo(NullableType.IsNullable(right.Type) ? NullableType.Create(compilation, targetType) : targetType, this); | 
						|
				rr = resolverWithOverflowCheck.ResolveBinaryOperator(op, left.ResolveResult, right.ResolveResult); | 
						|
			} | 
						|
			if (op.IsBitwise()) | 
						|
			{ | 
						|
				if (left.ResolveResult.ConstantValue != null) | 
						|
				{ | 
						|
					long value = (long)CSharpPrimitiveCast.Cast(TypeCode.Int64, left.ResolveResult.ConstantValue, checkForOverflow: false); | 
						|
 | 
						|
					left = ConvertConstantValue( | 
						|
						left.ResolveResult, | 
						|
						allowImplicitConversion: false, | 
						|
						ShouldDisplayAsHex(value, left.Type, inst) | 
						|
					).WithILInstruction(left.ILInstructions); | 
						|
				} | 
						|
				if (right.ResolveResult.ConstantValue != null) | 
						|
				{ | 
						|
					long value = (long)CSharpPrimitiveCast.Cast(TypeCode.Int64, right.ResolveResult.ConstantValue, checkForOverflow: false); | 
						|
 | 
						|
					right = ConvertConstantValue( | 
						|
						right.ResolveResult, | 
						|
						allowImplicitConversion: false, | 
						|
						ShouldDisplayAsHex(value, right.Type, inst) | 
						|
					).WithILInstruction(right.ILInstructions); | 
						|
				} | 
						|
			} | 
						|
			var resultExpr = new BinaryOperatorExpression(left.Expression, op, right.Expression) | 
						|
				.WithILInstruction(inst) | 
						|
				.WithRR(rr); | 
						|
			if (BinaryOperatorMightCheckForOverflow(op) && !inst.UnderlyingResultType.IsFloatType()) | 
						|
			{ | 
						|
				resultExpr.Expression.AddAnnotation(inst.CheckForOverflow ? AddCheckedBlocks.CheckedAnnotation : AddCheckedBlocks.UncheckedAnnotation); | 
						|
			} | 
						|
			return resultExpr; | 
						|
		} | 
						|
 | 
						|
		/// <summary> | 
						|
		/// Gets a type matching the stack type and sign. | 
						|
		/// </summary> | 
						|
		IType FindType(StackType stackType, Sign sign) | 
						|
		{ | 
						|
			if (stackType == StackType.I && settings.NativeIntegers) | 
						|
			{ | 
						|
				return sign == Sign.Unsigned ? SpecialType.NUInt : SpecialType.NInt; | 
						|
			} | 
						|
			else | 
						|
			{ | 
						|
				return compilation.FindType(stackType, sign); | 
						|
			} | 
						|
		} | 
						|
 | 
						|
		/// <summary> | 
						|
		/// Gets a type used for performing arithmetic with the stack type and sign. | 
						|
		///  | 
						|
		/// This may result in a larger type than requested when the selected C# version | 
						|
		/// doesn't support native integers. | 
						|
		/// Should only be used after a call to PrepareArithmeticArgument() | 
						|
		/// to ensure that we're not preserving extra bits from an oversized TranslatedExpression. | 
						|
		/// </summary> | 
						|
		IType FindArithmeticType(StackType stackType, Sign sign) | 
						|
		{ | 
						|
			if (stackType == StackType.I) | 
						|
			{ | 
						|
				if (settings.NativeIntegers) | 
						|
				{ | 
						|
					return sign == Sign.Unsigned ? SpecialType.NUInt : SpecialType.NInt; | 
						|
				} | 
						|
				else | 
						|
				{ | 
						|
					// If native integers are not available, use 64-bit arithmetic instead | 
						|
					stackType = StackType.I8; | 
						|
				} | 
						|
			} | 
						|
			return compilation.FindType(stackType, sign); | 
						|
		} | 
						|
 | 
						|
		/// <summary> | 
						|
		/// Handle oversized arguments needing truncation; and avoid IntPtr/pointers in arguments. | 
						|
		/// </summary> | 
						|
		TranslatedExpression PrepareArithmeticArgument(TranslatedExpression arg, StackType argStackType, Sign sign, bool isLifted) | 
						|
		{ | 
						|
			if (isLifted && !NullableType.IsNullable(arg.Type)) | 
						|
			{ | 
						|
				isLifted = false; // don't cast to nullable if this input wasn't already nullable | 
						|
			} | 
						|
			IType argUType = isLifted ? NullableType.GetUnderlyingType(arg.Type) : arg.Type; | 
						|
			if (argStackType.IsIntegerType() && argStackType.GetSize() < argUType.GetSize()) | 
						|
			{ | 
						|
				// If the argument is oversized (needs truncation to match stack size of its ILInstruction), | 
						|
				// perform the truncation now. | 
						|
				IType targetType = FindType(argStackType, sign); | 
						|
				argUType = targetType; | 
						|
				if (isLifted) | 
						|
					targetType = NullableType.Create(compilation, targetType); | 
						|
				arg = arg.ConvertTo(targetType, this); | 
						|
			} | 
						|
			if (argUType.IsKnownType(KnownTypeCode.IntPtr) || argUType.IsKnownType(KnownTypeCode.UIntPtr)) | 
						|
			{ | 
						|
				// None of the operators we might want to apply are supported by IntPtr/UIntPtr. | 
						|
				// Also, pointer arithmetic has different semantics (works in number of elements, not bytes). | 
						|
				// So any inputs of size StackType.I must be converted to long/ulong. | 
						|
				IType targetType = FindArithmeticType(StackType.I, sign); | 
						|
				if (isLifted) | 
						|
					targetType = NullableType.Create(compilation, targetType); | 
						|
				arg = arg.ConvertTo(targetType, this); | 
						|
			} | 
						|
			return arg; | 
						|
		} | 
						|
 | 
						|
		/// <summary> | 
						|
		/// Gets whether <paramref name="type"/> has the specified <paramref name="sign"/>. | 
						|
		/// If <paramref name="sign"/> is None, always returns true. | 
						|
		/// </summary> | 
						|
		static bool IsCompatibleWithSign(IType type, Sign sign) | 
						|
		{ | 
						|
			return sign == Sign.None || NullableType.GetUnderlyingType(type).GetSign() == sign; | 
						|
		} | 
						|
 | 
						|
		static bool BinaryOperatorMightCheckForOverflow(BinaryOperatorType op) | 
						|
		{ | 
						|
			switch (op) | 
						|
			{ | 
						|
				case BinaryOperatorType.BitwiseAnd: | 
						|
				case BinaryOperatorType.BitwiseOr: | 
						|
				case BinaryOperatorType.ExclusiveOr: | 
						|
				case BinaryOperatorType.ShiftLeft: | 
						|
				case BinaryOperatorType.ShiftRight: | 
						|
					return false; | 
						|
				default: | 
						|
					return true; | 
						|
			} | 
						|
		} | 
						|
 | 
						|
		TranslatedExpression HandleShift(BinaryNumericInstruction inst, BinaryOperatorType op) | 
						|
		{ | 
						|
			var left = Translate(inst.Left); | 
						|
			var right = Translate(inst.Right); | 
						|
 | 
						|
			left = PrepareArithmeticArgument(left, inst.LeftInputType, inst.Sign, inst.IsLifted); | 
						|
 | 
						|
			Sign sign = inst.Sign; | 
						|
			var leftUType = NullableType.GetUnderlyingType(left.Type); | 
						|
			if (leftUType.IsCSharpSmallIntegerType() && sign != Sign.Unsigned && inst.UnderlyingResultType == StackType.I4) | 
						|
			{ | 
						|
				// With small integer types, C# will promote to int and perform signed shifts. | 
						|
				// We thus don't need any casts in this case. | 
						|
			} | 
						|
			else | 
						|
			{ | 
						|
				// Insert cast to target type. | 
						|
				if (sign == Sign.None) | 
						|
				{ | 
						|
					// if we don't need a specific sign, prefer keeping that of the input: | 
						|
					sign = leftUType.GetSign(); | 
						|
				} | 
						|
				IType targetType = FindArithmeticType(inst.UnderlyingResultType, sign); | 
						|
				if (NullableType.IsNullable(left.Type)) | 
						|
				{ | 
						|
					targetType = NullableType.Create(compilation, targetType); | 
						|
				} | 
						|
				left = left.ConvertTo(targetType, this); | 
						|
			} | 
						|
 | 
						|
			// Shift operators in C# always expect type 'int' on the right-hand-side | 
						|
			if (NullableType.IsNullable(right.Type)) | 
						|
			{ | 
						|
				right = right.ConvertTo(NullableType.Create(compilation, compilation.FindType(KnownTypeCode.Int32)), this); | 
						|
			} | 
						|
			else | 
						|
			{ | 
						|
				right = right.ConvertTo(compilation.FindType(KnownTypeCode.Int32), this); | 
						|
			} | 
						|
 | 
						|
			return new BinaryOperatorExpression(left.Expression, op, right.Expression) | 
						|
				.WithILInstruction(inst) | 
						|
				.WithRR(resolver.ResolveBinaryOperator(op, left.ResolveResult, right.ResolveResult)); | 
						|
		} | 
						|
 | 
						|
		protected internal override TranslatedExpression VisitUserDefinedCompoundAssign(UserDefinedCompoundAssign inst, TranslationContext context) | 
						|
		{ | 
						|
			IType loadType = inst.Method.Parameters[0].Type; | 
						|
			ExpressionWithResolveResult target; | 
						|
			if (inst.TargetKind == CompoundTargetKind.Address) | 
						|
			{ | 
						|
				target = LdObj(inst.Target, loadType); | 
						|
			} | 
						|
			else | 
						|
			{ | 
						|
				target = Translate(inst.Target, loadType); | 
						|
			} | 
						|
			if (UserDefinedCompoundAssign.IsStringConcat(inst.Method)) | 
						|
			{ | 
						|
				Debug.Assert(inst.Method.Parameters.Count == 2); | 
						|
				var value = Translate(inst.Value).ConvertTo(inst.Method.Parameters[1].Type, this, allowImplicitConversion: true); | 
						|
				var valueExpr = ReplaceMethodCallsWithOperators.RemoveRedundantToStringInConcat(value, inst.Method, isLastArgument: true).Detach(); | 
						|
				return new AssignmentExpression(target, AssignmentOperatorType.Add, valueExpr) | 
						|
					.WithILInstruction(inst) | 
						|
					.WithRR(new OperatorResolveResult(inst.Method.ReturnType, ExpressionType.AddAssign, inst.Method, inst.IsLifted, new[] { target.ResolveResult, value.ResolveResult })); | 
						|
			} | 
						|
			else if (inst.Method.Parameters.Count == 2) | 
						|
			{ | 
						|
				var value = Translate(inst.Value).ConvertTo(inst.Method.Parameters[1].Type, this); | 
						|
				AssignmentOperatorType? op = GetAssignmentOperatorTypeFromMetadataName(inst.Method.Name); | 
						|
				Debug.Assert(op != null); | 
						|
 | 
						|
				return new AssignmentExpression(target, op.Value, value) | 
						|
					.WithILInstruction(inst) | 
						|
					.WithRR(new OperatorResolveResult(inst.Method.ReturnType, AssignmentExpression.GetLinqNodeType(op.Value, false), inst.Method, inst.IsLifted, new[] { target.ResolveResult, value.ResolveResult })); | 
						|
			} | 
						|
			else | 
						|
			{ | 
						|
				UnaryOperatorType? op = GetUnaryOperatorTypeFromMetadataName(inst.Method.Name, inst.EvalMode == CompoundEvalMode.EvaluatesToOldValue); | 
						|
				Debug.Assert(op != null); | 
						|
 | 
						|
				return new UnaryOperatorExpression(op.Value, target) | 
						|
					.WithILInstruction(inst) | 
						|
					.WithRR(new OperatorResolveResult(inst.Method.ReturnType, UnaryOperatorExpression.GetLinqNodeType(op.Value, false), inst.Method, inst.IsLifted, new[] { target.ResolveResult })); | 
						|
			} | 
						|
		} | 
						|
 | 
						|
		internal static AssignmentOperatorType? GetAssignmentOperatorTypeFromMetadataName(string name) | 
						|
		{ | 
						|
			switch (name) | 
						|
			{ | 
						|
				case "op_Addition": | 
						|
					return AssignmentOperatorType.Add; | 
						|
				case "op_Subtraction": | 
						|
					return AssignmentOperatorType.Subtract; | 
						|
				case "op_Multiply": | 
						|
					return AssignmentOperatorType.Multiply; | 
						|
				case "op_Division": | 
						|
					return AssignmentOperatorType.Divide; | 
						|
				case "op_Modulus": | 
						|
					return AssignmentOperatorType.Modulus; | 
						|
				case "op_BitwiseAnd": | 
						|
					return AssignmentOperatorType.BitwiseAnd; | 
						|
				case "op_BitwiseOr": | 
						|
					return AssignmentOperatorType.BitwiseOr; | 
						|
				case "op_ExclusiveOr": | 
						|
					return AssignmentOperatorType.ExclusiveOr; | 
						|
				case "op_LeftShift": | 
						|
					return AssignmentOperatorType.ShiftLeft; | 
						|
				case "op_RightShift": | 
						|
					return AssignmentOperatorType.ShiftRight; | 
						|
				default: | 
						|
					return null; | 
						|
			} | 
						|
		} | 
						|
 | 
						|
		internal static UnaryOperatorType? GetUnaryOperatorTypeFromMetadataName(string name, bool isPostfix) | 
						|
		{ | 
						|
			switch (name) | 
						|
			{ | 
						|
				case "op_Increment": | 
						|
					return isPostfix ? UnaryOperatorType.PostIncrement : UnaryOperatorType.Increment; | 
						|
				case "op_Decrement": | 
						|
					return isPostfix ? UnaryOperatorType.PostDecrement : UnaryOperatorType.Decrement; | 
						|
				default: | 
						|
					return null; | 
						|
			} | 
						|
		} | 
						|
 | 
						|
		protected internal override TranslatedExpression VisitNumericCompoundAssign(NumericCompoundAssign inst, TranslationContext context) | 
						|
		{ | 
						|
			switch (inst.Operator) | 
						|
			{ | 
						|
				case BinaryNumericOperator.Add: | 
						|
					return HandleCompoundAssignment(inst, AssignmentOperatorType.Add); | 
						|
				case BinaryNumericOperator.Sub: | 
						|
					return HandleCompoundAssignment(inst, AssignmentOperatorType.Subtract); | 
						|
				case BinaryNumericOperator.Mul: | 
						|
					return HandleCompoundAssignment(inst, AssignmentOperatorType.Multiply); | 
						|
				case BinaryNumericOperator.Div: | 
						|
					return HandleCompoundAssignment(inst, AssignmentOperatorType.Divide); | 
						|
				case BinaryNumericOperator.Rem: | 
						|
					return HandleCompoundAssignment(inst, AssignmentOperatorType.Modulus); | 
						|
				case BinaryNumericOperator.BitAnd: | 
						|
					return HandleCompoundAssignment(inst, AssignmentOperatorType.BitwiseAnd); | 
						|
				case BinaryNumericOperator.BitOr: | 
						|
					return HandleCompoundAssignment(inst, AssignmentOperatorType.BitwiseOr); | 
						|
				case BinaryNumericOperator.BitXor: | 
						|
					return HandleCompoundAssignment(inst, AssignmentOperatorType.ExclusiveOr); | 
						|
				case BinaryNumericOperator.ShiftLeft: | 
						|
					return HandleCompoundShift(inst, AssignmentOperatorType.ShiftLeft); | 
						|
				case BinaryNumericOperator.ShiftRight: | 
						|
					return HandleCompoundShift(inst, AssignmentOperatorType.ShiftRight); | 
						|
				default: | 
						|
					throw new ArgumentOutOfRangeException(); | 
						|
			} | 
						|
		} | 
						|
 | 
						|
		TranslatedExpression HandleCompoundAssignment(NumericCompoundAssign inst, AssignmentOperatorType op) | 
						|
		{ | 
						|
			ExpressionWithResolveResult target; | 
						|
			if (inst.TargetKind == CompoundTargetKind.Address) | 
						|
			{ | 
						|
				target = LdObj(inst.Target, inst.Type); | 
						|
			} | 
						|
			else | 
						|
			{ | 
						|
				target = Translate(inst.Target, inst.Type); | 
						|
			} | 
						|
 | 
						|
			TranslatedExpression resultExpr; | 
						|
			if (inst.EvalMode == CompoundEvalMode.EvaluatesToOldValue) | 
						|
			{ | 
						|
				Debug.Assert(op == AssignmentOperatorType.Add || op == AssignmentOperatorType.Subtract); | 
						|
				Debug.Assert(inst.Value.MatchLdcI(1) || inst.Value.MatchLdcF4(1) || inst.Value.MatchLdcF8(1)); | 
						|
				UnaryOperatorType unary; | 
						|
				ExpressionType exprType; | 
						|
				if (op == AssignmentOperatorType.Add) | 
						|
				{ | 
						|
					unary = UnaryOperatorType.PostIncrement; | 
						|
					exprType = ExpressionType.PostIncrementAssign; | 
						|
				} | 
						|
				else | 
						|
				{ | 
						|
					unary = UnaryOperatorType.PostDecrement; | 
						|
					exprType = ExpressionType.PostDecrementAssign; | 
						|
				} | 
						|
				resultExpr = new UnaryOperatorExpression(unary, target) | 
						|
					.WithILInstruction(inst) | 
						|
					.WithRR(new OperatorResolveResult(target.Type, exprType, target.ResolveResult)); | 
						|
			} | 
						|
			else | 
						|
			{ | 
						|
				var value = Translate(inst.Value); | 
						|
				value = PrepareArithmeticArgument(value, inst.RightInputType, inst.Sign, inst.IsLifted); | 
						|
				switch (op) | 
						|
				{ | 
						|
					case AssignmentOperatorType.Add: | 
						|
					case AssignmentOperatorType.Subtract: | 
						|
						if (target.Type.Kind == TypeKind.Pointer) | 
						|
						{ | 
						|
							var pao = GetPointerArithmeticOffset(inst.Value, value, ((PointerType)target.Type).ElementType, inst.CheckForOverflow); | 
						|
							if (pao != null) | 
						|
							{ | 
						|
								value = pao.Value; | 
						|
							} | 
						|
							else | 
						|
							{ | 
						|
								value.Expression.AddChild(new Comment("ILSpy Error: GetPointerArithmeticOffset() failed", CommentType.MultiLine), Roles.Comment); | 
						|
							} | 
						|
						} | 
						|
						else | 
						|
						{ | 
						|
							IType targetType = NullableType.GetUnderlyingType(target.Type).GetEnumUnderlyingType(); | 
						|
							value = ConvertValue(value, targetType); | 
						|
						} | 
						|
						break; | 
						|
					case AssignmentOperatorType.Multiply: | 
						|
					case AssignmentOperatorType.Divide: | 
						|
					case AssignmentOperatorType.Modulus: | 
						|
					case AssignmentOperatorType.BitwiseAnd: | 
						|
					case AssignmentOperatorType.BitwiseOr: | 
						|
					case AssignmentOperatorType.ExclusiveOr: | 
						|
					{ | 
						|
						IType targetType = NullableType.GetUnderlyingType(target.Type); | 
						|
						value = ConvertValue(value, targetType); | 
						|
						break; | 
						|
					} | 
						|
				} | 
						|
				resultExpr = new AssignmentExpression(target.Expression, op, value.Expression) | 
						|
					.WithILInstruction(inst) | 
						|
					.WithRR(new OperatorResolveResult(target.Type, AssignmentExpression.GetLinqNodeType(op, inst.CheckForOverflow), target.ResolveResult, value.ResolveResult)); | 
						|
			} | 
						|
			if (AssignmentOperatorMightCheckForOverflow(op) && !inst.UnderlyingResultType.IsFloatType()) | 
						|
			{ | 
						|
				resultExpr.Expression.AddAnnotation(inst.CheckForOverflow ? AddCheckedBlocks.CheckedAnnotation : AddCheckedBlocks.UncheckedAnnotation); | 
						|
			} | 
						|
			return resultExpr; | 
						|
 | 
						|
			TranslatedExpression ConvertValue(TranslatedExpression value, IType targetType) | 
						|
			{ | 
						|
				bool allowImplicitConversion = true; | 
						|
				if (targetType.GetStackType() == StackType.I) | 
						|
				{ | 
						|
					// Force explicit cast for (U)IntPtr, keep allowing implicit conversion only for n(u)int | 
						|
					allowImplicitConversion = targetType.IsCSharpNativeIntegerType(); | 
						|
					targetType = targetType.GetSign() == Sign.Unsigned ? SpecialType.NUInt : SpecialType.NInt; | 
						|
				} | 
						|
				if (NullableType.IsNullable(value.Type)) | 
						|
				{ | 
						|
					targetType = NullableType.Create(compilation, targetType); | 
						|
				} | 
						|
				return value.ConvertTo(targetType, this, inst.CheckForOverflow, allowImplicitConversion); | 
						|
			} | 
						|
		} | 
						|
 | 
						|
		TranslatedExpression HandleCompoundShift(NumericCompoundAssign inst, AssignmentOperatorType op) | 
						|
		{ | 
						|
			Debug.Assert(inst.EvalMode == CompoundEvalMode.EvaluatesToNewValue); | 
						|
			ExpressionWithResolveResult target; | 
						|
			if (inst.TargetKind == CompoundTargetKind.Address) | 
						|
			{ | 
						|
				target = LdObj(inst.Target, inst.Type); | 
						|
			} | 
						|
			else | 
						|
			{ | 
						|
				target = Translate(inst.Target, inst.Type); | 
						|
			} | 
						|
			var value = Translate(inst.Value); | 
						|
 | 
						|
			// Shift operators in C# always expect type 'int' on the right-hand-side | 
						|
			if (NullableType.IsNullable(value.Type)) | 
						|
			{ | 
						|
				value = value.ConvertTo(NullableType.Create(compilation, compilation.FindType(KnownTypeCode.Int32)), this); | 
						|
			} | 
						|
			else | 
						|
			{ | 
						|
				value = value.ConvertTo(compilation.FindType(KnownTypeCode.Int32), this); | 
						|
			} | 
						|
 | 
						|
			return new AssignmentExpression(target.Expression, op, value.Expression) | 
						|
				.WithILInstruction(inst) | 
						|
				.WithRR(resolver.ResolveAssignment(op, target.ResolveResult, value.ResolveResult)); | 
						|
		} | 
						|
 | 
						|
		static bool AssignmentOperatorMightCheckForOverflow(AssignmentOperatorType op) | 
						|
		{ | 
						|
			switch (op) | 
						|
			{ | 
						|
				case AssignmentOperatorType.BitwiseAnd: | 
						|
				case AssignmentOperatorType.BitwiseOr: | 
						|
				case AssignmentOperatorType.ExclusiveOr: | 
						|
				case AssignmentOperatorType.ShiftLeft: | 
						|
				case AssignmentOperatorType.ShiftRight: | 
						|
					return false; | 
						|
				default: | 
						|
					return true; | 
						|
			} | 
						|
		} | 
						|
 | 
						|
		protected internal override TranslatedExpression VisitConv(Conv inst, TranslationContext context) | 
						|
		{ | 
						|
			Sign hintSign = inst.InputSign; | 
						|
			if (hintSign == Sign.None) | 
						|
			{ | 
						|
				hintSign = context.TypeHint.GetSign(); | 
						|
			} | 
						|
			var arg = Translate(inst.Argument, typeHint: FindArithmeticType(inst.InputType, hintSign)); | 
						|
			IType inputType = NullableType.GetUnderlyingType(arg.Type); | 
						|
			StackType inputStackType = inst.InputType; | 
						|
			// Note: we're dealing with two conversions here: | 
						|
			// a) the implicit conversion from `inputType` to `inputStackType` | 
						|
			//    (due to the ExpressionBuilder post-condition being flexible with regards to the integer type width) | 
						|
			//    If this is a widening conversion, I'm calling the argument C# type "oversized". | 
						|
			//    If this is a narrowing conversion, I'm calling the argument C# type "undersized". | 
						|
			// b) the actual conversion instruction from `inputStackType` to `inst.TargetType` | 
						|
 | 
						|
			// Also, we need to be very careful with regards to the conversions we emit: | 
						|
			// In C#, zero vs. sign-extension depends on the input type, | 
						|
			// but in the ILAst conv instruction it depends on the output type. | 
						|
			// However, in the conv.ovf instructions, the .NET runtime behavior seems to depend on the input type, | 
						|
			// in violation of the ECMA-335 spec! | 
						|
 | 
						|
			IType GetType(KnownTypeCode typeCode) | 
						|
			{ | 
						|
				IType type = compilation.FindType(typeCode); | 
						|
				// Prefer n(u)int over (U)IntPtr | 
						|
				if (typeCode == KnownTypeCode.IntPtr && settings.NativeIntegers && !type.Equals(context.TypeHint)) | 
						|
				{ | 
						|
					type = SpecialType.NInt; | 
						|
				} | 
						|
				else if (typeCode == KnownTypeCode.UIntPtr && settings.NativeIntegers && !type.Equals(context.TypeHint)) | 
						|
				{ | 
						|
					type = SpecialType.NUInt; | 
						|
				} | 
						|
				if (inst.IsLifted) | 
						|
				{ | 
						|
					type = NullableType.Create(compilation, type); | 
						|
				} | 
						|
				return type; | 
						|
			} | 
						|
 | 
						|
			if (inst.CheckForOverflow || inst.Kind == ConversionKind.IntToFloat) | 
						|
			{ | 
						|
				// We need to first convert the argument to the expected sign. | 
						|
				// We also need to perform any input narrowing conversion so that it doesn't get mixed up with the overflow check. | 
						|
				Debug.Assert(inst.InputSign != Sign.None); | 
						|
				if (inputType.GetSize() > inputStackType.GetSize() || inputType.GetSign() != inst.InputSign) | 
						|
				{ | 
						|
					arg = arg.ConvertTo(GetType(inputStackType.ToKnownTypeCode(inst.InputSign)), this); | 
						|
				} | 
						|
				// Because casts with overflow check match C# semantics (zero/sign-extension depends on source type), | 
						|
				// we can just directly cast to the target type. | 
						|
				return arg.ConvertTo(GetType(inst.TargetType.ToKnownTypeCode()), this, inst.CheckForOverflow) | 
						|
					.WithILInstruction(inst); | 
						|
			} | 
						|
 | 
						|
			switch (inst.Kind) | 
						|
			{ | 
						|
				case ConversionKind.StartGCTracking: | 
						|
					// A "start gc tracking" conversion is inserted in the ILAst whenever | 
						|
					// some instruction expects a managed pointer, but we pass an unmanaged pointer. | 
						|
					// We'll leave the C#-level conversion (from T* to ref T) to the consumer that expects the managed pointer. | 
						|
					return arg; | 
						|
				case ConversionKind.StopGCTracking: | 
						|
					if (inputType.Kind == TypeKind.ByReference) | 
						|
					{ | 
						|
						if (PointerArithmeticOffset.IsFixedVariable(inst.Argument)) | 
						|
						{ | 
						|
							// cast to corresponding pointer type: | 
						|
							var pointerType = new PointerType(((ByReferenceType)inputType).ElementType); | 
						|
							return arg.ConvertTo(pointerType, this).WithILInstruction(inst); | 
						|
						} | 
						|
						else | 
						|
						{ | 
						|
							// emit Unsafe.AsPointer() intrinsic: | 
						|
							return CallUnsafeIntrinsic("AsPointer", | 
						|
								arguments: new Expression[] { arg }, | 
						|
								returnType: new PointerType(compilation.FindType(KnownTypeCode.Void)), | 
						|
								inst: inst); | 
						|
						} | 
						|
					} | 
						|
					else if (arg.Type.GetStackType().IsIntegerType()) | 
						|
					{ | 
						|
						// ConversionKind.StopGCTracking should only be used with managed references, | 
						|
						// but it's possible that we're supposed to stop tracking something we just started to track. | 
						|
						return arg; | 
						|
					} | 
						|
					else | 
						|
					{ | 
						|
						goto default; | 
						|
					} | 
						|
				case ConversionKind.SignExtend: | 
						|
					// We just need to ensure the input type before the conversion is signed. | 
						|
					// Also, if the argument was translated into an oversized C# type, | 
						|
					// we need to perform the truncatation to the input stack type. | 
						|
					if (inputType.GetSign() != Sign.Signed || ValueMightBeOversized(arg.ResolveResult, inputStackType)) | 
						|
					{ | 
						|
						// Note that an undersized C# type is handled just fine: | 
						|
						// If it is unsigned we'll zero-extend it to the width of the inputStackType here, | 
						|
						// and it is signed we just combine the two sign-extensions into a single sign-extending conversion. | 
						|
						arg = arg.ConvertTo(GetType(inputStackType.ToKnownTypeCode(Sign.Signed)), this); | 
						|
					} | 
						|
					// Then, we can just return the argument as-is: the ExpressionBuilder post-condition allows us | 
						|
					// to force our parent instruction to handle the actual sign-extension conversion. | 
						|
					// (our caller may have more information to pick a better fitting target type) | 
						|
					return arg.WithILInstruction(inst); | 
						|
				case ConversionKind.ZeroExtend: | 
						|
					// If overflow check cannot fail, handle this just like sign extension (except for swapped signs) | 
						|
					if (inputType.GetSign() != Sign.Unsigned || inputType.GetSize() > inputStackType.GetSize()) | 
						|
					{ | 
						|
						arg = arg.ConvertTo(GetType(inputStackType.ToKnownTypeCode(Sign.Unsigned)), this); | 
						|
					} | 
						|
					return arg.WithILInstruction(inst); | 
						|
				case ConversionKind.Nop: | 
						|
					// no need to generate any C# code for a nop conversion | 
						|
					return arg.WithILInstruction(inst); | 
						|
				case ConversionKind.Truncate: | 
						|
					// Note: there are three sizes involved here: | 
						|
					// A = inputType.GetSize() | 
						|
					// B = inputStackType.GetSize() | 
						|
					// C = inst.TargetType.GetSize(). | 
						|
					// We know that C < B (otherwise this wouldn't be the truncation case). | 
						|
					// 1) If C < B < A, we just combine the two truncations into one. | 
						|
					// 2) If C < B = A, there's no input conversion, just the truncation | 
						|
					// 3) If C <= A < B, all the extended bits get removed again by the truncation. | 
						|
					// 4) If A < C < B, some extended bits remain even after truncation. | 
						|
					// In cases 1-3, the overall conversion is a truncation or no-op. | 
						|
					// In case 4, the overall conversion is a zero/sign extension, but to a smaller | 
						|
					// size than the original conversion. | 
						|
					if (inst.TargetType.IsSmallIntegerType()) | 
						|
					{ | 
						|
						// If the target type is a small integer type, IL will implicitly sign- or zero-extend | 
						|
						// the result after the truncation back to StackType.I4. | 
						|
						// (which means there's actually 3 conversions involved!) | 
						|
						// Note that we must handle truncation to small integer types ourselves: | 
						|
						// our caller only sees the StackType.I4 and doesn't know to truncate to the small type. | 
						|
 | 
						|
						if (inputType.GetSize() <= inst.TargetType.GetSize() && inputType.GetSign() == inst.TargetType.GetSign()) | 
						|
						{ | 
						|
							// There's no actual truncation involved, and the result of the Conv instruction is extended | 
						|
							// the same way as the original instruction | 
						|
							// -> we can return arg directly | 
						|
							return arg.WithILInstruction(inst); | 
						|
						} | 
						|
						else | 
						|
						{ | 
						|
							// We need to actually truncate; *or* we need to change the sign for the remaining extension to I4. | 
						|
							goto default; // Emit simple cast to inst.TargetType | 
						|
						} | 
						|
					} | 
						|
					else | 
						|
					{ | 
						|
						Debug.Assert(inst.TargetType.GetSize() == inst.UnderlyingResultType.GetSize()); | 
						|
						// For non-small integer types, we can let the whole unchecked truncation | 
						|
						// get handled by our caller (using the ExpressionBuilder post-condition). | 
						|
 | 
						|
						// Case 4 (left-over extension from implicit conversion) can also be handled by our caller. | 
						|
						return arg.WithILInstruction(inst); | 
						|
					} | 
						|
				case ConversionKind.Invalid: | 
						|
					if (inst.InputType == StackType.Unknown && inst.TargetType == IL.PrimitiveType.None && arg.Type.Kind == TypeKind.Unknown) | 
						|
					{ | 
						|
						// Unknown -> O conversion. | 
						|
						// Our post-condition allows us to also use expressions with unknown type where O is expected, | 
						|
						// so avoid introducing an `(object)` cast because we're likely to cast back to the same unknown type, | 
						|
						// just in a signature context where we know that it's a class type. | 
						|
						return arg.WithILInstruction(inst); | 
						|
					} | 
						|
					goto default; | 
						|
				default: | 
						|
				{ | 
						|
					// We need to convert to inst.TargetType, or to an equivalent type. | 
						|
					IType targetType; | 
						|
					if (inst.TargetType == NullableType.GetUnderlyingType(context.TypeHint).ToPrimitiveType() | 
						|
						&& NullableType.IsNullable(context.TypeHint) == inst.IsLifted) | 
						|
					{ | 
						|
						targetType = context.TypeHint; | 
						|
					} | 
						|
					else if (inst.TargetType == IL.PrimitiveType.Ref) | 
						|
					{ | 
						|
						// converting to unknown ref-type | 
						|
						targetType = new ByReferenceType(compilation.FindType(KnownTypeCode.Byte)); | 
						|
					} | 
						|
					else if (inst.TargetType == IL.PrimitiveType.None) | 
						|
					{ | 
						|
						// convert to some object type | 
						|
						// (e.g. invalid I4->O conversion) | 
						|
						targetType = compilation.FindType(KnownTypeCode.Object); | 
						|
					} | 
						|
					else | 
						|
					{ | 
						|
						targetType = GetType(inst.TargetType.ToKnownTypeCode()); | 
						|
					} | 
						|
					return arg.ConvertTo(targetType, this, inst.CheckForOverflow) | 
						|
						.WithILInstruction(inst); | 
						|
				} | 
						|
			} | 
						|
		} | 
						|
 | 
						|
		/// <summary> | 
						|
		/// Gets whether the ResolveResult computes a value that might be oversized for the specified stack type. | 
						|
		/// </summary> | 
						|
		bool ValueMightBeOversized(ResolveResult rr, StackType stackType) | 
						|
		{ | 
						|
			IType inputType = NullableType.GetUnderlyingType(rr.Type); | 
						|
			if (inputType.GetSize() <= stackType.GetSize()) | 
						|
			{ | 
						|
				// The input type is smaller or equal to the stack type, | 
						|
				// it can't be an oversized value. | 
						|
				return false; | 
						|
			} | 
						|
			if (rr is OperatorResolveResult orr) | 
						|
			{ | 
						|
				if (stackType == StackType.I && orr.OperatorType == ExpressionType.Subtract | 
						|
					&& orr.Operands.Count == 2 | 
						|
					&& orr.Operands[0].Type.Kind == TypeKind.Pointer | 
						|
					&& orr.Operands[1].Type.Kind == TypeKind.Pointer) | 
						|
				{ | 
						|
					// Even though a pointer subtraction produces a value of type long in C#, | 
						|
					// the value will always fit in a native int. | 
						|
					return false; | 
						|
				} | 
						|
			} | 
						|
			// We don't have any information about the value, so it might be oversized. | 
						|
			return true; | 
						|
		} | 
						|
 | 
						|
		protected internal override TranslatedExpression VisitCall(Call inst, TranslationContext context) | 
						|
		{ | 
						|
			return WrapInRef(new CallBuilder(this, typeSystem, settings).Build(inst), inst.Method.ReturnType); | 
						|
		} | 
						|
 | 
						|
		protected internal override TranslatedExpression VisitCallVirt(CallVirt inst, TranslationContext context) | 
						|
		{ | 
						|
			return WrapInRef(new CallBuilder(this, typeSystem, settings).Build(inst), inst.Method.ReturnType); | 
						|
		} | 
						|
 | 
						|
		TranslatedExpression WrapInRef(TranslatedExpression expr, IType type) | 
						|
		{ | 
						|
			if (type.Kind == TypeKind.ByReference) | 
						|
			{ | 
						|
				return new DirectionExpression(FieldDirection.Ref, expr.Expression) | 
						|
					.WithoutILInstruction() | 
						|
					.WithRR(new ByReferenceResolveResult(expr.ResolveResult, ReferenceKind.Ref)); | 
						|
			} | 
						|
			return expr; | 
						|
		} | 
						|
 | 
						|
		internal bool IsCurrentOrContainingType(ITypeDefinition type) | 
						|
		{ | 
						|
			var currentTypeDefinition = decompilationContext.CurrentTypeDefinition; | 
						|
			while (currentTypeDefinition != null) | 
						|
			{ | 
						|
				if (type == currentTypeDefinition) | 
						|
					return true; | 
						|
				currentTypeDefinition = currentTypeDefinition.DeclaringTypeDefinition; | 
						|
			} | 
						|
			return false; | 
						|
		} | 
						|
 | 
						|
		internal bool IsBaseTypeOfCurrentType(ITypeDefinition type) | 
						|
		{ | 
						|
			return decompilationContext.CurrentTypeDefinition.GetAllBaseTypeDefinitions().Any(t => t == type); | 
						|
		} | 
						|
 | 
						|
		internal ExpressionWithResolveResult TranslateFunction(IType delegateType, ILFunction function) | 
						|
		{ | 
						|
			var method = function.Method?.MemberDefinition as IMethod; | 
						|
 | 
						|
			// Create AnonymousMethodExpression and prepare parameters | 
						|
			AnonymousMethodExpression ame = new AnonymousMethodExpression(); | 
						|
			ame.IsAsync = function.IsAsync; | 
						|
			ame.Parameters.AddRange(MakeParameters(function.Parameters, function)); | 
						|
			ame.HasParameterList = ame.Parameters.Count > 0; | 
						|
			var builder = new StatementBuilder( | 
						|
				typeSystem, | 
						|
				this.decompilationContext, | 
						|
				function, | 
						|
				settings, | 
						|
				statementBuilder.decompileRun, | 
						|
				cancellationToken | 
						|
			); | 
						|
			var body = builder.ConvertAsBlock(function.Body); | 
						|
 | 
						|
			Comment prev = null; | 
						|
			foreach (string warning in function.Warnings) | 
						|
			{ | 
						|
				body.InsertChildAfter(prev, prev = new Comment(warning), Roles.Comment); | 
						|
			} | 
						|
			var attributeSections = new List<AttributeSection>(); | 
						|
			foreach (var attr in method?.GetAttributes() ?? Enumerable.Empty<IAttribute>()) | 
						|
			{ | 
						|
				if (attr.AttributeType.IsKnownType(KnownAttribute.CompilerGenerated)) | 
						|
					continue; | 
						|
				if (function.IsAsync) | 
						|
				{ | 
						|
					if (attr.AttributeType.IsKnownType(KnownAttribute.AsyncStateMachine)) | 
						|
						continue; | 
						|
					if (attr.AttributeType.IsKnownType(KnownAttribute.DebuggerStepThrough)) | 
						|
						continue; | 
						|
				} | 
						|
				attributeSections.Add(new AttributeSection(astBuilder.ConvertAttribute(attr))); | 
						|
			} | 
						|
			foreach (var attr in method?.GetReturnTypeAttributes() ?? Enumerable.Empty<IAttribute>()) | 
						|
			{ | 
						|
				attributeSections.Add(new AttributeSection(astBuilder.ConvertAttribute(attr)) { AttributeTarget = "return" }); | 
						|
			} | 
						|
 | 
						|
			bool isLambda = false; | 
						|
			if (ame.Parameters.Any(p => p.Type.IsNull)) | 
						|
			{ | 
						|
				// if there is an anonymous type involved, we are forced to use a lambda expression. | 
						|
				isLambda = true; | 
						|
			} | 
						|
			else if (attributeSections.Count > 0 || ame.Parameters.Any(p => p.Attributes.Any())) | 
						|
			{ | 
						|
				// C# 10 lambdas can have attributes, but anonymous methods cannot | 
						|
				isLambda = true; | 
						|
			} | 
						|
			else if (settings.UseLambdaSyntax && ame.Parameters.All(p => p.ParameterModifier == ParameterModifier.None)) | 
						|
			{ | 
						|
				// otherwise use lambda only if an expression lambda is possible | 
						|
				isLambda = (body.Statements.Count == 1 && body.Statements.Single() is ReturnStatement); | 
						|
			} | 
						|
			// Remove the parameter list from an AnonymousMethodExpression if the parameters are not used in the method body | 
						|
			var parameterReferencingIdentifiers = | 
						|
				from ident in body.Descendants.OfType<IdentifierExpression>() | 
						|
				let v = ident.GetILVariable() | 
						|
				where v != null && v.Function == function && v.Kind == VariableKind.Parameter | 
						|
				select ident; | 
						|
			if (!isLambda && !parameterReferencingIdentifiers.Any()) | 
						|
			{ | 
						|
				ame.Parameters.Clear(); | 
						|
				ame.HasParameterList = false; | 
						|
			} | 
						|
 | 
						|
			Expression replacement; | 
						|
			IType inferredReturnType; | 
						|
			if (isLambda) | 
						|
			{ | 
						|
				LambdaExpression lambda = new LambdaExpression(); | 
						|
				lambda.Attributes.AddRange(attributeSections); | 
						|
				lambda.IsAsync = ame.IsAsync; | 
						|
				lambda.CopyAnnotationsFrom(ame); | 
						|
				ame.Parameters.MoveTo(lambda.Parameters); | 
						|
				if (body.Statements.Count == 1 && body.Statements.Single() is ReturnStatement returnStmt) | 
						|
				{ | 
						|
					lambda.Body = returnStmt.Expression.Detach(); | 
						|
					inferredReturnType = lambda.Body.GetResolveResult().Type; | 
						|
				} | 
						|
				else | 
						|
				{ | 
						|
					lambda.Body = body; | 
						|
					inferredReturnType = InferReturnType(body); | 
						|
				} | 
						|
				replacement = lambda; | 
						|
			} | 
						|
			else | 
						|
			{ | 
						|
				ame.Body = body; | 
						|
				inferredReturnType = InferReturnType(body); | 
						|
				replacement = ame; | 
						|
			} | 
						|
			if (ame.IsAsync) | 
						|
			{ | 
						|
				inferredReturnType = GetTaskType(inferredReturnType); | 
						|
			} | 
						|
 | 
						|
			var rr = new DecompiledLambdaResolveResult( | 
						|
				function, delegateType, inferredReturnType, | 
						|
				hasParameterList: isLambda || ame.HasParameterList, | 
						|
				isAnonymousMethod: !isLambda, | 
						|
				isImplicitlyTyped: ame.Parameters.Any(p => p.Type.IsNull)); | 
						|
 | 
						|
			TranslatedExpression translatedLambda = replacement.WithILInstruction(function).WithRR(rr); | 
						|
			return new CastExpression(ConvertType(delegateType), translatedLambda) | 
						|
				.WithRR(new ConversionResolveResult(delegateType, rr, LambdaConversion.Instance)); | 
						|
		} | 
						|
 | 
						|
		protected internal override TranslatedExpression VisitILFunction(ILFunction function, TranslationContext context) | 
						|
		{ | 
						|
			return TranslateFunction(function.DelegateType, function) | 
						|
				.WithILInstruction(function); | 
						|
		} | 
						|
 | 
						|
		IType InferReturnType(BlockStatement body) | 
						|
		{ | 
						|
			var returnExpressions = new List<ResolveResult>(); | 
						|
			CollectReturnExpressions(body); | 
						|
			var ti = new TypeInference(compilation, resolver.conversions); | 
						|
			return ti.GetBestCommonType(returnExpressions, out _); | 
						|
			// Failure to infer a return type does not make the lambda invalid, | 
						|
			// so we can ignore the 'success' value | 
						|
 | 
						|
			void CollectReturnExpressions(AstNode node) | 
						|
			{ | 
						|
				if (node is ReturnStatement ret) | 
						|
				{ | 
						|
					if (!ret.Expression.IsNull) | 
						|
					{ | 
						|
						returnExpressions.Add(ret.Expression.GetResolveResult()); | 
						|
					} | 
						|
				} | 
						|
				else if (node is LambdaExpression || node is AnonymousMethodExpression) | 
						|
				{ | 
						|
					// do not recurse into nested lambdas | 
						|
					return; | 
						|
				} | 
						|
				foreach (var child in node.Children) | 
						|
				{ | 
						|
					CollectReturnExpressions(child); | 
						|
				} | 
						|
			} | 
						|
		} | 
						|
 | 
						|
		IType GetTaskType(IType resultType) | 
						|
		{ | 
						|
			if (resultType.Kind == TypeKind.Unknown) | 
						|
				return SpecialType.UnknownType; | 
						|
			if (resultType.Kind == TypeKind.Void) | 
						|
				return compilation.FindType(KnownTypeCode.Task); | 
						|
 | 
						|
			ITypeDefinition def = compilation.FindType(KnownTypeCode.TaskOfT).GetDefinition(); | 
						|
			if (def != null) | 
						|
				return new ParameterizedType(def, new[] { resultType }); | 
						|
			else | 
						|
				return SpecialType.UnknownType; | 
						|
		} | 
						|
 | 
						|
		IEnumerable<ParameterDeclaration> MakeParameters(IReadOnlyList<IParameter> parameters, ILFunction function) | 
						|
		{ | 
						|
			var variables = function.Variables.Where(v => v.Kind == VariableKind.Parameter).ToDictionary(v => v.Index); | 
						|
			int i = 0; | 
						|
			foreach (var parameter in parameters) | 
						|
			{ | 
						|
				var pd = astBuilder.ConvertParameter(parameter); | 
						|
				if (string.IsNullOrEmpty(pd.Name) && !pd.Type.IsArgList()) | 
						|
				{ | 
						|
					// needs to be consistent with logic in ILReader.CreateILVarable(ParameterDefinition) | 
						|
					pd.Name = "P_" + i; | 
						|
				} | 
						|
				if (settings.AnonymousTypes && parameter.Type.ContainsAnonymousType()) | 
						|
					pd.Type = null; | 
						|
				if (variables.TryGetValue(i, out var v)) | 
						|
					pd.AddAnnotation(new ILVariableResolveResult(v, parameters[i].Type)); | 
						|
				yield return pd; | 
						|
				i++; | 
						|
			} | 
						|
		} | 
						|
 | 
						|
		protected internal override TranslatedExpression VisitBlockContainer(BlockContainer container, TranslationContext context) | 
						|
		{ | 
						|
			var oldReturnContainer = statementBuilder.currentReturnContainer; | 
						|
			var oldResultType = statementBuilder.currentResultType; | 
						|
			var oldIsIterator = statementBuilder.currentIsIterator; | 
						|
 | 
						|
			statementBuilder.currentReturnContainer = container; | 
						|
			statementBuilder.currentResultType = context.TypeHint; | 
						|
			statementBuilder.currentIsIterator = false; | 
						|
			try | 
						|
			{ | 
						|
				var body = statementBuilder.ConvertAsBlock(container); | 
						|
				var comment = new Comment(" Could not convert BlockContainer to single expression"); | 
						|
				body.InsertChildAfter(null, comment, Roles.Comment); | 
						|
				// set ILVariable.UsesInitialValue for any variables being used inside the container | 
						|
				foreach (var stloc in container.Descendants.OfType<StLoc>()) | 
						|
					stloc.Variable.UsesInitialValue = true; | 
						|
				var ame = new AnonymousMethodExpression { Body = body }; | 
						|
				var systemFuncType = compilation.FindType(typeof(Func<>)); | 
						|
				var blockReturnType = InferReturnType(body); | 
						|
				var delegateType = new ParameterizedType(systemFuncType, blockReturnType); | 
						|
				var invocationTarget = new CastExpression(ConvertType(delegateType), ame); | 
						|
				ResolveResult rr; | 
						|
				// This might happen when trying to decompile an assembly built for a target framework | 
						|
				// where System.Func<T> does not exist yet. | 
						|
				if (systemFuncType.Kind == TypeKind.Unknown) | 
						|
				{ | 
						|
					rr = new ResolveResult(blockReturnType); | 
						|
				} | 
						|
				else | 
						|
				{ | 
						|
					var invokeMethod = delegateType.GetDelegateInvokeMethod(); | 
						|
					rr = new CSharpInvocationResolveResult( | 
						|
						new ResolveResult(delegateType), | 
						|
						invokeMethod, | 
						|
						EmptyList<ResolveResult>.Instance); | 
						|
				} | 
						|
				return new InvocationExpression(new MemberReferenceExpression(invocationTarget, "Invoke")) | 
						|
					.WithILInstruction(container) | 
						|
					.WithRR(rr); | 
						|
			} | 
						|
			finally | 
						|
			{ | 
						|
				statementBuilder.currentReturnContainer = oldReturnContainer; | 
						|
				statementBuilder.currentResultType = oldResultType; | 
						|
				statementBuilder.currentIsIterator = oldIsIterator; | 
						|
			} | 
						|
		} | 
						|
 | 
						|
		internal TranslatedExpression TranslateTarget(ILInstruction target, bool nonVirtualInvocation, | 
						|
			bool memberStatic, IType memberDeclaringType) | 
						|
		{ | 
						|
			// If references are missing member.IsStatic might not be set correctly. | 
						|
			// Additionally check target for null, in order to avoid a crash. | 
						|
			if (!memberStatic && target != null) | 
						|
			{ | 
						|
				if (ShouldUseBaseReference()) | 
						|
				{ | 
						|
					var baseReferenceType = resolver.CurrentTypeDefinition.DirectBaseTypes | 
						|
						.FirstOrDefault(t => t.Kind != TypeKind.Interface); | 
						|
					return new BaseReferenceExpression() | 
						|
						.WithILInstruction(target) | 
						|
						.WithRR(new ThisResolveResult(baseReferenceType ?? memberDeclaringType, nonVirtualInvocation)); | 
						|
				} | 
						|
				else | 
						|
				{ | 
						|
					IType targetTypeHint = memberDeclaringType; | 
						|
					if (CallInstruction.ExpectedTypeForThisPointer(memberDeclaringType) == StackType.Ref) | 
						|
					{ | 
						|
						if (target.ResultType == StackType.Ref) | 
						|
						{ | 
						|
							targetTypeHint = new ByReferenceType(targetTypeHint); | 
						|
						} | 
						|
						else | 
						|
						{ | 
						|
							targetTypeHint = new PointerType(targetTypeHint); | 
						|
						} | 
						|
					} | 
						|
					var translatedTarget = Translate(target, targetTypeHint); | 
						|
					if (CallInstruction.ExpectedTypeForThisPointer(memberDeclaringType) == StackType.Ref) | 
						|
					{ | 
						|
						// When accessing members on value types, ensure we use a reference of the correct type, | 
						|
						// and not a pointer or a reference to a different type (issue #1333) | 
						|
						if (!(translatedTarget.Type is ByReferenceType brt && NormalizeTypeVisitor.TypeErasure.EquivalentTypes(brt.ElementType, memberDeclaringType))) | 
						|
						{ | 
						|
							translatedTarget = translatedTarget.ConvertTo(new ByReferenceType(memberDeclaringType), this); | 
						|
						} | 
						|
					} | 
						|
					if (translatedTarget.Expression is DirectionExpression) | 
						|
					{ | 
						|
						// (ref x).member => x.member | 
						|
						translatedTarget = translatedTarget.UnwrapChild(((DirectionExpression)translatedTarget).Expression); | 
						|
					} | 
						|
					else if (translatedTarget.Expression is UnaryOperatorExpression uoe | 
						|
					  && uoe.Operator == UnaryOperatorType.NullConditional | 
						|
					  && uoe.Expression is DirectionExpression) | 
						|
					{ | 
						|
						// (ref x)?.member => x?.member | 
						|
						translatedTarget = translatedTarget.UnwrapChild(((DirectionExpression)uoe.Expression).Expression); | 
						|
						// note: we need to create a new ResolveResult for the null-conditional operator, | 
						|
						// using the underlying type of the input expression without the DirectionExpression | 
						|
						translatedTarget = new UnaryOperatorExpression(UnaryOperatorType.NullConditional, translatedTarget) | 
						|
							.WithRR(new ResolveResult(NullableType.GetUnderlyingType(translatedTarget.Type))) | 
						|
							.WithoutILInstruction(); | 
						|
					} | 
						|
					translatedTarget = EnsureTargetNotNullable(translatedTarget, target); | 
						|
					return translatedTarget; | 
						|
				} | 
						|
			} | 
						|
			else | 
						|
			{ | 
						|
				return new TypeReferenceExpression(ConvertType(memberDeclaringType)) | 
						|
					.WithoutILInstruction() | 
						|
					.WithRR(new TypeResolveResult(memberDeclaringType)); | 
						|
			} | 
						|
 | 
						|
			bool ShouldUseBaseReference() | 
						|
			{ | 
						|
				if (!nonVirtualInvocation) | 
						|
					return false; | 
						|
				if (!MatchLdThis(target)) | 
						|
					return false; | 
						|
				if (memberDeclaringType.GetDefinition() == resolver.CurrentTypeDefinition) | 
						|
					return false; | 
						|
				return true; | 
						|
			} | 
						|
 | 
						|
			bool MatchLdThis(ILInstruction inst) | 
						|
			{ | 
						|
				// ldloc this | 
						|
				if (inst.MatchLdThis()) | 
						|
					return true; | 
						|
				if (resolver.CurrentTypeDefinition.Kind == TypeKind.Struct) | 
						|
				{ | 
						|
					// box T(ldobj T(ldloc this)) | 
						|
					if (!inst.MatchBox(out var arg, out var type)) | 
						|
						return false; | 
						|
					if (!arg.MatchLdObj(out var arg2, out var type2)) | 
						|
						return false; | 
						|
					if (!type.Equals(type2) || !type.Equals(resolver.CurrentTypeDefinition)) | 
						|
						return false; | 
						|
					return arg2.MatchLdThis(); | 
						|
				} | 
						|
				return false; | 
						|
			} | 
						|
		} | 
						|
 | 
						|
		private TranslatedExpression EnsureTargetNotNullable(TranslatedExpression expr, ILInstruction inst) | 
						|
		{ | 
						|
			/* | 
						|
			// TODO Improve nullability support so that we do not sprinkle ! operators everywhere. | 
						|
			// inst is the instruction that got translated into expr. | 
						|
			if (expr.Type.Nullability == Nullability.Nullable) | 
						|
			{ | 
						|
				if (expr.Expression is UnaryOperatorExpression uoe && uoe.Operator == UnaryOperatorType.NullConditional) | 
						|
				{ | 
						|
					return expr; | 
						|
				} | 
						|
				if (inst.HasFlag(InstructionFlags.MayUnwrapNull)) | 
						|
				{ | 
						|
					// We can't use ! in the chain of operators after a NullConditional, due to | 
						|
					// https://github.com/dotnet/roslyn/issues/43659 | 
						|
					return expr; | 
						|
				} | 
						|
				return new UnaryOperatorExpression(UnaryOperatorType.SuppressNullableWarning, expr) | 
						|
					.WithRR(new ResolveResult(expr.Type.ChangeNullability(Nullability.Oblivious))) | 
						|
					.WithoutILInstruction(); | 
						|
			} | 
						|
			*/ | 
						|
			return expr; | 
						|
		} | 
						|
 | 
						|
		protected internal override TranslatedExpression VisitLdObj(LdObj inst, TranslationContext context) | 
						|
		{ | 
						|
			IType loadType = inst.Type; | 
						|
			bool loadTypeUsedInGeneric = inst.UnalignedPrefix != 0 || inst.Target.ResultType == StackType.Ref; | 
						|
			if (context.TypeHint.Kind != TypeKind.Unknown | 
						|
				&& TypeUtils.IsCompatibleTypeForMemoryAccess(context.TypeHint, loadType) | 
						|
				&& !(loadTypeUsedInGeneric && context.TypeHint.Kind.IsAnyPointer())) | 
						|
			{ | 
						|
				loadType = context.TypeHint; | 
						|
			} | 
						|
			if (inst.UnalignedPrefix != 0) | 
						|
			{ | 
						|
				// Use one of: Unsafe.ReadUnaligned<T>(void*) | 
						|
				//         or: Unsafe.ReadUnaligned<T>(ref byte) | 
						|
				var pointer = Translate(inst.Target); | 
						|
				if (pointer.Expression is DirectionExpression) | 
						|
				{ | 
						|
					pointer = pointer.ConvertTo(new ByReferenceType(compilation.FindType(KnownTypeCode.Byte)), this); | 
						|
				} | 
						|
				else | 
						|
				{ | 
						|
					pointer = pointer.ConvertTo(new PointerType(compilation.FindType(KnownTypeCode.Void)), this, allowImplicitConversion: true); | 
						|
				} | 
						|
				return CallUnsafeIntrinsic( | 
						|
					name: "ReadUnaligned", | 
						|
					arguments: new Expression[] { pointer }, | 
						|
					returnType: loadType, | 
						|
					inst: inst, | 
						|
					typeArguments: new IType[] { loadType } | 
						|
				); | 
						|
			} | 
						|
			var result = LdObj(inst.Target, loadType); | 
						|
			//if (target.Type.IsSmallIntegerType() && loadType.IsSmallIntegerType() && target.Type.GetSign() != loadType.GetSign()) | 
						|
			//	return result.ConvertTo(loadType, this); | 
						|
			return result.WithILInstruction(inst); | 
						|
		} | 
						|
 | 
						|
		ExpressionWithResolveResult LdObj(ILInstruction address, IType loadType) | 
						|
		{ | 
						|
			IType addressTypeHint = address.ResultType == StackType.Ref ? new ByReferenceType(loadType) : (IType)new PointerType(loadType); | 
						|
			var target = Translate(address, typeHint: addressTypeHint); | 
						|
			if (TypeUtils.IsCompatiblePointerTypeForMemoryAccess(target.Type, loadType)) | 
						|
			{ | 
						|
				ExpressionWithResolveResult result; | 
						|
				if (target.Expression is DirectionExpression dirExpr) | 
						|
				{ | 
						|
					// we can dereference the managed reference by stripping away the 'ref' | 
						|
					result = target.UnwrapChild(dirExpr.Expression); | 
						|
				} | 
						|
				else if (target.Type is PointerType pointerType) | 
						|
				{ | 
						|
					if (target.Expression is UnaryOperatorExpression uoe && uoe.Operator == UnaryOperatorType.AddressOf) | 
						|
					{ | 
						|
						// We can dereference the pointer by stripping away the '&' | 
						|
						result = target.UnwrapChild(uoe.Expression); | 
						|
					} | 
						|
					else | 
						|
					{ | 
						|
						// Dereference the existing pointer | 
						|
						result = new UnaryOperatorExpression(UnaryOperatorType.Dereference, target.Expression) | 
						|
							.WithRR(new ResolveResult(pointerType.ElementType)); | 
						|
					} | 
						|
				} | 
						|
				else | 
						|
				{ | 
						|
					// reference type behind non-DirectionExpression? | 
						|
					// this case should be impossible, but we can use a pointer cast | 
						|
					// just to make sure | 
						|
					target = target.ConvertTo(new PointerType(loadType), this); | 
						|
					return new UnaryOperatorExpression(UnaryOperatorType.Dereference, target.Expression) | 
						|
						.WithRR(new ResolveResult(loadType)); | 
						|
				} | 
						|
				// we don't convert result to inst.Type, because the LdObj type | 
						|
				// might be inaccurate (it's often System.Object for all reference types), | 
						|
				// and our parent node should already insert casts where necessary | 
						|
				return result; | 
						|
			} | 
						|
			else | 
						|
			{ | 
						|
				// We need to cast the pointer type: | 
						|
				if (target.Expression is DirectionExpression) | 
						|
				{ | 
						|
					target = target.ConvertTo(new ByReferenceType(loadType), this); | 
						|
				} | 
						|
				else | 
						|
				{ | 
						|
					target = target.ConvertTo(new PointerType(loadType), this); | 
						|
				} | 
						|
				if (target.Expression is DirectionExpression dirExpr) | 
						|
				{ | 
						|
					return target.UnwrapChild(dirExpr.Expression); | 
						|
				} | 
						|
				else | 
						|
				{ | 
						|
					return new UnaryOperatorExpression(UnaryOperatorType.Dereference, target.Expression) | 
						|
						.WithRR(new ResolveResult(loadType)); | 
						|
				} | 
						|
			} | 
						|
		} | 
						|
 | 
						|
		protected internal override TranslatedExpression VisitStObj(StObj inst, TranslationContext context) | 
						|
		{ | 
						|
			if (inst.UnalignedPrefix != 0) | 
						|
			{ | 
						|
				return UnalignedStObj(inst); | 
						|
			} | 
						|
 | 
						|
			IType pointerTypeHint = inst.Target.ResultType == StackType.Ref ? new ByReferenceType(inst.Type) : (IType)new PointerType(inst.Type); | 
						|
			var pointer = Translate(inst.Target, typeHint: pointerTypeHint); | 
						|
			TranslatedExpression target; | 
						|
			TranslatedExpression value = default; | 
						|
			IType memoryType; | 
						|
			// Check if we need to cast to pointer type: | 
						|
			if (TypeUtils.IsCompatiblePointerTypeForMemoryAccess(pointer.Type, inst.Type)) | 
						|
			{ | 
						|
				// cast not necessary, we can use the existing type | 
						|
				memoryType = ((TypeWithElementType)pointer.Type).ElementType; | 
						|
			} | 
						|
			else | 
						|
			{ | 
						|
				// We need to introduce a pointer cast | 
						|
				value = Translate(inst.Value, typeHint: inst.Type); | 
						|
				if (TypeUtils.IsCompatibleTypeForMemoryAccess(value.Type, inst.Type)) | 
						|
				{ | 
						|
					memoryType = value.Type; | 
						|
				} | 
						|
				else | 
						|
				{ | 
						|
					memoryType = inst.Type; | 
						|
				} | 
						|
				if (pointer.Expression is DirectionExpression) | 
						|
				{ | 
						|
					pointer = pointer.ConvertTo(new ByReferenceType(memoryType), this); | 
						|
				} | 
						|
				else | 
						|
				{ | 
						|
					pointer = pointer.ConvertTo(new PointerType(memoryType), this); | 
						|
				} | 
						|
			} | 
						|
 | 
						|
			if (pointer.Expression is DirectionExpression) | 
						|
			{ | 
						|
				// we can deference the managed reference by stripping away the 'ref' | 
						|
				target = pointer.UnwrapChild(((DirectionExpression)pointer.Expression).Expression); | 
						|
			} | 
						|
			else | 
						|
			{ | 
						|
				if (pointer.Expression is UnaryOperatorExpression uoe && uoe.Operator == UnaryOperatorType.AddressOf) | 
						|
				{ | 
						|
					// *&ptr -> ptr | 
						|
					target = pointer.UnwrapChild(uoe.Expression); | 
						|
				} | 
						|
				else | 
						|
				{ | 
						|
					target = new UnaryOperatorExpression(UnaryOperatorType.Dereference, pointer.Expression) | 
						|
						.WithoutILInstruction() | 
						|
						.WithRR(new ResolveResult(memoryType)); | 
						|
				} | 
						|
			} | 
						|
			if (value.Expression == null) | 
						|
			{ | 
						|
				value = Translate(inst.Value, typeHint: target.Type); | 
						|
			} | 
						|
			if (target.Expression is DirectionExpression dirExpr && target.ResolveResult is ByReferenceResolveResult lhsRefRR) | 
						|
			{ | 
						|
				// ref (re-)assignment, emit "ref (a = ref b)". | 
						|
				target = target.UnwrapChild(dirExpr.Expression); | 
						|
				value = value.ConvertTo(lhsRefRR.Type, this, allowImplicitConversion: true); | 
						|
				var assign = new AssignmentExpression(target.Expression, value.Expression) | 
						|
					.WithRR(new OperatorResolveResult(target.Type, ExpressionType.Assign, lhsRefRR, value.ResolveResult)); | 
						|
				return new DirectionExpression(FieldDirection.Ref, assign) | 
						|
					.WithoutILInstruction().WithRR(lhsRefRR); | 
						|
			} | 
						|
			else | 
						|
			{ | 
						|
				return Assignment(target, value).WithILInstruction(inst); | 
						|
			} | 
						|
		} | 
						|
 | 
						|
		private TranslatedExpression UnalignedStObj(StObj inst) | 
						|
		{ | 
						|
			// "unaligned.1; stobj" -> decompile to a call of | 
						|
			//    Unsafe.WriteUnaligned<T>(void*, T) | 
						|
			// or Unsafe.WriteUnaligned<T>(ref byte, T) | 
						|
			var pointer = Translate(inst.Target); | 
						|
			var value = Translate(inst.Value, typeHint: inst.Type); | 
						|
			if (pointer.Expression is DirectionExpression) | 
						|
			{ | 
						|
				pointer = pointer.ConvertTo(new ByReferenceType(compilation.FindType(KnownTypeCode.Byte)), this); | 
						|
			} | 
						|
			else | 
						|
			{ | 
						|
				pointer = pointer.ConvertTo(new PointerType(compilation.FindType(KnownTypeCode.Void)), this, allowImplicitConversion: true); | 
						|
			} | 
						|
			if (!TypeUtils.IsCompatibleTypeForMemoryAccess(value.Type, inst.Type)) | 
						|
			{ | 
						|
				value = value.ConvertTo(inst.Type, this); | 
						|
			} | 
						|
			return CallUnsafeIntrinsic( | 
						|
				name: "WriteUnaligned", | 
						|
				arguments: new Expression[] { pointer, value }, | 
						|
				returnType: compilation.FindType(KnownTypeCode.Void), | 
						|
				inst: inst | 
						|
			); | 
						|
		} | 
						|
 | 
						|
		protected internal override TranslatedExpression VisitLdLen(LdLen inst, TranslationContext context) | 
						|
		{ | 
						|
			IType arrayType = compilation.FindType(KnownTypeCode.Array); | 
						|
			TranslatedExpression arrayExpr = Translate(inst.Array, typeHint: arrayType); | 
						|
			if (arrayExpr.Type.Kind != TypeKind.Array) | 
						|
			{ | 
						|
				arrayExpr = arrayExpr.ConvertTo(arrayType, this); | 
						|
			} | 
						|
			arrayExpr = EnsureTargetNotNullable(arrayExpr, inst.Array); | 
						|
			string memberName; | 
						|
			KnownTypeCode code; | 
						|
			if (inst.ResultType == StackType.I4) | 
						|
			{ | 
						|
				memberName = "Length"; | 
						|
				code = KnownTypeCode.Int32; | 
						|
			} | 
						|
			else | 
						|
			{ | 
						|
				memberName = "LongLength"; | 
						|
				code = KnownTypeCode.Int64; | 
						|
			} | 
						|
			IProperty member = arrayType.GetProperties(p => p.Name == memberName).FirstOrDefault(); | 
						|
			ResolveResult rr = member == null | 
						|
				? new ResolveResult(compilation.FindType(code)) | 
						|
				: new MemberResolveResult(arrayExpr.ResolveResult, member); | 
						|
			return new MemberReferenceExpression(arrayExpr.Expression, memberName) | 
						|
				.WithILInstruction(inst) | 
						|
				.WithRR(rr); | 
						|
		} | 
						|
 | 
						|
		protected internal override TranslatedExpression VisitLdFlda(LdFlda inst, TranslationContext context) | 
						|
		{ | 
						|
			if (settings.FixedBuffers && inst.Field.Name == "FixedElementField" | 
						|
				&& inst.Target is LdFlda nestedLdFlda | 
						|
				&& CSharpDecompiler.IsFixedField(nestedLdFlda.Field, out var elementType, out _)) | 
						|
			{ | 
						|
				Expression fieldAccess = ConvertField(nestedLdFlda.Field, nestedLdFlda.Target); | 
						|
				var mrr = (MemberResolveResult)fieldAccess.GetResolveResult(); | 
						|
				fieldAccess.RemoveAnnotations<ResolveResult>(); | 
						|
				var result = fieldAccess.WithRR(new MemberResolveResult(mrr.TargetResult, mrr.Member, new PointerType(elementType))) | 
						|
					.WithILInstruction(inst); | 
						|
				if (inst.ResultType == StackType.Ref) | 
						|
				{ | 
						|
					// `target.field` has pointer-type. | 
						|
					if (inst.SlotInfo == PinnedRegion.InitSlot || inst.Parent is Conv { TargetType: IL.PrimitiveType.U }) | 
						|
					{ | 
						|
						// Convert pointer to ref if we're in a context where we're going to convert | 
						|
						// the ref back to a pointer. | 
						|
						return result.ConvertTo(new ByReferenceType(elementType), this); | 
						|
					} | 
						|
					else | 
						|
					{ | 
						|
						// We can't use `ref *target.field` unless `target` is non-movable, | 
						|
						// but we can use `ref target.field[0]`. | 
						|
						var arrayAccess = new IndexerExpression(result, new PrimitiveExpression(0)) | 
						|
							.WithRR(new ResolveResult(elementType)); | 
						|
						return new DirectionExpression(FieldDirection.Ref, arrayAccess) | 
						|
							.WithoutILInstruction().WithRR(new ByReferenceResolveResult(arrayAccess.ResolveResult, ReferenceKind.Ref)); | 
						|
					} | 
						|
				} | 
						|
				else | 
						|
				{ | 
						|
					return result; | 
						|
				} | 
						|
			} | 
						|
			TranslatedExpression expr; | 
						|
			if (TupleTransform.MatchTupleFieldAccess(inst, out IType underlyingTupleType, out var target, out int position)) | 
						|
			{ | 
						|
				var translatedTarget = TranslateTarget(target, | 
						|
					nonVirtualInvocation: true, | 
						|
					memberStatic: false, | 
						|
					memberDeclaringType: underlyingTupleType); | 
						|
				if (translatedTarget.Type is TupleType tupleType && NormalizeTypeVisitor.TypeErasure.EquivalentTypes(tupleType, underlyingTupleType) && position <= tupleType.ElementNames.Length) | 
						|
				{ | 
						|
					string elementName = tupleType.ElementNames[position - 1]; | 
						|
					if (elementName == null) | 
						|
					{ | 
						|
						elementName = "Item" + position; | 
						|
					} | 
						|
					// tupleType.ElementTypes are more accurate w.r.t. nullability/dynamic than inst.Field.Type | 
						|
					var rr = new MemberResolveResult(translatedTarget.ResolveResult, inst.Field, | 
						|
						returnTypeOverride: tupleType.ElementTypes[position - 1]); | 
						|
					expr = new MemberReferenceExpression(translatedTarget, elementName) | 
						|
						.WithRR(rr).WithILInstruction(inst); | 
						|
				} | 
						|
				else | 
						|
				{ | 
						|
					expr = ConvertField(inst.Field, inst.Target).WithILInstruction(inst); | 
						|
				} | 
						|
			} | 
						|
			else | 
						|
			{ | 
						|
				expr = ConvertField(inst.Field, inst.Target).WithILInstruction(inst); | 
						|
			} | 
						|
			if (inst.ResultType == StackType.I) | 
						|
			{ | 
						|
				// ldflda producing native pointer | 
						|
				return new UnaryOperatorExpression(UnaryOperatorType.AddressOf, expr) | 
						|
					.WithoutILInstruction().WithRR(new ResolveResult(new PointerType(expr.Type))); | 
						|
			} | 
						|
			else | 
						|
			{ | 
						|
				// ldflda producing managed pointer | 
						|
				return new DirectionExpression(FieldDirection.Ref, expr) | 
						|
					.WithoutILInstruction().WithRR(new ByReferenceResolveResult(expr.ResolveResult, ReferenceKind.Ref)); | 
						|
			} | 
						|
		} | 
						|
 | 
						|
		protected internal override TranslatedExpression VisitLdsFlda(LdsFlda inst, TranslationContext context) | 
						|
		{ | 
						|
			var expr = ConvertField(inst.Field).WithILInstruction(inst); | 
						|
			return new DirectionExpression(FieldDirection.Ref, expr) | 
						|
				.WithoutILInstruction().WithRR(new ByReferenceResolveResult(expr.ResolveResult, ReferenceKind.Ref)); | 
						|
		} | 
						|
 | 
						|
		protected internal override TranslatedExpression VisitLdElema(LdElema inst, TranslationContext context) | 
						|
		{ | 
						|
			TranslatedExpression arrayExpr = Translate(inst.Array); | 
						|
			var arrayType = arrayExpr.Type as ArrayType; | 
						|
			if (arrayType == null || !TypeUtils.IsCompatibleTypeForMemoryAccess(arrayType.ElementType, inst.Type)) | 
						|
			{ | 
						|
				arrayType = new ArrayType(compilation, inst.Type, inst.Indices.Count); | 
						|
				arrayExpr = arrayExpr.ConvertTo(arrayType, this); | 
						|
			} | 
						|
			IndexerExpression indexerExpr; | 
						|
			if (inst.WithSystemIndex) | 
						|
			{ | 
						|
				var systemIndex = compilation.FindType(KnownTypeCode.Index); | 
						|
				indexerExpr = new IndexerExpression( | 
						|
					arrayExpr, inst.Indices.Select(i => Translate(i, typeHint: systemIndex).ConvertTo(systemIndex, this).Expression) | 
						|
				); | 
						|
			} | 
						|
			else | 
						|
			{ | 
						|
				indexerExpr = new IndexerExpression( | 
						|
					arrayExpr, inst.Indices.Select(i => TranslateArrayIndex(i).Expression) | 
						|
				); | 
						|
			} | 
						|
			TranslatedExpression expr = indexerExpr.WithILInstruction(inst).WithRR(new ResolveResult(arrayType.ElementType)); | 
						|
			return new DirectionExpression(FieldDirection.Ref, expr) | 
						|
				.WithoutILInstruction().WithRR(new ByReferenceResolveResult(expr.ResolveResult, ReferenceKind.Ref)); | 
						|
		} | 
						|
 | 
						|
		TranslatedExpression TranslateArrayIndex(ILInstruction i) | 
						|
		{ | 
						|
			var input = Translate(i); | 
						|
			return ConvertArrayIndex(input, i.ResultType, allowIntPtr: false); | 
						|
		} | 
						|
 | 
						|
		TranslatedExpression ConvertArrayIndex(TranslatedExpression input, StackType stackType, bool allowIntPtr) | 
						|
		{ | 
						|
			if (input.Type.GetSize() > stackType.GetSize()) | 
						|
			{ | 
						|
				// truncate oversized result | 
						|
				return input.ConvertTo(FindType(stackType, input.Type.GetSign()), this); | 
						|
			} | 
						|
			if (input.Type.IsCSharpPrimitiveIntegerType() || input.Type.IsCSharpNativeIntegerType()) | 
						|
			{ | 
						|
				// can be used as array index as-is | 
						|
				return input; | 
						|
			} | 
						|
			if (allowIntPtr && (input.Type.IsKnownType(KnownTypeCode.IntPtr) || input.Type.IsKnownType(KnownTypeCode.UIntPtr))) | 
						|
			{ | 
						|
				return input; | 
						|
			} | 
						|
			if (stackType != StackType.I4 && input.Type.GetStackType() == StackType.I4) | 
						|
			{ | 
						|
				// prefer casting to int if that's big enough | 
						|
				stackType = StackType.I4; | 
						|
			} | 
						|
			IType targetType = FindArithmeticType(stackType, input.Type.GetSign()); | 
						|
			return input.ConvertTo(targetType, this); | 
						|
		} | 
						|
 | 
						|
		internal static bool IsUnboxAnyWithIsInst(UnboxAny unboxAny, IsInst isInst) | 
						|
		{ | 
						|
			return unboxAny.Type.Equals(isInst.Type) | 
						|
				&& (unboxAny.Type.IsKnownType(KnownTypeCode.NullableOfT) || isInst.Type.IsReferenceType == true); | 
						|
		} | 
						|
 | 
						|
		protected internal override TranslatedExpression VisitUnboxAny(UnboxAny inst, TranslationContext context) | 
						|
		{ | 
						|
			TranslatedExpression arg; | 
						|
			if (inst.Argument is IsInst isInst && IsUnboxAnyWithIsInst(inst, isInst)) | 
						|
			{ | 
						|
				// unbox.any T(isinst T(expr)) ==> expr as T | 
						|
				// This is used for generic types and nullable value types | 
						|
				arg = UnwrapBoxingConversion(Translate(isInst.Argument)); | 
						|
				return new AsExpression(arg, ConvertType(inst.Type)) | 
						|
					.WithILInstruction(inst) | 
						|
					.WithRR(new ConversionResolveResult(inst.Type, arg.ResolveResult, Conversion.TryCast)); | 
						|
			} | 
						|
 | 
						|
			arg = Translate(inst.Argument); | 
						|
			IType targetType = inst.Type; | 
						|
			if (targetType.Kind == TypeKind.TypeParameter) | 
						|
			{ | 
						|
				var rr = resolver.ResolveCast(targetType, arg.ResolveResult); | 
						|
				if (rr.IsError) | 
						|
				{ | 
						|
					// C# 6.2.7 Explicit conversions involving type parameters: | 
						|
					// if we can't directly convert to a type parameter, | 
						|
					// try via its effective base class. | 
						|
					arg = arg.ConvertTo(((ITypeParameter)targetType).EffectiveBaseClass, this); | 
						|
				} | 
						|
			} | 
						|
			else | 
						|
			{ | 
						|
				// Before unboxing arg must be a object | 
						|
				arg = arg.ConvertTo(compilation.FindType(KnownTypeCode.Object), this); | 
						|
			} | 
						|
 | 
						|
			return new CastExpression(ConvertType(targetType), arg.Expression) | 
						|
				.WithILInstruction(inst) | 
						|
				.WithRR(new ConversionResolveResult(targetType, arg.ResolveResult, Conversion.UnboxingConversion)); | 
						|
		} | 
						|
 | 
						|
		protected internal override TranslatedExpression VisitUnbox(Unbox inst, TranslationContext context) | 
						|
		{ | 
						|
			var arg = Translate(inst.Argument); | 
						|
			var castExpression = new CastExpression(ConvertType(inst.Type), arg.Expression) | 
						|
				.WithRR(new ConversionResolveResult(inst.Type, arg.ResolveResult, Conversion.UnboxingConversion)); | 
						|
			return new DirectionExpression(FieldDirection.Ref, castExpression) | 
						|
				.WithILInstruction(inst) | 
						|
				.WithRR(new ByReferenceResolveResult(castExpression.ResolveResult, ReferenceKind.Ref)); | 
						|
		} | 
						|
 | 
						|
		protected internal override TranslatedExpression VisitBox(Box inst, TranslationContext context) | 
						|
		{ | 
						|
			IType targetType = inst.Type; | 
						|
			var arg = Translate(inst.Argument, typeHint: targetType); | 
						|
			if (settings.NativeIntegers && !arg.Type.Equals(targetType)) | 
						|
			{ | 
						|
				if (targetType.IsKnownType(KnownTypeCode.IntPtr)) | 
						|
				{ | 
						|
					targetType = SpecialType.NInt; | 
						|
				} | 
						|
				else if (targetType.IsKnownType(KnownTypeCode.UIntPtr)) | 
						|
				{ | 
						|
					targetType = SpecialType.NUInt; | 
						|
				} | 
						|
			} | 
						|
			arg = arg.ConvertTo(targetType, this); | 
						|
			var obj = compilation.FindType(KnownTypeCode.Object); | 
						|
			return new CastExpression(ConvertType(obj), arg.Expression) | 
						|
				.WithILInstruction(inst) | 
						|
				.WithRR(new ConversionResolveResult(obj, arg.ResolveResult, Conversion.BoxingConversion)); | 
						|
		} | 
						|
 | 
						|
		protected internal override TranslatedExpression VisitCastClass(CastClass inst, TranslationContext context) | 
						|
		{ | 
						|
			return Translate(inst.Argument).ConvertTo(inst.Type, this); | 
						|
		} | 
						|
 | 
						|
		protected internal override TranslatedExpression VisitExpressionTreeCast(ExpressionTreeCast inst, TranslationContext context) | 
						|
		{ | 
						|
			return Translate(inst.Argument).ConvertTo(inst.Type, this, inst.IsChecked); | 
						|
		} | 
						|
 | 
						|
		protected internal override TranslatedExpression VisitArglist(Arglist inst, TranslationContext context) | 
						|
		{ | 
						|
			return new UndocumentedExpression { UndocumentedExpressionType = UndocumentedExpressionType.ArgListAccess } | 
						|
			.WithILInstruction(inst) | 
						|
				.WithRR(new TypeResolveResult(compilation.FindType(new TopLevelTypeName("System", "RuntimeArgumentHandle")))); | 
						|
		} | 
						|
 | 
						|
		protected internal override TranslatedExpression VisitMakeRefAny(MakeRefAny inst, TranslationContext context) | 
						|
		{ | 
						|
			var arg = Translate(inst.Argument).Expression; | 
						|
			if (arg is DirectionExpression) | 
						|
			{ | 
						|
				arg = ((DirectionExpression)arg).Expression; | 
						|
			} | 
						|
			return new UndocumentedExpression { | 
						|
				UndocumentedExpressionType = UndocumentedExpressionType.MakeRef, | 
						|
				Arguments = { arg.Detach() } | 
						|
			} | 
						|
			.WithILInstruction(inst) | 
						|
				.WithRR(new TypeResolveResult(compilation.FindType(new TopLevelTypeName("System", "TypedReference")))); | 
						|
		} | 
						|
 | 
						|
		protected internal override TranslatedExpression VisitRefAnyType(RefAnyType inst, TranslationContext context) | 
						|
		{ | 
						|
			return new MemberReferenceExpression(new UndocumentedExpression { | 
						|
				UndocumentedExpressionType = UndocumentedExpressionType.RefType, | 
						|
				Arguments = { Translate(inst.Argument).Expression.Detach() } | 
						|
			}, "TypeHandle") | 
						|
				.WithILInstruction(inst) | 
						|
				.WithRR(new TypeResolveResult(compilation.FindType(new TopLevelTypeName("System", "RuntimeTypeHandle")))); | 
						|
		} | 
						|
 | 
						|
		protected internal override TranslatedExpression VisitRefAnyValue(RefAnyValue inst, TranslationContext context) | 
						|
		{ | 
						|
			var expr = new UndocumentedExpression { | 
						|
				UndocumentedExpressionType = UndocumentedExpressionType.RefValue, | 
						|
				Arguments = { Translate(inst.Argument).Expression, new TypeReferenceExpression(ConvertType(inst.Type)) } | 
						|
			}.WithRR(new ResolveResult(inst.Type)); | 
						|
			return new DirectionExpression(FieldDirection.Ref, expr.WithILInstruction(inst)).WithoutILInstruction() | 
						|
				.WithRR(new ByReferenceResolveResult(expr.ResolveResult, ReferenceKind.Ref)); | 
						|
		} | 
						|
 | 
						|
		protected internal override TranslatedExpression VisitBlock(Block block, TranslationContext context) | 
						|
		{ | 
						|
			switch (block.Kind) | 
						|
			{ | 
						|
				case BlockKind.ArrayInitializer: | 
						|
					return TranslateArrayInitializer(block); | 
						|
				case BlockKind.StackAllocInitializer: | 
						|
					return TranslateStackAllocInitializer(block, context.TypeHint); | 
						|
				case BlockKind.CollectionInitializer: | 
						|
				case BlockKind.ObjectInitializer: | 
						|
					return TranslateObjectAndCollectionInitializer(block); | 
						|
				case BlockKind.WithInitializer: | 
						|
					return TranslateWithInitializer(block); | 
						|
				case BlockKind.CallInlineAssign: | 
						|
					return TranslateSetterCallAssignment(block); | 
						|
				case BlockKind.CallWithNamedArgs: | 
						|
					return TranslateCallWithNamedArgs(block); | 
						|
				case BlockKind.InterpolatedString: | 
						|
					return TranslateInterpolatedString(block); | 
						|
				default: | 
						|
					return ErrorExpression("Unknown block type: " + block.Kind); | 
						|
			} | 
						|
		} | 
						|
 | 
						|
		private TranslatedExpression TranslateInterpolatedString(Block block) | 
						|
		{ | 
						|
			var content = new List<InterpolatedStringContent>(); | 
						|
 | 
						|
			for (int i = 1; i < block.Instructions.Count; i++) | 
						|
			{ | 
						|
				var call = (Call)block.Instructions[i]; | 
						|
				switch (call.Method.Name) | 
						|
				{ | 
						|
					case "AppendLiteral": | 
						|
						content.Add(new InterpolatedStringText(((LdStr)call.Arguments[1]).Value.Replace("{", "{{").Replace("}", "}}"))); | 
						|
						break; | 
						|
					case "AppendFormatted" when call.Arguments.Count == 2: | 
						|
						content.Add(new Interpolation(Translate(call.Arguments[1]))); | 
						|
						break; | 
						|
					case "AppendFormatted" when call.Arguments.Count == 3 && call.Arguments[2] is LdStr ldstr: | 
						|
						content.Add(new Interpolation(Translate(call.Arguments[1]), suffix: ldstr.Value)); | 
						|
						break; | 
						|
					case "AppendFormatted" when call.Arguments.Count == 3 && call.Arguments[2] is LdcI4 ldci4: | 
						|
						content.Add(new Interpolation(Translate(call.Arguments[1]), alignment: ldci4.Value)); | 
						|
						break; | 
						|
					case "AppendFormatted" when call.Arguments.Count == 4 && call.Arguments[2] is LdcI4 ldci4 && call.Arguments[3] is LdStr ldstr: | 
						|
						content.Add(new Interpolation(Translate(call.Arguments[1]), ldci4.Value, ldstr.Value)); | 
						|
						break; | 
						|
					default: | 
						|
						throw new NotSupportedException(); | 
						|
				} | 
						|
			} | 
						|
 | 
						|
			return new InterpolatedStringExpression(content) | 
						|
				.WithILInstruction(block) | 
						|
				.WithRR(new ResolveResult(compilation.FindType(KnownTypeCode.String))); | 
						|
		} | 
						|
 | 
						|
		private TranslatedExpression TranslateCallWithNamedArgs(Block block) | 
						|
		{ | 
						|
			return WrapInRef( | 
						|
				new CallBuilder(this, typeSystem, settings).CallWithNamedArgs(block), | 
						|
				((CallInstruction)block.FinalInstruction).Method.ReturnType); | 
						|
		} | 
						|
 | 
						|
		private TranslatedExpression TranslateSetterCallAssignment(Block block) | 
						|
		{ | 
						|
			if (!block.MatchInlineAssignBlock(out var call, out var value)) | 
						|
			{ | 
						|
				// should never happen unless the ILAst is invalid | 
						|
				return ErrorExpression("Error: MatchInlineAssignBlock() returned false"); | 
						|
			} | 
						|
			var arguments = call.Arguments.ToList(); | 
						|
			arguments[arguments.Count - 1] = value; | 
						|
			return new CallBuilder(this, typeSystem, settings) | 
						|
				.Build(call.OpCode, call.Method, arguments) | 
						|
				.WithILInstruction(call); | 
						|
		} | 
						|
 | 
						|
		TranslatedExpression TranslateObjectAndCollectionInitializer(Block block) | 
						|
		{ | 
						|
			var stloc = block.Instructions.FirstOrDefault() as StLoc; | 
						|
			var final = block.FinalInstruction as LdLoc; | 
						|
			// Check basic structure of block | 
						|
			if (stloc == null || final == null || stloc.Variable != final.Variable | 
						|
				|| stloc.Variable.Kind != VariableKind.InitializerTarget) | 
						|
				throw new ArgumentException("given Block is invalid!"); | 
						|
			InitializedObjectResolveResult initObjRR; | 
						|
			TranslatedExpression expr; | 
						|
			// Detect type of initializer | 
						|
			switch (stloc.Value) | 
						|
			{ | 
						|
				case NewObj newObjInst: | 
						|
					initObjRR = new InitializedObjectResolveResult(newObjInst.Method.DeclaringType); | 
						|
					expr = new CallBuilder(this, typeSystem, settings).Build(newObjInst); | 
						|
					break; | 
						|
				case DefaultValue defaultVal: | 
						|
					initObjRR = new InitializedObjectResolveResult(defaultVal.Type); | 
						|
					expr = new ObjectCreateExpression(ConvertType(defaultVal.Type)) | 
						|
						.WithILInstruction(defaultVal) | 
						|
						.WithRR(new TypeResolveResult(defaultVal.Type)); | 
						|
					break; | 
						|
				case Block callWithNamedArgs when callWithNamedArgs.Kind == BlockKind.CallWithNamedArgs: | 
						|
					expr = TranslateCallWithNamedArgs(callWithNamedArgs); | 
						|
					initObjRR = new InitializedObjectResolveResult(expr.Type); | 
						|
					break; | 
						|
				case Call c when c.Method.FullNameIs("System.Activator", "CreateInstance") && c.Method.TypeArguments.Count == 1: | 
						|
					IType type = c.Method.TypeArguments[0]; | 
						|
					initObjRR = new InitializedObjectResolveResult(type); | 
						|
					expr = new ObjectCreateExpression(ConvertType(type)) | 
						|
						.WithILInstruction(c) | 
						|
						.WithRR(new TypeResolveResult(type)); | 
						|
					break; | 
						|
				default: | 
						|
					throw new ArgumentException("given Block is invalid!"); | 
						|
			} | 
						|
			var oce = (ObjectCreateExpression)expr.Expression; | 
						|
			oce.Initializer = BuildArrayInitializerExpression(block, initObjRR); | 
						|
			return expr.WithILInstruction(block); | 
						|
		} | 
						|
 | 
						|
		private ArrayInitializerExpression BuildArrayInitializerExpression(Block block, InitializedObjectResolveResult initObjRR) | 
						|
		{ | 
						|
			var elementsStack = new Stack<List<TranslatedExpression>>(); | 
						|
			var elements = new List<TranslatedExpression>(block.Instructions.Count); | 
						|
			elementsStack.Push(elements); | 
						|
			List<IL.Transforms.AccessPathElement> currentPath = null; | 
						|
			var indexVariables = new Dictionary<ILVariable, ILInstruction>(); | 
						|
			foreach (var inst in block.Instructions.Skip(1)) | 
						|
			{ | 
						|
				// Collect indexer variables (for C# 6 dictionary initializers) | 
						|
				if (inst is StLoc indexStore) | 
						|
				{ | 
						|
					indexVariables.Add(indexStore.Variable, indexStore.Value); | 
						|
					continue; | 
						|
				} | 
						|
				// Get current path | 
						|
				var info = IL.Transforms.AccessPathElement.GetAccessPath(inst, initObjRR.Type, settings: settings); | 
						|
				// This should not happen, because the IL transform should not create invalid access paths, | 
						|
				// but we leave it here as sanity check. | 
						|
				if (info.Kind == IL.Transforms.AccessPathKind.Invalid) | 
						|
					continue; | 
						|
				// Calculate "difference" to previous path | 
						|
				if (currentPath == null) | 
						|
				{ | 
						|
					currentPath = info.Path; | 
						|
				} | 
						|
				else | 
						|
				{ | 
						|
					int minLen = Math.Min(currentPath.Count, info.Path.Count); | 
						|
					int firstDifferenceIndex = 0; | 
						|
					while (firstDifferenceIndex < minLen && info.Path[firstDifferenceIndex] == currentPath[firstDifferenceIndex]) | 
						|
						firstDifferenceIndex++; | 
						|
					while (elementsStack.Count - 1 > firstDifferenceIndex) | 
						|
					{ | 
						|
						var methodElement = currentPath[elementsStack.Count - 1]; | 
						|
						var pathElement = currentPath[elementsStack.Count - 2]; | 
						|
						var values = elementsStack.Pop(); | 
						|
						elementsStack.Peek().Add(MakeInitializerAssignment(initObjRR, methodElement, pathElement, values, indexVariables)); | 
						|
					} | 
						|
					currentPath = info.Path; | 
						|
				} | 
						|
				// Fill the stack with empty expression lists | 
						|
				while (elementsStack.Count < currentPath.Count) | 
						|
					elementsStack.Push(new List<TranslatedExpression>()); | 
						|
				var lastElement = currentPath.Last(); | 
						|
				var memberRR = new MemberResolveResult(initObjRR, lastElement.Member); | 
						|
				switch (info.Kind) | 
						|
				{ | 
						|
					case IL.Transforms.AccessPathKind.Adder: | 
						|
						Debug.Assert(lastElement.Member is IMethod); | 
						|
						elementsStack.Peek().Add( | 
						|
							new CallBuilder(this, typeSystem, settings) | 
						|
								.BuildCollectionInitializerExpression(lastElement.OpCode, (IMethod)lastElement.Member, initObjRR, info.Values) | 
						|
								.WithILInstruction(inst) | 
						|
						); | 
						|
						break; | 
						|
					case IL.Transforms.AccessPathKind.Setter: | 
						|
						Debug.Assert(lastElement.Member is IProperty || lastElement.Member is IField); | 
						|
						if (lastElement.Indices?.Length > 0) | 
						|
						{ | 
						|
							var property = (IProperty)lastElement.Member; | 
						|
							Debug.Assert(property.IsIndexer); | 
						|
							Debug.Assert(property.Setter != null, $"Indexer property {property} has no setter"); | 
						|
							elementsStack.Peek().Add( | 
						|
								new CallBuilder(this, typeSystem, settings) | 
						|
									.BuildDictionaryInitializerExpression(lastElement.OpCode, property.Setter, initObjRR, GetIndices(lastElement.Indices, indexVariables).ToList(), info.Values.Single()) | 
						|
									.WithILInstruction(inst) | 
						|
							); | 
						|
						} | 
						|
						else | 
						|
						{ | 
						|
							var value = Translate(info.Values.Single(), typeHint: memberRR.Type) | 
						|
								.ConvertTo(memberRR.Type, this, allowImplicitConversion: true); | 
						|
							var assignment = new NamedExpression(lastElement.Member.Name, value) | 
						|
								.WithILInstruction(inst).WithRR(memberRR); | 
						|
							elementsStack.Peek().Add(assignment); | 
						|
						} | 
						|
						break; | 
						|
				} | 
						|
			} | 
						|
			while (elementsStack.Count > 1) | 
						|
			{ | 
						|
				var methodElement = currentPath[elementsStack.Count - 1]; | 
						|
				var pathElement = currentPath[elementsStack.Count - 2]; | 
						|
				var values = elementsStack.Pop(); | 
						|
				elementsStack.Peek().Add( | 
						|
					MakeInitializerAssignment(initObjRR, methodElement, pathElement, values, indexVariables) | 
						|
				); | 
						|
			} | 
						|
			return new ArrayInitializerExpression(elements.SelectArray(e => e.Expression)); | 
						|
		} | 
						|
 | 
						|
		IEnumerable<ILInstruction> GetIndices(IEnumerable<ILInstruction> indices, Dictionary<ILVariable, ILInstruction> indexVariables) | 
						|
		{ | 
						|
			foreach (var inst in indices) | 
						|
			{ | 
						|
				if (inst is LdLoc ld && indexVariables.TryGetValue(ld.Variable, out var newInst)) | 
						|
					yield return newInst; | 
						|
				else | 
						|
					yield return inst; | 
						|
			} | 
						|
		} | 
						|
 | 
						|
		TranslatedExpression MakeInitializerAssignment(InitializedObjectResolveResult rr, IL.Transforms.AccessPathElement memberPath, | 
						|
			IL.Transforms.AccessPathElement valuePath, List<TranslatedExpression> values, | 
						|
			Dictionary<ILVariable, ILInstruction> indexVariables) | 
						|
		{ | 
						|
			TranslatedExpression value; | 
						|
			if (memberPath.Member is IMethod method && method.Name == "Add") | 
						|
			{ | 
						|
				value = new ArrayInitializerExpression(values.Select(v => v.Expression)) | 
						|
					.WithRR(new ResolveResult(SpecialType.UnknownType)) | 
						|
					.WithoutILInstruction(); | 
						|
			} | 
						|
			else if (values.Count == 1 && !(values[0].Expression is AssignmentExpression || values[0].Expression is NamedExpression)) | 
						|
			{ | 
						|
				value = values[0]; | 
						|
			} | 
						|
			else | 
						|
			{ | 
						|
				value = new ArrayInitializerExpression(values.Select(v => v.Expression)) | 
						|
					.WithRR(new ResolveResult(SpecialType.UnknownType)) | 
						|
					.WithoutILInstruction(); | 
						|
			} | 
						|
			if (valuePath.Indices?.Length > 0) | 
						|
			{ | 
						|
				Expression index = new IndexerExpression(null, GetIndices(valuePath.Indices, indexVariables).Select(i => Translate(i).Expression)); | 
						|
				return new AssignmentExpression(index, value) | 
						|
					.WithRR(new MemberResolveResult(rr, valuePath.Member)) | 
						|
					.WithoutILInstruction(); | 
						|
			} | 
						|
			else | 
						|
			{ | 
						|
				return new NamedExpression(valuePath.Member.Name, value) | 
						|
					.WithRR(new MemberResolveResult(rr, valuePath.Member)) | 
						|
					.WithoutILInstruction(); | 
						|
			} | 
						|
		} | 
						|
 | 
						|
		class ArrayInitializer | 
						|
		{ | 
						|
			public ArrayInitializer(ArrayInitializerExpression expression) | 
						|
			{ | 
						|
				this.Expression = expression; | 
						|
				this.CurrentElementCount = 0; | 
						|
			} | 
						|
 | 
						|
			public ArrayInitializerExpression Expression; | 
						|
			// HACK: avoid using Expression.Elements.Count: https://github.com/icsharpcode/ILSpy/issues/1202 | 
						|
			public int CurrentElementCount; | 
						|
		} | 
						|
 | 
						|
		TranslatedExpression TranslateArrayInitializer(Block block) | 
						|
		{ | 
						|
			var stloc = block.Instructions.FirstOrDefault() as StLoc; | 
						|
			var final = block.FinalInstruction as LdLoc; | 
						|
			if (stloc == null || final == null || !stloc.Value.MatchNewArr(out IType type)) | 
						|
				throw new ArgumentException("given Block is invalid!"); | 
						|
			if (stloc.Variable != final.Variable || stloc.Variable.Kind != VariableKind.InitializerTarget) | 
						|
				throw new ArgumentException("given Block is invalid!"); | 
						|
			var newArr = (NewArr)stloc.Value; | 
						|
 | 
						|
			var translatedDimensions = newArr.Indices.SelectArray(i => Translate(i)); | 
						|
 | 
						|
			if (!translatedDimensions.All(dim => dim.ResolveResult.IsCompileTimeConstant)) | 
						|
				throw new ArgumentException("given Block is invalid!"); | 
						|
			int dimensions = newArr.Indices.Count; | 
						|
			int[] dimensionSizes = translatedDimensions.SelectArray(dim => (int)dim.ResolveResult.ConstantValue); | 
						|
			var container = new Stack<ArrayInitializer>(); | 
						|
			var root = new ArrayInitializer(new ArrayInitializerExpression()); | 
						|
			container.Push(root); | 
						|
			var elementResolveResults = new List<ResolveResult>(); | 
						|
 | 
						|
			for (int i = 1; i < block.Instructions.Count; i++) | 
						|
			{ | 
						|
				if (!block.Instructions[i].MatchStObj(out ILInstruction target, out ILInstruction value, out IType t) || !type.Equals(t)) | 
						|
					throw new ArgumentException("given Block is invalid!"); | 
						|
				if (!target.MatchLdElema(out t, out ILInstruction array) || !type.Equals(t)) | 
						|
					throw new ArgumentException("given Block is invalid!"); | 
						|
				if (!array.MatchLdLoc(out ILVariable v) || v != final.Variable) | 
						|
					throw new ArgumentException("given Block is invalid!"); | 
						|
				while (container.Count < dimensions) | 
						|
				{ | 
						|
					var aie = new ArrayInitializerExpression(); | 
						|
					var parentInitializer = container.Peek(); | 
						|
					if (parentInitializer.CurrentElementCount > 0) | 
						|
						parentInitializer.Expression.AddChild(new CSharpTokenNode(TextLocation.Empty, Roles.Comma), Roles.Comma); | 
						|
					parentInitializer.Expression.Elements.Add(aie); | 
						|
					parentInitializer.CurrentElementCount++; | 
						|
					container.Push(new ArrayInitializer(aie)); | 
						|
				} | 
						|
				TranslatedExpression val; | 
						|
				var old = astBuilder.UseSpecialConstants; | 
						|
				try | 
						|
				{ | 
						|
					astBuilder.UseSpecialConstants = !type.IsCSharpPrimitiveIntegerType() && !type.IsKnownType(KnownTypeCode.Decimal); | 
						|
					val = Translate(value, typeHint: type).ConvertTo(type, this, allowImplicitConversion: true); | 
						|
				} | 
						|
				finally | 
						|
				{ | 
						|
					astBuilder.UseSpecialConstants = old; | 
						|
				} | 
						|
				var currentInitializer = container.Peek(); | 
						|
				if (currentInitializer.CurrentElementCount > 0) | 
						|
					currentInitializer.Expression.AddChild(new CSharpTokenNode(TextLocation.Empty, Roles.Comma), Roles.Comma); | 
						|
				currentInitializer.Expression.Elements.Add(val); | 
						|
				currentInitializer.CurrentElementCount++; | 
						|
				elementResolveResults.Add(val.ResolveResult); | 
						|
				while (container.Count > 0 && container.Peek().CurrentElementCount == dimensionSizes[container.Count - 1]) | 
						|
				{ | 
						|
					container.Pop(); | 
						|
				} | 
						|
			} | 
						|
			ArraySpecifier[] additionalSpecifiers; | 
						|
			AstType typeExpression; | 
						|
			if (settings.AnonymousTypes && type.ContainsAnonymousType()) | 
						|
			{ | 
						|
				typeExpression = null; | 
						|
				additionalSpecifiers = new[] { new ArraySpecifier() }; | 
						|
			} | 
						|
			else | 
						|
			{ | 
						|
				typeExpression = ConvertType(type); | 
						|
				if (typeExpression is ComposedType compType && compType.ArraySpecifiers.Count > 0) | 
						|
				{ | 
						|
					additionalSpecifiers = compType.ArraySpecifiers.Select(a => (ArraySpecifier)a.Clone()).ToArray(); | 
						|
					compType.ArraySpecifiers.Clear(); | 
						|
				} | 
						|
				else | 
						|
				{ | 
						|
					additionalSpecifiers = Empty<ArraySpecifier>.Array; | 
						|
				} | 
						|
			} | 
						|
			var expr = new ArrayCreateExpression { | 
						|
				Type = typeExpression, | 
						|
				Initializer = root.Expression | 
						|
			}; | 
						|
			expr.AdditionalArraySpecifiers.AddRange(additionalSpecifiers); | 
						|
			if (!type.ContainsAnonymousType()) | 
						|
				expr.Arguments.AddRange(newArr.Indices.Select(i => Translate(i).Expression)); | 
						|
			return expr.WithILInstruction(block) | 
						|
				.WithRR(new ArrayCreateResolveResult(new ArrayType(compilation, type, dimensions), newArr.Indices.Select(i => Translate(i).ResolveResult).ToArray(), elementResolveResults)); | 
						|
		} | 
						|
 | 
						|
		TranslatedExpression TranslateStackAllocInitializer(Block block, IType typeHint) | 
						|
		{ | 
						|
			var stloc = block.Instructions.FirstOrDefault() as StLoc; | 
						|
			var final = block.FinalInstruction as LdLoc; | 
						|
			if (stloc == null || final == null || stloc.Variable != final.Variable || stloc.Variable.Kind != VariableKind.InitializerTarget) | 
						|
				throw new ArgumentException("given Block is invalid!"); | 
						|
			StackAllocExpression stackAllocExpression; | 
						|
			IType elementType; | 
						|
			if (block.Instructions.Count < 2 || !block.Instructions[1].MatchStObj(out _, out _, out var t)) | 
						|
				throw new ArgumentException("given Block is invalid!"); | 
						|
			if (typeHint is PointerType pt && !TypeUtils.IsCompatibleTypeForMemoryAccess(t, pt.ElementType)) | 
						|
			{ | 
						|
				typeHint = new PointerType(t); | 
						|
			} | 
						|
			switch (stloc.Value) | 
						|
			{ | 
						|
				case LocAlloc locAlloc: | 
						|
					stackAllocExpression = TranslateLocAlloc(locAlloc, typeHint, out elementType); | 
						|
					break; | 
						|
				case LocAllocSpan locAllocSpan: | 
						|
					stackAllocExpression = TranslateLocAllocSpan(locAllocSpan, typeHint, out elementType); | 
						|
					break; | 
						|
				default: | 
						|
					throw new ArgumentException("given Block is invalid!"); | 
						|
			} | 
						|
			var initializer = stackAllocExpression.Initializer = new ArrayInitializerExpression(); | 
						|
			var pointerType = new PointerType(elementType); | 
						|
			long expectedOffset = 0; | 
						|
 | 
						|
			for (int i = 1; i < block.Instructions.Count; i++) | 
						|
			{ | 
						|
				// stobj type(binary.add.i(ldloc I_0, conv i4->i <sign extend> (ldc.i4 offset)), value) | 
						|
				if (!block.Instructions[i].MatchStObj(out var target, out var value, out t) || !TypeUtils.IsCompatibleTypeForMemoryAccess(elementType, t)) | 
						|
					throw new ArgumentException("given Block is invalid!"); | 
						|
				long offset = 0; | 
						|
				target = target.UnwrapConv(ConversionKind.StopGCTracking); | 
						|
 | 
						|
				if (!target.MatchLdLoc(stloc.Variable)) | 
						|
				{ | 
						|
					if (!target.MatchBinaryNumericInstruction(BinaryNumericOperator.Add, out var left, out var right)) | 
						|
						throw new ArgumentException("given Block is invalid!"); | 
						|
					var binary = (BinaryNumericInstruction)target; | 
						|
					left = left.UnwrapConv(ConversionKind.StopGCTracking); | 
						|
					var offsetInst = PointerArithmeticOffset.Detect(right, pointerType.ElementType, binary.CheckForOverflow); | 
						|
					if (!left.MatchLdLoc(final.Variable) || offsetInst == null) | 
						|
						throw new ArgumentException("given Block is invalid!"); | 
						|
					if (!offsetInst.MatchLdcI(out offset)) | 
						|
						throw new ArgumentException("given Block is invalid!"); | 
						|
				} | 
						|
				while (expectedOffset < offset) | 
						|
				{ | 
						|
					initializer.Elements.Add(Translate(IL.Transforms.TransformArrayInitializers.GetNullExpression(elementType), typeHint: elementType)); | 
						|
					expectedOffset++; | 
						|
				} | 
						|
				var val = Translate(value, typeHint: elementType).ConvertTo(elementType, this, allowImplicitConversion: true); | 
						|
				initializer.Elements.Add(val); | 
						|
				expectedOffset++; | 
						|
			} | 
						|
			return stackAllocExpression.WithILInstruction(block) | 
						|
				.WithRR(new ResolveResult(stloc.Variable.Type)); | 
						|
		} | 
						|
 | 
						|
		TranslatedExpression TranslateWithInitializer(Block block) | 
						|
		{ | 
						|
			var stloc = block.Instructions.FirstOrDefault() as StLoc; | 
						|
			var final = block.FinalInstruction as LdLoc; | 
						|
			if (stloc == null || final == null || stloc.Variable != final.Variable || stloc.Variable.Kind != VariableKind.InitializerTarget) | 
						|
				throw new ArgumentException("given Block is invalid!"); | 
						|
 | 
						|
			WithInitializerExpression withInitializerExpression = new WithInitializerExpression(); | 
						|
			withInitializerExpression.Expression = Translate(stloc.Value, stloc.Variable.Type); | 
						|
			withInitializerExpression.Initializer = BuildArrayInitializerExpression(block, new InitializedObjectResolveResult(stloc.Variable.Type)); | 
						|
 | 
						|
			return withInitializerExpression.WithILInstruction(block) | 
						|
				.WithRR(new ResolveResult(stloc.Variable.Type)); | 
						|
		} | 
						|
 | 
						|
		/// <summary> | 
						|
		/// If expr is a constant integer expression, and its value fits into type, | 
						|
		/// convert the expression into the target type. | 
						|
		/// Otherwise, returns the expression unmodified. | 
						|
		/// </summary> | 
						|
		TranslatedExpression AdjustConstantExpressionToType(TranslatedExpression expr, IType typeHint) | 
						|
		{ | 
						|
			var newRR = AdjustConstantToType(expr.ResolveResult, typeHint); | 
						|
			if (newRR == expr.ResolveResult) | 
						|
			{ | 
						|
				return expr; | 
						|
			} | 
						|
			else | 
						|
			{ | 
						|
				return ConvertConstantValue(newRR, allowImplicitConversion: true).WithILInstruction(expr.ILInstructions); | 
						|
			} | 
						|
		} | 
						|
 | 
						|
		private ResolveResult AdjustConstantToType(ResolveResult rr, IType typeHint) | 
						|
		{ | 
						|
			if (!rr.IsCompileTimeConstant) | 
						|
			{ | 
						|
				return rr; | 
						|
			} | 
						|
			typeHint = NullableType.GetUnderlyingType(typeHint); | 
						|
			if (rr.Type.Equals(typeHint)) | 
						|
			{ | 
						|
				return rr; | 
						|
			} | 
						|
			// Convert to type hint, if this is possible without loss of accuracy | 
						|
			if (typeHint.IsKnownType(KnownTypeCode.Boolean)) | 
						|
			{ | 
						|
				if (object.Equals(rr.ConstantValue, 0) || object.Equals(rr.ConstantValue, 0u)) | 
						|
				{ | 
						|
					rr = new ConstantResolveResult(typeHint, false); | 
						|
				} | 
						|
				else if (object.Equals(rr.ConstantValue, 1) || object.Equals(rr.ConstantValue, 1u)) | 
						|
				{ | 
						|
					rr = new ConstantResolveResult(typeHint, true); | 
						|
				} | 
						|
			} | 
						|
			else if (typeHint.Kind == TypeKind.Enum || typeHint.IsKnownType(KnownTypeCode.Char) || typeHint.IsCSharpSmallIntegerType()) | 
						|
			{ | 
						|
				var castRR = resolver.WithCheckForOverflow(true).ResolveCast(typeHint, rr); | 
						|
				if (castRR.IsCompileTimeConstant && !castRR.IsError) | 
						|
				{ | 
						|
					rr = castRR; | 
						|
				} | 
						|
			} | 
						|
			else if (typeHint.Kind.IsAnyPointer() && (object.Equals(rr.ConstantValue, 0) || object.Equals(rr.ConstantValue, 0u))) | 
						|
			{ | 
						|
				rr = new ConstantResolveResult(typeHint, null); | 
						|
			} | 
						|
			return rr; | 
						|
		} | 
						|
 | 
						|
		protected internal override TranslatedExpression VisitNullCoalescingInstruction(NullCoalescingInstruction inst, TranslationContext context) | 
						|
		{ | 
						|
			var value = Translate(inst.ValueInst); | 
						|
			var fallback = Translate(inst.FallbackInst); | 
						|
			fallback = AdjustConstantExpressionToType(fallback, value.Type); | 
						|
			var rr = resolver.ResolveBinaryOperator(BinaryOperatorType.NullCoalescing, value.ResolveResult, fallback.ResolveResult); | 
						|
			if (rr.IsError) | 
						|
			{ | 
						|
				IType targetType; | 
						|
				if (fallback.Expression is ThrowExpression && fallback.Type.Equals(SpecialType.NoType)) | 
						|
				{ | 
						|
					targetType = NullableType.GetUnderlyingType(value.Type); | 
						|
				} | 
						|
				else if (!value.Type.Equals(SpecialType.NullType) && !fallback.Type.Equals(SpecialType.NullType) && !value.Type.Equals(fallback.Type)) | 
						|
				{ | 
						|
					targetType = compilation.FindType(inst.UnderlyingResultType); | 
						|
				} | 
						|
				else | 
						|
				{ | 
						|
					targetType = value.Type.Equals(SpecialType.NullType) ? fallback.Type : value.Type; | 
						|
				} | 
						|
				if (inst.Kind != NullCoalescingKind.Ref) | 
						|
				{ | 
						|
					value = value.ConvertTo(NullableType.Create(compilation, targetType), this); | 
						|
				} | 
						|
				else | 
						|
				{ | 
						|
					value = value.ConvertTo(targetType, this); | 
						|
				} | 
						|
				if (inst.Kind == NullCoalescingKind.Nullable) | 
						|
				{ | 
						|
					value = value.ConvertTo(NullableType.Create(compilation, targetType), this); | 
						|
				} | 
						|
				else | 
						|
				{ | 
						|
					fallback = fallback.ConvertTo(targetType, this); | 
						|
				} | 
						|
				rr = new ResolveResult(targetType); | 
						|
			} | 
						|
			return new BinaryOperatorExpression(value, BinaryOperatorType.NullCoalescing, fallback) | 
						|
				.WithILInstruction(inst) | 
						|
				.WithRR(rr); | 
						|
		} | 
						|
 | 
						|
		protected internal override TranslatedExpression VisitIfInstruction(IfInstruction inst, TranslationContext context) | 
						|
		{ | 
						|
			var condition = TranslateCondition(inst.Condition); | 
						|
			var trueBranch = Translate(inst.TrueInst, typeHint: context.TypeHint); | 
						|
			var falseBranch = Translate(inst.FalseInst, typeHint: context.TypeHint); | 
						|
			BinaryOperatorType op = BinaryOperatorType.Any; | 
						|
			TranslatedExpression rhs = default(TranslatedExpression); | 
						|
 | 
						|
			if (inst.MatchLogicAnd(out var lhsInst, out var rhsInst) && !rhsInst.MatchLdcI4(1)) | 
						|
			{ | 
						|
				op = BinaryOperatorType.ConditionalAnd; | 
						|
				Debug.Assert(rhsInst == inst.TrueInst); | 
						|
				rhs = trueBranch; | 
						|
			} | 
						|
			else if (inst.MatchLogicOr(out lhsInst, out rhsInst) && !rhsInst.MatchLdcI4(0)) | 
						|
			{ | 
						|
				op = BinaryOperatorType.ConditionalOr; | 
						|
				Debug.Assert(rhsInst == inst.FalseInst); | 
						|
				rhs = falseBranch; | 
						|
			} | 
						|
			// ILAst LogicAnd/LogicOr can return a different value than 0 or 1 | 
						|
			// if the rhs is evaluated. | 
						|
			// We can only correctly translate it to C# if the rhs is of type boolean: | 
						|
			if (op != BinaryOperatorType.Any && (rhs.Type.IsKnownType(KnownTypeCode.Boolean) || IfInstruction.IsInConditionSlot(inst))) | 
						|
			{ | 
						|
				if (rhs.Type.GetStackType().GetSize() > 4) | 
						|
				{ | 
						|
					rhs = rhs.ConvertTo(FindType(StackType.I4, rhs.Type.GetSign()), this); | 
						|
				} | 
						|
				rhs = rhs.ConvertToBoolean(this); | 
						|
				return new BinaryOperatorExpression(condition, op, rhs) | 
						|
					.WithILInstruction(inst) | 
						|
					.WithRR(new ResolveResult(compilation.FindType(KnownTypeCode.Boolean))); | 
						|
			} | 
						|
 | 
						|
			condition = condition.UnwrapImplicitBoolConversion(); | 
						|
			trueBranch = AdjustConstantExpressionToType(trueBranch, falseBranch.Type); | 
						|
			falseBranch = AdjustConstantExpressionToType(falseBranch, trueBranch.Type); | 
						|
 | 
						|
			var rr = resolver.ResolveConditional(condition.ResolveResult, trueBranch.ResolveResult, falseBranch.ResolveResult); | 
						|
			if (rr.IsError) | 
						|
			{ | 
						|
				IType targetType; | 
						|
				if (!trueBranch.Type.Equals(SpecialType.NullType) && !falseBranch.Type.Equals(SpecialType.NullType) && !trueBranch.Type.Equals(falseBranch.Type)) | 
						|
				{ | 
						|
					targetType = typeInference.GetBestCommonType(new[] { trueBranch.ResolveResult, falseBranch.ResolveResult }, out bool success); | 
						|
					if (!success || targetType.GetStackType() != inst.ResultType) | 
						|
					{ | 
						|
						// Figure out the target type based on inst.ResultType. | 
						|
						if (context.TypeHint.Kind != TypeKind.Unknown && context.TypeHint.GetStackType() == inst.ResultType) | 
						|
						{ | 
						|
							targetType = context.TypeHint; | 
						|
						} | 
						|
						else if (inst.ResultType == StackType.Ref) | 
						|
						{ | 
						|
							// targetType should be a ref-type | 
						|
							if (trueBranch.Type.Kind == TypeKind.ByReference) | 
						|
							{ | 
						|
								targetType = trueBranch.Type; | 
						|
							} | 
						|
							else if (falseBranch.Type.Kind == TypeKind.ByReference) | 
						|
							{ | 
						|
								targetType = falseBranch.Type; | 
						|
							} | 
						|
							else | 
						|
							{ | 
						|
								// fall back to 'ref byte' if we can't determine a referenced type otherwise | 
						|
								targetType = new ByReferenceType(compilation.FindType(KnownTypeCode.Byte)); | 
						|
							} | 
						|
						} | 
						|
						else | 
						|
						{ | 
						|
							targetType = FindType(inst.ResultType, context.TypeHint.GetSign()); | 
						|
						} | 
						|
					} | 
						|
				} | 
						|
				else | 
						|
				{ | 
						|
					targetType = trueBranch.Type.Equals(SpecialType.NullType) ? falseBranch.Type : trueBranch.Type; | 
						|
				} | 
						|
				trueBranch = trueBranch.ConvertTo(targetType, this); | 
						|
				falseBranch = falseBranch.ConvertTo(targetType, this); | 
						|
				rr = new ResolveResult(targetType); | 
						|
			} | 
						|
			if (rr.Type.Kind == TypeKind.ByReference) | 
						|
			{ | 
						|
				// C# conditional ref looks like this: | 
						|
				// ref (arr != null ? ref trueBranch : ref falseBranch); | 
						|
				var conditionalResolveResult = new ResolveResult(((ByReferenceType)rr.Type).ElementType); | 
						|
				return new DirectionExpression(FieldDirection.Ref, | 
						|
					new ConditionalExpression(condition.Expression, trueBranch.Expression, falseBranch.Expression) | 
						|
						.WithILInstruction(inst) | 
						|
						.WithRR(conditionalResolveResult) | 
						|
				).WithoutILInstruction().WithRR(new ByReferenceResolveResult(conditionalResolveResult, ReferenceKind.Ref)); | 
						|
			} | 
						|
			else | 
						|
			{ | 
						|
				return new ConditionalExpression(condition.Expression, trueBranch.Expression, falseBranch.Expression) | 
						|
					.WithILInstruction(inst) | 
						|
					.WithRR(rr); | 
						|
			} | 
						|
		} | 
						|
 | 
						|
		protected internal override TranslatedExpression VisitSwitchInstruction(SwitchInstruction inst, TranslationContext context) | 
						|
		{ | 
						|
			TranslatedExpression value; | 
						|
			IType type; | 
						|
			if (inst.Value is StringToInt strToInt) | 
						|
			{ | 
						|
				value = Translate(strToInt.Argument) | 
						|
					.ConvertTo( | 
						|
						typeSystem.FindType(KnownTypeCode.String), | 
						|
						this, | 
						|
						allowImplicitConversion: false // switch-expression does not support implicit conversions | 
						|
					); | 
						|
				type = compilation.FindType(KnownTypeCode.String); | 
						|
			} | 
						|
			else | 
						|
			{ | 
						|
				strToInt = null; | 
						|
				value = Translate(inst.Value); | 
						|
				if (inst.Type != null) | 
						|
				{ | 
						|
					value = value.ConvertTo(inst.Type, this, allowImplicitConversion: true); | 
						|
				} | 
						|
				type = value.Type; | 
						|
			} | 
						|
 | 
						|
			IL.SwitchSection defaultSection = inst.GetDefaultSection(); | 
						|
			SwitchExpression switchExpr = new SwitchExpression(); | 
						|
			switchExpr.Expression = value; | 
						|
			IType resultType; | 
						|
			if (context.TypeHint.Kind != TypeKind.Unknown && context.TypeHint.GetStackType() == inst.ResultType) | 
						|
			{ | 
						|
				resultType = context.TypeHint; | 
						|
			} | 
						|
			else | 
						|
			{ | 
						|
				resultType = compilation.FindType(inst.ResultType); | 
						|
			} | 
						|
 | 
						|
			foreach (var section in inst.Sections) | 
						|
			{ | 
						|
				if (section == defaultSection) | 
						|
					continue; | 
						|
				var ses = new SwitchExpressionSection(); | 
						|
				if (section.HasNullLabel) | 
						|
				{ | 
						|
					Debug.Assert(section.Labels.IsEmpty); | 
						|
					ses.Pattern = new NullReferenceExpression(); | 
						|
				} | 
						|
				else | 
						|
				{ | 
						|
					long val = section.Labels.Values.Single(); | 
						|
					var rr = statementBuilder.CreateTypedCaseLabel(val, type, strToInt?.Map).Single(); | 
						|
					ses.Pattern = astBuilder.ConvertConstantValue(rr); | 
						|
				} | 
						|
				ses.Body = TranslateSectionBody(section); | 
						|
				switchExpr.SwitchSections.Add(ses); | 
						|
			} | 
						|
 | 
						|
			var defaultSES = new SwitchExpressionSection(); | 
						|
			defaultSES.Pattern = new IdentifierExpression("_"); | 
						|
			defaultSES.Body = TranslateSectionBody(defaultSection); | 
						|
			switchExpr.SwitchSections.Add(defaultSES); | 
						|
 | 
						|
			return switchExpr.WithILInstruction(inst).WithRR(new ResolveResult(resultType)); | 
						|
 | 
						|
			Expression TranslateSectionBody(IL.SwitchSection section) | 
						|
			{ | 
						|
				var body = Translate(section.Body, resultType); | 
						|
				return body.ConvertTo(resultType, this, allowImplicitConversion: true); | 
						|
			} | 
						|
		} | 
						|
 | 
						|
		protected internal override TranslatedExpression VisitAddressOf(AddressOf inst, TranslationContext context) | 
						|
		{ | 
						|
			var classification = ILInlining.ClassifyExpression(inst.Value); | 
						|
			var value = Translate(inst.Value, inst.Type); | 
						|
			value = value.ConvertTo(inst.Type, this); | 
						|
			// ILAst AddressOf copies the value to a temporary, but when invoking a method in C# | 
						|
			// on a mutable lvalue, we would end up modifying the original lvalue, not just the copy. | 
						|
			// We solve this by introducing a "redundant" cast. Casts are classified as rvalue | 
						|
			// and ensure that the C# compiler will also create a copy. | 
						|
			if (classification == ExpressionClassification.MutableLValue | 
						|
				&& !CanIgnoreCopy() | 
						|
				&& value.Expression is not CastExpression) | 
						|
			{ | 
						|
				value = new CastExpression(ConvertType(inst.Type), value.Expression) | 
						|
					.WithoutILInstruction() | 
						|
					.WithRR(new ConversionResolveResult(inst.Type, value.ResolveResult, Conversion.IdentityConversion)); | 
						|
			} | 
						|
			return new DirectionExpression(FieldDirection.Ref, value) | 
						|
				.WithILInstruction(inst) | 
						|
				.WithRR(new ByReferenceResolveResult(value.ResolveResult, ReferenceKind.Ref)); | 
						|
 | 
						|
			bool CanIgnoreCopy() | 
						|
			{ | 
						|
				ILInstruction loadAddress = inst; | 
						|
				while (loadAddress.Parent is LdFlda parent) | 
						|
				{ | 
						|
					loadAddress = parent; | 
						|
				} | 
						|
				if (loadAddress.Parent is LdObj) | 
						|
				{ | 
						|
					// Ignore copy, never introduce a cast | 
						|
					return true; | 
						|
				} | 
						|
				return false; | 
						|
			} | 
						|
		} | 
						|
 | 
						|
		protected internal override TranslatedExpression VisitAwait(Await inst, TranslationContext context) | 
						|
		{ | 
						|
			IType expectedType = null; | 
						|
			if (inst.GetAwaiterMethod != null) | 
						|
			{ | 
						|
				if (inst.GetAwaiterMethod.IsStatic) | 
						|
				{ | 
						|
					expectedType = inst.GetAwaiterMethod.Parameters.FirstOrDefault()?.Type; | 
						|
				} | 
						|
				else | 
						|
				{ | 
						|
					expectedType = inst.GetAwaiterMethod.DeclaringType; | 
						|
				} | 
						|
			} | 
						|
 | 
						|
			var value = Translate(inst.Value, typeHint: expectedType); | 
						|
			if (value.Expression is DirectionExpression) | 
						|
			{ | 
						|
				// we can deference the managed reference by stripping away the 'ref' | 
						|
				value = value.UnwrapChild(((DirectionExpression)value.Expression).Expression); | 
						|
			} | 
						|
			if (expectedType != null) | 
						|
			{ | 
						|
				value = value.ConvertTo(expectedType, this, allowImplicitConversion: true); | 
						|
			} | 
						|
			return new UnaryOperatorExpression(UnaryOperatorType.Await, value.Expression) | 
						|
				.WithILInstruction(inst) | 
						|
				.WithRR(new ResolveResult(inst.GetResultMethod?.ReturnType ?? SpecialType.UnknownType)); | 
						|
		} | 
						|
 | 
						|
		protected internal override TranslatedExpression VisitNullableRewrap(NullableRewrap inst, TranslationContext context) | 
						|
		{ | 
						|
			var arg = Translate(inst.Argument); | 
						|
			IType type = arg.Type; | 
						|
			if (NullableType.IsNonNullableValueType(type)) | 
						|
			{ | 
						|
				type = NullableType.Create(compilation, type); | 
						|
			} | 
						|
			return new UnaryOperatorExpression(UnaryOperatorType.NullConditionalRewrap, arg) | 
						|
				.WithILInstruction(inst) | 
						|
				.WithRR(new ResolveResult(type)); | 
						|
		} | 
						|
 | 
						|
		protected internal override TranslatedExpression VisitNullableUnwrap(NullableUnwrap inst, TranslationContext context) | 
						|
		{ | 
						|
			var arg = Translate(inst.Argument); | 
						|
			if (inst.RefInput && !inst.RefOutput && arg.Expression is DirectionExpression dir) | 
						|
			{ | 
						|
				arg = arg.UnwrapChild(dir.Expression); | 
						|
			} | 
						|
			return new UnaryOperatorExpression(UnaryOperatorType.NullConditional, arg) | 
						|
				.WithILInstruction(inst) | 
						|
				.WithRR(new ResolveResult(NullableType.GetUnderlyingType(arg.Type))); | 
						|
		} | 
						|
 | 
						|
		protected internal override TranslatedExpression VisitDynamicConvertInstruction(DynamicConvertInstruction inst, TranslationContext context) | 
						|
		{ | 
						|
			var operand = Translate(inst.Argument).ConvertTo(SpecialType.Dynamic, this); | 
						|
			var result = new CastExpression(ConvertType(inst.Type), operand) | 
						|
				.WithILInstruction(inst) | 
						|
				.WithRR(new ConversionResolveResult( | 
						|
					inst.Type, operand.ResolveResult, | 
						|
					inst.IsExplicit ? Conversion.ExplicitDynamicConversion : Conversion.ImplicitDynamicConversion | 
						|
				)); | 
						|
			result.Expression.AddAnnotation(inst.IsChecked ? AddCheckedBlocks.CheckedAnnotation : AddCheckedBlocks.UncheckedAnnotation); | 
						|
			return result; | 
						|
		} | 
						|
 | 
						|
		protected internal override TranslatedExpression VisitDynamicGetIndexInstruction(DynamicGetIndexInstruction inst, TranslationContext context) | 
						|
		{ | 
						|
			var target = TranslateDynamicTarget(inst.Arguments[0], inst.ArgumentInfo[0]); | 
						|
			var arguments = TranslateDynamicArguments(inst.Arguments.Skip(1), inst.ArgumentInfo.Skip(1)).ToList(); | 
						|
			return new IndexerExpression(target, arguments.Select(a => a.Expression)) | 
						|
				.WithILInstruction(inst) | 
						|
				.WithRR(new DynamicInvocationResolveResult(target.ResolveResult, DynamicInvocationType.Indexing, arguments.Select(a => a.ResolveResult).ToArray())); | 
						|
		} | 
						|
 | 
						|
		protected internal override TranslatedExpression VisitDynamicGetMemberInstruction(DynamicGetMemberInstruction inst, TranslationContext context) | 
						|
		{ | 
						|
			var target = TranslateDynamicTarget(inst.Target, inst.TargetArgumentInfo); | 
						|
			return new MemberReferenceExpression(target, inst.Name) | 
						|
				.WithILInstruction(inst) | 
						|
				.WithRR(new DynamicMemberResolveResult(target.ResolveResult, inst.Name)); | 
						|
		} | 
						|
 | 
						|
		protected internal override TranslatedExpression VisitDynamicInvokeConstructorInstruction(DynamicInvokeConstructorInstruction inst, TranslationContext context) | 
						|
		{ | 
						|
			if (!(inst.ArgumentInfo[0].HasFlag(CSharpArgumentInfoFlags.IsStaticType) && IL.Transforms.TransformExpressionTrees.MatchGetTypeFromHandle(inst.Arguments[0], out var constructorType))) | 
						|
				return ErrorExpression("Could not detect static type for DynamicInvokeConstructorInstruction"); | 
						|
			var arguments = TranslateDynamicArguments(inst.Arguments.Skip(1), inst.ArgumentInfo.Skip(1)).ToList(); | 
						|
			//var names = inst.ArgumentInfo.Skip(1).Select(a => a.Name).ToArray(); | 
						|
			return new ObjectCreateExpression(ConvertType(constructorType), arguments.Select(a => a.Expression)) | 
						|
				.WithILInstruction(inst).WithRR(new ResolveResult(constructorType)); | 
						|
		} | 
						|
 | 
						|
		protected internal override TranslatedExpression VisitDynamicInvokeMemberInstruction(DynamicInvokeMemberInstruction inst, TranslationContext context) | 
						|
		{ | 
						|
			Expression targetExpr; | 
						|
			var target = TranslateDynamicTarget(inst.Arguments[0], inst.ArgumentInfo[0]); | 
						|
			if (inst.BinderFlags.HasFlag(CSharpBinderFlags.InvokeSimpleName) && target.Expression is ThisReferenceExpression) | 
						|
			{ | 
						|
				targetExpr = new IdentifierExpression(inst.Name); | 
						|
				((IdentifierExpression)targetExpr).TypeArguments.AddRange(inst.TypeArguments.Select(ConvertType)); | 
						|
			} | 
						|
			else | 
						|
			{ | 
						|
				targetExpr = new MemberReferenceExpression(target, inst.Name, inst.TypeArguments.Select(ConvertType)); | 
						|
			} | 
						|
			var arguments = TranslateDynamicArguments(inst.Arguments.Skip(1), inst.ArgumentInfo.Skip(1)).ToList(); | 
						|
			return new InvocationExpression(targetExpr, arguments.Select(a => a.Expression)) | 
						|
				.WithILInstruction(inst) | 
						|
				.WithRR(new DynamicInvocationResolveResult(target.ResolveResult, DynamicInvocationType.Invocation, arguments.Select(a => a.ResolveResult).ToArray())); | 
						|
		} | 
						|
 | 
						|
		protected internal override TranslatedExpression VisitDynamicInvokeInstruction(DynamicInvokeInstruction inst, TranslationContext context) | 
						|
		{ | 
						|
			var target = TranslateDynamicTarget(inst.Arguments[0], inst.ArgumentInfo[0]); | 
						|
			var arguments = TranslateDynamicArguments(inst.Arguments.Skip(1), inst.ArgumentInfo.Skip(1)).ToList(); | 
						|
			return new InvocationExpression(target, arguments.Select(a => a.Expression)) | 
						|
				.WithILInstruction(inst) | 
						|
				.WithRR(new DynamicInvocationResolveResult(target.ResolveResult, DynamicInvocationType.Invocation, arguments.Select(a => a.ResolveResult).ToArray())); | 
						|
		} | 
						|
 | 
						|
		TranslatedExpression TranslateDynamicTarget(ILInstruction inst, CSharpArgumentInfo argumentInfo) | 
						|
		{ | 
						|
			Debug.Assert(!argumentInfo.HasFlag(CSharpArgumentInfoFlags.NamedArgument)); | 
						|
			Debug.Assert(!argumentInfo.HasFlag(CSharpArgumentInfoFlags.IsOut)); | 
						|
 | 
						|
			if (argumentInfo.HasFlag(CSharpArgumentInfoFlags.IsStaticType) && IL.Transforms.TransformExpressionTrees.MatchGetTypeFromHandle(inst, out var callTargetType)) | 
						|
			{ | 
						|
				return new TypeReferenceExpression(ConvertType(callTargetType)) | 
						|
					.WithoutILInstruction() | 
						|
					.WithRR(new TypeResolveResult(callTargetType)); | 
						|
			} | 
						|
 | 
						|
			IType targetType = SpecialType.Dynamic; | 
						|
			if (argumentInfo.HasFlag(CSharpArgumentInfoFlags.UseCompileTimeType)) | 
						|
			{ | 
						|
				targetType = argumentInfo.CompileTimeType; | 
						|
			} | 
						|
 | 
						|
			var translatedTarget = Translate(inst, targetType).ConvertTo(targetType, this); | 
						|
 | 
						|
			if (argumentInfo.HasFlag(CSharpArgumentInfoFlags.IsRef) && translatedTarget.Expression is DirectionExpression) | 
						|
			{ | 
						|
				// (ref x).member => x.member | 
						|
				translatedTarget = translatedTarget.UnwrapChild(((DirectionExpression)translatedTarget).Expression); | 
						|
			} | 
						|
 | 
						|
			return translatedTarget; | 
						|
		} | 
						|
 | 
						|
		IEnumerable<TranslatedExpression> TranslateDynamicArguments(IEnumerable<ILInstruction> arguments, IEnumerable<CSharpArgumentInfo> argumentInfo) | 
						|
		{ | 
						|
			foreach (var (argument, info) in arguments.Zip(argumentInfo)) | 
						|
			{ | 
						|
				yield return TranslateDynamicArgument(argument, info); | 
						|
			} | 
						|
		} | 
						|
 | 
						|
		TranslatedExpression TranslateDynamicArgument(ILInstruction argument, CSharpArgumentInfo info) | 
						|
		{ | 
						|
			Debug.Assert(!info.HasFlag(CSharpArgumentInfoFlags.IsStaticType)); | 
						|
 | 
						|
			IType typeHint = SpecialType.Dynamic; | 
						|
			if (info.HasFlag(CSharpArgumentInfoFlags.UseCompileTimeType)) | 
						|
			{ | 
						|
				typeHint = info.CompileTimeType; | 
						|
			} | 
						|
			var translatedExpression = Translate(argument, typeHint); | 
						|
			if (!(typeHint.Equals(SpecialType.Dynamic) && translatedExpression.Type.Equals(SpecialType.NullType))) | 
						|
			{ | 
						|
				translatedExpression = translatedExpression.ConvertTo(typeHint, this); | 
						|
			} | 
						|
			if (info.HasFlag(CSharpArgumentInfoFlags.IsOut)) | 
						|
			{ | 
						|
				translatedExpression = ChangeDirectionExpressionTo(translatedExpression, ReferenceKind.Out); | 
						|
			} | 
						|
			if (info.HasFlag(CSharpArgumentInfoFlags.NamedArgument) && !string.IsNullOrWhiteSpace(info.Name)) | 
						|
			{ | 
						|
				translatedExpression = new TranslatedExpression(new NamedArgumentExpression(info.Name, translatedExpression.Expression)); | 
						|
			} | 
						|
 | 
						|
			return translatedExpression; | 
						|
		} | 
						|
 | 
						|
		internal static TranslatedExpression ChangeDirectionExpressionTo(TranslatedExpression input, ReferenceKind kind) | 
						|
		{ | 
						|
			if (!(input.Expression is DirectionExpression dirExpr && input.ResolveResult is ByReferenceResolveResult brrr)) | 
						|
				return input; | 
						|
			dirExpr.FieldDirection = (FieldDirection)kind; | 
						|
			dirExpr.RemoveAnnotations<ByReferenceResolveResult>(); | 
						|
			if (brrr.ElementResult == null) | 
						|
				brrr = new ByReferenceResolveResult(brrr.ElementType, kind); | 
						|
			else | 
						|
				brrr = new ByReferenceResolveResult(brrr.ElementResult, kind); | 
						|
			dirExpr.AddAnnotation(brrr); | 
						|
			return new TranslatedExpression(dirExpr); | 
						|
		} | 
						|
 | 
						|
		protected internal override TranslatedExpression VisitDynamicSetIndexInstruction(DynamicSetIndexInstruction inst, TranslationContext context) | 
						|
		{ | 
						|
			Debug.Assert(inst.Arguments.Count >= 3); | 
						|
			var target = TranslateDynamicTarget(inst.Arguments[0], inst.ArgumentInfo[0]); | 
						|
			var arguments = TranslateDynamicArguments(inst.Arguments.Skip(1), inst.ArgumentInfo.Skip(1)).ToList(); | 
						|
			var value = new TranslatedExpression(arguments.Last()); | 
						|
			var indexer = new IndexerExpression(target, arguments.SkipLast(1).Select(a => a.Expression)) | 
						|
				.WithoutILInstruction() | 
						|
				.WithRR(new DynamicInvocationResolveResult(target.ResolveResult, DynamicInvocationType.Indexing, arguments.SkipLast(1).Select(a => a.ResolveResult).ToArray())); | 
						|
			return Assignment(indexer, value).WithILInstruction(inst); | 
						|
		} | 
						|
 | 
						|
		protected internal override TranslatedExpression VisitDynamicSetMemberInstruction(DynamicSetMemberInstruction inst, TranslationContext context) | 
						|
		{ | 
						|
			var target = TranslateDynamicTarget(inst.Target, inst.TargetArgumentInfo); | 
						|
			var value = TranslateDynamicArgument(inst.Value, inst.ValueArgumentInfo); | 
						|
			var member = new MemberReferenceExpression(target, inst.Name) | 
						|
				.WithoutILInstruction() | 
						|
				.WithRR(new DynamicMemberResolveResult(target.ResolveResult, inst.Name)); | 
						|
			return Assignment(member, value).WithILInstruction(inst); | 
						|
		} | 
						|
 | 
						|
		protected internal override TranslatedExpression VisitDynamicBinaryOperatorInstruction(DynamicBinaryOperatorInstruction inst, TranslationContext context) | 
						|
		{ | 
						|
			switch (inst.Operation) | 
						|
			{ | 
						|
				case ExpressionType.Add: | 
						|
				case ExpressionType.AddAssign: | 
						|
					return CreateBinaryOperator(BinaryOperatorType.Add, isChecked: inst.BinderFlags.HasFlag(CSharpBinderFlags.CheckedContext)); | 
						|
				case ExpressionType.AddChecked: | 
						|
				case ExpressionType.AddAssignChecked: | 
						|
					return CreateBinaryOperator(BinaryOperatorType.Add, isChecked: true); | 
						|
				case ExpressionType.Subtract: | 
						|
				case ExpressionType.SubtractAssign: | 
						|
					return CreateBinaryOperator(BinaryOperatorType.Subtract, isChecked: inst.BinderFlags.HasFlag(CSharpBinderFlags.CheckedContext)); | 
						|
				case ExpressionType.SubtractChecked: | 
						|
				case ExpressionType.SubtractAssignChecked: | 
						|
					return CreateBinaryOperator(BinaryOperatorType.Subtract, isChecked: true); | 
						|
				case ExpressionType.Multiply: | 
						|
				case ExpressionType.MultiplyAssign: | 
						|
					return CreateBinaryOperator(BinaryOperatorType.Multiply, isChecked: inst.BinderFlags.HasFlag(CSharpBinderFlags.CheckedContext)); | 
						|
				case ExpressionType.MultiplyChecked: | 
						|
				case ExpressionType.MultiplyAssignChecked: | 
						|
					return CreateBinaryOperator(BinaryOperatorType.Multiply, isChecked: true); | 
						|
				case ExpressionType.Divide: | 
						|
				case ExpressionType.DivideAssign: | 
						|
					return CreateBinaryOperator(BinaryOperatorType.Divide); | 
						|
				case ExpressionType.Modulo: | 
						|
				case ExpressionType.ModuloAssign: | 
						|
					return CreateBinaryOperator(BinaryOperatorType.Modulus); | 
						|
				case ExpressionType.Equal: | 
						|
					return CreateBinaryOperator(BinaryOperatorType.Equality); | 
						|
				case ExpressionType.NotEqual: | 
						|
					return CreateBinaryOperator(BinaryOperatorType.InEquality); | 
						|
				case ExpressionType.LessThan: | 
						|
					return CreateBinaryOperator(BinaryOperatorType.LessThan); | 
						|
				case ExpressionType.LessThanOrEqual: | 
						|
					return CreateBinaryOperator(BinaryOperatorType.LessThanOrEqual); | 
						|
				case ExpressionType.GreaterThan: | 
						|
					return CreateBinaryOperator(BinaryOperatorType.GreaterThan); | 
						|
				case ExpressionType.GreaterThanOrEqual: | 
						|
					return CreateBinaryOperator(BinaryOperatorType.GreaterThanOrEqual); | 
						|
				case ExpressionType.And: | 
						|
				case ExpressionType.AndAssign: | 
						|
					return CreateBinaryOperator(BinaryOperatorType.BitwiseAnd); | 
						|
				case ExpressionType.Or: | 
						|
				case ExpressionType.OrAssign: | 
						|
					return CreateBinaryOperator(BinaryOperatorType.BitwiseOr); | 
						|
				case ExpressionType.ExclusiveOr: | 
						|
				case ExpressionType.ExclusiveOrAssign: | 
						|
					return CreateBinaryOperator(BinaryOperatorType.ExclusiveOr); | 
						|
				case ExpressionType.LeftShift: | 
						|
				case ExpressionType.LeftShiftAssign: | 
						|
					return CreateBinaryOperator(BinaryOperatorType.ShiftLeft); | 
						|
				case ExpressionType.RightShift: | 
						|
				case ExpressionType.RightShiftAssign: | 
						|
					return CreateBinaryOperator(BinaryOperatorType.ShiftRight); | 
						|
				default: | 
						|
					return base.VisitDynamicBinaryOperatorInstruction(inst, context); | 
						|
			} | 
						|
 | 
						|
			TranslatedExpression CreateBinaryOperator(BinaryOperatorType operatorType, bool? isChecked = null) | 
						|
			{ | 
						|
				var left = TranslateDynamicArgument(inst.Left, inst.LeftArgumentInfo); | 
						|
				var right = TranslateDynamicArgument(inst.Right, inst.RightArgumentInfo); | 
						|
				var boe = new BinaryOperatorExpression(left.Expression, operatorType, right.Expression); | 
						|
				if (isChecked == true) | 
						|
					boe.AddAnnotation(AddCheckedBlocks.CheckedAnnotation); | 
						|
				else if (isChecked == false) | 
						|
					boe.AddAnnotation(AddCheckedBlocks.UncheckedAnnotation); | 
						|
				return boe.WithILInstruction(inst).WithRR(new ResolveResult(SpecialType.Dynamic)); | 
						|
			} | 
						|
		} | 
						|
 | 
						|
		protected internal override TranslatedExpression VisitDynamicLogicOperatorInstruction(DynamicLogicOperatorInstruction inst, TranslationContext context) | 
						|
		{ | 
						|
			BinaryOperatorType operatorType; | 
						|
			if (inst.Operation == ExpressionType.AndAlso) | 
						|
			{ | 
						|
				operatorType = BinaryOperatorType.ConditionalAnd; | 
						|
			} | 
						|
			else if (inst.Operation == ExpressionType.OrElse) | 
						|
			{ | 
						|
				operatorType = BinaryOperatorType.ConditionalOr; | 
						|
			} | 
						|
			else | 
						|
			{ | 
						|
				Debug.Fail("Unknown operation for DynamicLogicOperatorInstruction"); | 
						|
				return base.VisitDynamicLogicOperatorInstruction(inst, context); | 
						|
			} | 
						|
			var left = TranslateDynamicArgument(inst.Left, inst.LeftArgumentInfo); | 
						|
			var right = TranslateDynamicArgument(inst.Right, inst.RightArgumentInfo); | 
						|
			var boe = new BinaryOperatorExpression(left.Expression, operatorType, right.Expression); | 
						|
			return boe.WithILInstruction(inst).WithRR(new ResolveResult(SpecialType.Dynamic)); | 
						|
		} | 
						|
 | 
						|
		protected internal override TranslatedExpression VisitDynamicUnaryOperatorInstruction(DynamicUnaryOperatorInstruction inst, TranslationContext context) | 
						|
		{ | 
						|
			switch (inst.Operation) | 
						|
			{ | 
						|
				case ExpressionType.Not: | 
						|
					return CreateUnaryOperator(UnaryOperatorType.Not); | 
						|
				case ExpressionType.Decrement: | 
						|
					return CreateUnaryOperator(UnaryOperatorType.Decrement, isChecked: inst.BinderFlags.HasFlag(CSharpBinderFlags.CheckedContext)); | 
						|
				case ExpressionType.Increment: | 
						|
					return CreateUnaryOperator(UnaryOperatorType.Increment, isChecked: inst.BinderFlags.HasFlag(CSharpBinderFlags.CheckedContext)); | 
						|
				case ExpressionType.Negate: | 
						|
					return CreateUnaryOperator(UnaryOperatorType.Minus, isChecked: inst.BinderFlags.HasFlag(CSharpBinderFlags.CheckedContext)); | 
						|
				case ExpressionType.NegateChecked: | 
						|
					return CreateUnaryOperator(UnaryOperatorType.Minus, isChecked: true); | 
						|
				case ExpressionType.UnaryPlus: | 
						|
					return CreateUnaryOperator(UnaryOperatorType.Plus, isChecked: inst.BinderFlags.HasFlag(CSharpBinderFlags.CheckedContext)); | 
						|
				case ExpressionType.IsTrue: | 
						|
					var operand = TranslateDynamicArgument(inst.Operand, inst.OperandArgumentInfo); | 
						|
					Expression expr; | 
						|
					if (inst.SlotInfo == IfInstruction.ConditionSlot) | 
						|
					{ | 
						|
						// We rely on the context implicitly invoking "operator true". | 
						|
						expr = new UnaryOperatorExpression(UnaryOperatorType.IsTrue, operand); | 
						|
					} | 
						|
					else | 
						|
					{ | 
						|
						// Create a dummy conditional to ensure "operator true" will be invoked. | 
						|
						expr = new ConditionalExpression(operand, new PrimitiveExpression(true), new PrimitiveExpression(false)); | 
						|
					} | 
						|
					return expr.WithILInstruction(inst) | 
						|
						.WithRR(new ResolveResult(compilation.FindType(KnownTypeCode.Boolean))); | 
						|
				case ExpressionType.IsFalse: | 
						|
					operand = TranslateDynamicArgument(inst.Operand, inst.OperandArgumentInfo); | 
						|
					// Create a dummy conditional to ensure "operator false" will be invoked. | 
						|
					expr = new ConditionalExpression(operand, new PrimitiveExpression(false), new PrimitiveExpression(true)); | 
						|
					return expr.WithILInstruction(inst) | 
						|
						.WithRR(new ResolveResult(compilation.FindType(KnownTypeCode.Boolean))); | 
						|
				default: | 
						|
					return base.VisitDynamicUnaryOperatorInstruction(inst, context); | 
						|
			} | 
						|
 | 
						|
			TranslatedExpression CreateUnaryOperator(UnaryOperatorType operatorType, bool? isChecked = null) | 
						|
			{ | 
						|
				var operand = TranslateDynamicArgument(inst.Operand, inst.OperandArgumentInfo); | 
						|
				var uoe = new UnaryOperatorExpression(operatorType, operand.Expression); | 
						|
				if (isChecked == true) | 
						|
					uoe.AddAnnotation(AddCheckedBlocks.CheckedAnnotation); | 
						|
				else if (isChecked == false) | 
						|
					uoe.AddAnnotation(AddCheckedBlocks.UncheckedAnnotation); | 
						|
				return uoe.WithILInstruction(inst).WithRR(new ResolveResult(SpecialType.Dynamic)); | 
						|
			} | 
						|
		} | 
						|
 | 
						|
		protected internal override TranslatedExpression VisitDynamicCompoundAssign(DynamicCompoundAssign inst, TranslationContext context) | 
						|
		{ | 
						|
			ExpressionWithResolveResult target; | 
						|
			if (inst.TargetKind == CompoundTargetKind.Address) | 
						|
			{ | 
						|
				target = LdObj(inst.Target, SpecialType.Dynamic); | 
						|
			} | 
						|
			else | 
						|
			{ | 
						|
				target = TranslateDynamicArgument(inst.Target, inst.TargetArgumentInfo); | 
						|
			} | 
						|
			var value = TranslateDynamicArgument(inst.Value, inst.ValueArgumentInfo); | 
						|
 | 
						|
			var ae = new AssignmentExpression(target, AssignmentExpression.GetAssignmentOperatorTypeFromExpressionType(inst.Operation).Value, value); | 
						|
			if (inst.BinderFlags.HasFlag(CSharpBinderFlags.CheckedContext)) | 
						|
				ae.AddAnnotation(AddCheckedBlocks.CheckedAnnotation); | 
						|
			else | 
						|
				ae.AddAnnotation(AddCheckedBlocks.UncheckedAnnotation); | 
						|
			return ae.WithILInstruction(inst) | 
						|
				.WithRR(new OperatorResolveResult(SpecialType.Dynamic, inst.Operation, new[] { target.ResolveResult, value.ResolveResult })); | 
						|
		} | 
						|
 | 
						|
		protected internal override TranslatedExpression VisitLdFtn(LdFtn inst, TranslationContext context) | 
						|
		{ | 
						|
			ExpressionWithResolveResult delegateRef = new CallBuilder(this, typeSystem, settings).BuildMethodReference(inst.Method, isVirtual: false); | 
						|
			if (!inst.Method.IsStatic) | 
						|
			{ | 
						|
				// C# 9 function pointers don't support instance methods | 
						|
				return new InvocationExpression(new IdentifierExpression("__ldftn"), delegateRef) | 
						|
					.WithRR(new ResolveResult(new PointerType(compilation.FindType(KnownTypeCode.Void)))) | 
						|
					.WithILInstruction(inst); | 
						|
			} | 
						|
			// C# 9 function pointer | 
						|
			var ftp = new FunctionPointerType( | 
						|
				typeSystem.MainModule, | 
						|
				// TODO: calling convention | 
						|
				SignatureCallingConvention.Default, ImmutableArray.Create<IType>(), | 
						|
				inst.Method.ReturnType, inst.Method.ReturnTypeIsRefReadOnly, | 
						|
				inst.Method.Parameters.SelectImmutableArray(p => p.Type), | 
						|
				inst.Method.Parameters.SelectImmutableArray(p => p.ReferenceKind) | 
						|
			); | 
						|
			ExpressionWithResolveResult addressOf = new UnaryOperatorExpression( | 
						|
				UnaryOperatorType.AddressOf, | 
						|
				delegateRef | 
						|
			).WithRR(new ResolveResult(SpecialType.NoType)).WithILInstruction(inst); | 
						|
			var conversion = Conversion.MethodGroupConversion( | 
						|
				inst.Method, isVirtualMethodLookup: false, delegateCapturesFirstArgument: false); | 
						|
			return new CastExpression(ConvertType(ftp), addressOf) | 
						|
				.WithRR(new ConversionResolveResult(ftp, addressOf.ResolveResult, conversion)) | 
						|
				.WithoutILInstruction(); | 
						|
		} | 
						|
 | 
						|
		protected internal override TranslatedExpression VisitLdVirtFtn(LdVirtFtn inst, TranslationContext context) | 
						|
		{ | 
						|
			// C# 9 function pointers don't support instance methods | 
						|
			ExpressionWithResolveResult delegateRef = new CallBuilder(this, typeSystem, settings).BuildMethodReference(inst.Method, isVirtual: true); | 
						|
			return new InvocationExpression(new IdentifierExpression("__ldvirtftn"), delegateRef) | 
						|
				.WithRR(new ResolveResult(new PointerType(compilation.FindType(KnownTypeCode.Void)))) | 
						|
				.WithILInstruction(inst); | 
						|
		} | 
						|
 | 
						|
		protected internal override TranslatedExpression VisitCallIndirect(CallIndirect inst, TranslationContext context) | 
						|
		{ | 
						|
			if (inst.IsInstance) | 
						|
			{ | 
						|
				return ErrorExpression("calli with instance method signature not supportd"); | 
						|
			} | 
						|
 | 
						|
			var functionPointer = Translate(inst.FunctionPointer, typeHint: inst.FunctionPointerType); | 
						|
			if (!NormalizeTypeVisitor.TypeErasure.EquivalentTypes(functionPointer.Type, inst.FunctionPointerType)) | 
						|
			{ | 
						|
				functionPointer = functionPointer.ConvertTo(inst.FunctionPointerType, this); | 
						|
			} | 
						|
			var fpt = (FunctionPointerType)functionPointer.Type.SkipModifiers(); | 
						|
			var invocation = new InvocationExpression(); | 
						|
			invocation.Target = functionPointer; | 
						|
			foreach (var (argInst, (paramType, paramRefKind)) in inst.Arguments.Zip(fpt.ParameterTypes.Zip(fpt.ParameterReferenceKinds))) | 
						|
			{ | 
						|
				var arg = Translate(argInst, typeHint: paramType).ConvertTo(paramType, this, allowImplicitConversion: true); | 
						|
				if (paramRefKind != ReferenceKind.None) | 
						|
				{ | 
						|
					arg = ChangeDirectionExpressionTo(arg, paramRefKind); | 
						|
				} | 
						|
				invocation.Arguments.Add(arg); | 
						|
			} | 
						|
			if (fpt.ReturnType.SkipModifiers() is ByReferenceType brt) | 
						|
			{ | 
						|
				var rr = new ResolveResult(brt.ElementType); | 
						|
				return new DirectionExpression( | 
						|
					FieldDirection.Ref, | 
						|
					invocation.WithRR(rr).WithILInstruction(inst) | 
						|
				).WithRR(new ByReferenceResolveResult(rr, ReferenceKind.Ref)).WithoutILInstruction(); | 
						|
			} | 
						|
			else | 
						|
			{ | 
						|
				return invocation.WithRR(new ResolveResult(fpt.ReturnType)).WithILInstruction(inst); | 
						|
			} | 
						|
		} | 
						|
 | 
						|
		protected internal override TranslatedExpression VisitDeconstructInstruction(DeconstructInstruction inst, TranslationContext context) | 
						|
		{ | 
						|
			IType rhsType = inst.Pattern.Variable.Type; | 
						|
			var rhs = Translate(inst.Pattern.TestedOperand, rhsType); | 
						|
			rhs = rhs.ConvertTo(rhsType, this); // TODO allowImplicitConversion | 
						|
			var assignments = inst.Assignments.Instructions; | 
						|
			int assignmentPos = 0; | 
						|
			var inits = inst.Init; | 
						|
			int initPos = 0; | 
						|
 | 
						|
			Dictionary<ILVariable, ILVariable> conversionMapping = new Dictionary<ILVariable, ILVariable>(); | 
						|
 | 
						|
			foreach (var conv in inst.Conversions.Instructions) | 
						|
			{ | 
						|
				if (!DeconstructInstruction.IsConversionStLoc(conv, out var outputVariable, out var inputVariable)) | 
						|
					continue; | 
						|
				conversionMapping.Add(inputVariable, outputVariable); | 
						|
			} | 
						|
 | 
						|
 | 
						|
			var lhs = ConstructTuple(inst.Pattern); | 
						|
			return new AssignmentExpression(lhs, rhs) | 
						|
				.WithILInstruction(inst) | 
						|
				.WithRR(new ResolveResult(compilation.FindType(KnownTypeCode.Void))); | 
						|
 | 
						|
			TupleExpression ConstructTuple(MatchInstruction matchInstruction) | 
						|
			{ | 
						|
				var expr = new TupleExpression(); | 
						|
				foreach (var subPattern in matchInstruction.SubPatterns.Cast<MatchInstruction>()) | 
						|
				{ | 
						|
					if (subPattern.IsVar) | 
						|
					{ | 
						|
						if (subPattern.HasDesignator) | 
						|
						{ | 
						|
							if (!conversionMapping.TryGetValue(subPattern.Variable, out ILVariable value)) | 
						|
							{ | 
						|
								value = subPattern.Variable; | 
						|
							} | 
						|
							expr.Elements.Add(ConstructAssignmentTarget(assignments[assignmentPos], value)); | 
						|
							assignmentPos++; | 
						|
						} | 
						|
						else | 
						|
							expr.Elements.Add(new IdentifierExpression("_")); | 
						|
					} | 
						|
					else | 
						|
					{ | 
						|
						expr.Elements.Add(ConstructTuple(subPattern)); | 
						|
					} | 
						|
				} | 
						|
				return expr; | 
						|
			} | 
						|
 | 
						|
			TranslatedExpression ConstructAssignmentTarget(ILInstruction assignment, ILVariable value) | 
						|
			{ | 
						|
				switch (assignment) | 
						|
				{ | 
						|
					case StLoc stloc: | 
						|
						Debug.Assert(stloc.Value.MatchLdLoc(value)); | 
						|
						break; | 
						|
					case CallInstruction call: | 
						|
						for (int i = 0; i < call.Arguments.Count - 1; i++) | 
						|
						{ | 
						|
							ReplaceAssignmentTarget(call.Arguments[i]); | 
						|
						} | 
						|
						Debug.Assert(call.Arguments.Last().MatchLdLoc(value)); | 
						|
						break; | 
						|
					case StObj stobj: | 
						|
						var target = stobj.Target; | 
						|
						while (target.MatchLdFlda(out var nestedTarget, out _)) | 
						|
							target = nestedTarget; | 
						|
						ReplaceAssignmentTarget(target); | 
						|
						Debug.Assert(stobj.Value.MatchLdLoc(value)); | 
						|
						break; | 
						|
					default: | 
						|
						throw new NotSupportedException(); | 
						|
				} | 
						|
				var expr = Translate(assignment); | 
						|
				return expr.UnwrapChild(((AssignmentExpression)expr).Left); | 
						|
			} | 
						|
 | 
						|
			void ReplaceAssignmentTarget(ILInstruction target) | 
						|
			{ | 
						|
				if (target.MatchLdLoc(out var v) | 
						|
					&& v.Kind == VariableKind.DeconstructionInitTemporary) | 
						|
				{ | 
						|
					Debug.Assert(inits[initPos].Variable == v); | 
						|
					target.ReplaceWith(inits[initPos].Value); | 
						|
					initPos++; | 
						|
				} | 
						|
			} | 
						|
		} | 
						|
 | 
						|
		protected internal override TranslatedExpression VisitMatchInstruction(MatchInstruction inst, TranslationContext context) | 
						|
		{ | 
						|
			var left = Translate(inst.TestedOperand); | 
						|
			var right = TranslatePattern(inst); | 
						|
 | 
						|
			return new BinaryOperatorExpression(left, BinaryOperatorType.IsPattern, right) | 
						|
				.WithRR(new ResolveResult(compilation.FindType(KnownTypeCode.Boolean))) | 
						|
				.WithILInstruction(inst); | 
						|
		} | 
						|
 | 
						|
		ExpressionWithILInstruction TranslatePattern(ILInstruction pattern) | 
						|
		{ | 
						|
			switch (pattern) | 
						|
			{ | 
						|
				case MatchInstruction matchInstruction: | 
						|
					if (!matchInstruction.CheckType) | 
						|
						throw new NotImplementedException(); | 
						|
					if (matchInstruction.IsDeconstructCall) | 
						|
						throw new NotImplementedException(); | 
						|
					if (matchInstruction.IsDeconstructTuple) | 
						|
						throw new NotImplementedException(); | 
						|
					if (matchInstruction.SubPatterns.Any()) | 
						|
						throw new NotImplementedException(); | 
						|
					if (matchInstruction.HasDesignator) | 
						|
					{ | 
						|
						SingleVariableDesignation designator = new SingleVariableDesignation { Identifier = matchInstruction.Variable.Name }; | 
						|
						designator.AddAnnotation(new ILVariableResolveResult(matchInstruction.Variable)); | 
						|
						return new DeclarationExpression { | 
						|
							Type = ConvertType(matchInstruction.Variable.Type), | 
						|
							Designation = designator | 
						|
						}.WithILInstruction(matchInstruction); | 
						|
					} | 
						|
					else | 
						|
					{ | 
						|
						return new TypeReferenceExpression(ConvertType(matchInstruction.Variable.Type)) | 
						|
							.WithILInstruction(matchInstruction); | 
						|
					} | 
						|
				default: | 
						|
					throw new NotImplementedException(); | 
						|
			} | 
						|
		} | 
						|
 | 
						|
		protected internal override TranslatedExpression VisitInvalidBranch(InvalidBranch inst, TranslationContext context) | 
						|
		{ | 
						|
			string message = "Error"; | 
						|
			if (inst.StartILOffset != 0) | 
						|
			{ | 
						|
				message += $" near IL_{inst.StartILOffset:x4}"; | 
						|
			} | 
						|
			if (!string.IsNullOrEmpty(inst.Message)) | 
						|
			{ | 
						|
				message += ": " + inst.Message; | 
						|
			} | 
						|
			return ErrorExpression(message); | 
						|
		} | 
						|
 | 
						|
		protected internal override TranslatedExpression VisitInvalidExpression(InvalidExpression inst, TranslationContext context) | 
						|
		{ | 
						|
			string message = inst.Severity; | 
						|
			if (inst.StartILOffset != 0) | 
						|
			{ | 
						|
				message += $" near IL_{inst.StartILOffset:x4}"; | 
						|
			} | 
						|
			if (!string.IsNullOrEmpty(inst.Message)) | 
						|
			{ | 
						|
				message += ": " + inst.Message; | 
						|
			} | 
						|
			return ErrorExpression(message); | 
						|
		} | 
						|
 | 
						|
		protected override TranslatedExpression Default(ILInstruction inst, TranslationContext context) | 
						|
		{ | 
						|
			return ErrorExpression("OpCode not supported: " + inst.OpCode); | 
						|
		} | 
						|
 | 
						|
		static TranslatedExpression ErrorExpression(string message) | 
						|
		{ | 
						|
			var e = new ErrorExpression(); | 
						|
			e.AddChild(new Comment(message, CommentType.MultiLine), Roles.Comment); | 
						|
			return e.WithoutILInstruction().WithRR(ErrorResolveResult.UnknownError); | 
						|
		} | 
						|
	} | 
						|
}
 | 
						|
 |