Browse Source

Add disassembling support for the method header.

pull/1/head
Daniel Grunwald 15 years ago
parent
commit
1b8a68d73d
  1. 71
      ILSpy/Disassembler/DisassemblerHelpers.cs
  2. 117
      ILSpy/Disassembler/ILLanguage.cs
  3. 169
      ILSpy/Disassembler/MethodBodyDisassembler.cs
  4. 240
      ILSpy/Disassembler/ReflectionDisassembler.cs
  5. 2
      ILSpy/ILSpy.csproj

71
ILSpy/Disassembler/DisassemblerHelpers.cs

@ -17,6 +17,7 @@ @@ -17,6 +17,7 @@
// DEALINGS IN THE SOFTWARE.
using System;
using System.Linq;
using ICSharpCode.Decompiler;
using Mono.Cecil;
using Mono.Cecil.Cil;
@ -83,7 +84,7 @@ namespace ICSharpCode.ILSpy.Disassembler @@ -83,7 +84,7 @@ namespace ICSharpCode.ILSpy.Disassembler
: value.ToString();
}
static void WriteTo(this MethodReference method, ITextOutput writer)
public static void WriteTo(this MethodReference method, ITextOutput writer)
{
method.ReturnType.WriteTo(writer);
writer.Write(' ');
@ -108,13 +109,69 @@ namespace ICSharpCode.ILSpy.Disassembler @@ -108,13 +109,69 @@ namespace ICSharpCode.ILSpy.Disassembler
writer.WriteReference(field.Name, field);
}
public static void WriteTo(this TypeReference type, ITextOutput writer)
public static string Escape(string identifier)
{
string name = ShortTypeName(type);
if (name != null)
writer.Write(name);
else
writer.WriteReference(type.FullName, type);
return identifier;
}
public static void WriteTo(this TypeReference type, ITextOutput writer, bool onlyName = false, bool convertPrimitive = true)
{
if (type is PinnedType) {
writer.Write("pinned ");
type.GetElementType().WriteTo(writer, onlyName, convertPrimitive);
} else if (type is ArrayType) {
ArrayType at = (ArrayType)type;
at.ElementType.WriteTo(writer, onlyName, convertPrimitive);
writer.Write('[');
writer.Write(string.Join(", ", at.Dimensions));
writer.Write(']');
} else if (type is GenericParameter) {
writer.WriteReference(type.Name, type);
} else if (type is ByReferenceType) {
type.GetElementType().WriteTo(writer, onlyName, convertPrimitive);
writer.Write('&');
} else if (type is PointerType) {
type.GetElementType().WriteTo(writer, onlyName, convertPrimitive);
writer.Write('*');
} else if (type is GenericInstanceType) {
type.GetElementType().WriteTo(writer, onlyName, convertPrimitive);
writer.Write('<');
var arguments = ((GenericInstanceType)type).GenericArguments;
for (int i = 0; i < arguments.Count; i++) {
if (i > 0)
writer.Write(", ");
arguments[i].WriteTo(writer, onlyName, convertPrimitive);
}
writer.Write('>');
} else if (type is OptionalModifierType) {
writer.Write("modopt(");
((OptionalModifierType)type).ModifierType.WriteTo(writer, true, false);
writer.Write(") ");
type.GetElementType().WriteTo(writer, onlyName, convertPrimitive);
} else if (type is RequiredModifierType) {
writer.Write("modreq(");
((RequiredModifierType)type).ModifierType.WriteTo(writer, true, false);
writer.Write(") ");
type.GetElementType().WriteTo(writer, onlyName, convertPrimitive);
} else {
if (!onlyName)
writer.Write(type.IsValueType ? "valuetype " : "class ");
if (type.DeclaringType != null) {
type.DeclaringType.WriteTo(writer, true, false);
writer.Write('/');
writer.WriteReference(Escape(type.Name), type);
} else {
string name = ShortTypeName(type);
if (name != null) {
writer.Write(name);
} else {
if (!type.IsDefinition && type.Scope != null && !(type is TypeSpecification))
writer.Write("[{0}]", Escape(type.Scope.Name));
writer.WriteReference(type.FullName, type);
}
}
}
}
public static void WriteOperand(ITextOutput writer, object operand)

117
ILSpy/Disassembler/ILLanguage.cs

@ -43,122 +43,7 @@ namespace ICSharpCode.ILSpy.Disassembler @@ -43,122 +43,7 @@ namespace ICSharpCode.ILSpy.Disassembler
public override void Decompile(MethodDefinition method, ITextOutput output, CancellationToken cancellationToken)
{
output.WriteCommentLine("// Method begins at RVA 0x{0:x4}", method.RVA);
output.WriteCommentLine("// Code size {0} (0x{0:x})", method.Body.CodeSize);
output.WriteLine(".maxstack {0}", method.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) {
v.VariableType.WriteTo(output);
output.Write(' ');
output.WriteDefinition(string.IsNullOrEmpty(v.Name) ? v.Index.ToString() : v.Name, v);
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(output, 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(ITextOutput output, 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(output, 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();
}
}
new ReflectionDisassembler(output, detectControlStructure, cancellationToken).DisassembleMethod(method);
}
}
}

169
ILSpy/Disassembler/MethodBodyDisassembler.cs

@ -0,0 +1,169 @@ @@ -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();
}
}
}
}
}

240
ILSpy/Disassembler/ReflectionDisassembler.cs

@ -0,0 +1,240 @@ @@ -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();
}
}
}
}

2
ILSpy/ILSpy.csproj

@ -89,6 +89,8 @@ @@ -89,6 +89,8 @@
<Compile Include="Decompiler\CSharpLanguage.cs" />
<Compile Include="Disassembler\DisassemblerHelpers.cs" />
<Compile Include="Disassembler\ILLanguage.cs" />
<Compile Include="Disassembler\MethodBodyDisassembler.cs" />
<Compile Include="Disassembler\ReflectionDisassembler.cs" />
<Compile Include="EventTreeNode.cs" />
<Compile Include="ExtensionMethods.cs" />
<Compile Include="FieldTreeNode.cs" />

Loading…
Cancel
Save