mirror of https://github.com/icsharpcode/ILSpy.git
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
951 lines
34 KiB
951 lines
34 KiB
// Copyright (c) 2017 Siegfried Pammer |
|
// |
|
// Permission is hereby granted, free of charge, to any person obtaining a copy of this |
|
// software and associated documentation files (the "Software"), to deal in the Software |
|
// without restriction, including without limitation the rights to use, copy, modify, merge, |
|
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons |
|
// to whom the Software is furnished to do so, subject to the following conditions: |
|
// |
|
// The above copyright notice and this permission notice shall be included in all copies or |
|
// substantial portions of the Software. |
|
// |
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, |
|
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR |
|
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE |
|
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR |
|
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER |
|
// DEALINGS IN THE SOFTWARE. |
|
|
|
using System; |
|
using System.Collections.Generic; |
|
using System.Linq; |
|
using ICSharpCode.Decompiler.CSharp; |
|
using ICSharpCode.Decompiler.CSharp.Syntax; |
|
using ICSharpCode.Decompiler.TypeSystem; |
|
using ICSharpCode.Decompiler.TypeSystem.Implementation; |
|
using ICSharpCode.Decompiler.Util; |
|
|
|
namespace ICSharpCode.Decompiler.IL.Transforms |
|
{ |
|
public class TransformExpressionTrees : IStatementTransform |
|
{ |
|
static bool MightBeExpressionTree(ILInstruction inst, ILInstruction stmt) |
|
{ |
|
if (!(inst is CallInstruction call |
|
&& call.Method.FullName == "System.Linq.Expressions.Expression.Lambda" |
|
&& call.Arguments.Count == 2)) |
|
return false; |
|
if (call.Parent is CallInstruction parentCall && parentCall.Method.FullName == "System.Linq.Expressions.Expression.Quote") |
|
return false; |
|
if (!(IsEmptyParameterList(call.Arguments[1]) || (call.Arguments[1] is Block block && block.Kind == BlockKind.ArrayInitializer))) |
|
return false; |
|
//if (!ILInlining.CanUninline(call, stmt)) |
|
// return false; |
|
return true; |
|
} |
|
|
|
static bool IsEmptyParameterList(ILInstruction inst) |
|
{ |
|
if (inst is CallInstruction emptyCall && emptyCall.Method.FullName == "System.Array.Empty" && emptyCall.Arguments.Count == 0) |
|
return true; |
|
if (inst.MatchNewArr(out var type) && type.FullName == "System.Linq.Expressions.ParameterExpression") |
|
return true; |
|
if (inst.MatchNewArr(out type) && type.FullName == "System.Linq.Expressions.Expression") |
|
return true; |
|
return false; |
|
} |
|
|
|
bool MatchParameterVariableAssignment(ILInstruction expr, out ILVariable parameterReferenceVar, out IType type, out string name) |
|
{ |
|
// stloc(v, call(Expression::Parameter, call(Type::GetTypeFromHandle, ldtoken(...)), ldstr(...))) |
|
type = null; |
|
name = null; |
|
if (!expr.MatchStLoc(out parameterReferenceVar, out var init)) |
|
return false; |
|
if (!parameterReferenceVar.IsSingleDefinition) |
|
return false; |
|
if (!(parameterReferenceVar.Kind == VariableKind.Local || parameterReferenceVar.Kind == VariableKind.StackSlot)) |
|
return false; |
|
if (parameterReferenceVar.Type == null || parameterReferenceVar.Type.FullName != "System.Linq.Expressions.ParameterExpression") |
|
return false; |
|
if (!(init is CallInstruction initCall && initCall.Arguments.Count == 2)) |
|
return false; |
|
IMethod parameterMethod = initCall.Method; |
|
if (!(parameterMethod.Name == "Parameter" && parameterMethod.DeclaringType.FullName == "System.Linq.Expressions.Expression")) |
|
return false; |
|
CallInstruction typeArg = initCall.Arguments[0] as CallInstruction; |
|
if (typeArg == null || typeArg.Arguments.Count != 1) |
|
return false; |
|
if (typeArg.Method.FullName != "System.Type.GetTypeFromHandle") |
|
return false; |
|
return typeArg.Arguments[0].MatchLdTypeToken(out type) && initCall.Arguments[1].MatchLdStr(out name); |
|
} |
|
|
|
StatementTransformContext context; |
|
Dictionary<ILVariable, (IType, string)> parameters; |
|
Dictionary<ILVariable, ILVariable> parameterMapping; |
|
List<ILInstruction> instructionsToRemove; |
|
|
|
public void Run(Block block, int pos, StatementTransformContext context) |
|
{ |
|
this.context = context; |
|
this.parameters = new Dictionary<ILVariable, (IType, string)>(); |
|
this.parameterMapping = new Dictionary<ILVariable, ILVariable>(); |
|
this.instructionsToRemove = new List<ILInstruction>(); |
|
for (int i = pos; i < block.Instructions.Count; i++) { |
|
if (MatchParameterVariableAssignment(block.Instructions[i], out var v, out var type, out var name)) { |
|
parameters.Add(v, (type, name)); |
|
continue; |
|
} |
|
if (TryConvertExpressionTree(block.Instructions[i], block.Instructions[i])) { |
|
foreach (var inst in instructionsToRemove) |
|
block.Instructions.Remove(inst); |
|
instructionsToRemove.Clear(); |
|
continue; |
|
} |
|
} |
|
} |
|
|
|
bool TryConvertExpressionTree(ILInstruction instruction, ILInstruction statement) |
|
{ |
|
if (MightBeExpressionTree(instruction, statement)) { |
|
var lambda = ConvertLambda((CallInstruction)instruction); |
|
if (lambda != null) { |
|
context.Step("Convert Expression Tree", instruction); |
|
instruction.ReplaceWith(lambda); |
|
return true; |
|
} |
|
return false; |
|
} |
|
foreach (var child in instruction.Children) { |
|
if (TryConvertExpressionTree(child, statement)) |
|
return true; |
|
} |
|
return false; |
|
} |
|
|
|
NewObj ConvertLambda(CallInstruction instruction) |
|
{ |
|
if (instruction.Method.Name != "Lambda" || instruction.Arguments.Count != 2 || instruction.Method.ReturnType.FullName != "System.Linq.Expressions.Expression" || instruction.Method.ReturnType.TypeArguments.Count != 1) |
|
return null; |
|
var parameterList = new List<IParameter>(); |
|
var parameterVariablesList = new List<ILVariable>(); |
|
if (!ReadParameters(instruction.Arguments[1], parameterList, parameterVariablesList, new SimpleTypeResolveContext(context.Function.Method))) |
|
return null; |
|
var bodyInstruction = ConvertInstruction(instruction.Arguments[0]); |
|
if (bodyInstruction == null) |
|
return null; |
|
var container = new BlockContainer(expectedResultType: bodyInstruction.ResultType); |
|
container.Blocks.Add(new Block() { Instructions = { new Leave(container, bodyInstruction) } }); |
|
var function = new ILFunction(instruction.Method.ReturnType.TypeArguments[0].TypeArguments.LastOrDefault() ?? context.TypeSystem.Compilation.FindType(KnownTypeCode.Void), parameterList, container); |
|
function.IsExpressionTree = true; |
|
function.Variables.AddRange(parameterVariablesList); |
|
return new NewObj(instruction.Method.ReturnType.TypeArguments[0].GetConstructors().First()) { |
|
Arguments = { |
|
new LdNull(), |
|
function |
|
} |
|
}; |
|
} |
|
|
|
bool ReadParameters(ILInstruction initializer, IList<IParameter> parameters, IList<ILVariable> parameterVariables, ITypeResolveContext resolveContext) |
|
{ |
|
if (!context.Function.Method.IsStatic) { |
|
var thisParam = context.Function.Variables[0]; |
|
parameterVariables.Add(new ILVariable(VariableKind.Parameter, thisParam.Type, -1) { Name = "this" }); |
|
} |
|
switch (initializer) { |
|
case Block initializerBlock: |
|
if (initializerBlock.Kind != BlockKind.ArrayInitializer) |
|
return false; |
|
int i = 0; |
|
foreach (var inst in initializerBlock.Instructions.OfType<StObj>()) { |
|
if (i >= this.parameters.Count) |
|
return false; |
|
if (!inst.Value.MatchLdLoc(out var v)) |
|
return false; |
|
if (!this.parameters.TryGetValue(v, out var value)) |
|
return false; |
|
var param = new ILVariable(VariableKind.Parameter, value.Item1, i) { Name = value.Item2 }; |
|
parameterMapping.Add(v, param); |
|
parameterVariables.Add(param); |
|
parameters.Add(new DefaultUnresolvedParameter(value.Item1.ToTypeReference(), value.Item2).CreateResolvedParameter(resolveContext)); |
|
instructionsToRemove.Add((ILInstruction)v.StoreInstructions[0]); |
|
i++; |
|
} |
|
return true; |
|
default: |
|
return IsEmptyParameterList(initializer); |
|
} |
|
} |
|
|
|
ILInstruction ConvertInstruction(ILInstruction instruction) |
|
{ |
|
switch (instruction) { |
|
case CallInstruction invocation: |
|
if (invocation.Method.DeclaringType.FullName != "System.Linq.Expressions.Expression") |
|
return null; |
|
|
|
switch (invocation.Method.Name) { |
|
case "Add": |
|
return ConvertBinaryNumericOperator(invocation, BinaryNumericOperator.Add, false); |
|
case "AddChecked": |
|
return ConvertBinaryNumericOperator(invocation, BinaryNumericOperator.Add, true); |
|
case "And": |
|
return ConvertBinaryNumericOperator(invocation, BinaryNumericOperator.BitAnd); |
|
case "AndAlso": |
|
return ConvertLogicOperator(invocation, true); |
|
case "ArrayAccess": |
|
case "ArrayIndex": |
|
return ConvertArrayIndex(invocation); |
|
case "ArrayLength": |
|
return ConvertArrayLength(invocation); |
|
case "Call": |
|
return ConvertCall(invocation); |
|
case "Coalesce": |
|
return ConvertCoalesce(invocation); |
|
case "Condition": |
|
return ConvertCondition(invocation); |
|
case "Constant": |
|
return ConvertConstant(invocation); |
|
case "Convert": |
|
return ConvertCast(invocation, false); |
|
case "ConvertChecked": |
|
return ConvertCast(invocation, true); |
|
case "Divide": |
|
return ConvertBinaryNumericOperator(invocation, BinaryNumericOperator.Div); |
|
case "Equal": |
|
return ConvertComparison(invocation, ComparisonKind.Equality); |
|
case "ExclusiveOr": |
|
return ConvertBinaryNumericOperator(invocation, BinaryNumericOperator.BitXor); |
|
case "Field": |
|
return ConvertField(invocation); |
|
case "GreaterThan": |
|
return ConvertComparison(invocation, ComparisonKind.GreaterThan); |
|
case "GreaterThanOrEqual": |
|
return ConvertComparison(invocation, ComparisonKind.GreaterThanOrEqual); |
|
case "Invoke": |
|
return ConvertInvoke(invocation); |
|
case "Lambda": |
|
return ConvertLambda(invocation); |
|
case "LeftShift": |
|
return ConvertBinaryNumericOperator(invocation, BinaryNumericOperator.ShiftLeft); |
|
case "LessThan": |
|
return ConvertComparison(invocation, ComparisonKind.LessThan); |
|
case "LessThanOrEqual": |
|
return ConvertComparison(invocation, ComparisonKind.LessThanOrEqual); |
|
case "ListInit": |
|
return ConvertListInit(invocation); |
|
case "MemberInit": |
|
return ConvertMemberInit(invocation); |
|
case "Modulo": |
|
return ConvertBinaryNumericOperator(invocation, BinaryNumericOperator.Rem); |
|
case "Multiply": |
|
return ConvertBinaryNumericOperator(invocation, BinaryNumericOperator.Mul, false); |
|
case "MultiplyChecked": |
|
return ConvertBinaryNumericOperator(invocation, BinaryNumericOperator.Mul, true); |
|
case "Negate": |
|
return ConvertUnaryNumericOperator(invocation, BinaryNumericOperator.Sub, false); |
|
case "NegateChecked": |
|
return ConvertUnaryNumericOperator(invocation, BinaryNumericOperator.Sub, true); |
|
case "New": |
|
return ConvertNewObject(invocation); |
|
case "NewArrayBounds": |
|
return ConvertNewArrayBounds(invocation); |
|
case "NewArrayInit": |
|
return ConvertNewArrayInit(invocation); |
|
case "Not": |
|
return ConvertNotOperator(invocation); |
|
case "NotEqual": |
|
return ConvertComparison(invocation, ComparisonKind.Inequality); |
|
case "OnesComplement": |
|
return ConvertNotOperator(invocation); |
|
case "Or": |
|
return ConvertBinaryNumericOperator(invocation, BinaryNumericOperator.BitOr); |
|
case "OrElse": |
|
return ConvertLogicOperator(invocation, false); |
|
case "Property": |
|
return ConvertProperty(invocation); |
|
case "Quote": |
|
if (invocation.Arguments.Count == 1) |
|
return ConvertInstruction(invocation.Arguments.Single()); |
|
else |
|
return null; |
|
case "RightShift": |
|
return ConvertBinaryNumericOperator(invocation, BinaryNumericOperator.ShiftRight); |
|
case "Subtract": |
|
return ConvertBinaryNumericOperator(invocation, BinaryNumericOperator.Sub, false); |
|
case "SubtractChecked": |
|
return ConvertBinaryNumericOperator(invocation, BinaryNumericOperator.Sub, true); |
|
case "TypeAs": |
|
return ConvertTypeAs(invocation); |
|
case "TypeIs": |
|
return ConvertTypeIs(invocation); |
|
} |
|
return null; |
|
default: |
|
return ConvertValue(instruction, instruction.Parent); |
|
} |
|
} |
|
|
|
ILInstruction ConvertArrayIndex(CallInstruction invocation) |
|
{ |
|
if (invocation.Arguments.Count != 2) |
|
return null; |
|
var array = ConvertInstruction(invocation.Arguments[0]); |
|
if (array == null) |
|
return null; |
|
var arrayType = InferType(array); |
|
if (!(arrayType is ArrayType type)) |
|
return null; |
|
if (!MatchArgumentList(invocation.Arguments[1], out var arguments)) |
|
arguments = new[] { invocation.Arguments[1] }; |
|
for (int i = 0; i < arguments.Count; i++) { |
|
var converted = ConvertInstruction(arguments[i]); |
|
if (converted == null) |
|
return null; |
|
arguments[i] = converted; |
|
} |
|
return new LdObj(new LdElema(type.ElementType, array, arguments.ToArray()), type.ElementType); |
|
} |
|
|
|
ILInstruction ConvertArrayLength(CallInstruction invocation) |
|
{ |
|
if (invocation.Arguments.Count != 1) |
|
return null; |
|
var converted = ConvertInstruction(invocation.Arguments[0]); |
|
if (converted == null) |
|
return null; |
|
return new LdLen(StackType.I4, converted); |
|
} |
|
|
|
ILInstruction ConvertBinaryNumericOperator(CallInstruction invocation, BinaryNumericOperator op, bool? isChecked = null) |
|
{ |
|
if (invocation.Arguments.Count < 2) |
|
return null; |
|
var left = ConvertInstruction(invocation.Arguments[0]); |
|
if (left == null) |
|
return null; |
|
var right = ConvertInstruction(invocation.Arguments[1]); |
|
if (right == null) |
|
return null; |
|
IMember method; |
|
switch (invocation.Arguments.Count) { |
|
case 2: |
|
return new BinaryNumericInstruction(op, left, right, isChecked == true, Sign.None); |
|
case 3: |
|
if (!MatchGetMethodFromHandle(invocation.Arguments[2], out method)) |
|
return null; |
|
return new Call((IMethod)method) { |
|
Arguments = { left, right } |
|
}; |
|
case 4: |
|
//if (!trueOrFalse.IsMatch(invocation.Arguments[2])) |
|
// return null; |
|
if (!MatchGetMethodFromHandle(invocation.Arguments[3], out method)) |
|
return null; |
|
return new Call((IMethod)method) { |
|
Arguments = { left, right } |
|
}; |
|
default: |
|
return null; |
|
} |
|
} |
|
|
|
ILInstruction ConvertCall(CallInstruction invocation) |
|
{ |
|
if (invocation.Arguments.Count < 2) |
|
return null; |
|
IList<ILInstruction> arguments = null; |
|
if (MatchGetMethodFromHandle(invocation.Arguments[0], out var member)) { |
|
// static method |
|
if (invocation.Arguments.Count != 2 || !MatchArgumentList(invocation.Arguments[1], out arguments)) { |
|
arguments = new List<ILInstruction>(invocation.Arguments.Skip(1)); |
|
} |
|
} else if (MatchGetMethodFromHandle(invocation.Arguments[1], out member)) { |
|
if (invocation.Arguments.Count != 3 || !MatchArgumentList(invocation.Arguments[2], out arguments)) { |
|
arguments = new List<ILInstruction>(invocation.Arguments.Skip(2)); |
|
} |
|
if (!invocation.Arguments[0].MatchLdNull()) |
|
arguments.Insert(0, invocation.Arguments[0]); |
|
} |
|
if (arguments == null) |
|
return null; |
|
arguments = arguments.Select(ConvertInstruction).ToArray(); |
|
if (arguments.Any(p => p == null)) |
|
return null; |
|
IMethod method = (IMethod)member; |
|
if (method.FullName == "System.Reflection.MethodInfo.CreateDelegate" && method.Parameters.Count == 2) { |
|
if (!MatchGetMethodFromHandle(arguments[0], out var targetMethod)) |
|
return null; |
|
if (!MatchGetTypeFromHandle(arguments[1], out var delegateType)) |
|
return null; |
|
return new NewObj(delegateType.GetConstructors().Single()) { |
|
Arguments = { arguments[2], new LdFtn((IMethod)targetMethod) } |
|
}; |
|
} |
|
CallInstruction call; |
|
if (method.IsAbstract || method.IsVirtual || method.IsOverridable) { |
|
call = new CallVirt(method); |
|
} else { |
|
call = new Call(method); |
|
} |
|
call.Arguments.AddRange(arguments); |
|
return call; |
|
} |
|
|
|
ILInstruction ConvertCast(CallInstruction invocation, bool isChecked) |
|
{ |
|
if (invocation.Arguments.Count < 2) |
|
return null; |
|
if (!MatchGetTypeFromHandle(invocation.Arguments[1], out var targetType)) |
|
return null; |
|
var expr = ConvertInstruction(invocation.Arguments[0]); |
|
if (expr == null) |
|
return null; |
|
return new ExpressionTreeCast(targetType, expr, isChecked); |
|
} |
|
|
|
ILInstruction ConvertCoalesce(CallInstruction invocation) |
|
{ |
|
if (invocation.Arguments.Count != 2) |
|
return null; |
|
var trueInst = ConvertInstruction(invocation.Arguments[0]); |
|
if (trueInst == null) |
|
return null; |
|
var fallbackInst = ConvertInstruction(invocation.Arguments[1]); |
|
if (fallbackInst == null) |
|
return null; |
|
// TODO : nullable types? |
|
return new NullCoalescingInstruction(NullCoalescingKind.Ref, trueInst, fallbackInst); |
|
} |
|
|
|
ILInstruction ConvertComparison(CallInstruction invocation, ComparisonKind kind) |
|
{ |
|
if (invocation.Arguments.Count < 2) |
|
return null; |
|
var left = ConvertInstruction(invocation.Arguments[0]); |
|
if (left == null) |
|
return null; |
|
var right = ConvertInstruction(invocation.Arguments[1]); |
|
if (right == null) |
|
return null; |
|
if (invocation.Arguments.Count == 3 && MatchGetMethodFromHandle(invocation.Arguments[2], out var method)) { |
|
return new Call((IMethod)method) { Arguments = { left, right } }; |
|
} |
|
// TODO: Sign?? |
|
return new Comp(kind, Sign.None, left, right); |
|
} |
|
|
|
ILInstruction ConvertCondition(CallInstruction invocation) |
|
{ |
|
if (invocation.Arguments.Count != 3) |
|
return null; |
|
var condition = ConvertInstruction(invocation.Arguments[0]); |
|
if (condition == null) |
|
return null; |
|
var trueInst = ConvertInstruction(invocation.Arguments[1]); |
|
if (trueInst == null) |
|
return null; |
|
var falseInst = ConvertInstruction(invocation.Arguments[2]); |
|
if (falseInst == null) |
|
return null; |
|
return new IfInstruction(condition, trueInst, falseInst); |
|
} |
|
|
|
ILInstruction ConvertConstant(CallInstruction invocation) |
|
{ |
|
if (!MatchConstantCall(invocation, out var value, out var type)) |
|
return null; |
|
if (value.MatchBox(out var arg, out var boxType)) { |
|
if (boxType.Kind == TypeKind.Enum) |
|
return new ExpressionTreeCast(boxType, ConvertValue(arg, invocation), false); |
|
value = arg; |
|
} |
|
return ConvertValue(value, invocation); |
|
} |
|
|
|
ILInstruction ConvertElementInit(CallInstruction invocation) |
|
{ |
|
if (invocation.Arguments.Count != 2) |
|
return null; |
|
if (!MatchGetMethodFromHandle(invocation.Arguments[0], out var member)) |
|
return null; |
|
if (!MatchArgumentList(invocation.Arguments[1], out var arguments)) |
|
return null; |
|
CallInstruction call = new Call((IMethod)member); |
|
for (int i = 0; i < arguments.Count; i++) { |
|
ILInstruction arg = ConvertInstruction(arguments[i]); |
|
if (arg == null) |
|
return null; |
|
arguments[i] = arg; |
|
} |
|
call.Arguments.AddRange(arguments); |
|
return call; |
|
} |
|
|
|
ILInstruction ConvertField(CallInstruction invocation) |
|
{ |
|
if (invocation.Arguments.Count != 2) |
|
return null; |
|
ILInstruction target = null; |
|
if (!invocation.Arguments[0].MatchLdNull()) { |
|
target = ConvertInstruction(invocation.Arguments[0]); |
|
if (target == null) |
|
return null; |
|
} |
|
if (!MatchGetFieldFromHandle(invocation.Arguments[1], out var member)) |
|
return null; |
|
if (target == null) { |
|
return new LdObj(new LdsFlda((IField)member), member.ReturnType); |
|
} else { |
|
return new LdObj(new LdFlda(target, (IField)member), member.ReturnType); |
|
} |
|
} |
|
|
|
ILInstruction ConvertInvoke(CallInstruction invocation) |
|
{ |
|
if (invocation.Arguments.Count != 2) |
|
return null; |
|
var target = ConvertInstruction(invocation.Arguments[0]); |
|
if (target == null) |
|
return null; |
|
var invokeMethod = InferType(target).GetDelegateInvokeMethod(); |
|
if (invokeMethod == null) |
|
return null; |
|
if (!MatchArgumentList(invocation.Arguments[1], out var arguments)) |
|
return null; |
|
for (int i = 0; i < arguments.Count; i++) { |
|
var arg = ConvertInstruction(arguments[i]); |
|
if (arg == null) |
|
return null; |
|
arguments[i] = arg; |
|
} |
|
var call = new Call(invokeMethod); |
|
call.Arguments.Add(target); |
|
call.Arguments.AddRange(arguments); |
|
return call; |
|
} |
|
|
|
ILInstruction ConvertListInit(CallInstruction invocation) |
|
{ |
|
if (invocation.Arguments.Count < 2) |
|
return null; |
|
var newObj = ConvertInstruction(invocation.Arguments[0]) as NewObj; |
|
if (newObj == null) |
|
return null; |
|
IList<ILInstruction> arguments = null; |
|
ILFunction function; |
|
if (!MatchGetMethodFromHandle(invocation.Arguments[1], out var member)) { |
|
if (!MatchArgumentList(invocation.Arguments[1], out arguments)) |
|
return null; |
|
function = ((LdLoc)((Block)invocation.Arguments[1]).FinalInstruction).Variable.Function; |
|
} else { |
|
if (invocation.Arguments.Count != 3 || !MatchArgumentList(invocation.Arguments[2], out arguments)) |
|
return null; |
|
function = ((LdLoc)((Block)invocation.Arguments[2]).FinalInstruction).Variable.Function; |
|
} |
|
if (arguments == null || arguments.Count == 0) |
|
return null; |
|
var initializer = function.RegisterVariable(VariableKind.InitializerTarget, newObj.Method.DeclaringType); |
|
for (int i = 0; i < arguments.Count; i++) { |
|
ILInstruction arg; |
|
if (arguments[i] is CallInstruction elementInit && elementInit.Method.FullName == "System.Linq.Expressions.Expression.ElementInit") { |
|
arg = ConvertElementInit(elementInit); |
|
if (arg == null) |
|
return null; |
|
((CallInstruction)arg).Arguments.Insert(0, new LdLoc(initializer)); |
|
} else { |
|
arg = ConvertInstruction(arguments[i]); |
|
if (arg == null) |
|
return null; |
|
} |
|
arguments[i] = arg; |
|
} |
|
var initializerBlock = new Block(BlockKind.CollectionInitializer); |
|
initializerBlock.FinalInstruction = new LdLoc(initializer); |
|
initializerBlock.Instructions.Add(new StLoc(initializer, newObj)); |
|
initializerBlock.Instructions.AddRange(arguments); |
|
return initializerBlock; |
|
} |
|
|
|
ILInstruction ConvertLogicOperator(CallInstruction invocation, bool and) |
|
{ |
|
if (invocation.Arguments.Count < 2) |
|
return null; |
|
var left = ConvertInstruction(invocation.Arguments[0]); |
|
if (left == null) |
|
return null; |
|
var right = ConvertInstruction(invocation.Arguments[1]); |
|
if (right == null) |
|
return null; |
|
IMember method; |
|
switch (invocation.Arguments.Count) { |
|
case 2: |
|
return and ? IfInstruction.LogicAnd(left, right) : IfInstruction.LogicOr(left, right); |
|
case 3: |
|
if (!MatchGetMethodFromHandle(invocation.Arguments[2], out method)) |
|
return null; |
|
return new Call((IMethod)method) { |
|
Arguments = { left, right } |
|
}; |
|
case 4: |
|
//if (!trueOrFalse.IsMatch(invocation.Arguments[2])) |
|
// return null; |
|
if (!MatchGetMethodFromHandle(invocation.Arguments[3], out method)) |
|
return null; |
|
return new Call((IMethod)method) { |
|
Arguments = { left, right } |
|
}; |
|
default: |
|
return null; |
|
} |
|
} |
|
|
|
ILInstruction ConvertMemberInit(CallInstruction invocation) |
|
{ |
|
return null; |
|
} |
|
|
|
ILInstruction ConvertNewArrayBounds(CallInstruction invocation) |
|
{ |
|
if (invocation.Arguments.Count != 2) |
|
return null; |
|
if (!MatchGetTypeFromHandle(invocation.Arguments[0], out var type)) |
|
return null; |
|
if (!MatchArgumentList(invocation.Arguments[1], out var arguments)) |
|
return null; |
|
if (arguments.Count == 0) |
|
return null; |
|
var indices = new ILInstruction[arguments.Count]; |
|
for (int i = 0; i < arguments.Count; i++) { |
|
var index = ConvertInstruction(arguments[i]); |
|
if (index == null) |
|
return null; |
|
indices[i] = index; |
|
} |
|
return new NewArr(type, indices); |
|
} |
|
|
|
ILInstruction ConvertNewArrayInit(CallInstruction invocation) |
|
{ |
|
if (invocation.Arguments.Count != 2) |
|
return null; |
|
if (!MatchGetTypeFromHandle(invocation.Arguments[0], out var type)) |
|
return null; |
|
if (!MatchArgumentList(invocation.Arguments[1], out var arguments)) |
|
return null; |
|
if (arguments.Count == 0) |
|
return null; |
|
var block = (Block)invocation.Arguments[1]; |
|
var function = ((LdLoc)block.FinalInstruction).Variable.Function; |
|
var variable = function.RegisterVariable(VariableKind.InitializerTarget, new ArrayType(context.BlockContext.TypeSystem.Compilation, type)); |
|
Block initializer = new Block(BlockKind.ArrayInitializer); |
|
int i = 0; |
|
initializer.Instructions.Add(new StLoc(variable, new NewArr(type, new LdcI4(arguments.Count)))); |
|
foreach (var item in arguments) { |
|
var value = ConvertInstruction(item); |
|
if (value == null) |
|
return null; |
|
initializer.Instructions.Add(new StObj(new LdElema(type, new LdLoc(variable), new LdcI4(i)), value, type)); |
|
} |
|
initializer.FinalInstruction = new LdLoc(variable); |
|
return initializer; |
|
} |
|
|
|
ILInstruction ConvertNewObject(CallInstruction invocation) |
|
{ |
|
IMember member; |
|
IList<ILInstruction> arguments; |
|
NewObj newObj; |
|
switch (invocation.Arguments.Count) { |
|
case 1: |
|
if (MatchGetTypeFromHandle(invocation.Arguments[0], out var type)) { |
|
var ctors = type.GetConstructors().ToArray(); |
|
if (ctors.Length != 1 || ctors[0].Parameters.Count > 0) |
|
return null; |
|
return new NewObj(ctors[0]); |
|
} |
|
if (MatchGetConstructorFromHandle(invocation.Arguments[0], out member)) { |
|
return new NewObj((IMethod)member); |
|
} |
|
return null; |
|
case 2: |
|
if (!MatchGetConstructorFromHandle(invocation.Arguments[0], out member)) |
|
return null; |
|
if (!MatchArgumentList(invocation.Arguments[1], out arguments)) |
|
return null; |
|
var args = arguments.SelectArray(ConvertInstruction); |
|
if (args.Any(a => a == null)) |
|
return null; |
|
newObj = new NewObj((IMethod)member); |
|
newObj.Arguments.AddRange(args); |
|
return newObj; |
|
case 3: |
|
if (!MatchGetConstructorFromHandle(invocation.Arguments[0], out member)) |
|
return null; |
|
if (!MatchArgumentList(invocation.Arguments[1], out arguments)) |
|
return null; |
|
var args2 = arguments.SelectArray(ConvertInstruction); |
|
if (args2.Any(a => a == null)) |
|
return null; |
|
newObj = new NewObj((IMethod)member); |
|
newObj.Arguments.AddRange(args2); |
|
return newObj; |
|
} |
|
return null; |
|
} |
|
|
|
ILInstruction ConvertNotOperator(CallInstruction invocation) |
|
{ |
|
if (invocation.Arguments.Count < 1) |
|
return null; |
|
var argument = ConvertInstruction(invocation.Arguments[0]); |
|
if (argument == null) |
|
return null; |
|
switch (invocation.Arguments.Count) { |
|
case 1: |
|
return InferType(argument).IsKnownType(KnownTypeCode.Boolean) ? Comp.LogicNot(argument) : (ILInstruction)new BitNot(argument); |
|
case 2: |
|
if (!MatchGetMethodFromHandle(invocation.Arguments[1], out var method)) |
|
return null; |
|
return new Call((IMethod)method) { |
|
Arguments = { argument } |
|
}; |
|
default: |
|
return null; |
|
} |
|
} |
|
|
|
ILInstruction ConvertProperty(CallInstruction invocation) |
|
{ |
|
if (invocation.Arguments.Count < 2) |
|
return null; |
|
ILInstruction target = null; |
|
if (!invocation.Arguments[0].MatchLdNull()) { |
|
target = ConvertInstruction(invocation.Arguments[0]); |
|
if (target == null) |
|
return null; |
|
} |
|
if (!MatchGetMethodFromHandle(invocation.Arguments[1], out var member)) |
|
return null; |
|
IList<ILInstruction> arguments; |
|
if (invocation.Arguments.Count != 3 || !MatchArgumentList(invocation.Arguments[2], out arguments)) { |
|
arguments = new List<ILInstruction>(); |
|
} else { |
|
for (int i = 0; i < arguments.Count; i++) { |
|
arguments[i] = ConvertInstruction(arguments[i]); |
|
if (arguments[i] == null) |
|
return null; |
|
} |
|
} |
|
if (target != null) { |
|
arguments.Insert(0, target); |
|
} |
|
CallInstruction call; |
|
if (member.IsAbstract || member.IsVirtual || member.IsOverridable) { |
|
call = new CallVirt((IMethod)member); |
|
} else { |
|
call = new Call((IMethod)member); |
|
} |
|
call.Arguments.AddRange(arguments); |
|
return call; |
|
} |
|
|
|
ILInstruction ConvertTypeAs(CallInstruction invocation) |
|
{ |
|
if (invocation.Arguments.Count != 2) |
|
return null; |
|
var converted = ConvertInstruction(invocation.Arguments[0]); |
|
if (!MatchGetTypeFromHandle(invocation.Arguments[1], out var type)) |
|
return null; |
|
if (converted != null) |
|
return new IsInst(converted, type); |
|
return null; |
|
} |
|
|
|
ILInstruction ConvertTypeIs(CallInstruction invocation) |
|
{ |
|
if (invocation.Arguments.Count != 2) |
|
return null; |
|
var converted = ConvertInstruction(invocation.Arguments[0]); |
|
if (!MatchGetTypeFromHandle(invocation.Arguments[1], out var type)) |
|
return null; |
|
if (converted != null) |
|
return new Comp(ComparisonKind.Inequality, Sign.None, new IsInst(converted, type), new LdNull()); |
|
return null; |
|
} |
|
|
|
ILInstruction ConvertUnaryNumericOperator(CallInstruction invocation, BinaryNumericOperator op, bool? isChecked = null) |
|
{ |
|
if (invocation.Arguments.Count < 1) |
|
return null; |
|
var argument = ConvertInstruction(invocation.Arguments[0]); |
|
if (argument == null) |
|
return null; |
|
switch (invocation.Arguments.Count) { |
|
case 1: |
|
return new BinaryNumericInstruction(op, new LdcI4(0), argument, isChecked == true, Sign.None); |
|
case 2: |
|
if (!MatchGetMethodFromHandle(invocation.Arguments[1], out var method)) |
|
return null; |
|
return new Call((IMethod)method) { |
|
Arguments = { argument } |
|
}; |
|
} |
|
return null; |
|
} |
|
|
|
ILInstruction ConvertValue(ILInstruction value, ILInstruction context) |
|
{ |
|
switch (value) { |
|
case LdLoc ldloc: |
|
if (IsExpressionTreeParameter(ldloc.Variable)) { |
|
if (!parameterMapping.TryGetValue(ldloc.Variable, out var v)) |
|
return null; |
|
if (context is CallInstruction parentCall |
|
&& parentCall.Method.FullName == "System.Linq.Expressions.Expression.Call" |
|
&& v.StackType.IsIntegerType()) |
|
return new LdLoca(v); |
|
return new LdLoc(v); |
|
} else { |
|
if (ldloc.Variable.Kind != VariableKind.StackSlot) |
|
return new LdLoc(ldloc.Variable); |
|
return null; |
|
} |
|
default: |
|
if (SemanticHelper.IsPure(value.Flags)) |
|
return value.Clone(); |
|
return value; |
|
} |
|
} |
|
|
|
bool IsExpressionTreeParameter(ILVariable variable) |
|
{ |
|
return variable.Type.FullName == "System.Linq.Expressions.ParameterExpression"; |
|
} |
|
|
|
bool MatchConstantCall(ILInstruction inst, out ILInstruction value, out IType type) |
|
{ |
|
value = null; |
|
type = null; |
|
if (inst is CallInstruction call && call.Method.FullName == "System.Linq.Expressions.Expression.Constant") { |
|
value = call.Arguments[0]; |
|
if (call.Arguments.Count == 2) |
|
return MatchGetTypeFromHandle(call.Arguments[1], out type); |
|
type = InferType(value); |
|
return true; |
|
} |
|
return false; |
|
} |
|
|
|
bool MatchGetTypeFromHandle(ILInstruction inst, out IType type) |
|
{ |
|
type = null; |
|
return inst is CallInstruction getTypeCall |
|
&& getTypeCall.Method.FullName == "System.Type.GetTypeFromHandle" |
|
&& getTypeCall.Arguments.Count == 1 |
|
&& getTypeCall.Arguments[0].MatchLdTypeToken(out type); |
|
} |
|
|
|
bool MatchGetMethodFromHandle(ILInstruction inst, out IMember member) |
|
{ |
|
member = null; |
|
//castclass System.Reflection.MethodInfo(call GetMethodFromHandle(ldmembertoken op_Addition)) |
|
if (!inst.MatchCastClass(out var arg, out var type)) |
|
return false; |
|
if (!type.Equals(context.TypeSystem.Compilation.FindType(new FullTypeName("System.Reflection.MethodInfo")))) |
|
return false; |
|
if (!(arg is CallInstruction call && call.Method.FullName == "System.Reflection.MethodBase.GetMethodFromHandle")) |
|
return false; |
|
switch (call.Arguments.Count) { |
|
case 1: |
|
if (!call.Arguments[0].MatchLdMemberToken(out member)) |
|
return false; |
|
break; |
|
case 2: |
|
if (!call.Arguments[0].MatchLdMemberToken(out member)) |
|
return false; |
|
if (!call.Arguments[1].MatchLdTypeToken(out var genericType)) |
|
return false; |
|
break; |
|
} |
|
return true; |
|
} |
|
|
|
bool MatchGetConstructorFromHandle(ILInstruction inst, out IMember member) |
|
{ |
|
member = null; |
|
//castclass System.Reflection.ConstructorInfo(call GetMethodFromHandle(ldmembertoken op_Addition)) |
|
if (!inst.MatchCastClass(out var arg, out var type)) |
|
return false; |
|
if (!type.Equals(context.TypeSystem.Compilation.FindType(new FullTypeName("System.Reflection.ConstructorInfo")))) |
|
return false; |
|
if (!(arg is CallInstruction call && call.Method.FullName == "System.Reflection.MethodBase.GetMethodFromHandle")) |
|
return false; |
|
switch (call.Arguments.Count) { |
|
case 1: |
|
if (!call.Arguments[0].MatchLdMemberToken(out member)) |
|
return false; |
|
break; |
|
case 2: |
|
if (!call.Arguments[0].MatchLdMemberToken(out member)) |
|
return false; |
|
if (!call.Arguments[1].MatchLdTypeToken(out var genericType)) |
|
return false; |
|
break; |
|
} |
|
return true; |
|
} |
|
|
|
bool MatchGetFieldFromHandle(ILInstruction inst, out IMember member) |
|
{ |
|
member = null; |
|
if (!(inst is CallInstruction call && call.Method.FullName == "System.Reflection.FieldInfo.GetFieldFromHandle")) |
|
return false; |
|
switch (call.Arguments.Count) { |
|
case 1: |
|
if (!call.Arguments[0].MatchLdMemberToken(out member)) |
|
return false; |
|
break; |
|
case 2: |
|
if (!call.Arguments[0].MatchLdMemberToken(out member)) |
|
return false; |
|
if (!call.Arguments[1].MatchLdTypeToken(out var genericType)) |
|
return false; |
|
break; |
|
} |
|
return true; |
|
} |
|
|
|
bool MatchArgumentList(ILInstruction inst, out IList<ILInstruction> arguments) |
|
{ |
|
arguments = null; |
|
if (!(inst is Block block && block.Kind == BlockKind.ArrayInitializer)) { |
|
if (IsEmptyParameterList(inst)) { |
|
arguments = new List<ILInstruction>(); |
|
return true; |
|
} |
|
return false; |
|
} |
|
int i = 0; |
|
arguments = new List<ILInstruction>(); |
|
foreach (var item in block.Instructions.OfType<StObj>()) { |
|
if (!(item.Target is LdElema ldelem && ldelem.Indices.Single().MatchLdcI4(i))) |
|
return false; |
|
arguments.Add(item.Value); |
|
i++; |
|
} |
|
return true; |
|
} |
|
|
|
IType InferType(ILInstruction inst) |
|
{ |
|
if (inst is Block b && b.Kind == BlockKind.ArrayInitializer) |
|
return b.FinalInstruction.InferType(); |
|
if (inst is ExpressionTreeCast cast) |
|
return cast.Type; |
|
return inst.InferType(); |
|
} |
|
} |
|
}
|
|
|