diff options
Diffstat (limited to 'ICSharpCode.Decompiler/Disassembler')
4 files changed, 2082 insertions, 0 deletions
diff --git a/ICSharpCode.Decompiler/Disassembler/DisassemblerHelpers.cs b/ICSharpCode.Decompiler/Disassembler/DisassemblerHelpers.cs new file mode 100644 index 00000000..ea6e54db --- /dev/null +++ b/ICSharpCode.Decompiler/Disassembler/DisassemblerHelpers.cs @@ -0,0 +1,447 @@ +// Copyright (c) 2011 AlphaSierraPapa for the SharpDevelop Team +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of this +// software and associated documentation files (the "Software"), to deal in the Software +// without restriction, including without limitation the rights to use, copy, modify, merge, +// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons +// to whom the Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all copies or +// substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, +// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE +// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. + +using System; +using System.Collections.Generic; +using Mono.Cecil; +using Mono.Cecil.Cil; + +namespace ICSharpCode.Decompiler.Disassembler +{ + public enum ILNameSyntax + { + /// <summary> + /// class/valuetype + TypeName (built-in types use keyword syntax) + /// </summary> + Signature, + /// <summary> + /// Like signature, but always refers to type parameters using their position + /// </summary> + SignatureNoNamedTypeParameters, + /// <summary> + /// [assembly]Full.Type.Name (even for built-in types) + /// </summary> + TypeName, + /// <summary> + /// Name (but built-in types use keyword syntax) + /// </summary> + ShortTypeName + } + + public static class DisassemblerHelpers + { + public static void WriteOffsetReference(ITextOutput writer, Instruction instruction) + { + writer.WriteReference(CecilExtensions.OffsetToString(instruction.Offset), instruction); + } + + public static void WriteTo(this ExceptionHandler exceptionHandler, ITextOutput writer) + { + writer.Write("Try "); + WriteOffsetReference(writer, exceptionHandler.TryStart); + writer.Write('-'); + WriteOffsetReference(writer, exceptionHandler.TryEnd); + writer.Write(' '); + writer.Write(exceptionHandler.HandlerType.ToString()); + if (exceptionHandler.FilterStart != null) { + writer.Write(' '); + WriteOffsetReference(writer, exceptionHandler.FilterStart); + writer.Write(" handler "); + } + if (exceptionHandler.CatchType != null) { + writer.Write(' '); + exceptionHandler.CatchType.WriteTo(writer); + } + writer.Write(' '); + WriteOffsetReference(writer, exceptionHandler.HandlerStart); + writer.Write('-'); + WriteOffsetReference(writer, exceptionHandler.HandlerEnd); + } + + public static void WriteTo(this Instruction instruction, ITextOutput writer) + { + writer.WriteDefinition(CecilExtensions.OffsetToString(instruction.Offset), instruction); + writer.Write(": "); + writer.WriteReference(instruction.OpCode.Name, instruction.OpCode); + if (instruction.Operand != null) { + writer.Write(' '); + if (instruction.OpCode == OpCodes.Ldtoken) { + if (instruction.Operand is MethodReference) + writer.Write("method "); + else if (instruction.Operand is FieldReference) + writer.Write("field "); + } + WriteOperand(writer, instruction.Operand); + } + } + + static void WriteLabelList(ITextOutput writer, Instruction[] instructions) + { + writer.Write("("); + for(int i = 0; i < instructions.Length; i++) { + if(i != 0) writer.Write(", "); + WriteOffsetReference(writer, instructions[i]); + } + writer.Write(")"); + } + + static string ToInvariantCultureString(object value) + { + IConvertible convertible = value as IConvertible; + return(null != convertible) + ? convertible.ToString(System.Globalization.CultureInfo.InvariantCulture) + : value.ToString(); + } + + public static void WriteTo(this MethodReference method, ITextOutput writer) + { + if (method.ExplicitThis) { + writer.Write("instance explicit "); + } + else if (method.HasThis) { + writer.Write("instance "); + } + method.ReturnType.WriteTo(writer, ILNameSyntax.SignatureNoNamedTypeParameters); + writer.Write(' '); + if (method.DeclaringType != null) { + method.DeclaringType.WriteTo(writer, ILNameSyntax.TypeName); + writer.Write("::"); + } + MethodDefinition md = method as MethodDefinition; + if (md != null && md.IsCompilerControlled) { + writer.WriteReference(Escape(method.Name + "$PST" + method.MetadataToken.ToInt32().ToString("X8")), method); + } else { + writer.WriteReference(Escape(method.Name), method); + } + GenericInstanceMethod gim = method as GenericInstanceMethod; + if (gim != null) { + writer.Write('<'); + for (int i = 0; i < gim.GenericArguments.Count; i++) { + if (i > 0) + writer.Write(", "); + gim.GenericArguments[i].WriteTo(writer); + } + writer.Write('>'); + } + writer.Write("("); + var parameters = method.Parameters; + for(int i = 0; i < parameters.Count; ++i) { + if (i > 0) writer.Write(", "); + parameters[i].ParameterType.WriteTo(writer, ILNameSyntax.SignatureNoNamedTypeParameters); + } + writer.Write(")"); + } + + static void WriteTo(this FieldReference field, ITextOutput writer) + { + field.FieldType.WriteTo(writer, ILNameSyntax.SignatureNoNamedTypeParameters); + writer.Write(' '); + field.DeclaringType.WriteTo(writer, ILNameSyntax.TypeName); + writer.Write("::"); + writer.WriteReference(Escape(field.Name), field); + } + + static bool IsValidIdentifierCharacter(char c) + { + return c == '_' || c == '$' || c == '@' || c == '?' || c == '`'; + } + + static bool IsValidIdentifier(string identifier) + { + if (string.IsNullOrEmpty(identifier)) + return false; + if (!(char.IsLetter(identifier[0]) || IsValidIdentifierCharacter(identifier[0]))) { + // As a special case, .ctor and .cctor are valid despite starting with a dot + return identifier == ".ctor" || identifier == ".cctor"; + } + for (int i = 1; i < identifier.Length; i++) { + if (!(char.IsLetterOrDigit(identifier[i]) || IsValidIdentifierCharacter(identifier[i]) || identifier[i] == '.')) + return false; + } + return true; + } + + static readonly HashSet<string> ilKeywords = BuildKeywordList( + "abstract", "algorithm", "alignment", "ansi", "any", "arglist", + "array", "as", "assembly", "assert", "at", "auto", "autochar", "beforefieldinit", + "blob", "blob_object", "bool", "brnull", "brnull.s", "brzero", "brzero.s", "bstr", + "bytearray", "byvalstr", "callmostderived", "carray", "catch", "cdecl", "cf", + "char", "cil", "class", "clsid", "const", "currency", "custom", "date", "decimal", + "default", "demand", "deny", "endmac", "enum", "error", "explicit", "extends", "extern", + "false", "famandassem", "family", "famorassem", "fastcall", "fault", "field", "filetime", + "filter", "final", "finally", "fixed", "float", "float32", "float64", "forwardref", + "fromunmanaged", "handler", "hidebysig", "hresult", "idispatch", "il", "illegal", + "implements", "implicitcom", "implicitres", "import", "in", "inheritcheck", "init", + "initonly", "instance", "int", "int16", "int32", "int64", "int8", "interface", "internalcall", + "iunknown", "lasterr", "lcid", "linkcheck", "literal", "localloc", "lpstr", "lpstruct", "lptstr", + "lpvoid", "lpwstr", "managed", "marshal", "method", "modopt", "modreq", "native", "nested", + "newslot", "noappdomain", "noinlining", "nomachine", "nomangle", "nometadata", "noncasdemand", + "noncasinheritance", "noncaslinkdemand", "noprocess", "not", "not_in_gc_heap", "notremotable", + "notserialized", "null", "nullref", "object", "objectref", "opt", "optil", "out", + "permitonly", "pinned", "pinvokeimpl", "prefix1", "prefix2", "prefix3", "prefix4", "prefix5", "prefix6", + "prefix7", "prefixref", "prejitdeny", "prejitgrant", "preservesig", "private", "privatescope", "protected", + "public", "record", "refany", "reqmin", "reqopt", "reqrefuse", "reqsecobj", "request", "retval", + "rtspecialname", "runtime", "safearray", "sealed", "sequential", "serializable", "special", "specialname", + "static", "stdcall", "storage", "stored_object", "stream", "streamed_object", "string", "struct", + "synchronized", "syschar", "sysstring", "tbstr", "thiscall", "tls", "to", "true", "typedref", + "unicode", "unmanaged", "unmanagedexp", "unsigned", "unused", "userdefined", "value", "valuetype", + "vararg", "variant", "vector", "virtual", "void", "wchar", "winapi", "with", "wrapper", + + // These are not listed as keywords in spec, but ILAsm treats them as such + "property", "type", "flags", "callconv", "strict" + ); + + static HashSet<string> BuildKeywordList(params string[] keywords) + { + HashSet<string> s = new HashSet<string>(keywords); + foreach (var field in typeof(OpCodes).GetFields()) { + s.Add(((OpCode)field.GetValue(null)).Name); + } + return s; + } + + public static string Escape(string identifier) + { + if (IsValidIdentifier(identifier) && !ilKeywords.Contains(identifier)) { + return identifier; + } else { + // The ECMA specification says that ' inside SQString should be ecaped using an octal escape sequence, + // but we follow Microsoft's ILDasm and use \'. + return "'" + NRefactory.CSharp.TextWriterTokenWriter.ConvertString(identifier).Replace("'", "\\'") + "'"; + } + } + + public static void WriteTo(this TypeReference type, ITextOutput writer, ILNameSyntax syntax = ILNameSyntax.Signature) + { + ILNameSyntax syntaxForElementTypes = syntax == ILNameSyntax.SignatureNoNamedTypeParameters ? syntax : ILNameSyntax.Signature; + if (type is PinnedType) { + ((PinnedType)type).ElementType.WriteTo(writer, syntaxForElementTypes); + writer.Write(" pinned"); + } else if (type is ArrayType) { + ArrayType at = (ArrayType)type; + at.ElementType.WriteTo(writer, syntaxForElementTypes); + writer.Write('['); + writer.Write(string.Join(", ", at.Dimensions)); + writer.Write(']'); + } else if (type is GenericParameter) { + writer.Write('!'); + if (((GenericParameter)type).Owner.GenericParameterType == GenericParameterType.Method) + writer.Write('!'); + if (string.IsNullOrEmpty(type.Name) || type.Name[0] == '!' || syntax == ILNameSyntax.SignatureNoNamedTypeParameters) + writer.Write(((GenericParameter)type).Position.ToString()); + else + writer.Write(Escape(type.Name)); + } else if (type is ByReferenceType) { + ((ByReferenceType)type).ElementType.WriteTo(writer, syntaxForElementTypes); + writer.Write('&'); + } else if (type is PointerType) { + ((PointerType)type).ElementType.WriteTo(writer, syntaxForElementTypes); + writer.Write('*'); + } else if (type is GenericInstanceType) { + type.GetElementType().WriteTo(writer, syntaxForElementTypes); + writer.Write('<'); + var arguments = ((GenericInstanceType)type).GenericArguments; + for (int i = 0; i < arguments.Count; i++) { + if (i > 0) + writer.Write(", "); + arguments[i].WriteTo(writer, syntaxForElementTypes); + } + writer.Write('>'); + } else if (type is OptionalModifierType) { + ((OptionalModifierType)type).ElementType.WriteTo(writer, syntax); + writer.Write(" modopt("); + ((OptionalModifierType)type).ModifierType.WriteTo(writer, ILNameSyntax.TypeName); + writer.Write(") "); + } else if (type is RequiredModifierType) { + ((RequiredModifierType)type).ElementType.WriteTo(writer, syntax); + writer.Write(" modreq("); + ((RequiredModifierType)type).ModifierType.WriteTo(writer, ILNameSyntax.TypeName); + writer.Write(") "); + } else { + string name = PrimitiveTypeName(type.FullName); + if (syntax == ILNameSyntax.ShortTypeName) { + if (name != null) + writer.Write(name); + else + writer.WriteReference(Escape(type.Name), type); + } else if ((syntax == ILNameSyntax.Signature || syntax == ILNameSyntax.SignatureNoNamedTypeParameters) && name != null) { + writer.Write(name); + } else { + if (syntax == ILNameSyntax.Signature || syntax == ILNameSyntax.SignatureNoNamedTypeParameters) + writer.Write(type.IsValueType ? "valuetype " : "class "); + + if (type.DeclaringType != null) { + type.DeclaringType.WriteTo(writer, ILNameSyntax.TypeName); + writer.Write('/'); + writer.WriteReference(Escape(type.Name), type); + } else { + if (!type.IsDefinition && type.Scope != null && !(type is TypeSpecification)) + writer.Write("[{0}]", Escape(type.Scope.Name)); + writer.WriteReference(Escape(type.FullName), type); + } + } + } + } + + public static void WriteOperand(ITextOutput writer, object operand) + { + if (operand == null) + throw new ArgumentNullException("operand"); + + Instruction targetInstruction = operand as Instruction; + if (targetInstruction != null) { + WriteOffsetReference(writer, targetInstruction); + return; + } + + Instruction[] targetInstructions = operand as Instruction[]; + if (targetInstructions != null) { + WriteLabelList(writer, targetInstructions); + return; + } + + VariableReference variableRef = operand as VariableReference; + if (variableRef != null) { + if (string.IsNullOrEmpty(variableRef.Name)) + writer.WriteReference(variableRef.Index.ToString(), variableRef); + else + writer.WriteReference(Escape(variableRef.Name), variableRef); + return; + } + + ParameterReference paramRef = operand as ParameterReference; + if (paramRef != null) { + if (string.IsNullOrEmpty(paramRef.Name)) + writer.WriteReference(paramRef.Index.ToString(), paramRef); + else + writer.WriteReference(Escape(paramRef.Name), paramRef); + return; + } + + MethodReference methodRef = operand as MethodReference; + if (methodRef != null) { + methodRef.WriteTo(writer); + return; + } + + TypeReference typeRef = operand as TypeReference; + if (typeRef != null) { + typeRef.WriteTo(writer, ILNameSyntax.TypeName); + return; + } + + FieldReference fieldRef = operand as FieldReference; + if (fieldRef != null) { + fieldRef.WriteTo(writer); + return; + } + + string s = operand as string; + if (s != null) { + writer.Write("\"" + NRefactory.CSharp.TextWriterTokenWriter.ConvertString(s) + "\""); + } else if (operand is char) { + writer.Write(((int)(char)operand).ToString()); + } else if (operand is float) { + float val = (float)operand; + if (val == 0) { + if (1 / val == float.NegativeInfinity) { + // negative zero is a special case + writer.Write('-'); + } + writer.Write("0.0"); + } else if (float.IsInfinity(val) || float.IsNaN(val)) { + byte[] data = BitConverter.GetBytes(val); + writer.Write('('); + for (int i = 0; i < data.Length; i++) { + if (i > 0) + writer.Write(' '); + writer.Write(data[i].ToString("X2")); + } + writer.Write(')'); + } else { + writer.Write(val.ToString("R", System.Globalization.CultureInfo.InvariantCulture)); + } + } else if (operand is double) { + double val = (double)operand; + if (val == 0) { + if (1 / val == double.NegativeInfinity) { + // negative zero is a special case + writer.Write('-'); + } + writer.Write("0.0"); + } else if (double.IsInfinity(val) || double.IsNaN(val)) { + byte[] data = BitConverter.GetBytes(val); + writer.Write('('); + for (int i = 0; i < data.Length; i++) { + if (i > 0) + writer.Write(' '); + writer.Write(data[i].ToString("X2")); + } + writer.Write(')'); + } else { + writer.Write(val.ToString("R", System.Globalization.CultureInfo.InvariantCulture)); + } + } else if (operand is bool) { + writer.Write((bool)operand ? "true" : "false"); + } else { + s = ToInvariantCultureString(operand); + writer.Write(s); + } + } + + public static string PrimitiveTypeName(string fullName) + { + switch (fullName) { + case "System.SByte": + return "int8"; + case "System.Int16": + return "int16"; + case "System.Int32": + return "int32"; + case "System.Int64": + return "int64"; + case "System.Byte": + return "uint8"; + case "System.UInt16": + return "uint16"; + case "System.UInt32": + return "uint32"; + case "System.UInt64": + return "uint64"; + case "System.Single": + return "float32"; + case "System.Double": + return "float64"; + case "System.Void": + return "void"; + case "System.Boolean": + return "bool"; + case "System.String": + return "string"; + case "System.Char": + return "char"; + case "System.Object": + return "object"; + case "System.IntPtr": + return "native int"; + default: + return null; + } + } + } +} diff --git a/ICSharpCode.Decompiler/Disassembler/ILStructure.cs b/ICSharpCode.Decompiler/Disassembler/ILStructure.cs new file mode 100644 index 00000000..ff9285b2 --- /dev/null +++ b/ICSharpCode.Decompiler/Disassembler/ILStructure.cs @@ -0,0 +1,228 @@ +// Copyright (c) 2011 AlphaSierraPapa for the SharpDevelop Team +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of this +// software and associated documentation files (the "Software"), to deal in the Software +// without restriction, including without limitation the rights to use, copy, modify, merge, +// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons +// to whom the Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all copies or +// substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, +// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE +// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using ICSharpCode.Decompiler.FlowAnalysis; +using Mono.Cecil.Cil; + +namespace ICSharpCode.Decompiler.Disassembler +{ + /// <summary> + /// Specifies the type of an IL structure. + /// </summary> + public enum ILStructureType + { + /// <summary> + /// The root block of the method + /// </summary> + Root, + /// <summary> + /// A nested control structure representing a loop. + /// </summary> + Loop, + /// <summary> + /// A nested control structure representing a try block. + /// </summary> + Try, + /// <summary> + /// A nested control structure representing a catch, finally, or fault block. + /// </summary> + Handler, + /// <summary> + /// A nested control structure representing an exception filter block. + /// </summary> + Filter + } + + /// <summary> + /// An IL structure. + /// </summary> + public class ILStructure + { + public readonly ILStructureType Type; + + /// <summary> + /// Start position of the structure. + /// </summary> + public readonly int StartOffset; + + /// <summary> + /// End position of the structure. (exclusive) + /// </summary> + public readonly int EndOffset; + + /// <summary> + /// The exception handler associated with the Try, Filter or Handler block. + /// </summary> + public readonly ExceptionHandler ExceptionHandler; + + /// <summary> + /// The loop's entry point. + /// </summary> + public readonly Instruction LoopEntryPoint; + + /// <summary> + /// The list of child structures. + /// </summary> + public readonly List<ILStructure> Children = new List<ILStructure>(); + + public ILStructure(MethodBody body) + : this(ILStructureType.Root, 0, body.CodeSize) + { + // Build the tree of exception structures: + for (int i = 0; i < body.ExceptionHandlers.Count; i++) { + ExceptionHandler eh = body.ExceptionHandlers[i]; + if (!body.ExceptionHandlers.Take(i).Any(oldEh => oldEh.TryStart == eh.TryStart && oldEh.TryEnd == eh.TryEnd)) + AddNestedStructure(new ILStructure(ILStructureType.Try, eh.TryStart.Offset, eh.TryEnd.Offset, eh)); + if (eh.HandlerType == ExceptionHandlerType.Filter) + AddNestedStructure(new ILStructure(ILStructureType.Filter, eh.FilterStart.Offset, eh.HandlerStart.Offset, eh)); + AddNestedStructure(new ILStructure(ILStructureType.Handler, eh.HandlerStart.Offset, eh.HandlerEnd == null ? body.CodeSize : eh.HandlerEnd.Offset, eh)); + } + // Very simple loop detection: look for backward branches + List<KeyValuePair<Instruction, Instruction>> allBranches = FindAllBranches(body); + // We go through the branches in reverse so that we find the biggest possible loop boundary first (think loops with "continue;") + for (int i = allBranches.Count - 1; i >= 0; i--) { + int loopEnd = allBranches[i].Key.GetEndOffset(); + int loopStart = allBranches[i].Value.Offset; + if (loopStart < loopEnd) { + // We found a backward branch. This is a potential loop. + // Check that is has only one entry point: + Instruction entryPoint = null; + + // entry point is first instruction in loop if prev inst isn't an unconditional branch + Instruction prev = allBranches[i].Value.Previous; + if (prev != null && !OpCodeInfo.IsUnconditionalBranch(prev.OpCode)) + entryPoint = allBranches[i].Value; + + bool multipleEntryPoints = false; + foreach (var pair in allBranches) { + if (pair.Key.Offset < loopStart || pair.Key.Offset >= loopEnd) { + if (loopStart <= pair.Value.Offset && pair.Value.Offset < loopEnd) { + // jump from outside the loop into the loop + if (entryPoint == null) + entryPoint = pair.Value; + else if (pair.Value != entryPoint) + multipleEntryPoints = true; + } + } + } + if (!multipleEntryPoints) { + AddNestedStructure(new ILStructure(ILStructureType.Loop, loopStart, loopEnd, entryPoint)); + } + } + } + SortChildren(); + } + + public ILStructure(ILStructureType type, int startOffset, int endOffset, ExceptionHandler handler = null) + { + Debug.Assert(startOffset < endOffset); + Type = type; + StartOffset = startOffset; + EndOffset = endOffset; + ExceptionHandler = handler; + } + + public ILStructure(ILStructureType type, int startOffset, int endOffset, Instruction loopEntryPoint) + { + Debug.Assert(startOffset < endOffset); + Type = type; + StartOffset = startOffset; + EndOffset = endOffset; + LoopEntryPoint = loopEntryPoint; + } + + bool AddNestedStructure(ILStructure newStructure) + { + // special case: don't consider the loop-like structure of "continue;" statements to be nested loops + if (Type == ILStructureType.Loop && newStructure.Type == ILStructureType.Loop && newStructure.StartOffset == StartOffset) + return false; + + // use <= for end-offset comparisons because both end and EndOffset are exclusive + Debug.Assert(StartOffset <= newStructure.StartOffset && newStructure.EndOffset <= EndOffset); + foreach (ILStructure child in Children) { + if (child.StartOffset <= newStructure.StartOffset && newStructure.EndOffset <= child.EndOffset) { + return child.AddNestedStructure(newStructure); + } else if (!(child.EndOffset <= newStructure.StartOffset || newStructure.EndOffset <= child.StartOffset)) { + // child and newStructure overlap + if (!(newStructure.StartOffset <= child.StartOffset && child.EndOffset <= newStructure.EndOffset)) { + // Invalid nesting, can't build a tree. -> Don't add the new structure. + return false; + } + } + } + // Move existing structures into the new structure: + for (int i = 0; i < Children.Count; i++) { + ILStructure child = Children[i]; + if (newStructure.StartOffset <= child.StartOffset && child.EndOffset <= newStructure.EndOffset) { + Children.RemoveAt(i--); + newStructure.Children.Add(child); + } + } + // Add the structure here: + Children.Add(newStructure); + return true; + } + + /// <summary> + /// Finds all branches. Returns list of source offset->target offset mapping. + /// Multiple entries for the same source offset are possible (switch statements). + /// The result is sorted by source offset. + /// </summary> + List<KeyValuePair<Instruction, Instruction>> FindAllBranches(MethodBody body) + { + var result = new List<KeyValuePair<Instruction, Instruction>>(); + foreach (Instruction inst in body.Instructions) { + switch (inst.OpCode.OperandType) { + case OperandType.InlineBrTarget: + case OperandType.ShortInlineBrTarget: + result.Add(new KeyValuePair<Instruction, Instruction>(inst, (Instruction)inst.Operand)); + break; + case OperandType.InlineSwitch: + foreach (Instruction target in (Instruction[])inst.Operand) + result.Add(new KeyValuePair<Instruction, Instruction>(inst, target)); + break; + } + } + return result; + } + + void SortChildren() + { + Children.Sort((a, b) => a.StartOffset.CompareTo(b.StartOffset)); + foreach (ILStructure child in Children) + child.SortChildren(); + } + + /// <summary> + /// Gets the innermost structure containing the specified offset. + /// </summary> + public ILStructure GetInnermost(int offset) + { + Debug.Assert(StartOffset <= offset && offset < EndOffset); + foreach (ILStructure child in Children) { + if (child.StartOffset <= offset && offset < child.EndOffset) + return child.GetInnermost(offset); + } + return this; + } + } +} diff --git a/ICSharpCode.Decompiler/Disassembler/MethodBodyDisassembler.cs b/ICSharpCode.Decompiler/Disassembler/MethodBodyDisassembler.cs new file mode 100644 index 00000000..2b5db78b --- /dev/null +++ b/ICSharpCode.Decompiler/Disassembler/MethodBodyDisassembler.cs @@ -0,0 +1,239 @@ +// Copyright (c) 2011 AlphaSierraPapa for the SharpDevelop Team +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of this +// software and associated documentation files (the "Software"), to deal in the Software +// without restriction, including without limitation the rights to use, copy, modify, merge, +// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons +// to whom the Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all copies or +// substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, +// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE +// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading; + +using ICSharpCode.Decompiler; +using ICSharpCode.Decompiler.FlowAnalysis; +using ICSharpCode.Decompiler.ILAst; +using Mono.Cecil; +using Mono.Cecil.Cil; + +namespace ICSharpCode.Decompiler.Disassembler +{ + /// <summary> + /// Disassembles a method body. + /// </summary> + public sealed class MethodBodyDisassembler + { + readonly ITextOutput output; + readonly bool detectControlStructure; + readonly CancellationToken cancellationToken; + + public MethodBodyDisassembler(ITextOutput output, bool detectControlStructure, CancellationToken cancellationToken) + { + if (output == null) + throw new ArgumentNullException("output"); + this.output = output; + this.detectControlStructure = detectControlStructure; + this.cancellationToken = cancellationToken; + } + + public void Disassemble(MethodBody body, MethodDebugSymbols debugSymbols) + { + // start writing IL code + MethodDefinition method = body.Method; + output.WriteLine("// Method begins at RVA 0x{0:x4}", method.RVA); + output.WriteLine("// Code size {0} (0x{0:x})", body.CodeSize); + output.WriteLine(".maxstack {0}", body.MaxStackSize); + if (method.DeclaringType.Module.Assembly != null && method.DeclaringType.Module.Assembly.EntryPoint == method) + output.WriteLine (".entrypoint"); + + if (method.Body.HasVariables) { + output.Write(".locals "); + if (method.Body.InitLocals) + output.Write("init "); + output.WriteLine("("); + output.Indent(); + foreach (var v in method.Body.Variables) { + output.WriteDefinition("[" + v.Index + "] ", v); + v.VariableType.WriteTo(output); + if (!string.IsNullOrEmpty(v.Name)) { + output.Write(' '); + output.Write(DisassemblerHelpers.Escape(v.Name)); + } + if (v.Index + 1 < method.Body.Variables.Count) + output.Write(','); + output.WriteLine(); + } + output.Unindent(); + output.WriteLine(")"); + } + output.WriteLine(); + + if (detectControlStructure && body.Instructions.Count > 0) { + Instruction inst = body.Instructions[0]; + HashSet<int> branchTargets = GetBranchTargets(body.Instructions); + WriteStructureBody(new ILStructure(body), branchTargets, ref inst, debugSymbols, method.Body.CodeSize); + } else { + foreach (var inst in method.Body.Instructions) { + var startLocation = output.Location; + inst.WriteTo(output); + + if (debugSymbols != null) { + // add IL code mappings - used in debugger + debugSymbols.SequencePoints.Add( + new SequencePoint() { + StartLocation = output.Location, + EndLocation = output.Location, + ILRanges = new ILRange[] { new ILRange(inst.Offset, inst.Next == null ? method.Body.CodeSize : inst.Next.Offset) } + }); + } + + output.WriteLine(); + } + if (method.Body.HasExceptionHandlers) { + output.WriteLine(); + foreach (var eh in method.Body.ExceptionHandlers) { + eh.WriteTo(output); + output.WriteLine(); + } + } + } + } + + HashSet<int> GetBranchTargets(IEnumerable<Instruction> instructions) + { + HashSet<int> branchTargets = new HashSet<int>(); + foreach (var inst in instructions) { + Instruction target = inst.Operand as Instruction; + if (target != null) + branchTargets.Add(target.Offset); + Instruction[] targets = inst.Operand as Instruction[]; + if (targets != null) + foreach (Instruction t in targets) + branchTargets.Add(t.Offset); + } + return branchTargets; + } + + void WriteStructureHeader(ILStructure s) + { + switch (s.Type) { + case ILStructureType.Loop: + output.Write("// loop start"); + if (s.LoopEntryPoint != null) { + output.Write(" (head: "); + DisassemblerHelpers.WriteOffsetReference(output, s.LoopEntryPoint); + output.Write(')'); + } + output.WriteLine(); + break; + case ILStructureType.Try: + output.WriteLine(".try"); + output.WriteLine("{"); + break; + case ILStructureType.Handler: + switch (s.ExceptionHandler.HandlerType) { + case ExceptionHandlerType.Catch: + case ExceptionHandlerType.Filter: + output.Write("catch"); + if (s.ExceptionHandler.CatchType != null) { + output.Write(' '); + s.ExceptionHandler.CatchType.WriteTo(output, ILNameSyntax.TypeName); + } + output.WriteLine(); + break; + case ExceptionHandlerType.Finally: + output.WriteLine("finally"); + break; + case ExceptionHandlerType.Fault: + output.WriteLine("fault"); + break; + default: + throw new NotSupportedException(); + } + output.WriteLine("{"); + break; + case ILStructureType.Filter: + output.WriteLine("filter"); + output.WriteLine("{"); + break; + default: + throw new NotSupportedException(); + } + output.Indent(); + } + + void WriteStructureBody(ILStructure s, HashSet<int> branchTargets, ref Instruction inst, MethodDebugSymbols debugSymbols, int codeSize) + { + bool isFirstInstructionInStructure = true; + bool prevInstructionWasBranch = false; + int childIndex = 0; + while (inst != null && inst.Offset < s.EndOffset) { + int offset = inst.Offset; + if (childIndex < s.Children.Count && s.Children[childIndex].StartOffset <= offset && offset < s.Children[childIndex].EndOffset) { + ILStructure child = s.Children[childIndex++]; + WriteStructureHeader(child); + WriteStructureBody(child, branchTargets, ref inst, debugSymbols, codeSize); + WriteStructureFooter(child); + } else { + if (!isFirstInstructionInStructure && (prevInstructionWasBranch || branchTargets.Contains(offset))) { + output.WriteLine(); // put an empty line after branches, and in front of branch targets + } + var startLocation = output.Location; + inst.WriteTo(output); + + // add IL code mappings - used in debugger + if (debugSymbols != null) { + debugSymbols.SequencePoints.Add( + new SequencePoint() { + StartLocation = startLocation, + EndLocation = output.Location, + ILRanges = new ILRange[] { new ILRange(inst.Offset, inst.Next == null ? codeSize : inst.Next.Offset) } + }); + } + + output.WriteLine(); + + prevInstructionWasBranch = inst.OpCode.FlowControl == FlowControl.Branch + || inst.OpCode.FlowControl == FlowControl.Cond_Branch + || inst.OpCode.FlowControl == FlowControl.Return + || inst.OpCode.FlowControl == FlowControl.Throw; + + inst = inst.Next; + } + isFirstInstructionInStructure = false; + } + } + + void WriteStructureFooter(ILStructure s) + { + output.Unindent(); + switch (s.Type) { + case ILStructureType.Loop: + output.WriteLine("// end loop"); + break; + case ILStructureType.Try: + output.WriteLine("} // end .try"); + break; + case ILStructureType.Handler: + output.WriteLine("} // end handler"); + break; + case ILStructureType.Filter: + output.WriteLine("} // end filter"); + break; + default: + throw new NotSupportedException(); + } + } + } +} diff --git a/ICSharpCode.Decompiler/Disassembler/ReflectionDisassembler.cs b/ICSharpCode.Decompiler/Disassembler/ReflectionDisassembler.cs new file mode 100644 index 00000000..2a374c3a --- /dev/null +++ b/ICSharpCode.Decompiler/Disassembler/ReflectionDisassembler.cs @@ -0,0 +1,1168 @@ +// Copyright (c) 2011 AlphaSierraPapa for the SharpDevelop Team +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of this +// software and associated documentation files (the "Software"), to deal in the Software +// without restriction, including without limitation the rights to use, copy, modify, merge, +// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons +// to whom the Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all copies or +// substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, +// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE +// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using System.Text; +using System.Threading; +using ICSharpCode.NRefactory; +using Mono.Cecil; +using Mono.Collections.Generic; + +namespace ICSharpCode.Decompiler.Disassembler +{ + /// <summary> + /// Disassembles type and member definitions. + /// </summary> + public sealed class ReflectionDisassembler + { + ITextOutput output; + CancellationToken cancellationToken; + bool isInType; // whether we are currently disassembling a whole type (-> defaultCollapsed for foldings) + MethodBodyDisassembler methodBodyDisassembler; + MemberReference currentMember; + + public ReflectionDisassembler(ITextOutput output, bool detectControlStructure, CancellationToken cancellationToken) + { + if (output == null) + throw new ArgumentNullException("output"); + this.output = output; + this.cancellationToken = cancellationToken; + methodBodyDisassembler = new MethodBodyDisassembler(output, detectControlStructure, cancellationToken); + } + + #region Disassemble Method + EnumNameCollection<MethodAttributes> methodAttributeFlags = new EnumNameCollection<MethodAttributes>() { + { MethodAttributes.Final, "final" }, + { MethodAttributes.HideBySig, "hidebysig" }, + { MethodAttributes.SpecialName, "specialname" }, + { MethodAttributes.PInvokeImpl, null }, // handled separately + { MethodAttributes.UnmanagedExport, "export" }, + { MethodAttributes.RTSpecialName, "rtspecialname" }, + { MethodAttributes.RequireSecObject, "reqsecobj" }, + { MethodAttributes.NewSlot, "newslot" }, + { MethodAttributes.CheckAccessOnOverride, "strict" }, + { MethodAttributes.Abstract, "abstract" }, + { MethodAttributes.Virtual, "virtual" }, + { MethodAttributes.Static, "static" }, + { MethodAttributes.HasSecurity, null }, // ?? also invisible in ILDasm + }; + + EnumNameCollection<MethodAttributes> methodVisibility = new EnumNameCollection<MethodAttributes>() { + { MethodAttributes.Private, "private" }, + { MethodAttributes.FamANDAssem, "famandassem" }, + { MethodAttributes.Assembly, "assembly" }, + { MethodAttributes.Family, "family" }, + { MethodAttributes.FamORAssem, "famorassem" }, + { MethodAttributes.Public, "public" }, + }; + + EnumNameCollection<MethodCallingConvention> callingConvention = new EnumNameCollection<MethodCallingConvention>() { + { MethodCallingConvention.C, "unmanaged cdecl" }, + { MethodCallingConvention.StdCall, "unmanaged stdcall" }, + { MethodCallingConvention.ThisCall, "unmanaged thiscall" }, + { MethodCallingConvention.FastCall, "unmanaged fastcall" }, + { MethodCallingConvention.VarArg, "vararg" }, + { MethodCallingConvention.Generic, null }, + }; + + EnumNameCollection<MethodImplAttributes> methodCodeType = new EnumNameCollection<MethodImplAttributes>() { + { MethodImplAttributes.IL, "cil" }, + { MethodImplAttributes.Native, "native" }, + { MethodImplAttributes.OPTIL, "optil" }, + { MethodImplAttributes.Runtime, "runtime" }, + }; + + EnumNameCollection<MethodImplAttributes> methodImpl = new EnumNameCollection<MethodImplAttributes>() { + { MethodImplAttributes.Synchronized, "synchronized" }, + { MethodImplAttributes.NoInlining, "noinlining" }, + { MethodImplAttributes.NoOptimization, "nooptimization" }, + { MethodImplAttributes.PreserveSig, "preservesig" }, + { MethodImplAttributes.InternalCall, "internalcall" }, + { MethodImplAttributes.ForwardRef, "forwardref" }, + }; + + public void DisassembleMethod(MethodDefinition method) + { + // set current member + currentMember = method; + + // write method header + output.WriteDefinition(".method ", method); + DisassembleMethodInternal(method); + } + + void DisassembleMethodInternal(MethodDefinition method) + { + // .method public hidebysig specialname + // instance default class [mscorlib]System.IO.TextWriter get_BaseWriter () cil managed + // + + TextLocation startLocation = output.Location; + + //emit flags + WriteEnum(method.Attributes & MethodAttributes.MemberAccessMask, methodVisibility); + WriteFlags(method.Attributes & ~MethodAttributes.MemberAccessMask, methodAttributeFlags); + if(method.IsCompilerControlled) output.Write("privatescope "); + + if ((method.Attributes & MethodAttributes.PInvokeImpl) == MethodAttributes.PInvokeImpl) { + output.Write("pinvokeimpl"); + if (method.HasPInvokeInfo && method.PInvokeInfo != null) { + PInvokeInfo info = method.PInvokeInfo; + output.Write("(\"" + NRefactory.CSharp.TextWriterTokenWriter.ConvertString(info.Module.Name) + "\""); + + if (!string.IsNullOrEmpty(info.EntryPoint) && info.EntryPoint != method.Name) + output.Write(" as \"" + NRefactory.CSharp.TextWriterTokenWriter.ConvertString(info.EntryPoint) + "\""); + + if (info.IsNoMangle) + output.Write(" nomangle"); + + if (info.IsCharSetAnsi) + output.Write(" ansi"); + else if (info.IsCharSetAuto) + output.Write(" autochar"); + else if (info.IsCharSetUnicode) + output.Write(" unicode"); + + if (info.SupportsLastError) + output.Write(" lasterr"); + + if (info.IsCallConvCdecl) + output.Write(" cdecl"); + else if (info.IsCallConvFastcall) + output.Write(" fastcall"); + else if (info.IsCallConvStdCall) + output.Write(" stdcall"); + else if (info.IsCallConvThiscall) + output.Write(" thiscall"); + else if (info.IsCallConvWinapi) + output.Write(" winapi"); + + output.Write(')'); + } + output.Write(' '); + } + + output.WriteLine(); + output.Indent(); + if (method.ExplicitThis) { + output.Write("instance explicit "); + } else if (method.HasThis) { + output.Write("instance "); + } + + //call convention + WriteEnum(method.CallingConvention & (MethodCallingConvention)0x1f, callingConvention); + + //return type + method.ReturnType.WriteTo(output); + output.Write(' '); + if (method.MethodReturnType.HasMarshalInfo) { + WriteMarshalInfo(method.MethodReturnType.MarshalInfo); + } + + if (method.IsCompilerControlled) { + output.Write(DisassemblerHelpers.Escape(method.Name + "$PST" + method.MetadataToken.ToInt32().ToString("X8"))); + } else { + output.Write(DisassemblerHelpers.Escape(method.Name)); + } + + WriteTypeParameters(output, method); + + //( params ) + output.Write(" ("); + if (method.HasParameters) { + output.WriteLine(); + output.Indent(); + WriteParameters(method.Parameters); + output.Unindent(); + } + output.Write(") "); + //cil managed + WriteEnum(method.ImplAttributes & MethodImplAttributes.CodeTypeMask, methodCodeType); + if ((method.ImplAttributes & MethodImplAttributes.ManagedMask) == MethodImplAttributes.Managed) + output.Write("managed "); + else + output.Write("unmanaged "); + WriteFlags(method.ImplAttributes & ~(MethodImplAttributes.CodeTypeMask | MethodImplAttributes.ManagedMask), methodImpl); + + output.Unindent(); + OpenBlock(defaultCollapsed: isInType); + WriteAttributes(method.CustomAttributes); + if (method.HasOverrides) { + foreach (var methodOverride in method.Overrides) { + output.Write(".override method "); + methodOverride.WriteTo(output); + output.WriteLine(); + } + } + foreach (var p in method.Parameters) { + WriteParameterAttributes(p); + } + WriteSecurityDeclarations(method); + + if (method.HasBody) { + // create IL code mappings - used in debugger + MethodDebugSymbols debugSymbols = new MethodDebugSymbols(method); + debugSymbols.StartLocation = startLocation; + methodBodyDisassembler.Disassemble(method.Body, debugSymbols); + debugSymbols.EndLocation = output.Location; + output.AddDebugSymbols(debugSymbols); + } + + CloseBlock("end of method " + DisassemblerHelpers.Escape(method.DeclaringType.Name) + "::" + DisassemblerHelpers.Escape(method.Name)); + } + + #region Write Security Declarations + void WriteSecurityDeclarations(ISecurityDeclarationProvider secDeclProvider) + { + if (!secDeclProvider.HasSecurityDeclarations) + return; + foreach (var secdecl in secDeclProvider.SecurityDeclarations) { + output.Write(".permissionset "); + switch (secdecl.Action) { + case SecurityAction.Request: + output.Write("request"); + break; + case SecurityAction.Demand: + output.Write("demand"); + break; + case SecurityAction.Assert: + output.Write("assert"); + break; + case SecurityAction.Deny: + output.Write("deny"); + break; + case SecurityAction.PermitOnly: + output.Write("permitonly"); + break; + case SecurityAction.LinkDemand: + output.Write("linkcheck"); + break; + case SecurityAction.InheritDemand: + output.Write("inheritcheck"); + break; + case SecurityAction.RequestMinimum: + output.Write("reqmin"); + break; + case SecurityAction.RequestOptional: + output.Write("reqopt"); + break; + case SecurityAction.RequestRefuse: + output.Write("reqrefuse"); + break; + case SecurityAction.PreJitGrant: + output.Write("prejitgrant"); + break; + case SecurityAction.PreJitDeny: + output.Write("prejitdeny"); + break; + case SecurityAction.NonCasDemand: + output.Write("noncasdemand"); + break; + case SecurityAction.NonCasLinkDemand: + output.Write("noncaslinkdemand"); + break; + case SecurityAction.NonCasInheritance: + output.Write("noncasinheritance"); + break; + default: + output.Write(secdecl.Action.ToString()); + break; + } + output.WriteLine(" = {"); + output.Indent(); + for (int i = 0; i < secdecl.SecurityAttributes.Count; i++) { + SecurityAttribute sa = secdecl.SecurityAttributes[i]; + if (sa.AttributeType.Scope == sa.AttributeType.Module) { + output.Write("class "); + output.Write(DisassemblerHelpers.Escape(GetAssemblyQualifiedName(sa.AttributeType))); + } else { + sa.AttributeType.WriteTo(output, ILNameSyntax.TypeName); + } + output.Write(" = {"); + if (sa.HasFields || sa.HasProperties) { + output.WriteLine(); + output.Indent(); + + foreach (CustomAttributeNamedArgument na in sa.Fields) { + output.Write("field "); + WriteSecurityDeclarationArgument(na); + output.WriteLine(); + } + + foreach (CustomAttributeNamedArgument na in sa.Properties) { + output.Write("property "); + WriteSecurityDeclarationArgument(na); + output.WriteLine(); + } + + output.Unindent(); + } + output.Write('}'); + + if (i + 1< secdecl.SecurityAttributes.Count) + output.Write(','); + output.WriteLine(); + } + output.Unindent(); + output.WriteLine("}"); + } + } + + void WriteSecurityDeclarationArgument(CustomAttributeNamedArgument na) + { + TypeReference type = na.Argument.Type; + if (type.MetadataType == MetadataType.Class || type.MetadataType == MetadataType.ValueType) { + output.Write("enum "); + if (type.Scope != type.Module) { + output.Write("class "); + output.Write(DisassemblerHelpers.Escape(GetAssemblyQualifiedName(type))); + } else { + type.WriteTo(output, ILNameSyntax.TypeName); + } + } else { + type.WriteTo(output); + } + output.Write(' '); + output.Write(DisassemblerHelpers.Escape(na.Name)); + output.Write(" = "); + if (na.Argument.Value is string) { + // secdecls use special syntax for strings + output.Write("string('{0}')", NRefactory.CSharp.TextWriterTokenWriter.ConvertString((string)na.Argument.Value).Replace("'", "\'")); + } else { + WriteConstant(na.Argument.Value); + } + } + + string GetAssemblyQualifiedName(TypeReference type) + { + AssemblyNameReference anr = type.Scope as AssemblyNameReference; + if (anr == null) { + ModuleDefinition md = type.Scope as ModuleDefinition; + if (md != null) { + anr = md.Assembly.Name; + } + } + if (anr != null) { + return type.FullName + ", " + anr.FullName; + } else { + return type.FullName; + } + } + #endregion + + #region WriteMarshalInfo + void WriteMarshalInfo(MarshalInfo marshalInfo) + { + output.Write("marshal("); + WriteNativeType(marshalInfo.NativeType, marshalInfo); + output.Write(") "); + } + + void WriteNativeType(NativeType nativeType, MarshalInfo marshalInfo = null) + { + switch (nativeType) { + case NativeType.None: + break; + case NativeType.Boolean: + output.Write("bool"); + break; + case NativeType.I1: + output.Write("int8"); + break; + case NativeType.U1: + output.Write("unsigned int8"); + break; + case NativeType.I2: + output.Write("int16"); + break; + case NativeType.U2: + output.Write("unsigned int16"); + break; + case NativeType.I4: + output.Write("int32"); + break; + case NativeType.U4: + output.Write("unsigned int32"); + break; + case NativeType.I8: + output.Write("int64"); + break; + case NativeType.U8: + output.Write("unsigned int64"); + break; + case NativeType.R4: + output.Write("float32"); + break; + case NativeType.R8: + output.Write("float64"); + break; + case NativeType.LPStr: + output.Write("lpstr"); + break; + case NativeType.Int: + output.Write("int"); + break; + case NativeType.UInt: + output.Write("unsigned int"); + break; + case NativeType.Func: + goto default; // ?? + case NativeType.Array: + ArrayMarshalInfo ami = (ArrayMarshalInfo)marshalInfo; + if (ami == null) + goto default; + if (ami.ElementType != NativeType.Max) + WriteNativeType(ami.ElementType); + output.Write('['); + if (ami.SizeParameterMultiplier == 0) { + output.Write(ami.Size.ToString()); + } else { + if (ami.Size >= 0) + output.Write(ami.Size.ToString()); + output.Write(" + "); + output.Write(ami.SizeParameterIndex.ToString()); + } + output.Write(']'); + break; + case NativeType.Currency: + output.Write("currency"); + break; + case NativeType.BStr: + output.Write("bstr"); + break; + case NativeType.LPWStr: + output.Write("lpwstr"); + break; + case NativeType.LPTStr: + output.Write("lptstr"); + break; + case NativeType.FixedSysString: + output.Write("fixed sysstring[{0}]", ((FixedSysStringMarshalInfo)marshalInfo).Size); + break; + case NativeType.IUnknown: + output.Write("iunknown"); + break; + case NativeType.IDispatch: + output.Write("idispatch"); + break; + case NativeType.Struct: + output.Write("struct"); + break; + case NativeType.IntF: + output.Write("interface"); + break; + case NativeType.SafeArray: + output.Write("safearray "); + SafeArrayMarshalInfo sami = marshalInfo as SafeArrayMarshalInfo; + if (sami != null) { + switch (sami.ElementType) { + case VariantType.None: + break; + case VariantType.I2: + output.Write("int16"); + break; + case VariantType.I4: + output.Write("int32"); + break; + case VariantType.R4: + output.Write("float32"); + break; + case VariantType.R8: + output.Write("float64"); + break; + case VariantType.CY: + output.Write("currency"); + break; + case VariantType.Date: + output.Write("date"); + break; + case VariantType.BStr: + output.Write("bstr"); + break; + case VariantType.Dispatch: + output.Write("idispatch"); + break; + case VariantType.Error: + output.Write("error"); + break; + case VariantType.Bool: + output.Write("bool"); + break; + case VariantType.Variant: + output.Write("variant"); + break; + case VariantType.Unknown: + output.Write("iunknown"); + break; + case VariantType.Decimal: + output.Write("decimal"); + break; + case VariantType.I1: + output.Write("int8"); + break; + case VariantType.UI1: + output.Write("unsigned int8"); + break; + case VariantType.UI2: + output.Write("unsigned int16"); + break; + case VariantType.UI4: + output.Write("unsigned int32"); + break; + case VariantType.Int: + output.Write("int"); + break; + case VariantType.UInt: + output.Write("unsigned int"); + break; + default: + output.Write(sami.ElementType.ToString()); + break; + } + } + break; + case NativeType.FixedArray: + output.Write("fixed array"); + FixedArrayMarshalInfo fami = marshalInfo as FixedArrayMarshalInfo; + if (fami != null) { + output.Write("[{0}]", fami.Size); + if (fami.ElementType != NativeType.None) { + output.Write(' '); + WriteNativeType(fami.ElementType); + } + } + break; + case NativeType.ByValStr: + output.Write("byvalstr"); + break; + case NativeType.ANSIBStr: + output.Write("ansi bstr"); + break; + case NativeType.TBStr: + output.Write("tbstr"); + break; + case NativeType.VariantBool: + output.Write("variant bool"); + break; + case NativeType.ASAny: + output.Write("as any"); + break; + case NativeType.LPStruct: + output.Write("lpstruct"); + break; + case NativeType.CustomMarshaler: + CustomMarshalInfo cmi = marshalInfo as CustomMarshalInfo; + if (cmi == null) + goto default; + output.Write("custom(\"{0}\", \"{1}\"", + NRefactory.CSharp.TextWriterTokenWriter.ConvertString(cmi.ManagedType.FullName), + NRefactory.CSharp.TextWriterTokenWriter.ConvertString(cmi.Cookie)); + if (cmi.Guid != Guid.Empty || !string.IsNullOrEmpty(cmi.UnmanagedType)) { + output.Write(", \"{0}\", \"{1}\"", cmi.Guid.ToString(), NRefactory.CSharp.TextWriterTokenWriter.ConvertString(cmi.UnmanagedType)); + } + output.Write(')'); + break; + case NativeType.Error: + output.Write("error"); + break; + default: + output.Write(nativeType.ToString()); + break; + } + } + #endregion + + void WriteParameters(Collection<ParameterDefinition> parameters) + { + for (int i = 0; i < parameters.Count; i++) { + var p = parameters[i]; + if (p.IsIn) + output.Write("[in] "); + if (p.IsOut) + output.Write("[out] "); + if (p.IsOptional) + output.Write("[opt] "); + p.ParameterType.WriteTo(output); + output.Write(' '); + if (p.HasMarshalInfo) { + WriteMarshalInfo(p.MarshalInfo); + } + output.WriteDefinition(DisassemblerHelpers.Escape(p.Name), p); + if (i < parameters.Count - 1) + output.Write(','); + output.WriteLine(); + } + } + + bool HasParameterAttributes(ParameterDefinition p) + { + return p.HasConstant || p.HasCustomAttributes; + } + + void WriteParameterAttributes(ParameterDefinition p) + { + if (!HasParameterAttributes(p)) + return; + output.Write(".param [{0}]", p.Index + 1); + if (p.HasConstant) { + output.Write(" = "); + WriteConstant(p.Constant); + } + output.WriteLine(); + WriteAttributes(p.CustomAttributes); + } + + void WriteConstant(object constant) + { + if (constant == null) { + output.Write("nullref"); + } else { + string typeName = DisassemblerHelpers.PrimitiveTypeName(constant.GetType().FullName); + if (typeName != null && typeName != "string") { + output.Write(typeName); + output.Write('('); + float? cf = constant as float?; + double? cd = constant as double?; + if (cf.HasValue && (float.IsNaN(cf.Value) || float.IsInfinity(cf.Value))) { + output.Write("0x{0:x8}", BitConverter.ToInt32(BitConverter.GetBytes(cf.Value), 0)); + } else if (cd.HasValue && (double.IsNaN(cd.Value) || double.IsInfinity(cd.Value))) { + output.Write("0x{0:x16}", BitConverter.DoubleToInt64Bits(cd.Value)); + } else { + DisassemblerHelpers.WriteOperand(output, constant); + } + output.Write(')'); + } else { + DisassemblerHelpers.WriteOperand(output, constant); + } + } + } + #endregion + + #region Disassemble Field + EnumNameCollection<FieldAttributes> fieldVisibility = new EnumNameCollection<FieldAttributes>() { + { FieldAttributes.Private, "private" }, + { FieldAttributes.FamANDAssem, "famandassem" }, + { FieldAttributes.Assembly, "assembly" }, + { FieldAttributes.Family, "family" }, + { FieldAttributes.FamORAssem, "famorassem" }, + { FieldAttributes.Public, "public" }, + }; + + EnumNameCollection<FieldAttributes> fieldAttributes = new EnumNameCollection<FieldAttributes>() { + { FieldAttributes.Static, "static" }, + { FieldAttributes.Literal, "literal" }, + { FieldAttributes.InitOnly, "initonly" }, + { FieldAttributes.SpecialName, "specialname" }, + { FieldAttributes.RTSpecialName, "rtspecialname" }, + { FieldAttributes.NotSerialized, "notserialized" }, + }; + + public void DisassembleField(FieldDefinition field) + { + output.WriteDefinition(".field ", field); + if (field.HasLayoutInfo) { + output.Write("[" + field.Offset + "] "); + } + WriteEnum(field.Attributes & FieldAttributes.FieldAccessMask, fieldVisibility); + const FieldAttributes hasXAttributes = FieldAttributes.HasDefault | FieldAttributes.HasFieldMarshal | FieldAttributes.HasFieldRVA; + WriteFlags(field.Attributes & ~(FieldAttributes.FieldAccessMask | hasXAttributes), fieldAttributes); + if (field.HasMarshalInfo) { + WriteMarshalInfo(field.MarshalInfo); + } + field.FieldType.WriteTo(output); + output.Write(' '); + output.Write(DisassemblerHelpers.Escape(field.Name)); + if ((field.Attributes & FieldAttributes.HasFieldRVA) == FieldAttributes.HasFieldRVA) { + output.Write(" at I_{0:x8}", field.RVA); + } + if (field.HasConstant) { + output.Write(" = "); + WriteConstant(field.Constant); + } + output.WriteLine(); + if (field.HasCustomAttributes) { + output.MarkFoldStart(); + WriteAttributes(field.CustomAttributes); + output.MarkFoldEnd(); + } + } + #endregion + + #region Disassemble Property + EnumNameCollection<PropertyAttributes> propertyAttributes = new EnumNameCollection<PropertyAttributes>() { + { PropertyAttributes.SpecialName, "specialname" }, + { PropertyAttributes.RTSpecialName, "rtspecialname" }, + { PropertyAttributes.HasDefault, "hasdefault" }, + }; + + public void DisassembleProperty(PropertyDefinition property) + { + // set current member + currentMember = property; + + output.WriteDefinition(".property ", property); + WriteFlags(property.Attributes, propertyAttributes); + if (property.HasThis) + output.Write("instance "); + property.PropertyType.WriteTo(output); + output.Write(' '); + output.Write(DisassemblerHelpers.Escape(property.Name)); + + output.Write("("); + if (property.HasParameters) { + output.WriteLine(); + output.Indent(); + WriteParameters(property.Parameters); + output.Unindent(); + } + output.Write(")"); + + OpenBlock(false); + WriteAttributes(property.CustomAttributes); + WriteNestedMethod(".get", property.GetMethod); + WriteNestedMethod(".set", property.SetMethod); + + foreach (var method in property.OtherMethods) { + WriteNestedMethod(".other", method); + } + CloseBlock(); + } + + void WriteNestedMethod(string keyword, MethodDefinition method) + { + if (method == null) + return; + + output.Write(keyword); + output.Write(' '); + method.WriteTo(output); + output.WriteLine(); + } + #endregion + + #region Disassemble Event + EnumNameCollection<EventAttributes> eventAttributes = new EnumNameCollection<EventAttributes>() { + { EventAttributes.SpecialName, "specialname" }, + { EventAttributes.RTSpecialName, "rtspecialname" }, + }; + + public void DisassembleEvent(EventDefinition ev) + { + // set current member + currentMember = ev; + + output.WriteDefinition(".event ", ev); + WriteFlags(ev.Attributes, eventAttributes); + ev.EventType.WriteTo(output, ILNameSyntax.TypeName); + output.Write(' '); + output.Write(DisassemblerHelpers.Escape(ev.Name)); + OpenBlock(false); + WriteAttributes(ev.CustomAttributes); + WriteNestedMethod(".addon", ev.AddMethod); + WriteNestedMethod(".removeon", ev.RemoveMethod); + WriteNestedMethod(".fire", ev.InvokeMethod); + foreach (var method in ev.OtherMethods) { + WriteNestedMethod(".other", method); + } + CloseBlock(); + } + #endregion + + #region Disassemble Type + EnumNameCollection<TypeAttributes> typeVisibility = new EnumNameCollection<TypeAttributes>() { + { TypeAttributes.Public, "public" }, + { TypeAttributes.NotPublic, "private" }, + { TypeAttributes.NestedPublic, "nested public" }, + { TypeAttributes.NestedPrivate, "nested private" }, + { TypeAttributes.NestedAssembly, "nested assembly" }, + { TypeAttributes.NestedFamily, "nested family" }, + { TypeAttributes.NestedFamANDAssem, "nested famandassem" }, + { TypeAttributes.NestedFamORAssem, "nested famorassem" }, + }; + + EnumNameCollection<TypeAttributes> typeLayout = new EnumNameCollection<TypeAttributes>() { + { TypeAttributes.AutoLayout, "auto" }, + { TypeAttributes.SequentialLayout, "sequential" }, + { TypeAttributes.ExplicitLayout, "explicit" }, + }; + + EnumNameCollection<TypeAttributes> typeStringFormat = new EnumNameCollection<TypeAttributes>() { + { TypeAttributes.AutoClass, "auto" }, + { TypeAttributes.AnsiClass, "ansi" }, + { TypeAttributes.UnicodeClass, "unicode" }, + }; + + EnumNameCollection<TypeAttributes> typeAttributes = new EnumNameCollection<TypeAttributes>() { + { TypeAttributes.Abstract, "abstract" }, + { TypeAttributes.Sealed, "sealed" }, + { TypeAttributes.SpecialName, "specialname" }, + { TypeAttributes.Import, "import" }, + { TypeAttributes.Serializable, "serializable" }, + { TypeAttributes.WindowsRuntime, "windowsruntime" }, + { TypeAttributes.BeforeFieldInit, "beforefieldinit" }, + { TypeAttributes.HasSecurity, null }, + }; + + public void DisassembleType(TypeDefinition type) + { + // start writing IL + output.WriteDefinition(".class ", type); + + if ((type.Attributes & TypeAttributes.ClassSemanticMask) == TypeAttributes.Interface) + output.Write("interface "); + WriteEnum(type.Attributes & TypeAttributes.VisibilityMask, typeVisibility); + WriteEnum(type.Attributes & TypeAttributes.LayoutMask, typeLayout); + WriteEnum(type.Attributes & TypeAttributes.StringFormatMask, typeStringFormat); + const TypeAttributes masks = TypeAttributes.ClassSemanticMask | TypeAttributes.VisibilityMask | TypeAttributes.LayoutMask | TypeAttributes.StringFormatMask; + WriteFlags(type.Attributes & ~masks, typeAttributes); + + output.Write(DisassemblerHelpers.Escape(type.DeclaringType != null ? type.Name : type.FullName)); + WriteTypeParameters(output, type); + output.MarkFoldStart(defaultCollapsed: isInType); + output.WriteLine(); + + if (type.BaseType != null) { + output.Indent(); + output.Write("extends "); + type.BaseType.WriteTo(output, ILNameSyntax.TypeName); + output.WriteLine(); + output.Unindent(); + } + if (type.HasInterfaces) { + output.Indent(); + for (int index = 0; index < type.Interfaces.Count; index++) { + if (index > 0) + output.WriteLine(","); + if (index == 0) + output.Write("implements "); + else + output.Write(" "); + type.Interfaces[index].WriteTo(output, ILNameSyntax.TypeName); + } + output.WriteLine(); + output.Unindent(); + } + + output.WriteLine("{"); + output.Indent(); + bool oldIsInType = isInType; + isInType = true; + WriteAttributes(type.CustomAttributes); + WriteSecurityDeclarations(type); + if (type.HasLayoutInfo) { + output.WriteLine(".pack {0}", type.PackingSize); + output.WriteLine(".size {0}", type.ClassSize); + output.WriteLine(); + } + if (type.HasNestedTypes) { + output.WriteLine("// Nested Types"); + foreach (var nestedType in type.NestedTypes) { + cancellationToken.ThrowIfCancellationRequested(); + DisassembleType(nestedType); + output.WriteLine(); + } + output.WriteLine(); + } + if (type.HasFields) { + output.WriteLine("// Fields"); + foreach (var field in type.Fields) { + cancellationToken.ThrowIfCancellationRequested(); + DisassembleField(field); + } + output.WriteLine(); + } + if (type.HasMethods) { + output.WriteLine("// Methods"); + foreach (var m in type.Methods) { + cancellationToken.ThrowIfCancellationRequested(); + DisassembleMethod(m); + output.WriteLine(); + } + } + if (type.HasEvents) { + output.WriteLine("// Events"); + foreach (var ev in type.Events) { + cancellationToken.ThrowIfCancellationRequested(); + DisassembleEvent(ev); + output.WriteLine(); + } + output.WriteLine(); + } + if (type.HasProperties) { + output.WriteLine("// Properties"); + foreach (var prop in type.Properties) { + cancellationToken.ThrowIfCancellationRequested(); + DisassembleProperty(prop); + } + output.WriteLine(); + } + CloseBlock("end of class " + (type.DeclaringType != null ? type.Name : type.FullName)); + isInType = oldIsInType; + } + + void WriteTypeParameters(ITextOutput output, IGenericParameterProvider p) + { + if (p.HasGenericParameters) { + output.Write('<'); + for (int i = 0; i < p.GenericParameters.Count; i++) { + if (i > 0) + output.Write(", "); + GenericParameter gp = p.GenericParameters[i]; + if (gp.HasReferenceTypeConstraint) { + output.Write("class "); + } else if (gp.HasNotNullableValueTypeConstraint) { + output.Write("valuetype "); + } + if (gp.HasDefaultConstructorConstraint) { + output.Write(".ctor "); + } + if (gp.HasConstraints) { + output.Write('('); + for (int j = 0; j < gp.Constraints.Count; j++) { + if (j > 0) + output.Write(", "); + gp.Constraints[j].WriteTo(output, ILNameSyntax.TypeName); + } + output.Write(") "); + } + if (gp.IsContravariant) { + output.Write('-'); + } else if (gp.IsCovariant) { + output.Write('+'); + } + output.Write(DisassemblerHelpers.Escape(gp.Name)); + } + output.Write('>'); + } + } + #endregion + + #region Helper methods + void WriteAttributes(Collection<CustomAttribute> attributes) + { + foreach (CustomAttribute a in attributes) { + output.Write(".custom "); + a.Constructor.WriteTo(output); + byte[] blob = a.GetBlob(); + if (blob != null) { + output.Write(" = "); + WriteBlob(blob); + } + output.WriteLine(); + } + } + + void WriteBlob(byte[] blob) + { + output.Write("("); + output.Indent(); + + for (int i = 0; i < blob.Length; i++) { + if (i % 16 == 0 && i < blob.Length - 1) { + output.WriteLine(); + } else { + output.Write(' '); + } + output.Write(blob[i].ToString("x2")); + } + + output.WriteLine(); + output.Unindent(); + output.Write(")"); + } + + void OpenBlock(bool defaultCollapsed) + { + output.MarkFoldStart(defaultCollapsed: defaultCollapsed); + output.WriteLine(); + output.WriteLine("{"); + output.Indent(); + } + + void CloseBlock(string comment = null) + { + output.Unindent(); + output.Write("}"); + if (comment != null) + output.Write(" // " + comment); + output.MarkFoldEnd(); + output.WriteLine(); + } + + void WriteFlags<T>(T flags, EnumNameCollection<T> flagNames) where T : struct + { + long val = Convert.ToInt64(flags); + long tested = 0; + foreach (var pair in flagNames) { + tested |= pair.Key; + if ((val & pair.Key) != 0 && pair.Value != null) { + output.Write(pair.Value); + output.Write(' '); + } + } + if ((val & ~tested) != 0) + output.Write("flag({0:x4}) ", val & ~tested); + } + + void WriteEnum<T>(T enumValue, EnumNameCollection<T> enumNames) where T : struct + { + long val = Convert.ToInt64(enumValue); + foreach (var pair in enumNames) { + if (pair.Key == val) { + if (pair.Value != null) { + output.Write(pair.Value); + output.Write(' '); + } + return; + } + } + if (val != 0) { + output.Write("flag({0:x4})", val); + output.Write(' '); + } + + } + + sealed class EnumNameCollection<T> : IEnumerable<KeyValuePair<long, string>> where T : struct + { + List<KeyValuePair<long, string>> names = new List<KeyValuePair<long, string>>(); + + public void Add(T flag, string name) + { + names.Add(new KeyValuePair<long, string>(Convert.ToInt64(flag), name)); + } + + public IEnumerator<KeyValuePair<long, string>> GetEnumerator() + { + return names.GetEnumerator(); + } + + System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() + { + return names.GetEnumerator(); + } + } + #endregion + + public void DisassembleNamespace(string nameSpace, IEnumerable<TypeDefinition> types) + { + if (!string.IsNullOrEmpty(nameSpace)) { + output.Write(".namespace " + DisassemblerHelpers.Escape(nameSpace)); + OpenBlock(false); + } + bool oldIsInType = isInType; + isInType = true; + foreach (TypeDefinition td in types) { + cancellationToken.ThrowIfCancellationRequested(); + DisassembleType(td); + output.WriteLine(); + } + if (!string.IsNullOrEmpty(nameSpace)) { + CloseBlock(); + isInType = oldIsInType; + } + } + + public void WriteAssemblyHeader(AssemblyDefinition asm) + { + output.Write(".assembly "); + if (asm.Name.IsWindowsRuntime) + output.Write("windowsruntime "); + output.Write(DisassemblerHelpers.Escape(asm.Name.Name)); + OpenBlock(false); + WriteAttributes(asm.CustomAttributes); + WriteSecurityDeclarations(asm); + if (asm.Name.PublicKey != null && asm.Name.PublicKey.Length > 0) { + output.Write(".publickey = "); + WriteBlob(asm.Name.PublicKey); + output.WriteLine(); + } + if (asm.Name.HashAlgorithm != AssemblyHashAlgorithm.None) { + output.Write(".hash algorithm 0x{0:x8}", (int)asm.Name.HashAlgorithm); + if (asm.Name.HashAlgorithm == AssemblyHashAlgorithm.SHA1) + output.Write(" // SHA1"); + output.WriteLine(); + } + Version v = asm.Name.Version; + if (v != null) { + output.WriteLine(".ver {0}:{1}:{2}:{3}", v.Major, v.Minor, v.Build, v.Revision); + } + CloseBlock(); + } + + public void WriteAssemblyReferences(ModuleDefinition module) + { + foreach (var mref in module.ModuleReferences) { + output.WriteLine(".module extern {0}", DisassemblerHelpers.Escape(mref.Name)); + } + foreach (var aref in module.AssemblyReferences) { + output.Write(".assembly extern "); + if (aref.IsWindowsRuntime) + output.Write("windowsruntime "); + output.Write(DisassemblerHelpers.Escape(aref.Name)); + OpenBlock(false); + if (aref.PublicKeyToken != null) { + output.Write(".publickeytoken = "); + WriteBlob(aref.PublicKeyToken); + output.WriteLine(); + } + if (aref.Version != null) { + output.WriteLine(".ver {0}:{1}:{2}:{3}", aref.Version.Major, aref.Version.Minor, aref.Version.Build, aref.Version.Revision); + } + CloseBlock(); + } + } + + public void WriteModuleHeader(ModuleDefinition module) + { + if (module.HasExportedTypes) { + foreach (ExportedType exportedType in module.ExportedTypes) { + output.Write(".class extern "); + if (exportedType.IsForwarder) + output.Write("forwarder "); + output.Write(exportedType.DeclaringType != null ? exportedType.Name : exportedType.FullName); + OpenBlock(false); + if (exportedType.DeclaringType != null) + output.WriteLine(".class extern {0}", DisassemblerHelpers.Escape(exportedType.DeclaringType.FullName)); + else + output.WriteLine(".assembly extern {0}", DisassemblerHelpers.Escape(exportedType.Scope.Name)); + CloseBlock(); + } + } + + output.WriteLine(".module {0}", module.Name); + output.WriteLine("// MVID: {0}", module.Mvid.ToString("B").ToUpperInvariant()); + // TODO: imagebase, file alignment, stackreserve, subsystem + output.WriteLine(".corflags 0x{0:x} // {1}", module.Attributes, module.Attributes.ToString()); + + WriteAttributes(module.CustomAttributes); + } + + public void WriteModuleContents(ModuleDefinition module) + { + foreach (TypeDefinition td in module.Types) { + DisassembleType(td); + output.WriteLine(); + } + } + } +} |