ExpressionWithResolveResult HandleAccessorCall(ExpectedTargetDetails expectedTargetDetails, IMethod method, TranslatedExpression target, IList <TranslatedExpression> arguments) { var lookup = new MemberLookup(resolver.CurrentTypeDefinition, resolver.CurrentTypeDefinition.ParentAssembly); var result = lookup.Lookup(target.ResolveResult, method.AccessorOwner.Name, EmptyList <IType> .Instance, isInvocation: false); if (result.IsError || (result is MemberResolveResult && !IsAppropriateCallTarget(expectedTargetDetails, method.AccessorOwner, ((MemberResolveResult)result).Member))) { target = target.ConvertTo(method.AccessorOwner.DeclaringType, expressionBuilder); } var rr = new MemberResolveResult(target.ResolveResult, method.AccessorOwner); if (method.ReturnType.IsKnownType(KnownTypeCode.Void)) { var value = arguments.Last(); arguments.Remove(value); TranslatedExpression expr; if (arguments.Count == 0) { expr = new MemberReferenceExpression(target.Expression, method.AccessorOwner.Name) .WithoutILInstruction().WithRR(rr); } else { expr = new IndexerExpression(target.Expression, arguments.Select(a => a.Expression)) .WithoutILInstruction().WithRR(rr); } var op = AssignmentOperatorType.Assign; var parentEvent = method.AccessorOwner as IEvent; if (parentEvent != null) { 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 MemberReferenceExpression(target.Expression, method.AccessorOwner.Name).WithRR(rr)); } else { return(new IndexerExpression(target.Expression, arguments.Select(a => a.Expression)).WithRR(rr)); } } }
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.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); } return(new CastExpression(expressionBuilder.ConvertType(targetType), argument.Expression) .WithRR(new ConversionResolveResult(targetType, argument.ResolveResult, conv))); }
ExpressionWithResolveResult HandleAccessorCall(ExpectedTargetDetails expectedTargetDetails, IMethod method, TranslatedExpression target, IList <TranslatedExpression> arguments) { bool requireTarget = expressionBuilder.HidesVariableWithName(method.AccessorOwner.Name) || (method.IsStatic ? !expressionBuilder.IsCurrentOrContainingType(method.DeclaringTypeDefinition) : !(target.Expression is ThisReferenceExpression)); bool targetCasted = false; var targetResolveResult = requireTarget ? target.ResolveResult : null; while (!IsUnambiguousAccess(expectedTargetDetails, targetResolveResult, method)) { if (!requireTarget) { requireTarget = true; targetResolveResult = target.ResolveResult; } else if (!targetCasted) { targetCasted = true; target = target.ConvertTo(method.AccessorOwner.DeclaringType, expressionBuilder); targetResolveResult = target.ResolveResult; } else { break; } } var rr = new MemberResolveResult(target.ResolveResult, method.AccessorOwner); if (method.ReturnType.IsKnownType(KnownTypeCode.Void)) { var value = arguments.Last(); arguments.Remove(value); TranslatedExpression expr; if (arguments.Count != 0) { expr = new IndexerExpression(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; var parentEvent = method.AccessorOwner as IEvent; if (parentEvent != null) { 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)); } } }
public ExpressionWithResolveResult Build(OpCode callOpCode, IMethod method, IReadOnlyList <ILInstruction> callArguments, IReadOnlyList <int> argumentToParameterMap = null, IType constrainedTo = null) { // Used for Call, CallVirt and NewObj var expectedTargetDetails = new ExpectedTargetDetails { CallOpCode = callOpCode }; TranslatedExpression target; if (callOpCode == OpCode.NewObj) { target = default(TranslatedExpression); // no target } else { target = expressionBuilder.TranslateTarget( callArguments.FirstOrDefault(), nonVirtualInvocation: callOpCode == OpCode.Call, 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); // 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>(arguments.Count); // parameters, but in argument order bool isExpandedForm = false; 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 (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 (arg.ResolveResult is ArrayCreateResolveResult acrr && acrr.SizeArguments.Count == 1 && acrr.SizeArguments[0].IsCompileTimeConstant && acrr.SizeArguments[0].ConstantValue is int length) { var expandedParameters = expectedParameters.Take(expectedParameters.Count - 1).ToList(); var expandedArguments = new List <TranslatedExpression>(arguments); if (length > 0) { var arrayElements = ((ArrayCreateExpression)arg.Expression).Initializer.Elements.ToArray(); var elementType = ((ArrayType)acrr.Type).ElementType; 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()); } } } Debug.Assert(argumentNames == null); if (IsUnambiguousCall(expectedTargetDetails, method, target.ResolveResult, Empty <IType> .Array, expandedArguments, argumentNames, out _) == OverloadResolutionErrors.None) { isExpandedForm = true; expectedParameters = expandedParameters; arguments = expandedArguments.SelectList(a => new TranslatedExpression(a.Expression.Detach())); continue; } } } arg = arg.ConvertTo(parameter.Type, expressionBuilder, allowImplicitConversion: true); if (parameter.IsOut && arg.Expression is DirectionExpression dirExpr && arg.ResolveResult is ByReferenceResolveResult brrr) { dirExpr.FieldDirection = FieldDirection.Out; dirExpr.RemoveAnnotations <ByReferenceResolveResult>(); if (brrr.ElementResult == null) { brrr = new ByReferenceResolveResult(brrr.ElementType, isOut: true); } else { brrr = new ByReferenceResolveResult(brrr.ElementResult, isOut: true); } dirExpr.AddAnnotation(brrr); arg = new TranslatedExpression(dirExpr); } arguments.Add(arg); expectedParameters.Add(parameter); } if (method is VarArgInstanceMethod) { int regularParameterCount = ((VarArgInstanceMethod)method).RegularParameterCount; var argListArg = new UndocumentedExpression(); argListArg.UndocumentedExpressionType = UndocumentedExpressionType.ArgList; int paramIndex = regularParameterCount; var builder = expressionBuilder; Debug.Assert(argumentToParameterMap == null && argumentNames == null); argListArg.Arguments.AddRange(arguments.Skip(regularParameterCount).Select(arg => arg.ConvertTo(expectedParameters[paramIndex++].Type, builder).Expression)); var argListRR = new ResolveResult(SpecialType.ArgList); arguments = arguments.Take(regularParameterCount) .Concat(new[] { argListArg.WithoutILInstruction().WithRR(argListRR) }).ToList(); method = ((VarArgInstanceMethod)method).BaseMethod; expectedParameters = method.Parameters.ToList(); } var argumentResolveResults = arguments.Select(arg => arg.ResolveResult).ToList(); if (callOpCode == OpCode.NewObj) { ResolveResult rr = new CSharpInvocationResolveResult(target.ResolveResult, method, argumentResolveResults, isExpandedForm: isExpandedForm, argumentToParameterMap: argumentToParameterMap); if (settings.AnonymousTypes && method.DeclaringType.IsAnonymousType()) { Debug.Assert(argumentToParameterMap == null && argumentNames == null); var argumentExpressions = arguments.SelectArray(arg => arg.Expression); AnonymousTypeCreateExpression atce = new AnonymousTypeCreateExpression(); if (CanInferAnonymousTypePropertyNamesFromArguments(argumentExpressions, expectedParameters)) { atce.Initializers.AddRange(argumentExpressions); } else { for (int i = 0; i < argumentExpressions.Length; i++) { atce.Initializers.Add( new NamedExpression { Name = expectedParameters[i].Name, Expression = arguments[i].ConvertTo(expectedParameters[i].Type, expressionBuilder) }); } } return(atce .WithRR(rr)); } else { if (IsUnambiguousCall(expectedTargetDetails, method, null, Empty <IType> .Array, arguments, argumentNames, out _) != OverloadResolutionErrors.None) { 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 { arguments[i] = arguments[i].ConvertTo(expectedParameters[i].Type, expressionBuilder); } } } return(new ObjectCreateExpression( expressionBuilder.ConvertType(method.DeclaringType), GetArgumentExpressions(arguments, argumentNames) ).WithRR(rr)); } } else { int allowedParamCount = (method.ReturnType.IsKnownType(KnownTypeCode.Void) ? 1 : 0); if (method.IsAccessor && (method.AccessorOwner.SymbolKind == SymbolKind.Indexer || expectedParameters.Count == allowedParamCount)) { Debug.Assert(argumentToParameterMap == null && argumentNames == null); return(HandleAccessorCall(expectedTargetDetails, method, target, arguments.ToList())); } else if (method.Name == "Invoke" && method.DeclaringType.Kind == TypeKind.Delegate && !IsNullConditional(target)) { return(new InvocationExpression(target, GetArgumentExpressions(arguments, argumentNames)) .WithRR(new CSharpInvocationResolveResult(target.ResolveResult, method, argumentResolveResults, isExpandedForm: isExpandedForm))); } else if (IsDelegateEqualityComparison(method, arguments)) { Debug.Assert(argumentToParameterMap == null && argumentNames == null); return(HandleDelegateEqualityComparison(method, arguments) .WithRR(new CSharpInvocationResolveResult(target.ResolveResult, method, argumentResolveResults, isExpandedForm: isExpandedForm))); } else if (method.IsOperator && method.Name == "op_Implicit" && arguments.Count == 1) { return(HandleImplicitConversion(method, arguments[0])); } else { bool requireTypeArguments = false; bool requireTarget; if (expressionBuilder.HidesVariableWithName(method.Name)) { requireTarget = true; } 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 = (callOpCode != OpCode.CallVirt && method.IsVirtual); } else { requireTarget = !(target.Expression is ThisReferenceExpression); } } bool targetCasted = false; bool argumentsCasted = false; IType[] typeArguments = Empty <IType> .Array; var targetResolveResult = requireTarget ? target.ResolveResult : null; IParameterizedMember foundMethod; OverloadResolutionErrors errors; while ((errors = IsUnambiguousCall(expectedTargetDetails, method, targetResolveResult, typeArguments, arguments, argumentNames, out foundMethod)) != OverloadResolutionErrors.None) { switch (errors) { case OverloadResolutionErrors.TypeInferenceFailed: case OverloadResolutionErrors.WrongNumberOfTypeArguments: if (requireTypeArguments) { goto default; } requireTypeArguments = true; typeArguments = method.TypeArguments.ToArray(); 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 (!argumentsCasted) { argumentsCasted = true; 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 { arguments[i] = arguments[i].ConvertTo(expectedParameters[i].Type, expressionBuilder); } } } else if (!requireTarget) { requireTarget = true; targetResolveResult = target.ResolveResult; } else if (!targetCasted) { targetCasted = true; target = target.ConvertTo(method.DeclaringType, expressionBuilder); targetResolveResult = target.ResolveResult; } else if (!requireTypeArguments) { requireTypeArguments = true; typeArguments = method.TypeArguments.ToArray(); } else { break; } continue; } // We've given up. foundMethod = method; break; } // Note: after this loop, 'method' and 'foundMethod' may differ, // but as far as allowed by IsAppropriateCallTarget(). Expression targetExpr; string methodName = method.Name; AstNodeCollection <AstType> typeArgumentList; if (requireTarget) { 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. if (method.IsExplicitInterfaceImplementation && target.Expression is ThisReferenceExpression) { var castExpression = new CastExpression(expressionBuilder.ConvertType(method.ImplementedInterfaceMembers[0].DeclaringType), target.Expression); methodName = method.ImplementedInterfaceMembers[0].Name; targetExpr = new MemberReferenceExpression(castExpression, methodName); typeArgumentList = ((MemberReferenceExpression)targetExpr).TypeArguments; } } else { targetExpr = new IdentifierExpression(methodName); typeArgumentList = ((IdentifierExpression)targetExpr).TypeArguments; } if (requireTypeArguments && (!settings.AnonymousTypes || !method.TypeArguments.Any(a => a.ContainsAnonymousType()))) { typeArgumentList.AddRange(method.TypeArguments.Select(expressionBuilder.ConvertType)); } var argumentExpressions = GetArgumentExpressions(arguments, argumentNames); return(new InvocationExpression(targetExpr, argumentExpressions) .WithRR(new CSharpInvocationResolveResult(target.ResolveResult, foundMethod, argumentResolveResults, isExpandedForm: isExpandedForm))); } } }