From 273a1c24ff980df46e7862c53d9fcbaaac7f429a Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Fri, 1 Jun 2018 20:15:03 +0200 Subject: [PATCH] Add dynamic compound assignment transforms --- .../Expressions/AssignmentExpression.cs | 31 +++++++ .../IL/Transforms/ExpressionTransforms.cs | 84 +++++++++++++------ .../IL/Transforms/TransformAssignment.cs | 5 ++ 3 files changed, 93 insertions(+), 27 deletions(-) diff --git a/ICSharpCode.Decompiler/CSharp/Syntax/Expressions/AssignmentExpression.cs b/ICSharpCode.Decompiler/CSharp/Syntax/Expressions/AssignmentExpression.cs index e2ffd0fa0..c840261e2 100644 --- a/ICSharpCode.Decompiler/CSharp/Syntax/Expressions/AssignmentExpression.cs +++ b/ICSharpCode.Decompiler/CSharp/Syntax/Expressions/AssignmentExpression.cs @@ -202,6 +202,37 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax throw new NotSupportedException("Invalid value for AssignmentOperatorType"); } } + + public static AssignmentOperatorType GetAssignmentOperatorTypeFromExpressionType(ExpressionType expressionType) + { + switch (expressionType) { + case ExpressionType.AddAssign: + case ExpressionType.AddAssignChecked: + return AssignmentOperatorType.Add; + case ExpressionType.AndAssign: + return AssignmentOperatorType.BitwiseAnd; + case ExpressionType.DivideAssign: + return AssignmentOperatorType.Divide; + case ExpressionType.ExclusiveOrAssign: + return AssignmentOperatorType.ExclusiveOr; + case ExpressionType.LeftShiftAssign: + return AssignmentOperatorType.ShiftLeft; + case ExpressionType.ModuloAssign: + return AssignmentOperatorType.Modulus; + case ExpressionType.MultiplyAssign: + case ExpressionType.MultiplyAssignChecked: + return AssignmentOperatorType.Multiply; + case ExpressionType.OrAssign: + return AssignmentOperatorType.BitwiseOr; + case ExpressionType.RightShiftAssign: + return AssignmentOperatorType.ShiftRight; + case ExpressionType.SubtractAssign: + case ExpressionType.SubtractAssignChecked: + return AssignmentOperatorType.Subtract; + default: + throw new NotSupportedException($"ExpressionType.{expressionType} not supported!"); + } + } } public enum AssignmentOperatorType diff --git a/ICSharpCode.Decompiler/IL/Transforms/ExpressionTransforms.cs b/ICSharpCode.Decompiler/IL/Transforms/ExpressionTransforms.cs index e32732099..50357d262 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/ExpressionTransforms.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/ExpressionTransforms.cs @@ -303,7 +303,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms return; } TransformAssignment.HandleCompoundAssign(inst, context); - } + } protected internal override void VisitIfInstruction(IfInstruction inst) { @@ -336,53 +336,83 @@ namespace ICSharpCode.Decompiler.IL.Transforms } /// - /// if (logic.not(dynamic.isevent (ldloc a))) Block IL_0045 { - /// dynamic.setmember.compound Setter2(ldloc a, dynamic.binary.operator AddAssign(dynamic.getmember Setter2(ldloc a), ldc.i4 5)) - /// } else Block IL_0144 { - /// dynamic.invokemember.invokespecial.discard add_Setter2(ldloc a, ldc.i4 5) + /// op is either add or remove/subtract: + /// if (dynamic.isevent (target)) { + /// dynamic.invokemember.invokespecial.discard op_Name(target, value) + /// } else { + /// dynamic.compound.op (dynamic.getmember Name(target), value) /// } /// => - /// dynamic.setmember.compound Setter2(ldloc a, dynamic.binary.operator AddAssign(dynamic.getmember Setter2(ldloc a), ldc.i4 5)) + /// dynamic.compound.op (dynamic.getmember Name(target), value) /// bool TransformDynamicAddAssignOrRemoveAssign(IfInstruction inst) { - if (!inst.Condition.MatchLogicNot(out var possibleIsEvent)) + if (!inst.MatchIfInstructionPositiveCondition(out var condition, out var trueInst, out var falseInst)) return false; - if (!(possibleIsEvent is DynamicIsEventInstruction isEvent)) + if (!(condition is DynamicIsEventInstruction isEvent)) return false; - var trueInst = Block.Unwrap(inst.TrueInst); - var falseInst = Block.Unwrap(inst.FalseInst); - if (!(trueInst is DynamicSetMemberInstruction dynamicSetMember - && dynamicSetMember.BinderFlags.HasFlag(CSharpBinderFlags.ValueFromCompoundAssignment) - && isEvent.Argument.Match(dynamicSetMember.Target).Success - && dynamicSetMember.Value is DynamicBinaryOperatorInstruction binaryOp - && binaryOp.Left is DynamicGetMemberInstruction dynamicGetMember - && dynamicGetMember.Target.Match(dynamicSetMember.Target).Success - && dynamicSetMember.Name == dynamicGetMember.Name - && falseInst is DynamicInvokeMemberInstruction invokeMember - && invokeMember.BinderFlags.HasFlag(CSharpBinderFlags.InvokeSpecialName) - && invokeMember.Arguments.Count == 2 && invokeMember.Arguments[0].Match(dynamicGetMember.Target).Success) - ) { + trueInst = Block.Unwrap(trueInst); + falseInst = Block.Unwrap(falseInst); + if (!(falseInst is DynamicCompoundAssign dynamicCompoundAssign)) return false; - } - switch (binaryOp.Operation) { + if (!(dynamicCompoundAssign.Target is DynamicGetMemberInstruction getMember)) + return false; + if (!isEvent.Argument.Match(getMember.Target).Success) + return false; + if (!(trueInst is DynamicInvokeMemberInstruction invokeMember)) + return false; + if (!(invokeMember.BinderFlags.HasFlag(CSharpBinderFlags.InvokeSpecialName) && invokeMember.BinderFlags.HasFlag(CSharpBinderFlags.ResultDiscarded))) + return false; + switch (dynamicCompoundAssign.Operation) { case ExpressionType.AddAssign: - if (invokeMember.Name != "add_" + dynamicGetMember.Name) + if (invokeMember.Name != "add_" + getMember.Name) return false; break; case ExpressionType.SubtractAssign: - if (invokeMember.Name != "remove_" + dynamicGetMember.Name) + if (invokeMember.Name != "remove_" + getMember.Name) return false; break; default: return false; } - if (!binaryOp.Right.Match(invokeMember.Arguments[1]).Success) + if (!dynamicCompoundAssign.Value.Match(invokeMember.Arguments[1]).Success) + return false; + if (!invokeMember.Arguments[0].Match(getMember.Target).Success) return false; - inst.ReplaceWith(trueInst); + inst.ReplaceWith(dynamicCompoundAssign); return true; } + /// + /// dynamic.setmember.compound Name(target, dynamic.binary.operator AddAssign(dynamic.getmember Name(target), value)) + /// => + /// dynamic.compound.op (dynamic.getmember Name(target), value) + /// + protected internal override void VisitDynamicSetMemberInstruction(DynamicSetMemberInstruction inst) + { + if (!inst.BinderFlags.HasFlag(CSharpBinderFlags.ValueFromCompoundAssignment)) { + base.VisitDynamicSetMemberInstruction(inst); + return; + } + if (!(inst.Value is DynamicBinaryOperatorInstruction binaryOp)) { + base.VisitDynamicSetMemberInstruction(inst); + return; + } + if (!(binaryOp.Left is DynamicGetMemberInstruction dynamicGetMember)) { + base.VisitDynamicSetMemberInstruction(inst); + return; + } + if (!dynamicGetMember.Target.Match(inst.Target).Success) { + base.VisitDynamicSetMemberInstruction(inst); + return; + } + if (inst.Name != dynamicGetMember.Name || !DynamicCompoundAssign.IsExpressionTypeSupported(binaryOp.Operation)) { + base.VisitDynamicSetMemberInstruction(inst); + return; + } + inst.ReplaceWith(new DynamicCompoundAssign(binaryOp.Operation, binaryOp.Left, binaryOp.LeftArgumentInfo, binaryOp.Right, binaryOp.RightArgumentInfo)); + } + IfInstruction HandleConditionalOperator(IfInstruction inst) { // if (cond) stloc (A, V1) else stloc (A, V2) --> stloc (A, if (cond) V1 else V2) diff --git a/ICSharpCode.Decompiler/IL/Transforms/TransformAssignment.cs b/ICSharpCode.Decompiler/IL/Transforms/TransformAssignment.cs index 25a398df8..66fbaea2c 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/TransformAssignment.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/TransformAssignment.cs @@ -293,6 +293,11 @@ namespace ICSharpCode.Decompiler.IL.Transforms context.Step($"Compound assignment (user-defined binary)", compoundStore); newInst = new UserDefinedCompoundAssign(operatorCall.Method, CompoundAssignmentType.EvaluatesToNewValue, operatorCall.Arguments[0], rhs); + } else if (setterValue is DynamicBinaryOperatorInstruction dynamicBinaryOp) { + if (!IsMatchingCompoundLoad(dynamicBinaryOp.Left, compoundStore, forbiddenVariable: storeInSetter?.Variable)) + return false; + context.Step($"Compound assignment (dynamic binary)", compoundStore); + newInst = new DynamicCompoundAssign(dynamicBinaryOp.Operation, dynamicBinaryOp.Left, dynamicBinaryOp.LeftArgumentInfo, dynamicBinaryOp.Right, dynamicBinaryOp.RightArgumentInfo); } else { return false; }