namespace WoofWare.PawPrint open System open System.Collections.Generic open System.Collections.Immutable open System.Reflection open System.Reflection.Metadata open System.Reflection.PortableExecutable open Microsoft.Extensions.Logging open Microsoft.FSharp.Core [] type BaseTypeInfo = | TypeDef of TypeDefinitionHandle | TypeRef of TypeReferenceHandle | TypeSpec of TypeSpecificationHandle | ForeignAssemblyType of assemblyName : AssemblyName * TypeDefinitionHandle type MethodImplParsed = | MethodImplementation of MethodImplementationHandle | MethodDefinition of MethodDefinitionHandle type InterfaceImplementation = { /// TypeDefinition, TypeReference, or TypeSpecification InterfaceHandle : MetadataToken /// The assembly which InterfaceHandle is relative to RelativeToAssembly : AssemblyName } /// /// Represents detailed information about a type definition in a .NET assembly. /// This is a strongly-typed representation of TypeDefinition from System.Reflection.Metadata. /// type TypeInfo<'generic, 'fieldGeneric> = { /// The namespace containing the type. Namespace : string /// The name of the type. Name : string /// /// All methods defined within this type. /// Methods : WoofWare.PawPrint.MethodInfo list /// /// Method implementation mappings for this type, often used for interface implementations /// or overriding virtual methods from base classes. /// MethodImpls : ImmutableDictionary /// /// Fields defined in this type. /// Fields : WoofWare.PawPrint.FieldInfo list /// /// The base type that this type inherits from, or None for types that don't have a base type /// (like System.Object). /// /// Value types inherit *directly* from System.ValueType; enums directly from System.Enum. /// BaseType : BaseTypeInfo option /// /// Attributes applied to this type, such as visibility, inheritance characteristics, /// special handling, and other flags. /// TypeAttributes : TypeAttributes /// /// Custom attributes applied to this type. /// Attributes : WoofWare.PawPrint.CustomAttribute list /// /// The metadata token handle that uniquely identifies this type in the assembly. /// TypeDefHandle : TypeDefinitionHandle DeclaringType : TypeDefinitionHandle /// /// The assembly in which this type is defined. /// Assembly : AssemblyName Generics : 'generic ImmutableArray Events : EventDefn ImmutableArray ImplementedInterfaces : InterfaceImplementation ImmutableArray } member this.IsInterface = this.TypeAttributes.HasFlag TypeAttributes.Interface member this.IsNested = [ TypeAttributes.NestedPublic TypeAttributes.NestedPrivate TypeAttributes.NestedFamily TypeAttributes.NestedAssembly TypeAttributes.NestedFamANDAssem TypeAttributes.NestedFamORAssem ] |> List.exists this.TypeAttributes.HasFlag override this.ToString () = $"%s{this.Assembly.Name}.%s{this.Namespace}.%s{this.Name}" static member NominallyEqual (a : TypeInfo<'generic, 'fieldGeneric>) (b : TypeInfo<'generic, 'fieldGeneric>) : bool = a.Assembly.FullName = b.Assembly.FullName && a.Namespace = b.Namespace && a.Name = b.Name && a.Generics = b.Generics type TypeInfoEval<'ret> = abstract Eval<'a, 'field> : TypeInfo<'a, 'field> -> 'ret type TypeInfoCrate = abstract Apply<'ret> : TypeInfoEval<'ret> -> 'ret abstract ToString : unit -> string abstract BaseType : BaseTypeInfo option abstract Assembly : AssemblyName abstract Namespace : string abstract Name : string [] module TypeInfoCrate = let make<'a, 'field> (t : TypeInfo<'a, 'field>) : TypeInfoCrate = { new TypeInfoCrate with member _.Apply e = e.Eval t member this.ToString () = { new TypeInfoEval<_> with member _.Eval this = string> this } |> this.Apply member this.BaseType = t.BaseType member this.Assembly = t.Assembly member this.Namespace = t.Namespace member this.Name = t.Name } type BaseClassTypes<'corelib> = { Corelib : 'corelib String : TypeInfo Boolean : TypeInfo Char : TypeInfo SByte : TypeInfo Byte : TypeInfo Int16 : TypeInfo UInt16 : TypeInfo Int32 : TypeInfo UInt32 : TypeInfo Int64 : TypeInfo UInt64 : TypeInfo Single : TypeInfo Double : TypeInfo Array : TypeInfo Enum : TypeInfo ValueType : TypeInfo DelegateType : TypeInfo Object : TypeInfo RuntimeMethodHandle : TypeInfo RuntimeFieldHandle : TypeInfo RuntimeTypeHandle : TypeInfo RuntimeFieldInfoStub : TypeInfo RuntimeFieldHandleInternal : TypeInfo RuntimeType : TypeInfo Void : TypeInfo TypedReference : TypeInfo IntPtr : TypeInfo UIntPtr : TypeInfo } [] module TypeInfo = let rec fullName (get : TypeDefinitionHandle -> TypeInfo<_, _>) (ty : TypeInfo<'a, 'b>) = if ty.IsNested then let parent = get ty.DeclaringType |> fullName get $"%s{parent}.{ty.Name}" else if not (String.IsNullOrEmpty ty.Namespace) then $"{ty.Namespace}.{ty.Name}" else ty.Name let withGenerics<'a, 'b, 'field> (gen : 'b ImmutableArray) (t : TypeInfo<'a, 'field>) : TypeInfo<'b, 'field> = { Namespace = t.Namespace Name = t.Name Methods = t.Methods MethodImpls = t.MethodImpls Fields = t.Fields BaseType = t.BaseType TypeAttributes = t.TypeAttributes Attributes = t.Attributes TypeDefHandle = t.TypeDefHandle DeclaringType = t.DeclaringType Assembly = t.Assembly Generics = gen Events = t.Events ImplementedInterfaces = t.ImplementedInterfaces } let mapGeneric<'a, 'b, 'field> (f : 'a -> 'b) (t : TypeInfo<'a, 'field>) : TypeInfo<'b, 'field> = withGenerics (t.Generics |> ImmutableArray.map f) t let internal read (loggerFactory : ILoggerFactory) (peReader : PEReader) (thisAssembly : AssemblyName) (metadataReader : MetadataReader) (typeHandle : TypeDefinitionHandle) : TypeInfo = let typeDef = metadataReader.GetTypeDefinition typeHandle let declaringType = typeDef.GetDeclaringType () let methods = typeDef.GetMethods () let methodImpls = typeDef.GetMethodImplementations () |> Seq.map (fun handle -> let m = metadataReader.GetMethodImplementation handle let methodBody = MetadataToken.ofEntityHandle m.MethodBody match methodBody with | MetadataToken.MethodImplementation t -> KeyValuePair (handle, MethodImplParsed.MethodImplementation t) | MetadataToken.MethodDef t -> KeyValuePair (handle, MethodImplParsed.MethodDefinition t) | k -> failwith $"unexpected kind: {k}" ) |> ImmutableDictionary.CreateRange let fields = typeDef.GetFields () |> Seq.map (fun h -> FieldInfo.make metadataReader thisAssembly h (metadataReader.GetFieldDefinition h)) |> Seq.toList let name = metadataReader.GetString typeDef.Name let ns = metadataReader.GetString typeDef.Namespace let typeAttrs = typeDef.Attributes let attrs = typeDef.GetCustomAttributes () |> Seq.map (fun h -> CustomAttribute.make h (metadataReader.GetCustomAttribute h)) |> Seq.toList let genericParams = GenericParameter.readAll metadataReader (typeDef.GetGenericParameters ()) let methods = methods |> Seq.choose (fun m -> let result = MethodInfo.read loggerFactory peReader metadataReader m match result with | None -> None | Some x -> Some x ) |> Seq.toList let baseType = match MetadataToken.ofEntityHandle typeDef.BaseType with | TypeReference typeReferenceHandle -> Some (BaseTypeInfo.TypeRef typeReferenceHandle) | TypeDefinition typeDefinitionHandle -> if typeDefinitionHandle.IsNil then None else Some (BaseTypeInfo.TypeDef typeDefinitionHandle) | TypeSpecification typeSpecHandle -> Some (BaseTypeInfo.TypeSpec typeSpecHandle) | t -> failwith $"Unrecognised base-type entity identifier: %O{t}" let events = let result = ImmutableArray.CreateBuilder () for evt in typeDef.GetEvents () do metadataReader.GetEventDefinition evt |> EventDefn.make metadataReader |> result.Add result.ToImmutable () let interfaces = let result = ImmutableArray.CreateBuilder () for i in typeDef.GetInterfaceImplementations () do let impl = metadataReader.GetInterfaceImplementation i { InterfaceHandle = MetadataToken.ofEntityHandle impl.Interface RelativeToAssembly = thisAssembly } |> result.Add result.ToImmutable () { Namespace = ns Name = name Methods = methods MethodImpls = methodImpls Fields = fields BaseType = baseType TypeAttributes = typeAttrs Attributes = attrs TypeDefHandle = typeHandle Assembly = thisAssembly Generics = genericParams Events = events ImplementedInterfaces = interfaces DeclaringType = declaringType } let isBaseType<'corelib> (baseClassTypes : BaseClassTypes<'corelib>) (getName : 'corelib -> AssemblyName) (typeAssy : AssemblyName) (typeDefinitionHandle : TypeDefinitionHandle) : ResolvedBaseType option = if typeAssy = getName baseClassTypes.Corelib then if typeDefinitionHandle = baseClassTypes.Enum.TypeDefHandle then Some ResolvedBaseType.Enum elif typeDefinitionHandle = baseClassTypes.ValueType.TypeDefHandle then Some ResolvedBaseType.ValueType elif typeDefinitionHandle = baseClassTypes.DelegateType.TypeDefHandle then Some ResolvedBaseType.Delegate elif typeDefinitionHandle = baseClassTypes.Object.TypeDefHandle then Some ResolvedBaseType.Object else None else None let rec resolveBaseType<'corelib, 'generic, 'field> (baseClassTypes : BaseClassTypes<'corelib>) (sourceAssy : 'corelib) (getName : 'corelib -> AssemblyName) (getTypeDef : 'corelib -> TypeDefinitionHandle -> TypeInfo<'generic, 'field>) (getTypeRef : 'corelib -> TypeReferenceHandle -> 'corelib * TypeInfo<'generic, 'field>) (value : BaseTypeInfo option) : ResolvedBaseType = match value with | None -> ResolvedBaseType.Object | Some value -> match value with | BaseTypeInfo.TypeDef typeDefinitionHandle -> match isBaseType baseClassTypes getName (getName sourceAssy) typeDefinitionHandle with | Some x -> x | None -> let baseType = getTypeDef baseClassTypes.Corelib typeDefinitionHandle resolveBaseType baseClassTypes sourceAssy getName getTypeDef getTypeRef baseType.BaseType | BaseTypeInfo.TypeRef typeReferenceHandle -> let targetAssy, typeRef = getTypeRef sourceAssy typeReferenceHandle match isBaseType baseClassTypes getName (getName targetAssy) typeRef.TypeDefHandle with | Some x -> x | None -> let baseType = getTypeDef baseClassTypes.Corelib typeRef.TypeDefHandle resolveBaseType baseClassTypes sourceAssy getName getTypeDef getTypeRef baseType.BaseType | BaseTypeInfo.TypeSpec typeSpecificationHandle -> failwith "todo" | BaseTypeInfo.ForeignAssemblyType (assemblyName, typeDefinitionHandle) -> resolveBaseType baseClassTypes sourceAssy getName getTypeDef getTypeRef (Some (BaseTypeInfo.TypeDef typeDefinitionHandle)) let toTypeDefn (baseClassTypes : BaseClassTypes<'corelib>) (assemblies : AssemblyName -> 'corelib) (getName : 'corelib -> AssemblyName) (getTypeDef : 'corelib -> TypeDefinitionHandle -> TypeInfo<'generic, 'field>) (getTypeRef : 'corelib -> TypeReferenceHandle -> 'corelib * TypeInfo<'generic, 'field>) (ty : TypeInfo) : TypeDefn = let stk = match resolveBaseType baseClassTypes (assemblies ty.Assembly) getName getTypeDef getTypeRef ty.BaseType with | ResolvedBaseType.Enum | ResolvedBaseType.ValueType -> SignatureTypeKind.ValueType | ResolvedBaseType.Object | ResolvedBaseType.Delegate -> SignatureTypeKind.Class let defn = // The only allowed construction of FromDefinition! // All other constructions should use DumpedAssembly.typeInfoToTypeDefn. TypeDefn.FromDefinition (ComparableTypeDefinitionHandle.Make ty.TypeDefHandle, ty.Assembly.FullName, stk) if ty.Generics.IsEmpty then defn else let generics = ty.Generics TypeDefn.GenericInstantiation (defn, generics)