using System; using System.Collections.Generic; using System.Collections.Immutable; using System.Linq; using System.Reflection; using System.Reflection.Metadata; using System.Reflection.Metadata.Ecma335; using System.Reflection.PortableExecutable; using System.Security.Cryptography; using System.Text; using System.Threading.Tasks; using ICSharpCode.Decompiler.TypeSystem; using ICSharpCode.Decompiler.TypeSystem.Implementation; using ICSharpCode.Decompiler.Util; using SRM = System.Reflection.Metadata; namespace ICSharpCode.Decompiler.Metadata { public static class MetadataExtensions { static HashAlgorithm GetHashAlgorithm(this MetadataReader reader) { switch (reader.GetAssemblyDefinition().HashAlgorithm) { case AssemblyHashAlgorithm.None: // only for multi-module assemblies? return SHA1.Create(); case AssemblyHashAlgorithm.MD5: return MD5.Create(); case AssemblyHashAlgorithm.Sha1: return SHA1.Create(); case AssemblyHashAlgorithm.Sha256: return SHA256.Create(); case AssemblyHashAlgorithm.Sha384: return SHA384.Create(); case AssemblyHashAlgorithm.Sha512: return SHA512.Create(); default: return SHA1.Create(); // default? } } static string CalculatePublicKeyToken(BlobHandle blob, MetadataReader reader) { // Calculate public key token: // 1. hash the public key using the appropriate algorithm. byte[] publicKeyTokenBytes = reader.GetHashAlgorithm().ComputeHash(reader.GetBlobBytes(blob)); // 2. take the last 8 bytes // 3. according to Cecil we need to reverse them, other sources did not mention this. return publicKeyTokenBytes.TakeLast(8).Reverse().ToHexString(8); } public static string GetPublicKeyToken(this MetadataReader reader) { if (!reader.IsAssembly) return string.Empty; var asm = reader.GetAssemblyDefinition(); string publicKey = "null"; if (!asm.PublicKey.IsNil) { // AssemblyFlags.PublicKey does not apply to assembly definitions publicKey = CalculatePublicKeyToken(asm.PublicKey, reader); } return publicKey; } public static string GetFullAssemblyName(this MetadataReader reader) { if (!reader.IsAssembly) return string.Empty; var asm = reader.GetAssemblyDefinition(); string publicKey = reader.GetPublicKeyToken(); return $"{reader.GetString(asm.Name)}, " + $"Version={asm.Version}, " + $"Culture={(asm.Culture.IsNil ? "neutral" : reader.GetString(asm.Culture))}, " + $"PublicKeyToken={publicKey}"; } public static bool TryGetFullAssemblyName(this MetadataReader reader, out string assemblyName) { try { assemblyName = GetFullAssemblyName(reader); return true; } catch (BadImageFormatException) { assemblyName = null; return false; } } public static string GetFullAssemblyName(this SRM.AssemblyReference reference, MetadataReader reader) { StringBuilder builder = new StringBuilder(); builder.Append(reader.GetString(reference.Name)); builder.Append(", Version="); builder.Append(reference.Version); builder.Append(", Culture="); if (reference.Culture.IsNil) { builder.Append("neutral"); } else { builder.Append(reader.GetString(reference.Culture)); } if (reference.PublicKeyOrToken.IsNil) { builder.Append(", PublicKeyToken=null"); } else if ((reference.Flags & AssemblyFlags.PublicKey) != 0) { builder.Append(", PublicKeyToken="); builder.Append(CalculatePublicKeyToken(reference.PublicKeyOrToken, reader)); } else { builder.Append(", PublicKeyToken="); builder.AppendHexString(reader.GetBlobReader(reference.PublicKeyOrToken)); } if ((reference.Flags & AssemblyFlags.Retargetable) != 0) { builder.Append(", Retargetable=true"); } return builder.ToString(); } public static bool TryGetFullAssemblyName(this SRM.AssemblyReference reference, MetadataReader reader, out string assemblyName) { try { assemblyName = GetFullAssemblyName(reference, reader); return true; } catch (BadImageFormatException) { assemblyName = null; return false; } } public static string ToHexString(this IEnumerable bytes, int estimatedLength) { if (bytes == null) throw new ArgumentNullException(nameof(bytes)); StringBuilder sb = new StringBuilder(estimatedLength * 2); foreach (var b in bytes) sb.AppendFormat("{0:x2}", b); return sb.ToString(); } public static void AppendHexString(this StringBuilder builder, BlobReader reader) { for (int i = 0; i < reader.Length; i++) { builder.AppendFormat("{0:x2}", reader.ReadByte()); } } public static string ToHexString(this BlobReader reader) { StringBuilder sb = new StringBuilder(reader.Length * 3); for (int i = 0; i < reader.Length; i++) { if (i == 0) sb.AppendFormat("{0:X2}", reader.ReadByte()); else sb.AppendFormat("-{0:X2}", reader.ReadByte()); } return sb.ToString(); } public static IEnumerable GetTopLevelTypeDefinitions(this MetadataReader reader) { foreach (var handle in reader.TypeDefinitions) { var td = reader.GetTypeDefinition(handle); if (td.GetDeclaringType().IsNil) yield return handle; } } public static string ToILNameString(this FullTypeName typeName, bool omitGenerics = false) { string name; if (typeName.IsNested) { name = typeName.Name; if (!omitGenerics) { int localTypeParameterCount = typeName.GetNestedTypeAdditionalTypeParameterCount(typeName.NestingLevel - 1); if (localTypeParameterCount > 0) name += "`" + localTypeParameterCount; } name = Disassembler.DisassemblerHelpers.Escape(name); return $"{typeName.GetDeclaringType().ToILNameString(omitGenerics)}/{name}"; } if (!string.IsNullOrEmpty(typeName.TopLevelTypeName.Namespace)) { name = $"{typeName.TopLevelTypeName.Namespace}.{typeName.Name}"; if (!omitGenerics && typeName.TypeParameterCount > 0) name += "`" + typeName.TypeParameterCount; } else { name = typeName.Name; if (!omitGenerics && typeName.TypeParameterCount > 0) name += "`" + typeName.TypeParameterCount; } return Disassembler.DisassemblerHelpers.Escape(name); } [Obsolete("Use MetadataModule.GetDeclaringModule() instead")] public static IModuleReference GetDeclaringModule(this TypeReferenceHandle handle, MetadataReader reader) { var tr = reader.GetTypeReference(handle); switch (tr.ResolutionScope.Kind) { case HandleKind.TypeReference: return ((TypeReferenceHandle)tr.ResolutionScope).GetDeclaringModule(reader); case HandleKind.AssemblyReference: var asmRef = reader.GetAssemblyReference((AssemblyReferenceHandle)tr.ResolutionScope); return new DefaultAssemblyReference(reader.GetString(asmRef.Name)); case HandleKind.ModuleReference: var modRef = reader.GetModuleReference((ModuleReferenceHandle)tr.ResolutionScope); return new DefaultAssemblyReference(reader.GetString(modRef.Name)); default: return DefaultAssemblyReference.CurrentAssembly; } } internal static readonly TypeProvider minimalCorlibTypeProvider = new TypeProvider(new SimpleCompilation(MinimalCorlib.Instance)); /// /// An attribute type provider that can be used to decode attribute signatures /// that only mention built-in types. /// public static ICustomAttributeTypeProvider MinimalAttributeTypeProvider { get => minimalCorlibTypeProvider; } public static ISignatureTypeProvider MinimalSignatureTypeProvider { get => minimalCorlibTypeProvider; } /// /// Converts to . /// Returns 0 for known types that are not primitive types (such as ). /// public static PrimitiveTypeCode ToPrimitiveTypeCode(this KnownTypeCode typeCode) { switch (typeCode) { case KnownTypeCode.Object: return PrimitiveTypeCode.Object; case KnownTypeCode.Boolean: return PrimitiveTypeCode.Boolean; case KnownTypeCode.Char: return PrimitiveTypeCode.Char; case KnownTypeCode.SByte: return PrimitiveTypeCode.SByte; case KnownTypeCode.Byte: return PrimitiveTypeCode.Byte; case KnownTypeCode.Int16: return PrimitiveTypeCode.Int16; case KnownTypeCode.UInt16: return PrimitiveTypeCode.UInt16; case KnownTypeCode.Int32: return PrimitiveTypeCode.Int32; case KnownTypeCode.UInt32: return PrimitiveTypeCode.UInt32; case KnownTypeCode.Int64: return PrimitiveTypeCode.Int64; case KnownTypeCode.UInt64: return PrimitiveTypeCode.UInt64; case KnownTypeCode.Single: return PrimitiveTypeCode.Single; case KnownTypeCode.Double: return PrimitiveTypeCode.Double; case KnownTypeCode.String: return PrimitiveTypeCode.String; case KnownTypeCode.Void: return PrimitiveTypeCode.Void; case KnownTypeCode.TypedReference: return PrimitiveTypeCode.TypedReference; case KnownTypeCode.IntPtr: return PrimitiveTypeCode.IntPtr; case KnownTypeCode.UIntPtr: return PrimitiveTypeCode.UIntPtr; default: return 0; } } public static KnownTypeCode ToKnownTypeCode(this PrimitiveTypeCode typeCode) { switch (typeCode) { case PrimitiveTypeCode.Boolean: return KnownTypeCode.Boolean; case PrimitiveTypeCode.Byte: return KnownTypeCode.Byte; case PrimitiveTypeCode.SByte: return KnownTypeCode.SByte; case PrimitiveTypeCode.Char: return KnownTypeCode.Char; case PrimitiveTypeCode.Int16: return KnownTypeCode.Int16; case PrimitiveTypeCode.UInt16: return KnownTypeCode.UInt16; case PrimitiveTypeCode.Int32: return KnownTypeCode.Int32; case PrimitiveTypeCode.UInt32: return KnownTypeCode.UInt32; case PrimitiveTypeCode.Int64: return KnownTypeCode.Int64; case PrimitiveTypeCode.UInt64: return KnownTypeCode.UInt64; case PrimitiveTypeCode.Single: return KnownTypeCode.Single; case PrimitiveTypeCode.Double: return KnownTypeCode.Double; case PrimitiveTypeCode.IntPtr: return KnownTypeCode.IntPtr; case PrimitiveTypeCode.UIntPtr: return KnownTypeCode.UIntPtr; case PrimitiveTypeCode.Object: return KnownTypeCode.Object; case PrimitiveTypeCode.String: return KnownTypeCode.String; case PrimitiveTypeCode.TypedReference: return KnownTypeCode.TypedReference; case PrimitiveTypeCode.Void: return KnownTypeCode.Void; default: return KnownTypeCode.None; } } public static IEnumerable GetModuleReferences(this MetadataReader metadata) { var rowCount = metadata.GetTableRowCount(TableIndex.ModuleRef); for (int row = 1; row <= rowCount; row++) { yield return MetadataTokens.ModuleReferenceHandle(row); } } public static IEnumerable GetTypeSpecifications(this MetadataReader metadata) { var rowCount = metadata.GetTableRowCount(TableIndex.TypeSpec); for (int row = 1; row <= rowCount; row++) { yield return MetadataTokens.TypeSpecificationHandle(row); } } public static IEnumerable GetMethodSpecifications(this MetadataReader metadata) { var rowCount = metadata.GetTableRowCount(TableIndex.MethodSpec); for (int row = 1; row <= rowCount; row++) { yield return MetadataTokens.MethodSpecificationHandle(row); } } public static IEnumerable<(Handle Handle, MethodSemanticsAttributes Semantics, MethodDefinitionHandle Method, EntityHandle Association)> GetMethodSemantics(this MetadataReader metadata) { int offset = metadata.GetTableMetadataOffset(TableIndex.MethodSemantics); int rowSize = metadata.GetTableRowSize(TableIndex.MethodSemantics); int rowCount = metadata.GetTableRowCount(TableIndex.MethodSemantics); bool methodSmall = metadata.GetTableRowCount(TableIndex.MethodDef) <= ushort.MaxValue; bool assocSmall = metadata.GetTableRowCount(TableIndex.Property) <= ushort.MaxValue && metadata.GetTableRowCount(TableIndex.Event) <= ushort.MaxValue; int assocOffset = (methodSmall ? 2 : 4) + 2; for (int row = 0; row < rowCount; row++) { yield return Read(row); } unsafe (Handle Handle, MethodSemanticsAttributes Semantics, MethodDefinitionHandle Method, EntityHandle Association) Read(int row) { byte* ptr = metadata.MetadataPointer + offset + rowSize * row; int methodDef = methodSmall ? *(ushort*)(ptr + 2) : (int)*(uint*)(ptr + 2); int assocDef = assocSmall ? *(ushort*)(ptr + assocOffset) : (int)*(uint*)(ptr + assocOffset); EntityHandle propOrEvent; if ((assocDef & 0x1) == 1) { propOrEvent = MetadataTokens.PropertyDefinitionHandle(assocDef >> 1); } else { propOrEvent = MetadataTokens.EventDefinitionHandle(assocDef >> 1); } return (MetadataTokens.Handle(0x18000000 | (row + 1)), (MethodSemanticsAttributes)(*(ushort*)ptr), MetadataTokens.MethodDefinitionHandle(methodDef), propOrEvent); } } public static IEnumerable GetFieldLayouts(this MetadataReader metadata) { var rowCount = metadata.GetTableRowCount(TableIndex.FieldLayout); for (int row = 1; row <= rowCount; row++) { yield return MetadataTokens.EntityHandle(TableIndex.FieldLayout, row); } } public unsafe static (int Offset, FieldDefinitionHandle FieldDef) GetFieldLayout(this MetadataReader metadata, EntityHandle fieldLayoutHandle) { byte* startPointer = metadata.MetadataPointer; int offset = metadata.GetTableMetadataOffset(TableIndex.FieldLayout); int rowSize = metadata.GetTableRowSize(TableIndex.FieldLayout); int rowCount = metadata.GetTableRowCount(TableIndex.FieldLayout); int fieldRowNo = metadata.GetRowNumber(fieldLayoutHandle); bool small = metadata.GetTableRowCount(TableIndex.Field) <= ushort.MaxValue; for (int row = rowCount - 1; row >= 0; row--) { byte* ptr = startPointer + offset + rowSize * row; uint rowNo = small ? *(ushort*)(ptr + 4) : *(uint*)(ptr + 4); if (fieldRowNo == rowNo) { return (*(int*)ptr, MetadataTokens.FieldDefinitionHandle(fieldRowNo)); } } return (0, default); } } }