// 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.Collections.Generic; using System.Diagnostics; using System.IO; using System.Linq; using System.Text; using ICSharpCode.Decompiler; using ICSharpCode.Decompiler.Disassembler; using ICSharpCode.NRefactory.Utils; using Mono.Cecil; using Mono.Cecil.Cil; using Mono.CSharp; using Cecil = Mono.Cecil; namespace ICSharpCode.Decompiler.ILAst { public abstract class ILNode { public IEnumerable GetSelfAndChildrenRecursive(Func predicate = null) where T: ILNode { List result = new List(16); AccumulateSelfAndChildrenRecursive(result, predicate); return result; } void AccumulateSelfAndChildrenRecursive(List list, Func predicate) where T:ILNode { // Note: RemoveEndFinally depends on self coming before children T thisAsT = this as T; if (thisAsT != null && (predicate == null || predicate(thisAsT))) list.Add(thisAsT); foreach (ILNode node in GetChildren()) { if (node != null) node.AccumulateSelfAndChildrenRecursive(list, predicate); } } public virtual IEnumerable GetChildren() { yield break; } public override string ToString() { StringWriter w = new StringWriter(); WriteTo(new PlainTextOutput(w)); return w.ToString().Replace("\r\n", "; "); } public abstract void WriteTo(ITextOutput output); } public class ILBlock: ILNode { public ILExpression EntryGoto; public List Body; public ILBlock(params ILNode[] body) { Body = new List(body); } public ILBlock(List body) { Body = body; } public override IEnumerable GetChildren() { if (EntryGoto != null) yield return EntryGoto; foreach(ILNode child in Body) { yield return child; } } public override void WriteTo(ITextOutput output) { foreach(ILNode child in GetChildren()) { child.WriteTo(output); output.WriteLine(); } } } public class ILBasicBlock: ILNode { /// Body has to start with a label and end with unconditional control flow public List Body = new List(); public override IEnumerable GetChildren() { return Body; } public override void WriteTo(ITextOutput output) { foreach(ILNode child in GetChildren()) { child.WriteTo(output); output.WriteLine(); } } } public class ILLabel: ILNode { public string Name; public override void WriteTo(ITextOutput output) { output.WriteDefinition(Name + ":", this); } } public class ILTryCatchBlock: ILNode { public class CatchBlock: ILBlock { public TypeReference ExceptionType; public ILVariable ExceptionVariable; public override void WriteTo(ITextOutput output) { output.Write("catch "); output.WriteReference(ExceptionType.FullName, ExceptionType); if (ExceptionVariable != null) { output.Write(' '); output.Write(ExceptionVariable.Name); } output.WriteLine(" {"); output.Indent(); base.WriteTo(output); output.Unindent(); output.WriteLine("}"); } } public ILBlock TryBlock; public List CatchBlocks; public ILBlock FinallyBlock; public ILBlock FaultBlock; public override IEnumerable GetChildren() { if (TryBlock != null) yield return TryBlock; foreach (var catchBlock in CatchBlocks) { yield return catchBlock; } if (FaultBlock != null) yield return FaultBlock; if (FinallyBlock != null) yield return FinallyBlock; } public override void WriteTo(ITextOutput output) { output.WriteLine(".try {"); output.Indent(); TryBlock.WriteTo(output); output.Unindent(); output.WriteLine("}"); foreach (CatchBlock block in CatchBlocks) { block.WriteTo(output); } if (FaultBlock != null) { output.WriteLine("fault {"); output.Indent(); FaultBlock.WriteTo(output); output.Unindent(); output.WriteLine("}"); } if (FinallyBlock != null) { output.WriteLine("finally {"); output.Indent(); FinallyBlock.WriteTo(output); output.Unindent(); output.WriteLine("}"); } } } public class ILVariable { public string Name; public bool IsGenerated; public TypeReference Type; public VariableDefinition OriginalVariable; public ParameterDefinition OriginalParameter; public bool IsPinned { get { return OriginalVariable != null && OriginalVariable.IsPinned; } } public bool IsParameter { get { return OriginalParameter != null; } } public override string ToString() { return Name; } } public struct ILRange { public readonly int From; public readonly int To; // Exlusive public ILRange(int @from, int to) { From = @from; To = to; } public override string ToString() { return string.Format("{0:X2}-{1:X2}", From, To); } public static List OrderAndJoin(IEnumerable input) { if (input == null) throw new ArgumentNullException("Input is null!"); List result = new List(); foreach(ILRange curr in input.OrderBy(r => r.From)) { if (result.Count > 0) { // Merge consequtive ranges if possible ILRange last = result[result.Count - 1]; if (curr.From <= last.To) { result[result.Count - 1] = new ILRange(last.From, Math.Max(last.To, curr.To)); continue; } } result.Add(curr); } return result; } public static List Invert(IEnumerable input, int codeSize) { if (input == null) throw new ArgumentNullException("Input is null!"); if (codeSize <= 0) throw new ArgumentException("Code size must be grater than 0"); List ordered = OrderAndJoin(input); List result = new List(ordered.Count + 1); if (ordered.Count == 0) { result.Add(new ILRange(0, codeSize)); } else { // Gap before the first element if (ordered.First().From != 0) result.Add(new ILRange(0, ordered.First().From)); // Gaps between elements for (int i = 0; i < ordered.Count - 1; i++) result.Add(new ILRange(ordered[i].To, ordered[i + 1].From)); // Gap after the last element Debug.Assert(ordered.Last().To <= codeSize); if (ordered.Last().To != codeSize) result.Add(new ILRange(ordered.Last().To, codeSize)); } return result; } } public class ILExpressionPrefix { public readonly ILCode Code; public readonly object Operand; public ILExpressionPrefix(ILCode code, object operand = null) { Code = code; Operand = operand; } } public class ILExpression : ILNode { public ILCode Code { get; set; } public object Operand { get; set; } public List Arguments { get; set; } public ILExpressionPrefix[] Prefixes { get; set; } // Mapping to the original instructions (useful for debugging) public List ILRanges { get; set; } public TypeReference ExpectedType { get; set; } public TypeReference InferredType { get; set; } public static readonly object AnyOperand = new object(); public ILExpression(ILCode code, object operand, List args) { if (operand is ILExpression) throw new ArgumentException("operand"); Code = code; Operand = operand; Arguments = new List(args); ILRanges = new List(1); } public ILExpression(ILCode code, object operand, params ILExpression[] args) { if (operand is ILExpression) throw new ArgumentException("operand"); Code = code; Operand = operand; Arguments = new List(args); ILRanges = new List(1); } public void AddPrefix(ILExpressionPrefix prefix) { ILExpressionPrefix[] arr = Prefixes; if (arr == null) arr = new ILExpressionPrefix[1]; else Array.Resize(ref arr, arr.Length + 1); arr[arr.Length - 1] = prefix; Prefixes = arr; } public ILExpressionPrefix GetPrefix(ILCode code) { var prefixes = Prefixes; if (prefixes != null) { foreach (ILExpressionPrefix p in prefixes) { if (p.Code == code) return p; } } return null; } public override IEnumerable GetChildren() { return Arguments; } public bool IsBranch() { return Operand is ILLabel || Operand is ILLabel[]; } public IEnumerable GetBranchTargets() { if (Operand is ILLabel) { return new ILLabel[] { (ILLabel)Operand }; } else if (Operand is ILLabel[]) { return (ILLabel[])Operand; } else { return new ILLabel[] { }; } } public override void WriteTo(ITextOutput output) { if (Operand is ILVariable && ((ILVariable)Operand).IsGenerated) { if (Code == ILCode.Stloc && InferredType == null) { output.Write(((ILVariable)Operand).Name); output.Write(" = "); Arguments.First().WriteTo(output); return; } else if (Code == ILCode.Ldloc) { output.Write(((ILVariable)Operand).Name); if (InferredType != null) { output.Write(':'); InferredType.WriteTo(output, ILNameSyntax.ShortTypeName); if (ExpectedType != null && ExpectedType.FullName != InferredType.FullName) { output.Write("[exp:"); ExpectedType.WriteTo(output, ILNameSyntax.ShortTypeName); output.Write(']'); } } return; } } if (Prefixes != null) { foreach (var prefix in Prefixes) { output.Write(prefix.Code.GetName()); output.Write(". "); } } output.Write(Code.GetName()); if (InferredType != null) { output.Write(':'); InferredType.WriteTo(output, ILNameSyntax.ShortTypeName); if (ExpectedType != null && ExpectedType.FullName != InferredType.FullName) { output.Write("[exp:"); ExpectedType.WriteTo(output, ILNameSyntax.ShortTypeName); output.Write(']'); } } else if (ExpectedType != null) { output.Write("[exp:"); ExpectedType.WriteTo(output, ILNameSyntax.ShortTypeName); output.Write(']'); } output.Write('('); bool first = true; if (Operand != null) { if (Operand is ILLabel) { output.WriteReference(((ILLabel)Operand).Name, Operand); } else if (Operand is ILLabel[]) { ILLabel[] labels = (ILLabel[])Operand; for (int i = 0; i < labels.Length; i++) { if (i > 0) output.Write(", "); output.WriteReference(labels[i].Name, labels[i]); } } else if (Operand is MethodReference) { MethodReference method = (MethodReference)Operand; if (method.DeclaringType != null) { method.DeclaringType.WriteTo(output, ILNameSyntax.ShortTypeName); output.Write("::"); } output.WriteReference(method.Name, method); } else if (Operand is FieldReference) { FieldReference field = (FieldReference)Operand; field.DeclaringType.WriteTo(output, ILNameSyntax.ShortTypeName); output.Write("::"); output.WriteReference(field.Name, field); } else { DisassemblerHelpers.WriteOperand(output, Operand); } first = false; } foreach (ILExpression arg in Arguments) { if (!first) output.Write(", "); arg.WriteTo(output); first = false; } output.Write(')'); } } public class ILWhileLoop : ILNode { public ILExpression Condition; public ILBlock BodyBlock; public override IEnumerable GetChildren() { if (Condition != null) yield return Condition; if (BodyBlock != null) yield return BodyBlock; } public override void WriteTo(ITextOutput output) { output.WriteLine(""); output.Write("loop ("); if (Condition != null) Condition.WriteTo(output); output.WriteLine(") {"); output.Indent(); BodyBlock.WriteTo(output); output.Unindent(); output.WriteLine("}"); } } public class ILCondition : ILNode { public ILExpression Condition; public ILBlock TrueBlock; // Branch was taken public ILBlock FalseBlock; // Fall-though public override IEnumerable GetChildren() { if (Condition != null) yield return Condition; if (TrueBlock != null) yield return TrueBlock; if (FalseBlock != null) yield return FalseBlock; } public override void WriteTo(ITextOutput output) { output.Write("if ("); Condition.WriteTo(output); output.WriteLine(") {"); output.Indent(); TrueBlock.WriteTo(output); output.Unindent(); output.Write("}"); if (FalseBlock != null) { output.WriteLine(" else {"); output.Indent(); FalseBlock.WriteTo(output); output.Unindent(); output.WriteLine("}"); } } } public class ILSwitch: ILNode { public class CaseBlock: ILBlock { public List Values; // null for the default case public override void WriteTo(ITextOutput output) { if (Values != null) { foreach (int i in Values) { output.WriteLine("case {0}:", i); } } else { output.WriteLine("default:"); } output.Indent(); base.WriteTo(output); output.Unindent(); } } public ILExpression Condition; public List CaseBlocks = new List(); public override IEnumerable GetChildren() { if (Condition != null) yield return Condition; foreach (ILBlock caseBlock in CaseBlocks) { yield return caseBlock; } } public override void WriteTo(ITextOutput output) { output.Write("switch ("); Condition.WriteTo(output); output.WriteLine(") {"); output.Indent(); foreach (CaseBlock caseBlock in CaseBlocks) { caseBlock.WriteTo(output); } output.Unindent(); output.WriteLine("}"); } } public class ILFixedStatement : ILNode { public List Initializers = new List(); public ILBlock BodyBlock; public override IEnumerable GetChildren() { foreach (ILExpression initializer in Initializers) yield return initializer; if (BodyBlock != null) yield return BodyBlock; } public override void WriteTo(ITextOutput output) { output.Write("fixed ("); for (int i = 0; i < Initializers.Count; i++) { if (i > 0) output.Write(", "); Initializers[i].WriteTo(output); } output.WriteLine(") {"); output.Indent(); BodyBlock.WriteTo(output); output.Unindent(); output.WriteLine("}"); } } }