diff --git a/CSharpExample/Class1.cs b/CSharpExample/Class1.cs
index c238f1b..f2f9431 100644
--- a/CSharpExample/Class1.cs
+++ b/CSharpExample/Class1.cs
@@ -1,15 +1,364 @@
using System;
-using System.Collections.Generic;
-using System.Linq;
-namespace HelloWorldApp
+unsafe class LdindTest
{
- class Program
+ static int Main(string[] args)
{
- static int Main(string[] args)
+ var failures = 0;
+ // Test Ldind.i1 (signed byte)
+ failures += TestLdindI1();
+
+ // Test Ldind.u1 (unsigned byte)
+ failures += TestLdindU1();
+
+ // Test Ldind.i2 (signed short)
+ failures += TestLdindI2();
+
+ // Test Ldind.u2 (unsigned short)
+ failures += TestLdindU2();
+
+ // Test Ldind.i4 (signed int)
+ failures += TestLdindI4();
+
+ // Test Ldind.u4 (unsigned int)
+ failures += TestLdindU4();
+
+ // Test Ldind.i8 (signed long)
+ failures += TestLdindI8();
+
+ // Test Ldind.i8 via u8 (there's no Ldind.u8)
+ failures += TestLdindI8ViaU8();
+
+ // Test Ldind.r4 (float)
+ failures += TestLdindR4();
+
+ // Test Ldind.r8 (double)
+ failures += TestLdindR8();
+
+ // Test truncation behavior
+ failures += TestTruncation();
+
+ // Test with managed pointers (ref)
+ failures += TestManagedPointers();
+
+ // Test Ldind.i (native int)
+ failures += TestLdindI();
+
+ return failures;
+ }
+
+ static int TestLdindI1()
+ {
+ sbyte value = -128;
+ sbyte* ptr = &value;
+ sbyte loaded = *ptr; // This generates ldind.i1
+ if (value != loaded)
{
- Console.WriteLine("Hello");
- return 0;
+ return 1;
}
+
+ value = 127;
+ loaded = *ptr;
+ if (value != loaded)
+ {
+ return 1;
+ }
+
+ return 0;
+ }
+
+ static int TestLdindU1()
+ {
+ byte value = 255;
+ byte* ptr = &value;
+ byte loaded = *ptr; // This generates ldind.u1
+ if (value != loaded)
+ {
+ return 1;
+ }
+
+ value = 0;
+ loaded = *ptr;
+ if (value != loaded)
+ {
+ return 1;
+ }
+
+ return 0;
+ }
+
+ static int TestLdindI2()
+ {
+ short value = -32768;
+ short* ptr = &value;
+ short loaded = *ptr; // This generates ldind.i2
+ if (value != loaded)
+ {
+ return 1;
+ }
+
+ value = 32767;
+ loaded = *ptr;
+ if (value != loaded)
+ {
+ return 1;
+ }
+
+ return 0;
+ }
+
+ static int TestLdindU2()
+ {
+ ushort value = 65535;
+ ushort* ptr = &value;
+ ushort loaded = *ptr; // This generates ldind.u2
+ if (value != loaded)
+ {
+ return 1;
+ }
+
+ value = 0;
+ loaded = *ptr;
+ if (value != loaded)
+ {
+ return 1;
+ }
+
+ return 0;
+ }
+
+ static int TestLdindI4()
+ {
+ int value = int.MinValue;
+ int* ptr = &value;
+ int loaded = *ptr; // This generates ldind.i4
+ if (value != loaded)
+ {
+ return 1;
+ }
+
+ value = int.MaxValue;
+ loaded = *ptr;
+ if (value != loaded)
+ {
+ return 1;
+ }
+
+ return 0;
+ }
+
+ static int TestLdindU4()
+ {
+ uint value = uint.MaxValue;
+ uint* ptr = &value;
+ uint loaded = *ptr; // This generates ldind.u4
+ if (value != loaded)
+ {
+ return 1;
+ }
+
+ value = 0;
+ loaded = *ptr;
+ if (value != loaded)
+ {
+ return 1;
+ }
+
+ return 0;
+ }
+
+ static int TestLdindI8()
+ {
+ long value = long.MinValue;
+ long* ptr = &value;
+ long loaded = *ptr; // This generates ldind.i8
+ if (value != loaded)
+ {
+ return 1;
+ }
+
+ value = long.MaxValue;
+ loaded = *ptr;
+ if (value != loaded)
+ {
+ return 1;
+ }
+
+ return 0;
+ }
+
+ static int TestLdindI8ViaU8()
+ {
+ ulong value = ulong.MaxValue;
+ ulong* ptr = &value;
+ ulong loaded = *ptr; // This generates ldind.i8 again, because there's no ldind.u8
+ if (value != loaded)
+ {
+ return 1;
+ }
+
+ value = 0;
+ loaded = *ptr;
+ if (value != loaded)
+ {
+ return 1;
+ }
+
+ return 0;
+ }
+
+ static int TestLdindR4()
+ {
+ float value = float.MinValue;
+ float* ptr = &value;
+ float loaded = *ptr; // This generates ldind.r4
+ if (BitConverter.SingleToInt32Bits(value) != BitConverter.SingleToInt32Bits(loaded))
+ {
+ return 1;
+ }
+
+ value = float.MaxValue;
+ loaded = *ptr;
+ if (BitConverter.SingleToInt32Bits(value) != BitConverter.SingleToInt32Bits(loaded))
+ {
+ return 1;
+ }
+
+ value = float.NaN;
+ loaded = *ptr;
+ if (BitConverter.SingleToInt32Bits(value) != BitConverter.SingleToInt32Bits(loaded))
+ {
+ return 1;
+ }
+
+ return 0;
+ }
+
+ static int TestLdindR8()
+ {
+ double value = double.MinValue;
+ double* ptr = &value;
+ double loaded = *ptr; // This generates ldind.r8
+ if (BitConverter.DoubleToInt64Bits(value) != BitConverter.DoubleToInt64Bits(loaded))
+ {
+ return 1;
+ }
+
+ value = double.MaxValue;
+ loaded = *ptr;
+ if (BitConverter.DoubleToInt64Bits(value) != BitConverter.DoubleToInt64Bits(loaded))
+ {
+ return 1;
+ }
+
+ value = double.NaN;
+ loaded = *ptr;
+ if (BitConverter.DoubleToInt64Bits(value) != BitConverter.DoubleToInt64Bits(loaded))
+ {
+ return 1;
+ }
+
+ return 0;
+ }
+
+ static int TestLdindI()
+ {
+ IntPtr value = new IntPtr(42);
+ IntPtr* ptr = &value;
+ IntPtr loaded = *ptr; // This generates ldind.i
+ if (value != loaded)
+ {
+ return 1;
+ }
+
+ value = IntPtr.Zero;
+ loaded = *ptr;
+ if (value != loaded)
+ {
+ return 1;
+ }
+
+ return 0;
+ }
+
+ static int TestTruncation()
+ {
+ // Store a larger value and load as smaller type
+ int largeValue = 0x1234ABCD;
+ void* ptr = &largeValue;
+
+ byte byteValue = *(byte*)ptr; // Should truncate to 0xCD
+ if (byteValue != 0xCD)
+ {
+ return 1;
+ }
+
+ short shortValue = *(short*)ptr; // Should truncate to 0xABCD
+ if (shortValue != -21555)
+ {
+ return 1;
+ }
+
+ // Test sign extension
+ sbyte signedByte = *(sbyte*)ptr; // 0xCD as signed byte is -51
+ if (signedByte != -51)
+ {
+ return 1;
+ }
+
+ return 0;
+ }
+
+ static int TestManagedPointers()
+ {
+ int value = 42;
+ ref int refValue = ref value;
+ var expected42 = TestRefParameter(ref refValue);
+ if (expected42 != 42)
+ {
+ return 1;
+ }
+
+ if (refValue != 84)
+ {
+ return 1;
+ }
+
+ // Test with array element
+ int[] array = new int[3];
+ array[0] = 10;
+ array[1] = 20;
+ array[2] = 30;
+ ref int element = ref array[1];
+ if (element != 20)
+ {
+ return 1;
+ }
+
+ // Test with local variable
+ int local = 100;
+ var expected100 = TestRefLocal(ref local);
+ if (expected100 != 100)
+ {
+ return 1;
+ }
+
+ return 0;
+ }
+
+ static int TestRefParameter(ref int param)
+ {
+ var result = 0;
+ // This will use ldind instructions when accessing param
+ result += param;
+ param = 84;
+ return result;
+ }
+
+ static int TestRefLocal(ref int local)
+ {
+ // Create a ref local
+ ref int refLocal = ref local;
+ return refLocal;
}
}
diff --git a/WoofWare.PawPrint.Test/TestHarness.fs b/WoofWare.PawPrint.Test/TestHarness.fs
index bcaa7e5..b59aa4e 100644
--- a/WoofWare.PawPrint.Test/TestHarness.fs
+++ b/WoofWare.PawPrint.Test/TestHarness.fs
@@ -30,5 +30,4 @@ type TestCase =
FileName : string
ExpectedReturnCode : int
NativeImpls : NativeImpls
- LocalVariablesOfMain : CliType list option
}
diff --git a/WoofWare.PawPrint.Test/TestImpureCases.fs b/WoofWare.PawPrint.Test/TestImpureCases.fs
index 5c136a3..638fbc1 100644
--- a/WoofWare.PawPrint.Test/TestImpureCases.fs
+++ b/WoofWare.PawPrint.Test/TestImpureCases.fs
@@ -17,7 +17,29 @@ module TestImpureCases =
FileName = "WriteLine.cs"
ExpectedReturnCode = 1
NativeImpls = NativeImpls.PassThru ()
- LocalVariablesOfMain = [] |> Some
+ }
+
+ {
+ FileName = "ConsoleColor.cs"
+ ExpectedReturnCode = 1
+ NativeImpls =
+ let mock = MockEnv.make ()
+
+ { mock with
+ System_Environment =
+ { System_EnvironmentMock.Empty with
+ GetProcessorCount =
+ fun thread state ->
+ let state =
+ state |> IlMachineState.pushToEvalStack' (EvalStackValue.Int32 1) thread
+
+ (state, WhatWeDid.Executed) |> ExecutionResult.Stepped
+ _Exit =
+ fun thread state ->
+ let state = state |> IlMachineState.loadArgument thread 0
+ ExecutionResult.Terminated (state, thread)
+ }
+ }
}
]
@@ -44,7 +66,6 @@ module TestImpureCases =
ExecutionResult.Terminated (state, thread)
}
}
- LocalVariablesOfMain = [] |> Some
}
]
@@ -71,15 +92,6 @@ module TestImpureCases =
| ret -> failwith $"expected program to return an int, but it returned %O{ret}"
exitCode |> shouldEqual case.ExpectedReturnCode
-
- let finalVariables =
- terminalState.ThreadState.[terminatingThread].MethodState.LocalVariables
- |> Seq.toList
-
- match case.LocalVariablesOfMain with
- | None -> ()
- | Some expected -> finalVariables |> shouldEqual expected
-
with _ ->
for message in messages () do
System.Console.Error.WriteLine $"{message}"
diff --git a/WoofWare.PawPrint.Test/TestPureCases.fs b/WoofWare.PawPrint.Test/TestPureCases.fs
index adab284..b6f2d7d 100644
--- a/WoofWare.PawPrint.Test/TestPureCases.fs
+++ b/WoofWare.PawPrint.Test/TestPureCases.fs
@@ -17,73 +17,36 @@ module TestPureCases =
FileName = "CrossAssemblyTypes.cs"
ExpectedReturnCode = 0
NativeImpls = MockEnv.make ()
- LocalVariablesOfMain = None
}
{
FileName = "InitializeArray.cs"
ExpectedReturnCode = 0
NativeImpls = MockEnv.make ()
- LocalVariablesOfMain = None
- }
- {
- FileName = "GenericEdgeCases.cs"
- ExpectedReturnCode = 0
- NativeImpls = MockEnv.make ()
- LocalVariablesOfMain = None
}
{
FileName = "Threads.cs"
ExpectedReturnCode = 3
NativeImpls = MockEnv.make ()
- LocalVariablesOfMain = [] |> Some
}
{
FileName = "ComplexTryCatch.cs"
ExpectedReturnCode = 14
NativeImpls = NativeImpls.PassThru ()
- LocalVariablesOfMain =
- [
- 4
- 20
- 115
- 12
- 1
- 10
- 2
- 112
- 12
- 1111
- 42
- 99
- 25
- 50
- 123
- 20
- 35
- 5
- 11111
- 100001
- ]
- |> List.map (fun i -> CliType.Numeric (CliNumericType.Int32 i))
- |> Some
}
{
FileName = "ResizeArray.cs"
ExpectedReturnCode = 109
NativeImpls = MockEnv.make ()
- LocalVariablesOfMain = [ CliType.Numeric (CliNumericType.Int32 10) ] |> Some
}
{
FileName = "Sizeof.cs"
ExpectedReturnCode = 0
NativeImpls = MockEnv.make ()
- LocalVariablesOfMain = None
}
{
FileName = "LdtokenField.cs"
ExpectedReturnCode = 0
NativeImpls = MockEnv.make ()
- LocalVariablesOfMain = None
}
]
@@ -93,61 +56,41 @@ module TestPureCases =
FileName = "NoOp.cs"
ExpectedReturnCode = 1
NativeImpls = MockEnv.make ()
- LocalVariablesOfMain = [ CliType.Numeric (CliNumericType.Int32 1) ] |> Some
+ }
+ {
+ FileName = "GenericEdgeCases.cs"
+ ExpectedReturnCode = 0
+ NativeImpls = MockEnv.make ()
}
{
FileName = "TestShl.cs"
ExpectedReturnCode = 0
NativeImpls = MockEnv.make ()
- LocalVariablesOfMain = None
}
{
FileName = "TestShr.cs"
ExpectedReturnCode = 0
NativeImpls = MockEnv.make ()
- LocalVariablesOfMain = None
}
{
FileName = "StaticVariables.cs"
ExpectedReturnCode = 0
NativeImpls = MockEnv.make ()
- LocalVariablesOfMain = None
}
{
FileName = "Ldind.cs"
ExpectedReturnCode = 0
NativeImpls = MockEnv.make ()
- LocalVariablesOfMain =
- [
- // `failures`
- CliType.Numeric (CliNumericType.Int32 0)
- // Return value
- CliType.Numeric (CliNumericType.Int32 0)
- ]
- |> Some
}
{
FileName = "CustomDelegate.cs"
ExpectedReturnCode = 8
NativeImpls = MockEnv.make ()
- LocalVariablesOfMain =
- [
- // filter
- CliType.ObjectRef (Some (ManagedHeapAddress 2))
- // result
- CliType.ofBool true
- // result, cloned for "if(result)" check
- CliType.ofBool true
- // ret
- CliType.Numeric (CliNumericType.Int32 8)
- ]
- |> Some
}
{
FileName = "ArgumentOrdering.cs"
ExpectedReturnCode = 0
NativeImpls = MockEnv.make ()
- LocalVariablesOfMain = None
}
{
FileName = "BasicLock.cs"
@@ -158,92 +101,46 @@ module TestPureCases =
{ mock with
System_Threading_Monitor = System_Threading_Monitor.passThru
}
- LocalVariablesOfMain =
- [
- // Four variables:
- // locker
- CliType.ObjectRef (Some (ManagedHeapAddress 2))
- // a copy of locker, taken so that the contents of the implicit `finally` have a stable copy
- CliType.ObjectRef (Some (ManagedHeapAddress 2))
- // out param of `ReliableEnter`
- CliType.ofBool true
- // return value
- CliType.Numeric (CliNumericType.Int32 1)
- ]
- |> Some
}
{
FileName = "TriangleNumber.cs"
ExpectedReturnCode = 10
NativeImpls = MockEnv.make ()
- LocalVariablesOfMain =
- [
- // answer
- CliType.Numeric (CliNumericType.Int32 10)
- // i
- CliType.Numeric (CliNumericType.Int32 5)
- // End-loop condition
- CliType.ofBool false
- // Ret
- CliType.Numeric (CliNumericType.Int32 10)
- ]
- |> Some
}
{
FileName = "ExceptionWithNoOpFinally.cs"
ExpectedReturnCode = 3
NativeImpls = MockEnv.make ()
- LocalVariablesOfMain =
- [
- // Variable 1 is `x`, variable 2 is the implicit return value
- 4
- 3
- ]
- |> List.map (fun i -> CliType.Numeric (CliNumericType.Int32 i))
- |> Some
}
{
FileName = "ExceptionWithNoOpCatch.cs"
ExpectedReturnCode = 10
NativeImpls = MockEnv.make ()
- LocalVariablesOfMain = [ CliType.Numeric (CliNumericType.Int32 10) ] |> Some
}
{
FileName = "Floats.cs"
ExpectedReturnCode = 0
NativeImpls = MockEnv.make ()
- LocalVariablesOfMain = None
}
{
FileName = "TryCatchWithThrowInBody.cs"
ExpectedReturnCode = 4
NativeImpls = MockEnv.make ()
- LocalVariablesOfMain =
- [
- // one variable is x, one variable is the return value which also happens to have the same value
- 4
- 4
- ]
- |> List.map (fun i -> CliType.Numeric (CliNumericType.Int32 i))
- |> Some
}
{
FileName = "Ldelema.cs"
ExpectedReturnCode = 0
NativeImpls = MockEnv.make ()
- LocalVariablesOfMain = None
}
{
FileName = "TypeConcretization.cs"
ExpectedReturnCode = 0
NativeImpls = MockEnv.make ()
- LocalVariablesOfMain = None
}
{
FileName = "TestOr.cs"
ExpectedReturnCode = 0
NativeImpls = MockEnv.make ()
- LocalVariablesOfMain = None
}
]
@@ -274,14 +171,6 @@ module TestPureCases =
exitCode |> shouldEqual realResult.ExitCode
exitCode |> shouldEqual case.ExpectedReturnCode
-
- let finalVariables =
- terminalState.ThreadState.[terminatingThread].MethodState.LocalVariables
- |> Seq.toList
-
- match case.LocalVariablesOfMain with
- | None -> ()
- | Some expected -> finalVariables |> shouldEqual expected
with _ ->
for message in messages () do
System.Console.Error.WriteLine $"{message}"
diff --git a/WoofWare.PawPrint.Test/WoofWare.PawPrint.Test.fsproj b/WoofWare.PawPrint.Test/WoofWare.PawPrint.Test.fsproj
index bb485bd..4c64d84 100644
--- a/WoofWare.PawPrint.Test/WoofWare.PawPrint.Test.fsproj
+++ b/WoofWare.PawPrint.Test/WoofWare.PawPrint.Test.fsproj
@@ -45,6 +45,7 @@
+
diff --git a/WoofWare.PawPrint.Test/sourcesImpure/ConsoleColor.cs b/WoofWare.PawPrint.Test/sourcesImpure/ConsoleColor.cs
new file mode 100644
index 0000000..c453f95
--- /dev/null
+++ b/WoofWare.PawPrint.Test/sourcesImpure/ConsoleColor.cs
@@ -0,0 +1,13 @@
+using System;
+
+namespace HelloWorldApp
+{
+ class Program
+ {
+ static int Main(string[] args)
+ {
+ Console.WriteLine("Hello, world!");
+ return 1;
+ }
+ }
+}
diff --git a/WoofWare.PawPrint.Test/sourcesImpure/WriteLine.cs b/WoofWare.PawPrint.Test/sourcesImpure/WriteLine.cs
index c453f95..5907c52 100644
--- a/WoofWare.PawPrint.Test/sourcesImpure/WriteLine.cs
+++ b/WoofWare.PawPrint.Test/sourcesImpure/WriteLine.cs
@@ -6,7 +6,8 @@ namespace HelloWorldApp
{
static int Main(string[] args)
{
- Console.WriteLine("Hello, world!");
+ var c = Console.BackgroundColor;
+ var d = Console.ForegroundColor;
return 1;
}
}
diff --git a/WoofWare.PawPrint/AbstractMachine.fs b/WoofWare.PawPrint/AbstractMachine.fs
index c063560..24e5fcf 100644
--- a/WoofWare.PawPrint/AbstractMachine.fs
+++ b/WoofWare.PawPrint/AbstractMachine.fs
@@ -55,18 +55,19 @@ module AbstractMachine =
// We've been instructed to run a delegate.
let delegateToRunAddr =
match instruction.Arguments.[0] with
+ | CliType.RuntimePointer (CliRuntimePointer.Managed (CliRuntimePointerSource.Heap addr))
| CliType.ObjectRef (Some addr) -> addr
| _ -> failwith "expected a managed object ref to delegate"
let delegateToRun = state.ManagedHeap.NonArrayObjects.[delegateToRunAddr]
let target =
- match delegateToRun.Fields.["_target"] with
+ match delegateToRun.Fields |> List.find (fun (x, _) -> x = "_target") |> snd with
| CliType.ObjectRef addr -> addr
| x -> failwith $"TODO: delegate target wasn't an object ref: %O{x}"
let methodPtr =
- match delegateToRun.Fields.["_methodPtr"] with
+ match delegateToRun.Fields |> List.find (fun (x, _) -> x = "_methodPtr") |> snd with
| CliType.Numeric (CliNumericType.NativeInt (NativeIntSource.FunctionPointer mi)) -> mi
| d -> failwith $"unexpectedly not a method pointer in delegate invocation: {d}"
diff --git a/WoofWare.PawPrint/BasicCliType.fs b/WoofWare.PawPrint/BasicCliType.fs
index 7ae763c..623a923 100644
--- a/WoofWare.PawPrint/BasicCliType.fs
+++ b/WoofWare.PawPrint/BasicCliType.fs
@@ -41,6 +41,7 @@ type ManagedPointerSource =
| Argument of sourceThread : ThreadId * methodFrame : int * whichVar : uint16
| Heap of ManagedHeapAddress
| ArrayIndex of arr : ManagedHeapAddress * index : int
+ | Field of ManagedPointerSource * fieldName : string
| Null
override this.ToString () =
@@ -52,6 +53,7 @@ type ManagedPointerSource =
| ManagedPointerSource.Argument (source, method, var) ->
$""
| ManagedPointerSource.ArrayIndex (arr, index) -> $""
+ | ManagedPointerSource.Field (source, name) -> $""
[]
type UnsignedNativeIntSource =
@@ -117,10 +119,27 @@ type CliNumericType =
type CliRuntimePointerSource =
| LocalVariable of sourceThread : ThreadId * methodFrame : int * whichVar : uint16
| Argument of sourceThread : ThreadId * methodFrame : int * whichVar : uint16
+ | Field of source : CliRuntimePointerSource * fieldName : string
| Heap of ManagedHeapAddress
| ArrayIndex of arr : ManagedHeapAddress * index : int
| Null
+[]
+module CliRuntimePointerSource =
+ let rec ofManagedPointerSource (ptrSource : ManagedPointerSource) : CliRuntimePointerSource =
+ match ptrSource with
+ | ManagedPointerSource.LocalVariable (sourceThread, methodFrame, whichVar) ->
+ CliRuntimePointerSource.LocalVariable (sourceThread, methodFrame, whichVar)
+ | ManagedPointerSource.Argument (sourceThread, methodFrame, whichVar) ->
+ CliRuntimePointerSource.Argument (sourceThread, methodFrame, whichVar)
+ | ManagedPointerSource.Heap managedHeapAddress -> CliRuntimePointerSource.Heap managedHeapAddress
+ | ManagedPointerSource.Null -> CliRuntimePointerSource.Null
+ | ManagedPointerSource.ArrayIndex (arr, ind) -> CliRuntimePointerSource.ArrayIndex (arr, ind)
+ | ManagedPointerSource.Field (a, ind) ->
+ let a = ofManagedPointerSource a
+ CliRuntimePointerSource.Field (a, ind)
+
+
type CliRuntimePointer =
| Unmanaged of int64
| Managed of CliRuntimePointerSource
@@ -204,8 +223,22 @@ module CliType =
| PrimitiveType.Double -> CliType.Numeric (CliNumericType.Float64 0.0)
| PrimitiveType.String -> CliType.ObjectRef None
| PrimitiveType.TypedReference -> failwith "todo"
- | PrimitiveType.IntPtr -> CliType.RuntimePointer (CliRuntimePointer.Managed CliRuntimePointerSource.Null)
- | PrimitiveType.UIntPtr -> CliType.RuntimePointer (CliRuntimePointer.Managed CliRuntimePointerSource.Null)
+ | PrimitiveType.IntPtr ->
+ {
+ Fields =
+ [
+ "_value", CliType.RuntimePointer (CliRuntimePointer.Managed CliRuntimePointerSource.Null)
+ ]
+ }
+ |> CliType.ValueType
+ | PrimitiveType.UIntPtr ->
+ {
+ Fields =
+ [
+ "_value", CliType.RuntimePointer (CliRuntimePointer.Managed CliRuntimePointerSource.Null)
+ ]
+ }
+ |> CliType.ValueType
| PrimitiveType.Object -> CliType.ObjectRef None
let rec zeroOf
diff --git a/WoofWare.PawPrint/EvalStack.fs b/WoofWare.PawPrint/EvalStack.fs
index 8366dfb..87651df 100644
--- a/WoofWare.PawPrint/EvalStack.fs
+++ b/WoofWare.PawPrint/EvalStack.fs
@@ -156,19 +156,13 @@ module EvalStackValue =
| CliType.ObjectRef _ ->
match popped with
| EvalStackValue.ManagedPointer ptrSource ->
- match ptrSource with
- | ManagedPointerSource.LocalVariable (sourceThread, methodFrame, whichVar) ->
- CliRuntimePointerSource.LocalVariable (sourceThread, methodFrame, whichVar)
- |> CliRuntimePointer.Managed
- |> CliType.RuntimePointer
- | ManagedPointerSource.Argument (sourceThread, methodFrame, whichVar) ->
- CliRuntimePointerSource.Argument (sourceThread, methodFrame, whichVar)
- |> CliRuntimePointer.Managed
- |> CliType.RuntimePointer
- | ManagedPointerSource.Heap managedHeapAddress -> CliType.ObjectRef (Some managedHeapAddress)
- | ManagedPointerSource.Null -> CliType.ObjectRef None
- | ManagedPointerSource.ArrayIndex (arr, ind) ->
- CliType.RuntimePointer (CliRuntimePointer.Managed (CliRuntimePointerSource.ArrayIndex (arr, ind)))
+ CliRuntimePointerSource.ofManagedPointerSource ptrSource
+ |> CliRuntimePointer.Managed
+ |> CliType.RuntimePointer
+ | EvalStackValue.ObjectRef ptr ->
+ CliRuntimePointerSource.Heap ptr
+ |> CliRuntimePointer.Managed
+ |> CliType.RuntimePointer
| EvalStackValue.NativeInt nativeIntSource ->
match nativeIntSource with
| NativeIntSource.Verbatim 0L -> CliType.ObjectRef None
@@ -196,40 +190,23 @@ module EvalStackValue =
| CliType.RuntimePointer _ ->
match popped with
| EvalStackValue.ManagedPointer src ->
- match src with
- | ManagedPointerSource.Heap addr -> CliType.ofManagedObject addr
- | ManagedPointerSource.Null -> CliType.ObjectRef None
- | ManagedPointerSource.LocalVariable (sourceThread, methodFrame, var) ->
- CliRuntimePointerSource.LocalVariable (sourceThread, methodFrame, var)
- |> CliRuntimePointer.Managed
- |> CliType.RuntimePointer
- | ManagedPointerSource.Argument (sourceThread, methodFrame, var) ->
- CliRuntimePointerSource.Argument (sourceThread, methodFrame, var)
- |> CliRuntimePointer.Managed
- |> CliType.RuntimePointer
- | ManagedPointerSource.ArrayIndex (arr, index) ->
- CliRuntimePointerSource.ArrayIndex (arr, index)
- |> CliRuntimePointer.Managed
- |> CliType.RuntimePointer
+ CliRuntimePointerSource.ofManagedPointerSource src
+ |> CliRuntimePointer.Managed
+ |> CliType.RuntimePointer
| EvalStackValue.NativeInt intSrc ->
match intSrc with
| NativeIntSource.Verbatim i -> CliType.RuntimePointer (CliRuntimePointer.Unmanaged i)
| NativeIntSource.ManagedPointer src ->
- match src with
- | ManagedPointerSource.Heap src ->
- CliType.RuntimePointer (CliRuntimePointer.Managed (CliRuntimePointerSource.Heap src))
- | ManagedPointerSource.Null ->
- CliType.RuntimePointer (CliRuntimePointer.Managed CliRuntimePointerSource.Null)
- | ManagedPointerSource.LocalVariable (a, b, c) ->
- CliType.RuntimePointer (
- CliRuntimePointer.Managed (CliRuntimePointerSource.LocalVariable (a, b, c))
- )
- | ManagedPointerSource.Argument (a, b, c) ->
- CliType.RuntimePointer (CliRuntimePointer.Managed (CliRuntimePointerSource.Argument (a, b, c)))
- | ManagedPointerSource.ArrayIndex _ -> failwith "TODO"
+ CliRuntimePointerSource.ofManagedPointerSource src
+ |> CliRuntimePointer.Managed
+ |> CliType.RuntimePointer
| NativeIntSource.FunctionPointer methodInfo ->
CliType.Numeric (CliNumericType.NativeInt (NativeIntSource.FunctionPointer methodInfo))
| NativeIntSource.TypeHandlePtr int64 -> failwith "todo"
+ | EvalStackValue.ObjectRef addr ->
+ CliRuntimePointerSource.Heap addr
+ |> CliRuntimePointer.Managed
+ |> CliType.RuntimePointer
| _ -> failwith $"TODO: %O{popped}"
| CliType.Char _ ->
match popped with
@@ -303,6 +280,9 @@ module EvalStackValue =
|> EvalStackValue.ManagedPointer
| CliRuntimePointerSource.Heap addr -> EvalStackValue.ObjectRef addr
| CliRuntimePointerSource.Null -> EvalStackValue.ManagedPointer ManagedPointerSource.Null
+ | CliRuntimePointerSource.Field (source, fieldName) ->
+ ManagedPointerSource.Field (failwith "TODO", fieldName)
+ |> EvalStackValue.ManagedPointer
| CliType.ValueType fields ->
fields.Fields
|> List.map (fun (name, f) -> name, ofCliType f)
diff --git a/WoofWare.PawPrint/ExternImplementations/System.Threading.Monitor.fs b/WoofWare.PawPrint/ExternImplementations/System.Threading.Monitor.fs
index f765601..9b139a5 100644
--- a/WoofWare.PawPrint/ExternImplementations/System.Threading.Monitor.fs
+++ b/WoofWare.PawPrint/ExternImplementations/System.Threading.Monitor.fs
@@ -62,6 +62,7 @@ module System_Threading_Monitor =
match lockObj with
| EvalStackValue.ManagedPointer ManagedPointerSource.Null ->
failwith "TODO: throw ArgumentNullException"
+ | EvalStackValue.ObjectRef addr
| EvalStackValue.ManagedPointer (ManagedPointerSource.Heap addr) ->
match IlMachineState.getSyncBlock addr state with
| SyncBlock.Free ->
@@ -87,6 +88,7 @@ module System_Threading_Monitor =
failwith "not really expecting to *edit* an argument..."
| ManagedPointerSource.Heap addr -> failwith "todo: managed heap"
| ManagedPointerSource.ArrayIndex _ -> failwith "todo: array index"
+ | ManagedPointerSource.Field (managedPointerSource, fieldName) -> failwith "todo"
(state, WhatWeDid.Executed) |> ExecutionResult.Stepped
@@ -100,6 +102,7 @@ module System_Threading_Monitor =
match lockObj with
| EvalStackValue.ManagedPointer ManagedPointerSource.Null ->
failwith "TODO: throw ArgumentNullException"
+ | EvalStackValue.ObjectRef addr
| EvalStackValue.ManagedPointer (ManagedPointerSource.Heap addr) ->
match IlMachineState.getSyncBlock addr state with
| SyncBlock.Free -> failwith "TODO: throw SynchronizationLockException"
diff --git a/WoofWare.PawPrint/IlMachineState.fs b/WoofWare.PawPrint/IlMachineState.fs
index 3dee5ee..21f4bd4 100644
--- a/WoofWare.PawPrint/IlMachineState.fs
+++ b/WoofWare.PawPrint/IlMachineState.fs
@@ -674,14 +674,14 @@ module IlMachineState =
(state : IlMachineState)
: IlMachineState
=
- let heap = ManagedHeap.SetArrayValue arrayAllocation index v state.ManagedHeap
+ let heap = ManagedHeap.setArrayValue arrayAllocation index v state.ManagedHeap
{ state with
ManagedHeap = heap
}
let getArrayValue (arrayAllocation : ManagedHeapAddress) (index : int) (state : IlMachineState) : CliType =
- ManagedHeap.GetArrayValue arrayAllocation index state.ManagedHeap
+ ManagedHeap.getArrayValue arrayAllocation index state.ManagedHeap
/// There might be no stack frame to return to, so you might get None.
let returnStackFrame
@@ -737,7 +737,7 @@ module IlMachineState =
| ResolvedBaseType.ValueType ->
let vt =
{
- Fields = Map.toList constructed.Fields
+ Fields = constructed.Fields
}
state
@@ -1043,7 +1043,7 @@ module IlMachineState =
Logger = logger
NextThreadId = 0
// CallStack = []
- ManagedHeap = ManagedHeap.Empty
+ ManagedHeap = ManagedHeap.empty
ThreadState = Map.empty
InternedStrings = ImmutableDictionary.Empty
_LoadedAssemblies = ImmutableDictionary.Empty
@@ -1089,7 +1089,7 @@ module IlMachineState =
Elements = initialisation
}
- let alloc, heap = state.ManagedHeap |> ManagedHeap.AllocateArray o
+ let alloc, heap = state.ManagedHeap |> ManagedHeap.allocateArray o
let state =
{ state with
@@ -1099,7 +1099,7 @@ module IlMachineState =
alloc, state
let allocateStringData (len : int) (state : IlMachineState) : int * IlMachineState =
- let addr, heap = state.ManagedHeap |> ManagedHeap.AllocateString len
+ let addr, heap = state.ManagedHeap |> ManagedHeap.allocateString len
let state =
{ state with
@@ -1109,7 +1109,7 @@ module IlMachineState =
addr, state
let setStringData (addr : int) (contents : string) (state : IlMachineState) : IlMachineState =
- let heap = ManagedHeap.SetStringData addr contents state.ManagedHeap
+ let heap = ManagedHeap.setStringData addr contents state.ManagedHeap
{ state with
ManagedHeap = heap
@@ -1123,12 +1123,12 @@ module IlMachineState =
=
let o =
{
- Fields = Map.ofList fields
+ Fields = fields
Type = TypeInfoCrate.make typeInfo
SyncBlock = SyncBlock.Free
}
- let alloc, heap = state.ManagedHeap |> ManagedHeap.AllocateNonArray o
+ let alloc, heap = state.ManagedHeap |> ManagedHeap.allocateNonArray o
let state =
{ state with
@@ -1399,11 +1399,11 @@ module IlMachineState =
: IlMachineState
=
{ state with
- ManagedHeap = state.ManagedHeap |> ManagedHeap.SetSyncBlock addr syncBlockValue
+ ManagedHeap = state.ManagedHeap |> ManagedHeap.setSyncBlock addr syncBlockValue
}
let getSyncBlock (addr : ManagedHeapAddress) (state : IlMachineState) : SyncBlock =
- state.ManagedHeap |> ManagedHeap.GetSyncBlock addr
+ state.ManagedHeap |> ManagedHeap.getSyncBlock addr
let executeDelegateConstructor (instruction : MethodState) (state : IlMachineState) : IlMachineState =
// We've been called with arguments already popped from the stack into local arguments.
@@ -1413,12 +1413,17 @@ module IlMachineState =
let targetObj =
match targetObj with
- | CliType.ObjectRef target -> target
+ | CliType.RuntimePointer (CliRuntimePointer.Managed (CliRuntimePointerSource.Heap target))
+ | CliType.ObjectRef (Some target) -> Some target
+ | CliType.ObjectRef None
+ | CliType.RuntimePointer (CliRuntimePointer.Managed CliRuntimePointerSource.Null) -> None
| _ -> failwith $"Unexpected target type for delegate: {targetObj}"
let constructing =
match constructing with
+ | CliType.RuntimePointer (CliRuntimePointer.Managed CliRuntimePointerSource.Null)
| CliType.ObjectRef None -> failwith "unexpectedly constructing the null delegate"
+ | CliType.RuntimePointer (CliRuntimePointer.Managed (CliRuntimePointerSource.Heap target))
| CliType.ObjectRef (Some target) -> target
| _ -> failwith $"Unexpectedly not constructing a managed object: {constructing}"
@@ -1430,9 +1435,10 @@ module IlMachineState =
// Standard delegate fields in .NET are _target and _methodPtr
// Update the fields with the target object and method pointer
let updatedFields =
- heapObj.Fields
- |> Map.add "_target" (CliType.ObjectRef targetObj)
- |> Map.add "_methodPtr" methodPtr
+ // TODO: field ordering here is probably wrong
+ ("_target", CliType.ObjectRef targetObj)
+ :: ("_methodPtr", methodPtr)
+ :: heapObj.Fields
let updatedObj =
{ heapObj with
@@ -1533,3 +1539,19 @@ module IlMachineState =
match v.TryGetValue field with
| false, _ -> None
| true, v -> Some v
+
+ let rec dereferencePointer (state : IlMachineState) (src : ManagedPointerSource) : CliType =
+ match src with
+ | ManagedPointerSource.Null -> failwith "TODO: throw NRE"
+ | ManagedPointerSource.LocalVariable (sourceThread, methodFrame, whichVar) ->
+ state.ThreadState.[sourceThread].MethodStates.[methodFrame].LocalVariables.[int whichVar]
+ | ManagedPointerSource.Argument (sourceThread, methodFrame, whichVar) ->
+ state.ThreadState.[sourceThread].MethodStates.[methodFrame].Arguments.[int whichVar]
+ | ManagedPointerSource.Heap addr -> failwith "todo"
+ | ManagedPointerSource.ArrayIndex (arr, index) -> getArrayValue arr index state
+ | ManagedPointerSource.Field (addr, name) ->
+ let obj = dereferencePointer state addr
+
+ match obj with
+ | CliType.ValueType vt -> vt.Fields |> Map.ofList |> Map.find name
+ | v -> failwith $"could not find field {name} on object {v}"
diff --git a/WoofWare.PawPrint/Intrinsics.fs b/WoofWare.PawPrint/Intrinsics.fs
index 37b09ce..e8be9bf 100644
--- a/WoofWare.PawPrint/Intrinsics.fs
+++ b/WoofWare.PawPrint/Intrinsics.fs
@@ -90,6 +90,7 @@ module Intrinsics =
CliRuntimePointer.Managed (CliRuntimePointerSource.Heap managedHeapAddress)
| ManagedPointerSource.Null -> failwith "todo"
| ManagedPointerSource.ArrayIndex _ -> failwith "TODO"
+ | ManagedPointerSource.Field _ -> failwith "TODO"
| x -> failwith $"TODO: Unsafe.AsPointer(%O{x})"
IlMachineState.pushToEvalStack (CliType.RuntimePointer toPush) currentThread state
@@ -154,6 +155,7 @@ module Intrinsics =
let arg1 =
match arg1 with
+ | EvalStackValue.ObjectRef h
| EvalStackValue.ManagedPointer (ManagedPointerSource.Heap h) -> h
| EvalStackValue.Int32 _
| EvalStackValue.Int64 _
@@ -164,6 +166,7 @@ module Intrinsics =
let arg2 =
match arg2 with
+ | EvalStackValue.ObjectRef h
| EvalStackValue.ManagedPointer (ManagedPointerSource.Heap h) -> h
| EvalStackValue.Int32 _
| EvalStackValue.Int64 _
@@ -183,14 +186,7 @@ module Intrinsics =
let v : CliType =
let rec go ptr =
match ptr with
- | EvalStackValue.ManagedPointer src ->
- match src with
- | ManagedPointerSource.LocalVariable (sourceThread, methodFrame, whichVar) -> failwith "todo"
- | ManagedPointerSource.Argument (sourceThread, methodFrame, whichVar) -> failwith "todo"
- | ManagedPointerSource.Heap managedHeapAddress -> failwith "todo"
- | ManagedPointerSource.ArrayIndex (arr, index) ->
- state |> IlMachineState.getArrayValue arr index
- | ManagedPointerSource.Null -> failwith "TODO: throw NRE"
+ | EvalStackValue.ManagedPointer src -> IlMachineState.dereferencePointer state src
| EvalStackValue.NativeInt src -> failwith "TODO"
| EvalStackValue.ObjectRef ptr -> failwith "TODO"
| EvalStackValue.UserDefinedValueType [ _, field ] -> go field
@@ -234,8 +230,14 @@ module Intrinsics =
else
failwith "TODO: unexpected params to String.op_Implicit"
| _ -> failwith "TODO: unexpected params to String.op_Implicit"
+ | "System.Private.CoreLib", "RuntimeHelpers", "IsReferenceOrContainsReferences" ->
+ // https://github.com/dotnet/runtime/blob/1d1bf92fcf43aa6981804dc53c5174445069c9e4/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs#L207
+ failwith "TODO: get generic type parameter and then do the thing"
| "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"
+ | "System.Private.CoreLib", "RuntimeHelpers", "CreateSpan" ->
+ // https://github.com/dotnet/runtime/blob/9e5e6aa7bc36aeb2a154709a9d1192030c30a2ef/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.cs#L153
+ None
| 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/List.fs b/WoofWare.PawPrint/List.fs
new file mode 100644
index 0000000..33a4d9e
--- /dev/null
+++ b/WoofWare.PawPrint/List.fs
@@ -0,0 +1,12 @@
+namespace WoofWare.PawPrint
+
+[]
+module List =
+ let replaceWhere (f : 'a -> 'a option) (l : 'a list) : 'a list =
+ ([], l)
+ ||> List.fold (fun acc x ->
+ match f x with
+ | None -> x :: acc
+ | Some y -> y :: acc
+ )
+ |> List.rev
diff --git a/WoofWare.PawPrint/ManagedHeap.fs b/WoofWare.PawPrint/ManagedHeap.fs
index 0de1a88..c037537 100644
--- a/WoofWare.PawPrint/ManagedHeap.fs
+++ b/WoofWare.PawPrint/ManagedHeap.fs
@@ -8,7 +8,7 @@ type SyncBlock =
type AllocatedNonArrayObject =
{
- Fields : Map
+ Fields : (string * CliType) list
Type : WoofWare.PawPrint.TypeInfoCrate
SyncBlock : SyncBlock
}
@@ -29,7 +29,9 @@ type ManagedHeap =
StringArrayData : ImmutableArray
}
- static member Empty : ManagedHeap =
+[]
+module ManagedHeap =
+ let empty : ManagedHeap =
{
NonArrayObjects = Map.empty
FirstAvailableAddress = 1
@@ -37,12 +39,12 @@ type ManagedHeap =
StringArrayData = ImmutableArray.Empty
}
- static member GetSyncBlock (addr : ManagedHeapAddress) (heap : ManagedHeap) : SyncBlock =
+ let getSyncBlock (addr : ManagedHeapAddress) (heap : ManagedHeap) : SyncBlock =
match heap.NonArrayObjects.TryGetValue addr with
| false, _ -> failwith "TODO: getting sync block of array"
| true, v -> v.SyncBlock
- static member SetSyncBlock (addr : ManagedHeapAddress) (syncValue : SyncBlock) (heap : ManagedHeap) : ManagedHeap =
+ let setSyncBlock (addr : ManagedHeapAddress) (syncValue : SyncBlock) (heap : ManagedHeap) : ManagedHeap =
match heap.NonArrayObjects.TryGetValue addr with
| false, _ -> failwith "TODO: locked on an array object"
| true, v ->
@@ -55,7 +57,7 @@ type ManagedHeap =
NonArrayObjects = heap.NonArrayObjects |> Map.add addr newV
}
- static member AllocateArray (ty : AllocatedArray) (heap : ManagedHeap) : ManagedHeapAddress * ManagedHeap =
+ let allocateArray (ty : AllocatedArray) (heap : ManagedHeap) : ManagedHeapAddress * ManagedHeap =
let addr = heap.FirstAvailableAddress
let heap =
@@ -68,7 +70,7 @@ type ManagedHeap =
ManagedHeapAddress addr, heap
- static member AllocateString (len : int) (heap : ManagedHeap) : int * ManagedHeap =
+ let allocateString (len : int) (heap : ManagedHeap) : int * ManagedHeap =
let addr = heap.StringArrayData.Length
let heap =
@@ -80,7 +82,7 @@ type ManagedHeap =
addr, heap
- static member SetStringData (addr : int) (contents : string) (heap : ManagedHeap) : ManagedHeap =
+ let setStringData (addr : int) (contents : string) (heap : ManagedHeap) : ManagedHeap =
let newArr =
(heap.StringArrayData, seq { 0 .. contents.Length - 1 })
||> Seq.fold (fun data count -> data.SetItem (addr + count, contents.[count]))
@@ -92,11 +94,7 @@ type ManagedHeap =
heap
- static member AllocateNonArray
- (ty : AllocatedNonArrayObject)
- (heap : ManagedHeap)
- : ManagedHeapAddress * ManagedHeap
- =
+ let allocateNonArray (ty : AllocatedNonArrayObject) (heap : ManagedHeap) : ManagedHeapAddress * ManagedHeap =
let addr = heap.FirstAvailableAddress
let heap =
@@ -109,7 +107,7 @@ type ManagedHeap =
ManagedHeapAddress addr, heap
- static member GetArrayValue (alloc : ManagedHeapAddress) (offset : int) (heap : ManagedHeap) : CliType =
+ let getArrayValue (alloc : ManagedHeapAddress) (offset : int) (heap : ManagedHeap) : CliType =
match heap.Arrays.TryGetValue alloc with
| false, _ -> failwith "TODO: array not on heap"
| true, arr ->
@@ -119,13 +117,7 @@ type ManagedHeap =
arr.Elements.[offset]
- static member SetArrayValue
- (alloc : ManagedHeapAddress)
- (offset : int)
- (v : CliType)
- (heap : ManagedHeap)
- : ManagedHeap
- =
+ let setArrayValue (alloc : ManagedHeapAddress) (offset : int) (v : CliType) (heap : ManagedHeap) : ManagedHeap =
let newArrs =
heap.Arrays
|> Map.change
diff --git a/WoofWare.PawPrint/NullaryIlOp.fs b/WoofWare.PawPrint/NullaryIlOp.fs
index 8e85aae..dacd518 100644
--- a/WoofWare.PawPrint/NullaryIlOp.fs
+++ b/WoofWare.PawPrint/NullaryIlOp.fs
@@ -37,19 +37,6 @@ module NullaryIlOp =
| LdindR4 -> CliType.Numeric (CliNumericType.Float32 0.0f)
| LdindR8 -> CliType.Numeric (CliNumericType.Float64 0.0)
- /// Retrieve a value from a pointer
- let private loadFromPointerSource (state : IlMachineState) (src : ManagedPointerSource) : CliType =
- match src with
- | ManagedPointerSource.Null -> failwith "unexpected null pointer in Ldind operation"
- | ManagedPointerSource.Argument (sourceThread, methodFrame, whichVar) ->
- state.ThreadState.[sourceThread].MethodStates.[methodFrame].Arguments.[int whichVar]
- | 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 (arr, index) ->
- let arr = state.ManagedHeap.Arrays.[arr]
- arr.Elements.[index]
-
// Unified Ldind implementation
let private executeLdind
(targetType : LdindTargetType)
@@ -61,7 +48,7 @@ module NullaryIlOp =
let loadedValue =
match popped with
- | EvalStackValue.ManagedPointer src -> loadFromPointerSource state src
+ | EvalStackValue.ManagedPointer src -> IlMachineState.dereferencePointer state src
| EvalStackValue.NativeInt nativeIntSource ->
failwith $"TODO: Native int pointer dereferencing not implemented for {targetType}"
| EvalStackValue.ObjectRef managedHeapAddress ->
@@ -125,6 +112,7 @@ module NullaryIlOp =
}
| ManagedPointerSource.Heap managedHeapAddress -> failwith "todo"
| ManagedPointerSource.ArrayIndex _ -> failwith "todo"
+ | Field (managedPointerSource, fieldName) -> failwith "todo"
| EvalStackValue.ObjectRef managedHeapAddress -> failwith "todo"
let internal ldElem
@@ -684,6 +672,7 @@ module NullaryIlOp =
let popped =
match popped with
| EvalStackValue.ManagedPointer ManagedPointerSource.Null -> failwith "TODO: throw NRE"
+ | EvalStackValue.ObjectRef addr
| EvalStackValue.ManagedPointer (ManagedPointerSource.Heap addr) -> addr
| _ -> failwith $"can't get len of {popped}"
@@ -891,16 +880,7 @@ module NullaryIlOp =
let referenced =
match addr with
- | EvalStackValue.ManagedPointer src ->
- match src with
- | ManagedPointerSource.Null -> failwith "TODO: throw NRE"
- | ManagedPointerSource.LocalVariable (sourceThread, methodFrame, whichVar) ->
- state.ThreadState.[sourceThread].MethodStates.[methodFrame].LocalVariables
- .[int whichVar]
- | ManagedPointerSource.Argument (sourceThread, methodFrame, whichVar) ->
- state.ThreadState.[sourceThread].MethodStates.[methodFrame].Arguments.[int whichVar]
- | ManagedPointerSource.Heap managedHeapAddress -> failwith "todo"
- | ManagedPointerSource.ArrayIndex _ -> failwith "todo"
+ | EvalStackValue.ManagedPointer src -> IlMachineState.dereferencePointer state src
| a -> failwith $"TODO: {a}"
let state =
@@ -928,6 +908,7 @@ module NullaryIlOp =
arr
(EvalStackValue.toCliTypeCoerced (CliType.ObjectRef None) value)
index
+ | ManagedPointerSource.Field _ -> failwith "TODO"
| addr -> failwith $"TODO: {addr}"
let state = state |> IlMachineState.advanceProgramCounter currentThread
diff --git a/WoofWare.PawPrint/UnaryConstIlOp.fs b/WoofWare.PawPrint/UnaryConstIlOp.fs
index b19b384..22b789d 100644
--- a/WoofWare.PawPrint/UnaryConstIlOp.fs
+++ b/WoofWare.PawPrint/UnaryConstIlOp.fs
@@ -129,8 +129,8 @@ module internal UnaryConstIlOp =
| EvalStackValue.NativeInt i -> not (NativeIntSource.isZero i)
| EvalStackValue.Float f -> failwith "TODO: Brtrue_s float semantics undocumented"
| EvalStackValue.ManagedPointer ManagedPointerSource.Null -> false
- | EvalStackValue.ManagedPointer _ -> true
- | EvalStackValue.ObjectRef _ -> failwith "TODO: Brtrue_s ObjectRef comparison unimplemented"
+ | EvalStackValue.ManagedPointer _
+ | EvalStackValue.ObjectRef _ -> true
| EvalStackValue.UserDefinedValueType _ ->
failwith "TODO: Brtrue_s UserDefinedValueType comparison unimplemented"
diff --git a/WoofWare.PawPrint/UnaryMetadataIlOp.fs b/WoofWare.PawPrint/UnaryMetadataIlOp.fs
index f0164e7..7a709bc 100644
--- a/WoofWare.PawPrint/UnaryMetadataIlOp.fs
+++ b/WoofWare.PawPrint/UnaryMetadataIlOp.fs
@@ -554,6 +554,7 @@ module internal UnaryMetadataIlOp =
)
let valueToStore, state = IlMachineState.popEvalStack thread state
+ let currentObj, state = IlMachineState.popEvalStack thread state
let state, declaringTypeHandle, typeGenerics =
IlMachineState.concretizeFieldForExecution loggerFactory baseClassTypes thread field state
@@ -570,8 +571,6 @@ module internal UnaryMetadataIlOp =
let valueToStore = EvalStackValue.toCliTypeCoerced zero valueToStore
- let currentObj, state = IlMachineState.popEvalStack thread state
-
if field.Attributes.HasFlag FieldAttributes.Static then
let state =
IlMachineState.setStatic declaringTypeHandle field.Name valueToStore state
@@ -589,10 +588,17 @@ module internal UnaryMetadataIlOp =
match source with
| ManagedPointerSource.Null -> failwith "TODO: raise NullReferenceException"
| ManagedPointerSource.LocalVariable (sourceThread, methodFrame, whichVar) ->
+ let var = IlMachineState.getLocalVariable sourceThread methodFrame whichVar state
+
+ failwith
+ $"TODO: compute value with its field set. %O{var} field %s{field.Name} to %O{valueToStore}"
+
state
|> IlMachineState.setLocalVariable sourceThread methodFrame whichVar valueToStore
| ManagedPointerSource.Argument (sourceThread, methodFrame, whichVar) -> failwith "todo"
| ManagedPointerSource.ArrayIndex (arr, index) ->
+ let var = IlMachineState.getArrayValue arr index state
+ failwith "TODO: compute value with its field set"
state |> IlMachineState.setArrayValue arr valueToStore index
| ManagedPointerSource.Heap addr ->
match state.ManagedHeap.NonArrayObjects.TryGetValue addr with
@@ -600,7 +606,11 @@ module internal UnaryMetadataIlOp =
| true, v ->
let v =
{ v with
- Fields = v.Fields |> Map.add field.Name valueToStore
+ Fields =
+ v.Fields
+ |> List.replaceWhere (fun (x, _) ->
+ if x = field.Name then Some (x, valueToStore) else None
+ )
}
let heap =
@@ -611,6 +621,7 @@ module internal UnaryMetadataIlOp =
{ state with
ManagedHeap = heap
}
+ | ManagedPointerSource.Field (managedPointerSource, fieldName) -> failwith "todo"
| EvalStackValue.ObjectRef managedHeapAddress -> failwith "todo"
| EvalStackValue.UserDefinedValueType _ -> failwith "todo"
@@ -757,20 +768,30 @@ module internal UnaryMetadataIlOp =
state.ThreadState.[sourceThread].MethodStates.[methodFrame].LocalVariables
.[int whichVar]
+ failwith $"TODO: need to get a field on {currentValue}"
+
IlMachineState.pushToEvalStack currentValue thread state
| ManagedPointerSource.Argument (sourceThread, methodFrame, whichVar) ->
let currentValue =
state.ThreadState.[sourceThread].MethodStates.[methodFrame].Arguments.[int whichVar]
+ failwith $"TODO: need to get a field on {currentValue}"
+
IlMachineState.pushToEvalStack currentValue thread state
| ManagedPointerSource.Heap managedHeapAddress ->
match state.ManagedHeap.NonArrayObjects.TryGetValue managedHeapAddress with
| false, _ -> failwith $"todo: array {managedHeapAddress}"
- | true, v -> IlMachineState.pushToEvalStack v.Fields.[field.Name] thread state
+ | true, v ->
+ IlMachineState.pushToEvalStack
+ (v.Fields |> List.find (fun (x, _) -> field.Name = x) |> snd)
+ thread
+ state
| ManagedPointerSource.ArrayIndex (arr, index) ->
let currentValue = state |> IlMachineState.getArrayValue arr index
+ failwith $"TODO: need to get a field on {currentValue}"
IlMachineState.pushToEvalStack currentValue thread state
| ManagedPointerSource.Null -> failwith "TODO: raise NullReferenceException"
+ | ManagedPointerSource.Field _ -> failwith "TODO: get a field on a field ptr"
| EvalStackValue.ObjectRef managedHeapAddress -> failwith $"todo: {managedHeapAddress}"
| EvalStackValue.UserDefinedValueType fields ->
let result =
@@ -782,7 +803,64 @@ module internal UnaryMetadataIlOp =
|> IlMachineState.advanceProgramCounter thread
|> Tuple.withRight WhatWeDid.Executed
- | Ldflda -> failwith "TODO: Ldflda unimplemented"
+ | Ldflda ->
+ let state, field =
+ match metadataToken with
+ | MetadataToken.FieldDefinition f ->
+ let field = activeAssy.Fields.[f]
+ // Map the field to have TypeDefn type parameters
+ let mappedField =
+ field |> FieldInfo.mapTypeGenerics (fun i _ -> TypeDefn.GenericTypeParameter i)
+
+ state, mappedField
+ | MetadataToken.MemberReference m ->
+ let state, _, resolved, _ =
+ IlMachineState.resolveMember loggerFactory baseClassTypes thread activeAssy m state
+
+ match resolved with
+ | Choice2Of2 field -> state, field
+ | Choice1Of2 _ -> failwith "Expected field in Ldflda but got method"
+ | _ -> failwith $"Unexpected in Ldflda: {metadataToken}"
+
+ let source, state = IlMachineState.popEvalStack thread state
+
+ // Ldflda needs to return a pointer to the field within the object/struct
+ let toPush =
+ match source with
+ | EvalStackValue.ObjectRef heapAddr ->
+ // For object references, we need to create a pointer to the field
+ // TODO: The current ManagedPointerSource doesn't have a case for field pointers
+ // We're using Heap pointer for now, but this doesn't capture the field offset
+ // This will need to be enhanced to support field-specific pointers
+ ManagedPointerSource.Heap heapAddr |> EvalStackValue.ManagedPointer
+ | EvalStackValue.ManagedPointer ptr ->
+ // If we already have a managed pointer, we need to handle field access
+ // through that pointer. For now, return the same pointer type
+ // TODO: This needs to track the field offset within the pointed-to object
+ match ptr with
+ | ManagedPointerSource.Null -> failwith "TODO: NullReferenceException in Ldflda"
+ | _ -> ptr |> EvalStackValue.ManagedPointer
+ | EvalStackValue.NativeInt (NativeIntSource.ManagedPointer ptr) ->
+ // Unmanaged pointer input produces unmanaged pointer output
+ // TODO: This also needs field offset tracking
+ match ptr with
+ | ManagedPointerSource.Null -> failwith "TODO: NullReferenceException in Ldflda"
+ | _ -> ptr |> NativeIntSource.ManagedPointer |> EvalStackValue.NativeInt
+ | EvalStackValue.NativeInt (NativeIntSource.Verbatim _) ->
+ // Native int that's not from a managed pointer
+ // This represents an unmanaged pointer scenario
+ failwith "TODO: Ldflda with unmanaged pointer - not allowed in verifiable code"
+ | EvalStackValue.UserDefinedValueType vt ->
+ // For value types on the stack, we need to store them somewhere
+ // and return a pointer to the field
+ // This is complex because we need to materialize the value type
+ failwith "TODO: Ldflda on value type - need to allocate temporary storage and create field pointer"
+ | _ -> failwith $"unexpected Ldflda source: {source}"
+
+ state
+ |> IlMachineState.pushToEvalStack' toPush thread
+ |> IlMachineState.advanceProgramCounter thread
+ |> Tuple.withRight WhatWeDid.Executed
| Ldsfld ->
let state, field =
match metadataToken with
@@ -978,7 +1056,44 @@ module internal UnaryMetadataIlOp =
IlMachineState.pushToEvalStack toPush thread state
|> IlMachineState.advanceProgramCounter thread
|> Tuple.withRight WhatWeDid.Executed
- | Initobj -> failwith "TODO: Initobj unimplemented"
+ | Initobj ->
+ let addr, state = IlMachineState.popEvalStack thread state
+ let declaringTypeGenerics = currentMethod.DeclaringType.Generics
+
+ let state, assy, ty =
+ match metadataToken with
+ | MetadataToken.TypeSpecification spec ->
+ let state, assy, ty =
+ IlMachineState.resolveTypeFromSpecConcrete
+ loggerFactory
+ baseClassTypes
+ spec
+ activeAssy
+ declaringTypeGenerics
+ currentMethod.Generics
+ state
+
+ state, assy, ty
+ | x -> failwith $"unexpected in Initobj: %O{x}"
+
+ let state =
+ match addr with
+ | EvalStackValue.ManagedPointer src ->
+ match src with
+ | ManagedPointerSource.Null -> failwith "TODO: probably NRE here"
+ | ManagedPointerSource.Heap _ -> failwith "TODO: heap"
+ | ManagedPointerSource.LocalVariable (thread, frame, var) ->
+ let oldValue = state |> IlMachineState.getLocalVariable thread frame var
+ let newValue = failwith "TODO"
+ state |> IlMachineState.setLocalVariable thread frame var newValue
+ | ManagedPointerSource.Argument (thread, frame, arg) -> failwith "TODO: Argument"
+ | ManagedPointerSource.ArrayIndex (arr, index) -> failwith "todo: array index"
+ | Field (managedPointerSource, fieldName) -> failwith "todo"
+ | addr -> failwith $"Bad address in Initobj: {addr}"
+
+ state
+ |> IlMachineState.advanceProgramCounter thread
+ |> Tuple.withRight WhatWeDid.Executed
| Ldsflda ->
// TODO: check whether we should throw FieldAccessException
@@ -1073,6 +1188,35 @@ module internal UnaryMetadataIlOp =
| Stobj -> failwith "TODO: Stobj unimplemented"
| Constrained -> failwith "TODO: Constrained unimplemented"
| Ldtoken ->
+ // Helper function to handle type tokens and create RuntimeTypeHandle
+ let handleTypeToken (typeDefn : TypeDefn) (state : IlMachineState) : IlMachineState =
+ let ty = baseClassTypes.RuntimeTypeHandle
+ let field = ty.Fields |> List.exactlyOne
+
+ if field.Name <> "m_type" then
+ failwith $"unexpected field name ${field.Name} for BCL type RuntimeTypeHandle"
+
+ let methodGenerics = currentMethod.Generics
+ let typeGenerics = currentMethod.DeclaringType.Generics
+
+ let state, handle =
+ IlMachineState.concretizeType
+ baseClassTypes
+ state
+ activeAssy.Name
+ typeGenerics
+ methodGenerics
+ typeDefn
+
+ let alloc, state = IlMachineState.getOrAllocateType baseClassTypes handle state
+
+ let vt =
+ {
+ Fields = [ "m_type", CliType.ObjectRef (Some alloc) ]
+ }
+
+ IlMachineState.pushToEvalStack (CliType.ValueType vt) thread state
+
let state =
match metadataToken with
| MetadataToken.FieldDefinition h ->
@@ -1086,35 +1230,12 @@ module internal UnaryMetadataIlOp =
let field = ty.Fields |> List.exactlyOne
failwith ""
| MetadataToken.TypeDefinition h ->
- let ty = baseClassTypes.RuntimeTypeHandle
- let field = ty.Fields |> List.exactlyOne
-
- if field.Name <> "m_type" then
- failwith $"unexpected field name ${field.Name} for BCL type RuntimeTypeHandle"
-
- let methodGenerics = currentMethod.Generics
-
- let typeGenerics = currentMethod.DeclaringType.Generics
-
let state, typeDefn = lookupTypeDefn baseClassTypes state activeAssy h
-
- let state, handle =
- IlMachineState.concretizeType
- baseClassTypes
- state
- activeAssy.Name
- typeGenerics
- methodGenerics
- typeDefn
-
- let alloc, state = IlMachineState.getOrAllocateType baseClassTypes handle state
-
- let vt =
- {
- Fields = [ "m_type", CliType.ObjectRef (Some alloc) ]
- }
-
- IlMachineState.pushToEvalStack (CliType.ValueType vt) thread state
+ handleTypeToken typeDefn state
+ | MetadataToken.TypeSpecification h ->
+ // Get the TypeSpec from the assembly and use its signature
+ let typeSpec = activeAssy.TypeSpecs.[h]
+ handleTypeToken typeSpec.Signature state
| _ -> failwith $"Unexpected metadata token %O{metadataToken} in LdToken"
state
diff --git a/WoofWare.PawPrint/WoofWare.PawPrint.fsproj b/WoofWare.PawPrint/WoofWare.PawPrint.fsproj
index b86ab97..299d17d 100644
--- a/WoofWare.PawPrint/WoofWare.PawPrint.fsproj
+++ b/WoofWare.PawPrint/WoofWare.PawPrint.fsproj
@@ -7,6 +7,7 @@
+