// Copyright (c) 2011 AlphaSierraPapa for the SharpDevelop Team // // 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.IO; using ICSharpCode.Decompiler.Disassembler; using Mono.Cecil; using Mono.Cecil.Cil; namespace ICSharpCode.Decompiler.FlowAnalysis { public enum SpecialOpCode { /// /// No special op code: SsaInstruction has a normal IL instruction /// None, /// /// Φ function: chooses the appropriate variable based on which CFG edge was used to enter this block /// Phi, /// /// Variable is read from before passing it by ref. /// This instruction constructs a managed reference to the variable. /// PrepareByRefCall, /// /// This instruction constructs a managed reference to the variable. /// The variable is not really read from. /// PrepareByOutCall, /// /// This instruction constructs a managed reference to the variable. /// The reference is used for a field access on a value type. /// PrepareForFieldAccess, /// /// Variable is written to after passing it by ref or out. /// WriteAfterByRefOrOutCall, /// /// Variable is not initialized. /// Uninitialized, /// /// Value is passed in as parameter /// Parameter, /// /// Value is a caught exception. /// TypeOperand is set to the exception type. /// Exception, /// /// Initialize a value type. Unlike the real initobj instruction, this one does not take an address /// but assigns to the target variable. /// TypeOperand is set to the type being created. /// InitObj } public sealed class SsaInstruction { public readonly SsaBlock ParentBlock; public readonly SpecialOpCode SpecialOpCode; /// /// The original IL instruction. /// May be null for "invented" instructions (SpecialOpCode != None). /// public readonly Instruction Instruction; /// /// Prefixes in front of the IL instruction. /// public readonly Instruction[] Prefixes; /// /// Gets the type operand. This is used only in combination with some special opcodes. /// public readonly TypeReference TypeOperand; public SsaVariable Target; public SsaVariable[] Operands; static readonly SsaVariable[] emptyVariableArray = {}; static readonly Instruction[] emptyInstructionArray = {}; public SsaInstruction(SsaBlock parentBlock, Instruction instruction, SsaVariable target, SsaVariable[] operands, Instruction[] prefixes = null, SpecialOpCode specialOpCode = SpecialOpCode.None, TypeReference typeOperand = null) { ParentBlock = parentBlock; Instruction = instruction; Prefixes = prefixes ?? emptyInstructionArray; Target = target; Operands = operands ?? emptyVariableArray; SpecialOpCode = specialOpCode; TypeOperand = typeOperand; Debug.Assert((typeOperand != null) == (specialOpCode == SpecialOpCode.Exception || specialOpCode == SpecialOpCode.InitObj)); } /// /// Gets whether this instruction is a simple assignment from one variable to another. /// public bool IsMoveInstruction { get { return Target != null && Operands.Length == 1 && Instruction != null && OpCodeInfo.Get(Instruction.OpCode).IsMoveInstruction; } } public void ReplaceVariableInOperands(SsaVariable oldVar, SsaVariable newVar) { for (int i = 0; i < Operands.Length; i++) { if (Operands[i] == oldVar) Operands[i] = newVar; } } public override string ToString() { StringWriter w = new StringWriter(); WriteTo(w); return w.ToString(); } public void WriteTo(TextWriter writer) { foreach (Instruction prefix in Prefixes) { DisassemblerHelpers.WriteTo(prefix, new PlainTextOutput(writer)); writer.WriteLine(); } if (Instruction != null && Instruction.Offset >= 0) { writer.Write(CecilExtensions.OffsetToString(Instruction.Offset)); writer.Write(": "); } if (Target != null) { writer.Write(Target.ToString()); writer.Write(" = "); } if (IsMoveInstruction) { writer.Write(Operands[0].ToString()); if (Instruction != null) { writer.Write(" (" + Instruction.OpCode.Name + ")"); } } else { if (Instruction == null) { writer.Write(SpecialOpCode.ToString()); } else { writer.Write(Instruction.OpCode.Name); if(null != Instruction.Operand) { writer.Write(' '); DisassemblerHelpers.WriteOperand(new PlainTextOutput(writer), Instruction.Operand); writer.Write(' '); } } if (TypeOperand != null) { writer.Write(' '); writer.Write(TypeOperand.ToString()); writer.Write(' '); } if (Operands.Length > 0) { writer.Write('('); for (int i = 0; i < Operands.Length; i++) { if (i > 0) writer.Write(", "); writer.Write(Operands[i].ToString()); } writer.Write(')'); } } } } }