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

5 years ago
  1. // Copyright (c) 2014 Daniel Grunwald
  2. //
  3. // Permission is hereby granted, free of charge, to any person obtaining a copy of this
  4. // software and associated documentation files (the "Software"), to deal in the Software
  5. // without restriction, including without limitation the rights to use, copy, modify, merge,
  6. // publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
  7. // to whom the Software is furnished to do so, subject to the following conditions:
  8. //
  9. // The above copyright notice and this permission notice shall be included in all copies or
  10. // substantial portions of the Software.
  11. //
  12. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
  13. // INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
  14. // PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
  15. // FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
  16. // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
  17. // DEALINGS IN THE SOFTWARE.
  18. using System;
  19. using System.Collections.Generic;
  20. using System.Collections.Immutable;
  21. using System.Diagnostics;
  22. using System.Linq;
  23. using System.Text;
  24. using ICSharpCode.Decompiler.CSharp.Resolver;
  25. using ICSharpCode.Decompiler.CSharp.Syntax;
  26. using ICSharpCode.Decompiler.IL;
  27. using ICSharpCode.Decompiler.Semantics;
  28. using ICSharpCode.Decompiler.TypeSystem;
  29. using ICSharpCode.Decompiler.TypeSystem.Implementation;
  30. using ICSharpCode.Decompiler.Util;
  31. namespace ICSharpCode.Decompiler.CSharp
  32. {
  33. struct CallBuilder
  34. {
  35. struct ExpectedTargetDetails
  36. {
  37. public OpCode CallOpCode;
  38. public bool NeedsBoxingConversion;
  39. }
  40. struct ArgumentList
  41. {
  42. public TranslatedExpression[] Arguments;
  43. public IParameter[] ExpectedParameters;
  44. public string[] ParameterNames;
  45. public string[] ArgumentNames;
  46. public int FirstOptionalArgumentIndex;
  47. public BitSet IsPrimitiveValue;
  48. public IReadOnlyList<int> ArgumentToParameterMap;
  49. public bool AddNamesToPrimitiveValues;
  50. public bool UseImplicitlyTypedOut;
  51. public bool IsExpandedForm;
  52. public int Length => Arguments.Length;
  53. private int GetActualArgumentCount()
  54. {
  55. if (FirstOptionalArgumentIndex < 0)
  56. return Arguments.Length;
  57. return FirstOptionalArgumentIndex;
  58. }
  59. public IList<ResolveResult> GetArgumentResolveResults(int skipCount = 0)
  60. {
  61. var expectedParameters = ExpectedParameters;
  62. var useImplicitlyTypedOut = UseImplicitlyTypedOut;
  63. return Arguments
  64. .SelectWithIndex(GetResolveResult)
  65. .Skip(skipCount)
  66. .Take(GetActualArgumentCount())
  67. .ToArray();
  68. ResolveResult GetResolveResult(int index, TranslatedExpression expression)
  69. {
  70. var param = expectedParameters[index];
  71. if (useImplicitlyTypedOut && param.IsOut && expression.Type is ByReferenceType brt)
  72. return new OutVarResolveResult(brt.ElementType);
  73. return expression.ResolveResult;
  74. }
  75. }
  76. public IList<ResolveResult> GetArgumentResolveResultsDirect(int skipCount = 0)
  77. {
  78. return Arguments
  79. .Skip(skipCount)
  80. .Take(GetActualArgumentCount())
  81. .Select(a => a.ResolveResult)
  82. .ToArray();
  83. }
  84. public IEnumerable<Expression> GetArgumentExpressions(int skipCount = 0)
  85. {
  86. if (AddNamesToPrimitiveValues && IsPrimitiveValue.Any() && !IsExpandedForm
  87. && !ParameterNames.Any(p => string.IsNullOrEmpty(p)))
  88. {
  89. Debug.Assert(skipCount == 0);
  90. if (ArgumentNames == null)
  91. {
  92. ArgumentNames = new string[Arguments.Length];
  93. }
  94. for (int i = 0; i < Arguments.Length; i++)
  95. {
  96. if (IsPrimitiveValue[i] && ArgumentNames[i] == null)
  97. {
  98. ArgumentNames[i] = ParameterNames[i];
  99. }
  100. }
  101. }
  102. int argumentCount = GetActualArgumentCount();
  103. var useImplicitlyTypedOut = UseImplicitlyTypedOut;
  104. if (ArgumentNames == null)
  105. {
  106. return Arguments.Skip(skipCount).Take(argumentCount).Select(arg => AddAnnotations(arg.Expression));
  107. }
  108. else
  109. {
  110. Debug.Assert(skipCount == 0);
  111. return Arguments.Take(argumentCount).Zip(ArgumentNames.Take(argumentCount),
  112. (arg, name) => {
  113. if (name == null)
  114. return AddAnnotations(arg.Expression);
  115. else
  116. return new NamedArgumentExpression(name, AddAnnotations(arg.Expression));
  117. });
  118. }
  119. Expression AddAnnotations(Expression expression)
  120. {
  121. if (!useImplicitlyTypedOut)
  122. return expression;
  123. if (expression.GetResolveResult() is ByReferenceResolveResult brrr
  124. && brrr.IsOut)
  125. {
  126. expression.AddAnnotation(UseImplicitlyTypedOutAnnotation.Instance);
  127. }
  128. return expression;
  129. }
  130. }
  131. public bool CanInferAnonymousTypePropertyNamesFromArguments()
  132. {
  133. for (int i = 0; i < Arguments.Length; i++)
  134. {
  135. string inferredName;
  136. switch (Arguments[i].Expression)
  137. {
  138. case IdentifierExpression identifier:
  139. inferredName = identifier.Identifier;
  140. break;
  141. case MemberReferenceExpression member:
  142. inferredName = member.MemberName;
  143. break;
  144. default:
  145. inferredName = null;
  146. break;
  147. }
  148. if (inferredName != ExpectedParameters[i].Name)
  149. {
  150. return false;
  151. }
  152. }
  153. return true;
  154. }
  155. [Conditional("DEBUG")]
  156. public void CheckNoNamedOrOptionalArguments()
  157. {
  158. Debug.Assert(ArgumentToParameterMap == null && ArgumentNames == null && FirstOptionalArgumentIndex < 0);
  159. }
  160. }
  161. readonly DecompilerSettings settings;
  162. readonly ExpressionBuilder expressionBuilder;
  163. readonly CSharpResolver resolver;
  164. readonly IDecompilerTypeSystem typeSystem;
  165. public CallBuilder(ExpressionBuilder expressionBuilder, IDecompilerTypeSystem typeSystem, DecompilerSettings settings)
  166. {
  167. this.expressionBuilder = expressionBuilder;
  168. this.resolver = expressionBuilder.resolver;
  169. this.settings = settings;
  170. this.typeSystem = typeSystem;
  171. }
  172. public TranslatedExpression Build(CallInstruction inst)
  173. {
  174. if (inst is NewObj newobj && IL.Transforms.DelegateConstruction.MatchDelegateConstruction(newobj, out _, out _, out _))
  175. {
  176. return HandleDelegateConstruction(newobj);
  177. }
  178. if (settings.TupleTypes && TupleTransform.MatchTupleConstruction(inst as NewObj, out var tupleElements) && tupleElements.Length >= 2)
  179. {
  180. var elementTypes = TupleType.GetTupleElementTypes(inst.Method.DeclaringType);
  181. Debug.Assert(!elementTypes.IsDefault, "MatchTupleConstruction should not success unless we got a valid tuple type.");
  182. Debug.Assert(elementTypes.Length == tupleElements.Length);
  183. var tuple = new TupleExpression();
  184. var elementRRs = new List<ResolveResult>();
  185. foreach (var (element, elementType) in tupleElements.Zip(elementTypes))
  186. {
  187. var translatedElement = expressionBuilder.Translate(element, elementType)
  188. .ConvertTo(elementType, expressionBuilder, allowImplicitConversion: true);
  189. tuple.Elements.Add(translatedElement.Expression);
  190. elementRRs.Add(translatedElement.ResolveResult);
  191. }
  192. return tuple.WithRR(new TupleResolveResult(
  193. expressionBuilder.compilation,
  194. elementRRs.ToImmutableArray(),
  195. valueTupleAssembly: inst.Method.DeclaringType.GetDefinition()?.ParentModule
  196. )).WithILInstruction(inst);
  197. }
  198. return Build(inst.OpCode, inst.Method, inst.Arguments, constrainedTo: inst.ConstrainedTo)
  199. .WithILInstruction(inst);
  200. }
  201. public ExpressionWithResolveResult Build(OpCode callOpCode, IMethod method,
  202. IReadOnlyList<ILInstruction> callArguments,
  203. IReadOnlyList<int> argumentToParameterMap = null,
  204. IType constrainedTo = null)
  205. {
  206. if (method.IsExplicitInterfaceImplementation && callOpCode == OpCode.Call)
  207. {
  208. // Direct non-virtual call to explicit interface implementation.
  209. // This can't really be represented in C#, but at least in the case where
  210. // the class is sealed, we can equivalently call the interface member instead:
  211. var interfaceMembers = method.ExplicitlyImplementedInterfaceMembers.ToList();
  212. if (method.DeclaringTypeDefinition?.Kind == TypeKind.Class && method.DeclaringTypeDefinition.IsSealed && interfaceMembers.Count == 1)
  213. {
  214. method = (IMethod)interfaceMembers.Single();
  215. callOpCode = OpCode.CallVirt;
  216. }
  217. }
  218. // Used for Call, CallVirt and NewObj
  219. var expectedTargetDetails = new ExpectedTargetDetails {
  220. CallOpCode = callOpCode
  221. };
  222. ILFunction localFunction = null;
  223. if (method.IsLocalFunction)
  224. {
  225. localFunction = expressionBuilder.ResolveLocalFunction(method);
  226. Debug.Assert(localFunction != null);
  227. }
  228. TranslatedExpression target;
  229. if (callOpCode == OpCode.NewObj)
  230. {
  231. target = default(TranslatedExpression); // no target
  232. }
  233. else if (localFunction != null)
  234. {
  235. var ide = new IdentifierExpression(localFunction.Name);
  236. if (method.TypeArguments.Count > 0)
  237. {
  238. ide.TypeArguments.AddRange(method.TypeArguments.Select(expressionBuilder.ConvertType));
  239. }
  240. ide.AddAnnotation(localFunction);
  241. target = ide.WithoutILInstruction()
  242. .WithRR(ToMethodGroup(method, localFunction));
  243. }
  244. else
  245. {
  246. target = expressionBuilder.TranslateTarget(
  247. callArguments.FirstOrDefault(),
  248. nonVirtualInvocation: callOpCode == OpCode.Call || method.IsConstructor,
  249. memberStatic: method.IsStatic,
  250. memberDeclaringType: constrainedTo ?? method.DeclaringType);
  251. if (constrainedTo == null
  252. && target.Expression is CastExpression cast
  253. && target.ResolveResult is ConversionResolveResult conversion
  254. && target.Type.IsKnownType(KnownTypeCode.Object)
  255. && conversion.Conversion.IsBoxingConversion)
  256. {
  257. // boxing conversion on call target?
  258. // let's see if we can make that implicit:
  259. target = target.UnwrapChild(cast.Expression);
  260. // we'll need to make sure the boxing effect is preserved
  261. expectedTargetDetails.NeedsBoxingConversion = true;
  262. }
  263. }
  264. int firstParamIndex = (method.IsStatic || callOpCode == OpCode.NewObj) ? 0 : 1;
  265. Debug.Assert(firstParamIndex == 0 || argumentToParameterMap == null
  266. || argumentToParameterMap[0] == -1);
  267. var argumentList = BuildArgumentList(expectedTargetDetails, target.ResolveResult, method,
  268. firstParamIndex, callArguments, argumentToParameterMap);
  269. if (localFunction != null)
  270. {
  271. return new InvocationExpression(target, argumentList.GetArgumentExpressions())
  272. .WithRR(new CSharpInvocationResolveResult(target.ResolveResult, method,
  273. argumentList.GetArgumentResolveResults(), isExpandedForm: argumentList.IsExpandedForm));
  274. }
  275. if (method is VarArgInstanceMethod)
  276. {
  277. argumentList.FirstOptionalArgumentIndex = -1;
  278. argumentList.AddNamesToPrimitiveValues = false;
  279. argumentList.UseImplicitlyTypedOut = false;
  280. int regularParameterCount = ((VarArgInstanceMethod)method).RegularParameterCount;
  281. var argListArg = new UndocumentedExpression();
  282. argListArg.UndocumentedExpressionType = UndocumentedExpressionType.ArgList;
  283. int paramIndex = regularParameterCount;
  284. var builder = expressionBuilder;
  285. Debug.Assert(argumentToParameterMap == null && argumentList.ArgumentNames == null);
  286. argListArg.Arguments.AddRange(argumentList.Arguments.Skip(regularParameterCount).Select(arg => arg.ConvertTo(argumentList.ExpectedParameters[paramIndex++].Type, builder).Expression));
  287. var argListRR = new ResolveResult(SpecialType.ArgList);
  288. argumentList.Arguments = argumentList.Arguments.Take(regularParameterCount)
  289. .Concat(new[] { argListArg.WithoutILInstruction().WithRR(argListRR) }).ToArray();
  290. method = ((VarArgInstanceMethod)method).BaseMethod;
  291. argumentList.ExpectedParameters = method.Parameters.ToArray();
  292. }
  293. if (settings.Ranges)
  294. {
  295. if (HandleRangeConstruction(out var result, callOpCode, method, target, argumentList))
  296. {
  297. return result;
  298. }
  299. }
  300. if (callOpCode == OpCode.NewObj)
  301. {
  302. return HandleConstructorCall(expectedTargetDetails, target.ResolveResult, method, argumentList);
  303. }
  304. if (method.Name == "Invoke" && method.DeclaringType.Kind == TypeKind.Delegate && !IsNullConditional(target))
  305. {
  306. return new InvocationExpression(target, argumentList.GetArgumentExpressions())
  307. .WithRR(new CSharpInvocationResolveResult(target.ResolveResult, method,
  308. argumentList.GetArgumentResolveResults(), isExpandedForm: argumentList.IsExpandedForm, isDelegateInvocation: true));
  309. }
  310. if (settings.StringInterpolation && IsInterpolatedStringCreation(method, argumentList) &&
  311. TryGetStringInterpolationTokens(argumentList, out string format, out var tokens))
  312. {
  313. var arguments = argumentList.Arguments;
  314. var content = new List<InterpolatedStringContent>();
  315. bool unpackSingleElementArray = !argumentList.IsExpandedForm && argumentList.Length == 2
  316. && argumentList.Arguments[1].Expression is ArrayCreateExpression ace
  317. && ace.Initializer?.Elements.Count == 1;
  318. void UnpackSingleElementArray(ref TranslatedExpression argument)
  319. {
  320. if (!unpackSingleElementArray)
  321. return;
  322. var arrayCreation = (ArrayCreateExpression)argumentList.Arguments[1].Expression;
  323. var arrayCreationRR = (ArrayCreateResolveResult)argumentList.Arguments[1].ResolveResult;
  324. var element = arrayCreation.Initializer.Elements.First().Detach();
  325. argument = new TranslatedExpression(element, arrayCreationRR.InitializerElements.First());
  326. }
  327. if (tokens.Count > 0)
  328. {
  329. foreach (var (kind, index, alignment, text) in tokens)
  330. {
  331. TranslatedExpression argument;
  332. switch (kind)
  333. {
  334. case TokenKind.String:
  335. content.Add(new InterpolatedStringText(text));
  336. break;
  337. case TokenKind.Argument:
  338. argument = arguments[index + 1];
  339. UnpackSingleElementArray(ref argument);
  340. content.Add(new Interpolation(argument));
  341. break;
  342. case TokenKind.ArgumentWithFormat:
  343. argument = arguments[index + 1];
  344. UnpackSingleElementArray(ref argument);
  345. content.Add(new Interpolation(argument, suffix: text));
  346. break;
  347. case TokenKind.ArgumentWithAlignment:
  348. argument = arguments[index + 1];
  349. UnpackSingleElementArray(ref argument);
  350. content.Add(new Interpolation(argument, alignment));
  351. break;
  352. case TokenKind.ArgumentWithAlignmentAndFormat:
  353. argument = arguments[index + 1];
  354. UnpackSingleElementArray(ref argument);
  355. content.Add(new Interpolation(argument, alignment, text));
  356. break;
  357. }
  358. }
  359. var formattableStringType = expressionBuilder.compilation.FindType(KnownTypeCode.FormattableString);
  360. var isrr = new InterpolatedStringResolveResult(expressionBuilder.compilation.FindType(KnownTypeCode.String),
  361. format, argumentList.GetArgumentResolveResults(1).ToArray());
  362. var expr = new InterpolatedStringExpression();
  363. expr.Content.AddRange(content);
  364. if (method.Name == "Format")
  365. return expr.WithRR(isrr);
  366. return new CastExpression(expressionBuilder.ConvertType(formattableStringType),
  367. expr.WithRR(isrr))
  368. .WithRR(new ConversionResolveResult(formattableStringType, isrr, Conversion.ImplicitInterpolatedStringConversion));
  369. }
  370. }
  371. int allowedParamCount = (method.ReturnType.IsKnownType(KnownTypeCode.Void) ? 1 : 0);
  372. if (method.IsAccessor && (method.AccessorOwner.SymbolKind == SymbolKind.Indexer || argumentList.ExpectedParameters.Length == allowedParamCount))
  373. {
  374. argumentList.CheckNoNamedOrOptionalArguments();
  375. return HandleAccessorCall(expectedTargetDetails, method, target, argumentList.Arguments.ToList(), argumentList.ArgumentNames);
  376. }
  377. if (IsDelegateEqualityComparison(method, argumentList.Arguments))
  378. {
  379. argumentList.CheckNoNamedOrOptionalArguments();
  380. return HandleDelegateEqualityComparison(method, argumentList.Arguments)
  381. .WithRR(new CSharpInvocationResolveResult(target.ResolveResult, method,
  382. argumentList.GetArgumentResolveResults(), isExpandedForm: argumentList.IsExpandedForm));
  383. }
  384. if (method.IsOperator && method.Name == "op_Implicit" && argumentList.Length == 1)
  385. {
  386. argumentList.CheckNoNamedOrOptionalArguments();
  387. return HandleImplicitConversion(method, argumentList.Arguments[0]);
  388. }
  389. var transform = GetRequiredTransformationsForCall(expectedTargetDetails, method, ref target,
  390. ref argumentList, CallTransformation.All, out IParameterizedMember foundMethod);
  391. // Note: after this, 'method' and 'foundMethod' may differ,
  392. // but as far as allowed by IsAppropriateCallTarget().
  393. // Need to update list of parameter names, because foundMethod is different and thus might use different names.
  394. if (!method.Equals(foundMethod) && argumentList.ParameterNames.Length >= foundMethod.Parameters.Count)
  395. {
  396. for (int i = 0; i < foundMethod.Parameters.Count; i++)
  397. {
  398. argumentList.ParameterNames[i] = foundMethod.Parameters[i].Name;
  399. }
  400. }
  401. Expression targetExpr;
  402. string methodName = method.Name;
  403. AstNodeCollection<AstType> typeArgumentList;
  404. if ((transform & CallTransformation.NoOptionalArgumentAllowed) != 0)
  405. {
  406. argumentList.FirstOptionalArgumentIndex = -1;
  407. }
  408. if ((transform & CallTransformation.RequireTarget) != 0)
  409. {
  410. targetExpr = new MemberReferenceExpression(target.Expression, methodName);
  411. typeArgumentList = ((MemberReferenceExpression)targetExpr).TypeArguments;
  412. // HACK : convert this.Dispose() to ((IDisposable)this).Dispose(), if Dispose is an explicitly implemented interface method.
  413. // settings.AlwaysCastTargetsOfExplicitInterfaceImplementationCalls == true is used in Windows Forms' InitializeComponent methods.
  414. if (method.IsExplicitInterfaceImplementation && (target.Expression is ThisReferenceExpression || settings.AlwaysCastTargetsOfExplicitInterfaceImplementationCalls))
  415. {
  416. var interfaceMember = method.ExplicitlyImplementedInterfaceMembers.First();
  417. var castExpression = new CastExpression(expressionBuilder.ConvertType(interfaceMember.DeclaringType), target.Expression.Detach());
  418. methodName = interfaceMember.Name;
  419. targetExpr = new MemberReferenceExpression(castExpression, methodName);
  420. typeArgumentList = ((MemberReferenceExpression)targetExpr).TypeArguments;
  421. }
  422. }
  423. else
  424. {
  425. targetExpr = new IdentifierExpression(methodName);
  426. typeArgumentList = ((IdentifierExpression)targetExpr).TypeArguments;
  427. }
  428. if ((transform & CallTransformation.RequireTypeArguments) != 0 && (!settings.AnonymousTypes || !method.TypeArguments.Any(a => a.ContainsAnonymousType())))
  429. typeArgumentList.AddRange(method.TypeArguments.Select(expressionBuilder.ConvertType));
  430. return new InvocationExpression(targetExpr, argumentList.GetArgumentExpressions())
  431. .WithRR(new CSharpInvocationResolveResult(target.ResolveResult, foundMethod,
  432. argumentList.GetArgumentResolveResultsDirect(), isExpandedForm: argumentList.IsExpandedForm));
  433. }
  434. /// <summary>
  435. /// Converts a call to an Add method to a collection initializer expression.
  436. /// </summary>
  437. public ExpressionWithResolveResult BuildCollectionInitializerExpression(OpCode callOpCode, IMethod method,
  438. InitializedObjectResolveResult target, IReadOnlyList<ILInstruction> callArguments)
  439. {
  440. // (see ECMA-334, section 12.7.11.4):
  441. // The collection object to which a collection initializer is applied shall be of a type that implements
  442. // System.Collections.IEnumerable or a compile-time error occurs. For each specified element in order,
  443. // the collection initializer invokes an Add method on the target object with the expression list of the
  444. // element initializer as argument list, applying normal overload resolution for each invocation. Thus, the
  445. // collection object shall contain an applicable Add method for each element initializer.
  446. // The list of applicable methods includes all methods (as of C# 6.0 extension methods, too) named 'Add'
  447. // that can be invoked on the target object, with the following exceptions:
  448. // - Methods with ref or out parameters may not be used,
  449. // - methods that have type parameters, that cannot be inferred from the parameter list may not be used,
  450. // - vararg methods may not be used.
  451. // - named arguments are not supported.
  452. // However, note that params methods may be used.
  453. // At this point, we assume that 'method' fulfills all the conditions mentioned above. We just need to make
  454. // sure that the correct method is called by resolving any ambiguities by inserting casts, if necessary.
  455. ExpectedTargetDetails expectedTargetDetails = new ExpectedTargetDetails { CallOpCode = callOpCode };
  456. var unused = new IdentifierExpression("initializedObject").WithRR(target).WithoutILInstruction();
  457. var args = callArguments.ToList();
  458. if (method.IsExtensionMethod)
  459. args.Insert(0, new Nop());
  460. var argumentList = BuildArgumentList(expectedTargetDetails, target, method,
  461. firstParamIndex: 0, args, null);
  462. argumentList.ArgumentNames = null;
  463. argumentList.AddNamesToPrimitiveValues = false;
  464. argumentList.UseImplicitlyTypedOut = false;
  465. var transform = GetRequiredTransformationsForCall(expectedTargetDetails, method, ref unused,
  466. ref argumentList, CallTransformation.None, out _);
  467. Debug.Assert(transform == CallTransformation.None || transform == CallTransformation.NoOptionalArgumentAllowed);
  468. // Calls with only one argument do not need an array initializer expression to wrap them.
  469. // Any special cases are handled by the caller (i.e., ExpressionBuilder.TranslateObjectAndCollectionInitializer)
  470. // Note: we intentionally ignore the firstOptionalArgumentIndex in this case.
  471. int skipCount;
  472. if (method.IsExtensionMethod)
  473. {
  474. if (argumentList.Arguments.Length == 2)
  475. return argumentList.Arguments[1];
  476. skipCount = 1;
  477. }
  478. else
  479. {
  480. if (argumentList.Arguments.Length == 1)
  481. return argumentList.Arguments[0];
  482. skipCount = 0;
  483. }
  484. if ((transform & CallTransformation.NoOptionalArgumentAllowed) != 0)
  485. argumentList.FirstOptionalArgumentIndex = -1;
  486. return new ArrayInitializerExpression(argumentList.GetArgumentExpressions(skipCount))
  487. .WithRR(new CSharpInvocationResolveResult(target, method, argumentList.GetArgumentResolveResults(skipCount).ToArray(),
  488. isExtensionMethodInvocation: method.IsExtensionMethod, isExpandedForm: argumentList.IsExpandedForm));
  489. }
  490. public ExpressionWithResolveResult BuildDictionaryInitializerExpression(OpCode callOpCode, IMethod method,
  491. InitializedObjectResolveResult target, IReadOnlyList<ILInstruction> indices, ILInstruction value = null)
  492. {
  493. if (method is null)
  494. throw new ArgumentNullException(nameof(method));
  495. ExpectedTargetDetails expectedTargetDetails = new ExpectedTargetDetails { CallOpCode = callOpCode };
  496. var callArguments = new List<ILInstruction>();
  497. callArguments.Add(new LdNull());
  498. callArguments.AddRange(indices);
  499. callArguments.Add(value ?? new Nop());
  500. var argumentList = BuildArgumentList(expectedTargetDetails, target, method, 1, callArguments, null);
  501. var unused = new IdentifierExpression("initializedObject").WithRR(target).WithoutILInstruction();
  502. var assignment = HandleAccessorCall(expectedTargetDetails, method, unused,
  503. argumentList.Arguments.ToList(), argumentList.ArgumentNames);
  504. if (((AssignmentExpression)assignment).Left is IndexerExpression indexer && !indexer.Target.IsNull)
  505. indexer.Target.Remove();
  506. if (value != null)
  507. return assignment;
  508. return new ExpressionWithResolveResult(((AssignmentExpression)assignment).Left.Detach());
  509. }
  510. private static bool IsInterpolatedStringCreation(IMethod method, ArgumentList argumentList)
  511. {
  512. return method.IsStatic && (
  513. (method.DeclaringType.IsKnownType(KnownTypeCode.String) && method.Name == "Format") ||
  514. (method.Name == "Create" && method.DeclaringType.Name == "FormattableStringFactory" &&
  515. method.DeclaringType.Namespace == "System.Runtime.CompilerServices")
  516. )
  517. && argumentList.ArgumentNames == null // Argument names are not allowed
  518. && (
  519. argumentList.IsExpandedForm // Must be expanded form
  520. || !method.Parameters.Last().IsParams // -or- not a params overload
  521. || (argumentList.Length == 2 && argumentList.Arguments[1].Expression is ArrayCreateExpression) // -or- an array literal
  522. );
  523. }
  524. private bool TryGetStringInterpolationTokens(ArgumentList argumentList, out string format, out List<(TokenKind Kind, int Index, int Alignment, string Format)> tokens)
  525. {
  526. tokens = null;
  527. format = null;
  528. TranslatedExpression[] arguments = argumentList.Arguments;
  529. if (arguments.Length == 0 || argumentList.ArgumentNames != null || argumentList.ArgumentToParameterMap != null)
  530. return false;
  531. if (!(arguments[(int)0].ResolveResult is ConstantResolveResult crr && crr.Type.IsKnownType((KnownTypeCode)KnownTypeCode.String)))
  532. return false;
  533. if (!arguments.Skip(1).All(a => !a.Expression.DescendantsAndSelf.OfType<PrimitiveExpression>().Any(p => p.Value is string)))
  534. return false;
  535. tokens = new List<(TokenKind Kind, int Index, int Alignment, string Format)>();
  536. int i = 0;
  537. format = (string)crr.ConstantValue;
  538. foreach (var (kind, data) in TokenizeFormatString(format))
  539. {
  540. int index;
  541. string[] arg;
  542. switch (kind)
  543. {
  544. case TokenKind.Error:
  545. return false;
  546. case TokenKind.String:
  547. tokens.Add((kind, -1, 0, data));
  548. break;
  549. case TokenKind.Argument:
  550. if (!int.TryParse(data, out index) || index != i)
  551. return false;
  552. i++;
  553. tokens.Add((kind, index, 0, null));
  554. break;
  555. case TokenKind.ArgumentWithFormat:
  556. arg = data.Split(new[] { ':' }, 2);
  557. if (arg.Length != 2 || arg[1].Length == 0)
  558. return false;
  559. if (!int.TryParse(arg[0], out index) || index != i)
  560. return false;
  561. i++;
  562. tokens.Add((kind, index, 0, arg[1]));
  563. break;
  564. case TokenKind.ArgumentWithAlignment:
  565. arg = data.Split(new[] { ',' }, 2);
  566. if (arg.Length != 2 || arg[1].Length == 0)
  567. return false;
  568. if (!int.TryParse(arg[0], out index) || index != i)
  569. return false;
  570. if (!int.TryParse(arg[1], out int alignment))
  571. return false;
  572. i++;
  573. tokens.Add((kind, index, alignment, null));
  574. break;
  575. case TokenKind.ArgumentWithAlignmentAndFormat:
  576. arg = data.Split(new[] { ',', ':' }, 3);
  577. if (arg.Length != 3 || arg[1].Length == 0 || arg[2].Length == 0)
  578. return false;
  579. if (!int.TryParse(arg[0], out index) || index != i)
  580. return false;
  581. if (!int.TryParse(arg[1], out alignment))
  582. return false;
  583. i++;
  584. tokens.Add((kind, index, alignment, arg[2]));
  585. break;
  586. default:
  587. return false;
  588. }
  589. }
  590. return i == arguments.Length - 1;
  591. }
  592. private enum TokenKind
  593. {
  594. Error,
  595. String,
  596. Argument,
  597. ArgumentWithFormat,
  598. ArgumentWithAlignment,
  599. ArgumentWithAlignmentAndFormat,
  600. }
  601. private IEnumerable<(TokenKind, string)> TokenizeFormatString(string value)
  602. {
  603. int pos = -1;
  604. int Peek(int steps = 1)
  605. {
  606. if (pos + steps < value.Length)
  607. return value[pos + steps];
  608. return -1;
  609. }
  610. int Next()
  611. {
  612. int val = Peek();
  613. pos++;
  614. return val;
  615. }
  616. int next;
  617. TokenKind kind = TokenKind.String;
  618. StringBuilder sb = new StringBuilder();
  619. while ((next = Next()) > -1)
  620. {
  621. switch ((char)next)
  622. {
  623. case '{':
  624. if (Peek() == '{')
  625. {
  626. kind = TokenKind.String;
  627. sb.Append("{{");
  628. Next();
  629. }
  630. else
  631. {
  632. if (sb.Length > 0)
  633. {
  634. yield return (kind, sb.ToString());
  635. }
  636. kind = TokenKind.Argument;
  637. sb.Clear();
  638. }
  639. break;
  640. case '}':
  641. if (kind != TokenKind.String)
  642. {
  643. yield return (kind, sb.ToString());
  644. sb.Clear();
  645. kind = TokenKind.String;
  646. }
  647. else if (Peek() == '}')
  648. {
  649. sb.Append("}}");
  650. Next();
  651. }
  652. else
  653. {
  654. yield return (TokenKind.Error, null);
  655. }
  656. break;
  657. case ':':
  658. if (kind == TokenKind.Argument)
  659. {
  660. kind = TokenKind.ArgumentWithFormat;
  661. }
  662. else if (kind == TokenKind.ArgumentWithAlignment)
  663. {
  664. kind = TokenKind.ArgumentWithAlignmentAndFormat;
  665. }
  666. sb.Append(':');
  667. break;
  668. case ',':
  669. if (kind == TokenKind.Argument)
  670. {
  671. kind = TokenKind.ArgumentWithAlignment;
  672. }
  673. sb.Append(',');
  674. break;
  675. default:
  676. sb.Append((char)next);
  677. break;
  678. }
  679. }
  680. if (sb.Length > 0)
  681. {
  682. if (kind == TokenKind.String)
  683. yield return (kind, sb.ToString());
  684. else
  685. yield return (TokenKind.Error, null);
  686. }
  687. }
  688. private ArgumentList BuildArgumentList(ExpectedTargetDetails expectedTargetDetails, ResolveResult target, IMethod method,
  689. int firstParamIndex, IReadOnlyList<ILInstruction> callArguments, IReadOnlyList<int> argumentToParameterMap)
  690. {
  691. ArgumentList list = new ArgumentList();
  692. // Translate arguments to the expected parameter types
  693. var arguments = new List<TranslatedExpression>(method.Parameters.Count);
  694. string[] argumentNames = null;
  695. Debug.Assert(callArguments.Count == firstParamIndex + method.Parameters.Count);
  696. var expectedParameters = new List<IParameter>(method.Parameters.Count); // parameters, but in argument order
  697. bool isExpandedForm = false;
  698. BitSet isPrimitiveValue = new BitSet(method.Parameters.Count);
  699. // Optional arguments:
  700. // This value has the following values:
  701. // -2 - there are no optional arguments
  702. // -1 - optional arguments are forbidden
  703. // >= 0 - the index of the first argument that can be removed, because it is optional
  704. // and is the default value of the parameter.
  705. int firstOptionalArgumentIndex = expressionBuilder.settings.OptionalArguments ? -2 : -1;
  706. for (int i = firstParamIndex; i < callArguments.Count; i++)
  707. {
  708. IParameter parameter;
  709. if (argumentToParameterMap != null)
  710. {
  711. if (argumentNames == null && argumentToParameterMap[i] != i - firstParamIndex)
  712. {
  713. // Starting at the first argument that is out-of-place,
  714. // assign names to that argument and all following arguments:
  715. argumentNames = new string[method.Parameters.Count];
  716. }
  717. parameter = method.Parameters[argumentToParameterMap[i]];
  718. if (argumentNames != null)
  719. {
  720. argumentNames[arguments.Count] = parameter.Name;
  721. }
  722. }
  723. else
  724. {
  725. parameter = method.Parameters[i - firstParamIndex];
  726. }
  727. var arg = expressionBuilder.Translate(callArguments[i], parameter.Type);
  728. if (IsPrimitiveValueThatShouldBeNamedArgument(arg, method, parameter))
  729. {
  730. isPrimitiveValue.Set(arguments.Count);
  731. }
  732. if (IsOptionalArgument(parameter, arg))
  733. {
  734. if (firstOptionalArgumentIndex == -2)
  735. firstOptionalArgumentIndex = i - firstParamIndex;
  736. }
  737. else
  738. {
  739. firstOptionalArgumentIndex = -2;
  740. }
  741. if (parameter.IsParams && i + 1 == callArguments.Count && argumentToParameterMap == null)
  742. {
  743. // Parameter is marked params
  744. // If the argument is an array creation, inline all elements into the call and add missing default values.
  745. // Otherwise handle it normally.
  746. if (TransformParamsArgument(expectedTargetDetails, target, method, parameter,
  747. arg, ref expectedParameters, ref arguments))
  748. {
  749. Debug.Assert(argumentNames == null);
  750. firstOptionalArgumentIndex = -1;
  751. isExpandedForm = true;
  752. continue;
  753. }
  754. }
  755. IType parameterType;
  756. if (parameter.Type.Kind == TypeKind.Dynamic)
  757. {
  758. parameterType = expressionBuilder.compilation.FindType(KnownTypeCode.Object);
  759. }
  760. else
  761. {
  762. parameterType = parameter.Type;
  763. }
  764. arg = arg.ConvertTo(parameterType, expressionBuilder, allowImplicitConversion: arg.Type.Kind != TypeKind.Dynamic);
  765. if (parameter.ReferenceKind != ReferenceKind.None)
  766. {
  767. arg = ExpressionBuilder.ChangeDirectionExpressionTo(arg, parameter.ReferenceKind);
  768. }
  769. arguments.Add(arg);
  770. expectedParameters.Add(parameter);
  771. }
  772. list.ExpectedParameters = expectedParameters.ToArray();
  773. list.Arguments = arguments.ToArray();
  774. list.ParameterNames = expectedParameters.SelectArray(p => p.Name);
  775. list.ArgumentNames = argumentNames;
  776. list.ArgumentToParameterMap = argumentToParameterMap;
  777. list.IsExpandedForm = isExpandedForm;
  778. list.IsPrimitiveValue = isPrimitiveValue;
  779. list.FirstOptionalArgumentIndex = firstOptionalArgumentIndex;
  780. list.UseImplicitlyTypedOut = true;
  781. list.AddNamesToPrimitiveValues = expressionBuilder.settings.NamedArguments && expressionBuilder.settings.NonTrailingNamedArguments;
  782. return list;
  783. }
  784. private bool IsPrimitiveValueThatShouldBeNamedArgument(TranslatedExpression arg, IMethod method, IParameter p)
  785. {
  786. if (!arg.ResolveResult.IsCompileTimeConstant || method.DeclaringType.IsKnownType(KnownTypeCode.NullableOfT))
  787. return false;
  788. return p.Type.IsKnownType(KnownTypeCode.Boolean);
  789. }
  790. private bool TransformParamsArgument(ExpectedTargetDetails expectedTargetDetails, ResolveResult targetResolveResult,
  791. IMethod method, IParameter parameter, TranslatedExpression arg, ref List<IParameter> expectedParameters,
  792. ref List<TranslatedExpression> arguments)
  793. {
  794. if (CheckArgument(out int length, out IType elementType))
  795. {
  796. var expandedParameters = new List<IParameter>(expectedParameters);
  797. var expandedArguments = new List<TranslatedExpression>(arguments);
  798. if (length > 0)
  799. {
  800. var arrayElements = ((ArrayCreateExpression)arg.Expression).Initializer.Elements.ToArray();
  801. for (int j = 0; j < length; j++)
  802. {
  803. expandedParameters.Add(new DefaultParameter(elementType, parameter.Name + j));
  804. if (j < arrayElements.Length)
  805. expandedArguments.Add(new TranslatedExpression(arrayElements[j]));
  806. else
  807. expandedArguments.Add(expressionBuilder.GetDefaultValueExpression(elementType).WithoutILInstruction());
  808. }
  809. }
  810. if (IsUnambiguousCall(expectedTargetDetails, method, targetResolveResult, Empty<IType>.Array,
  811. expandedArguments.SelectArray(a => a.ResolveResult), argumentNames: null,
  812. firstOptionalArgumentIndex: -1, out _,
  813. out var bestCandidateIsExpandedForm) == OverloadResolutionErrors.None && bestCandidateIsExpandedForm)
  814. {
  815. expectedParameters = expandedParameters;
  816. arguments = expandedArguments.SelectList(a => new TranslatedExpression(a.Expression.Detach()));
  817. return true;
  818. }
  819. }
  820. return false;
  821. bool CheckArgument(out int len, out IType t)
  822. {
  823. len = 0;
  824. t = null;
  825. if (arg.ResolveResult is CSharpInvocationResolveResult csirr &&
  826. csirr.Arguments.Count == 0 && csirr.Member is IMethod emptyMethod &&
  827. emptyMethod.IsStatic &&
  828. "System.Array.Empty" == emptyMethod.FullName &&
  829. emptyMethod.TypeArguments.Count == 1)
  830. {
  831. t = emptyMethod.TypeArguments[0];
  832. return true;
  833. }
  834. if (arg.ResolveResult is ArrayCreateResolveResult acrr &&
  835. acrr.SizeArguments.Count == 1 &&
  836. acrr.SizeArguments[0].IsCompileTimeConstant &&
  837. acrr.SizeArguments[0].ConstantValue is int l)
  838. {
  839. len = l;
  840. t = ((ArrayType)acrr.Type).ElementType;
  841. return true;
  842. }
  843. return false;
  844. }
  845. }
  846. bool IsOptionalArgument(IParameter parameter, TranslatedExpression arg)
  847. {
  848. if (!parameter.IsOptional || !arg.ResolveResult.IsCompileTimeConstant)
  849. return false;
  850. if (parameter.GetAttributes().Any(a => a.AttributeType.IsKnownType(KnownAttribute.CallerMemberName)
  851. || a.AttributeType.IsKnownType(KnownAttribute.CallerFilePath)
  852. || a.AttributeType.IsKnownType(KnownAttribute.CallerLineNumber)))
  853. return false;
  854. return object.Equals(parameter.GetConstantValue(), arg.ResolveResult.ConstantValue);
  855. }
  856. [Flags]
  857. enum CallTransformation
  858. {
  859. None = 0,
  860. RequireTarget = 1,
  861. RequireTypeArguments = 2,
  862. NoOptionalArgumentAllowed = 4,
  863. All = 7
  864. }
  865. private CallTransformation GetRequiredTransformationsForCall(ExpectedTargetDetails expectedTargetDetails, IMethod method,
  866. ref TranslatedExpression target, ref ArgumentList argumentList, CallTransformation allowedTransforms, out IParameterizedMember foundMethod)
  867. {
  868. CallTransformation transform = CallTransformation.None;
  869. // initialize requireTarget flag
  870. bool requireTarget;
  871. ResolveResult targetResolveResult;
  872. if ((allowedTransforms & CallTransformation.RequireTarget) != 0)
  873. {
  874. if (settings.AlwaysQualifyMemberReferences || expressionBuilder.HidesVariableWithName(method.Name))
  875. {
  876. requireTarget = true;
  877. }
  878. else
  879. {
  880. if (method.IsLocalFunction)
  881. requireTarget = false;
  882. else if (method.IsStatic)
  883. requireTarget = !expressionBuilder.IsCurrentOrContainingType(method.DeclaringTypeDefinition) || method.Name == ".cctor";
  884. else if (method.Name == ".ctor")
  885. requireTarget = true; // always use target for base/this-ctor-call, the constructor initializer pattern depends on this
  886. else if (target.Expression is BaseReferenceExpression)
  887. requireTarget = (expectedTargetDetails.CallOpCode != OpCode.CallVirt && method.IsVirtual);
  888. else
  889. requireTarget = target.Expression is not ThisReferenceExpression;
  890. }
  891. targetResolveResult = requireTarget ? target.ResolveResult : null;
  892. }
  893. else
  894. {
  895. // HACK: this is a special case for collection initializer calls, they do not allow a target to be
  896. // emitted, but we still need it for overload resolution.
  897. requireTarget = true;
  898. targetResolveResult = target.ResolveResult;
  899. }
  900. // initialize requireTypeArguments flag
  901. bool requireTypeArguments;
  902. IType[] typeArguments;
  903. bool appliedRequireTypeArgumentsShortcut = false;
  904. if (method.TypeParameters.Count > 0 && (allowedTransforms & CallTransformation.RequireTypeArguments) != 0
  905. && !IsPossibleExtensionMethodCallOnNull(method, argumentList.Arguments))
  906. {
  907. // The ambiguity resolution below only adds type arguments as last resort measure, however there are
  908. // methods, such as Enumerable.OfType<TResult>(IEnumerable input) that always require type arguments,
  909. // as those cannot be inferred from the parameters, which leads to bloated expressions full of extra casts
  910. // that are no longer required once we add the type arguments.
  911. // We lend overload resolution a hand by detecting such cases beforehand and requiring type arguments,
  912. // if necessary.
  913. if (!CanInferTypeArgumentsFromArguments(method, argumentList, expressionBuilder.typeInference))
  914. {
  915. requireTypeArguments = true;
  916. typeArguments = method.TypeArguments.ToArray();
  917. appliedRequireTypeArgumentsShortcut = true;
  918. }
  919. else
  920. {
  921. requireTypeArguments = false;
  922. typeArguments = Empty<IType>.Array;
  923. }
  924. }
  925. else
  926. {
  927. requireTypeArguments = false;
  928. typeArguments = Empty<IType>.Array;
  929. }
  930. bool targetCasted = false;
  931. bool argumentsCasted = false;
  932. bool originalRequireTarget = requireTarget;
  933. bool skipTargetCast = method.Accessibility <= Accessibility.Protected && expressionBuilder.IsBaseTypeOfCurrentType(method.DeclaringTypeDefinition);
  934. OverloadResolutionErrors errors;
  935. while ((errors = IsUnambiguousCall(expectedTargetDetails, method, targetResolveResult, typeArguments,
  936. argumentList.GetArgumentResolveResults().ToArray(), argumentList.ArgumentNames, argumentList.FirstOptionalArgumentIndex, out foundMethod,
  937. out var bestCandidateIsExpandedForm)) != OverloadResolutionErrors.None || bestCandidateIsExpandedForm != argumentList.IsExpandedForm)
  938. {
  939. switch (errors)
  940. {
  941. case OverloadResolutionErrors.OutVarTypeMismatch:
  942. Debug.Assert(argumentList.UseImplicitlyTypedOut);
  943. argumentList.UseImplicitlyTypedOut = false;
  944. continue;
  945. case OverloadResolutionErrors.TypeInferenceFailed:
  946. if ((allowedTransforms & CallTransformation.RequireTypeArguments) != 0)
  947. {
  948. goto case OverloadResolutionErrors.WrongNumberOfTypeArguments;
  949. }
  950. goto default;
  951. case OverloadResolutionErrors.WrongNumberOfTypeArguments:
  952. Debug.Assert((allowedTransforms & CallTransformation.RequireTypeArguments) != 0);
  953. if (requireTypeArguments)
  954. goto default;
  955. requireTypeArguments = true;
  956. typeArguments = method.TypeArguments.ToArray();
  957. continue;
  958. case OverloadResolutionErrors.MissingArgumentForRequiredParameter:
  959. if (argumentList.FirstOptionalArgumentIndex == -1)
  960. goto default;
  961. argumentList.FirstOptionalArgumentIndex = -1;
  962. continue;
  963. default:
  964. // TODO : implement some more intelligent algorithm that decides which of these fixes (cast args, add target, cast target, add type args)
  965. // 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.
  966. if (argumentList.FirstOptionalArgumentIndex >= 0)
  967. {
  968. argumentList.FirstOptionalArgumentIndex = -1;
  969. }
  970. else if (!argumentsCasted)
  971. {
  972. // If we added type arguments beforehand, but that didn't make the code any better,
  973. // undo that decision and add casts first.
  974. if (appliedRequireTypeArgumentsShortcut)
  975. {
  976. requireTypeArguments = false;
  977. typeArguments = Empty<IType>.Array;
  978. appliedRequireTypeArgumentsShortcut = false;
  979. }
  980. argumentsCasted = true;
  981. argumentList.UseImplicitlyTypedOut = false;
  982. CastArguments(argumentList.Arguments, argumentList.ExpectedParameters);
  983. }
  984. else if ((allowedTransforms & CallTransformation.RequireTarget) != 0 && !requireTarget)
  985. {
  986. requireTarget = true;
  987. targetResolveResult = target.ResolveResult;
  988. }
  989. else if ((allowedTransforms & CallTransformation.RequireTarget) != 0 && !targetCasted)
  990. {
  991. if (skipTargetCast && requireTarget != originalRequireTarget)
  992. {
  993. requireTarget = originalRequireTarget;
  994. if (!originalRequireTarget)
  995. targetResolveResult = null;
  996. allowedTransforms &= ~CallTransformation.RequireTarget;
  997. }
  998. else
  999. {
  1000. targetCasted = true;
  1001. target = target.ConvertTo(method.DeclaringType, expressionBuilder);
  1002. targetResolveResult = target.ResolveResult;
  1003. }
  1004. }
  1005. else if ((allowedTransforms & CallTransformation.RequireTypeArguments) != 0 && !requireTypeArguments)
  1006. {
  1007. requireTypeArguments = true;
  1008. typeArguments = method.TypeArguments.ToArray();
  1009. }
  1010. else
  1011. {
  1012. break;
  1013. }
  1014. continue;
  1015. }
  1016. // We've given up.
  1017. foundMethod = method;
  1018. break;
  1019. }
  1020. if ((allowedTransforms & CallTransformation.RequireTarget) != 0 && requireTarget)
  1021. transform |= CallTransformation.RequireTarget;
  1022. if ((allowedTransforms & CallTransformation.RequireTypeArguments) != 0 && requireTypeArguments)
  1023. transform |= CallTransformation.RequireTypeArguments;
  1024. if (argumentList.FirstOptionalArgumentIndex < 0)
  1025. transform |= CallTransformation.NoOptionalArgumentAllowed;
  1026. return transform;
  1027. }
  1028. private bool IsPossibleExtensionMethodCallOnNull(IMethod method, IList<TranslatedExpression> arguments)
  1029. {
  1030. return method.IsExtensionMethod && arguments.Count > 0 && arguments[0].Expression is NullReferenceExpression;
  1031. }
  1032. static bool CanInferTypeArgumentsFromArguments(IMethod method, ArgumentList argumentList, TypeInference typeInference)
  1033. {
  1034. if (method.TypeParameters.Count == 0)
  1035. return true;
  1036. // always use unspecialized member, otherwise type inference fails
  1037. method = (IMethod)method.MemberDefinition;
  1038. IReadOnlyList<IType> paramTypesInArgumentOrder;
  1039. if (argumentList.ArgumentToParameterMap == null)
  1040. paramTypesInArgumentOrder = method.Parameters.SelectReadOnlyArray(p => p.Type);
  1041. else
  1042. paramTypesInArgumentOrder = argumentList.ArgumentToParameterMap
  1043. .SelectReadOnlyArray(
  1044. index => index >= 0 ? method.Parameters[index].Type : SpecialType.UnknownType
  1045. );
  1046. typeInference.InferTypeArguments(method.TypeParameters,
  1047. argumentList.Arguments.SelectReadOnlyArray(a => a.ResolveResult), paramTypesInArgumentOrder,
  1048. out bool success);
  1049. return success;
  1050. }
  1051. private void CastArguments(IList<TranslatedExpression> arguments, IList<IParameter> expectedParameters)
  1052. {
  1053. for (int i = 0; i < arguments.Count; i++)
  1054. {
  1055. if (settings.AnonymousTypes && expectedParameters[i].Type.ContainsAnonymousType())
  1056. {
  1057. if (arguments[i].Expression is LambdaExpression lambda)
  1058. {
  1059. ModifyReturnTypeOfLambda(lambda);
  1060. }
  1061. }
  1062. else
  1063. {
  1064. IType parameterType;
  1065. if (expectedParameters[i].Type.Kind == TypeKind.Dynamic)
  1066. {
  1067. parameterType = expressionBuilder.compilation.FindType(KnownTypeCode.Object);
  1068. }
  1069. else
  1070. {
  1071. parameterType = expectedParameters[i].Type;
  1072. }
  1073. arguments[i] = arguments[i].ConvertTo(parameterType, expressionBuilder, allowImplicitConversion: false);
  1074. }
  1075. }
  1076. }
  1077. static bool IsNullConditional(Expression expr)
  1078. {
  1079. return expr is UnaryOperatorExpression uoe && uoe.Operator == UnaryOperatorType.NullConditional;
  1080. }
  1081. private void ModifyReturnTypeOfLambda(LambdaExpression lambda)
  1082. {
  1083. var resolveResult = (DecompiledLambdaResolveResult)lambda.GetResolveResult();
  1084. if (lambda.Body is Expression exprBody)
  1085. lambda.Body = new TranslatedExpression(exprBody.Detach()).ConvertTo(resolveResult.ReturnType, expressionBuilder);
  1086. else
  1087. ModifyReturnStatementInsideLambda(resolveResult.ReturnType, lambda);
  1088. resolveResult.InferredReturnType = resolveResult.ReturnType;
  1089. }
  1090. private void ModifyReturnStatementInsideLambda(IType returnType, AstNode parent)
  1091. {
  1092. foreach (var child in parent.Children)
  1093. {
  1094. if (child is LambdaExpression || child is AnonymousMethodExpression)
  1095. continue;
  1096. if (child is ReturnStatement ret)
  1097. {
  1098. ret.Expression = new TranslatedExpression(ret.Expression.Detach()).ConvertTo(returnType, expressionBuilder);
  1099. continue;
  1100. }
  1101. ModifyReturnStatementInsideLambda(returnType, child);
  1102. }
  1103. }
  1104. private bool IsDelegateEqualityComparison(IMethod method, IList<TranslatedExpression> arguments)
  1105. {
  1106. // Comparison on a delegate type is a C# builtin operator
  1107. // that compiles down to a Delegate.op_Equality call.
  1108. // We handle this as a special case to avoid inserting a cast to System.Delegate.
  1109. return method.IsOperator
  1110. && method.DeclaringType.IsKnownType(KnownTypeCode.Delegate)
  1111. && (method.Name == "op_Equality" || method.Name == "op_Inequality")
  1112. && arguments.Count == 2
  1113. && arguments[0].Type.Kind == TypeKind.Delegate
  1114. && arguments[1].Type.Equals(arguments[0].Type);
  1115. }
  1116. private Expression HandleDelegateEqualityComparison(IMethod method, IList<TranslatedExpression> arguments)
  1117. {
  1118. return new BinaryOperatorExpression(
  1119. arguments[0],
  1120. method.Name == "op_Equality" ? BinaryOperatorType.Equality : BinaryOperatorType.InEquality,
  1121. arguments[1]
  1122. );
  1123. }
  1124. private ExpressionWithResolveResult HandleImplicitConversion(IMethod method, TranslatedExpression argument)
  1125. {
  1126. var conversions = CSharpConversions.Get(expressionBuilder.compilation);
  1127. IType targetType = method.ReturnType;
  1128. var conv = conversions.ImplicitConversion(argument.Type, targetType);
  1129. if (!(conv.IsUserDefined && conv.IsValid && conv.Method.Equals(method)))
  1130. {
  1131. // implicit conversion to targetType isn't directly possible, so first insert a cast to the argument type
  1132. argument = argument.ConvertTo(method.Parameters[0].Type, expressionBuilder);
  1133. conv = conversions.ImplicitConversion(argument.Type, targetType);
  1134. }
  1135. if (argument.Expression is DirectionExpression { FieldDirection: FieldDirection.In, Expression: var lvalueExpr })
  1136. {
  1137. // `(TargetType)(in arg)` is invalid syntax.
  1138. // Also, `f(in arg)` is invalid when there's an implicit conversion involved.
  1139. argument = argument.UnwrapChild(lvalueExpr);
  1140. }
  1141. return new CastExpression(expressionBuilder.ConvertType(targetType), argument.Expression)
  1142. .WithRR(new ConversionResolveResult(targetType, argument.ResolveResult, conv));
  1143. }
  1144. OverloadResolutionErrors IsUnambiguousCall(ExpectedTargetDetails expectedTargetDetails, IMethod method,
  1145. ResolveResult target, IType[] typeArguments, ResolveResult[] arguments,
  1146. string[] argumentNames, int firstOptionalArgumentIndex,
  1147. out IParameterizedMember foundMember, out bool bestCandidateIsExpandedForm)
  1148. {
  1149. foundMember = null;
  1150. bestCandidateIsExpandedForm = false;
  1151. var currentTypeDefinition = resolver.CurrentTypeDefinition;
  1152. var lookup = new MemberLookup(currentTypeDefinition, currentTypeDefinition.ParentModule);
  1153. Log.WriteLine("IsUnambiguousCall: Performing overload resolution for " + method);
  1154. Log.WriteCollection(" Arguments: ", arguments);
  1155. argumentNames = firstOptionalArgumentIndex < 0 || argumentNames == null
  1156. ? argumentNames
  1157. : argumentNames.Take(firstOptionalArgumentIndex).ToArray();
  1158. var or = new OverloadResolution(resolver.Compilation,
  1159. arguments, argumentNames, typeArguments,
  1160. conversions: expressionBuilder.resolver.conversions);
  1161. if (expectedTargetDetails.CallOpCode == OpCode.NewObj)
  1162. {
  1163. foreach (IMethod ctor in method.DeclaringType.GetConstructors())
  1164. {
  1165. bool allowProtectedAccess =
  1166. resolver.CurrentTypeDefinition == method.DeclaringTypeDefinition;
  1167. if (lookup.IsAccessible(ctor, allowProtectedAccess))
  1168. {
  1169. or.AddCandidate(ctor);
  1170. }
  1171. }
  1172. }
  1173. else if (method.IsOperator)
  1174. {
  1175. IEnumerable<IParameterizedMember> operatorCandidates;
  1176. if (arguments.Length == 1)
  1177. {
  1178. IType argType = NullableType.GetUnderlyingType(arguments[0].Type);
  1179. operatorCandidates = resolver.GetUserDefinedOperatorCandidates(argType, method.Name);
  1180. if (method.Name == "op_Explicit")
  1181. {
  1182. // For casts, also consider candidates from the target type we are casting to.
  1183. var hashSet = new HashSet<IParameterizedMember>(operatorCandidates);
  1184. IType targetType = NullableType.GetUnderlyingType(method.ReturnType);
  1185. hashSet.UnionWith(
  1186. resolver.GetUserDefinedOperatorCandidates(targetType, method.Name)
  1187. );
  1188. operatorCandidates = hashSet;
  1189. }
  1190. }
  1191. else if (arguments.Length == 2)
  1192. {
  1193. IType lhsType = NullableType.GetUnderlyingType(arguments[0].Type);
  1194. IType rhsType = NullableType.GetUnderlyingType(arguments[1].Type);
  1195. var hashSet = new HashSet<IParameterizedMember>();
  1196. hashSet.UnionWith(resolver.GetUserDefinedOperatorCandidates(lhsType, method.Name));
  1197. hashSet.UnionWith(resolver.GetUserDefinedOperatorCandidates(rhsType, method.Name));
  1198. operatorCandidates = hashSet;
  1199. }
  1200. else
  1201. {
  1202. operatorCandidates = EmptyList<IParameterizedMember>.Instance;
  1203. }
  1204. foreach (var m in operatorCandidates)
  1205. {
  1206. or.AddCandidate(m);
  1207. }
  1208. }
  1209. else if (target == null)
  1210. {
  1211. var result = resolver.ResolveSimpleName(method.Name, typeArguments, isInvocationTarget: true)
  1212. as MethodGroupResolveResult;
  1213. if (result == null)
  1214. return OverloadResolutionErrors.AmbiguousMatch;
  1215. or.AddMethodLists(result.MethodsGroupedByDeclaringType.ToArray());
  1216. }
  1217. else
  1218. {
  1219. var result = lookup.Lookup(target, method.Name, typeArguments, isInvocation: true) as MethodGroupResolveResult;
  1220. if (result == null)
  1221. return OverloadResolutionErrors.AmbiguousMatch;
  1222. or.AddMethodLists(result.MethodsGroupedByDeclaringType.ToArray());
  1223. }
  1224. bestCandidateIsExpandedForm = or.BestCandidateIsExpandedForm;
  1225. if (or.BestCandidateErrors != OverloadResolutionErrors.None)
  1226. return or.BestCandidateErrors;
  1227. if (or.IsAmbiguous)
  1228. return OverloadResolutionErrors.AmbiguousMatch;
  1229. foundMember = or.GetBestCandidateWithSubstitutedTypeArguments();
  1230. if (!IsAppropriateCallTarget(expectedTargetDetails, method, foundMember))
  1231. return OverloadResolutionErrors.AmbiguousMatch;
  1232. var map = or.GetArgumentToParameterMap();
  1233. for (int i = 0; i < arguments.Length; i++)
  1234. {
  1235. ResolveResult arg = arguments[i];
  1236. int parameterIndex = map[i];
  1237. if (arg is OutVarResolveResult rr && parameterIndex >= 0)
  1238. {
  1239. var param = foundMember.Parameters[parameterIndex];
  1240. var paramType = param.Type.UnwrapByRef();
  1241. if (!paramType.Equals(rr.OriginalVariableType))
  1242. return OverloadResolutionErrors.OutVarTypeMismatch;
  1243. }
  1244. }
  1245. return OverloadResolutionErrors.None;
  1246. }
  1247. bool IsUnambiguousAccess(ExpectedTargetDetails expectedTargetDetails, ResolveResult target, IMethod method,
  1248. IList<TranslatedExpression> arguments, string[] argumentNames, out IMember foundMember)
  1249. {
  1250. Log.WriteLine("IsUnambiguousAccess: Performing overload resolution for " + method);
  1251. Log.WriteCollection(" Arguments: ", arguments.Select(a => a.ResolveResult));
  1252. foundMember = null;
  1253. if (target == null)
  1254. {
  1255. var result = resolver.ResolveSimpleName(method.AccessorOwner.Name,
  1256. EmptyList<IType>.Instance,
  1257. isInvocationTarget: false) as MemberResolveResult;
  1258. if (result == null || result.IsError)
  1259. return false;
  1260. foundMember = result.Member;
  1261. }
  1262. else
  1263. {
  1264. var lookup = new MemberLookup(resolver.CurrentTypeDefinition, resolver.CurrentTypeDefinition.ParentModule);
  1265. if (method.AccessorOwner.SymbolKind == SymbolKind.Indexer)
  1266. {
  1267. var or = new OverloadResolution(resolver.Compilation,
  1268. arguments.SelectArray(a => a.ResolveResult),
  1269. argumentNames: argumentNames,
  1270. typeArguments: Empty<IType>.Array,
  1271. conversions: expressionBuilder.resolver.conversions);
  1272. or.AddMethodLists(lookup.LookupIndexers(target));
  1273. if (or.BestCandidateErrors != OverloadResolutionErrors.None)
  1274. return false;
  1275. if (or.IsAmbiguous)
  1276. return false;
  1277. foundMember = or.GetBestCandidateWithSubstitutedTypeArguments();
  1278. }
  1279. else
  1280. {
  1281. var result = lookup.Lookup(target,
  1282. method.AccessorOwner.Name,
  1283. EmptyList<IType>.Instance,
  1284. isInvocation: false) as MemberResolveResult;
  1285. if (result == null || result.IsError)
  1286. return false;
  1287. foundMember = result.Member;
  1288. }
  1289. }
  1290. return foundMember != null && IsAppropriateCallTarget(expectedTargetDetails, method.AccessorOwner, foundMember);
  1291. }
  1292. ExpressionWithResolveResult HandleAccessorCall(ExpectedTargetDetails expectedTargetDetails, IMethod method,
  1293. TranslatedExpression target, List<TranslatedExpression> arguments, string[] argumentNames)
  1294. {
  1295. bool requireTarget;
  1296. if (settings.AlwaysQualifyMemberReferences || method.AccessorOwner.SymbolKind == SymbolKind.Indexer || expressionBuilder.HidesVariableWithName(method.AccessorOwner.Name))
  1297. requireTarget = true;
  1298. else if (method.IsStatic)
  1299. requireTarget = !expressionBuilder.IsCurrentOrContainingType(method.DeclaringTypeDefinition);
  1300. else
  1301. requireTarget = !(target.Expression is ThisReferenceExpression);
  1302. bool targetCasted = false;
  1303. bool isSetter = method.ReturnType.IsKnownType(KnownTypeCode.Void);
  1304. bool argumentsCasted = (isSetter && method.Parameters.Count == 1) || (!isSetter && method.Parameters.Count == 0);
  1305. var targetResolveResult = requireTarget ? target.ResolveResult : null;
  1306. TranslatedExpression value = default(TranslatedExpression);
  1307. if (isSetter)
  1308. {
  1309. value = arguments.Last();
  1310. arguments.Remove(value);
  1311. }
  1312. IMember foundMember;
  1313. while (!IsUnambiguousAccess(expectedTargetDetails, targetResolveResult, method, arguments, argumentNames, out foundMember))
  1314. {
  1315. if (!argumentsCasted)
  1316. {
  1317. argumentsCasted = true;
  1318. CastArguments(arguments, method.Parameters.ToList());
  1319. }
  1320. else if (!requireTarget)
  1321. {
  1322. requireTarget = true;
  1323. targetResolveResult = target.ResolveResult;
  1324. }
  1325. else if (!targetCasted)
  1326. {
  1327. targetCasted = true;
  1328. target = target.ConvertTo(method.AccessorOwner.DeclaringType, expressionBuilder);
  1329. targetResolveResult = target.ResolveResult;
  1330. }
  1331. else
  1332. {
  1333. foundMember = method.AccessorOwner;
  1334. break;
  1335. }
  1336. }
  1337. var rr = new MemberResolveResult(target.ResolveResult, foundMember);
  1338. if (isSetter)
  1339. {
  1340. TranslatedExpression expr;
  1341. if (arguments.Count != 0)
  1342. {
  1343. expr = new IndexerExpression(target.ResolveResult is InitializedObjectResolveResult ? null : target.Expression, arguments.Select(a => a.Expression))
  1344. .WithoutILInstruction().WithRR(rr);
  1345. }
  1346. else if (requireTarget)
  1347. {
  1348. expr = new MemberReferenceExpression(target.Expression, method.AccessorOwner.Name)
  1349. .WithoutILInstruction().WithRR(rr);
  1350. }
  1351. else
  1352. {
  1353. expr = new IdentifierExpression(method.AccessorOwner.Name)
  1354. .WithoutILInstruction().WithRR(rr);
  1355. }
  1356. var op = AssignmentOperatorType.Assign;
  1357. if (method.AccessorOwner is IEvent parentEvent)
  1358. {
  1359. if (method.Equals(parentEvent.AddAccessor))
  1360. {
  1361. op = AssignmentOperatorType.Add;
  1362. }
  1363. if (method.Equals(parentEvent.RemoveAccessor))
  1364. {
  1365. op = AssignmentOperatorType.Subtract;
  1366. }
  1367. }
  1368. return new AssignmentExpression(expr, op, value.Expression).WithRR(new TypeResolveResult(method.AccessorOwner.ReturnType));
  1369. }
  1370. else
  1371. {
  1372. if (arguments.Count != 0)
  1373. {
  1374. return new IndexerExpression(target.Expression, arguments.Select(a => a.Expression))
  1375. .WithoutILInstruction().WithRR(rr);
  1376. }
  1377. else if (requireTarget)
  1378. {
  1379. return new MemberReferenceExpression(target.Expression, method.AccessorOwner.Name)
  1380. .WithoutILInstruction().WithRR(rr);
  1381. }
  1382. else
  1383. {
  1384. return new IdentifierExpression(method.AccessorOwner.Name)
  1385. .WithoutILInstruction().WithRR(rr);
  1386. }
  1387. }
  1388. }
  1389. bool IsAppropriateCallTarget(ExpectedTargetDetails expectedTargetDetails, IMember expectedTarget, IMember actualTarget)
  1390. {
  1391. if (expectedTarget.Equals(actualTarget, NormalizeTypeVisitor.TypeErasure))
  1392. return true;
  1393. if (expectedTargetDetails.CallOpCode == OpCode.CallVirt && actualTarget.IsOverride)
  1394. {
  1395. if (expectedTargetDetails.NeedsBoxingConversion && actualTarget.DeclaringType.IsReferenceType != true)
  1396. return false;
  1397. foreach (var possibleTarget in InheritanceHelper.GetBaseMembers(actualTarget, false))
  1398. {
  1399. if (expectedTarget.Equals(possibleTarget, NormalizeTypeVisitor.TypeErasure))
  1400. return true;
  1401. if (!possibleTarget.IsOverride)
  1402. break;
  1403. }
  1404. }
  1405. return false;
  1406. }
  1407. ExpressionWithResolveResult HandleConstructorCall(ExpectedTargetDetails expectedTargetDetails, ResolveResult target, IMethod method, ArgumentList argumentList)
  1408. {
  1409. if (settings.AnonymousTypes && method.DeclaringType.IsAnonymousType())
  1410. {
  1411. Debug.Assert(argumentList.ArgumentToParameterMap == null && argumentList.ArgumentNames == null && argumentList.FirstOptionalArgumentIndex < 0);
  1412. var atce = new AnonymousTypeCreateExpression();
  1413. if (argumentList.CanInferAnonymousTypePropertyNamesFromArguments())
  1414. {
  1415. atce.Initializers.AddRange(argumentList.GetArgumentExpressions());
  1416. }
  1417. else
  1418. {
  1419. for (int i = 0; i < argumentList.Length; i++)
  1420. {
  1421. atce.Initializers.Add(
  1422. new NamedExpression {
  1423. Name = argumentList.ExpectedParameters[i].Name,
  1424. Expression = argumentList.Arguments[i].ConvertTo(argumentList.ExpectedParameters[i].Type, expressionBuilder)
  1425. });
  1426. }
  1427. }
  1428. return atce.WithRR(new CSharpInvocationResolveResult(
  1429. target, method, argumentList.GetArgumentResolveResults(),
  1430. isExpandedForm: argumentList.IsExpandedForm, argumentToParameterMap: argumentList.ArgumentToParameterMap
  1431. ));
  1432. }
  1433. else
  1434. {
  1435. while (IsUnambiguousCall(expectedTargetDetails, method, null, Empty<IType>.Array,
  1436. argumentList.GetArgumentResolveResults().ToArray(),
  1437. argumentList.ArgumentNames, argumentList.FirstOptionalArgumentIndex, out _,
  1438. out var bestCandidateIsExpandedForm) != OverloadResolutionErrors.None || bestCandidateIsExpandedForm != argumentList.IsExpandedForm)
  1439. {
  1440. if (argumentList.FirstOptionalArgumentIndex >= 0)
  1441. {
  1442. argumentList.FirstOptionalArgumentIndex = -1;
  1443. continue;
  1444. }
  1445. CastArguments(argumentList.Arguments, argumentList.ExpectedParameters);
  1446. break; // make sure that we don't not end up in an infinite loop
  1447. }
  1448. return new ObjectCreateExpression(
  1449. expressionBuilder.ConvertType(method.DeclaringType),
  1450. argumentList.GetArgumentExpressions()
  1451. ).WithRR(new CSharpInvocationResolveResult(
  1452. target, method, argumentList.GetArgumentResolveResults().ToArray(),
  1453. isExpandedForm: argumentList.IsExpandedForm, argumentToParameterMap: argumentList.ArgumentToParameterMap
  1454. ));
  1455. }
  1456. }
  1457. TranslatedExpression HandleDelegateConstruction(CallInstruction inst)
  1458. {
  1459. ILInstruction thisArg = inst.Arguments[0];
  1460. ILInstruction func = inst.Arguments[1];
  1461. IMethod method;
  1462. ExpectedTargetDetails expectedTargetDetails = default;
  1463. switch (func.OpCode)
  1464. {
  1465. case OpCode.LdFtn:
  1466. method = ((LdFtn)func).Method;
  1467. expectedTargetDetails.CallOpCode = OpCode.Call;
  1468. break;
  1469. case OpCode.LdVirtFtn:
  1470. method = ((LdVirtFtn)func).Method;
  1471. expectedTargetDetails.CallOpCode = OpCode.CallVirt;
  1472. break;
  1473. default:
  1474. throw new ArgumentException($"Unknown instruction type: {func.OpCode}");
  1475. }
  1476. if (CanUseDelegateConstruction(method, thisArg, inst.Method.DeclaringType.GetDelegateInvokeMethod()))
  1477. {
  1478. return HandleDelegateConstruction(inst.Method.DeclaringType, method, expectedTargetDetails, thisArg, inst);
  1479. }
  1480. else
  1481. {
  1482. var argumentList = BuildArgumentList(expectedTargetDetails, null, inst.Method,
  1483. 0, inst.Arguments, null);
  1484. return HandleConstructorCall(new ExpectedTargetDetails { CallOpCode = OpCode.NewObj }, null, inst.Method, argumentList).WithILInstruction(inst);
  1485. }
  1486. }
  1487. private bool CanUseDelegateConstruction(IMethod targetMethod, ILInstruction thisArg, IMethod invokeMethod)
  1488. {
  1489. // Accessors cannot be directly referenced as method group in C#
  1490. // see https://github.com/icsharpcode/ILSpy/issues/1741#issuecomment-540179101
  1491. if (targetMethod.IsAccessor)
  1492. return false;
  1493. if (targetMethod.IsStatic)
  1494. {
  1495. // If the invoke method is known, we can compare the parameter counts to figure out whether the
  1496. // delegate is static or binds the first argument
  1497. if (invokeMethod != null)
  1498. {
  1499. if (invokeMethod.Parameters.Count == targetMethod.Parameters.Count)
  1500. {
  1501. return thisArg.MatchLdNull();
  1502. }
  1503. else if (targetMethod.IsExtensionMethod && invokeMethod.Parameters.Count == targetMethod.Parameters.Count - 1)
  1504. {
  1505. return true;
  1506. }
  1507. else
  1508. {
  1509. return false;
  1510. }
  1511. }
  1512. else
  1513. {
  1514. // delegate type unknown:
  1515. return thisArg.MatchLdNull() || targetMethod.IsExtensionMethod;
  1516. }
  1517. }
  1518. else
  1519. {
  1520. // targetMethod is instance method
  1521. if (invokeMethod != null && invokeMethod.Parameters.Count != targetMethod.Parameters.Count)
  1522. return false;
  1523. return true;
  1524. }
  1525. }
  1526. internal TranslatedExpression Build(LdVirtDelegate inst)
  1527. {
  1528. return HandleDelegateConstruction(inst.Type, inst.Method, new ExpectedTargetDetails { CallOpCode = OpCode.CallVirt }, inst.Argument, inst);
  1529. }
  1530. internal ExpressionWithResolveResult BuildMethodReference(IMethod method, bool isVirtual)
  1531. {
  1532. var expr = BuildDelegateReference(method, invokeMethod: null, new ExpectedTargetDetails { CallOpCode = isVirtual ? OpCode.CallVirt : OpCode.Call }, thisArg: null);
  1533. expr.Expression.RemoveAnnotations<ResolveResult>();
  1534. return expr.Expression.WithRR(new MemberResolveResult(null, method));
  1535. }
  1536. ExpressionWithResolveResult BuildDelegateReference(IMethod method, IMethod invokeMethod, ExpectedTargetDetails expectedTargetDetails, ILInstruction thisArg)
  1537. {
  1538. ExpressionBuilder expressionBuilder = this.expressionBuilder;
  1539. ExpressionWithResolveResult targetExpression;
  1540. (TranslatedExpression target, bool addTypeArguments, string methodName, ResolveResult result) = DisambiguateDelegateReference(method, invokeMethod, expectedTargetDetails, thisArg);
  1541. if (target.Expression != null)
  1542. {
  1543. var mre = new MemberReferenceExpression(target, methodName);
  1544. if (addTypeArguments)
  1545. {
  1546. mre.TypeArguments.AddRange(method.TypeArguments.Select(expressionBuilder.ConvertType));
  1547. }
  1548. targetExpression = mre.WithRR(result);
  1549. }
  1550. else
  1551. {
  1552. var ide = new IdentifierExpression(methodName);
  1553. if (addTypeArguments)
  1554. {
  1555. ide.TypeArguments.AddRange(method.TypeArguments.Select(expressionBuilder.ConvertType));
  1556. }
  1557. targetExpression = ide.WithRR(result);
  1558. }
  1559. return targetExpression;
  1560. }
  1561. (TranslatedExpression target, bool addTypeArguments, string methodName, ResolveResult result) DisambiguateDelegateReference(IMethod method, IMethod invokeMethod, ExpectedTargetDetails expectedTargetDetails, ILInstruction thisArg)
  1562. {
  1563. if (method.IsLocalFunction)
  1564. {
  1565. ILFunction localFunction = expressionBuilder.ResolveLocalFunction(method);
  1566. Debug.Assert(localFunction != null);
  1567. return (default, addTypeArguments: true, localFunction.Name, ToMethodGroup(method, localFunction));
  1568. }
  1569. if (method.IsExtensionMethod && method.Parameters.Count - 1 == invokeMethod?.Parameters.Count)
  1570. {
  1571. IType targetType = method.Parameters[0].Type;
  1572. if (targetType.Kind == TypeKind.ByReference && thisArg is Box thisArgBox)
  1573. {
  1574. targetType = ((ByReferenceType)targetType).ElementType;
  1575. thisArg = thisArgBox.Argument;
  1576. }
  1577. TranslatedExpression target = expressionBuilder.Translate(thisArg, targetType);
  1578. var currentTarget = target;
  1579. bool targetCasted = false;
  1580. bool addTypeArguments = false;
  1581. // Initial inputs for IsUnambiguousMethodReference:
  1582. ResolveResult targetResolveResult = target.ResolveResult;
  1583. IReadOnlyList<IType> typeArguments = EmptyList<IType>.Instance;
  1584. if (thisArg.MatchLdNull())
  1585. {
  1586. targetCasted = true;
  1587. currentTarget = currentTarget.ConvertTo(targetType, expressionBuilder);
  1588. targetResolveResult = currentTarget.ResolveResult;
  1589. }
  1590. // Find somewhat minimal solution:
  1591. ResolveResult result;
  1592. while (!IsUnambiguousMethodReference(expectedTargetDetails, method, targetResolveResult, typeArguments, true, out result))
  1593. {
  1594. if (!targetCasted)
  1595. {
  1596. // try casting target
  1597. targetCasted = true;
  1598. currentTarget = currentTarget.ConvertTo(targetType, expressionBuilder);
  1599. targetResolveResult = currentTarget.ResolveResult;
  1600. continue;
  1601. }
  1602. if (!addTypeArguments)
  1603. {
  1604. // try adding type arguments
  1605. addTypeArguments = true;
  1606. typeArguments = method.TypeArguments;
  1607. continue;
  1608. }
  1609. break;
  1610. }
  1611. return (currentTarget, addTypeArguments, method.Name, result);
  1612. }
  1613. else
  1614. {
  1615. // Prepare call target
  1616. IType targetType = method.DeclaringType;
  1617. if (targetType.IsReferenceType == false && thisArg is Box thisArgBox)
  1618. {
  1619. // Normal struct instance method calls (which TranslateTarget is meant for) expect a 'ref T',
  1620. // but delegate construction uses a 'box T'.
  1621. if (thisArgBox.Argument is LdObj ldobj)
  1622. {
  1623. thisArg = ldobj.Target;
  1624. }
  1625. else
  1626. {
  1627. thisArg = new AddressOf(thisArgBox.Argument, thisArgBox.Type);
  1628. }
  1629. }
  1630. TranslatedExpression target = expressionBuilder.TranslateTarget(thisArg,
  1631. nonVirtualInvocation: expectedTargetDetails.CallOpCode == OpCode.Call,
  1632. memberStatic: method.IsStatic,
  1633. memberDeclaringType: method.DeclaringType);
  1634. // check if target is required
  1635. bool requireTarget = expressionBuilder.HidesVariableWithName(method.Name)
  1636. || (method.IsStatic ? !expressionBuilder.IsCurrentOrContainingType(method.DeclaringTypeDefinition) : !(target.Expression is ThisReferenceExpression));
  1637. // Try to find minimal expression
  1638. // If target is required, include it from the start
  1639. bool targetAdded = requireTarget;
  1640. TranslatedExpression currentTarget = targetAdded ? target : default;
  1641. // Remember other decisions:
  1642. bool targetCasted = false;
  1643. bool addTypeArguments = false;
  1644. // Initial inputs for IsUnambiguousMethodReference:
  1645. ResolveResult targetResolveResult = targetAdded ? target.ResolveResult : null;
  1646. IReadOnlyList<IType> typeArguments = EmptyList<IType>.Instance;
  1647. // Find somewhat minimal solution:
  1648. ResolveResult result;
  1649. while (!IsUnambiguousMethodReference(expectedTargetDetails, method, targetResolveResult, typeArguments, false, out result))
  1650. {
  1651. if (!addTypeArguments)
  1652. {
  1653. // try adding type arguments
  1654. addTypeArguments = true;
  1655. typeArguments = method.TypeArguments;
  1656. continue;
  1657. }
  1658. if (!targetAdded)
  1659. {
  1660. // try adding target
  1661. targetAdded = true;
  1662. currentTarget = target;
  1663. targetResolveResult = target.ResolveResult;
  1664. continue;
  1665. }
  1666. if (!targetCasted)
  1667. {
  1668. // try casting target
  1669. targetCasted = true;
  1670. currentTarget = currentTarget.ConvertTo(targetType, expressionBuilder);
  1671. targetResolveResult = currentTarget.ResolveResult;
  1672. continue;
  1673. }
  1674. break;
  1675. }
  1676. return (currentTarget, addTypeArguments, method.Name, result);
  1677. }
  1678. }
  1679. TranslatedExpression HandleDelegateConstruction(IType delegateType, IMethod method, ExpectedTargetDetails expectedTargetDetails, ILInstruction thisArg, ILInstruction inst)
  1680. {
  1681. var invokeMethod = delegateType.GetDelegateInvokeMethod();
  1682. var targetExpression = BuildDelegateReference(method, invokeMethod, expectedTargetDetails, thisArg);
  1683. var oce = new ObjectCreateExpression(expressionBuilder.ConvertType(delegateType), targetExpression)
  1684. .WithILInstruction(inst)
  1685. .WithRR(new ConversionResolveResult(
  1686. delegateType,
  1687. targetExpression.ResolveResult,
  1688. Conversion.MethodGroupConversion(method, expectedTargetDetails.CallOpCode == OpCode.CallVirt, false)));
  1689. return oce;
  1690. }
  1691. bool IsUnambiguousMethodReference(ExpectedTargetDetails expectedTargetDetails, IMethod method, ResolveResult target, IReadOnlyList<IType> typeArguments, bool isExtensionMethodReference, out ResolveResult result)
  1692. {
  1693. Log.WriteLine("IsUnambiguousMethodReference: Performing overload resolution for " + method);
  1694. var lookup = new MemberLookup(resolver.CurrentTypeDefinition, resolver.CurrentTypeDefinition.ParentModule);
  1695. OverloadResolution or;
  1696. if (isExtensionMethodReference)
  1697. {
  1698. var resolver = this.resolver.WithCurrentUsingScope(this.expressionBuilder.statementBuilder.decompileRun.UsingScope.Resolve(this.resolver.Compilation));
  1699. result = resolver.ResolveMemberAccess(target, method.Name, typeArguments, NameLookupMode.InvocationTarget) as MethodGroupResolveResult;
  1700. if (result == null)
  1701. return false;
  1702. or = ((MethodGroupResolveResult)result).PerformOverloadResolution(resolver.CurrentTypeResolveContext.Compilation,
  1703. method.Parameters.SelectReadOnlyArray(p => new TypeResolveResult(p.Type)),
  1704. argumentNames: null, allowExtensionMethods: true);
  1705. if (or == null || or.IsAmbiguous)
  1706. return false;
  1707. }
  1708. else
  1709. {
  1710. or = new OverloadResolution(resolver.Compilation,
  1711. arguments: method.Parameters.SelectReadOnlyArray(p => new TypeResolveResult(p.Type)), // there are no arguments, use parameter types
  1712. argumentNames: null, // argument names are not possible
  1713. typeArguments.ToArray(),
  1714. conversions: expressionBuilder.resolver.conversions
  1715. );
  1716. if (target == null)
  1717. {
  1718. result = resolver.ResolveSimpleName(method.Name, typeArguments, isInvocationTarget: false);
  1719. if (!(result is MethodGroupResolveResult mgrr))
  1720. return false;
  1721. or.AddMethodLists(mgrr.MethodsGroupedByDeclaringType.ToArray());
  1722. }
  1723. else
  1724. {
  1725. result = lookup.Lookup(target, method.Name, typeArguments, isInvocation: false);
  1726. if (!(result is MethodGroupResolveResult mgrr))
  1727. return false;
  1728. or.AddMethodLists(mgrr.MethodsGroupedByDeclaringType.ToArray());
  1729. }
  1730. }
  1731. var foundMethod = or.GetBestCandidateWithSubstitutedTypeArguments();
  1732. if (!IsAppropriateCallTarget(expectedTargetDetails, method, foundMethod))
  1733. return false;
  1734. return result is MethodGroupResolveResult;
  1735. }
  1736. static MethodGroupResolveResult ToMethodGroup(IMethod method, ILFunction localFunction)
  1737. {
  1738. return new MethodGroupResolveResult(
  1739. null,
  1740. localFunction.Name,
  1741. new[] {
  1742. new MethodListWithDeclaringType(
  1743. method.DeclaringType,
  1744. new IParameterizedMember[] { method }
  1745. )
  1746. }, method.TypeArguments
  1747. );
  1748. }
  1749. internal TranslatedExpression CallWithNamedArgs(Block block)
  1750. {
  1751. Debug.Assert(block.Kind == BlockKind.CallWithNamedArgs);
  1752. var call = (CallInstruction)block.FinalInstruction;
  1753. var arguments = new ILInstruction[call.Arguments.Count];
  1754. var argumentToParameterMap = new int[arguments.Length];
  1755. int firstParamIndex = call.IsInstanceCall ? 1 : 0;
  1756. // Arguments from temporary variables (VariableKind.NamedArgument):
  1757. int pos = 0;
  1758. foreach (StLoc stloc in block.Instructions)
  1759. {
  1760. Debug.Assert(stloc.Variable.LoadInstructions.Single().Parent == call);
  1761. arguments[pos] = stloc.Value;
  1762. argumentToParameterMap[pos] = stloc.Variable.LoadInstructions.Single().ChildIndex - firstParamIndex;
  1763. pos++;
  1764. }
  1765. // Remaining argument:
  1766. foreach (var arg in call.Arguments)
  1767. {
  1768. if (arg.MatchLdLoc(out var v) && v.Kind == VariableKind.NamedArgument)
  1769. {
  1770. continue; // already handled in loop above
  1771. }
  1772. arguments[pos] = arg;
  1773. argumentToParameterMap[pos] = arg.ChildIndex - firstParamIndex;
  1774. pos++;
  1775. }
  1776. Debug.Assert(pos == arguments.Length);
  1777. return Build(call.OpCode, call.Method, arguments, argumentToParameterMap, call.ConstrainedTo)
  1778. .WithILInstruction(call).WithILInstruction(block);
  1779. }
  1780. private bool HandleRangeConstruction(out ExpressionWithResolveResult result, OpCode callOpCode, IMethod method, TranslatedExpression target, ArgumentList argumentList)
  1781. {
  1782. result = default;
  1783. if (argumentList.ArgumentNames != null)
  1784. {
  1785. return false; // range syntax doesn't support named arguments
  1786. }
  1787. if (method.DeclaringType.IsKnownType(KnownTypeCode.Range))
  1788. {
  1789. if (callOpCode == OpCode.NewObj && argumentList.Length == 2)
  1790. {
  1791. result = new BinaryOperatorExpression(argumentList.Arguments[0], BinaryOperatorType.Range, argumentList.Arguments[1])
  1792. .WithRR(new MemberResolveResult(null, method));
  1793. return true;
  1794. }
  1795. else if (callOpCode == OpCode.Call && method.Name == "get_All" && argumentList.Length == 0)
  1796. {
  1797. result = new BinaryOperatorExpression(Expression.Null, BinaryOperatorType.Range, Expression.Null)
  1798. .WithRR(new MemberResolveResult(null, method.AccessorOwner ?? method));
  1799. return true;
  1800. }
  1801. else if (callOpCode == OpCode.Call && method.Name == "StartAt" && argumentList.Length == 1)
  1802. {
  1803. result = new BinaryOperatorExpression(argumentList.Arguments[0], BinaryOperatorType.Range, Expression.Null)
  1804. .WithRR(new MemberResolveResult(null, method));
  1805. return true;
  1806. }
  1807. else if (callOpCode == OpCode.Call && method.Name == "EndAt" && argumentList.Length == 1)
  1808. {
  1809. result = new BinaryOperatorExpression(Expression.Null, BinaryOperatorType.Range, argumentList.Arguments[0])
  1810. .WithRR(new MemberResolveResult(null, method));
  1811. return true;
  1812. }
  1813. }
  1814. else if (callOpCode == OpCode.NewObj && method.DeclaringType.IsKnownType(KnownTypeCode.Index))
  1815. {
  1816. if (argumentList.Length != 2)
  1817. return false;
  1818. if (!(argumentList.Arguments[1].Expression is PrimitiveExpression pe && pe.Value is true))
  1819. return false;
  1820. result = new UnaryOperatorExpression(UnaryOperatorType.IndexFromEnd, argumentList.Arguments[0])
  1821. .WithRR(new MemberResolveResult(null, method));
  1822. return true;
  1823. }
  1824. else if (method is SyntheticRangeIndexAccessor rangeIndexAccessor && rangeIndexAccessor.IsSlicing)
  1825. {
  1826. // For slicing the method is called Slice()/Substring(), but we still need to output indexer notation.
  1827. // So special-case range-based slicing here.
  1828. result = new IndexerExpression(target, argumentList.Arguments.Select(a => a.Expression))
  1829. .WithRR(new MemberResolveResult(target.ResolveResult, method));
  1830. return true;
  1831. }
  1832. return false;
  1833. }
  1834. }
  1835. }