using System; using System.Collections.Generic; using System.Collections.Immutable; using System.Linq; using System.Reflection; using System.Reflection.Metadata; using SRM = System.Reflection.Metadata; using System.Reflection.PortableExecutable; using ICSharpCode.Decompiler.TypeSystem; using ICSharpCode.Decompiler.TypeSystem.Implementation; using ICSharpCode.Decompiler.Util; namespace ICSharpCode.Decompiler { public static partial class SRMExtensions { public static bool HasFlag(this TypeDefinition typeDefinition, TypeAttributes attribute) => (typeDefinition.Attributes & attribute) == attribute; public static bool HasFlag(this MethodDefinition methodDefinition, MethodAttributes attribute) => (methodDefinition.Attributes & attribute) == attribute; public static bool HasFlag(this FieldDefinition fieldDefinition, FieldAttributes attribute) => (fieldDefinition.Attributes & attribute) == attribute; public static bool HasFlag(this PropertyDefinition propertyDefinition, PropertyAttributes attribute) => (propertyDefinition.Attributes & attribute) == attribute; public static bool HasFlag(this EventDefinition eventDefinition, EventAttributes attribute) => (eventDefinition.Attributes & attribute) == attribute; public static bool IsTypeKind(this HandleKind kind) => kind == HandleKind.TypeDefinition || kind == HandleKind.TypeReference || kind == HandleKind.TypeSpecification; public static bool IsMemberKind(this HandleKind kind) => kind == HandleKind.MethodDefinition || kind == HandleKind.PropertyDefinition || kind == HandleKind.FieldDefinition || kind == HandleKind.EventDefinition || kind == HandleKind.MemberReference || kind == HandleKind.MethodSpecification; public static bool IsValueType(this TypeDefinitionHandle handle, MetadataReader reader) { return reader.GetTypeDefinition(handle).IsValueType(reader); } public static bool IsValueType(this TypeDefinition typeDefinition, MetadataReader reader) { var baseType = typeDefinition.BaseType; if (baseType.IsNil) return false; if (baseType.IsKnownType(reader, KnownTypeCode.Enum)) return true; if (!baseType.IsKnownType(reader, KnownTypeCode.ValueType)) return false; var thisType = typeDefinition.GetFullTypeName(reader); return !thisType.IsKnownType(KnownTypeCode.Enum); } public static bool IsEnum(this TypeDefinitionHandle handle, MetadataReader reader) { return reader.GetTypeDefinition(handle).IsEnum(reader); } public static bool IsEnum(this TypeDefinition typeDefinition, MetadataReader reader) { if (typeDefinition.BaseType.IsNil) return false; return typeDefinition.BaseType.IsKnownType(reader, KnownTypeCode.Enum); } public static bool IsEnum(this TypeDefinitionHandle handle, MetadataReader reader, out PrimitiveTypeCode underlyingType) { return reader.GetTypeDefinition(handle).IsEnum(reader, out underlyingType); } public static bool IsEnum(this TypeDefinition typeDefinition, MetadataReader reader, out PrimitiveTypeCode underlyingType) { underlyingType = 0; if (typeDefinition.BaseType.IsNil) return false; if (!typeDefinition.BaseType.IsKnownType(reader, KnownTypeCode.Enum)) return false; var field = reader.GetFieldDefinition(typeDefinition.GetFields().First()); var blob = reader.GetBlobReader(field.Signature); if (blob.ReadSignatureHeader().Kind != SignatureKind.Field) return false; underlyingType = (PrimitiveTypeCode)blob.ReadByte(); return true; } public static bool IsDelegate(this TypeDefinitionHandle handle, MetadataReader reader) { return reader.GetTypeDefinition(handle).IsDelegate(reader); } public static bool IsDelegate(this TypeDefinition typeDefinition, MetadataReader reader) { var baseType = typeDefinition.BaseType; return !baseType.IsNil && baseType.IsKnownType(reader, KnownTypeCode.MulticastDelegate); } public static bool HasBody(this MethodDefinition methodDefinition) { const MethodAttributes noBodyAttrs = MethodAttributes.Abstract | MethodAttributes.PinvokeImpl; const MethodImplAttributes noBodyImplAttrs = MethodImplAttributes.InternalCall | MethodImplAttributes.Native | MethodImplAttributes.Unmanaged | MethodImplAttributes.Runtime; return (methodDefinition.Attributes & noBodyAttrs) == 0 && (methodDefinition.ImplAttributes & noBodyImplAttrs) == 0 && methodDefinition.RelativeVirtualAddress > 0; } public static int GetCodeSize(this MethodBodyBlock body) { if (body == null) throw new ArgumentNullException(nameof(body)); return body.GetILReader().Length; } public static MethodDefinitionHandle GetAny(this PropertyAccessors accessors) { if (!accessors.Getter.IsNil) return accessors.Getter; return accessors.Setter; } public static MethodDefinitionHandle GetAny(this EventAccessors accessors) { if (!accessors.Adder.IsNil) return accessors.Adder; if (!accessors.Remover.IsNil) return accessors.Remover; return accessors.Raiser; } public static TypeDefinitionHandle GetDeclaringType(this EntityHandle entity, MetadataReader metadata) { switch (entity.Kind) { case HandleKind.TypeDefinition: var td = metadata.GetTypeDefinition((TypeDefinitionHandle)entity); return td.GetDeclaringType(); case HandleKind.FieldDefinition: var fd = metadata.GetFieldDefinition((FieldDefinitionHandle)entity); return fd.GetDeclaringType(); case HandleKind.MethodDefinition: var md = metadata.GetMethodDefinition((MethodDefinitionHandle)entity); return md.GetDeclaringType(); case HandleKind.EventDefinition: var ed = metadata.GetEventDefinition((EventDefinitionHandle)entity); return metadata.GetMethodDefinition(ed.GetAccessors().GetAny()).GetDeclaringType(); case HandleKind.PropertyDefinition: var pd = metadata.GetPropertyDefinition((PropertyDefinitionHandle)entity); return metadata.GetMethodDefinition(pd.GetAccessors().GetAny()).GetDeclaringType(); default: throw new ArgumentOutOfRangeException(); } } public static TypeReferenceHandle GetDeclaringType(this TypeReference tr) { switch (tr.ResolutionScope.Kind) { case HandleKind.TypeReference: return (TypeReferenceHandle)tr.ResolutionScope; default: return default(TypeReferenceHandle); } } public static FullTypeName GetFullTypeName(this EntityHandle handle, MetadataReader reader) { if (handle.IsNil) throw new ArgumentNullException(nameof(handle)); switch (handle.Kind) { case HandleKind.TypeDefinition: return ((TypeDefinitionHandle)handle).GetFullTypeName(reader); case HandleKind.TypeReference: return ((TypeReferenceHandle)handle).GetFullTypeName(reader); case HandleKind.TypeSpecification: return ((TypeSpecificationHandle)handle).GetFullTypeName(reader); default: throw new ArgumentOutOfRangeException(); } } public static bool IsKnownType(this EntityHandle handle, MetadataReader reader, KnownTypeCode knownType) { return GetFullTypeName(handle, reader) == KnownTypeReference.Get(knownType).TypeName; } internal static bool IsKnownType(this EntityHandle handle, MetadataReader reader, KnownAttribute knownType) { return GetFullTypeName(handle, reader) == knownType.GetTypeName(); } public static FullTypeName GetFullTypeName(this TypeSpecificationHandle handle, MetadataReader reader) { if (handle.IsNil) throw new ArgumentNullException(nameof(handle)); var ts = reader.GetTypeSpecification(handle); return ts.DecodeSignature(new Metadata.FullTypeNameSignatureDecoder(reader), default(Unit)); } public static FullTypeName GetFullTypeName(this TypeReferenceHandle handle, MetadataReader reader) { if (handle.IsNil) throw new ArgumentNullException(nameof(handle)); var tr = reader.GetTypeReference(handle); string name = ReflectionHelper.SplitTypeParameterCountFromReflectionName(reader.GetString(tr.Name), out var typeParameterCount); TypeReferenceHandle declaringTypeHandle; if ((declaringTypeHandle = tr.GetDeclaringType()).IsNil) { string @namespace = tr.Namespace.IsNil ? "" : reader.GetString(tr.Namespace); return new FullTypeName(new TopLevelTypeName(@namespace, name, typeParameterCount)); } else { return declaringTypeHandle.GetFullTypeName(reader).NestedType(name, typeParameterCount); } } public static FullTypeName GetFullTypeName(this TypeDefinitionHandle handle, MetadataReader reader) { if (handle.IsNil) throw new ArgumentNullException(nameof(handle)); return reader.GetTypeDefinition(handle).GetFullTypeName(reader); } public static FullTypeName GetFullTypeName(this TypeDefinition td, MetadataReader reader) { TypeDefinitionHandle declaringTypeHandle; string name = ReflectionHelper.SplitTypeParameterCountFromReflectionName(reader.GetString(td.Name), out var typeParameterCount); if ((declaringTypeHandle = td.GetDeclaringType()).IsNil) { string @namespace = td.Namespace.IsNil ? "" : reader.GetString(td.Namespace); return new FullTypeName(new TopLevelTypeName(@namespace, name, typeParameterCount)); } else { return declaringTypeHandle.GetFullTypeName(reader).NestedType(name, typeParameterCount); } } public static FullTypeName GetFullTypeName(this ExportedType type, MetadataReader metadata) { string ns = type.Namespace.IsNil ? "" : metadata.GetString(type.Namespace); string name = ReflectionHelper.SplitTypeParameterCountFromReflectionName(metadata.GetString(type.Name), out int typeParameterCount); return new TopLevelTypeName(ns, name, typeParameterCount); } public static bool IsAnonymousType(this TypeDefinition type, MetadataReader metadata) { string name = metadata.GetString(type.Name); if (type.Namespace.IsNil && type.HasGeneratedName(metadata) && (name.Contains("AnonType") || name.Contains("AnonymousType"))) { return type.IsCompilerGenerated(metadata); } return false; } #region HasGeneratedName public static bool IsGeneratedName(this StringHandle handle, MetadataReader metadata) { return !handle.IsNil && metadata.GetString(handle).StartsWith("<", StringComparison.Ordinal); } public static bool HasGeneratedName(this MethodDefinitionHandle handle, MetadataReader metadata) { return metadata.GetMethodDefinition(handle).Name.IsGeneratedName(metadata); } public static bool HasGeneratedName(this TypeDefinitionHandle handle, MetadataReader metadata) { return metadata.GetTypeDefinition(handle).Name.IsGeneratedName(metadata); } public static bool HasGeneratedName(this TypeDefinition type, MetadataReader metadata) { return type.Name.IsGeneratedName(metadata); } public static bool HasGeneratedName(this FieldDefinitionHandle handle, MetadataReader metadata) { return metadata.GetFieldDefinition(handle).Name.IsGeneratedName(metadata); } #endregion #region IsCompilerGenerated public static bool IsCompilerGenerated(this MethodDefinitionHandle handle, MetadataReader metadata) { return metadata.GetMethodDefinition(handle).IsCompilerGenerated(metadata); } public static bool IsCompilerGenerated(this MethodDefinition method, MetadataReader metadata) { return method.GetCustomAttributes().HasKnownAttribute(metadata, KnownAttribute.CompilerGenerated); } public static bool IsCompilerGenerated(this FieldDefinitionHandle handle, MetadataReader metadata) { return metadata.GetFieldDefinition(handle).IsCompilerGenerated(metadata); } public static bool IsCompilerGenerated(this FieldDefinition field, MetadataReader metadata) { return field.GetCustomAttributes().HasKnownAttribute(metadata, KnownAttribute.CompilerGenerated); } public static bool IsCompilerGenerated(this TypeDefinitionHandle handle, MetadataReader metadata) { return metadata.GetTypeDefinition(handle).IsCompilerGenerated(metadata); } public static bool IsCompilerGenerated(this TypeDefinition type, MetadataReader metadata) { return type.GetCustomAttributes().HasKnownAttribute(metadata, KnownAttribute.CompilerGenerated); } #endregion #region Attribute extensions /// /// Gets the type of the attribute. /// public static EntityHandle GetAttributeType(this SRM.CustomAttribute attribute, MetadataReader reader) { switch (attribute.Constructor.Kind) { case HandleKind.MethodDefinition: var md = reader.GetMethodDefinition((MethodDefinitionHandle)attribute.Constructor); return md.GetDeclaringType(); case HandleKind.MemberReference: var mr = reader.GetMemberReference((MemberReferenceHandle)attribute.Constructor); return mr.Parent; default: throw new BadImageFormatException("Unexpected token kind for attribute constructor: " + attribute.Constructor.Kind); } } internal static bool HasKnownAttribute(this CustomAttributeHandleCollection customAttributes, MetadataReader metadata, KnownAttribute type) { foreach (var handle in customAttributes) { var customAttribute = metadata.GetCustomAttribute(handle); if (customAttribute.IsKnownAttribute(metadata, type)) return true; } return false; } internal static bool IsKnownAttribute(this SRM.CustomAttribute attr, MetadataReader metadata, KnownAttribute attrType) { return attr.GetAttributeType(metadata).IsKnownType(metadata, attrType); } #endregion public static unsafe SRM.BlobReader GetInitialValue(this FieldDefinition field, PEReader pefile, IDecompilerTypeSystem typeSystem) { if (!field.HasFlag(FieldAttributes.HasFieldRVA) || field.GetRelativeVirtualAddress() == 0) return default; int rva = field.GetRelativeVirtualAddress(); int size = field.DecodeSignature(new FieldValueSizeDecoder(typeSystem), default); return pefile.GetSectionData(rva).GetReader(0, size); } class FieldValueSizeDecoder : ISignatureTypeProvider { IDecompilerTypeSystem typeSystem; public FieldValueSizeDecoder(IDecompilerTypeSystem typeSystem) { this.typeSystem = typeSystem ?? throw new ArgumentNullException(nameof(typeSystem)); } public int GetArrayType(int elementType, ArrayShape shape) => elementType; public int GetByReferenceType(int elementType) => elementType; public int GetFunctionPointerType(MethodSignature signature) => IntPtr.Size; public int GetGenericInstantiation(int genericType, ImmutableArray typeArguments) => genericType; public int GetGenericMethodParameter(Unit genericContext, int index) => 0; public int GetGenericTypeParameter(Unit genericContext, int index) => 0; public int GetModifiedType(int modifier, int unmodifiedType, bool isRequired) => unmodifiedType; public int GetPinnedType(int elementType) => elementType; public int GetPointerType(int elementType) => elementType; public int GetPrimitiveType(PrimitiveTypeCode typeCode) { switch (typeCode) { case PrimitiveTypeCode.Boolean: case PrimitiveTypeCode.Byte: case PrimitiveTypeCode.SByte: return 1; case PrimitiveTypeCode.Char: case PrimitiveTypeCode.Int16: case PrimitiveTypeCode.UInt16: return 2; case PrimitiveTypeCode.Int32: case PrimitiveTypeCode.UInt32: case PrimitiveTypeCode.Single: return 4; case PrimitiveTypeCode.Int64: case PrimitiveTypeCode.UInt64: case PrimitiveTypeCode.Double: return 8; case PrimitiveTypeCode.IntPtr: case PrimitiveTypeCode.UIntPtr: // this is the same as Cecil does. return IntPtr.Size; default: // we assume pointer size for object, string and typedref return IntPtr.Size; } } public int GetSZArrayType(int elementType) => 0; public int GetTypeFromDefinition(MetadataReader reader, TypeDefinitionHandle handle, byte rawTypeKind) { var td = reader.GetTypeDefinition(handle); return td.GetLayout().Size; } public int GetTypeFromReference(MetadataReader reader, TypeReferenceHandle handle, byte rawTypeKind) { var typeDef = typeSystem.ResolveAsType(handle).GetDefinition(); if (typeDef == null || typeDef.MetadataToken.IsNil) return 0; reader = typeDef.ParentAssembly.PEFile.Metadata; var td = reader.GetTypeDefinition((TypeDefinitionHandle)typeDef.MetadataToken); return td.GetLayout().Size; } public int GetTypeFromSpecification(MetadataReader reader, Unit genericContext, TypeSpecificationHandle handle, byte rawTypeKind) { var typeDef = typeSystem.ResolveAsType(handle).GetDefinition(); if (typeDef == null || typeDef.MetadataToken.IsNil) return 0; reader = typeDef.ParentAssembly.PEFile.Metadata; var td = reader.GetTypeDefinition((TypeDefinitionHandle)typeDef.MetadataToken); return td.GetLayout().Size; } } } }