diff --git a/CSharpExample/CSharpExample.csproj b/CSharpExample/CSharpExample.csproj
index 47f6818..6b0a667 100644
--- a/CSharpExample/CSharpExample.csproj
+++ b/CSharpExample/CSharpExample.csproj
@@ -4,6 +4,8 @@
net9.0
Exe
true
+ false
+ false
diff --git a/WoofWare.PawPrint.Domain/IlOp.fs b/WoofWare.PawPrint.Domain/IlOp.fs
index ff2ef94..4a371c9 100644
--- a/WoofWare.PawPrint.Domain/IlOp.fs
+++ b/WoofWare.PawPrint.Domain/IlOp.fs
@@ -123,6 +123,7 @@ type NullaryIlOp =
| Localloc
/// Dereferences the pointer on top of the stack, and pushes the target to the stack as a type O (object reference).
| Ldind_ref
+ /// Stores an object reference value at a supplied address.
| Stind_ref
| Stind_I
| Stind_I1
@@ -166,18 +167,25 @@ type NullaryIlOp =
| Ldelem_u8
| Ldelem_r4
| Ldelem_r8
+ /// Loads the element containing an object reference at a specified array index onto the top of the evaluation stack as type O (object reference).
| Ldelem_ref
+ /// Replaces the array element at a given index with the nativeint value on the evaluation stack.
| Stelem_i
+ /// Replaces the array element at a given index with the int8 value on the evaluation stack.
| Stelem_i1
| Stelem_u1
+ /// Replaces the array element at a given index with the int16 value on the evaluation stack.
| Stelem_i2
| Stelem_u2
+ /// Replaces the array element at a given index with the int32 value on the evaluation stack.
| Stelem_i4
| Stelem_u4
+ /// Replaces the array element at a given index with the int64 value on the evaluation stack.
| Stelem_i8
| Stelem_u8
| Stelem_r4
| Stelem_r8
+ /// Replaces the array element at a given index with the object ref value (type O) on the evaluation stack.
| Stelem_ref
| Cpblk
| Initblk
@@ -384,6 +392,7 @@ type UnaryConstIlOp =
| Bge_un_s of int8
| Bgt_un_s of int8
| Ble_un_s of int8
+ /// Transfers control to a target instruction if the first value is less than the second value.
| Blt_un_s of int8
| Bne_un of int32
| Bge_un of int32
@@ -529,6 +538,7 @@ type UnaryMetadataTokenIlOp =
| Newobj
| Newarr
| Box
+ /// Loads the address of the array element at a specified array index onto the top of the evaluation stack as type "managed pointer"
| Ldelema
| Isinst
/// Pop value from stack; pop object ref from stack; set specified field on that object to that value.
diff --git a/WoofWare.PawPrint.Test/TestPureCases.fs b/WoofWare.PawPrint.Test/TestPureCases.fs
index 827b7e4..0408375 100644
--- a/WoofWare.PawPrint.Test/TestPureCases.fs
+++ b/WoofWare.PawPrint.Test/TestPureCases.fs
@@ -311,6 +311,12 @@ module TestPureCases =
|> List.map (fun i -> CliType.Numeric (CliNumericType.Int32 i))
|> Some
}
+ {
+ FileName = "Ldelema.cs"
+ ExpectedReturnCode = 0
+ NativeImpls = MockEnv.make ()
+ LocalVariablesOfMain = None
+ }
]
[]
diff --git a/WoofWare.PawPrint.Test/WoofWare.PawPrint.Test.fsproj b/WoofWare.PawPrint.Test/WoofWare.PawPrint.Test.fsproj
index a5d2826..3ca83b3 100644
--- a/WoofWare.PawPrint.Test/WoofWare.PawPrint.Test.fsproj
+++ b/WoofWare.PawPrint.Test/WoofWare.PawPrint.Test.fsproj
@@ -21,6 +21,7 @@
+
diff --git a/WoofWare.PawPrint.Test/sourcesPure/Ldelema.cs b/WoofWare.PawPrint.Test/sourcesPure/Ldelema.cs
new file mode 100644
index 0000000..735faa7
--- /dev/null
+++ b/WoofWare.PawPrint.Test/sourcesPure/Ldelema.cs
@@ -0,0 +1,66 @@
+using System;
+
+///
+/// A simple value type used for testing ldelema.
+///
+public struct TestStruct
+{
+ public int Value;
+}
+
+public class Program
+{
+ ///
+ /// Modifies a TestStruct instance by reference. Calling this with an array element
+ /// (e.g., `ModifyStruct(ref array[i], ...)` ) will cause the C# compiler to
+ /// generate an `ldelema` instruction.
+ ///
+ /// A reference to the TestStruct to modify.
+ /// The new value to assign.
+ public static void ModifyStruct(ref TestStruct s, int newValue)
+ {
+ s.Value = newValue;
+ }
+
+ ///
+ /// Modifies a string reference.
+ ///
+ /// A reference to a string variable.
+ /// The new string to assign.
+ public static void ModifyStringRef(ref string s, string newValue)
+ {
+ s = newValue;
+ }
+
+ ///
+ /// Main entry point for the ldelema test.
+ ///
+ /// 0 if all tests pass, otherwise a non-zero error code.
+ public static int Main(string[] args)
+ {
+ // --- Test 1: Modifying a value type element in an array ---
+ TestStruct[] structArray = new TestStruct[5];
+ structArray[2].Value = 100;
+
+ // This call should generate an `ldelema` instruction to get the address of structArray[2].
+ ModifyStruct(ref structArray[2], 999);
+
+ if (structArray[2].Value != 999)
+ {
+ return 301; // Unique error code for this test
+ }
+
+ // --- Test 2: Modifying a reference type element in an array ---
+ string[] stringArray = new string[] { "alpha", "beta", "gamma" };
+
+ // This call should also generate an `ldelema` instruction.
+ ModifyStringRef(ref stringArray[1], "zeta");
+
+ if (stringArray[1] != "zeta")
+ {
+ return 302; // Unique error code for this test
+ }
+
+ return 0; // Success
+ }
+}
diff --git a/WoofWare.PawPrint/BasicCliType.fs b/WoofWare.PawPrint/BasicCliType.fs
index ec6f6f2..98e622f 100644
--- a/WoofWare.PawPrint/BasicCliType.fs
+++ b/WoofWare.PawPrint/BasicCliType.fs
@@ -41,6 +41,7 @@ type ManagedPointerSource =
| LocalVariable of sourceThread : ThreadId * methodFrame : int * whichVar : uint16
| Argument of sourceThread : ThreadId * methodFrame : int * whichVar : uint16
| Heap of ManagedHeapAddress
+ | ArrayIndex of arr : ManagedHeapAddress * index : int
| Null
override this.ToString () =
@@ -51,6 +52,7 @@ type ManagedPointerSource =
$""
| ManagedPointerSource.Argument (source, method, var) ->
$""
+ | ManagedPointerSource.ArrayIndex (arr, index) -> $""
[]
type UnsignedNativeIntSource =
@@ -129,6 +131,7 @@ type CliRuntimePointerSource =
| LocalVariable of sourceThread : ThreadId * methodFrame : int * whichVar : uint16
| Argument of sourceThread : ThreadId * methodFrame : int * whichVar : uint16
| Heap of ManagedHeapAddress
+ | ArrayIndex of arr : ManagedHeapAddress * index : int
| Null
type CliRuntimePointer =
diff --git a/WoofWare.PawPrint/BinaryArithmetic.fs b/WoofWare.PawPrint/BinaryArithmetic.fs
new file mode 100644
index 0000000..0d0e390
--- /dev/null
+++ b/WoofWare.PawPrint/BinaryArithmetic.fs
@@ -0,0 +1,50 @@
+namespace WoofWare.PawPrint
+
+#nowarn "42"
+
+type IArithmeticOperation =
+ abstract Int32Int32 : int32 -> int32 -> int32
+ abstract Int64Int64 : int64 -> int64 -> int64
+ abstract FloatFloat : float -> float -> float
+ abstract Name : string
+
+[]
+module ArithmeticOperation =
+ let add =
+ { new IArithmeticOperation with
+ member _.Int32Int32 a b = (# "add" a b : int32 #)
+ member _.Int64Int64 a b = (# "add" a b : int64 #)
+ member _.FloatFloat a b = (# "add" a b : float #)
+ member _.Name = "add"
+ }
+
+ let mul =
+ { new IArithmeticOperation with
+ member _.Int32Int32 a b = (# "mul" a b : int32 #)
+ member _.Int64Int64 a b = (# "mul" a b : int64 #)
+ member _.FloatFloat a b = (# "mul" a b : float #)
+ member _.Name = "mul"
+ }
+
+[]
+module BinaryArithmetic =
+ let execute (op : IArithmeticOperation) (val1 : EvalStackValue) (val2 : EvalStackValue) : EvalStackValue =
+ // see table at https://learn.microsoft.com/en-us/dotnet/api/system.reflection.emit.opcodes.add?view=net-9.0
+ match val1, val2 with
+ | EvalStackValue.Int32 val1, EvalStackValue.Int32 val2 -> op.Int32Int32 val1 val2 |> EvalStackValue.Int32
+ | EvalStackValue.Int32 val1, EvalStackValue.NativeInt val2 -> failwith "" |> EvalStackValue.NativeInt
+ | EvalStackValue.Int32 val1, EvalStackValue.ManagedPointer val2 -> failwith "" |> EvalStackValue.ManagedPointer
+ | EvalStackValue.Int32 val1, EvalStackValue.ObjectRef val2 -> failwith "" |> EvalStackValue.ObjectRef
+ | EvalStackValue.Int64 val1, EvalStackValue.Int64 val2 -> op.Int64Int64 val1 val2 |> EvalStackValue.Int64
+ | EvalStackValue.NativeInt val1, EvalStackValue.Int32 val2 -> failwith "" |> EvalStackValue.NativeInt
+ | EvalStackValue.NativeInt val1, EvalStackValue.NativeInt val2 -> failwith "" |> EvalStackValue.NativeInt
+ | EvalStackValue.NativeInt val1, EvalStackValue.ManagedPointer val2 ->
+ failwith "" |> EvalStackValue.ManagedPointer
+ | EvalStackValue.NativeInt val1, EvalStackValue.ObjectRef val2 -> failwith "" |> EvalStackValue.ObjectRef
+ | EvalStackValue.Float val1, EvalStackValue.Float val2 -> op.FloatFloat val1 val2 |> EvalStackValue.Float
+ | EvalStackValue.ManagedPointer val1, EvalStackValue.NativeInt val2 ->
+ failwith "" |> EvalStackValue.ManagedPointer
+ | EvalStackValue.ObjectRef val1, EvalStackValue.NativeInt val2 -> failwith "" |> EvalStackValue.ObjectRef
+ | EvalStackValue.ManagedPointer val1, EvalStackValue.Int32 val2 -> failwith "" |> EvalStackValue.ManagedPointer
+ | EvalStackValue.ObjectRef val1, EvalStackValue.Int32 val2 -> failwith "" |> EvalStackValue.ObjectRef
+ | val1, val2 -> failwith $"invalid %s{op.Name} operation: {val1} and {val2}"
diff --git a/WoofWare.PawPrint/EvalStack.fs b/WoofWare.PawPrint/EvalStack.fs
index c964435..0c66eef 100644
--- a/WoofWare.PawPrint/EvalStack.fs
+++ b/WoofWare.PawPrint/EvalStack.fs
@@ -162,6 +162,8 @@ module EvalStackValue =
|> 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)))
| EvalStackValue.NativeInt nativeIntSource ->
match nativeIntSource with
| NativeIntSource.Verbatim 0L -> CliType.ObjectRef None
@@ -200,6 +202,7 @@ module EvalStackValue =
CliRuntimePointerSource.Argument (sourceThread, methodFrame, var)
|> CliRuntimePointer.Managed
|> CliType.RuntimePointer
+ | ManagedPointerSource.ArrayIndex _ -> failwith "TODO"
| EvalStackValue.NativeInt intSrc ->
match intSrc with
| NativeIntSource.Verbatim i -> CliType.RuntimePointer (CliRuntimePointer.Unmanaged i)
@@ -215,6 +218,7 @@ module EvalStackValue =
)
| ManagedPointerSource.Argument (a, b, c) ->
CliType.RuntimePointer (CliRuntimePointer.Managed (CliRuntimePointerSource.Argument (a, b, c)))
+ | ManagedPointerSource.ArrayIndex _ -> failwith "TODO"
| NativeIntSource.FunctionPointer methodInfo ->
CliType.Numeric (CliNumericType.NativeInt (NativeIntSource.FunctionPointer methodInfo))
| NativeIntSource.TypeHandlePtr int64 -> failwith "todo"
@@ -269,6 +273,8 @@ module EvalStackValue =
| CliRuntimePointerSource.LocalVariable (sourceThread, methodFrame, var) ->
ManagedPointerSource.LocalVariable (sourceThread, methodFrame, var)
|> EvalStackValue.ManagedPointer
+ | CliRuntimePointerSource.ArrayIndex (arr, ind) ->
+ ManagedPointerSource.ArrayIndex (arr, ind) |> EvalStackValue.ManagedPointer
| CliRuntimePointerSource.Argument (sourceThread, methodFrame, var) ->
ManagedPointerSource.Argument (sourceThread, methodFrame, var)
|> EvalStackValue.ManagedPointer
diff --git a/WoofWare.PawPrint/EvalStackValueComparisons.fs b/WoofWare.PawPrint/EvalStackValueComparisons.fs
new file mode 100644
index 0000000..188dfdc
--- /dev/null
+++ b/WoofWare.PawPrint/EvalStackValueComparisons.fs
@@ -0,0 +1,71 @@
+namespace WoofWare.PawPrint
+
+[]
+module EvalStackValueComparisons =
+
+ let clt (var1 : EvalStackValue) (var2 : EvalStackValue) : bool =
+ match var1, var2 with
+ | EvalStackValue.Int64 var1, EvalStackValue.Int64 var2 -> var1 < var2
+ | EvalStackValue.Float var1, EvalStackValue.Float var2 -> failwith "TODO: Clt float comparison unimplemented"
+ | EvalStackValue.ObjectRef var1, EvalStackValue.ObjectRef var2 ->
+ failwith $"Clt instruction invalid for comparing object refs, {var1} vs {var2}"
+ | EvalStackValue.ObjectRef var1, other -> failwith $"invalid comparison, ref %O{var1} vs %O{other}"
+ | other, EvalStackValue.ObjectRef var2 -> failwith $"invalid comparison, %O{other} vs ref %O{var2}"
+ | EvalStackValue.Float i, other -> failwith $"invalid comparison, float %f{i} vs %O{other}"
+ | other, EvalStackValue.Float i -> failwith $"invalid comparison, %O{other} vs float %f{i}"
+ | EvalStackValue.Int64 i, other -> failwith $"invalid comparison, int64 %i{i} vs %O{other}"
+ | other, EvalStackValue.Int64 i -> failwith $"invalid comparison, %O{other} vs int64 %i{i}"
+ | EvalStackValue.Int32 var1, EvalStackValue.Int32 var2 -> var1 < var2
+ | EvalStackValue.Int32 var1, EvalStackValue.NativeInt var2 ->
+ failwith "TODO: Clt Int32 vs NativeInt comparison unimplemented"
+ | EvalStackValue.Int32 i, other -> failwith $"invalid comparison, int32 %i{i} vs %O{other}"
+ | EvalStackValue.NativeInt var1, EvalStackValue.Int32 var2 ->
+ failwith "TODO: Clt NativeInt vs Int32 comparison unimplemented"
+ | other, EvalStackValue.Int32 var2 -> failwith $"invalid comparison, {other} vs int32 {var2}"
+ | EvalStackValue.NativeInt var1, EvalStackValue.NativeInt var2 -> NativeIntSource.isLess var1 var2
+ | EvalStackValue.NativeInt var1, other -> failwith $"invalid comparison, nativeint {var1} vs %O{other}"
+ | EvalStackValue.ManagedPointer managedPointerSource, NativeInt int64 ->
+ failwith "TODO: Clt ManagedPointer vs NativeInt comparison unimplemented"
+ | EvalStackValue.ManagedPointer managedPointerSource, ManagedPointer pointerSource ->
+ failwith "TODO: Clt ManagedPointer vs ManagedPointer comparison unimplemented"
+ | EvalStackValue.ManagedPointer managedPointerSource, UserDefinedValueType _ ->
+ failwith "TODO: Clt ManagedPointer vs UserDefinedValueType comparison unimplemented"
+ | EvalStackValue.UserDefinedValueType _, NativeInt int64 ->
+ failwith "TODO: Clt UserDefinedValueType vs NativeInt comparison unimplemented"
+ | EvalStackValue.UserDefinedValueType _, ManagedPointer managedPointerSource ->
+ failwith "TODO: Clt UserDefinedValueType vs ManagedPointer comparison unimplemented"
+ | EvalStackValue.UserDefinedValueType _, UserDefinedValueType _ ->
+ failwith "TODO: Clt UserDefinedValueType vs UserDefinedValueType comparison unimplemented"
+
+ let ceq var1 var2 : bool =
+ // Table III.4
+ match var1, var2 with
+ | EvalStackValue.Int32 var1, EvalStackValue.Int32 var2 -> var1 = var2
+ | EvalStackValue.Int32 var1, EvalStackValue.NativeInt var2 -> failwith "TODO: int32 CEQ nativeint"
+ | EvalStackValue.Int32 _, _ -> failwith $"bad ceq: Int32 vs {var2}"
+ | EvalStackValue.Int64 var1, EvalStackValue.Int64 var2 -> var1 = var2
+ | EvalStackValue.Int64 _, _ -> failwith $"bad ceq: Int64 vs {var2}"
+ | 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 ->
+ match var1, var2 with
+ | NativeIntSource.FunctionPointer f1, NativeIntSource.FunctionPointer f2 ->
+ if f1 = f2 then
+ true
+ else
+ failwith $"TODO(CEQ): nativeint vs nativeint, {f1} vs {f2}"
+ | NativeIntSource.TypeHandlePtr f1, NativeIntSource.TypeHandlePtr f2 -> f1 = f2
+ | NativeIntSource.Verbatim f1, NativeIntSource.Verbatim f2 -> f1 = f2
+ | NativeIntSource.ManagedPointer f1, NativeIntSource.ManagedPointer f2 -> f1 = f2
+ | _, _ -> 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 -> var1 = var2
+ | EvalStackValue.ObjectRef _, _ -> failwith $"bad ceq: ObjectRef vs {var2}"
+ | EvalStackValue.ManagedPointer var1, EvalStackValue.ManagedPointer var2 -> var1 = var2
+ | EvalStackValue.ManagedPointer var1, EvalStackValue.NativeInt var2 ->
+ failwith $"TODO (CEQ): managed pointer vs nativeint"
+ | EvalStackValue.ManagedPointer _, _ -> failwith $"bad ceq: ManagedPointer vs {var2}"
+ | EvalStackValue.UserDefinedValueType _, _ -> failwith $"bad ceq: UserDefinedValueType vs {var2}"
diff --git a/WoofWare.PawPrint/ExternImplementations/System.Threading.Monitor.fs b/WoofWare.PawPrint/ExternImplementations/System.Threading.Monitor.fs
index 497ad47..de2acd2 100644
--- a/WoofWare.PawPrint/ExternImplementations/System.Threading.Monitor.fs
+++ b/WoofWare.PawPrint/ExternImplementations/System.Threading.Monitor.fs
@@ -86,6 +86,7 @@ module System_Threading_Monitor =
| ManagedPointerSource.Argument (sourceThread, methodFrame, whichVar) ->
failwith "not really expecting to *edit* an argument..."
| ManagedPointerSource.Heap addr -> failwith "todo: managed heap"
+ | ManagedPointerSource.ArrayIndex _ -> failwith "todo: array index"
(state, WhatWeDid.Executed) |> ExecutionResult.Stepped
diff --git a/WoofWare.PawPrint/IlMachineState.fs b/WoofWare.PawPrint/IlMachineState.fs
index aa10bca..6cedfe0 100644
--- a/WoofWare.PawPrint/IlMachineState.fs
+++ b/WoofWare.PawPrint/IlMachineState.fs
@@ -587,6 +587,7 @@ module IlMachineState =
| ManagedPointerSource.Heap managedHeapAddress ->
CliRuntimePointer.Managed (CliRuntimePointerSource.Heap managedHeapAddress)
| ManagedPointerSource.Null -> failwith "todo"
+ | ManagedPointerSource.ArrayIndex _ -> failwith "TODO"
| x -> failwith $"TODO: Unsafe.AsPointer(%O{x})"
pushToEvalStack (CliType.RuntimePointer toPush) currentThread state
@@ -616,6 +617,34 @@ module IlMachineState =
|> pushToEvalStack' result currentThread
|> advanceProgramCounter currentThread
|> Some
+ | "System.Private.CoreLib", "String", "Equals" ->
+ let arg1, state = popEvalStack currentThread state
+
+ let arg1 =
+ match arg1 with
+ | EvalStackValue.ManagedPointer (ManagedPointerSource.Heap h) -> h
+ | EvalStackValue.Int32 _
+ | EvalStackValue.Int64 _
+ | EvalStackValue.Float _ -> failwith $"this isn't a string! {arg1}"
+ | _ -> failwith $"TODO: %O{arg1}"
+
+ let arg2, state = popEvalStack currentThread state
+
+ let arg2 =
+ match arg2 with
+ | EvalStackValue.ManagedPointer (ManagedPointerSource.Heap h) -> h
+ | EvalStackValue.Int32 _
+ | EvalStackValue.Int64 _
+ | EvalStackValue.Float _ -> failwith $"this isn't a string! {arg2}"
+ | _ -> failwith $"TODO: %O{arg2}"
+
+ if arg1 = arg2 then
+ state
+ |> pushToEvalStack (CliType.OfBool true) currentThread
+ |> advanceProgramCounter currentThread
+ |> Some
+ else
+ failwith "TODO"
| a, b, c -> failwith $"TODO: implement JIT intrinsic {a}.{b}.{c}"
|> Option.map (fun s -> s.WithThreadSwitchedToAssembly callerAssy currentThread |> fst)
@@ -1150,6 +1179,9 @@ module IlMachineState =
ManagedHeap = heap
}
+ let getArrayValue (arrayAllocation : ManagedHeapAddress) (index : int) (state : IlMachineState) : CliType =
+ ManagedHeap.GetArrayValue arrayAllocation index state.ManagedHeap
+
let jumpProgramCounter (thread : ThreadId) (bytes : int) (state : IlMachineState) : IlMachineState =
{ state with
ThreadState =
diff --git a/WoofWare.PawPrint/ManagedHeap.fs b/WoofWare.PawPrint/ManagedHeap.fs
index 7765458..0de1a88 100644
--- a/WoofWare.PawPrint/ManagedHeap.fs
+++ b/WoofWare.PawPrint/ManagedHeap.fs
@@ -109,6 +109,16 @@ type ManagedHeap =
ManagedHeapAddress addr, heap
+ static member GetArrayValue (alloc : ManagedHeapAddress) (offset : int) (heap : ManagedHeap) : CliType =
+ match heap.Arrays.TryGetValue alloc with
+ | false, _ -> failwith "TODO: array not on heap"
+ | true, arr ->
+
+ if offset < 0 || offset >= arr.Length then
+ failwith "TODO: raise IndexOutOfBoundsException"
+
+ arr.Elements.[offset]
+
static member SetArrayValue
(alloc : ManagedHeapAddress)
(offset : int)
@@ -124,6 +134,9 @@ type ManagedHeap =
match arr with
| None -> failwith "tried to change element of nonexistent array"
| Some arr ->
+ if offset < 0 || offset >= arr.Elements.Length then
+ failwith "TODO: throw somehow"
+
{ arr with
Elements = arr.Elements.SetItem (offset, v)
}
diff --git a/WoofWare.PawPrint/NullaryIlOp.fs b/WoofWare.PawPrint/NullaryIlOp.fs
index c6c01bc..fcd304e 100644
--- a/WoofWare.PawPrint/NullaryIlOp.fs
+++ b/WoofWare.PawPrint/NullaryIlOp.fs
@@ -1,33 +1,7 @@
namespace WoofWare.PawPrint
-#nowarn "42"
-
open Microsoft.Extensions.Logging
-type private IArithmeticOperation =
- abstract Int32Int32 : int32 -> int32 -> int32
- abstract Int64Int64 : int64 -> int64 -> int64
- abstract FloatFloat : float -> float -> float
- abstract Name : string
-
-[]
-module private ArithmeticOperation =
- let add =
- { new IArithmeticOperation with
- member _.Int32Int32 a b = (# "add" a b : int32 #)
- member _.Int64Int64 a b = (# "add" a b : int64 #)
- member _.FloatFloat a b = (# "add" a b : float #)
- member _.Name = "add"
- }
-
- let mul =
- { new IArithmeticOperation with
- member _.Int32Int32 a b = (# "mul" a b : int32 #)
- member _.Int64Int64 a b = (# "mul" a b : int64 #)
- member _.FloatFloat a b = (# "mul" a b : float #)
- member _.Name = "mul"
- }
-
[]
[]
module NullaryIlOp =
@@ -72,6 +46,7 @@ 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"
// Unified Ldind implementation
let private executeLdind
@@ -103,41 +78,6 @@ module NullaryIlOp =
(state, WhatWeDid.Executed) |> ExecutionResult.Stepped
- let private binaryArithmeticOperation
- (op : IArithmeticOperation)
- (currentThread : ThreadId)
- (state : IlMachineState)
- =
- let val1, state = IlMachineState.popEvalStack currentThread state
- let val2, state = IlMachineState.popEvalStack currentThread state
- // see table at https://learn.microsoft.com/en-us/dotnet/api/system.reflection.emit.opcodes.add?view=net-9.0
- let result =
- match val1, val2 with
- | EvalStackValue.Int32 val1, EvalStackValue.Int32 val2 ->
- (# "add" val1 val2 : int32 #) |> EvalStackValue.Int32
- | EvalStackValue.Int32 val1, EvalStackValue.NativeInt val2 -> failwith "" |> EvalStackValue.NativeInt
- | EvalStackValue.Int32 val1, EvalStackValue.ManagedPointer val2 ->
- failwith "" |> EvalStackValue.ManagedPointer
- | EvalStackValue.Int32 val1, EvalStackValue.ObjectRef val2 -> failwith "" |> EvalStackValue.ObjectRef
- | EvalStackValue.Int64 val1, EvalStackValue.Int64 val2 ->
- (# "add" val1 val2 : int64 #) |> EvalStackValue.Int64
- | EvalStackValue.NativeInt val1, EvalStackValue.Int32 val2 -> failwith "" |> EvalStackValue.NativeInt
- | EvalStackValue.NativeInt val1, EvalStackValue.NativeInt val2 -> failwith "" |> EvalStackValue.NativeInt
- | EvalStackValue.NativeInt val1, EvalStackValue.ManagedPointer val2 ->
- failwith "" |> EvalStackValue.ManagedPointer
- | EvalStackValue.NativeInt val1, EvalStackValue.ObjectRef val2 -> failwith "" |> EvalStackValue.ObjectRef
- | EvalStackValue.Float val1, EvalStackValue.Float val2 ->
- (# "add" val1 val2 : float #) |> EvalStackValue.Float
- | EvalStackValue.ManagedPointer val1, EvalStackValue.NativeInt val2 ->
- failwith "" |> EvalStackValue.ManagedPointer
- | EvalStackValue.ObjectRef val1, EvalStackValue.NativeInt val2 -> failwith "" |> EvalStackValue.ObjectRef
- | EvalStackValue.ManagedPointer val1, EvalStackValue.Int32 val2 ->
- failwith "" |> EvalStackValue.ManagedPointer
- | EvalStackValue.ObjectRef val1, EvalStackValue.Int32 val2 -> failwith "" |> EvalStackValue.ObjectRef
- | val1, val2 -> failwith $"invalid %s{op.Name} operation: {val1} and {val2}"
-
- result, state
-
let private stind (varType : CliType) (currentThread : ThreadId) (state : IlMachineState) : IlMachineState =
// TODO: throw NullReferenceException if unaligned target
let valueToStore, state = IlMachineState.popEvalStack currentThread state
@@ -182,8 +122,84 @@ module NullaryIlOp =
)
}
| ManagedPointerSource.Heap managedHeapAddress -> failwith "todo"
+ | ManagedPointerSource.ArrayIndex _ -> failwith "todo"
| EvalStackValue.ObjectRef managedHeapAddress -> failwith "todo"
+ let internal ldElem
+ (targetCliTypeZero : CliType)
+ (index : EvalStackValue)
+ (arr : EvalStackValue)
+ (currentThread : ThreadId)
+ (state : IlMachineState)
+ : ExecutionResult
+ =
+ let index =
+ match index with
+ | EvalStackValue.NativeInt src ->
+ match src with
+ | NativeIntSource.FunctionPointer _
+ | NativeIntSource.TypeHandlePtr _
+ | NativeIntSource.ManagedPointer _ -> failwith "Refusing to treat a pointer as an array index"
+ | NativeIntSource.Verbatim i -> i |> int32
+ | EvalStackValue.Int32 i -> i
+ | _ -> failwith $"Invalid index: {index}"
+
+ let arrAddr =
+ match arr with
+ | EvalStackValue.ManagedPointer (ManagedPointerSource.Heap addr)
+ | EvalStackValue.ObjectRef addr -> addr
+ | EvalStackValue.ManagedPointer ManagedPointerSource.Null -> failwith "TODO: throw NRE"
+ | _ -> failwith $"Invalid array: %O{arr}"
+
+ let value = IlMachineState.getArrayValue arrAddr index state
+
+ let state =
+ state
+ |> IlMachineState.pushToEvalStack value currentThread
+ |> IlMachineState.advanceProgramCounter currentThread
+
+ ExecutionResult.Stepped (state, WhatWeDid.Executed)
+
+ let internal stElem
+ (targetCliTypeZero : CliType)
+ (value : EvalStackValue)
+ (index : EvalStackValue)
+ (arr : EvalStackValue)
+ (currentThread : ThreadId)
+ (state : IlMachineState)
+ : ExecutionResult
+ =
+ let index =
+ match index with
+ | EvalStackValue.NativeInt src ->
+ match src with
+ | NativeIntSource.FunctionPointer _
+ | NativeIntSource.TypeHandlePtr _
+ | NativeIntSource.ManagedPointer _ -> failwith "Refusing to treat a pointer as an array index"
+ | NativeIntSource.Verbatim i -> i |> int32
+ | EvalStackValue.Int32 i -> i
+ | _ -> failwith $"Invalid index: {index}"
+
+ let arrAddr =
+ match arr with
+ | EvalStackValue.ManagedPointer (ManagedPointerSource.Heap addr)
+ | EvalStackValue.ObjectRef addr -> addr
+ | EvalStackValue.ManagedPointer ManagedPointerSource.Null -> failwith "TODO: throw NRE"
+ | _ -> failwith $"Invalid array: %O{arr}"
+ // TODO: throw ArrayTypeMismatchException if incorrect types
+
+ let arr = state.ManagedHeap.Arrays.[arrAddr]
+
+ if index < 0 || index >= arr.Length then
+ failwith "TODO: throw IndexOutOfRangeException"
+
+ let state =
+ state
+ |> IlMachineState.setArrayValue arrAddr (EvalStackValue.toCliTypeCoerced targetCliTypeZero value) index
+ |> IlMachineState.advanceProgramCounter currentThread
+
+ ExecutionResult.Stepped (state, WhatWeDid.Executed)
+
let internal execute
(loggerFactory : ILoggerFactory)
(corelib : BaseClassTypes)
@@ -346,38 +362,7 @@ module NullaryIlOp =
let var2, state = state |> IlMachineState.popEvalStack currentThread
let var1, state = state |> IlMachineState.popEvalStack currentThread
- let comparisonResult =
- // Table III.4
- match var1, var2 with
- | EvalStackValue.Int32 var1, EvalStackValue.Int32 var2 -> if var1 = var2 then 1 else 0
- | EvalStackValue.Int32 var1, EvalStackValue.NativeInt var2 -> failwith "TODO: int32 CEQ nativeint"
- | EvalStackValue.Int32 _, _ -> failwith $"bad ceq: Int32 vs {var2}"
- | EvalStackValue.Int64 var1, EvalStackValue.Int64 var2 -> if var1 = var2 then 1 else 0
- | EvalStackValue.Int64 _, _ -> failwith $"bad ceq: Int64 vs {var2}"
- | 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 ->
- 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 -> 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}"
- | EvalStackValue.UserDefinedValueType _, _ -> failwith $"bad ceq: UserDefinedValueType vs {var2}"
+ let comparisonResult = if EvalStackValueComparisons.ceq var1 var2 then 1 else 0
state
|> IlMachineState.pushToEvalStack' (EvalStackValue.Int32 comparisonResult) currentThread
@@ -390,41 +375,7 @@ module NullaryIlOp =
let var2, state = state |> IlMachineState.popEvalStack currentThread
let var1, state = state |> IlMachineState.popEvalStack currentThread
- let comparisonResult =
- match var1, var2 with
- | EvalStackValue.Int64 var1, EvalStackValue.Int64 var2 -> if var1 < var2 then 1 else 0
- | EvalStackValue.Float var1, EvalStackValue.Float var2 ->
- failwith "TODO: Clt float comparison unimplemented"
- | EvalStackValue.ObjectRef var1, EvalStackValue.ObjectRef var2 ->
- failwith $"Clt instruction invalid for comparing object refs, {var1} vs {var2}"
- | EvalStackValue.ObjectRef var1, other -> failwith $"invalid comparison, ref %O{var1} vs %O{other}"
- | other, EvalStackValue.ObjectRef var2 -> failwith $"invalid comparison, %O{other} vs ref %O{var2}"
- | EvalStackValue.Float i, other -> failwith $"invalid comparison, float %f{i} vs %O{other}"
- | other, EvalStackValue.Float i -> failwith $"invalid comparison, %O{other} vs float %f{i}"
- | EvalStackValue.Int64 i, other -> failwith $"invalid comparison, int64 %i{i} vs %O{other}"
- | other, EvalStackValue.Int64 i -> failwith $"invalid comparison, %O{other} vs int64 %i{i}"
- | EvalStackValue.Int32 var1, EvalStackValue.Int32 var2 -> if var1 < var2 then 1 else 0
- | EvalStackValue.Int32 var1, EvalStackValue.NativeInt var2 ->
- failwith "TODO: Clt Int32 vs NativeInt comparison unimplemented"
- | EvalStackValue.Int32 i, other -> failwith $"invalid comparison, int32 %i{i} vs %O{other}"
- | EvalStackValue.NativeInt var1, EvalStackValue.Int32 var2 ->
- failwith "TODO: Clt NativeInt vs Int32 comparison unimplemented"
- | other, EvalStackValue.Int32 var2 -> failwith $"invalid comparison, {other} vs int32 {var2}"
- | EvalStackValue.NativeInt var1, EvalStackValue.NativeInt var2 ->
- if NativeIntSource.isLess var1 var2 then 1 else 0
- | EvalStackValue.NativeInt var1, other -> failwith $"invalid comparison, nativeint {var1} vs %O{other}"
- | EvalStackValue.ManagedPointer managedPointerSource, NativeInt int64 ->
- failwith "TODO: Clt ManagedPointer vs NativeInt comparison unimplemented"
- | EvalStackValue.ManagedPointer managedPointerSource, ManagedPointer pointerSource ->
- failwith "TODO: Clt ManagedPointer vs ManagedPointer comparison unimplemented"
- | EvalStackValue.ManagedPointer managedPointerSource, UserDefinedValueType _ ->
- failwith "TODO: Clt ManagedPointer vs UserDefinedValueType comparison unimplemented"
- | EvalStackValue.UserDefinedValueType _, NativeInt int64 ->
- failwith "TODO: Clt UserDefinedValueType vs NativeInt comparison unimplemented"
- | EvalStackValue.UserDefinedValueType _, ManagedPointer managedPointerSource ->
- failwith "TODO: Clt UserDefinedValueType vs ManagedPointer comparison unimplemented"
- | EvalStackValue.UserDefinedValueType _, UserDefinedValueType _ ->
- failwith "TODO: Clt UserDefinedValueType vs UserDefinedValueType comparison unimplemented"
+ let comparisonResult = if EvalStackValueComparisons.clt var1 var2 then 1 else 0
state
|> IlMachineState.pushToEvalStack' (EvalStackValue.Int32 comparisonResult) currentThread
@@ -460,8 +411,9 @@ module NullaryIlOp =
| Sub_ovf -> failwith "TODO: Sub_ovf unimplemented"
| Sub_ovf_un -> failwith "TODO: Sub_ovf_un unimplemented"
| Add ->
- let result, state =
- binaryArithmeticOperation ArithmeticOperation.add currentThread state
+ let val1, state = IlMachineState.popEvalStack currentThread state
+ let val2, state = IlMachineState.popEvalStack currentThread state
+ let result = BinaryArithmetic.execute ArithmeticOperation.add val1 val2
state
|> IlMachineState.pushToEvalStack' result currentThread
@@ -471,8 +423,9 @@ module NullaryIlOp =
| Add_ovf -> failwith "TODO: Add_ovf unimplemented"
| Add_ovf_un -> failwith "TODO: Add_ovf_un unimplemented"
| Mul ->
- let result, state =
- binaryArithmeticOperation ArithmeticOperation.mul currentThread state
+ let val1, state = IlMachineState.popEvalStack currentThread state
+ let val2, state = IlMachineState.popEvalStack currentThread state
+ let result = BinaryArithmetic.execute ArithmeticOperation.mul val1 val2
state
|> IlMachineState.pushToEvalStack' result currentThread
@@ -798,6 +751,7 @@ module NullaryIlOp =
| ManagedPointerSource.Argument (sourceThread, methodFrame, whichVar) ->
state.ThreadState.[sourceThread].MethodStates.[methodFrame].Arguments.[int whichVar]
| ManagedPointerSource.Heap managedHeapAddress -> failwith "todo"
+ | ManagedPointerSource.ArrayIndex _ -> failwith "todo"
| a -> failwith $"TODO: {a}"
let state =
@@ -807,7 +761,29 @@ module NullaryIlOp =
|> IlMachineState.advanceProgramCounter currentThread
(state, WhatWeDid.Executed) |> ExecutionResult.Stepped
- | Stind_ref -> failwith "TODO: Stind_ref unimplemented"
+ | Stind_ref ->
+ let value, state = IlMachineState.popEvalStack currentThread state
+ let addr, state = IlMachineState.popEvalStack currentThread state
+
+ let state =
+ match addr with
+ | EvalStackValue.ManagedPointer src ->
+ match src with
+ | ManagedPointerSource.Null -> failwith "TODO: throw NRE"
+ | 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.setArrayValue
+ arr
+ (EvalStackValue.toCliTypeCoerced (CliType.ObjectRef None) value)
+ index
+ | addr -> failwith $"TODO: {addr}"
+
+ let state = state |> IlMachineState.advanceProgramCounter currentThread
+
+ (state, WhatWeDid.Executed) |> ExecutionResult.Stepped
| Ldelem_i -> failwith "TODO: Ldelem_i unimplemented"
| Ldelem_i1 -> failwith "TODO: Ldelem_i1 unimplemented"
| Ldelem_u1 -> failwith "TODO: Ldelem_u1 unimplemented"
@@ -819,19 +795,54 @@ module NullaryIlOp =
| Ldelem_u8 -> failwith "TODO: Ldelem_u8 unimplemented"
| Ldelem_r4 -> failwith "TODO: Ldelem_r4 unimplemented"
| Ldelem_r8 -> failwith "TODO: Ldelem_r8 unimplemented"
- | Ldelem_ref -> failwith "TODO: Ldelem_ref unimplemented"
- | Stelem_i -> failwith "TODO: Stelem_i unimplemented"
- | Stelem_i1 -> failwith "TODO: Stelem_i1 unimplemented"
+ | Ldelem_ref ->
+ let index, state = IlMachineState.popEvalStack currentThread state
+ let arr, state = IlMachineState.popEvalStack currentThread state
+
+ ldElem (CliType.ObjectRef None) index arr currentThread state
+ | Stelem_i ->
+ let value, state = IlMachineState.popEvalStack currentThread state
+ let index, state = IlMachineState.popEvalStack currentThread state
+ let arr, state = IlMachineState.popEvalStack currentThread state
+
+ stElem
+ (CliType.Numeric (CliNumericType.NativeInt (NativeIntSource.Verbatim 0L)))
+ value
+ index
+ arr
+ currentThread
+ state
+ | Stelem_i1 ->
+ let value, state = IlMachineState.popEvalStack currentThread state
+ let index, state = IlMachineState.popEvalStack currentThread state
+ let arr, state = IlMachineState.popEvalStack currentThread state
+ stElem (CliType.Numeric (CliNumericType.Int8 0y)) value index arr currentThread state
| Stelem_u1 -> failwith "TODO: Stelem_u1 unimplemented"
- | Stelem_i2 -> failwith "TODO: Stelem_i2 unimplemented"
+ | Stelem_i2 ->
+ let value, state = IlMachineState.popEvalStack currentThread state
+ let index, state = IlMachineState.popEvalStack currentThread state
+ let arr, state = IlMachineState.popEvalStack currentThread state
+ stElem (CliType.Numeric (CliNumericType.Int16 0s)) value index arr currentThread state
| Stelem_u2 -> failwith "TODO: Stelem_u2 unimplemented"
- | Stelem_i4 -> failwith "TODO: Stelem_i4 unimplemented"
+ | Stelem_i4 ->
+ let value, state = IlMachineState.popEvalStack currentThread state
+ let index, state = IlMachineState.popEvalStack currentThread state
+ let arr, state = IlMachineState.popEvalStack currentThread state
+ stElem (CliType.Numeric (CliNumericType.Int32 0)) value index arr currentThread state
| Stelem_u4 -> failwith "TODO: Stelem_u4 unimplemented"
- | Stelem_i8 -> failwith "TODO: Stelem_i8 unimplemented"
+ | Stelem_i8 ->
+ let value, state = IlMachineState.popEvalStack currentThread state
+ let index, state = IlMachineState.popEvalStack currentThread state
+ let arr, state = IlMachineState.popEvalStack currentThread state
+ stElem (CliType.Numeric (CliNumericType.Int64 0L)) value index arr currentThread state
| Stelem_u8 -> failwith "TODO: Stelem_u8 unimplemented"
| Stelem_r4 -> failwith "TODO: Stelem_r4 unimplemented"
| Stelem_r8 -> failwith "TODO: Stelem_r8 unimplemented"
- | Stelem_ref -> failwith "TODO: Stelem_ref unimplemented"
+ | Stelem_ref ->
+ let value, state = IlMachineState.popEvalStack currentThread state
+ let index, state = IlMachineState.popEvalStack currentThread state
+ let arr, state = IlMachineState.popEvalStack currentThread state
+ stElem (CliType.ObjectRef None) value index arr currentThread state
| Cpblk -> failwith "TODO: Cpblk unimplemented"
| Initblk -> failwith "TODO: Initblk unimplemented"
| Conv_ovf_u1 -> failwith "TODO: Conv_ovf_u1 unimplemented"
diff --git a/WoofWare.PawPrint/UnaryMetadataIlOp.fs b/WoofWare.PawPrint/UnaryMetadataIlOp.fs
index b93b339..5e07426 100644
--- a/WoofWare.PawPrint/UnaryMetadataIlOp.fs
+++ b/WoofWare.PawPrint/UnaryMetadataIlOp.fs
@@ -340,7 +340,38 @@ module internal UnaryMetadataIlOp =
state, WhatWeDid.Executed
| Box -> failwith "TODO: Box unimplemented"
- | Ldelema -> failwith "TODO: Ldelema unimplemented"
+ | Ldelema ->
+ let index, state = IlMachineState.popEvalStack thread state
+ let arr, state = IlMachineState.popEvalStack thread state
+
+ let index =
+ match index with
+ | EvalStackValue.Int32 i -> i
+ | _ -> failwith $"TODO: {index}"
+
+ let arrAddr =
+ match arr with
+ | EvalStackValue.ManagedPointer (ManagedPointerSource.Heap addr)
+ | EvalStackValue.ObjectRef addr -> addr
+ | EvalStackValue.ManagedPointer ManagedPointerSource.Null -> failwith "TODO: throw NRE"
+ | _ -> failwith $"Invalid array: %O{arr}"
+
+ // TODO: throw ArrayTypeMismatchException if incorrect types
+
+ let arr = state.ManagedHeap.Arrays.[arrAddr]
+
+ if index < 0 || index >= arr.Length then
+ failwith "TODO: throw IndexOutOfRangeException"
+
+ let result =
+ ManagedPointerSource.ArrayIndex (arrAddr, index)
+ |> EvalStackValue.ManagedPointer
+
+ let state =
+ IlMachineState.pushToEvalStack' result thread state
+ |> IlMachineState.advanceProgramCounter thread
+
+ state, WhatWeDid.Executed
| Isinst ->
let actualObj, state = IlMachineState.popEvalStack thread state
@@ -464,6 +495,8 @@ module internal UnaryMetadataIlOp =
state
|> IlMachineState.setLocalVariable sourceThread methodFrame whichVar valueToStore
| ManagedPointerSource.Argument (sourceThread, methodFrame, whichVar) -> failwith "todo"
+ | ManagedPointerSource.ArrayIndex (arr, index) ->
+ state |> IlMachineState.setArrayValue arr valueToStore index
| ManagedPointerSource.Heap addr ->
match state.ManagedHeap.NonArrayObjects.TryGetValue addr with
| false, _ -> failwith $"todo: array {addr}"
@@ -641,6 +674,9 @@ module internal UnaryMetadataIlOp =
match state.ManagedHeap.NonArrayObjects.TryGetValue managedHeapAddress with
| false, _ -> failwith $"todo: array {managedHeapAddress}"
| true, v -> IlMachineState.pushToEvalStack v.Fields.[field.Name] thread state
+ | ManagedPointerSource.ArrayIndex (arr, index) ->
+ let currentValue = state |> IlMachineState.getArrayValue arr index
+ IlMachineState.pushToEvalStack currentValue thread state
| ManagedPointerSource.Null -> failwith "TODO: raise NullReferenceException"
| EvalStackValue.ObjectRef managedHeapAddress -> failwith $"todo: {managedHeapAddress}"
| EvalStackValue.UserDefinedValueType _ as udvt -> IlMachineState.pushToEvalStack' udvt thread state
diff --git a/WoofWare.PawPrint/WoofWare.PawPrint.fsproj b/WoofWare.PawPrint/WoofWare.PawPrint.fsproj
index 3e8a007..948d72c 100644
--- a/WoofWare.PawPrint/WoofWare.PawPrint.fsproj
+++ b/WoofWare.PawPrint/WoofWare.PawPrint.fsproj
@@ -16,6 +16,8 @@
+
+