diff --git a/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs b/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs index a90ab5c00..8a59d640d 100644 --- a/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs +++ b/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs @@ -597,6 +597,101 @@ namespace ICSharpCode.Decompiler.CSharp return result; } + protected internal override TranslatedExpression VisitCompoundAssignmentInstruction(CompoundAssignmentInstruction inst) + { + switch (inst.Operator) { + case BinaryNumericOperator.Add: + return HandleCompoundAssigment(inst, AssignmentOperatorType.Add); + case BinaryNumericOperator.Sub: + return HandleCompoundAssigment(inst, AssignmentOperatorType.Subtract); + case BinaryNumericOperator.Mul: + return HandleCompoundAssigment(inst, AssignmentOperatorType.Multiply); + case BinaryNumericOperator.Div: + return HandleCompoundAssigment(inst, AssignmentOperatorType.Divide); + case BinaryNumericOperator.Rem: + return HandleCompoundAssigment(inst, AssignmentOperatorType.Modulus); + case BinaryNumericOperator.BitAnd: + return HandleCompoundAssigment(inst, AssignmentOperatorType.BitwiseAnd); + case BinaryNumericOperator.BitOr: + return HandleCompoundAssigment(inst, AssignmentOperatorType.BitwiseOr); + case BinaryNumericOperator.BitXor: + return HandleCompoundAssigment(inst, AssignmentOperatorType.ExclusiveOr); + case BinaryNumericOperator.ShiftLeft: + return HandleCompoundShift(inst, AssignmentOperatorType.ShiftLeft); + case BinaryNumericOperator.ShiftRight: + return HandleCompoundShift(inst, AssignmentOperatorType.ShiftRight); + default: + throw new ArgumentOutOfRangeException(); + } + } + + TranslatedExpression HandleCompoundAssigment(CompoundAssignmentInstruction inst, AssignmentOperatorType op) + { + var resolverWithOverflowCheck = resolver.WithCheckForOverflow(inst.CheckForOverflow); + var target = Translate(inst.Target); + var value = Translate(inst.Value); + value = PrepareArithmeticArgument(value, inst.Value.ResultType, inst.Sign); + + var rr = resolverWithOverflowCheck.ResolveAssignment(op, target.ResolveResult, value.ResolveResult); + if (rr.IsError || rr.Type.GetStackType() != inst.ResultType + || !IsCompatibleWithSign(target.Type, inst.Sign) || !IsCompatibleWithSign(value.Type, inst.Sign)) + { + // Target and value are incompatible, so convert value to the target type + // inst.ResultType should match inst.Target.ResultType + Debug.Assert(inst.ResultType == inst.Target.ResultType); + StackType targetStackType = inst.ResultType == StackType.I ? StackType.I8 : inst.ResultType; + IType targetType = compilation.FindType(targetStackType.ToKnownTypeCode(inst.Sign)); + value = value.ConvertTo(targetType, this); + rr = resolverWithOverflowCheck.ResolveAssignment(op, target.ResolveResult, value.ResolveResult); + } + var resultExpr = new AssignmentExpression(target.Expression, op, value.Expression) + .WithILInstruction(inst) + .WithRR(rr); + if (AssignmentOperatorMightCheckForOverflow(op)) + resultExpr.Expression.AddAnnotation(inst.CheckForOverflow ? AddCheckedBlocks.CheckedAnnotation : AddCheckedBlocks.UncheckedAnnotation); + return resultExpr; + } + + TranslatedExpression HandleCompoundShift(CompoundAssignmentInstruction inst, AssignmentOperatorType op) + { + var target = Translate(inst.Target); + var value = Translate(inst.Value); + + IType targetType; + if (inst.ResultType == StackType.I4) + targetType = compilation.FindType(inst.Sign == Sign.Unsigned ? KnownTypeCode.UInt32 : KnownTypeCode.Int32); + else + targetType = compilation.FindType(inst.Sign == Sign.Unsigned ? KnownTypeCode.UInt64 : KnownTypeCode.Int64); + target = target.ConvertTo(targetType, this); + + // Shift operators in C# always expect type 'int' on the right-hand-side + value = value.ConvertTo(compilation.FindType(KnownTypeCode.Int32), this); + + TranslatedExpression result = new AssignmentExpression(target.Expression, op, value.Expression) + .WithILInstruction(inst) + .WithRR(resolver.ResolveAssignment(op, target.ResolveResult, value.ResolveResult)); + if (inst.ResultType == StackType.I) { + // C# doesn't have shift operators for IntPtr, so we first shifted a long/ulong, + // and now have to case back down to IntPtr/UIntPtr: + result = result.ConvertTo(compilation.FindType(inst.Sign == Sign.Unsigned ? KnownTypeCode.UIntPtr : KnownTypeCode.IntPtr), this); + } + return result; + } + + static bool AssignmentOperatorMightCheckForOverflow(AssignmentOperatorType op) + { + switch (op) { + case AssignmentOperatorType.BitwiseAnd: + case AssignmentOperatorType.BitwiseOr: + case AssignmentOperatorType.ExclusiveOr: + case AssignmentOperatorType.ShiftLeft: + case AssignmentOperatorType.ShiftRight: + return false; + default: + return true; + } + } + protected internal override TranslatedExpression VisitConv(Conv inst) { var arg = Translate(inst.Argument); @@ -1050,12 +1145,12 @@ namespace ICSharpCode.Decompiler.CSharp // { // return Assignment(ConvertField(inst.Field, inst.Target).WithoutILInstruction(), Translate(inst.Value)).WithILInstruction(inst); // } -// +// // protected internal override TranslatedExpression VisitLdsFld(LdsFld inst) // { // return ConvertField(inst.Field).WithILInstruction(inst); // } -// +// // protected internal override TranslatedExpression VisitStsFld(StsFld inst) // { // return Assignment(ConvertField(inst.Field).WithoutILInstruction(), Translate(inst.Value)).WithILInstruction(inst); diff --git a/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj b/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj index c6f077acb..4db9ec8e7 100644 --- a/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj +++ b/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj @@ -106,6 +106,7 @@ + diff --git a/ICSharpCode.Decompiler/IL/Instructions.cs b/ICSharpCode.Decompiler/IL/Instructions.cs index 099991e4f..e9d3c71e3 100644 --- a/ICSharpCode.Decompiler/IL/Instructions.cs +++ b/ICSharpCode.Decompiler/IL/Instructions.cs @@ -45,6 +45,8 @@ namespace ICSharpCode.Decompiler.IL LogicNot, /// Common instruction for add, sub, mul, div, rem, bit.and, bit.or, bit.xor, shl and shr. BinaryNumericInstruction, + /// Common instruction for compound assignments. + CompoundAssignmentInstruction, /// Bitwise NOT BitNot, /// Retrieves the RuntimeArgumentHandle. @@ -718,6 +720,84 @@ namespace ICSharpCode.Decompiler.IL } } + /// Common instruction for compound assignments. + public sealed 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; + } + public override StackType ResultType { get { return target.ResultType; } } + public override void AcceptVisitor(ILVisitor visitor) + { + visitor.VisitCompoundAssignmentInstruction(this); + } + public override T AcceptVisitor(ILVisitor visitor) + { + return visitor.VisitCompoundAssignmentInstruction(this); + } + } + /// Bitwise NOT public sealed partial class BitNot : UnaryInstruction { @@ -2922,6 +3002,10 @@ namespace ICSharpCode.Decompiler.IL { Default(inst); } + protected internal virtual void VisitCompoundAssignmentInstruction(CompoundAssignmentInstruction inst) + { + Default(inst); + } protected internal virtual void VisitBitNot(BitNot inst) { Default(inst); @@ -3180,6 +3264,10 @@ namespace ICSharpCode.Decompiler.IL { return Default(inst); } + protected internal virtual T VisitCompoundAssignmentInstruction(CompoundAssignmentInstruction inst) + { + return Default(inst); + } protected internal virtual T VisitBitNot(BitNot inst) { return Default(inst); @@ -3420,6 +3508,7 @@ namespace ICSharpCode.Decompiler.IL "PinnedRegion", "logic.not", "binary", + "compound", "bit.not", "arglist", "br", diff --git a/ICSharpCode.Decompiler/IL/Instructions.tt b/ICSharpCode.Decompiler/IL/Instructions.tt index 96ed0bc88..29f327df7 100644 --- a/ICSharpCode.Decompiler/IL/Instructions.tt +++ b/ICSharpCode.Decompiler/IL/Instructions.tt @@ -60,6 +60,9 @@ ResultType("I4"), Unary), 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), + new OpCode("compound", "Common instruction for compound assignments.", + CustomClassName("CompoundAssignmentInstruction"), CustomConstructor, CustomComputeFlags, + MayThrow, CustomArguments("target", "value"), ResultType("target.ResultType"), CustomWriteTo), new OpCode("bit.not", "Bitwise NOT", Unary), new OpCode("arglist", "Retrieves the RuntimeArgumentHandle.", NoArguments, ResultType("O")), new OpCode("br", "Unconditional branch. goto target;", diff --git a/ICSharpCode.Decompiler/IL/Instructions/BinaryNumericInstruction.cs b/ICSharpCode.Decompiler/IL/Instructions/BinaryNumericInstruction.cs index 74c63f283..02dc4d478 100644 --- a/ICSharpCode.Decompiler/IL/Instructions/BinaryNumericInstruction.cs +++ b/ICSharpCode.Decompiler/IL/Instructions/BinaryNumericInstruction.cs @@ -17,22 +17,16 @@ // DEALINGS IN THE SOFTWARE. using System; -using System.Collections.Generic; using System.Diagnostics; using System.Linq; -using System.Text; -using System.Threading.Tasks; + +using ICSharpCode.NRefactory.TypeSystem; + namespace ICSharpCode.Decompiler.IL -{ - public enum CompoundAssignmentType : byte - { - None, - EvaluatesToOldValue, - EvaluatesToNewValue - } - +{ public enum BinaryNumericOperator : byte { + None, Add, Sub, Mul, @@ -74,21 +68,6 @@ namespace ICSharpCode.Decompiler.IL this.Operator = op; this.resultType = ComputeResultType(op, left.ResultType, right.ResultType); Debug.Assert(resultType != StackType.Unknown); - //Debug.Assert(CompoundAssignmentType == CompoundAssignmentType.None || IsValidCompoundAssignmentTarget(Left)); - } - - internal static bool IsValidCompoundAssignmentTarget(ILInstruction inst) - { - switch (inst.OpCode) { - case OpCode.LdLoc: - case OpCode.LdObj: - return true; - case OpCode.Call: - case OpCode.CallVirt: - return true; // TODO: check if corresponding setter exists - default: - return false; - } } internal static StackType ComputeResultType(BinaryNumericOperator op, StackType left, StackType right) diff --git a/ICSharpCode.Decompiler/IL/Instructions/CompoundAssignmentInstruction.cs b/ICSharpCode.Decompiler/IL/Instructions/CompoundAssignmentInstruction.cs new file mode 100644 index 000000000..b78fa11b1 --- /dev/null +++ b/ICSharpCode.Decompiler/IL/Instructions/CompoundAssignmentInstruction.cs @@ -0,0 +1,150 @@ +// Copyright (c) 2016 Siegfried Pammer +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of this +// software and associated documentation files (the "Software"), to deal in the Software +// without restriction, including without limitation the rights to use, copy, modify, merge, +// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons +// to whom the Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all copies or +// substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, +// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE +// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. + +using System; +using System.Diagnostics; +using System.Linq; + +using ICSharpCode.NRefactory.TypeSystem; + +namespace ICSharpCode.Decompiler.IL +{ + public enum CompoundAssignmentType : byte + { + EvaluatesToOldValue, + EvaluatesToNewValue + } + + public partial class CompoundAssignmentInstruction : ILInstruction + { + /// + /// Gets whether the instruction checks for overflow. + /// + public readonly bool CheckForOverflow; + + /// + /// For integer operations that depend on the sign, specifies whether the operation + /// is signed or unsigned. + /// For instructions that produce the same result for either sign, returns Sign.None. + /// + public readonly Sign Sign; + + /// + /// The operator used by this assignment operator instruction. + /// + public readonly BinaryNumericOperator Operator; + + public readonly CompoundAssignmentType CompoundAssignmentType; + + public CompoundAssignmentInstruction(BinaryNumericOperator op, ILInstruction target, ILInstruction value, bool checkForOverflow, Sign sign, CompoundAssignmentType compoundAssigmentType) + : base(OpCode.CompoundAssignmentInstruction) + { + this.CheckForOverflow = checkForOverflow; + this.Sign = sign; + this.Operator = op; + this.Target = target; + this.Value = value; + this.CompoundAssignmentType = compoundAssigmentType; + Debug.Assert(compoundAssigmentType == CompoundAssignmentType.EvaluatesToNewValue || (op == BinaryNumericOperator.Add || op == BinaryNumericOperator.Sub)); + Debug.Assert(IsValidCompoundAssignmentTarget(Target)); + } + + internal static bool IsValidCompoundAssignmentTarget(ILInstruction inst) + { + switch (inst.OpCode) { + case OpCode.LdLoc: + 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() + { + var flags = target.Flags | value.Flags | InstructionFlags.SideEffect; + if (CheckForOverflow || (Operator == BinaryNumericOperator.Div || Operator == BinaryNumericOperator.Rem)) + flags |= InstructionFlags.MayThrow; + return flags; + } + + public override InstructionFlags DirectFlags { + get { + var flags = InstructionFlags.SideEffect; + if (Operator == BinaryNumericOperator.Div || Operator == BinaryNumericOperator.Rem) + flags |= InstructionFlags.MayThrow; + return flags; + } + } + + string GetOperatorName(BinaryNumericOperator @operator) + { + switch (@operator) { + case BinaryNumericOperator.Add: + return "add"; + case BinaryNumericOperator.Sub: + return "sub"; + case BinaryNumericOperator.Mul: + return "mul"; + case BinaryNumericOperator.Div: + return "div"; + case BinaryNumericOperator.Rem: + return "rem"; + case BinaryNumericOperator.BitAnd: + return "bit.and"; + case BinaryNumericOperator.BitOr: + return "bit.or"; + case BinaryNumericOperator.BitXor: + return "bit.xor"; + case BinaryNumericOperator.ShiftLeft: + return "bit.shl"; + case BinaryNumericOperator.ShiftRight: + return "bit.shr"; + default: + throw new ArgumentOutOfRangeException(); + } + } + + public override void WriteTo(ITextOutput output) + { + output.Write(OpCode); + output.Write("." + GetOperatorName(Operator)); + if (CompoundAssignmentType == CompoundAssignmentType.EvaluatesToNewValue) + output.Write(".new"); + else + output.Write(".old"); + if (CheckForOverflow) + output.Write(".ovf"); + if (Sign == Sign.Unsigned) + output.Write(".unsigned"); + else if (Sign == Sign.Signed) + output.Write(".signed"); + output.Write('('); + Target.WriteTo(output); + output.Write(", "); + Value.WriteTo(output); + output.Write(')'); + } + } +} + + diff --git a/ICSharpCode.Decompiler/IL/Instructions/PatternMatching.cs b/ICSharpCode.Decompiler/IL/Instructions/PatternMatching.cs index c728c5650..77c219bb8 100644 --- a/ICSharpCode.Decompiler/IL/Instructions/PatternMatching.cs +++ b/ICSharpCode.Decompiler/IL/Instructions/PatternMatching.cs @@ -221,6 +221,21 @@ namespace ICSharpCode.Decompiler.IL return false; } + public bool MatchBinaryNumericInstruction(out BinaryNumericOperator @operator, out ILInstruction left, out ILInstruction right) + { + var op = this as BinaryNumericInstruction; + if (op != null) { + @operator = op.Operator; + left = op.Left; + right = op.Right; + return true; + } + @operator = BinaryNumericOperator.None; + left = null; + right = null; + return false; + } + /// /// If this instruction is a conversion of the specified kind, return its argument. /// Otherwise, return the instruction itself. diff --git a/ICSharpCode.Decompiler/IL/Transforms/ExpressionTransforms.cs b/ICSharpCode.Decompiler/IL/Transforms/ExpressionTransforms.cs index 609aedf7d..357d611e2 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/ExpressionTransforms.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/ExpressionTransforms.cs @@ -188,6 +188,24 @@ namespace ICSharpCode.Decompiler.IL.Transforms // => stloc(v, ...) inst.ReplaceWith(new StLoc(v, inst.Value)); } + + ILInstruction target; + IType t; + BinaryNumericInstruction binary = inst.Value as BinaryNumericInstruction; + if (binary != null && binary.Left.MatchLdObj(out target, out t) && IsSameTarget(inst.Target, target)) { + // stobj(target, binary.op(ldobj(target), ...)) + // => compound.op(target, ...) + inst.ReplaceWith(new CompoundAssignmentInstruction(binary.Operator, binary.Left, binary.Right, binary.CheckForOverflow, binary.Sign, CompoundAssignmentType.EvaluatesToNewValue)); + } + } + + bool IsSameTarget(ILInstruction target, ILInstruction left) + { + IField f, f2; + ILInstruction t, t2; + if (target.MatchLdFlda(out t, out f) && left.MatchLdFlda(out t2, out f2) && f.Equals(f2)) + return true; + return false; } protected internal override void VisitIfInstruction(IfInstruction inst) diff --git a/ICSharpCode.Decompiler/Tests/TestCases/Pretty/CompoundAssignmentTest.cs b/ICSharpCode.Decompiler/Tests/TestCases/Pretty/CompoundAssignmentTest.cs index 8a05baba6..07e18ec31 100644 --- a/ICSharpCode.Decompiler/Tests/TestCases/Pretty/CompoundAssignmentTest.cs +++ b/ICSharpCode.Decompiler/Tests/TestCases/Pretty/CompoundAssignmentTest.cs @@ -22,6 +22,8 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty { public class CompoundAssignmentTest { + private int test1; + public static void Main() { @@ -37,5 +39,19 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty V_0 -= i; } } + + public void IntegerWithInline(int i) + { + Console.WriteLine(i += 5); + Console.WriteLine(i); + } + + public void IntegerField(int i) + { + Console.WriteLine(this.test1 += i); + Console.WriteLine(this.test1); + Console.WriteLine(this.test1 -= i); + Console.WriteLine(this.test1); + } } } diff --git a/ICSharpCode.Decompiler/Tests/TestCases/Pretty/CompoundAssignmentTest.il b/ICSharpCode.Decompiler/Tests/TestCases/Pretty/CompoundAssignmentTest.il index e34b0bf24..2135537a1 100644 --- a/ICSharpCode.Decompiler/Tests/TestCases/Pretty/CompoundAssignmentTest.il +++ b/ICSharpCode.Decompiler/Tests/TestCases/Pretty/CompoundAssignmentTest.il @@ -10,7 +10,7 @@ .publickeytoken = (B7 7A 5C 56 19 34 E0 89 ) // .z\V.4.. .ver 4:0:0:0 } -.assembly '2lky0ish' +.assembly baqif21x { .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilationRelaxationsAttribute::.ctor(int32) = ( 01 00 08 00 00 00 00 00 ) .custom instance void [mscorlib]System.Runtime.CompilerServices.RuntimeCompatibilityAttribute::.ctor() = ( 01 00 01 00 54 02 16 57 72 61 70 4E 6F 6E 45 78 // ....T..WrapNonEx @@ -20,15 +20,15 @@ .hash algorithm 0x00008004 .ver 0:0:0:0 } -.module '2lky0ish.exe' -// MVID: {EF50960E-81BF-4C58-B168-629EA572CC14} +.module baqif21x.exe +// MVID: {1149F1EE-8A3E-4309-9B0E-D81380AC971E} .custom instance void [mscorlib]System.Security.UnverifiableCodeAttribute::.ctor() = ( 01 00 00 00 ) .imagebase 0x00400000 .file alignment 0x00000200 .stackreserve 0x00100000 .subsystem 0x0003 // WINDOWS_CUI .corflags 0x00000001 // ILONLY -// Image base: 0x00D20000 +// Image base: 0x011D0000 // =============== CLASS MEMBERS DECLARATION =================== @@ -36,6 +36,7 @@ .class public auto ansi beforefieldinit ICSharpCode.Decompiler.Tests.TestCases.Pretty.CompoundAssignmentTest extends [mscorlib]System.Object { + .field private int32 test1 .method public hidebysig static void Main() cil managed { .entrypoint @@ -48,7 +49,7 @@ .method public hidebysig instance void Integer(int32 i) cil managed { - // Code size 25 (0x19) + // Code size 29 (0x1d) .maxstack 2 .locals init (int32 V_0, bool V_1) @@ -62,21 +63,84 @@ IL_0008: ceq IL_000a: stloc.1 IL_000b: ldloc.1 - IL_000c: brtrue.s IL_0014 - - IL_000e: ldloc.0 - IL_000f: ldarg.1 - IL_0010: add - IL_0011: stloc.0 - IL_0012: br.s IL_0018 - - IL_0014: ldloc.0 - IL_0015: ldarg.1 - IL_0016: sub - IL_0017: stloc.0 - IL_0018: ret + IL_000c: brtrue.s IL_0016 + + IL_000e: nop + IL_000f: ldloc.0 + IL_0010: ldarg.1 + IL_0011: add + IL_0012: stloc.0 + IL_0013: nop + IL_0014: br.s IL_001c + + IL_0016: nop + IL_0017: ldloc.0 + IL_0018: ldarg.1 + IL_0019: sub + IL_001a: stloc.0 + IL_001b: nop + IL_001c: ret } // end of method CompoundAssignmentTest::Integer + .method public hidebysig instance void + IntegerWithInline(int32 i) cil managed + { + // Code size 21 (0x15) + .maxstack 8 + IL_0000: nop + IL_0001: ldarg.1 + IL_0002: ldc.i4.5 + IL_0003: add + IL_0004: dup + IL_0005: starg.s i + IL_0007: call void [mscorlib]System.Console::WriteLine(int32) + IL_000c: nop + IL_000d: ldarg.1 + IL_000e: call void [mscorlib]System.Console::WriteLine(int32) + IL_0013: nop + IL_0014: ret + } // end of method CompoundAssignmentTest::IntegerWithInline + + .method public hidebysig instance void + IntegerField(int32 i) cil managed + { + // Code size 72 (0x48) + .maxstack 3 + .locals init (int32 V_0) + IL_0000: nop + IL_0001: ldarg.0 + IL_0002: dup + IL_0003: ldfld int32 ICSharpCode.Decompiler.Tests.TestCases.Pretty.CompoundAssignmentTest::test1 + IL_0008: ldarg.1 + IL_0009: add + IL_000a: dup + IL_000b: stloc.0 + IL_000c: stfld int32 ICSharpCode.Decompiler.Tests.TestCases.Pretty.CompoundAssignmentTest::test1 + IL_0011: ldloc.0 + IL_0012: call void [mscorlib]System.Console::WriteLine(int32) + IL_0017: nop + IL_0018: ldarg.0 + IL_0019: ldfld int32 ICSharpCode.Decompiler.Tests.TestCases.Pretty.CompoundAssignmentTest::test1 + IL_001e: call void [mscorlib]System.Console::WriteLine(int32) + IL_0023: nop + IL_0024: ldarg.0 + IL_0025: dup + IL_0026: ldfld int32 ICSharpCode.Decompiler.Tests.TestCases.Pretty.CompoundAssignmentTest::test1 + IL_002b: ldarg.1 + IL_002c: sub + IL_002d: dup + IL_002e: stloc.0 + IL_002f: stfld int32 ICSharpCode.Decompiler.Tests.TestCases.Pretty.CompoundAssignmentTest::test1 + IL_0034: ldloc.0 + IL_0035: call void [mscorlib]System.Console::WriteLine(int32) + IL_003a: nop + IL_003b: ldarg.0 + IL_003c: ldfld int32 ICSharpCode.Decompiler.Tests.TestCases.Pretty.CompoundAssignmentTest::test1 + IL_0041: call void [mscorlib]System.Console::WriteLine(int32) + IL_0046: nop + IL_0047: ret + } // end of method CompoundAssignmentTest::IntegerField + .method public hidebysig specialname rtspecialname instance void .ctor() cil managed {