diff --git a/ICSharpCode.Decompiler/IL/ILVariable.cs b/ICSharpCode.Decompiler/IL/ILVariable.cs index eae0c89cb..66e47a773 100644 --- a/ICSharpCode.Decompiler/IL/ILVariable.cs +++ b/ICSharpCode.Decompiler/IL/ILVariable.cs @@ -17,6 +17,7 @@ // DEALINGS IN THE SOFTWARE. using System; +using System.Collections.Generic; using ICSharpCode.Decompiler.TypeSystem; namespace ICSharpCode.Decompiler.IL @@ -115,14 +116,24 @@ namespace ICSharpCode.Decompiler.IL /// It may change if an item with a lower index is removed from the collection. /// public int IndexInFunction { get; internal set; } - + /// /// Number of ldloc instructions referencing this variable. /// /// /// This variable is automatically updated when adding/removing ldloc instructions from the ILAst. /// - public int LoadCount { get; internal set; } + public int LoadCount => LoadInstructions.Count; + + readonly List loadInstructions = new List(); + + /// + /// List of ldloc instructions referencing this variable. + /// + /// + /// This list is automatically updated when adding/removing ldloc instructions from the ILAst. + /// + public IReadOnlyList LoadInstructions => loadInstructions; /// /// Number of store instructions referencing this variable. @@ -138,15 +149,69 @@ namespace ICSharpCode.Decompiler.IL /// /// This variable is automatically updated when adding/removing stores instructions from the ILAst. /// - public int StoreCount { get; internal set; } - + public int StoreCount => (hasInitialValue ? 1 : 0) + StoreInstructions.Count; + + readonly List storeInstructions = new List(); + + /// + /// List of store instructions referencing this variable. + /// + /// Stores are: + /// + /// stloc + /// TryCatchHandler (assigning the exception variable) + /// PinnedRegion (assigning the pointer variable) + /// initial values () + /// + /// + /// + /// This list is automatically updated when adding/removing stores instructions from the ILAst. + /// + public IReadOnlyList StoreInstructions => storeInstructions; + /// /// Number of ldloca instructions referencing this variable. /// /// /// This variable is automatically updated when adding/removing ldloca instructions from the ILAst. /// - public int AddressCount { get; internal set; } + public int AddressCount => AddressInstructions.Count; + + readonly List addressInstructions = new List(); + + /// + /// List of ldloca instructions referencing this variable. + /// + /// + /// This list is automatically updated when adding/removing ldloca instructions from the ILAst. + /// + public IReadOnlyList AddressInstructions => addressInstructions; + + internal void AddLoadInstruction(ILoadInstruction inst) => inst.IndexInLoadInstructionList = AddInstruction(loadInstructions, inst); + internal void AddStoreInstruction(IStoreInstruction inst) => inst.IndexInStoreInstructionList = AddInstruction(storeInstructions, inst); + internal void AddAddressInstruction(LdLoca inst) => inst.IndexInAddressInstructionList = AddInstruction(addressInstructions, inst); + + internal void RemoveLoadInstruction(ILoadInstruction inst) => RemoveInstruction(loadInstructions, inst.IndexInLoadInstructionList); + internal void RemoveStoreInstruction(IStoreInstruction inst) => RemoveInstruction(storeInstructions, inst.IndexInStoreInstructionList); + internal void RemoveAddressInstruction(LdLoca inst) => RemoveInstruction(addressInstructions, inst.IndexInAddressInstructionList); + + int AddInstruction(List list, T inst) where T : IInstructionWithVariableOperand + { + list.Add(inst); + return list.Count - 1; + } + + void RemoveInstruction(List list, int index) where T : IInstructionWithVariableOperand + { + if (list.Count > 1) { + int indexToMove = list.Count - 1; + list[index] = list[indexToMove]; + list[index].IndexInVariableInstructionMapping = index; + list.RemoveAt(indexToMove); + } else { + list.RemoveAt(0); + } + } bool hasInitialValue; @@ -165,13 +230,7 @@ namespace ICSharpCode.Decompiler.IL set { if (Kind == VariableKind.Parameter && !value) throw new InvalidOperationException("Cannot remove HasInitialValue from parameters"); - if (hasInitialValue) { - StoreCount--; - } hasInitialValue = value; - if (value) { - StoreCount++; - } } } @@ -254,5 +313,21 @@ namespace ICSharpCode.Decompiler.IL public interface IInstructionWithVariableOperand { ILVariable Variable { get; set; } + int IndexInVariableInstructionMapping { get; set; } + } + + public interface IStoreInstruction : IInstructionWithVariableOperand + { + int IndexInStoreInstructionList { get; set; } + } + + public interface ILoadInstruction : IInstructionWithVariableOperand + { + int IndexInLoadInstructionList { get; set; } + } + + public interface IAddressInstruction : IInstructionWithVariableOperand + { + int IndexInAddressInstructionList { get; set; } } } diff --git a/ICSharpCode.Decompiler/IL/Instructions.cs b/ICSharpCode.Decompiler/IL/Instructions.cs index 020c1baa9..734f841a8 100644 --- a/ICSharpCode.Decompiler/IL/Instructions.cs +++ b/ICSharpCode.Decompiler/IL/Instructions.cs @@ -677,7 +677,7 @@ namespace ICSharpCode.Decompiler.IL namespace ICSharpCode.Decompiler.IL { /// A region where a pinned variable is used (initial representation of future fixed statement). - public sealed partial class PinnedRegion : ILInstruction, IInstructionWithVariableOperand + public sealed partial class PinnedRegion : ILInstruction, IStoreInstruction { public PinnedRegion(ILVariable variable, ILInstruction init, ILInstruction body) : base(OpCode.PinnedRegion) { @@ -693,21 +693,29 @@ namespace ICSharpCode.Decompiler.IL set { Debug.Assert(value != null); if (IsConnected) - variable.StoreCount--; + variable.RemoveStoreInstruction(this); variable = value; if (IsConnected) - variable.StoreCount++; + variable.AddStoreInstruction(this); } } + + public int IndexInStoreInstructionList { get; set; } = -1; + + int IInstructionWithVariableOperand.IndexInVariableInstructionMapping { + get { return ((IStoreInstruction)this).IndexInStoreInstructionList; } + set { ((IStoreInstruction)this).IndexInStoreInstructionList = value; } + } + protected override void Connected() { base.Connected(); - variable.StoreCount++; + variable.AddStoreInstruction(this); } protected override void Disconnected() { - variable.StoreCount--; + variable.RemoveStoreInstruction(this); base.Disconnected(); } @@ -1300,7 +1308,7 @@ namespace ICSharpCode.Decompiler.IL namespace ICSharpCode.Decompiler.IL { /// Catch handler within a try-catch statement. - public sealed partial class TryCatchHandler : ILInstruction, IInstructionWithVariableOperand + public sealed partial class TryCatchHandler : ILInstruction, IStoreInstruction { public TryCatchHandler(ILInstruction filter, ILInstruction body, ILVariable variable) : base(OpCode.TryCatchHandler) { @@ -1379,21 +1387,29 @@ namespace ICSharpCode.Decompiler.IL set { Debug.Assert(value != null); if (IsConnected) - variable.StoreCount--; + variable.RemoveStoreInstruction(this); variable = value; if (IsConnected) - variable.StoreCount++; + variable.AddStoreInstruction(this); } } + + public int IndexInStoreInstructionList { get; set; } = -1; + + int IInstructionWithVariableOperand.IndexInVariableInstructionMapping { + get { return ((IStoreInstruction)this).IndexInStoreInstructionList; } + set { ((IStoreInstruction)this).IndexInStoreInstructionList = value; } + } + protected override void Connected() { base.Connected(); - variable.StoreCount++; + variable.AddStoreInstruction(this); } protected override void Disconnected() { - variable.StoreCount--; + variable.RemoveStoreInstruction(this); base.Disconnected(); } @@ -1639,7 +1655,7 @@ namespace ICSharpCode.Decompiler.IL namespace ICSharpCode.Decompiler.IL { /// Loads the value of a local variable. (ldarg/ldloc) - public sealed partial class LdLoc : SimpleInstruction, IInstructionWithVariableOperand + public sealed partial class LdLoc : SimpleInstruction, ILoadInstruction { public LdLoc(ILVariable variable) : base(OpCode.LdLoc) { @@ -1652,21 +1668,29 @@ namespace ICSharpCode.Decompiler.IL set { Debug.Assert(value != null); if (IsConnected) - variable.LoadCount--; + variable.RemoveLoadInstruction(this); variable = value; if (IsConnected) - variable.LoadCount++; + variable.AddLoadInstruction(this); } } + + public int IndexInLoadInstructionList { get; set; } = -1; + + int IInstructionWithVariableOperand.IndexInVariableInstructionMapping { + get { return ((ILoadInstruction)this).IndexInLoadInstructionList; } + set { ((ILoadInstruction)this).IndexInLoadInstructionList = value; } + } + protected override void Connected() { base.Connected(); - variable.LoadCount++; + variable.AddLoadInstruction(this); } protected override void Disconnected() { - variable.LoadCount--; + variable.RemoveLoadInstruction(this); base.Disconnected(); } @@ -1713,7 +1737,7 @@ namespace ICSharpCode.Decompiler.IL namespace ICSharpCode.Decompiler.IL { /// Loads the address of a local variable. (ldarga/ldloca) - public sealed partial class LdLoca : SimpleInstruction, IInstructionWithVariableOperand + public sealed partial class LdLoca : SimpleInstruction, IAddressInstruction { public LdLoca(ILVariable variable) : base(OpCode.LdLoca) { @@ -1727,21 +1751,29 @@ namespace ICSharpCode.Decompiler.IL set { Debug.Assert(value != null); if (IsConnected) - variable.AddressCount--; + variable.RemoveAddressInstruction(this); variable = value; if (IsConnected) - variable.AddressCount++; + variable.AddAddressInstruction(this); } } + + public int IndexInAddressInstructionList { get; set; } = -1; + + int IInstructionWithVariableOperand.IndexInVariableInstructionMapping { + get { return ((IAddressInstruction)this).IndexInAddressInstructionList; } + set { ((IAddressInstruction)this).IndexInAddressInstructionList = value; } + } + protected override void Connected() { base.Connected(); - variable.AddressCount++; + variable.AddAddressInstruction(this); } protected override void Disconnected() { - variable.AddressCount--; + variable.RemoveAddressInstruction(this); base.Disconnected(); } @@ -1778,7 +1810,7 @@ namespace ICSharpCode.Decompiler.IL namespace ICSharpCode.Decompiler.IL { /// Stores a value into a local variable. (starg/stloc) - public sealed partial class StLoc : ILInstruction, IInstructionWithVariableOperand + public sealed partial class StLoc : ILInstruction, IStoreInstruction { public StLoc(ILVariable variable, ILInstruction value) : base(OpCode.StLoc) { @@ -1792,21 +1824,29 @@ namespace ICSharpCode.Decompiler.IL set { Debug.Assert(value != null); if (IsConnected) - variable.StoreCount--; + variable.RemoveStoreInstruction(this); variable = value; if (IsConnected) - variable.StoreCount++; + variable.AddStoreInstruction(this); } } + + public int IndexInStoreInstructionList { get; set; } = -1; + + int IInstructionWithVariableOperand.IndexInVariableInstructionMapping { + get { return ((IStoreInstruction)this).IndexInStoreInstructionList; } + set { ((IStoreInstruction)this).IndexInStoreInstructionList = value; } + } + protected override void Connected() { base.Connected(); - variable.StoreCount++; + variable.AddStoreInstruction(this); } protected override void Disconnected() { - variable.StoreCount--; + variable.RemoveStoreInstruction(this); base.Disconnected(); } diff --git a/ICSharpCode.Decompiler/IL/Instructions.tt b/ICSharpCode.Decompiler/IL/Instructions.tt index 1a8756175..d51e325be 100644 --- a/ICSharpCode.Decompiler/IL/Instructions.tt +++ b/ICSharpCode.Decompiler/IL/Instructions.tt @@ -824,27 +824,35 @@ namespace ICSharpCode.Decompiler.IL opCode.GenerateWriteTo = true; opCode.WriteOperand.Add("output.Write(' ');"); opCode.WriteOperand.Add("variable.WriteTo(output);"); - opCode.Interfaces.Add("IInstructionWithVariableOperand"); + opCode.Interfaces.Add("I" + accessType + "Instruction"); opCode.Members.Add(@"public ILVariable Variable { get { return variable; } set { Debug.Assert(value != null); if (IsConnected) - variable.AccessCount--; + variable.RemoveAccessInstruction(this); variable = value; if (IsConnected) - variable.AccessCount++; + variable.AddAccessInstruction(this); } } + +public int IndexInAccessInstructionList { get; set; } = -1; + +int IInstructionWithVariableOperand.IndexInVariableInstructionMapping { + get { return ((IAccessInstruction)this).IndexInAccessInstructionList; } + set { ((IAccessInstruction)this).IndexInAccessInstructionList = value; } +} + protected override void Connected() { base.Connected(); - variable.AccessCount++; + variable.AddAccessInstruction(this); } protected override void Disconnected() { - variable.AccessCount--; + variable.RemoveAccessInstruction(this); base.Disconnected(); } ".Replace("Access", accessType));