mirror of
https://github.com/Smaug123/WoofWare.PawPrint
synced 2025-10-07 23:18:41 +00:00
WIP
This commit is contained in:
@@ -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;
|
||||
}
|
||||
}
|
||||
|
@@ -30,5 +30,4 @@ type TestCase =
|
||||
FileName : string
|
||||
ExpectedReturnCode : int
|
||||
NativeImpls : NativeImpls
|
||||
LocalVariablesOfMain : CliType list option
|
||||
}
|
||||
|
@@ -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}"
|
||||
|
@@ -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}"
|
||||
|
@@ -45,6 +45,7 @@
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<EmbeddedResource Include="sourcesImpure\WriteLine.cs" />
|
||||
<EmbeddedResource Include="sourcesImpure\ConsoleColor.cs" />
|
||||
<EmbeddedResource Include="sourcesImpure\InstaQuit.cs" />
|
||||
</ItemGroup>
|
||||
|
||||
|
13
WoofWare.PawPrint.Test/sourcesImpure/ConsoleColor.cs
Normal file
13
WoofWare.PawPrint.Test/sourcesImpure/ConsoleColor.cs
Normal file
@@ -0,0 +1,13 @@
|
||||
using System;
|
||||
|
||||
namespace HelloWorldApp
|
||||
{
|
||||
class Program
|
||||
{
|
||||
static int Main(string[] args)
|
||||
{
|
||||
Console.WriteLine("Hello, world!");
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
}
|
@@ -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;
|
||||
}
|
||||
}
|
||||
|
@@ -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}"
|
||||
|
||||
|
@@ -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
|
||||
|
@@ -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)
|
||||
|
@@ -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"
|
||||
|
@@ -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}"
|
||||
|
@@ -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
12
WoofWare.PawPrint/List.fs
Normal 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
|
@@ -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
|
||||
|
@@ -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
|
||||
|
@@ -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"
|
||||
|
||||
|
@@ -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
|
||||
|
@@ -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" />
|
||||
|
Reference in New Issue
Block a user