This commit is contained in:
Smaug123
2025-08-02 20:57:53 +01:00
parent d8b7e84f6c
commit 9bafd0f4b0
19 changed files with 695 additions and 283 deletions

View File

@@ -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;
}
}

View File

@@ -30,5 +30,4 @@ type TestCase =
FileName : string
ExpectedReturnCode : int
NativeImpls : NativeImpls
LocalVariablesOfMain : CliType list option
}

View File

@@ -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}"

View File

@@ -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}"

View File

@@ -45,6 +45,7 @@
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="sourcesImpure\WriteLine.cs" />
<EmbeddedResource Include="sourcesImpure\ConsoleColor.cs" />
<EmbeddedResource Include="sourcesImpure\InstaQuit.cs" />
</ItemGroup>

View File

@@ -0,0 +1,13 @@
using System;
namespace HelloWorldApp
{
class Program
{
static int Main(string[] args)
{
Console.WriteLine("Hello, world!");
return 1;
}
}
}

View File

@@ -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;
}
}

View File

@@ -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}"

View File

@@ -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) ->
$"<argument %i{var} in method frame %i{method} of thread %O{source}>"
| ManagedPointerSource.ArrayIndex (arr, index) -> $"<index %i{index} of array %O{arr}>"
| ManagedPointerSource.Field (source, name) -> $"<field %s{name} of %O{source}>"
[<RequireQualifiedAccess>]
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
[<RequireQualifiedAccess>]
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

View File

@@ -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)

View File

@@ -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"

View File

@@ -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<uint16> whichVar]
| ManagedPointerSource.Argument (sourceThread, methodFrame, whichVar) ->
state.ThreadState.[sourceThread].MethodStates.[methodFrame].Arguments.[int<uint16> 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}"

View File

@@ -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)

12
WoofWare.PawPrint/List.fs Normal file
View File

@@ -0,0 +1,12 @@
namespace WoofWare.PawPrint
[<RequireQualifiedAccess>]
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

View File

@@ -8,7 +8,7 @@ type SyncBlock =
type AllocatedNonArrayObject =
{
Fields : Map<string, CliType>
Fields : (string * CliType) list
Type : WoofWare.PawPrint.TypeInfoCrate
SyncBlock : SyncBlock
}
@@ -29,7 +29,9 @@ type ManagedHeap =
StringArrayData : ImmutableArray<char>
}
static member Empty : ManagedHeap =
[<RequireQualifiedAccess>]
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

View File

@@ -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<uint16> whichVar]
| ManagedPointerSource.LocalVariable (sourceThread, methodFrame, whichVar) ->
state.ThreadState.[sourceThread].MethodStates.[methodFrame].LocalVariables.[int<uint16> 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<uint16> whichVar]
| ManagedPointerSource.Argument (sourceThread, methodFrame, whichVar) ->
state.ThreadState.[sourceThread].MethodStates.[methodFrame].Arguments.[int<uint16> 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

View File

@@ -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"

View File

@@ -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<uint16> 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<uint16> 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

View File

@@ -7,6 +7,7 @@
<ItemGroup>
<Compile Include="Tuple.fs" />
<Compile Include="List.fs" />
<Compile Include="Result.fs" />
<Compile Include="Corelib.fs" />
<Compile Include="AbstractMachineDomain.fs" />