Browse Source

Implement UserDefinedCompoundAssign

rename CompoundAssignmentInstruction -> NumericCompoundAssign
pull/1129/head
Siegfried Pammer 7 years ago
parent
commit
845c620a9f
  1. 69
      ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs
  2. 219
      ICSharpCode.Decompiler/IL/Instructions.cs
  3. 21
      ICSharpCode.Decompiler/IL/Instructions.tt
  4. 91
      ICSharpCode.Decompiler/IL/Instructions/CompoundAssignmentInstruction.cs
  5. 4
      ICSharpCode.Decompiler/IL/Transforms/DelegateConstruction.cs
  6. 19
      ICSharpCode.Decompiler/IL/Transforms/ExpressionTransforms.cs
  7. 3
      ICSharpCode.Decompiler/IL/Transforms/HighLevelLoopTransform.cs
  8. 3
      ICSharpCode.Decompiler/IL/Transforms/ILInlining.cs

69
ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs

@ -1102,8 +1102,69 @@ namespace ICSharpCode.Decompiler.CSharp
.WithILInstruction(inst) .WithILInstruction(inst)
.WithRR(resolver.ResolveBinaryOperator(op, left.ResolveResult, right.ResolveResult)); .WithRR(resolver.ResolveBinaryOperator(op, left.ResolveResult, right.ResolveResult));
} }
protected internal override TranslatedExpression VisitCompoundAssignmentInstruction(CompoundAssignmentInstruction inst, TranslationContext context) protected internal override TranslatedExpression VisitUserDefinedCompoundAssign(UserDefinedCompoundAssign inst, TranslationContext context)
{
var target = Translate(inst.Target);
if (inst.Method.Parameters.Count == 2) {
var value = Translate(inst.Value).ConvertTo(inst.Method.Parameters[1].Type, this);
AssignmentOperatorType? op = GetAssignmentOperatorTypeFromMetadataName(inst.Method.Name);
Debug.Assert(op != null);
return new AssignmentExpression(target, op.Value, value)
.WithILInstruction(inst)
.WithRR(new OperatorResolveResult(inst.Method.ReturnType, AssignmentExpression.GetLinqNodeType(op.Value, false), inst.Method, inst.IsLifted, new[] { target.ResolveResult, value.ResolveResult }));
} else {
UnaryOperatorType? op = GetUnaryOperatorTypeFromMetadataName(inst.Method.Name, inst.CompoundAssignmentType == CompoundAssignmentType.EvaluatesToOldValue);
Debug.Assert(op != null);
return new UnaryOperatorExpression(op.Value, target)
.WithILInstruction(inst)
.WithRR(new OperatorResolveResult(inst.Method.ReturnType, UnaryOperatorExpression.GetLinqNodeType(op.Value, false), inst.Method, inst.IsLifted, new[] { target.ResolveResult }));
}
}
internal static AssignmentOperatorType? GetAssignmentOperatorTypeFromMetadataName(string name)
{
switch (name) {
case "op_Addition":
return AssignmentOperatorType.Add;
case "op_Subtraction":
return AssignmentOperatorType.Subtract;
case "op_Multiply":
return AssignmentOperatorType.Multiply;
case "op_Division":
return AssignmentOperatorType.Divide;
case "op_Modulus":
return AssignmentOperatorType.Modulus;
case "op_BitwiseAnd":
return AssignmentOperatorType.BitwiseAnd;
case "op_BitwiseOr":
return AssignmentOperatorType.BitwiseOr;
case "op_ExclusiveOr":
return AssignmentOperatorType.ExclusiveOr;
case "op_LeftShift":
return AssignmentOperatorType.ShiftLeft;
case "op_RightShift":
return AssignmentOperatorType.ShiftRight;
default:
return null;
}
}
internal static UnaryOperatorType? GetUnaryOperatorTypeFromMetadataName(string name, bool isPostfix)
{
switch (name) {
case "op_Increment":
return isPostfix ? UnaryOperatorType.PostIncrement : UnaryOperatorType.Increment;
case "op_Decrement":
return isPostfix ? UnaryOperatorType.PostDecrement : UnaryOperatorType.Decrement;
default:
return null;
}
}
protected internal override TranslatedExpression VisitNumericCompoundAssign(NumericCompoundAssign inst, TranslationContext context)
{ {
switch (inst.Operator) { switch (inst.Operator) {
case BinaryNumericOperator.Add: case BinaryNumericOperator.Add:
@ -1131,7 +1192,7 @@ namespace ICSharpCode.Decompiler.CSharp
} }
} }
TranslatedExpression HandleCompoundAssignment(CompoundAssignmentInstruction inst, AssignmentOperatorType op) TranslatedExpression HandleCompoundAssignment(NumericCompoundAssign inst, AssignmentOperatorType op)
{ {
var target = Translate(inst.Target); var target = Translate(inst.Target);
var value = Translate(inst.Value); var value = Translate(inst.Value);
@ -1196,7 +1257,7 @@ namespace ICSharpCode.Decompiler.CSharp
return resultExpr; return resultExpr;
} }
TranslatedExpression HandleCompoundShift(CompoundAssignmentInstruction inst, AssignmentOperatorType op) TranslatedExpression HandleCompoundShift(NumericCompoundAssign inst, AssignmentOperatorType op)
{ {
Debug.Assert(inst.CompoundAssignmentType == CompoundAssignmentType.EvaluatesToNewValue); Debug.Assert(inst.CompoundAssignmentType == CompoundAssignmentType.EvaluatesToNewValue);
var target = Translate(inst.Target); var target = Translate(inst.Target);

219
ICSharpCode.Decompiler/IL/Instructions.cs

@ -45,8 +45,10 @@ namespace ICSharpCode.Decompiler.IL
PinnedRegion, PinnedRegion,
/// <summary>Common instruction for add, sub, mul, div, rem, bit.and, bit.or, bit.xor, shl and shr.</summary> /// <summary>Common instruction for add, sub, mul, div, rem, bit.and, bit.or, bit.xor, shl and shr.</summary>
BinaryNumericInstruction, BinaryNumericInstruction,
/// <summary>Common instruction for compound assignments.</summary> /// <summary>Common instruction for numeric compound assignments.</summary>
CompoundAssignmentInstruction, NumericCompoundAssign,
/// <summary>Common instruction for user-defined compound assignments.</summary>
UserDefinedCompoundAssign,
/// <summary>Bitwise NOT</summary> /// <summary>Bitwise NOT</summary>
BitNot, BitNot,
/// <summary>Retrieves the RuntimeArgumentHandle.</summary> /// <summary>Retrieves the RuntimeArgumentHandle.</summary>
@ -485,6 +487,96 @@ namespace ICSharpCode.Decompiler.IL.Patterns
} }
} }
namespace ICSharpCode.Decompiler.IL namespace ICSharpCode.Decompiler.IL
{
/// <summary>Common instruction for compound assignments.</summary>
public abstract partial class CompoundAssignmentInstruction : ILInstruction
{
public static readonly SlotInfo TargetSlot = new SlotInfo("Target", canInlineInto: true);
ILInstruction target;
public ILInstruction Target {
get { return this.target; }
set {
ValidateChild(value);
SetChildInstruction(ref this.target, value, 0);
}
}
public static readonly SlotInfo ValueSlot = new SlotInfo("Value", canInlineInto: true);
ILInstruction value;
public ILInstruction Value {
get { return this.value; }
set {
ValidateChild(value);
SetChildInstruction(ref this.value, value, 1);
}
}
protected sealed override int GetChildCount()
{
return 2;
}
protected sealed override ILInstruction GetChild(int index)
{
switch (index) {
case 0:
return this.target;
case 1:
return this.value;
default:
throw new IndexOutOfRangeException();
}
}
protected sealed override void SetChild(int index, ILInstruction value)
{
switch (index) {
case 0:
this.Target = value;
break;
case 1:
this.Value = value;
break;
default:
throw new IndexOutOfRangeException();
}
}
protected sealed override SlotInfo GetChildSlot(int index)
{
switch (index) {
case 0:
return TargetSlot;
case 1:
return ValueSlot;
default:
throw new IndexOutOfRangeException();
}
}
public sealed override ILInstruction Clone()
{
var clone = (CompoundAssignmentInstruction)ShallowClone();
clone.Target = this.target.Clone();
clone.Value = this.value.Clone();
return clone;
}
protected override InstructionFlags ComputeFlags()
{
return target.Flags | value.Flags;
}
public override InstructionFlags DirectFlags {
get {
return InstructionFlags.None;
}
}
public override void WriteTo(ITextOutput output, ILAstWritingOptions options)
{
ILRange.WriteTo(output, options);
output.Write(OpCode);
output.Write('(');
this.target.WriteTo(output, options);
output.Write(", ");
this.value.WriteTo(output, options);
output.Write(')');
}
}
}
namespace ICSharpCode.Decompiler.IL
{ {
/// <summary>Represents invalid IL. Semantically, this instruction is considered to throw some kind of exception.</summary> /// <summary>Represents invalid IL. Semantically, this instruction is considered to throw some kind of exception.</summary>
public sealed partial class InvalidBranch : SimpleInstruction public sealed partial class InvalidBranch : SimpleInstruction
@ -891,96 +983,66 @@ namespace ICSharpCode.Decompiler.IL
} }
namespace ICSharpCode.Decompiler.IL namespace ICSharpCode.Decompiler.IL
{ {
/// <summary>Common instruction for compound assignments.</summary> /// <summary>Common instruction for numeric compound assignments.</summary>
public sealed partial class CompoundAssignmentInstruction : ILInstruction public sealed partial class NumericCompoundAssign : CompoundAssignmentInstruction
{ {
public static readonly SlotInfo TargetSlot = new SlotInfo("Target", canInlineInto: true); IType type;
ILInstruction target; /// <summary>Returns the type operand.</summary>
public ILInstruction Target { public IType Type {
get { return this.target; } get { return type; }
set { set { type = value; InvalidateFlags(); }
ValidateChild(value);
SetChildInstruction(ref this.target, value, 0);
}
}
public static readonly SlotInfo ValueSlot = new SlotInfo("Value", canInlineInto: true);
ILInstruction value;
public ILInstruction Value {
get { return this.value; }
set {
ValidateChild(value);
SetChildInstruction(ref this.value, value, 1);
}
} }
protected sealed override int GetChildCount() public override StackType ResultType { get { return type.GetStackType(); } }
public override void AcceptVisitor(ILVisitor visitor)
{ {
return 2; visitor.VisitNumericCompoundAssign(this);
} }
protected sealed override ILInstruction GetChild(int index) public override T AcceptVisitor<T>(ILVisitor<T> visitor)
{ {
switch (index) { return visitor.VisitNumericCompoundAssign(this);
case 0:
return this.target;
case 1:
return this.value;
default:
throw new IndexOutOfRangeException();
}
} }
protected sealed override void SetChild(int index, ILInstruction value) public override T AcceptVisitor<C, T>(ILVisitor<C, T> visitor, C context)
{ {
switch (index) { return visitor.VisitNumericCompoundAssign(this, context);
case 0:
this.Target = value;
break;
case 1:
this.Value = value;
break;
default:
throw new IndexOutOfRangeException();
}
} }
protected sealed override SlotInfo GetChildSlot(int index) protected internal override bool PerformMatch(ILInstruction other, ref Patterns.Match match)
{ {
switch (index) { var o = other as NumericCompoundAssign;
case 0: return o != null && type.Equals(o.type) && CheckForOverflow == o.CheckForOverflow && Sign == o.Sign && Operator == o.Operator && Target.PerformMatch(o.Target, ref match) && Value.PerformMatch(o.Value, ref match);
return TargetSlot;
case 1:
return ValueSlot;
default:
throw new IndexOutOfRangeException();
}
} }
public sealed override ILInstruction Clone() }
}
namespace ICSharpCode.Decompiler.IL
{
/// <summary>Common instruction for user-defined compound assignments.</summary>
public sealed partial class UserDefinedCompoundAssign : CompoundAssignmentInstruction
{
protected override InstructionFlags ComputeFlags()
{ {
var clone = (CompoundAssignmentInstruction)ShallowClone(); return base.ComputeFlags() | InstructionFlags.MayThrow | InstructionFlags.SideEffect;
clone.Target = this.target.Clone();
clone.Value = this.value.Clone();
return clone;
} }
IType type; public override InstructionFlags DirectFlags {
/// <summary>Returns the type operand.</summary> get {
public IType Type { return base.DirectFlags | InstructionFlags.MayThrow | InstructionFlags.SideEffect;
get { return type; } }
set { type = value; InvalidateFlags(); }
} }
public override StackType ResultType { get { return type.GetStackType(); } }
public override void AcceptVisitor(ILVisitor visitor) public override void AcceptVisitor(ILVisitor visitor)
{ {
visitor.VisitCompoundAssignmentInstruction(this); visitor.VisitUserDefinedCompoundAssign(this);
} }
public override T AcceptVisitor<T>(ILVisitor<T> visitor) public override T AcceptVisitor<T>(ILVisitor<T> visitor)
{ {
return visitor.VisitCompoundAssignmentInstruction(this); return visitor.VisitUserDefinedCompoundAssign(this);
} }
public override T AcceptVisitor<C, T>(ILVisitor<C, T> visitor, C context) public override T AcceptVisitor<C, T>(ILVisitor<C, T> visitor, C context)
{ {
return visitor.VisitCompoundAssignmentInstruction(this, context); return visitor.VisitUserDefinedCompoundAssign(this, context);
} }
protected internal override bool PerformMatch(ILInstruction other, ref Patterns.Match match) protected internal override bool PerformMatch(ILInstruction other, ref Patterns.Match match)
{ {
var o = other as CompoundAssignmentInstruction; var o = other as UserDefinedCompoundAssign;
return o != null && this.target.PerformMatch(o.target, ref match) && this.value.PerformMatch(o.value, ref match) && type.Equals(o.type) && CheckForOverflow == o.CheckForOverflow && Sign == o.Sign && Operator == o.Operator; return o != null && this.Method.Equals(o.Method) && this.CompoundAssignmentType == o.CompoundAssignmentType && Target.PerformMatch(o.Target, ref match) && Value.PerformMatch(o.Value, ref match);
} }
} }
} }
@ -5099,7 +5161,11 @@ namespace ICSharpCode.Decompiler.IL
{ {
Default(inst); Default(inst);
} }
protected internal virtual void VisitCompoundAssignmentInstruction(CompoundAssignmentInstruction inst) protected internal virtual void VisitNumericCompoundAssign(NumericCompoundAssign inst)
{
Default(inst);
}
protected internal virtual void VisitUserDefinedCompoundAssign(UserDefinedCompoundAssign inst)
{ {
Default(inst); Default(inst);
} }
@ -5417,7 +5483,11 @@ namespace ICSharpCode.Decompiler.IL
{ {
return Default(inst); return Default(inst);
} }
protected internal virtual T VisitCompoundAssignmentInstruction(CompoundAssignmentInstruction inst) protected internal virtual T VisitNumericCompoundAssign(NumericCompoundAssign inst)
{
return Default(inst);
}
protected internal virtual T VisitUserDefinedCompoundAssign(UserDefinedCompoundAssign inst)
{ {
return Default(inst); return Default(inst);
} }
@ -5735,7 +5805,11 @@ namespace ICSharpCode.Decompiler.IL
{ {
return Default(inst, context); return Default(inst, context);
} }
protected internal virtual T VisitCompoundAssignmentInstruction(CompoundAssignmentInstruction inst, C context) protected internal virtual T VisitNumericCompoundAssign(NumericCompoundAssign inst, C context)
{
return Default(inst, context);
}
protected internal virtual T VisitUserDefinedCompoundAssign(UserDefinedCompoundAssign inst, C context)
{ {
return Default(inst, context); return Default(inst, context);
} }
@ -6024,7 +6098,8 @@ namespace ICSharpCode.Decompiler.IL
"Block", "Block",
"PinnedRegion", "PinnedRegion",
"binary", "binary",
"compound", "numeric.compound",
"user.compound",
"bit.not", "bit.not",
"arglist", "arglist",
"br", "br",

21
ICSharpCode.Decompiler/IL/Instructions.tt

@ -33,7 +33,9 @@
new OpCode("CallInstruction", "Instruction with a list of arguments.", new OpCode("CallInstruction", "Instruction with a list of arguments.",
AbstractBaseClass, CustomChildren(new []{ new ArgumentInfo("arguments") { IsCollection = true }}), AbstractBaseClass, CustomChildren(new []{ new ArgumentInfo("arguments") { IsCollection = true }}),
CustomWriteTo, MayThrow, SideEffect), CustomWriteTo, MayThrow, SideEffect),
new OpCode("PatternInstruction", "Base class for pattern matching in ILAst.", AbstractBaseClass, ResultType("Unknown")) { Namespace = "ICSharpCode.Decompiler.IL.Patterns" } new OpCode("PatternInstruction", "Base class for pattern matching in ILAst.", AbstractBaseClass, ResultType("Unknown")) { Namespace = "ICSharpCode.Decompiler.IL.Patterns" },
new OpCode("CompoundAssignmentInstruction", "Common instruction for compound assignments.",
AbstractBaseClass, CustomConstructor, CustomArguments(("target", null), ("value", null))),
}; };
OpCode[] opCodes = { OpCode[] opCodes = {
@ -66,10 +68,19 @@
new OpCode("binary", "Common instruction for add, sub, mul, div, rem, bit.and, bit.or, bit.xor, shl and shr.", new OpCode("binary", "Common instruction for add, sub, mul, div, rem, bit.and, bit.or, bit.xor, shl and shr.",
CustomClassName("BinaryNumericInstruction"), Binary, CustomWriteTo, CustomConstructor, CustomComputeFlags, CustomClassName("BinaryNumericInstruction"), Binary, CustomWriteTo, CustomConstructor, CustomComputeFlags,
MatchCondition("CheckForOverflow == o.CheckForOverflow && Sign == o.Sign && Operator == o.Operator && IsLifted == o.IsLifted")), MatchCondition("CheckForOverflow == o.CheckForOverflow && Sign == o.Sign && Operator == o.Operator && IsLifted == o.IsLifted")),
new OpCode("compound", "Common instruction for compound assignments.", new OpCode("numeric.compound", "Common instruction for numeric compound assignments.",
CustomClassName("CompoundAssignmentInstruction"), CustomConstructor, CustomComputeFlags, CustomClassName("NumericCompoundAssign"), BaseClass("CompoundAssignmentInstruction"), CustomConstructor, CustomComputeFlags,
MayThrow, CustomArguments(("target", null), ("value", null)), HasTypeOperand, ResultType("type.GetStackType()"), CustomWriteTo, MayThrow, HasTypeOperand, ResultType("type.GetStackType()"), CustomWriteTo,
MatchCondition("CheckForOverflow == o.CheckForOverflow && Sign == o.Sign && Operator == o.Operator")), MatchCondition("CheckForOverflow == o.CheckForOverflow && Sign == o.Sign && Operator == o.Operator"),
MatchCondition("Target.PerformMatch(o.Target, ref match)"),
MatchCondition("Value.PerformMatch(o.Value, ref match)")),
new OpCode("user.compound", "Common instruction for user-defined compound assignments.",
CustomClassName("UserDefinedCompoundAssign"), BaseClass("CompoundAssignmentInstruction"), CustomConstructor,
MayThrow, SideEffect, CustomWriteTo,
MatchCondition("this.Method.Equals(o.Method)"),
MatchCondition("this.CompoundAssignmentType == o.CompoundAssignmentType"),
MatchCondition("Target.PerformMatch(o.Target, ref match)"),
MatchCondition("Value.PerformMatch(o.Value, ref match)")),
new OpCode("bit.not", "Bitwise NOT", Unary, CustomConstructor, MatchCondition("IsLifted == o.IsLifted && UnderlyingResultType == o.UnderlyingResultType")), new OpCode("bit.not", "Bitwise NOT", Unary, CustomConstructor, MatchCondition("IsLifted == o.IsLifted && UnderlyingResultType == o.UnderlyingResultType")),
new OpCode("arglist", "Retrieves the RuntimeArgumentHandle.", NoArguments, ResultType("O")), new OpCode("arglist", "Retrieves the RuntimeArgumentHandle.", NoArguments, ResultType("O")),
new OpCode("br", "Unconditional branch. <c>goto target;</c>", new OpCode("br", "Unconditional branch. <c>goto target;</c>",

91
ICSharpCode.Decompiler/IL/Instructions/CompoundAssignmentInstruction.cs

@ -28,7 +28,35 @@ namespace ICSharpCode.Decompiler.IL
EvaluatesToNewValue EvaluatesToNewValue
} }
public partial class CompoundAssignmentInstruction : ILInstruction public abstract partial class CompoundAssignmentInstruction : ILInstruction
{
public readonly CompoundAssignmentType CompoundAssignmentType;
public CompoundAssignmentInstruction(OpCode opCode, CompoundAssignmentType compoundAssignmentType, ILInstruction target, ILInstruction value)
: base(opCode)
{
this.CompoundAssignmentType = compoundAssignmentType;
this.Target = target;
this.Value = value;
}
internal static bool IsValidCompoundAssignmentTarget(ILInstruction inst)
{
switch (inst.OpCode) {
// case OpCode.LdLoc: -- not valid -- does not mark the variable as written to
case OpCode.LdObj:
return true;
case OpCode.Call:
case OpCode.CallVirt:
var owner = ((CallInstruction)inst).Method.AccessorOwner as IProperty;
return owner != null && owner.CanSet;
default:
return false;
}
}
}
public partial class NumericCompoundAssign : CompoundAssignmentInstruction
{ {
/// <summary> /// <summary>
/// Gets whether the instruction checks for overflow. /// Gets whether the instruction checks for overflow.
@ -50,13 +78,11 @@ namespace ICSharpCode.Decompiler.IL
/// The operator used by this assignment operator instruction. /// The operator used by this assignment operator instruction.
/// </summary> /// </summary>
public readonly BinaryNumericOperator Operator; public readonly BinaryNumericOperator Operator;
public readonly CompoundAssignmentType CompoundAssignmentType;
public bool IsLifted { get; } public bool IsLifted { get; }
public CompoundAssignmentInstruction(BinaryNumericInstruction binary, ILInstruction target, ILInstruction value, IType type, CompoundAssignmentType compoundAssignmentType) public NumericCompoundAssign(BinaryNumericInstruction binary, ILInstruction target, ILInstruction value, IType type, CompoundAssignmentType compoundAssignmentType)
: base(OpCode.CompoundAssignmentInstruction) : base(OpCode.NumericCompoundAssign, compoundAssignmentType, target, value)
{ {
Debug.Assert(IsBinaryCompatibleWithType(binary, type)); Debug.Assert(IsBinaryCompatibleWithType(binary, type));
this.CheckForOverflow = binary.CheckForOverflow; this.CheckForOverflow = binary.CheckForOverflow;
@ -65,11 +91,8 @@ namespace ICSharpCode.Decompiler.IL
this.RightInputType = binary.RightInputType; this.RightInputType = binary.RightInputType;
this.UnderlyingResultType = binary.UnderlyingResultType; this.UnderlyingResultType = binary.UnderlyingResultType;
this.Operator = binary.Operator; this.Operator = binary.Operator;
this.CompoundAssignmentType = compoundAssignmentType;
this.IsLifted = binary.IsLifted; this.IsLifted = binary.IsLifted;
this.Target = target;
this.type = type; this.type = type;
this.Value = value;
this.ILRange = binary.ILRange; this.ILRange = binary.ILRange;
Debug.Assert(compoundAssignmentType == CompoundAssignmentType.EvaluatesToNewValue || (Operator == BinaryNumericOperator.Add || Operator == BinaryNumericOperator.Sub)); Debug.Assert(compoundAssignmentType == CompoundAssignmentType.EvaluatesToNewValue || (Operator == BinaryNumericOperator.Add || Operator == BinaryNumericOperator.Sub));
Debug.Assert(IsValidCompoundAssignmentTarget(Target)); Debug.Assert(IsValidCompoundAssignmentTarget(Target));
@ -127,24 +150,9 @@ namespace ICSharpCode.Decompiler.IL
return true; return true;
} }
internal static bool IsValidCompoundAssignmentTarget(ILInstruction inst)
{
switch (inst.OpCode) {
// case OpCode.LdLoc: -- not valid -- does not mark the variable as written to
case OpCode.LdObj:
return true;
case OpCode.Call:
case OpCode.CallVirt:
var owner = ((CallInstruction)inst).Method.AccessorOwner as IProperty;
return owner != null && owner.CanSet;
default:
return false;
}
}
protected override InstructionFlags ComputeFlags() protected override InstructionFlags ComputeFlags()
{ {
var flags = target.Flags | value.Flags | InstructionFlags.SideEffect; var flags = Target.Flags | Value.Flags | InstructionFlags.SideEffect;
if (CheckForOverflow || (Operator == BinaryNumericOperator.Div || Operator == BinaryNumericOperator.Rem)) if (CheckForOverflow || (Operator == BinaryNumericOperator.Div || Operator == BinaryNumericOperator.Rem))
flags |= InstructionFlags.MayThrow; flags |= InstructionFlags.MayThrow;
return flags; return flags;
@ -181,6 +189,41 @@ namespace ICSharpCode.Decompiler.IL
output.Write(')'); output.Write(')');
} }
} }
public partial class UserDefinedCompoundAssign : CompoundAssignmentInstruction
{
public readonly IMethod Method;
public readonly bool IsLifted;
public UserDefinedCompoundAssign(IMethod method, CompoundAssignmentType compoundAssignmentType, ILInstruction target, ILInstruction value, bool isLifted)
: base(OpCode.UserDefinedCompoundAssign, compoundAssignmentType, target, value)
{
this.Method = method;
this.IsLifted = isLifted;
Debug.Assert(Method.IsOperator);
Debug.Assert(compoundAssignmentType == CompoundAssignmentType.EvaluatesToNewValue || (Method.Name == "op_Increment" || Method.Name == "op_Decrement"));
Debug.Assert(IsValidCompoundAssignmentTarget(Target));
}
public override StackType ResultType => Method.ReturnType.GetStackType();
public override void WriteTo(ITextOutput output, ILAstWritingOptions options)
{
ILRange.WriteTo(output, options);
output.Write(OpCode);
if (CompoundAssignmentType == CompoundAssignmentType.EvaluatesToNewValue)
output.Write(".new");
else
output.Write(".old");
output.Write(' ');
Method.WriteTo(output);
output.Write('(');
this.Target.WriteTo(output, options);
output.Write(", ");
this.Value.WriteTo(output, options);
output.Write(')');
}
}
} }

4
ICSharpCode.Decompiler/IL/Transforms/DelegateConstruction.cs

@ -321,9 +321,9 @@ namespace ICSharpCode.Decompiler.IL.Transforms
} }
} }
protected internal override void VisitCompoundAssignmentInstruction(CompoundAssignmentInstruction inst) protected internal override void VisitNumericCompoundAssign(NumericCompoundAssign inst)
{ {
base.VisitCompoundAssignmentInstruction(inst); base.VisitNumericCompoundAssign(inst);
if (inst.Target.MatchLdLoc(out var v)) { if (inst.Target.MatchLdLoc(out var v)) {
inst.ReplaceWith(new StLoc(v, new BinaryNumericInstruction(inst.Operator, inst.Target, inst.Value, inst.CheckForOverflow, inst.Sign))); inst.ReplaceWith(new StLoc(v, new BinaryNumericInstruction(inst.Operator, inst.Target, inst.Value, inst.CheckForOverflow, inst.Sign)));
} }

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

@ -241,14 +241,20 @@ namespace ICSharpCode.Decompiler.IL.Transforms
expr.AcceptVisitor(this); expr.AcceptVisitor(this);
} else { } else {
base.VisitCall(inst); base.VisitCall(inst);
TransformAssignment.HandleCallCompoundAssign(inst, context); if (TransformAssignment.HandleCallCompoundAssign(inst, context))
return;
if (TransformAssignment.HandleUserDefinedCompoundAssignOnCall(inst, context))
return;
} }
} }
protected internal override void VisitCallVirt(CallVirt inst) protected internal override void VisitCallVirt(CallVirt inst)
{ {
base.VisitCallVirt(inst); base.VisitCallVirt(inst);
TransformAssignment.HandleCallCompoundAssign(inst, context); if (TransformAssignment.HandleCallCompoundAssign(inst, context))
return;
if (TransformAssignment.HandleUserDefinedCompoundAssignOnCall(inst, context))
return;
} }
protected internal override void VisitNewObj(NewObj inst) protected internal override void VisitNewObj(NewObj inst)
@ -301,7 +307,14 @@ namespace ICSharpCode.Decompiler.IL.Transforms
context.RequestRerun(); context.RequestRerun();
return; return;
} }
TransformAssignment.HandleStObjCompoundAssign(inst, context); if (TransformAssignment.HandleStObjCompoundAssign(inst, context)) {
context.RequestRerun();
return;
}
if (TransformAssignment.HandleUserDefinedCompoundAssignOnReference(inst, context)) {
context.RequestRerun();
return;
}
} }
protected internal override void VisitIfInstruction(IfInstruction inst) protected internal override void VisitIfInstruction(IfInstruction inst)

3
ICSharpCode.Decompiler/IL/Transforms/HighLevelLoopTransform.cs

@ -451,7 +451,8 @@ namespace ICSharpCode.Decompiler.IL.Transforms
case OpCode.NewObj: case OpCode.NewObj:
case OpCode.StLoc: case OpCode.StLoc:
case OpCode.StObj: case OpCode.StObj:
case OpCode.CompoundAssignmentInstruction: case OpCode.NumericCompoundAssign:
case OpCode.UserDefinedCompoundAssign:
return true; return true;
default: default:
return false; return false;

3
ICSharpCode.Decompiler/IL/Transforms/ILInlining.cs

@ -296,7 +296,8 @@ namespace ICSharpCode.Decompiler.IL.Transforms
switch (inlinedExpression.OpCode) { switch (inlinedExpression.OpCode) {
case OpCode.DefaultValue: case OpCode.DefaultValue:
case OpCode.StObj: case OpCode.StObj:
case OpCode.CompoundAssignmentInstruction: case OpCode.NumericCompoundAssign:
case OpCode.UserDefinedCompoundAssign:
case OpCode.Await: case OpCode.Await:
return true; return true;
case OpCode.LdLoc: case OpCode.LdLoc:

Loading…
Cancel
Save