diff --git a/CLAUDE.md b/CLAUDE.md index 3708265..74f3c1d 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -182,3 +182,47 @@ When encountering errors: 4. Add logging to see generic contexts: `failwithf "Failed to concretize: %A" typeDefn` 5. Check if you're in a generic method calling another generic method 6. Verify TypeRefs are being resolved in the correct assembly + +## Common Type System Patterns + +### Creating TypeDefn from Type Metadata + +When you need to create a `TypeDefn` from type metadata (e.g., from a `TypeInfo`), there's a common pattern that involves: +1. Resolving the base type to determine `SignatureTypeKind` +2. Creating the base `TypeDefn.FromDefinition` +3. For generic types, creating a `GenericInstantiation` with type parameters + +This pattern is implemented in `UnaryMetadataIlOp.lookupTypeDefn`. Example usage: +```fsharp +let state, typeDefn = + UnaryMetadataIlOp.lookupTypeDefn + baseClassTypes + state + activeAssembly + typeDefHandle +``` + +### Field Signature Comparison in Generic Contexts + +When comparing field signatures in generic contexts (e.g., when resolving member references), signatures must be concretized before comparison. This ensures generic type parameters are properly substituted: + +```fsharp +// Concretize both signatures before comparing +let state, concreteFieldSig = concretizeType ... fieldSig +let state, fieldSigConcrete = concretizeType ... fi.Signature +if fieldSigConcrete = concreteFieldSig then ... +``` + +### Static vs Instance Fields + +When constructing objects with `Newobj`: +- Only instance fields should be included in the object +- Static fields belong to the type, not instances +- Filter using: `field.Attributes.HasFlag FieldAttributes.Static` + +Example: +```fsharp +let instanceFields = + ctorType.Fields + |> List.filter (fun field -> not (field.Attributes.HasFlag FieldAttributes.Static)) +``` diff --git a/WoofWare.PawPrint.Domain/ComparableFieldDefinitionHandle.fs b/WoofWare.PawPrint.Domain/ComparableFieldDefinitionHandle.fs new file mode 100644 index 0000000..d17960f --- /dev/null +++ b/WoofWare.PawPrint.Domain/ComparableFieldDefinitionHandle.fs @@ -0,0 +1,37 @@ +namespace WoofWare.PawPrint + +open System +open System.Reflection.Metadata + +[] +[] +type ComparableFieldDefinitionHandle = + private + { + _Inner : FieldDefinitionHandle + } + + override this.Equals other = + match other with + | :? ComparableFieldDefinitionHandle as other -> this._Inner.GetHashCode () = other._Inner.GetHashCode () + | _ -> false + + override this.GetHashCode () : int = this._Inner.GetHashCode () + + interface IComparable with + member this.CompareTo (other : ComparableFieldDefinitionHandle) : int = + this._Inner.GetHashCode().CompareTo (other._Inner.GetHashCode ()) + + interface IComparable with + member this.CompareTo (other : obj) : int = + match other with + | :? ComparableFieldDefinitionHandle as other -> + (this :> IComparable).CompareTo other + | _ -> failwith "invalid comparison" + + static member Make (h : FieldDefinitionHandle) = + { + _Inner = h + } + + member this.Get = this._Inner diff --git a/WoofWare.PawPrint.Domain/ComparableTypeDefinitionHandle.fs b/WoofWare.PawPrint.Domain/ComparableTypeDefinitionHandle.fs index fc34bd6..e0ed060 100644 --- a/WoofWare.PawPrint.Domain/ComparableTypeDefinitionHandle.fs +++ b/WoofWare.PawPrint.Domain/ComparableTypeDefinitionHandle.fs @@ -11,7 +11,7 @@ type ComparableTypeDefinitionHandle = _Inner : TypeDefinitionHandle } - override this.Equals (other) = + override this.Equals other = match other with | :? ComparableTypeDefinitionHandle as other -> this._Inner.GetHashCode () = other._Inner.GetHashCode () | _ -> false diff --git a/WoofWare.PawPrint.Domain/FieldInfo.fs b/WoofWare.PawPrint.Domain/FieldInfo.fs index e014459..3ff4918 100644 --- a/WoofWare.PawPrint.Domain/FieldInfo.fs +++ b/WoofWare.PawPrint.Domain/FieldInfo.fs @@ -36,6 +36,8 @@ type FieldInfo<'typeGeneric, 'fieldGeneric when 'typeGeneric : comparison and 't Attributes : FieldAttributes } + member this.HasFieldRVA = this.Attributes.HasFlag FieldAttributes.HasFieldRVA + override this.ToString () : string = $"%s{this.DeclaringType.Assembly.Name}.{this.DeclaringType.Name}.%s{this.Name}" @@ -52,7 +54,7 @@ module FieldInfo = let fieldSig = def.DecodeSignature (TypeDefn.typeProvider assembly, ()) let declaringType = def.GetDeclaringType () let typeGenerics = mr.GetTypeDefinition(declaringType).GetGenericParameters().Count - let decType = mr.GetTypeDefinition (declaringType) + let decType = mr.GetTypeDefinition declaringType let declaringTypeNamespace = mr.GetString decType.Namespace let declaringTypeName = mr.GetString decType.Name diff --git a/WoofWare.PawPrint.Domain/TypeConcretisation.fs b/WoofWare.PawPrint.Domain/TypeConcretisation.fs index 67ec891..24f1525 100644 --- a/WoofWare.PawPrint.Domain/TypeConcretisation.fs +++ b/WoofWare.PawPrint.Domain/TypeConcretisation.fs @@ -1,5 +1,6 @@ namespace WoofWare.PawPrint +open System open System.Collections.Immutable open System.Reflection open System.Reflection.Metadata @@ -81,10 +82,72 @@ module AllConcreteTypes = toRet, newState +// Active patterns for matching concrete types + +[] +module ConcreteActivePatterns = + /// Active pattern to match primitive types from concrete type handles + let (|ConcretePrimitive|_|) (concreteTypes : AllConcreteTypes) (handle : ConcreteTypeHandle) = + match handle with + | ConcreteTypeHandle.Concrete id -> + match concreteTypes.Mapping |> Map.tryFind id with + | Some ct when ct.Namespace = "System" && ct.Generics.IsEmpty -> + match ct.Name with + | "Int32" -> Some PrimitiveType.Int32 + | "Int64" -> Some PrimitiveType.Int64 + | "Int16" -> Some PrimitiveType.Int16 + | "UInt32" -> Some PrimitiveType.UInt32 + | "UInt64" -> Some PrimitiveType.UInt64 + | "UInt16" -> Some PrimitiveType.UInt16 + | "Byte" -> Some PrimitiveType.Byte + | "SByte" -> Some PrimitiveType.SByte + | "Single" -> Some PrimitiveType.Single + | "Double" -> Some PrimitiveType.Double + | "String" -> Some PrimitiveType.String + | "Boolean" -> Some PrimitiveType.Boolean + | "Char" -> Some PrimitiveType.Char + | "Object" -> Some PrimitiveType.Object + | "IntPtr" -> Some PrimitiveType.IntPtr + | "UIntPtr" -> Some PrimitiveType.UIntPtr + | "TypedReference" -> Some PrimitiveType.TypedReference + | _ -> None + | _ -> None + | _ -> None + + /// Active pattern to match void type + let (|ConcreteVoid|_|) (concreteTypes : AllConcreteTypes) (handle : ConcreteTypeHandle) = + match handle with + | ConcreteTypeHandle.Concrete id -> + match concreteTypes.Mapping |> Map.tryFind id with + | Some ct when ct.Namespace = "System" && ct.Name = "Void" && ct.Generics.IsEmpty -> Some () + | _ -> None + | _ -> None + + /// Active pattern to match any concrete type by assembly/namespace/name and generics + let (|ConcreteType|_|) (concreteTypes : AllConcreteTypes) (handle : ConcreteTypeHandle) = + match handle with + | ConcreteTypeHandle.Concrete id -> + match concreteTypes.Mapping |> Map.tryFind id with + | Some ct -> Some (ct.Assembly.Name, ct.Namespace, ct.Name, ct.Generics) + | None -> None + | _ -> None + + /// Active pattern to match byref types + let (|ConcreteByref|_|) (handle : ConcreteTypeHandle) = + match handle with + | ConcreteTypeHandle.Byref inner -> Some inner + | _ -> None + + /// Active pattern to match pointer types + let (|ConcretePointer|_|) (handle : ConcreteTypeHandle) = + match handle with + | ConcreteTypeHandle.Pointer inner -> Some inner + | _ -> None + [] module TypeConcretization = - type ConcretizationContext = + type ConcretizationContext<'corelib> = { /// Types currently being processed (to detect cycles) InProgress : ImmutableDictionary @@ -92,7 +155,7 @@ module TypeConcretization = ConcreteTypes : AllConcreteTypes /// For resolving type references LoadedAssemblies : ImmutableDictionary - BaseTypes : BaseClassTypes + BaseTypes : BaseClassTypes<'corelib> } // Helper function to find existing types by assembly, namespace, name, and generics @@ -128,13 +191,13 @@ module TypeConcretization = // Helper function to create and add a ConcreteType to the context let private createAndAddConcreteType - (ctx : ConcretizationContext) + (ctx : ConcretizationContext<'corelib>) (assembly : AssemblyName) (definition : ComparableTypeDefinitionHandle) (ns : string) (name : string) (generics : ConcreteTypeHandle ImmutableArray) - : ConcreteTypeHandle * ConcretizationContext + : ConcreteTypeHandle * ConcretizationContext<'corelib> = let concreteType = { @@ -158,10 +221,10 @@ module TypeConcretization = let private loadAssemblyAndResolveTypeRef (loadAssembly : AssemblyName -> AssemblyReferenceHandle -> ImmutableDictionary * DumpedAssembly) - (ctx : ConcretizationContext) + (ctx : ConcretizationContext<'corelib>) (currentAssembly : AssemblyName) (typeRef : TypeRef) - : (DumpedAssembly * WoofWare.PawPrint.TypeInfo<_, _>) * ConcretizationContext + : (DumpedAssembly * WoofWare.PawPrint.TypeInfo<_, _>) * ConcretizationContext<'corelib> = let currentAssy = match ctx.LoadedAssemblies.TryGetValue currentAssembly.FullName with @@ -196,9 +259,9 @@ module TypeConcretization = | _ -> failwith "Unexpected resolution scope" let private concretizePrimitive - (ctx : ConcretizationContext) + (ctx : ConcretizationContext<'corelib>) (prim : PrimitiveType) - : ConcreteTypeHandle * ConcretizationContext + : ConcreteTypeHandle * ConcretizationContext<'corelib> = // Get the TypeInfo for this primitive from BaseClassTypes @@ -238,10 +301,10 @@ module TypeConcretization = ImmutableArray.Empty // Primitives have no generic parameters let private concretizeArray - (ctx : ConcretizationContext) + (ctx : ConcretizationContext<'corelib>) (elementHandle : ConcreteTypeHandle) (shape : 'a) - : ConcreteTypeHandle * ConcretizationContext + : ConcreteTypeHandle * ConcretizationContext<'corelib> = // Arrays are System.Array where T is the element type @@ -268,9 +331,9 @@ module TypeConcretization = (ImmutableArray.Create elementHandle) // Array has one generic parameter let private concretizeOneDimArray - (ctx : ConcretizationContext) + (ctx : ConcretizationContext<'corelib>) (elementHandle : ConcreteTypeHandle) - : ConcreteTypeHandle * ConcretizationContext + : ConcreteTypeHandle * ConcretizationContext<'corelib> = // One-dimensional arrays with lower bound 0 are also System.Array @@ -298,10 +361,10 @@ module TypeConcretization = (ImmutableArray.Create elementHandle) // Array has one generic parameter let concretizeTypeDefinition - (ctx : ConcretizationContext) + (ctx : ConcretizationContext<'corelib>) (assemblyName : AssemblyName) (typeDefHandle : ComparableTypeDefinitionHandle) - : ConcreteTypeHandle * ConcretizationContext + : ConcreteTypeHandle * ConcretizationContext<'corelib> = // Look up the type definition in the assembly @@ -336,10 +399,10 @@ module TypeConcretization = let private concretizeTypeReference (loadAssembly : AssemblyName -> AssemblyReferenceHandle -> ImmutableDictionary * DumpedAssembly) - (ctx : ConcretizationContext) + (ctx : ConcretizationContext<'corelib>) (currentAssembly : AssemblyName) (typeRef : TypeRef) - : ConcreteTypeHandle * ConcretizationContext + : ConcreteTypeHandle * ConcretizationContext<'corelib> = // Use the helper to load assembly and resolve the type reference let (targetAssy, typeInfo), ctx = @@ -358,14 +421,14 @@ module TypeConcretization = /// Concretize a type in a specific generic context let rec concretizeType - (ctx : ConcretizationContext) + (ctx : ConcretizationContext<'corelib>) (loadAssembly : AssemblyName -> AssemblyReferenceHandle -> (ImmutableDictionary * DumpedAssembly)) (assembly : AssemblyName) (typeGenerics : ConcreteTypeHandle ImmutableArray) (methodGenerics : ConcreteTypeHandle ImmutableArray) (typeDefn : TypeDefn) - : ConcreteTypeHandle * ConcretizationContext + : ConcreteTypeHandle * ConcretizationContext<'corelib> = let key = (assembly, typeDefn) @@ -394,13 +457,13 @@ module TypeConcretization = if index < typeGenerics.Length then typeGenerics.[index], ctx else - failwithf "Generic type parameter %d out of range" index + raise (IndexOutOfRangeException $"Generic type parameter %i{index}") | TypeDefn.GenericMethodParameter index -> if index < methodGenerics.Length then methodGenerics.[index], ctx else - failwithf "Generic method parameter %d out of range" index + raise (IndexOutOfRangeException $"Generic method parameter %i{index}") | TypeDefn.GenericInstantiation (genericDef, args) -> concretizeGenericInstantiation ctx loadAssembly assembly typeGenerics methodGenerics genericDef args @@ -455,15 +518,15 @@ module TypeConcretization = | _ -> failwithf "TODO: Concretization of %A not implemented" typeDefn and private concretizeGenericInstantiation - (ctx : ConcretizationContext) + (ctx : ConcretizationContext<'corelib>) (loadAssembly : - AssemblyName -> AssemblyReferenceHandle -> (ImmutableDictionary * DumpedAssembly)) + AssemblyName -> AssemblyReferenceHandle -> ImmutableDictionary * DumpedAssembly) (assembly : AssemblyName) (typeGenerics : ConcreteTypeHandle ImmutableArray) (methodGenerics : ConcreteTypeHandle ImmutableArray) (genericDef : TypeDefn) (args : ImmutableArray) - : ConcreteTypeHandle * ConcretizationContext + : ConcreteTypeHandle * ConcretizationContext<'corelib> = // First, concretize all type arguments let argHandles, ctxAfterArgs = @@ -614,17 +677,17 @@ module Concretization = /// Helper to concretize an array of types let private concretizeTypeArray - (ctx : TypeConcretization.ConcretizationContext) + (ctx : TypeConcretization.ConcretizationContext<'corelib>) (loadAssembly : - AssemblyName -> AssemblyReferenceHandle -> (ImmutableDictionary * DumpedAssembly)) + AssemblyName -> AssemblyReferenceHandle -> ImmutableDictionary * DumpedAssembly) (assembly : AssemblyName) (typeArgs : ConcreteTypeHandle ImmutableArray) (methodArgs : ConcreteTypeHandle ImmutableArray) (types : ImmutableArray) - : ImmutableArray * TypeConcretization.ConcretizationContext + : ImmutableArray * TypeConcretization.ConcretizationContext<'corelib> = - let handles = ImmutableArray.CreateBuilder (types.Length) + let handles = ImmutableArray.CreateBuilder types.Length let mutable ctx = ctx for i = 0 to types.Length - 1 do @@ -638,14 +701,14 @@ module Concretization = /// Helper to concretize a method signature let private concretizeMethodSignature - (ctx : TypeConcretization.ConcretizationContext) + (ctx : TypeConcretization.ConcretizationContext<'corelib>) (loadAssembly : - AssemblyName -> AssemblyReferenceHandle -> (ImmutableDictionary * DumpedAssembly)) + AssemblyName -> AssemblyReferenceHandle -> ImmutableDictionary * DumpedAssembly) (assembly : AssemblyName) (typeArgs : ConcreteTypeHandle ImmutableArray) (methodArgs : ConcreteTypeHandle ImmutableArray) (signature : TypeMethodSignature) - : TypeMethodSignature * TypeConcretization.ConcretizationContext + : TypeMethodSignature * TypeConcretization.ConcretizationContext<'corelib> = // Concretize return type diff --git a/WoofWare.PawPrint.Domain/TypeInfo.fs b/WoofWare.PawPrint.Domain/TypeInfo.fs index 56a48a0..ce6d6a6 100644 --- a/WoofWare.PawPrint.Domain/TypeInfo.fs +++ b/WoofWare.PawPrint.Domain/TypeInfo.fs @@ -150,6 +150,8 @@ type BaseClassTypes<'corelib> = RuntimeMethodHandle : TypeInfo RuntimeFieldHandle : TypeInfo RuntimeTypeHandle : TypeInfo + RuntimeFieldInfoStub : TypeInfo + RuntimeFieldHandleInternal : TypeInfo RuntimeType : TypeInfo Void : TypeInfo TypedReference : TypeInfo @@ -323,7 +325,7 @@ module TypeInfo = (Some (BaseTypeInfo.TypeDef typeDefinitionHandle)) let toTypeDefn - (corelib : BaseClassTypes<'corelib>) + (baseClassTypes : BaseClassTypes<'corelib>) (getName : 'corelib -> AssemblyName) (getTypeDef : 'corelib -> TypeDefinitionHandle -> TypeInfo<'generic, 'field>) (getTypeRef : 'corelib -> TypeReferenceHandle -> TypeInfo<'generic, 'field>) @@ -331,7 +333,7 @@ module TypeInfo = : TypeDefn = let stk = - match resolveBaseType corelib getName getTypeDef getTypeRef ty.Assembly ty.BaseType with + match resolveBaseType baseClassTypes getName getTypeDef getTypeRef ty.Assembly ty.BaseType with | ResolvedBaseType.Enum | ResolvedBaseType.ValueType -> SignatureTypeKind.ValueType | ResolvedBaseType.Object diff --git a/WoofWare.PawPrint.Domain/WoofWare.PawPrint.Domain.fsproj b/WoofWare.PawPrint.Domain/WoofWare.PawPrint.Domain.fsproj index c1e079b..b4e15c3 100644 --- a/WoofWare.PawPrint.Domain/WoofWare.PawPrint.Domain.fsproj +++ b/WoofWare.PawPrint.Domain/WoofWare.PawPrint.Domain.fsproj @@ -14,6 +14,7 @@ + diff --git a/WoofWare.PawPrint.Test/TestPureCases.fs b/WoofWare.PawPrint.Test/TestPureCases.fs index 9ab141d..adab284 100644 --- a/WoofWare.PawPrint.Test/TestPureCases.fs +++ b/WoofWare.PawPrint.Test/TestPureCases.fs @@ -19,6 +19,12 @@ module TestPureCases = NativeImpls = MockEnv.make () LocalVariablesOfMain = None } + { + FileName = "InitializeArray.cs" + ExpectedReturnCode = 0 + NativeImpls = MockEnv.make () + LocalVariablesOfMain = None + } { FileName = "GenericEdgeCases.cs" ExpectedReturnCode = 0 @@ -73,6 +79,12 @@ module TestPureCases = NativeImpls = MockEnv.make () LocalVariablesOfMain = None } + { + FileName = "LdtokenField.cs" + ExpectedReturnCode = 0 + NativeImpls = MockEnv.make () + LocalVariablesOfMain = None + } ] let cases : TestCase list = diff --git a/WoofWare.PawPrint.Test/WoofWare.PawPrint.Test.fsproj b/WoofWare.PawPrint.Test/WoofWare.PawPrint.Test.fsproj index cfa8eeb..bb485bd 100644 --- a/WoofWare.PawPrint.Test/WoofWare.PawPrint.Test.fsproj +++ b/WoofWare.PawPrint.Test/WoofWare.PawPrint.Test.fsproj @@ -21,6 +21,8 @@ + + diff --git a/WoofWare.PawPrint.Test/sourcesPure/InitializeArray.cs b/WoofWare.PawPrint.Test/sourcesPure/InitializeArray.cs new file mode 100644 index 0000000..d05854a --- /dev/null +++ b/WoofWare.PawPrint.Test/sourcesPure/InitializeArray.cs @@ -0,0 +1,19 @@ +using System.Linq; + +namespace HelloWorldApp +{ + class Program + { + static int Main(string[] args) + { + int[] array = new[] { 1, 2, 3 }; + + if (array.Sum() != 60) + { + return 1; + } + + return 0; + } + } +} diff --git a/WoofWare.PawPrint.Test/sourcesPure/Ldind.cs b/WoofWare.PawPrint.Test/sourcesPure/Ldind.cs index 36fe163..4a3dcba 100644 --- a/WoofWare.PawPrint.Test/sourcesPure/Ldind.cs +++ b/WoofWare.PawPrint.Test/sourcesPure/Ldind.cs @@ -39,7 +39,7 @@ unsafe class LdindTest failures += TestTruncation(); // Test with managed pointers (ref) - // failures += TestManagedPointers(); + failures += TestManagedPointers(); // Test Ldind.i (native int) failures += TestLdindI(); @@ -325,7 +325,10 @@ unsafe class LdindTest } // Test with array element - int[] array = { 10, 20, 30 }; + int[] array = new int[3]; + array[0] = 10; + array[1] = 20; + array[2] = 30; ref int element = ref array[1]; if (element != 20) { diff --git a/WoofWare.PawPrint.Test/sourcesPure/LdtokenField.cs b/WoofWare.PawPrint.Test/sourcesPure/LdtokenField.cs new file mode 100644 index 0000000..9e8e6ee --- /dev/null +++ b/WoofWare.PawPrint.Test/sourcesPure/LdtokenField.cs @@ -0,0 +1,128 @@ +using System; +using System.Linq; +using System.Reflection; +using System.Runtime.CompilerServices; + +namespace LdtokenFieldTest +{ + class Program + { + // Various field types to test ldtoken with + public static int StaticIntField = 42; + public string InstanceStringField = "test"; + private readonly double PrivateReadonlyField = 3.14; + internal decimal InternalField; + protected bool ProtectedField; + public static readonly DateTime StaticReadonlyField = DateTime.MinValue; + + // Generic type fields + public GenericClass.NestedClass GenericField; + + static int Main(string[] args) + { + int testsFailed = 0; + + // Test 1: Static field via FieldInfo + FieldInfo staticField = typeof(Program).GetField(nameof(StaticIntField)); + if (staticField == null || staticField.FieldType != typeof(int)) + { + testsFailed++; + } + + // Test 2: Instance field via FieldInfo + FieldInfo instanceField = typeof(Program).GetField(nameof(InstanceStringField)); + if (instanceField == null || instanceField.FieldType != typeof(string)) + { + testsFailed++; + } + + // Test 3: Private field via FieldInfo with binding flags + FieldInfo privateField = typeof(Program).GetField("PrivateReadonlyField", + BindingFlags.NonPublic | BindingFlags.Instance); + if (privateField == null || privateField.FieldType != typeof(double)) + { + testsFailed++; + } + + // Test 4: Using RuntimeFieldHandle directly + RuntimeFieldHandle handle = staticField.FieldHandle; + FieldInfo fieldFromHandle = FieldInfo.GetFieldFromHandle(handle); + if (!ReferenceEquals(fieldFromHandle, staticField)) + { + testsFailed++; + } + + // Test 5: Field from generic type + Type genericType = typeof(GenericClass<>); + FieldInfo genericFieldInfo = genericType.GetField("GenericField"); + if (genericFieldInfo == null) + { + testsFailed++; + } + + // Test 6: Field from nested type + Type nestedType = typeof(OuterClass.InnerClass); + FieldInfo nestedField = nestedType.GetField("NestedField"); + if (nestedField == null || nestedField.FieldType != typeof(int)) + { + testsFailed++; + } + + // Test 7: Field handle with generic context + Type constructedGeneric = typeof(GenericClass); + FieldInfo constructedField = constructedGeneric.GetField("GenericField"); + RuntimeFieldHandle genericHandle = constructedField.FieldHandle; + FieldInfo reconstructed = FieldInfo.GetFieldFromHandle(genericHandle, constructedGeneric.TypeHandle); + if (reconstructed.DeclaringType != constructedGeneric) + { + testsFailed++; + } + + // Test 8: Struct field + Type structType = typeof(TestStruct); + FieldInfo structField = structType.GetField("StructField"); + if (structField == null || structField.FieldType != typeof(long)) + { + testsFailed++; + } + + // Test 9: Volatile field + FieldInfo volatileField = typeof(VolatileFieldClass).GetField("VolatileField"); + if (volatileField == null || !volatileField.GetRequiredCustomModifiers().Any(t => t == typeof(IsVolatile))) + { + testsFailed++; + } + + return testsFailed; + } + } + + // Supporting types for testing + public class GenericClass + { + public T GenericField; + + public class NestedClass + { + public U NestedGenericField; + } + } + + public class OuterClass + { + public class InnerClass + { + public int NestedField = 100; + } + } + + public struct TestStruct + { + public long StructField; + } + + public class VolatileFieldClass + { + public volatile int VolatileField; + } +} diff --git a/WoofWare.PawPrint/AbstractMachine.fs b/WoofWare.PawPrint/AbstractMachine.fs index 08374cf..c063560 100644 --- a/WoofWare.PawPrint/AbstractMachine.fs +++ b/WoofWare.PawPrint/AbstractMachine.fs @@ -125,42 +125,55 @@ module AbstractMachine = targetType.Namespace, targetType.Name, instruction.ExecutingMethod.Name, - instruction.ExecutingMethod.RawSignature.ParameterTypes, - instruction.ExecutingMethod.RawSignature.ReturnType + instruction.ExecutingMethod.Signature.ParameterTypes, + instruction.ExecutingMethod.Signature.ReturnType with | "System.Private.CoreLib", "System", "Environment", "GetProcessorCount", [], - TypeDefn.PrimitiveType PrimitiveType.Int32 -> + ConcretePrimitive state.ConcreteTypes PrimitiveType.Int32 -> let env = ISystem_Environment_Env.get impls env.GetProcessorCount thread state | "System.Private.CoreLib", "System", "Environment", "_Exit", - [ TypeDefn.PrimitiveType PrimitiveType.Int32 ], - TypeDefn.Void -> + [ ConcretePrimitive state.ConcreteTypes PrimitiveType.Int32 ], + ConcreteVoid state.ConcreteTypes -> let env = ISystem_Environment_Env.get impls env._Exit thread state | "System.Private.CoreLib", "System.Threading", "Monitor", "ReliableEnter", - [ TypeDefn.PrimitiveType PrimitiveType.Object - TypeDefn.Byref (TypeDefn.PrimitiveType PrimitiveType.Boolean) ], - TypeDefn.Void -> + [ ConcretePrimitive state.ConcreteTypes PrimitiveType.Object + ConcreteByref (ConcretePrimitive state.ConcreteTypes PrimitiveType.Boolean) ], + ConcreteVoid state.ConcreteTypes -> let env = ISystem_Threading_Monitor_Env.get impls env.ReliableEnter thread state | "System.Private.CoreLib", "System.Threading", "Monitor", "Exit", - [ TypeDefn.PrimitiveType PrimitiveType.Object ], - TypeDefn.Void -> + [ ConcretePrimitive state.ConcreteTypes PrimitiveType.Object ], + ConcreteVoid state.ConcreteTypes -> let env = ISystem_Threading_Monitor_Env.get impls env.Exit thread state + | "System.Private.CoreLib", + "System", + "Type", + "GetField", + [ ConcretePrimitive state.ConcreteTypes PrimitiveType.String ; ty ], + ret -> + let ty = AllConcreteTypes.lookup ty state.ConcreteTypes |> Option.get + let ret = AllConcreteTypes.lookup ret state.ConcreteTypes |> Option.get + + match ty.Namespace, ty.Name, ty.Generics.IsEmpty, ret.Namespace, ret.Name, ret.Generics.IsEmpty with + | "System.Reflection", "BindingFlags", true, "System.Reflection", "FieldInfo", true -> + failwith "TODO: GetField" + | _ -> failwith "unexpected signature for Type.GetField" | assy, ns, typeName, methName, param, retType -> failwith $"TODO: tried to IL-interpret a method in {assy} {ns}.{typeName} named {methName} with no implementation; {param} -> {retType}" diff --git a/WoofWare.PawPrint/BasicCliType.fs b/WoofWare.PawPrint/BasicCliType.fs index e3b67aa..7ae763c 100644 --- a/WoofWare.PawPrint/BasicCliType.fs +++ b/WoofWare.PawPrint/BasicCliType.fs @@ -396,7 +396,7 @@ module CliType = let loadAssembly (assyName : AssemblyName) (ref : AssemblyReferenceHandle) - : (ImmutableDictionary * DumpedAssembly) + : ImmutableDictionary * DumpedAssembly = match assemblies.TryGetValue assyName.FullName with | true, currentAssy -> diff --git a/WoofWare.PawPrint/Corelib.fs b/WoofWare.PawPrint/Corelib.fs index 7fef4ce..62cf81f 100644 --- a/WoofWare.PawPrint/Corelib.fs +++ b/WoofWare.PawPrint/Corelib.fs @@ -134,6 +134,21 @@ module Corelib = |> Seq.choose (fun (KeyValue (_, v)) -> if v.Name = "UIntPtr" then Some v else None) |> Seq.exactlyOne + let runtimeFieldInfoStubType = + corelib.TypeDefs + |> Seq.choose (fun (KeyValue (_, v)) -> if v.Name = "RuntimeFieldInfoStub" then Some v else None) + |> Seq.exactlyOne + + let runtimeFieldHandleInternalType = + corelib.TypeDefs + |> Seq.choose (fun (KeyValue (_, v)) -> + if v.Name = "RuntimeFieldHandleInternal" then + Some v + else + None + ) + |> Seq.exactlyOne + { Corelib = corelib String = stringType @@ -157,6 +172,8 @@ module Corelib = RuntimeTypeHandle = runtimeTypeHandleType RuntimeMethodHandle = runtimeMethodHandleType RuntimeFieldHandle = runtimeFieldHandleType + RuntimeFieldInfoStub = runtimeFieldInfoStubType + RuntimeFieldHandleInternal = runtimeFieldHandleInternalType RuntimeType = runtimeTypeType Void = voidType TypedReference = typedReferenceType diff --git a/WoofWare.PawPrint/FieldHandleRegistry.fs b/WoofWare.PawPrint/FieldHandleRegistry.fs new file mode 100644 index 0000000..b90a955 --- /dev/null +++ b/WoofWare.PawPrint/FieldHandleRegistry.fs @@ -0,0 +1,111 @@ +namespace WoofWare.PawPrint + +open System.Reflection +open System.Reflection.Metadata + +type FieldHandle = + private + { + AssemblyFullName : string + DeclaringType : ConcreteTypeHandle + FieldHandle : ComparableFieldDefinitionHandle + } + +type FieldHandleRegistry = + private + { + FieldHandleToId : Map + FieldHandleToField : Map + FieldToHandle : Map + NextHandle : int64 + } + +[] +module FieldHandleRegistry = + let empty () = + { + FieldHandleToField = Map.empty + FieldToHandle = Map.empty + FieldHandleToId = Map.empty + NextHandle = 1L + } + + /// Returns a (struct) System.RuntimeFieldHandle, with its contents (reference type) freshly allocated if necessary. + let getOrAllocate + (baseClassTypes : BaseClassTypes<'corelib>) + (allocState : 'allocState) + (allocate : (string * CliType) list -> 'allocState -> ManagedHeapAddress * 'allocState) + (declaringAssy : AssemblyName) + (declaringType : ConcreteTypeHandle) + (handle : FieldDefinitionHandle) + (reg : FieldHandleRegistry) + : CliType * FieldHandleRegistry * 'allocState + = + + let runtimeFieldHandle (runtimeFieldInfoStub : ManagedHeapAddress) = + // RuntimeFieldHandle is a struct; it contains one field, an IRuntimeFieldInfo + // https://github.com/dotnet/runtime/blob/1d1bf92fcf43aa6981804dc53c5174445069c9e4/src/coreclr/System.Private.CoreLib/src/System/RuntimeHandles.cs#L1048 + // In practice we expect to use RuntimeFieldInfoStub for that IRuntimeFieldInfo: + // https://github.com/dotnet/runtime/blob/1d1bf92fcf43aa6981804dc53c5174445069c9e4/src/coreclr/System.Private.CoreLib/src/System/RuntimeHandles.cs#L1157 + let runtimeFieldHandleType = baseClassTypes.RuntimeFieldHandle + let field = runtimeFieldHandleType.Fields |> List.exactlyOne + + if field.Name <> "m_ptr" then + failwith $"unexpected field name %s{field.Name} for BCL type RuntimeFieldHandle" + + { + Fields = [ "m_ptr", CliType.ofManagedObject runtimeFieldInfoStub ] + } + |> CliType.ValueType + + let handle = + { + AssemblyFullName = declaringAssy.FullName + FieldHandle = ComparableFieldDefinitionHandle.Make handle + DeclaringType = declaringType + } + + match Map.tryFind handle reg.FieldToHandle with + | Some v -> runtimeFieldHandle v, reg, allocState + | None -> + + let newHandle = reg.NextHandle + + let runtimeFieldHandleInternal = + let field = baseClassTypes.RuntimeFieldHandleInternal.Fields |> List.exactlyOne + + if field.Name <> "m_handle" then + failwith $"unexpected field name %s{field.Name} for BCL type RuntimeFieldHandleInternal" + + match field.Signature with + | TypeDefn.PrimitiveType PrimitiveType.IntPtr -> () + | s -> failwith $"bad sig: {s}" + + { + Fields = [ "m_handle", CliType.RuntimePointer (CliRuntimePointer.Unmanaged newHandle) ] + } + |> CliType.ValueType + + let runtimeFieldInfoStub = + [ + // If we ever implement a GC, something should change here + "m_keepalive", CliType.ObjectRef None + "m_c", CliType.ObjectRef None + "m_d", CliType.ObjectRef None + "m_b", CliType.Numeric (CliNumericType.Int32 0) + "m_e", CliType.ObjectRef None + // RuntimeFieldHandleInternal: https://github.com/dotnet/runtime/blob/1d1bf92fcf43aa6981804dc53c5174445069c9e4/src/coreclr/System.Private.CoreLib/src/System/RuntimeHandles.cs#L1048 + "m_fieldHandle", runtimeFieldHandleInternal + ] + + let alloc, state = allocate runtimeFieldInfoStub allocState + + let reg = + { + FieldHandleToField = reg.FieldHandleToField |> Map.add alloc handle + FieldToHandle = reg.FieldToHandle |> Map.add handle alloc + FieldHandleToId = reg.FieldHandleToId |> Map.add handle newHandle + NextHandle = reg.NextHandle + 1L + } + + runtimeFieldHandle alloc, reg, state diff --git a/WoofWare.PawPrint/IlMachineState.fs b/WoofWare.PawPrint/IlMachineState.fs index 2ea8d11..6ba0327 100644 --- a/WoofWare.PawPrint/IlMachineState.fs +++ b/WoofWare.PawPrint/IlMachineState.fs @@ -1,5 +1,6 @@ namespace WoofWare.PawPrint +open System open System.Collections.Immutable open System.IO open System.Reflection @@ -26,6 +27,7 @@ type IlMachineState = _Statics : ImmutableDictionary> DotnetRuntimeDirs : string ImmutableArray TypeHandles : TypeHandleRegistry + FieldHandles : FieldHandleRegistry } member this.WithTypeBeginInit (thread : ThreadId) (ty : ConcreteTypeHandle) = @@ -147,6 +149,47 @@ type StateLoadResult = module IlMachineState = type private Dummy = class end + let concretizeType + (baseClassTypes : BaseClassTypes) + (state : IlMachineState) + (declaringAssembly : AssemblyName) + (typeGenerics : ImmutableArray) + (methodGenerics : ImmutableArray) + (ty : TypeDefn) + : IlMachineState * ConcreteTypeHandle + = + let ctx = + { + TypeConcretization.ConcretizationContext.InProgress = ImmutableDictionary.Empty + TypeConcretization.ConcretizationContext.ConcreteTypes = state.ConcreteTypes + TypeConcretization.ConcretizationContext.LoadedAssemblies = state._LoadedAssemblies + TypeConcretization.ConcretizationContext.BaseTypes = baseClassTypes + } + + let handle, ctx = + TypeConcretization.concretizeType + ctx + (fun assyName ref -> + let currentAssy = state.LoadedAssembly assyName |> Option.get + + let targetAssy = + currentAssy.AssemblyReferences.[ref].Name |> state.LoadedAssembly |> Option.get + + state._LoadedAssemblies, targetAssy + ) + declaringAssembly + typeGenerics + methodGenerics + ty + + let state = + { state with + _LoadedAssemblies = ctx.LoadedAssemblies + ConcreteTypes = ctx.ConcreteTypes + } + + state, handle + /// /// Create a new IlMachineState which has loaded the given assembly. /// This involves reading assemblies from the disk and doing a complete parse of them, so it might be quite slow! @@ -267,7 +310,7 @@ module IlMachineState = let rec resolveTypeFromDefn (loggerFactory : ILoggerFactory) - (corelib : BaseClassTypes) + (baseClassTypes : BaseClassTypes) (ty : TypeDefn) (typeGenericArgs : ImmutableArray) (methodGenericArgs : ImmutableArray) @@ -283,14 +326,21 @@ module IlMachineState = (state, args) ||> Seq.fold (fun state arg -> let state, assy, resolvedArg = - resolveTypeFromDefn loggerFactory corelib arg typeGenericArgs methodGenericArgs assy state + resolveTypeFromDefn + loggerFactory + baseClassTypes + arg + typeGenericArgs + methodGenericArgs + assy + state // If the resolved argument has generics, create a GenericInstantiation // Otherwise, create a FromDefinition let preservedArg = let baseType = resolvedArg.BaseType - |> DumpedAssembly.resolveBaseType corelib state._LoadedAssemblies assy.Name + |> DumpedAssembly.resolveBaseType baseClassTypes state._LoadedAssemblies assy.Name let signatureTypeKind = match baseType with @@ -322,7 +372,7 @@ module IlMachineState = ) let args' = args'.ToImmutable () - resolveTypeFromDefn loggerFactory corelib generic args' methodGenericArgs assy state + resolveTypeFromDefn loggerFactory baseClassTypes generic args' methodGenericArgs assy state | TypeDefn.FromDefinition (defn, assy, _typeKind) -> let assy = state._LoadedAssemblies.[assy] @@ -339,39 +389,39 @@ module IlMachineState = | TypeDefn.PrimitiveType prim -> let ty = match prim with - | PrimitiveType.Boolean -> corelib.Boolean - | PrimitiveType.Char -> corelib.Char - | PrimitiveType.SByte -> corelib.SByte - | PrimitiveType.Byte -> corelib.Byte - | PrimitiveType.Int16 -> corelib.Int16 - | PrimitiveType.UInt16 -> corelib.UInt16 - | PrimitiveType.Int32 -> corelib.Int32 - | PrimitiveType.UInt32 -> corelib.UInt32 - | PrimitiveType.Int64 -> corelib.Int64 - | PrimitiveType.UInt64 -> corelib.UInt64 - | PrimitiveType.Single -> corelib.Single - | PrimitiveType.Double -> corelib.Double - | PrimitiveType.String -> corelib.String + | PrimitiveType.Boolean -> baseClassTypes.Boolean + | PrimitiveType.Char -> baseClassTypes.Char + | PrimitiveType.SByte -> baseClassTypes.SByte + | PrimitiveType.Byte -> baseClassTypes.Byte + | PrimitiveType.Int16 -> baseClassTypes.Int16 + | PrimitiveType.UInt16 -> baseClassTypes.UInt16 + | PrimitiveType.Int32 -> baseClassTypes.Int32 + | PrimitiveType.UInt32 -> baseClassTypes.UInt32 + | PrimitiveType.Int64 -> baseClassTypes.Int64 + | PrimitiveType.UInt64 -> baseClassTypes.UInt64 + | PrimitiveType.Single -> baseClassTypes.Single + | PrimitiveType.Double -> baseClassTypes.Double + | PrimitiveType.String -> baseClassTypes.String | PrimitiveType.TypedReference -> failwith "todo" | PrimitiveType.IntPtr -> failwith "todo" | PrimitiveType.UIntPtr -> failwith "todo" | PrimitiveType.Object -> failwith "todo" |> TypeInfo.mapGeneric (fun _ -> failwith "none of these types are generic") - state, corelib.Corelib, ty + state, baseClassTypes.Corelib, ty | TypeDefn.GenericTypeParameter param -> let arg = typeGenericArgs.[param] // TODO: this assembly is probably wrong? - resolveTypeFromDefn loggerFactory corelib arg typeGenericArgs methodGenericArgs assy state + resolveTypeFromDefn loggerFactory baseClassTypes arg typeGenericArgs methodGenericArgs assy state | TypeDefn.GenericMethodParameter param -> let arg = methodGenericArgs.[param] // TODO: this assembly is probably wrong? - resolveTypeFromDefn loggerFactory corelib arg typeGenericArgs methodGenericArgs assy state + resolveTypeFromDefn loggerFactory baseClassTypes arg typeGenericArgs methodGenericArgs assy state | s -> failwith $"TODO: resolveTypeFromDefn unimplemented for {s}" let resolveTypeFromSpec (loggerFactory : ILoggerFactory) - (corelib : BaseClassTypes) + (baseClassTypes : BaseClassTypes) (ty : TypeSpecificationHandle) (assy : DumpedAssembly) (typeGenericArgs : TypeDefn ImmutableArray) @@ -380,12 +430,12 @@ module IlMachineState = : IlMachineState * DumpedAssembly * WoofWare.PawPrint.TypeInfo = let sign = assy.TypeSpecs.[ty].Signature - resolveTypeFromDefn loggerFactory corelib sign typeGenericArgs methodGenericArgs assy state + resolveTypeFromDefn loggerFactory baseClassTypes sign typeGenericArgs methodGenericArgs assy state /// Resolve a TypeSpecification using concrete type handles from execution context let resolveTypeFromSpecConcrete (loggerFactory : ILoggerFactory) - (corelib : BaseClassTypes) + (baseClassTypes : BaseClassTypes) (ty : TypeSpecificationHandle) (assy : DumpedAssembly) (typeGenericArgs : ConcreteTypeHandle ImmutableArray) @@ -399,28 +449,36 @@ module IlMachineState = let typeGenericArgsAsDefn = typeGenericArgs |> Seq.map (fun handle -> - Concretization.concreteHandleToTypeDefn corelib handle state.ConcreteTypes state._LoadedAssemblies + Concretization.concreteHandleToTypeDefn + baseClassTypes + handle + state.ConcreteTypes + state._LoadedAssemblies ) |> ImmutableArray.CreateRange let methodGenericArgsAsDefn = methodGenericArgs |> Seq.map (fun handle -> - Concretization.concreteHandleToTypeDefn corelib handle state.ConcreteTypes state._LoadedAssemblies + Concretization.concreteHandleToTypeDefn + baseClassTypes + handle + state.ConcreteTypes + state._LoadedAssemblies ) |> ImmutableArray.CreateRange - resolveTypeFromDefn loggerFactory corelib sign typeGenericArgsAsDefn methodGenericArgsAsDefn assy state + resolveTypeFromDefn loggerFactory baseClassTypes sign typeGenericArgsAsDefn methodGenericArgsAsDefn assy state /// Get zero value for a type that's already been concretized let cliTypeZeroOfHandle (state : IlMachineState) - (corelib : BaseClassTypes) + (baseClassTypes : BaseClassTypes) (handle : ConcreteTypeHandle) : CliType * IlMachineState = let zero, updatedConcreteTypes = - CliType.zeroOf state.ConcreteTypes state._LoadedAssemblies corelib handle + CliType.zeroOf state.ConcreteTypes state._LoadedAssemblies baseClassTypes handle let newState = { state with @@ -440,25 +498,25 @@ module IlMachineState = /// Concretize a ConcreteType to get a ConcreteTypeHandle for static field access let concretizeFieldDeclaringType (loggerFactory : ILoggerFactory) - (corelib : BaseClassTypes) + (baseClassTypes : BaseClassTypes<'corelib>) (declaringType : ConcreteType) (state : IlMachineState) : ConcreteTypeHandle * IlMachineState = // Create a concretization context from the current state - let ctx : TypeConcretization.ConcretizationContext = + let ctx : TypeConcretization.ConcretizationContext<'corelib> = { InProgress = ImmutableDictionary.Empty ConcreteTypes = state.ConcreteTypes LoadedAssemblies = state._LoadedAssemblies - BaseTypes = corelib + BaseTypes = baseClassTypes } // Helper function to get assembly from reference let loadAssembly (currentAssembly : AssemblyName) (assyRef : AssemblyReferenceHandle) - : (ImmutableDictionary * DumpedAssembly) + : ImmutableDictionary * DumpedAssembly = let assyToLoad = match state.LoadedAssembly currentAssembly with @@ -537,7 +595,7 @@ module IlMachineState = /// Get zero value for a TypeDefn, concretizing it first let cliTypeZeroOf (loggerFactory : ILoggerFactory) - (corelib : BaseClassTypes) + (baseClassTypes : BaseClassTypes) (assy : DumpedAssembly) (ty : TypeDefn) (typeGenerics : ConcreteTypeHandle ImmutableArray) @@ -554,39 +612,11 @@ module IlMachineState = else state.WithLoadedAssembly assy.Name assy - let ctx = - { - TypeConcretization.ConcretizationContext.InProgress = ImmutableDictionary.Empty - TypeConcretization.ConcretizationContext.ConcreteTypes = state.ConcreteTypes - TypeConcretization.ConcretizationContext.LoadedAssemblies = state._LoadedAssemblies - TypeConcretization.ConcretizationContext.BaseTypes = corelib - } - - let handle, newCtx = - TypeConcretization.concretizeType - ctx - (fun assyName ref -> - // Helper to get assembly from reference - let currentAssy = state.LoadedAssembly (assyName) |> Option.get - - let targetAssy = - currentAssy.AssemblyReferences.[ref].Name |> state.LoadedAssembly |> Option.get - - state._LoadedAssemblies, targetAssy - ) - assy.Name - typeGenerics - methodGenerics - ty - - let state = - { state with - ConcreteTypes = newCtx.ConcreteTypes - _LoadedAssemblies = newCtx.LoadedAssemblies - } + let state, handle = + concretizeType baseClassTypes state assy.Name typeGenerics methodGenerics ty // Now get the zero value - let zero, state = cliTypeZeroOfHandle state corelib handle + let zero, state = cliTypeZeroOfHandle state baseClassTypes handle state, zero let pushToEvalStack' (o : EvalStackValue) (thread : ThreadId) (state : IlMachineState) = @@ -656,7 +686,7 @@ module IlMachineState = /// There might be no stack frame to return to, so you might get None. let returnStackFrame (loggerFactory : ILoggerFactory) - (corelib : BaseClassTypes) + (baseClassTypes : BaseClassTypes) (currentThread : ThreadId) (state : IlMachineState) : IlMachineState option @@ -696,7 +726,7 @@ module IlMachineState = let resolvedBaseType = DumpedAssembly.resolveBaseType - corelib + baseClassTypes state._LoadedAssemblies constructed.Type.Assembly constructed.Type.BaseType @@ -728,7 +758,7 @@ module IlMachineState = // | TypeDefn.Void -> state | retType -> // TODO: generics - let zero, state = cliTypeZeroOfHandle state corelib retType + let zero, state = cliTypeZeroOfHandle state baseClassTypes retType let toPush = EvalStackValue.toCliTypeCoerced zero retVal @@ -741,7 +771,7 @@ module IlMachineState = let concretizeMethodWithTypeGenerics (loggerFactory : ILoggerFactory) - (corelib : BaseClassTypes) + (baseClassTypes : BaseClassTypes) (typeGenerics : ImmutableArray) (methodToCall : WoofWare.PawPrint.MethodInfo) (methodGenerics : TypeDefn ImmutableArray option) @@ -762,45 +792,17 @@ module IlMachineState = let mutable state = state for i = 0 to generics.Length - 1 do - let ctx = - { - TypeConcretization.ConcretizationContext.InProgress = ImmutableDictionary.Empty - TypeConcretization.ConcretizationContext.ConcreteTypes = state.ConcreteTypes - TypeConcretization.ConcretizationContext.LoadedAssemblies = state._LoadedAssemblies - TypeConcretization.ConcretizationContext.BaseTypes = corelib - } - - // When concretizing method generic arguments, we need to handle the case where - // the generic argument itself doesn't reference method parameters - try - let handle, newCtx = - TypeConcretization.concretizeType - ctx - (fun assyName ref -> - let currentAssy = state.LoadedAssembly assyName |> Option.get - - let targetAssy = - currentAssy.AssemblyReferences.[ref].Name |> state.LoadedAssembly |> Option.get - - state._LoadedAssemblies, targetAssy - ) - callingAssembly - typeGenerics - currentExecutingMethodGenerics - generics.[i] - - state <- - { state with - ConcreteTypes = newCtx.ConcreteTypes - } - - handles.Add handle - with ex -> - failwithf - "Failed to concretize method generic argument %d: %A. Exception: %s" - i + let state2, handle = + concretizeType + baseClassTypes + state + callingAssembly + typeGenerics + currentExecutingMethodGenerics generics.[i] - ex.Message + + state <- state2 + handles.Add handle state, handles.ToImmutable () @@ -825,7 +827,7 @@ module IlMachineState = failwithf "Current assembly %s not loaded when trying to resolve reference" assyName.FullName ) state._LoadedAssemblies - corelib + baseClassTypes methodToCall typeGenerics concretizedMethodGenerics @@ -846,7 +848,7 @@ module IlMachineState = let concretizeMethodForExecution (loggerFactory : ILoggerFactory) - (corelib : BaseClassTypes) + (baseClassTypes : BaseClassTypes) (thread : ThreadId) (methodToCall : WoofWare.PawPrint.MethodInfo) (methodGenerics : TypeDefn ImmutableArray option) @@ -871,7 +873,7 @@ module IlMachineState = TypeConcretization.ConcretizationContext.InProgress = ImmutableDictionary.Empty TypeConcretization.ConcretizationContext.ConcreteTypes = state.ConcreteTypes TypeConcretization.ConcretizationContext.LoadedAssemblies = state._LoadedAssemblies - TypeConcretization.ConcretizationContext.BaseTypes = corelib + TypeConcretization.ConcretizationContext.BaseTypes = baseClassTypes } let handle, newCtx = @@ -910,7 +912,7 @@ module IlMachineState = concretizeMethodWithTypeGenerics loggerFactory - corelib + baseClassTypes typeGenerics methodToCall methodGenerics @@ -921,7 +923,7 @@ module IlMachineState = // Add to IlMachineState module let concretizeFieldForExecution (loggerFactory : ILoggerFactory) - (corelib : BaseClassTypes) + (baseClassTypes : BaseClassTypes) (thread : ThreadId) (field : WoofWare.PawPrint.FieldInfo) (state : IlMachineState) @@ -940,7 +942,7 @@ module IlMachineState = TypeConcretization.ConcretizationContext.InProgress = ImmutableDictionary.Empty TypeConcretization.ConcretizationContext.ConcreteTypes = state.ConcreteTypes TypeConcretization.ConcretizationContext.LoadedAssemblies = state._LoadedAssemblies - TypeConcretization.ConcretizationContext.BaseTypes = corelib + TypeConcretization.ConcretizationContext.BaseTypes = baseClassTypes } // Create a TypeDefn for the field's declaring type @@ -952,7 +954,7 @@ module IlMachineState = let baseType = typeDef.BaseType - |> DumpedAssembly.resolveBaseType corelib state._LoadedAssemblies assy.Name + |> DumpedAssembly.resolveBaseType baseClassTypes state._LoadedAssemblies assy.Name let signatureTypeKind = match baseType with @@ -973,7 +975,7 @@ module IlMachineState = let baseTypeResolved = typeDef.BaseType - |> DumpedAssembly.resolveBaseType corelib state._LoadedAssemblies assy.Name + |> DumpedAssembly.resolveBaseType baseClassTypes state._LoadedAssemblies assy.Name let signatureTypeKind = match baseTypeResolved with @@ -1049,6 +1051,7 @@ module IlMachineState = TypeInitTable = ImmutableDictionary.Empty DotnetRuntimeDirs = dotnetRuntimeDirs TypeHandles = TypeHandleRegistry.empty () + FieldHandles = FieldHandleRegistry.empty () } state.WithLoadedAssembly assyName entryAssembly @@ -1188,7 +1191,7 @@ module IlMachineState = let resolveMember (loggerFactory : ILoggerFactory) - (corelib : BaseClassTypes) + (baseClassTypes : BaseClassTypes) (currentThread : ThreadId) (assy : DumpedAssembly) (m : MemberReferenceHandle) @@ -1211,7 +1214,11 @@ module IlMachineState = let typeGenerics = executing.DeclaringType.Generics |> Seq.map (fun handle -> - Concretization.concreteHandleToTypeDefn corelib handle state.ConcreteTypes state._LoadedAssemblies + Concretization.concreteHandleToTypeDefn + baseClassTypes + handle + state.ConcreteTypes + state._LoadedAssemblies ) |> ImmutableArray.CreateRange @@ -1222,13 +1229,13 @@ module IlMachineState = let state, assy, targetType = resolveType loggerFactory parent ImmutableArray.Empty assy state - state, assy, targetType, ImmutableArray.Empty // No type args from TypeReference + state, assy, targetType, ImmutableArray.Empty // No type args from TypeReference | MetadataToken.TypeSpecification parent -> let methodGenerics = executing.Generics |> Seq.map (fun handle -> Concretization.concreteHandleToTypeDefn - corelib + baseClassTypes handle state.ConcreteTypes state._LoadedAssemblies @@ -1236,7 +1243,7 @@ module IlMachineState = |> ImmutableArray.CreateRange let state, assy, targetType = - resolveTypeFromSpec loggerFactory corelib parent assy typeGenerics methodGenerics state + resolveTypeFromSpec loggerFactory baseClassTypes parent assy typeGenerics methodGenerics state // Extract type arguments from the resolved type let extractedTypeArgs = targetType.Generics @@ -1244,12 +1251,58 @@ module IlMachineState = state, assy, targetType, extractedTypeArgs | parent -> failwith $"Unexpected: {parent}" + let state, concreteExtractedTypeArgs = + ((state, ImmutableArray.CreateBuilder ()), extractedTypeArgs) + ||> Seq.fold (fun (state, acc) ty -> + // TODO: generics? + let state, t = + concretizeType + baseClassTypes + state + targetType.Assembly + ImmutableArray.Empty + ImmutableArray.Empty + ty + + acc.Add t + state, acc + ) + |> Tuple.rmap (fun x -> x.ToImmutable ()) + match mem.Signature with | MemberSignature.Field fieldSig -> - let availableFields = - targetType.Fields - |> List.filter (fun fi -> fi.Name = memberName) - |> List.filter (fun fi -> fi.Signature = fieldSig) + // Concretize the field signature from the member reference + let state, concreteFieldSig = + concretizeType + baseClassTypes + state + (state.ActiveAssembly(currentThread).Name) + concreteExtractedTypeArgs + ImmutableArray.Empty + fieldSig + + // Find matching fields by comparing concretized signatures + let state, availableFields = + ((state, []), targetType.Fields) + ||> List.fold (fun (state, acc) fi -> + if fi.Name <> memberName then + state, acc + else + // Concretize the field's signature for comparison + let state, fieldSigConcrete = + concretizeType + baseClassTypes + state + assy.Name + concreteExtractedTypeArgs + ImmutableArray.Empty + fi.Signature + + if fieldSigConcrete = concreteFieldSig then + state, fi :: acc + else + state, acc + ) let field = match availableFields with @@ -1265,16 +1318,50 @@ module IlMachineState = | MemberSignature.Method memberSig -> let availableMethods = - targetType.Methods - |> List.filter (fun mi -> mi.Name = memberName) - // TODO: this needs to resolve the TypeMethodSignature to e.g. remove references to generic parameters - |> List.filter (fun mi -> mi.Signature = memberSig) + targetType.Methods |> List.filter (fun mi -> mi.Name = memberName) + + let state, memberSig = + memberSig + |> TypeMethodSignature.map + state + (fun state ty -> + concretizeType + baseClassTypes + state + (state.ActiveAssembly(currentThread).Name) + concreteExtractedTypeArgs + ImmutableArray.Empty + ty + ) + + let state, availableMethods = + ((state, []), availableMethods) + ||> List.fold (fun (state, acc) meth -> + let state, methSig = + meth.Signature + |> TypeMethodSignature.map + state + (fun state ty -> + concretizeType + baseClassTypes + state + assy.Name + concreteExtractedTypeArgs + ImmutableArray.Empty + ty + ) + + if methSig = memberSig then + state, meth :: acc + else + state, acc + ) let method = match availableMethods with | [] -> failwith - $"Could not find member {memberName} with the right signature on {targetType.Namespace}.{targetType.Name}" + $"Could not find member {memberName} with the right signature {memberSig} on {targetType.Namespace}.{targetType.Name}" | [ x ] -> x |> MethodInfo.mapTypeGenerics (fun i _ -> targetType.Generics.[i]) | _ -> failwith @@ -1379,6 +1466,47 @@ module IlMachineState = result, state + /// Returns a System.RuntimeFieldHandle. + let getOrAllocateField<'corelib> + (loggerFactory : ILoggerFactory) + (baseClassTypes : BaseClassTypes<'corelib>) + (declaringAssy : AssemblyName) + (fieldHandle : FieldDefinitionHandle) + (state : IlMachineState) + : CliType * IlMachineState + = + let field = state.LoadedAssembly(declaringAssy).Value.Fields.[fieldHandle] + + // For LdToken, we don't have generic context, so we create a non-generic type + // TODO: This might need to be revisited if we need to support generic field handles + let declaringTypeWithGenerics : ConcreteType = + ConcreteType.make + field.DeclaringType.Assembly + field.DeclaringType.Namespace + field.DeclaringType.Name + field.DeclaringType.Definition.Get + ImmutableArray.Empty // No generic arguments in this context + + let declaringType, state = + concretizeFieldDeclaringType loggerFactory baseClassTypes declaringTypeWithGenerics state + + let result, reg, state = + FieldHandleRegistry.getOrAllocate + baseClassTypes + state + (fun fields state -> allocateManagedObject baseClassTypes.RuntimeType fields state) + declaringAssy + declaringType + fieldHandle + state.FieldHandles + + let state = + { state with + FieldHandles = reg + } + + result, state + let setStatic (ty : ConcreteTypeHandle) (field : string) diff --git a/WoofWare.PawPrint/IlMachineStateExecution.fs b/WoofWare.PawPrint/IlMachineStateExecution.fs index 8d64a19..8ca7d0e 100644 --- a/WoofWare.PawPrint/IlMachineStateExecution.fs +++ b/WoofWare.PawPrint/IlMachineStateExecution.fs @@ -7,7 +7,7 @@ open Microsoft.Extensions.Logging module IlMachineStateExecution = let callMethod (loggerFactory : ILoggerFactory) - (corelib : BaseClassTypes) + (baseClassTypes : BaseClassTypes) (wasInitialising : ConcreteTypeHandle option) (wasConstructing : ManagedHeapAddress option) (wasClassConstructor : bool) @@ -34,7 +34,7 @@ module IlMachineStateExecution = match if isIntrinsic then - Intrinsics.call corelib methodToCall thread state + Intrinsics.call baseClassTypes methodToCall thread state else None with @@ -45,7 +45,7 @@ module IlMachineStateExecution = let state, argZeroObjects = ((state, []), methodToCall.Signature.ParameterTypes) ||> List.fold (fun (state, zeros) tyHandle -> - let zero, state = IlMachineState.cliTypeZeroOfHandle state corelib tyHandle + let zero, state = IlMachineState.cliTypeZeroOfHandle state baseClassTypes tyHandle state, zero :: zeros ) @@ -129,7 +129,7 @@ module IlMachineStateExecution = match MethodState.Empty state.ConcreteTypes - corelib + baseClassTypes state._LoadedAssemblies (state.ActiveAssembly thread) methodToCall @@ -174,7 +174,7 @@ module IlMachineStateExecution = let rec loadClass (loggerFactory : ILoggerFactory) - (corelib : BaseClassTypes) + (baseClassTypes : BaseClassTypes) (ty : ConcreteTypeHandle) (currentThread : ThreadId) (state : IlMachineState) @@ -251,7 +251,10 @@ module IlMachineStateExecution = let baseType = baseTypeDef.BaseType - |> DumpedAssembly.resolveBaseType corelib state._LoadedAssemblies sourceAssembly.Name + |> DumpedAssembly.resolveBaseType + baseClassTypes + state._LoadedAssemblies + sourceAssembly.Name let signatureTypeKind = match baseType with @@ -267,30 +270,18 @@ module IlMachineStateExecution = ) // Concretize the base type - let ctx = - { - TypeConcretization.ConcretizationContext.InProgress = ImmutableDictionary.Empty - TypeConcretization.ConcretizationContext.ConcreteTypes = state.ConcreteTypes - TypeConcretization.ConcretizationContext.LoadedAssemblies = state._LoadedAssemblies - TypeConcretization.ConcretizationContext.BaseTypes = corelib - } - - let baseTypeHandle, newCtx = - TypeConcretization.concretizeType - ctx - (fun _ _ -> failwith "getAssembly not needed for base type concretization") + let state, baseTypeHandle = + IlMachineState.concretizeType + baseClassTypes + state sourceAssembly.Name - concreteType.Generics // Use the current type's generics - ImmutableArray.Empty // No method generics + concreteType.Generics + // TODO: surely we have generics in scope here? + ImmutableArray.Empty baseTypeDefn - let state = - { state with - ConcreteTypes = newCtx.ConcreteTypes - } - // Recursively load the base class - match loadClass loggerFactory corelib baseTypeHandle currentThread state with + match loadClass loggerFactory baseClassTypes baseTypeHandle currentThread state with | FirstLoadThis state -> Error state | NothingToDo state -> Ok state | BaseTypeInfo.TypeRef typeReferenceHandle -> @@ -316,7 +307,7 @@ module IlMachineStateExecution = let baseTypeDefn = let baseType = targetType.BaseType - |> DumpedAssembly.resolveBaseType corelib state._LoadedAssemblies assy.Name + |> DumpedAssembly.resolveBaseType baseClassTypes state._LoadedAssemblies assy.Name let signatureTypeKind = match baseType with @@ -332,30 +323,18 @@ module IlMachineStateExecution = ) // Concretize the base type - let ctx = - { - TypeConcretization.ConcretizationContext.InProgress = ImmutableDictionary.Empty - TypeConcretization.ConcretizationContext.ConcreteTypes = state.ConcreteTypes - TypeConcretization.ConcretizationContext.LoadedAssemblies = state._LoadedAssemblies - TypeConcretization.ConcretizationContext.BaseTypes = corelib - } - - let baseTypeHandle, newCtx = - TypeConcretization.concretizeType - ctx - (fun _ _ -> failwith "getAssembly not needed for base type concretization") - assy.Name - concreteType.Generics // Use the current type's generics - ImmutableArray.Empty // No method generics + let state, baseTypeHandle = + IlMachineState.concretizeType + baseClassTypes + state + sourceAssembly.Name + concreteType.Generics + // TODO: surely we have generics in scope here? + ImmutableArray.Empty baseTypeDefn - let state = - { state with - ConcreteTypes = newCtx.ConcreteTypes - } - // Recursively load the base class - match loadClass loggerFactory corelib baseTypeHandle currentThread state with + match loadClass loggerFactory baseClassTypes baseTypeHandle currentThread state with | FirstLoadThis state -> Error state | NothingToDo state -> Ok state | BaseTypeInfo.TypeSpec typeSpecificationHandle -> @@ -395,40 +374,14 @@ module IlMachineStateExecution = |> TypeMethodSignature.map state (fun state typeDefn -> - // Concretize each TypeDefn in the signature - let ctx = - { - TypeConcretization.ConcretizationContext.InProgress = ImmutableDictionary.Empty - TypeConcretization.ConcretizationContext.ConcreteTypes = state.ConcreteTypes - TypeConcretization.ConcretizationContext.LoadedAssemblies = state._LoadedAssemblies - TypeConcretization.ConcretizationContext.BaseTypes = corelib - } - - let handle, ctx = - TypeConcretization.concretizeType - ctx - (fun assyName ref -> - let currentAssy = state.LoadedAssembly assyName |> Option.get - - let targetAssy = - currentAssy.AssemblyReferences.[ref].Name - |> state.LoadedAssembly - |> Option.get - - state._LoadedAssemblies, targetAssy - ) - concreteType.Assembly - concreteType.Generics - ImmutableArray.Empty // no method generics for cctor - typeDefn - - let state = - { state with - _LoadedAssemblies = ctx.LoadedAssemblies - ConcreteTypes = ctx.ConcreteTypes - } - - state, handle + IlMachineState.concretizeType + baseClassTypes + state + concreteType.Assembly + concreteType.Generics + // no method generics for cctor + ImmutableArray.Empty + typeDefn ) // Convert method instructions (local variables) @@ -444,41 +397,15 @@ module IlMachineStateExecution = let state, convertedVars = ((state, []), localVars) ||> Seq.fold (fun (state, acc) typeDefn -> - let ctx = - { - TypeConcretization.ConcretizationContext.InProgress = - ImmutableDictionary.Empty - TypeConcretization.ConcretizationContext.ConcreteTypes = - state.ConcreteTypes - TypeConcretization.ConcretizationContext.LoadedAssemblies = - state._LoadedAssemblies - TypeConcretization.ConcretizationContext.BaseTypes = corelib - } - - let handle, ctx = - TypeConcretization.concretizeType - ctx - (fun assyName ref -> - let currentAssy = state.LoadedAssembly assyName |> Option.get - - let targetAssy = - currentAssy.AssemblyReferences.[ref].Name - |> state.LoadedAssembly - |> Option.get - - state._LoadedAssemblies, targetAssy - ) + let state, handle = + IlMachineState.concretizeType + baseClassTypes + state concreteType.Assembly concreteType.Generics ImmutableArray.Empty // no method generics for cctor typeDefn - let state = - { state with - _LoadedAssemblies = ctx.LoadedAssemblies - ConcreteTypes = ctx.ConcreteTypes - } - state, handle :: acc ) |> Tuple.rmap ImmutableArray.CreateRange @@ -492,7 +419,7 @@ module IlMachineStateExecution = callMethod loggerFactory - corelib + baseClassTypes (Some ty) None true @@ -516,7 +443,7 @@ module IlMachineStateExecution = let ensureTypeInitialised (loggerFactory : ILoggerFactory) - (corelib : BaseClassTypes) + (baseClassTypes : BaseClassTypes) (thread : ThreadId) (ty : ConcreteTypeHandle) (state : IlMachineState) @@ -524,7 +451,7 @@ module IlMachineStateExecution = = match TypeInitTable.tryGet ty state.TypeInitTable with | None -> - match loadClass loggerFactory corelib ty thread state with + match loadClass loggerFactory baseClassTypes ty thread state with | NothingToDo state -> state, WhatWeDid.Executed | FirstLoadThis state -> state, WhatWeDid.SuspendedForClassInit | Some TypeInitState.Initialized -> state, WhatWeDid.Executed @@ -541,7 +468,7 @@ module IlMachineStateExecution = /// another call to its function pointer.) let callMethodInActiveAssembly (loggerFactory : ILoggerFactory) - (corelib : BaseClassTypes) + (baseClassTypes : BaseClassTypes) (thread : ThreadId) (advanceProgramCounterOfCaller : bool) (methodGenerics : TypeDefn ImmutableArray option) @@ -556,7 +483,7 @@ module IlMachineStateExecution = let state, concretizedMethod, declaringTypeHandle = IlMachineState.concretizeMethodForExecution loggerFactory - corelib + baseClassTypes thread methodToCall methodGenerics @@ -564,13 +491,13 @@ module IlMachineStateExecution = state let state, typeInit = - ensureTypeInitialised loggerFactory corelib thread declaringTypeHandle state + ensureTypeInitialised loggerFactory baseClassTypes thread declaringTypeHandle state match typeInit with | WhatWeDid.Executed -> callMethod loggerFactory - corelib + baseClassTypes None weAreConstructingObj false diff --git a/WoofWare.PawPrint/Intrinsics.fs b/WoofWare.PawPrint/Intrinsics.fs index 51c0be9..37b09ce 100644 --- a/WoofWare.PawPrint/Intrinsics.fs +++ b/WoofWare.PawPrint/Intrinsics.fs @@ -234,5 +234,8 @@ module Intrinsics = else failwith "TODO: unexpected params to String.op_Implicit" | _ -> failwith "TODO: unexpected params to String.op_Implicit" + | "System.Private.CoreLib", "RuntimeHelpers", "InitializeArray" -> + // https://github.com/dotnet/runtime/blob/9e5e6aa7bc36aeb2a154709a9d1192030c30a2ef/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs#L18 + failwith "TODO: array initialization" | a, b, c -> failwith $"TODO: implement JIT intrinsic {a}.{b}.{c}" |> Option.map (fun s -> s.WithThreadSwitchedToAssembly callerAssy currentThread |> fst) diff --git a/WoofWare.PawPrint/MethodState.fs b/WoofWare.PawPrint/MethodState.fs index b311f60..f60c7de 100644 --- a/WoofWare.PawPrint/MethodState.fs +++ b/WoofWare.PawPrint/MethodState.fs @@ -137,7 +137,7 @@ and MethodState = /// If `method` is static, `args` must be of length numParams. static member Empty (concreteTypes : AllConcreteTypes) - (corelib : BaseClassTypes) + (baseClassTypes : BaseClassTypes) (loadedAssemblies : ImmutableDictionary) (containingAssembly : DumpedAssembly) (method : WoofWare.PawPrint.MethodInfo) @@ -172,7 +172,7 @@ and MethodState = // Note: This assumes all types have already been concretized // If this fails with "ConcreteTypeHandle not found", it means // we need to ensure types are concretized before creating the MethodState - let zero, _ = CliType.zeroOf concreteTypes loadedAssemblies corelib var + let zero, _ = CliType.zeroOf concreteTypes loadedAssemblies baseClassTypes var result.Add zero result.ToImmutable () diff --git a/WoofWare.PawPrint/NullaryIlOp.fs b/WoofWare.PawPrint/NullaryIlOp.fs index 82998cf..8e85aae 100644 --- a/WoofWare.PawPrint/NullaryIlOp.fs +++ b/WoofWare.PawPrint/NullaryIlOp.fs @@ -46,7 +46,9 @@ module NullaryIlOp = | ManagedPointerSource.LocalVariable (sourceThread, methodFrame, whichVar) -> state.ThreadState.[sourceThread].MethodStates.[methodFrame].LocalVariables.[int whichVar] | ManagedPointerSource.Heap managedHeapAddress -> failwith "TODO: Heap pointer dereferencing not implemented" - | ManagedPointerSource.ArrayIndex _ -> failwith "TODO: array index pointer dereferencing not implemented" + | ManagedPointerSource.ArrayIndex (arr, index) -> + let arr = state.ManagedHeap.Arrays.[arr] + arr.Elements.[index] // Unified Ldind implementation let private executeLdind diff --git a/WoofWare.PawPrint/UnaryMetadataIlOp.fs b/WoofWare.PawPrint/UnaryMetadataIlOp.fs index 8934e4b..f0164e7 100644 --- a/WoofWare.PawPrint/UnaryMetadataIlOp.fs +++ b/WoofWare.PawPrint/UnaryMetadataIlOp.fs @@ -337,7 +337,12 @@ module internal UnaryMetadataIlOp = let typeGenerics = concretizedCtor.DeclaringType.Generics let state, fieldZeros = - ((state, []), ctorType.Fields) + // Only include instance fields when constructing objects + let instanceFields = + ctorType.Fields + |> List.filter (fun field -> not (field.Attributes.HasFlag FieldAttributes.Static)) + + ((state, []), instanceFields) ||> List.fold (fun (state, zeros) field -> // TODO: generics let state, zero = @@ -1071,15 +1076,11 @@ module internal UnaryMetadataIlOp = let state = match metadataToken with | MetadataToken.FieldDefinition h -> - let ty = baseClassTypes.RuntimeFieldHandle - // this is a struct; it contains one field, an IRuntimeFieldInfo - // https://github.com/dotnet/runtime/blob/1d1bf92fcf43aa6981804dc53c5174445069c9e4/src/coreclr/System.Private.CoreLib/src/System/RuntimeHandles.cs#L1097 - let field = ty.Fields |> List.exactlyOne + // TODO: how do we know what concrete type this is a field on? + let runtimeFieldHandle, state = + IlMachineState.getOrAllocateField loggerFactory baseClassTypes activeAssy.Name h state - if field.Name <> "m_ptr" then - failwith $"unexpected field name ${field.Name} for BCL type RuntimeFieldHandle" - - failwith "" + IlMachineState.pushToEvalStack runtimeFieldHandle thread state | MetadataToken.MethodDef h -> let ty = baseClassTypes.RuntimeMethodHandle let field = ty.Fields |> List.exactlyOne @@ -1095,34 +1096,17 @@ module internal UnaryMetadataIlOp = let typeGenerics = currentMethod.DeclaringType.Generics - if not (methodGenerics.IsEmpty && typeGenerics.IsEmpty) then - failwith "TODO: generics" - let state, typeDefn = lookupTypeDefn baseClassTypes state activeAssy h - let ctx = - { - TypeConcretization.ConcretizationContext.InProgress = ImmutableDictionary.Empty - TypeConcretization.ConcretizationContext.ConcreteTypes = state.ConcreteTypes - TypeConcretization.ConcretizationContext.LoadedAssemblies = state._LoadedAssemblies - TypeConcretization.ConcretizationContext.BaseTypes = baseClassTypes - } - - let handle, newCtx = - TypeConcretization.concretizeType - ctx - (fun _ _ -> failwith "getAssembly not needed for type def concretization") + let state, handle = + IlMachineState.concretizeType + baseClassTypes + state activeAssy.Name typeGenerics methodGenerics typeDefn - let state = - { state with - _LoadedAssemblies = newCtx.LoadedAssemblies - ConcreteTypes = newCtx.ConcreteTypes - } - let alloc, state = IlMachineState.getOrAllocateType baseClassTypes handle state let vt = @@ -1148,29 +1132,15 @@ module internal UnaryMetadataIlOp = lookupTypeRef loggerFactory baseClassTypes state activeAssy currentMethod.DeclaringType.Generics ref | _ -> failwith $"unexpected token {metadataToken} in Sizeof" - let ctx = - { - TypeConcretization.ConcretizationContext.InProgress = ImmutableDictionary.Empty - TypeConcretization.ConcretizationContext.ConcreteTypes = state.ConcreteTypes - TypeConcretization.ConcretizationContext.LoadedAssemblies = state._LoadedAssemblies - TypeConcretization.ConcretizationContext.BaseTypes = baseClassTypes - } - - let typeHandle, newCtx = - TypeConcretization.concretizeType - ctx - (fun _ _ -> failwith "getAssembly not needed for base type concretization") + let state, typeHandle = + IlMachineState.concretizeType + baseClassTypes + state assy.Name currentMethod.DeclaringType.Generics currentMethod.Generics ty - let state = - { state with - _LoadedAssemblies = newCtx.LoadedAssemblies - ConcreteTypes = newCtx.ConcreteTypes - } - let zero, state = IlMachineState.cliTypeZeroOfHandle state baseClassTypes typeHandle let size = CliType.sizeOf zero diff --git a/WoofWare.PawPrint/WoofWare.PawPrint.fsproj b/WoofWare.PawPrint/WoofWare.PawPrint.fsproj index 219ef4d..b86ab97 100644 --- a/WoofWare.PawPrint/WoofWare.PawPrint.fsproj +++ b/WoofWare.PawPrint/WoofWare.PawPrint.fsproj @@ -12,6 +12,7 @@ +