Browse Source

#1940: Refactor TransformExpressionTrees to support rolling back all changes if the transform fails.

pull/1994/head
Siegfried Pammer 5 years ago
parent
commit
c676665a61
  1. 503
      ICSharpCode.Decompiler/IL/Transforms/TransformExpressionTrees.cs

503
ICSharpCode.Decompiler/IL/Transforms/TransformExpressionTrees.cs

@ -30,6 +30,8 @@ namespace ICSharpCode.Decompiler.IL.Transforms
{ {
/// <summary> /// <summary>
/// Converts LINQ Expression Trees to ILFunctions/ILAst instructions. /// Converts LINQ Expression Trees to ILFunctions/ILAst instructions.
///
/// We build a tree of Func{ILInstruction}s, which are only executed, if the whole transform succeeds.
/// </summary> /// </summary>
public class TransformExpressionTrees : IStatementTransform public class TransformExpressionTrees : IStatementTransform
{ {
@ -122,9 +124,10 @@ namespace ICSharpCode.Decompiler.IL.Transforms
if (MightBeExpressionTree(instruction, statement)) { if (MightBeExpressionTree(instruction, statement)) {
var (lambda, type) = ConvertLambda((CallInstruction)instruction); var (lambda, type) = ConvertLambda((CallInstruction)instruction);
if (lambda != null) { if (lambda != null) {
SetExpressionTreeFlag((ILFunction)lambda, (CallInstruction)instruction);
context.Step("Convert Expression Tree", instruction); context.Step("Convert Expression Tree", instruction);
instruction.ReplaceWith(lambda); var newLambda = (ILFunction)lambda();
SetExpressionTreeFlag(newLambda, (CallInstruction)instruction);
instruction.ReplaceWith(newLambda);
return true; return true;
} }
return false; return false;
@ -142,7 +145,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
/// Converts a Expression.Lambda call into an ILFunction. /// Converts a Expression.Lambda call into an ILFunction.
/// If the conversion fails, null is returned. /// If the conversion fails, null is returned.
/// </summary> /// </summary>
(ILInstruction, IType) ConvertLambda(CallInstruction instruction) (Func<ILInstruction>, IType) 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) 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, SpecialType.UnknownType); return (null, SpecialType.UnknownType);
@ -164,8 +167,15 @@ namespace ICSharpCode.Decompiler.IL.Transforms
lambdaStack.Pop(); lambdaStack.Pop();
if (bodyInstruction == null) if (bodyInstruction == null)
return (null, SpecialType.UnknownType); return (null, SpecialType.UnknownType);
container.ExpectedResultType = bodyInstruction.ResultType; return (BuildFunction, function.DelegateType);
container.Blocks.Add(new Block() { Instructions = { new Leave(container, bodyInstruction) } });
ILFunction BuildFunction()
{
lambdaStack.Push(function);
var convertedBody = bodyInstruction();
lambdaStack.Pop();
container.ExpectedResultType = convertedBody.ResultType;
container.Blocks.Add(new Block() { Instructions = { new Leave(container, convertedBody) } });
// Replace all other usages of the parameter variable // Replace all other usages of the parameter variable
foreach (var mapping in parameterMapping) { foreach (var mapping in parameterMapping) {
foreach (var load in mapping.Key.LoadInstructions.ToArray()) { foreach (var load in mapping.Key.LoadInstructions.ToArray()) {
@ -174,24 +184,32 @@ namespace ICSharpCode.Decompiler.IL.Transforms
load.ReplaceWith(new LdLoc(mapping.Value)); load.ReplaceWith(new LdLoc(mapping.Value));
} }
} }
return (function, function.DelegateType); return function;
}
} }
(ILInstruction, IType) ConvertQuote(CallInstruction invocation) (Func<ILInstruction>, IType) ConvertQuote(CallInstruction invocation)
{ {
if (invocation.Arguments.Count != 1) if (invocation.Arguments.Count != 1)
return (null, SpecialType.UnknownType); return (null, SpecialType.UnknownType);
var argument = invocation.Arguments.Single(); var argument = invocation.Arguments.Single();
if (argument is ILFunction function) { if (argument is ILFunction function) {
return (function, function.DelegateType); return (() => function, function.DelegateType);
} else { } else {
var converted = ConvertInstruction(argument); var (converted, type) = ConvertInstruction(argument);
if (converted == null)
return (converted, type);
return (BuildQuote, type);
if (converted.Item1 is ILFunction lambda && argument is CallInstruction call) { ILInstruction BuildQuote()
{
var f = converted();
if (f is ILFunction lambda && argument is CallInstruction call) {
SetExpressionTreeFlag(lambda, call); SetExpressionTreeFlag(lambda, call);
} }
return converted; return f;
}
} }
} }
@ -232,22 +250,28 @@ namespace ICSharpCode.Decompiler.IL.Transforms
} }
} }
(ILInstruction, IType) ConvertInstruction(ILInstruction instruction, IType typeHint = null) (Func<ILInstruction>, IType) ConvertInstruction(ILInstruction instruction, IType typeHint = null)
{
var (inst, type) = Convert();
if (inst == null)
return (null, type);
ILInstruction DoConvert()
{ {
var result = Convert(); var result = inst();
if (result.Item1 != null) { Debug.Assert(type != null, "IType must be non-null!");
Debug.Assert(result.Item2 != null, "IType must be non-null!"); Debug.Assert(result.ResultType == type.GetStackType(), "StackTypes must match!");
Debug.Assert(result.Item1.ResultType == result.Item2.GetStackType(), "StackTypes must match!");
if (typeHint != null) { if (typeHint != null) {
var inst = result.Item1; if (result.ResultType != typeHint.GetStackType()) {
if (inst.ResultType != typeHint.GetStackType()) { return new Conv(result, typeHint.GetStackType().ToPrimitiveType(), false, typeHint.GetSign());
return (new Conv(inst, typeHint.GetStackType().ToPrimitiveType(), false, typeHint.GetSign()), typeHint);
}
} }
} }
return result; return result;
}
return (DoConvert, typeHint ?? type);
(ILInstruction, IType) Convert() { (Func<ILInstruction>, IType) Convert() {
switch (instruction) { switch (instruction) {
case CallInstruction invocation: case CallInstruction invocation:
if (invocation.Method.DeclaringType.FullName != "System.Linq.Expressions.Expression") if (invocation.Method.DeclaringType.FullName != "System.Linq.Expressions.Expression")
@ -348,19 +372,23 @@ namespace ICSharpCode.Decompiler.IL.Transforms
} }
return (null, SpecialType.UnknownType); return (null, SpecialType.UnknownType);
case ILFunction function: case ILFunction function:
ILFunction ApplyChangesToILFunction()
{
if (function.Kind == ILFunctionKind.ExpressionTree) { if (function.Kind == ILFunctionKind.ExpressionTree) {
function.DelegateType = UnwrapExpressionTree(function.DelegateType); function.DelegateType = UnwrapExpressionTree(function.DelegateType);
function.Kind = ILFunctionKind.Delegate; function.Kind = ILFunctionKind.Delegate;
} }
return (function, function.DelegateType); return function;
}
return (ApplyChangesToILFunction, function.DelegateType);
case LdLoc ldloc: case LdLoc ldloc:
if (IsExpressionTreeParameter(ldloc.Variable)) { if (IsExpressionTreeParameter(ldloc.Variable)) {
// Replace an already mapped parameter with the actual ILVariable, // Replace an already mapped parameter with the actual ILVariable,
// we generated earlier. // we generated earlier.
if (parameterMapping.TryGetValue(ldloc.Variable, out var v)) { if (parameterMapping.TryGetValue(ldloc.Variable, out var v)) {
if (typeHint.SkipModifiers() is ByReferenceType && !v.Type.IsByRefLike) if (typeHint.SkipModifiers() is ByReferenceType && !v.Type.IsByRefLike)
return (new LdLoca(v), typeHint); return (() => new LdLoca(v), typeHint);
return (new LdLoc(v), v.Type); return (() => new LdLoc(v), v.Type);
} }
// This is a parameter variable from an outer scope. // This is a parameter variable from an outer scope.
// We can't replace these variables just yet, because the transform works backwards. // We can't replace these variables just yet, because the transform works backwards.
@ -368,9 +396,9 @@ namespace ICSharpCode.Decompiler.IL.Transforms
// so our transform can continue normally. // so our transform can continue normally.
// Later, we will replace all references to unmapped variables, // Later, we will replace all references to unmapped variables,
// with references to mapped parameters. // with references to mapped parameters.
if (ldloc.Variable.IsSingleDefinition && ldloc.Variable.StoreInstructions[0] is ILInstruction inst) { if (ldloc.Variable.IsSingleDefinition && ldloc.Variable.StoreInstructions[0] is ILInstruction instr) {
if (MatchParameterVariableAssignment(inst, out _, out var type, out _)) if (MatchParameterVariableAssignment(instr, out _, out var t, out _))
return (new ExpressionTreeCast(type, ldloc, false), type); return (() => new ExpressionTreeCast(t, ldloc, false), t);
} }
} }
return (null, SpecialType.UnknownType); return (null, SpecialType.UnknownType);
@ -392,7 +420,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
return delegateType; return delegateType;
} }
(ILInstruction, IType) ConvertArrayIndex(CallInstruction invocation) (Func<ILInstruction>, IType) ConvertArrayIndex(CallInstruction invocation)
{ {
if (invocation.Arguments.Count != 2) if (invocation.Arguments.Count != 2)
return (null, SpecialType.UnknownType); return (null, SpecialType.UnknownType);
@ -403,26 +431,32 @@ namespace ICSharpCode.Decompiler.IL.Transforms
return (null, SpecialType.UnknownType); return (null, SpecialType.UnknownType);
if (!MatchArgumentList(invocation.Arguments[1], out var arguments)) if (!MatchArgumentList(invocation.Arguments[1], out var arguments))
arguments = new[] { invocation.Arguments[1] }; arguments = new[] { invocation.Arguments[1] };
ILInstruction Convert()
{
Func<ILInstruction>[] toBeConverted = new Func<ILInstruction>[arguments.Count];
for (int i = 0; i < arguments.Count; i++) { for (int i = 0; i < arguments.Count; i++) {
var (converted, indexType) = ConvertInstruction(arguments[i]); var (converted, indexType) = ConvertInstruction(arguments[i]);
if (converted == null) if (converted == null)
return (null, SpecialType.UnknownType); return null;
arguments[i] = converted; toBeConverted[i] = converted;
}
return new LdObj(new LdElema(type.ElementType, array(), toBeConverted.SelectArray(f => f())), type.ElementType);
} }
return (new LdObj(new LdElema(type.ElementType, array, arguments.ToArray()), type.ElementType), type.ElementType); return (Convert, type.ElementType);
} }
(ILInstruction, IType) ConvertArrayLength(CallInstruction invocation) (Func<ILInstruction>, IType) ConvertArrayLength(CallInstruction invocation)
{ {
if (invocation.Arguments.Count != 1) if (invocation.Arguments.Count != 1)
return (null, SpecialType.UnknownType); return (null, SpecialType.UnknownType);
var (converted, arrayType) = ConvertInstruction(invocation.Arguments[0]); var (converted, _) = ConvertInstruction(invocation.Arguments[0]);
if (converted == null) if (converted == null)
return (null, SpecialType.UnknownType); return (null, SpecialType.UnknownType);
return (new LdLen(StackType.I4, converted), context.TypeSystem.FindType(KnownTypeCode.Int32)); return (() => new LdLen(StackType.I4, converted()), context.TypeSystem.FindType(KnownTypeCode.Int32));
} }
(ILInstruction, IType) ConvertBinaryNumericOperator(CallInstruction invocation, BinaryNumericOperator op, bool? isChecked = null) (Func<ILInstruction>, IType) ConvertBinaryNumericOperator(CallInstruction invocation, BinaryNumericOperator op, bool? isChecked = null)
{ {
if (invocation.Arguments.Count < 2) if (invocation.Arguments.Count < 2)
return (null, SpecialType.UnknownType); return (null, SpecialType.UnknownType);
@ -442,12 +476,12 @@ namespace ICSharpCode.Decompiler.IL.Transforms
if (!rightType.Equals(leftType)) if (!rightType.Equals(leftType))
return (null, SpecialType.UnknownType); return (null, SpecialType.UnknownType);
} }
return (new BinaryNumericInstruction(op, left, right, isChecked == true, leftType.GetSign()), leftType); return (() => new BinaryNumericInstruction(op, left(), right(), isChecked == true, leftType.GetSign()), leftType);
case 3: case 3:
if (!MatchGetMethodFromHandle(invocation.Arguments[2], out method)) if (!MatchGetMethodFromHandle(invocation.Arguments[2], out method))
return (null, SpecialType.UnknownType); return (null, SpecialType.UnknownType);
return (new Call((IMethod)method) { return (() => new Call((IMethod)method) {
Arguments = { left, right } Arguments = { left(), right() }
}, method.ReturnType); }, method.ReturnType);
case 4: case 4:
if (!invocation.Arguments[2].MatchLdcI4(out var isLifted)) if (!invocation.Arguments[2].MatchLdcI4(out var isLifted))
@ -456,15 +490,15 @@ namespace ICSharpCode.Decompiler.IL.Transforms
return (null, SpecialType.UnknownType); return (null, SpecialType.UnknownType);
if (isLifted != 0) if (isLifted != 0)
method = CSharpOperators.LiftUserDefinedOperator((IMethod)method); method = CSharpOperators.LiftUserDefinedOperator((IMethod)method);
return (new Call((IMethod)method) { return (() => new Call((IMethod)method) {
Arguments = { left, right } Arguments = { left(), right() }
}, method.ReturnType); }, method.ReturnType);
default: default:
return (null, SpecialType.UnknownType); return (null, SpecialType.UnknownType);
} }
} }
(ILInstruction, IType) ConvertBind(CallInstruction invocation, ILVariable targetVariable) (Func<ILVariable, ILInstruction>, IType) ConvertBind(CallInstruction invocation)
{ {
if (invocation.Arguments.Count != 2) if (invocation.Arguments.Count != 2)
return (null, SpecialType.UnknownType); return (null, SpecialType.UnknownType);
@ -478,19 +512,19 @@ namespace ICSharpCode.Decompiler.IL.Transforms
} }
switch (member) { switch (member) {
case IMethod method: case IMethod method:
return (new Call(method) { Arguments = { new LdLoc(targetVariable), value } }, method.ReturnType); return (targetVariable => new Call(method) { Arguments = { new LdLoc(targetVariable), value() } }, method.ReturnType);
case IField field: case IField field:
return (new StObj(new LdFlda(new LdLoc(targetVariable), (IField)member), value, member.ReturnType), field.ReturnType); return (targetVariable => new StObj(new LdFlda(new LdLoc(targetVariable), (IField)member), value(), member.ReturnType), field.ReturnType);
} }
return (null, SpecialType.UnknownType); return (null, SpecialType.UnknownType);
} }
(ILInstruction, IType) ConvertCall(CallInstruction invocation) (Func<ILInstruction>, IType) ConvertCall(CallInstruction invocation)
{ {
if (invocation.Arguments.Count < 2) if (invocation.Arguments.Count < 2)
return (null, SpecialType.UnknownType); return (null, SpecialType.UnknownType);
IList<ILInstruction> arguments = null; IList<ILInstruction> arguments = null;
ILInstruction target = null; Func<ILInstruction> targetConverter = null;
IType targetType = null; IType targetType = null;
if (MatchGetMethodFromHandle(invocation.Arguments[0], out var member)) { if (MatchGetMethodFromHandle(invocation.Arguments[0], out var member)) {
// static method // static method
@ -502,36 +536,42 @@ namespace ICSharpCode.Decompiler.IL.Transforms
arguments = new List<ILInstruction>(invocation.Arguments.Skip(2)); arguments = new List<ILInstruction>(invocation.Arguments.Skip(2));
} }
if (!invocation.Arguments[0].MatchLdNull()) { if (!invocation.Arguments[0].MatchLdNull()) {
(target, targetType) = ConvertInstruction(invocation.Arguments[0]); (targetConverter, targetType) = ConvertInstruction(invocation.Arguments[0]);
if (target == null) if (targetConverter == null)
return (null, SpecialType.UnknownType); return (null, SpecialType.UnknownType);
} }
} }
if (arguments == null) if (arguments == null)
return (null, SpecialType.UnknownType); return (null, SpecialType.UnknownType);
IMethod method = (IMethod)member; IMethod method = (IMethod)member;
if (!ConvertCallArguments(arguments, method)) var convertedArguments = ConvertCallArguments(arguments, method);
if (convertedArguments == null)
return (null, SpecialType.UnknownType); return (null, SpecialType.UnknownType);
if (method.FullName == "System.Reflection.MethodInfo.CreateDelegate" && method.Parameters.Count == 2) { if (method.FullName == "System.Reflection.MethodInfo.CreateDelegate" && method.Parameters.Count == 2) {
if (!MatchGetMethodFromHandle(target, out var targetMethod)) if (!MatchGetMethodFromHandle(UnpackConstant(invocation.Arguments[0]), out var targetMethod))
return (null, SpecialType.UnknownType); return (null, SpecialType.UnknownType);
if (!MatchGetTypeFromHandle(arguments[0], out var delegateType)) if (!MatchGetTypeFromHandle(UnpackConstant(arguments[0]), out var delegateType))
return (null, SpecialType.UnknownType); return (null, SpecialType.UnknownType);
return (new NewObj(delegateType.GetConstructors().Single()) { return (() => new NewObj(delegateType.GetConstructors().Single()) {
Arguments = { arguments[1], new LdFtn((IMethod)targetMethod) } Arguments = { convertedArguments[1](), new LdFtn((IMethod)targetMethod) }
}, delegateType); }, delegateType);
} }
CallInstruction BuildCall()
{
CallInstruction call; CallInstruction call;
if (method.IsAbstract || method.IsVirtual || method.IsOverride) { if (method.IsAbstract || method.IsVirtual || method.IsOverride) {
call = new CallVirt(method); call = new CallVirt(method);
} else { } else {
call = new Call(method); call = new Call(method);
} }
if (target != null) { if (targetConverter != null) {
call.Arguments.Add(PrepareCallTarget(method.DeclaringType, target, targetType)); call.Arguments.Add(PrepareCallTarget(method.DeclaringType, targetConverter(), targetType));
}
call.Arguments.AddRange(convertedArguments.Select(f => f()));
return call;
} }
call.Arguments.AddRange(arguments); return (BuildCall, method.ReturnType);
return (call, method.ReturnType);
} }
ILInstruction PrepareCallTarget(IType expectedType, ILInstruction target, IType targetType) ILInstruction PrepareCallTarget(IType expectedType, ILInstruction target, IType targetType)
@ -556,20 +596,28 @@ namespace ICSharpCode.Decompiler.IL.Transforms
} }
} }
bool ConvertCallArguments(IList<ILInstruction> arguments, IMethod method) ILInstruction UnpackConstant(ILInstruction inst)
{ {
if (!(inst is CallInstruction call && call.Method.FullName == "System.Linq.Expressions.Expression.Constant" && call.Arguments.Count == 2))
return inst;
return call.Arguments[0];
}
Func<ILInstruction>[] ConvertCallArguments(IList<ILInstruction> arguments, IMethod method)
{
var converted = new Func<ILInstruction>[arguments.Count];
Debug.Assert(arguments.Count == method.Parameters.Count); Debug.Assert(arguments.Count == method.Parameters.Count);
for (int i = 0; i < arguments.Count; i++) { for (int i = 0; i < arguments.Count; i++) {
var expectedType = method.Parameters[i].Type; var expectedType = method.Parameters[i].Type;
var argument = ConvertInstruction(arguments[i], expectedType).Item1; var argument = ConvertInstruction(arguments[i], expectedType).Item1;
if (argument == null) if (argument == null)
return false; return null;
arguments[i] = argument; converted[i] = argument;
} }
return true; return converted;
} }
(ILInstruction, IType) ConvertCast(CallInstruction invocation, bool isChecked) (Func<ILInstruction>, IType) ConvertCast(CallInstruction invocation, bool isChecked)
{ {
if (invocation.Arguments.Count < 2) if (invocation.Arguments.Count < 2)
return (null, SpecialType.UnknownType); return (null, SpecialType.UnknownType);
@ -580,10 +628,10 @@ namespace ICSharpCode.Decompiler.IL.Transforms
return (null, SpecialType.UnknownType); return (null, SpecialType.UnknownType);
if (exprType.IsSmallIntegerType() && targetType.IsKnownType(KnownTypeCode.Int32)) if (exprType.IsSmallIntegerType() && targetType.IsKnownType(KnownTypeCode.Int32))
return (expr, targetType); return (expr, targetType);
return (new ExpressionTreeCast(targetType, expr, isChecked), targetType); return (() => new ExpressionTreeCast(targetType, expr(), isChecked), targetType);
} }
(ILInstruction, IType) ConvertCoalesce(CallInstruction invocation) (Func<ILInstruction>, IType) ConvertCoalesce(CallInstruction invocation)
{ {
if (invocation.Arguments.Count != 2) if (invocation.Arguments.Count != 2)
return (null, SpecialType.UnknownType); return (null, SpecialType.UnknownType);
@ -604,12 +652,12 @@ namespace ICSharpCode.Decompiler.IL.Transforms
} else { } else {
targetType = fallbackInstType; targetType = fallbackInstType;
} }
return (new NullCoalescingInstruction(kind, trueInst, fallbackInst) { return (() => new NullCoalescingInstruction(kind, trueInst(), fallbackInst()) {
UnderlyingResultType = trueInstTypeNonNullable.GetStackType() UnderlyingResultType = trueInstTypeNonNullable.GetStackType()
}, targetType); }, targetType);
} }
(ILInstruction, IType) ConvertComparison(CallInstruction invocation, ComparisonKind kind) (Func<ILInstruction>, IType) ConvertComparison(CallInstruction invocation, ComparisonKind kind)
{ {
if (invocation.Arguments.Count < 2) if (invocation.Arguments.Count < 2)
return (null, SpecialType.UnknownType); return (null, SpecialType.UnknownType);
@ -623,11 +671,11 @@ namespace ICSharpCode.Decompiler.IL.Transforms
if (isLifted != 0) { if (isLifted != 0) {
method = CSharpOperators.LiftUserDefinedOperator((IMethod)method); method = CSharpOperators.LiftUserDefinedOperator((IMethod)method);
} }
return (new Call((IMethod)method) { Arguments = { left, right } }, method.ReturnType); return (() => new Call((IMethod)method) { Arguments = { left(), right() } }, method.ReturnType);
} }
var rr = resolver.ResolveBinaryOperator(kind.ToBinaryOperatorType(), new ResolveResult(leftType), new ResolveResult(rightType)) as OperatorResolveResult; var rr = resolver.ResolveBinaryOperator(kind.ToBinaryOperatorType(), new ResolveResult(leftType), new ResolveResult(rightType)) as OperatorResolveResult;
if (rr != null && !rr.IsError && rr.UserDefinedOperatorMethod != null) { if (rr != null && !rr.IsError && rr.UserDefinedOperatorMethod != null) {
return (new Call(rr.UserDefinedOperatorMethod) { Arguments = { left, right } }, rr.UserDefinedOperatorMethod.ReturnType); return (() => new Call(rr.UserDefinedOperatorMethod) { Arguments = { left(), right() } }, rr.UserDefinedOperatorMethod.ReturnType);
} }
if (leftType.IsKnownType(KnownTypeCode.String) && rightType.IsKnownType(KnownTypeCode.String)) { if (leftType.IsKnownType(KnownTypeCode.String) && rightType.IsKnownType(KnownTypeCode.String)) {
IMethod operatorMethod; IMethod operatorMethod;
@ -645,15 +693,15 @@ namespace ICSharpCode.Decompiler.IL.Transforms
default: default:
return (null, SpecialType.UnknownType); return (null, SpecialType.UnknownType);
} }
return (new Call(operatorMethod) { Arguments = { left, right } }, operatorMethod.ReturnType); return (() => new Call(operatorMethod) { Arguments = { left(), right() } }, operatorMethod.ReturnType);
} }
var resultType = context.TypeSystem.FindType(KnownTypeCode.Boolean); var resultType = context.TypeSystem.FindType(KnownTypeCode.Boolean);
var lifting = NullableType.IsNullable(leftType) ? ComparisonLiftingKind.CSharp : ComparisonLiftingKind.None; var lifting = NullableType.IsNullable(leftType) ? ComparisonLiftingKind.CSharp : ComparisonLiftingKind.None;
var utype = NullableType.GetUnderlyingType(leftType); var utype = NullableType.GetUnderlyingType(leftType);
return (new Comp(kind, lifting, utype.GetStackType(), utype.GetSign(), left, right), resultType); return (() => new Comp(kind, lifting, utype.GetStackType(), utype.GetSign(), left(), right()), resultType);
} }
(ILInstruction, IType) ConvertCondition(CallInstruction invocation) (Func<ILInstruction>, IType) ConvertCondition(CallInstruction invocation)
{ {
if (invocation.Arguments.Count != 3) if (invocation.Arguments.Count != 3)
return (null, SpecialType.UnknownType); return (null, SpecialType.UnknownType);
@ -668,23 +716,22 @@ namespace ICSharpCode.Decompiler.IL.Transforms
return (null, SpecialType.UnknownType); return (null, SpecialType.UnknownType);
if (!trueInstType.Equals(falseInstType)) if (!trueInstType.Equals(falseInstType))
return (null, SpecialType.UnknownType); return (null, SpecialType.UnknownType);
return (new IfInstruction(condition, trueInst, falseInst), trueInstType); return (() => new IfInstruction(condition(), trueInst(), falseInst()), trueInstType);
} }
(ILInstruction, IType) ConvertConstant(CallInstruction invocation) (Func<ILInstruction>, IType) ConvertConstant(CallInstruction invocation)
{ {
if (!MatchConstantCall(invocation, out var value, out var type)) if (!MatchConstantCall(invocation, out var value, out var type))
return (null, SpecialType.UnknownType); return (null, SpecialType.UnknownType);
if (value.MatchBox(out var arg, out var boxType)) { if (value.MatchBox(out var arg, out var boxType)) {
if (boxType.Kind == TypeKind.Enum || boxType.IsKnownType(KnownTypeCode.Boolean)) if (boxType.Kind == TypeKind.Enum || boxType.IsKnownType(KnownTypeCode.Boolean))
return (new ExpressionTreeCast(boxType, ConvertValue(arg, invocation), false), boxType); return (() => new ExpressionTreeCast(boxType, ConvertValue(arg, invocation), false), boxType);
value = ConvertValue(arg, invocation); return (() => ConvertValue(arg, invocation), type);
return (value, type);
} }
return (ConvertValue(value, invocation), type); return (() => ConvertValue(value, invocation), type);
} }
(ILInstruction, IType) ConvertElementInit(CallInstruction invocation) (Func<ILInstruction>, IType) ConvertElementInit(CallInstruction invocation)
{ {
if (invocation.Arguments.Count != 2) if (invocation.Arguments.Count != 2)
return (null, SpecialType.UnknownType); return (null, SpecialType.UnknownType);
@ -692,77 +739,97 @@ namespace ICSharpCode.Decompiler.IL.Transforms
return (null, SpecialType.UnknownType); return (null, SpecialType.UnknownType);
if (!MatchArgumentList(invocation.Arguments[1], out var arguments)) if (!MatchArgumentList(invocation.Arguments[1], out var arguments))
return (null, SpecialType.UnknownType); return (null, SpecialType.UnknownType);
CallInstruction call = new Call((IMethod)member); var args = new Func<ILInstruction>[arguments.Count];
for (int i = 0; i < arguments.Count; i++) { for (int i = 0; i < arguments.Count; i++) {
ILInstruction arg = ConvertInstruction(arguments[i]).Item1; var arg = ConvertInstruction(arguments[i]).Item1;
if (arg == null) if (arg == null)
return (null, SpecialType.UnknownType); return (null, SpecialType.UnknownType);
arguments[i] = arg; args[i] = arg;
}
ILInstruction BuildCall()
{
CallInstruction call = new Call((IMethod)member);
call.Arguments.AddRange(args.Select(f => f()));
return call;
} }
call.Arguments.AddRange(arguments); return (BuildCall, member.ReturnType);
return (call, member.ReturnType);
} }
(ILInstruction, IType) ConvertField(CallInstruction invocation, IType typeHint) (Func<ILInstruction>, IType) ConvertField(CallInstruction invocation, IType typeHint)
{ {
if (invocation.Arguments.Count != 2) if (invocation.Arguments.Count != 2)
return (null, SpecialType.UnknownType); return (null, SpecialType.UnknownType);
ILInstruction target = null; Func<ILInstruction> targetConverter = null;
if (!invocation.Arguments[0].MatchLdNull()) { if (!invocation.Arguments[0].MatchLdNull()) {
target = ConvertInstruction(invocation.Arguments[0]).Item1; targetConverter = ConvertInstruction(invocation.Arguments[0]).Item1;
if (target == null) if (targetConverter == null)
return (null, SpecialType.UnknownType); return (null, SpecialType.UnknownType);
} }
if (!MatchGetFieldFromHandle(invocation.Arguments[1], out var member)) if (!MatchGetFieldFromHandle(invocation.Arguments[1], out var member))
return (null, SpecialType.UnknownType); return (null, SpecialType.UnknownType);
IType type = member.ReturnType; IType type = member.ReturnType;
if (typeHint.SkipModifiers() is ByReferenceType && !member.ReturnType.IsByRefLike) {
type = typeHint;
}
return (BuildField, type);
ILInstruction BuildField()
{
ILInstruction inst; ILInstruction inst;
if (target == null) { if (targetConverter == null) {
inst = new LdsFlda((IField)member); inst = new LdsFlda((IField)member);
} else { } else {
var target = targetConverter();
if (member.DeclaringType.IsReferenceType == true) { if (member.DeclaringType.IsReferenceType == true) {
inst = new LdFlda(target, (IField)member); inst = new LdFlda(target, (IField)member);
} else { } else {
inst = new LdFlda(new AddressOf(target, member.DeclaringType), (IField)member); inst = new LdFlda(new AddressOf(target, member.DeclaringType), (IField)member);
} }
} }
if (typeHint.SkipModifiers() is ByReferenceType brt && !member.ReturnType.IsByRefLike) { if (!(typeHint.SkipModifiers() is ByReferenceType && !member.ReturnType.IsByRefLike)) {
type = typeHint;
} else {
inst = new LdObj(inst, member.ReturnType); inst = new LdObj(inst, member.ReturnType);
} }
return (inst, type); return inst;
}
} }
(ILInstruction, IType) ConvertInvoke(CallInstruction invocation) (Func<ILInstruction>, IType) ConvertInvoke(CallInstruction invocation)
{ {
if (invocation.Arguments.Count != 2) if (invocation.Arguments.Count != 2)
return (null, SpecialType.UnknownType); return (null, SpecialType.UnknownType);
var (target, targetType) = ConvertInstruction(invocation.Arguments[0]); var (targetConverter, targetType) = ConvertInstruction(invocation.Arguments[0]);
if (target == null) if (targetConverter == null)
return (null, SpecialType.UnknownType); return (null, SpecialType.UnknownType);
var invokeMethod = targetType.GetDelegateInvokeMethod(); var invokeMethod = targetType.GetDelegateInvokeMethod();
if (invokeMethod == null) if (invokeMethod == null)
return (null, SpecialType.UnknownType); return (null, SpecialType.UnknownType);
if (!MatchArgumentList(invocation.Arguments[1], out var arguments)) if (!MatchArgumentList(invocation.Arguments[1], out var arguments))
return (null, SpecialType.UnknownType); return (null, SpecialType.UnknownType);
if (!ConvertCallArguments(arguments, invokeMethod)) var convertedArguments = ConvertCallArguments(arguments, invokeMethod);
if (convertedArguments == null)
return (null, SpecialType.UnknownType); return (null, SpecialType.UnknownType);
ILInstruction BuildCall()
{
var call = new CallVirt(invokeMethod); var call = new CallVirt(invokeMethod);
call.Arguments.Add(target); call.Arguments.Add(targetConverter());
call.Arguments.AddRange(arguments); call.Arguments.AddRange(convertedArguments.Select(f => f()));
return (call, invokeMethod.ReturnType); return call;
}
return (BuildCall, invokeMethod.ReturnType);
} }
(ILInstruction, IType) ConvertListInit(CallInstruction invocation) (Func<ILInstruction>, IType) ConvertListInit(CallInstruction invocation)
{ {
if (invocation.Arguments.Count < 2) if (invocation.Arguments.Count < 2)
return (null, SpecialType.UnknownType); return (null, SpecialType.UnknownType);
var newObj = ConvertInstruction(invocation.Arguments[0]).Item1 as NewObj; var newObj = ConvertInstruction(invocation.Arguments[0]).Item1;
if (newObj == null) if (newObj == null)
return (null, SpecialType.UnknownType); return (null, SpecialType.UnknownType);
IList<ILInstruction> arguments = null; if (!MatchNew((CallInstruction)invocation.Arguments[0], out var ctor))
ILFunction function = lambdaStack.Peek(); return (null, SpecialType.UnknownType);
IList<ILInstruction> arguments;
if (!MatchGetMethodFromHandle(invocation.Arguments[1], out var member)) { if (!MatchGetMethodFromHandle(invocation.Arguments[1], out var member)) {
if (!MatchArgumentList(invocation.Arguments[1], out arguments)) if (!MatchArgumentList(invocation.Arguments[1], out arguments))
return (null, SpecialType.UnknownType); return (null, SpecialType.UnknownType);
@ -772,29 +839,37 @@ namespace ICSharpCode.Decompiler.IL.Transforms
} }
if (arguments == null || arguments.Count == 0) if (arguments == null || arguments.Count == 0)
return (null, SpecialType.UnknownType); return (null, SpecialType.UnknownType);
var initializer = function.RegisterVariable(VariableKind.InitializerTarget, newObj.Method.DeclaringType); Func<ILVariable, ILInstruction>[] convertedArguments = new Func<ILVariable, ILInstruction>[arguments.Count];
for (int i = 0; i < arguments.Count; i++) { for (int i = 0; i < arguments.Count; i++) {
ILInstruction arg;
if (arguments[i] is CallInstruction elementInit && elementInit.Method.FullName == "System.Linq.Expressions.Expression.ElementInit") { if (arguments[i] is CallInstruction elementInit && elementInit.Method.FullName == "System.Linq.Expressions.Expression.ElementInit") {
arg = ConvertElementInit(elementInit).Item1; var arg = ConvertElementInit(elementInit).Item1;
if (arg == null) if (arg == null)
return (null, SpecialType.UnknownType); return (null, SpecialType.UnknownType);
((CallInstruction)arg).Arguments.Insert(0, new LdLoc(initializer));
convertedArguments[i] = v => { var a = arg(); ((CallInstruction)a).Arguments.Insert(0, new LdLoc(v)); return a; };
} else { } else {
arg = ConvertInstruction(arguments[i]).Item1; var arg = ConvertInstruction(arguments[i]).Item1;
if (arg == null) if (arg == null)
return (null, SpecialType.UnknownType); return (null, SpecialType.UnknownType);
convertedArguments[i] = v => arg();
} }
arguments[i] = arg;
} }
Block BuildBlock()
{
var initializerBlock = new Block(BlockKind.CollectionInitializer); var initializerBlock = new Block(BlockKind.CollectionInitializer);
ILFunction function = lambdaStack.Peek();
var initializer = function.RegisterVariable(VariableKind.InitializerTarget, ctor.DeclaringType);
initializerBlock.FinalInstruction = new LdLoc(initializer); initializerBlock.FinalInstruction = new LdLoc(initializer);
initializerBlock.Instructions.Add(new StLoc(initializer, newObj)); initializerBlock.Instructions.Add(new StLoc(initializer, newObj()));
initializerBlock.Instructions.AddRange(arguments); initializerBlock.Instructions.AddRange(convertedArguments.Select(f => f(initializer)));
return (initializerBlock, initializer.Type); return initializerBlock;
}
return (BuildBlock, ctor.DeclaringType);
} }
(ILInstruction, IType) ConvertLogicOperator(CallInstruction invocation, bool and) (Func<ILInstruction>, IType) ConvertLogicOperator(CallInstruction invocation, bool and)
{ {
if (invocation.Arguments.Count < 2) if (invocation.Arguments.Count < 2)
return (null, SpecialType.UnknownType); return (null, SpecialType.UnknownType);
@ -808,12 +883,12 @@ namespace ICSharpCode.Decompiler.IL.Transforms
switch (invocation.Arguments.Count) { switch (invocation.Arguments.Count) {
case 2: case 2:
var resultType = context.TypeSystem.FindType(KnownTypeCode.Boolean); var resultType = context.TypeSystem.FindType(KnownTypeCode.Boolean);
return (and ? IfInstruction.LogicAnd(left, right) : IfInstruction.LogicOr(left, right), resultType); return (() => and ? IfInstruction.LogicAnd(left(), right()) : IfInstruction.LogicOr(left(), right()), resultType);
case 3: case 3:
if (!MatchGetMethodFromHandle(invocation.Arguments[2], out method)) if (!MatchGetMethodFromHandle(invocation.Arguments[2], out method))
return (null, SpecialType.UnknownType); return (null, SpecialType.UnknownType);
return (new Call((IMethod)method) { return (() => new Call((IMethod)method) {
Arguments = { left, right } Arguments = { left(), right() }
}, method.ReturnType); }, method.ReturnType);
case 4: case 4:
if (!invocation.Arguments[2].MatchLdcI4(out var isLifted)) if (!invocation.Arguments[2].MatchLdcI4(out var isLifted))
@ -822,46 +897,59 @@ namespace ICSharpCode.Decompiler.IL.Transforms
return (null, SpecialType.UnknownType); return (null, SpecialType.UnknownType);
if (isLifted != 0) if (isLifted != 0)
method = CSharpOperators.LiftUserDefinedOperator((IMethod)method); method = CSharpOperators.LiftUserDefinedOperator((IMethod)method);
return (new Call((IMethod)method) { return (() => new Call((IMethod)method) {
Arguments = { left, right } Arguments = { left(), right() }
}, method.ReturnType); }, method.ReturnType);
default: default:
return (null, SpecialType.UnknownType); return (null, SpecialType.UnknownType);
} }
} }
(ILInstruction, IType) ConvertMemberInit(CallInstruction invocation) (Func<ILInstruction>, IType) ConvertMemberInit(CallInstruction invocation)
{ {
if (invocation.Arguments.Count != 2) if (invocation.Arguments.Count != 2)
return (null, SpecialType.UnknownType); return (null, SpecialType.UnknownType);
var newObj = ConvertInstruction(invocation.Arguments[0]).Item1 as NewObj; var newObj = ConvertInstruction(invocation.Arguments[0]).Item1;
if (newObj == null) if (newObj == null)
return (null, SpecialType.UnknownType); return (null, SpecialType.UnknownType);
if (!MatchNew((CallInstruction)invocation.Arguments[0], out var ctor))
return (null, SpecialType.UnknownType);
if (!MatchArgumentList(invocation.Arguments[1], out var arguments)) if (!MatchArgumentList(invocation.Arguments[1], out var arguments))
return (null, SpecialType.UnknownType); return (null, SpecialType.UnknownType);
if (arguments == null || arguments.Count == 0) if (arguments == null || arguments.Count == 0)
return (null, SpecialType.UnknownType); return (null, SpecialType.UnknownType);
var function = lambdaStack.Peek();
var initializer = function.RegisterVariable(VariableKind.InitializerTarget, newObj.Method.DeclaringType); Func<ILVariable, ILInstruction>[] convertedArguments = new Func<ILVariable, ILInstruction>[arguments.Count];
for (int i = 0; i < arguments.Count; i++) { for (int i = 0; i < arguments.Count; i++) {
ILInstruction arg; Func<ILVariable, ILInstruction> arg;
if (arguments[i] is CallInstruction bind && bind.Method.FullName == "System.Linq.Expressions.Expression.Bind") { if (arguments[i] is CallInstruction bind && bind.Method.FullName == "System.Linq.Expressions.Expression.Bind") {
arg = ConvertBind(bind, initializer).Item1; arg = ConvertBind(bind).Item1;
if (arg == null) if (arg == null)
return (null, SpecialType.UnknownType); return (null, SpecialType.UnknownType);
} else { } else {
return (null, SpecialType.UnknownType); return (null, SpecialType.UnknownType);
} }
arguments[i] = arg; convertedArguments[i] = arg;
} }
ILInstruction BuildBlock()
{
var function = lambdaStack.Peek();
var initializer = function.RegisterVariable(VariableKind.InitializerTarget, ctor.DeclaringType);
var initializerBlock = new Block(BlockKind.CollectionInitializer); var initializerBlock = new Block(BlockKind.CollectionInitializer);
initializerBlock.FinalInstruction = new LdLoc(initializer); initializerBlock.FinalInstruction = new LdLoc(initializer);
initializerBlock.Instructions.Add(new StLoc(initializer, newObj)); initializerBlock.Instructions.Add(new StLoc(initializer, newObj()));
initializerBlock.Instructions.AddRange(arguments); initializerBlock.Instructions.AddRange(convertedArguments.Select(f => f(initializer)));
return (initializerBlock, initializer.Type);
return initializerBlock;
} }
(ILInstruction, IType) ConvertNewArrayBounds(CallInstruction invocation)
return (BuildBlock, ctor.DeclaringType);
}
(Func<ILInstruction>, IType) ConvertNewArrayBounds(CallInstruction invocation)
{ {
if (invocation.Arguments.Count != 2) if (invocation.Arguments.Count != 2)
return (null, SpecialType.UnknownType); return (null, SpecialType.UnknownType);
@ -871,17 +959,17 @@ namespace ICSharpCode.Decompiler.IL.Transforms
return (null, SpecialType.UnknownType); return (null, SpecialType.UnknownType);
if (arguments.Count == 0) if (arguments.Count == 0)
return (null, SpecialType.UnknownType); return (null, SpecialType.UnknownType);
var indices = new ILInstruction[arguments.Count]; var indices = new Func<ILInstruction>[arguments.Count];
for (int i = 0; i < arguments.Count; i++) { for (int i = 0; i < arguments.Count; i++) {
var index = ConvertInstruction(arguments[i]).Item1; var index = ConvertInstruction(arguments[i]).Item1;
if (index == null) if (index == null)
return (null, SpecialType.UnknownType); return (null, SpecialType.UnknownType);
indices[i] = index; indices[i] = index;
} }
return (new NewArr(type, indices), new ArrayType(context.TypeSystem, type, arguments.Count)); return (() => new NewArr(type, indices.SelectArray(f => f())), new ArrayType(context.TypeSystem, type, arguments.Count));
} }
(ILInstruction, IType) ConvertNewArrayInit(CallInstruction invocation) (Func<ILInstruction>, IType) ConvertNewArrayInit(CallInstruction invocation)
{ {
if (invocation.Arguments.Count != 2) if (invocation.Arguments.Count != 2)
return (null, SpecialType.UnknownType); return (null, SpecialType.UnknownType);
@ -891,67 +979,107 @@ namespace ICSharpCode.Decompiler.IL.Transforms
return (null, SpecialType.UnknownType); return (null, SpecialType.UnknownType);
ArrayType arrayType = new ArrayType(context.BlockContext.TypeSystem, type); ArrayType arrayType = new ArrayType(context.BlockContext.TypeSystem, type);
if (arguments.Count == 0) if (arguments.Count == 0)
return (new NewArr(type, new LdcI4(0)), arrayType); return (() => new NewArr(type, new LdcI4(0)), arrayType);
var convertedArguments = new Func<ILInstruction>[arguments.Count];
for (int i = 0; i < arguments.Count; i++) {
ILInstruction item = arguments[i];
var value = ConvertInstruction(item).Item1;
if (value == null)
return (null, SpecialType.UnknownType);
convertedArguments[i] = value;
}
ILInstruction BuildInitializer()
{
var block = (Block)invocation.Arguments[1]; var block = (Block)invocation.Arguments[1];
var function = lambdaStack.Peek(); var function = lambdaStack.Peek();
var variable = function.RegisterVariable(VariableKind.InitializerTarget, arrayType); var variable = function.RegisterVariable(VariableKind.InitializerTarget, arrayType);
Block initializer = new Block(BlockKind.ArrayInitializer); Block initializer = new Block(BlockKind.ArrayInitializer);
int i = 0; initializer.Instructions.Add(new StLoc(variable, new NewArr(type, new LdcI4(convertedArguments.Length))));
initializer.Instructions.Add(new StLoc(variable, new NewArr(type, new LdcI4(arguments.Count)))); for (int i = 0; i < convertedArguments.Length; i++) {
foreach (var item in arguments) { initializer.Instructions.Add(new StObj(new LdElema(type, new LdLoc(variable), new LdcI4(i)), convertedArguments[i](), type));
var value = ConvertInstruction(item).Item1;
if (value == null)
return (null, SpecialType.UnknownType);
initializer.Instructions.Add(new StObj(new LdElema(type, new LdLoc(variable), new LdcI4(i)), value, type));
} }
initializer.FinalInstruction = new LdLoc(variable); initializer.FinalInstruction = new LdLoc(variable);
return (initializer, variable.Type); return initializer;
}
return (BuildInitializer, arrayType);
} }
(ILInstruction, IType) ConvertNewObject(CallInstruction invocation) bool MatchNew(CallInstruction invocation, out IMethod ctor)
{
ctor = null;
if (invocation.Method.Name != "New")
return false;
switch (invocation.Arguments.Count) {
case 1:
if (MatchGetTypeFromHandle(invocation.Arguments[0], out var type)) {
ctor = type.GetConstructors(c => c.Parameters.Count == 0).FirstOrDefault();
return ctor != null;
}
if (MatchGetConstructorFromHandle(invocation.Arguments[0], out var member)) {
ctor = (IMethod)member;
return true;
}
return false;
case 2:
case 3:
if (!MatchGetConstructorFromHandle(invocation.Arguments[0], out member))
return false;
ctor = (IMethod)member;
return true;
default:
return false;
}
}
(Func<ILInstruction>, IType) ConvertNewObject(CallInstruction invocation)
{ {
IMember member;
IList<ILInstruction> arguments;
NewObj newObj;
switch (invocation.Arguments.Count) { switch (invocation.Arguments.Count) {
case 1: case 1:
if (MatchGetTypeFromHandle(invocation.Arguments[0], out var type)) { if (MatchGetTypeFromHandle(invocation.Arguments[0], out var type)) {
var ctor = type.GetConstructors(c => c.Parameters.Count == 0).FirstOrDefault(); var ctor = type.GetConstructors(c => c.Parameters.Count == 0).FirstOrDefault();
if (ctor == null) if (ctor == null)
return (null, SpecialType.UnknownType); return (null, SpecialType.UnknownType);
return (new NewObj(ctor), type); return (() => new NewObj(ctor), type);
} }
if (MatchGetConstructorFromHandle(invocation.Arguments[0], out member)) { if (MatchGetConstructorFromHandle(invocation.Arguments[0], out var member)) {
return (new NewObj((IMethod)member), member.DeclaringType); return (() => new NewObj((IMethod)member), member.DeclaringType);
} }
return (null, SpecialType.UnknownType); return (null, SpecialType.UnknownType);
case 2: case 2:
if (!MatchGetConstructorFromHandle(invocation.Arguments[0], out member)) if (!MatchGetConstructorFromHandle(invocation.Arguments[0], out member))
return (null, SpecialType.UnknownType); return (null, SpecialType.UnknownType);
if (!MatchArgumentList(invocation.Arguments[1], out arguments)) if (!MatchArgumentList(invocation.Arguments[1], out var arguments))
return (null, SpecialType.UnknownType); return (null, SpecialType.UnknownType);
IMethod method = (IMethod)member; IMethod method = (IMethod)member;
if (!ConvertCallArguments(arguments, method)) Func<ILInstruction>[] convertedArguments = ConvertCallArguments(arguments, method);
if (convertedArguments == null)
return (null, SpecialType.UnknownType); return (null, SpecialType.UnknownType);
newObj = new NewObj(method); return (() => BuildNewObj(method, convertedArguments), member.DeclaringType);
newObj.Arguments.AddRange(arguments);
return (newObj, member.DeclaringType);
case 3: case 3:
if (!MatchGetConstructorFromHandle(invocation.Arguments[0], out member)) if (!MatchGetConstructorFromHandle(invocation.Arguments[0], out member))
return (null, SpecialType.UnknownType); return (null, SpecialType.UnknownType);
if (!MatchArgumentList(invocation.Arguments[1], out arguments)) if (!MatchArgumentList(invocation.Arguments[1], out arguments))
return (null, SpecialType.UnknownType); return (null, SpecialType.UnknownType);
method = (IMethod)member; method = (IMethod)member;
if (!ConvertCallArguments(arguments, method)) convertedArguments = ConvertCallArguments(arguments, method);
if (convertedArguments == null)
return (null, SpecialType.UnknownType); return (null, SpecialType.UnknownType);
newObj = new NewObj(method); return (() => BuildNewObj(method, convertedArguments), member.DeclaringType);
newObj.Arguments.AddRange(arguments); }
return (newObj, member.DeclaringType);
ILInstruction BuildNewObj(IMethod method, Func<ILInstruction>[] args)
{
var newObj = new NewObj(method);
newObj.Arguments.AddRange(args.Select(f => f()));
return newObj;
} }
return (null, SpecialType.UnknownType); return (null, SpecialType.UnknownType);
} }
(ILInstruction, IType) ConvertNotOperator(CallInstruction invocation) (Func<ILInstruction>, IType) ConvertNotOperator(CallInstruction invocation)
{ {
if (invocation.Arguments.Count < 1) if (invocation.Arguments.Count < 1)
return (null, SpecialType.UnknownType); return (null, SpecialType.UnknownType);
@ -960,27 +1088,27 @@ namespace ICSharpCode.Decompiler.IL.Transforms
return (null, SpecialType.UnknownType); return (null, SpecialType.UnknownType);
switch (invocation.Arguments.Count) { switch (invocation.Arguments.Count) {
case 1: case 1:
return (argumentType.IsKnownType(KnownTypeCode.Boolean) ? Comp.LogicNot(argument) : (ILInstruction)new BitNot(argument), argumentType); return (() => argumentType.IsKnownType(KnownTypeCode.Boolean) ? Comp.LogicNot(argument()) : (ILInstruction)new BitNot(argument()), argumentType);
case 2: case 2:
if (!MatchGetMethodFromHandle(invocation.Arguments[1], out var method)) if (!MatchGetMethodFromHandle(invocation.Arguments[1], out var method))
return (null, SpecialType.UnknownType); return (null, SpecialType.UnknownType);
return (new Call((IMethod)method) { return (() => new Call((IMethod)method) {
Arguments = { argument } Arguments = { argument() }
}, method.ReturnType); }, method.ReturnType);
default: default:
return (null, SpecialType.UnknownType); return (null, SpecialType.UnknownType);
} }
} }
(ILInstruction, IType) ConvertProperty(CallInstruction invocation) (Func<ILInstruction>, IType) ConvertProperty(CallInstruction invocation)
{ {
if (invocation.Arguments.Count < 2) if (invocation.Arguments.Count < 2)
return (null, SpecialType.UnknownType); return (null, SpecialType.UnknownType);
ILInstruction target = null; Func<ILInstruction> targetConverter = null;
IType targetType = null; IType targetType = null;
if (!invocation.Arguments[0].MatchLdNull()) { if (!invocation.Arguments[0].MatchLdNull()) {
(target, targetType) = ConvertInstruction(invocation.Arguments[0]); (targetConverter, targetType) = ConvertInstruction(invocation.Arguments[0]);
if (target == null) if (targetConverter == null)
return (null, SpecialType.UnknownType); return (null, SpecialType.UnknownType);
} }
if (!MatchGetMethodFromHandle(invocation.Arguments[1], out var member)) if (!MatchGetMethodFromHandle(invocation.Arguments[1], out var member))
@ -988,24 +1116,28 @@ namespace ICSharpCode.Decompiler.IL.Transforms
IList<ILInstruction> arguments; IList<ILInstruction> arguments;
if (invocation.Arguments.Count != 3 || !MatchArgumentList(invocation.Arguments[2], out arguments)) { if (invocation.Arguments.Count != 3 || !MatchArgumentList(invocation.Arguments[2], out arguments)) {
arguments = new List<ILInstruction>(); arguments = new List<ILInstruction>();
} else {
if (!ConvertCallArguments(arguments, (IMethod)member))
return (null, SpecialType.UnknownType);
} }
var convertedArguments = ConvertCallArguments(arguments, (IMethod)member);
if (convertedArguments == null)
return (null, SpecialType.UnknownType);
ILInstruction BuildProperty()
{
CallInstruction call; CallInstruction call;
if (member.IsAbstract || member.IsVirtual || member.IsOverride) { if (member.IsAbstract || member.IsVirtual || member.IsOverride) {
call = new CallVirt((IMethod)member); call = new CallVirt((IMethod)member);
} else { } else {
call = new Call((IMethod)member); call = new Call((IMethod)member);
} }
if (target != null) { if (targetConverter != null) {
call.Arguments.Add(PrepareCallTarget(member.DeclaringType, target, targetType)); call.Arguments.Add(PrepareCallTarget(member.DeclaringType, targetConverter(), targetType));
} }
call.Arguments.AddRange(arguments); call.Arguments.AddRange(convertedArguments.Select(f => f()));
return (call, member.ReturnType); return call;
}
return (BuildProperty, member.ReturnType);
} }
(ILInstruction, IType) ConvertTypeAs(CallInstruction invocation) (Func<ILInstruction>, IType) ConvertTypeAs(CallInstruction invocation)
{ {
if (invocation.Arguments.Count != 2) if (invocation.Arguments.Count != 2)
return (null, SpecialType.UnknownType); return (null, SpecialType.UnknownType);
@ -1014,16 +1146,19 @@ namespace ICSharpCode.Decompiler.IL.Transforms
return (null, SpecialType.UnknownType); return (null, SpecialType.UnknownType);
if (converted == null) if (converted == null)
return (null, SpecialType.UnknownType); return (null, SpecialType.UnknownType);
ILInstruction BuildTypeAs()
ILInstruction inst = new IsInst(converted, type); {
ILInstruction inst = new IsInst(converted(), type);
// We must follow ECMA-335, III.4.6: // We must follow ECMA-335, III.4.6:
// If typeTok is a nullable type, Nullable<T>, it is interpreted as "boxed" T. // If typeTok is a nullable type, Nullable<T>, it is interpreted as "boxed" T.
if (type.IsKnownType(KnownTypeCode.NullableOfT)) if (type.IsKnownType(KnownTypeCode.NullableOfT))
inst = new UnboxAny(inst, type); inst = new UnboxAny(inst, type);
return (inst, type); return inst;
}
return (BuildTypeAs, type);
} }
(ILInstruction, IType) ConvertTypeIs(CallInstruction invocation) (Func<ILInstruction>, IType) ConvertTypeIs(CallInstruction invocation)
{ {
if (invocation.Arguments.Count != 2) if (invocation.Arguments.Count != 2)
return (null, SpecialType.UnknownType); return (null, SpecialType.UnknownType);
@ -1032,11 +1167,11 @@ namespace ICSharpCode.Decompiler.IL.Transforms
return (null, SpecialType.UnknownType); return (null, SpecialType.UnknownType);
var resultType = context.TypeSystem.FindType(KnownTypeCode.Boolean); var resultType = context.TypeSystem.FindType(KnownTypeCode.Boolean);
if (converted != null) if (converted != null)
return (new Comp(ComparisonKind.Inequality, Sign.None, new IsInst(converted, type), new LdNull()), resultType); return (() => new Comp(ComparisonKind.Inequality, Sign.None, new IsInst(converted(), type), new LdNull()), resultType);
return (null, SpecialType.UnknownType); return (null, SpecialType.UnknownType);
} }
(ILInstruction, IType) ConvertUnaryNumericOperator(CallInstruction invocation, BinaryNumericOperator op, bool? isChecked = null) (Func<ILInstruction>, IType) ConvertUnaryNumericOperator(CallInstruction invocation, BinaryNumericOperator op, bool? isChecked = null)
{ {
if (invocation.Arguments.Count < 1) if (invocation.Arguments.Count < 1)
return (null, SpecialType.UnknownType); return (null, SpecialType.UnknownType);
@ -1046,7 +1181,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
switch (invocation.Arguments.Count) { switch (invocation.Arguments.Count) {
case 1: case 1:
ILInstruction left; ILInstruction left;
switch (argument.ResultType) { switch (argumentType.GetStackType()) {
case StackType.I4: case StackType.I4:
left = new LdcI4(0); left = new LdcI4(0);
break; break;
@ -1065,12 +1200,12 @@ namespace ICSharpCode.Decompiler.IL.Transforms
default: default:
return (null, SpecialType.UnknownType); return (null, SpecialType.UnknownType);
} }
return (new BinaryNumericInstruction(op, left, argument, isChecked == true, argumentType.GetSign()), argumentType); return (() => new BinaryNumericInstruction(op, left, argument(), isChecked == true, argumentType.GetSign()), argumentType);
case 2: case 2:
if (!MatchGetMethodFromHandle(invocation.Arguments[1], out var method)) if (!MatchGetMethodFromHandle(invocation.Arguments[1], out var method))
return (null, SpecialType.UnknownType); return (null, SpecialType.UnknownType);
return (new Call((IMethod)method) { return (() => new Call((IMethod)method) {
Arguments = { argument } Arguments = { argument() }
}, method.ReturnType); }, method.ReturnType);
} }
return (null, SpecialType.UnknownType); return (null, SpecialType.UnknownType);
@ -1082,11 +1217,11 @@ namespace ICSharpCode.Decompiler.IL.Transforms
case LdLoc ldloc: case LdLoc ldloc:
if (IsExpressionTreeParameter(ldloc.Variable)) { if (IsExpressionTreeParameter(ldloc.Variable)) {
if (!parameterMapping.TryGetValue(ldloc.Variable, out var v)) if (!parameterMapping.TryGetValue(ldloc.Variable, out var v))
return ldloc; return ldloc.Clone();
if (context is CallInstruction parentCall if (context is CallInstruction parentCall
&& parentCall.Method.FullName == "System.Linq.Expressions.Expression.Call" && parentCall.Method.FullName == "System.Linq.Expressions.Expression.Call"
&& v.StackType.IsIntegerType()) && v.StackType.IsIntegerType())
return new LdLoca(v); return new LdLoca(v).WithILRange(ldloc);
return null; return null;
} else if (IsClosureReference(ldloc.Variable)) { } else if (IsClosureReference(ldloc.Variable)) {
if (ldloc.Variable.Kind == VariableKind.Local) { if (ldloc.Variable.Kind == VariableKind.Local) {

Loading…
Cancel
Save