mirror of https://github.com/icsharpcode/ILSpy.git
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
559 lines
14 KiB
559 lines
14 KiB
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<T> GetSelfAndChildrenRecursive<T>(Func<T, bool> predicate = null) where T: ILNode |
|
{ |
|
List<T> result = new List<T>(16); |
|
AccumulateSelfAndChildrenRecursive(result, predicate); |
|
return result; |
|
} |
|
|
|
void AccumulateSelfAndChildrenRecursive<T>(List<T> list, Func<T, bool> predicate) where T:ILNode |
|
{ |
|
T thisAsT = this as T; |
|
if (thisAsT != null && (predicate == null || predicate(thisAsT))) |
|
list.Add(thisAsT); |
|
foreach (ILNode node in this.GetChildren()) { |
|
if (node != null) |
|
node.AccumulateSelfAndChildrenRecursive(list, predicate); |
|
} |
|
} |
|
|
|
public virtual IEnumerable<ILNode> 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<ILNode> Body; |
|
|
|
public ILBlock(params ILNode[] body) |
|
{ |
|
this.Body = new List<ILNode>(body); |
|
} |
|
|
|
public ILBlock(List<ILNode> body) |
|
{ |
|
this.Body = body; |
|
} |
|
|
|
public override IEnumerable<ILNode> GetChildren() |
|
{ |
|
if (this.EntryGoto != null) |
|
yield return this.EntryGoto; |
|
foreach(ILNode child in this.Body) { |
|
yield return child; |
|
} |
|
} |
|
|
|
public override void WriteTo(ITextOutput output) |
|
{ |
|
foreach(ILNode child in this.GetChildren()) { |
|
child.WriteTo(output); |
|
output.WriteLine(); |
|
} |
|
} |
|
} |
|
|
|
public class ILBasicBlock: ILNode |
|
{ |
|
/// <remarks> Body has to start with a label and end with unconditional control flow </remarks> |
|
public List<ILNode> Body = new List<ILNode>(); |
|
|
|
public override IEnumerable<ILNode> GetChildren() |
|
{ |
|
return this.Body; |
|
} |
|
|
|
public override void WriteTo(ITextOutput output) |
|
{ |
|
foreach(ILNode child in this.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); |
|
output.WriteLine(" {"); |
|
output.Indent(); |
|
base.WriteTo(output); |
|
output.Unindent(); |
|
output.WriteLine("}"); |
|
} |
|
} |
|
|
|
public ILBlock TryBlock; |
|
public List<CatchBlock> CatchBlocks; |
|
public ILBlock FinallyBlock; |
|
public ILBlock FaultBlock; |
|
|
|
public override IEnumerable<ILNode> GetChildren() |
|
{ |
|
if (this.TryBlock != null) |
|
yield return this.TryBlock; |
|
foreach (var catchBlock in this.CatchBlocks) { |
|
yield return catchBlock; |
|
} |
|
if (this.FaultBlock != null) |
|
yield return this.FaultBlock; |
|
if (this.FinallyBlock != null) |
|
yield return this.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 class ILRange |
|
{ |
|
public int From; |
|
public int To; // Exlusive |
|
|
|
public override string ToString() |
|
{ |
|
return string.Format("{0}-{1}", From.ToString("X"), To.ToString("X")); |
|
} |
|
|
|
public static List<ILRange> OrderAndJoint(IEnumerable<ILRange> input) |
|
{ |
|
List<ILRange> ranges = input.OrderBy(r => r.From).ToList(); |
|
for (int i = 0; i < ranges.Count - 1;) { |
|
ILRange curr = ranges[i]; |
|
ILRange next = ranges[i + 1]; |
|
// Merge consequtive ranges if they intersect |
|
if (curr.From <= next.From && next.From <= curr.To) { |
|
curr.To = Math.Max(curr.To, next.To); |
|
ranges.RemoveAt(i + 1); |
|
} else { |
|
i++; |
|
} |
|
} |
|
return ranges; |
|
} |
|
|
|
public static IEnumerable<ILRange> Invert(IEnumerable<ILRange> input, int codeSize) |
|
{ |
|
var ordered = OrderAndJoint(input); |
|
if (ordered.Count == 0) { |
|
yield return new ILRange() { From = 0, To = codeSize }; |
|
} else { |
|
// Gap before the first element |
|
if (ordered.First().From != 0) |
|
yield return new ILRange() { From = 0, To = ordered.First().From }; |
|
|
|
// Gaps between elements |
|
for (int i = 0; i < ordered.Count - 1; i++) |
|
yield return new ILRange() { From = ordered[i].To, To = ordered[i + 1].From }; |
|
|
|
// Gap after the last element |
|
Debug.Assert(ordered.Last().To <= codeSize); |
|
if (ordered.Last().To != codeSize) |
|
yield return new ILRange() { From = ordered.Last().To, To = codeSize }; |
|
} |
|
} |
|
} |
|
|
|
public class ILExpressionPrefix |
|
{ |
|
public readonly ILCode Code; |
|
public readonly object Operand; |
|
|
|
public ILExpressionPrefix(ILCode code, object operand = null) |
|
{ |
|
this.Code = code; |
|
this.Operand = operand; |
|
} |
|
} |
|
|
|
public class ILExpression : ILNode |
|
{ |
|
public ILCode Code { get; set; } |
|
public object Operand { get; set; } |
|
public List<ILExpression> Arguments { get; set; } |
|
public ILExpressionPrefix[] Prefixes { get; set; } |
|
// Mapping to the original instructions (useful for debugging) |
|
public List<ILRange> 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<ILExpression> args) |
|
{ |
|
if (operand is ILExpression) |
|
throw new ArgumentException("operand"); |
|
|
|
this.Code = code; |
|
this.Operand = operand; |
|
this.Arguments = new List<ILExpression>(args); |
|
this.ILRanges = new List<ILRange>(1); |
|
} |
|
|
|
public ILExpression(ILCode code, object operand, params ILExpression[] args) |
|
{ |
|
if (operand is ILExpression) |
|
throw new ArgumentException("operand"); |
|
|
|
this.Code = code; |
|
this.Operand = operand; |
|
this.Arguments = new List<ILExpression>(args); |
|
this.ILRanges = new List<ILRange>(1); |
|
} |
|
|
|
public void AddPrefix(ILExpressionPrefix prefix) |
|
{ |
|
ILExpressionPrefix[] arr = this.Prefixes; |
|
if (arr == null) |
|
arr = new ILExpressionPrefix[1]; |
|
else |
|
Array.Resize(ref arr, arr.Length + 1); |
|
arr[arr.Length - 1] = prefix; |
|
this.Prefixes = arr; |
|
} |
|
|
|
public ILExpressionPrefix GetPrefix(ILCode code) |
|
{ |
|
var prefixes = this.Prefixes; |
|
if (prefixes != null) { |
|
foreach (ILExpressionPrefix p in prefixes) { |
|
if (p.Code == code) |
|
return p; |
|
} |
|
} |
|
return null; |
|
} |
|
|
|
public override IEnumerable<ILNode> GetChildren() |
|
{ |
|
return Arguments; |
|
} |
|
|
|
public bool IsBranch() |
|
{ |
|
return this.Operand is ILLabel || this.Operand is ILLabel[]; |
|
} |
|
|
|
public IEnumerable<ILLabel> GetBranchTargets() |
|
{ |
|
if (this.Operand is ILLabel) { |
|
return new ILLabel[] { (ILLabel)this.Operand }; |
|
} else if (this.Operand is ILLabel[]) { |
|
return (ILLabel[])this.Operand; |
|
} else { |
|
return new ILLabel[] { }; |
|
} |
|
} |
|
|
|
public override void WriteTo(ITextOutput output) |
|
{ |
|
if (Operand is ILVariable && ((ILVariable)Operand).IsGenerated) { |
|
if (Code == ILCode.Stloc && this.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 (this.InferredType != null) { |
|
output.Write(':'); |
|
this.InferredType.WriteTo(output, true, true); |
|
if (this.ExpectedType != null && this.ExpectedType.FullName != this.InferredType.FullName) { |
|
output.Write("[exp:"); |
|
this.ExpectedType.WriteTo(output, true, true); |
|
output.Write(']'); |
|
} |
|
} |
|
return; |
|
} |
|
} |
|
|
|
if (this.Prefixes != null) { |
|
foreach (var prefix in this.Prefixes) { |
|
output.Write(prefix.Code.GetName()); |
|
output.Write(". "); |
|
} |
|
} |
|
|
|
output.Write(Code.GetName()); |
|
if (this.InferredType != null) { |
|
output.Write(':'); |
|
this.InferredType.WriteTo(output, true, true); |
|
if (this.ExpectedType != null && this.ExpectedType.FullName != this.InferredType.FullName) { |
|
output.Write("[exp:"); |
|
this.ExpectedType.WriteTo(output, true, true); |
|
output.Write(']'); |
|
} |
|
} else if (this.ExpectedType != null) { |
|
output.Write("[exp:"); |
|
this.ExpectedType.WriteTo(output, true, true); |
|
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; |
|
method.DeclaringType.WriteTo(output, true, true); |
|
output.Write("::"); |
|
output.WriteReference(method.Name, method); |
|
} else if (Operand is FieldReference) { |
|
FieldReference field = (FieldReference)Operand; |
|
field.DeclaringType.WriteTo(output, true, true); |
|
output.Write("::"); |
|
output.WriteReference(field.Name, field); |
|
} else { |
|
DisassemblerHelpers.WriteOperand(output, Operand); |
|
} |
|
first = false; |
|
} |
|
foreach (ILExpression arg in this.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<ILNode> GetChildren() |
|
{ |
|
if (this.Condition != null) |
|
yield return this.Condition; |
|
if (this.BodyBlock != null) |
|
yield return this.BodyBlock; |
|
} |
|
|
|
public override void WriteTo(ITextOutput output) |
|
{ |
|
output.WriteLine(""); |
|
output.Write("loop ("); |
|
if (this.Condition != null) |
|
this.Condition.WriteTo(output); |
|
output.WriteLine(") {"); |
|
output.Indent(); |
|
this.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<ILNode> GetChildren() |
|
{ |
|
if (this.Condition != null) |
|
yield return this.Condition; |
|
if (this.TrueBlock != null) |
|
yield return this.TrueBlock; |
|
if (this.FalseBlock != null) |
|
yield return this.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<int> Values; // null for the default case |
|
|
|
public override void WriteTo(ITextOutput output) |
|
{ |
|
if (this.Values != null) { |
|
foreach (int i in this.Values) { |
|
output.WriteLine("case {0}:", i); |
|
} |
|
} else { |
|
output.WriteLine("default:"); |
|
} |
|
output.Indent(); |
|
base.WriteTo(output); |
|
output.Unindent(); |
|
} |
|
} |
|
|
|
public ILExpression Condition; |
|
public List<CaseBlock> CaseBlocks = new List<CaseBlock>(); |
|
|
|
public override IEnumerable<ILNode> GetChildren() |
|
{ |
|
if (this.Condition != null) |
|
yield return this.Condition; |
|
foreach (ILBlock caseBlock in this.CaseBlocks) { |
|
yield return caseBlock; |
|
} |
|
} |
|
|
|
public override void WriteTo(ITextOutput output) |
|
{ |
|
output.Write("switch ("); |
|
Condition.WriteTo(output); |
|
output.WriteLine(") {"); |
|
output.Indent(); |
|
foreach (CaseBlock caseBlock in this.CaseBlocks) { |
|
caseBlock.WriteTo(output); |
|
} |
|
output.Unindent(); |
|
output.WriteLine("}"); |
|
} |
|
} |
|
|
|
public class ILFixedStatement : ILNode |
|
{ |
|
public List<ILExpression> Initializers = new List<ILExpression>(); |
|
public ILBlock BodyBlock; |
|
|
|
public override IEnumerable<ILNode> GetChildren() |
|
{ |
|
foreach (ILExpression initializer in this.Initializers) |
|
yield return initializer; |
|
if (this.BodyBlock != null) |
|
yield return this.BodyBlock; |
|
} |
|
|
|
public override void WriteTo(ITextOutput output) |
|
{ |
|
output.Write("fixed ("); |
|
for (int i = 0; i < this.Initializers.Count; i++) { |
|
if (i > 0) |
|
output.Write(", "); |
|
this.Initializers[i].WriteTo(output); |
|
} |
|
output.WriteLine(") {"); |
|
output.Indent(); |
|
this.BodyBlock.WriteTo(output); |
|
output.Unindent(); |
|
output.WriteLine("}"); |
|
} |
|
} |
|
} |