diff --git a/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj b/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj index 5c8b1c1ae..880b32c1f 100644 --- a/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj +++ b/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj @@ -83,6 +83,7 @@ + diff --git a/ICSharpCode.Decompiler/IL/ILReader.cs b/ICSharpCode.Decompiler/IL/ILReader.cs index 4de264a85..65a8bcf0c 100644 --- a/ICSharpCode.Decompiler/IL/ILReader.cs +++ b/ICSharpCode.Decompiler/IL/ILReader.cs @@ -55,7 +55,7 @@ namespace ICSharpCode.Decompiler.IL ILInstruction decodedInstruction = DecodeInstruction(); decodedInstruction.ILRange = new Interval(start, reader.Position); instructionBuilder.Add(decodedInstruction); - if ((var branch = decodedInstruction as BranchInstruction) != null) { + if ((var branch = decodedInstruction as Branch) != null) { if (branch.TargetILOffset >= reader.Position) { branchStackDict[branch.TargetILOffset] = stack.ToImmutableArray(); } @@ -70,7 +70,7 @@ namespace ICSharpCode.Decompiler.IL } } return instructionBuilder.ToImmutable(); - } + } private bool IsUnconditionalBranch(OpCode opCode) { @@ -101,7 +101,7 @@ namespace ICSharpCode.Decompiler.IL return BinaryNumeric(OpCode.BitAnd); case ILOpCode.Arglist: stack.Push(StackType.O); - return new SimpleInstruction(OpCode.Arglist); + return new Arglist(); case ILOpCode.Beq: return DecodeComparisonBranch(false, OpCode.Ceq, OpCode.Ceq, false); case ILOpCode.Beq_S: @@ -147,7 +147,7 @@ namespace ICSharpCode.Decompiler.IL case ILOpCode.Br_S: return DecodeUnconditionalBranch(true, OpCode.Branch); case ILOpCode.Break: - return new SimpleInstruction(OpCode.Break); + return new DebugBreak(); case ILOpCode.Brfalse: return DecodeConditionalBranch(false, true); case ILOpCode.Brfalse_S: @@ -173,7 +173,7 @@ namespace ICSharpCode.Decompiler.IL case ILOpCode.Clt_Un: return Comparison(OpCode.Clt_Un, OpCode.Clt_Un); case ILOpCode.Ckfinite: - return new PeekInstruction(OpCode.Ckfinite); + return new Ckfinite(); case ILOpCode.Conv_I1: return Conv(PrimitiveType.I1, OverflowMode.None); case ILOpCode.Conv_I2: @@ -247,7 +247,7 @@ namespace ICSharpCode.Decompiler.IL case ILOpCode.Div_Un: return BinaryNumeric(OpCode.Div, OverflowMode.Un); case ILOpCode.Dup: - return new PeekInstruction(OpCode.Peek); + return new Peek(); case ILOpCode.Endfilter: throw new NotImplementedException(); case ILOpCode.Endfinally: @@ -292,7 +292,7 @@ namespace ICSharpCode.Decompiler.IL return LdcI4(reader.ReadSByte()); case ILOpCode.Ldnull: stack.Push(StackType.O); - return new SimpleInstruction(OpCode.LdNull); + return new ConstantNull(); case ILOpCode.Ldstr: return DecodeLdstr(); case ILOpCode.Ldftn: @@ -337,14 +337,14 @@ namespace ICSharpCode.Decompiler.IL case ILOpCode.Neg: return UnaryNumeric(OpCode.Neg); case ILOpCode.Nop: - return new SimpleInstruction(OpCode.Nop); + return new Nop(); case ILOpCode.Not: return UnaryNumeric(OpCode.BitNot); case ILOpCode.Or: return BinaryNumeric(OpCode.BitOr); case ILOpCode.Pop: stack.PopOrDefault(); - return new UnaryInstruction(OpCode.Void); + return new VoidInstruction(); case ILOpCode.Rem: return BinaryNumeric(OpCode.Rem, OverflowMode.None); case ILOpCode.Rem_Un: @@ -406,21 +406,36 @@ namespace ICSharpCode.Decompiler.IL case ILOpCode.Ldelema: throw new NotImplementedException(); case ILOpCode.Ldfld: - throw new NotImplementedException(); + { + stack.PopOrDefault(); + FieldReference field = (FieldReference)ReadAndDecodeMetadataToken(); + stack.Push(field.FieldType.GetStackType()); + return new LoadInstanceField(field); + } case ILOpCode.Ldflda: - throw new NotImplementedException(); + stack.PopOrDefault(); + stack.Push(StackType.Ref); + return new LoadInstanceField((FieldReference)ReadAndDecodeMetadataToken(), OpCode.Ldflda); case ILOpCode.Stfld: - throw new NotImplementedException(); + stack.PopOrDefault(); + stack.PopOrDefault(); + return new StoreInstanceField((FieldReference)ReadAndDecodeMetadataToken()); case ILOpCode.Ldlen: throw new NotImplementedException(); case ILOpCode.Ldobj: throw new NotImplementedException(); case ILOpCode.Ldsfld: - throw new NotImplementedException(); + { + FieldReference field = (FieldReference)ReadAndDecodeMetadataToken(); + stack.Push(field.FieldType.GetStackType()); + return new LoadStaticField(field); + } case ILOpCode.Ldsflda: - throw new NotImplementedException(); + stack.Push(StackType.Ref); + return new LoadStaticField((FieldReference)ReadAndDecodeMetadataToken(), OpCode.Ldsflda); case ILOpCode.Stsfld: - throw new NotImplementedException(); + stack.PopOrDefault(); + return new StoreStaticField((FieldReference)ReadAndDecodeMetadataToken()); case ILOpCode.Ldtoken: throw new NotImplementedException(); case ILOpCode.Ldvirtftn: @@ -470,9 +485,9 @@ namespace ICSharpCode.Decompiler.IL private ILInstruction Ret() { if (body.Method.ReturnType.GetStackType() == StackType.Void) - return new SimpleInstruction(OpCode.Ret); + return new RetVoid(); else - return new UnaryInstruction(OpCode.Ret); + return new Ret(); } private ILInstruction UnaryNumeric(OpCode opCode) @@ -486,61 +501,61 @@ namespace ICSharpCode.Decompiler.IL { stack.Push(StackType.O); var metadataToken = ReadMetadataToken(ref reader); - return new ConstantStringInstruction(body.LookupStringToken(metadataToken)); - } + return new ConstantString(body.LookupStringToken(metadataToken)); + } private ILInstruction LdcI4(int val) { stack.Push(StackType.I4); - return new ConstantI4Instruction(val); + return new ConstantI4(val); } private ILInstruction LdcI8(long val) { stack.Push(StackType.I8); - return new ConstantI8Instruction(val); + return new ConstantI8(val); } private ILInstruction LdcF(double val) { stack.Push(StackType.F); - return new ConstantFloatInstruction(val); + return new ConstantFloat(val); } private ILInstruction Ldarg(ushort v) { stack.Push(parameterVariables[v].Type.GetStackType()); - return new LoadVarInstruction(parameterVariables[v]); - } + return new LdLoc(parameterVariables[v]); + } private ILInstruction Ldarga(ushort v) { stack.Push(StackType.Ref); - return new LoadVarInstruction(parameterVariables[v], OpCode.LoadVarAddress); + return new LdLoca(parameterVariables[v]); } private ILInstruction Starg(ushort v) { stack.PopOrDefault(); - return new StoreVarInstruction(parameterVariables[v]); + return new StLoc(parameterVariables[v]); } private ILInstruction Ldloc(ushort v) { stack.Push(localVariables[v].Type.GetStackType()); - return new LoadVarInstruction(localVariables[v]); + return new LdLoc(localVariables[v]); } private ILInstruction Ldloca(ushort v) { stack.Push(StackType.Ref); - return new LoadVarInstruction(localVariables[v], OpCode.LoadVarAddress); + return new LdLoca(localVariables[v]); } private ILInstruction Stloc(ushort v) { stack.PopOrDefault(); - return new StoreVarInstruction(localVariables[v]); + return new StLoc(localVariables[v]); } private ILInstruction DecodeConstrainedCall() @@ -619,7 +634,7 @@ namespace ICSharpCode.Decompiler.IL if (negate) { condition = new LogicNotInstruction { Operand = condition }; } - return new ConditionalBranchInstruction(condition, target); + return new ConditionalBranch(condition, target); } ILInstruction DecodeConditionalBranch(bool shortForm, bool negate) @@ -630,14 +645,14 @@ namespace ICSharpCode.Decompiler.IL if (negate) { condition = new LogicNotInstruction { Operand = condition }; } - return new ConditionalBranchInstruction(condition, target); + return new ConditionalBranch(condition, target); } ILInstruction DecodeUnconditionalBranch(bool shortForm, OpCode opCode) { int start = reader.Position - 1; int target = start + (shortForm ? reader.ReadSByte() : reader.ReadInt32()); - return new BranchInstruction(opCode, target); + return new Branch(opCode, target); } ILInstruction BinaryNumeric(OpCode opCode, OverflowMode overflowMode = OverflowMode.None) diff --git a/ICSharpCode.Decompiler/IL/Instructions/BinaryInstruction.cs b/ICSharpCode.Decompiler/IL/Instructions/BinaryInstruction.cs index 90ce5c4f1..407097f57 100644 --- a/ICSharpCode.Decompiler/IL/Instructions/BinaryInstruction.cs +++ b/ICSharpCode.Decompiler/IL/Instructions/BinaryInstruction.cs @@ -12,6 +12,12 @@ namespace ICSharpCode.Decompiler.IL public ILInstruction Right = Pop; public override bool IsPeeking { get { return Left.IsPeeking; } } + + public override void TransformChildren(Func transformFunc) + { + Left = transformFunc(Left); + Right = transformFunc(Right); + } } class BinaryNumericInstruction(OpCode opCode, StackType opType, OverflowMode overflowMode) diff --git a/ICSharpCode.Decompiler/IL/Instructions/BranchInstruction.cs b/ICSharpCode.Decompiler/IL/Instructions/BranchInstruction.cs index 325ed4a19..115c02c98 100644 --- a/ICSharpCode.Decompiler/IL/Instructions/BranchInstruction.cs +++ b/ICSharpCode.Decompiler/IL/Instructions/BranchInstruction.cs @@ -9,7 +9,7 @@ namespace ICSharpCode.Decompiler.IL /// /// Base class for unconditional and conditional branches. /// - class BranchInstruction(OpCode opCode, public int TargetILOffset) : ILInstruction(opCode) + class Branch(OpCode opCode, public int TargetILOffset) : ILInstruction(opCode) { public override bool IsPeeking { get { return false; } } @@ -19,13 +19,26 @@ namespace ICSharpCode.Decompiler.IL output.Write(' '); output.WriteReference(CecilExtensions.OffsetToString(TargetILOffset), TargetILOffset, isLocal: true); } + + public override bool IsEndReachable + { + get + { + // end is reachable for conditional branches, but not unconditional ones + return OpCode == OpCode.ConditionalBranch; + } + } + + public override void TransformChildren(Func transformFunc) + { + } } /// /// Special instruction for unresolved branches. /// Created by ILReader phase, replaced with TODO when building basic blocks. /// - class ConditionalBranchInstruction(public ILInstruction Condition, int targetILOffset) : BranchInstruction(OpCode.ConditionalBranch, targetILOffset) + class ConditionalBranch(public ILInstruction Condition, int targetILOffset) : Branch(OpCode.ConditionalBranch, targetILOffset) { public override bool IsPeeking { get { return Condition.IsPeeking; } } @@ -36,5 +49,20 @@ namespace ICSharpCode.Decompiler.IL Condition.WriteTo(output); output.Write(')'); } + + public override void TransformChildren(Func transformFunc) + { + Condition = transformFunc(Condition); + } + } + + class RetVoid() : SimpleInstruction(OpCode.Ret) + { + public override bool IsEndReachable { get { return false; } } + } + + class Ret() : UnaryInstruction(OpCode.Ret) + { + public override bool IsEndReachable { get { return false; } } } } diff --git a/ICSharpCode.Decompiler/IL/Instructions/CallInstruction.cs b/ICSharpCode.Decompiler/IL/Instructions/CallInstruction.cs index af31af6e7..5d2eb7f11 100644 --- a/ICSharpCode.Decompiler/IL/Instructions/CallInstruction.cs +++ b/ICSharpCode.Decompiler/IL/Instructions/CallInstruction.cs @@ -24,6 +24,21 @@ namespace ICSharpCode.Decompiler.IL public override bool IsPeeking { get { return Operands.Length > 0 && Operands[0].IsPeeking; } } + public override bool NoResult + { + get + { + return Method.ReturnType.GetStackType() == StackType.Void; + } + } + + public override void TransformChildren(Func transformFunc) + { + for (int i = 0; i < Operands.Length; i++) { + Operands[i] = transformFunc(Operands[i]); + } + } + /// /// Gets/Sets whether the call has the 'tail.' prefix. /// diff --git a/ICSharpCode.Decompiler/IL/Instructions/FieldAccess.cs b/ICSharpCode.Decompiler/IL/Instructions/FieldAccess.cs new file mode 100644 index 000000000..c736b6279 --- /dev/null +++ b/ICSharpCode.Decompiler/IL/Instructions/FieldAccess.cs @@ -0,0 +1,88 @@ +using Mono.Cecil; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace ICSharpCode.Decompiler.IL +{ + class LoadStaticField(FieldReference field, OpCode opCode = OpCode.Ldsfld) : UnaryInstruction(opCode), ISupportsVolatilePrefix + { + public readonly FieldReference Field = field; + + public bool IsVolatile { get; set; } + + public override void WriteTo(ITextOutput output) + { + if (IsVolatile) + output.Write("volatile."); + output.Write(OpCode); + output.Write(' '); + Disassembler.DisassemblerHelpers.WriteOperand(output, Field); + } + } + + class StoreStaticField(FieldReference field) : UnaryInstruction(OpCode.Stsfld), ISupportsVolatilePrefix + { + public readonly FieldReference Field = field; + + public bool IsVolatile { get; set; } + + public override bool NoResult { get { return true; } } + + public override void WriteTo(ITextOutput output) + { + if (IsVolatile) + output.Write("volatile."); + output.Write(OpCode); + output.Write(' '); + Disassembler.DisassemblerHelpers.WriteOperand(output, Field); + output.Write('('); + Operand.WriteTo(output); + output.Write(')'); + } + } + + class LoadInstanceField(FieldReference field, OpCode opCode = OpCode.Ldfld) : UnaryInstruction(opCode), ISupportsVolatilePrefix + { + public readonly FieldReference Field = field; + + public bool IsVolatile { get; set; } + + public override void WriteTo(ITextOutput output) + { + if (IsVolatile) + output.Write("volatile."); + output.Write(OpCode); + output.Write(' '); + output.WriteReference(Field.Name, Field); + output.Write('('); + Operand.WriteTo(output); + output.Write(')'); + } + } + + class StoreInstanceField(FieldReference field, OpCode opCode = OpCode.Ldfld) : BinaryInstruction(opCode), ISupportsVolatilePrefix + { + public readonly FieldReference Field = field; + + public bool IsVolatile { get; set; } + + public override bool NoResult { get { return true; } } + + public override void WriteTo(ITextOutput output) + { + if (IsVolatile) + output.Write("volatile."); + output.Write(OpCode); + output.Write(' '); + output.WriteReference(Field.Name, Field); + output.Write('('); + Left.WriteTo(output); + output.Write(", "); + Right.WriteTo(output); + output.Write(')'); + } + } +} diff --git a/ICSharpCode.Decompiler/IL/Instructions/ILInstruction.cs b/ICSharpCode.Decompiler/IL/Instructions/ILInstruction.cs index e6077a8d1..c98027e8c 100644 --- a/ICSharpCode.Decompiler/IL/Instructions/ILInstruction.cs +++ b/ICSharpCode.Decompiler/IL/Instructions/ILInstruction.cs @@ -11,8 +11,8 @@ namespace ICSharpCode.Decompiler.IL /// public abstract class ILInstruction(public readonly OpCode OpCode) { - public static readonly ILInstruction Nop = new SimpleInstruction(OpCode.Nop); - public static readonly ILInstruction Pop = new SimpleInstruction(OpCode.Pop); + public static readonly ILInstruction Nop = new Nop(); + public static readonly ILInstruction Pop = new Pop(); /// /// Gets the ILRange for this instruction alone, ignoring the operands. @@ -26,6 +26,30 @@ namespace ICSharpCode.Decompiler.IL /// public abstract bool IsPeeking { get; } - public abstract void WriteTo(ITextOutput output); + /// + /// Gets whether the instruction produces no result. + /// Instructions without result may not be used as arguments to other instructions; + /// and do not result in a stack push when used as a top-level instruction within a block. + /// + public virtual bool NoResult + { + get { return false; } + } + + /// + /// Gets whether the end point of this instruction is reachable from the start point. + /// Returns false if the instruction performs an unconditional branch, or always throws an exception. + /// + public virtual bool IsEndReachable + { + get { return true; } + } + + public virtual void WriteTo(ITextOutput output) + { + output.Write(OpCode); + } + + public abstract void TransformChildren(Func transformFunc); } } diff --git a/ICSharpCode.Decompiler/IL/Instructions/OpCode.cs b/ICSharpCode.Decompiler/IL/Instructions/OpCode.cs index 6d994207a..663d54741 100644 --- a/ICSharpCode.Decompiler/IL/Instructions/OpCode.cs +++ b/ICSharpCode.Decompiler/IL/Instructions/OpCode.cs @@ -8,28 +8,26 @@ namespace ICSharpCode.Decompiler.IL { public enum OpCode { - /// - /// A instruction that could not be decoded correctly. - /// Invalid instructions may appear in unreachable code. - /// - Invalid, /// /// No operation. Takes 0 arguments and returns void. + /// /// Nop, /// /// Pops the top of the evaluation stack and returns the value. /// Does not correspond to any IL instruction, but encodes the implicit stack use by the IL instruction. + /// /// Pop, /// /// Peeks at the top of the evaluation stack and returns the value. - /// Corresponds to IL 'dup'. + /// Corresponds to IL 'dup'. /// Peek, /// /// Ignore the arguments and produce void. Used to prevent the end result of an instruction /// from being pushed to the evaluation stack. + /// /// Void, /// @@ -79,24 +77,24 @@ namespace ICSharpCode.Decompiler.IL /// BitNot, /// - /// Retrieves the RuntimeArgumentHandle + /// Retrieves the RuntimeArgumentHandle. /// Arglist, /// /// if (cond) goto target; - /// + /// /// ConditionalBranch, /// /// goto target; - /// + /// /// Branch, Leave, /// /// Breakpoint instruction. /// - Break, + DebugBreak, /// /// Compare equal. /// Returns 1 (of type I4) if two numbers or object references are equal; 0 otherwise. @@ -149,33 +147,33 @@ namespace ICSharpCode.Decompiler.IL Conv, /// /// Loads the value of a variable. (ldarg/ldloc) - /// + /// /// - LoadVar, + LdLoc, /// - /// Loads the address of a variable as managed ref. (ldarga/ldloca) - /// + /// Loads the address of a variable. (ldarga/ldloca) + /// /// - LoadVarAddress, + LdLoca, /// /// Stores a value into a variable. (starg/stloc) - /// + /// /// - StoreVar, + StLoc, /// - /// Loads a constant string. + /// Loads a constant string. /// LdStr, /// - /// Loads a constant 32-bit integer. + /// Loads a constant 32-bit integer. /// LdcI4, /// - /// Loads a constant 64-bit integer. + /// Loads a constant 64-bit integer. /// LdcI8, /// - /// Loads a constant floating point number. + /// Loads a constant floating point number. /// LdcF, /// @@ -184,7 +182,7 @@ namespace ICSharpCode.Decompiler.IL LdNull, /// /// Returns from the current method or lambda. - /// or , depending on whether + /// or , depending on whether /// the method has return type void. /// Ret, @@ -196,5 +194,29 @@ namespace ICSharpCode.Decompiler.IL /// Shift right. /// Shr, + /// + /// Load instance field. + /// + Ldfld, + /// + /// Load instance field address. + /// + Ldflda, + /// + /// Store to instance field. + /// + Stfld, + /// + /// Load static field. + /// + Ldsfld, + /// + /// Load static field address. + /// + Ldsflda, + /// + /// Store to static field. + /// + Stsfld, } } diff --git a/ICSharpCode.Decompiler/IL/Instructions/Prefix.cs b/ICSharpCode.Decompiler/IL/Instructions/Prefix.cs index 15d1dd579..b33506d7a 100644 --- a/ICSharpCode.Decompiler/IL/Instructions/Prefix.cs +++ b/ICSharpCode.Decompiler/IL/Instructions/Prefix.cs @@ -6,13 +6,16 @@ using System.Threading.Tasks; namespace ICSharpCode.Decompiler.IL { - interface ISupportsMemoryPrefix + interface ISupportsMemoryPrefix : ISupportsVolatilePrefix { /// /// Returns the alignment specified by the 'unaligned' prefix; or 0 if there was no 'unaligned' prefix. /// byte UnalignedPrefix { get; set; } + } + interface ISupportsVolatilePrefix + { /// /// Gets/Sets whether the memory access is volatile. /// diff --git a/ICSharpCode.Decompiler/IL/Instructions/SimpleInstruction.cs b/ICSharpCode.Decompiler/IL/Instructions/SimpleInstruction.cs index 9f7310f34..b37461adf 100644 --- a/ICSharpCode.Decompiler/IL/Instructions/SimpleInstruction.cs +++ b/ICSharpCode.Decompiler/IL/Instructions/SimpleInstruction.cs @@ -7,29 +7,47 @@ using System.Threading.Tasks; namespace ICSharpCode.Decompiler.IL { /// - /// A simple instruction that does not pop any elements from the stack. + /// A simple instruction that does not have any arguments. /// - class SimpleInstruction(OpCode opCode) : ILInstruction(opCode) + abstract class SimpleInstruction(OpCode opCode) : ILInstruction(opCode) { public override bool IsPeeking { get { return false; } } - public override void WriteTo(ITextOutput output) + public override void TransformChildren(Func transformFunc) { - output.Write(OpCode); } } - class PeekInstruction(OpCode opCode = OpCode.Peek) : ILInstruction(opCode) + class Nop() : SimpleInstruction(OpCode.Nop) + { + public override bool NoResult { get { return true; } } + } + + class Pop() : SimpleInstruction(OpCode.Pop) + { + } + + class Peek() : SimpleInstruction(OpCode.Peek) { public override bool IsPeeking { get { return true; } } + } - public override void WriteTo(ITextOutput output) - { - output.Write(OpCode); - } + class Ckfinite() : SimpleInstruction(OpCode.Ckfinite) + { + public override bool IsPeeking { get { return true; } } + public override bool NoResult { get { return true; } } + } + + class Arglist() : SimpleInstruction(OpCode.Arglist) + { } - class ConstantStringInstruction(public readonly string Value) : SimpleInstruction(OpCode.LdStr) + class DebugBreak() : SimpleInstruction(OpCode.DebugBreak) + { + public override bool NoResult { get { return true; } } + } + + class ConstantString(public readonly string Value) : SimpleInstruction(OpCode.LdStr) { public override void WriteTo(ITextOutput output) { @@ -37,7 +55,7 @@ namespace ICSharpCode.Decompiler.IL } } - class ConstantI4Instruction(public readonly int Value) : SimpleInstruction(OpCode.LdcI4) + class ConstantI4(public readonly int Value) : SimpleInstruction(OpCode.LdcI4) { public override void WriteTo(ITextOutput output) { @@ -45,7 +63,7 @@ namespace ICSharpCode.Decompiler.IL } } - class ConstantI8Instruction(public readonly long Value) : SimpleInstruction(OpCode.LdcI8) + class ConstantI8(public readonly long Value) : SimpleInstruction(OpCode.LdcI8) { public override void WriteTo(ITextOutput output) { @@ -53,11 +71,15 @@ namespace ICSharpCode.Decompiler.IL } } - class ConstantFloatInstruction(public readonly double Value) : SimpleInstruction(OpCode.LdcI8) + class ConstantFloat(public readonly double Value) : SimpleInstruction(OpCode.LdcI8) { public override void WriteTo(ITextOutput output) { Disassembler.DisassemblerHelpers.WriteOperand(output, Value); } } + + class ConstantNull() : SimpleInstruction(OpCode.LdNull) + { + } } diff --git a/ICSharpCode.Decompiler/IL/Instructions/UnaryInstruction.cs b/ICSharpCode.Decompiler/IL/Instructions/UnaryInstruction.cs index 409a7fc75..f4f8f822b 100644 --- a/ICSharpCode.Decompiler/IL/Instructions/UnaryInstruction.cs +++ b/ICSharpCode.Decompiler/IL/Instructions/UnaryInstruction.cs @@ -6,11 +6,11 @@ using System.Threading.Tasks; namespace ICSharpCode.Decompiler.IL { - class UnaryInstruction(OpCode opCode) : ILInstruction(opCode) + abstract class UnaryInstruction(OpCode opCode) : ILInstruction(opCode) { public ILInstruction Operand = Pop; - public override bool IsPeeking { get { return Operand.IsPeeking; } } + public sealed override bool IsPeeking { get { return Operand.IsPeeking; } } public override void WriteTo(ITextOutput output) { @@ -19,6 +19,16 @@ namespace ICSharpCode.Decompiler.IL Operand.WriteTo(output); output.Write(')'); } + + public override void TransformChildren(Func transformFunc) + { + Operand = transformFunc(Operand); + } + } + + class VoidInstruction() : UnaryInstruction(OpCode.Void) + { + public override bool NoResult { get { return true; } } } class LogicNotInstruction() : UnaryInstruction(OpCode.LogicNot) diff --git a/ICSharpCode.Decompiler/IL/Instructions/VarInstructions.cs b/ICSharpCode.Decompiler/IL/Instructions/VarInstructions.cs index 5b4b5c973..705f5e4b2 100644 --- a/ICSharpCode.Decompiler/IL/Instructions/VarInstructions.cs +++ b/ICSharpCode.Decompiler/IL/Instructions/VarInstructions.cs @@ -6,24 +6,25 @@ using System.Threading.Tasks; namespace ICSharpCode.Decompiler.IL { - class LoadVarInstruction(public readonly ILVariable Variable, OpCode opCode = OpCode.LoadVar) : ILInstruction(opCode) + class LdLoc(public readonly ILVariable Variable) : SimpleInstruction(OpCode.LdLoc) { - public override bool IsPeeking { get { return false; } } - public override void WriteTo(ITextOutput output) { - if (OpCode != OpCode.LoadVar) { - output.Write(OpCode); - output.Write(' '); - } output.WriteReference(Variable.ToString(), Variable, isLocal: true); } } - class StoreVarInstruction(public readonly ILVariable Variable) : UnaryInstruction(OpCode.StoreVar) + class LdLoca(public readonly ILVariable Variable) : SimpleInstruction(OpCode.LdLoca) { - public override bool IsPeeking { get { return false; } } + public override void WriteTo(ITextOutput output) + { + output.Write("ref "); + output.WriteReference(Variable.ToString(), Variable, isLocal: true); + } + } + class StLoc(public readonly ILVariable Variable) : UnaryInstruction(OpCode.StLoc) + { public override void WriteTo(ITextOutput output) { output.WriteReference(Variable.ToString(), Variable, isLocal: true);