// Copyright (c) 2014 Daniel Grunwald // // 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.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Text; using System.Threading.Tasks; using ICSharpCode.NRefactory.Utils; using ICSharpCode.Decompiler.CSharp; namespace ICSharpCode.Decompiler.IL { /// /// Represents a decoded IL instruction /// public abstract partial class ILInstruction { public readonly OpCode OpCode; protected ILInstruction(OpCode opCode) { this.OpCode = opCode; } internal static void ValidateArgument(ILInstruction inst) { if (inst == null) throw new ArgumentNullException("inst"); if (inst.ResultType == StackType.Void) throw new ArgumentException("Argument must not be of type void", "inst"); } internal void ValidateChild(ILInstruction inst) { if (inst == null) throw new ArgumentNullException("inst"); Debug.Assert(!this.IsDescendantOf(inst), "ILAst must form a tree"); } [Conditional("DEBUG")] internal virtual void CheckInvariant() { foreach (var child in Children) { Debug.Assert(child.Parent == this); // if child flags are invalid, parent flags must be too Debug.Assert(child.flags != invalidFlags || this.flags == invalidFlags); Debug.Assert(child.IsConnected == this.IsConnected); child.CheckInvariant(); } } /// /// Gets whether this node is a descendant of . /// Also returns true if this==. /// public bool IsDescendantOf(ILInstruction possibleAncestor) { for (ILInstruction ancestor = this; ancestor != null; ancestor = ancestor.Parent) { if (ancestor == possibleAncestor) return true; } return false; } /// /// Gets the stack type of the value produced by this instruction. /// public abstract StackType ResultType { get; } internal static StackType CommonResultType(StackType a, StackType b) { if (a == StackType.I || b == StackType.I) return StackType.I; Debug.Assert(a == b); return a; } const InstructionFlags invalidFlags = (InstructionFlags)(-1); InstructionFlags flags = invalidFlags; public InstructionFlags Flags { get { if (flags == invalidFlags) { flags = ComputeFlags(); } return flags; } } /// /// Returns whether the instruction has at least one of the specified flags. /// public bool HasFlag(InstructionFlags flags) { return (this.Flags & flags) != 0; } protected void InvalidateFlags() { for (ILInstruction inst = this; inst != null && inst.flags != invalidFlags; inst = inst.parent) inst.flags = invalidFlags; } protected abstract InstructionFlags ComputeFlags(); /// /// Gets the ILRange for this instruction alone, ignoring the operands. /// public Interval ILRange; /// /// Writes the ILAst to the text output. /// public abstract void WriteTo(ITextOutput output); public override string ToString() { var output = new PlainTextOutput(); WriteTo(output); return output.ToString(); } /// /// Calls the Visit*-method on the visitor corresponding to the concrete type of this instruction. /// public abstract T AcceptVisitor(ILVisitor visitor); /// /// Gets the child nodes of this instruction. /// public abstract IEnumerable Children { get; } /// /// Transforms the children of this instruction by applying the specified visitor. /// public abstract void TransformChildren(ILVisitor visitor); /// /// Returns all descendants of the ILInstruction. /// public IEnumerable Descendants { get { return TreeTraversal.PreOrder(Children, inst => inst.Children); } } /// /// Attempts inlining from the inline context into this instruction. /// /// Combined instruction flags of the instructions /// that the instructions getting inlined would get moved over. /// The inline context providing the values on the evaluation stack. /// /// Returns the modified ILInstruction after inlining is complete. /// Note that inlining modifies the AST in-place, so this method usually returns this /// (unless this should be replaced by another node) /// /// /// Inlining from an inline context representing the actual evaluation stack /// is equivalent to phase-1 execution of the instruction. /// internal abstract ILInstruction Inline(InstructionFlags flagsBefore, IInlineContext context); /// /// Transforms the evaluation stack 'pop' and 'peek' instructions into local copy. /// internal abstract void TransformStackIntoVariables(TransformStackIntoVariablesState state); /// /// Number of parents that refer to this instruction and are connected to the root. /// Usually is 0 for unconnected nodes and 1 for connected nodes, but may temporarily increase to 2 /// when the ILAst is re-arranged (e.g. within SetChildInstruction). /// byte refCount; internal void AddRef() { if (refCount++ == 0) { Connected(); } } internal void ReleaseRef() { Debug.Assert(refCount > 0); if (--refCount == 0) { Disconnected(); } } protected bool IsConnected { get { return refCount > 0; } } protected virtual void Connected() { foreach (var child in Children) child.AddRef(); } protected virtual void Disconnected() { foreach (var child in Children) child.ReleaseRef(); } ILInstruction parent; /// /// Gets the parent of this ILInstruction. /// public ILInstruction Parent { get { return parent; } } protected void SetChildInstruction(ref ILInstruction childPointer, ILInstruction newValue) { if (childPointer == newValue) return; if (refCount > 0) { // The new value may be a subtree of the old value. // We first call AddRef(), then ReleaseRef() to prevent the subtree // that stays connected from receiving a Disconnected() notification followed by a Connected() notification. newValue.AddRef(); childPointer.ReleaseRef(); } childPointer = newValue; newValue.parent = this; InvalidateFlags(); } /// /// Called when a new child is added to a InstructionCollection. /// protected internal void InstructionCollectionAdded(ILInstruction newChild) { if (refCount > 0) newChild.AddRef(); newChild.parent = this; } /// /// Called when a child is removed from a InstructionCollection. /// protected internal void InstructionCollectionRemoved(ILInstruction newChild) { if (refCount > 0) newChild.ReleaseRef(); } /// /// Called when a series of add/remove operations on the InstructionCollection is complete. /// protected internal virtual void InstructionCollectionUpdateComplete() { InvalidateFlags(); } } }