.NET Decompiler with support for PDB generation, ReadyToRun, Metadata (&more) - cross-platform!
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.
 
 
 
 

392 lines
8.9 KiB

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using Decompiler.ControlFlow;
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 Decompiler
{
public abstract class ILNode
{
public IEnumerable<T> GetSelfAndChildrenRecursive<T>() where T: ILNode
{
return TreeTraversal.PreOrder(this, c => c != null ? c.GetChildren() : null).OfType<T>();
}
public virtual IEnumerable<ILNode> GetChildren()
{
yield break;
}
public override string ToString()
{
StringWriter w = new StringWriter();
WriteTo(new PlainTextOutput(w));
return w.ToString();
}
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
{
public ILLabel EntryLabel;
public List<ILNode> Body = new List<ILNode>();
public ILExpression FallthoughGoto;
public override IEnumerable<ILNode> GetChildren()
{
if (this.EntryLabel != null)
yield return this.EntryLabel;
foreach (ILNode child in this.Body) {
yield return child;
}
if (this.FallthoughGoto != null)
yield return this.FallthoughGoto;
}
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 ILComment: ILNode
{
public string Text;
public List<ILRange> ILRanges { get; set; }
public override void WriteTo(ITextOutput output)
{
output.WriteLine("// " + this.Text);
}
}
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 override IEnumerable<ILNode> GetChildren()
{
if (this.TryBlock != null)
yield return this.TryBlock;
foreach (var catchBlock in this.CatchBlocks) {
yield return catchBlock;
}
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 (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 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, To);
}
}
public class ILExpression : ILNode
{
public ILCode Code { get; set; }
public object Operand { get; set; }
public List<ILExpression> Arguments { get; set; }
// Mapping to the original instructions (useful for debugging)
public List<ILRange> ILRanges { get; set; }
public TypeReference InferredType { get; set; }
public ILExpression(ILCode code, object operand, params ILExpression[] args)
{
this.Code = code;
this.Operand = operand;
this.Arguments = new List<ILExpression>(args);
this.ILRanges = new List<ILRange>(1);
}
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 List<ILRange> GetILRanges()
{
List<ILRange> ranges = new List<ILRange>();
foreach(ILExpression expr in this.GetSelfAndChildrenRecursive<ILExpression>()) {
ranges.AddRange(expr.ILRanges);
}
ranges = ranges.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 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);
}
return;
}
}
output.Write(Code.GetName());
if (this.InferredType != null) {
output.Write(':');
this.InferredType.WriteTo(output, true, true);
}
output.Write('(');
bool first = true;
if (Operand != null) {
if (Operand is ILLabel) {
output.WriteReference(((ILLabel)Operand).Name, Operand);
} else if (Operand is MethodReference) {
MethodReference method = (MethodReference)Operand;
method.DeclaringType.WriteTo(output, true, true);
output.Write("::");
output.WriteReference(method.Name, method);
} 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 ILLoop : ILNode
{
public ILBlock ContentBlock;
public override IEnumerable<ILNode> GetChildren()
{
if (this.ContentBlock != null)
yield return ContentBlock;
}
public override void WriteTo(ITextOutput output)
{
output.WriteLine("loop {");
output.Indent();
ContentBlock.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 ILExpression Condition;
public List<ILBlock> CaseBlocks = new List<ILBlock>();
public ILExpression DefaultGoto;
public override IEnumerable<ILNode> GetChildren()
{
if (this.Condition != null)
yield return this.Condition;
foreach (ILBlock caseBlock in this.CaseBlocks) {
yield return caseBlock;
}
if (this.DefaultGoto != null)
yield return this.DefaultGoto;
}
public override void WriteTo(ITextOutput output)
{
output.Write("switch (");
Condition.WriteTo(output);
output.WriteLine(") {");
output.Indent();
for (int i = 0; i < CaseBlocks.Count; i++) {
output.WriteLine("case {0}:", i);
output.Indent();
CaseBlocks[i].WriteTo(output);
output.Unindent();
}
output.Unindent();
output.WriteLine("}");
}
}
}