namespace WoofWare.PawPrint #nowarn "9" 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 /// /// Represents information about a method parameter. /// Corresponds to Parameter in System.Reflection.Metadata. /// type Parameter = { /// The name of the parameter. Name : string /// /// The default value of the parameter, if one is specified. /// This is used for optional parameters. /// DefaultValue : Constant /// /// The position of the parameter in the parameter list. /// For instance methods, index 0 is the 'this' parameter. /// SequenceNumber : int } [] module Parameter = let readAll (metadata : MetadataReader) (param : ParameterHandleCollection) : Parameter ImmutableArray = let result = ImmutableArray.CreateBuilder () for param in param do let param = metadata.GetParameter param // The spec doesn't seem to mention this behaviour, but a sequence number of 0 (and an unnamed parameter) // seems to correspond with a ref return. if param.SequenceNumber <> 0 then { Name = metadata.GetString param.Name DefaultValue = metadata.GetConstant (param.GetDefaultValue ()) SequenceNumber = param.SequenceNumber } |> result.Add result.ToImmutable () type ExceptionOffset = { TryLength : int TryOffset : int HandlerLength : int HandlerOffset : int } type ExceptionRegion = | Filter of filterOffset : int * ExceptionOffset /// Token is a TypeRef, TypeDef, or TypeSpec | Catch of MetadataToken * ExceptionOffset | Finally of ExceptionOffset | Fault of ExceptionOffset static member OfExceptionRegion (r : System.Reflection.Metadata.ExceptionRegion) : ExceptionRegion = let offset = { HandlerLength = r.HandlerLength HandlerOffset = r.HandlerOffset TryLength = r.TryLength TryOffset = r.TryOffset } match r.Kind with | ExceptionRegionKind.Catch -> ExceptionRegion.Catch (MetadataToken.ofEntityHandle r.CatchType, offset) | ExceptionRegionKind.Filter -> ExceptionRegion.Filter (r.FilterOffset, offset) | ExceptionRegionKind.Finally -> ExceptionRegion.Finally offset | ExceptionRegionKind.Fault -> ExceptionRegion.Fault offset | _ -> raise (ArgumentOutOfRangeException ()) type MethodInstructions<'methodVars> = { /// /// The IL instructions that compose the method body, along with their offset positions. /// Each tuple contains the instruction and its offset in the method body. /// Instructions : (IlOp * int) list /// /// A map from instruction offset (program counter) to the corresponding IL operation. /// This is the inverse of Instructions for efficient lookup. /// Locations : Map /// /// Whether local variables in this method should be initialized to their default values. /// This corresponds to the localsinit flag in the method header. /// LocalsInit : bool LocalVars : ImmutableArray<'methodVars> option ExceptionRegions : ImmutableArray } [] module MethodInstructions = let onlyRet () : MethodInstructions<'methodVars> = let op = IlOp.Nullary NullaryIlOp.Ret { Instructions = [ op, 0 ] Locations = Map.empty |> Map.add 0 op LocalsInit = false LocalVars = None ExceptionRegions = ImmutableArray.Empty } let setLocalVars<'a, 'b> (v : ImmutableArray<'b> option) (s : MethodInstructions<'a>) : MethodInstructions<'b> = { Instructions = s.Instructions Locations = s.Locations LocalsInit = s.LocalsInit LocalVars = v ExceptionRegions = s.ExceptionRegions } /// /// Represents detailed information about a method in a .NET assembly. /// This is a strongly-typed representation of MethodDefinition from System.Reflection.Metadata. /// type MethodInfo<'typeGenerics, 'methodGenerics, 'methodVars> = { /// /// The type that declares this method, along with its assembly information. /// DeclaringType : ConcreteType<'typeGenerics> /// /// The metadata token handle that uniquely identifies this method in the assembly. /// Handle : MethodDefinitionHandle /// The name of the method. Name : string /// /// The IL instructions that compose the method body, along with their offset positions. /// /// There may be no instructions for this method, e.g. if it's an `InternalCall`. /// Instructions : MethodInstructions<'methodVars> option /// /// The parameters of this method. /// Parameters : Parameter ImmutableArray /// /// The generic type parameters defined by this method, if any. /// Generics : 'methodGenerics ImmutableArray /// /// The signature of the method, including return type and parameter types. /// Signature : TypeMethodSignature<'methodVars> /// /// The signature as it was read from assembly metadata. /// RawSignature : TypeMethodSignature /// /// Custom attributes defined on the method. I've never yet seen one of these in practice. /// CustomAttributes : WoofWare.PawPrint.CustomAttribute ImmutableArray MethodAttributes : MethodAttributes ImplAttributes : MethodImplAttributes /// /// Whether this method is static (true) or an instance method (false). /// IsStatic : bool } override this.ToString () = $"{this.DeclaringType.Assembly.Name}.{this.DeclaringType.Name}.{this.Name}" /// /// Whether this method's implementation is directly supplied by the CLI, rather than being loaded /// from an assembly as IL. /// member this.IsCliInternal : bool = this.ImplAttributes.HasFlag MethodImplAttributes.InternalCall /// /// Whether this method is implemented as a platform invoke (P/Invoke) to unmanaged code. /// member this.IsPinvokeImpl : bool = this.MethodAttributes.HasFlag MethodAttributes.PinvokeImpl [] module MethodInfo = let isJITIntrinsic (getMemberRefParentType : MemberReferenceHandle -> TypeRef) (methodDefs : IReadOnlyDictionary>) (this : MethodInfo<'d, 'e, 'f>) : bool = this.CustomAttributes |> Seq.exists (fun attr -> match attr.Constructor with | MetadataToken.MethodDef handle -> let constructor = methodDefs.[handle] constructor.DeclaringType.Name = "IntrinsicAttribute" && constructor.DeclaringType.Assembly.FullName.StartsWith ( "System.Private.CoreLib, ", StringComparison.Ordinal ) | MetadataToken.MemberReference handle -> let ty = getMemberRefParentType handle ty.Namespace = "System" && ty.Name = "IntrinsicAttribute" | con -> failwith $"TODO: {con}" ) let mapTypeGenerics<'a, 'b, 'methodGen, 'vars> (f : 'a -> 'b) (m : MethodInfo<'a, 'methodGen, 'vars>) : MethodInfo<'b, 'methodGen, 'vars> = { DeclaringType = m.DeclaringType |> ConcreteType.mapGeneric (fun _ -> f) Handle = m.Handle Name = m.Name Instructions = m.Instructions Parameters = m.Parameters Generics = m.Generics Signature = m.Signature RawSignature = m.RawSignature CustomAttributes = m.CustomAttributes MethodAttributes = m.MethodAttributes ImplAttributes = m.ImplAttributes IsStatic = m.IsStatic } let mapMethodGenerics<'a, 'b, 'vars, 'typeGen> (f : int -> 'a -> 'b) (m : MethodInfo<'typeGen, 'a, 'vars>) : MethodInfo<'typeGen, 'b, 'vars> = let generics = m.Generics |> Seq.mapi f |> ImmutableArray.CreateRange { DeclaringType = m.DeclaringType Handle = m.Handle Name = m.Name Instructions = m.Instructions Parameters = m.Parameters Generics = generics Signature = m.Signature RawSignature = m.RawSignature CustomAttributes = m.CustomAttributes MethodAttributes = m.MethodAttributes ImplAttributes = m.ImplAttributes IsStatic = m.IsStatic } let setMethodVars (vars2 : MethodInstructions<'vars2> option) (signature : TypeMethodSignature<'vars2>) (m : MethodInfo<'typeGen, 'methodGen, 'vars1>) : MethodInfo<'typeGen, 'methodGen, 'vars2> = { DeclaringType = m.DeclaringType Handle = m.Handle Name = m.Name Instructions = vars2 Parameters = m.Parameters Generics = m.Generics Signature = signature RawSignature = m.RawSignature CustomAttributes = m.CustomAttributes MethodAttributes = m.MethodAttributes ImplAttributes = m.ImplAttributes IsStatic = m.IsStatic } type private Dummy = class end type private MethodBody = { Instructions : (IlOp * int) list LocalInit : bool LocalSig : ImmutableArray option MaxStackSize : int ExceptionRegions : ImmutableArray } let private readMetadataToken (reader : byref) : MetadataToken = reader.ReadUInt32 () |> int |> MetadataToken.ofInt let private readStringToken (reader : byref) : StringToken = let value = reader.ReadUInt32 () |> int StringToken.ofInt value // TODO: each opcode probably ought to store how many bytes it takes, so we can advance the program counter? let private readOpCode (reader : byref) : ILOpCode = let op = reader.ReadByte () if op = 0xFEuy then let op2 = reader.ReadByte () LanguagePrimitives.EnumOfValue (0xFE00us ||| (uint16 op2)) else LanguagePrimitives.EnumOfValue (uint16 op) let private readMethodBody (peReader : PEReader) (metadataReader : MetadataReader) (assembly : AssemblyName) (methodDef : MethodDefinition) : MethodBody option = if methodDef.RelativeVirtualAddress = 0 then None else let methodBody = peReader.GetMethodBody methodDef.RelativeVirtualAddress let localSig = if methodBody.LocalSignature.IsNil then None else let s = methodBody.LocalSignature |> metadataReader.GetStandaloneSignature s.DecodeLocalSignature (TypeDefn.typeProvider assembly, ()) |> Some let ilBytes = methodBody.GetILBytes () use bytes = fixed ilBytes let mutable reader : BlobReader = BlobReader (bytes, ilBytes.Length) let rec readInstructions acc = if reader.Offset >= ilBytes.Length then List.rev acc else let offset = reader.Offset let opCode = readOpCode (&reader) let opCode = match opCode with | ILOpCode.Nop -> IlOp.Nullary NullaryIlOp.Nop | ILOpCode.Break -> IlOp.Nullary NullaryIlOp.Break | ILOpCode.Ldarg_0 -> IlOp.Nullary NullaryIlOp.LdArg0 | ILOpCode.Ldarg_1 -> IlOp.Nullary NullaryIlOp.LdArg1 | ILOpCode.Ldarg_2 -> IlOp.Nullary NullaryIlOp.LdArg2 | ILOpCode.Ldarg_3 -> IlOp.Nullary NullaryIlOp.LdArg3 | ILOpCode.Ldloc_0 -> IlOp.Nullary NullaryIlOp.Ldloc_0 | ILOpCode.Ldloc_1 -> IlOp.Nullary NullaryIlOp.Ldloc_1 | ILOpCode.Ldloc_2 -> IlOp.Nullary NullaryIlOp.Ldloc_2 | ILOpCode.Ldloc_3 -> IlOp.Nullary NullaryIlOp.Ldloc_3 | ILOpCode.Stloc_0 -> IlOp.Nullary NullaryIlOp.Stloc_0 | ILOpCode.Stloc_1 -> IlOp.Nullary NullaryIlOp.Stloc_1 | ILOpCode.Stloc_2 -> IlOp.Nullary NullaryIlOp.Stloc_2 | ILOpCode.Stloc_3 -> IlOp.Nullary NullaryIlOp.Stloc_3 | ILOpCode.Ldarg_s -> IlOp.UnaryConst (UnaryConstIlOp.Ldarg_s (reader.ReadByte ())) | ILOpCode.Ldarga_s -> IlOp.UnaryConst (UnaryConstIlOp.Ldarga_s (reader.ReadByte ())) | ILOpCode.Starg_s -> IlOp.UnaryConst (UnaryConstIlOp.Starg_s (reader.ReadByte ())) | ILOpCode.Ldloc_s -> IlOp.UnaryConst (UnaryConstIlOp.Ldloc_s (reader.ReadByte ())) | ILOpCode.Ldloca_s -> IlOp.UnaryConst (UnaryConstIlOp.Ldloca_s (reader.ReadByte ())) | ILOpCode.Stloc_s -> IlOp.UnaryConst (UnaryConstIlOp.Stloc_s (reader.ReadSByte ())) | ILOpCode.Ldnull -> IlOp.Nullary NullaryIlOp.LdNull | ILOpCode.Ldc_i4_m1 -> IlOp.Nullary NullaryIlOp.LdcI4_m1 | ILOpCode.Ldc_i4_0 -> IlOp.Nullary NullaryIlOp.LdcI4_0 | ILOpCode.Ldc_i4_1 -> IlOp.Nullary NullaryIlOp.LdcI4_1 | ILOpCode.Ldc_i4_2 -> IlOp.Nullary NullaryIlOp.LdcI4_2 | ILOpCode.Ldc_i4_3 -> IlOp.Nullary NullaryIlOp.LdcI4_3 | ILOpCode.Ldc_i4_4 -> IlOp.Nullary NullaryIlOp.LdcI4_4 | ILOpCode.Ldc_i4_5 -> IlOp.Nullary NullaryIlOp.LdcI4_5 | ILOpCode.Ldc_i4_6 -> IlOp.Nullary NullaryIlOp.LdcI4_6 | ILOpCode.Ldc_i4_7 -> IlOp.Nullary NullaryIlOp.LdcI4_7 | ILOpCode.Ldc_i4_8 -> IlOp.Nullary NullaryIlOp.LdcI4_8 | ILOpCode.Ldc_i4_s -> IlOp.UnaryConst (UnaryConstIlOp.Ldc_I4_s (reader.ReadSByte ())) | ILOpCode.Ldc_i4 -> IlOp.UnaryConst (UnaryConstIlOp.Ldc_I4 (reader.ReadInt32 ())) | ILOpCode.Ldc_i8 -> IlOp.UnaryConst (UnaryConstIlOp.Ldc_I8 (reader.ReadInt64 ())) | ILOpCode.Ldc_r4 -> IlOp.UnaryConst (UnaryConstIlOp.Ldc_R4 (reader.ReadSingle ())) | ILOpCode.Ldc_r8 -> IlOp.UnaryConst (UnaryConstIlOp.Ldc_R8 (reader.ReadDouble ())) | ILOpCode.Dup -> IlOp.Nullary NullaryIlOp.Dup | ILOpCode.Pop -> IlOp.Nullary NullaryIlOp.Pop | ILOpCode.Jmp -> IlOp.UnaryMetadataToken (UnaryMetadataTokenIlOp.Jmp, readMetadataToken &reader) | ILOpCode.Call -> IlOp.UnaryMetadataToken (UnaryMetadataTokenIlOp.Call, readMetadataToken &reader) | ILOpCode.Calli -> IlOp.UnaryMetadataToken (UnaryMetadataTokenIlOp.Calli, readMetadataToken &reader) | ILOpCode.Ret -> IlOp.Nullary NullaryIlOp.Ret | ILOpCode.Br_s -> IlOp.UnaryConst (UnaryConstIlOp.Br_s (reader.ReadSByte ())) | ILOpCode.Brfalse_s -> IlOp.UnaryConst (UnaryConstIlOp.Brfalse_s (reader.ReadSByte ())) | ILOpCode.Brtrue_s -> IlOp.UnaryConst (UnaryConstIlOp.Brtrue_s (reader.ReadSByte ())) | ILOpCode.Beq_s -> IlOp.UnaryConst (UnaryConstIlOp.Beq_s (reader.ReadSByte ())) | ILOpCode.Bge_s -> IlOp.UnaryConst (UnaryConstIlOp.Bge_s (reader.ReadSByte ())) | ILOpCode.Bgt_s -> IlOp.UnaryConst (UnaryConstIlOp.Bgt_s (reader.ReadSByte ())) | ILOpCode.Ble_s -> IlOp.UnaryConst (UnaryConstIlOp.Ble_s (reader.ReadSByte ())) | ILOpCode.Blt_s -> IlOp.UnaryConst (UnaryConstIlOp.Blt_s (reader.ReadSByte ())) | ILOpCode.Bne_un_s -> IlOp.UnaryConst (UnaryConstIlOp.Bne_un_s (reader.ReadSByte ())) | ILOpCode.Bge_un_s -> IlOp.UnaryConst (UnaryConstIlOp.Bge_un_s (reader.ReadSByte ())) | ILOpCode.Bgt_un_s -> IlOp.UnaryConst (UnaryConstIlOp.Bgt_un_s (reader.ReadSByte ())) | ILOpCode.Ble_un_s -> IlOp.UnaryConst (UnaryConstIlOp.Ble_un_s (reader.ReadSByte ())) | ILOpCode.Blt_un_s -> IlOp.UnaryConst (UnaryConstIlOp.Blt_un_s (reader.ReadSByte ())) | ILOpCode.Br -> IlOp.UnaryConst (UnaryConstIlOp.Br (reader.ReadInt32 ())) | ILOpCode.Brfalse -> IlOp.UnaryConst (UnaryConstIlOp.Brfalse (reader.ReadInt32 ())) | ILOpCode.Brtrue -> IlOp.UnaryConst (UnaryConstIlOp.Brtrue (reader.ReadInt32 ())) | ILOpCode.Beq -> IlOp.UnaryConst (UnaryConstIlOp.Beq (reader.ReadInt32 ())) | ILOpCode.Bge -> IlOp.UnaryConst (UnaryConstIlOp.Bge (reader.ReadInt32 ())) | ILOpCode.Bgt -> IlOp.UnaryConst (UnaryConstIlOp.Bgt (reader.ReadInt32 ())) | ILOpCode.Ble -> IlOp.UnaryConst (UnaryConstIlOp.Ble (reader.ReadInt32 ())) | ILOpCode.Blt -> IlOp.UnaryConst (UnaryConstIlOp.Blt (reader.ReadInt32 ())) | ILOpCode.Bne_un -> IlOp.UnaryConst (UnaryConstIlOp.Bne_un (reader.ReadInt32 ())) | ILOpCode.Bge_un -> IlOp.UnaryConst (UnaryConstIlOp.Bge_un (reader.ReadInt32 ())) | ILOpCode.Bgt_un -> IlOp.UnaryConst (UnaryConstIlOp.Bgt_un (reader.ReadInt32 ())) | ILOpCode.Ble_un -> IlOp.UnaryConst (UnaryConstIlOp.Ble_un (reader.ReadInt32 ())) | ILOpCode.Blt_un -> IlOp.UnaryConst (UnaryConstIlOp.Blt_un (reader.ReadInt32 ())) | ILOpCode.Switch -> let count = reader.ReadUInt32 () if count > uint32 System.Int32.MaxValue then failwith "Debugger error: can't create a jump table with more than int32.Max entries" let count = int count let result = ImmutableArray.CreateBuilder count for i = 0 to count - 1 do result.Add (reader.ReadInt32 ()) IlOp.Switch (result.ToImmutable ()) | ILOpCode.Ldind_i -> IlOp.Nullary NullaryIlOp.Ldind_i | ILOpCode.Ldind_i1 -> IlOp.Nullary NullaryIlOp.Ldind_i1 | ILOpCode.Ldind_u1 -> IlOp.Nullary NullaryIlOp.Ldind_u1 | ILOpCode.Ldind_i2 -> IlOp.Nullary NullaryIlOp.Ldind_i2 | ILOpCode.Ldind_u2 -> IlOp.Nullary NullaryIlOp.Ldind_u2 | ILOpCode.Ldind_i4 -> IlOp.Nullary NullaryIlOp.Ldind_i4 | ILOpCode.Ldind_u4 -> IlOp.Nullary NullaryIlOp.Ldind_u4 | ILOpCode.Ldind_i8 -> IlOp.Nullary NullaryIlOp.Ldind_i8 | ILOpCode.Ldind_r4 -> IlOp.Nullary NullaryIlOp.Ldind_r4 | ILOpCode.Ldind_r8 -> IlOp.Nullary NullaryIlOp.Ldind_r8 | ILOpCode.Ldind_ref -> IlOp.Nullary NullaryIlOp.Ldind_ref | ILOpCode.Stind_ref -> IlOp.Nullary NullaryIlOp.Stind_ref | ILOpCode.Stind_i1 -> IlOp.Nullary NullaryIlOp.Stind_I1 | ILOpCode.Stind_i2 -> IlOp.Nullary NullaryIlOp.Stind_I2 | ILOpCode.Stind_i4 -> IlOp.Nullary NullaryIlOp.Stind_I4 | ILOpCode.Stind_i8 -> IlOp.Nullary NullaryIlOp.Stind_I8 | ILOpCode.Stind_r4 -> IlOp.Nullary NullaryIlOp.Stind_R4 | ILOpCode.Stind_r8 -> IlOp.Nullary NullaryIlOp.Stind_R8 | ILOpCode.Add -> IlOp.Nullary NullaryIlOp.Add | ILOpCode.Sub -> IlOp.Nullary NullaryIlOp.Sub | ILOpCode.Mul -> IlOp.Nullary NullaryIlOp.Mul | ILOpCode.Div -> IlOp.Nullary NullaryIlOp.Div | ILOpCode.Div_un -> IlOp.Nullary NullaryIlOp.Div_un | ILOpCode.Rem -> IlOp.Nullary NullaryIlOp.Rem | ILOpCode.Rem_un -> IlOp.Nullary NullaryIlOp.Rem_un | ILOpCode.And -> IlOp.Nullary NullaryIlOp.And | ILOpCode.Or -> IlOp.Nullary NullaryIlOp.Or | ILOpCode.Xor -> IlOp.Nullary NullaryIlOp.Xor | ILOpCode.Shl -> IlOp.Nullary NullaryIlOp.Shl | ILOpCode.Shr -> IlOp.Nullary NullaryIlOp.Shr | ILOpCode.Shr_un -> IlOp.Nullary NullaryIlOp.Shr_un | ILOpCode.Neg -> IlOp.Nullary NullaryIlOp.Neg | ILOpCode.Not -> IlOp.Nullary NullaryIlOp.Not | ILOpCode.Conv_i1 -> IlOp.Nullary NullaryIlOp.Conv_I1 | ILOpCode.Conv_i2 -> IlOp.Nullary NullaryIlOp.Conv_I2 | ILOpCode.Conv_i4 -> IlOp.Nullary NullaryIlOp.Conv_I4 | ILOpCode.Conv_i8 -> IlOp.Nullary NullaryIlOp.Conv_I8 | ILOpCode.Conv_r4 -> IlOp.Nullary NullaryIlOp.Conv_R4 | ILOpCode.Conv_r8 -> IlOp.Nullary NullaryIlOp.Conv_R8 | ILOpCode.Conv_u4 -> IlOp.Nullary NullaryIlOp.Conv_U4 | ILOpCode.Conv_u8 -> IlOp.Nullary NullaryIlOp.Conv_U8 | ILOpCode.Callvirt -> IlOp.UnaryMetadataToken (UnaryMetadataTokenIlOp.Callvirt, readMetadataToken &reader) | ILOpCode.Cpobj -> IlOp.UnaryMetadataToken (UnaryMetadataTokenIlOp.Cpobj, readMetadataToken &reader) | ILOpCode.Ldobj -> IlOp.UnaryMetadataToken (UnaryMetadataTokenIlOp.Ldobj, readMetadataToken &reader) | ILOpCode.Ldstr -> IlOp.UnaryStringToken (UnaryStringTokenIlOp.Ldstr, readStringToken &reader) | ILOpCode.Newobj -> IlOp.UnaryMetadataToken (UnaryMetadataTokenIlOp.Newobj, readMetadataToken &reader) | ILOpCode.Castclass -> IlOp.UnaryMetadataToken (UnaryMetadataTokenIlOp.Castclass, readMetadataToken &reader) | ILOpCode.Isinst -> IlOp.UnaryMetadataToken (UnaryMetadataTokenIlOp.Isinst, readMetadataToken &reader) | ILOpCode.Conv_r_un -> IlOp.Nullary NullaryIlOp.Conv_r_un | ILOpCode.Unbox -> IlOp.UnaryMetadataToken (UnaryMetadataTokenIlOp.Unbox, readMetadataToken &reader) | ILOpCode.Throw -> IlOp.Nullary NullaryIlOp.Throw | ILOpCode.Ldfld -> IlOp.UnaryMetadataToken (UnaryMetadataTokenIlOp.Ldfld, readMetadataToken &reader) | ILOpCode.Ldflda -> IlOp.UnaryMetadataToken (UnaryMetadataTokenIlOp.Ldflda, readMetadataToken &reader) | ILOpCode.Stfld -> IlOp.UnaryMetadataToken (UnaryMetadataTokenIlOp.Stfld, readMetadataToken &reader) | ILOpCode.Ldsfld -> IlOp.UnaryMetadataToken (UnaryMetadataTokenIlOp.Ldsfld, readMetadataToken &reader) | ILOpCode.Ldsflda -> IlOp.UnaryMetadataToken (UnaryMetadataTokenIlOp.Ldsflda, readMetadataToken &reader) | ILOpCode.Stsfld -> IlOp.UnaryMetadataToken (UnaryMetadataTokenIlOp.Stsfld, readMetadataToken &reader) | ILOpCode.Stobj -> IlOp.UnaryMetadataToken (UnaryMetadataTokenIlOp.Stobj, readMetadataToken &reader) | ILOpCode.Conv_ovf_i_un -> IlOp.Nullary NullaryIlOp.Conv_ovf_i_un | ILOpCode.Conv_ovf_i1_un -> IlOp.Nullary NullaryIlOp.Conv_ovf_i1_un | ILOpCode.Conv_ovf_i2_un -> IlOp.Nullary NullaryIlOp.Conv_ovf_i2_un | ILOpCode.Conv_ovf_i4_un -> IlOp.Nullary NullaryIlOp.Conv_ovf_i4_un | ILOpCode.Conv_ovf_i8_un -> IlOp.Nullary NullaryIlOp.Conv_ovf_i8_un | ILOpCode.Conv_ovf_u_un -> IlOp.Nullary NullaryIlOp.Conv_ovf_u_un | ILOpCode.Conv_ovf_u1_un -> IlOp.Nullary NullaryIlOp.Conv_ovf_u1_un | ILOpCode.Conv_ovf_u2_un -> IlOp.Nullary NullaryIlOp.Conv_ovf_u2_un | ILOpCode.Conv_ovf_u4_un -> IlOp.Nullary NullaryIlOp.Conv_ovf_u4_un | ILOpCode.Conv_ovf_u8_un -> IlOp.Nullary NullaryIlOp.Conv_ovf_u8_un | ILOpCode.Box -> IlOp.UnaryMetadataToken (UnaryMetadataTokenIlOp.Box, readMetadataToken &reader) | ILOpCode.Newarr -> IlOp.UnaryMetadataToken (UnaryMetadataTokenIlOp.Newarr, readMetadataToken &reader) | ILOpCode.Ldlen -> IlOp.Nullary NullaryIlOp.LdLen | ILOpCode.Ldelema -> IlOp.UnaryMetadataToken (UnaryMetadataTokenIlOp.Ldelema, readMetadataToken &reader) | ILOpCode.Ldelem_i1 -> IlOp.Nullary NullaryIlOp.Ldelem_i1 | ILOpCode.Ldelem_u1 -> IlOp.Nullary NullaryIlOp.Ldelem_u1 | ILOpCode.Ldelem_i2 -> IlOp.Nullary NullaryIlOp.Ldelem_i2 | ILOpCode.Ldelem_u2 -> IlOp.Nullary NullaryIlOp.Ldelem_u2 | ILOpCode.Ldelem_i4 -> IlOp.Nullary NullaryIlOp.Ldelem_i4 | ILOpCode.Ldelem_u4 -> IlOp.Nullary NullaryIlOp.Ldelem_u4 | ILOpCode.Ldelem_i8 -> IlOp.Nullary NullaryIlOp.Ldelem_i8 | ILOpCode.Ldelem_i -> IlOp.Nullary NullaryIlOp.Ldelem_i | ILOpCode.Ldelem_r4 -> IlOp.Nullary NullaryIlOp.Ldelem_r4 | ILOpCode.Ldelem_r8 -> IlOp.Nullary NullaryIlOp.Ldelem_r8 | ILOpCode.Ldelem_ref -> IlOp.Nullary NullaryIlOp.Ldelem_ref | ILOpCode.Stelem_i -> IlOp.Nullary NullaryIlOp.Stelem_i | ILOpCode.Stelem_i1 -> IlOp.Nullary NullaryIlOp.Stelem_i1 | ILOpCode.Stelem_i2 -> IlOp.Nullary NullaryIlOp.Stelem_i2 | ILOpCode.Stelem_i4 -> IlOp.Nullary NullaryIlOp.Stelem_i4 | ILOpCode.Stelem_i8 -> IlOp.Nullary NullaryIlOp.Stelem_i8 | ILOpCode.Stelem_r4 -> IlOp.Nullary NullaryIlOp.Stelem_r4 | ILOpCode.Stelem_r8 -> IlOp.Nullary NullaryIlOp.Stelem_r8 | ILOpCode.Stelem_ref -> IlOp.Nullary NullaryIlOp.Stelem_ref | ILOpCode.Ldelem -> IlOp.UnaryMetadataToken (UnaryMetadataTokenIlOp.Ldelem, readMetadataToken &reader) | ILOpCode.Stelem -> IlOp.UnaryMetadataToken (UnaryMetadataTokenIlOp.Stelem, readMetadataToken &reader) | ILOpCode.Unbox_any -> IlOp.UnaryMetadataToken (UnaryMetadataTokenIlOp.Unbox_Any, readMetadataToken &reader) | ILOpCode.Conv_ovf_i1 -> IlOp.Nullary NullaryIlOp.Conv_ovf_i1 | ILOpCode.Conv_ovf_u1 -> IlOp.Nullary NullaryIlOp.Conv_ovf_u1 | ILOpCode.Conv_ovf_i2 -> IlOp.Nullary NullaryIlOp.Conv_ovf_i2 | ILOpCode.Conv_ovf_u2 -> IlOp.Nullary NullaryIlOp.Conv_ovf_u2 | ILOpCode.Conv_ovf_i4 -> IlOp.Nullary NullaryIlOp.Conv_ovf_i4 | ILOpCode.Conv_ovf_u4 -> IlOp.Nullary NullaryIlOp.Conv_ovf_u4 | ILOpCode.Conv_ovf_i8 -> IlOp.Nullary NullaryIlOp.Conv_ovf_i8 | ILOpCode.Conv_ovf_u8 -> IlOp.Nullary NullaryIlOp.Conv_ovf_u8 | ILOpCode.Refanyval -> IlOp.UnaryMetadataToken (UnaryMetadataTokenIlOp.Refanyval, readMetadataToken &reader) | ILOpCode.Ckfinite -> IlOp.Nullary NullaryIlOp.Ckfinite | ILOpCode.Mkrefany -> IlOp.UnaryMetadataToken (UnaryMetadataTokenIlOp.Mkrefany, readMetadataToken &reader) | ILOpCode.Ldtoken -> IlOp.UnaryMetadataToken (UnaryMetadataTokenIlOp.Ldtoken, readMetadataToken &reader) | ILOpCode.Conv_u2 -> IlOp.Nullary NullaryIlOp.Conv_U2 | ILOpCode.Conv_u1 -> IlOp.Nullary NullaryIlOp.Conv_U1 | ILOpCode.Conv_i -> IlOp.Nullary NullaryIlOp.Conv_I | ILOpCode.Conv_ovf_i -> IlOp.Nullary NullaryIlOp.Conv_ovf_i | ILOpCode.Conv_ovf_u -> IlOp.Nullary NullaryIlOp.Conv_ovf_u | ILOpCode.Add_ovf -> IlOp.Nullary NullaryIlOp.Add_ovf | ILOpCode.Add_ovf_un -> IlOp.Nullary NullaryIlOp.Add_ovf_un | ILOpCode.Mul_ovf -> IlOp.Nullary NullaryIlOp.Mul_ovf | ILOpCode.Mul_ovf_un -> IlOp.Nullary NullaryIlOp.Mul_ovf_un | ILOpCode.Sub_ovf -> IlOp.Nullary NullaryIlOp.Sub_ovf | ILOpCode.Sub_ovf_un -> IlOp.Nullary NullaryIlOp.Sub_ovf_un | ILOpCode.Endfinally -> IlOp.Nullary NullaryIlOp.Endfinally | ILOpCode.Leave -> IlOp.UnaryConst (UnaryConstIlOp.Leave (reader.ReadInt32 ())) | ILOpCode.Leave_s -> IlOp.UnaryConst (UnaryConstIlOp.Leave_s (reader.ReadSByte ())) | ILOpCode.Stind_i -> IlOp.Nullary NullaryIlOp.Stind_I | ILOpCode.Conv_u -> IlOp.Nullary NullaryIlOp.Conv_U | ILOpCode.Arglist -> IlOp.Nullary NullaryIlOp.Arglist | ILOpCode.Ceq -> IlOp.Nullary NullaryIlOp.Ceq | ILOpCode.Cgt -> IlOp.Nullary NullaryIlOp.Cgt | ILOpCode.Cgt_un -> IlOp.Nullary NullaryIlOp.Cgt_un | ILOpCode.Clt -> IlOp.Nullary NullaryIlOp.Clt | ILOpCode.Clt_un -> IlOp.Nullary NullaryIlOp.Clt_un | ILOpCode.Ldftn -> IlOp.UnaryMetadataToken (UnaryMetadataTokenIlOp.Ldftn, readMetadataToken &reader) | ILOpCode.Ldvirtftn -> IlOp.UnaryMetadataToken (UnaryMetadataTokenIlOp.Ldvirtftn, readMetadataToken &reader) | ILOpCode.Ldarg -> IlOp.UnaryConst (UnaryConstIlOp.Ldarg (reader.ReadUInt16 ())) | ILOpCode.Ldarga -> IlOp.UnaryConst (UnaryConstIlOp.Ldarga (reader.ReadUInt16 ())) | ILOpCode.Starg -> IlOp.UnaryConst (UnaryConstIlOp.Starg (reader.ReadUInt16 ())) | ILOpCode.Ldloc -> IlOp.UnaryConst (UnaryConstIlOp.Ldloc (reader.ReadUInt16 ())) | ILOpCode.Ldloca -> IlOp.UnaryConst (UnaryConstIlOp.Ldloca (reader.ReadUInt16 ())) | ILOpCode.Stloc -> IlOp.UnaryConst (UnaryConstIlOp.Stloc (reader.ReadUInt16 ())) | ILOpCode.Localloc -> IlOp.Nullary NullaryIlOp.Localloc | ILOpCode.Endfilter -> IlOp.Nullary NullaryIlOp.Endfilter | ILOpCode.Unaligned -> IlOp.UnaryConst (UnaryConstIlOp.Unaligned (reader.ReadByte ())) | ILOpCode.Volatile -> IlOp.Nullary NullaryIlOp.Volatile | ILOpCode.Tail -> IlOp.Nullary NullaryIlOp.Tail | ILOpCode.Initobj -> IlOp.UnaryMetadataToken (UnaryMetadataTokenIlOp.Initobj, readMetadataToken &reader) | ILOpCode.Constrained -> IlOp.UnaryMetadataToken (UnaryMetadataTokenIlOp.Constrained, readMetadataToken &reader) | ILOpCode.Cpblk -> IlOp.Nullary NullaryIlOp.Cpblk | ILOpCode.Initblk -> IlOp.Nullary NullaryIlOp.Initblk | ILOpCode.Rethrow -> IlOp.Nullary NullaryIlOp.Rethrow | ILOpCode.Sizeof -> IlOp.UnaryMetadataToken (UnaryMetadataTokenIlOp.Sizeof, readMetadataToken &reader) | ILOpCode.Refanytype -> IlOp.Nullary NullaryIlOp.Refanytype | ILOpCode.Readonly -> IlOp.Nullary NullaryIlOp.Readonly | i -> failwithf "Unknown opcode: %A" i readInstructions ((opCode, offset) :: acc) let instructions = readInstructions [] let er = methodBody.ExceptionRegions |> Seq.map ExceptionRegion.OfExceptionRegion |> ImmutableArray.CreateRange { Instructions = instructions LocalInit = methodBody.LocalVariablesInitialized LocalSig = localSig MaxStackSize = methodBody.MaxStack ExceptionRegions = er } |> Some let read (loggerFactory : ILoggerFactory) (peReader : PEReader) (metadataReader : MetadataReader) (methodHandle : MethodDefinitionHandle) : MethodInfo option = let logger = loggerFactory.CreateLogger "MethodInfo" let assemblyName = metadataReader.GetAssemblyDefinition().GetAssemblyName () let methodDef = metadataReader.GetMethodDefinition methodHandle let methodName = metadataReader.GetString methodDef.Name let methodSig = methodDef.DecodeSignature (TypeDefn.typeProvider assemblyName, ()) let implAttrs = methodDef.ImplAttributes let methodBody = if implAttrs.HasFlag MethodImplAttributes.InternalCall || implAttrs.HasFlag MethodImplAttributes.Runtime then None elif methodDef.Attributes.HasFlag MethodAttributes.PinvokeImpl then None else match readMethodBody peReader metadataReader assemblyName methodDef with | None -> logger.LogTrace $"no method body in {assemblyName.Name} {methodName}" None | Some body -> { MethodInstructions.Instructions = body.Instructions Locations = body.Instructions |> List.map (fun (a, b) -> b, a) |> Map.ofList LocalsInit = body.LocalInit LocalVars = body.LocalSig ExceptionRegions = body.ExceptionRegions } |> Some let declaringType = methodDef.GetDeclaringType () let declaringDefn = metadataReader.GetTypeDefinition (declaringType) let declaringTypeNamespace = metadataReader.GetString declaringDefn.Namespace let declaringTypeName = metadataReader.GetString declaringDefn.Name let declaringTypeGenericParams = metadataReader.GetTypeDefinition(declaringType).GetGenericParameters () |> GenericParameter.readAll metadataReader let attrs = let result = ImmutableArray.CreateBuilder () let attrs = methodDef.GetCustomAttributes () for attr in attrs do metadataReader.GetCustomAttribute attr |> CustomAttribute.make attr |> result.Add result.ToImmutable () let typeSig = TypeMethodSignature.make methodSig let methodParams = Parameter.readAll metadataReader (methodDef.GetParameters ()) let methodGenericParams = GenericParameter.readAll metadataReader (methodDef.GetGenericParameters ()) let declaringType = ConcreteType.make assemblyName declaringType declaringTypeNamespace declaringTypeName declaringTypeGenericParams { DeclaringType = declaringType Handle = methodHandle Name = methodName Instructions = methodBody Parameters = methodParams Generics = methodGenericParams Signature = typeSig RawSignature = typeSig MethodAttributes = methodDef.Attributes CustomAttributes = attrs IsStatic = not methodSig.Header.IsInstance ImplAttributes = implAttrs } |> Some let rec resolveBaseType (methodGenerics : TypeDefn ImmutableArray option) (executingMethod : MethodInfo) (td : TypeDefn) : ResolvedBaseType = match td with | TypeDefn.Void -> failwith "Void isn't a type that appears at runtime and has no base type" | TypeDefn.PrimitiveType ty -> match ty with | PrimitiveType.SByte | PrimitiveType.Byte | PrimitiveType.Int16 | PrimitiveType.UInt16 | PrimitiveType.Int32 | PrimitiveType.UInt32 | PrimitiveType.Int64 | PrimitiveType.UInt64 | PrimitiveType.Single | PrimitiveType.Double | PrimitiveType.Char | PrimitiveType.Boolean -> ResolvedBaseType.ValueType | PrimitiveType.String -> ResolvedBaseType.Object | PrimitiveType.TypedReference -> failwith "todo" | PrimitiveType.IntPtr -> failwith "todo" | PrimitiveType.UIntPtr -> failwith "todo" | PrimitiveType.Object -> failwith "todo" | TypeDefn.Array (elt, shape) -> failwith "todo" | TypeDefn.Pinned typeDefn -> failwith "todo" | TypeDefn.Pointer typeDefn -> failwith "todo" | TypeDefn.Byref typeDefn -> failwith "todo" | TypeDefn.OneDimensionalArrayLowerBoundZero elements -> failwith "todo" | TypeDefn.Modified (original, afterMod, modificationRequired) -> failwith "todo" | TypeDefn.FromReference (typeRef, signatureTypeKind) -> failwith "todo" | TypeDefn.FromDefinition (comparableTypeDefinitionHandle, _, signatureTypeKind) -> failwith "todo" | TypeDefn.GenericInstantiation (generic, args) -> failwith "todo" | TypeDefn.FunctionPointer typeMethodSignature -> failwith "todo" | TypeDefn.GenericTypeParameter index -> resolveBaseType methodGenerics executingMethod executingMethod.DeclaringType.Generics.[index] | TypeDefn.GenericMethodParameter index -> match methodGenerics with | None -> failwith "unexpectedly asked for a generic method parameter when we had none" | Some generics -> resolveBaseType methodGenerics executingMethod generics.[index]