Add more test cases (#11)

This commit is contained in:
Patrick Stevens
2025-05-20 22:09:02 +01:00
committed by GitHub
parent bc389f1f23
commit 109b3a70fc
7 changed files with 223 additions and 30 deletions

View File

@@ -9,12 +9,24 @@ open WoofWare.PawPrint
open WoofWare.PawPrint.Test
[<TestFixture>]
module TestNoOp =
module TestCases =
let assy = typeof<RunResult>.Assembly
[<Test>]
let ``Can run a no-op`` () : unit =
let source = Assembly.getEmbeddedResourceAsString "NoOp.cs" assy
let cases : TestCase list =
[
{
FileName = "NoOp.cs"
ExpectedReturnCode = 1
}
{
FileName = "TriangleNumber.cs"
ExpectedReturnCode = 10
}
]
[<TestCaseSource(nameof cases)>]
let ``Can run a no-op`` (case : TestCase) : unit =
let source = Assembly.getEmbeddedResourceAsString case.FileName assy
let image = Roslyn.compile [ source ]
let messages, loggerFactory = LoggerFactory.makeTest ()
@@ -28,10 +40,10 @@ module TestNoOp =
let exitCode =
match terminalState.ThreadState.[terminatingThread].MethodState.EvaluationStack.Values with
| [] -> failwith "expected program to return 1, but it returned void"
| [] -> failwith "expected program to return a value, but it returned void"
| head :: _ ->
match head with
| EvalStackValue.Int32 i -> i
| _ -> failwith "TODO"
| ret -> failwith "expected program to return an int, but it returned %O{ret}"
exitCode |> shouldEqual 1
exitCode |> shouldEqual case.ExpectedReturnCode

View File

@@ -15,3 +15,9 @@ type RunResult =
/// Final interpreter state after we stopped executing.
FinalState : IlMachineState
}
type TestCase =
{
FileName : string
ExpectedReturnCode : int
}

View File

@@ -1,5 +1,6 @@
namespace WoofWare.Pawprint.Test
open System
open System.Collections.Immutable
open System.IO
open FsUnitTyped
@@ -21,17 +22,23 @@ module TestHelloWorld =
let dotnetRuntimes =
DotnetRuntime.SelectForDll assy.Location |> ImmutableArray.CreateRange
use peImage = new MemoryStream (image)
try
use peImage = new MemoryStream (image)
let terminalState, terminatingThread =
Program.run loggerFactory peImage dotnetRuntimes []
let terminalState, terminatingThread =
Program.run loggerFactory peImage dotnetRuntimes []
let exitCode =
match terminalState.ThreadState.[terminatingThread].MethodState.EvaluationStack.Values with
| [] -> failwith "expected program to return 1, but it returned void"
| head :: _ ->
match head with
| EvalStackValue.Int32 i -> i
| _ -> failwith "TODO"
let exitCode =
match terminalState.ThreadState.[terminatingThread].MethodState.EvaluationStack.Values with
| [] -> failwith "expected program to return 1, but it returned void"
| head :: _ ->
match head with
| EvalStackValue.Int32 i -> i
| _ -> failwith "TODO"
exitCode |> shouldEqual 0
exitCode |> shouldEqual 0
with _ ->
for m in messages () do
Console.Error.WriteLine $"{m}"
reraise ()

View File

@@ -14,12 +14,13 @@
<Compile Include="Assembly.fs" />
<Compile Include="Roslyn.fs" />
<Compile Include="TestHarness.fs"/>
<Compile Include="TestNoOp.fs" />
<Compile Include="TestCases.fs" />
<Compile Include="TestHelloWorld.fs" />
<Compile Include="TestBasicLock.fs" />
<EmbeddedResource Include="sources\BasicLock.cs" />
<EmbeddedResource Include="sources\NoOp.cs" />
<EmbeddedResource Include="sources\HelloWorld.cs" />
<EmbeddedResource Include="sources\TriangleNumber.cs" />
</ItemGroup>
<ItemGroup>

View File

@@ -0,0 +1,17 @@
using System;
namespace TriangleNumber
{
class Program
{
static int Main(string[] args)
{
var answer = 0;
for (int i = 0; i < 5; i++)
{
answer += i;
}
return answer;
}
}
}

View File

@@ -1,5 +1,7 @@
namespace WoofWare.PawPrint
#nowarn "42"
open System.Collections.Immutable
open System.IO
open System.Reflection
@@ -1096,7 +1098,44 @@ module AbstractMachine =
| Ceq -> failwith "todo"
| Cgt -> failwith "todo"
| Cgt_un -> failwith "todo"
| Clt -> failwith "todo"
| Clt ->
let var2, state = state |> IlMachineState.popEvalStack currentThread
let var1, state = state |> IlMachineState.popEvalStack currentThread
let comparisonResult =
match var1, var2 with
| EvalStackValue.Int64 var1, EvalStackValue.Int64 var2 -> if var1 < var2 then 1 else 0
| EvalStackValue.Float var1, EvalStackValue.Float var2 -> failwith "todo"
| EvalStackValue.ObjectRef var1, EvalStackValue.ObjectRef var2 ->
failwith $"Clt instruction invalid for comparing object refs, {var1} vs {var2}"
| EvalStackValue.ObjectRef var1, other -> failwith $"invalid comparison, ref %O{var1} vs %O{other}"
| other, EvalStackValue.ObjectRef var2 -> failwith $"invalid comparison, %O{other} vs ref %O{var2}"
| EvalStackValue.Float i, other -> failwith $"invalid comparison, float %f{i} vs %O{other}"
| other, EvalStackValue.Float i -> failwith $"invalid comparison, %O{other} vs float %f{i}"
| EvalStackValue.Int64 i, other -> failwith $"invalid comparison, int64 %i{i} vs %O{other}"
| other, EvalStackValue.Int64 i -> failwith $"invalid comparison, %O{other} vs int64 %i{i}"
| EvalStackValue.Int32 var1, EvalStackValue.Int32 var2 -> if var1 < var2 then 1 else 0
| EvalStackValue.Int32 var1, EvalStackValue.NativeInt var2 ->
failwith "todo: this is valid but no idea how"
| EvalStackValue.Int32 i, other -> failwith $"invalid comparison, int32 %i{i} vs %O{other}"
| EvalStackValue.NativeInt var1, EvalStackValue.Int32 var2 ->
failwith "todo: this is valid but no idea how"
| other, EvalStackValue.Int32 var2 -> failwith $"invalid comparison, {other} vs int32 {var2}"
| EvalStackValue.NativeInt var1, EvalStackValue.NativeInt var2 -> if var1 < var2 then 1 else 0
| EvalStackValue.NativeInt var1, other ->
failwith $"invalid comparison, nativeint %i{var1} vs %O{other}"
| EvalStackValue.ManagedPointer managedPointerSource, NativeInt int64 -> failwith "todo"
| EvalStackValue.ManagedPointer managedPointerSource, ManagedPointer pointerSource -> failwith "todo"
| EvalStackValue.ManagedPointer managedPointerSource, UserDefinedValueType -> failwith "todo"
| EvalStackValue.UserDefinedValueType, NativeInt int64 -> failwith "todo"
| EvalStackValue.UserDefinedValueType, ManagedPointer managedPointerSource -> failwith "todo"
| EvalStackValue.UserDefinedValueType, UserDefinedValueType -> failwith "todo"
state
|> IlMachineState.pushToEvalStack' (EvalStackValue.Int32 comparisonResult) currentThread
|> IlMachineState.advanceProgramCounter currentThread
|> Tuple.withRight WhatWeDid.Executed
|> ExecutionResult.Stepped
| Clt_un -> failwith "todo"
| Stloc_0 ->
state
@@ -1125,7 +1164,43 @@ module AbstractMachine =
| Sub -> failwith "todo"
| Sub_ovf -> failwith "todo"
| Sub_ovf_un -> failwith "todo"
| Add -> failwith "todo"
| Add ->
let val1, state = IlMachineState.popEvalStack currentThread state
let val2, state = IlMachineState.popEvalStack currentThread state
// see table at https://learn.microsoft.com/en-us/dotnet/api/system.reflection.emit.opcodes.add?view=net-9.0
let result =
match val1, val2 with
| EvalStackValue.Int32 val1, EvalStackValue.Int32 val2 ->
(# "add" val1 val2 : int32 #) |> EvalStackValue.Int32
| EvalStackValue.Int32 val1, EvalStackValue.NativeInt val2 -> failwith "" |> EvalStackValue.NativeInt
| EvalStackValue.Int32 val1, EvalStackValue.ManagedPointer val2 ->
failwith "" |> EvalStackValue.ManagedPointer
| EvalStackValue.Int32 val1, EvalStackValue.ObjectRef val2 -> failwith "" |> EvalStackValue.ObjectRef
| EvalStackValue.Int64 val1, EvalStackValue.Int64 val2 ->
(# "add" val1 val2 : int64 #) |> EvalStackValue.Int64
| EvalStackValue.NativeInt val1, EvalStackValue.Int32 val2 -> failwith "" |> EvalStackValue.NativeInt
| EvalStackValue.NativeInt val1, EvalStackValue.NativeInt val2 ->
failwith "" |> EvalStackValue.NativeInt
| EvalStackValue.NativeInt val1, EvalStackValue.ManagedPointer val2 ->
failwith "" |> EvalStackValue.ManagedPointer
| EvalStackValue.NativeInt val1, EvalStackValue.ObjectRef val2 ->
failwith "" |> EvalStackValue.ObjectRef
| EvalStackValue.Float val1, EvalStackValue.Float val2 ->
(# "add" val1 val2 : float #) |> EvalStackValue.Float
| EvalStackValue.ManagedPointer val1, EvalStackValue.NativeInt val2 ->
failwith "" |> EvalStackValue.ManagedPointer
| EvalStackValue.ObjectRef val1, EvalStackValue.NativeInt val2 ->
failwith "" |> EvalStackValue.ObjectRef
| EvalStackValue.ManagedPointer val1, EvalStackValue.Int32 val2 ->
failwith "" |> EvalStackValue.ManagedPointer
| EvalStackValue.ObjectRef val1, EvalStackValue.Int32 val2 -> failwith "" |> EvalStackValue.ObjectRef
| val1, val2 -> failwith $"invalid add operation: {val1} and {val2}"
state
|> IlMachineState.pushToEvalStack' result currentThread
|> IlMachineState.advanceProgramCounter currentThread
|> Tuple.withRight WhatWeDid.Executed
|> ExecutionResult.Stepped
| Add_ovf -> failwith "todo"
| Add_ovf_un -> failwith "todo"
| Mul -> failwith "todo"
@@ -1600,14 +1675,26 @@ module AbstractMachine =
|> IlMachineState.popFromStackToLocalVariable currentThread (int b)
|> IlMachineState.advanceProgramCounter currentThread
|> Tuple.withRight WhatWeDid.Executed
| Ldc_I8 int64 -> failwith "todo"
| Ldc_I8 i ->
state
|> IlMachineState.pushToEvalStack (CliType.Numeric (CliNumericType.Int64 i)) currentThread
|> IlMachineState.advanceProgramCounter currentThread
|> Tuple.withRight WhatWeDid.Executed
| Ldc_I4 i ->
state
|> IlMachineState.pushToEvalStack (CliType.Numeric (CliNumericType.Int32 i)) currentThread
|> IlMachineState.advanceProgramCounter currentThread
|> Tuple.withRight WhatWeDid.Executed
| Ldc_R4 f -> failwith "todo"
| Ldc_R8 f -> failwith "todo"
| Ldc_R4 f ->
state
|> IlMachineState.pushToEvalStack (CliType.Numeric (CliNumericType.Float32 f)) currentThread
|> IlMachineState.advanceProgramCounter currentThread
|> Tuple.withRight WhatWeDid.Executed
| Ldc_R8 f ->
state
|> IlMachineState.pushToEvalStack (CliType.Numeric (CliNumericType.Float64 f)) currentThread
|> IlMachineState.advanceProgramCounter currentThread
|> Tuple.withRight WhatWeDid.Executed
| Ldc_I4_s b ->
state
|> IlMachineState.pushToEvalStack (CliType.Numeric (CliNumericType.Int8 b)) currentThread
@@ -1620,9 +1707,69 @@ module AbstractMachine =
|> IlMachineState.jumpProgramCounter currentThread (int b)
|> Tuple.withRight WhatWeDid.Executed
| Brfalse_s b -> failwith "todo"
| Brtrue_s b -> failwith "todo"
| Brfalse i -> failwith "todo"
| Brtrue i -> failwith "todo"
| Brtrue_s b ->
let popped, state = IlMachineState.popEvalStack currentThread state
let isTrue =
match popped with
| EvalStackValue.Int32 i -> i <> 0
| EvalStackValue.Int64 i -> i <> 0L
| EvalStackValue.NativeInt i -> i <> 0L
| EvalStackValue.Float f -> failwith "semantics are undocumented"
| EvalStackValue.ManagedPointer ManagedPointerSource.Null -> false
| EvalStackValue.ManagedPointer _ -> true
| EvalStackValue.ObjectRef _ -> failwith "todo"
| EvalStackValue.UserDefinedValueType -> failwith "todo"
state
|> IlMachineState.advanceProgramCounter currentThread
|> if isTrue then
IlMachineState.jumpProgramCounter currentThread (int b)
else
id
|> Tuple.withRight WhatWeDid.Executed
| Brfalse i ->
let popped, state = IlMachineState.popEvalStack currentThread state
let isFalse =
match popped with
| EvalStackValue.Int32 i -> i = 0
| EvalStackValue.Int64 i -> i = 0L
| EvalStackValue.NativeInt i -> i = 0L
| EvalStackValue.Float f -> failwith "semantics are undocumented"
| EvalStackValue.ManagedPointer ManagedPointerSource.Null -> true
| EvalStackValue.ManagedPointer _ -> false
| EvalStackValue.ObjectRef _ -> failwith "todo"
| EvalStackValue.UserDefinedValueType -> failwith "todo"
state
|> IlMachineState.advanceProgramCounter currentThread
|> if isFalse then
IlMachineState.jumpProgramCounter currentThread i
else
id
|> Tuple.withRight WhatWeDid.Executed
| Brtrue i ->
let popped, state = IlMachineState.popEvalStack currentThread state
let isTrue =
match popped with
| EvalStackValue.Int32 i -> i <> 0
| EvalStackValue.Int64 i -> i <> 0L
| EvalStackValue.NativeInt i -> i <> 0L
| EvalStackValue.Float f -> failwith "semantics are undocumented"
| EvalStackValue.ManagedPointer ManagedPointerSource.Null -> false
| EvalStackValue.ManagedPointer _ -> true
| EvalStackValue.ObjectRef _ -> failwith "todo"
| EvalStackValue.UserDefinedValueType -> failwith "todo"
state
|> IlMachineState.advanceProgramCounter currentThread
|> if isTrue then
IlMachineState.jumpProgramCounter currentThread i
else
id
|> Tuple.withRight WhatWeDid.Executed
| Beq_s b -> failwith "todo"
| Blt_s b -> failwith "todo"
| Ble_s b -> failwith "todo"

View File

@@ -54,7 +54,10 @@ module EvalStackValue =
| CliNumericType.UInt8 b -> failwith "todo"
| CliNumericType.UInt16 s -> failwith "todo"
| CliNumericType.Float32 f -> failwith "todo"
| CliNumericType.Float64 f -> failwith "todo"
| CliNumericType.Float64 _ ->
match popped with
| EvalStackValue.Float f -> CliType.Numeric (CliNumericType.Float64 f)
| _ -> failwith "todo"
| CliType.ObjectRef _ ->
match popped with
| EvalStackValue.ManagedPointer ptrSource ->
@@ -117,8 +120,8 @@ type EvalStack =
| CliNumericType.Int16 s -> int32 s |> EvalStackValue.Int32
| CliNumericType.UInt16 s -> int32 s |> EvalStackValue.Int32
| CliNumericType.Float32 f -> failwith "todo"
| CliNumericType.Float64 f -> failwith "todo"
| CliNumericType.NativeFloat f -> failwith "todo"
| CliNumericType.Float64 f -> EvalStackValue.Float f
| CliNumericType.NativeFloat f -> EvalStackValue.Float f
| CliType.ObjectRef i ->
match i with
| None -> EvalStackValue.ManagedPointer ManagedPointerSource.Null