mirror of https://github.com/icsharpcode/ILSpy.git
5 changed files with 476 additions and 123 deletions
@ -0,0 +1,169 @@ |
|||||||
|
// 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:
|
||||||
|
// 4
|
||||||
|
// 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.Linq; |
||||||
|
using System.Threading; |
||||||
|
|
||||||
|
using ICSharpCode.Decompiler.FlowAnalysis; |
||||||
|
using Mono.Cecil; |
||||||
|
using Mono.Cecil.Cil; |
||||||
|
|
||||||
|
namespace ICSharpCode.ILSpy.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("output"); |
||||||
|
this.output = output; |
||||||
|
this.detectControlStructure = detectControlStructure; |
||||||
|
this.cancellationToken = cancellationToken; |
||||||
|
} |
||||||
|
|
||||||
|
public void Disassemble(MethodBody body) |
||||||
|
{ |
||||||
|
MethodDefinition method = body.Method; |
||||||
|
output.WriteCommentLine("// Method begins at RVA 0x{0:x4}", method.RVA); |
||||||
|
output.WriteCommentLine("// Code size {0} (0x{0:x})", body.CodeSize); |
||||||
|
output.WriteLine(".maxstack {0}", body.MaxStackSize); |
||||||
|
if (method.DeclaringType.Module.Assembly.EntryPoint == method) |
||||||
|
output.WriteLine (".entrypoint"); |
||||||
|
|
||||||
|
if (method.Body.HasVariables) { |
||||||
|
output.Write(".locals "); |
||||||
|
if (method.Body.InitLocals) |
||||||
|
output.Write("init "); |
||||||
|
output.WriteLine("("); |
||||||
|
output.Indent(); |
||||||
|
foreach (var v in method.Body.Variables) { |
||||||
|
output.WriteDefinition("[" + v.Index + "] ", v); |
||||||
|
v.VariableType.WriteTo(output); |
||||||
|
output.Write(' '); |
||||||
|
output.Write(DisassemblerHelpers.Escape(v.Name)); |
||||||
|
output.WriteLine(); |
||||||
|
} |
||||||
|
output.Unindent(); |
||||||
|
output.WriteLine(")"); |
||||||
|
} |
||||||
|
output.WriteLine(); |
||||||
|
|
||||||
|
if (detectControlStructure) { |
||||||
|
var cfg = ControlFlowGraphBuilder.Build(method.Body); |
||||||
|
cfg.ComputeDominance(cancellationToken); |
||||||
|
cfg.ComputeDominanceFrontier(); |
||||||
|
var s = ControlStructureDetector.DetectStructure(cfg, method.Body.ExceptionHandlers, cancellationToken); |
||||||
|
WriteStructure(s); |
||||||
|
} else { |
||||||
|
foreach (var inst in method.Body.Instructions) { |
||||||
|
inst.WriteTo(output); |
||||||
|
output.WriteLine(); |
||||||
|
} |
||||||
|
output.WriteLine(); |
||||||
|
foreach (var eh in method.Body.ExceptionHandlers) { |
||||||
|
eh.WriteTo(output); |
||||||
|
output.WriteLine(); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
void WriteStructure(ControlStructure s) |
||||||
|
{ |
||||||
|
if (s.Type != ControlStructureType.Root) { |
||||||
|
switch (s.Type) { |
||||||
|
case ControlStructureType.Loop: |
||||||
|
output.Write("// loop start"); |
||||||
|
if (s.EntryPoint.Start != null) { |
||||||
|
output.Write(" (head: "); |
||||||
|
DisassemblerHelpers.WriteOffsetReference(output, s.EntryPoint.Start); |
||||||
|
output.Write(')'); |
||||||
|
} |
||||||
|
output.WriteLine(); |
||||||
|
break; |
||||||
|
case ControlStructureType.Try: |
||||||
|
output.WriteLine(".try {"); |
||||||
|
break; |
||||||
|
case ControlStructureType.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); |
||||||
|
} |
||||||
|
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(); |
||||||
|
} |
||||||
|
break; |
||||||
|
case ControlStructureType.Filter: |
||||||
|
output.WriteLine("filter {"); |
||||||
|
break; |
||||||
|
default: |
||||||
|
throw new NotSupportedException(); |
||||||
|
} |
||||||
|
output.Indent(); |
||||||
|
} |
||||||
|
foreach (var node in s.Nodes.Concat(s.Children.Select(c => c.EntryPoint)).OrderBy(n => n.BlockIndex)) { |
||||||
|
if (s.Nodes.Contains(node)) { |
||||||
|
foreach (var inst in node.Instructions) { |
||||||
|
inst.WriteTo(output); |
||||||
|
output.WriteLine(); |
||||||
|
} |
||||||
|
} else { |
||||||
|
WriteStructure(s.Children.Single(c => c.EntryPoint == node)); |
||||||
|
} |
||||||
|
} |
||||||
|
if (s.Type != ControlStructureType.Root) { |
||||||
|
output.Unindent(); |
||||||
|
switch (s.Type) { |
||||||
|
case ControlStructureType.Loop: |
||||||
|
output.WriteCommentLine("// end loop"); |
||||||
|
break; |
||||||
|
case ControlStructureType.Try: |
||||||
|
output.WriteLine("} // end .try"); |
||||||
|
break; |
||||||
|
case ControlStructureType.Handler: |
||||||
|
output.WriteLine("} // end handler"); |
||||||
|
break; |
||||||
|
case ControlStructureType.Filter: |
||||||
|
output.WriteLine("} // end filter"); |
||||||
|
break; |
||||||
|
default: |
||||||
|
throw new NotSupportedException(); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
@ -0,0 +1,240 @@ |
|||||||
|
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
|
||||||
|
// This code is distributed under MIT X11 license (for details please see \doc\license.txt)
|
||||||
|
|
||||||
|
using System; |
||||||
|
using System.Collections.Generic; |
||||||
|
using System.Threading; |
||||||
|
|
||||||
|
using Mono.Cecil; |
||||||
|
using Mono.Collections.Generic; |
||||||
|
|
||||||
|
namespace ICSharpCode.ILSpy.Disassembler |
||||||
|
{ |
||||||
|
/// <summary>
|
||||||
|
/// Disassembles type and member definitions.
|
||||||
|
/// </summary>
|
||||||
|
public class ReflectionDisassembler |
||||||
|
{ |
||||||
|
ITextOutput output; |
||||||
|
CancellationToken cancellationToken; |
||||||
|
MethodBodyDisassembler methodBodyDisassembler; |
||||||
|
|
||||||
|
public ReflectionDisassembler(ITextOutput output, bool detectControlStructure, CancellationToken cancellationToken) |
||||||
|
{ |
||||||
|
if (output == null) |
||||||
|
throw new ArgumentNullException("output"); |
||||||
|
this.output = output; |
||||||
|
this.cancellationToken = cancellationToken; |
||||||
|
this.methodBodyDisassembler = new MethodBodyDisassembler(output, detectControlStructure, cancellationToken); |
||||||
|
} |
||||||
|
|
||||||
|
EnumNameCollection<MethodAttributes> methodAttributeFlags = new EnumNameCollection<MethodAttributes>() { |
||||||
|
{ MethodAttributes.Static, "static" }, |
||||||
|
{ MethodAttributes.Final, "final" }, |
||||||
|
{ MethodAttributes.Virtual, "virtual" }, |
||||||
|
{ MethodAttributes.HideBySig, "hidebysig" }, |
||||||
|
{ MethodAttributes.Abstract, "abstract" }, |
||||||
|
{ MethodAttributes.SpecialName, "specialname" }, |
||||||
|
{ MethodAttributes.PInvokeImpl, "pinvokeimpl" }, |
||||||
|
{ MethodAttributes.UnmanagedExport, "export" }, |
||||||
|
{ MethodAttributes.RTSpecialName, "rtspecialname" }, |
||||||
|
{ MethodAttributes.RequireSecObject, "requiresecobj" }, |
||||||
|
{ MethodAttributes.NewSlot, "newslot" } |
||||||
|
}; |
||||||
|
|
||||||
|
EnumNameCollection<MethodAttributes> methodVisibility = new EnumNameCollection<MethodAttributes>() { |
||||||
|
{ MethodAttributes.Private, "private" }, |
||||||
|
{ MethodAttributes.FamANDAssem, "famandassem" }, |
||||||
|
{ MethodAttributes.Assembly, "assembly" }, |
||||||
|
{ MethodAttributes.Family, "family" }, |
||||||
|
{ MethodAttributes.FamORAssem, "famorassem" }, |
||||||
|
{ MethodAttributes.Public, "public" }, |
||||||
|
}; |
||||||
|
|
||||||
|
EnumNameCollection<MethodCallingConvention> callingConvention = new EnumNameCollection<MethodCallingConvention>() { |
||||||
|
{ MethodCallingConvention.C, "unmanaged cdecl" }, |
||||||
|
{ MethodCallingConvention.StdCall, "unmanaged stdcall" }, |
||||||
|
{ MethodCallingConvention.ThisCall, "unmanaged thiscall" }, |
||||||
|
{ MethodCallingConvention.FastCall, "unmanaged fastcall" }, |
||||||
|
{ MethodCallingConvention.VarArg, "vararg" }, |
||||||
|
{ MethodCallingConvention.Generic, "generic" }, |
||||||
|
}; |
||||||
|
|
||||||
|
EnumNameCollection<MethodImplAttributes> methodCodeType = new EnumNameCollection<MethodImplAttributes>() { |
||||||
|
{ MethodImplAttributes.IL, "cil" }, |
||||||
|
{ MethodImplAttributes.Native, "native" }, |
||||||
|
{ MethodImplAttributes.OPTIL, "optil" }, |
||||||
|
{ MethodImplAttributes.Runtime, "runtime" }, |
||||||
|
}; |
||||||
|
|
||||||
|
EnumNameCollection<MethodImplAttributes> methodImpl = new EnumNameCollection<MethodImplAttributes>() { |
||||||
|
{ MethodImplAttributes.Synchronized, "synchronized" }, |
||||||
|
}; |
||||||
|
|
||||||
|
public void DisassembleMethod(MethodDefinition method) |
||||||
|
{ |
||||||
|
// .method public hidebysig specialname
|
||||||
|
// instance default class [mscorlib]System.IO.TextWriter get_BaseWriter () cil managed
|
||||||
|
//
|
||||||
|
|
||||||
|
// write method header
|
||||||
|
output.WriteDefinition(".method ", method); |
||||||
|
|
||||||
|
//emit flags
|
||||||
|
WriteEnum(method.Attributes & MethodAttributes.MemberAccessMask, methodVisibility); |
||||||
|
WriteFlags(method.Attributes & ~MethodAttributes.MemberAccessMask, methodAttributeFlags); |
||||||
|
|
||||||
|
output.WriteLine(); |
||||||
|
output.Indent(); |
||||||
|
|
||||||
|
if (method.HasThis) |
||||||
|
output.Write("instance "); |
||||||
|
|
||||||
|
//call convention
|
||||||
|
WriteEnum(method.CallingConvention & (MethodCallingConvention)0x1f, callingConvention); |
||||||
|
|
||||||
|
|
||||||
|
//return type
|
||||||
|
method.ReturnType.WriteTo(output, false, true); |
||||||
|
output.Write(' '); |
||||||
|
output.Write(DisassemblerHelpers.Escape(method.Name)); |
||||||
|
|
||||||
|
//( params )
|
||||||
|
output.Write(" ("); |
||||||
|
if (method.HasParameters) { |
||||||
|
output.WriteLine(); |
||||||
|
output.Indent(); |
||||||
|
WriteParameters(method.Parameters); |
||||||
|
output.Unindent(); |
||||||
|
} |
||||||
|
output.Write(") "); |
||||||
|
//cil managed
|
||||||
|
WriteEnum(method.ImplAttributes & MethodImplAttributes.CodeTypeMask, methodCodeType); |
||||||
|
if ((method.ImplAttributes & MethodImplAttributes.ManagedMask) == MethodImplAttributes.Managed) |
||||||
|
output.Write("managed "); |
||||||
|
else |
||||||
|
output.Write("unmanaged "); |
||||||
|
WriteFlags(method.ImplAttributes & ~(MethodImplAttributes.CodeTypeMask | MethodImplAttributes.ManagedMask), methodImpl); |
||||||
|
|
||||||
|
output.WriteLine(); |
||||||
|
output.Unindent(); |
||||||
|
OpenBlock(); |
||||||
|
WriteAttributes(method.CustomAttributes); |
||||||
|
|
||||||
|
if (method.HasBody) |
||||||
|
methodBodyDisassembler.Disassemble(method.Body); |
||||||
|
|
||||||
|
CloseBlock(); |
||||||
|
} |
||||||
|
|
||||||
|
void WriteParameters(Collection<ParameterDefinition> parameters) |
||||||
|
{ |
||||||
|
for (int i = 0; i < parameters.Count; i++) { |
||||||
|
var p = parameters[i]; |
||||||
|
p.ParameterType.WriteTo(output); |
||||||
|
output.Write(' '); |
||||||
|
output.WriteDefinition(DisassemblerHelpers.Escape(p.Name), p); |
||||||
|
if (i < parameters.Count - 1) |
||||||
|
output.Write(','); |
||||||
|
output.WriteLine(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
void WriteAttributes(Collection<CustomAttribute> attributes) |
||||||
|
{ |
||||||
|
foreach (CustomAttribute a in attributes) { |
||||||
|
output.Write(".custom"); |
||||||
|
a.Constructor.WriteTo(output); |
||||||
|
byte[] blob = a.GetBlob(); |
||||||
|
if (blob != null) { |
||||||
|
output.Write(" = "); |
||||||
|
WriteBlob(blob); |
||||||
|
} |
||||||
|
output.WriteLine(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
void WriteBlob(byte[] blob) |
||||||
|
{ |
||||||
|
output.Write("("); |
||||||
|
output.Indent(); |
||||||
|
|
||||||
|
for (int i = 0; i < blob.Length; i++) { |
||||||
|
if (i % 16 == 0 && i < blob.Length - 1) { |
||||||
|
output.WriteLine(); |
||||||
|
} else { |
||||||
|
output.Write(' '); |
||||||
|
} |
||||||
|
output.Write(blob[i].ToString("x2")); |
||||||
|
} |
||||||
|
|
||||||
|
output.WriteLine(); |
||||||
|
output.Unindent(); |
||||||
|
output.Write(")"); |
||||||
|
} |
||||||
|
|
||||||
|
void OpenBlock() |
||||||
|
{ |
||||||
|
output.WriteLine("{"); |
||||||
|
output.Indent(); |
||||||
|
} |
||||||
|
|
||||||
|
void CloseBlock() |
||||||
|
{ |
||||||
|
output.Unindent(); |
||||||
|
output.WriteLine("}"); |
||||||
|
} |
||||||
|
|
||||||
|
void WriteFlags<T>(T flags, EnumNameCollection<T> flagNames) where T : struct |
||||||
|
{ |
||||||
|
long val = Convert.ToInt64(flags); |
||||||
|
long tested = 0; |
||||||
|
foreach (var pair in flagNames) { |
||||||
|
tested |= pair.Key; |
||||||
|
if ((val & pair.Key) != 0) { |
||||||
|
output.Write(pair.Value); |
||||||
|
output.Write(' '); |
||||||
|
} |
||||||
|
} |
||||||
|
if ((val & ~tested) != 0) |
||||||
|
output.Write("flag({0}) ", val & ~tested); |
||||||
|
} |
||||||
|
|
||||||
|
void WriteEnum<T>(T enumValue, EnumNameCollection<T> enumNames) where T : struct |
||||||
|
{ |
||||||
|
long val = Convert.ToInt64(enumValue); |
||||||
|
foreach (var pair in enumNames) { |
||||||
|
if (pair.Key == val) { |
||||||
|
output.Write(pair.Value); |
||||||
|
output.Write(' '); |
||||||
|
return; |
||||||
|
} |
||||||
|
} |
||||||
|
if (val != 0) { |
||||||
|
output.Write("flag({0})", val); |
||||||
|
output.Write(' '); |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
sealed class EnumNameCollection<T> : IEnumerable<KeyValuePair<long, string>> where T : struct |
||||||
|
{ |
||||||
|
List<KeyValuePair<long, string>> names = new List<KeyValuePair<long, string>>(); |
||||||
|
|
||||||
|
public void Add(T flag, string name) |
||||||
|
{ |
||||||
|
this.names.Add(new KeyValuePair<long, string>(Convert.ToInt64(flag), name)); |
||||||
|
} |
||||||
|
|
||||||
|
public IEnumerator<KeyValuePair<long, string>> GetEnumerator() |
||||||
|
{ |
||||||
|
return names.GetEnumerator(); |
||||||
|
} |
||||||
|
|
||||||
|
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() |
||||||
|
{ |
||||||
|
return names.GetEnumerator(); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
Loading…
Reference in new issue