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;
}