// 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.Reflection.Metadata; using System.Text; using ICSharpCode.Decompiler.Metadata; using ICSharpCode.Decompiler.Util; using SRM = System.Reflection.Metadata; namespace ICSharpCode.Decompiler.Disassembler { public enum ILNameSyntax { /// /// class/valuetype + TypeName (built-in types use keyword syntax) /// Signature, /// /// Like signature, but always refers to type parameters using their position /// SignatureNoNamedTypeParameters, /// /// [assembly]Full.Type.Name (even for built-in types) /// TypeName, /// /// Name (but built-in types use keyword syntax) /// ShortTypeName } public static class DisassemblerHelpers { public static string OffsetToString(int offset) { return string.Format("IL_{0:x4}", offset); } public static string OffsetToString(long offset) { return string.Format("IL_{0:x4}", offset); } public static void WriteOffsetReference(ITextOutput writer, int? offset) { if (offset == null) writer.Write("null"); else writer.WriteReference(OffsetToString(offset.Value), offset); } public static void WriteTo(this SRM.ExceptionRegion exceptionHandler, Metadata.MethodDefinition method, ITextOutput writer) { writer.Write(".try "); WriteOffsetReference(writer, exceptionHandler.TryOffset); writer.Write('-'); WriteOffsetReference(writer, exceptionHandler.TryOffset + exceptionHandler.TryLength); writer.Write(' '); writer.Write(exceptionHandler.Kind.ToString().ToLowerInvariant()); if (exceptionHandler.FilterOffset != -1) { writer.Write(' '); WriteOffsetReference(writer, exceptionHandler.FilterOffset); writer.Write(" handler "); } if (!exceptionHandler.CatchType.IsNil) { writer.Write(' '); exceptionHandler.CatchType.WriteTo(method.Module, writer, new GenericContext(method)); } writer.Write(' '); WriteOffsetReference(writer, exceptionHandler.HandlerOffset); writer.Write('-'); WriteOffsetReference(writer, exceptionHandler.HandlerOffset + exceptionHandler.HandlerLength); } static string ToInvariantCultureString(object value) { IConvertible convertible = value as IConvertible; return (null != convertible) ? convertible.ToString(System.Globalization.CultureInfo.InvariantCulture) : value.ToString(); } 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; } public static string Escape(string identifier) { if (IsValidIdentifier(identifier) && !Metadata.ILOpCodeExtensions.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 "'" + EscapeString(identifier).Replace("'", "\\'") + "'"; } } public static void WriteParameterReference(ITextOutput writer, Metadata.MethodDefinition method, int sequence) { var metadata = method.Module.GetMetadataReader(); var methodDefinition = metadata.GetMethodDefinition(method.Handle); var signatureHeader = methodDefinition.DecodeSignature(new FullTypeNameSignatureDecoder(metadata), default(Unit)).Header; int index = signatureHeader.IsInstance && !signatureHeader.HasExplicitThis ? sequence - 1 : sequence; var parameters = methodDefinition.GetParameters(); if (index < 0 || index >= parameters.Count) { writer.Write(sequence.ToString()); } else { var param = metadata.GetParameter(parameters.ElementAt(index)); if (param.Name.IsNil) { writer.Write(sequence.ToString()); } else { writer.Write(Escape(metadata.GetString(param.Name))); } } } public static void WriteVariableReference(ITextOutput writer, Metadata.MethodDefinition method, int index) { writer.Write(index.ToString()); } public static void WriteOperand(ITextOutput writer, object operand) { if (operand == null) throw new ArgumentNullException(nameof(operand)); /*VariableReference variableRef = operand as VariableReference; if (variableRef != null) { writer.WriteReference(variableRef.Index.ToString(), variableRef); return; } ParameterReference paramRef = operand as ParameterReference; if (paramRef != null) { if (string.IsNullOrEmpty(paramRef.Name)) { var paramDef = paramRef.Resolve(); writer.WriteReference((paramDef == null ? paramRef.Index : paramDef.Sequence).ToString(), paramRef); } else writer.WriteReference(Escape(paramRef.Name), paramRef); return; }*/ string s = operand as string; if (s != null) { WriteOperand(writer, s); } else if (operand is char) { writer.Write(((int)(char)operand).ToString()); } else if (operand is float) { WriteOperand(writer, (float)operand); } else if (operand is double) { WriteOperand(writer, (double)operand); } else if (operand is bool) { writer.Write((bool)operand ? "true" : "false"); } else { s = ToInvariantCultureString(operand); writer.Write(s); } } public static void WriteOperand(ITextOutput writer, long val) { writer.Write(ToInvariantCultureString(val)); } public static void WriteOperand(ITextOutput writer, float val) { 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)); } } public static void WriteOperand(ITextOutput writer, double val) { 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)); } } public static void WriteOperand(ITextOutput writer, string operand) { writer.Write('"'); writer.Write(EscapeString(operand)); writer.Write('"'); } public static string EscapeString(string str) { StringBuilder sb = new StringBuilder(); foreach (char ch in str) { switch (ch) { case '"': sb.Append("\\\""); break; case '\\': sb.Append("\\\\"); break; case '\0': sb.Append("\\0"); break; case '\a': sb.Append("\\a"); break; case '\b': sb.Append("\\b"); break; case '\f': sb.Append("\\f"); break; case '\n': sb.Append("\\n"); break; case '\r': sb.Append("\\r"); break; case '\t': sb.Append("\\t"); break; case '\v': sb.Append("\\v"); break; default: // print control characters and uncommon white spaces as numbers if (char.IsControl(ch) || char.IsSurrogate(ch) || (char.IsWhiteSpace(ch) && ch != ' ')) { sb.Append("\\u" + ((int)ch).ToString("x4")); } else { sb.Append(ch); } break; } } return sb.ToString(); } 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; } } } }