Add method generics to type resolution (#48)

This commit is contained in:
Patrick Stevens
2025-06-15 18:42:37 +01:00
committed by GitHub
parent edbf3b71e5
commit a0ee1f9713
11 changed files with 92 additions and 36 deletions

View File

@@ -61,7 +61,11 @@ type DumpedAssembly =
/// <summary>
/// Dictionary of all method definitions in this assembly, keyed by their handle.
/// </summary>
Methods : IReadOnlyDictionary<MethodDefinitionHandle, WoofWare.PawPrint.MethodInfo<FakeUnit>>
Methods :
IReadOnlyDictionary<
MethodDefinitionHandle,
WoofWare.PawPrint.MethodInfo<FakeUnit, WoofWare.PawPrint.GenericParameter>
>
/// <summary>
/// Dictionary of all member references in this assembly, keyed by their handle.

View File

@@ -145,7 +145,8 @@ type MethodInstructions =
/// Represents detailed information about a method in a .NET assembly.
/// This is a strongly-typed representation of MethodDefinition from System.Reflection.Metadata.
/// </summary>
type MethodInfo<'typeGenerics when 'typeGenerics :> IComparable<'typeGenerics> and 'typeGenerics : comparison> =
type MethodInfo<'typeGenerics, 'methodGenerics
when 'typeGenerics :> IComparable<'typeGenerics> and 'typeGenerics : comparison> =
{
/// <summary>
/// The type that declares this method, along with its assembly information.
@@ -175,7 +176,7 @@ type MethodInfo<'typeGenerics when 'typeGenerics :> IComparable<'typeGenerics> a
/// <summary>
/// The generic type parameters defined by this method, if any.
/// </summary>
Generics : GenericParameter ImmutableArray
Generics : 'methodGenerics ImmutableArray
/// <summary>
/// The signature of the method, including return type and parameter types.
@@ -215,11 +216,11 @@ type MethodInfo<'typeGenerics when 'typeGenerics :> IComparable<'typeGenerics> a
[<RequireQualifiedAccess>]
module MethodInfo =
let mapTypeGenerics<'a, 'b
let mapTypeGenerics<'a, 'b, 'methodGen
when 'a :> IComparable<'a> and 'a : comparison and 'b : comparison and 'b :> IComparable<'b>>
(f : int -> 'a -> 'b)
(m : MethodInfo<'a>)
: MethodInfo<'b>
(m : MethodInfo<'a, 'methodGen>)
: MethodInfo<'b, 'methodGen>
=
{
DeclaringType = m.DeclaringType |> ConcreteType.mapGeneric f
@@ -235,6 +236,26 @@ module MethodInfo =
IsStatic = m.IsStatic
}
let mapMethodGenerics<'a, 'b, 'typeGen when 'typeGen :> IComparable<'typeGen> and 'typeGen : comparison>
(f : int -> 'a -> 'b)
(m : MethodInfo<'typeGen, 'a>)
: MethodInfo<'typeGen, 'b>
=
{
DeclaringType = m.DeclaringType
Handle = m.Handle
Name = m.Name
Instructions = m.Instructions
Parameters = m.Parameters
Generics = m.Generics |> Seq.mapi f |> ImmutableArray.CreateRange
Signature = m.Signature
CustomAttributes = m.CustomAttributes
MethodAttributes = m.MethodAttributes
ImplAttributes = m.ImplAttributes
IsStatic = m.IsStatic
}
type private Dummy = class end
type private MethodBody =
@@ -582,7 +603,7 @@ module MethodInfo =
(peReader : PEReader)
(metadataReader : MetadataReader)
(methodHandle : MethodDefinitionHandle)
: MethodInfo<FakeUnit> option
: MethodInfo<FakeUnit, GenericParameter> option
=
let logger = loggerFactory.CreateLogger "MethodInfo"
let assemblyName = metadataReader.GetAssemblyDefinition().GetAssemblyName ()
@@ -660,7 +681,7 @@ module MethodInfo =
let rec resolveBaseType
(methodGenerics : TypeDefn ImmutableArray option)
(executingMethod : MethodInfo<TypeDefn>)
(executingMethod : MethodInfo<TypeDefn, 'methodGen>)
(td : TypeDefn)
: ResolvedBaseType
=

View File

@@ -34,7 +34,7 @@ type TypeInfo<'generic> =
/// <summary>
/// All methods defined within this type.
/// </summary>
Methods : WoofWare.PawPrint.MethodInfo<FakeUnit> list
Methods : WoofWare.PawPrint.MethodInfo<FakeUnit, WoofWare.PawPrint.GenericParameter> list
/// <summary>
/// Method implementation mappings for this type, often used for interface implementations
@@ -128,6 +128,7 @@ type BaseClassTypes<'corelib> =
RuntimeMethodHandle : TypeInfo<WoofWare.PawPrint.GenericParameter>
RuntimeFieldHandle : TypeInfo<WoofWare.PawPrint.GenericParameter>
RuntimeTypeHandle : TypeInfo<WoofWare.PawPrint.GenericParameter>
RuntimeType : TypeInfo<WoofWare.PawPrint.GenericParameter>
}
[<RequireQualifiedAccess>]

View File

@@ -57,7 +57,7 @@ type CliNumericType =
| Float32 of float32
| Float64 of float
/// Not a real CLI numeric type! Represents an int64 obtained by taking a NativeInt from the eval stack.
| ProvenanceTrackedNativeInt64 of MethodInfo<FakeUnit>
| ProvenanceTrackedNativeInt64 of MethodInfo<FakeUnit, WoofWare.PawPrint.GenericParameter>
type CliValueType =
private

View File

@@ -104,6 +104,11 @@ module Corelib =
|> Seq.choose (fun (KeyValue (_, v)) -> if v.Name = "RuntimeTypeHandle" then Some v else None)
|> Seq.exactlyOne
let runtimeTypeType =
corelib.TypeDefs
|> Seq.choose (fun (KeyValue (_, v)) -> if v.Name = "RuntimeType" then Some v else None)
|> Seq.exactlyOne
let runtimeFieldHandleType =
corelib.TypeDefs
|> Seq.choose (fun (KeyValue (_, v)) -> if v.Name = "RuntimeFieldHandle" then Some v else None)
@@ -132,4 +137,5 @@ module Corelib =
RuntimeTypeHandle = runtimeTypeHandleType
RuntimeMethodHandle = runtimeMethodHandleType
RuntimeFieldHandle = runtimeFieldHandleType
RuntimeType = runtimeTypeType
}

View File

@@ -16,7 +16,7 @@ type ManagedPointerSource =
[<RequireQualifiedAccess>]
type NativeIntSource =
| Verbatim of int64
| FunctionPointer of MethodInfo<FakeUnit>
| FunctionPointer of MethodInfo<FakeUnit, GenericParameter>
override this.ToString () : string =
match this with

View File

@@ -5,7 +5,7 @@ open System.Collections.Immutable
/// Represents a location in the code where an exception occurred
type ExceptionStackFrame =
{
Method : WoofWare.PawPrint.MethodInfo<TypeDefn>
Method : WoofWare.PawPrint.MethodInfo<TypeDefn, TypeDefn>
/// The number of bytes into the IL of the method we were in
IlOffset : int
}
@@ -44,7 +44,7 @@ module ExceptionHandling =
let findExceptionHandler
(currentPC : int)
(exceptionTypeCrate : TypeInfoCrate)
(method : WoofWare.PawPrint.MethodInfo<TypeDefn>)
(method : WoofWare.PawPrint.MethodInfo<TypeDefn, 'methodGeneric>)
(assemblies : ImmutableDictionary<string, DumpedAssembly>)
: (WoofWare.PawPrint.ExceptionRegion * bool) option // handler, isFinally
=
@@ -92,7 +92,7 @@ module ExceptionHandling =
let findFinallyBlocksToRun
(currentPC : int)
(targetPC : int)
(method : WoofWare.PawPrint.MethodInfo<TypeDefn>)
(method : WoofWare.PawPrint.MethodInfo<TypeDefn, 'methodGeneric>)
: ExceptionOffset list
=
match method.Instructions with
@@ -122,7 +122,7 @@ module ExceptionHandling =
/// Get the active exception regions at a given offset
let getActiveRegionsAtOffset
(offset : int)
(method : WoofWare.PawPrint.MethodInfo<TypeDefn>)
(method : WoofWare.PawPrint.MethodInfo<TypeDefn, 'methodGeneric>)
: WoofWare.PawPrint.ExceptionRegion list
=
match method.Instructions with

View File

@@ -1,6 +1,5 @@
namespace WoofWare.PawPrint
open System
open System.Collections.Immutable
open System.IO
open System.Reflection
@@ -246,11 +245,11 @@ module IlMachineState =
(loggerFactory : ILoggerFactory)
(referencedInAssembly : DumpedAssembly)
(target : TypeRef)
(genericArgs : ImmutableArray<TypeDefn> option)
(typeGenericArgs : ImmutableArray<TypeDefn> option)
(state : IlMachineState)
: IlMachineState * DumpedAssembly * WoofWare.PawPrint.TypeInfo<TypeDefn>
=
match Assembly.resolveTypeRef state._LoadedAssemblies referencedInAssembly target genericArgs with
match Assembly.resolveTypeRef state._LoadedAssemblies referencedInAssembly target typeGenericArgs with
| TypeResolutionResult.Resolved (assy, typeDef) -> state, assy, typeDef
| TypeResolutionResult.FirstLoadAssy loadFirst ->
let state, _, _ =
@@ -260,7 +259,7 @@ module IlMachineState =
(fst loadFirst.Handle)
state
resolveTypeFromRef loggerFactory referencedInAssembly target genericArgs state
resolveTypeFromRef loggerFactory referencedInAssembly target typeGenericArgs state
and resolveType
(loggerFactory : ILoggerFactory)
@@ -278,7 +277,8 @@ module IlMachineState =
(loggerFactory : ILoggerFactory)
(corelib : BaseClassTypes<DumpedAssembly>)
(ty : TypeDefn)
(genericArgs : ImmutableArray<TypeDefn> option)
(typeGenericArgs : ImmutableArray<TypeDefn> option)
(methodGenericArgs : ImmutableArray<TypeDefn>)
(assy : DumpedAssembly)
(state : IlMachineState)
: IlMachineState * DumpedAssembly * WoofWare.PawPrint.TypeInfo<TypeDefn>
@@ -291,7 +291,7 @@ module IlMachineState =
(state, args)
||> Seq.fold (fun state arg ->
let state, assy, arg =
resolveTypeFromDefn loggerFactory corelib arg genericArgs assy state
resolveTypeFromDefn loggerFactory corelib arg typeGenericArgs methodGenericArgs assy state
let baseType =
arg.BaseType
@@ -316,21 +316,23 @@ module IlMachineState =
)
let args' = args'.ToImmutable ()
resolveTypeFromDefn loggerFactory corelib generic (Some args') assy state
resolveTypeFromDefn loggerFactory corelib generic (Some args') methodGenericArgs assy state
| TypeDefn.FromDefinition (defn, assy, _typeKind) ->
let assy = state._LoadedAssemblies.[assy]
let defn =
assy.TypeDefs.[defn.Get]
|> TypeInfo.mapGeneric (fun _ param ->
match genericArgs with
| None -> failwith "somehow got a generic TypeDefn.FromDefinition without any generic args"
match typeGenericArgs with
| None -> failwith "somehow got a generic TypeDefn.FromDefinition without any type generic args"
| Some genericArgs -> genericArgs.[param.SequenceNumber]
)
state, assy, defn
| TypeDefn.FromReference (ref, _typeKind) ->
let state, assy, ty = resolveTypeFromRef loggerFactory assy ref genericArgs state
let state, assy, ty =
resolveTypeFromRef loggerFactory assy ref typeGenericArgs state
state, assy, ty
| TypeDefn.PrimitiveType prim ->
let ty =
@@ -356,12 +358,16 @@ module IlMachineState =
state, corelib.Corelib, ty
| TypeDefn.GenericTypeParameter param ->
match genericArgs with
match typeGenericArgs with
| None -> failwith "tried to resolve generic parameter without generic args"
| Some genericArgs ->
let arg = genericArgs.[param]
// TODO: this assembly is probably wrong?
resolveTypeFromDefn loggerFactory corelib arg (Some genericArgs) assy state
resolveTypeFromDefn loggerFactory corelib arg (Some genericArgs) methodGenericArgs assy state
| TypeDefn.GenericMethodParameter param ->
let arg = methodGenericArgs.[param]
// TODO: this assembly is probably wrong?
resolveTypeFromDefn loggerFactory corelib arg typeGenericArgs methodGenericArgs assy state
| s -> failwith $"TODO: resolveTypeFromDefn unimplemented for {s}"
let resolveTypeFromSpec
@@ -370,10 +376,12 @@ module IlMachineState =
(ty : TypeSpecificationHandle)
(assy : DumpedAssembly)
(typeGenericArgs : TypeDefn ImmutableArray option)
(methodGenericArgs : TypeDefn ImmutableArray)
(state : IlMachineState)
: IlMachineState * DumpedAssembly * WoofWare.PawPrint.TypeInfo<TypeDefn>
=
resolveTypeFromDefn loggerFactory corelib assy.TypeSpecs.[ty].Signature typeGenericArgs assy state
let sign = assy.TypeSpecs.[ty].Signature
resolveTypeFromDefn loggerFactory corelib sign typeGenericArgs methodGenericArgs assy state
let rec cliTypeZeroOf
(loggerFactory : ILoggerFactory)
@@ -400,7 +408,7 @@ module IlMachineState =
(wasConstructing : ManagedHeapAddress option)
(wasClassConstructor : bool)
(methodGenerics : ImmutableArray<TypeDefn> option)
(methodToCall : WoofWare.PawPrint.MethodInfo<TypeDefn>)
(methodToCall : WoofWare.PawPrint.MethodInfo<TypeDefn, WoofWare.PawPrint.GenericParameter>)
(thread : ThreadId)
(threadState : ThreadState)
(state : IlMachineState)
@@ -431,6 +439,10 @@ module IlMachineState =
let activeMethodState = threadState.MethodStates.[threadState.ActiveMethodState]
let methodToCall =
methodToCall
|> MethodInfo.mapMethodGenerics (fun _ param -> methodGenerics.Value.[param.SequenceNumber])
let state, newFrame, oldFrame =
if methodToCall.IsStatic then
let args = ImmutableArray.CreateBuilder methodToCall.Parameters.Length
@@ -672,7 +684,9 @@ module IlMachineState =
let currentThreadState = state.ThreadState.[currentThread]
let cctorMethod =
cctorMethod |> MethodInfo.mapTypeGenerics (fun i _ -> ty.Generics.[i])
cctorMethod
|> MethodInfo.mapTypeGenerics (fun i _ -> ty.Generics.[i])
|> MethodInfo.mapMethodGenerics (fun _ -> failwith "cctor cannot be generic")
callMethod
loggerFactory
@@ -723,7 +737,7 @@ module IlMachineState =
(corelib : BaseClassTypes<DumpedAssembly>)
(thread : ThreadId)
(methodGenerics : TypeDefn ImmutableArray option)
(methodToCall : WoofWare.PawPrint.MethodInfo<TypeDefn>)
(methodToCall : WoofWare.PawPrint.MethodInfo<TypeDefn, WoofWare.PawPrint.GenericParameter>)
(weAreConstructingObj : ManagedHeapAddress option)
(state : IlMachineState)
: IlMachineState * WhatWeDid
@@ -977,7 +991,10 @@ module IlMachineState =
(state : IlMachineState)
: IlMachineState *
AssemblyName *
Choice<WoofWare.PawPrint.MethodInfo<TypeDefn>, WoofWare.PawPrint.FieldInfo<TypeDefn>>
Choice<
WoofWare.PawPrint.MethodInfo<TypeDefn, WoofWare.PawPrint.GenericParameter>,
WoofWare.PawPrint.FieldInfo<TypeDefn>
>
=
// TODO: do we need to initialise the parent class here?
let mem = assy.Members.[m]
@@ -988,12 +1005,16 @@ module IlMachineState =
match mem.Parent with
| MetadataToken.TypeReference parent -> resolveType loggerFactory parent None assy state
| MetadataToken.TypeSpecification parent ->
let executing = state.ThreadState.[currentThread].MethodState.ExecutingMethod
let typeGenerics =
match state.ThreadState.[currentThread].MethodState.ExecutingMethod.DeclaringType.Generics with
match executing.DeclaringType.Generics with
| [] -> None
| l -> Some (ImmutableArray.CreateRange l)
resolveTypeFromSpec loggerFactory corelib parent assy typeGenerics state
let methodGenerics = executing.Generics
resolveTypeFromSpec loggerFactory corelib parent assy typeGenerics methodGenerics state
| parent -> failwith $"Unexpected: {parent}"
match mem.Signature with

View File

@@ -19,7 +19,7 @@ and MethodState =
_IlOpIndex : int
EvaluationStack : EvalStack
Arguments : CliType ImmutableArray
ExecutingMethod : WoofWare.PawPrint.MethodInfo<TypeDefn>
ExecutingMethod : WoofWare.PawPrint.MethodInfo<TypeDefn, TypeDefn>
/// We don't implement the local memory pool right now
LocalMemoryPool : unit
/// On return, we restore this state. This should be Some almost always; an exception is the entry point.
@@ -138,7 +138,7 @@ and MethodState =
(corelib : BaseClassTypes<DumpedAssembly>)
(loadedAssemblies : ImmutableDictionary<string, DumpedAssembly>)
(containingAssembly : DumpedAssembly)
(method : WoofWare.PawPrint.MethodInfo<TypeDefn>)
(method : WoofWare.PawPrint.MethodInfo<TypeDefn, TypeDefn>)
(methodGenerics : ImmutableArray<TypeDefn> option)
(args : ImmutableArray<CliType>)
(returnState : MethodReturnState option)

View File

@@ -68,6 +68,7 @@ module Program =
let mainMethod =
mainMethod
|> MethodInfo.mapTypeGenerics (fun _ -> failwith "Refusing to execute generic main method")
|> MethodInfo.mapMethodGenerics (fun _ -> failwith "Refusing to execute generic main method")
let rec computeState (baseClassTypes : BaseClassTypes<DumpedAssembly> option) (state : IlMachineState) =
// The thread's state is slightly fake: we will need to put arguments onto the stack before actually

View File

@@ -681,6 +681,7 @@ module internal UnaryMetadataIlOp =
spec
assy
declaringTypeGenerics
currentMethod.Generics
state
| x -> failwith $"TODO: Stelem element type resolution unimplemented for {x}"
@@ -754,6 +755,7 @@ module internal UnaryMetadataIlOp =
spec
assy
declaringTypeGenerics
currentMethod.Generics
state
| x -> failwith $"TODO: Ldelem element type resolution unimplemented for {x}"