Browse Source

Add dynamic compound assignment transforms

pull/1165/head
Siegfried Pammer 7 years ago
parent
commit
273a1c24ff
  1. 31
      ICSharpCode.Decompiler/CSharp/Syntax/Expressions/AssignmentExpression.cs
  2. 84
      ICSharpCode.Decompiler/IL/Transforms/ExpressionTransforms.cs
  3. 5
      ICSharpCode.Decompiler/IL/Transforms/TransformAssignment.cs

31
ICSharpCode.Decompiler/CSharp/Syntax/Expressions/AssignmentExpression.cs

@ -202,6 +202,37 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax @@ -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

84
ICSharpCode.Decompiler/IL/Transforms/ExpressionTransforms.cs

@ -303,7 +303,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -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 @@ -336,53 +336,83 @@ namespace ICSharpCode.Decompiler.IL.Transforms
}
/// <summary>
/// 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)
/// </summary>
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;
}
/// <summary>
/// dynamic.setmember.compound Name(target, dynamic.binary.operator AddAssign(dynamic.getmember Name(target), value))
/// =>
/// dynamic.compound.op (dynamic.getmember Name(target), value)
/// </summary>
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)

5
ICSharpCode.Decompiler/IL/Transforms/TransformAssignment.cs

@ -293,6 +293,11 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -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;
}

Loading…
Cancel
Save