.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.
 
 
 
 

1181 lines
36 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.Collections.Generic;
namespace ICSharpCode.Decompiler.Disassembler
{
/// <summary>
/// Disassembles type and member definitions.
/// </summary>
public sealed class ReflectionDisassembler
{
readonly ITextOutput output;
CancellationToken cancellationToken;
bool isInType; // whether we are currently disassembling a whole type (-> defaultCollapsed for foldings)
MethodBodyDisassembler methodBodyDisassembler;
MemberReference currentMember;
public bool DetectControlStructure {
get => methodBodyDisassembler.DetectControlStructure;
set => methodBodyDisassembler.DetectControlStructure = value;
}
public bool ShowSequencePoints {
get => methodBodyDisassembler.ShowSequencePoints;
set => methodBodyDisassembler.ShowSequencePoints = value;
}
public bool ExpandMemberDefinitions { get; set; } = false;
public ReflectionDisassembler(ITextOutput output, CancellationToken cancellationToken)
: this(output, new MethodBodyDisassembler(output, cancellationToken), cancellationToken)
{
}
public ReflectionDisassembler(ITextOutput output, MethodBodyDisassembler methodBodyDisassembler, CancellationToken cancellationToken)
{
if (output == null)
throw new ArgumentNullException(nameof(output));
this.output = output;
this.cancellationToken = cancellationToken;
this.methodBodyDisassembler = methodBodyDisassembler;
}
#region Disassemble Method
EnumNameCollection<MethodAttributes> methodAttributeFlags = new EnumNameCollection<MethodAttributes>() {
{ MethodAttributes.Final, "final" },
{ MethodAttributes.HideBySig, "hidebysig" },
{ MethodAttributes.SpecialName, "specialname" },
{ MethodAttributes.PInvokeImpl, null }, // handled separately
{ MethodAttributes.UnmanagedExport, "export" },
{ MethodAttributes.RTSpecialName, "rtspecialname" },
{ MethodAttributes.RequireSecObject, "reqsecobj" },
{ MethodAttributes.NewSlot, "newslot" },
{ MethodAttributes.CheckAccessOnOverride, "strict" },
{ MethodAttributes.Abstract, "abstract" },
{ MethodAttributes.Virtual, "virtual" },
{ MethodAttributes.Static, "static" },
{ MethodAttributes.HasSecurity, null }, // ?? also invisible in ILDasm
};
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, null },
};
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" },
{ MethodImplAttributes.NoInlining, "noinlining" },
{ MethodImplAttributes.NoOptimization, "nooptimization" },
{ MethodImplAttributes.PreserveSig, "preservesig" },
{ MethodImplAttributes.InternalCall, "internalcall" },
{ MethodImplAttributes.ForwardRef, "forwardref" },
};
public void DisassembleMethod(MethodDefinition method)
{
DisassembleMethodHeader(method);
DisassembleMethodBlock(method);
}
public void DisassembleMethodHeader(MethodDefinition method)
{
// set current member
currentMember = method;
// write method header
output.WriteDefinition(".method ", method);
DisassembleMethodHeaderInternal(method);
}
void DisassembleMethodHeaderInternal(MethodDefinition method)
{
// .method public hidebysig specialname
// instance default class [mscorlib]System.IO.TextWriter get_BaseWriter () cil managed
//
//emit flags
WriteEnum(method.Attributes & MethodAttributes.MemberAccessMask, methodVisibility);
WriteFlags(method.Attributes & ~MethodAttributes.MemberAccessMask, methodAttributeFlags);
if (method.IsCompilerControlled) output.Write("privatescope ");
if ((method.Attributes & MethodAttributes.PInvokeImpl) == MethodAttributes.PInvokeImpl) {
output.Write("pinvokeimpl");
if (method.HasPInvokeInfo && method.PInvokeInfo != null) {
PInvokeInfo info = method.PInvokeInfo;
output.Write("(\"" + DisassemblerHelpers.EscapeString(info.Module.Name) + "\"");
if (!string.IsNullOrEmpty(info.EntryPoint) && info.EntryPoint != method.Name)
output.Write(" as \"" + DisassemblerHelpers.EscapeString(info.EntryPoint) + "\"");
if (info.IsNoMangle)
output.Write(" nomangle");
if (info.IsCharSetAnsi)
output.Write(" ansi");
else if (info.IsCharSetAuto)
output.Write(" autochar");
else if (info.IsCharSetUnicode)
output.Write(" unicode");
if (info.SupportsLastError)
output.Write(" lasterr");
if (info.IsCallConvCdecl)
output.Write(" cdecl");
else if (info.IsCallConvFastcall)
output.Write(" fastcall");
else if (info.IsCallConvStdCall)
output.Write(" stdcall");
else if (info.IsCallConvThiscall)
output.Write(" thiscall");
else if (info.IsCallConvWinapi)
output.Write(" winapi");
output.Write(')');
}
output.Write(' ');
}
output.WriteLine();
output.Indent();
if (method.ExplicitThis) {
output.Write("instance explicit ");
} else if (method.HasThis) {
output.Write("instance ");
}
//call convention
WriteEnum(method.CallingConvention & (MethodCallingConvention)0x1f, callingConvention);
//return type
method.ReturnType.WriteTo(output);
output.Write(' ');
if (method.MethodReturnType.HasMarshalInfo) {
WriteMarshalInfo(method.MethodReturnType.MarshalInfo);
}
if (method.IsCompilerControlled) {
output.Write(DisassemblerHelpers.Escape(method.Name + "$PST" + method.MetadataToken.ToInt32().ToString("X8")));
} else {
output.Write(DisassemblerHelpers.Escape(method.Name));
}
WriteTypeParameters(output, method);
//( 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.Unindent();
}
void DisassembleMethodBlock(MethodDefinition method)
{
OpenBlock(defaultCollapsed: isInType);
WriteAttributes(method.CustomAttributes);
if (method.HasOverrides) {
foreach (var methodOverride in method.Overrides) {
output.Write(".override method ");
methodOverride.WriteTo(output);
output.WriteLine();
}
}
WriteParameterAttributes(0, method.MethodReturnType, method.MethodReturnType);
foreach (var p in method.Parameters) {
WriteParameterAttributes(p.Index + 1, p, p);
}
WriteSecurityDeclarations(method);
if (method.HasBody) {
methodBodyDisassembler.Disassemble(method.Body);
}
CloseBlock("end of method " + DisassemblerHelpers.Escape(method.DeclaringType.Name) + "::" + DisassemblerHelpers.Escape(method.Name));
}
#region Write Security Declarations
void WriteSecurityDeclarations(ISecurityDeclarationProvider secDeclProvider)
{
if (!secDeclProvider.HasSecurityDeclarations)
return;
foreach (var secdecl in secDeclProvider.SecurityDeclarations) {
output.Write(".permissionset ");
switch (secdecl.Action) {
case SecurityAction.Request:
output.Write("request");
break;
case SecurityAction.Demand:
output.Write("demand");
break;
case SecurityAction.Assert:
output.Write("assert");
break;
case SecurityAction.Deny:
output.Write("deny");
break;
case SecurityAction.PermitOnly:
output.Write("permitonly");
break;
case SecurityAction.LinkDemand:
output.Write("linkcheck");
break;
case SecurityAction.InheritDemand:
output.Write("inheritcheck");
break;
case SecurityAction.RequestMinimum:
output.Write("reqmin");
break;
case SecurityAction.RequestOptional:
output.Write("reqopt");
break;
case SecurityAction.RequestRefuse:
output.Write("reqrefuse");
break;
case SecurityAction.PreJitGrant:
output.Write("prejitgrant");
break;
case SecurityAction.PreJitDeny:
output.Write("prejitdeny");
break;
case SecurityAction.NonCasDemand:
output.Write("noncasdemand");
break;
case SecurityAction.NonCasLinkDemand:
output.Write("noncaslinkdemand");
break;
case SecurityAction.NonCasInheritance:
output.Write("noncasinheritance");
break;
default:
output.Write(secdecl.Action.ToString());
break;
}
output.WriteLine(" = {");
output.Indent();
for (int i = 0; i < secdecl.SecurityAttributes.Count; i++) {
SecurityAttribute sa = secdecl.SecurityAttributes[i];
if (sa.AttributeType.Scope == sa.AttributeType.Module) {
output.Write("class ");
output.Write(DisassemblerHelpers.Escape(GetAssemblyQualifiedName(sa.AttributeType)));
} else {
sa.AttributeType.WriteTo(output, ILNameSyntax.TypeName);
}
output.Write(" = {");
if (sa.HasFields || sa.HasProperties) {
output.WriteLine();
output.Indent();
foreach (CustomAttributeNamedArgument na in sa.Fields) {
output.Write("field ");
WriteSecurityDeclarationArgument(na);
output.WriteLine();
}
foreach (CustomAttributeNamedArgument na in sa.Properties) {
output.Write("property ");
WriteSecurityDeclarationArgument(na);
output.WriteLine();
}
output.Unindent();
}
output.Write('}');
if (i + 1 < secdecl.SecurityAttributes.Count)
output.Write(',');
output.WriteLine();
}
output.Unindent();
output.WriteLine("}");
}
}
void WriteSecurityDeclarationArgument(CustomAttributeNamedArgument na)
{
TypeReference type = na.Argument.Type;
if (type.MetadataType == MetadataType.Class || type.MetadataType == MetadataType.ValueType) {
output.Write("enum ");
if (type.Scope != type.Module) {
output.Write("class ");
output.Write(DisassemblerHelpers.Escape(GetAssemblyQualifiedName(type)));
} else {
type.WriteTo(output, ILNameSyntax.TypeName);
}
} else {
type.WriteTo(output);
}
output.Write(' ');
output.Write(DisassemblerHelpers.Escape(na.Name));
output.Write(" = ");
if (na.Argument.Value is string) {
// secdecls use special syntax for strings
output.Write("string('{0}')", DisassemblerHelpers.EscapeString((string)na.Argument.Value).Replace("'", "\'"));
} else {
WriteConstant(na.Argument.Value);
}
}
string GetAssemblyQualifiedName(TypeReference type)
{
AssemblyNameReference anr = type.Scope as AssemblyNameReference;
if (anr == null) {
ModuleDefinition md = type.Scope as ModuleDefinition;
if (md != null) {
anr = md.Assembly.Name;
}
}
if (anr != null) {
return type.FullName + ", " + anr.FullName;
} else {
return type.FullName;
}
}
#endregion
#region WriteMarshalInfo
void WriteMarshalInfo(MarshalInfo marshalInfo)
{
output.Write("marshal(");
WriteNativeType(marshalInfo.NativeType, marshalInfo);
output.Write(") ");
}
void WriteNativeType(NativeType nativeType, MarshalInfo marshalInfo = null)
{
switch (nativeType) {
case NativeType.None:
break;
case NativeType.Boolean:
output.Write("bool");
break;
case NativeType.I1:
output.Write("int8");
break;
case NativeType.U1:
output.Write("unsigned int8");
break;
case NativeType.I2:
output.Write("int16");
break;
case NativeType.U2:
output.Write("unsigned int16");
break;
case NativeType.I4:
output.Write("int32");
break;
case NativeType.U4:
output.Write("unsigned int32");
break;
case NativeType.I8:
output.Write("int64");
break;
case NativeType.U8:
output.Write("unsigned int64");
break;
case NativeType.R4:
output.Write("float32");
break;
case NativeType.R8:
output.Write("float64");
break;
case NativeType.LPStr:
output.Write("lpstr");
break;
case NativeType.Int:
output.Write("int");
break;
case NativeType.UInt:
output.Write("unsigned int");
break;
case NativeType.Func:
goto default; // ??
case NativeType.Array:
ArrayMarshalInfo ami = (ArrayMarshalInfo)marshalInfo;
if (ami == null)
goto default;
if (ami.ElementType != NativeType.Max)
WriteNativeType(ami.ElementType);
output.Write('[');
if (ami.Size >= 0) {
output.Write(ami.Size.ToString());
}
if (ami.SizeParameterMultiplier != 0 && ami.SizeParameterIndex >= 0) {
output.Write(" + ");
output.Write(ami.SizeParameterIndex.ToString());
}
output.Write(']');
break;
case NativeType.Currency:
output.Write("currency");
break;
case NativeType.BStr:
output.Write("bstr");
break;
case NativeType.LPWStr:
output.Write("lpwstr");
break;
case NativeType.LPTStr:
output.Write("lptstr");
break;
case NativeType.FixedSysString:
output.Write("fixed sysstring[{0}]", ((FixedSysStringMarshalInfo)marshalInfo).Size);
break;
case NativeType.IUnknown:
output.Write("iunknown");
break;
case NativeType.IDispatch:
output.Write("idispatch");
break;
case NativeType.Struct:
output.Write("struct");
break;
case NativeType.IntF:
output.Write("interface");
break;
case NativeType.SafeArray:
output.Write("safearray ");
SafeArrayMarshalInfo sami = marshalInfo as SafeArrayMarshalInfo;
if (sami != null) {
switch (sami.ElementType) {
case VariantType.None:
break;
case VariantType.I2:
output.Write("int16");
break;
case VariantType.I4:
output.Write("int32");
break;
case VariantType.R4:
output.Write("float32");
break;
case VariantType.R8:
output.Write("float64");
break;
case VariantType.CY:
output.Write("currency");
break;
case VariantType.Date:
output.Write("date");
break;
case VariantType.BStr:
output.Write("bstr");
break;
case VariantType.Dispatch:
output.Write("idispatch");
break;
case VariantType.Error:
output.Write("error");
break;
case VariantType.Bool:
output.Write("bool");
break;
case VariantType.Variant:
output.Write("variant");
break;
case VariantType.Unknown:
output.Write("iunknown");
break;
case VariantType.Decimal:
output.Write("decimal");
break;
case VariantType.I1:
output.Write("int8");
break;
case VariantType.UI1:
output.Write("unsigned int8");
break;
case VariantType.UI2:
output.Write("unsigned int16");
break;
case VariantType.UI4:
output.Write("unsigned int32");
break;
case VariantType.Int:
output.Write("int");
break;
case VariantType.UInt:
output.Write("unsigned int");
break;
default:
output.Write(sami.ElementType.ToString());
break;
}
}
break;
case NativeType.FixedArray:
output.Write("fixed array");
FixedArrayMarshalInfo fami = marshalInfo as FixedArrayMarshalInfo;
if (fami != null) {
output.Write("[{0}]", fami.Size);
if (fami.ElementType != NativeType.None) {
output.Write(' ');
WriteNativeType(fami.ElementType);
}
}
break;
case NativeType.ByValStr:
output.Write("byvalstr");
break;
case NativeType.ANSIBStr:
output.Write("ansi bstr");
break;
case NativeType.TBStr:
output.Write("tbstr");
break;
case NativeType.VariantBool:
output.Write("variant bool");
break;
case NativeType.ASAny:
output.Write("as any");
break;
case NativeType.LPStruct:
output.Write("lpstruct");
break;
case NativeType.CustomMarshaler:
CustomMarshalInfo cmi = marshalInfo as CustomMarshalInfo;
if (cmi == null)
goto default;
output.Write("custom(\"{0}\", \"{1}\"",
DisassemblerHelpers.EscapeString(cmi.ManagedType.FullName),
DisassemblerHelpers.EscapeString(cmi.Cookie));
if (cmi.Guid != Guid.Empty || !string.IsNullOrEmpty(cmi.UnmanagedType)) {
output.Write(", \"{0}\", \"{1}\"", cmi.Guid.ToString(), DisassemblerHelpers.EscapeString(cmi.UnmanagedType));
}
output.Write(')');
break;
case NativeType.Error:
output.Write("error");
break;
default:
output.Write(nativeType.ToString());
break;
}
}
#endregion
void WriteParameters(Collection<ParameterDefinition> parameters)
{
for (int i = 0; i < parameters.Count; i++) {
var p = parameters[i];
if (p.IsIn)
output.Write("[in] ");
if (p.IsOut)
output.Write("[out] ");
if (p.IsOptional)
output.Write("[opt] ");
p.ParameterType.WriteTo(output);
output.Write(' ');
if (p.HasMarshalInfo) {
WriteMarshalInfo(p.MarshalInfo);
}
output.WriteDefinition(DisassemblerHelpers.Escape(p.Name), p);
if (i < parameters.Count - 1)
output.Write(',');
output.WriteLine();
}
}
void WriteParameterAttributes(int index, IConstantProvider cp, ICustomAttributeProvider cap)
{
if (!cp.HasConstant && !cap.HasCustomAttributes)
return;
output.Write(".param [{0}]", index);
if (cp.HasConstant) {
output.Write(" = ");
WriteConstant(cp.Constant);
}
output.WriteLine();
WriteAttributes(cap.CustomAttributes);
}
void WriteConstant(object constant)
{
if (constant == null) {
output.Write("nullref");
} else {
string typeName = DisassemblerHelpers.PrimitiveTypeName(constant.GetType().FullName);
if (typeName != null && typeName != "string") {
output.Write(typeName);
output.Write('(');
float? cf = constant as float?;
double? cd = constant as double?;
if (cf.HasValue && (float.IsNaN(cf.Value) || float.IsInfinity(cf.Value))) {
output.Write("0x{0:x8}", BitConverter.ToInt32(BitConverter.GetBytes(cf.Value), 0));
} else if (cd.HasValue && (double.IsNaN(cd.Value) || double.IsInfinity(cd.Value))) {
output.Write("0x{0:x16}", BitConverter.DoubleToInt64Bits(cd.Value));
} else {
DisassemblerHelpers.WriteOperand(output, constant);
}
output.Write(')');
} else {
DisassemblerHelpers.WriteOperand(output, constant);
}
}
}
#endregion
#region Disassemble Field
EnumNameCollection<FieldAttributes> fieldVisibility = new EnumNameCollection<FieldAttributes>() {
{ FieldAttributes.Private, "private" },
{ FieldAttributes.FamANDAssem, "famandassem" },
{ FieldAttributes.Assembly, "assembly" },
{ FieldAttributes.Family, "family" },
{ FieldAttributes.FamORAssem, "famorassem" },
{ FieldAttributes.Public, "public" },
};
EnumNameCollection<FieldAttributes> fieldAttributes = new EnumNameCollection<FieldAttributes>() {
{ FieldAttributes.Static, "static" },
{ FieldAttributes.Literal, "literal" },
{ FieldAttributes.InitOnly, "initonly" },
{ FieldAttributes.SpecialName, "specialname" },
{ FieldAttributes.RTSpecialName, "rtspecialname" },
{ FieldAttributes.NotSerialized, "notserialized" },
};
public void DisassembleField(FieldDefinition field)
{
output.WriteDefinition(".field ", field);
if (field.HasLayoutInfo) {
output.Write("[" + field.Offset + "] ");
}
WriteEnum(field.Attributes & FieldAttributes.FieldAccessMask, fieldVisibility);
const FieldAttributes hasXAttributes = FieldAttributes.HasDefault | FieldAttributes.HasFieldMarshal | FieldAttributes.HasFieldRVA;
WriteFlags(field.Attributes & ~(FieldAttributes.FieldAccessMask | hasXAttributes), fieldAttributes);
if (field.HasMarshalInfo) {
WriteMarshalInfo(field.MarshalInfo);
}
field.FieldType.WriteTo(output);
output.Write(' ');
output.Write(DisassemblerHelpers.Escape(field.Name));
if ((field.Attributes & FieldAttributes.HasFieldRVA) == FieldAttributes.HasFieldRVA) {
output.Write(" at I_{0:x8}", field.RVA);
}
if (field.HasConstant) {
output.Write(" = ");
WriteConstant(field.Constant);
}
output.WriteLine();
if (field.HasCustomAttributes) {
output.MarkFoldStart();
WriteAttributes(field.CustomAttributes);
output.MarkFoldEnd();
}
}
#endregion
#region Disassemble Property
EnumNameCollection<PropertyAttributes> propertyAttributes = new EnumNameCollection<PropertyAttributes>() {
{ PropertyAttributes.SpecialName, "specialname" },
{ PropertyAttributes.RTSpecialName, "rtspecialname" },
{ PropertyAttributes.HasDefault, "hasdefault" },
};
public void DisassembleProperty(PropertyDefinition property)
{
// set current member
currentMember = property;
output.WriteDefinition(".property ", property);
WriteFlags(property.Attributes, propertyAttributes);
if (property.HasThis)
output.Write("instance ");
property.PropertyType.WriteTo(output);
output.Write(' ');
output.Write(DisassemblerHelpers.Escape(property.Name));
output.Write("(");
if (property.HasParameters) {
output.WriteLine();
output.Indent();
WriteParameters(property.Parameters);
output.Unindent();
}
output.Write(")");
OpenBlock(false);
WriteAttributes(property.CustomAttributes);
WriteNestedMethod(".get", property.GetMethod);
WriteNestedMethod(".set", property.SetMethod);
foreach (var method in property.OtherMethods) {
WriteNestedMethod(".other", method);
}
CloseBlock();
}
void WriteNestedMethod(string keyword, MethodDefinition method)
{
if (method == null)
return;
output.Write(keyword);
output.Write(' ');
method.WriteTo(output);
output.WriteLine();
}
#endregion
#region Disassemble Event
EnumNameCollection<EventAttributes> eventAttributes = new EnumNameCollection<EventAttributes>() {
{ EventAttributes.SpecialName, "specialname" },
{ EventAttributes.RTSpecialName, "rtspecialname" },
};
public void DisassembleEvent(EventDefinition ev)
{
// set current member
currentMember = ev;
output.WriteDefinition(".event ", ev);
WriteFlags(ev.Attributes, eventAttributes);
ev.EventType.WriteTo(output, ILNameSyntax.TypeName);
output.Write(' ');
output.Write(DisassemblerHelpers.Escape(ev.Name));
OpenBlock(false);
WriteAttributes(ev.CustomAttributes);
WriteNestedMethod(".addon", ev.AddMethod);
WriteNestedMethod(".removeon", ev.RemoveMethod);
WriteNestedMethod(".fire", ev.InvokeMethod);
foreach (var method in ev.OtherMethods) {
WriteNestedMethod(".other", method);
}
CloseBlock();
}
#endregion
#region Disassemble Type
EnumNameCollection<TypeAttributes> typeVisibility = new EnumNameCollection<TypeAttributes>() {
{ TypeAttributes.Public, "public" },
{ TypeAttributes.NotPublic, "private" },
{ TypeAttributes.NestedPublic, "nested public" },
{ TypeAttributes.NestedPrivate, "nested private" },
{ TypeAttributes.NestedAssembly, "nested assembly" },
{ TypeAttributes.NestedFamily, "nested family" },
{ TypeAttributes.NestedFamANDAssem, "nested famandassem" },
{ TypeAttributes.NestedFamORAssem, "nested famorassem" },
};
EnumNameCollection<TypeAttributes> typeLayout = new EnumNameCollection<TypeAttributes>() {
{ TypeAttributes.AutoLayout, "auto" },
{ TypeAttributes.SequentialLayout, "sequential" },
{ TypeAttributes.ExplicitLayout, "explicit" },
};
EnumNameCollection<TypeAttributes> typeStringFormat = new EnumNameCollection<TypeAttributes>() {
{ TypeAttributes.AutoClass, "auto" },
{ TypeAttributes.AnsiClass, "ansi" },
{ TypeAttributes.UnicodeClass, "unicode" },
};
EnumNameCollection<TypeAttributes> typeAttributes = new EnumNameCollection<TypeAttributes>() {
{ TypeAttributes.Abstract, "abstract" },
{ TypeAttributes.Sealed, "sealed" },
{ TypeAttributes.SpecialName, "specialname" },
{ TypeAttributes.Import, "import" },
{ TypeAttributes.Serializable, "serializable" },
{ TypeAttributes.WindowsRuntime, "windowsruntime" },
{ TypeAttributes.BeforeFieldInit, "beforefieldinit" },
{ TypeAttributes.HasSecurity, null },
};
public void DisassembleType(TypeDefinition type)
{
// start writing IL
output.WriteDefinition(".class ", type);
if ((type.Attributes & TypeAttributes.ClassSemanticMask) == TypeAttributes.Interface)
output.Write("interface ");
WriteEnum(type.Attributes & TypeAttributes.VisibilityMask, typeVisibility);
WriteEnum(type.Attributes & TypeAttributes.LayoutMask, typeLayout);
WriteEnum(type.Attributes & TypeAttributes.StringFormatMask, typeStringFormat);
const TypeAttributes masks = TypeAttributes.ClassSemanticMask | TypeAttributes.VisibilityMask | TypeAttributes.LayoutMask | TypeAttributes.StringFormatMask;
WriteFlags(type.Attributes & ~masks, typeAttributes);
output.Write(DisassemblerHelpers.Escape(type.DeclaringType != null ? type.Name : type.FullName));
WriteTypeParameters(output, type);
output.MarkFoldStart(defaultCollapsed: !ExpandMemberDefinitions && isInType);
output.WriteLine();
if (type.BaseType != null) {
output.Indent();
output.Write("extends ");
type.BaseType.WriteTo(output, ILNameSyntax.TypeName);
output.WriteLine();
output.Unindent();
}
if (type.HasInterfaces) {
output.Indent();
for (int index = 0; index < type.Interfaces.Count; index++) {
if (index > 0)
output.WriteLine(",");
if (index == 0)
output.Write("implements ");
else
output.Write(" ");
var iface = type.Interfaces[index];
WriteAttributes(iface.CustomAttributes);
iface.InterfaceType.WriteTo(output, ILNameSyntax.TypeName);
}
output.WriteLine();
output.Unindent();
}
output.WriteLine("{");
output.Indent();
bool oldIsInType = isInType;
isInType = true;
WriteAttributes(type.CustomAttributes);
WriteSecurityDeclarations(type);
if (type.HasLayoutInfo) {
output.WriteLine(".pack {0}", type.PackingSize);
output.WriteLine(".size {0}", type.ClassSize);
output.WriteLine();
}
if (type.HasNestedTypes) {
output.WriteLine("// Nested Types");
foreach (var nestedType in type.NestedTypes) {
cancellationToken.ThrowIfCancellationRequested();
DisassembleType(nestedType);
output.WriteLine();
}
output.WriteLine();
}
if (type.HasFields) {
output.WriteLine("// Fields");
foreach (var field in type.Fields) {
cancellationToken.ThrowIfCancellationRequested();
DisassembleField(field);
}
output.WriteLine();
}
if (type.HasMethods) {
output.WriteLine("// Methods");
foreach (var m in type.Methods) {
cancellationToken.ThrowIfCancellationRequested();
DisassembleMethod(m);
output.WriteLine();
}
}
if (type.HasEvents) {
output.WriteLine("// Events");
foreach (var ev in type.Events) {
cancellationToken.ThrowIfCancellationRequested();
DisassembleEvent(ev);
output.WriteLine();
}
output.WriteLine();
}
if (type.HasProperties) {
output.WriteLine("// Properties");
foreach (var prop in type.Properties) {
cancellationToken.ThrowIfCancellationRequested();
DisassembleProperty(prop);
}
output.WriteLine();
}
CloseBlock("end of class " + (type.DeclaringType != null ? type.Name : type.FullName));
isInType = oldIsInType;
}
void WriteTypeParameters(ITextOutput output, IGenericParameterProvider p)
{
if (p.HasGenericParameters) {
output.Write('<');
for (int i = 0; i < p.GenericParameters.Count; i++) {
if (i > 0)
output.Write(", ");
GenericParameter gp = p.GenericParameters[i];
if (gp.HasReferenceTypeConstraint) {
output.Write("class ");
} else if (gp.HasNotNullableValueTypeConstraint) {
output.Write("valuetype ");
}
if (gp.HasDefaultConstructorConstraint) {
output.Write(".ctor ");
}
if (gp.HasConstraints) {
output.Write('(');
for (int j = 0; j < gp.Constraints.Count; j++) {
if (j > 0)
output.Write(", ");
gp.Constraints[j].WriteTo(output, ILNameSyntax.TypeName);
}
output.Write(") ");
}
if (gp.IsContravariant) {
output.Write('-');
} else if (gp.IsCovariant) {
output.Write('+');
}
output.Write(DisassemblerHelpers.Escape(gp.Name));
}
output.Write('>');
}
}
#endregion
#region Helper methods
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(bool defaultCollapsed)
{
output.MarkFoldStart(defaultCollapsed: !ExpandMemberDefinitions && defaultCollapsed);
output.WriteLine();
output.WriteLine("{");
output.Indent();
}
void CloseBlock(string comment = null)
{
output.Unindent();
output.Write("}");
if (comment != null)
output.Write(" // " + comment);
output.MarkFoldEnd();
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 && pair.Value != null) {
output.Write(pair.Value);
output.Write(' ');
}
}
if ((val & ~tested) != 0)
output.Write("flag({0:x4}) ", 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) {
if (pair.Value != null) {
output.Write(pair.Value);
output.Write(' ');
}
return;
}
}
if (val != 0) {
output.Write("flag({0:x4})", 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();
}
}
#endregion
public void DisassembleNamespace(string nameSpace, IEnumerable<TypeDefinition> types)
{
if (!string.IsNullOrEmpty(nameSpace)) {
output.Write(".namespace " + DisassemblerHelpers.Escape(nameSpace));
OpenBlock(false);
}
bool oldIsInType = isInType;
isInType = true;
foreach (TypeDefinition td in types) {
cancellationToken.ThrowIfCancellationRequested();
DisassembleType(td);
output.WriteLine();
}
if (!string.IsNullOrEmpty(nameSpace)) {
CloseBlock();
isInType = oldIsInType;
}
}
public void WriteAssemblyHeader(AssemblyDefinition asm)
{
output.Write(".assembly ");
if (asm.Name.IsWindowsRuntime)
output.Write("windowsruntime ");
output.Write(DisassemblerHelpers.Escape(asm.Name.Name));
OpenBlock(false);
WriteAttributes(asm.CustomAttributes);
WriteSecurityDeclarations(asm);
if (asm.Name.PublicKey != null && asm.Name.PublicKey.Length > 0) {
output.Write(".publickey = ");
WriteBlob(asm.Name.PublicKey);
output.WriteLine();
}
if (asm.Name.HashAlgorithm != AssemblyHashAlgorithm.None) {
output.Write(".hash algorithm 0x{0:x8}", (int)asm.Name.HashAlgorithm);
if (asm.Name.HashAlgorithm == AssemblyHashAlgorithm.SHA1)
output.Write(" // SHA1");
output.WriteLine();
}
Version v = asm.Name.Version;
if (v != null) {
output.WriteLine(".ver {0}:{1}:{2}:{3}", v.Major, v.Minor, v.Build, v.Revision);
}
CloseBlock();
}
public void WriteAssemblyReferences(ModuleDefinition module)
{
foreach (var mref in module.ModuleReferences) {
output.WriteLine(".module extern {0}", DisassemblerHelpers.Escape(mref.Name));
}
foreach (var aref in module.AssemblyReferences) {
output.Write(".assembly extern ");
if (aref.IsWindowsRuntime)
output.Write("windowsruntime ");
output.Write(DisassemblerHelpers.Escape(aref.Name));
OpenBlock(false);
if (aref.PublicKeyToken != null) {
output.Write(".publickeytoken = ");
WriteBlob(aref.PublicKeyToken);
output.WriteLine();
}
if (aref.Version != null) {
output.WriteLine(".ver {0}:{1}:{2}:{3}", aref.Version.Major, aref.Version.Minor, aref.Version.Build, aref.Version.Revision);
}
CloseBlock();
}
}
public void WriteModuleHeader(ModuleDefinition module)
{
if (module.HasExportedTypes) {
foreach (ExportedType exportedType in module.ExportedTypes) {
output.Write(".class extern ");
if (exportedType.IsForwarder)
output.Write("forwarder ");
output.Write(exportedType.DeclaringType != null ? exportedType.Name : exportedType.FullName);
OpenBlock(false);
if (exportedType.DeclaringType != null)
output.WriteLine(".class extern {0}", DisassemblerHelpers.Escape(exportedType.DeclaringType.FullName));
else
output.WriteLine(".assembly extern {0}", DisassemblerHelpers.Escape(exportedType.Scope.Name));
CloseBlock();
}
}
output.WriteLine(".module {0}", module.Name);
output.WriteLine("// MVID: {0}", module.Mvid.ToString("B").ToUpperInvariant());
// TODO: imagebase, file alignment, stackreserve, subsystem
output.WriteLine(".corflags 0x{0:x} // {1}", module.Attributes, module.Attributes.ToString());
WriteAttributes(module.CustomAttributes);
}
public void WriteModuleContents(ModuleDefinition module)
{
foreach (TypeDefinition td in module.Types) {
DisassembleType(td);
output.WriteLine();
}
}
}
}