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.
221 lines
7.1 KiB
221 lines
7.1 KiB
// 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.Threading; |
|
using Mono.Cecil; |
|
using Mono.Cecil.Cil; |
|
|
|
namespace ICSharpCode.Decompiler.Disassembler |
|
{ |
|
/// <summary> |
|
/// Disassembles a method body. |
|
/// </summary> |
|
public class MethodBodyDisassembler |
|
{ |
|
readonly ITextOutput output; |
|
readonly bool detectControlStructure; |
|
readonly CancellationToken cancellationToken; |
|
|
|
public MethodBodyDisassembler(ITextOutput output, bool detectControlStructure, CancellationToken cancellationToken) |
|
{ |
|
if (output == null) |
|
throw new ArgumentNullException(nameof(output)); |
|
this.output = output; |
|
this.detectControlStructure = detectControlStructure; |
|
this.cancellationToken = cancellationToken; |
|
} |
|
|
|
public virtual void Disassemble(MethodBody body) |
|
{ |
|
// start writing IL code |
|
MethodDefinition method = body.Method; |
|
output.WriteLine("// Method begins at RVA 0x{0:x4}", method.RVA); |
|
output.WriteLine("// Code size {0} (0x{0:x})", body.CodeSize); |
|
output.WriteLine(".maxstack {0}", body.MaxStackSize); |
|
if (method.DeclaringType.Module.Assembly != null && method.DeclaringType.Module.Assembly.EntryPoint == method) |
|
output.WriteLine(".entrypoint"); |
|
|
|
DisassembleLocalsBlock(body); |
|
output.WriteLine(); |
|
|
|
if (detectControlStructure && body.Instructions.Count > 0) { |
|
Instruction inst = body.Instructions[0]; |
|
HashSet<int> branchTargets = GetBranchTargets(body.Instructions); |
|
WriteStructureBody(new ILStructure(body), branchTargets, ref inst, method.Body.CodeSize); |
|
} else { |
|
foreach (var inst in method.Body.Instructions) { |
|
WriteInstruction(output, inst); |
|
output.WriteLine(); |
|
} |
|
WriteExceptionHandlers(body); |
|
} |
|
} |
|
|
|
private void DisassembleLocalsBlock(MethodBody body) |
|
{ |
|
if (body.HasVariables) { |
|
output.Write(".locals "); |
|
if (body.InitLocals) |
|
output.Write("init "); |
|
output.WriteLine("("); |
|
output.Indent(); |
|
foreach (var v in body.Variables) { |
|
output.WriteDefinition("[" + v.Index + "] ", v); |
|
v.VariableType.WriteTo(output); |
|
if (v.Index + 1 < body.Variables.Count) |
|
output.Write(','); |
|
output.WriteLine(); |
|
} |
|
output.Unindent(); |
|
output.WriteLine(")"); |
|
} |
|
} |
|
|
|
internal void WriteExceptionHandlers(MethodBody body) |
|
{ |
|
if (body.HasExceptionHandlers) { |
|
output.WriteLine(); |
|
foreach (var eh in body.ExceptionHandlers) { |
|
eh.WriteTo(output); |
|
output.WriteLine(); |
|
} |
|
} |
|
} |
|
|
|
HashSet<int> GetBranchTargets(IEnumerable<Instruction> instructions) |
|
{ |
|
HashSet<int> branchTargets = new HashSet<int>(); |
|
foreach (var inst in instructions) { |
|
Instruction target = inst.Operand as Instruction; |
|
if (target != null) |
|
branchTargets.Add(target.Offset); |
|
Instruction[] targets = inst.Operand as Instruction[]; |
|
if (targets != null) |
|
foreach (Instruction t in targets) |
|
branchTargets.Add(t.Offset); |
|
} |
|
return branchTargets; |
|
} |
|
|
|
void WriteStructureHeader(ILStructure s) |
|
{ |
|
switch (s.Type) { |
|
case ILStructureType.Loop: |
|
output.Write("// loop start"); |
|
if (s.LoopEntryPoint != null) { |
|
output.Write(" (head: "); |
|
DisassemblerHelpers.WriteOffsetReference(output, s.LoopEntryPoint); |
|
output.Write(')'); |
|
} |
|
output.WriteLine(); |
|
break; |
|
case ILStructureType.Try: |
|
output.WriteLine(".try"); |
|
output.WriteLine("{"); |
|
break; |
|
case ILStructureType.Handler: |
|
switch (s.ExceptionHandler.HandlerType) { |
|
case Mono.Cecil.Cil.ExceptionHandlerType.Catch: |
|
case Mono.Cecil.Cil.ExceptionHandlerType.Filter: |
|
output.Write("catch"); |
|
if (s.ExceptionHandler.CatchType != null) { |
|
output.Write(' '); |
|
s.ExceptionHandler.CatchType.WriteTo(output, ILNameSyntax.TypeName); |
|
} |
|
output.WriteLine(); |
|
break; |
|
case Mono.Cecil.Cil.ExceptionHandlerType.Finally: |
|
output.WriteLine("finally"); |
|
break; |
|
case Mono.Cecil.Cil.ExceptionHandlerType.Fault: |
|
output.WriteLine("fault"); |
|
break; |
|
default: |
|
throw new NotSupportedException(); |
|
} |
|
output.WriteLine("{"); |
|
break; |
|
case ILStructureType.Filter: |
|
output.WriteLine("filter"); |
|
output.WriteLine("{"); |
|
break; |
|
default: |
|
throw new NotSupportedException(); |
|
} |
|
output.Indent(); |
|
} |
|
|
|
void WriteStructureBody(ILStructure s, HashSet<int> branchTargets, ref Instruction inst, int codeSize) |
|
{ |
|
bool isFirstInstructionInStructure = true; |
|
bool prevInstructionWasBranch = false; |
|
int childIndex = 0; |
|
while (inst != null && inst.Offset < s.EndOffset) { |
|
int offset = inst.Offset; |
|
if (childIndex < s.Children.Count && s.Children[childIndex].StartOffset <= offset && offset < s.Children[childIndex].EndOffset) { |
|
ILStructure child = s.Children[childIndex++]; |
|
WriteStructureHeader(child); |
|
WriteStructureBody(child, branchTargets, ref inst, codeSize); |
|
WriteStructureFooter(child); |
|
} else { |
|
if (!isFirstInstructionInStructure && (prevInstructionWasBranch || branchTargets.Contains(offset))) { |
|
output.WriteLine(); // put an empty line after branches, and in front of branch targets |
|
} |
|
WriteInstruction(output, inst); |
|
output.WriteLine(); |
|
|
|
prevInstructionWasBranch = inst.OpCode.FlowControl == FlowControl.Branch |
|
|| inst.OpCode.FlowControl == FlowControl.Cond_Branch |
|
|| inst.OpCode.FlowControl == FlowControl.Return |
|
|| inst.OpCode.FlowControl == FlowControl.Throw; |
|
|
|
inst = inst.Next; |
|
} |
|
isFirstInstructionInStructure = false; |
|
} |
|
} |
|
|
|
void WriteStructureFooter(ILStructure s) |
|
{ |
|
output.Unindent(); |
|
switch (s.Type) { |
|
case ILStructureType.Loop: |
|
output.WriteLine("// end loop"); |
|
break; |
|
case ILStructureType.Try: |
|
output.WriteLine("} // end .try"); |
|
break; |
|
case ILStructureType.Handler: |
|
output.WriteLine("} // end handler"); |
|
break; |
|
case ILStructureType.Filter: |
|
output.WriteLine("} // end filter"); |
|
break; |
|
default: |
|
throw new NotSupportedException(); |
|
} |
|
} |
|
|
|
protected virtual void WriteInstruction(ITextOutput output, Instruction instruction) |
|
{ |
|
instruction.WriteTo(output); |
|
} |
|
} |
|
}
|
|
|