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.

1541 lines
57 KiB

7 years ago
  1. // Copyright (c) 2017 Siegfried Pammer
  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.Diagnostics;
  21. using System.Linq;
  22. using ICSharpCode.Decompiler.CSharp.Resolver;
  23. using ICSharpCode.Decompiler.Semantics;
  24. using ICSharpCode.Decompiler.TypeSystem;
  25. using ICSharpCode.Decompiler.TypeSystem.Implementation;
  26. using ICSharpCode.Decompiler.Util;
  27. namespace ICSharpCode.Decompiler.IL.Transforms
  28. {
  29. /// <summary>
  30. /// Converts LINQ Expression Trees to ILFunctions/ILAst instructions.
  31. ///
  32. /// We build a tree of Func{ILInstruction}s, which are only executed, if the whole transform succeeds.
  33. /// </summary>
  34. public class TransformExpressionTrees : IStatementTransform
  35. {
  36. /// <summary>
  37. /// Returns true if the instruction matches the pattern for Expression.Lambda calls.
  38. /// </summary>
  39. static bool MightBeExpressionTree(ILInstruction inst, ILInstruction stmt)
  40. {
  41. if (!(inst is CallInstruction call
  42. && call.Method.FullNameIs("System.Linq.Expressions.Expression", "Lambda")
  43. && call.Arguments.Count == 2))
  44. return false;
  45. if (!(IsEmptyParameterList(call.Arguments[1]) || (call.Arguments[1] is Block block && block.Kind == BlockKind.ArrayInitializer)))
  46. return false;
  47. //if (!ILInlining.CanUninline(call, stmt))
  48. // return false;
  49. return true;
  50. }
  51. static bool IsEmptyParameterList(ILInstruction inst)
  52. {
  53. if (inst is CallInstruction emptyCall && emptyCall.Method.FullNameIs("System.Array", "Empty") && emptyCall.Arguments.Count == 0)
  54. return true;
  55. if (inst.MatchNewArr(out var type) && type.FullName == "System.Linq.Expressions.ParameterExpression")
  56. return true;
  57. if (inst.MatchNewArr(out type) && type.FullName == "System.Linq.Expressions.Expression")
  58. return true;
  59. return false;
  60. }
  61. bool MatchParameterVariableAssignment(ILInstruction expr, out ILVariable parameterReferenceVar, out IType type, out string name)
  62. {
  63. // stloc(v, call(Expression::Parameter, call(Type::GetTypeFromHandle, ldtoken(...)), ldstr(...)))
  64. type = null;
  65. name = null;
  66. if (!expr.MatchStLoc(out parameterReferenceVar, out var init))
  67. return false;
  68. if (!parameterReferenceVar.IsSingleDefinition)
  69. return false;
  70. if (!(parameterReferenceVar.Kind == VariableKind.Local || parameterReferenceVar.Kind == VariableKind.StackSlot))
  71. return false;
  72. if (parameterReferenceVar.Type == null || parameterReferenceVar.Type.FullName != "System.Linq.Expressions.ParameterExpression")
  73. return false;
  74. if (!(init is CallInstruction initCall && initCall.Arguments.Count == 2))
  75. return false;
  76. if (!(initCall.Method.FullNameIs("System.Linq.Expressions.Expression", "Parameter")))
  77. return false;
  78. CallInstruction typeArg = initCall.Arguments[0] as CallInstruction;
  79. if (typeArg == null || typeArg.Arguments.Count != 1)
  80. return false;
  81. if (!typeArg.Method.FullNameIs("System.Type", "GetTypeFromHandle"))
  82. return false;
  83. return typeArg.Arguments[0].MatchLdTypeToken(out type) && initCall.Arguments[1].MatchLdStr(out name);
  84. }
  85. StatementTransformContext context;
  86. Dictionary<ILVariable, (IType, string)> parameters;
  87. Dictionary<ILVariable, ILVariable> parameterMapping;
  88. List<ILInstruction> instructionsToRemove;
  89. Stack<ILFunction> lambdaStack;
  90. CSharpConversions conversions;
  91. CSharpResolver resolver;
  92. public void Run(Block block, int pos, StatementTransformContext context)
  93. {
  94. if (!context.Settings.ExpressionTrees)
  95. return;
  96. this.context = context;
  97. this.conversions = CSharpConversions.Get(context.TypeSystem);
  98. this.resolver = new CSharpResolver(context.TypeSystem);
  99. this.parameters = new Dictionary<ILVariable, (IType, string)>();
  100. this.parameterMapping = new Dictionary<ILVariable, ILVariable>();
  101. this.instructionsToRemove = new List<ILInstruction>();
  102. this.lambdaStack = new Stack<ILFunction>();
  103. for (int i = pos; i < block.Instructions.Count; i++)
  104. {
  105. if (MatchParameterVariableAssignment(block.Instructions[i], out var v, out var type, out var name))
  106. {
  107. parameters.Add(v, (type, name));
  108. continue;
  109. }
  110. if (TryConvertExpressionTree(block.Instructions[i], block.Instructions[i]))
  111. {
  112. foreach (var inst in instructionsToRemove)
  113. block.Instructions.Remove(inst);
  114. instructionsToRemove.Clear();
  115. }
  116. break;
  117. }
  118. }
  119. bool TryConvertExpressionTree(ILInstruction instruction, ILInstruction statement)
  120. {
  121. if (MightBeExpressionTree(instruction, statement))
  122. {
  123. var (lambda, type) = ConvertLambda((CallInstruction)instruction);
  124. if (lambda != null)
  125. {
  126. context.Step("Convert Expression Tree", instruction);
  127. var newLambda = (ILFunction)lambda();
  128. SetExpressionTreeFlag(newLambda, (CallInstruction)instruction);
  129. instruction.ReplaceWith(newLambda);
  130. return true;
  131. }
  132. return false;
  133. }
  134. if (instruction is Block block && block.Kind == BlockKind.ControlFlow)
  135. return false; // don't look into nested blocks
  136. foreach (var child in instruction.Children)
  137. {
  138. if (TryConvertExpressionTree(child, statement))
  139. return true;
  140. }
  141. return false;
  142. }
  143. /// <summary>
  144. /// Converts a Expression.Lambda call into an ILFunction.
  145. /// If the conversion fails, null is returned.
  146. /// </summary>
  147. (Func<ILInstruction>, IType) ConvertLambda(CallInstruction instruction)
  148. {
  149. if (instruction.Method.Name != "Lambda" || instruction.Arguments.Count != 2 || instruction.Method.ReturnType.FullName != "System.Linq.Expressions.Expression" || instruction.Method.ReturnType.TypeArguments.Count != 1)
  150. return (null, SpecialType.UnknownType);
  151. var parameterList = new List<IParameter>();
  152. var parameterVariablesList = new List<ILVariable>();
  153. if (!ReadParameters(instruction.Arguments[1], parameterList, parameterVariablesList, new SimpleTypeResolveContext(context.Function.Method)))
  154. return (null, SpecialType.UnknownType);
  155. var container = new BlockContainer();
  156. container.AddILRange(instruction);
  157. var functionType = instruction.Method.ReturnType.TypeArguments[0];
  158. var returnType = functionType.GetDelegateInvokeMethod()?.ReturnType ?? SpecialType.UnknownType;
  159. var function = new ILFunction(returnType, parameterList, context.Function.GenericContext, container, ILFunctionKind.ExpressionTree);
  160. function.DelegateType = functionType;
  161. function.Kind = IsExpressionTree(functionType) ? ILFunctionKind.ExpressionTree : ILFunctionKind.Delegate;
  162. function.Variables.AddRange(parameterVariablesList);
  163. function.AddILRange(instruction);
  164. lambdaStack.Push(function);
  165. var (bodyInstruction, type) = ConvertInstruction(instruction.Arguments[0]);
  166. lambdaStack.Pop();
  167. if (bodyInstruction == null)
  168. return (null, SpecialType.UnknownType);
  169. return (BuildFunction, function.DelegateType);
  170. ILFunction BuildFunction()
  171. {
  172. lambdaStack.Push(function);
  173. var convertedBody = bodyInstruction();
  174. lambdaStack.Pop();
  175. container.ExpectedResultType = convertedBody.ResultType;
  176. container.Blocks.Add(new Block() { Instructions = { new Leave(container, convertedBody) } });
  177. // Replace all other usages of the parameter variable
  178. foreach (var mapping in parameterMapping)
  179. {
  180. foreach (var load in mapping.Key.LoadInstructions.ToArray())
  181. {
  182. if (load.IsDescendantOf(instruction))
  183. continue;
  184. load.ReplaceWith(new LdLoc(mapping.Value));
  185. }
  186. }
  187. return function;
  188. }
  189. }
  190. (Func<ILInstruction>, IType) ConvertQuote(CallInstruction invocation)
  191. {
  192. if (invocation.Arguments.Count != 1)
  193. return (null, SpecialType.UnknownType);
  194. var argument = invocation.Arguments.Single();
  195. if (argument is ILFunction function)
  196. {
  197. return (() => function, function.DelegateType);
  198. }
  199. else
  200. {
  201. var (converted, type) = ConvertInstruction(argument);
  202. if (converted == null)
  203. return (converted, type);
  204. return (BuildQuote, type);
  205. ILInstruction BuildQuote()
  206. {
  207. var f = converted();
  208. if (f is ILFunction lambda && argument is CallInstruction call)
  209. {
  210. SetExpressionTreeFlag(lambda, call);
  211. }
  212. return f;
  213. }
  214. }
  215. }
  216. void SetExpressionTreeFlag(ILFunction lambda, CallInstruction call)
  217. {
  218. lambda.Kind = IsExpressionTree(call.Method.ReturnType) ? ILFunctionKind.ExpressionTree : ILFunctionKind.Delegate;
  219. lambda.DelegateType = call.Method.ReturnType;
  220. }
  221. bool ReadParameters(ILInstruction initializer, IList<IParameter> parameters, IList<ILVariable> parameterVariables, ITypeResolveContext resolveContext)
  222. {
  223. switch (initializer)
  224. {
  225. case Block initializerBlock:
  226. if (initializerBlock.Kind != BlockKind.ArrayInitializer)
  227. return false;
  228. int i = 0;
  229. foreach (var inst in initializerBlock.Instructions.OfType<StObj>())
  230. {
  231. if (i >= this.parameters.Count)
  232. return false;
  233. if (!inst.Value.MatchLdLoc(out var v))
  234. return false;
  235. if (!this.parameters.TryGetValue(v, out var value))
  236. return false;
  237. // Add parameter variable only once to mapping.
  238. if (!this.parameterMapping.ContainsKey(v))
  239. {
  240. var param = new ILVariable(VariableKind.Parameter, value.Item1, i) { Name = value.Item2 };
  241. parameterMapping.Add(v, param);
  242. parameterVariables.Add(param);
  243. parameters.Add(new DefaultParameter(value.Item1, value.Item2));
  244. instructionsToRemove.Add((ILInstruction)v.StoreInstructions[0]);
  245. }
  246. i++;
  247. }
  248. return true;
  249. default:
  250. return IsEmptyParameterList(initializer);
  251. }
  252. }
  253. (Func<ILInstruction>, IType) ConvertInstruction(ILInstruction instruction, IType typeHint = null)
  254. {
  255. var (inst, type) = Convert();
  256. if (inst == null)
  257. return (null, type);
  258. ILInstruction DoConvert()
  259. {
  260. var result = inst();
  261. Debug.Assert(type != null, "IType must be non-null!");
  262. Debug.Assert(result.ResultType == type.GetStackType(), "StackTypes must match!");
  263. if (typeHint != null)
  264. {
  265. if (result.ResultType != typeHint.GetStackType())
  266. {
  267. return new Conv(result, typeHint.GetStackType().ToPrimitiveType(), false, typeHint.GetSign());
  268. }
  269. }
  270. return result;
  271. }
  272. return (DoConvert, typeHint ?? type);
  273. (Func<ILInstruction>, IType) Convert()
  274. {
  275. switch (instruction)
  276. {
  277. case CallInstruction invocation:
  278. if (invocation.Method.DeclaringType.FullName != "System.Linq.Expressions.Expression")
  279. return (null, SpecialType.UnknownType);
  280. switch (invocation.Method.Name)
  281. {
  282. case "Add":
  283. return ConvertBinaryNumericOperator(invocation, BinaryNumericOperator.Add, false);
  284. case "AddChecked":
  285. return ConvertBinaryNumericOperator(invocation, BinaryNumericOperator.Add, true);
  286. case "And":
  287. return ConvertBinaryNumericOperator(invocation, BinaryNumericOperator.BitAnd);
  288. case "AndAlso":
  289. return ConvertLogicOperator(invocation, true);
  290. case "ArrayAccess":
  291. case "ArrayIndex":
  292. return ConvertArrayIndex(invocation);
  293. case "ArrayLength":
  294. return ConvertArrayLength(invocation);
  295. case "Call":
  296. return ConvertCall(invocation);
  297. case "Coalesce":
  298. return ConvertCoalesce(invocation);
  299. case "Condition":
  300. return ConvertCondition(invocation);
  301. case "Constant":
  302. return ConvertConstant(invocation);
  303. case "Convert":
  304. return ConvertCast(invocation, false);
  305. case "ConvertChecked":
  306. return ConvertCast(invocation, true);
  307. case "Divide":
  308. return ConvertBinaryNumericOperator(invocation, BinaryNumericOperator.Div);
  309. case "Equal":
  310. return ConvertComparison(invocation, ComparisonKind.Equality);
  311. case "ExclusiveOr":
  312. return ConvertBinaryNumericOperator(invocation, BinaryNumericOperator.BitXor);
  313. case "Field":
  314. return ConvertField(invocation, typeHint);
  315. case "GreaterThan":
  316. return ConvertComparison(invocation, ComparisonKind.GreaterThan);
  317. case "GreaterThanOrEqual":
  318. return ConvertComparison(invocation, ComparisonKind.GreaterThanOrEqual);
  319. case "Invoke":
  320. return ConvertInvoke(invocation);
  321. case "Lambda":
  322. return ConvertLambda(invocation);
  323. case "LeftShift":
  324. return ConvertBinaryNumericOperator(invocation, BinaryNumericOperator.ShiftLeft);
  325. case "LessThan":
  326. return ConvertComparison(invocation, ComparisonKind.LessThan);
  327. case "LessThanOrEqual":
  328. return ConvertComparison(invocation, ComparisonKind.LessThanOrEqual);
  329. case "ListInit":
  330. return ConvertListInit(invocation);
  331. case "MemberInit":
  332. return ConvertMemberInit(invocation);
  333. case "Modulo":
  334. return ConvertBinaryNumericOperator(invocation, BinaryNumericOperator.Rem);
  335. case "Multiply":
  336. return ConvertBinaryNumericOperator(invocation, BinaryNumericOperator.Mul, false);
  337. case "MultiplyChecked":
  338. return ConvertBinaryNumericOperator(invocation, BinaryNumericOperator.Mul, true);
  339. case "Negate":
  340. return ConvertUnaryNumericOperator(invocation, BinaryNumericOperator.Sub, false);
  341. case "NegateChecked":
  342. return ConvertUnaryNumericOperator(invocation, BinaryNumericOperator.Sub, true);
  343. case "New":
  344. return ConvertNewObject(invocation);
  345. case "NewArrayBounds":
  346. return ConvertNewArrayBounds(invocation);
  347. case "NewArrayInit":
  348. return ConvertNewArrayInit(invocation);
  349. case "Not":
  350. return ConvertNotOperator(invocation);
  351. case "NotEqual":
  352. return ConvertComparison(invocation, ComparisonKind.Inequality);
  353. case "OnesComplement":
  354. return ConvertNotOperator(invocation);
  355. case "Or":
  356. return ConvertBinaryNumericOperator(invocation, BinaryNumericOperator.BitOr);
  357. case "OrElse":
  358. return ConvertLogicOperator(invocation, false);
  359. case "Property":
  360. return ConvertProperty(invocation);
  361. case "Quote":
  362. return ConvertQuote(invocation);
  363. case "RightShift":
  364. return ConvertBinaryNumericOperator(invocation, BinaryNumericOperator.ShiftRight);
  365. case "Subtract":
  366. return ConvertBinaryNumericOperator(invocation, BinaryNumericOperator.Sub, false);
  367. case "SubtractChecked":
  368. return ConvertBinaryNumericOperator(invocation, BinaryNumericOperator.Sub, true);
  369. case "TypeAs":
  370. return ConvertTypeAs(invocation);
  371. case "TypeIs":
  372. return ConvertTypeIs(invocation);
  373. }
  374. return (null, SpecialType.UnknownType);
  375. case ILFunction function:
  376. ILFunction ApplyChangesToILFunction()
  377. {
  378. if (function.Kind == ILFunctionKind.ExpressionTree)
  379. {
  380. function.DelegateType = UnwrapExpressionTree(function.DelegateType);
  381. function.Kind = ILFunctionKind.Delegate;
  382. }
  383. return function;
  384. }
  385. return (ApplyChangesToILFunction, function.DelegateType);
  386. case LdLoc ldloc:
  387. if (IsExpressionTreeParameter(ldloc.Variable))
  388. {
  389. // Replace an already mapped parameter with the actual ILVariable,
  390. // we generated earlier.
  391. if (parameterMapping.TryGetValue(ldloc.Variable, out var v))
  392. {
  393. if (typeHint.SkipModifiers() is ByReferenceType && !v.Type.IsByRefLike)
  394. return (() => new LdLoca(v), typeHint);
  395. return (() => new LdLoc(v), v.Type);
  396. }
  397. // This is a parameter variable from an outer scope.
  398. // We can't replace these variables just yet, because the transform works backwards.
  399. // We simply return the same instruction again, but return the actual expected type,
  400. // so our transform can continue normally.
  401. // Later, we will replace all references to unmapped variables,
  402. // with references to mapped parameters.
  403. if (ldloc.Variable.IsSingleDefinition && ldloc.Variable.StoreInstructions[0] is ILInstruction instr)
  404. {
  405. if (MatchParameterVariableAssignment(instr, out _, out var t, out _))
  406. return (() => new ExpressionTreeCast(t, ldloc, false), t);
  407. }
  408. }
  409. return (null, SpecialType.UnknownType);
  410. default:
  411. return (null, SpecialType.UnknownType);
  412. }
  413. }
  414. }
  415. bool IsExpressionTree(IType delegateType) => delegateType is ParameterizedType pt
  416. && pt.FullName == "System.Linq.Expressions.Expression"
  417. && pt.TypeArguments.Count == 1;
  418. IType UnwrapExpressionTree(IType delegateType)
  419. {
  420. if (delegateType is ParameterizedType pt && pt.FullName == "System.Linq.Expressions.Expression" && pt.TypeArguments.Count == 1)
  421. {
  422. return pt.TypeArguments[0];
  423. }
  424. return delegateType;
  425. }
  426. (Func<ILInstruction>, IType) ConvertArrayIndex(CallInstruction invocation)
  427. {
  428. if (invocation.Arguments.Count != 2)
  429. return (null, SpecialType.UnknownType);
  430. var (array, arrayType) = ConvertInstruction(invocation.Arguments[0]);
  431. if (array == null)
  432. return (null, SpecialType.UnknownType);
  433. if (!(arrayType is ArrayType type))
  434. return (null, SpecialType.UnknownType);
  435. if (!MatchArgumentList(invocation.Arguments[1], out var arguments))
  436. arguments = new[] { invocation.Arguments[1] };
  437. ILInstruction Convert()
  438. {
  439. Func<ILInstruction>[] toBeConverted = new Func<ILInstruction>[arguments.Count];
  440. for (int i = 0; i < arguments.Count; i++)
  441. {
  442. var (converted, indexType) = ConvertInstruction(arguments[i]);
  443. if (converted == null)
  444. return null;
  445. toBeConverted[i] = converted;
  446. }
  447. return new LdObj(new LdElema(type.ElementType, array(), toBeConverted.SelectArray(f => f())) { DelayExceptions = true }, type.ElementType);
  448. }
  449. return (Convert, type.ElementType);
  450. }
  451. (Func<ILInstruction>, IType) ConvertArrayLength(CallInstruction invocation)
  452. {
  453. if (invocation.Arguments.Count != 1)
  454. return (null, SpecialType.UnknownType);
  455. var (converted, _) = ConvertInstruction(invocation.Arguments[0]);
  456. if (converted == null)
  457. return (null, SpecialType.UnknownType);
  458. return (() => new LdLen(StackType.I4, converted()), context.TypeSystem.FindType(KnownTypeCode.Int32));
  459. }
  460. (Func<ILInstruction>, IType) ConvertBinaryNumericOperator(CallInstruction invocation, BinaryNumericOperator op, bool? isChecked = null)
  461. {
  462. if (invocation.Arguments.Count < 2)
  463. return (null, SpecialType.UnknownType);
  464. var (left, leftType) = ConvertInstruction(invocation.Arguments[0]);
  465. if (left == null)
  466. return (null, SpecialType.UnknownType);
  467. var (right, rightType) = ConvertInstruction(invocation.Arguments[1]);
  468. if (right == null)
  469. return (null, SpecialType.UnknownType);
  470. IMember method;
  471. switch (invocation.Arguments.Count)
  472. {
  473. case 2:
  474. if (op == BinaryNumericOperator.ShiftLeft || op == BinaryNumericOperator.ShiftRight)
  475. {
  476. if (!NullableType.GetUnderlyingType(rightType).IsKnownType(KnownTypeCode.Int32))
  477. return (null, SpecialType.UnknownType);
  478. }
  479. else
  480. {
  481. if (!rightType.Equals(leftType))
  482. return (null, SpecialType.UnknownType);
  483. }
  484. return (() => new BinaryNumericInstruction(op, left(), right(),
  485. NullableType.GetUnderlyingType(leftType).GetStackType(),
  486. NullableType.GetUnderlyingType(rightType).GetStackType(),
  487. isChecked == true,
  488. leftType.GetSign(),
  489. isLifted: NullableType.IsNullable(leftType) || NullableType.IsNullable(rightType)), leftType);
  490. case 3:
  491. if (!MatchGetMethodFromHandle(invocation.Arguments[2], out method))
  492. return (null, SpecialType.UnknownType);
  493. return (() => new Call((IMethod)method) {
  494. Arguments = { left(), right() }
  495. }, method.ReturnType);
  496. case 4:
  497. if (!invocation.Arguments[2].MatchLdcI4(out var isLiftedToNull))
  498. return (null, SpecialType.UnknownType);
  499. if (!MatchGetMethodFromHandle(invocation.Arguments[3], out method))
  500. return (null, SpecialType.UnknownType);
  501. bool isLifted = NullableType.IsNullable(leftType);
  502. if (isLifted)
  503. method = CSharpOperators.LiftUserDefinedOperator((IMethod)method);
  504. return (() => new Call((IMethod)method) {
  505. Arguments = { left(), right() }
  506. }, isLiftedToNull != 0 ? NullableType.Create(method.Compilation, method.ReturnType) : method.ReturnType);
  507. default:
  508. return (null, SpecialType.UnknownType);
  509. }
  510. }
  511. (Func<ILVariable, ILInstruction>, IType) ConvertBind(CallInstruction invocation)
  512. {
  513. if (invocation.Arguments.Count != 2)
  514. return (null, SpecialType.UnknownType);
  515. var (value, typeValue) = ConvertInstruction(invocation.Arguments[1]);
  516. if (value == null)
  517. return (null, SpecialType.UnknownType);
  518. if (MatchGetMethodFromHandle(invocation.Arguments[0], out var member))
  519. {
  520. var method = (IMethod)member;
  521. // It is possible to use Expression.Bind with a get-accessor,
  522. // however, it would be an invalid expression tree if the property is readonly.
  523. // As this is an assignment, the ILAst expects a set-accessor. To avoid any problems
  524. // constructing property assignments, we explicitly use the set-accessor instead.
  525. if (method.AccessorOwner is IProperty { CanSet: true } property && method != property.Setter)
  526. {
  527. member = property.Setter;
  528. }
  529. }
  530. else if (MatchGetFieldFromHandle(invocation.Arguments[0], out member))
  531. {
  532. }
  533. else
  534. {
  535. return (null, SpecialType.UnknownType);
  536. }
  537. switch (member)
  538. {
  539. case IMethod method:
  540. if (method.IsStatic)
  541. return (targetVariable => new Call(method) { Arguments = { new LdLoc(targetVariable), value() } }, method.ReturnType);
  542. else
  543. return (targetVariable => new CallVirt(method) { Arguments = { new LdLoc(targetVariable), value() } }, method.ReturnType);
  544. case IField field:
  545. return (targetVariable => new StObj(new LdFlda(new LdLoc(targetVariable), (IField)member) { DelayExceptions = true }, value(), member.ReturnType), field.ReturnType);
  546. }
  547. return (null, SpecialType.UnknownType);
  548. }
  549. (Func<ILInstruction>, IType) ConvertCall(CallInstruction invocation)
  550. {
  551. if (invocation.Arguments.Count < 2)
  552. return (null, SpecialType.UnknownType);
  553. IList<ILInstruction> arguments = null;
  554. Func<ILInstruction> targetConverter = null;
  555. IType targetType = null;
  556. if (MatchGetMethodFromHandle(invocation.Arguments[0], out var member))
  557. {
  558. // static method
  559. if (invocation.Arguments.Count != 2 || !MatchArgumentList(invocation.Arguments[1], out arguments))
  560. {
  561. arguments = new List<ILInstruction>(invocation.Arguments.Skip(1));
  562. }
  563. }
  564. else if (MatchGetMethodFromHandle(invocation.Arguments[1], out member))
  565. {
  566. if (invocation.Arguments.Count != 3 || !MatchArgumentList(invocation.Arguments[2], out arguments))
  567. {
  568. arguments = new List<ILInstruction>(invocation.Arguments.Skip(2));
  569. }
  570. if (!invocation.Arguments[0].MatchLdNull())
  571. {
  572. (targetConverter, targetType) = ConvertInstruction(invocation.Arguments[0]);
  573. if (targetConverter == null)
  574. return (null, SpecialType.UnknownType);
  575. }
  576. }
  577. if (arguments == null)
  578. return (null, SpecialType.UnknownType);
  579. IMethod method = (IMethod)member;
  580. var convertedArguments = ConvertCallArguments(arguments, method);
  581. if (convertedArguments == null)
  582. return (null, SpecialType.UnknownType);
  583. if (method.FullName == "System.Reflection.MethodInfo.CreateDelegate" && method.Parameters.Count == 2)
  584. {
  585. if (!MatchGetMethodFromHandle(UnpackConstant(invocation.Arguments[0]), out var targetMethod))
  586. return (null, SpecialType.UnknownType);
  587. if (!MatchGetTypeFromHandle(UnpackConstant(arguments[0]), out var delegateType))
  588. return (null, SpecialType.UnknownType);
  589. return (() => new NewObj(delegateType.GetConstructors().Single()) {
  590. Arguments = { convertedArguments[1](), new LdFtn((IMethod)targetMethod) }
  591. }, delegateType);
  592. }
  593. CallInstruction BuildCall()
  594. {
  595. CallInstruction call;
  596. if (method.IsStatic)
  597. {
  598. call = new Call(method);
  599. }
  600. else
  601. {
  602. call = new CallVirt(method);
  603. }
  604. if (targetConverter != null)
  605. {
  606. call.Arguments.Add(PrepareCallTarget(method.DeclaringType, targetConverter(), targetType));
  607. }
  608. call.Arguments.AddRange(convertedArguments.Select(f => f()));
  609. return call;
  610. }
  611. return (BuildCall, method.ReturnType);
  612. }
  613. ILInstruction PrepareCallTarget(IType expectedType, ILInstruction target, IType targetType)
  614. {
  615. ILInstruction result;
  616. switch (CallInstruction.ExpectedTypeForThisPointer(expectedType))
  617. {
  618. case StackType.Ref:
  619. if (target.ResultType == StackType.Ref)
  620. {
  621. result = target;
  622. }
  623. else if (target is LdLoc ldloc)
  624. {
  625. result = new LdLoca(ldloc.Variable).WithILRange(ldloc);
  626. }
  627. else
  628. {
  629. result = new AddressOf(target, expectedType);
  630. }
  631. break;
  632. case StackType.O:
  633. if (targetType.IsReferenceType == false)
  634. {
  635. result = new Box(target, targetType);
  636. }
  637. else
  638. {
  639. result = target;
  640. }
  641. break;
  642. default:
  643. result = target;
  644. break;
  645. }
  646. if (expectedType.Kind == TypeKind.Unknown && result.ResultType != StackType.Unknown)
  647. {
  648. result = new Conv(target, PrimitiveType.Unknown, false, Sign.None);
  649. }
  650. else if (expectedType.Kind != TypeKind.Unknown && result.ResultType == StackType.Unknown)
  651. {
  652. // if references are missing, we need to coerce the unknown type to the expected type.
  653. // Otherwise we will get loads of assertions and expression trees
  654. // are usually explicit about any conversions.
  655. result = new Conv(result, expectedType.ToPrimitiveType(), false, Sign.None);
  656. }
  657. return result;
  658. }
  659. ILInstruction UnpackConstant(ILInstruction inst)
  660. {
  661. if (!(inst is CallInstruction call && call.Method.FullName == "System.Linq.Expressions.Expression.Constant" && call.Arguments.Count == 2))
  662. return inst;
  663. return call.Arguments[0];
  664. }
  665. Func<ILInstruction>[] ConvertCallArguments(IList<ILInstruction> arguments, IMethod method)
  666. {
  667. var converted = new Func<ILInstruction>[arguments.Count];
  668. Debug.Assert(arguments.Count == method.Parameters.Count);
  669. for (int i = 0; i < arguments.Count; i++)
  670. {
  671. var expectedType = method.Parameters[i].Type;
  672. var argument = ConvertInstruction(arguments[i], expectedType).Item1;
  673. if (argument == null)
  674. return null;
  675. converted[i] = argument;
  676. }
  677. return converted;
  678. }
  679. (Func<ILInstruction>, IType) ConvertCast(CallInstruction invocation, bool isChecked)
  680. {
  681. if (invocation.Arguments.Count < 2)
  682. return (null, SpecialType.UnknownType);
  683. if (!MatchGetTypeFromHandle(invocation.Arguments[1], out var targetType))
  684. return (null, SpecialType.UnknownType);
  685. var (expr, exprType) = ConvertInstruction(invocation.Arguments[0]);
  686. if (expr == null)
  687. return (null, SpecialType.UnknownType);
  688. if (exprType.IsSmallIntegerType() && targetType.IsKnownType(KnownTypeCode.Int32))
  689. return (expr, targetType);
  690. return (() => new ExpressionTreeCast(targetType, expr(), isChecked), targetType);
  691. }
  692. (Func<ILInstruction>, IType) ConvertCoalesce(CallInstruction invocation)
  693. {
  694. if (invocation.Arguments.Count != 2)
  695. return (null, SpecialType.UnknownType);
  696. var (trueInst, trueInstType) = ConvertInstruction(invocation.Arguments[0]);
  697. if (trueInst == null)
  698. return (null, SpecialType.UnknownType);
  699. var (fallbackInst, fallbackInstType) = ConvertInstruction(invocation.Arguments[1]);
  700. if (fallbackInst == null)
  701. return (null, SpecialType.UnknownType);
  702. var kind = NullCoalescingKind.Ref;
  703. var trueInstTypeNonNullable = NullableType.GetUnderlyingType(trueInstType);
  704. IType targetType;
  705. if (NullableType.IsNullable(trueInstType) && conversions.ImplicitConversion(fallbackInstType, trueInstTypeNonNullable).IsValid)
  706. {
  707. targetType = trueInstTypeNonNullable;
  708. kind = NullableType.IsNullable(fallbackInstType) ? NullCoalescingKind.Nullable : NullCoalescingKind.NullableWithValueFallback;
  709. }
  710. else if (conversions.ImplicitConversion(fallbackInstType, trueInstType).IsValid)
  711. {
  712. targetType = trueInstType;
  713. }
  714. else
  715. {
  716. targetType = fallbackInstType;
  717. }
  718. return (() => new NullCoalescingInstruction(kind, trueInst(), fallbackInst()) {
  719. UnderlyingResultType = trueInstTypeNonNullable.GetStackType()
  720. }, targetType);
  721. }
  722. (Func<ILInstruction>, IType) ConvertComparison(CallInstruction invocation, ComparisonKind kind)
  723. {
  724. if (invocation.Arguments.Count < 2)
  725. return (null, SpecialType.UnknownType);
  726. var (left, leftType) = ConvertInstruction(invocation.Arguments[0]);
  727. if (left == null)
  728. return (null, SpecialType.UnknownType);
  729. var (right, rightType) = ConvertInstruction(invocation.Arguments[1]);
  730. if (right == null)
  731. return (null, SpecialType.UnknownType);
  732. if (invocation.Arguments.Count == 4 && invocation.Arguments[2].MatchLdcI4(out var isLiftedToNull) && MatchGetMethodFromHandle(invocation.Arguments[3], out var method))
  733. {
  734. bool isLifted = NullableType.IsNullable(leftType);
  735. if (isLifted)
  736. method = CSharpOperators.LiftUserDefinedOperator((IMethod)method);
  737. return (() => new Call((IMethod)method) { Arguments = { left(), right() } }, isLiftedToNull != 0 ? NullableType.Create(method.Compilation, method.ReturnType) : method.ReturnType);
  738. }
  739. var rr = resolver.ResolveBinaryOperator(kind.ToBinaryOperatorType(), new ResolveResult(leftType), new ResolveResult(rightType)) as OperatorResolveResult;
  740. if (rr != null && !rr.IsError && rr.UserDefinedOperatorMethod != null)
  741. {
  742. return (() => new Call(rr.UserDefinedOperatorMethod) { Arguments = { left(), right() } }, rr.UserDefinedOperatorMethod.ReturnType);
  743. }
  744. if (leftType.IsKnownType(KnownTypeCode.String) && rightType.IsKnownType(KnownTypeCode.String))
  745. {
  746. IMethod operatorMethod;
  747. switch (kind)
  748. {
  749. case ComparisonKind.Equality:
  750. operatorMethod = leftType.GetMethods(m => m.IsOperator && m.Name == "op_Equality" && m.Parameters.Count == 2).FirstOrDefault(m => m.Parameters[0].Type.IsKnownType(KnownTypeCode.String) && m.Parameters[1].Type.IsKnownType(KnownTypeCode.String));
  751. if (operatorMethod == null)
  752. return (null, SpecialType.UnknownType);
  753. break;
  754. case ComparisonKind.Inequality:
  755. operatorMethod = leftType.GetMethods(m => m.IsOperator && m.Name == "op_Inequality" && m.Parameters.Count == 2).FirstOrDefault(m => m.Parameters[0].Type.IsKnownType(KnownTypeCode.String) && m.Parameters[1].Type.IsKnownType(KnownTypeCode.String));
  756. if (operatorMethod == null)
  757. return (null, SpecialType.UnknownType);
  758. break;
  759. default:
  760. return (null, SpecialType.UnknownType);
  761. }
  762. return (() => new Call(operatorMethod) { Arguments = { left(), right() } }, operatorMethod.ReturnType);
  763. }
  764. var resultType = context.TypeSystem.FindType(KnownTypeCode.Boolean);
  765. var lifting = NullableType.IsNullable(leftType) ? ComparisonLiftingKind.CSharp : ComparisonLiftingKind.None;
  766. var utype = NullableType.GetUnderlyingType(leftType);
  767. return (() => new Comp(kind, lifting, utype.GetStackType(), utype.GetSign(), left(), right()), resultType);
  768. }
  769. (Func<ILInstruction>, IType) ConvertCondition(CallInstruction invocation)
  770. {
  771. if (invocation.Arguments.Count != 3)
  772. return (null, SpecialType.UnknownType);
  773. var (condition, conditionType) = ConvertInstruction(invocation.Arguments[0]);
  774. if (condition == null || !conditionType.IsKnownType(KnownTypeCode.Boolean))
  775. return (null, SpecialType.UnknownType);
  776. var (trueInst, trueInstType) = ConvertInstruction(invocation.Arguments[1]);
  777. if (trueInst == null)
  778. return (null, SpecialType.UnknownType);
  779. var (falseInst, falseInstType) = ConvertInstruction(invocation.Arguments[2]);
  780. if (falseInst == null)
  781. return (null, SpecialType.UnknownType);
  782. if (!NormalizeTypeVisitor.TypeErasure.EquivalentTypes(trueInstType, falseInstType))
  783. return (null, SpecialType.UnknownType);
  784. return (() => new IfInstruction(condition(), trueInst(), falseInst()), trueInstType);
  785. }
  786. (Func<ILInstruction>, IType) ConvertConstant(CallInstruction invocation)
  787. {
  788. if (!MatchConstantCall(invocation, out var value, out var type))
  789. return (null, SpecialType.UnknownType);
  790. if (value.MatchBox(out var arg, out var boxType))
  791. {
  792. if (boxType.Kind == TypeKind.Enum || boxType.IsKnownType(KnownTypeCode.Boolean))
  793. return (() => new ExpressionTreeCast(boxType, ConvertValue(arg, invocation), false), boxType);
  794. return (() => ConvertValue(arg, invocation), type);
  795. }
  796. return (() => ConvertValue(value, invocation), type);
  797. }
  798. (Func<ILInstruction>, IType) ConvertElementInit(CallInstruction invocation)
  799. {
  800. if (invocation.Arguments.Count != 2)
  801. return (null, SpecialType.UnknownType);
  802. if (!MatchGetMethodFromHandle(invocation.Arguments[0], out var member))
  803. return (null, SpecialType.UnknownType);
  804. if (!MatchArgumentList(invocation.Arguments[1], out var arguments))
  805. return (null, SpecialType.UnknownType);
  806. var args = new Func<ILInstruction>[arguments.Count];
  807. for (int i = 0; i < arguments.Count; i++)
  808. {
  809. var arg = ConvertInstruction(arguments[i]).Item1;
  810. if (arg == null)
  811. return (null, SpecialType.UnknownType);
  812. args[i] = arg;
  813. }
  814. ILInstruction BuildCall()
  815. {
  816. CallInstruction call = member.IsStatic
  817. ? (CallInstruction)new Call((IMethod)member)
  818. : new CallVirt((IMethod)member);
  819. call.Arguments.AddRange(args.Select(f => f()));
  820. return call;
  821. }
  822. return (BuildCall, member.ReturnType);
  823. }
  824. (Func<ILInstruction>, IType) ConvertField(CallInstruction invocation, IType typeHint)
  825. {
  826. if (invocation.Arguments.Count != 2)
  827. return (null, SpecialType.UnknownType);
  828. Func<ILInstruction> targetConverter = null;
  829. if (!invocation.Arguments[0].MatchLdNull())
  830. {
  831. targetConverter = ConvertInstruction(invocation.Arguments[0]).Item1;
  832. if (targetConverter == null)
  833. return (null, SpecialType.UnknownType);
  834. }
  835. if (!MatchGetFieldFromHandle(invocation.Arguments[1], out var member))
  836. return (null, SpecialType.UnknownType);
  837. IType type = member.ReturnType;
  838. if (typeHint.SkipModifiers() is ByReferenceType && !member.ReturnType.IsByRefLike)
  839. {
  840. type = typeHint;
  841. }
  842. return (BuildField, type);
  843. ILInstruction BuildField()
  844. {
  845. ILInstruction inst;
  846. if (targetConverter == null)
  847. {
  848. inst = new LdsFlda((IField)member);
  849. }
  850. else
  851. {
  852. var target = targetConverter();
  853. if (member.DeclaringType.IsReferenceType == true)
  854. {
  855. inst = new LdFlda(target, (IField)member) { DelayExceptions = true };
  856. }
  857. else
  858. {
  859. inst = new LdFlda(new AddressOf(target, member.DeclaringType), (IField)member) { DelayExceptions = true };
  860. }
  861. }
  862. if (!(typeHint.SkipModifiers() is ByReferenceType && !member.ReturnType.IsByRefLike))
  863. {
  864. inst = new LdObj(inst, member.ReturnType);
  865. }
  866. return inst;
  867. }
  868. }
  869. (Func<ILInstruction>, IType) ConvertInvoke(CallInstruction invocation)
  870. {
  871. if (invocation.Arguments.Count != 2)
  872. return (null, SpecialType.UnknownType);
  873. var (targetConverter, targetType) = ConvertInstruction(invocation.Arguments[0]);
  874. if (targetConverter == null)
  875. return (null, SpecialType.UnknownType);
  876. var invokeMethod = targetType.GetDelegateInvokeMethod();
  877. if (invokeMethod == null)
  878. return (null, SpecialType.UnknownType);
  879. if (!MatchArgumentList(invocation.Arguments[1], out var arguments))
  880. return (null, SpecialType.UnknownType);
  881. var convertedArguments = ConvertCallArguments(arguments, invokeMethod);
  882. if (convertedArguments == null)
  883. return (null, SpecialType.UnknownType);
  884. ILInstruction BuildCall()
  885. {
  886. var call = new CallVirt(invokeMethod);
  887. call.Arguments.Add(targetConverter());
  888. call.Arguments.AddRange(convertedArguments.Select(f => f()));
  889. return call;
  890. }
  891. return (BuildCall, invokeMethod.ReturnType);
  892. }
  893. (Func<ILInstruction>, IType) ConvertListInit(CallInstruction invocation)
  894. {
  895. if (invocation.Arguments.Count < 2)
  896. return (null, SpecialType.UnknownType);
  897. var newObj = ConvertInstruction(invocation.Arguments[0]).Item1;
  898. if (newObj == null)
  899. return (null, SpecialType.UnknownType);
  900. if (!MatchNew((CallInstruction)invocation.Arguments[0], out var ctor))
  901. return (null, SpecialType.UnknownType);
  902. IList<ILInstruction> arguments;
  903. if (!MatchGetMethodFromHandle(invocation.Arguments[1], out var member))
  904. {
  905. if (!MatchArgumentList(invocation.Arguments[1], out arguments))
  906. return (null, SpecialType.UnknownType);
  907. }
  908. else
  909. {
  910. if (invocation.Arguments.Count != 3 || !MatchArgumentList(invocation.Arguments[2], out arguments))
  911. return (null, SpecialType.UnknownType);
  912. }
  913. if (arguments == null || arguments.Count == 0)
  914. return (null, SpecialType.UnknownType);
  915. Func<ILVariable, ILInstruction>[] convertedArguments = new Func<ILVariable, ILInstruction>[arguments.Count];
  916. for (int i = 0; i < arguments.Count; i++)
  917. {
  918. if (arguments[i] is CallInstruction elementInit && elementInit.Method.FullName == "System.Linq.Expressions.Expression.ElementInit")
  919. {
  920. var arg = ConvertElementInit(elementInit).Item1;
  921. if (arg == null)
  922. return (null, SpecialType.UnknownType);
  923. convertedArguments[i] = v => { var a = arg(); ((CallInstruction)a).Arguments.Insert(0, new LdLoc(v)); return a; };
  924. }
  925. else
  926. {
  927. var arg = ConvertInstruction(arguments[i]).Item1;
  928. if (arg == null)
  929. return (null, SpecialType.UnknownType);
  930. convertedArguments[i] = v => arg();
  931. }
  932. }
  933. Block BuildBlock()
  934. {
  935. var initializerBlock = new Block(BlockKind.CollectionInitializer);
  936. ILFunction function = lambdaStack.Peek();
  937. var initializer = function.RegisterVariable(VariableKind.InitializerTarget, ctor.DeclaringType);
  938. initializerBlock.FinalInstruction = new LdLoc(initializer);
  939. initializerBlock.Instructions.Add(new StLoc(initializer, newObj()));
  940. initializerBlock.Instructions.AddRange(convertedArguments.Select(f => f(initializer)));
  941. return initializerBlock;
  942. }
  943. return (BuildBlock, ctor.DeclaringType);
  944. }
  945. (Func<ILInstruction>, IType) ConvertLogicOperator(CallInstruction invocation, bool and)
  946. {
  947. if (invocation.Arguments.Count < 2)
  948. return (null, SpecialType.UnknownType);
  949. var (left, leftType) = ConvertInstruction(invocation.Arguments[0]);
  950. if (left == null)
  951. return (null, SpecialType.UnknownType);
  952. var (right, rightType) = ConvertInstruction(invocation.Arguments[1]);
  953. if (right == null)
  954. return (null, SpecialType.UnknownType);
  955. IMember method;
  956. switch (invocation.Arguments.Count)
  957. {
  958. case 2:
  959. var resultType = context.TypeSystem.FindType(KnownTypeCode.Boolean);
  960. return (() => and ? IfInstruction.LogicAnd(left(), right()) : IfInstruction.LogicOr(left(), right()), resultType);
  961. case 3:
  962. if (!MatchGetMethodFromHandle(invocation.Arguments[2], out method))
  963. return (null, SpecialType.UnknownType);
  964. return (() => new Call((IMethod)method) {
  965. Arguments = { left(), right() }
  966. }, method.ReturnType);
  967. case 4:
  968. if (!invocation.Arguments[2].MatchLdcI4(out var isLiftedToNull))
  969. return (null, SpecialType.UnknownType);
  970. if (!MatchGetMethodFromHandle(invocation.Arguments[3], out method))
  971. return (null, SpecialType.UnknownType);
  972. bool isLifted = NullableType.IsNullable(leftType);
  973. if (isLifted)
  974. method = CSharpOperators.LiftUserDefinedOperator((IMethod)method);
  975. return (() => new Call((IMethod)method) {
  976. Arguments = { left(), right() }
  977. }, isLiftedToNull != 0 ? NullableType.Create(method.Compilation, method.ReturnType) : method.ReturnType);
  978. default:
  979. return (null, SpecialType.UnknownType);
  980. }
  981. }
  982. (Func<ILInstruction>, IType) ConvertMemberInit(CallInstruction invocation)
  983. {
  984. if (invocation.Arguments.Count != 2)
  985. return (null, SpecialType.UnknownType);
  986. var newObj = ConvertInstruction(invocation.Arguments[0]).Item1;
  987. if (newObj == null)
  988. return (null, SpecialType.UnknownType);
  989. if (!MatchNew((CallInstruction)invocation.Arguments[0], out var ctor))
  990. return (null, SpecialType.UnknownType);
  991. if (!MatchArgumentList(invocation.Arguments[1], out var arguments))
  992. return (null, SpecialType.UnknownType);
  993. if (arguments == null || arguments.Count == 0)
  994. return (null, SpecialType.UnknownType);
  995. Func<ILVariable, ILInstruction>[] convertedArguments = new Func<ILVariable, ILInstruction>[arguments.Count];
  996. for (int i = 0; i < arguments.Count; i++)
  997. {
  998. Func<ILVariable, ILInstruction> arg;
  999. if (arguments[i] is CallInstruction bind && bind.Method.FullName == "System.Linq.Expressions.Expression.Bind")
  1000. {
  1001. arg = ConvertBind(bind).Item1;
  1002. if (arg == null)
  1003. return (null, SpecialType.UnknownType);
  1004. }
  1005. else
  1006. {
  1007. return (null, SpecialType.UnknownType);
  1008. }
  1009. convertedArguments[i] = arg;
  1010. }
  1011. ILInstruction BuildBlock()
  1012. {
  1013. var function = lambdaStack.Peek();
  1014. var initializer = function.RegisterVariable(VariableKind.InitializerTarget, ctor.DeclaringType);
  1015. var initializerBlock = new Block(BlockKind.CollectionInitializer);
  1016. initializerBlock.FinalInstruction = new LdLoc(initializer);
  1017. initializerBlock.Instructions.Add(new StLoc(initializer, newObj()));
  1018. initializerBlock.Instructions.AddRange(convertedArguments.Select(f => f(initializer)));
  1019. return initializerBlock;
  1020. }
  1021. return (BuildBlock, ctor.DeclaringType);
  1022. }
  1023. (Func<ILInstruction>, IType) ConvertNewArrayBounds(CallInstruction invocation)
  1024. {
  1025. if (invocation.Arguments.Count != 2)
  1026. return (null, SpecialType.UnknownType);
  1027. if (!MatchGetTypeFromHandle(invocation.Arguments[0], out var type))
  1028. return (null, SpecialType.UnknownType);
  1029. if (!MatchArgumentList(invocation.Arguments[1], out var arguments))
  1030. return (null, SpecialType.UnknownType);
  1031. if (arguments.Count == 0)
  1032. return (null, SpecialType.UnknownType);
  1033. var indices = new Func<ILInstruction>[arguments.Count];
  1034. for (int i = 0; i < arguments.Count; i++)
  1035. {
  1036. var index = ConvertInstruction(arguments[i]).Item1;
  1037. if (index == null)
  1038. return (null, SpecialType.UnknownType);
  1039. indices[i] = index;
  1040. }
  1041. return (() => new NewArr(type, indices.SelectArray(f => f())), new ArrayType(context.TypeSystem, type, arguments.Count));
  1042. }
  1043. (Func<ILInstruction>, IType) ConvertNewArrayInit(CallInstruction invocation)
  1044. {
  1045. if (invocation.Arguments.Count != 2)
  1046. return (null, SpecialType.UnknownType);
  1047. if (!MatchGetTypeFromHandle(invocation.Arguments[0], out var type))
  1048. return (null, SpecialType.UnknownType);
  1049. if (!MatchArgumentList(invocation.Arguments[1], out var arguments))
  1050. return (null, SpecialType.UnknownType);
  1051. ArrayType arrayType = new ArrayType(context.BlockContext.TypeSystem, type);
  1052. if (arguments.Count == 0)
  1053. return (() => new NewArr(type, new LdcI4(0)), arrayType);
  1054. var convertedArguments = new Func<ILInstruction>[arguments.Count];
  1055. for (int i = 0; i < arguments.Count; i++)
  1056. {
  1057. ILInstruction item = arguments[i];
  1058. var value = ConvertInstruction(item).Item1;
  1059. if (value == null)
  1060. return (null, SpecialType.UnknownType);
  1061. convertedArguments[i] = value;
  1062. }
  1063. ILInstruction BuildInitializer()
  1064. {
  1065. var block = (Block)invocation.Arguments[1];
  1066. var function = lambdaStack.Peek();
  1067. var variable = function.RegisterVariable(VariableKind.InitializerTarget, arrayType);
  1068. Block initializer = new Block(BlockKind.ArrayInitializer);
  1069. initializer.Instructions.Add(new StLoc(variable, new NewArr(type, new LdcI4(convertedArguments.Length))));
  1070. for (int i = 0; i < convertedArguments.Length; i++)
  1071. {
  1072. initializer.Instructions.Add(new StObj(new LdElema(type, new LdLoc(variable), new LdcI4(i)) { DelayExceptions = true }, convertedArguments[i](), type));
  1073. }
  1074. initializer.FinalInstruction = new LdLoc(variable);
  1075. return initializer;
  1076. }
  1077. return (BuildInitializer, arrayType);
  1078. }
  1079. bool MatchNew(CallInstruction invocation, out IMethod ctor)
  1080. {
  1081. ctor = null;
  1082. if (invocation.Method.Name != "New")
  1083. return false;
  1084. switch (invocation.Arguments.Count)
  1085. {
  1086. case 1:
  1087. if (MatchGetTypeFromHandle(invocation.Arguments[0], out var type))
  1088. {
  1089. ctor = type.GetConstructors(c => c.Parameters.Count == 0).FirstOrDefault();
  1090. return ctor != null;
  1091. }
  1092. if (MatchGetConstructorFromHandle(invocation.Arguments[0], out var member))
  1093. {
  1094. ctor = (IMethod)member;
  1095. return true;
  1096. }
  1097. return false;
  1098. case 2:
  1099. case 3:
  1100. if (!MatchGetConstructorFromHandle(invocation.Arguments[0], out member))
  1101. return false;
  1102. ctor = (IMethod)member;
  1103. return true;
  1104. default:
  1105. return false;
  1106. }
  1107. }
  1108. (Func<ILInstruction>, IType) ConvertNewObject(CallInstruction invocation)
  1109. {
  1110. switch (invocation.Arguments.Count)
  1111. {
  1112. case 1:
  1113. if (MatchGetTypeFromHandle(invocation.Arguments[0], out var type))
  1114. {
  1115. var ctor = type.GetConstructors(c => c.Parameters.Count == 0).FirstOrDefault();
  1116. if (ctor == null)
  1117. return (null, SpecialType.UnknownType);
  1118. return (() => new NewObj(ctor), type);
  1119. }
  1120. if (MatchGetConstructorFromHandle(invocation.Arguments[0], out var member))
  1121. {
  1122. return (() => new NewObj((IMethod)member), member.DeclaringType);
  1123. }
  1124. return (null, SpecialType.UnknownType);
  1125. case 2:
  1126. if (!MatchGetConstructorFromHandle(invocation.Arguments[0], out member))
  1127. return (null, SpecialType.UnknownType);
  1128. if (!MatchArgumentList(invocation.Arguments[1], out var arguments))
  1129. return (null, SpecialType.UnknownType);
  1130. IMethod method = (IMethod)member;
  1131. Func<ILInstruction>[] convertedArguments = ConvertCallArguments(arguments, method);
  1132. if (convertedArguments == null)
  1133. return (null, SpecialType.UnknownType);
  1134. return (() => BuildNewObj(method, convertedArguments), member.DeclaringType);
  1135. case 3:
  1136. if (!MatchGetConstructorFromHandle(invocation.Arguments[0], out member))
  1137. return (null, SpecialType.UnknownType);
  1138. if (!MatchArgumentList(invocation.Arguments[1], out arguments))
  1139. return (null, SpecialType.UnknownType);
  1140. method = (IMethod)member;
  1141. convertedArguments = ConvertCallArguments(arguments, method);
  1142. if (convertedArguments == null)
  1143. return (null, SpecialType.UnknownType);
  1144. return (() => BuildNewObj(method, convertedArguments), member.DeclaringType);
  1145. }
  1146. ILInstruction BuildNewObj(IMethod method, Func<ILInstruction>[] args)
  1147. {
  1148. var newObj = new NewObj(method);
  1149. newObj.Arguments.AddRange(args.Select(f => f()));
  1150. return newObj;
  1151. }
  1152. return (null, SpecialType.UnknownType);
  1153. }
  1154. (Func<ILInstruction>, IType) ConvertNotOperator(CallInstruction invocation)
  1155. {
  1156. if (invocation.Arguments.Count < 1)
  1157. return (null, SpecialType.UnknownType);
  1158. var (argument, argumentType) = ConvertInstruction(invocation.Arguments[0]);
  1159. if (argument == null)
  1160. return (null, SpecialType.UnknownType);
  1161. var underlyingType = NullableType.GetUnderlyingType(argumentType);
  1162. switch (invocation.Arguments.Count)
  1163. {
  1164. case 1:
  1165. bool isLifted = NullableType.IsNullable(argumentType);
  1166. return (() => underlyingType.IsKnownType(KnownTypeCode.Boolean)
  1167. ? Comp.LogicNot(argument(), isLifted)
  1168. : (ILInstruction)new BitNot(argument(), isLifted, underlyingType.GetStackType()), argumentType);
  1169. case 2:
  1170. if (!MatchGetMethodFromHandle(invocation.Arguments[1], out var method))
  1171. return (null, SpecialType.UnknownType);
  1172. return (() => new Call((IMethod)method) {
  1173. Arguments = { argument() }
  1174. }, method.ReturnType);
  1175. default:
  1176. return (null, SpecialType.UnknownType);
  1177. }
  1178. }
  1179. (Func<ILInstruction>, IType) ConvertProperty(CallInstruction invocation)
  1180. {
  1181. if (invocation.Arguments.Count < 2)
  1182. return (null, SpecialType.UnknownType);
  1183. Func<ILInstruction> targetConverter = null;
  1184. IType targetType = null;
  1185. if (!invocation.Arguments[0].MatchLdNull())
  1186. {
  1187. (targetConverter, targetType) = ConvertInstruction(invocation.Arguments[0]);
  1188. if (targetConverter == null)
  1189. return (null, SpecialType.UnknownType);
  1190. }
  1191. if (!MatchGetMethodFromHandle(invocation.Arguments[1], out var member))
  1192. return (null, SpecialType.UnknownType);
  1193. IList<ILInstruction> arguments;
  1194. if (invocation.Arguments.Count != 3 || !MatchArgumentList(invocation.Arguments[2], out arguments))
  1195. {
  1196. arguments = new List<ILInstruction>();
  1197. }
  1198. var convertedArguments = ConvertCallArguments(arguments, (IMethod)member);
  1199. if (convertedArguments == null)
  1200. return (null, SpecialType.UnknownType);
  1201. ILInstruction BuildProperty()
  1202. {
  1203. CallInstruction call;
  1204. if (member.IsStatic)
  1205. {
  1206. call = new Call((IMethod)member);
  1207. }
  1208. else
  1209. {
  1210. call = new CallVirt((IMethod)member);
  1211. }
  1212. if (targetConverter != null)
  1213. {
  1214. call.Arguments.Add(PrepareCallTarget(member.DeclaringType, targetConverter(), targetType));
  1215. }
  1216. call.Arguments.AddRange(convertedArguments.Select(f => f()));
  1217. return call;
  1218. }
  1219. return (BuildProperty, member.ReturnType);
  1220. }
  1221. (Func<ILInstruction>, IType) ConvertTypeAs(CallInstruction invocation)
  1222. {
  1223. if (invocation.Arguments.Count != 2)
  1224. return (null, SpecialType.UnknownType);
  1225. var converted = ConvertInstruction(invocation.Arguments[0]).Item1;
  1226. if (!MatchGetTypeFromHandle(invocation.Arguments[1], out var type))
  1227. return (null, SpecialType.UnknownType);
  1228. if (converted == null)
  1229. return (null, SpecialType.UnknownType);
  1230. ILInstruction BuildTypeAs()
  1231. {
  1232. ILInstruction inst = new IsInst(converted(), type);
  1233. // We must follow ECMA-335, III.4.6:
  1234. // If typeTok is a nullable type, Nullable<T>, it is interpreted as "boxed" T.
  1235. if (type.IsKnownType(KnownTypeCode.NullableOfT))
  1236. inst = new UnboxAny(inst, type);
  1237. return inst;
  1238. }
  1239. return (BuildTypeAs, type);
  1240. }
  1241. (Func<ILInstruction>, IType) ConvertTypeIs(CallInstruction invocation)
  1242. {
  1243. if (invocation.Arguments.Count != 2)
  1244. return (null, SpecialType.UnknownType);
  1245. var converted = ConvertInstruction(invocation.Arguments[0]).Item1;
  1246. if (!MatchGetTypeFromHandle(invocation.Arguments[1], out var type))
  1247. return (null, SpecialType.UnknownType);
  1248. var resultType = context.TypeSystem.FindType(KnownTypeCode.Boolean);
  1249. if (converted != null)
  1250. return (() => new Comp(ComparisonKind.Inequality, Sign.None, new IsInst(converted(), type), new LdNull()), resultType);
  1251. return (null, SpecialType.UnknownType);
  1252. }
  1253. (Func<ILInstruction>, IType) ConvertUnaryNumericOperator(CallInstruction invocation, BinaryNumericOperator op, bool? isChecked = null)
  1254. {
  1255. if (invocation.Arguments.Count < 1)
  1256. return (null, SpecialType.UnknownType);
  1257. var (argument, argumentType) = ConvertInstruction(invocation.Arguments[0]);
  1258. if (argument == null)
  1259. return (null, SpecialType.UnknownType);
  1260. switch (invocation.Arguments.Count)
  1261. {
  1262. case 1:
  1263. ILInstruction left;
  1264. var underlyingType = NullableType.GetUnderlyingType(argumentType);
  1265. switch (underlyingType.GetStackType())
  1266. {
  1267. case StackType.I4:
  1268. left = new LdcI4(0);
  1269. break;
  1270. case StackType.I8:
  1271. left = new LdcI8(0);
  1272. break;
  1273. case StackType.I:
  1274. left = new Conv(new LdcI4(0), PrimitiveType.I, false, Sign.None);
  1275. break;
  1276. case StackType.F4:
  1277. left = new LdcF4(0);
  1278. break;
  1279. case StackType.F8:
  1280. left = new LdcF8(0);
  1281. break;
  1282. case StackType.O when underlyingType.IsKnownType(KnownTypeCode.Decimal):
  1283. left = new LdcDecimal(0);
  1284. break;
  1285. default:
  1286. return (null, SpecialType.UnknownType);
  1287. }
  1288. return (() => new BinaryNumericInstruction(op, left, argument(),
  1289. underlyingType.GetStackType(),
  1290. underlyingType.GetStackType(),
  1291. isChecked == true,
  1292. argumentType.GetSign(),
  1293. isLifted: NullableType.IsNullable(argumentType)), argumentType);
  1294. case 2:
  1295. if (!MatchGetMethodFromHandle(invocation.Arguments[1], out var method))
  1296. return (null, SpecialType.UnknownType);
  1297. return (() => new Call((IMethod)method) {
  1298. Arguments = { argument() }
  1299. }, method.ReturnType);
  1300. }
  1301. return (null, SpecialType.UnknownType);
  1302. }
  1303. ILInstruction ConvertValue(ILInstruction value, ILInstruction context)
  1304. {
  1305. switch (value)
  1306. {
  1307. case LdLoc ldloc:
  1308. if (IsExpressionTreeParameter(ldloc.Variable))
  1309. {
  1310. if (!parameterMapping.TryGetValue(ldloc.Variable, out var v))
  1311. return ldloc.Clone();
  1312. if (context is CallInstruction parentCall
  1313. && parentCall.Method.FullName == "System.Linq.Expressions.Expression.Call"
  1314. && v.StackType.IsIntegerType())
  1315. return new LdLoca(v).WithILRange(ldloc);
  1316. return null;
  1317. }
  1318. else if (IsClosureReference(ldloc.Variable))
  1319. {
  1320. if (ldloc.Variable.Kind == VariableKind.Local)
  1321. {
  1322. ldloc.Variable.Kind = VariableKind.DisplayClassLocal;
  1323. }
  1324. if (ldloc.Variable.CaptureScope == null)
  1325. {
  1326. ldloc.Variable.CaptureScope = BlockContainer.FindClosestContainer(context);
  1327. var f = ldloc.Variable.CaptureScope.Ancestors.OfType<ILFunction>().FirstOrDefault();
  1328. if (f != null)
  1329. {
  1330. f.CapturedVariables.Add(ldloc.Variable);
  1331. }
  1332. }
  1333. return ldloc;
  1334. }
  1335. else
  1336. {
  1337. return ldloc;
  1338. }
  1339. default:
  1340. return value.Clone();
  1341. }
  1342. }
  1343. bool IsClosureReference(ILVariable variable)
  1344. {
  1345. if (!variable.IsSingleDefinition || !(variable.StoreInstructions.SingleOrDefault() is StLoc store))
  1346. return false;
  1347. if (!(store.Value is NewObj newObj))
  1348. return false;
  1349. return TransformDisplayClassUsage.IsPotentialClosure(this.context, newObj);
  1350. }
  1351. bool IsExpressionTreeParameter(ILVariable variable)
  1352. {
  1353. return variable.Type.FullName == "System.Linq.Expressions.ParameterExpression";
  1354. }
  1355. bool MatchConstantCall(ILInstruction inst, out ILInstruction value, out IType type)
  1356. {
  1357. value = null;
  1358. type = null;
  1359. if (inst is CallInstruction call && call.Method.FullName == "System.Linq.Expressions.Expression.Constant")
  1360. {
  1361. value = call.Arguments[0];
  1362. if (call.Arguments.Count == 2)
  1363. return MatchGetTypeFromHandle(call.Arguments[1], out type);
  1364. type = value switch {
  1365. LdNull => SpecialType.NullType,
  1366. LdStr => context.TypeSystem.FindType(KnownTypeCode.String),
  1367. LdcF4 => context.TypeSystem.FindType(KnownTypeCode.Single),
  1368. LdcF8 => context.TypeSystem.FindType(KnownTypeCode.Double),
  1369. LdcI4 => context.TypeSystem.FindType(KnownTypeCode.Int32),
  1370. LdcI8 => context.TypeSystem.FindType(KnownTypeCode.Int64),
  1371. _ => value.InferType(context.TypeSystem),
  1372. };
  1373. return true;
  1374. }
  1375. return false;
  1376. }
  1377. internal static bool MatchGetTypeFromHandle(ILInstruction inst, out IType type)
  1378. {
  1379. type = null;
  1380. return inst is CallInstruction getTypeCall
  1381. && getTypeCall.Method.FullName == "System.Type.GetTypeFromHandle"
  1382. && getTypeCall.Arguments.Count == 1
  1383. && getTypeCall.Arguments[0].MatchLdTypeToken(out type);
  1384. }
  1385. bool MatchGetMethodFromHandle(ILInstruction inst, out IMember member)
  1386. {
  1387. member = null;
  1388. //castclass System.Reflection.MethodInfo(call GetMethodFromHandle(ldmembertoken op_Addition))
  1389. if (!inst.MatchCastClass(out var arg, out var type))
  1390. return false;
  1391. if (type.FullName != "System.Reflection.MethodInfo")
  1392. return false;
  1393. if (!(arg is CallInstruction call && call.Method.FullName == "System.Reflection.MethodBase.GetMethodFromHandle"))
  1394. return false;
  1395. return MatchFromHandleParameterList(call, out member);
  1396. }
  1397. bool MatchGetConstructorFromHandle(ILInstruction inst, out IMember member)
  1398. {
  1399. member = null;
  1400. //castclass System.Reflection.ConstructorInfo(call GetMethodFromHandle(ldmembertoken op_Addition))
  1401. if (!inst.MatchCastClass(out var arg, out var type))
  1402. return false;
  1403. if (type.FullName != "System.Reflection.ConstructorInfo")
  1404. return false;
  1405. if (!(arg is CallInstruction call && call.Method.FullName == "System.Reflection.MethodBase.GetMethodFromHandle"))
  1406. return false;
  1407. return MatchFromHandleParameterList(call, out member);
  1408. }
  1409. bool MatchGetFieldFromHandle(ILInstruction inst, out IMember member)
  1410. {
  1411. member = null;
  1412. if (!(inst is CallInstruction call && call.Method.FullName == "System.Reflection.FieldInfo.GetFieldFromHandle"))
  1413. return false;
  1414. return MatchFromHandleParameterList(call, out member);
  1415. }
  1416. static bool MatchFromHandleParameterList(CallInstruction call, out IMember member)
  1417. {
  1418. member = null;
  1419. switch (call.Arguments.Count)
  1420. {
  1421. case 1:
  1422. if (!call.Arguments[0].MatchLdMemberToken(out member))
  1423. return false;
  1424. break;
  1425. case 2:
  1426. if (!call.Arguments[0].MatchLdMemberToken(out member))
  1427. return false;
  1428. if (!call.Arguments[1].MatchLdTypeToken(out _))
  1429. return false;
  1430. break;
  1431. default:
  1432. return false;
  1433. }
  1434. return true;
  1435. }
  1436. bool MatchArgumentList(ILInstruction inst, out IList<ILInstruction> arguments)
  1437. {
  1438. arguments = null;
  1439. if (!(inst is Block block && block.Kind == BlockKind.ArrayInitializer))
  1440. {
  1441. if (IsEmptyParameterList(inst))
  1442. {
  1443. arguments = new List<ILInstruction>();
  1444. return true;
  1445. }
  1446. return false;
  1447. }
  1448. int i = 0;
  1449. arguments = new List<ILInstruction>();
  1450. foreach (var item in block.Instructions.OfType<StObj>())
  1451. {
  1452. if (!(item.Target is LdElema ldelem && ldelem.Indices.Single().MatchLdcI4(i)))
  1453. return false;
  1454. arguments.Add(item.Value);
  1455. i++;
  1456. }
  1457. return true;
  1458. }
  1459. }
  1460. }