mirror of
https://github.com/Smaug123/WoofWare.PawPrint
synced 2025-10-05 14:18:40 +00:00
Track generics at runtime (#32)
This commit is contained in:
@@ -5,6 +5,7 @@ open System.IO
|
||||
open FsUnitTyped
|
||||
open NUnit.Framework
|
||||
open WoofWare.PawPrint
|
||||
open WoofWare.PawPrint.ExternImplementations
|
||||
open WoofWare.PawPrint.Test
|
||||
open WoofWare.DotnetRuntimeLocator
|
||||
|
||||
@@ -21,7 +22,7 @@ module TestBasicLock =
|
||||
let dotnetRuntimes =
|
||||
DotnetRuntime.SelectForDll assy.Location |> ImmutableArray.CreateRange
|
||||
|
||||
let impls = MockEnv.make ()
|
||||
let impls = NativeImpls.PassThru ()
|
||||
|
||||
use peImage = new MemoryStream (image)
|
||||
|
||||
|
@@ -41,7 +41,7 @@ module TestCases =
|
||||
{
|
||||
FileName = "WriteLine.cs"
|
||||
ExpectedReturnCode = 1
|
||||
NativeImpls = MockEnv.make ()
|
||||
NativeImpls = NativeImpls.PassThru ()
|
||||
LocalVariablesOfMain = []
|
||||
}
|
||||
]
|
||||
|
@@ -6,6 +6,7 @@ open System.IO
|
||||
open FsUnitTyped
|
||||
open NUnit.Framework
|
||||
open WoofWare.PawPrint
|
||||
open WoofWare.PawPrint.ExternImplementations
|
||||
open WoofWare.PawPrint.Test
|
||||
open WoofWare.DotnetRuntimeLocator
|
||||
|
||||
@@ -15,14 +16,14 @@ module TestHelloWorld =
|
||||
|
||||
[<Test ; Explicit "This test doesn't run yet">]
|
||||
let ``Can run Hello World`` () : unit =
|
||||
let source = Assembly.getEmbeddedResourceAsString "HelloWorld.cs" assy
|
||||
let source = Assembly.getEmbeddedResourceAsString "WriteLine.cs" assy
|
||||
let image = Roslyn.compile [ source ]
|
||||
let messages, loggerFactory = LoggerFactory.makeTest ()
|
||||
|
||||
let dotnetRuntimes =
|
||||
DotnetRuntime.SelectForDll assy.Location |> ImmutableArray.CreateRange
|
||||
|
||||
let impls = MockEnv.make ()
|
||||
let impls = NativeImpls.PassThru ()
|
||||
|
||||
try
|
||||
use peImage = new MemoryStream (image)
|
||||
|
@@ -22,10 +22,11 @@ module AbstractMachine =
|
||||
match instruction.ExecutingMethod.Instructions with
|
||||
| None ->
|
||||
let targetAssy =
|
||||
state.LoadedAssembly (snd instruction.ExecutingMethod.DeclaringType)
|
||||
state.LoadedAssembly instruction.ExecutingMethod.DeclaringType.Assembly
|
||||
|> Option.get
|
||||
|
||||
let targetType = targetAssy.TypeDefs.[fst instruction.ExecutingMethod.DeclaringType]
|
||||
let targetType =
|
||||
targetAssy.TypeDefs.[instruction.ExecutingMethod.DeclaringType.Definition.Get]
|
||||
|
||||
let outcome =
|
||||
match
|
||||
@@ -80,10 +81,10 @@ module AbstractMachine =
|
||||
| true, executingInstruction ->
|
||||
|
||||
let executingInType =
|
||||
match state.LoadedAssembly (snd instruction.ExecutingMethod.DeclaringType) with
|
||||
match state.LoadedAssembly instruction.ExecutingMethod.DeclaringType.Assembly with
|
||||
| None -> "<unloaded assembly>"
|
||||
| Some assy ->
|
||||
match assy.TypeDefs.TryGetValue (fst instruction.ExecutingMethod.DeclaringType) with
|
||||
match assy.TypeDefs.TryGetValue instruction.ExecutingMethod.DeclaringType.Definition.Get with
|
||||
| true, v -> v.Name
|
||||
| false, _ -> "<unrecognised type>"
|
||||
|
||||
|
@@ -61,7 +61,7 @@ type DumpedAssembly =
|
||||
/// <summary>
|
||||
/// Dictionary of all method definitions in this assembly, keyed by their handle.
|
||||
/// </summary>
|
||||
Methods : IReadOnlyDictionary<MethodDefinitionHandle, WoofWare.PawPrint.MethodInfo>
|
||||
Methods : IReadOnlyDictionary<MethodDefinitionHandle, WoofWare.PawPrint.MethodInfo<FakeUnit>>
|
||||
|
||||
/// <summary>
|
||||
/// Dictionary of all member references in this assembly, keyed by their handle.
|
||||
|
@@ -144,7 +144,7 @@ module CliType =
|
||||
match signatureTypeKind with
|
||||
| SignatureTypeKind.Unknown -> failwith "todo"
|
||||
| SignatureTypeKind.ValueType ->
|
||||
let typeDef = assy.TypeDefs.[typeDefinitionHandle]
|
||||
let typeDef = assy.TypeDefs.[typeDefinitionHandle.Get]
|
||||
|
||||
let fields =
|
||||
typeDef.Fields
|
||||
|
36
WoofWare.PawPrint/ComparableSignatureHeader.fs
Normal file
36
WoofWare.PawPrint/ComparableSignatureHeader.fs
Normal file
@@ -0,0 +1,36 @@
|
||||
namespace WoofWare.PawPrint
|
||||
|
||||
open System
|
||||
open System.Reflection.Metadata
|
||||
|
||||
[<CustomEquality>]
|
||||
[<CustomComparison>]
|
||||
type ComparableSignatureHeader =
|
||||
private
|
||||
{
|
||||
_Inner : SignatureHeader
|
||||
}
|
||||
|
||||
member this.Get = this._Inner
|
||||
|
||||
override this.Equals (other : obj) =
|
||||
match other with
|
||||
| :? ComparableSignatureHeader as other -> this._Inner.RawValue = other._Inner.RawValue
|
||||
| _ -> false
|
||||
|
||||
override this.GetHashCode () = this._Inner.RawValue.GetHashCode ()
|
||||
|
||||
interface IComparable<ComparableSignatureHeader> with
|
||||
member this.CompareTo (other : ComparableSignatureHeader) =
|
||||
this._Inner.RawValue.CompareTo other._Inner.RawValue
|
||||
|
||||
interface IComparable with
|
||||
member this.CompareTo (other : obj) =
|
||||
match other with
|
||||
| :? ComparableSignatureHeader as other -> (this :> IComparable<ComparableSignatureHeader>).CompareTo other
|
||||
| _ -> failwith "invalid comparison"
|
||||
|
||||
static member Make x : ComparableSignatureHeader =
|
||||
{
|
||||
_Inner = x
|
||||
}
|
37
WoofWare.PawPrint/ComparableTypeDefinitionHandle.fs
Normal file
37
WoofWare.PawPrint/ComparableTypeDefinitionHandle.fs
Normal file
@@ -0,0 +1,37 @@
|
||||
namespace WoofWare.PawPrint
|
||||
|
||||
open System
|
||||
open System.Reflection.Metadata
|
||||
|
||||
[<CustomEquality>]
|
||||
[<CustomComparison>]
|
||||
type ComparableTypeDefinitionHandle =
|
||||
private
|
||||
{
|
||||
_Inner : TypeDefinitionHandle
|
||||
}
|
||||
|
||||
override this.Equals (other) =
|
||||
match other with
|
||||
| :? ComparableTypeDefinitionHandle as other -> this._Inner.GetHashCode () = other._Inner.GetHashCode ()
|
||||
| _ -> false
|
||||
|
||||
override this.GetHashCode () : int = this._Inner.GetHashCode ()
|
||||
|
||||
interface IComparable<ComparableTypeDefinitionHandle> with
|
||||
member this.CompareTo (other : ComparableTypeDefinitionHandle) : int =
|
||||
this._Inner.GetHashCode().CompareTo (other._Inner.GetHashCode ())
|
||||
|
||||
interface IComparable with
|
||||
member this.CompareTo (other : obj) : int =
|
||||
match other with
|
||||
| :? ComparableTypeDefinitionHandle as other ->
|
||||
(this :> IComparable<ComparableTypeDefinitionHandle>).CompareTo other
|
||||
| _ -> failwith "invalid comparison"
|
||||
|
||||
static member Make (h : TypeDefinitionHandle) =
|
||||
{
|
||||
_Inner = h
|
||||
}
|
||||
|
||||
member this.Get = this._Inner
|
93
WoofWare.PawPrint/ConcreteType.fs
Normal file
93
WoofWare.PawPrint/ConcreteType.fs
Normal file
@@ -0,0 +1,93 @@
|
||||
namespace WoofWare.PawPrint
|
||||
|
||||
open System
|
||||
open System.Reflection
|
||||
open System.Reflection.Metadata
|
||||
|
||||
type FakeUnit = private | FakeUnit
|
||||
|
||||
/// A type which has been concretised, runtime-representable, etc.
|
||||
[<CustomEquality>]
|
||||
[<CustomComparison>]
|
||||
type ConcreteType<'typeGeneric when 'typeGeneric : comparison and 'typeGeneric :> IComparable<'typeGeneric>> =
|
||||
private
|
||||
{
|
||||
_AssemblyName : AssemblyName
|
||||
_Definition : ComparableTypeDefinitionHandle
|
||||
_Generics : 'typeGeneric list
|
||||
}
|
||||
|
||||
member this.Assembly : AssemblyName = this._AssemblyName
|
||||
member this.Definition : ComparableTypeDefinitionHandle = this._Definition
|
||||
member this.Generics : 'typeGeneric list = this._Generics
|
||||
|
||||
override this.Equals (other : obj) : bool =
|
||||
match other with
|
||||
| :? ConcreteType<'typeGeneric> as other ->
|
||||
this._Generics = other._Generics
|
||||
&& this._Definition = other._Definition
|
||||
&& this._AssemblyName.FullName = other._AssemblyName.FullName
|
||||
| _ -> false
|
||||
|
||||
override this.GetHashCode () : int =
|
||||
hash (this._AssemblyName.FullName, this._Definition, this._Generics)
|
||||
|
||||
interface IComparable<ConcreteType<'typeGeneric>> with
|
||||
member this.CompareTo (other : ConcreteType<'typeGeneric>) : int =
|
||||
let comp = this._AssemblyName.FullName.CompareTo other._AssemblyName.FullName
|
||||
|
||||
if comp = 0 then
|
||||
let comp =
|
||||
(this._Definition :> IComparable<ComparableTypeDefinitionHandle>).CompareTo other._Definition
|
||||
|
||||
if comp = 0 then
|
||||
let thisGen = (this._Generics : 'typeGeneric list) :> IComparable<'typeGeneric list>
|
||||
thisGen.CompareTo other._Generics
|
||||
else
|
||||
comp
|
||||
else
|
||||
comp
|
||||
|
||||
interface IComparable with
|
||||
member this.CompareTo other =
|
||||
match other with
|
||||
| :? ConcreteType<'typeGeneric> as other ->
|
||||
(this :> IComparable<ConcreteType<'typeGeneric>>).CompareTo other
|
||||
| _ -> failwith "bad comparison"
|
||||
|
||||
type RuntimeConcreteType = ConcreteType<TypeDefn>
|
||||
|
||||
[<RequireQualifiedAccess>]
|
||||
module ConcreteType =
|
||||
let make (assemblyName : AssemblyName) (defn : TypeDefinitionHandle) (generics : TypeDefn list) =
|
||||
{
|
||||
_AssemblyName = assemblyName
|
||||
_Definition = ComparableTypeDefinitionHandle.Make defn
|
||||
_Generics = generics
|
||||
}
|
||||
|
||||
let make'
|
||||
(assemblyName : AssemblyName)
|
||||
(defn : TypeDefinitionHandle)
|
||||
(genericParamCount : int)
|
||||
: ConcreteType<FakeUnit>
|
||||
=
|
||||
{
|
||||
_AssemblyName = assemblyName
|
||||
_Definition = ComparableTypeDefinitionHandle.Make defn
|
||||
_Generics = List.replicate genericParamCount FakeUnit.FakeUnit
|
||||
}
|
||||
|
||||
let mapGeneric<'a, 'b
|
||||
when 'a : comparison and 'a :> IComparable<'a> and 'b : equality and 'b : comparison and 'b :> IComparable<'b>>
|
||||
(f : int -> 'a -> 'b)
|
||||
(x : ConcreteType<'a>)
|
||||
: ConcreteType<'b>
|
||||
=
|
||||
let generics = x._Generics |> List.mapi f
|
||||
|
||||
{
|
||||
_AssemblyName = x._AssemblyName
|
||||
_Definition = x._Definition
|
||||
_Generics = generics
|
||||
}
|
@@ -75,7 +75,10 @@ module EvalStackValue =
|
||||
| CliNumericType.Int64 int64 -> failwith "todo"
|
||||
| CliNumericType.NativeInt int64 -> failwith "todo"
|
||||
| CliNumericType.NativeFloat f -> failwith "todo"
|
||||
| CliNumericType.Int8 b -> failwith "todo"
|
||||
| CliNumericType.Int8 _ ->
|
||||
match popped with
|
||||
| EvalStackValue.Int32 i -> CliType.Numeric (CliNumericType.Int8 (i % 256 |> int8))
|
||||
| i -> failwith $"TODO: %O{i}"
|
||||
| CliNumericType.Int16 s -> failwith "todo"
|
||||
| CliNumericType.UInt8 b -> failwith "todo"
|
||||
| CliNumericType.UInt16 s -> failwith "todo"
|
||||
|
@@ -1,5 +1,6 @@
|
||||
namespace WoofWare.PawPrint
|
||||
|
||||
open System
|
||||
open System.Collections.Immutable
|
||||
open System.IO
|
||||
open System.Reflection
|
||||
@@ -21,16 +22,12 @@ type IlMachineState =
|
||||
_LoadedAssemblies : ImmutableDictionary<string, DumpedAssembly>
|
||||
/// Tracks initialization state of types across assemblies
|
||||
TypeInitTable : TypeInitTable
|
||||
Statics : ImmutableDictionary<TypeDefinitionHandle * AssemblyName, ImmutableDictionary<string, CliType>>
|
||||
/// For each type, specialised to each set of generic args, a map of string field name to static value contained therein.
|
||||
Statics : ImmutableDictionary<RuntimeConcreteType, ImmutableDictionary<string, CliType>>
|
||||
DotnetRuntimeDirs : string ImmutableArray
|
||||
}
|
||||
|
||||
member this.SetStatic
|
||||
(ty : TypeDefinitionHandle * AssemblyName)
|
||||
(field : string)
|
||||
(value : CliType)
|
||||
: IlMachineState
|
||||
=
|
||||
member this.SetStatic (ty : RuntimeConcreteType) (field : string) (value : CliType) : IlMachineState =
|
||||
let statics =
|
||||
match this.Statics.TryGetValue ty with
|
||||
| false, _ -> this.Statics.Add (ty, ImmutableDictionary.Create().Add (field, value))
|
||||
@@ -40,31 +37,29 @@ type IlMachineState =
|
||||
Statics = statics
|
||||
}
|
||||
|
||||
member this.WithTypeBeginInit (thread : ThreadId) (handle : TypeDefinitionHandle, assy : AssemblyName) =
|
||||
member this.WithTypeBeginInit (thread : ThreadId) (ty : RuntimeConcreteType) =
|
||||
this.Logger.LogDebug (
|
||||
"Beginning initialisation of type {TypeName}, handle {TypeDefinitionHandle} from assy {AssemblyHash}",
|
||||
this.LoadedAssembly(assy).Value.TypeDefs.[handle].Name,
|
||||
handle.GetHashCode (),
|
||||
assy.GetHashCode ()
|
||||
"Beginning initialisation of type {s_Assembly}.{TypeName}, handle {TypeDefinitionHandle}",
|
||||
ty.Assembly.FullName,
|
||||
this.LoadedAssembly(ty.Assembly).Value.TypeDefs.[ty.Definition.Get].Name,
|
||||
ty.Definition.Get.GetHashCode ()
|
||||
)
|
||||
|
||||
let typeInitTable =
|
||||
this.TypeInitTable |> TypeInitTable.beginInitialising thread (handle, assy)
|
||||
let typeInitTable = this.TypeInitTable |> TypeInitTable.beginInitialising thread ty
|
||||
|
||||
{ this with
|
||||
TypeInitTable = typeInitTable
|
||||
}
|
||||
|
||||
member this.WithTypeEndInit (thread : ThreadId) (handle : TypeDefinitionHandle, assy : AssemblyName) =
|
||||
member this.WithTypeEndInit (thread : ThreadId) (ty : RuntimeConcreteType) =
|
||||
this.Logger.LogDebug (
|
||||
"Marking complete initialisation of type {TypeName}, handle {TypeDefinitionHandle} from assy {AssemblyHash}",
|
||||
this.LoadedAssembly(assy).Value.TypeDefs.[handle].Name,
|
||||
handle.GetHashCode (),
|
||||
assy.GetHashCode ()
|
||||
"Marking complete initialisation of type {s_Assembly}.{TypeName}, handle {TypeDefinitionHandle}",
|
||||
ty.Assembly.FullName,
|
||||
this.LoadedAssembly(ty.Assembly).Value.TypeDefs.[ty.Definition.Get].Name,
|
||||
ty.Definition.Get.GetHashCode ()
|
||||
)
|
||||
|
||||
let typeInitTable =
|
||||
this.TypeInitTable |> TypeInitTable.markInitialised thread (handle, assy)
|
||||
let typeInitTable = this.TypeInitTable |> TypeInitTable.markInitialised thread ty
|
||||
|
||||
{ this with
|
||||
TypeInitTable = typeInitTable
|
||||
@@ -75,11 +70,13 @@ type IlMachineState =
|
||||
_LoadedAssemblies = this._LoadedAssemblies.Add (name.FullName, value)
|
||||
}
|
||||
|
||||
member this.LoadedAssembly (name : AssemblyName) : DumpedAssembly option =
|
||||
match this._LoadedAssemblies.TryGetValue name.FullName with
|
||||
member this.LoadedAssembly' (fullName : string) : DumpedAssembly option =
|
||||
match this._LoadedAssemblies.TryGetValue fullName with
|
||||
| false, _ -> None
|
||||
| true, v -> Some v
|
||||
|
||||
member this.LoadedAssembly (name : AssemblyName) : DumpedAssembly option = this.LoadedAssembly' name.FullName
|
||||
|
||||
/// Returns also the original assembly name.
|
||||
member this.WithThreadSwitchedToAssembly (assy : AssemblyName) (thread : ThreadId) : IlMachineState * AssemblyName =
|
||||
let mutable existing = Unchecked.defaultof<AssemblyName>
|
||||
@@ -263,7 +260,7 @@ module IlMachineState =
|
||||
| None ->
|
||||
|
||||
state, assy, generic, Some args
|
||||
| TypeDefn.FromDefinition (defn, _typeKind) -> state, assy, assy.TypeDefs.[defn], None
|
||||
| TypeDefn.FromDefinition (defn, _typeKind) -> state, assy, assy.TypeDefs.[defn.Get], None
|
||||
| s -> failwith $"TODO: resolveTypeFromDefn unimplemented for {s}"
|
||||
|
||||
let resolveTypeFromSpec
|
||||
@@ -303,11 +300,11 @@ module IlMachineState =
|
||||
|
||||
let callMethod
|
||||
(loggerFactory : ILoggerFactory)
|
||||
(wasInitialising : (TypeDefinitionHandle * AssemblyName) option)
|
||||
(wasInitialising : RuntimeConcreteType option)
|
||||
(wasConstructing : ManagedHeapAddress option)
|
||||
(wasClassConstructor : bool)
|
||||
(generics : ImmutableArray<TypeDefn> option)
|
||||
(methodToCall : WoofWare.PawPrint.MethodInfo)
|
||||
(methodGenerics : ImmutableArray<TypeDefn> option)
|
||||
(methodToCall : WoofWare.PawPrint.MethodInfo<TypeDefn>)
|
||||
(thread : ThreadId)
|
||||
(threadState : ThreadState)
|
||||
(state : IlMachineState)
|
||||
@@ -321,7 +318,7 @@ module IlMachineState =
|
||||
loggerFactory
|
||||
(state.ActiveAssembly thread)
|
||||
ty
|
||||
(generics |> Option.defaultValue ImmutableArray.Empty)
|
||||
(methodGenerics |> Option.defaultValue ImmutableArray.Empty)
|
||||
state
|
||||
|
||||
state, zero :: zeros
|
||||
@@ -353,6 +350,7 @@ module IlMachineState =
|
||||
state._LoadedAssemblies
|
||||
(state.ActiveAssembly thread)
|
||||
methodToCall
|
||||
methodGenerics
|
||||
(args.ToImmutable ())
|
||||
(Some
|
||||
{
|
||||
@@ -369,7 +367,7 @@ module IlMachineState =
|
||||
let state, _, _ =
|
||||
loadAssembly
|
||||
loggerFactory
|
||||
(state.LoadedAssembly (snd methodToCall.DeclaringType) |> Option.get)
|
||||
(state.LoadedAssembly methodToCall.DeclaringType.Assembly |> Option.get)
|
||||
toLoad.Handle
|
||||
state
|
||||
|
||||
@@ -413,6 +411,7 @@ module IlMachineState =
|
||||
state._LoadedAssemblies
|
||||
(state.ActiveAssembly thread)
|
||||
methodToCall
|
||||
methodGenerics
|
||||
(args.ToImmutable ())
|
||||
(Some
|
||||
{
|
||||
@@ -429,7 +428,7 @@ module IlMachineState =
|
||||
let state, _, _ =
|
||||
loadAssembly
|
||||
loggerFactory
|
||||
(state.LoadedAssembly (snd methodToCall.DeclaringType) |> Option.get)
|
||||
(state.LoadedAssembly methodToCall.DeclaringType.Assembly |> Option.get)
|
||||
toLoad.Handle
|
||||
state
|
||||
|
||||
@@ -453,18 +452,14 @@ module IlMachineState =
|
||||
|
||||
let rec loadClass
|
||||
(loggerFactory : ILoggerFactory)
|
||||
(typeDefHandle : TypeDefinitionHandle)
|
||||
(assemblyName : AssemblyName)
|
||||
(ty : RuntimeConcreteType)
|
||||
(currentThread : ThreadId)
|
||||
(state : IlMachineState)
|
||||
: StateLoadResult
|
||||
=
|
||||
if typeDefHandle.IsNil then
|
||||
failwith "Called `loadClass` with a nil typedef"
|
||||
|
||||
let logger = loggerFactory.CreateLogger "LoadClass"
|
||||
|
||||
match TypeInitTable.tryGet (typeDefHandle, assemblyName) state.TypeInitTable with
|
||||
match TypeInitTable.tryGet ty state.TypeInitTable with
|
||||
| Some TypeInitState.Initialized ->
|
||||
// Type already initialized; nothing to do
|
||||
StateLoadResult.NothingToDo state
|
||||
@@ -480,19 +475,19 @@ module IlMachineState =
|
||||
// We have work to do!
|
||||
|
||||
let state, origAssyName =
|
||||
state.WithThreadSwitchedToAssembly assemblyName currentThread
|
||||
state.WithThreadSwitchedToAssembly ty.Assembly currentThread
|
||||
|
||||
let sourceAssembly = state.LoadedAssembly assemblyName |> Option.get
|
||||
let sourceAssembly = state.LoadedAssembly ty.Assembly |> Option.get
|
||||
|
||||
let typeDef =
|
||||
match sourceAssembly.TypeDefs.TryGetValue typeDefHandle with
|
||||
| false, _ -> failwith $"Failed to find type definition {typeDefHandle} in {assemblyName.Name}"
|
||||
match sourceAssembly.TypeDefs.TryGetValue ty.Definition.Get with
|
||||
| false, _ -> failwith $"Failed to find type definition {ty.Definition.Get} in {ty.Assembly.FullName}"
|
||||
| true, v -> v
|
||||
|
||||
logger.LogDebug ("Resolving type {TypeDefNamespace}.{TypeDefName}", typeDef.Namespace, typeDef.Name)
|
||||
|
||||
// First mark as in-progress to detect cycles
|
||||
let state = state.WithTypeBeginInit currentThread (typeDefHandle, assemblyName)
|
||||
let state = state.WithTypeBeginInit currentThread ty
|
||||
|
||||
// Check if the type has a base type that needs initialization
|
||||
let firstDoBaseClass =
|
||||
@@ -500,17 +495,17 @@ module IlMachineState =
|
||||
| Some baseTypeInfo ->
|
||||
// Determine if base type is in the same or different assembly
|
||||
match baseTypeInfo with
|
||||
| ForeignAssemblyType (baseAssemblyName, baseTypeHandle) ->
|
||||
logger.LogDebug (
|
||||
"Resolved base type of {TypeDefNamespace}.{TypeDefName} to foreign assembly {ForeignAssemblyName}",
|
||||
typeDef.Namespace,
|
||||
typeDef.Name,
|
||||
baseAssemblyName.Name
|
||||
)
|
||||
| ForeignAssemblyType _ -> failwith "TODO"
|
||||
//logger.LogDebug (
|
||||
// "Resolved base type of {TypeDefNamespace}.{TypeDefName} to foreign assembly {ForeignAssemblyName}",
|
||||
// typeDef.Namespace,
|
||||
// typeDef.Name,
|
||||
// baseAssemblyName.Name
|
||||
//)
|
||||
|
||||
match loadClass loggerFactory baseTypeHandle baseAssemblyName currentThread state with
|
||||
| FirstLoadThis state -> Error state
|
||||
| NothingToDo state -> Ok state
|
||||
//match loadClass loggerFactory baseTypeHandle baseAssemblyName currentThread state with
|
||||
//| FirstLoadThis state -> Error state
|
||||
//| NothingToDo state -> Ok state
|
||||
| TypeDef typeDefinitionHandle ->
|
||||
logger.LogDebug (
|
||||
"Resolved base type of {TypeDefNamespace}.{TypeDefName} to this assembly, typedef",
|
||||
@@ -518,7 +513,10 @@ module IlMachineState =
|
||||
typeDef.Name
|
||||
)
|
||||
|
||||
match loadClass loggerFactory typeDefinitionHandle assemblyName currentThread state with
|
||||
// TypeDef won't have any generics; it would be a TypeSpec if it did
|
||||
let ty = ConcreteType.make ty.Assembly typeDefinitionHandle []
|
||||
|
||||
match loadClass loggerFactory ty currentThread state with
|
||||
| FirstLoadThis state -> Error state
|
||||
| NothingToDo state -> Ok state
|
||||
| TypeRef typeReferenceHandle ->
|
||||
@@ -534,7 +532,10 @@ module IlMachineState =
|
||||
targetType.Name
|
||||
)
|
||||
|
||||
match loadClass loggerFactory targetType.TypeDefHandle assy.Name currentThread state with
|
||||
// TypeRef won't have any generics; it would be a TypeSpec if it did
|
||||
let ty = ConcreteType.make assy.Name targetType.TypeDefHandle []
|
||||
|
||||
match loadClass loggerFactory ty currentThread state with
|
||||
| FirstLoadThis state -> Error state
|
||||
| NothingToDo state -> Ok state
|
||||
| TypeSpec typeSpecificationHandle -> failwith "TODO: TypeSpec base type loading unimplemented"
|
||||
@@ -552,20 +553,23 @@ module IlMachineState =
|
||||
|> List.tryFind (fun method -> method.Name = ".cctor" && method.IsStatic && method.Parameters.IsEmpty)
|
||||
|
||||
match cctor with
|
||||
| Some ctorMethod ->
|
||||
| Some cctorMethod ->
|
||||
// Call the class constructor! Note that we *don't* use `callMethodInActiveAssembly`, because that
|
||||
// performs class loading, but we're already in the middle of loading this class.
|
||||
// TODO: factor out the common bit.
|
||||
let currentThreadState = state.ThreadState.[currentThread]
|
||||
|
||||
let cctorMethod =
|
||||
cctorMethod |> MethodInfo.mapTypeGenerics (fun i _ -> ty.Generics.[i])
|
||||
|
||||
callMethod
|
||||
loggerFactory
|
||||
(Some (typeDefHandle, assemblyName))
|
||||
(Some ty)
|
||||
None
|
||||
true
|
||||
// constructor is surely not generic
|
||||
None
|
||||
ctorMethod
|
||||
cctorMethod
|
||||
currentThread
|
||||
currentThreadState
|
||||
state
|
||||
@@ -573,7 +577,7 @@ module IlMachineState =
|
||||
| None ->
|
||||
// No constructor, just continue.
|
||||
// Mark the type as initialized.
|
||||
let state = state.WithTypeEndInit currentThread (typeDefHandle, assemblyName)
|
||||
let state = state.WithTypeEndInit currentThread ty
|
||||
|
||||
// Restore original assembly context if needed
|
||||
state.WithThreadSwitchedToAssembly origAssyName currentThread
|
||||
@@ -583,13 +587,13 @@ module IlMachineState =
|
||||
let ensureTypeInitialised
|
||||
(loggerFactory : ILoggerFactory)
|
||||
(thread : ThreadId)
|
||||
(ty : TypeDefinitionHandle * AssemblyName)
|
||||
(ty : RuntimeConcreteType)
|
||||
(state : IlMachineState)
|
||||
: IlMachineState * WhatWeDid
|
||||
=
|
||||
match TypeInitTable.tryGet ty state.TypeInitTable with
|
||||
| None ->
|
||||
match loadClass loggerFactory (fst ty) (snd ty) thread state with
|
||||
match loadClass loggerFactory ty thread state with
|
||||
| NothingToDo state -> state, WhatWeDid.Executed
|
||||
| FirstLoadThis state -> state, WhatWeDid.SuspendedForClassInit
|
||||
| Some TypeInitState.Initialized -> state, WhatWeDid.Executed
|
||||
@@ -603,8 +607,8 @@ module IlMachineState =
|
||||
let callMethodInActiveAssembly
|
||||
(loggerFactory : ILoggerFactory)
|
||||
(thread : ThreadId)
|
||||
(generics : TypeDefn ImmutableArray option)
|
||||
(methodToCall : WoofWare.PawPrint.MethodInfo)
|
||||
(methodGenerics : TypeDefn ImmutableArray option)
|
||||
(methodToCall : WoofWare.PawPrint.MethodInfo<TypeDefn>)
|
||||
(weAreConstructingObj : ManagedHeapAddress option)
|
||||
(state : IlMachineState)
|
||||
: IlMachineState * WhatWeDid
|
||||
@@ -616,7 +620,16 @@ module IlMachineState =
|
||||
|
||||
match typeInit with
|
||||
| WhatWeDid.Executed ->
|
||||
callMethod loggerFactory None weAreConstructingObj false generics methodToCall thread threadState state,
|
||||
callMethod
|
||||
loggerFactory
|
||||
None
|
||||
weAreConstructingObj
|
||||
false
|
||||
methodGenerics
|
||||
methodToCall
|
||||
thread
|
||||
threadState
|
||||
state,
|
||||
WhatWeDid.Executed
|
||||
| _ -> state, typeInit
|
||||
|
||||
@@ -844,7 +857,7 @@ module IlMachineState =
|
||||
(assy : DumpedAssembly)
|
||||
(m : MemberReferenceHandle)
|
||||
(state : IlMachineState)
|
||||
: IlMachineState * AssemblyName * Choice<WoofWare.PawPrint.MethodInfo, WoofWare.PawPrint.FieldInfo>
|
||||
: IlMachineState * AssemblyName * Choice<WoofWare.PawPrint.MethodInfo<TypeDefn>, WoofWare.PawPrint.FieldInfo>
|
||||
=
|
||||
// TODO: do we need to initialise the parent class here?
|
||||
let mem = assy.Members.[m]
|
||||
@@ -887,7 +900,7 @@ module IlMachineState =
|
||||
| [] ->
|
||||
failwith
|
||||
$"Could not find member {memberName} with the right signature on {targetType.Namespace}.{targetType.Name}"
|
||||
| [ x ] -> x
|
||||
| [ x ] -> x |> MethodInfo.mapTypeGenerics (fun i _ -> targetType.Generics.[i])
|
||||
| _ ->
|
||||
failwith
|
||||
$"Multiple overloads matching signature for call to {targetType.Namespace}.{targetType.Name}'s {memberName}!"
|
||||
@@ -922,9 +935,8 @@ module IlMachineState =
|
||||
{ threadStateAtEndOfMethod with
|
||||
ActiveMethodState = returnState.JumpTo
|
||||
ActiveAssembly =
|
||||
snd
|
||||
threadStateAtEndOfMethod.MethodStates.[returnState.JumpTo].ExecutingMethod
|
||||
.DeclaringType
|
||||
threadStateAtEndOfMethod.MethodStates.[returnState.JumpTo].ExecutingMethod.DeclaringType
|
||||
.Assembly
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -145,12 +145,12 @@ 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 =
|
||||
type MethodInfo<'typeGenerics when 'typeGenerics :> IComparable<'typeGenerics> and 'typeGenerics : comparison> =
|
||||
{
|
||||
/// <summary>
|
||||
/// The type that declares this method, along with its assembly information.
|
||||
/// </summary>
|
||||
DeclaringType : TypeDefinitionHandle * AssemblyName
|
||||
DeclaringType : ConcreteType<'typeGenerics>
|
||||
|
||||
/// <summary>
|
||||
/// The metadata token handle that uniquely identifies this method in the assembly.
|
||||
@@ -212,6 +212,26 @@ type MethodInfo =
|
||||
|
||||
[<RequireQualifiedAccess>]
|
||||
module MethodInfo =
|
||||
let mapTypeGenerics<'a, 'b
|
||||
when 'a :> IComparable<'a> and 'a : comparison and 'b : comparison and 'b :> IComparable<'b>>
|
||||
(f : int -> 'a -> 'b)
|
||||
(m : MethodInfo<'a>)
|
||||
: MethodInfo<'b>
|
||||
=
|
||||
{
|
||||
DeclaringType = m.DeclaringType |> ConcreteType.mapGeneric f
|
||||
Handle = m.Handle
|
||||
Name = m.Name
|
||||
Instructions = m.Instructions
|
||||
Parameters = m.Parameters
|
||||
Generics = m.Generics
|
||||
Signature = m.Signature
|
||||
CustomAttributes = m.CustomAttributes
|
||||
MethodAttributes = m.MethodAttributes
|
||||
ImplAttributes = m.ImplAttributes
|
||||
IsStatic = m.IsStatic
|
||||
}
|
||||
|
||||
type private Dummy = class end
|
||||
|
||||
type private MethodBody =
|
||||
@@ -558,7 +578,7 @@ module MethodInfo =
|
||||
(peReader : PEReader)
|
||||
(metadataReader : MetadataReader)
|
||||
(methodHandle : MethodDefinitionHandle)
|
||||
: MethodInfo option
|
||||
: MethodInfo<FakeUnit> option
|
||||
=
|
||||
let logger = loggerFactory.CreateLogger "MethodInfo"
|
||||
let assemblyName = metadataReader.GetAssemblyDefinition().GetAssemblyName ()
|
||||
@@ -592,6 +612,9 @@ module MethodInfo =
|
||||
|
||||
let declaringType = methodDef.GetDeclaringType ()
|
||||
|
||||
let declaringTypeGenericParams =
|
||||
metadataReader.GetTypeDefinition(declaringType).GetGenericParameters().Count
|
||||
|
||||
let attrs =
|
||||
let result = ImmutableArray.CreateBuilder ()
|
||||
let attrs = methodDef.GetCustomAttributes ()
|
||||
@@ -611,7 +634,7 @@ module MethodInfo =
|
||||
GenericParameter.readAll metadataReader (methodDef.GetGenericParameters ())
|
||||
|
||||
{
|
||||
DeclaringType = (declaringType, assemblyName)
|
||||
DeclaringType = ConcreteType.make' assemblyName declaringType declaringTypeGenericParams
|
||||
Handle = methodHandle
|
||||
Name = methodName
|
||||
Instructions = methodBody
|
||||
@@ -624,3 +647,47 @@ module MethodInfo =
|
||||
ImplAttributes = implAttrs
|
||||
}
|
||||
|> Some
|
||||
|
||||
let rec resolveBaseType
|
||||
(methodGenerics : TypeDefn ImmutableArray option)
|
||||
(executingMethod : MethodInfo<TypeDefn>)
|
||||
(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]
|
||||
|
@@ -1,14 +1,12 @@
|
||||
namespace WoofWare.PawPrint
|
||||
|
||||
open System.Collections.Immutable
|
||||
open System.Reflection
|
||||
open System.Reflection.Metadata
|
||||
|
||||
type MethodReturnState =
|
||||
{
|
||||
/// Index in the MethodStates array of a ThreadState
|
||||
JumpTo : int
|
||||
WasInitialisingType : (TypeDefinitionHandle * AssemblyName) option
|
||||
WasInitialisingType : RuntimeConcreteType option
|
||||
/// The Newobj instruction means we need to push a reference immediately after Ret.
|
||||
WasConstructingObj : ManagedHeapAddress option
|
||||
}
|
||||
@@ -21,11 +19,12 @@ and MethodState =
|
||||
IlOpIndex : int
|
||||
EvaluationStack : EvalStack
|
||||
Arguments : CliType ImmutableArray
|
||||
ExecutingMethod : WoofWare.PawPrint.MethodInfo
|
||||
ExecutingMethod : WoofWare.PawPrint.MethodInfo<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.
|
||||
ReturnState : MethodReturnState option
|
||||
Generics : ImmutableArray<TypeDefn> option
|
||||
}
|
||||
|
||||
static member jumpProgramCounter (bytes : int) (state : MethodState) =
|
||||
@@ -104,7 +103,8 @@ and MethodState =
|
||||
static member Empty
|
||||
(loadedAssemblies : ImmutableDictionary<string, DumpedAssembly>)
|
||||
(containingAssembly : DumpedAssembly)
|
||||
(method : WoofWare.PawPrint.MethodInfo)
|
||||
(method : WoofWare.PawPrint.MethodInfo<TypeDefn>)
|
||||
(methodGenerics : ImmutableArray<TypeDefn> option)
|
||||
(args : ImmutableArray<CliType>)
|
||||
(returnState : MethodReturnState option)
|
||||
: Result<MethodState, WoofWare.PawPrint.AssemblyReference list>
|
||||
@@ -154,5 +154,6 @@ and MethodState =
|
||||
ExecutingMethod = method
|
||||
LocalMemoryPool = ()
|
||||
ReturnState = returnState
|
||||
Generics = methodGenerics
|
||||
}
|
||||
|> Ok
|
||||
|
@@ -65,6 +65,10 @@ module Program =
|
||||
if mainMethod.Signature.GenericParameterCount > 0 then
|
||||
failwith "Refusing to execute generic main method"
|
||||
|
||||
let mainMethod =
|
||||
mainMethod
|
||||
|> MethodInfo.mapTypeGenerics (fun _ -> failwith "Refusing to execute generic main method")
|
||||
|
||||
let state, mainThread =
|
||||
IlMachineState.initial loggerFactory dotnetRuntimeDirs dumped
|
||||
// The thread's state is slightly fake: we will need to put arguments onto the stack before actually
|
||||
@@ -78,6 +82,7 @@ module Program =
|
||||
s._LoadedAssemblies
|
||||
dumped
|
||||
mainMethod
|
||||
None
|
||||
(ImmutableArray.CreateRange [ CliType.ObjectRef None ])
|
||||
None
|
||||
with
|
||||
@@ -87,11 +92,7 @@ module Program =
|
||||
let rec loadInitialState (state : IlMachineState) =
|
||||
match
|
||||
state
|
||||
|> IlMachineState.loadClass
|
||||
loggerFactory
|
||||
(fst mainMethod.DeclaringType)
|
||||
(snd mainMethod.DeclaringType)
|
||||
mainThread
|
||||
|> IlMachineState.loadClass loggerFactory mainMethod.DeclaringType mainThread
|
||||
with
|
||||
| StateLoadResult.NothingToDo ilMachineState -> ilMachineState
|
||||
| StateLoadResult.FirstLoadThis ilMachineState -> loadInitialState ilMachineState
|
||||
@@ -126,6 +127,7 @@ module Program =
|
||||
state._LoadedAssemblies
|
||||
dumped
|
||||
mainMethod
|
||||
None
|
||||
(ImmutableArray.Create (CliType.OfManagedObject arrayAllocation))
|
||||
None
|
||||
with
|
||||
|
@@ -3,6 +3,13 @@ namespace WoofWare.PawPrint
|
||||
open System.Collections.Immutable
|
||||
open System.Reflection.Metadata
|
||||
open System.Reflection.Metadata.Ecma335
|
||||
open Microsoft.FSharp.Core
|
||||
|
||||
type ResolvedBaseType =
|
||||
| Enum
|
||||
| ValueType
|
||||
| Object
|
||||
| Delegate
|
||||
|
||||
/// <summary>
|
||||
/// Represents a method signature with type parameters.
|
||||
@@ -13,7 +20,7 @@ type TypeMethodSignature<'Types> =
|
||||
/// <summary>
|
||||
/// Contains calling convention and other method attributes encoded in the metadata.
|
||||
/// </summary>
|
||||
Header : SignatureHeader
|
||||
Header : ComparableSignatureHeader
|
||||
|
||||
/// <summary>
|
||||
/// The types of all parameters of the method.
|
||||
@@ -40,7 +47,7 @@ type TypeMethodSignature<'Types> =
|
||||
module TypeMethodSignature =
|
||||
let make<'T> (p : MethodSignature<'T>) : TypeMethodSignature<'T> =
|
||||
{
|
||||
Header = p.Header
|
||||
Header = ComparableSignatureHeader.Make p.Header
|
||||
ReturnType = p.ReturnType
|
||||
ParameterTypes = List.ofSeq p.ParameterTypes
|
||||
GenericParameterCount = p.GenericParameterCount
|
||||
@@ -91,14 +98,15 @@ type PrimitiveType =
|
||||
|
||||
type TypeDefn =
|
||||
| PrimitiveType of PrimitiveType
|
||||
| Array of elt : TypeDefn * shape : ArrayShape
|
||||
// TODO: array shapes
|
||||
| Array of elt : TypeDefn * shape : unit
|
||||
| Pinned of TypeDefn
|
||||
| Pointer of TypeDefn
|
||||
| Byref of TypeDefn
|
||||
| OneDimensionalArrayLowerBoundZero of elements : TypeDefn
|
||||
| Modified of original : TypeDefn * afterMod : TypeDefn * modificationRequired : bool
|
||||
| FromReference of TypeRef * SignatureTypeKind
|
||||
| FromDefinition of TypeDefinitionHandle * SignatureTypeKind
|
||||
| FromDefinition of ComparableTypeDefinitionHandle * SignatureTypeKind
|
||||
| GenericInstantiation of generic : TypeDefn * args : ImmutableArray<TypeDefn>
|
||||
| FunctionPointer of TypeMethodSignature<TypeDefn>
|
||||
| GenericTypeParameter of index : int
|
||||
@@ -169,7 +177,7 @@ module TypeDefn =
|
||||
let typeProvider : ISignatureTypeProvider<TypeDefn, unit> =
|
||||
{ new ISignatureTypeProvider<TypeDefn, unit> with
|
||||
member this.GetArrayType (elementType : TypeDefn, shape : ArrayShape) : TypeDefn =
|
||||
TypeDefn.Array (elementType, shape)
|
||||
TypeDefn.Array (elementType, ())
|
||||
|
||||
member this.GetByReferenceType (elementType : TypeDefn) : TypeDefn = TypeDefn.Byref elementType
|
||||
|
||||
@@ -194,7 +202,7 @@ module TypeDefn =
|
||||
let handle' : EntityHandle = TypeDefinitionHandle.op_Implicit handle
|
||||
let typeKind = reader.ResolveSignatureTypeKind (handle', rawTypeKind)
|
||||
|
||||
TypeDefn.FromDefinition (handle, typeKind)
|
||||
TypeDefn.FromDefinition (ComparableTypeDefinitionHandle.Make handle, typeKind)
|
||||
|
||||
member this.GetTypeFromReference
|
||||
(reader : MetadataReader, handle : TypeReferenceHandle, rawTypeKind : byte)
|
||||
|
@@ -14,12 +14,6 @@ type BaseTypeInfo =
|
||||
| TypeSpec of TypeSpecificationHandle
|
||||
| ForeignAssemblyType of assemblyName : AssemblyName * TypeDefinitionHandle
|
||||
|
||||
type ResolvedBaseType =
|
||||
| Enum
|
||||
| ValueType
|
||||
| Object
|
||||
| Delegate
|
||||
|
||||
type MethodImplParsed =
|
||||
| MethodImplementation of MethodImplementationHandle
|
||||
| MethodDefinition of MethodDefinitionHandle
|
||||
@@ -39,7 +33,7 @@ type TypeInfo<'generic> =
|
||||
/// <summary>
|
||||
/// All methods defined within this type.
|
||||
/// </summary>
|
||||
Methods : WoofWare.PawPrint.MethodInfo list
|
||||
Methods : WoofWare.PawPrint.MethodInfo<FakeUnit> list
|
||||
|
||||
/// <summary>
|
||||
/// Method implementation mappings for this type, often used for interface implementations
|
||||
|
@@ -11,32 +11,22 @@ type TypeInitState =
|
||||
|
||||
/// Tracks the initialization state of types across assemblies. The string in the key is the FullName of the AssemblyName where the type comes from.
|
||||
// TODO: need a better solution than string here! AssemblyName didn't work, we had nonequal assembly names.
|
||||
type TypeInitTable = ImmutableDictionary<TypeDefinitionHandle * string, TypeInitState>
|
||||
type TypeInitTable = ImmutableDictionary<RuntimeConcreteType, TypeInitState>
|
||||
|
||||
[<RequireQualifiedAccess>]
|
||||
module TypeInitTable =
|
||||
let tryGet (typeDef : TypeDefinitionHandle, assy : AssemblyName) (t : TypeInitTable) =
|
||||
match t.TryGetValue ((typeDef, assy.FullName)) with
|
||||
let tryGet (ty : RuntimeConcreteType) (t : TypeInitTable) =
|
||||
match t.TryGetValue ty with
|
||||
| true, v -> Some v
|
||||
| false, _ -> None
|
||||
|
||||
let beginInitialising
|
||||
(thread : ThreadId)
|
||||
(typeDef : TypeDefinitionHandle, assy : AssemblyName)
|
||||
(t : TypeInitTable)
|
||||
: TypeInitTable
|
||||
=
|
||||
match t.TryGetValue ((typeDef, assy.FullName)) with
|
||||
| false, _ -> t.Add ((typeDef, assy.FullName), TypeInitState.InProgress thread)
|
||||
let beginInitialising (thread : ThreadId) (ty : RuntimeConcreteType) (t : TypeInitTable) : TypeInitTable =
|
||||
match t.TryGetValue ty with
|
||||
| false, _ -> t.Add (ty, TypeInitState.InProgress thread)
|
||||
| true, v -> failwith "Logic error: tried initialising a type which has already started initialising"
|
||||
|
||||
let markInitialised
|
||||
(thread : ThreadId)
|
||||
(typeDef : TypeDefinitionHandle, assy : AssemblyName)
|
||||
(t : TypeInitTable)
|
||||
: TypeInitTable
|
||||
=
|
||||
match t.TryGetValue ((typeDef, assy.FullName)) with
|
||||
let markInitialised (thread : ThreadId) (ty : RuntimeConcreteType) (t : TypeInitTable) : TypeInitTable =
|
||||
match t.TryGetValue ty with
|
||||
| false, _ -> failwith "Logic error: completing initialisation of a type which never started initialising"
|
||||
| true, TypeInitState.Initialized ->
|
||||
failwith "Logic error: completing initialisation of a type which has already finished initialising"
|
||||
@@ -45,4 +35,4 @@ module TypeInitTable =
|
||||
failwith
|
||||
"Logic error: completed initialisation of a type on a different thread to the one which started it!"
|
||||
else
|
||||
t.SetItem ((typeDef, assy.FullName), TypeInitState.Initialized)
|
||||
t.SetItem (ty, TypeInitState.Initialized)
|
||||
|
@@ -1,12 +1,60 @@
|
||||
namespace WoofWare.PawPrint
|
||||
|
||||
open System
|
||||
open System.Reflection.Metadata
|
||||
open Microsoft.FSharp.Core
|
||||
|
||||
[<CustomComparison>]
|
||||
[<CustomEquality>]
|
||||
type TypeRefResolutionScope =
|
||||
| Assembly of AssemblyReferenceHandle
|
||||
| ModuleRef of ModuleReferenceHandle
|
||||
| TypeRef of TypeReferenceHandle
|
||||
|
||||
override this.Equals (other : obj) : bool =
|
||||
let other =
|
||||
match other with
|
||||
| :? TypeRefResolutionScope as other -> other
|
||||
| _ -> failwith "should never compare with non-TypeRefResolutionScope"
|
||||
|
||||
match this, other with
|
||||
| TypeRefResolutionScope.Assembly a1, TypeRefResolutionScope.Assembly a2 -> a1 = a2
|
||||
| TypeRefResolutionScope.Assembly _, _ -> false
|
||||
| TypeRefResolutionScope.ModuleRef m1, TypeRefResolutionScope.ModuleRef m2 -> m1 = m2
|
||||
| TypeRefResolutionScope.ModuleRef _, _ -> false
|
||||
| TypeRefResolutionScope.TypeRef t1, TypeRefResolutionScope.TypeRef t2 -> t1 = t2
|
||||
| TypeRefResolutionScope.TypeRef _, _ -> false
|
||||
|
||||
override this.GetHashCode () : int =
|
||||
match this with
|
||||
| TypeRefResolutionScope.Assembly h -> hash (1, h)
|
||||
| TypeRefResolutionScope.ModuleRef h -> hash (2, h)
|
||||
| TypeRefResolutionScope.TypeRef h -> hash (3, h)
|
||||
|
||||
interface IComparable<TypeRefResolutionScope> with
|
||||
member this.CompareTo other =
|
||||
match this, other with
|
||||
| TypeRefResolutionScope.Assembly h1, TypeRefResolutionScope.Assembly h2 ->
|
||||
// this happens to get the underlying int
|
||||
h1.GetHashCode().CompareTo (h2.GetHashCode ())
|
||||
| TypeRefResolutionScope.Assembly _, TypeRefResolutionScope.ModuleRef _ -> -1
|
||||
| TypeRefResolutionScope.Assembly _, TypeRefResolutionScope.TypeRef _ -> -1
|
||||
| TypeRefResolutionScope.ModuleRef _, Assembly _ -> 1
|
||||
| TypeRefResolutionScope.ModuleRef m1, ModuleRef m2 -> m1.GetHashCode().CompareTo (m2.GetHashCode ())
|
||||
| TypeRefResolutionScope.ModuleRef _, TypeRef _ -> -1
|
||||
| TypeRefResolutionScope.TypeRef _, Assembly _ -> 1
|
||||
| TypeRefResolutionScope.TypeRef _, ModuleRef _ -> 1
|
||||
| TypeRefResolutionScope.TypeRef t1, TypeRef t2 -> t1.GetHashCode().CompareTo (t2.GetHashCode ())
|
||||
|
||||
interface IComparable with
|
||||
member this.CompareTo (other : obj) : int =
|
||||
let other =
|
||||
match other with
|
||||
| :? TypeRefResolutionScope as other -> other
|
||||
| _ -> failwith "unexpectedly comparing TypeRefResolutionScope with something else"
|
||||
|
||||
(this :> IComparable<TypeRefResolutionScope>).CompareTo other
|
||||
|
||||
/// <summary>
|
||||
/// Represents a type reference in a .NET assembly metadata.
|
||||
/// This corresponds to a TypeReferenceHandle in System.Reflection.Metadata.
|
||||
|
@@ -17,14 +17,20 @@ module internal UnaryMetadataIlOp =
|
||||
=
|
||||
match op with
|
||||
| Call ->
|
||||
let state, methodToCall, generics =
|
||||
let activeAssy = state.ActiveAssembly thread
|
||||
|
||||
let state, methodToCall, methodGenerics =
|
||||
match metadataToken with
|
||||
| MetadataToken.MethodSpecification h ->
|
||||
let spec = (state.ActiveAssembly thread).MethodSpecs.[h]
|
||||
let spec = activeAssy.MethodSpecs.[h]
|
||||
|
||||
match spec.Method with
|
||||
| MetadataToken.MethodDef token ->
|
||||
state, (state.ActiveAssembly thread).Methods.[token], Some spec.Signature
|
||||
let method =
|
||||
activeAssy.Methods.[token]
|
||||
|> MethodInfo.mapTypeGenerics (fun i _ -> spec.Signature.[i])
|
||||
|
||||
state, method, Some spec.Signature
|
||||
| MetadataToken.MemberReference ref ->
|
||||
let state, _, method =
|
||||
IlMachineState.resolveMember loggerFactory (state.ActiveAssembly thread) ref state
|
||||
@@ -42,25 +48,18 @@ module internal UnaryMetadataIlOp =
|
||||
| Choice1Of2 method -> state, method, None
|
||||
|
||||
| MetadataToken.MethodDef defn ->
|
||||
let activeAssy = state.ActiveAssembly thread
|
||||
|
||||
match activeAssy.Methods.TryGetValue defn with
|
||||
| true, method -> state, method, None
|
||||
| true, method ->
|
||||
let method = method |> MethodInfo.mapTypeGenerics (fun _ -> failwith "not generic")
|
||||
state, method, None
|
||||
| false, _ -> failwith $"could not find method in {activeAssy.Name}"
|
||||
| k -> failwith $"Unrecognised kind: %O{k}"
|
||||
|
||||
match
|
||||
IlMachineState.loadClass
|
||||
loggerFactory
|
||||
(fst methodToCall.DeclaringType)
|
||||
(snd methodToCall.DeclaringType)
|
||||
thread
|
||||
state
|
||||
with
|
||||
match IlMachineState.loadClass loggerFactory methodToCall.DeclaringType thread state with
|
||||
| NothingToDo state ->
|
||||
state.WithThreadSwitchedToAssembly (snd methodToCall.DeclaringType) thread
|
||||
state.WithThreadSwitchedToAssembly methodToCall.DeclaringType.Assembly thread
|
||||
|> fst
|
||||
|> IlMachineState.callMethodInActiveAssembly loggerFactory thread generics methodToCall None
|
||||
|> IlMachineState.callMethodInActiveAssembly loggerFactory thread methodGenerics methodToCall None
|
||||
| FirstLoadThis state -> state, WhatWeDid.SuspendedForClassInit
|
||||
|
||||
| Callvirt ->
|
||||
@@ -82,8 +81,8 @@ module internal UnaryMetadataIlOp =
|
||||
| Some obj -> obj
|
||||
|
||||
do
|
||||
let assy = state.LoadedAssembly (snd method.DeclaringType) |> Option.get
|
||||
let ty = assy.TypeDefs.[fst method.DeclaringType]
|
||||
let assy = state.LoadedAssembly method.DeclaringType.Assembly |> Option.get
|
||||
let ty = assy.TypeDefs.[method.DeclaringType.Definition.Get]
|
||||
|
||||
logger.LogTrace (
|
||||
"Calling method {Assembly}.{Type}.{CallvirtMethod} on object {CallvirtObject}",
|
||||
@@ -122,7 +121,11 @@ module internal UnaryMetadataIlOp =
|
||||
| EvalStackValue.ObjectRef managedHeapAddress -> failwith "todo"
|
||||
| _ -> failwith $"TODO (Callvirt): can't identify type of {currentObj}"
|
||||
|
||||
state.WithThreadSwitchedToAssembly (snd methodToCall.DeclaringType) thread
|
||||
let methodToCall =
|
||||
methodToCall
|
||||
|> MethodInfo.mapTypeGenerics (fun _ -> failwith "TODO: look up generics from runtime type information")
|
||||
|
||||
state.WithThreadSwitchedToAssembly methodToCall.DeclaringType.Assembly thread
|
||||
|> fst
|
||||
|> IlMachineState.callMethodInActiveAssembly loggerFactory thread generics methodToCall None
|
||||
| Castclass -> failwith "TODO: Castclass unimplemented"
|
||||
@@ -134,7 +137,7 @@ module internal UnaryMetadataIlOp =
|
||||
| MethodDef md ->
|
||||
let activeAssy = state.ActiveAssembly thread
|
||||
let method = activeAssy.Methods.[md]
|
||||
state, activeAssy.Name, method
|
||||
state, activeAssy.Name, MethodInfo.mapTypeGenerics (fun _ -> failwith "non-generic method") method
|
||||
| MemberReference mr ->
|
||||
let state, name, method =
|
||||
IlMachineState.resolveMember loggerFactory (state.ActiveAssembly thread) mr state
|
||||
@@ -152,9 +155,8 @@ module internal UnaryMetadataIlOp =
|
||||
| WhatWeDid.SuspendedForClassInit -> state, SuspendedForClassInit
|
||||
| WhatWeDid.Executed ->
|
||||
|
||||
let ctorType, ctorAssembly = ctor.DeclaringType
|
||||
let ctorAssembly = state.LoadedAssembly ctorAssembly |> Option.get
|
||||
let ctorType = ctorAssembly.TypeDefs.[ctorType]
|
||||
let ctorAssembly = state.LoadedAssembly ctor.DeclaringType.Assembly |> Option.get
|
||||
let ctorType = ctorAssembly.TypeDefs.[ctor.DeclaringType.Definition.Get]
|
||||
|
||||
do
|
||||
logger.LogDebug (
|
||||
@@ -215,7 +217,7 @@ module internal UnaryMetadataIlOp =
|
||||
| EvalStackValue.Int32 v -> v
|
||||
| popped -> failwith $"unexpectedly popped value %O{popped} to serve as array len"
|
||||
|
||||
let elementType, baseType =
|
||||
let baseType =
|
||||
match metadataToken with
|
||||
| MetadataToken.TypeDefinition defn ->
|
||||
let assy = state.LoadedAssembly currentState.ActiveAssembly |> Option.get
|
||||
@@ -229,11 +231,12 @@ module internal UnaryMetadataIlOp =
|
||||
baseClassTypes
|
||||
elementType.Assembly
|
||||
|
||||
elementType, baseType
|
||||
baseType
|
||||
| MetadataToken.TypeSpecification spec ->
|
||||
let assy = state.LoadedAssembly currentState.ActiveAssembly |> Option.get
|
||||
let elementType = assy.TypeSpecs.[spec]
|
||||
failwith ""
|
||||
let elementType = assy.TypeSpecs.[spec].Signature
|
||||
|
||||
MethodInfo.resolveBaseType newMethodState.Generics newMethodState.ExecutingMethod elementType
|
||||
| x -> failwith $"TODO: Newarr element type resolution unimplemented for {x}"
|
||||
|
||||
let zeroOfType =
|
||||
@@ -279,15 +282,20 @@ module internal UnaryMetadataIlOp =
|
||||
|
||||
state, WhatWeDid.Executed
|
||||
| Stfld ->
|
||||
let state, assyName, field =
|
||||
let activeAssy = state.ActiveAssembly thread
|
||||
|
||||
let state, declaringType, field =
|
||||
match metadataToken with
|
||||
| MetadataToken.FieldDefinition f ->
|
||||
state, (state.ActiveAssembly thread).Name, state.ActiveAssembly(thread).Fields.[f]
|
||||
let field = activeAssy.Fields.[f]
|
||||
// No generics on a type if we're accessing it by FieldDefinition
|
||||
let declaringType = ConcreteType.make activeAssy.Name field.DeclaringType []
|
||||
state, declaringType, field
|
||||
| t -> failwith $"Unexpectedly asked to store to a non-field: {t}"
|
||||
|
||||
do
|
||||
let logger = loggerFactory.CreateLogger "Stfld"
|
||||
let declaring = state.ActiveAssembly(thread).TypeDefs.[field.DeclaringType]
|
||||
let declaring = activeAssy.TypeDefs.[field.DeclaringType]
|
||||
|
||||
logger.LogInformation (
|
||||
"Storing in object field {FieldAssembly}.{FieldDeclaringType}.{FieldName} (type {FieldType})",
|
||||
@@ -300,12 +308,11 @@ module internal UnaryMetadataIlOp =
|
||||
let valueToStore, state = IlMachineState.popEvalStack thread state
|
||||
|
||||
let state, zero =
|
||||
// TODO: generics
|
||||
IlMachineState.cliTypeZeroOf
|
||||
loggerFactory
|
||||
(state.ActiveAssembly thread)
|
||||
field.Signature
|
||||
ImmutableArray.Empty
|
||||
(ImmutableArray.CreateRange declaringType.Generics)
|
||||
state
|
||||
|
||||
let valueToStore = EvalStackValue.toCliTypeCoerced zero valueToStore
|
||||
@@ -313,7 +320,7 @@ module internal UnaryMetadataIlOp =
|
||||
let currentObj, state = IlMachineState.popEvalStack thread state
|
||||
|
||||
if field.Attributes.HasFlag FieldAttributes.Static then
|
||||
let state = state.SetStatic (field.DeclaringType, assyName) field.Name valueToStore
|
||||
let state = state.SetStatic declaringType field.Name valueToStore
|
||||
|
||||
state, WhatWeDid.Executed
|
||||
else
|
||||
@@ -353,16 +360,29 @@ module internal UnaryMetadataIlOp =
|
||||
|> Tuple.withRight WhatWeDid.Executed
|
||||
|
||||
| Stsfld ->
|
||||
let fieldHandle =
|
||||
match metadataToken with
|
||||
| MetadataToken.FieldDefinition f -> f
|
||||
| t -> failwith $"Unexpectedly asked to store to a non-field: {t}"
|
||||
|
||||
let activeAssy = state.ActiveAssembly thread
|
||||
|
||||
match activeAssy.Fields.TryGetValue fieldHandle with
|
||||
| false, _ -> failwith "TODO: Stsfld - throw MissingFieldException"
|
||||
| true, field ->
|
||||
let state, field, declaringType =
|
||||
match metadataToken with
|
||||
| MetadataToken.FieldDefinition fieldHandle ->
|
||||
match activeAssy.Fields.TryGetValue fieldHandle with
|
||||
| false, _ -> failwith "TODO: Stsfld - throw MissingFieldException"
|
||||
| true, field -> state, field, ConcreteType.make activeAssy.Name field.DeclaringType []
|
||||
| MetadataToken.MemberReference mr ->
|
||||
let state, assy, method =
|
||||
IlMachineState.resolveMember loggerFactory (state.ActiveAssembly thread) mr state
|
||||
|
||||
match method with
|
||||
| Choice1Of2 methodInfo ->
|
||||
failwith $"unexpectedly asked to store to a non-field method: {methodInfo.Name}"
|
||||
| Choice2Of2 fieldInfo ->
|
||||
state,
|
||||
fieldInfo,
|
||||
ConcreteType.make
|
||||
assy
|
||||
fieldInfo.DeclaringType
|
||||
(failwith "TODO: refactor FieldInfo to store a Runtime type")
|
||||
| t -> failwith $"Unexpectedly asked to store to a non-field: {t}"
|
||||
|
||||
do
|
||||
let logger = loggerFactory.CreateLogger "Stsfld"
|
||||
@@ -376,41 +396,57 @@ module internal UnaryMetadataIlOp =
|
||||
field.Signature
|
||||
)
|
||||
|
||||
match IlMachineState.loadClass loggerFactory field.DeclaringType activeAssy.Name thread state with
|
||||
match IlMachineState.loadClass loggerFactory declaringType thread state with
|
||||
| FirstLoadThis state -> state, WhatWeDid.SuspendedForClassInit
|
||||
| NothingToDo state ->
|
||||
|
||||
let popped, state = IlMachineState.popEvalStack thread state
|
||||
|
||||
let state, zero =
|
||||
// TODO: generics
|
||||
IlMachineState.cliTypeZeroOf loggerFactory activeAssy field.Signature ImmutableArray.Empty state
|
||||
IlMachineState.cliTypeZeroOf
|
||||
loggerFactory
|
||||
activeAssy
|
||||
field.Signature
|
||||
(ImmutableArray.CreateRange declaringType.Generics)
|
||||
state
|
||||
|
||||
let toStore = EvalStackValue.toCliTypeCoerced zero popped
|
||||
|
||||
let state =
|
||||
state.SetStatic (field.DeclaringType, activeAssy.Name) field.Name toStore
|
||||
state.SetStatic declaringType field.Name toStore
|
||||
|> IlMachineState.advanceProgramCounter thread
|
||||
|
||||
state, WhatWeDid.Executed
|
||||
|
||||
| Ldfld ->
|
||||
let state, assyName, field =
|
||||
let activeAssembly = state.ActiveAssembly thread
|
||||
|
||||
let state, declaringType, field =
|
||||
match metadataToken with
|
||||
| MetadataToken.FieldDefinition f ->
|
||||
state, (state.ActiveAssembly thread).Name, state.ActiveAssembly(thread).Fields.[f]
|
||||
let declaringType =
|
||||
ConcreteType.make activeAssembly.Name activeAssembly.Fields.[f].DeclaringType []
|
||||
|
||||
state, declaringType, activeAssembly.Fields.[f]
|
||||
| MetadataToken.MemberReference mr ->
|
||||
let state, assyName, field =
|
||||
IlMachineState.resolveMember loggerFactory (state.ActiveAssembly thread) mr state
|
||||
IlMachineState.resolveMember loggerFactory activeAssembly mr state
|
||||
|
||||
match field with
|
||||
| Choice1Of2 _method -> failwith "member reference was unexpectedly a method"
|
||||
| Choice2Of2 field -> state, assyName, field
|
||||
| Choice2Of2 field ->
|
||||
let parentTy = state.LoadedAssembly(assyName).Value.TypeDefs.[field.DeclaringType]
|
||||
|
||||
if parentTy.Generics.Length > 0 then
|
||||
failwith "oh no: generics"
|
||||
|
||||
let declaringType = ConcreteType.make assyName field.DeclaringType []
|
||||
state, declaringType, field
|
||||
| t -> failwith $"Unexpectedly asked to load from a non-field: {t}"
|
||||
|
||||
do
|
||||
let logger = loggerFactory.CreateLogger "Ldfld"
|
||||
let declaring = state.ActiveAssembly(thread).TypeDefs.[field.DeclaringType]
|
||||
let declaring = activeAssembly.TypeDefs.[field.DeclaringType]
|
||||
|
||||
logger.LogInformation (
|
||||
"Loading object field {FieldAssembly}.{FieldDeclaringType}.{FieldName} (type {FieldType})",
|
||||
@@ -423,7 +459,7 @@ module internal UnaryMetadataIlOp =
|
||||
let currentObj, state = IlMachineState.popEvalStack thread state
|
||||
|
||||
if field.Attributes.HasFlag FieldAttributes.Static then
|
||||
let staticField = state.Statics.[field.DeclaringType, assyName].[field.Name]
|
||||
let staticField = state.Statics.[declaringType].[field.Name]
|
||||
let state = state |> IlMachineState.pushToEvalStack staticField thread
|
||||
state, WhatWeDid.Executed
|
||||
else
|
||||
@@ -458,19 +494,23 @@ module internal UnaryMetadataIlOp =
|
||||
| Ldsfld ->
|
||||
let logger = loggerFactory.CreateLogger "Ldsfld"
|
||||
|
||||
let fieldHandle =
|
||||
match metadataToken with
|
||||
| MetadataToken.FieldDefinition f -> f
|
||||
| t -> failwith $"Unexpectedly asked to load from a non-field: {t}"
|
||||
|
||||
let activeAssy = state.ActiveAssembly thread
|
||||
|
||||
match activeAssy.Fields.TryGetValue fieldHandle with
|
||||
| false, _ -> failwith "TODO: Ldsfld - throw MissingFieldException"
|
||||
| true, field ->
|
||||
let field, declaringType =
|
||||
match metadataToken with
|
||||
| MetadataToken.FieldDefinition fieldHandle ->
|
||||
match activeAssy.Fields.TryGetValue fieldHandle with
|
||||
| false, _ -> failwith "TODO: Ldsfld - throw MissingFieldException"
|
||||
| true, field ->
|
||||
// FieldDefinition won't come on a generic type
|
||||
field, ConcreteType.make activeAssy.Name field.DeclaringType []
|
||||
| t -> failwith $"Unexpectedly asked to load from a non-field: {t}"
|
||||
|
||||
do
|
||||
let declaring = state.ActiveAssembly(thread).TypeDefs.[field.DeclaringType]
|
||||
let declaring =
|
||||
state.LoadedAssembly (declaringType.Assembly)
|
||||
|> Option.get
|
||||
|> fun a -> a.TypeDefs.[declaringType.Definition.Get]
|
||||
|
||||
logger.LogInformation (
|
||||
"Loading from static field {FieldAssembly}.{FieldDeclaringType}.{FieldName} (type {FieldType})",
|
||||
@@ -480,28 +520,35 @@ module internal UnaryMetadataIlOp =
|
||||
field.Signature
|
||||
)
|
||||
|
||||
match IlMachineState.loadClass loggerFactory field.DeclaringType activeAssy.Name thread state with
|
||||
match IlMachineState.loadClass loggerFactory declaringType thread state with
|
||||
| FirstLoadThis state -> state, WhatWeDid.SuspendedForClassInit
|
||||
| NothingToDo state ->
|
||||
|
||||
// TODO: generics
|
||||
let generics = ImmutableArray.Empty
|
||||
|
||||
let fieldValue, state =
|
||||
match state.Statics.TryGetValue ((field.DeclaringType, activeAssy.Name)) with
|
||||
match state.Statics.TryGetValue declaringType with
|
||||
| false, _ ->
|
||||
let state, newVal =
|
||||
IlMachineState.cliTypeZeroOf loggerFactory activeAssy field.Signature generics state
|
||||
IlMachineState.cliTypeZeroOf
|
||||
loggerFactory
|
||||
activeAssy
|
||||
field.Signature
|
||||
(declaringType.Generics |> ImmutableArray.CreateRange)
|
||||
state
|
||||
|
||||
newVal, state.SetStatic (field.DeclaringType, activeAssy.Name) field.Name newVal
|
||||
newVal, state.SetStatic declaringType field.Name newVal
|
||||
| true, v ->
|
||||
match v.TryGetValue field.Name with
|
||||
| true, v -> v, state
|
||||
| false, _ ->
|
||||
let state, newVal =
|
||||
IlMachineState.cliTypeZeroOf loggerFactory activeAssy field.Signature generics state
|
||||
IlMachineState.cliTypeZeroOf
|
||||
loggerFactory
|
||||
activeAssy
|
||||
field.Signature
|
||||
(declaringType.Generics |> ImmutableArray.CreateRange)
|
||||
state
|
||||
|
||||
newVal, state.SetStatic (field.DeclaringType, activeAssy.Name) field.Name newVal
|
||||
newVal, state.SetStatic declaringType field.Name newVal
|
||||
|
||||
do
|
||||
let logger = loggerFactory.CreateLogger "Ldsfld"
|
||||
@@ -527,51 +574,51 @@ module internal UnaryMetadataIlOp =
|
||||
| Ldelem -> failwith "TODO: Ldelem unimplemented"
|
||||
| Initobj -> failwith "TODO: Initobj unimplemented"
|
||||
| Ldsflda ->
|
||||
// TODO: check whether we should throw FieldAccessException
|
||||
let fieldHandle =
|
||||
match metadataToken with
|
||||
| MetadataToken.FieldDefinition f -> f
|
||||
| t -> failwith $"Unexpectedly asked to load a non-field: {t}"
|
||||
|
||||
let activeAssy = state.ActiveAssembly thread
|
||||
|
||||
match activeAssy.Fields.TryGetValue fieldHandle with
|
||||
| false, _ -> failwith "TODO: Ldsflda - throw MissingFieldException"
|
||||
| true, field ->
|
||||
match IlMachineState.loadClass loggerFactory field.DeclaringType activeAssy.Name thread state with
|
||||
| FirstLoadThis state -> state, WhatWeDid.SuspendedForClassInit
|
||||
| NothingToDo state ->
|
||||
// TODO: check whether we should throw FieldAccessException
|
||||
|
||||
if TypeDefn.isManaged field.Signature then
|
||||
let typeId = field.DeclaringType, activeAssy.Name
|
||||
let field, declaringType =
|
||||
match metadataToken with
|
||||
| MetadataToken.FieldDefinition fieldHandle ->
|
||||
match activeAssy.Fields.TryGetValue fieldHandle with
|
||||
| false, _ -> failwith "TODO: Ldsflda - throw MissingFieldException"
|
||||
| true, field ->
|
||||
// FieldDefinition is not found on generic type
|
||||
field, ConcreteType.make activeAssy.Name field.DeclaringType []
|
||||
| t -> failwith $"Unexpectedly asked to load a non-field: {t}"
|
||||
|
||||
let allocateStatic () =
|
||||
// TODO: generics
|
||||
let state, zero =
|
||||
IlMachineState.cliTypeZeroOf
|
||||
loggerFactory
|
||||
activeAssy
|
||||
field.Signature
|
||||
ImmutableArray.Empty
|
||||
state
|
||||
match IlMachineState.loadClass loggerFactory declaringType thread state with
|
||||
| FirstLoadThis state -> state, WhatWeDid.SuspendedForClassInit
|
||||
| NothingToDo state ->
|
||||
|
||||
state.SetStatic typeId field.Name zero
|
||||
|> IlMachineState.pushToEvalStack (CliType.ObjectRef None) thread
|
||||
|> Tuple.withRight WhatWeDid.Executed
|
||||
if TypeDefn.isManaged field.Signature then
|
||||
let allocateStatic () =
|
||||
let state, zero =
|
||||
IlMachineState.cliTypeZeroOf
|
||||
loggerFactory
|
||||
activeAssy
|
||||
field.Signature
|
||||
(declaringType.Generics |> ImmutableArray.CreateRange)
|
||||
state
|
||||
|
||||
match state.Statics.TryGetValue typeId with
|
||||
state.SetStatic declaringType field.Name zero
|
||||
|> IlMachineState.pushToEvalStack (CliType.ObjectRef None) thread
|
||||
|> Tuple.withRight WhatWeDid.Executed
|
||||
|
||||
match state.Statics.TryGetValue declaringType with
|
||||
| true, v ->
|
||||
match v.TryGetValue field.Name with
|
||||
| true, v ->
|
||||
match v.TryGetValue field.Name with
|
||||
| true, v ->
|
||||
IlMachineState.pushToEvalStack v thread state
|
||||
|> IlMachineState.advanceProgramCounter thread
|
||||
|> Tuple.withRight WhatWeDid.Executed
|
||||
| false, _ ->
|
||||
// Field has not yet been initialised.
|
||||
allocateStatic ()
|
||||
| false, _ -> allocateStatic ()
|
||||
else
|
||||
failwith "TODO: Ldsflda - push unmanaged pointer"
|
||||
IlMachineState.pushToEvalStack v thread state
|
||||
|> IlMachineState.advanceProgramCounter thread
|
||||
|> Tuple.withRight WhatWeDid.Executed
|
||||
| false, _ ->
|
||||
// Field has not yet been initialised.
|
||||
allocateStatic ()
|
||||
| false, _ -> allocateStatic ()
|
||||
else
|
||||
failwith "TODO: Ldsflda - push unmanaged pointer"
|
||||
| Ldftn -> failwith "TODO: Ldftn unimplemented"
|
||||
| Stobj -> failwith "TODO: Stobj unimplemented"
|
||||
| Constrained -> failwith "TODO: Constrained unimplemented"
|
||||
|
@@ -1,4 +1,4 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<WoofWareMyriadPluginsVersion>7.0.7</WoofWareMyriadPluginsVersion>
|
||||
@@ -15,8 +15,11 @@
|
||||
<Compile Include="CustomAttribute.fs" />
|
||||
<Compile Include="AssemblyReference.fs" />
|
||||
<Compile Include="EventDefn.fs" />
|
||||
<Compile Include="ComparableTypeDefinitionHandle.fs" />
|
||||
<Compile Include="ComparableSignatureHeader.fs" />
|
||||
<Compile Include="TypeDefn.fs" />
|
||||
<Compile Include="FieldInfo.fs" />
|
||||
<Compile Include="ConcreteType.fs" />
|
||||
<Compile Include="MethodInfo.fs" />
|
||||
<Compile Include="TypeInfo.fs" />
|
||||
<Compile Include="MethodSpec.fs" />
|
||||
|
Reference in New Issue
Block a user