diff options
Diffstat (limited to 'ICSharpCode.Decompiler/Disassembler/ReflectionDisassembler.cs')
-rw-r--r-- | ICSharpCode.Decompiler/Disassembler/ReflectionDisassembler.cs | 1168 |
1 files changed, 1168 insertions, 0 deletions
diff --git a/ICSharpCode.Decompiler/Disassembler/ReflectionDisassembler.cs b/ICSharpCode.Decompiler/Disassembler/ReflectionDisassembler.cs new file mode 100644 index 00000000..2a374c3a --- /dev/null +++ b/ICSharpCode.Decompiler/Disassembler/ReflectionDisassembler.cs @@ -0,0 +1,1168 @@ +// 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.Diagnostics; +using System.Linq; +using System.Text; +using System.Threading; +using ICSharpCode.NRefactory; +using Mono.Cecil; +using Mono.Collections.Generic; + +namespace ICSharpCode.Decompiler.Disassembler +{ + /// <summary> + /// Disassembles type and member definitions. + /// </summary> + public sealed class ReflectionDisassembler + { + ITextOutput output; + CancellationToken cancellationToken; + bool isInType; // whether we are currently disassembling a whole type (-> defaultCollapsed for foldings) + MethodBodyDisassembler methodBodyDisassembler; + MemberReference currentMember; + + public ReflectionDisassembler(ITextOutput output, bool detectControlStructure, CancellationToken cancellationToken) + { + if (output == null) + throw new ArgumentNullException("output"); + this.output = output; + this.cancellationToken = cancellationToken; + methodBodyDisassembler = new MethodBodyDisassembler(output, detectControlStructure, cancellationToken); + } + + #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) + { + // set current member + currentMember = method; + + // write method header + output.WriteDefinition(".method ", method); + DisassembleMethodInternal(method); + } + + void DisassembleMethodInternal(MethodDefinition method) + { + // .method public hidebysig specialname + // instance default class [mscorlib]System.IO.TextWriter get_BaseWriter () cil managed + // + + TextLocation startLocation = output.Location; + + //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("(\"" + NRefactory.CSharp.TextWriterTokenWriter.ConvertString(info.Module.Name) + "\""); + + if (!string.IsNullOrEmpty(info.EntryPoint) && info.EntryPoint != method.Name) + output.Write(" as \"" + NRefactory.CSharp.TextWriterTokenWriter.ConvertString(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(); + OpenBlock(defaultCollapsed: isInType); + WriteAttributes(method.CustomAttributes); + if (method.HasOverrides) { + foreach (var methodOverride in method.Overrides) { + output.Write(".override method "); + methodOverride.WriteTo(output); + output.WriteLine(); + } + } + foreach (var p in method.Parameters) { + WriteParameterAttributes(p); + } + WriteSecurityDeclarations(method); + + if (method.HasBody) { + // create IL code mappings - used in debugger + MethodDebugSymbols debugSymbols = new MethodDebugSymbols(method); + debugSymbols.StartLocation = startLocation; + methodBodyDisassembler.Disassemble(method.Body, debugSymbols); + debugSymbols.EndLocation = output.Location; + output.AddDebugSymbols(debugSymbols); + } + + 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}')", NRefactory.CSharp.TextWriterTokenWriter.ConvertString((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.SizeParameterMultiplier == 0) { + output.Write(ami.Size.ToString()); + } else { + if (ami.Size >= 0) + output.Write(ami.Size.ToString()); + 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}\"", + NRefactory.CSharp.TextWriterTokenWriter.ConvertString(cmi.ManagedType.FullName), + NRefactory.CSharp.TextWriterTokenWriter.ConvertString(cmi.Cookie)); + if (cmi.Guid != Guid.Empty || !string.IsNullOrEmpty(cmi.UnmanagedType)) { + output.Write(", \"{0}\", \"{1}\"", cmi.Guid.ToString(), NRefactory.CSharp.TextWriterTokenWriter.ConvertString(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(); + } + } + + bool HasParameterAttributes(ParameterDefinition p) + { + return p.HasConstant || p.HasCustomAttributes; + } + + void WriteParameterAttributes(ParameterDefinition p) + { + if (!HasParameterAttributes(p)) + return; + output.Write(".param [{0}]", p.Index + 1); + if (p.HasConstant) { + output.Write(" = "); + WriteConstant(p.Constant); + } + output.WriteLine(); + WriteAttributes(p.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: 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(" "); + type.Interfaces[index].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: 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) + { + 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(); + } + } + } +} |