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 f6cbe30..777c24b 100644
--- a/WoofWare.PawPrint.Test/TestPureCases.fs
+++ b/WoofWare.PawPrint.Test/TestPureCases.fs
@@ -185,6 +185,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 8624f96..95d3dfb 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/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/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 847ef5b..fcd304e 100644
--- a/WoofWare.PawPrint/NullaryIlOp.fs
+++ b/WoofWare.PawPrint/NullaryIlOp.fs
@@ -46,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
@@ -121,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)
@@ -674,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 =
@@ -683,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"
@@ -695,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