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.
		
		
		
		
		
			
		
			
				
					
					
						
							1959 lines
						
					
					
						
							74 KiB
						
					
					
				
			
		
		
	
	
							1959 lines
						
					
					
						
							74 KiB
						
					
					
				// Copyright (c) 2014 Daniel Grunwald | 
						|
//  | 
						|
// Permission is hereby granted, free of charge, to any person obtaining a copy of this | 
						|
// software and associated documentation files (the "Software"), to deal in the Software | 
						|
// without restriction, including without limitation the rights to use, copy, modify, merge, | 
						|
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons | 
						|
// to whom the Software is furnished to do so, subject to the following conditions: | 
						|
//  | 
						|
// The above copyright notice and this permission notice shall be included in all copies or | 
						|
// substantial portions of the Software. | 
						|
//  | 
						|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, | 
						|
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR | 
						|
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE | 
						|
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR | 
						|
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER | 
						|
// DEALINGS IN THE SOFTWARE. | 
						|
 | 
						|
using System; | 
						|
using System.Collections.Generic; | 
						|
using System.Collections.Immutable; | 
						|
using System.Diagnostics; | 
						|
using System.Linq; | 
						|
using System.Text; | 
						|
 | 
						|
using ICSharpCode.Decompiler.CSharp.Resolver; | 
						|
using ICSharpCode.Decompiler.CSharp.Syntax; | 
						|
using ICSharpCode.Decompiler.IL; | 
						|
using ICSharpCode.Decompiler.Semantics; | 
						|
using ICSharpCode.Decompiler.TypeSystem; | 
						|
using ICSharpCode.Decompiler.TypeSystem.Implementation; | 
						|
using ICSharpCode.Decompiler.Util; | 
						|
 | 
						|
namespace ICSharpCode.Decompiler.CSharp | 
						|
{ | 
						|
	struct CallBuilder | 
						|
	{ | 
						|
		struct ExpectedTargetDetails | 
						|
		{ | 
						|
			public OpCode CallOpCode; | 
						|
			public bool NeedsBoxingConversion; | 
						|
		} | 
						|
 | 
						|
		struct ArgumentList | 
						|
		{ | 
						|
			public TranslatedExpression[] Arguments; | 
						|
			public IParameter[] ExpectedParameters; | 
						|
			public string[] ParameterNames; | 
						|
			public string[] ArgumentNames; | 
						|
			public int FirstOptionalArgumentIndex; | 
						|
			public BitSet IsPrimitiveValue; | 
						|
			public IReadOnlyList<int> ArgumentToParameterMap; | 
						|
 | 
						|
			public bool AddNamesToPrimitiveValues; | 
						|
			public bool UseImplicitlyTypedOut; | 
						|
			public bool IsExpandedForm; | 
						|
			public int Length => Arguments.Length; | 
						|
 | 
						|
			private int GetActualArgumentCount() | 
						|
			{ | 
						|
				if (FirstOptionalArgumentIndex < 0) | 
						|
					return Arguments.Length; | 
						|
				return FirstOptionalArgumentIndex; | 
						|
			} | 
						|
 | 
						|
			public IList<ResolveResult> GetArgumentResolveResults(int skipCount = 0) | 
						|
			{ | 
						|
				var expectedParameters = ExpectedParameters; | 
						|
				var useImplicitlyTypedOut = UseImplicitlyTypedOut; | 
						|
 | 
						|
				return Arguments | 
						|
					.SelectWithIndex(GetResolveResult) | 
						|
					.Skip(skipCount) | 
						|
					.Take(GetActualArgumentCount()) | 
						|
					.ToArray(); | 
						|
 | 
						|
				ResolveResult GetResolveResult(int index, TranslatedExpression expression) | 
						|
				{ | 
						|
					var param = expectedParameters[index]; | 
						|
					if (useImplicitlyTypedOut && param.IsOut && expression.Type is ByReferenceType brt) | 
						|
						return new OutVarResolveResult(brt.ElementType); | 
						|
					return expression.ResolveResult; | 
						|
				} | 
						|
			} | 
						|
 | 
						|
			public IList<ResolveResult> GetArgumentResolveResultsDirect(int skipCount = 0) | 
						|
			{ | 
						|
				return Arguments | 
						|
					.Skip(skipCount) | 
						|
					.Take(GetActualArgumentCount()) | 
						|
					.Select(a => a.ResolveResult) | 
						|
					.ToArray(); | 
						|
			} | 
						|
 | 
						|
			public IEnumerable<Expression> GetArgumentExpressions(int skipCount = 0) | 
						|
			{ | 
						|
				if (AddNamesToPrimitiveValues && IsPrimitiveValue.Any() && !IsExpandedForm | 
						|
					&& !ParameterNames.Any(p => string.IsNullOrEmpty(p))) | 
						|
				{ | 
						|
					Debug.Assert(skipCount == 0); | 
						|
					if (ArgumentNames == null) | 
						|
					{ | 
						|
						ArgumentNames = new string[Arguments.Length]; | 
						|
					} | 
						|
 | 
						|
					for (int i = 0; i < Arguments.Length; i++) | 
						|
					{ | 
						|
						if (IsPrimitiveValue[i] && ArgumentNames[i] == null) | 
						|
						{ | 
						|
							ArgumentNames[i] = ParameterNames[i]; | 
						|
						} | 
						|
					} | 
						|
				} | 
						|
				int argumentCount = GetActualArgumentCount(); | 
						|
				var useImplicitlyTypedOut = UseImplicitlyTypedOut; | 
						|
				if (ArgumentNames == null) | 
						|
				{ | 
						|
					return Arguments.Skip(skipCount).Take(argumentCount).Select(arg => AddAnnotations(arg.Expression)); | 
						|
				} | 
						|
				else | 
						|
				{ | 
						|
					Debug.Assert(skipCount == 0); | 
						|
					return Arguments.Take(argumentCount).Zip(ArgumentNames.Take(argumentCount), | 
						|
						(arg, name) => { | 
						|
							if (name == null) | 
						|
								return AddAnnotations(arg.Expression); | 
						|
							else | 
						|
								return new NamedArgumentExpression(name, AddAnnotations(arg.Expression)); | 
						|
						}); | 
						|
				} | 
						|
 | 
						|
				Expression AddAnnotations(Expression expression) | 
						|
				{ | 
						|
					if (!useImplicitlyTypedOut) | 
						|
						return expression; | 
						|
					if (expression.GetResolveResult() is ByReferenceResolveResult brrr | 
						|
						&& brrr.IsOut) | 
						|
					{ | 
						|
						expression.AddAnnotation(UseImplicitlyTypedOutAnnotation.Instance); | 
						|
					} | 
						|
					return expression; | 
						|
				} | 
						|
			} | 
						|
 | 
						|
			public bool CanInferAnonymousTypePropertyNamesFromArguments() | 
						|
			{ | 
						|
				for (int i = 0; i < Arguments.Length; i++) | 
						|
				{ | 
						|
					string inferredName; | 
						|
					switch (Arguments[i].Expression) | 
						|
					{ | 
						|
						case IdentifierExpression identifier: | 
						|
							inferredName = identifier.Identifier; | 
						|
							break; | 
						|
						case MemberReferenceExpression member: | 
						|
							inferredName = member.MemberName; | 
						|
							break; | 
						|
						default: | 
						|
							inferredName = null; | 
						|
							break; | 
						|
					} | 
						|
 | 
						|
					if (inferredName != ExpectedParameters[i].Name) | 
						|
					{ | 
						|
						return false; | 
						|
					} | 
						|
				} | 
						|
				return true; | 
						|
			} | 
						|
 | 
						|
			[Conditional("DEBUG")] | 
						|
			public void CheckNoNamedOrOptionalArguments() | 
						|
			{ | 
						|
				Debug.Assert(ArgumentToParameterMap == null && ArgumentNames == null && FirstOptionalArgumentIndex < 0); | 
						|
			} | 
						|
		} | 
						|
 | 
						|
		readonly DecompilerSettings settings; | 
						|
		readonly ExpressionBuilder expressionBuilder; | 
						|
		readonly CSharpResolver resolver; | 
						|
		readonly IDecompilerTypeSystem typeSystem; | 
						|
 | 
						|
		public CallBuilder(ExpressionBuilder expressionBuilder, IDecompilerTypeSystem typeSystem, DecompilerSettings settings) | 
						|
		{ | 
						|
			this.expressionBuilder = expressionBuilder; | 
						|
			this.resolver = expressionBuilder.resolver; | 
						|
			this.settings = settings; | 
						|
			this.typeSystem = typeSystem; | 
						|
		} | 
						|
 | 
						|
		public TranslatedExpression Build(CallInstruction inst) | 
						|
		{ | 
						|
			if (inst is NewObj newobj && IL.Transforms.DelegateConstruction.MatchDelegateConstruction(newobj, out _, out _, out _)) | 
						|
			{ | 
						|
				return HandleDelegateConstruction(newobj); | 
						|
			} | 
						|
			if (settings.TupleTypes && TupleTransform.MatchTupleConstruction(inst as NewObj, out var tupleElements) && tupleElements.Length >= 2) | 
						|
			{ | 
						|
				var elementTypes = TupleType.GetTupleElementTypes(inst.Method.DeclaringType); | 
						|
				Debug.Assert(!elementTypes.IsDefault, "MatchTupleConstruction should not success unless we got a valid tuple type."); | 
						|
				Debug.Assert(elementTypes.Length == tupleElements.Length); | 
						|
				var tuple = new TupleExpression(); | 
						|
				var elementRRs = new List<ResolveResult>(); | 
						|
				foreach (var (element, elementType) in tupleElements.Zip(elementTypes)) | 
						|
				{ | 
						|
					var translatedElement = expressionBuilder.Translate(element, elementType) | 
						|
						.ConvertTo(elementType, expressionBuilder, allowImplicitConversion: true); | 
						|
					tuple.Elements.Add(translatedElement.Expression); | 
						|
					elementRRs.Add(translatedElement.ResolveResult); | 
						|
				} | 
						|
				return tuple.WithRR(new TupleResolveResult( | 
						|
					expressionBuilder.compilation, | 
						|
					elementRRs.ToImmutableArray(), | 
						|
					valueTupleAssembly: inst.Method.DeclaringType.GetDefinition()?.ParentModule | 
						|
				)).WithILInstruction(inst); | 
						|
			} | 
						|
			return Build(inst.OpCode, inst.Method, inst.Arguments, constrainedTo: inst.ConstrainedTo) | 
						|
				.WithILInstruction(inst); | 
						|
		} | 
						|
 | 
						|
		public ExpressionWithResolveResult Build(OpCode callOpCode, IMethod method, | 
						|
			IReadOnlyList<ILInstruction> callArguments, | 
						|
			IReadOnlyList<int> argumentToParameterMap = null, | 
						|
			IType constrainedTo = null) | 
						|
		{ | 
						|
			if (method.IsExplicitInterfaceImplementation && callOpCode == OpCode.Call) | 
						|
			{ | 
						|
				// Direct non-virtual call to explicit interface implementation. | 
						|
				// This can't really be represented in C#, but at least in the case where | 
						|
				// the class is sealed, we can equivalently call the interface member instead: | 
						|
				var interfaceMembers = method.ExplicitlyImplementedInterfaceMembers.ToList(); | 
						|
				if (method.DeclaringTypeDefinition?.Kind == TypeKind.Class && method.DeclaringTypeDefinition.IsSealed && interfaceMembers.Count == 1) | 
						|
				{ | 
						|
					method = (IMethod)interfaceMembers.Single(); | 
						|
					callOpCode = OpCode.CallVirt; | 
						|
				} | 
						|
			} | 
						|
			// Used for Call, CallVirt and NewObj | 
						|
			var expectedTargetDetails = new ExpectedTargetDetails { | 
						|
				CallOpCode = callOpCode | 
						|
			}; | 
						|
			ILFunction localFunction = null; | 
						|
			if (method.IsLocalFunction) | 
						|
			{ | 
						|
				localFunction = expressionBuilder.ResolveLocalFunction(method); | 
						|
				Debug.Assert(localFunction != null); | 
						|
			} | 
						|
			TranslatedExpression target; | 
						|
			if (callOpCode == OpCode.NewObj) | 
						|
			{ | 
						|
				target = default(TranslatedExpression); // no target | 
						|
			} | 
						|
			else if (localFunction != null) | 
						|
			{ | 
						|
				var ide = new IdentifierExpression(localFunction.Name); | 
						|
				if (method.TypeArguments.Count > 0) | 
						|
				{ | 
						|
					ide.TypeArguments.AddRange(method.TypeArguments.Select(expressionBuilder.ConvertType)); | 
						|
				} | 
						|
				ide.AddAnnotation(localFunction); | 
						|
				target = ide.WithoutILInstruction() | 
						|
					.WithRR(ToMethodGroup(method, localFunction)); | 
						|
			} | 
						|
			else | 
						|
			{ | 
						|
				target = expressionBuilder.TranslateTarget( | 
						|
					callArguments.FirstOrDefault(), | 
						|
					nonVirtualInvocation: callOpCode == OpCode.Call || method.IsConstructor, | 
						|
					memberStatic: method.IsStatic, | 
						|
					memberDeclaringType: constrainedTo ?? method.DeclaringType); | 
						|
				if (constrainedTo == null | 
						|
					&& target.Expression is CastExpression cast | 
						|
					&& target.ResolveResult is ConversionResolveResult conversion | 
						|
					&& target.Type.IsKnownType(KnownTypeCode.Object) | 
						|
					&& conversion.Conversion.IsBoxingConversion) | 
						|
				{ | 
						|
					// boxing conversion on call target? | 
						|
					// let's see if we can make that implicit: | 
						|
					target = target.UnwrapChild(cast.Expression); | 
						|
					// we'll need to make sure the boxing effect is preserved | 
						|
					expectedTargetDetails.NeedsBoxingConversion = true; | 
						|
				} | 
						|
			} | 
						|
 | 
						|
			int firstParamIndex = (method.IsStatic || callOpCode == OpCode.NewObj) ? 0 : 1; | 
						|
			Debug.Assert(firstParamIndex == 0 || argumentToParameterMap == null | 
						|
				|| argumentToParameterMap[0] == -1); | 
						|
 | 
						|
			var argumentList = BuildArgumentList(expectedTargetDetails, target.ResolveResult, method, | 
						|
				firstParamIndex, callArguments, argumentToParameterMap); | 
						|
 | 
						|
			if (localFunction != null) | 
						|
			{ | 
						|
				return new InvocationExpression(target, argumentList.GetArgumentExpressions()) | 
						|
					.WithRR(new CSharpInvocationResolveResult(target.ResolveResult, method, | 
						|
						argumentList.GetArgumentResolveResults(), isExpandedForm: argumentList.IsExpandedForm)); | 
						|
			} | 
						|
 | 
						|
			if (method is VarArgInstanceMethod) | 
						|
			{ | 
						|
				argumentList.FirstOptionalArgumentIndex = -1; | 
						|
				argumentList.AddNamesToPrimitiveValues = false; | 
						|
				argumentList.UseImplicitlyTypedOut = false; | 
						|
				int regularParameterCount = ((VarArgInstanceMethod)method).RegularParameterCount; | 
						|
				var argListArg = new UndocumentedExpression(); | 
						|
				argListArg.UndocumentedExpressionType = UndocumentedExpressionType.ArgList; | 
						|
				int paramIndex = regularParameterCount; | 
						|
				var builder = expressionBuilder; | 
						|
				Debug.Assert(argumentToParameterMap == null && argumentList.ArgumentNames == null); | 
						|
				argListArg.Arguments.AddRange(argumentList.Arguments.Skip(regularParameterCount).Select(arg => arg.ConvertTo(argumentList.ExpectedParameters[paramIndex++].Type, builder).Expression)); | 
						|
				var argListRR = new ResolveResult(SpecialType.ArgList); | 
						|
				argumentList.Arguments = argumentList.Arguments.Take(regularParameterCount) | 
						|
					.Concat(new[] { argListArg.WithoutILInstruction().WithRR(argListRR) }).ToArray(); | 
						|
				method = ((VarArgInstanceMethod)method).BaseMethod; | 
						|
				argumentList.ExpectedParameters = method.Parameters.ToArray(); | 
						|
			} | 
						|
 | 
						|
			if (settings.Ranges) | 
						|
			{ | 
						|
				if (HandleRangeConstruction(out var result, callOpCode, method, target, argumentList)) | 
						|
				{ | 
						|
					return result; | 
						|
				} | 
						|
			} | 
						|
 | 
						|
			if (callOpCode == OpCode.NewObj) | 
						|
			{ | 
						|
				return HandleConstructorCall(expectedTargetDetails, target.ResolveResult, method, argumentList); | 
						|
			} | 
						|
 | 
						|
			if (method.Name == "Invoke" && method.DeclaringType.Kind == TypeKind.Delegate && !IsNullConditional(target)) | 
						|
			{ | 
						|
				return new InvocationExpression(target, argumentList.GetArgumentExpressions()) | 
						|
					.WithRR(new CSharpInvocationResolveResult(target.ResolveResult, method, | 
						|
						argumentList.GetArgumentResolveResults(), isExpandedForm: argumentList.IsExpandedForm, isDelegateInvocation: true)); | 
						|
			} | 
						|
 | 
						|
			if (settings.StringInterpolation && IsInterpolatedStringCreation(method, argumentList) && | 
						|
				TryGetStringInterpolationTokens(argumentList, out string format, out var tokens)) | 
						|
			{ | 
						|
				var arguments = argumentList.Arguments; | 
						|
				var content = new List<InterpolatedStringContent>(); | 
						|
 | 
						|
				bool unpackSingleElementArray = !argumentList.IsExpandedForm && argumentList.Length == 2 | 
						|
					&& argumentList.Arguments[1].Expression is ArrayCreateExpression ace | 
						|
					&& ace.Initializer?.Elements.Count == 1; | 
						|
 | 
						|
				void UnpackSingleElementArray(ref TranslatedExpression argument) | 
						|
				{ | 
						|
					if (!unpackSingleElementArray) | 
						|
						return; | 
						|
					var arrayCreation = (ArrayCreateExpression)argumentList.Arguments[1].Expression; | 
						|
					var arrayCreationRR = (ArrayCreateResolveResult)argumentList.Arguments[1].ResolveResult; | 
						|
					var element = arrayCreation.Initializer.Elements.First().Detach(); | 
						|
					argument = new TranslatedExpression(element, arrayCreationRR.InitializerElements.First()); | 
						|
				} | 
						|
 | 
						|
				if (tokens.Count > 0) | 
						|
				{ | 
						|
					foreach (var (kind, index, alignment, text) in tokens) | 
						|
					{ | 
						|
						TranslatedExpression argument; | 
						|
						switch (kind) | 
						|
						{ | 
						|
							case TokenKind.String: | 
						|
								content.Add(new InterpolatedStringText(text)); | 
						|
								break; | 
						|
							case TokenKind.Argument: | 
						|
								argument = arguments[index + 1]; | 
						|
								UnpackSingleElementArray(ref argument); | 
						|
								content.Add(new Interpolation(argument)); | 
						|
								break; | 
						|
							case TokenKind.ArgumentWithFormat: | 
						|
								argument = arguments[index + 1]; | 
						|
								UnpackSingleElementArray(ref argument); | 
						|
								content.Add(new Interpolation(argument, suffix: text)); | 
						|
								break; | 
						|
							case TokenKind.ArgumentWithAlignment: | 
						|
								argument = arguments[index + 1]; | 
						|
								UnpackSingleElementArray(ref argument); | 
						|
								content.Add(new Interpolation(argument, alignment)); | 
						|
								break; | 
						|
							case TokenKind.ArgumentWithAlignmentAndFormat: | 
						|
								argument = arguments[index + 1]; | 
						|
								UnpackSingleElementArray(ref argument); | 
						|
								content.Add(new Interpolation(argument, alignment, text)); | 
						|
								break; | 
						|
						} | 
						|
					} | 
						|
					var formattableStringType = expressionBuilder.compilation.FindType(KnownTypeCode.FormattableString); | 
						|
					var isrr = new InterpolatedStringResolveResult(expressionBuilder.compilation.FindType(KnownTypeCode.String), | 
						|
						format, argumentList.GetArgumentResolveResults(1).ToArray()); | 
						|
					var expr = new InterpolatedStringExpression(); | 
						|
					expr.Content.AddRange(content); | 
						|
					if (method.Name == "Format") | 
						|
						return expr.WithRR(isrr); | 
						|
					return new CastExpression(expressionBuilder.ConvertType(formattableStringType), | 
						|
						expr.WithRR(isrr)) | 
						|
						.WithRR(new ConversionResolveResult(formattableStringType, isrr, Conversion.ImplicitInterpolatedStringConversion)); | 
						|
				} | 
						|
			} | 
						|
 | 
						|
			int allowedParamCount = (method.ReturnType.IsKnownType(KnownTypeCode.Void) ? 1 : 0); | 
						|
			if (method.IsAccessor && (method.AccessorOwner.SymbolKind == SymbolKind.Indexer || argumentList.ExpectedParameters.Length == allowedParamCount)) | 
						|
			{ | 
						|
				argumentList.CheckNoNamedOrOptionalArguments(); | 
						|
				return HandleAccessorCall(expectedTargetDetails, method, target, argumentList.Arguments.ToList(), argumentList.ArgumentNames); | 
						|
			} | 
						|
 | 
						|
			if (IsDelegateEqualityComparison(method, argumentList.Arguments)) | 
						|
			{ | 
						|
				argumentList.CheckNoNamedOrOptionalArguments(); | 
						|
				return HandleDelegateEqualityComparison(method, argumentList.Arguments) | 
						|
					.WithRR(new CSharpInvocationResolveResult(target.ResolveResult, method, | 
						|
						argumentList.GetArgumentResolveResults(), isExpandedForm: argumentList.IsExpandedForm)); | 
						|
			} | 
						|
 | 
						|
			if (method.IsOperator && method.Name == "op_Implicit" && argumentList.Length == 1) | 
						|
			{ | 
						|
				argumentList.CheckNoNamedOrOptionalArguments(); | 
						|
				return HandleImplicitConversion(method, argumentList.Arguments[0]); | 
						|
			} | 
						|
 | 
						|
			var transform = GetRequiredTransformationsForCall(expectedTargetDetails, method, ref target, | 
						|
				ref argumentList, CallTransformation.All, out IParameterizedMember foundMethod); | 
						|
 | 
						|
			// Note: after this, 'method' and 'foundMethod' may differ, | 
						|
			// but as far as allowed by IsAppropriateCallTarget(). | 
						|
 | 
						|
			// Need to update list of parameter names, because foundMethod is different and thus might use different names. | 
						|
			if (!method.Equals(foundMethod) && argumentList.ParameterNames.Length >= foundMethod.Parameters.Count) | 
						|
			{ | 
						|
				for (int i = 0; i < foundMethod.Parameters.Count; i++) | 
						|
				{ | 
						|
					argumentList.ParameterNames[i] = foundMethod.Parameters[i].Name; | 
						|
				} | 
						|
			} | 
						|
 | 
						|
			Expression targetExpr; | 
						|
			string methodName = method.Name; | 
						|
			AstNodeCollection<AstType> typeArgumentList; | 
						|
			if ((transform & CallTransformation.NoOptionalArgumentAllowed) != 0) | 
						|
			{ | 
						|
				argumentList.FirstOptionalArgumentIndex = -1; | 
						|
			} | 
						|
			if ((transform & CallTransformation.RequireTarget) != 0) | 
						|
			{ | 
						|
				targetExpr = new MemberReferenceExpression(target.Expression, methodName); | 
						|
				typeArgumentList = ((MemberReferenceExpression)targetExpr).TypeArguments; | 
						|
 | 
						|
				// HACK : convert this.Dispose() to ((IDisposable)this).Dispose(), if Dispose is an explicitly implemented interface method. | 
						|
				// settings.AlwaysCastTargetsOfExplicitInterfaceImplementationCalls == true is used in Windows Forms' InitializeComponent methods. | 
						|
				if (method.IsExplicitInterfaceImplementation && (target.Expression is ThisReferenceExpression || settings.AlwaysCastTargetsOfExplicitInterfaceImplementationCalls)) | 
						|
				{ | 
						|
					var interfaceMember = method.ExplicitlyImplementedInterfaceMembers.First(); | 
						|
					var castExpression = new CastExpression(expressionBuilder.ConvertType(interfaceMember.DeclaringType), target.Expression.Detach()); | 
						|
					methodName = interfaceMember.Name; | 
						|
					targetExpr = new MemberReferenceExpression(castExpression, methodName); | 
						|
					typeArgumentList = ((MemberReferenceExpression)targetExpr).TypeArguments; | 
						|
				} | 
						|
			} | 
						|
			else | 
						|
			{ | 
						|
				targetExpr = new IdentifierExpression(methodName); | 
						|
				typeArgumentList = ((IdentifierExpression)targetExpr).TypeArguments; | 
						|
			} | 
						|
 | 
						|
			if ((transform & CallTransformation.RequireTypeArguments) != 0 && (!settings.AnonymousTypes || !method.TypeArguments.Any(a => a.ContainsAnonymousType()))) | 
						|
				typeArgumentList.AddRange(method.TypeArguments.Select(expressionBuilder.ConvertType)); | 
						|
			return new InvocationExpression(targetExpr, argumentList.GetArgumentExpressions()) | 
						|
				.WithRR(new CSharpInvocationResolveResult(target.ResolveResult, foundMethod, | 
						|
					argumentList.GetArgumentResolveResultsDirect(), isExpandedForm: argumentList.IsExpandedForm)); | 
						|
		} | 
						|
 | 
						|
		/// <summary> | 
						|
		/// Converts a call to an Add method to a collection initializer expression. | 
						|
		/// </summary> | 
						|
		public ExpressionWithResolveResult BuildCollectionInitializerExpression(OpCode callOpCode, IMethod method, | 
						|
			InitializedObjectResolveResult target, IReadOnlyList<ILInstruction> callArguments) | 
						|
		{ | 
						|
			// (see ECMA-334, section 12.7.11.4): | 
						|
			// The collection object to which a collection initializer is applied shall be of a type that implements | 
						|
			// System.Collections.IEnumerable or a compile-time error occurs. For each specified element in order, | 
						|
			// the collection initializer invokes an Add method on the target object with the expression list of the | 
						|
			// element initializer as argument list, applying normal overload resolution for each invocation. Thus, the | 
						|
			// collection object shall contain an applicable Add method for each element initializer. | 
						|
 | 
						|
			// The list of applicable methods includes all methods (as of C# 6.0 extension methods, too) named 'Add' | 
						|
			// that can be invoked on the target object, with the following exceptions: | 
						|
			// - Methods with ref or out parameters may not be used, | 
						|
			// - methods that have type parameters, that cannot be inferred from the parameter list may not be used, | 
						|
			// - vararg methods may not be used. | 
						|
			// - named arguments are not supported. | 
						|
			// However, note that params methods may be used. | 
						|
 | 
						|
			// At this point, we assume that 'method' fulfills all the conditions mentioned above. We just need to make | 
						|
			// sure that the correct method is called by resolving any ambiguities by inserting casts, if necessary. | 
						|
 | 
						|
			ExpectedTargetDetails expectedTargetDetails = new ExpectedTargetDetails { CallOpCode = callOpCode }; | 
						|
			var unused = new IdentifierExpression("initializedObject").WithRR(target).WithoutILInstruction(); | 
						|
			var args = callArguments.ToList(); | 
						|
			if (method.IsExtensionMethod) | 
						|
				args.Insert(0, new Nop()); | 
						|
 | 
						|
			var argumentList = BuildArgumentList(expectedTargetDetails, target, method, | 
						|
				firstParamIndex: 0, args, null); | 
						|
			argumentList.ArgumentNames = null; | 
						|
			argumentList.AddNamesToPrimitiveValues = false; | 
						|
			argumentList.UseImplicitlyTypedOut = false; | 
						|
			var transform = GetRequiredTransformationsForCall(expectedTargetDetails, method, ref unused, | 
						|
				ref argumentList, CallTransformation.None, out _); | 
						|
			Debug.Assert(transform == CallTransformation.None || transform == CallTransformation.NoOptionalArgumentAllowed); | 
						|
 | 
						|
			// Calls with only one argument do not need an array initializer expression to wrap them. | 
						|
			// Any special cases are handled by the caller (i.e., ExpressionBuilder.TranslateObjectAndCollectionInitializer) | 
						|
			// Note: we intentionally ignore the firstOptionalArgumentIndex in this case. | 
						|
			int skipCount; | 
						|
			if (method.IsExtensionMethod) | 
						|
			{ | 
						|
				if (argumentList.Arguments.Length == 2) | 
						|
					return argumentList.Arguments[1]; | 
						|
				skipCount = 1; | 
						|
			} | 
						|
			else | 
						|
			{ | 
						|
				if (argumentList.Arguments.Length == 1) | 
						|
					return argumentList.Arguments[0]; | 
						|
				skipCount = 0; | 
						|
			} | 
						|
 | 
						|
			if ((transform & CallTransformation.NoOptionalArgumentAllowed) != 0) | 
						|
				argumentList.FirstOptionalArgumentIndex = -1; | 
						|
 | 
						|
			return new ArrayInitializerExpression(argumentList.GetArgumentExpressions(skipCount)) | 
						|
				.WithRR(new CSharpInvocationResolveResult(target, method, argumentList.GetArgumentResolveResults(skipCount).ToArray(), | 
						|
					isExtensionMethodInvocation: method.IsExtensionMethod, isExpandedForm: argumentList.IsExpandedForm)); | 
						|
		} | 
						|
 | 
						|
		public ExpressionWithResolveResult BuildDictionaryInitializerExpression(OpCode callOpCode, IMethod method, | 
						|
			InitializedObjectResolveResult target, IReadOnlyList<ILInstruction> indices, ILInstruction value = null) | 
						|
		{ | 
						|
			if (method is null) | 
						|
				throw new ArgumentNullException(nameof(method)); | 
						|
			ExpectedTargetDetails expectedTargetDetails = new ExpectedTargetDetails { CallOpCode = callOpCode }; | 
						|
 | 
						|
			var callArguments = new List<ILInstruction>(); | 
						|
			callArguments.Add(new LdNull()); | 
						|
			callArguments.AddRange(indices); | 
						|
			callArguments.Add(value ?? new Nop()); | 
						|
 | 
						|
			var argumentList = BuildArgumentList(expectedTargetDetails, target, method, 1, callArguments, null); | 
						|
			var unused = new IdentifierExpression("initializedObject").WithRR(target).WithoutILInstruction(); | 
						|
 | 
						|
			var assignment = HandleAccessorCall(expectedTargetDetails, method, unused, | 
						|
				argumentList.Arguments.ToList(), argumentList.ArgumentNames); | 
						|
 | 
						|
			if (((AssignmentExpression)assignment).Left is IndexerExpression indexer && !indexer.Target.IsNull) | 
						|
				indexer.Target.Remove(); | 
						|
 | 
						|
			if (value != null) | 
						|
				return assignment; | 
						|
 | 
						|
			return new ExpressionWithResolveResult(((AssignmentExpression)assignment).Left.Detach()); | 
						|
		} | 
						|
 | 
						|
		private static bool IsInterpolatedStringCreation(IMethod method, ArgumentList argumentList) | 
						|
		{ | 
						|
			return method.IsStatic && ( | 
						|
				(method.DeclaringType.IsKnownType(KnownTypeCode.String) && method.Name == "Format") || | 
						|
				(method.Name == "Create" && method.DeclaringType.Name == "FormattableStringFactory" && | 
						|
					method.DeclaringType.Namespace == "System.Runtime.CompilerServices") | 
						|
			) | 
						|
			&& argumentList.ArgumentNames == null // Argument names are not allowed | 
						|
			&& ( | 
						|
				argumentList.IsExpandedForm // Must be expanded form | 
						|
				|| !method.Parameters.Last().IsParams // -or- not a params overload | 
						|
				|| (argumentList.Length == 2 && argumentList.Arguments[1].Expression is ArrayCreateExpression) // -or- an array literal | 
						|
			); | 
						|
		} | 
						|
 | 
						|
		private bool TryGetStringInterpolationTokens(ArgumentList argumentList, out string format, out List<(TokenKind Kind, int Index, int Alignment, string Format)> tokens) | 
						|
		{ | 
						|
			tokens = null; | 
						|
			format = null; | 
						|
			TranslatedExpression[] arguments = argumentList.Arguments; | 
						|
			if (arguments.Length == 0 || argumentList.ArgumentNames != null || argumentList.ArgumentToParameterMap != null) | 
						|
				return false; | 
						|
			if (!(arguments[(int)0].ResolveResult is ConstantResolveResult crr && crr.Type.IsKnownType((KnownTypeCode)KnownTypeCode.String))) | 
						|
				return false; | 
						|
			if (!arguments.Skip(1).All(a => !a.Expression.DescendantsAndSelf.OfType<PrimitiveExpression>().Any(p => p.Value is string))) | 
						|
				return false; | 
						|
			tokens = new List<(TokenKind Kind, int Index, int Alignment, string Format)>(); | 
						|
			int i = 0; | 
						|
			format = (string)crr.ConstantValue; | 
						|
			foreach (var (kind, data) in TokenizeFormatString(format)) | 
						|
			{ | 
						|
				int index; | 
						|
				string[] arg; | 
						|
				switch (kind) | 
						|
				{ | 
						|
					case TokenKind.Error: | 
						|
						return false; | 
						|
					case TokenKind.String: | 
						|
						tokens.Add((kind, -1, 0, data)); | 
						|
						break; | 
						|
					case TokenKind.Argument: | 
						|
						if (!int.TryParse(data, out index) || index != i) | 
						|
							return false; | 
						|
						i++; | 
						|
						tokens.Add((kind, index, 0, null)); | 
						|
						break; | 
						|
					case TokenKind.ArgumentWithFormat: | 
						|
						arg = data.Split(new[] { ':' }, 2); | 
						|
						if (arg.Length != 2 || arg[1].Length == 0) | 
						|
							return false; | 
						|
						if (!int.TryParse(arg[0], out index) || index != i) | 
						|
							return false; | 
						|
						i++; | 
						|
						tokens.Add((kind, index, 0, arg[1])); | 
						|
						break; | 
						|
					case TokenKind.ArgumentWithAlignment: | 
						|
						arg = data.Split(new[] { ',' }, 2); | 
						|
						if (arg.Length != 2 || arg[1].Length == 0) | 
						|
							return false; | 
						|
						if (!int.TryParse(arg[0], out index) || index != i) | 
						|
							return false; | 
						|
						if (!int.TryParse(arg[1], out int alignment)) | 
						|
							return false; | 
						|
						i++; | 
						|
						tokens.Add((kind, index, alignment, null)); | 
						|
						break; | 
						|
					case TokenKind.ArgumentWithAlignmentAndFormat: | 
						|
						arg = data.Split(new[] { ',', ':' }, 3); | 
						|
						if (arg.Length != 3 || arg[1].Length == 0 || arg[2].Length == 0) | 
						|
							return false; | 
						|
						if (!int.TryParse(arg[0], out index) || index != i) | 
						|
							return false; | 
						|
						if (!int.TryParse(arg[1], out alignment)) | 
						|
							return false; | 
						|
						i++; | 
						|
						tokens.Add((kind, index, alignment, arg[2])); | 
						|
						break; | 
						|
					default: | 
						|
						return false; | 
						|
				} | 
						|
			} | 
						|
			return i == arguments.Length - 1; | 
						|
		} | 
						|
 | 
						|
		private enum TokenKind | 
						|
		{ | 
						|
			Error, | 
						|
			String, | 
						|
			Argument, | 
						|
			ArgumentWithFormat, | 
						|
			ArgumentWithAlignment, | 
						|
			ArgumentWithAlignmentAndFormat, | 
						|
		} | 
						|
 | 
						|
		private IEnumerable<(TokenKind, string)> TokenizeFormatString(string value) | 
						|
		{ | 
						|
			int pos = -1; | 
						|
 | 
						|
			int Peek(int steps = 1) | 
						|
			{ | 
						|
				if (pos + steps < value.Length) | 
						|
					return value[pos + steps]; | 
						|
				return -1; | 
						|
			} | 
						|
 | 
						|
			int Next() | 
						|
			{ | 
						|
				int val = Peek(); | 
						|
				pos++; | 
						|
				return val; | 
						|
			} | 
						|
 | 
						|
			int next; | 
						|
			TokenKind kind = TokenKind.String; | 
						|
			StringBuilder sb = new StringBuilder(); | 
						|
 | 
						|
			while ((next = Next()) > -1) | 
						|
			{ | 
						|
				switch ((char)next) | 
						|
				{ | 
						|
					case '{': | 
						|
						if (Peek() == '{') | 
						|
						{ | 
						|
							kind = TokenKind.String; | 
						|
							sb.Append("{{"); | 
						|
							Next(); | 
						|
						} | 
						|
						else | 
						|
						{ | 
						|
							if (sb.Length > 0) | 
						|
							{ | 
						|
								yield return (kind, sb.ToString()); | 
						|
							} | 
						|
							kind = TokenKind.Argument; | 
						|
							sb.Clear(); | 
						|
						} | 
						|
						break; | 
						|
					case '}': | 
						|
						if (kind != TokenKind.String) | 
						|
						{ | 
						|
							yield return (kind, sb.ToString()); | 
						|
							sb.Clear(); | 
						|
							kind = TokenKind.String; | 
						|
						} | 
						|
						else if (Peek() == '}') | 
						|
						{ | 
						|
							sb.Append("}}"); | 
						|
							Next(); | 
						|
						} | 
						|
						else | 
						|
						{ | 
						|
							yield return (TokenKind.Error, null); | 
						|
						} | 
						|
						break; | 
						|
					case ':': | 
						|
						if (kind == TokenKind.Argument) | 
						|
						{ | 
						|
							kind = TokenKind.ArgumentWithFormat; | 
						|
						} | 
						|
						else if (kind == TokenKind.ArgumentWithAlignment) | 
						|
						{ | 
						|
							kind = TokenKind.ArgumentWithAlignmentAndFormat; | 
						|
						} | 
						|
						sb.Append(':'); | 
						|
						break; | 
						|
					case ',': | 
						|
						if (kind == TokenKind.Argument) | 
						|
						{ | 
						|
							kind = TokenKind.ArgumentWithAlignment; | 
						|
						} | 
						|
						sb.Append(','); | 
						|
						break; | 
						|
					default: | 
						|
						sb.Append((char)next); | 
						|
						break; | 
						|
				} | 
						|
			} | 
						|
			if (sb.Length > 0) | 
						|
			{ | 
						|
				if (kind == TokenKind.String) | 
						|
					yield return (kind, sb.ToString()); | 
						|
				else | 
						|
					yield return (TokenKind.Error, null); | 
						|
			} | 
						|
		} | 
						|
 | 
						|
		private ArgumentList BuildArgumentList(ExpectedTargetDetails expectedTargetDetails, ResolveResult target, IMethod method, | 
						|
			int firstParamIndex, IReadOnlyList<ILInstruction> callArguments, IReadOnlyList<int> argumentToParameterMap) | 
						|
		{ | 
						|
			ArgumentList list = new ArgumentList(); | 
						|
 | 
						|
			// Translate arguments to the expected parameter types | 
						|
			var arguments = new List<TranslatedExpression>(method.Parameters.Count); | 
						|
			string[] argumentNames = null; | 
						|
			Debug.Assert(callArguments.Count == firstParamIndex + method.Parameters.Count); | 
						|
			var expectedParameters = new List<IParameter>(method.Parameters.Count); // parameters, but in argument order | 
						|
			bool isExpandedForm = false; | 
						|
			BitSet isPrimitiveValue = new BitSet(method.Parameters.Count); | 
						|
 | 
						|
			// Optional arguments: | 
						|
			// This value has the following values: | 
						|
			// -2 - there are no optional arguments | 
						|
			// -1 - optional arguments are forbidden | 
						|
			// >= 0 - the index of the first argument that can be removed, because it is optional | 
						|
			// and is the default value of the parameter.  | 
						|
			int firstOptionalArgumentIndex = expressionBuilder.settings.OptionalArguments ? -2 : -1; | 
						|
			for (int i = firstParamIndex; i < callArguments.Count; i++) | 
						|
			{ | 
						|
				IParameter parameter; | 
						|
				if (argumentToParameterMap != null) | 
						|
				{ | 
						|
					if (argumentNames == null && argumentToParameterMap[i] != i - firstParamIndex) | 
						|
					{ | 
						|
						// Starting at the first argument that is out-of-place, | 
						|
						// assign names to that argument and all following arguments: | 
						|
						argumentNames = new string[method.Parameters.Count]; | 
						|
					} | 
						|
					parameter = method.Parameters[argumentToParameterMap[i]]; | 
						|
					if (argumentNames != null) | 
						|
					{ | 
						|
						argumentNames[arguments.Count] = parameter.Name; | 
						|
					} | 
						|
				} | 
						|
				else | 
						|
				{ | 
						|
					parameter = method.Parameters[i - firstParamIndex]; | 
						|
				} | 
						|
				var arg = expressionBuilder.Translate(callArguments[i], parameter.Type); | 
						|
				if (IsPrimitiveValueThatShouldBeNamedArgument(arg, method, parameter)) | 
						|
				{ | 
						|
					isPrimitiveValue.Set(arguments.Count); | 
						|
				} | 
						|
				if (IsOptionalArgument(parameter, arg)) | 
						|
				{ | 
						|
					if (firstOptionalArgumentIndex == -2) | 
						|
						firstOptionalArgumentIndex = i - firstParamIndex; | 
						|
				} | 
						|
				else | 
						|
				{ | 
						|
					firstOptionalArgumentIndex = -2; | 
						|
				} | 
						|
				if (parameter.IsParams && i + 1 == callArguments.Count && argumentToParameterMap == null) | 
						|
				{ | 
						|
					// Parameter is marked params | 
						|
					// If the argument is an array creation, inline all elements into the call and add missing default values. | 
						|
					// Otherwise handle it normally. | 
						|
					if (TransformParamsArgument(expectedTargetDetails, target, method, parameter, | 
						|
						arg, ref expectedParameters, ref arguments)) | 
						|
					{ | 
						|
						Debug.Assert(argumentNames == null); | 
						|
						firstOptionalArgumentIndex = -1; | 
						|
						isExpandedForm = true; | 
						|
						continue; | 
						|
					} | 
						|
				} | 
						|
 | 
						|
				IType parameterType; | 
						|
				if (parameter.Type.Kind == TypeKind.Dynamic) | 
						|
				{ | 
						|
					parameterType = expressionBuilder.compilation.FindType(KnownTypeCode.Object); | 
						|
				} | 
						|
				else | 
						|
				{ | 
						|
					parameterType = parameter.Type; | 
						|
				} | 
						|
 | 
						|
				arg = arg.ConvertTo(parameterType, expressionBuilder, allowImplicitConversion: arg.Type.Kind != TypeKind.Dynamic); | 
						|
 | 
						|
				if (parameter.ReferenceKind != ReferenceKind.None) | 
						|
				{ | 
						|
					arg = ExpressionBuilder.ChangeDirectionExpressionTo(arg, parameter.ReferenceKind); | 
						|
				} | 
						|
 | 
						|
				arguments.Add(arg); | 
						|
				expectedParameters.Add(parameter); | 
						|
			} | 
						|
 | 
						|
			list.ExpectedParameters = expectedParameters.ToArray(); | 
						|
			list.Arguments = arguments.ToArray(); | 
						|
			list.ParameterNames = expectedParameters.SelectArray(p => p.Name); | 
						|
			list.ArgumentNames = argumentNames; | 
						|
			list.ArgumentToParameterMap = argumentToParameterMap; | 
						|
			list.IsExpandedForm = isExpandedForm; | 
						|
			list.IsPrimitiveValue = isPrimitiveValue; | 
						|
			list.FirstOptionalArgumentIndex = firstOptionalArgumentIndex; | 
						|
			list.UseImplicitlyTypedOut = true; | 
						|
			list.AddNamesToPrimitiveValues = expressionBuilder.settings.NamedArguments && expressionBuilder.settings.NonTrailingNamedArguments; | 
						|
			return list; | 
						|
		} | 
						|
 | 
						|
		private bool IsPrimitiveValueThatShouldBeNamedArgument(TranslatedExpression arg, IMethod method, IParameter p) | 
						|
		{ | 
						|
			if (!arg.ResolveResult.IsCompileTimeConstant || method.DeclaringType.IsKnownType(KnownTypeCode.NullableOfT)) | 
						|
				return false; | 
						|
			return p.Type.IsKnownType(KnownTypeCode.Boolean); | 
						|
		} | 
						|
 | 
						|
		private bool TransformParamsArgument(ExpectedTargetDetails expectedTargetDetails, ResolveResult targetResolveResult, | 
						|
			IMethod method, IParameter parameter, TranslatedExpression arg, ref List<IParameter> expectedParameters, | 
						|
			ref List<TranslatedExpression> arguments) | 
						|
		{ | 
						|
			if (CheckArgument(out int length, out IType elementType)) | 
						|
			{ | 
						|
				var expandedParameters = new List<IParameter>(expectedParameters); | 
						|
				var expandedArguments = new List<TranslatedExpression>(arguments); | 
						|
				if (length > 0) | 
						|
				{ | 
						|
					var arrayElements = ((ArrayCreateExpression)arg.Expression).Initializer.Elements.ToArray(); | 
						|
					for (int j = 0; j < length; j++) | 
						|
					{ | 
						|
						expandedParameters.Add(new DefaultParameter(elementType, parameter.Name + j)); | 
						|
						if (j < arrayElements.Length) | 
						|
							expandedArguments.Add(new TranslatedExpression(arrayElements[j])); | 
						|
						else | 
						|
							expandedArguments.Add(expressionBuilder.GetDefaultValueExpression(elementType).WithoutILInstruction()); | 
						|
					} | 
						|
				} | 
						|
				if (IsUnambiguousCall(expectedTargetDetails, method, targetResolveResult, Empty<IType>.Array, | 
						|
					expandedArguments.SelectArray(a => a.ResolveResult), argumentNames: null, | 
						|
					firstOptionalArgumentIndex: -1, out _, | 
						|
					out var bestCandidateIsExpandedForm) == OverloadResolutionErrors.None && bestCandidateIsExpandedForm) | 
						|
				{ | 
						|
					expectedParameters = expandedParameters; | 
						|
					arguments = expandedArguments.SelectList(a => new TranslatedExpression(a.Expression.Detach())); | 
						|
					return true; | 
						|
				} | 
						|
			} | 
						|
			return false; | 
						|
 | 
						|
			bool CheckArgument(out int len, out IType t) | 
						|
			{ | 
						|
				len = 0; | 
						|
				t = null; | 
						|
				if (arg.ResolveResult is CSharpInvocationResolveResult csirr && | 
						|
					csirr.Arguments.Count == 0 && csirr.Member is IMethod emptyMethod && | 
						|
					emptyMethod.IsStatic && | 
						|
					"System.Array.Empty" == emptyMethod.FullName && | 
						|
					emptyMethod.TypeArguments.Count == 1) | 
						|
				{ | 
						|
					t = emptyMethod.TypeArguments[0]; | 
						|
					return true; | 
						|
				} | 
						|
 | 
						|
				if (arg.ResolveResult is ArrayCreateResolveResult acrr && | 
						|
					acrr.SizeArguments.Count == 1 && | 
						|
					acrr.SizeArguments[0].IsCompileTimeConstant && | 
						|
					acrr.SizeArguments[0].ConstantValue is int l) | 
						|
				{ | 
						|
					len = l; | 
						|
					t = ((ArrayType)acrr.Type).ElementType; | 
						|
					return true; | 
						|
				} | 
						|
				return false; | 
						|
			} | 
						|
		} | 
						|
 | 
						|
		bool IsOptionalArgument(IParameter parameter, TranslatedExpression arg) | 
						|
		{ | 
						|
			if (!parameter.IsOptional || !arg.ResolveResult.IsCompileTimeConstant) | 
						|
				return false; | 
						|
			if (parameter.GetAttributes().Any(a => a.AttributeType.IsKnownType(KnownAttribute.CallerMemberName) | 
						|
				|| a.AttributeType.IsKnownType(KnownAttribute.CallerFilePath) | 
						|
				|| a.AttributeType.IsKnownType(KnownAttribute.CallerLineNumber))) | 
						|
				return false; | 
						|
			return object.Equals(parameter.GetConstantValue(), arg.ResolveResult.ConstantValue); | 
						|
		} | 
						|
 | 
						|
		[Flags] | 
						|
		enum CallTransformation | 
						|
		{ | 
						|
			None = 0, | 
						|
			RequireTarget = 1, | 
						|
			RequireTypeArguments = 2, | 
						|
			NoOptionalArgumentAllowed = 4, | 
						|
			All = 7 | 
						|
		} | 
						|
 | 
						|
		private CallTransformation GetRequiredTransformationsForCall(ExpectedTargetDetails expectedTargetDetails, IMethod method, | 
						|
			ref TranslatedExpression target, ref ArgumentList argumentList, CallTransformation allowedTransforms, out IParameterizedMember foundMethod) | 
						|
		{ | 
						|
			CallTransformation transform = CallTransformation.None; | 
						|
 | 
						|
			// initialize requireTarget flag | 
						|
			bool requireTarget; | 
						|
			ResolveResult targetResolveResult; | 
						|
			if ((allowedTransforms & CallTransformation.RequireTarget) != 0) | 
						|
			{ | 
						|
				if (settings.AlwaysQualifyMemberReferences || expressionBuilder.HidesVariableWithName(method.Name)) | 
						|
				{ | 
						|
					requireTarget = true; | 
						|
				} | 
						|
				else | 
						|
				{ | 
						|
					if (method.IsLocalFunction) | 
						|
						requireTarget = false; | 
						|
					else if (method.IsStatic) | 
						|
						requireTarget = !expressionBuilder.IsCurrentOrContainingType(method.DeclaringTypeDefinition) || method.Name == ".cctor"; | 
						|
					else if (method.Name == ".ctor") | 
						|
						requireTarget = true; // always use target for base/this-ctor-call, the constructor initializer pattern depends on this | 
						|
					else if (target.Expression is BaseReferenceExpression) | 
						|
						requireTarget = (expectedTargetDetails.CallOpCode != OpCode.CallVirt && method.IsVirtual); | 
						|
					else | 
						|
						requireTarget = target.Expression is not ThisReferenceExpression; | 
						|
				} | 
						|
				targetResolveResult = requireTarget ? target.ResolveResult : null; | 
						|
			} | 
						|
			else | 
						|
			{ | 
						|
				// HACK: this is a special case for collection initializer calls, they do not allow a target to be | 
						|
				// emitted, but we still need it for overload resolution. | 
						|
				requireTarget = true; | 
						|
				targetResolveResult = target.ResolveResult; | 
						|
			} | 
						|
 | 
						|
			// initialize requireTypeArguments flag | 
						|
			bool requireTypeArguments; | 
						|
			IType[] typeArguments; | 
						|
			bool appliedRequireTypeArgumentsShortcut = false; | 
						|
			if (method.TypeParameters.Count > 0 && (allowedTransforms & CallTransformation.RequireTypeArguments) != 0 | 
						|
				&& !IsPossibleExtensionMethodCallOnNull(method, argumentList.Arguments)) | 
						|
			{ | 
						|
				// The ambiguity resolution below only adds type arguments as last resort measure, however there are | 
						|
				// methods, such as Enumerable.OfType<TResult>(IEnumerable input) that always require type arguments, | 
						|
				// as those cannot be inferred from the parameters, which leads to bloated expressions full of extra casts | 
						|
				// that are no longer required once we add the type arguments. | 
						|
				// We lend overload resolution a hand by detecting such cases beforehand and requiring type arguments, | 
						|
				// if necessary. | 
						|
				if (!CanInferTypeArgumentsFromArguments(method, argumentList, expressionBuilder.typeInference)) | 
						|
				{ | 
						|
					requireTypeArguments = true; | 
						|
					typeArguments = method.TypeArguments.ToArray(); | 
						|
					appliedRequireTypeArgumentsShortcut = true; | 
						|
				} | 
						|
				else | 
						|
				{ | 
						|
					requireTypeArguments = false; | 
						|
					typeArguments = Empty<IType>.Array; | 
						|
				} | 
						|
			} | 
						|
			else | 
						|
			{ | 
						|
				requireTypeArguments = false; | 
						|
				typeArguments = Empty<IType>.Array; | 
						|
			} | 
						|
 | 
						|
			bool targetCasted = false; | 
						|
			bool argumentsCasted = false; | 
						|
			bool originalRequireTarget = requireTarget; | 
						|
			bool skipTargetCast = method.Accessibility <= Accessibility.Protected && expressionBuilder.IsBaseTypeOfCurrentType(method.DeclaringTypeDefinition); | 
						|
			OverloadResolutionErrors errors; | 
						|
			while ((errors = IsUnambiguousCall(expectedTargetDetails, method, targetResolveResult, typeArguments, | 
						|
				argumentList.GetArgumentResolveResults().ToArray(), argumentList.ArgumentNames, argumentList.FirstOptionalArgumentIndex, out foundMethod, | 
						|
				out var bestCandidateIsExpandedForm)) != OverloadResolutionErrors.None || bestCandidateIsExpandedForm != argumentList.IsExpandedForm) | 
						|
			{ | 
						|
				switch (errors) | 
						|
				{ | 
						|
					case OverloadResolutionErrors.OutVarTypeMismatch: | 
						|
						Debug.Assert(argumentList.UseImplicitlyTypedOut); | 
						|
						argumentList.UseImplicitlyTypedOut = false; | 
						|
						continue; | 
						|
					case OverloadResolutionErrors.TypeInferenceFailed: | 
						|
						if ((allowedTransforms & CallTransformation.RequireTypeArguments) != 0) | 
						|
						{ | 
						|
							goto case OverloadResolutionErrors.WrongNumberOfTypeArguments; | 
						|
						} | 
						|
						goto default; | 
						|
					case OverloadResolutionErrors.WrongNumberOfTypeArguments: | 
						|
						Debug.Assert((allowedTransforms & CallTransformation.RequireTypeArguments) != 0); | 
						|
						if (requireTypeArguments) | 
						|
							goto default; | 
						|
						requireTypeArguments = true; | 
						|
						typeArguments = method.TypeArguments.ToArray(); | 
						|
						continue; | 
						|
					case OverloadResolutionErrors.MissingArgumentForRequiredParameter: | 
						|
						if (argumentList.FirstOptionalArgumentIndex == -1) | 
						|
							goto default; | 
						|
						argumentList.FirstOptionalArgumentIndex = -1; | 
						|
						continue; | 
						|
					default: | 
						|
						// TODO : implement some more intelligent algorithm that decides which of these fixes (cast args, add target, cast target, add type args) | 
						|
						// is best in this case. Additionally we should not cast all arguments at once, but step-by-step try to add only a minimal number of casts. | 
						|
						if (argumentList.FirstOptionalArgumentIndex >= 0) | 
						|
						{ | 
						|
							argumentList.FirstOptionalArgumentIndex = -1; | 
						|
						} | 
						|
						else if (!argumentsCasted) | 
						|
						{ | 
						|
							// If we added type arguments beforehand, but that didn't make the code any better, | 
						|
							// undo that decision and add casts first. | 
						|
							if (appliedRequireTypeArgumentsShortcut) | 
						|
							{ | 
						|
								requireTypeArguments = false; | 
						|
								typeArguments = Empty<IType>.Array; | 
						|
								appliedRequireTypeArgumentsShortcut = false; | 
						|
							} | 
						|
							argumentsCasted = true; | 
						|
							argumentList.UseImplicitlyTypedOut = false; | 
						|
							CastArguments(argumentList.Arguments, argumentList.ExpectedParameters); | 
						|
						} | 
						|
						else if ((allowedTransforms & CallTransformation.RequireTarget) != 0 && !requireTarget) | 
						|
						{ | 
						|
							requireTarget = true; | 
						|
							targetResolveResult = target.ResolveResult; | 
						|
						} | 
						|
						else if ((allowedTransforms & CallTransformation.RequireTarget) != 0 && !targetCasted) | 
						|
						{ | 
						|
							if (skipTargetCast && requireTarget != originalRequireTarget) | 
						|
							{ | 
						|
								requireTarget = originalRequireTarget; | 
						|
								if (!originalRequireTarget) | 
						|
									targetResolveResult = null; | 
						|
								allowedTransforms &= ~CallTransformation.RequireTarget; | 
						|
							} | 
						|
							else | 
						|
							{ | 
						|
								targetCasted = true; | 
						|
								target = target.ConvertTo(method.DeclaringType, expressionBuilder); | 
						|
								targetResolveResult = target.ResolveResult; | 
						|
							} | 
						|
						} | 
						|
						else if ((allowedTransforms & CallTransformation.RequireTypeArguments) != 0 && !requireTypeArguments) | 
						|
						{ | 
						|
							requireTypeArguments = true; | 
						|
							typeArguments = method.TypeArguments.ToArray(); | 
						|
						} | 
						|
						else | 
						|
						{ | 
						|
							break; | 
						|
						} | 
						|
						continue; | 
						|
				} | 
						|
				// We've given up. | 
						|
				foundMethod = method; | 
						|
				break; | 
						|
			} | 
						|
			if ((allowedTransforms & CallTransformation.RequireTarget) != 0 && requireTarget) | 
						|
				transform |= CallTransformation.RequireTarget; | 
						|
			if ((allowedTransforms & CallTransformation.RequireTypeArguments) != 0 && requireTypeArguments) | 
						|
				transform |= CallTransformation.RequireTypeArguments; | 
						|
			if (argumentList.FirstOptionalArgumentIndex < 0) | 
						|
				transform |= CallTransformation.NoOptionalArgumentAllowed; | 
						|
			return transform; | 
						|
		} | 
						|
 | 
						|
		private bool IsPossibleExtensionMethodCallOnNull(IMethod method, IList<TranslatedExpression> arguments) | 
						|
		{ | 
						|
			return method.IsExtensionMethod && arguments.Count > 0 && arguments[0].Expression is NullReferenceExpression; | 
						|
		} | 
						|
 | 
						|
		static bool CanInferTypeArgumentsFromArguments(IMethod method, ArgumentList argumentList, TypeInference typeInference) | 
						|
		{ | 
						|
			if (method.TypeParameters.Count == 0) | 
						|
				return true; | 
						|
			// always use unspecialized member, otherwise type inference fails | 
						|
			method = (IMethod)method.MemberDefinition; | 
						|
			IReadOnlyList<IType> paramTypesInArgumentOrder; | 
						|
			if (argumentList.ArgumentToParameterMap == null) | 
						|
				paramTypesInArgumentOrder = method.Parameters.SelectReadOnlyArray(p => p.Type); | 
						|
			else | 
						|
				paramTypesInArgumentOrder = argumentList.ArgumentToParameterMap | 
						|
					.SelectReadOnlyArray( | 
						|
						index => index >= 0 ? method.Parameters[index].Type : SpecialType.UnknownType | 
						|
					); | 
						|
			typeInference.InferTypeArguments(method.TypeParameters, | 
						|
				argumentList.Arguments.SelectReadOnlyArray(a => a.ResolveResult), paramTypesInArgumentOrder, | 
						|
				out bool success); | 
						|
			return success; | 
						|
		} | 
						|
 | 
						|
		private void CastArguments(IList<TranslatedExpression> arguments, IList<IParameter> expectedParameters) | 
						|
		{ | 
						|
			for (int i = 0; i < arguments.Count; i++) | 
						|
			{ | 
						|
				if (settings.AnonymousTypes && expectedParameters[i].Type.ContainsAnonymousType()) | 
						|
				{ | 
						|
					if (arguments[i].Expression is LambdaExpression lambda) | 
						|
					{ | 
						|
						ModifyReturnTypeOfLambda(lambda); | 
						|
					} | 
						|
				} | 
						|
				else | 
						|
				{ | 
						|
					IType parameterType; | 
						|
					if (expectedParameters[i].Type.Kind == TypeKind.Dynamic) | 
						|
					{ | 
						|
						parameterType = expressionBuilder.compilation.FindType(KnownTypeCode.Object); | 
						|
					} | 
						|
					else | 
						|
					{ | 
						|
						parameterType = expectedParameters[i].Type; | 
						|
					} | 
						|
 | 
						|
					arguments[i] = arguments[i].ConvertTo(parameterType, expressionBuilder, allowImplicitConversion: false); | 
						|
				} | 
						|
			} | 
						|
		} | 
						|
 | 
						|
		static bool IsNullConditional(Expression expr) | 
						|
		{ | 
						|
			return expr is UnaryOperatorExpression uoe && uoe.Operator == UnaryOperatorType.NullConditional; | 
						|
		} | 
						|
 | 
						|
		private void ModifyReturnTypeOfLambda(LambdaExpression lambda) | 
						|
		{ | 
						|
			var resolveResult = (DecompiledLambdaResolveResult)lambda.GetResolveResult(); | 
						|
			if (lambda.Body is Expression exprBody) | 
						|
				lambda.Body = new TranslatedExpression(exprBody.Detach()).ConvertTo(resolveResult.ReturnType, expressionBuilder); | 
						|
			else | 
						|
				ModifyReturnStatementInsideLambda(resolveResult.ReturnType, lambda); | 
						|
			resolveResult.InferredReturnType = resolveResult.ReturnType; | 
						|
		} | 
						|
 | 
						|
		private void ModifyReturnStatementInsideLambda(IType returnType, AstNode parent) | 
						|
		{ | 
						|
			foreach (var child in parent.Children) | 
						|
			{ | 
						|
				if (child is LambdaExpression || child is AnonymousMethodExpression) | 
						|
					continue; | 
						|
				if (child is ReturnStatement ret) | 
						|
				{ | 
						|
					ret.Expression = new TranslatedExpression(ret.Expression.Detach()).ConvertTo(returnType, expressionBuilder); | 
						|
					continue; | 
						|
				} | 
						|
				ModifyReturnStatementInsideLambda(returnType, child); | 
						|
			} | 
						|
		} | 
						|
 | 
						|
		private bool IsDelegateEqualityComparison(IMethod method, IList<TranslatedExpression> arguments) | 
						|
		{ | 
						|
			// Comparison on a delegate type is a C# builtin operator | 
						|
			// that compiles down to a Delegate.op_Equality call. | 
						|
			// We handle this as a special case to avoid inserting a cast to System.Delegate. | 
						|
			return method.IsOperator | 
						|
				&& method.DeclaringType.IsKnownType(KnownTypeCode.Delegate) | 
						|
				&& (method.Name == "op_Equality" || method.Name == "op_Inequality") | 
						|
				&& arguments.Count == 2 | 
						|
				&& arguments[0].Type.Kind == TypeKind.Delegate | 
						|
				&& arguments[1].Type.Equals(arguments[0].Type); | 
						|
		} | 
						|
 | 
						|
		private Expression HandleDelegateEqualityComparison(IMethod method, IList<TranslatedExpression> arguments) | 
						|
		{ | 
						|
			return new BinaryOperatorExpression( | 
						|
				arguments[0], | 
						|
				method.Name == "op_Equality" ? BinaryOperatorType.Equality : BinaryOperatorType.InEquality, | 
						|
				arguments[1] | 
						|
			); | 
						|
		} | 
						|
 | 
						|
		private ExpressionWithResolveResult HandleImplicitConversion(IMethod method, TranslatedExpression argument) | 
						|
		{ | 
						|
			var conversions = CSharpConversions.Get(expressionBuilder.compilation); | 
						|
			IType targetType = method.ReturnType; | 
						|
			var conv = conversions.ImplicitConversion(argument.Type, targetType); | 
						|
			if (!(conv.IsUserDefined && conv.IsValid && conv.Method.Equals(method))) | 
						|
			{ | 
						|
				// implicit conversion to targetType isn't directly possible, so first insert a cast to the argument type | 
						|
				argument = argument.ConvertTo(method.Parameters[0].Type, expressionBuilder); | 
						|
				conv = conversions.ImplicitConversion(argument.Type, targetType); | 
						|
			} | 
						|
			if (argument.Expression is DirectionExpression { FieldDirection: FieldDirection.In, Expression: var lvalueExpr }) | 
						|
			{ | 
						|
				// `(TargetType)(in arg)` is invalid syntax. | 
						|
				// Also, `f(in arg)` is invalid when there's an implicit conversion involved. | 
						|
				argument = argument.UnwrapChild(lvalueExpr); | 
						|
			} | 
						|
			return new CastExpression(expressionBuilder.ConvertType(targetType), argument.Expression) | 
						|
				.WithRR(new ConversionResolveResult(targetType, argument.ResolveResult, conv)); | 
						|
		} | 
						|
 | 
						|
		OverloadResolutionErrors IsUnambiguousCall(ExpectedTargetDetails expectedTargetDetails, IMethod method, | 
						|
			ResolveResult target, IType[] typeArguments, ResolveResult[] arguments, | 
						|
			string[] argumentNames, int firstOptionalArgumentIndex, | 
						|
			out IParameterizedMember foundMember, out bool bestCandidateIsExpandedForm) | 
						|
		{ | 
						|
			foundMember = null; | 
						|
			bestCandidateIsExpandedForm = false; | 
						|
			var currentTypeDefinition = resolver.CurrentTypeDefinition; | 
						|
			var lookup = new MemberLookup(currentTypeDefinition, currentTypeDefinition.ParentModule); | 
						|
 | 
						|
			Log.WriteLine("IsUnambiguousCall: Performing overload resolution for " + method); | 
						|
			Log.WriteCollection("  Arguments: ", arguments); | 
						|
 | 
						|
			argumentNames = firstOptionalArgumentIndex < 0 || argumentNames == null | 
						|
				? argumentNames | 
						|
				: argumentNames.Take(firstOptionalArgumentIndex).ToArray(); | 
						|
 | 
						|
			var or = new OverloadResolution(resolver.Compilation, | 
						|
				arguments, argumentNames, typeArguments, | 
						|
				conversions: expressionBuilder.resolver.conversions); | 
						|
			if (expectedTargetDetails.CallOpCode == OpCode.NewObj) | 
						|
			{ | 
						|
				foreach (IMethod ctor in method.DeclaringType.GetConstructors()) | 
						|
				{ | 
						|
					bool allowProtectedAccess = | 
						|
						resolver.CurrentTypeDefinition == method.DeclaringTypeDefinition; | 
						|
					if (lookup.IsAccessible(ctor, allowProtectedAccess)) | 
						|
					{ | 
						|
						or.AddCandidate(ctor); | 
						|
					} | 
						|
				} | 
						|
			} | 
						|
			else if (method.IsOperator) | 
						|
			{ | 
						|
				IEnumerable<IParameterizedMember> operatorCandidates; | 
						|
				if (arguments.Length == 1) | 
						|
				{ | 
						|
					IType argType = NullableType.GetUnderlyingType(arguments[0].Type); | 
						|
					operatorCandidates = resolver.GetUserDefinedOperatorCandidates(argType, method.Name); | 
						|
					if (method.Name == "op_Explicit") | 
						|
					{ | 
						|
						// For casts, also consider candidates from the target type we are casting to. | 
						|
						var hashSet = new HashSet<IParameterizedMember>(operatorCandidates); | 
						|
						IType targetType = NullableType.GetUnderlyingType(method.ReturnType); | 
						|
						hashSet.UnionWith( | 
						|
							resolver.GetUserDefinedOperatorCandidates(targetType, method.Name) | 
						|
						); | 
						|
						operatorCandidates = hashSet; | 
						|
					} | 
						|
				} | 
						|
				else if (arguments.Length == 2) | 
						|
				{ | 
						|
					IType lhsType = NullableType.GetUnderlyingType(arguments[0].Type); | 
						|
					IType rhsType = NullableType.GetUnderlyingType(arguments[1].Type); | 
						|
					var hashSet = new HashSet<IParameterizedMember>(); | 
						|
					hashSet.UnionWith(resolver.GetUserDefinedOperatorCandidates(lhsType, method.Name)); | 
						|
					hashSet.UnionWith(resolver.GetUserDefinedOperatorCandidates(rhsType, method.Name)); | 
						|
					operatorCandidates = hashSet; | 
						|
				} | 
						|
				else | 
						|
				{ | 
						|
					operatorCandidates = EmptyList<IParameterizedMember>.Instance; | 
						|
				} | 
						|
				foreach (var m in operatorCandidates) | 
						|
				{ | 
						|
					or.AddCandidate(m); | 
						|
				} | 
						|
			} | 
						|
			else if (target == null) | 
						|
			{ | 
						|
				var result = resolver.ResolveSimpleName(method.Name, typeArguments, isInvocationTarget: true) | 
						|
					as MethodGroupResolveResult; | 
						|
				if (result == null) | 
						|
					return OverloadResolutionErrors.AmbiguousMatch; | 
						|
				or.AddMethodLists(result.MethodsGroupedByDeclaringType.ToArray()); | 
						|
			} | 
						|
			else | 
						|
			{ | 
						|
				var result = lookup.Lookup(target, method.Name, typeArguments, isInvocation: true) as MethodGroupResolveResult; | 
						|
				if (result == null) | 
						|
					return OverloadResolutionErrors.AmbiguousMatch; | 
						|
				or.AddMethodLists(result.MethodsGroupedByDeclaringType.ToArray()); | 
						|
			} | 
						|
			bestCandidateIsExpandedForm = or.BestCandidateIsExpandedForm; | 
						|
			if (or.BestCandidateErrors != OverloadResolutionErrors.None) | 
						|
				return or.BestCandidateErrors; | 
						|
			if (or.IsAmbiguous) | 
						|
				return OverloadResolutionErrors.AmbiguousMatch; | 
						|
			foundMember = or.GetBestCandidateWithSubstitutedTypeArguments(); | 
						|
			if (!IsAppropriateCallTarget(expectedTargetDetails, method, foundMember)) | 
						|
				return OverloadResolutionErrors.AmbiguousMatch; | 
						|
			var map = or.GetArgumentToParameterMap(); | 
						|
			for (int i = 0; i < arguments.Length; i++) | 
						|
			{ | 
						|
				ResolveResult arg = arguments[i]; | 
						|
				int parameterIndex = map[i]; | 
						|
				if (arg is OutVarResolveResult rr && parameterIndex >= 0) | 
						|
				{ | 
						|
					var param = foundMember.Parameters[parameterIndex]; | 
						|
					var paramType = param.Type.UnwrapByRef(); | 
						|
					if (!paramType.Equals(rr.OriginalVariableType)) | 
						|
						return OverloadResolutionErrors.OutVarTypeMismatch; | 
						|
				} | 
						|
			} | 
						|
 | 
						|
			return OverloadResolutionErrors.None; | 
						|
		} | 
						|
 | 
						|
		bool IsUnambiguousAccess(ExpectedTargetDetails expectedTargetDetails, ResolveResult target, IMethod method, | 
						|
			IList<TranslatedExpression> arguments, string[] argumentNames, out IMember foundMember) | 
						|
		{ | 
						|
			Log.WriteLine("IsUnambiguousAccess: Performing overload resolution for " + method); | 
						|
			Log.WriteCollection("  Arguments: ", arguments.Select(a => a.ResolveResult)); | 
						|
 | 
						|
			foundMember = null; | 
						|
			if (target == null) | 
						|
			{ | 
						|
				var result = resolver.ResolveSimpleName(method.AccessorOwner.Name, | 
						|
					EmptyList<IType>.Instance, | 
						|
					isInvocationTarget: false) as MemberResolveResult; | 
						|
				if (result == null || result.IsError) | 
						|
					return false; | 
						|
				foundMember = result.Member; | 
						|
			} | 
						|
			else | 
						|
			{ | 
						|
				var lookup = new MemberLookup(resolver.CurrentTypeDefinition, resolver.CurrentTypeDefinition.ParentModule); | 
						|
				if (method.AccessorOwner.SymbolKind == SymbolKind.Indexer) | 
						|
				{ | 
						|
					var or = new OverloadResolution(resolver.Compilation, | 
						|
						arguments.SelectArray(a => a.ResolveResult), | 
						|
						argumentNames: argumentNames, | 
						|
						typeArguments: Empty<IType>.Array, | 
						|
						conversions: expressionBuilder.resolver.conversions); | 
						|
					or.AddMethodLists(lookup.LookupIndexers(target)); | 
						|
					if (or.BestCandidateErrors != OverloadResolutionErrors.None) | 
						|
						return false; | 
						|
					if (or.IsAmbiguous) | 
						|
						return false; | 
						|
					foundMember = or.GetBestCandidateWithSubstitutedTypeArguments(); | 
						|
				} | 
						|
				else | 
						|
				{ | 
						|
					var result = lookup.Lookup(target, | 
						|
						method.AccessorOwner.Name, | 
						|
						EmptyList<IType>.Instance, | 
						|
						isInvocation: false) as MemberResolveResult; | 
						|
					if (result == null || result.IsError) | 
						|
						return false; | 
						|
					foundMember = result.Member; | 
						|
				} | 
						|
			} | 
						|
			return foundMember != null && IsAppropriateCallTarget(expectedTargetDetails, method.AccessorOwner, foundMember); | 
						|
		} | 
						|
 | 
						|
		ExpressionWithResolveResult HandleAccessorCall(ExpectedTargetDetails expectedTargetDetails, IMethod method, | 
						|
			TranslatedExpression target, List<TranslatedExpression> arguments, string[] argumentNames) | 
						|
		{ | 
						|
			bool requireTarget; | 
						|
			if (settings.AlwaysQualifyMemberReferences || method.AccessorOwner.SymbolKind == SymbolKind.Indexer || expressionBuilder.HidesVariableWithName(method.AccessorOwner.Name)) | 
						|
				requireTarget = true; | 
						|
			else if (method.IsStatic) | 
						|
				requireTarget = !expressionBuilder.IsCurrentOrContainingType(method.DeclaringTypeDefinition); | 
						|
			else | 
						|
				requireTarget = !(target.Expression is ThisReferenceExpression); | 
						|
			bool targetCasted = false; | 
						|
			bool isSetter = method.ReturnType.IsKnownType(KnownTypeCode.Void); | 
						|
			bool argumentsCasted = (isSetter && method.Parameters.Count == 1) || (!isSetter && method.Parameters.Count == 0); | 
						|
			var targetResolveResult = requireTarget ? target.ResolveResult : null; | 
						|
 | 
						|
			TranslatedExpression value = default(TranslatedExpression); | 
						|
			if (isSetter) | 
						|
			{ | 
						|
				value = arguments.Last(); | 
						|
				arguments.Remove(value); | 
						|
			} | 
						|
 | 
						|
			IMember foundMember; | 
						|
			while (!IsUnambiguousAccess(expectedTargetDetails, targetResolveResult, method, arguments, argumentNames, out foundMember)) | 
						|
			{ | 
						|
				if (!argumentsCasted) | 
						|
				{ | 
						|
					argumentsCasted = true; | 
						|
					CastArguments(arguments, method.Parameters.ToList()); | 
						|
				} | 
						|
				else if (!requireTarget) | 
						|
				{ | 
						|
					requireTarget = true; | 
						|
					targetResolveResult = target.ResolveResult; | 
						|
				} | 
						|
				else if (!targetCasted) | 
						|
				{ | 
						|
					targetCasted = true; | 
						|
					target = target.ConvertTo(method.AccessorOwner.DeclaringType, expressionBuilder); | 
						|
					targetResolveResult = target.ResolveResult; | 
						|
				} | 
						|
				else | 
						|
				{ | 
						|
					foundMember = method.AccessorOwner; | 
						|
					break; | 
						|
				} | 
						|
			} | 
						|
 | 
						|
			var rr = new MemberResolveResult(target.ResolveResult, foundMember); | 
						|
 | 
						|
			if (isSetter) | 
						|
			{ | 
						|
				TranslatedExpression expr; | 
						|
 | 
						|
				if (arguments.Count != 0) | 
						|
				{ | 
						|
					expr = new IndexerExpression(target.ResolveResult is InitializedObjectResolveResult ? null : target.Expression, arguments.Select(a => a.Expression)) | 
						|
						.WithoutILInstruction().WithRR(rr); | 
						|
				} | 
						|
				else if (requireTarget) | 
						|
				{ | 
						|
					expr = new MemberReferenceExpression(target.Expression, method.AccessorOwner.Name) | 
						|
						.WithoutILInstruction().WithRR(rr); | 
						|
				} | 
						|
				else | 
						|
				{ | 
						|
					expr = new IdentifierExpression(method.AccessorOwner.Name) | 
						|
						.WithoutILInstruction().WithRR(rr); | 
						|
				} | 
						|
 | 
						|
				var op = AssignmentOperatorType.Assign; | 
						|
				if (method.AccessorOwner is IEvent parentEvent) | 
						|
				{ | 
						|
					if (method.Equals(parentEvent.AddAccessor)) | 
						|
					{ | 
						|
						op = AssignmentOperatorType.Add; | 
						|
					} | 
						|
					if (method.Equals(parentEvent.RemoveAccessor)) | 
						|
					{ | 
						|
						op = AssignmentOperatorType.Subtract; | 
						|
					} | 
						|
				} | 
						|
				return new AssignmentExpression(expr, op, value.Expression).WithRR(new TypeResolveResult(method.AccessorOwner.ReturnType)); | 
						|
			} | 
						|
			else | 
						|
			{ | 
						|
				if (arguments.Count != 0) | 
						|
				{ | 
						|
					return new IndexerExpression(target.Expression, arguments.Select(a => a.Expression)) | 
						|
						.WithoutILInstruction().WithRR(rr); | 
						|
				} | 
						|
				else if (requireTarget) | 
						|
				{ | 
						|
					return new MemberReferenceExpression(target.Expression, method.AccessorOwner.Name) | 
						|
						.WithoutILInstruction().WithRR(rr); | 
						|
				} | 
						|
				else | 
						|
				{ | 
						|
					return new IdentifierExpression(method.AccessorOwner.Name) | 
						|
						.WithoutILInstruction().WithRR(rr); | 
						|
				} | 
						|
			} | 
						|
		} | 
						|
 | 
						|
		bool IsAppropriateCallTarget(ExpectedTargetDetails expectedTargetDetails, IMember expectedTarget, IMember actualTarget) | 
						|
		{ | 
						|
			if (expectedTarget.Equals(actualTarget, NormalizeTypeVisitor.TypeErasure)) | 
						|
				return true; | 
						|
 | 
						|
			if (expectedTargetDetails.CallOpCode == OpCode.CallVirt && actualTarget.IsOverride) | 
						|
			{ | 
						|
				if (expectedTargetDetails.NeedsBoxingConversion && actualTarget.DeclaringType.IsReferenceType != true) | 
						|
					return false; | 
						|
				foreach (var possibleTarget in InheritanceHelper.GetBaseMembers(actualTarget, false)) | 
						|
				{ | 
						|
					if (expectedTarget.Equals(possibleTarget, NormalizeTypeVisitor.TypeErasure)) | 
						|
						return true; | 
						|
					if (!possibleTarget.IsOverride) | 
						|
						break; | 
						|
				} | 
						|
			} | 
						|
			return false; | 
						|
		} | 
						|
 | 
						|
		ExpressionWithResolveResult HandleConstructorCall(ExpectedTargetDetails expectedTargetDetails, ResolveResult target, IMethod method, ArgumentList argumentList) | 
						|
		{ | 
						|
			if (settings.AnonymousTypes && method.DeclaringType.IsAnonymousType()) | 
						|
			{ | 
						|
				Debug.Assert(argumentList.ArgumentToParameterMap == null && argumentList.ArgumentNames == null && argumentList.FirstOptionalArgumentIndex < 0); | 
						|
				var atce = new AnonymousTypeCreateExpression(); | 
						|
				if (argumentList.CanInferAnonymousTypePropertyNamesFromArguments()) | 
						|
				{ | 
						|
					atce.Initializers.AddRange(argumentList.GetArgumentExpressions()); | 
						|
				} | 
						|
				else | 
						|
				{ | 
						|
					for (int i = 0; i < argumentList.Length; i++) | 
						|
					{ | 
						|
						atce.Initializers.Add( | 
						|
							new NamedExpression { | 
						|
								Name = argumentList.ExpectedParameters[i].Name, | 
						|
								Expression = argumentList.Arguments[i].ConvertTo(argumentList.ExpectedParameters[i].Type, expressionBuilder) | 
						|
							}); | 
						|
					} | 
						|
				} | 
						|
				return atce.WithRR(new CSharpInvocationResolveResult( | 
						|
					target, method, argumentList.GetArgumentResolveResults(), | 
						|
					isExpandedForm: argumentList.IsExpandedForm, argumentToParameterMap: argumentList.ArgumentToParameterMap | 
						|
				)); | 
						|
			} | 
						|
			else | 
						|
			{ | 
						|
				while (IsUnambiguousCall(expectedTargetDetails, method, null, Empty<IType>.Array, | 
						|
					argumentList.GetArgumentResolveResults().ToArray(), | 
						|
					argumentList.ArgumentNames, argumentList.FirstOptionalArgumentIndex, out _, | 
						|
					out var bestCandidateIsExpandedForm) != OverloadResolutionErrors.None || bestCandidateIsExpandedForm != argumentList.IsExpandedForm) | 
						|
				{ | 
						|
					if (argumentList.FirstOptionalArgumentIndex >= 0) | 
						|
					{ | 
						|
						argumentList.FirstOptionalArgumentIndex = -1; | 
						|
						continue; | 
						|
					} | 
						|
					CastArguments(argumentList.Arguments, argumentList.ExpectedParameters); | 
						|
					break; // make sure that we don't not end up in an infinite loop | 
						|
				} | 
						|
				return new ObjectCreateExpression( | 
						|
					expressionBuilder.ConvertType(method.DeclaringType), | 
						|
					argumentList.GetArgumentExpressions() | 
						|
				).WithRR(new CSharpInvocationResolveResult( | 
						|
					target, method, argumentList.GetArgumentResolveResults().ToArray(), | 
						|
					isExpandedForm: argumentList.IsExpandedForm, argumentToParameterMap: argumentList.ArgumentToParameterMap | 
						|
				)); | 
						|
			} | 
						|
		} | 
						|
 | 
						|
		TranslatedExpression HandleDelegateConstruction(CallInstruction inst) | 
						|
		{ | 
						|
			ILInstruction thisArg = inst.Arguments[0]; | 
						|
			ILInstruction func = inst.Arguments[1]; | 
						|
			IMethod method; | 
						|
			ExpectedTargetDetails expectedTargetDetails = default; | 
						|
			switch (func.OpCode) | 
						|
			{ | 
						|
				case OpCode.LdFtn: | 
						|
					method = ((LdFtn)func).Method; | 
						|
					expectedTargetDetails.CallOpCode = OpCode.Call; | 
						|
					break; | 
						|
				case OpCode.LdVirtFtn: | 
						|
					method = ((LdVirtFtn)func).Method; | 
						|
					expectedTargetDetails.CallOpCode = OpCode.CallVirt; | 
						|
					break; | 
						|
				default: | 
						|
					throw new ArgumentException($"Unknown instruction type: {func.OpCode}"); | 
						|
			} | 
						|
			if (CanUseDelegateConstruction(method, thisArg, inst.Method.DeclaringType.GetDelegateInvokeMethod())) | 
						|
			{ | 
						|
				return HandleDelegateConstruction(inst.Method.DeclaringType, method, expectedTargetDetails, thisArg, inst); | 
						|
			} | 
						|
			else | 
						|
			{ | 
						|
				var argumentList = BuildArgumentList(expectedTargetDetails, null, inst.Method, | 
						|
					0, inst.Arguments, null); | 
						|
				return HandleConstructorCall(new ExpectedTargetDetails { CallOpCode = OpCode.NewObj }, null, inst.Method, argumentList).WithILInstruction(inst); | 
						|
			} | 
						|
		} | 
						|
 | 
						|
		private bool CanUseDelegateConstruction(IMethod targetMethod, ILInstruction thisArg, IMethod invokeMethod) | 
						|
		{ | 
						|
			// Accessors cannot be directly referenced as method group in C# | 
						|
			// see https://github.com/icsharpcode/ILSpy/issues/1741#issuecomment-540179101 | 
						|
			if (targetMethod.IsAccessor) | 
						|
				return false; | 
						|
			if (targetMethod.IsStatic) | 
						|
			{ | 
						|
				// If the invoke method is known, we can compare the parameter counts to figure out whether the | 
						|
				// delegate is static or binds the first argument | 
						|
				if (invokeMethod != null) | 
						|
				{ | 
						|
					if (invokeMethod.Parameters.Count == targetMethod.Parameters.Count) | 
						|
					{ | 
						|
						return thisArg.MatchLdNull(); | 
						|
					} | 
						|
					else if (targetMethod.IsExtensionMethod && invokeMethod.Parameters.Count == targetMethod.Parameters.Count - 1) | 
						|
					{ | 
						|
						return true; | 
						|
					} | 
						|
					else | 
						|
					{ | 
						|
						return false; | 
						|
					} | 
						|
				} | 
						|
				else | 
						|
				{ | 
						|
					// delegate type unknown: | 
						|
					return thisArg.MatchLdNull() || targetMethod.IsExtensionMethod; | 
						|
				} | 
						|
			} | 
						|
			else | 
						|
			{ | 
						|
				// targetMethod is instance method | 
						|
				if (invokeMethod != null && invokeMethod.Parameters.Count != targetMethod.Parameters.Count) | 
						|
					return false; | 
						|
				return true; | 
						|
			} | 
						|
		} | 
						|
 | 
						|
		internal TranslatedExpression Build(LdVirtDelegate inst) | 
						|
		{ | 
						|
			return HandleDelegateConstruction(inst.Type, inst.Method, new ExpectedTargetDetails { CallOpCode = OpCode.CallVirt }, inst.Argument, inst); | 
						|
		} | 
						|
 | 
						|
		internal ExpressionWithResolveResult BuildMethodReference(IMethod method, bool isVirtual) | 
						|
		{ | 
						|
			var expr = BuildDelegateReference(method, invokeMethod: null, new ExpectedTargetDetails { CallOpCode = isVirtual ? OpCode.CallVirt : OpCode.Call }, thisArg: null); | 
						|
			expr.Expression.RemoveAnnotations<ResolveResult>(); | 
						|
			return expr.Expression.WithRR(new MemberResolveResult(null, method)); | 
						|
		} | 
						|
 | 
						|
		ExpressionWithResolveResult BuildDelegateReference(IMethod method, IMethod invokeMethod, ExpectedTargetDetails expectedTargetDetails, ILInstruction thisArg) | 
						|
		{ | 
						|
			ExpressionBuilder expressionBuilder = this.expressionBuilder; | 
						|
			ExpressionWithResolveResult targetExpression; | 
						|
			(TranslatedExpression target, bool addTypeArguments, string methodName, ResolveResult result) = DisambiguateDelegateReference(method, invokeMethod, expectedTargetDetails, thisArg); | 
						|
			if (target.Expression != null) | 
						|
			{ | 
						|
				var mre = new MemberReferenceExpression(target, methodName); | 
						|
				if (addTypeArguments) | 
						|
				{ | 
						|
					mre.TypeArguments.AddRange(method.TypeArguments.Select(expressionBuilder.ConvertType)); | 
						|
				} | 
						|
				targetExpression = mre.WithRR(result); | 
						|
			} | 
						|
			else | 
						|
			{ | 
						|
				var ide = new IdentifierExpression(methodName); | 
						|
				if (addTypeArguments) | 
						|
				{ | 
						|
					ide.TypeArguments.AddRange(method.TypeArguments.Select(expressionBuilder.ConvertType)); | 
						|
				} | 
						|
				targetExpression = ide.WithRR(result); | 
						|
			} | 
						|
			return targetExpression; | 
						|
 | 
						|
		} | 
						|
 | 
						|
		(TranslatedExpression target, bool addTypeArguments, string methodName, ResolveResult result) DisambiguateDelegateReference(IMethod method, IMethod invokeMethod, ExpectedTargetDetails expectedTargetDetails, ILInstruction thisArg) | 
						|
		{ | 
						|
			if (method.IsLocalFunction) | 
						|
			{ | 
						|
				ILFunction localFunction = expressionBuilder.ResolveLocalFunction(method); | 
						|
				Debug.Assert(localFunction != null); | 
						|
				return (default, addTypeArguments: true, localFunction.Name, ToMethodGroup(method, localFunction)); | 
						|
			} | 
						|
			if (method.IsExtensionMethod && method.Parameters.Count - 1 == invokeMethod?.Parameters.Count) | 
						|
			{ | 
						|
				IType targetType = method.Parameters[0].Type; | 
						|
				if (targetType.Kind == TypeKind.ByReference && thisArg is Box thisArgBox) | 
						|
				{ | 
						|
					targetType = ((ByReferenceType)targetType).ElementType; | 
						|
					thisArg = thisArgBox.Argument; | 
						|
				} | 
						|
				TranslatedExpression target = expressionBuilder.Translate(thisArg, targetType); | 
						|
				var currentTarget = target; | 
						|
				bool targetCasted = false; | 
						|
				bool addTypeArguments = false; | 
						|
				// Initial inputs for IsUnambiguousMethodReference: | 
						|
				ResolveResult targetResolveResult = target.ResolveResult; | 
						|
				IReadOnlyList<IType> typeArguments = EmptyList<IType>.Instance; | 
						|
				if (thisArg.MatchLdNull()) | 
						|
				{ | 
						|
					targetCasted = true; | 
						|
					currentTarget = currentTarget.ConvertTo(targetType, expressionBuilder); | 
						|
					targetResolveResult = currentTarget.ResolveResult; | 
						|
				} | 
						|
				// Find somewhat minimal solution: | 
						|
				ResolveResult result; | 
						|
				while (!IsUnambiguousMethodReference(expectedTargetDetails, method, targetResolveResult, typeArguments, true, out result)) | 
						|
				{ | 
						|
					if (!targetCasted) | 
						|
					{ | 
						|
						// try casting target | 
						|
						targetCasted = true; | 
						|
						currentTarget = currentTarget.ConvertTo(targetType, expressionBuilder); | 
						|
						targetResolveResult = currentTarget.ResolveResult; | 
						|
						continue; | 
						|
					} | 
						|
					if (!addTypeArguments) | 
						|
					{ | 
						|
						// try adding type arguments | 
						|
						addTypeArguments = true; | 
						|
						typeArguments = method.TypeArguments; | 
						|
						continue; | 
						|
					} | 
						|
					break; | 
						|
				} | 
						|
				return (currentTarget, addTypeArguments, method.Name, result); | 
						|
			} | 
						|
			else | 
						|
			{ | 
						|
				// Prepare call target | 
						|
				IType targetType = method.DeclaringType; | 
						|
				if (targetType.IsReferenceType == false && thisArg is Box thisArgBox) | 
						|
				{ | 
						|
					// Normal struct instance method calls (which TranslateTarget is meant for) expect a 'ref T', | 
						|
					// but delegate construction uses a 'box T'. | 
						|
					if (thisArgBox.Argument is LdObj ldobj) | 
						|
					{ | 
						|
						thisArg = ldobj.Target; | 
						|
					} | 
						|
					else | 
						|
					{ | 
						|
						thisArg = new AddressOf(thisArgBox.Argument, thisArgBox.Type); | 
						|
					} | 
						|
				} | 
						|
				TranslatedExpression target = expressionBuilder.TranslateTarget(thisArg, | 
						|
					nonVirtualInvocation: expectedTargetDetails.CallOpCode == OpCode.Call, | 
						|
					memberStatic: method.IsStatic, | 
						|
					memberDeclaringType: method.DeclaringType); | 
						|
				// check if target is required | 
						|
				bool requireTarget = expressionBuilder.HidesVariableWithName(method.Name) | 
						|
					|| (method.IsStatic ? !expressionBuilder.IsCurrentOrContainingType(method.DeclaringTypeDefinition) : !(target.Expression is ThisReferenceExpression)); | 
						|
				// Try to find minimal expression | 
						|
				// If target is required, include it from the start | 
						|
				bool targetAdded = requireTarget; | 
						|
				TranslatedExpression currentTarget = targetAdded ? target : default; | 
						|
				// Remember other decisions: | 
						|
				bool targetCasted = false; | 
						|
				bool addTypeArguments = false; | 
						|
				// Initial inputs for IsUnambiguousMethodReference: | 
						|
				ResolveResult targetResolveResult = targetAdded ? target.ResolveResult : null; | 
						|
				IReadOnlyList<IType> typeArguments = EmptyList<IType>.Instance; | 
						|
				// Find somewhat minimal solution: | 
						|
				ResolveResult result; | 
						|
				while (!IsUnambiguousMethodReference(expectedTargetDetails, method, targetResolveResult, typeArguments, false, out result)) | 
						|
				{ | 
						|
					if (!addTypeArguments) | 
						|
					{ | 
						|
						// try adding type arguments | 
						|
						addTypeArguments = true; | 
						|
						typeArguments = method.TypeArguments; | 
						|
						continue; | 
						|
					} | 
						|
					if (!targetAdded) | 
						|
					{ | 
						|
						// try adding target | 
						|
						targetAdded = true; | 
						|
						currentTarget = target; | 
						|
						targetResolveResult = target.ResolveResult; | 
						|
						continue; | 
						|
					} | 
						|
					if (!targetCasted) | 
						|
					{ | 
						|
						// try casting target | 
						|
						targetCasted = true; | 
						|
						currentTarget = currentTarget.ConvertTo(targetType, expressionBuilder); | 
						|
						targetResolveResult = currentTarget.ResolveResult; | 
						|
						continue; | 
						|
					} | 
						|
					break; | 
						|
				} | 
						|
				return (currentTarget, addTypeArguments, method.Name, result); | 
						|
			} | 
						|
		} | 
						|
 | 
						|
 | 
						|
		TranslatedExpression HandleDelegateConstruction(IType delegateType, IMethod method, ExpectedTargetDetails expectedTargetDetails, ILInstruction thisArg, ILInstruction inst) | 
						|
		{ | 
						|
			var invokeMethod = delegateType.GetDelegateInvokeMethod(); | 
						|
			var targetExpression = BuildDelegateReference(method, invokeMethod, expectedTargetDetails, thisArg); | 
						|
			var oce = new ObjectCreateExpression(expressionBuilder.ConvertType(delegateType), targetExpression) | 
						|
				.WithILInstruction(inst) | 
						|
				.WithRR(new ConversionResolveResult( | 
						|
					delegateType, | 
						|
					targetExpression.ResolveResult, | 
						|
					Conversion.MethodGroupConversion(method, expectedTargetDetails.CallOpCode == OpCode.CallVirt, false))); | 
						|
			return oce; | 
						|
		} | 
						|
 | 
						|
		bool IsUnambiguousMethodReference(ExpectedTargetDetails expectedTargetDetails, IMethod method, ResolveResult target, IReadOnlyList<IType> typeArguments, bool isExtensionMethodReference, out ResolveResult result) | 
						|
		{ | 
						|
			Log.WriteLine("IsUnambiguousMethodReference: Performing overload resolution for " + method); | 
						|
 | 
						|
			var lookup = new MemberLookup(resolver.CurrentTypeDefinition, resolver.CurrentTypeDefinition.ParentModule); | 
						|
			OverloadResolution or; | 
						|
 | 
						|
			if (isExtensionMethodReference) | 
						|
			{ | 
						|
				var resolver = this.resolver.WithCurrentUsingScope(this.expressionBuilder.statementBuilder.decompileRun.UsingScope.Resolve(this.resolver.Compilation)); | 
						|
				result = resolver.ResolveMemberAccess(target, method.Name, typeArguments, NameLookupMode.InvocationTarget) as MethodGroupResolveResult; | 
						|
				if (result == null) | 
						|
					return false; | 
						|
				or = ((MethodGroupResolveResult)result).PerformOverloadResolution(resolver.CurrentTypeResolveContext.Compilation, | 
						|
					method.Parameters.SelectReadOnlyArray(p => new TypeResolveResult(p.Type)), | 
						|
					argumentNames: null, allowExtensionMethods: true); | 
						|
				if (or == null || or.IsAmbiguous) | 
						|
					return false; | 
						|
			} | 
						|
			else | 
						|
			{ | 
						|
				or = new OverloadResolution(resolver.Compilation, | 
						|
					arguments: method.Parameters.SelectReadOnlyArray(p => new TypeResolveResult(p.Type)), // there are no arguments, use parameter types | 
						|
					argumentNames: null, // argument names are not possible | 
						|
					typeArguments.ToArray(), | 
						|
					conversions: expressionBuilder.resolver.conversions | 
						|
				); | 
						|
				if (target == null) | 
						|
				{ | 
						|
					result = resolver.ResolveSimpleName(method.Name, typeArguments, isInvocationTarget: false); | 
						|
					if (!(result is MethodGroupResolveResult mgrr)) | 
						|
						return false; | 
						|
					or.AddMethodLists(mgrr.MethodsGroupedByDeclaringType.ToArray()); | 
						|
				} | 
						|
				else | 
						|
				{ | 
						|
					result = lookup.Lookup(target, method.Name, typeArguments, isInvocation: false); | 
						|
					if (!(result is MethodGroupResolveResult mgrr)) | 
						|
						return false; | 
						|
					or.AddMethodLists(mgrr.MethodsGroupedByDeclaringType.ToArray()); | 
						|
				} | 
						|
			} | 
						|
 | 
						|
			var foundMethod = or.GetBestCandidateWithSubstitutedTypeArguments(); | 
						|
			if (!IsAppropriateCallTarget(expectedTargetDetails, method, foundMethod)) | 
						|
				return false; | 
						|
			return result is MethodGroupResolveResult; | 
						|
		} | 
						|
 | 
						|
		static MethodGroupResolveResult ToMethodGroup(IMethod method, ILFunction localFunction) | 
						|
		{ | 
						|
			return new MethodGroupResolveResult( | 
						|
				null, | 
						|
				localFunction.Name, | 
						|
				new[] { | 
						|
					new MethodListWithDeclaringType( | 
						|
						method.DeclaringType, | 
						|
						new IParameterizedMember[] { method } | 
						|
					) | 
						|
				}, method.TypeArguments | 
						|
			); | 
						|
		} | 
						|
 | 
						|
		internal TranslatedExpression CallWithNamedArgs(Block block) | 
						|
		{ | 
						|
			Debug.Assert(block.Kind == BlockKind.CallWithNamedArgs); | 
						|
			var call = (CallInstruction)block.FinalInstruction; | 
						|
			var arguments = new ILInstruction[call.Arguments.Count]; | 
						|
			var argumentToParameterMap = new int[arguments.Length]; | 
						|
			int firstParamIndex = call.IsInstanceCall ? 1 : 0; | 
						|
			// Arguments from temporary variables (VariableKind.NamedArgument): | 
						|
			int pos = 0; | 
						|
			foreach (StLoc stloc in block.Instructions) | 
						|
			{ | 
						|
				Debug.Assert(stloc.Variable.LoadInstructions.Single().Parent == call); | 
						|
				arguments[pos] = stloc.Value; | 
						|
				argumentToParameterMap[pos] = stloc.Variable.LoadInstructions.Single().ChildIndex - firstParamIndex; | 
						|
				pos++; | 
						|
			} | 
						|
			// Remaining argument: | 
						|
			foreach (var arg in call.Arguments) | 
						|
			{ | 
						|
				if (arg.MatchLdLoc(out var v) && v.Kind == VariableKind.NamedArgument) | 
						|
				{ | 
						|
					continue; // already handled in loop above | 
						|
				} | 
						|
				arguments[pos] = arg; | 
						|
				argumentToParameterMap[pos] = arg.ChildIndex - firstParamIndex; | 
						|
				pos++; | 
						|
			} | 
						|
			Debug.Assert(pos == arguments.Length); | 
						|
			return Build(call.OpCode, call.Method, arguments, argumentToParameterMap, call.ConstrainedTo) | 
						|
				.WithILInstruction(call).WithILInstruction(block); | 
						|
		} | 
						|
 | 
						|
		private bool HandleRangeConstruction(out ExpressionWithResolveResult result, OpCode callOpCode, IMethod method, TranslatedExpression target, ArgumentList argumentList) | 
						|
		{ | 
						|
			result = default; | 
						|
			if (argumentList.ArgumentNames != null) | 
						|
			{ | 
						|
				return false; // range syntax doesn't support named arguments | 
						|
			} | 
						|
			if (method.DeclaringType.IsKnownType(KnownTypeCode.Range)) | 
						|
			{ | 
						|
				if (callOpCode == OpCode.NewObj && argumentList.Length == 2) | 
						|
				{ | 
						|
					result = new BinaryOperatorExpression(argumentList.Arguments[0], BinaryOperatorType.Range, argumentList.Arguments[1]) | 
						|
						.WithRR(new MemberResolveResult(null, method)); | 
						|
					return true; | 
						|
				} | 
						|
				else if (callOpCode == OpCode.Call && method.Name == "get_All" && argumentList.Length == 0) | 
						|
				{ | 
						|
					result = new BinaryOperatorExpression(Expression.Null, BinaryOperatorType.Range, Expression.Null) | 
						|
						.WithRR(new MemberResolveResult(null, method.AccessorOwner ?? method)); | 
						|
					return true; | 
						|
				} | 
						|
				else if (callOpCode == OpCode.Call && method.Name == "StartAt" && argumentList.Length == 1) | 
						|
				{ | 
						|
					result = new BinaryOperatorExpression(argumentList.Arguments[0], BinaryOperatorType.Range, Expression.Null) | 
						|
						.WithRR(new MemberResolveResult(null, method)); | 
						|
					return true; | 
						|
				} | 
						|
				else if (callOpCode == OpCode.Call && method.Name == "EndAt" && argumentList.Length == 1) | 
						|
				{ | 
						|
					result = new BinaryOperatorExpression(Expression.Null, BinaryOperatorType.Range, argumentList.Arguments[0]) | 
						|
						.WithRR(new MemberResolveResult(null, method)); | 
						|
					return true; | 
						|
				} | 
						|
			} | 
						|
			else if (callOpCode == OpCode.NewObj && method.DeclaringType.IsKnownType(KnownTypeCode.Index)) | 
						|
			{ | 
						|
				if (argumentList.Length != 2) | 
						|
					return false; | 
						|
				if (!(argumentList.Arguments[1].Expression is PrimitiveExpression pe && pe.Value is true)) | 
						|
					return false; | 
						|
				result = new UnaryOperatorExpression(UnaryOperatorType.IndexFromEnd, argumentList.Arguments[0]) | 
						|
					.WithRR(new MemberResolveResult(null, method)); | 
						|
				return true; | 
						|
			} | 
						|
			else if (method is SyntheticRangeIndexAccessor rangeIndexAccessor && rangeIndexAccessor.IsSlicing) | 
						|
			{ | 
						|
				// For slicing the method is called Slice()/Substring(), but we still need to output indexer notation. | 
						|
				// So special-case range-based slicing here. | 
						|
				result = new IndexerExpression(target, argumentList.Arguments.Select(a => a.Expression)) | 
						|
					.WithRR(new MemberResolveResult(target.ResolveResult, method)); | 
						|
				return true; | 
						|
			} | 
						|
			return false; | 
						|
		} | 
						|
	} | 
						|
}
 | 
						|
 |