diff options
author | Jeroen Frijters <jeroen@frijters.net> | 2013-02-25 14:27:25 +0400 |
---|---|---|
committer | Jeroen Frijters <jeroen@frijters.net> | 2013-02-25 14:27:25 +0400 |
commit | 9b7577d079431bc017de44cb6b58949de1c52282 (patch) | |
tree | 542195f11cf00a5d98c58f1039a4782e8b9bdcd2 |
Initial commit.
-rw-r--r-- | .gitattributes | 22 | ||||
-rw-r--r-- | .gitignore | 163 | ||||
-rw-r--r-- | ByteReader.cs | 194 | ||||
-rw-r--r-- | CABlob.cs | 597 | ||||
-rw-r--r-- | Disassembler.cs | 2979 | ||||
-rw-r--r-- | ExportedMethods.cs | 167 | ||||
-rw-r--r-- | IKVM.Reflection.dll | bin | 0 -> 442368 bytes | |||
-rw-r--r-- | IL.cs | 1040 | ||||
-rw-r--r-- | Keywords.cs | 240 | ||||
-rw-r--r-- | LineWriter.cs | 100 | ||||
-rw-r--r-- | Program.cs | 149 | ||||
-rw-r--r-- | Util.cs | 53 | ||||
-rw-r--r-- | VTableFixups.cs | 163 | ||||
-rw-r--r-- | ikdasm.csproj | 78 | ||||
-rw-r--r-- | ikdasm.sln | 20 |
15 files changed, 5965 insertions, 0 deletions
diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..412eeda --- /dev/null +++ b/.gitattributes @@ -0,0 +1,22 @@ +# Auto detect text files and perform LF normalization +* text=auto + +# Custom for Visual Studio +*.cs diff=csharp +*.sln merge=union +*.csproj merge=union +*.vbproj merge=union +*.fsproj merge=union +*.dbproj merge=union + +# Standard to msysgit +*.doc diff=astextplain +*.DOC diff=astextplain +*.docx diff=astextplain +*.DOCX diff=astextplain +*.dot diff=astextplain +*.DOT diff=astextplain +*.pdf diff=astextplain +*.PDF diff=astextplain +*.rtf diff=astextplain +*.RTF diff=astextplain diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..5ebd21a --- /dev/null +++ b/.gitignore @@ -0,0 +1,163 @@ +################# +## Eclipse +################# + +*.pydevproject +.project +.metadata +bin/ +tmp/ +*.tmp +*.bak +*.swp +*~.nib +local.properties +.classpath +.settings/ +.loadpath + +# External tool builders +.externalToolBuilders/ + +# Locally stored "Eclipse launch configurations" +*.launch + +# CDT-specific +.cproject + +# PDT-specific +.buildpath + + +################# +## Visual Studio +################# + +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. + +# User-specific files +*.suo +*.user +*.sln.docstates + +# Build results +[Dd]ebug/ +[Rr]elease/ +*_i.c +*_p.c +*.ilk +*.meta +*.obj +*.pch +*.pdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.vspscc +.builds +*.dotCover + +## TODO: If you have NuGet Package Restore enabled, uncomment this +#packages/ + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opensdf +*.sdf + +# Visual Studio profiler +*.psess +*.vsp + +# ReSharper is a .NET coding add-in +_ReSharper* + +# Installshield output folder +[Ee]xpress + +# DocProject is a documentation generator add-in +DocProject/buildhelp/ +DocProject/Help/*.HxT +DocProject/Help/*.HxC +DocProject/Help/*.hhc +DocProject/Help/*.hhk +DocProject/Help/*.hhp +DocProject/Help/Html2 +DocProject/Help/html + +# Click-Once directory +publish + +# Others +[Bb]in +[Oo]bj +sql +TestResults +*.Cache +ClientBin +stylecop.* +~$* +*.dbmdl +Generated_Code #added for RIA/Silverlight projects + +# Backup & report files from converting an old project file to a newer +# Visual Studio version. Backup files are not needed, because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML + + + +############ +## Windows +############ + +# Windows image file caches +Thumbs.db + +# Folder config file +Desktop.ini + + +############# +## Python +############# + +*.py[co] + +# Packages +*.egg +*.egg-info +dist +build +eggs +parts +bin +var +sdist +develop-eggs +.installed.cfg + +# Installer logs +pip-log.txt + +# Unit test / coverage reports +.coverage +.tox + +#Translations +*.mo + +#Mr Developer +.mr.developer.cfg + +# Mac crap +.DS_Store diff --git a/ByteReader.cs b/ByteReader.cs new file mode 100644 index 0000000..7e3c6f5 --- /dev/null +++ b/ByteReader.cs @@ -0,0 +1,194 @@ +/* + Copyright (C) 2009 Jeroen Frijters + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. + + Jeroen Frijters + jeroen@frijters.net + +*/ +using System; +using System.Collections.Generic; +using System.Text; + +namespace IKVM.Reflection.Reader +{ + sealed class ByteReader + { + private byte[] buffer; + private int pos; + private int end; + + internal ByteReader(byte[] buffer, int offset, int length) + { + this.buffer = buffer; + this.pos = offset; + this.end = pos + length; + } + + internal static ByteReader FromBlob(byte[] blobHeap, int blob) + { + ByteReader br = new ByteReader(blobHeap, blob, 4); + int length = br.ReadCompressedInt(); + br.end = br.pos + length; + return br; + } + + internal int Length + { + get { return end - pos; } + } + + internal byte PeekByte() + { + if (pos == end) + throw new BadImageFormatException(); + return buffer[pos]; + } + + internal byte ReadByte() + { + if (pos == end) + throw new BadImageFormatException(); + return buffer[pos++]; + } + + internal byte[] ReadBytes(int count) + { + if (count < 0) + throw new BadImageFormatException(); + if (end - pos < count) + throw new BadImageFormatException(); + byte[] buf = new byte[count]; + Buffer.BlockCopy(buffer, pos, buf, 0, count); + pos += count; + return buf; + } + + internal int ReadCompressedInt() + { + byte b1 = ReadByte(); + if (b1 <= 0x7F) + { + return b1; + } + else if ((b1 & 0xC0) == 0x80) + { + byte b2 = ReadByte(); + return ((b1 & 0x3F) << 8) | b2; + } + else + { + byte b2 = ReadByte(); + byte b3 = ReadByte(); + byte b4 = ReadByte(); + return ((b1 & 0x3F) << 24) + (b2 << 16) + (b3 << 8) + b4; + } + } + + internal string ReadString() + { + if (PeekByte() == 0xFF) + { + pos++; + return null; + } + int length = ReadCompressedInt(); + string str = Encoding.UTF8.GetString(buffer, pos, length); + pos += length; + return str; + } + + internal char ReadChar() + { + return (char)ReadInt16(); + } + + internal sbyte ReadSByte() + { + return (sbyte)ReadByte(); + } + + internal short ReadInt16() + { + if (end - pos < 2) + throw new BadImageFormatException(); + byte b1 = buffer[pos++]; + byte b2 = buffer[pos++]; + return (short)(b1 | (b2 << 8)); + } + + internal ushort ReadUInt16() + { + return (ushort)ReadInt16(); + } + + internal int ReadInt32() + { + if (end - pos < 4) + throw new BadImageFormatException(); + byte b1 = buffer[pos++]; + byte b2 = buffer[pos++]; + byte b3 = buffer[pos++]; + byte b4 = buffer[pos++]; + return (int)(b1 | (b2 << 8) | (b3 << 16) | (b4 << 24)); + } + + internal uint ReadUInt32() + { + return (uint)ReadInt32(); + } + + internal long ReadInt64() + { + ulong lo = ReadUInt32(); + ulong hi = ReadUInt32(); + return (long)(lo | (hi << 32)); + } + + internal ulong ReadUInt64() + { + return (ulong)ReadInt64(); + } + + internal float ReadSingle() + { + return SingleConverter.Int32BitsToSingle(ReadInt32()); + } + + internal double ReadDouble() + { + return BitConverter.Int64BitsToDouble(ReadInt64()); + } + + internal ByteReader Slice(int length) + { + if (end - pos < length) + throw new BadImageFormatException(); + ByteReader br = new ByteReader(buffer, pos, length); + pos += length; + return br; + } + + // NOTE this method only works if the original offset was aligned and for alignments that are a power of 2 + internal void Align(int alignment) + { + alignment--; + pos = (pos + alignment) & ~alignment; + } + } +} diff --git a/CABlob.cs b/CABlob.cs new file mode 100644 index 0000000..0e728fd --- /dev/null +++ b/CABlob.cs @@ -0,0 +1,597 @@ +/* + Copyright (C) 2012 Jeroen Frijters + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. + + Jeroen Frijters + jeroen@frijters.net + +*/ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using IKVM.Reflection; +using IKVM.Reflection.Reader; +using Type = IKVM.Reflection.Type; + +namespace Ildasm +{ + partial class Disassembler + { + bool DecodeDeclSecurity(StringBuilder sb, IList<CustomAttributeData> list, int level) + { + try + { + sb.Append(" = {"); + bool first = true; + foreach (var sec in list) + { + if (!first) + { + sb.Append(','); + sb.AppendLine(); + sb.Append(' ', level + 14); + } + first = false; + string typeName = sec.Constructor.DeclaringType.AssemblyQualifiedName; + if (typeName.EndsWith(", mscorlib", StringComparison.Ordinal)) + { + typeName = typeName.Substring(0, typeName.Length - 10); + } + AppendTypeName(sb, sec.Constructor.DeclaringType, typeName, compat != CompatLevel.None); + sb.Append(" = {"); + byte[] blob = sec.__GetBlob(); + // LAMESPEC the count of named arguments is a compressed integer (instead of UInt16 as NumNamed in custom attributes) + var br = new ByteReader(blob, 0, blob.Length); + int count = br.ReadCompressedInt(); + ReadNamedArguments(sb, br, count, 0, compat != CompatLevel.None && count > 1); + sb.Append('}'); + } + sb.Append('}'); + return true; + } + catch (IKVM.Reflection.BadImageFormatException) + { + return false; + } + } + + void AppendTypeName(StringBuilder sb, Type type) + { + if (type.IsNested) + { + AppendTypeName(sb, type.DeclaringType); + sb.Append('/'); + } + AppendTypeNameNoOuter(sb, type); + } + + void AppendTypeNameNoOuter(StringBuilder sb, Type type) + { + if (type.__Namespace != null) + { + sb.Append(QuoteIdentifier(type.__Namespace)); + sb.Append('.'); + } + sb.Append(QuoteIdentifier(type.__Name)); + } + + void AppendTypeName(StringBuilder sb, Type type, string typeName, bool noself = false, bool securityCompatHack = false) + { + if (type.Assembly == assembly && !noself && (!type.IsGenericType || type.IsGenericTypeDefinition) && !type.HasElementType) + { + AppendTypeName(sb, type); + } + else if (typerefs.Contains(type)) + { + sb.Append('[').Append(QuoteIdentifier(referencedAssemblies[type.Assembly])).Append(']'); + AppendTypeName(sb, type); + } + else + { + if (securityCompatHack) + { + throw new IKVM.Reflection.BadImageFormatException(); + } + sb.Append("class ").Append(QuoteIdentifier(typeName, true)); + } + } + + bool DecodeCABlob(StringBuilder sb, ConstructorInfo constructor, byte[] blob, int level) + { + try + { + // CustomAttribute + var br = new ByteReader(blob, 2, blob.Length - 4); + ReadConstructorArguments(sb, br, constructor, level); + br = new ByteReader(blob, blob.Length - (br.Length + 2), br.Length + 2); + int named = br.ReadUInt16(); + if (constructor.GetParameters().Length != 0 && named != 0) + { + AppendNewLine(sb, level); + } + ReadNamedArguments(sb, br, named, level, false); + return true; + } + catch (IKVM.Reflection.BadImageFormatException) { } + catch (ArgumentOutOfRangeException) { } + return false; + } + + void AppendNewLine(StringBuilder sb, int level) + { + sb.AppendLine(); + if (level < 0) + { + sb.Append(" //"); + sb.Append(' ', -level - 4); + } + else + { + sb.Append(' ', level); + } + } + + void ReadConstructorArguments(StringBuilder sb, ByteReader br, ConstructorInfo constructor, int level) + { + bool first = true; + foreach (var parameter in constructor.GetParameters()) + { + if (!first) + { + AppendNewLine(sb, level); + } + first = false; + ReadFixedArg(sb, br, parameter.ParameterType); + } + } + + void ReadNamedArguments(StringBuilder sb, ByteReader br, int named, int level, bool securityCompatHack) + { + for (int i = 0; i < named; i++) + { + if (i != 0) + { + AppendNewLine(sb, level); + } + byte fieldOrProperty = br.ReadByte(); + switch (fieldOrProperty) + { + case 0x53: + sb.Append("field "); + break; + case 0x54: + sb.Append("property "); + break; + default: + throw new IKVM.Reflection.BadImageFormatException(); + } + string typeName; + Type fieldOrPropertyType = ReadFieldOrPropType(sb, br, out typeName); + AppendCATypeName(sb, fieldOrPropertyType, typeName, securityCompatHack); + sb.Append(' ').Append(QuoteIdentifier(br.ReadString(), true)).Append(" = "); + ReadFixedArg(sb, br, fieldOrPropertyType); + } + } + + void ReadFixedArg(StringBuilder sb, ByteReader br, Type type, bool arrayElement = false) + { + if (type.IsArray) + { + int length = br.ReadInt32(); + if (length == -1 && compat == CompatLevel.None) + { + sb.Append("nullref"); + } + else if (length == 0 && compat != CompatLevel.None) + { + throw new IKVM.Reflection.BadImageFormatException(); + } + else + { + Type elementType = type.GetElementType(); + AppendCATypeName(sb, elementType, null); + sb.AppendFormat("[{0}](", length); + for (int i = 0; i < length; i++) + { + if (i != 0) + { + sb.Append(' '); + } + if (elementType == typeofSystemObject) + { + string typeName; + ReadFixedArg(sb, br, ReadFieldOrPropType(sb, br, out typeName), false); + } + else + { + ReadFixedArg(sb, br, elementType, true); + } + } + sb.Append(')'); + } + } + else if (type.FullName == "System.Type" && type.Assembly.GetName().Name == "mscorlib") + { + if (!arrayElement) + { + AppendCATypeName(sb, type, null); + sb.Append('('); + } + string typeName; + var type1 = ReadType(br, out typeName); + if (type1 == null) + { + if (typeName == null) + { + sb.Append("nullref"); + } + else + { + sb.Append("class ").Append(QuoteIdentifier(typeName, true)); + } + } + else + { + AppendTypeName(sb, type1, typeName, compat != CompatLevel.None && IsNestedTypeWithNamespace(type1)); + } + if (!arrayElement) + { + sb.Append(')'); + } + } + else if (type.Assembly == mscorlib) + { + if (!arrayElement) + { + AppendCATypeName(sb, type, null); + sb.Append('('); + } + if (type == typeofSystemBoolean) + { + sb.Append(br.ReadByte() == 0 ? "false" : "true"); + } + else if (type == typeofSystemByte) + { + sb.Append(br.ReadByte()); + } + else if (type == typeofSystemSByte) + { + sb.Append(br.ReadSByte()); + } + else if (type == typeofSystemChar) + { + sb.AppendFormat("0x{0:X4}", (int)br.ReadChar()); + } + else if (type == typeofSystemInt16) + { + sb.Append(br.ReadInt16()); + } + else if (type == typeofSystemUInt16) + { + sb.Append(br.ReadUInt16()); + } + else if (type == typeofSystemInt32) + { + sb.Append(br.ReadInt32()); + } + else if (type == typeofSystemUInt32) + { + sb.Append(br.ReadInt32()); + } + else if (type == typeofSystemInt64) + { + sb.Append(br.ReadInt64()); + } + else if (type == typeofSystemUInt64) + { + sb.Append(br.ReadInt64()); + } + else if (type == typeofSystemSingle) + { + sb.Append(ToString(br.ReadSingle(), true)); + } + else if (type == typeofSystemDouble) + { + sb.Append(ToString(br.ReadDouble(), true)); + } + else if (type == typeofSystemString) + { + var str = br.ReadString(); + if (str == null) + { + sb.Append("nullref"); + } + else + { + if (compat != CompatLevel.None) + { + int pos = str.IndexOf((char)0); + if (pos != -1) + { + str = str.Substring(0, pos); + } + } + sb.Append(QuoteIdentifier(str, true)); + } + } + else if (type == typeofSystemObject) + { + string typeName; + ReadFixedArg(sb, br, ReadFieldOrPropType(sb, br, out typeName)); + } + else + { + throw new NotImplementedException(type.FullName); + } + if (!arrayElement) + { + sb.Append(')'); + } + } + else if (type.__IsMissing || (compat != CompatLevel.None && typerefs.Contains(type))) + { + // ildasm actually tries to load the assembly, but we can't do that, so we cheat by having + // a list of 'known' enum types + if (type.Assembly.GetName().Name == "mscorlib") + { + switch (type.FullName) + { + case "System.AttributeTargets": + case "System.Runtime.ConstrainedExecution.Consistency": + case "System.Runtime.ConstrainedExecution.Cer": + case "System.Security.Permissions.SecurityAction": + case "System.Security.Permissions.SecurityPermissionFlag": + case "System.Runtime.Versioning.ResourceScope": + case "System.Runtime.InteropServices.CallingConvention": + case "System.Runtime.InteropServices.CharSet": + ReadFixedArg(sb, br, typeofSystemInt32); + return; + case "System.Security.SecurityRuleSet": + if (compat != CompatLevel.V20) + { + ReadFixedArg(sb, br, typeofSystemByte); + return; + } + break; + case "System.Diagnostics.Tracing.EventLevel": + case "System.Diagnostics.Tracing.EventTask": + case "System.Diagnostics.Tracing.EventOpcode": + if (compat != CompatLevel.V20 && compat != CompatLevel.V40) + { + ReadFixedArg(sb, br, typeofSystemInt32); + return; + } + break; + case "System.Type": + sb.Append("type("); + string typeName; + AppendTypeName(sb, ReadType(br, out typeName), typeName); + sb.Append(")"); + return; + } + } + switch (br.Length) + { + case 1: + if (compat != CompatLevel.None) + { + // ildasm uses bool (???) as the underlying type in this case + sb.AppendFormat("bool({0})", br.ReadByte() == 0 ? "false" : "true"); + } + else + { + // just guess that the enum has int8 as the underlying type + sb.AppendFormat("int8({0})", br.ReadSByte()); + } + break; + case 2: + // just guess that the enum has int16 as the underlying type + sb.AppendFormat("int16({0})", br.ReadInt16()); + break; + case 4: + // just guess that the enum has int32 as the underlying type + sb.AppendFormat("int32({0})", br.ReadInt32()); + break; + case 8: + // just guess that the enum has int64 as the underlying type + sb.AppendFormat("int64({0})", br.ReadInt64()); + break; + default: + throw new IKVM.Reflection.BadImageFormatException(); + } + } + else if (type.IsEnum) + { + ReadFixedArg(sb, br, type.GetEnumUnderlyingType(), arrayElement); + } + else + { + throw new NotImplementedException(type.FullName); + } + } + + static bool IsNestedTypeWithNamespace(Type type) + { + if (!type.IsNested) + { + return false; + } + if (type.IsConstructedGenericType) + { + type = type.GetGenericTypeDefinition(); + } + return type.__Namespace != null; + } + + void AppendCATypeName(StringBuilder sb, Type type, string typeName, bool securityCompatHack = false) + { + if (type.IsArray) + { + AppendCATypeName(sb, type.GetElementType(), null); + sb.Append("[]"); + } + else if (type == typeofSystemBoolean) + { + sb.Append("bool"); + } + else if (type == typeofSystemSByte) + { + sb.Append("int8"); + } + else if (type == typeofSystemByte) + { + sb.Append("uint8"); + } + else if (type == typeofSystemChar) + { + sb.Append("char"); + } + else if (type == typeofSystemInt16) + { + sb.Append("int16"); + } + else if (type == typeofSystemUInt16) + { + sb.Append("uint16"); + } + else if (type == typeofSystemInt32) + { + sb.Append("int32"); + } + else if (type == typeofSystemUInt32) + { + sb.Append("uint32"); + } + else if (type == typeofSystemInt64) + { + sb.Append("int64"); + } + else if (type == typeofSystemUInt64) + { + sb.Append("uint64"); + } + else if (type == typeofSystemSingle) + { + sb.Append("float32"); + } + else if (type == typeofSystemDouble) + { + sb.Append("float64"); + } + else if (type == typeofSystemString) + { + sb.Append("string"); + } + else if (type == typeofSystemObject) + { + sb.Append("object"); + } + else if (type.FullName == "System.Type" && type.Assembly.GetName().Name == "mscorlib") + { + sb.Append("type"); + } + else + { + sb.Append("enum "); + AppendTypeName(sb, type, typeName, false, securityCompatHack); + } + } + + Type ReadFieldOrPropType(StringBuilder sb, ByteReader br, out string typeName) + { + const byte ELEMENT_TYPE_BOOLEAN = 0x02; + const byte ELEMENT_TYPE_CHAR = 0x03; + const byte ELEMENT_TYPE_I1 = 0x04; + const byte ELEMENT_TYPE_U1 = 0x05; + const byte ELEMENT_TYPE_I2 = 0x06; + const byte ELEMENT_TYPE_U2 = 0x07; + const byte ELEMENT_TYPE_I4 = 0x08; + const byte ELEMENT_TYPE_U4 = 0x09; + const byte ELEMENT_TYPE_I8 = 0x0a; + const byte ELEMENT_TYPE_U8 = 0x0b; + const byte ELEMENT_TYPE_R4 = 0x0c; + const byte ELEMENT_TYPE_R8 = 0x0d; + const byte ELEMENT_TYPE_STRING = 0x0e; + const byte ELEMENT_TYPE_SZARRAY = 0x1d; + + typeName = null; + + switch (br.ReadByte()) + { + case ELEMENT_TYPE_BOOLEAN: + return typeofSystemBoolean; + case ELEMENT_TYPE_CHAR: + return typeofSystemChar; + case ELEMENT_TYPE_I1: + return typeofSystemSByte; + case ELEMENT_TYPE_U1: + return typeofSystemByte; + case ELEMENT_TYPE_I2: + return typeofSystemInt16; + case ELEMENT_TYPE_U2: + return typeofSystemUInt16; + case ELEMENT_TYPE_I4: + return typeofSystemInt32; + case ELEMENT_TYPE_U4: + return typeofSystemUInt32; + case ELEMENT_TYPE_I8: + return typeofSystemInt64; + case ELEMENT_TYPE_U8: + return typeofSystemUInt64; + case ELEMENT_TYPE_R4: + return typeofSystemSingle; + case ELEMENT_TYPE_R8: + return typeofSystemDouble; + case ELEMENT_TYPE_STRING: + return typeofSystemString; + case ELEMENT_TYPE_SZARRAY: + return ReadFieldOrPropType(sb, br, out typeName).MakeArrayType(); + case 0x55: + return ReadType(br, out typeName); + case 0x50: + return typeofSystemType; + case 0x51: + return typeofSystemObject; + default: + throw new IKVM.Reflection.BadImageFormatException(); + } + } + + Type ReadType(ByteReader br, out string typeName) + { + typeName = br.ReadString(); + if (typeName == null) + { + return null; + } + if (typeName.Length > 0 && typeName[typeName.Length - 1] == 0) + { + // there are broken compilers that emit an extra NUL character after the type name + typeName = typeName.Substring(0, typeName.Length - 1); + } + var type = universe.ResolveType(assembly, typeName); + if (type != null && type.Assembly == mscorlib) + { + // we don't want that! + type = universe.ResolveType(assembly, type.FullName + ", mscorlib, Version=0.0.0.0"); + } + return type; + } + } +} diff --git a/Disassembler.cs b/Disassembler.cs new file mode 100644 index 0000000..6cf9a21 --- /dev/null +++ b/Disassembler.cs @@ -0,0 +1,2979 @@ +/* + Copyright (C) 2013 Jeroen Frijters + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. + + Jeroen Frijters + jeroen@frijters.net + +*/ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using IKVM.Reflection; +using Type = IKVM.Reflection.Type; + +namespace Ildasm +{ + enum CompatLevel + { + None, + V20, + V40, + V45, + } + + sealed partial class Disassembler + { + const int IMAGE_SCN_CNT_CODE = 0x00000020; + const int IMAGE_SCN_CNT_INITIALIZED_DATA = 0x00000040; + const int IMAGE_SCN_CNT_UNINITIALIZED_DATA = 0x00000080; + const int IMAGE_SCN_MEM_READ = 0x40000000; + const int IMAGE_SCN_MEM_WRITE = unchecked((int)0x80000000); + const int COMIMAGE_FLAGS_ILONLY = 0x00000001; + const int COMIMAGE_FLAGS_32BITREQUIRED = 0x00000002; + const int COMIMAGE_FLAGS_IL_LIBRARY = 0x00000004; + const int COMIMAGE_FLAGS_STRONGNAMESIGNED = 0x00000008; + const int COMIMAGE_FLAGS_NATIVE_ENTRYPOINT = 0x00000010; + const int COMIMAGE_FLAGS_32BITPREFERRED = 0x00020000; + readonly Universe universe = new Universe(UniverseOptions.EnableFunctionPointers | UniverseOptions.ResolveMissingMembers | UniverseOptions.DisablePseudoCustomAttributeRetrieval); + readonly Assembly mscorlib; + readonly Type typeofSystemBoolean; + readonly Type typeofSystemSByte; + readonly Type typeofSystemByte; + readonly Type typeofSystemChar; + readonly Type typeofSystemInt16; + readonly Type typeofSystemUInt16; + readonly Type typeofSystemInt32; + readonly Type typeofSystemUInt32; + readonly Type typeofSystemInt64; + readonly Type typeofSystemUInt64; + readonly Type typeofSystemSingle; + readonly Type typeofSystemDouble; + readonly Type typeofSystemVoid; + readonly Type typeofSystemIntPtr; + readonly Type typeofSystemUIntPtr; + readonly Type typeofSystemTypedReference; + readonly Type typeofSystemObject; + readonly Type typeofSystemString; + readonly Type typeofSystemType; + readonly Assembly assembly; + readonly Module module; + readonly Dictionary<Assembly, string> referencedAssemblies = new Dictionary<Assembly, string>(); + readonly Assembly[] resolvedAssemblies; + readonly HashSet<Type> typerefs; + readonly List<FieldInfo> dataFields = new List<FieldInfo>(); + readonly Dictionary<int, List<ExportedMethod>> exportedMethods; + readonly string[] methodNames; + readonly string[] fieldNames; + readonly CompatLevel compat; + readonly string outputFile; + readonly bool diffMode; + + internal Disassembler(string inputFile, string outputFile, CompatLevel compat, bool diffMode) + { + this.outputFile = outputFile; + this.compat = compat; + this.diffMode = diffMode; + universe.AssemblyResolve += new IKVM.Reflection.ResolveEventHandler(universe_AssemblyResolve); + mscorlib = universe.Import(typeof(object)).Assembly; + typeofSystemBoolean = universe.Import(typeof(bool)); + typeofSystemSByte = universe.Import(typeof(sbyte)); + typeofSystemByte = universe.Import(typeof(byte)); + typeofSystemChar = universe.Import(typeof(char)); + typeofSystemInt16 = universe.Import(typeof(short)); + typeofSystemUInt16 = universe.Import(typeof(ushort)); + typeofSystemInt32 = universe.Import(typeof(int)); + typeofSystemUInt32 = universe.Import(typeof(uint)); + typeofSystemInt64 = universe.Import(typeof(long)); + typeofSystemUInt64 = universe.Import(typeof(ulong)); + typeofSystemSingle = universe.Import(typeof(float)); + typeofSystemDouble = universe.Import(typeof(double)); + typeofSystemVoid = universe.Import(typeof(void)); + typeofSystemIntPtr = universe.Import(typeof(IntPtr)); + typeofSystemUIntPtr = universe.Import(typeof(UIntPtr)); + typeofSystemTypedReference = universe.Import(typeof(TypedReference)); + typeofSystemObject = universe.Import(typeof(object)); + typeofSystemString = universe.Import(typeof(string)); + typeofSystemType = universe.Import(typeof(System.Type)); + // HACK we specify a bogus location to prevent IKVM.Reflection from loading external modules + // TODO IKVM.Reflection really should have a way to avoid it trying to load external modules (e.g. a UniverseOption to always use the ModuleResolve event) + var raw = universe.OpenRawModule(System.IO.File.OpenRead(inputFile), System.IO.Path.GetTempPath() + "/Dummy"); + if (raw.IsManifestModule) + { + assembly = universe.LoadAssembly(raw); + module = assembly.ManifestModule; + } + else + { + var ab = universe.DefineDynamicAssembly(new AssemblyName("<ModuleContainer>"), IKVM.Reflection.Emit.AssemblyBuilderAccess.ReflectionOnly); + assembly = ab; + module = ab.__AddModule(raw); + } + exportedMethods = GetExportedMethods(module); + + var names = new HashSet<string>(); + AssemblyName[] assemblyRefs = module.__GetReferencedAssemblies(); + resolvedAssemblies = new Assembly[assemblyRefs.Length]; + for (int i = 0; i < resolvedAssemblies.Length; i++) + { + string name = assemblyRefs[i].Name; + while (names.Contains(name)) + { + name = name + "_" + i; + } + names.Add(name); + resolvedAssemblies[i] = universe.CreateMissingAssembly(assemblyRefs[i].FullName); + referencedAssemblies.Add(resolvedAssemblies[i], name); + } + module.__ResolveReferencedAssemblies(resolvedAssemblies); + typerefs = new HashSet<Type>(module.__GetReferencedTypes()); + methodNames = GetMethodNames(); + fieldNames = GetFieldNames(); + } + + Assembly universe_AssemblyResolve(object sender, IKVM.Reflection.ResolveEventArgs args) + { + if (resolvedAssemblies != null) + { + foreach (var asm in resolvedAssemblies) + { + AssemblyComparisonResult result; + if (universe.CompareAssemblyIdentity(args.Name, false, asm.FullName, false, out result)) + { + return asm; + } + } + } + return universe.CreateMissingAssembly(args.Name); + } + + internal void Save(System.IO.TextWriter writer) + { + LineWriter lw = new LineWriter(writer); + WriteCopyrightHeader(lw); + WriteVTableFixupComment(lw); + if (!(assembly is IKVM.Reflection.Emit.AssemblyBuilder)) + { + WriteMscorlibDirective(lw); + } + WriteModuleManifest(lw); + if (!(assembly is IKVM.Reflection.Emit.AssemblyBuilder)) + { + WriteAssemblyManifest(lw); + } + WriteExportedTypes(lw); + if (!(assembly is IKVM.Reflection.Emit.AssemblyBuilder)) + { + WriteModules(lw); + WriteResources(lw); + } + WriteModuleHeader(lw); + WriteVTableFixups(lw); + WriteGlobalFields(lw); + WriteGlobalMethods(lw); + WriteTypes(lw); + WriteUnhandledCustomAttributes(lw); + WriteData(lw); + lw.WriteLine("// *********** DISASSEMBLY COMPLETE ***********************"); + WriteNativeResources(lw); + } + + string[] GetMethodNames() + { + var methods = new HashSet<MethodBase>(new MethodSignatureComparer()); + var names = new List<string>(); + try + { + for (int i = 0x06000001; i <= 0x06FFFFFF; i++) + { + string name; + var method = module.ResolveMethod(i); + if (methods.Contains(method)) + { + name = String.Format("$MD${0:X}", i - 0x06000000); + } + else + { + name = method.Name; + methods.Add(method); + } + if ((method.Attributes & MethodAttributes.MemberAccessMask) == MethodAttributes.PrivateScope) + { + names.Add(String.Format("{0}$PST{1:X8}", name, method.MetadataToken)); + } + else + { + names.Add(name); + } + } + } + catch (ArgumentOutOfRangeException) { } + return names.ToArray(); + } + + sealed class MethodSignatureComparer : IEqualityComparer<MethodBase> + { + public bool Equals(MethodBase x, MethodBase y) + { + return x.DeclaringType == y.DeclaringType + && x.Name == y.Name + && x.CallingConvention == y.CallingConvention + && Equals(GetReturnParameter(x), GetReturnParameter(y)) + && Equals(x.GetParameters(), y.GetParameters()) + && x.IsGenericMethodDefinition == y.IsGenericMethodDefinition + && (!x.IsGenericMethodDefinition || x.GetGenericArguments().Length == y.GetGenericArguments().Length) + ; + } + + static bool Equals(ParameterInfo p1, ParameterInfo p2) + { + return p1.ParameterType == p2.ParameterType + && p1.__GetCustomModifiers().Equals(p2.__GetCustomModifiers()); + } + + static bool Equals(ParameterInfo[] p1, ParameterInfo[] p2) + { + if (p1.Length != p2.Length) + { + return false; + } + for (int i = 0; i < p1.Length; i++) + { + if (!Equals(p1[i], p2[i])) + { + return false; + } + } + return true; + } + + static ParameterInfo GetReturnParameter(MethodBase mb) + { + ConstructorInfo ci = mb as ConstructorInfo; + return ci != null ? ci.__ReturnParameter : ((MethodInfo)mb).ReturnParameter; + } + + public int GetHashCode(MethodBase mb) + { + Type decl = mb.DeclaringType; + return mb.Name.GetHashCode() ^ (decl == null ? 0 : decl.GetHashCode()); + } + } + + string[] GetFieldNames() + { + var fields = new HashSet<FieldInfo>(new FieldSignatureComparer()); + var names = new List<string>(); + try + { + for (int i = 0x04000001; i <= 0x04FFFFFF; i++) + { + string name; + var field = module.ResolveField(i); + if (fields.Contains(field)) + { + name = String.Format("$FD${0:X}", i - 0x04000000); + } + else + { + name = field.Name; + fields.Add(field); + } + if ((field.Attributes & FieldAttributes.FieldAccessMask) == FieldAttributes.PrivateScope) + { + names.Add(String.Format("{0}$PST{1:X8}", name, field.MetadataToken)); + } + else + { + names.Add(name); + } + } + } + catch (ArgumentOutOfRangeException) { } + return names.ToArray(); + } + + sealed class FieldSignatureComparer : IEqualityComparer<FieldInfo> + { + public bool Equals(FieldInfo x, FieldInfo y) + { + return x.DeclaringType == y.DeclaringType + && x.Name == y.Name + && x.FieldType == y.FieldType + && x.__GetCustomModifiers().Equals(y.__GetCustomModifiers()); + } + + public int GetHashCode(FieldInfo field) + { + Type decl = field.DeclaringType; + return field.Name.GetHashCode() ^ (decl == null ? 0 : decl.GetHashCode()); + } + } + + void WriteExportedTypes(LineWriter lw) + { + Type[] exported = module.__GetExportedTypes(); + for (int i = 0; i < exported.Length; i++) + { + lw.Write(".class extern "); + if (!exported[i].IsNested) + { + lw.Write("forwarder "); + } + WriteTypeNameNoOuter(lw, exported[i]); + lw.WriteLine(); + lw.WriteLine("{"); + if (exported[i].IsNested) + { + lw.Write(" .class extern "); + WriteTypeName(lw, exported[i].DeclaringType); + lw.WriteLine(); + } + else + { + lw.WriteLine(" .assembly extern {0}", QuoteIdentifier(referencedAssemblies[exported[i].Assembly])); + } + if (exported[i].MetadataToken != 0) + { + lw.WriteLine(" .class 0x{0:X8}", exported[i].MetadataToken); + } + lw.WriteLine("}"); + } + } + + void WriteGlobalMethods(LineWriter lw) + { + MethodInfo[] methods = module.GetMethods(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Static); + ConstructorInfo cctor = module.__ModuleInitializer; + if (methods.Length != 0 || cctor != null) + { + lw.WriteLine(); + lw.WriteLine("// ================== GLOBAL METHODS ========================="); + lw.WriteLine(); + foreach (var method in methods) + { + if (cctor != null && method.MetadataToken > cctor.MetadataToken) + { + WriteMethod(lw, cctor); + cctor = null; + } + WriteMethod(lw, method); + } + if (cctor != null) + { + WriteMethod(lw, cctor); + } + lw.WriteLine(); + lw.WriteLine("// ============================================================="); + lw.WriteLine(); + } + } + + void WriteUnhandledCustomAttributes(LineWriter lw) + { + foreach (var ca in module.__EnumerateCustomAttributeTable()) + { + int parent = ca.__Parent; + switch (parent >> 24) + { + case 0x00: // Module + case 0x02: // TypeDef + case 0x04: // Field + case 0x06: // MethodDef + case 0x08: // Param + case 0x14: // Event + case 0x17: // Property + case 0x20: // Assembly + case 0x2a: // GenericParam + break; + case 0x01: // TypeRef + lw.Write(".custom ("); + int level = lw.Column - 1; + WriteTypeDefOrRef(lw, module.ResolveType(parent)); + lw.Write(") "); + WriteCustomAttributeImpl(lw, ca, false, level); + break; + default: + if (compat == CompatLevel.V20 || compat == CompatLevel.V40 || (parent >> 24) != 0x09) + { + lw.Write(".custom (UNKNOWN_OWNER) "); + WriteCustomAttributeImpl(lw, ca, false, lw.Column - 16); + } + break; + } + } + } + + void WriteGlobalFields(LineWriter lw) + { + FieldInfo[] fields = module.GetFields(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Static); + if (fields.Length != 0) + { + lw.WriteLine(); + lw.WriteLine(); + lw.WriteLine("// ================== GLOBAL FIELDS =========================="); + lw.WriteLine(); + foreach (var field in fields) + { + WriteField(lw, field); + } + lw.WriteLine(); + lw.WriteLine("// ============================================================="); + } + lw.WriteLine(); + } + + void WriteModules(LineWriter lw) + { + foreach (var module in assembly.GetModules(true)) + { + if (module != this.module) + { + lw.WriteLine(module.IsResource() ? ".file nometadata {0}" : ".file {0}", QuoteIdentifier(module.Name)); + byte[] hash = module.__ModuleHash; + if (hash != null) + { + lw.Write(" .hash = ("); + WriteBytes(lw, hash, false); + lw.WriteLine(); + } + } + } + } + + void WriteMscorlibDirective(LineWriter lw) + { + Type obj = assembly.GetType("System.Object"); + if (obj != null && !obj.__IsMissing && obj.BaseType == null && obj.IsClass) + { + lw.WriteLine(); + lw.WriteLine(".mscorlib "); + lw.WriteLine(); + } + } + + int GetPointerSize() + { + PortableExecutableKinds peKind; + ImageFileMachine machine; + module.GetPEKind(out peKind, out machine); + if ((peKind & PortableExecutableKinds.PE32Plus) != 0) + { + return 8; + } + else + { + return 4; + } + } + + int GetTypeSize(Type type) + { + int packingSize; + int typeSize; + if (!type.__IsMissing && type.IsEnum) + { + type = type.GetEnumUnderlyingType(); + } + if (type.__IsFunctionPointer || type.IsPointer || type == typeofSystemIntPtr || type == typeofSystemUIntPtr) + { + typeSize = GetPointerSize(); + } + else if (!type.__GetLayout(out packingSize, out typeSize)) + { + if (type == typeofSystemSByte + || type == typeofSystemByte + || type == typeofSystemBoolean) + { + typeSize = 1; + } + else if (type == typeofSystemInt16 + || type == typeofSystemUInt16 + || type == typeofSystemChar) + { + typeSize = 2; + } + else if (type == typeofSystemInt32 + || type == typeofSystemUInt32 + || type == typeofSystemSingle) + { + typeSize = 4; + } + else if (type == typeofSystemInt64 + || type == typeofSystemUInt64 + || type == typeofSystemDouble) + { + typeSize = 8; + } + } + return typeSize; + } + + struct DataPointer : IComparable<DataPointer> + { + internal readonly int RVA; + internal readonly int Size; + + internal DataPointer(int rva, int size) + { + this.RVA = rva; + this.Size = size; + } + + public int CompareTo(DataPointer other) + { + return RVA.CompareTo(other.RVA); + } + } + + void WriteData(LineWriter lw) + { + List<DataPointer> ptrs = new List<DataPointer>(); + foreach (VTableFixups fixup in GetVTableFixups()) + { + ptrs.Add(new DataPointer(fixup.RVA, fixup.Count * ((fixup.Type & COR_VTABLE_32BIT) != 0 ? 4 : 8))); + } + foreach (FieldInfo field in dataFields) + { + ptrs.Add(new DataPointer(field.__FieldRVA, GetTypeSize(field.FieldType))); + } + ptrs.Sort(); + for (int i = 0; i < ptrs.Count - 1; i++) + { + if (ptrs[i].RVA == ptrs[i + 1].RVA) + { + ptrs.RemoveAt(i + 1); + i--; + } + } + for (int i = 0; i < ptrs.Count; i++) + { + int rva = ptrs[i].RVA; + string name; + int characteristics; + int virtualAddress; + int virtualSize; + int pointerToRawData; + int sizeOfRawData; + if (!module.__GetSectionInfo(rva, out name, out characteristics, out virtualAddress, out virtualSize, out pointerToRawData, out sizeOfRawData)) + { + continue; + } + int alignment = 0; + int size = ptrs[i].Size; + if (rva + size >= virtualAddress + virtualSize) + { + size = virtualAddress + virtualSize - rva; + } + else if (i == ptrs.Count - 1) + { + // we're at the last pointer + } + else if (rva + size >= ptrs[i + 1].RVA) + { + size = ptrs[i + 1].RVA - rva; + } + else if (rva + size < virtualAddress + sizeOfRawData) + { + int next = ptrs[i + 1].RVA; + int align = (next & (~next + 1)) - 1; + alignment = ((rva + size + align) & ~align) - (rva + size); + } + WriteData(lw, name, rva, size, rva >= virtualAddress + sizeOfRawData); + if (alignment != 0) + { + WriteData(lw, name, rva + size, alignment, true); + } + } + } + + void WriteData(LineWriter lw, string section, int rva, int size, bool uninitialized) + { + lw.Write(".data "); + switch (section) + { + case ".text": + lw.Write("cil I_"); + break; + case ".tls": + lw.Write("tls T_"); + break; + default: + lw.Write("D_"); + break; + } + if (diffMode) + { + lw.Write("xxxxxxxx = "); + } + else + { + lw.Write("{0:X8} = ", rva); + } + if (uninitialized) + { + lw.WriteLine("int8[{0}]", size); + } + else + { + lw.WriteLine("bytearray ("); + lw.GoToColumn(17); + byte[] buf = new byte[size]; + module.__ReadDataFromRVA(rva, buf, 0, buf.Length); + WriteBytes(lw, buf, true); + lw.WriteLine(); + } + } + + void WriteNativeResources(LineWriter lw) + { + int rva; + int length; + module.__GetDataDirectoryEntry(2, out rva, out length); + if (rva != 0 && outputFile != null && !diffMode) + { + lw.WriteLine("// WARNING: Created Win32 resource file {0}", System.IO.Path.ChangeExtension(outputFile, "res")); + } + } + + void WriteCopyrightHeader(LineWriter lw) + { + if (compat == CompatLevel.V20) + { + lw.WriteLine(); + lw.WriteLine("// Microsoft (R) .NET Framework IL Disassembler. Version 2.0.50727.42"); + lw.WriteLine("// Copyright (c) Microsoft Corporation. All rights reserved."); + lw.WriteLine(); + lw.WriteLine(); + } + else if (compat == CompatLevel.V40) + { + lw.WriteLine(); + lw.WriteLine("// Microsoft (R) .NET Framework IL Disassembler. Version 4.0.30319.1"); + lw.WriteLine("// Copyright (c) Microsoft Corporation. All rights reserved."); + lw.WriteLine(); + lw.WriteLine(); + } + else if (compat == CompatLevel.V45) + { + lw.WriteLine(); + lw.WriteLine("// Microsoft (R) .NET Framework IL Disassembler. Version 4.0.30319.17929"); + lw.WriteLine("// Copyright (c) Microsoft Corporation. All rights reserved."); + lw.WriteLine(); + lw.WriteLine(); + } + } + + void WriteTypes(LineWriter lw) + { + Type[] types = module.GetTypes(); + if (types.Length != 0) + { + lw.WriteLine(); + lw.WriteLine("// =============== CLASS MEMBERS DECLARATION ==================="); + lw.WriteLine(); + foreach (var type in types) + { + if (!type.IsNested) + { + WriteType(lw, type); + } + } + lw.WriteLine(); + lw.WriteLine("// ============================================================="); + lw.WriteLine(); + } + } + + void WriteType(LineWriter lw, IKVM.Reflection.Type type) + { + int level = lw.Column; + lw.Write(".class "); + if (type.IsInterface) + { + lw.Write("interface "); + } + if (type.IsPublic) + { + lw.Write("public "); + } + else if (!type.IsNested) + { + lw.Write("private "); + } + if (type.IsAbstract) + { + lw.Write("abstract "); + } + switch (type.StructLayoutAttribute.Value) + { + case System.Runtime.InteropServices.LayoutKind.Auto: + lw.Write("auto "); + break; + case System.Runtime.InteropServices.LayoutKind.Sequential: + lw.Write("sequential "); + break; + case System.Runtime.InteropServices.LayoutKind.Explicit: + lw.Write("explicit "); + break; + } + switch (type.Attributes & TypeAttributes.StringFormatMask) + { + case TypeAttributes.AnsiClass: + lw.Write("ansi "); + break; + case TypeAttributes.UnicodeClass: + lw.Write("unicode "); + break; + case TypeAttributes.AutoClass: + lw.Write("autochar "); + break; + } + if ((type.Attributes & TypeAttributes.Import) != 0) + { + lw.Write("import "); + } + if ((type.Attributes & TypeAttributes.Serializable) != 0) + { + lw.Write("serializable "); + } + if ((type.Attributes & TypeAttributes.WindowsRuntime) != 0 && (compat == CompatLevel.None || compat >= CompatLevel.V45)) + { + lw.Write("windowsruntime "); + } + if (type.IsSealed) + { + lw.Write("sealed "); + } + switch (type.Attributes & TypeAttributes.VisibilityMask) + { + case TypeAttributes.NestedPublic: + lw.Write("nested public "); + break; + case TypeAttributes.NestedPrivate: + lw.Write("nested private "); + break; + case TypeAttributes.NestedAssembly: + lw.Write("nested assembly "); + break; + case TypeAttributes.NestedFamily: + lw.Write("nested family "); + break; + case TypeAttributes.NestedFamORAssem: + lw.Write("nested famorassem "); + break; + } + if ((type.Attributes & TypeAttributes.BeforeFieldInit) != 0) + { + lw.Write("beforefieldinit "); + } + if ((type.Attributes & TypeAttributes.SpecialName) != 0) + { + lw.Write("specialname "); + } + WriteTypeNameNoOuter(lw, type); + if (type.IsGenericTypeDefinition) + { + WriteGenericParameterDef(lw, type.GetGenericArguments(), true); + } + lw.WriteLine(); + lw.GoToColumn(level); + if (type.BaseType != null) + { + lw.Write(" extends "); + if (type.BaseType.__IsMissing || !type.BaseType.IsGenericType) + { + WriteTypeDefOrRef(lw, type.BaseType); + } + else + { + WriteSignatureType(lw, type.BaseType, TypeLocation.General); + } + lw.WriteLine(); + lw.GoToColumn(level); + } + Type[] interfaces = type.__GetDeclaredInterfaces(); + if (interfaces.Length != 0) + { + lw.Write(" implements "); + bool first = true; + foreach (var iface in interfaces) + { + if (!first) + { + lw.WriteLine(","); + lw.GoToColumn(level + 18); + } + first = false; + if (iface.__IsMissing || !iface.IsGenericType) + { + WriteTypeDefOrRef(lw, iface); + } + else + { + WriteSignatureType(lw, iface, TypeLocation.General); + } + } + lw.WriteLine(); + lw.GoToColumn(level); + } + lw.WriteLine("{"); + int packingSize; + int typeSize; + if (type.__GetLayout(out packingSize, out typeSize)) + { + lw.GoToColumn(level + 2); + lw.WriteLine(".pack {0}", packingSize); + lw.GoToColumn(level + 2); + lw.WriteLine(".size {0}", typeSize); + } + WriteCustomAttributes(lw, level + 2, type.__GetCustomAttributes(null, false)); + WriteDeclarativeSecurity(lw, level + 2, CustomAttributeData.__GetDeclarativeSecurity(type), type.MetadataToken); + WriteGenericParameterCustomAttributes(lw, level + 2, type.GetGenericArguments()); + if (compat == CompatLevel.None || compat >= CompatLevel.V45) + { + foreach (var iface in type.__GetDeclaredInterfaces()) + { + var cas = CustomAttributeData.__GetCustomAttributes(type, iface, null, false); + if (cas.Count != 0) + { + lw.GoToColumn(level + 2); + lw.Write(".interfaceimpl type "); + WriteTypeDefOrRef(lw, iface); + lw.WriteLine(); + WriteCustomAttributes(lw, level + 2, cas); + } + } + } + foreach (var nested in type.__GetDeclaredTypes()) + { + lw.GoToColumn(level + 2); + WriteType(lw, nested); + } + IEnumerable<FieldInfo> fields = type.__GetDeclaredFields(); + if (diffMode) + { + fields = fields.OrderBy(x => x.Name); + } + foreach (var field in fields) + { + lw.GoToColumn(level + 2); + WriteField(lw, field); + } + foreach (var method in type.__GetDeclaredMethods()) + { + lw.GoToColumn(level + 2); + WriteMethod(lw, method); + } + foreach (var evt in type.__GetDeclaredEvents()) + { + lw.GoToColumn(level + 2); + WriteEvent(lw, evt); + } + foreach (var prop in type.__GetDeclaredProperties()) + { + lw.GoToColumn(level + 2); + WriteProperty(lw, prop); + } + lw.GoToColumn(level); + lw.Write("} // end of class "); + WriteTypeNameNoOuter(lw, type); + lw.WriteLine(); + lw.WriteLine(); + } + + void WriteGenericParameterCustomAttributes(LineWriter lw, int level, Type[] args) + { + foreach (var typeParam in args) + { + IList<CustomAttributeData> cas = CustomAttributeData.__GetCustomAttributes(typeParam, null, false); + if (cas.Count != 0) + { + lw.GoToColumn(level); + lw.WriteLine(".param type {0} ", QuoteIdentifier(typeParam.Name)); + WriteCustomAttributes(lw, level, cas); + } + } + } + + void WriteGenericParameterDef(LineWriter lw, Type[] parameters, bool wrap) + { + lw.Write("<"); + int level = lw.Column; + string sep = ""; + for (int i = 0; i < parameters.Length; i++) + { + Type par = parameters[i]; + lw.Write(sep); + if (wrap && i != 0 && i % 4 == 0) + { + lw.WriteLine(); + lw.GoToColumn(level); + } + if ((par.GenericParameterAttributes & GenericParameterAttributes.Contravariant) != 0) + { + lw.Write("- "); + } + if ((par.GenericParameterAttributes & GenericParameterAttributes.Covariant) != 0) + { + lw.Write("+ "); + } + if ((par.GenericParameterAttributes & GenericParameterAttributes.ReferenceTypeConstraint) != 0) + { + lw.Write("class "); + } + if ((par.GenericParameterAttributes & GenericParameterAttributes.NotNullableValueTypeConstraint) != 0) + { + lw.Write("valuetype "); + } + if ((par.GenericParameterAttributes & GenericParameterAttributes.DefaultConstructorConstraint) != 0) + { + lw.Write(".ctor "); + } + string sep2 = "("; + foreach (var constraint in par.GetGenericParameterConstraints()) + { + lw.Write(sep2); + sep2 = ", "; + if (constraint.__IsMissing || !constraint.IsGenericType) + { + WriteTypeDefOrRef(lw, constraint); + } + else + { + WriteSignatureType(lw, constraint); + } + } + if (sep2 != "(") + { + lw.Write(") "); + } + lw.Write("{0}", QuoteIdentifier(par.Name)); + sep = ","; + } + lw.Write(">"); + } + + void WriteEvent(LineWriter lw, EventInfo evt) + { + int level = lw.Column; + lw.Write(".event "); + if (evt.IsSpecialName) + { + lw.Write("specialname "); + } + if (!evt.EventHandlerType.__IsMissing && evt.EventHandlerType.IsGenericType && !evt.EventHandlerType.IsGenericTypeDefinition) + { + WriteSignatureType(lw, evt.EventHandlerType, TypeLocation.General); + } + else + { + WriteTypeDefOrRef(lw, evt.EventHandlerType); + } + lw.WriteLine(" {0}", QuoteIdentifier(evt.Name)); + lw.GoToColumn(level); + lw.WriteLine("{"); + WriteCustomAttributes(lw, level + 2, evt.__GetCustomAttributes(null, false)); + IEnumerable<MethodInfo> accessors = evt.__GetMethods(); + if (diffMode) + { + accessors = accessors.OrderBy(x => x.Name); + } + foreach (var acc in accessors) + { + lw.GoToColumn(level + 2); + if (acc == evt.GetAddMethod(true)) + { + lw.Write(".addon "); + } + else if (acc == evt.GetRemoveMethod(true)) + { + lw.Write(".removeon "); + } + else if (acc == evt.GetRaiseMethod(true)) + { + lw.Write(".fire "); + } + else + { + lw.Write(".other "); + } + WriteCallingConvention(lw, acc.CallingConvention); + WriteSignatureType(lw, acc.ReturnType); + lw.Write(" "); + WriteTypeDefOrRef(lw, acc.DeclaringType); + lw.Write("::{0}(", QuoteIdentifier(GetMethodName(acc))); + WriteParameters(lw, acc.GetParameters()); + lw.WriteLine(")"); + } + lw.GoToColumn(level); + lw.WriteLine("}} // end of event {0}::{1}", QuoteIdentifier(evt.DeclaringType.__Name), QuoteIdentifier(evt.Name)); + } + + void WriteProperty(LineWriter lw, PropertyInfo prop) + { + int level = lw.Column; + lw.Write(".property "); + if ((prop.Attributes & PropertyAttributes.SpecialName) != 0) + { + lw.Write("specialname "); + } + if ((prop.Attributes & PropertyAttributes.RTSpecialName) != 0) + { + lw.Write("rtspecialname "); + } + if (lw.Column > 40) + { + lw.WriteLine(); + lw.GoToColumn(level + 8); + } + WriteCallingConvention(lw, prop.__CallingConvention); + WriteSignatureType(lw, prop.PropertyType); + if (lw.Column > 40) + { + lw.WriteLine(); + lw.GoToColumn(level + 7); + } + lw.Write(" {0}(", QuoteIdentifier(prop.Name)); + WriteParameters(lw, prop.GetIndexParameters()); + lw.WriteLine(")"); + lw.GoToColumn(level); + lw.WriteLine("{"); + WriteCustomAttributes(lw, level + 2, prop.__GetCustomAttributes(null, false)); + IEnumerable<MethodInfo> accessors = prop.GetAccessors(true); + if (diffMode) + { + accessors = accessors.OrderBy(x => x.Name); + } + foreach (var acc in accessors) + { + lw.GoToColumn(level + 2); + if (acc == prop.GetGetMethod(true)) + { + lw.Write(".get "); + } + else if (acc == prop.GetSetMethod(true)) + { + lw.Write(".set "); + } + else + { + lw.Write(".other "); + } + WriteCallingConvention(lw, acc.CallingConvention); + WriteSignatureType(lw, acc.ReturnType); + WriteCustomModifiers(lw, acc.ReturnParameter.__GetCustomModifiers()); + lw.Write(" "); + WriteTypeDefOrRef(lw, acc.DeclaringType); + lw.Write("::{0}(", QuoteIdentifier(GetMethodName(acc))); + WriteParameters(lw, acc.GetParameters()); + lw.WriteLine(")"); + } + lw.GoToColumn(level); + lw.WriteLine("}} // end of property {0}::{1}", QuoteIdentifier(prop.DeclaringType.Name), QuoteIdentifier(prop.Name)); + } + + void WriteParameters(LineWriter lw, ParameterInfo[] parameters) + { + int level = lw.Column; + bool first = true; + foreach (var parameter in parameters) + { + if (!first) + { + lw.WriteLine(","); + lw.GoToColumn(level); + } + first = false; + WriteSignatureType(lw, parameter.ParameterType); + WriteCustomModifiers(lw, parameter.__GetCustomModifiers()); + } + } + + void WriteMethod(LineWriter lw, MethodBase method) + { + int level0 = lw.Column; + lw.Write(".method "); + int level1 = lw.Column; + switch (method.Attributes & MethodAttributes.MemberAccessMask) + { + case MethodAttributes.Public: + lw.Write("public "); + break; + case MethodAttributes.Private: + lw.Write("private "); + break; + case MethodAttributes.Assembly: + lw.Write("assembly "); + break; + case MethodAttributes.Family: + lw.Write("family "); + break; + case MethodAttributes.FamORAssem: + lw.Write("famorassem "); + break; + case MethodAttributes.PrivateScope: + lw.Write("privatescope "); + break; + } + if ((method.Attributes & MethodAttributes.HideBySig) != 0) + { + lw.Write("hidebysig "); + } + if ((method.Attributes & MethodAttributes.NewSlot) != 0) + { + lw.Write("newslot "); + } + if ((method.Attributes & MethodAttributes.SpecialName) != 0) + { + lw.Write("specialname "); + } + if ((method.Attributes & MethodAttributes.RTSpecialName) != 0) + { + lw.Write("rtspecialname "); + } + if ((method.Attributes & MethodAttributes.Abstract) != 0) + { + lw.Write("abstract "); + } + if ((method.Attributes & MethodAttributes.CheckAccessOnOverride) != 0) + { + lw.Write("strict "); + } + if ((method.Attributes & MethodAttributes.Virtual) != 0) + { + lw.Write("virtual "); + } + if ((method.Attributes & MethodAttributes.Final) != 0) + { + lw.Write("final "); + } + if ((method.Attributes & MethodAttributes.Static) != 0) + { + lw.Write("static "); + } + if ((method.Attributes & MethodAttributes.RequireSecObject) != 0) + { + lw.Write("reqsecobj "); + } + if ((method.Attributes & MethodAttributes.PinvokeImpl) != 0) + { + WritePInvokeImpl(lw, method); + } + if (lw.Column > 40) + { + lw.WriteLine(); + lw.GoToColumn(level1); + } + WriteCallingConvention(lw, method.CallingConvention); + ParameterInfo returnParameter = method is ConstructorInfo ? ((ConstructorInfo)method).__ReturnParameter : ((MethodInfo)method).ReturnParameter; + WriteInOutOpt(lw, returnParameter); + WriteSignatureType(lw, returnParameter.ParameterType); + WriteCustomModifiers(lw, returnParameter.__GetCustomModifiers()); + lw.Write(" "); + if ((returnParameter.Attributes & ParameterAttributes.HasFieldMarshal) != 0) + { + var sb = new StringBuilder(); + FieldMarshal marshal; + WriteMarshalAs(sb, level1 - 1, returnParameter.__TryGetFieldMarshal(out marshal), marshal, false); + bool wrap = lw.Column + sb.Length > 40; + if (wrap) + { + lw.WriteLine(); + lw.GoToColumn(level1 - 1); + } + lw.Write("{0}", sb); + if (wrap) + { + lw.WriteLine(); + lw.GoToColumn(level1 - 1); + } + } + if (lw.Column > 40) + { + lw.WriteLine(); + lw.GoToColumn(level1 - 1); + } + lw.Write(" {0}", QuoteIdentifier(GetMethodName(method))); + if (method.IsGenericMethodDefinition) + { + WriteGenericParameterDef(lw, method.GetGenericArguments(), false); + } + lw.Write("("); + int level2 = lw.Column; + bool first = true; + foreach (var parameter in method.GetParameters()) + { + if (!first) + { + lw.WriteLine(","); + lw.GoToColumn(level2); + } + first = false; + WriteInOutOpt(lw, parameter); + WriteSignatureType(lw, parameter.ParameterType, TypeLocation.Parameter); + WriteCustomModifiers(lw, parameter.__GetCustomModifiers()); + lw.Write(" "); + if ((parameter.Attributes & ParameterAttributes.HasFieldMarshal) != 0) + { + FieldMarshal marshal; + WriteMarshalAs(lw, level2, parameter.__TryGetFieldMarshal(out marshal), marshal, true); + } + if (parameter.Name == null) + { + lw.Write("A_{0}", parameter.Position + (method.IsStatic ? 0 : 1)); + } + else + { + lw.Write("{0}", QuoteIdentifier(parameter.Name)); + } + } + lw.Write(")"); + MethodImplAttributes implflags = method.GetMethodImplementationFlags(); + switch (implflags & MethodImplAttributes.CodeTypeMask) + { + case MethodImplAttributes.IL: + lw.Write(" cil"); + break; + case MethodImplAttributes.Native: + lw.Write(" native"); + break; + case MethodImplAttributes.OPTIL: + lw.Write(" optil"); + break; + case MethodImplAttributes.Runtime: + lw.Write(" runtime"); + break; + } + if ((implflags & MethodImplAttributes.ManagedMask) == MethodImplAttributes.Managed) + { + lw.Write(" managed"); + } + else + { + lw.Write(" unmanaged"); + } + if ((implflags & MethodImplAttributes.Synchronized) != 0) + { + lw.Write(" synchronized"); + } + if ((implflags & MethodImplAttributes.NoInlining) != 0) + { + lw.Write(" noinlining"); + } + if ((implflags & MethodImplAttributes.PreserveSig) != 0) + { + lw.Write(" preservesig"); + } + if ((implflags & MethodImplAttributes.InternalCall) != 0) + { + lw.Write(" internalcall"); + } + if ((implflags & MethodImplAttributes.ForwardRef) != 0) + { + lw.Write(" forwardref"); + } + if ((implflags & MethodImplAttributes.NoOptimization) != 0 && compat != CompatLevel.V20) + { + lw.Write(" nooptimization"); + } + if ((implflags & MethodImplAttributes.AggressiveInlining) != 0 && compat != CompatLevel.V20 && compat != CompatLevel.V40) + { + lw.Write(" aggressiveinlining"); + } + lw.WriteLine(); + lw.GoToColumn(level0); + lw.WriteLine("{"); + if (method.MetadataToken == module.__EntryPointToken) + { + lw.GoToColumn(level0 + 2); + lw.WriteLine(".entrypoint"); + } + WriteCustomAttributes(lw, level0 + 2, method.__GetCustomAttributes(null, false)); + WriteGenericParameterCustomAttributes(lw, level0 + 2, method.GetGenericArguments()); + WriteParam(lw, level0 + 2, method is ConstructorInfo ? ((ConstructorInfo)method).__ReturnParameter : ((MethodInfo)method).ReturnParameter); + foreach (var parameter in method.GetParameters()) + { + WriteParam(lw, level0 + 2, parameter); + } + WriteDeclarativeSecurity(lw, level0 + 2, CustomAttributeData.__GetDeclarativeSecurity(method), method.MetadataToken); + List<KeyValuePair<int, int>> vtentries; + if (vtentryMap.TryGetValue(method, out vtentries)) + { + foreach (var vtentry in vtentries) + { + lw.GoToColumn(level0 + 2); + lw.WriteLine(".vtentry {0} : {1}", vtentry.Key, vtentry.Value); + } + } + List<ExportedMethod> exports; + if (exportedMethods.TryGetValue(method.MetadataToken, out exports)) + { + foreach (var export in exports) + { + lw.GoToColumn(level0 + 2); + lw.WriteLine(".export [{0}] as {1}", export.ordinal, QuoteIdentifier(export.name)); + } + } + if (method.DeclaringType != null && method is MethodInfo) + { + foreach (var impl in ((MethodInfo)method).__GetMethodImpls()) + { + lw.GoToColumn(level0 + 2); + lw.Write(".override "); + if (!impl.DeclaringType.__IsMissing && impl.DeclaringType.IsGenericType) + { + lw.Write(" method "); + WriteInlineMethod(lw, impl, Type.EmptyTypes, null, (MethodInfo)method); + lw.WriteLine(); + } + else + { + WriteTypeDefOrRef(lw, impl.DeclaringType); + lw.Write("::"); + lw.WriteLine("{0}", QuoteIdentifier(GetMethodName(impl))); + } + } + } + MethodBody body = method.GetMethodBody(); + if (body != null) + { + lw.GoToColumn(level0 + 2); + WriteIL(lw, method, body, method.DeclaringType == null ? null : method.DeclaringType.GetGenericArguments(), method.GetGenericArguments()); + } + else if ((implflags & MethodImplAttributes.CodeTypeMask) == MethodImplAttributes.Native) + { + lw.GoToColumn(level0 + 2); + lw.WriteLine("// Embedded native code"); + lw.GoToColumn(level0 + 2); + lw.WriteLine("// Disassembly of native methods is not supported."); + lw.GoToColumn(level0 + 2); + lw.WriteLine("// Managed TargetRVA = 0x{0:X8}", method.__MethodRVA); + } + lw.GoToColumn(level0); + if ((method.Attributes & MethodAttributes.PinvokeImpl) != 0 && (implflags & MethodImplAttributes.CodeTypeMask) != MethodImplAttributes.Native) + { + lw.WriteLine("}"); + } + else + { + string methodName = GetMethodName(method); + if ((method.Attributes & MethodAttributes.MemberAccessMask) == MethodAttributes.PrivateScope) + { + methodName = methodName.Substring(0, methodName.Length - 12); + } + if (method.DeclaringType == null) + { + lw.WriteLine("}} // end of global method {0}", QuoteIdentifier(methodName)); + } + else + { + lw.WriteLine("}} // end of method {0}::{1}", QuoteIdentifier(method.DeclaringType.__Name), QuoteIdentifier(methodName)); + } + lw.WriteLine(); + } + } + + string GetMethodName(MethodBase method) + { + if (method.Module == module && (method.DeclaringType == null || !method.DeclaringType.IsArray)) + { + return methodNames[method.__GetMethodOnTypeDefinition().MetadataToken - 0x06000001]; + } + else + { + return method.Name; + } + } + + static void WriteInOutOpt(LineWriter lw, ParameterInfo parameter) + { + if (parameter.IsIn || parameter.IsOut || parameter.IsOptional) + { + if (parameter.IsIn) + { + lw.Write("[in]"); + } + if (parameter.IsOut) + { + lw.Write("[out]"); + } + if (parameter.IsOptional) + { + lw.Write("[opt]"); + } + lw.Write(" "); + } + } + + void WriteParam(LineWriter lw, int level, ParameterInfo parameter) + { + IList<CustomAttributeData> ca = parameter.__GetCustomAttributes(null, false); + if (ca.Count != 0 || (parameter.Attributes & ParameterAttributes.HasDefault) != 0) + { + lw.GoToColumn(level); + lw.Write(".param [{0}]", parameter.Position + 1); + if ((parameter.Attributes & ParameterAttributes.HasDefault) != 0) + { + lw.Write(" = "); + WriteConstant(lw, parameter.RawDefaultValue, level); + } + lw.WriteLine(); + WriteCustomAttributes(lw, level, ca); + } + } + + static string EscapePInvoke(string str) + { + return str + .Replace("\\", "\\\\") + .Replace("?", "\\?") + .Replace("\n", "\\n") + .Replace("\r", "\\r") + .Replace("\t", "\\t") + .Replace("\b", "\\b") + .Replace("\f", "\\f") + .Replace("\v", "\\v") + .Replace("\a", "\\a") + .Replace("\"", "\\\"") + ; + } + + void WritePInvokeImpl(LineWriter lw, MethodBase method) + { + lw.Write("pinvokeimpl("); + MethodInfo mi = method as MethodInfo; + ImplMapFlags flags; + string importName; + string importScope; + if (mi != null && mi.__TryGetImplMap(out flags, out importName, out importScope)) + { + if (importScope != null) + { + lw.Write("\"{0}\"", EscapePInvoke(importScope)); + } + if (importName != null && importName != method.Name) + { + lw.Write(" as \"{0}\"", EscapePInvoke(importName)); + } + if ((flags & ImplMapFlags.NoMangle) != 0) + { + lw.Write(" nomangle"); + } + switch (flags & ImplMapFlags.CharSetMask) + { + case ImplMapFlags.CharSetAnsi: + lw.Write(" ansi"); + break; + case ImplMapFlags.CharSetAuto: + lw.Write(" autochar"); + break; + case ImplMapFlags.CharSetUnicode: + lw.Write(" unicode"); + break; + } + if ((flags & ImplMapFlags.SupportsLastError) != 0) + { + lw.Write(" lasterr"); + } + switch (flags & ImplMapFlags.CallConvMask) + { + case ImplMapFlags.CallConvWinapi: + lw.Write(" winapi"); + break; + case ImplMapFlags.CallConvStdcall: + lw.Write(" stdcall"); + break; + case ImplMapFlags.CallConvCdecl: + lw.Write(" cdecl"); + break; + case ImplMapFlags.CallConvThiscall: + lw.Write(" thiscall"); + break; + } + switch (flags & (ImplMapFlags.BestFitOff | ImplMapFlags.BestFitOn)) + { + case ImplMapFlags.BestFitOn: + lw.Write(" bestfit:on"); + break; + case ImplMapFlags.BestFitOff: + lw.Write(" bestfit:off"); + break; + } + switch (flags & (ImplMapFlags.CharMapErrorOff | ImplMapFlags.CharMapErrorOn)) + { + case ImplMapFlags.CharMapErrorOn: + lw.Write(" charmaperror:on"); + break; + case ImplMapFlags.CharMapErrorOff: + lw.Write(" charmaperror:off"); + break; + } + lw.Write(") "); + } + else + { + lw.Write("/* No map */) "); + } + } + + void WriteMarshalAs(LineWriter lw, int level, bool hasFieldMarshalRecord, FieldMarshal marshal, bool wrap) + { + var sb = new StringBuilder(); + WriteMarshalAs(sb, level, hasFieldMarshalRecord, marshal, wrap); + lw.Write(sb.ToString()); + } + + void WriteMarshalAs(StringBuilder sb, int level, bool hasFieldMarshalRecord, FieldMarshal marshal, bool wrap) + { + if (!hasFieldMarshalRecord) + { + if (compat == CompatLevel.None) + { + sb.Append(" /*HasFieldMarshal*/ "); + } + return; + } + sb.Append(" marshal("); + short? sizeParamIndex = marshal.SizeParamIndex; + if (marshal.ArraySubType != null && marshal.UnmanagedType != System.Runtime.InteropServices.UnmanagedType.ByValArray) + { + sb.Append(UnmanagedTypeToString(marshal.ArraySubType.Value)); + switch (marshal.ArraySubType.Value) + { + case System.Runtime.InteropServices.UnmanagedType.IUnknown: + case System.Runtime.InteropServices.UnmanagedType.Interface: + if (marshal.SizeParamIndex == null) + { + sb.Append(" "); + } + else + { + sb.AppendFormat("(iidparam = {0}) ", marshal.SizeParamIndex.Value); + sizeParamIndex = null; + } + break; + } + } + if (marshal.SafeArraySubType != null && marshal.UnmanagedType == System.Runtime.InteropServices.UnmanagedType.SafeArray) + { + sb.Append(VarEnumToString(marshal.SafeArraySubType.Value)); + } + else if (marshal.UnmanagedType == System.Runtime.InteropServices.UnmanagedType.LPArray + && (marshal.SizeConst != null || sizeParamIndex != null)) + { + // don't write [] it's implied by the following sizeConst or sizeParamIndex + } + else + { + sb.Append(UnmanagedTypeToString(marshal.UnmanagedType)); + if (marshal.IidParameterIndex == null + && (marshal.UnmanagedType == System.Runtime.InteropServices.UnmanagedType.Interface + || marshal.UnmanagedType == System.Runtime.InteropServices.UnmanagedType.IUnknown)) + { + sb.Append(" "); + } + } + if (marshal.SizeConst != null) + { + if (marshal.UnmanagedType != System.Runtime.InteropServices.UnmanagedType.LPArray) + { + sb.Append(" "); + } + if (sizeParamIndex != null || (compat != CompatLevel.None && !IsFixed(marshal.UnmanagedType))) + { + sb.AppendFormat("[{0} + {1}]", marshal.SizeConst.Value, sizeParamIndex.GetValueOrDefault()); + } + else + { + sb.AppendFormat("[{0}]", marshal.SizeConst.Value); + } + } + else if (sizeParamIndex != null) + { + sb.AppendFormat("[ + {0}]", sizeParamIndex.Value); + } + if (marshal.SafeArrayUserDefinedSubType != null) + { + sb.AppendFormat(", \"{0}\"", marshal.SafeArrayUserDefinedSubType); + } + if (marshal.IidParameterIndex != null) + { + sb.AppendFormat("(iidparam = {0}) ", marshal.IidParameterIndex.Value); + } + if (marshal.MarshalType != null || marshal.MarshalCookie != null) + { + sb.AppendFormat("(\"{0}\",", marshal.MarshalType); + if (wrap) + { + sb.AppendLine(); + sb.Append(' ', level); + } + sb.AppendFormat("\"{0}\")", marshal.MarshalCookie); + } + if (marshal.ArraySubType != null && marshal.UnmanagedType == System.Runtime.InteropServices.UnmanagedType.ByValArray) + { + sb.Append(UnmanagedTypeToString(marshal.ArraySubType.Value)); + } + sb.Append(") "); + } + + static string VarEnumToString(System.Runtime.InteropServices.VarEnum value) + { + switch (value) + { + case System.Runtime.InteropServices.VarEnum.VT_VARIANT: + return " safearray variant"; + case System.Runtime.InteropServices.VarEnum.VT_I1: + return " safearray int8"; + case System.Runtime.InteropServices.VarEnum.VT_I2: + return " safearray int16"; + case System.Runtime.InteropServices.VarEnum.VT_I4: + return " safearray int32"; + case System.Runtime.InteropServices.VarEnum.VT_I8: + return " safearray int64"; + case System.Runtime.InteropServices.VarEnum.VT_UI1: + return " safearray unsigned int8"; + case System.Runtime.InteropServices.VarEnum.VT_UI2: + return " safearray unsigned int16"; + case System.Runtime.InteropServices.VarEnum.VT_UI4: + return " safearray unsigned int32"; + case System.Runtime.InteropServices.VarEnum.VT_UI8: + return " safearray unsigned int64"; + case System.Runtime.InteropServices.VarEnum.VT_R4: + return " safearray float32"; + case System.Runtime.InteropServices.VarEnum.VT_R8: + return " safearray float64"; + case System.Runtime.InteropServices.VarEnum.VT_BSTR: + return " safearray bstr"; + case System.Runtime.InteropServices.VarEnum.VT_BOOL: + return " safearray bool"; + case System.Runtime.InteropServices.VarEnum.VT_UNKNOWN: + return " safearray iunknown"; + case System.Runtime.InteropServices.VarEnum.VT_RECORD: + return " safearray record"; + case System.Runtime.InteropServices.VarEnum.VT_DISPATCH: + return " safearray idispatch"; + case System.Runtime.InteropServices.VarEnum.VT_EMPTY: + return " safearray"; + default: + Console.WriteLine(value); + return ""; + } + } + + static bool IsFixed(System.Runtime.InteropServices.UnmanagedType unmanagedType) + { + switch (unmanagedType) + { + case System.Runtime.InteropServices.UnmanagedType.ByValArray: + case System.Runtime.InteropServices.UnmanagedType.ByValTStr: + return true; + default: + return false; + } + } + + string UnmanagedTypeToString(System.Runtime.InteropServices.UnmanagedType unmanagedType) + { + switch (unmanagedType) + { + case System.Runtime.InteropServices.UnmanagedType.Bool: + return " bool"; + case System.Runtime.InteropServices.UnmanagedType.SysInt: + return " int"; + case System.Runtime.InteropServices.UnmanagedType.SysUInt: + return " uint"; + case System.Runtime.InteropServices.UnmanagedType.I1: + return " int8"; + case System.Runtime.InteropServices.UnmanagedType.I2: + return " int16"; + case System.Runtime.InteropServices.UnmanagedType.I4: + return " int32"; + case System.Runtime.InteropServices.UnmanagedType.I8: + return " int64"; + case System.Runtime.InteropServices.UnmanagedType.U1: + return " unsigned int8"; + case System.Runtime.InteropServices.UnmanagedType.U2: + return " unsigned int16"; + case System.Runtime.InteropServices.UnmanagedType.U4: + return " unsigned int32"; + case System.Runtime.InteropServices.UnmanagedType.U8: + return " unsigned int64"; + case System.Runtime.InteropServices.UnmanagedType.R4: + return " float32"; + case System.Runtime.InteropServices.UnmanagedType.R8: + return " float64"; + case System.Runtime.InteropServices.UnmanagedType.BStr: + return " bstr"; + case System.Runtime.InteropServices.UnmanagedType.LPWStr: + return " lpwstr"; + case System.Runtime.InteropServices.UnmanagedType.SafeArray: + return " safearray"; + case System.Runtime.InteropServices.UnmanagedType.ByValArray: + return " fixed array"; + case System.Runtime.InteropServices.UnmanagedType.ByValTStr: + return " fixed sysstring"; + case System.Runtime.InteropServices.UnmanagedType.IUnknown: + return " iunknown"; + case System.Runtime.InteropServices.UnmanagedType.Interface: + return " interface"; + case System.Runtime.InteropServices.UnmanagedType.CustomMarshaler: + return " custom "; + case System.Runtime.InteropServices.UnmanagedType.LPArray: + return "[]"; + case System.Runtime.InteropServices.UnmanagedType.AsAny: + return " as any"; + case System.Runtime.InteropServices.UnmanagedType.LPStruct: + return " lpstruct"; + case System.Runtime.InteropServices.UnmanagedType.IDispatch: + return " idispatch "; + case System.Runtime.InteropServices.UnmanagedType.Struct: + return " struct"; + case System.Runtime.InteropServices.UnmanagedType.FunctionPtr: + return " method"; + case System.Runtime.InteropServices.UnmanagedType.LPStr: + return " lpstr"; + case System.Runtime.InteropServices.UnmanagedType.Error: + return " error"; + case System.Runtime.InteropServices.UnmanagedType.LPTStr: + return " lptstr"; + case System.Runtime.InteropServices.UnmanagedType.VBByRefStr: + return " byvalstr"; + case System.Runtime.InteropServices.UnmanagedType.Currency: + return " currency"; + case System.Runtime.InteropServices.UnmanagedType.VariantBool: + return " variant bool"; + case (System.Runtime.InteropServices.UnmanagedType)80: + return ""; // Microsoft.SqlServer.DTSRuntimeWrap.dll (9.0.242.0) has this bogus value + case (System.Runtime.InteropServices.UnmanagedType)46: + return compat == CompatLevel.None ? " iinspectable" : compat == CompatLevel.V45 ? "{ 2E }" : ""; + case (System.Runtime.InteropServices.UnmanagedType)47: + return compat == CompatLevel.None ? " hstring" : compat == CompatLevel.V45 ? "{ 2F }" : ""; + default: + Console.WriteLine("unsupported unmanagedType in marshal blob: {0}", unmanagedType); + return ""; + } + } + + string QuoteIdentifier(string str, bool quote = false) + { + if (diffMode) + { + if (str.StartsWith("$$method0x", StringComparison.Ordinal)) + { + return "'$$method0x...'"; + } + else if (str.Length == 68 && str.StartsWith("<PrivateImplementationDetails>{", StringComparison.Ordinal) && str.EndsWith("}", StringComparison.Ordinal)) + { + return "'<PrivateImplementationDetails>{nnnnnnnn-nnnn-nnnn-nnnn-nnnnnnnnnnnn}'"; + } + for (int pos, cur = 0; (pos = str.IndexOf("?A0x", cur)) != -1; cur = pos + 4) + { + str = str.Substring(0, pos + 4) + "________" + str.Substring(pos + 12); + } + } + quote |= str.Length > 0 && "0123456789".IndexOf(str[0]) != -1; + for (int i = str.IndexOf('.'); i != -1 && i < str.Length - 1; i = str.IndexOf('.', i + 1)) + { + if ("0123456789".IndexOf(str[i + 1]) != -1) + { + quote = true; + break; + } + } + foreach (char c in str) + { + if (c <= 32 || c >= 127) + { + quote = true; + break; + } + } + Escape(ref str, ref quote, "\\", "\\\\"); + Escape(ref str, ref quote, "'", "\\'"); + Escape(ref str, ref quote, "\"", "\\\""); + Escape(ref str, ref quote, "\t", "\\t"); + Escape(ref str, ref quote, "\r", "\\r"); + Escape(ref str, ref quote, "\n", "\\n"); + Escape(ref str, ref quote, "\a", "\\a"); + Escape(ref str, ref quote, "\b", "\\b"); + Escape(ref str, ref quote, "\u0000", "\\u0000"); + if (compat == CompatLevel.V20 || compat == CompatLevel.V40) + { + StringBuilder sb = null; + for (int i = 0; i < str.Length; i++) + { + if (str[i] >= 128) + { + if (sb == null) + { + sb = new StringBuilder(str); + } + sb[i] = '\uFFFD'; + } + } + if (sb != null) + { + str = sb.ToString(); + } + } + if (keywords.Contains(str)) + { + return "'" + str + "'"; + } + else if (quote + || str.IndexOf('<') != -1 + || str.IndexOf('>') != -1 + || str.StartsWith("?") + || (str.StartsWith(".") && str != ".ctor" && str != ".cctor") + || str.Contains(".?") + || str.Contains("..") + || str.IndexOf('{') != -1 + || str.IndexOf('}') != -1 + || str.IndexOf('[') != -1 + || str.IndexOf(']') != -1 + || str.IndexOf('!') != -1 + || str.IndexOf('/') != -1 + || str.IndexOf('=') != -1 + || str.IndexOf('+') != -1 + || str.IndexOf(':') != -1 + || str.IndexOf('|') != -1 + || str.IndexOf('~') != -1 + || str.IndexOf('^') != -1 + || str.IndexOf('-') != -1) + { + return "'" + str + "'"; + } + else + { + return str; + } + } + + static void Escape(ref string str, ref bool quote, string ch, string esc) + { + if (str.IndexOf(ch) != -1) + { + quote = true; + str = str.Replace(ch, esc); + } + } + + void WriteField(LineWriter lw, FieldInfo field) + { + int level = lw.Column; + lw.Write(".field "); + int offset; + if (field.__TryGetFieldOffset(out offset)) + { + lw.Write("[{0}] ", offset); + } + switch (field.Attributes & FieldAttributes.FieldAccessMask) + { + case FieldAttributes.Public: + lw.Write("public "); + break; + case FieldAttributes.Private: + lw.Write("private "); + break; + } + if ((field.Attributes & FieldAttributes.Static) != 0) + { + lw.Write("static "); + } + switch (field.Attributes & FieldAttributes.FieldAccessMask) + { + case FieldAttributes.Family: + lw.Write("family "); + break; + case FieldAttributes.Assembly: + lw.Write("assembly "); + break; + case FieldAttributes.FamORAssem: + lw.Write("famorassem "); + break; + case FieldAttributes.PrivateScope: + lw.Write("privatescope "); + break; + } + if ((field.Attributes & FieldAttributes.InitOnly) != 0) + { + lw.Write("initonly "); + } + if ((field.Attributes & FieldAttributes.Literal) != 0) + { + lw.Write("literal "); + } + if ((field.Attributes & FieldAttributes.SpecialName) != 0) + { + lw.Write("specialname "); + } + if ((field.Attributes & FieldAttributes.RTSpecialName) != 0) + { + lw.Write("rtspecialname "); + } + if ((field.Attributes & FieldAttributes.NotSerialized) != 0) + { + lw.Write("notserialized "); + } + if ((field.Attributes & FieldAttributes.HasFieldMarshal) != 0) + { + FieldMarshal marshal; + WriteMarshalAs(lw, level, field.__TryGetFieldMarshal(out marshal), marshal, false); + } + WriteSignatureType(lw, field.FieldType); + WriteCustomModifiers(lw, field.__GetCustomModifiers()); + lw.Write(" {0}", QuoteIdentifier(GetFieldName(field))); + if ((field.Attributes & FieldAttributes.HasDefault) != 0) + { + lw.Write(" = "); + WriteConstant(lw, field.GetRawConstantValue(), level); + } + if ((field.Attributes & FieldAttributes.HasFieldRVA) != 0) + { + dataFields.Add(field); + string name; + int characteristics; + if (module.__GetSectionInfo(field.__FieldRVA, out name, out characteristics)) + { + switch (name) + { + case ".text": + lw.Write(" at I_"); + break; + case ".tls": + lw.Write(" at T_"); + break; + default: + lw.Write(" at D_"); + break; + } + if (diffMode) + { + lw.Write("xxxxxxxx"); + } + else + { + lw.Write("{0:X8}", field.__FieldRVA); + } + } + else + { + lw.Write(" at 0x{0:X8} /* WARNING: rogue pointer! (size 0x{1:X8}) */", field.__FieldRVA, GetTypeSize(field.FieldType)); + } + } + lw.WriteLine(); + WriteCustomAttributes(lw, level, field.__GetCustomAttributes(null, false)); + } + + string GetFieldName(FieldInfo field) + { + if (field.Module == module) + { + return fieldNames[field.__GetFieldOnTypeDefinition().MetadataToken - 0x04000001]; + } + else + { + return field.Name; + } + } + + void WriteCustomModifiers(LineWriter lw, CustomModifiers mods) + { + foreach (var mod in mods.Reverse()) + { + lw.Write(" {0}(", mod.IsRequired ? "modreq" : "modopt"); + WriteTypeDefOrRef(lw, mod.Type); + lw.Write(")"); + } + } + + void WriteModuleManifest(LineWriter lw) + { + lw.WriteLine("// Metadata version: {0}", module.__ImageRuntimeVersion); + foreach (var refmodule in module.__GetReferencedModules()) + { + if (!String.IsNullOrEmpty(refmodule)) + { + lw.WriteLine(".module extern {0}", QuoteIdentifier(refmodule)); + } + } + AssemblyName[] referencedAssemblies = module.__GetReferencedAssemblies(); + for (int i = 0; i < referencedAssemblies.Length; i++) + { + AssemblyName asm = referencedAssemblies[i]; + lw.Write(".assembly extern {0}{1}", + asm.ContentType == AssemblyContentType.WindowsRuntime && (compat == CompatLevel.None || compat >= CompatLevel.V45) ? "windowsruntime " : "", + QuoteIdentifier(asm.Name)); + if (asm.Name != this.referencedAssemblies[resolvedAssemblies[i]]) + { + lw.Write(" as {0}", QuoteIdentifier(this.referencedAssemblies[resolvedAssemblies[i]])); + } + lw.WriteLine(); + lw.WriteLine("{"); + byte[] token = asm.GetPublicKeyToken(); + if (token != null && token.Length != 0) + { + lw.Write(" .publickeytoken = ("); + WriteBytes(lw, token, false); + lw.WriteLine(); + } + if (asm.__Hash != null) + { + lw.Write(" .hash = ("); + WriteBytes(lw, asm.__Hash, false); + lw.WriteLine(); + } + lw.WriteLine(" .ver {0}:{1}:{2}:{3}", asm.Version.Major, asm.Version.Minor, asm.Version.Build, asm.Version.Revision); + lw.WriteLine("}"); + } + } + + void WriteAssemblyManifest(LineWriter lw) + { + lw.WriteLine(".assembly {0}{1}{2}", + ((int)assembly.__AssemblyFlags & 16) != 0 ? "cil " : "", + ((int)assembly.__AssemblyFlags & 512) != 0 && (compat == CompatLevel.None || compat >= CompatLevel.V45) ? "windowsruntime " : "", + QuoteIdentifier(assembly.GetName().Name)); + lw.WriteLine("{"); + IEnumerable<CustomAttributeData> cas = assembly.__GetCustomAttributes(null, false); + if (diffMode) + { + cas = cas.OrderBy(x => x.Constructor.DeclaringType.FullName); + } + foreach (var ca in cas) + { + if (ca.Constructor.DeclaringType.FullName == "System.Diagnostics.DebuggableAttribute" + && ca.Constructor.DeclaringType.Assembly.GetName().Name == "mscorlib") + { + lw.WriteLine(); + lw.WriteLine(" // --- The following custom attribute is added automatically, do not uncomment -------"); + lw.Write(" // .custom "); + WriteCustomAttributeImpl(lw, ca, true, lw.Column); + lw.WriteLine(); + } + else + { + lw.Write(" "); + WriteCustomAttribute(lw, ca); + } + } + WriteDeclarativeSecurity(lw, 2, CustomAttributeData.__GetDeclarativeSecurity(assembly), 0x20000001); + byte[] publicKey = assembly.GetName().GetPublicKey(); + if (publicKey != null && publicKey.Length != 0) + { + lw.Write(" .publickey = ("); + WriteBytes(lw, publicKey, false); + lw.WriteLine(); + } + if (assembly.GetName().HashAlgorithm != 0) + { + lw.WriteLine(" .hash algorithm 0x{0:X8}", (int)assembly.GetName().HashAlgorithm); + } + lw.WriteLine(" .ver {0}:{1}:{2}:{3}", assembly.GetName().Version.Major, assembly.GetName().Version.Minor, assembly.GetName().Version.Build, assembly.GetName().Version.Revision); + if (assembly.GetName().CultureInfo != null) + { + string culture = assembly.GetName().CultureInfo.Name; + if (culture != "") + { + lw.Write(" .locale = ("); + byte[] buf = new byte[culture.Length * 2 + 2]; + for (int i = 0; i < culture.Length; i++) + { + buf[i * 2 + 0] = (byte)(culture[i] >> 0); + buf[i * 2 + 1] = (byte)(culture[i] >> 8); + } + WriteBytes(lw, buf, false); + lw.WriteLine(); + } + } + lw.WriteLine("}"); + } + + void WriteDeclarativeSecurity(LineWriter lw, int level, IList<CustomAttributeData> list, int metadataToken) + { + var action = (System.Security.Permissions.SecurityAction)(- 1); + var curr = new List<CustomAttributeData>(); + foreach (var sec in list) + { + if (action == (System.Security.Permissions.SecurityAction)sec.ConstructorArguments[0].Value) + { + curr.Add(sec); + } + else + { + WritePermissionSet(lw, level, action, curr, metadataToken); + curr.Clear(); + curr.Add(sec); + action = (System.Security.Permissions.SecurityAction)sec.ConstructorArguments[0].Value; + } + } + WritePermissionSet(lw, level, action, curr, metadataToken); + } + + void WritePermissionSet(LineWriter lw, int level, System.Security.Permissions.SecurityAction action, List<CustomAttributeData> list, int metadataToken) + { + if (list.Count != 0) + { + lw.GoToColumn(level); + lw.Write(".permissionset "); + switch (action) + { + case System.Security.Permissions.SecurityAction.Assert: + lw.Write("assert"); + break; + case System.Security.Permissions.SecurityAction.RequestMinimum: + lw.Write("reqmin"); + break; + case System.Security.Permissions.SecurityAction.RequestRefuse: + lw.Write("reqrefuse"); + break; + case System.Security.Permissions.SecurityAction.RequestOptional: + lw.Write("reqopt"); + break; + case System.Security.Permissions.SecurityAction.Demand: + lw.Write("demand"); + break; + case System.Security.Permissions.SecurityAction.LinkDemand: + lw.Write("linkcheck"); + break; + case System.Security.Permissions.SecurityAction.InheritanceDemand: + lw.Write("inheritcheck"); + break; + } + lw.WriteLine(); + lw.GoToColumn(level); + var sb = new StringBuilder(); + if (list.Count == 1) + { + try + { + var args = list[0].NamedArguments; + if (args.Count == 1 && args[0].MemberInfo.Name == "XML") + { + if (compat >= CompatLevel.V45) + { + // starting with 4.5 ildasm no longer supports the 1.1 format + lw.Write(" bytearray ("); + WriteBytes(lw, Encoding.Unicode.GetBytes((string)args[0].TypedValue.Value), false); + lw.WriteLine(); + return; + } + lw.Write(" "); + WriteInlineString(lw, (string)args[0].TypedValue.Value, level); + lw.WriteLine(); + return; + } + } + catch (IKVM.Reflection.MissingMemberException) { } + } + if (DecodeDeclSecurity(sb, list, level)) + { + lw.WriteLine("{0}", sb); + } + else + { + var mem = new System.IO.MemoryStream(); + mem.WriteByte((byte)'.'); + WriteCompressedInt(mem, list.Count); + foreach (var sec in list) + { + Write(mem, sec.Constructor.DeclaringType.AssemblyQualifiedName.Replace(", PublicKeyToken=null", "")); + byte[] buf = sec.__GetBlob(); + WriteCompressedInt(mem, buf.Length); + mem.Write(buf, 0, buf.Length); + } + byte[] blob = mem.ToArray(); + if ((compat == CompatLevel.V20 || compat == CompatLevel.V40) && (blob.Length & 1) == 1) + { + // ildasm bug http://connect.microsoft.com/VisualStudio/feedback/details/652653/ + Array.Resize(ref blob, blob.Length - 1); + } + lw.Write(" bytearray ("); + WriteBytes(lw, blob, false); + lw.WriteLine(); + } + } + } + + void Write(System.IO.MemoryStream mem, string str) + { + if (str == null) + { + mem.WriteByte((byte)0xFF); + } + else + { + byte[] buf = Encoding.UTF8.GetBytes(str); + WriteCompressedInt(mem, buf.Length); + mem.Write(buf, 0, buf.Length); + } + } + + void WriteCompressedInt(System.IO.MemoryStream mem, int value) + { + if (value <= 0x7F) + { + mem.WriteByte((byte)value); + } + else if (value <= 0x3FFF) + { + mem.WriteByte((byte)(0x80 | (value >> 8))); + mem.WriteByte((byte)value); + } + else + { + mem.WriteByte((byte)(0xC0 | (value >> 24))); + mem.WriteByte((byte)(value >> 16)); + mem.WriteByte((byte)(value >> 8)); + mem.WriteByte((byte)value); + } + } + + void WriteDeclarativeSecurity(LineWriter lw, int level, CustomAttributeData sec) + { + if (!typerefs.Contains(sec.Constructor.DeclaringType)) + { + lw.Write("class '{0}'", sec.Constructor.DeclaringType.AssemblyQualifiedName.Replace(", PublicKeyToken=null", "")); + } + else + { + WriteTypeDefOrRef(lw, sec.Constructor.DeclaringType); + } + lw.Write(" = {"); + bool first = true; + foreach (var arg in sec.NamedArguments) + { + if (!first) + { + lw.WriteLine(); + } + first = false; + lw.Write(arg.MemberInfo is PropertyInfo ? "property " : "field "); + if (!arg.TypedValue.ArgumentType.__IsMissing && arg.TypedValue.ArgumentType.IsEnum) + { + lw.Write("enum "); + WriteTypeDefOrRef(lw, arg.TypedValue.ArgumentType); + } + else + { + WriteSignatureType(lw, arg.TypedValue.ArgumentType); + } + lw.Write(" {0} = ", QuoteIdentifier(arg.MemberInfo.Name, true)); + WriteAttributeValue(lw, arg.TypedValue.Value); + } + lw.Write("}"); + } + + void WriteAttributeValue(LineWriter lw, object obj) + { + if (obj is byte) + { + lw.Write("uint8({0})", obj); + } + else if (obj is sbyte) + { + lw.Write("int8({0})", obj); + } + else if (obj is bool) + { + lw.Write("bool({0})", ((bool)obj) ? "true" : "false"); + } + else if (obj is char) + { + lw.Write("char(0x{0:X4})", (int)(char)obj); + } + else if (obj is short) + { + lw.Write("int16({0})", obj); + } + else if (obj is ushort) + { + lw.Write("uint16({0})", obj); + } + else if (obj is int) + { + lw.Write("int32({0})", obj); + } + else if (obj is uint) + { + lw.Write("uint32({0})", obj); + } + else if (obj is long) + { + lw.Write("int64({0})", obj); + } + else if (obj is ulong) + { + lw.Write("uint64({0})", obj); + } + else if (obj is float) + { + lw.Write("float32("); + WriteShortInlineR(lw, (float)obj, true); + lw.Write(")"); + } + else if (obj is double) + { + lw.Write("float64("); + WriteInlineR(lw, (double)obj, true); + lw.Write(")"); + } + else if (obj is string) + { + lw.Write("string({0})", QuoteIdentifier((string)obj, true)); + } + else if (obj == null) + { + lw.Write("nullref"); + } + } + + void WriteConstant(LineWriter lw, object obj, int level) + { + if (obj is byte) + { + lw.Write("uint8(0x{0:X2})", obj); + } + else if (obj is sbyte) + { + lw.Write("int8(0x{0:X2})", obj); + } + else if (obj is bool) + { + lw.Write("bool({0})", ((bool)obj) ? "true" : "false"); + } + else if (obj is char) + { + lw.Write("char(0x{0:X4})", (int)(char)obj); + } + else if (obj is short) + { + lw.Write("int16(0x{0:X4})", obj); + } + else if (obj is ushort) + { + lw.Write("uint16(0x{0:X4})", obj); + } + else if (obj is int) + { + lw.Write("int32(0x{0:X8})", obj); + } + else if (obj is uint) + { + lw.Write("uint32(0x{0:X8})", obj); + } + else if (obj is long) + { + lw.Write("int64(0x{0:X})", obj); + } + else if (obj is ulong) + { + lw.Write("uint64(0x{0:X})", obj); + } + else if (obj is float) + { + lw.Write("float32("); + WriteShortInlineR(lw, (float)obj, true); + lw.Write(")"); + } + else if (obj is double) + { + double v = (double)obj; + lw.Write("float64("); + WriteInlineR(lw, v, true); + lw.Write(")"); + if (Double.IsNegativeInfinity(v)) + { + lw.Write(" // -1.#INF"); + } + else if (Double.IsPositiveInfinity(v)) + { + lw.Write(" // 1.#INF"); + } + else if (Double.IsNaN(v)) + { + if (BitConverter.DoubleToInt64Bits(v) == 0x7FF8000000000000L) + { + lw.Write(" // 1.#QNAN"); + } + else + { + lw.Write(" // -1.#IND"); + } + } + } + else if (obj is string) + { + WriteInlineString(lw, (string)obj, level); + } + else if (obj == null) + { + lw.Write("nullref"); + } + } + + void WriteResources(LineWriter lw) + { + foreach (var resourceName in assembly.GetManifestResourceNames()) + { + ManifestResourceInfo mres = assembly.GetManifestResourceInfo(resourceName); + string access; + switch (mres.__ResourceAttributes) + { + case ResourceAttributes.Public: + access = "public "; + break; + case ResourceAttributes.Private: + access = "private "; + break; + default: + access = ""; + break; + } + lw.WriteLine(".mresource {0}{1}", access, QuoteIdentifier(resourceName)); + lw.WriteLine("{"); + if (mres.FileName != null) + { + lw.WriteLine(" .file {0} at 0x{1:x8}", QuoteIdentifier(mres.FileName), mres.__Offset); + } + else if (mres.ReferencedAssembly != null) + { + lw.WriteLine(" .assembly extern {0}", QuoteIdentifier(referencedAssemblies[mres.ReferencedAssembly])); + } + else + { + lw.WriteLine(" // Offset: 0x{0:X8} Length: 0x{1:X8}", mres.__Offset, assembly.GetManifestResourceStream(resourceName).Length); + lw.WriteLine(" // WARNING: managed resource file {0} created", QuoteIdentifier(resourceName)); + } + lw.WriteLine("}"); + } + } + + void WriteModuleHeader(LineWriter lw) + { + lw.WriteLine(".module {0}", QuoteIdentifier(module.ScopeName)); + if (!diffMode) + { + lw.WriteLine("// MVID: {0}", module.ModuleVersionId.ToString("B").ToUpperInvariant()); + } + WriteCustomAttributes(lw, 0, module.__GetCustomAttributes(null, false)); + if (compat == CompatLevel.V20 || GetPointerSize() == 4) + { + lw.WriteLine(".imagebase 0x{0:x8}", module.__ImageBase); + } + else + { + lw.WriteLine(".imagebase 0x{0:x16}", module.__ImageBase); + } + lw.WriteLine(".file alignment 0x{0:x8}", module.__FileAlignment); + if (GetPointerSize() == 4) + { + lw.WriteLine(".stackreserve 0x{0:x8}", module.__StackReserve); + } + else + { + lw.WriteLine(".stackreserve 0x{0:x16}", module.__StackReserve); + } + lw.WriteLine(".subsystem 0x{0:x4} // {1}", module.__Subsystem, SubsystemToString(module.__Subsystem)); + int corflags = GetCorFlags(); + lw.WriteLine(".corflags 0x{0:x8}{1}", corflags, CorFlagsToString(corflags)); + } + + static string CorFlagsToString(int flags) + { + StringBuilder sb = new StringBuilder(); + if (flags != 0) + { + sb.Append(" // "); + } + if ((flags & COMIMAGE_FLAGS_ILONLY) != 0) + { + sb.Append(" ILONLY"); + } + if ((flags & COMIMAGE_FLAGS_IL_LIBRARY) != 0) + { + sb.Append(" IL_LIBRARY"); + } + if ((flags & COMIMAGE_FLAGS_32BITREQUIRED) != 0) + { + sb.Append(" 32BITREQUIRED"); + } + return sb.ToString(); + } + + int GetCorFlags() + { + int rva; + int length; + module.__GetDataDirectoryEntry(14, out rva, out length); + var buf = new byte[4]; + module.__ReadDataFromRVA(rva + 16, buf, 0, 4); + return BitConverter.ToInt32(buf, 0); + } + + static string SubsystemToString(int subsystem) + { + switch (subsystem) + { + case 2: + return "WINDOWS_GUI"; + case 3: + return "WINDOWS_CUI"; + default: + throw new NotImplementedException(); + } + } + + void WriteCustomAttributes(LineWriter lw, int level, IEnumerable<CustomAttributeData> cas) + { + if (diffMode) + { + cas = cas.OrderBy(ca => ca.Constructor.DeclaringType.FullName); + } + foreach (var ca in cas) + { + lw.GoToColumn(level); + WriteCustomAttribute(lw, ca); + } + } + + void WriteCustomAttribute(LineWriter lw, CustomAttributeData ca) + { + lw.Write(".custom "); + WriteCustomAttributeImpl(lw, ca, false, lw.Column); + } + + void WriteCustomAttributeImpl(LineWriter lw, CustomAttributeData ca, bool comment, int level0) + { + lw.Write("instance void "); + WriteTypeDefOrRef(lw, ca.Constructor.DeclaringType); + lw.Write("::.ctor("); + int level = lw.Column; + bool first = true; + foreach (var parameter in ca.Constructor.GetParameters()) + { + if (!first) + { + lw.WriteLine(","); + if (comment) + { + lw.Write(" //"); + } + lw.GoToColumn(level); + } + first = false; + WriteSignatureType(lw, parameter.ParameterType); + WriteCustomModifiers(lw, parameter.__GetCustomModifiers()); + } + byte[] blob = ca.__GetBlob(); + if (blob.Length == 0) + { + lw.WriteLine(")"); + } + else + { + var wrap = lw.Column >= 80; + var sb = new StringBuilder(); + if (DecodeCABlob(sb, ca.Constructor, blob, wrap ? (level0 + 4) * (comment ? -1 : 1) : lw.Column + 5)) + { + lw.Write(")"); + if (wrap) + { + lw.WriteLine(); + if (comment) + { + lw.Write(" //"); + } + lw.GoToColumn(level0); + } + lw.Write(" = {{{0}", sb); + lw.WriteLine("}"); + } + else + { + lw.Write(") = ( "); + WriteBytes(lw, blob, false); + lw.WriteLine(); + } + } + } + + void WriteModuleOrAssemblyRef(LineWriter lw, Module mod) + { + if (mod.Assembly != assembly) + { + lw.Write("[{0}]", QuoteIdentifier(referencedAssemblies[mod.Assembly])); + } + else if (mod != this.module) + { + lw.Write("[.module {0}]", QuoteIdentifier(mod.Name)); + } + } + + void WriteSignatureType(LineWriter lw, Type type) + { + WriteSignatureType(lw, type, TypeLocation.General); + } + + enum TypeLocation + { + General, + MemberRef, + MemberRefNoWrap, + DeclaringType, + MethodGenericParameter, + Local, + Parameter, + GenericMethodImpl, + } + + void WriteSignatureType(LineWriter lw, Type type, TypeLocation loc) + { + WriteSignatureType(lw, type, loc, false); + } + + void WriteSignatureType(LineWriter lw, Type type, TypeLocation loc, bool skipGenArgs) + { + if (type.__IsVector) + { + WriteSignatureType(lw, type.GetElementType(), loc); + WriteCustomModifiers(lw, type.__GetCustomModifiers()); + lw.Write("[]"); + } + else if (type.IsArray) + { + WriteSignatureType(lw, type.GetElementType(), loc); + WriteCustomModifiers(lw, type.__GetCustomModifiers()); + lw.Write("["); + string sep = ""; + int[] lower = type.__GetArrayLowerBounds(); + foreach (var lb in lower) + { + lw.Write(sep); + sep = ","; + lw.Write("{0}...", lb); + } + for (int i = lower.Length + 1, rank = type.GetArrayRank(); i < rank; i++) + { + lw.Write(","); + } + lw.Write("]"); + } + else if (type.IsByRef) + { + WriteSignatureType(lw, type.GetElementType(), loc); + WriteCustomModifiers(lw, type.__GetCustomModifiers()); + lw.Write("&"); + } + else if (type.IsPointer) + { + WriteSignatureType(lw, type.GetElementType(), loc); + WriteCustomModifiers(lw, type.__GetCustomModifiers()); + lw.Write("*"); + } + else if (type.__IsFunctionPointer) + { + WriteStandAloneMethodSig(lw, type.__MethodSignature, true, loc == TypeLocation.Local || loc == TypeLocation.Parameter || loc == TypeLocation.MemberRef); + } + else if (!type.__IsMissing && type.IsGenericType && !type.IsGenericTypeDefinition) + { + WriteSignatureType(lw, type.GetGenericTypeDefinition(), loc, true); + lw.Write("<"); + string sep = ""; + Type[] args = type.GetGenericArguments(); + CustomModifiers[] mods = type.__GetGenericArgumentsCustomModifiers(); + for (int i = 0; i < args.Length; i++) + { + lw.Write(sep); + WriteSignatureType(lw, args[i], loc); + WriteCustomModifiers(lw, mods[i]); + sep = ","; + } + lw.Write(">"); + } + else if (type.IsGenericParameter) + { + if (type.DeclaringMethod != null) + { + lw.Write("!!{0}", + loc == TypeLocation.MemberRef || loc == TypeLocation.MemberRefNoWrap || loc == TypeLocation.DeclaringType || loc == TypeLocation.MethodGenericParameter || type.Name == null + ? (object)type.GenericParameterPosition + : QuoteIdentifier(type.Name)); + } + else + { + lw.Write("!{0}", + loc == TypeLocation.MemberRef || loc == TypeLocation.MemberRefNoWrap || loc == TypeLocation.GenericMethodImpl || type.Name == null + ? (object)type.GenericParameterPosition + : QuoteIdentifier(type.Name)); + } + } + else if (type == typeofSystemBoolean) + { + lw.Write("bool"); + } + else if (type == typeofSystemSByte) + { + lw.Write("int8"); + } + else if (type == typeofSystemByte) + { + lw.Write("uint8"); + } + else if (type == typeofSystemChar) + { + lw.Write("char"); + } + else if (type == typeofSystemInt16) + { + lw.Write("int16"); + } + else if (type == typeofSystemUInt16) + { + lw.Write("uint16"); + } + else if (type == typeofSystemInt32) + { + lw.Write("int32"); + } + else if (type == typeofSystemUInt32) + { + lw.Write("uint32"); + } + else if (type == typeofSystemInt64) + { + lw.Write("int64"); + } + else if (type == typeofSystemUInt64) + { + lw.Write("uint64"); + } + else if (type == typeofSystemSingle) + { + lw.Write("float32"); + } + else if (type == typeofSystemDouble) + { + lw.Write("float64"); + } + else if (type == typeofSystemString) + { + lw.Write("string"); + } + else if (type == typeofSystemObject) + { + lw.Write("object"); + } + else if (type == typeofSystemVoid) + { + lw.Write("void"); + } + else if (type == typeofSystemIntPtr) + { + lw.Write("native int"); + } + else if (type == typeofSystemUIntPtr) + { + lw.Write("native uint"); + } + else if (type == typeofSystemTypedReference) + { + lw.Write("typedref"); + } + else + { + lw.Write(type.IsValueType ? "valuetype " : "class "); + WriteModuleOrAssemblyRef(lw, type.Module); + WriteTypeName(lw, type); + if (!skipGenArgs && !type.__IsMissing && type.IsGenericTypeDefinition) + { + lw.Write("<"); + string sep = ""; + Type[] args = type.GetGenericArguments(); + CustomModifiers[] mods = type.__GetGenericArgumentsCustomModifiers(); + for (int i = 0; i < args.Length; i++) + { + lw.Write(sep); + WriteSignatureType(lw, args[i], loc); + WriteCustomModifiers(lw, mods[i]); + sep = ","; + } + lw.Write(">"); + } + } + } + + void WriteStandAloneMethodSig(LineWriter lw, __StandAloneMethodSig sig, bool type, bool wrap) + { + int level = lw.Column; + if (type) + { + lw.Write("method "); + } + if (sig.IsUnmanaged) + { + lw.Write("unmanaged "); + switch (sig.UnmanagedCallingConvention) + { + case System.Runtime.InteropServices.CallingConvention.Cdecl: + lw.Write("cdecl "); + break; + case System.Runtime.InteropServices.CallingConvention.StdCall: + lw.Write("stdcall "); + break; + case System.Runtime.InteropServices.CallingConvention.ThisCall: + lw.Write("thiscall "); + break; + } + } + else + { + WriteCallingConvention(lw, sig.CallingConvention); + } + WriteSignatureType(lw, sig.ReturnType); + WriteCustomModifiers(lw, sig.GetReturnTypeCustomModifiers()); + lw.Write(type ? " *(" : "("); + Type[] parameters = sig.ParameterTypes; + for (int i = 0; i < parameters.Length; i++) + { + if (i != 0) + { + if (wrap) + { + lw.WriteLine(","); + lw.GoToColumn(level); + } + else + { + lw.Write(","); + } + } + WriteSignatureType(lw, parameters[i]); + WriteCustomModifiers(lw, sig.GetParameterCustomModifiers(i)); + } + lw.Write(")"); + } + + private void WriteCallingConvention(LineWriter lw, CallingConventions callingConvention) + { + if ((callingConvention & CallingConventions.HasThis) != 0) + { + lw.Write("instance "); + } + if ((callingConvention & CallingConventions.VarArgs) != 0) + { + lw.Write("vararg "); + } + } + + void WriteTypeName(LineWriter lw, Type type) + { + if (type.IsNested) + { + WriteTypeName(lw, type.DeclaringType); + lw.Write("/"); + } + WriteTypeNameNoOuter(lw, type); + } + + void WriteTypeNameNoOuter(LineWriter lw, Type type) + { + if (type.__Namespace != null) + { + lw.Write("{0}.", QuoteIdentifier(type.__Namespace)); + } + lw.Write("{0}", QuoteIdentifier(type.__Name)); + } + + void WriteTypeDefOrRef(LineWriter lw, Type type) + { + if (type.IsGenericParameter) + { + lw.Write(type.DeclaringMethod == null ? "!" : "!!"); + lw.Write("{0}", QuoteIdentifier(type.Name)); + } + else if (type.IsArray) + { + WriteSignatureType(lw, type, TypeLocation.General); + } + else + { + WriteModuleOrAssemblyRef(lw, type.Module); + WriteTypeName(lw, type); + } + } + + static void WriteBytes(LineWriter lw, byte[] buf, bool data) + { + bool hasText = false; + int column = lw.Column; + for (int i = 0; i < buf.Length; i++) + { + if (i != 0) + { + if (i % 16 == 0) + { + if (hasText) + { + hasText = false; + lw.Write(data ? " // " : " // "); + for (int j = i - 16; j < i; j++) + { + lw.Write("{0}", IsText(buf[j]) ? (char)buf[j] : '.'); + } + } + else if (!data) + { + lw.Write(" "); + } + lw.WriteLine(); + lw.GoToColumn(column); + } + else + { + lw.Write(" "); + } + } + lw.Write("{0:X2}", buf[i]); + hasText |= IsText(buf[i]); + } + if (!data) + { + lw.Write(" "); + } + lw.Write(") "); + if (hasText) + { + lw.GoToColumn(column + 16 * 3 + (data ? 1 : 2)); + lw.Write("// "); + for (int j = (buf.Length + 15 & ~15) - 16; j < buf.Length; j++) + { + lw.Write("{0}", IsText(buf[j]) ? (char)buf[j] : '.'); + } + } + } + + static bool IsText(byte b) + { + return b >= 32 && b < 127; + } + } +} diff --git a/ExportedMethods.cs b/ExportedMethods.cs new file mode 100644 index 0000000..2e8eafd --- /dev/null +++ b/ExportedMethods.cs @@ -0,0 +1,167 @@ +/* + Copyright (C) 2012 Jeroen Frijters + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. + + Jeroen Frijters + jeroen@frijters.net + +*/ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.IO; +using IKVM.Reflection; + +namespace Ildasm +{ + partial class Disassembler + { + struct ExportDirectoryTable + { + internal uint Flags; + internal uint DateTimeStamp; + internal ushort MajorVersion; + internal ushort MinorVersion; + internal uint NameRVA; + internal uint OrdinalBase; + internal uint AddressTableEntries; + internal uint NumberOfNamePointers; + internal uint ExportAddressTableRVA; + internal uint NamePointerRVA; + internal uint OrdinalTableRVA; + + internal void Read(BinaryReader br) + { + Flags = br.ReadUInt32(); + DateTimeStamp = br.ReadUInt32(); + MajorVersion = br.ReadUInt16(); + MinorVersion = br.ReadUInt16(); + NameRVA = br.ReadUInt32(); + OrdinalBase = br.ReadUInt32(); + AddressTableEntries = br.ReadUInt32(); + NumberOfNamePointers = br.ReadUInt32(); + ExportAddressTableRVA = br.ReadUInt32(); + NamePointerRVA = br.ReadUInt32(); + OrdinalTableRVA = br.ReadUInt32(); + } + } + + struct ExportedMethod + { + internal int ordinal; + internal string name; + } + + static Dictionary<int, List<ExportedMethod>> GetExportedMethods(Module module) + { + int rva; + int length; + module.__GetDataDirectoryEntry(0, out rva, out length); + + if (rva == 0 || length < 40) + { + return new Dictionary<int, List<ExportedMethod>>(); + } + + ExportDirectoryTable edt = new ExportDirectoryTable(); + byte[] buf = new byte[512]; + module.__ReadDataFromRVA(rva, buf, 0, 40); + edt.Read(new BinaryReader(new MemoryStream(buf))); + + var methods = new Dictionary<int, List<ExportedMethod>>(); + for (int i = 0; i < edt.NumberOfNamePointers; i++) + { + module.__ReadDataFromRVA((int)edt.OrdinalTableRVA + i * 2, buf, 0, 2); + int ordinal = BitConverter.ToInt16(buf, 0) + (int)edt.OrdinalBase; + string name = null; + if (edt.NamePointerRVA != 0) + { + module.__ReadDataFromRVA((int)edt.NamePointerRVA + i * 4, buf, 0, 4); + module.__ReadDataFromRVA(BitConverter.ToInt32(buf, 0), buf, 0, buf.Length); + int len = 0; + while (buf[len] != 0) len++; + name = Encoding.ASCII.GetString(buf, 0, len); + } + int token = GetTokenFromExportOrdinal(module, edt, ordinal); + if (token == -1) + { + continue; + } + List<ExportedMethod> list; + if (!methods.TryGetValue(token, out list)) + { + list = new List<ExportedMethod>(); + methods.Add(token, list); + } + ExportedMethod method; + method.name = name; + method.ordinal = ordinal; + list.Add(method); + } + return methods; + } + + static int GetTokenFromExportOrdinal(Module module, ExportDirectoryTable edt, int ordinal) + { + PortableExecutableKinds peKind; + ImageFileMachine machine; + module.GetPEKind(out peKind, out machine); + byte[] buf = new byte[16]; + module.__ReadDataFromRVA((int)edt.ExportAddressTableRVA + (int)(ordinal - edt.OrdinalBase) * 4, buf, 0, 4); + int exportRVA = BitConverter.ToInt32(buf, 0); + if (machine == ImageFileMachine.ARM) + { + // mask out the instruction set selection flag + exportRVA &= ~1; + } + module.__ReadDataFromRVA(exportRVA, buf, 0, 16); + int offset; + if (machine == ImageFileMachine.I386 && buf[0] == 0xFF && buf[1] == 0x25) + { + // for x86 the code here is: + // FF 25 00 40 40 00 jmp dword ptr ds:[00404000h] + offset = 2; + } + else if (machine == ImageFileMachine.AMD64 && buf[0] == 0x48 && buf[1] == 0xA1) + { + // for x64 the code here is: + // 48 A1 00 40 40 00 00 00 00 00 mov rax,qword ptr [0000000000404000h] + // FF E0 jmp rax + offset = 2; + } + else if (machine == ImageFileMachine.ARM && buf[0] == 0xDF && buf[1] == 0xF8 && buf[2] == 0x08 && buf[3] == 0xC0) + { + // for arm the code here is: + // F8DF C008 ldr r12,0040145C + // F8DC C000 ldr r12,[r12] + // 4760 bx r12 + // DEFE __debugbreak + // here is the RVA + offset = 12; + } + else + { + return -1; + } + int vtableRVA = BitConverter.ToInt32(buf, offset) - (int)module.__ImageBase; + module.__ReadDataFromRVA(vtableRVA, buf, 0, 4); + return BitConverter.ToInt32(buf, 0); + } + } +} diff --git a/IKVM.Reflection.dll b/IKVM.Reflection.dll Binary files differnew file mode 100644 index 0000000..d79cd45 --- /dev/null +++ b/IKVM.Reflection.dll @@ -0,0 +1,1040 @@ +/* + Copyright (C) 2012 Jeroen Frijters + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. + + Jeroen Frijters + jeroen@frijters.net + +*/ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using IKVM.Reflection; +using IKVM.Reflection.Emit; +using Type = IKVM.Reflection.Type; +using System.Diagnostics; + +namespace Ildasm +{ + sealed partial class Disassembler : IComparer<ExceptionHandlingClause> + { + static readonly OpCode[] opcodes = GetOpCodes(); + + static OpCode[] GetOpCodes() + { + OpCode[] opcodes = new OpCode[768]; + foreach (System.Reflection.FieldInfo field in typeof(OpCodes).GetFields()) + { + OpCode opc = (OpCode)field.GetValue(null); + opcodes[opc.Value + 512] = opc; + } + return opcodes; + } + + void WriteIL(LineWriter lw, MethodBase mb, MethodBody body, Type[] genericTypeArguments, Type[] genericMethodArguments) + { + ParameterInfo[] parameters = mb.GetParameters(); + int level = lw.Column; + byte[] code = body.GetILAsByteArray(); + lw.GoToColumn(level); + lw.WriteLine("// Code size {0} (0x{0:x})", code.Length); + lw.GoToColumn(level); + lw.WriteLine(".maxstack {0}", body.MaxStackSize); + + IList<LocalVariableInfo> locals = body.LocalVariables; + if (locals.Count != 0) + { + lw.GoToColumn(level); + lw.Write(".locals "); + if (body.InitLocals) + { + lw.Write("init "); + } + lw.Write("("); + bool first = true; + foreach (var local in locals) + { + if (!first) + { + lw.WriteLine(","); + lw.GoToColumn(level + 9); + } + first = false; + WriteSignatureType(lw, local.LocalType, TypeLocation.Local); + if (local.IsPinned) + { + lw.Write(" pinned"); + } + WriteCustomModifiers(lw, local.__GetCustomModifiers()); + lw.Write(" V_{0}", local.LocalIndex); + } + lw.WriteLine(")"); + } + + var exceptions = new List<ExceptionHandlingClause>(); + var exceptions2 = new List<ExceptionHandlingClause>(); + SortExceptions(body.ExceptionHandlingClauses, exceptions, exceptions2); + + Stack<ExceptionHandlingClause> activeExceptions = new Stack<ExceptionHandlingClause>(); + ExceptionHandlingClause currentException = null; + bool extraNewLine = false; + int nextFlatException = 0; + int nextException = 0; + bool handler = false; + int pos = 0; + while (pos < code.Length) + { + if (extraNewLine) + { + lw.WriteLine(); + extraNewLine = false; + } + if (currentException != null) + { + if (currentException.HandlerOffset == pos) + { + switch (currentException.Flags) + { + case ExceptionHandlingClauseOptions.Clause: + lw.GoToColumn(level - 2); + if (currentException.TryOffset + currentException.TryLength == pos) + { + lw.WriteLine("} // end .try"); + } + else + { + lw.WriteLine("} // end handler"); + } + lw.GoToColumn(level - 2); + lw.Write("catch "); + if (currentException.CatchType.__IsMissing || !currentException.CatchType.IsGenericType) + { + WriteTypeDefOrRef(lw, currentException.CatchType); + } + else + { + WriteSignatureType(lw, currentException.CatchType); + } + lw.WriteLine(" "); + lw.GoToColumn(level - 2); + lw.WriteLine("{"); + handler = true; + break; + case ExceptionHandlingClauseOptions.Finally: + lw.GoToColumn(level - 2); + lw.WriteLine("} // end .try"); + lw.GoToColumn(level - 2); + lw.WriteLine("finally"); + lw.GoToColumn(level - 2); + lw.WriteLine("{"); + break; + case ExceptionHandlingClauseOptions.Fault: + lw.GoToColumn(level - 2); + lw.WriteLine("} // end .try"); + lw.GoToColumn(level - 2); + lw.WriteLine("fault"); + lw.GoToColumn(level - 2); + lw.WriteLine("{"); + break; + case ExceptionHandlingClauseOptions.Filter: + lw.GoToColumn(level - 2); + lw.WriteLine("} // end filter"); + lw.GoToColumn(level - 2); + lw.WriteLine("{ // handler"); + handler = true; + break; + default: + throw new IKVM.Reflection.BadImageFormatException(); + } + } + else if (currentException.FilterOffset == pos && pos != 0) + { + lw.GoToColumn(level - 2); + if (handler) + { + lw.WriteLine("} // end handler"); + } + else + { + lw.WriteLine("} // end .try"); + } + lw.GoToColumn(level - 2); + lw.WriteLine("filter"); + lw.GoToColumn(level - 2); + lw.WriteLine("{"); + } + } + while (nextException < exceptions.Count + && exceptions[nextException].TryOffset == pos) + { + activeExceptions.Push(currentException); + ExceptionHandlingClause prevException = currentException; + currentException = exceptions[nextException++]; + if (prevException != null && currentException.TryOffset == prevException.TryOffset && currentException.TryLength == prevException.TryLength) + { + // another handler for the same block + continue; + } + handler = false; + lw.GoToColumn(level); + lw.WriteLine(".try"); + lw.GoToColumn(level); + lw.WriteLine("{"); + level += 2; + } + lw.GoToColumn(level); + int currPos = pos; + lw.Write("IL_{0:x4}: ", pos); + int level1 = lw.Column; + short opcodeValue = code[pos++]; + if (opcodeValue == 0xFE) + { + opcodeValue = (short)(0xFE00 + code[pos++]); + } + OpCode opcode = opcodes[opcodeValue + 512]; + lw.Write("{0}", opcode.Name); + switch (opcode.OperandType) + { + case OperandType.InlineNone: + break; + case OperandType.InlineBrTarget: + lw.GoToColumn(level1 + 11); + lw.Write("IL_{0:x4}", ReadInt32(code, ref pos) + pos); + break; + case OperandType.ShortInlineBrTarget: + lw.GoToColumn(level1 + 11); + lw.Write("IL_{0:x4}", (sbyte)code[pos++] + pos); + break; + case OperandType.InlineMethod: + { + lw.GoToColumn(level1 + 11); + int token = ReadInt32(code, ref pos); + MethodBase methodOrConstructor = ResolveMethod(token, genericTypeArguments, genericMethodArguments); + if ((methodOrConstructor.CallingConvention & CallingConventions.Any) == CallingConventions.VarArgs) + { + CustomModifiers[] customModifiers; + Type[] optionalParameterTypes = ResolveOptionalParameterTypes(token, genericTypeArguments, genericMethodArguments, out customModifiers); + WriteInlineMethod(lw, methodOrConstructor, optionalParameterTypes, customModifiers); + } + else + { + WriteInlineMethod(lw, methodOrConstructor, Type.EmptyTypes, null); + } + } + break; + case OperandType.InlineField: + lw.GoToColumn(level1 + 11); + WriteInlineField(lw, ResolveField(ReadInt32(code, ref pos), genericTypeArguments, genericMethodArguments)); + break; + case OperandType.InlineI: + lw.GoToColumn(level1 + 11); + WriteInlineI(lw, ReadInt32(code, ref pos)); + break; + case OperandType.InlineI8: + lw.GoToColumn(level1 + 11); + WriteInlineI8(lw, ReadInt64(code, ref pos)); + break; + case OperandType.ShortInlineI: + lw.GoToColumn(level1 + 11); + lw.Write("{0}", (sbyte)code[pos++]); + break; + case OperandType.InlineR: + lw.GoToColumn(level1 + 11); + WriteInlineR(lw, ReadDouble(code, ref pos), false); + break; + case OperandType.ShortInlineR: + lw.GoToColumn(level1 + 11); + WriteShortInlineR(lw, ReadSingle(code, ref pos), false); + break; + case OperandType.InlineType: + if (opcode == OpCodes.Constrained) + { + // "constrained." is too long to fit in the opcode column + lw.Write(" "); + } + else + { + lw.GoToColumn(level1 + 11); + } + WriteInlineType(lw, ReadInt32(code, ref pos), genericTypeArguments, genericMethodArguments); + break; + case OperandType.InlineTok: + { + int token = ReadInt32(code, ref pos); + switch (token >> 24) + { + case 0x01: + case 0x02: + lw.GoToColumn(level1 + 11); + WriteTypeDefOrRef(lw, ResolveType(token, genericTypeArguments, genericMethodArguments)); + break; + case 0x1B: + { + Type type = ResolveType(token, genericTypeArguments, genericMethodArguments); + if (type.IsGenericTypeDefinition) + { + // HACK because typeof(Foo<>).MakeGenericType(typeof(Foo<>).GetGenericArguments()) == typeof(Foo<>) + // we need to inflate the builder here + type = type.MakeGenericType(type.GetGenericArguments()); + } + lw.GoToColumn(level1 + 11); + WriteSignatureType(lw, type); + break; + } + case 0x04: + case 0x06: + case 0x0A: + case 0x2B: + { + MemberInfo member = ResolveMember(token, genericTypeArguments, genericMethodArguments); + if (member is FieldInfo) + { + lw.GoToColumn(level1 + 11); + lw.Write("field "); + WriteInlineField(lw, (FieldInfo)member); + } + else + { + var mb1 = (MethodBase)member; + lw.GoToColumn(level1 + 11); + if (mb1.__IsMissing || !mb1.IsGenericMethod || compat != CompatLevel.V20) + { + lw.Write("method "); + } + WriteInlineMethod(lw, mb1, Type.EmptyTypes, null); + } + break; + } + default: + throw new NotImplementedException("token type = " + (token >> 24)); + } + } + break; + case OperandType.InlineVar: + lw.GoToColumn(level1 + 11); + WriteInlineVar(lw, mb, opcode, parameters, ReadInt16(code, ref pos)); + break; + case OperandType.ShortInlineVar: + lw.GoToColumn(level1 + 11); + WriteInlineVar(lw, mb, opcode, parameters, code[pos++]); + break; + case OperandType.InlineString: + lw.GoToColumn(level1 + 11); + WriteInlineString(lw, module.ResolveString(ReadInt32(code, ref pos)), level); + break; + case OperandType.InlineSwitch: + { + lw.GoToColumn(level1 + 11); + lw.WriteLine("( "); + int count = ReadInt32(code, ref pos); + int offset = pos + 4 * count; + for (int i = 0; i < count - 1; i++) + { + lw.GoToColumn(level + 22); + lw.WriteLine("IL_{0:x4},", offset + ReadInt32(code, ref pos)); + } + lw.GoToColumn(level + 22); + lw.Write("IL_{0:x4})", offset + ReadInt32(code, ref pos)); + } + break; + case OperandType.InlineSig: + lw.GoToColumn(level1 + 11); + WriteStandAloneMethodSig(lw, module.__ResolveStandAloneMethodSig(ReadInt32(code, ref pos), genericTypeArguments, genericMethodArguments), false, false); + break; + default: + throw new InvalidOperationException(); + } + lw.WriteLine(); + + if (opcode == OpCodes.Leave || opcode == OpCodes.Leave_S) + { + if (pos < code.Length) + { + lw.WriteLine(); + } + } + else if (opcode != OpCodes.Switch && opcode != OpCodes.Rethrow && opcode != OpCodes.Endfilter && opcode != OpCodes.Endfinally) + { + switch (opcode.FlowControl) + { + case FlowControl.Branch: + case FlowControl.Cond_Branch: + case FlowControl.Throw: + case FlowControl.Return: + extraNewLine = true; + break; + } + } + if (nextFlatException < exceptions2.Count && exceptions2[nextFlatException].HandlerOffset + exceptions2[nextFlatException].HandlerLength == currPos) + { + if (extraNewLine && pos < code.Length) + { + extraNewLine = false; + lw.WriteLine(); + } + lw.GoToColumn(level); + if (exceptions2[nextFlatException].FilterOffset == 0) + { + lw.Write(".try IL_{0:x4} to IL_{1:x4} catch ", exceptions2[nextFlatException].TryOffset, exceptions2[nextFlatException].TryOffset + exceptions2[nextFlatException].TryLength); + if (exceptions2[nextFlatException].CatchType.__IsMissing || !exceptions2[nextFlatException].CatchType.IsGenericType) + { + WriteTypeDefOrRef(lw, exceptions2[nextFlatException].CatchType); + } + else + { + WriteSignatureType(lw, exceptions2[nextFlatException].CatchType); + } + lw.WriteLine(" handler IL_{0:x4} to IL_{1:x4}", exceptions2[nextFlatException].HandlerOffset, exceptions2[nextFlatException].HandlerOffset + exceptions2[nextFlatException].HandlerLength); + } + else + { + lw.WriteLine(".try IL_{0:x4} to IL_{1:x4} filter IL_{2:x4} handler IL_{3:x4} to IL_{4:x4}", + exceptions2[nextFlatException].TryOffset, exceptions2[nextFlatException].TryOffset + exceptions2[nextFlatException].TryLength, + exceptions2[nextFlatException].FilterOffset, + exceptions2[nextFlatException].HandlerOffset, exceptions2[nextFlatException].HandlerOffset + exceptions2[nextFlatException].HandlerLength); + } + nextFlatException++; + } + + while (currentException != null && currentException.HandlerOffset + currentException.HandlerLength == pos) + { + ExceptionHandlingClause prevException = currentException; + currentException = activeExceptions.Pop(); + if (currentException == null || currentException.TryOffset != prevException.TryOffset || currentException.TryLength != prevException.TryLength) + { + if (extraNewLine && pos < code.Length) + { + extraNewLine = false; + lw.WriteLine(); + } + level -= 2; + lw.GoToColumn(level); + lw.WriteLine("} // end handler"); + handler = false; + } + else + { + handler = true; + } + } + } + } + + void WriteInlineVar(LineWriter lw, MethodBase mb, OpCode opcode, ParameterInfo[] parameters, int index) + { + if (opcode == OpCodes.Ldarg_S + || opcode == OpCodes.Starg_S + || opcode == OpCodes.Ldarga_S + || opcode == OpCodes.Ldarg + || opcode == OpCodes.Starg + || opcode == OpCodes.Ldarga) + { + ParameterInfo param = mb.IsStatic ? parameters[index] : index == 0 ? null : parameters[index - 1]; + if (param == null) + { + // this + lw.Write("0"); + } + else if (param.Name == null) + { + lw.Write("A_{0}", index); + } + else + { + lw.Write("{0}", QuoteIdentifier(param.Name)); + } + } + else + { + lw.Write("V_{0}", index); + } + } + + static int FloatToInt32Bits(float v) + { + return IKVM.Reflection.Reader.SingleConverter.SingleToInt32Bits(v); + } + + string ToString(float value, bool field) + { + if (value == 0 && !field) + { + return FloatToInt32Bits(value) < 0 + ? "-0.0" + : "0.0"; + } + else if (compat != CompatLevel.None) + { + byte[] buf = new byte[50]; + _gcvt(value, 8, buf); + string str = Encoding.ASCII.GetString(buf, 0, Array.IndexOf(buf, (byte)0)); + float v2; + if (Single.TryParse(str, out v2) && FloatToInt32Bits(value) == FloatToInt32Bits(v2)) + { + return str; + } + else + { + if (field) + { + return String.Format("0x{0:X}", FloatToInt32Bits(value)); + } + else + { + byte[] buf2 = BitConverter.GetBytes(value); + return String.Format("({0:X2} {1:X2} {2:X2} {3:X2})", buf2[0], buf2[1], buf2[2], buf2[3]); + } + } + } + else if (Single.IsInfinity(value) || Single.IsNaN(value)) + { + if (field) + { + return String.Format("0x{0:X}", FloatToInt32Bits(value)); + } + else + { + byte[] buf = BitConverter.GetBytes(value); + return String.Format("({0:X2} {1:X2} {2:X2} {3:X2})", buf[0], buf[1], buf[2], buf[3]); + } + } + else + { + return String.Format("{0:R}", value); + } + } + + void WriteShortInlineR(LineWriter lw, float value, bool field) + { + lw.Write(ToString(value, field)); + } + + [System.Runtime.InteropServices.DllImport("msvcrt.dll", CallingConvention = System.Runtime.InteropServices.CallingConvention.Cdecl)] + static extern IntPtr _gcvt(double value, int digits, byte[] buffer); + + string ToString(double value, bool field) + { + if (value == 0 && !field) + { + return BitConverter.DoubleToInt64Bits(value) < 0 + ? "-0.0" + : "0.0"; + } + else if (compat != CompatLevel.None) + { + byte[] buf = new byte[50]; + _gcvt(value, 17, buf); + string str = Encoding.ASCII.GetString(buf, 0, Array.IndexOf(buf, (byte)0)); + double v2; + if (Double.TryParse(str, out v2) && BitConverter.DoubleToInt64Bits(value) == BitConverter.DoubleToInt64Bits(v2)) + { + return str; + } + else + { + if (field) + { + return String.Format("0x{0:X}", BitConverter.DoubleToInt64Bits(value)); + } + else + { + byte[] buf2 = BitConverter.GetBytes(value); + return String.Format("({0:X2} {1:X2} {2:X2} {3:X2} {4:X2} {5:X2} {6:X2} {7:X2})", buf2[0], buf2[1], buf2[2], buf2[3], buf2[4], buf2[5], buf2[6], buf2[7]); + } + } + } + else if (Double.IsInfinity(value) || Double.IsNaN(value)) + { + if (field) + { + return String.Format("0x{0:X}", BitConverter.DoubleToInt64Bits(value)); + } + else + { + byte[] buf = BitConverter.GetBytes(value); + return String.Format("({0:X2} {1:X2} {2:X2} {3:X2} {4:X2} {5:X2} {6:X2} {7:X2})", buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], buf[6], buf[7]); + } + } + else + { + return String.Format("{0:R}", value.ToString("R")); + } + } + + void WriteInlineR(LineWriter lw, double value, bool field) + { + lw.Write(ToString(value, field)); + } + + void WriteInlineString(LineWriter lw, string str, int level) + { + int initial = 44 - lw.Column + level; + int pos = 44; + StringBuilder sb = new StringBuilder(str.Length + 10); + int backslashes = 0; + for (int i = 0; i < str.Length; i++) + { + char c = str[i]; + if (i < str.Length - 2 && ((pos == 94 && pos - initial != backslashes) || pos > 94)) + { + if (pos - initial == backslashes) + { + sb.Append('\\', backslashes * 2); + backslashes = 0; + } + pos = initial + backslashes; + sb.Append("\"\r\n"); + sb.Append(' ', level); + sb.Append("+ \""); + } + if (c == '\\') + { + backslashes++; + } + else if (backslashes != 0) + { + sb.Append('\\', backslashes * 2); + backslashes = 0; + } + if (c < 32) + { + switch (c) + { + case '\r': + sb.Append("\\r"); + break; + case '\n': + sb.Append("\\n"); + break; + case '\t': + sb.Append("\\t"); + break; + default: + lw.Write("bytearray ("); + WriteBytes(lw, GetBytes(str), false); + return; + } + } + else if (c > 126) + { + lw.Write("bytearray ("); + WriteBytes(lw, GetBytes(str), false); + return; + } + else + { + switch (c) + { + case '"': + sb.Append("\\\""); + break; + case '?': + sb.Append("\\?"); + break; + case '\\': + break; + default: + sb.Append(c); + break; + } + } + pos++; + } + sb.Append('\\', backslashes * 2); + lw.Write("\"{0}\"", sb); + } + + static byte[] GetBytes(string str) + { + byte[] buf = new byte[str.Length * 2]; + for (int i = 0; i < str.Length; i++) + { + char ch = str[i]; + buf[i * 2 + 0] = (byte)(ch >> 0); + buf[i * 2 + 1] = (byte)(ch >> 8); + } + return buf; + } + + void WriteInlineI(LineWriter lw, int value) + { + if (value >= 128 || value < 128) + { + lw.Write("0x{0:x}", value); + } + else + { + lw.Write("{0}", value); + } + } + + void WriteInlineI8(LineWriter lw, long value) + { + if (value >= 128 || value < 128) + { + lw.Write("0x{0:x}", value); + } + else + { + lw.Write("{0}", value); + } + } + + static bool IsArrayOfGenericParameter(Type type) + { + if (type != null && type.IsArray) + { + while (type.IsArray) + { + type = type.GetElementType(); + } + return !type.__IsMissing && type.IsGenericParameter; + } + return false; + } + + void WriteInlineMethod(LineWriter lw, MethodBase mb, Type[] optionalParameterTypes, CustomModifiers[] customModifiers, MethodInfo methodimpl = null) + { + WriteCallingConvention(lw, mb.CallingConvention); + if (mb is ConstructorInfo) + { + lw.Write("void "); + } + else + { + WriteSignatureType(lw, ((MethodInfo)mb.__GetMethodOnTypeDefinition()).ReturnType, IsArrayOfGenericParameter(mb.DeclaringType) ? TypeLocation.General : TypeLocation.MemberRefNoWrap); + WriteCustomModifiers(lw, ((MethodInfo)mb).ReturnParameter.__GetCustomModifiers()); + lw.Write(" "); + } + bool generic; + if (mb.DeclaringType == null) + { + generic = false; + lw.Write("{0}", QuoteIdentifier(GetMethodName(mb))); + } + else + { + if (mb.DeclaringType.__IsMissing || !mb.DeclaringType.IsGenericType) + { + generic = false; + WriteTypeDefOrRef(lw, mb.DeclaringType); + } + else + { + generic = true; + WriteSignatureType(lw, mb.DeclaringType, mb.IsGenericMethod ? TypeLocation.DeclaringType : TypeLocation.General); + } + lw.Write("::{0}", QuoteIdentifier(GetMethodName(mb))); + } + if (mb.IsGenericMethod) + { + if (methodimpl != null) + { + lw.Write("<[{0}]>", mb.GetGenericArguments().Length); + } + else + { + lw.Write("<"); + string sep = ""; + foreach (var par in mb.GetGenericArguments()) + { + lw.Write(sep); + sep = ","; + WriteSignatureType(lw, par, generic ? TypeLocation.MemberRefNoWrap : TypeLocation.MethodGenericParameter); + } + lw.Write(">"); + } + } + if (mb.IsGenericMethodDefinition && methodimpl != null) + { + mb = ((MethodInfo)mb).MakeGenericMethod(methodimpl.GetGenericArguments()); + } + else + { + mb = mb.__GetMethodOnTypeDefinition(); + } + lw.Write("("); + TypeLocation loc = (methodimpl != null && mb.IsGenericMethod) + ? TypeLocation.GenericMethodImpl + : IsArrayOfGenericParameter(mb.DeclaringType) ? TypeLocation.General : TypeLocation.MemberRefNoWrap; + int level = lw.Column; + if (compat != CompatLevel.None && loc == TypeLocation.GenericMethodImpl) + { + // ildasm doesn't take the length of the arity ("<[1]>") into account + level -= 5 + (int)Math.Log10(mb.GetGenericArguments().Length); + } + bool first = true; + bool noLineWrapCompat = false; + foreach (var parameter in mb.GetParameters()) + { + if (!first) + { + if (noLineWrapCompat) + { + lw.Write(","); + } + else + { + if (loc == TypeLocation.MemberRefNoWrap) + { + loc = TypeLocation.MemberRef; + } + lw.WriteLine(","); + lw.GoToColumn(level); + } + } + first = false; + lw.ClearWrappedFlag(); + WriteSignatureType(lw, parameter.ParameterType, loc); + noLineWrapCompat |= lw.Wrapped; + WriteCustomModifiers(lw, parameter.__GetCustomModifiers()); + } + if (optionalParameterTypes.Length != 0) + { + if (!first) + { + lw.WriteLine(","); + lw.GoToColumn(level); + } + first = false; + lw.Write("..."); + for (int i = 0; i < optionalParameterTypes.Length; i++) + { + if (!first) + { + lw.WriteLine(","); + lw.GoToColumn(level); + } + first = false; + WriteSignatureType(lw, optionalParameterTypes[i], TypeLocation.MemberRef); + WriteCustomModifiers(lw, customModifiers[i]); + } + } + lw.Write(")"); + } + + void WriteInlineType(LineWriter lw, int metadataToken, Type[] genericTypeArguments, Type[] genericMethodArguments) + { + CustomModifiers mods = new CustomModifiers(); + if (metadataToken >> 24 == 0x1B) + { + mods = module.__ResolveTypeSpecCustomModifiers(metadataToken, genericTypeArguments, genericMethodArguments); + } + if (!mods.IsEmpty) + { + lw.Write("class "); + } + Type type = ResolveType(metadataToken, genericTypeArguments, genericMethodArguments); + if (type.HasElementType) + { + WriteSignatureType(lw, type); + } + else if (!type.__IsMissing && type.IsGenericType) + { + WriteSignatureType(lw, type, TypeLocation.General); + } + else + { + WriteTypeDefOrRef(lw, type); + } + if (!mods.IsEmpty) + { + WriteCustomModifiers(lw, mods); + } + } + + void WriteInlineField(LineWriter lw, FieldInfo field) + { + WriteSignatureType(lw, field.__GetFieldOnTypeDefinition().FieldType, TypeLocation.MemberRefNoWrap); + WriteCustomModifiers(lw, field.__GetCustomModifiers()); + lw.Write(" "); + if (field.DeclaringType == null) + { + lw.Write("{0}", QuoteIdentifier(GetFieldName(field))); + } + else + { + if (field.DeclaringType.__IsMissing || !field.DeclaringType.IsGenericType) + { + WriteTypeDefOrRef(lw, field.DeclaringType); + } + else + { + WriteSignatureType(lw, field.DeclaringType, TypeLocation.General); + } + lw.Write("::{0}", QuoteIdentifier(GetFieldName(field))); + } + } + + Type[] ResolveOptionalParameterTypes(int token, Type[] genericTypeArguments, Type[] genericMethodArguments, out CustomModifiers[] customModifiers) + { + return module.__ResolveOptionalParameterTypes(token, genericTypeArguments, genericMethodArguments, out customModifiers); + } + + void SortExceptions(IList<ExceptionHandlingClause> all, List<ExceptionHandlingClause> nested, List<ExceptionHandlingClause> flat) + { + var exceptions = new List<ExceptionHandlingClause>(all); + exceptions.Sort(this); + int first = 0; + for (int i = 1; i <= exceptions.Count; i++) + { + if (i < exceptions.Count + && exceptions[first].TryOffset == exceptions[i].TryOffset + && exceptions[first].TryLength == exceptions[i].TryLength + && (compat != CompatLevel.V20 || exceptions[i].FilterOffset == 0)) + { + // part of a multiple handler block + } + else + { + if (compat == CompatLevel.V20) + { + for (int j = first; j < i; j++) + { + if (exceptions[j].FilterOffset != 0 + || (i < exceptions.Count + && exceptions[j].TryOffset == exceptions[i].TryOffset + && exceptions[j].TryLength == exceptions[i].TryLength + && exceptions[j].HandlerOffset + exceptions[j].HandlerLength > exceptions[i].FilterOffset)) + { + flat.Add(exceptions[j]); + } + else + { + nested.Add(exceptions[j]); + } + } + } + else + { + for (int j = first; j < i; j++) + { + nested.Add(exceptions[j]); + } + int end = exceptions[first].TryOffset + exceptions[first].TryLength; + for (int j = i - 1; j >= first; j--) + { + Debug.Assert(exceptions[j].TryOffset == exceptions[first].TryOffset && exceptions[j].TryLength == exceptions[first].TryLength); + if (exceptions[j].FilterOffset == end) + { + end += exceptions[j].HandlerOffset - exceptions[j].FilterOffset; + } + if (exceptions[j].HandlerOffset != end) + { + throw new NotImplementedException(); + } + end += exceptions[j].HandlerLength; + } + } + first = i; + } + } + flat.Sort(FlatExceptionComparer); + } + + static int FlatExceptionComparer(ExceptionHandlingClause x, ExceptionHandlingClause y) + { + return (x.HandlerOffset + x.HandlerLength).CompareTo(y.HandlerOffset + y.HandlerLength); + } + + Type ResolveType(int token, Type[] genericTypeArguments, Type[] genericMethodArguments) + { + return module.ResolveType(token, genericTypeArguments, genericMethodArguments); + } + + FieldInfo ResolveField(int token, Type[] genericTypeArguments, Type[] genericMethodArguments) + { + return module.ResolveField(token, genericTypeArguments, genericMethodArguments); + } + + MethodBase ResolveMethod(int token, Type[] genericTypeArguments, Type[] genericMethodArguments) + { + return module.ResolveMethod(token, genericTypeArguments, genericMethodArguments); + } + + MemberInfo ResolveMember(int token, Type[] genericTypeArguments, Type[] genericMethodArguments) + { + return module.ResolveMember(token, genericTypeArguments, genericMethodArguments); + } + + short ReadInt16(byte[] code, ref int pos) + { + short s = BitConverter.ToInt16(code, pos); + pos += 2; + return s; + } + + int ReadInt32(byte[] code, ref int pos) + { + int i = BitConverter.ToInt32(code, pos); + pos += 4; + return i; + } + + long ReadInt64(byte[] code, ref int pos) + { + long l = BitConverter.ToInt64(code, pos); + pos += 8; + return l; + } + + float ReadSingle(byte[] code, ref int pos) + { + float f = BitConverter.ToSingle(code, pos); + pos += 4; + return f; + } + + double ReadDouble(byte[] code, ref int pos) + { + double d = BitConverter.ToDouble(code, pos); + pos += 8; + return d; + } + + int IComparer<ExceptionHandlingClause>.Compare(ExceptionHandlingClause x, ExceptionHandlingClause y) + { + if (x.TryOffset < y.TryOffset) + { + return -1; + } + if (x.TryOffset > y.TryOffset) + { + return 1; + } + if (x.TryLength > y.TryLength) + { + return -1; + } + if (x.TryLength < y.TryLength) + { + return 1; + } + if (x.HandlerOffset > y.HandlerOffset) + { + return -1; + } + if (x.HandlerOffset < y.HandlerOffset) + { + return 1; + } + return 0; + } + } +} diff --git a/Keywords.cs b/Keywords.cs new file mode 100644 index 0000000..e77b28f --- /dev/null +++ b/Keywords.cs @@ -0,0 +1,240 @@ +/* + Copyright (C) 2012 Jeroen Frijters + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. + + Jeroen Frijters + jeroen@frijters.net + +*/ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace Ildasm +{ + partial class Disassembler + { + static readonly HashSet<string> keywords = new HashSet<string> + { + "abstract", + "add", + "algorithm", + "alignment", + "and", + "ansi", + "any", + "arglist", + "array", + "as", + "assembly", + "assert", + "at", + "auto", + "autochar", + "beq", + "bge", + "bgt", + "ble", + "blt", + "blob", + "bool", + "box", + "br", + "break", + "brfalse", + "brtrue", + "bstr", + "bytearray", + "call", + "callconv", + "calli", + "callvirt", + "carray", + "catch", + "castclass", + "cdecl", + "ceq", + "cf", + "cgt", + "char", + "ckfinite", + "clt", + "class", + "clsid", + "cpblk", + "cpobj", + "currency", + "custom", + "date", + "decimal", + "default", + "demand", + "deny", + "div", + "dup", + "endfilter", + "endfinally", + "enum", + "error", + "explicit", + "extends", + "extern", + "false", + "family", + "fastcall", + "fault", + "field", + "filetime", + "filter", + "final", + "finally", + "fixed", + "flags", + "float", + "float32", + "float64", + "forwarder", + "handler", + "hresult", + "il", + "in", + "int", + "illegal", + "implements", + "import", + "init", + "initblk", + "initobj", + "instance", + "int8", + "int16", + "int32", + "int64", + "interface", + "isinst", + "iunknown", + "jmp", + "lasterr", + "ldarg", + "ldarga", + "ldelem", + "ldelema", + "ldfld", + "ldflda", + "ldftn", + "ldlen", + "ldloc", + "ldloca", + "ldnull", + "ldobj", + "ldsfld", + "ldsflda", + "ldstr", + "ldtoken", + "leave", + "legacy", + "library", + "literal", + "localloc", + "lpstr", + "lpvoid", + "lpwstr", + "managed", + "marshal", + "method", + "mkrefany", + "modopt", + "modreq", + "mul", + "native", + "neg", + "nested", + "newarr", + "newobj", + "nomangle", + "nop", + "not", + "object", + "off", + "on", + "opt", + "or", + "out", + "pinned", + "pop", + "prefix1", + "prefix2", + "property", + "record", + "refanytype", + "refanyval", + "rem", + "ret", + "retargetable", + "rethrow", + "request", + "runtime", + "sealed", + "serializable", + "sizeof", + "shl", + "shr", + "starg", + "stdcall", + "stelem", + "stfld", + "stloc", + "stobj", + "storage", + "stored_object", + "stream", + "strict", + "string", + "struct", + "stsfld", + "sub", + "synchronized", + "thiscall", + "tls", + "to", + "true", + "type", + "uint", + "uint8", + "uint16", + "uint32", + "uint64", + "unbox", + "unicode", + "unmanaged", + "unsigned", + "unused", + "value", + "variant", + "vector", + "virtual", + "void", + "wchar", + "winapi", + "with", + "x86", + "xor", + }; + } +} diff --git a/LineWriter.cs b/LineWriter.cs new file mode 100644 index 0000000..4acf925 --- /dev/null +++ b/LineWriter.cs @@ -0,0 +1,100 @@ +/* + Copyright (C) 2012 Jeroen Frijters + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. + + Jeroen Frijters + jeroen@frijters.net + +*/ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.IO; + +namespace Ildasm +{ + sealed class LineWriter + { + readonly TextWriter writer; + int column; + bool wrapped; + + internal LineWriter(TextWriter writer) + { + this.writer = writer; + } + + internal int Column + { + get { return column; } + } + + internal bool Wrapped + { + get { return wrapped; } + } + + internal void ClearWrappedFlag() + { + wrapped = false; + } + + internal void Write(string str, params object[] args) + { + Write(String.Format(str, args)); + } + + internal void Write(string str) + { + writer.Write(str); + column += str.Length; + } + + internal void WriteLine(string str) + { + writer.WriteLine(str); + column = 0; + wrapped = true; + } + + internal void WriteLine(string str, params object[] args) + { + writer.WriteLine(str, args); + column = 0; + wrapped = true; + } + + internal void WriteLine() + { + writer.WriteLine(); + column = 0; + wrapped = true; + } + + internal void GoToColumn(int column) + { + Write(new String(' ', column - this.column)); + } + + internal void Flush() + { + writer.Flush(); + } + } +} diff --git a/Program.cs b/Program.cs new file mode 100644 index 0000000..528fdfb --- /dev/null +++ b/Program.cs @@ -0,0 +1,149 @@ +/* + Copyright (C) 2012 Jeroen Frijters + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. + + Jeroen Frijters + jeroen@frijters.net + +*/ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.IO; + +namespace Ildasm +{ + class Program + { + static void Main(string[] args) + { + string outputFile = null; + string inputFile = null; + var compatLevel = CompatLevel.None; + var diffMode = false; + foreach (var arg in args) + { + if (arg.StartsWith("-", StringComparison.Ordinal) || arg.StartsWith("/", StringComparison.Ordinal)) + { + string value; + if (TryMatchOption(arg, "out", out value)) + { + outputFile = value; + } + else if (TryMatchOption(arg, "compat", out value)) + { + switch (value) + { + case "2.0": + compatLevel = CompatLevel.V20; + break; + case "4.0": + compatLevel = CompatLevel.V40; + break; + case "4.5": + compatLevel = CompatLevel.V45; + break; + default: + PrintUsage(); + return; + } + } + else if (String.Compare(arg, 1, "diffmode", 0, 8, StringComparison.OrdinalIgnoreCase) == 0) + { + diffMode = true; + } + else + { + PrintUsage(); + return; + } + } + else + { + if (inputFile != null) + { + PrintUsage(); + return; + } + else + { + inputFile = arg; + } + } + } + if (inputFile == null) + { + PrintUsage(); + return; + } + var disassembler = new Disassembler(inputFile, outputFile, compatLevel, diffMode); + if (outputFile != null) + { + Encoding enc; + switch (compatLevel) + { + case CompatLevel.None: + enc = Encoding.UTF8; + break; + case CompatLevel.V20: + case CompatLevel.V40: + // instantiate new UTF8Encoding to avoid the preamble that Encoding.UTF8 has + // (note that the only non-ASCII character that we're encoding is the \uFFFD placeholder for non-ASCII characters) + enc = new UTF8Encoding(); + break; + default: + enc = Console.OutputEncoding; + break; + } + using (StreamWriter sw = new StreamWriter(outputFile, false, enc)) + { + disassembler.Save(sw); + } + } + else + { + disassembler.Save(Console.Out); + } + } + + static bool TryMatchOption(string arg, string key, out string value) + { + if (arg.Length > key.Length + 2 && (arg[key.Length + 1] == ':' || arg[key.Length + 1] == '=') && String.Compare(arg, 1, key, 0, key.Length, true) == 0) + { + value = arg.Substring(key.Length + 2); + return true; + } + value = null; + return false; + } + + static void PrintUsage() + { + Console.WriteLine("IKDASM - IL disassembler example for IKVM.Reflection"); + Console.WriteLine("Copyright (C) 2012-2013 Jeroen Frijters"); + Console.WriteLine(); + Console.WriteLine("Usage: ikdasm [options] <file_name> [options]"); + Console.WriteLine(); + Console.WriteLine("Options:"); + Console.WriteLine(" /OUT=<file name> Direct output to file rather than to stdout."); + Console.WriteLine(" /COMPAT=<version> Match ildasm behavior. (<version> = 2.0 | 4.0 | 4.5)"); + Console.WriteLine(" /DIFFMODE Remove superficial differences to allow assembly comparisons"); + } + } +} @@ -0,0 +1,53 @@ +/* + Copyright (C) 2012 Jeroen Frijters + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. + + Jeroen Frijters + jeroen@frijters.net + +*/ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace IKVM.Reflection.Reader +{ + [System.Runtime.InteropServices.StructLayout(System.Runtime.InteropServices.LayoutKind.Explicit)] + struct SingleConverter + { + [System.Runtime.InteropServices.FieldOffset(0)] + private int i; + [System.Runtime.InteropServices.FieldOffset(0)] + private float f; + + internal static int SingleToInt32Bits(float v) + { + SingleConverter c = new SingleConverter(); + c.f = v; + return c.i; + } + + internal static float Int32BitsToSingle(int v) + { + SingleConverter c = new SingleConverter(); + c.i = v; + return c.f; + } + } +} diff --git a/VTableFixups.cs b/VTableFixups.cs new file mode 100644 index 0000000..6526d23 --- /dev/null +++ b/VTableFixups.cs @@ -0,0 +1,163 @@ +/* + Copyright (C) 2012 Jeroen Frijters + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. + + Jeroen Frijters + jeroen@frijters.net + +*/ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using IKVM.Reflection; + +namespace Ildasm +{ + partial class Disassembler + { + const ushort COR_VTABLE_32BIT = 0x01; + const ushort COR_VTABLE_64BIT = 0x02; + const ushort COR_VTABLE_FROM_UNMANAGED = 0x04; + const ushort COR_VTABLE_FROM_UNMANAGED_RETAIN_APPDOMAIN = 0x08; + Dictionary<MethodBase, List<KeyValuePair<int, int>>> vtentryMap = new Dictionary<MethodBase, List<KeyValuePair<int, int>>>(); + + void WriteVTableFixupComment(LineWriter lw) + { + int ptrsize = GetPointerSize(); + VTableFixups[] fixups = GetVTableFixups(); + if (fixups.Length != 0) + { + lw.WriteLine("// VTableFixup Directory:"); + for (int i = 0; i < fixups.Length; i++) + { + lw.WriteLine("// IMAGE_COR_VTABLEFIXUP[{0}]:", i); + lw.WriteLine("// RVA: 0x{0:x8}", fixups[i].RVA); + lw.WriteLine("// Count: 0x{0:x4}", fixups[i].Count); + lw.WriteLine("// Type: 0x{0:x4}", fixups[i].Type); + var methods = GetVTableMethods(fixups[i]); + for (int j = 0; j < methods.Length; j++) + { + var method = methods[j]; + List<KeyValuePair<int,int>> list; + if (!vtentryMap.TryGetValue(method, out list)) + { + list = new List<KeyValuePair<int, int>>(); + vtentryMap.Add(method, list); + } + list.Add(new KeyValuePair<int,int>(i + 1, j + 1)); + if (ptrsize == 4) + { + lw.WriteLine("// [0x{0:x4}] (0x{1:x8})", j, method.MetadataToken); + } + else + { + lw.WriteLine("// [0x{0:x4}] (0x {1:x})", j, method.MetadataToken); + } + } + } + lw.WriteLine(); + } + lw.WriteLine(); + } + + void WriteVTableFixups(LineWriter lw) + { + int ptrsize = GetPointerSize(); + VTableFixups[] fixups = GetVTableFixups(); + if (fixups.Length != 0) + { + for (int i = 0; i < fixups.Length; i++) + { + lw.Write(".vtfixup [{0}] {1}{2} at D_{3:X8} //", + fixups[i].Count, + (fixups[i].Type & COR_VTABLE_32BIT) != 0 ? "int32" : "int64", + (fixups[i].Type & COR_VTABLE_FROM_UNMANAGED_RETAIN_APPDOMAIN) != 0 + ? " retainappdomain" + : (fixups[i].Type & COR_VTABLE_FROM_UNMANAGED) != 0 + ? " fromunmanaged" + : "", + fixups[i].RVA); + foreach (var method in GetVTableMethods(fixups[i])) + { + if (ptrsize == 4) + { + lw.Write(" {0:X8}", method.MetadataToken); + } + else + { + lw.Write(" {0:X16}", method.MetadataToken); + } + } + lw.WriteLine(); + } + } + } + + struct VTableFixups + { + internal int RVA; + internal short Count; + internal short Type; + } + + VTableFixups[] GetVTableFixups() + { + int rva; + int length; + module.__GetDataDirectoryEntry(14, out rva, out length); + byte[] buf = new byte[8]; + module.__ReadDataFromRVA(rva + 48, buf, 0, 8); + rva = BitConverter.ToInt32(buf, 0); + if (rva == 0) + { + return new VTableFixups[0]; + } + VTableFixups[] entries = new VTableFixups[BitConverter.ToInt32(buf, 4) / 8]; + for (int i = 0; i < entries.Length; i++) + { + module.__ReadDataFromRVA(rva + i * 8, buf, 0, 8); + entries[i].RVA = BitConverter.ToInt32(buf, 0); + entries[i].Count = BitConverter.ToInt16(buf, 4); + entries[i].Type = BitConverter.ToInt16(buf, 6); + } + return entries; + } + + MethodBase[] GetVTableMethods(VTableFixups fixups) + { + var methods = new MethodBase[fixups.Count]; + byte[] buf = new byte[8]; + int fixuprva = fixups.RVA; + for (int i = 0; i < fixups.Count; i++) + { + module.__ReadDataFromRVA(fixuprva, buf, 0, 4); + methods[i] = module.ResolveMethod(BitConverter.ToInt32(buf, 0)); + if ((fixups.Type & COR_VTABLE_32BIT) != 0) + { + fixuprva += 4; + } + if ((fixups.Type & COR_VTABLE_64BIT) != 0) + { + fixuprva += 8; + } + } + return methods; + } + } +} diff --git a/ikdasm.csproj b/ikdasm.csproj new file mode 100644 index 0000000..4d558e2 --- /dev/null +++ b/ikdasm.csproj @@ -0,0 +1,78 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <PropertyGroup> + <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration> + <Platform Condition=" '$(Platform)' == '' ">x86</Platform> + <ProductVersion>8.0.30703</ProductVersion> + <SchemaVersion>2.0</SchemaVersion> + <ProjectGuid>{6C6F65C4-81B6-46CC-A4BE-A0C6F9ED7EE6}</ProjectGuid> + <OutputType>Exe</OutputType> + <AppDesignerFolder>Properties</AppDesignerFolder> + <RootNamespace>Ikdasm</RootNamespace> + <AssemblyName>ikdasm</AssemblyName> + <TargetFrameworkVersion>v3.5</TargetFrameworkVersion> + <FileAlignment>512</FileAlignment> + <SccProjectName> + </SccProjectName> + <SccLocalPath> + </SccLocalPath> + <SccAuxPath> + </SccAuxPath> + <SccProvider> + </SccProvider> + </PropertyGroup> + <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|x86' "> + <PlatformTarget>x86</PlatformTarget> + <DebugSymbols>true</DebugSymbols> + <DebugType>full</DebugType> + <Optimize>false</Optimize> + <OutputPath>bin\Debug\</OutputPath> + <DefineConstants>DEBUG;TRACE</DefineConstants> + <ErrorReport>prompt</ErrorReport> + <WarningLevel>4</WarningLevel> + </PropertyGroup> + <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|x86' "> + <PlatformTarget>x86</PlatformTarget> + <DebugType>pdbonly</DebugType> + <Optimize>true</Optimize> + <OutputPath>bin\Release\</OutputPath> + <DefineConstants>TRACE</DefineConstants> + <ErrorReport>prompt</ErrorReport> + <WarningLevel>4</WarningLevel> + </PropertyGroup> + <ItemGroup> + <Reference Include="IKVM.Reflection, Version=7.3.4804.0, Culture=neutral, processorArchitecture=MSIL"> + <SpecificVersion>False</SpecificVersion> + <HintPath>.\IKVM.Reflection.dll</HintPath> + </Reference> + <Reference Include="System" /> + <Reference Include="System.Core" /> + <Reference Include="System.Xml.Linq" /> + <Reference Include="System.Data.DataSetExtensions" /> + <Reference Include="System.Data" /> + <Reference Include="System.Xml" /> + </ItemGroup> + <ItemGroup> + <Compile Include="ByteReader.cs" /> + <Compile Include="CABlob.cs" /> + <Compile Include="Disassembler.cs" /> + <Compile Include="ExportedMethods.cs" /> + <Compile Include="IL.cs" /> + <Compile Include="Keywords.cs" /> + <Compile Include="LineWriter.cs" /> + <Compile Include="Program.cs" /> + <Compile Include="Util.cs" /> + <Compile Include="VTableFixups.cs" /> + </ItemGroup> + <ItemGroup> + <Folder Include="Properties\" /> + </ItemGroup> + <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" /> + <!-- To modify your build process, add your task inside one of the targets below and uncomment it. + Other similar extension points exist, see Microsoft.Common.targets. + <Target Name="BeforeBuild"> + </Target> + <Target Name="AfterBuild"> + </Target> + --> +</Project>
\ No newline at end of file diff --git a/ikdasm.sln b/ikdasm.sln new file mode 100644 index 0000000..caf1ef8 --- /dev/null +++ b/ikdasm.sln @@ -0,0 +1,20 @@ + +Microsoft Visual Studio Solution File, Format Version 11.00 +# Visual Studio 2010 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ikdasm", "ikdasm.csproj", "{6C6F65C4-81B6-46CC-A4BE-A0C6F9ED7EE6}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|x86 = Debug|x86 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {6C6F65C4-81B6-46CC-A4BE-A0C6F9ED7EE6}.Debug|x86.ActiveCfg = Debug|x86 + {6C6F65C4-81B6-46CC-A4BE-A0C6F9ED7EE6}.Debug|x86.Build.0 = Debug|x86 + {6C6F65C4-81B6-46CC-A4BE-A0C6F9ED7EE6}.Release|x86.ActiveCfg = Release|x86 + {6C6F65C4-81B6-46CC-A4BE-A0C6F9ED7EE6}.Release|x86.Build.0 = Release|x86 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal |