mirror of https://github.com/icsharpcode/ILSpy.git
				
				
			
			You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
					
					
						
							435 lines
						
					
					
						
							14 KiB
						
					
					
				
			
		
		
	
	
							435 lines
						
					
					
						
							14 KiB
						
					
					
				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<byte> 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<TypeDefinitionHandle> 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)); | 
						|
 | 
						|
		/// <summary> | 
						|
		/// An attribute type provider that can be used to decode attribute signatures | 
						|
		/// that only mention built-in types. | 
						|
		/// </summary> | 
						|
		public static ICustomAttributeTypeProvider<IType> MinimalAttributeTypeProvider { | 
						|
			get => minimalCorlibTypeProvider; | 
						|
		} | 
						|
 | 
						|
		public static ISignatureTypeProvider<IType, TypeSystem.GenericContext> MinimalSignatureTypeProvider { | 
						|
			get => minimalCorlibTypeProvider; | 
						|
		} | 
						|
 | 
						|
		/// <summary> | 
						|
		/// Converts <see cref="KnownTypeCode"/> to <see cref="PrimitiveTypeCode"/>. | 
						|
		/// Returns 0 for known types that are not primitive types (such as <see cref="Span{T}"/>). | 
						|
		/// </summary> | 
						|
		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<ModuleReferenceHandle> 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<TypeSpecificationHandle> 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<MethodSpecificationHandle> 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<EntityHandle> 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); | 
						|
		} | 
						|
	} | 
						|
}
 | 
						|
 |