From 19ec9f867001e1b9ab2c93ed57be48479232dab8 Mon Sep 17 00:00:00 2001 From: Patrick Stevens <3138005+Smaug123@users.noreply.github.com> Date: Sun, 22 Jun 2025 19:36:47 +0100 Subject: [PATCH] Uncomment a bit of a test (#64) --- WoofWare.PawPrint.Domain/IlOp.fs | 6 ++ WoofWare.PawPrint.Domain/TypeInfo.fs | 6 ++ WoofWare.PawPrint.Test/sources/Ldind.cs | 2 +- WoofWare.PawPrint/AbstractMachine.fs | 2 +- WoofWare.PawPrint/BasicCliType.fs | 69 +++++++++++++++++-- WoofWare.PawPrint/EvalStack.fs | 91 +++++-------------------- WoofWare.PawPrint/NullaryIlOp.fs | 18 +++-- WoofWare.PawPrint/TypeHandleRegistry.fs | 4 +- 8 files changed, 110 insertions(+), 88 deletions(-) diff --git a/WoofWare.PawPrint.Domain/IlOp.fs b/WoofWare.PawPrint.Domain/IlOp.fs index 7a7856b..ff2ef94 100644 --- a/WoofWare.PawPrint.Domain/IlOp.fs +++ b/WoofWare.PawPrint.Domain/IlOp.fs @@ -519,6 +519,12 @@ type UnaryMetadataTokenIlOp = | Call | Calli | Callvirt + /// Attempts to cast an object passed by reference to the specified class. + /// If the class of the object on the top of the stack does not implement the new class + /// (assuming the new class is an interface) + /// and is not a derived class of the new class then an InvalidCastException is thrown. + /// If the object reference is a null reference, castclass succeeds + /// and returns the new object as a null reference. | Castclass | Newobj | Newarr diff --git a/WoofWare.PawPrint.Domain/TypeInfo.fs b/WoofWare.PawPrint.Domain/TypeInfo.fs index e26247a..37d33dd 100644 --- a/WoofWare.PawPrint.Domain/TypeInfo.fs +++ b/WoofWare.PawPrint.Domain/TypeInfo.fs @@ -92,6 +92,8 @@ type TypeInfoCrate = abstract ToString : unit -> string abstract BaseType : BaseTypeInfo option abstract Assembly : AssemblyName + abstract Namespace : string + abstract Name : string [] module TypeInfoCrate = @@ -108,6 +110,10 @@ module TypeInfoCrate = member this.BaseType = t.BaseType member this.Assembly = t.Assembly + + member this.Namespace = t.Namespace + + member this.Name = t.Name } type BaseClassTypes<'corelib> = diff --git a/WoofWare.PawPrint.Test/sources/Ldind.cs b/WoofWare.PawPrint.Test/sources/Ldind.cs index 572d868..36fe163 100644 --- a/WoofWare.PawPrint.Test/sources/Ldind.cs +++ b/WoofWare.PawPrint.Test/sources/Ldind.cs @@ -42,7 +42,7 @@ unsafe class LdindTest // failures += TestManagedPointers(); // Test Ldind.i (native int) - // failures += TestLdindI(); + failures += TestLdindI(); return failures; } diff --git a/WoofWare.PawPrint/AbstractMachine.fs b/WoofWare.PawPrint/AbstractMachine.fs index f9c93fc..d8ace69 100644 --- a/WoofWare.PawPrint/AbstractMachine.fs +++ b/WoofWare.PawPrint/AbstractMachine.fs @@ -65,7 +65,7 @@ module AbstractMachine = let methodPtr = match delegateToRun.Fields.["_methodPtr"] with - | CliType.Numeric (CliNumericType.ProvenanceTrackedNativeInt64 mi) -> mi + | CliType.Numeric (CliNumericType.NativeInt (NativeIntSource.FunctionPointer mi)) -> mi | d -> failwith $"unexpectedly not a method pointer in delegate invocation: {d}" let typeGenerics = diff --git a/WoofWare.PawPrint/BasicCliType.fs b/WoofWare.PawPrint/BasicCliType.fs index 0bc1f7f..ec6f6f2 100644 --- a/WoofWare.PawPrint/BasicCliType.fs +++ b/WoofWare.PawPrint/BasicCliType.fs @@ -36,11 +36,74 @@ type BasicCliType = | NativeInt of int64 | NativeFloat of float +[] +type ManagedPointerSource = + | LocalVariable of sourceThread : ThreadId * methodFrame : int * whichVar : uint16 + | Argument of sourceThread : ThreadId * methodFrame : int * whichVar : uint16 + | Heap of ManagedHeapAddress + | Null + + override this.ToString () = + match this with + | ManagedPointerSource.Null -> "" + | ManagedPointerSource.Heap addr -> $"%O{addr}" + | ManagedPointerSource.LocalVariable (source, method, var) -> + $"" + | ManagedPointerSource.Argument (source, method, var) -> + $"" + +[] +type UnsignedNativeIntSource = + | Verbatim of uint64 + | FromManagedPointer of ManagedPointerSource + +[] +type NativeIntSource = + | Verbatim of int64 + | ManagedPointer of ManagedPointerSource + | FunctionPointer of MethodInfo + | TypeHandlePtr of int64 + + override this.ToString () : string = + match this with + | NativeIntSource.Verbatim int64 -> $"%i{int64}" + | NativeIntSource.ManagedPointer ptr -> $"" + | NativeIntSource.FunctionPointer methodDefinition -> + $"" + | NativeIntSource.TypeHandlePtr ptr -> $"" + +[] +module NativeIntSource = + let isZero (n : NativeIntSource) : bool = + match n with + | NativeIntSource.Verbatim i -> i = 0L + | NativeIntSource.TypeHandlePtr _ -> false + | NativeIntSource.FunctionPointer _ -> failwith "TODO" + | NativeIntSource.ManagedPointer src -> + match src with + | ManagedPointerSource.Null -> true + | _ -> false + + let isNonnegative (n : NativeIntSource) : bool = + match n with + | NativeIntSource.Verbatim i -> i >= 0L + | NativeIntSource.FunctionPointer _ -> failwith "TODO" + | NativeIntSource.TypeHandlePtr _ -> true + | NativeIntSource.ManagedPointer _ -> true + + /// True if a < b. + let isLess (a : NativeIntSource) (b : NativeIntSource) : bool = + match a, b with + | NativeIntSource.Verbatim a, NativeIntSource.Verbatim b -> a < b + | _, _ -> failwith "TODO" + + /// Defined in III.1.1.1 type CliNumericType = | Int32 of int32 | Int64 of int64 - | NativeInt of int64 + /// The real CLR just represents these as native ints, but we track their provenance. + | NativeInt of NativeIntSource | NativeFloat of float | Int8 of int8 | Int16 of int16 @@ -48,10 +111,6 @@ type CliNumericType = | UInt16 of uint16 | Float32 of float32 | Float64 of float - /// Not a real CLI numeric type! Represents an int64 obtained by taking a NativeInt from the eval stack. - | ProvenanceTrackedNativeInt64 of MethodInfo - /// Not a real CLI numeric type! An opaque TypeHandle pointer. - | TypeHandlePtr of int64 type CliValueType = private diff --git a/WoofWare.PawPrint/EvalStack.fs b/WoofWare.PawPrint/EvalStack.fs index c3e6ed2..c964435 100644 --- a/WoofWare.PawPrint/EvalStack.fs +++ b/WoofWare.PawPrint/EvalStack.fs @@ -1,65 +1,5 @@ namespace WoofWare.PawPrint -type ManagedPointerSource = - | LocalVariable of sourceThread : ThreadId * methodFrame : int * whichVar : uint16 - | Argument of sourceThread : ThreadId * methodFrame : int * whichVar : uint16 - | Heap of ManagedHeapAddress - | Null - - override this.ToString () = - match this with - | ManagedPointerSource.Null -> "" - | ManagedPointerSource.Heap addr -> $"%O{addr}" - | ManagedPointerSource.LocalVariable (source, method, var) -> - $"" - | ManagedPointerSource.Argument (source, method, var) -> - $"" - -[] -type NativeIntSource = - | Verbatim of int64 - | ManagedPointer of ManagedPointerSource - | FunctionPointer of MethodInfo - | TypeHandlePtr of int64 - - override this.ToString () : string = - match this with - | NativeIntSource.Verbatim int64 -> $"%i{int64}" - | NativeIntSource.ManagedPointer ptr -> $"" - | NativeIntSource.FunctionPointer methodDefinition -> - $"" - | NativeIntSource.TypeHandlePtr ptr -> $"" - -[] -module NativeIntSource = - let isZero (n : NativeIntSource) : bool = - match n with - | NativeIntSource.Verbatim i -> i = 0L - | NativeIntSource.TypeHandlePtr _ -> false - | NativeIntSource.FunctionPointer _ -> failwith "TODO" - | NativeIntSource.ManagedPointer src -> - match src with - | ManagedPointerSource.Null -> true - | _ -> false - - let isNonnegative (n : NativeIntSource) : bool = - match n with - | NativeIntSource.Verbatim i -> i >= 0L - | NativeIntSource.FunctionPointer _ -> failwith "TODO" - | NativeIntSource.TypeHandlePtr _ -> true - | NativeIntSource.ManagedPointer _ -> true - - /// True if a < b. - let isLess (a : NativeIntSource) (b : NativeIntSource) : bool = - match a, b with - | NativeIntSource.Verbatim a, NativeIntSource.Verbatim b -> a < b - | _, _ -> failwith "TODO" - -[] -type UnsignedNativeIntSource = - | Verbatim of uint64 - | FromManagedPointer of ManagedPointerSource - /// See I.12.3.2.1 for definition type EvalStackValue = | Int32 of int32 @@ -164,8 +104,6 @@ module EvalStackValue = | EvalStackValue.Int32 i -> CliType.Numeric (CliNumericType.Int32 i) | EvalStackValue.UserDefinedValueType [ popped ] -> toCliTypeCoerced target popped | i -> failwith $"TODO: %O{i}" - | CliNumericType.TypeHandlePtr _ - | CliNumericType.ProvenanceTrackedNativeInt64 _ | CliNumericType.Int64 _ -> match popped with | EvalStackValue.Int64 i -> CliType.Numeric (CliNumericType.Int64 i) @@ -173,11 +111,18 @@ module EvalStackValue = match src with | NativeIntSource.Verbatim i -> CliType.Numeric (CliNumericType.Int64 i) | NativeIntSource.ManagedPointer ptr -> failwith "TODO" - | NativeIntSource.FunctionPointer f -> - CliType.Numeric (CliNumericType.ProvenanceTrackedNativeInt64 f) - | NativeIntSource.TypeHandlePtr f -> CliType.Numeric (CliNumericType.TypeHandlePtr f) + | NativeIntSource.FunctionPointer f -> failwith $"TODO: {f}" + // CliType.Numeric (CliNumericType.ProvenanceTrackedNativeInt64 f) + | NativeIntSource.TypeHandlePtr f -> failwith $"TODO: {f}" + // CliType.Numeric (CliNumericType.TypeHandlePtr f) | i -> failwith $"TODO: %O{i}" - | CliNumericType.NativeInt int64 -> failwith "todo" + | CliNumericType.NativeInt _ -> + match popped with + | EvalStackValue.NativeInt s -> CliNumericType.NativeInt s + | EvalStackValue.ManagedPointer ptrSrc -> + CliNumericType.NativeInt (NativeIntSource.ManagedPointer ptrSrc) + | _ -> failwith $"TODO: {popped}" + |> CliType.Numeric | CliNumericType.NativeFloat f -> failwith "todo" | CliNumericType.Int8 _ -> match popped with @@ -262,7 +207,8 @@ module EvalStackValue = match src with | ManagedPointerSource.Heap src -> CliType.RuntimePointer (CliRuntimePointer.Managed (CliRuntimePointerSource.Heap src)) - | ManagedPointerSource.Null -> failwith "TODO" + | ManagedPointerSource.Null -> + CliType.RuntimePointer (CliRuntimePointer.Managed CliRuntimePointerSource.Null) | ManagedPointerSource.LocalVariable (a, b, c) -> CliType.RuntimePointer ( CliRuntimePointer.Managed (CliRuntimePointerSource.LocalVariable (a, b, c)) @@ -270,7 +216,7 @@ module EvalStackValue = | ManagedPointerSource.Argument (a, b, c) -> CliType.RuntimePointer (CliRuntimePointer.Managed (CliRuntimePointerSource.Argument (a, b, c))) | NativeIntSource.FunctionPointer methodInfo -> - CliType.Numeric (CliNumericType.ProvenanceTrackedNativeInt64 methodInfo) + CliType.Numeric (CliNumericType.NativeInt (NativeIntSource.FunctionPointer methodInfo)) | NativeIntSource.TypeHandlePtr int64 -> failwith "todo" | _ -> failwith $"TODO: %O{popped}" | CliType.Char _ -> @@ -298,7 +244,7 @@ module EvalStackValue = match numeric with | CliNumericType.Int32 i -> EvalStackValue.Int32 i | CliNumericType.Int64 i -> EvalStackValue.Int64 i - | CliNumericType.NativeInt i -> failwith "TODO" + | CliNumericType.NativeInt i -> EvalStackValue.NativeInt i // Sign-extend types int8 and int16 // Zero-extend unsigned int8/unsigned int16 | CliNumericType.Int8 b -> int32 b |> EvalStackValue.Int32 @@ -308,9 +254,6 @@ module EvalStackValue = | CliNumericType.Float32 f -> EvalStackValue.Float (float f) | CliNumericType.Float64 f -> EvalStackValue.Float f | CliNumericType.NativeFloat f -> EvalStackValue.Float f - | CliNumericType.ProvenanceTrackedNativeInt64 f -> - EvalStackValue.NativeInt (NativeIntSource.FunctionPointer f) - | CliNumericType.TypeHandlePtr f -> EvalStackValue.NativeInt (NativeIntSource.TypeHandlePtr f) | CliType.ObjectRef i -> match i with | None -> EvalStackValue.ManagedPointer ManagedPointerSource.Null @@ -320,7 +263,7 @@ module EvalStackValue = | CliType.Char (high, low) -> int32 high * 256 + int32 low |> EvalStackValue.Int32 | CliType.RuntimePointer ptr -> match ptr with - | CliRuntimePointer.Unmanaged _ -> failwith "todo: unmanaged" + | CliRuntimePointer.Unmanaged ptrInt -> NativeIntSource.Verbatim ptrInt |> EvalStackValue.NativeInt | CliRuntimePointer.Managed ptr -> match ptr with | CliRuntimePointerSource.LocalVariable (sourceThread, methodFrame, var) -> @@ -330,7 +273,7 @@ module EvalStackValue = ManagedPointerSource.Argument (sourceThread, methodFrame, var) |> EvalStackValue.ManagedPointer | CliRuntimePointerSource.Heap addr -> EvalStackValue.ObjectRef addr - | CliRuntimePointerSource.Null -> failwith "TODO" + | CliRuntimePointerSource.Null -> EvalStackValue.ManagedPointer ManagedPointerSource.Null | CliType.ValueType fields -> fields |> List.map ofCliType |> EvalStackValue.UserDefinedValueType type EvalStack = diff --git a/WoofWare.PawPrint/NullaryIlOp.fs b/WoofWare.PawPrint/NullaryIlOp.fs index d7501d4..c6c01bc 100644 --- a/WoofWare.PawPrint/NullaryIlOp.fs +++ b/WoofWare.PawPrint/NullaryIlOp.fs @@ -47,7 +47,7 @@ module NullaryIlOp = // Helper to get the target CliType for each Ldind variant let private getTargetLdindCliType (targetType : LdindTargetType) : CliType = match targetType with - | LdindI -> CliType.Numeric (CliNumericType.NativeInt 0L) + | LdindI -> CliType.Numeric (CliNumericType.NativeInt (NativeIntSource.Verbatim 0L)) | LdindI1 -> CliType.Numeric (CliNumericType.Int8 0y) | LdindI2 -> CliType.Numeric (CliNumericType.Int16 0s) | LdindI4 -> CliType.Numeric (CliNumericType.Int32 0) @@ -357,15 +357,23 @@ module NullaryIlOp = | EvalStackValue.Float var1, EvalStackValue.Float var2 -> failwith "TODO: float CEQ float" | EvalStackValue.Float _, _ -> failwith $"bad ceq: Float vs {var2}" | EvalStackValue.NativeInt var1, EvalStackValue.NativeInt var2 -> - failwith $"TODO (CEQ): nativeint vs nativeint" + match var1, var2 with + | NativeIntSource.FunctionPointer f1, NativeIntSource.FunctionPointer f2 -> + if f1 = f2 then + 1 + else + failwith $"TODO(CEQ): nativeint vs nativeint, {f1} vs {f2}" + | NativeIntSource.TypeHandlePtr f1, NativeIntSource.TypeHandlePtr f2 -> if f1 = f2 then 1 else 0 + | NativeIntSource.Verbatim f1, NativeIntSource.Verbatim f2 -> if f1 = f2 then 1 else 0 + | NativeIntSource.ManagedPointer f1, NativeIntSource.ManagedPointer f2 -> if f1 = f2 then 1 else 0 + | _, _ -> failwith $"TODO (CEQ): nativeint vs nativeint, {var1} vs {var2}" | EvalStackValue.NativeInt var1, EvalStackValue.Int32 var2 -> failwith $"TODO (CEQ): nativeint vs int32" | EvalStackValue.NativeInt var1, EvalStackValue.ManagedPointer var2 -> failwith $"TODO (CEQ): nativeint vs managed pointer" | EvalStackValue.NativeInt _, _ -> failwith $"bad ceq: NativeInt vs {var2}" | EvalStackValue.ObjectRef var1, EvalStackValue.ObjectRef var2 -> if var1 = var2 then 1 else 0 | EvalStackValue.ObjectRef _, _ -> failwith $"bad ceq: ObjectRef vs {var2}" - | EvalStackValue.ManagedPointer var1, EvalStackValue.ManagedPointer var2 -> - failwith $"TODO (CEQ): managed pointers" + | EvalStackValue.ManagedPointer var1, EvalStackValue.ManagedPointer var2 -> if var1 = var2 then 1 else 0 | EvalStackValue.ManagedPointer var1, EvalStackValue.NativeInt var2 -> failwith $"TODO (CEQ): managed pointer vs nativeint" | EvalStackValue.ManagedPointer _, _ -> failwith $"bad ceq: ManagedPointer vs {var2}" @@ -717,7 +725,7 @@ module NullaryIlOp = | Localloc -> failwith "TODO: Localloc unimplemented" | Stind_I -> let state = - stind (CliType.Numeric (CliNumericType.NativeInt 0L)) currentThread state + stind (CliType.Numeric (CliNumericType.NativeInt (NativeIntSource.Verbatim 0L))) currentThread state |> IlMachineState.advanceProgramCounter currentThread (state, WhatWeDid.Executed) |> ExecutionResult.Stepped diff --git a/WoofWare.PawPrint/TypeHandleRegistry.fs b/WoofWare.PawPrint/TypeHandleRegistry.fs index 18be1c7..c267d53 100644 --- a/WoofWare.PawPrint/TypeHandleRegistry.fs +++ b/WoofWare.PawPrint/TypeHandleRegistry.fs @@ -45,8 +45,8 @@ module TypeHandleRegistry = // for the GC, I think? "m_keepalive", CliType.ObjectRef None // TODO: this is actually a System.IntPtr https://github.com/dotnet/runtime/blob/ec11903827fc28847d775ba17e0cd1ff56cfbc2e/src/coreclr/nativeaot/Runtime.Base/src/System/Primitives.cs#L339 - "m_cache", CliType.Numeric (CliNumericType.NativeInt 0L) - "m_handle", CliType.Numeric (CliNumericType.TypeHandlePtr handle) + "m_cache", CliType.Numeric (CliNumericType.NativeInt (NativeIntSource.Verbatim 0L)) + "m_handle", CliType.Numeric (CliNumericType.NativeInt (NativeIntSource.TypeHandlePtr handle)) // This is the const -1, apparently?! // https://github.com/dotnet/runtime/blob/f0168ee80ba9aca18a7e7140b2bb436defda623c/src/coreclr/System.Private.CoreLib/src/System/RuntimeType.CoreCLR.cs#L2496 "GenericParameterCountAny", CliType.Numeric (CliNumericType.Int32 -1)