mirror of
https://github.com/Smaug123/WoofWare.PawPrint
synced 2025-10-05 06:08:39 +00:00
Add test harness and run the first program (#5)
This commit is contained in:
@@ -7,32 +7,6 @@ open Microsoft.Extensions.Logging
|
||||
open WoofWare.DotnetRuntimeLocator
|
||||
|
||||
module Program =
|
||||
/// Returns the pointer to the resulting array on the heap.
|
||||
let allocateArgs (args : string list) (state : IlMachineState) : ManagedHeapAddress * IlMachineState =
|
||||
let argsAllocations, state =
|
||||
(state, args)
|
||||
||> Seq.mapFold (fun state arg -> IlMachineState.allocate (ReferenceType.String arg) state
|
||||
// TODO: set the char values in memory
|
||||
)
|
||||
|
||||
let arrayAllocation, state =
|
||||
IlMachineState.allocate
|
||||
(ReferenceType.Array (args.Length, Type.ReferenceType ReferenceType.ManagedObject))
|
||||
state
|
||||
// TODO: set the length of the array
|
||||
|
||||
let state =
|
||||
((state, 0), argsAllocations)
|
||||
||> Seq.fold (fun (state, i) arg ->
|
||||
let state =
|
||||
IlMachineState.setArrayValue arrayAllocation (CliObject.OfManagedObject arg) i state
|
||||
|
||||
state, i + 1
|
||||
)
|
||||
|> fst
|
||||
|
||||
arrayAllocation, state
|
||||
|
||||
let reallyMain (argv : string[]) : int =
|
||||
let loggerFactory =
|
||||
LoggerFactory.Create (fun builder ->
|
||||
@@ -54,54 +28,8 @@ module Program =
|
||||
ImmutableArray.Create (FileInfo(dllPath).Directory.FullName)
|
||||
|
||||
use fileStream = new FileStream (dllPath, FileMode.Open, FileAccess.Read)
|
||||
let dumped = Assembly.read loggerFactory fileStream
|
||||
|
||||
let entryPoint =
|
||||
match dumped.MainMethod with
|
||||
| None -> failwith $"No entry point in {dllPath}"
|
||||
| Some d -> d
|
||||
|
||||
let mainMethod = dumped.Methods.[entryPoint]
|
||||
|
||||
if mainMethod.Signature.GenericParameterCount > 0 then
|
||||
failwith "Refusing to execute generic main method"
|
||||
|
||||
let state = IlMachineState.initial dotnetRuntimes dumped
|
||||
|
||||
let arrayAllocation, state =
|
||||
match mainMethod.Signature.ParameterTypes |> Seq.toList with
|
||||
| [ TypeDefn.OneDimensionalArrayLowerBoundZero (TypeDefn.PrimitiveType PrimitiveType.String) ] ->
|
||||
allocateArgs args state
|
||||
| _ -> failwith "Main method must take an array of strings; other signatures not yet implemented"
|
||||
|
||||
match mainMethod.Signature.ReturnType with
|
||||
| TypeDefn.PrimitiveType PrimitiveType.Int32 -> ()
|
||||
| _ -> failwith "Main method must return int32; other types not currently supported"
|
||||
|
||||
let state, mainThread =
|
||||
state
|
||||
|> IlMachineState.addThread
|
||||
// TODO: we need to load the main method's class first, and that's a faff with the current layout
|
||||
{ MethodState.Empty mainMethod None with
|
||||
Arguments = ImmutableArray.Create (CliObject.OfManagedObject arrayAllocation)
|
||||
}
|
||||
dumped.Name
|
||||
|
||||
let mutable state = state
|
||||
|
||||
while true do
|
||||
let state', whatWeDid =
|
||||
AbstractMachine.executeOneStep loggerFactory state mainThread
|
||||
|
||||
state <- state'
|
||||
|
||||
match whatWeDid with
|
||||
| WhatWeDid.Executed -> logger.LogInformation "Executed one step."
|
||||
| WhatWeDid.SuspendedForClassInit ->
|
||||
logger.LogInformation "Suspended execution of current method for class initialisation."
|
||||
| WhatWeDid.NotTellingYou -> logger.LogInformation "(Execution outcome missing.)"
|
||||
| WhatWeDid.BlockedOnClassInit threadBlockingUs ->
|
||||
logger.LogInformation "Unable to execute because class has not yet initialised."
|
||||
let terminalState = Program.run loggerFactory fileStream dotnetRuntimes args
|
||||
|
||||
0
|
||||
| _ ->
|
||||
|
21
WoofWare.PawPrint.Test/Assembly.fs
Normal file
21
WoofWare.PawPrint.Test/Assembly.fs
Normal file
@@ -0,0 +1,21 @@
|
||||
namespace WoofWare.PawPrint.Test
|
||||
|
||||
open System
|
||||
open System.IO
|
||||
open System.Reflection
|
||||
|
||||
[<RequireQualifiedAccess>]
|
||||
module Assembly =
|
||||
|
||||
let getEmbeddedResource (name : string) (assy : Assembly) : Stream =
|
||||
let resourceName =
|
||||
assy.GetManifestResourceNames ()
|
||||
|> Seq.filter (fun a -> a.EndsWith (name, StringComparison.Ordinal))
|
||||
|> Seq.exactlyOne
|
||||
|
||||
assy.GetManifestResourceStream resourceName
|
||||
|
||||
let getEmbeddedResourceAsString (name : string) (assy : Assembly) : string =
|
||||
use stream = getEmbeddedResource name assy
|
||||
use reader = new StreamReader (stream, leaveOpen = true)
|
||||
reader.ReadToEnd ()
|
64
WoofWare.PawPrint.Test/LoggerFactory.fs
Normal file
64
WoofWare.PawPrint.Test/LoggerFactory.fs
Normal file
@@ -0,0 +1,64 @@
|
||||
namespace WoofWare.PawPrint.Test
|
||||
|
||||
open System
|
||||
open Microsoft.Extensions.Logging
|
||||
|
||||
type LogLine =
|
||||
{
|
||||
Level : LogLevel
|
||||
LoggerName : string
|
||||
Message : string
|
||||
}
|
||||
|
||||
/// Very small, in-memory implementation of `ILoggerFactory` for unit tests.
|
||||
[<RequireQualifiedAccess>]
|
||||
module LoggerFactory =
|
||||
|
||||
/// Returns a pair: `(getLogs, loggerFactory)` where `getLogs ()` retrieves all
|
||||
/// log messages emitted so far, in chronological order.
|
||||
let makeTest () : (unit -> LogLine list) * ILoggerFactory =
|
||||
// Shared sink for all loggers created by the factory.
|
||||
let sink = ResizeArray ()
|
||||
|
||||
let createLogger (category : string) : ILogger =
|
||||
{ new ILogger with
|
||||
member _.BeginScope _state =
|
||||
{ new IDisposable with
|
||||
member _.Dispose () = ()
|
||||
}
|
||||
|
||||
member _.IsEnabled _logLevel = true
|
||||
|
||||
member _.Log (logLevel, eventId, state, ex, formatter) =
|
||||
let message =
|
||||
try
|
||||
formatter.Invoke (state, ex)
|
||||
with _ ->
|
||||
"<formatter threw>"
|
||||
|
||||
lock
|
||||
sink
|
||||
(fun () ->
|
||||
{
|
||||
Level = logLevel
|
||||
LoggerName = category
|
||||
Message = message
|
||||
}
|
||||
|> sink.Add
|
||||
)
|
||||
}
|
||||
|
||||
// Minimal `ILoggerFactory` that just hands out instances of the above.
|
||||
let factory =
|
||||
{ new ILoggerFactory with
|
||||
member _.CreateLogger categoryName = createLogger categoryName
|
||||
|
||||
member _.AddProvider _provider = ()
|
||||
|
||||
member _.Dispose () = ()
|
||||
}
|
||||
|
||||
// Expose accessor that snapshots the current sink contents.
|
||||
let getLogs () = lock sink (fun () -> List.ofSeq sink)
|
||||
|
||||
getLogs, factory
|
58
WoofWare.PawPrint.Test/Roslyn.fs
Normal file
58
WoofWare.PawPrint.Test/Roslyn.fs
Normal file
@@ -0,0 +1,58 @@
|
||||
namespace WoofWare.PawPrint.Test
|
||||
|
||||
open System
|
||||
open System.IO
|
||||
open Microsoft.CodeAnalysis
|
||||
open Microsoft.CodeAnalysis.CSharp
|
||||
|
||||
[<RequireQualifiedAccess>]
|
||||
module Roslyn =
|
||||
|
||||
/// Compiles the supplied C# source strings into an in-memory PE image.
|
||||
/// Raises if compilation fails.
|
||||
let compile (sources : string list) : byte[] =
|
||||
// Create a syntax tree per source snippet.
|
||||
let parseOptions =
|
||||
CSharpParseOptions.Default.WithLanguageVersion (LanguageVersion.Preview)
|
||||
|
||||
let syntaxTrees : SyntaxTree[] =
|
||||
sources
|
||||
|> List.mapi (fun idx src ->
|
||||
let fileName = $"File{idx}.cs"
|
||||
CSharpSyntaxTree.ParseText (src, parseOptions, fileName)
|
||||
)
|
||||
|> List.toArray
|
||||
|
||||
// Reference every assembly found in the runtime directory – crude but
|
||||
// guarantees we can resolve System.* et al.
|
||||
let runtimeDir =
|
||||
System.Runtime.InteropServices.RuntimeEnvironment.GetRuntimeDirectory ()
|
||||
|
||||
let metadataReferences : MetadataReference[] =
|
||||
Directory.GetFiles (runtimeDir, "*.dll")
|
||||
|> Array.map (fun path -> MetadataReference.CreateFromFile path :> MetadataReference)
|
||||
|
||||
let compilationOptions = CSharpCompilationOptions (OutputKind.ConsoleApplication)
|
||||
|
||||
let compilation =
|
||||
CSharpCompilation.Create (
|
||||
assemblyName = "PawPrintTestAssembly",
|
||||
syntaxTrees = syntaxTrees,
|
||||
references = metadataReferences,
|
||||
options = compilationOptions
|
||||
)
|
||||
|
||||
use peStream = new MemoryStream ()
|
||||
|
||||
let emitResult = compilation.Emit (peStream)
|
||||
|
||||
if emitResult.Success then
|
||||
peStream.ToArray ()
|
||||
else
|
||||
let diagnostics =
|
||||
emitResult.Diagnostics
|
||||
|> Seq.filter (fun d -> d.Severity = DiagnosticSeverity.Error)
|
||||
|> Seq.map (fun d -> d.ToString ())
|
||||
|> String.concat Environment.NewLine
|
||||
|
||||
failwith $"Compilation failed:\n{diagnostics}"
|
17
WoofWare.PawPrint.Test/TestHarness.fs
Normal file
17
WoofWare.PawPrint.Test/TestHarness.fs
Normal file
@@ -0,0 +1,17 @@
|
||||
namespace WoofWare.PawPrint.Test
|
||||
|
||||
open WoofWare.PawPrint
|
||||
|
||||
/// Result of executing (some steps of) the program under PawPrint.
|
||||
type RunResult =
|
||||
{
|
||||
/// Value that was left on the evaluation stack when execution stopped, **if**
|
||||
/// the program executed a `ret` that produced a value and PawPrint
|
||||
/// subsequently pushed it onto the stack. This is only an early-stage
|
||||
/// approximation: once PawPrint supports a proper process-exit story we
|
||||
/// can promote this to a real exit–code.
|
||||
ExitCode : int option
|
||||
|
||||
/// Final interpreter state after we stopped executing.
|
||||
FinalState : IlMachineState
|
||||
}
|
@@ -1,9 +1,40 @@
|
||||
namespace WoofWare.Pawprint.Test
|
||||
|
||||
open System.Collections.Immutable
|
||||
open System.IO
|
||||
open FsUnitTyped
|
||||
open NUnit.Framework
|
||||
open WoofWare.PawPrint
|
||||
open WoofWare.PawPrint.Test
|
||||
|
||||
[<TestFixture>]
|
||||
module TestThing =
|
||||
let assy = typeof<RunResult>.Assembly
|
||||
|
||||
[<Test>]
|
||||
let ``foo is a thing`` () = 1 |> shouldEqual 1
|
||||
let ``Can run a no-op`` () : unit =
|
||||
let source = Assembly.getEmbeddedResourceAsString "NoOp.cs" assy
|
||||
let image = Roslyn.compile [ source ]
|
||||
let messages, loggerFactory = LoggerFactory.makeTest ()
|
||||
|
||||
let dotnetRuntimes =
|
||||
// TODO: work out which runtime it expects to use, parsing the runtimeconfig etc and using DotnetRuntimeLocator. For now we assume we're self-contained.
|
||||
// DotnetEnvironmentInfo.Get().Frameworks
|
||||
// |> Seq.map (fun fi -> Path.Combine (fi.Path, fi.Version.ToString ()))
|
||||
// |> ImmutableArray.CreateRange
|
||||
ImmutableArray.Create (FileInfo(assy.Location).Directory.FullName)
|
||||
|
||||
use peImage = new MemoryStream (image)
|
||||
|
||||
let terminalState, terminatingThread =
|
||||
Program.run loggerFactory peImage (ImmutableArray.CreateRange []) []
|
||||
|
||||
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 1
|
||||
|
@@ -10,7 +10,12 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Compile Include="LoggerFactory.fs" />
|
||||
<Compile Include="Assembly.fs" />
|
||||
<Compile Include="Roslyn.fs" />
|
||||
<Compile Include="TestHarness.fs"/>
|
||||
<Compile Include="TestThing.fs"/>
|
||||
<EmbeddedResource Include="sources\NoOp.cs" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
@@ -22,6 +27,8 @@
|
||||
<PackageReference Include="NUnit3TestAdapter" Version="5.0.0"/>
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.13.0"/>
|
||||
<PackageReference Include="NUnit" Version="4.3.2"/>
|
||||
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="4.8.0"/>
|
||||
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="9.0.2" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
12
WoofWare.PawPrint.Test/sources/NoOp.cs
Normal file
12
WoofWare.PawPrint.Test/sources/NoOp.cs
Normal file
@@ -0,0 +1,12 @@
|
||||
using System;
|
||||
|
||||
namespace HelloWorldApp
|
||||
{
|
||||
class Program
|
||||
{
|
||||
static int Main(string[] args)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
}
|
@@ -88,6 +88,17 @@ type EvalStack =
|
||||
Values = []
|
||||
}
|
||||
|
||||
static member Pop (stack : EvalStack) : EvalStackValue * EvalStack =
|
||||
match stack.Values with
|
||||
| [] -> failwith "eval stack was empty on pop instruction"
|
||||
| v :: rest ->
|
||||
let stack =
|
||||
{
|
||||
Values = rest
|
||||
}
|
||||
|
||||
v, stack
|
||||
|
||||
static member Push (v : CliObject) (stack : EvalStack) =
|
||||
let v =
|
||||
match v with
|
||||
@@ -134,25 +145,110 @@ and MethodState =
|
||||
ReturnState : MethodReturnState option
|
||||
}
|
||||
|
||||
static member advanceProgramCounter (state : MethodState) =
|
||||
static member jumpProgramCounter (bytes : int) (state : MethodState) =
|
||||
{ state with
|
||||
IlOpIndex =
|
||||
state.IlOpIndex
|
||||
+ (IlOp.NumberOfBytes state.ExecutingMethod.Locations.[state.IlOpIndex])
|
||||
IlOpIndex = state.IlOpIndex + bytes
|
||||
}
|
||||
|
||||
static member advanceProgramCounter (state : MethodState) =
|
||||
MethodState.jumpProgramCounter (IlOp.NumberOfBytes state.ExecutingMethod.Locations.[state.IlOpIndex]) state
|
||||
|
||||
static member loadArgument (index : int) (state : MethodState) : MethodState =
|
||||
// Correct CIL guarantees that we are loading an argument from an index that exists.
|
||||
{ state with
|
||||
EvaluationStack = state.EvaluationStack |> EvalStack.Push state.Arguments.[index]
|
||||
}
|
||||
|
||||
static member popFromStack (localVariableIndex : int) (state : MethodState) : MethodState =
|
||||
if localVariableIndex >= state.LocalVariables.Length then
|
||||
failwith
|
||||
$"Tried to access zero-indexed local variable %i{localVariableIndex} but only %i{state.LocalVariables.Length} exist"
|
||||
|
||||
if localVariableIndex < 0 || localVariableIndex >= 65535 then
|
||||
failwith $"Incorrect CIL encountered: local variable index has value %i{localVariableIndex}"
|
||||
|
||||
let popped, newStack = EvalStack.Pop state.EvaluationStack
|
||||
|
||||
let desiredValue =
|
||||
match state.LocalVariables.[localVariableIndex] with
|
||||
| Basic (BasicCliObject.Int32 _) ->
|
||||
match popped with
|
||||
| EvalStackValue.Int32 i -> CliObject.Basic (BasicCliObject.Int32 i)
|
||||
| EvalStackValue.Int64 int64 -> failwith "todo"
|
||||
| EvalStackValue.NativeInt int64 -> failwith "todo"
|
||||
| EvalStackValue.Float f -> failwith "todo"
|
||||
| EvalStackValue.ManagedPointer managedHeapAddressOption -> failwith "todo"
|
||||
| EvalStackValue.ObjectRef managedHeapAddress -> failwith "todo"
|
||||
| EvalStackValue.TransientPointer i -> failwith "todo"
|
||||
| EvalStackValue.UserDefinedValueType -> failwith "todo"
|
||||
| Basic (BasicCliObject.Int64 _) -> failwith "todo"
|
||||
| Basic (BasicCliObject.NativeFloat _) -> failwith "todo"
|
||||
| Basic (BasicCliObject.NativeInt _) -> failwith "todo"
|
||||
| Basic (BasicCliObject.ObjectReference _) -> failwith "todo"
|
||||
| Basic (BasicCliObject.PointerType _) -> failwith "todo"
|
||||
| Bool b -> failwith "todo"
|
||||
| Char (b, b1) -> failwith "todo"
|
||||
| UInt8 b -> failwith "todo"
|
||||
| UInt16 s -> failwith "todo"
|
||||
| Int8 b -> failwith "todo"
|
||||
| Int16 s -> failwith "todo"
|
||||
| Float32 f -> failwith "todo"
|
||||
| Float64 f -> failwith "todo"
|
||||
|
||||
{ state with
|
||||
EvaluationStack = newStack
|
||||
LocalVariables = state.LocalVariables.SetItem (localVariableIndex, desiredValue)
|
||||
}
|
||||
|
||||
static member Empty (method : WoofWare.PawPrint.MethodInfo) (returnState : MethodReturnState option) =
|
||||
let localVariableSig =
|
||||
match method.LocalVars with
|
||||
| None -> ImmutableArray.Empty
|
||||
| Some vars -> vars
|
||||
// I think valid code should remain valid if we unconditionally localsInit - it should be undefined
|
||||
// to use an uninitialised value? Not checked this; TODO.
|
||||
let localVars =
|
||||
localVariableSig
|
||||
|> Seq.map (fun var ->
|
||||
match var with
|
||||
| TypeDefn.PrimitiveType primitiveType ->
|
||||
match primitiveType with
|
||||
| PrimitiveType.Void -> failwith "todo"
|
||||
| PrimitiveType.Boolean -> CliObject.Bool 0uy
|
||||
| PrimitiveType.Char -> failwith "todo"
|
||||
| PrimitiveType.SByte -> failwith "todo"
|
||||
| PrimitiveType.Byte -> failwith "todo"
|
||||
| PrimitiveType.Int16 -> failwith "todo"
|
||||
| PrimitiveType.UInt16 -> failwith "todo"
|
||||
| PrimitiveType.Int32 -> CliObject.Basic (BasicCliObject.Int32 0)
|
||||
| PrimitiveType.UInt32 -> failwith "todo"
|
||||
| PrimitiveType.Int64 -> CliObject.Basic (BasicCliObject.Int64 0L)
|
||||
| PrimitiveType.UInt64 -> failwith "todo"
|
||||
| PrimitiveType.Single -> failwith "todo"
|
||||
| PrimitiveType.Double -> failwith "todo"
|
||||
| PrimitiveType.String -> failwith "todo"
|
||||
| PrimitiveType.TypedReference -> failwith "todo"
|
||||
| PrimitiveType.IntPtr -> failwith "todo"
|
||||
| PrimitiveType.UIntPtr -> failwith "todo"
|
||||
| PrimitiveType.Object -> failwith "todo"
|
||||
| TypeDefn.Array (elt, shape) -> failwith "todo"
|
||||
| TypeDefn.Pinned typeDefn -> failwith "todo"
|
||||
| TypeDefn.Pointer typeDefn -> failwith "todo"
|
||||
| TypeDefn.Byref typeDefn -> failwith "todo"
|
||||
| TypeDefn.OneDimensionalArrayLowerBoundZero elements -> failwith "todo"
|
||||
| TypeDefn.Modified (original, afterMod, modificationRequired) -> failwith "todo"
|
||||
| TypeDefn.FromReference signatureTypeKind -> CliObject.Basic (BasicCliObject.ObjectReference None)
|
||||
| TypeDefn.FromDefinition signatureTypeKind -> failwith "todo"
|
||||
| TypeDefn.GenericInstantiation (generic, args) -> failwith "todo"
|
||||
| TypeDefn.FunctionPointer typeMethodSignature -> failwith "todo"
|
||||
| TypeDefn.GenericTypeParameter index -> failwith "todo"
|
||||
| TypeDefn.GenericMethodParameter index -> failwith "todo"
|
||||
)
|
||||
|> ImmutableArray.CreateRange
|
||||
|
||||
{
|
||||
EvaluationStack = EvalStack.Empty
|
||||
LocalVariables =
|
||||
// TODO: use method.LocalsInit
|
||||
ImmutableArray.Empty
|
||||
LocalVariables = localVars
|
||||
IlOpIndex = 0
|
||||
Arguments = Array.zeroCreate method.Parameters.Length |> ImmutableArray.ToImmutableArray
|
||||
ExecutingMethod = method
|
||||
@@ -749,6 +845,30 @@ module IlMachineState =
|
||||
)
|
||||
}
|
||||
|
||||
let popFromStackToLocalVariable
|
||||
(thread : ThreadId)
|
||||
(localVariableIndex : int)
|
||||
(state : IlMachineState)
|
||||
: IlMachineState
|
||||
=
|
||||
let threadState =
|
||||
match Map.tryFind thread state.ThreadState with
|
||||
| None -> failwith "Logic error: tried to pop from stack of nonexistent thread"
|
||||
| Some threadState -> threadState
|
||||
|
||||
let methodState =
|
||||
MethodState.popFromStack localVariableIndex threadState.MethodState
|
||||
|
||||
{ state with
|
||||
ThreadState =
|
||||
state.ThreadState
|
||||
|> Map.add
|
||||
thread
|
||||
{ threadState with
|
||||
MethodState = methodState
|
||||
}
|
||||
}
|
||||
|
||||
let setArrayValue
|
||||
(arrayAllocation : ManagedHeapAddress)
|
||||
(v : CliObject)
|
||||
@@ -780,6 +900,23 @@ module IlMachineState =
|
||||
)
|
||||
}
|
||||
|
||||
let jumpProgramCounter (thread : ThreadId) (bytes : int) (state : IlMachineState) : IlMachineState =
|
||||
{ state with
|
||||
ThreadState =
|
||||
state.ThreadState
|
||||
|> Map.change
|
||||
thread
|
||||
(fun state ->
|
||||
match state with
|
||||
| None -> failwith "expected state"
|
||||
| Some (state : ThreadState) ->
|
||||
{ state with
|
||||
MethodState = state.MethodState |> MethodState.jumpProgramCounter bytes
|
||||
}
|
||||
|> Some
|
||||
)
|
||||
}
|
||||
|
||||
let loadArgument (thread : ThreadId) (index : int) (state : IlMachineState) : IlMachineState =
|
||||
{ state with
|
||||
ThreadState =
|
||||
@@ -797,6 +934,10 @@ module IlMachineState =
|
||||
)
|
||||
}
|
||||
|
||||
type ExecutionResult =
|
||||
| Terminated of IlMachineState * terminatingThread : ThreadId
|
||||
| Stepped of IlMachineState * WhatWeDid
|
||||
|
||||
[<RequireQualifiedAccess>]
|
||||
module AbstractMachine =
|
||||
type private Dummy = class end
|
||||
@@ -805,43 +946,76 @@ module AbstractMachine =
|
||||
(state : IlMachineState)
|
||||
(currentThread : ThreadId)
|
||||
(op : NullaryIlOp)
|
||||
: IlMachineState * WhatWeDid
|
||||
: ExecutionResult
|
||||
=
|
||||
match op with
|
||||
| Nop -> IlMachineState.advanceProgramCounter currentThread state, WhatWeDid.Executed
|
||||
| Nop ->
|
||||
(IlMachineState.advanceProgramCounter currentThread state, WhatWeDid.Executed)
|
||||
|> ExecutionResult.Stepped
|
||||
| LdArg0 ->
|
||||
state
|
||||
|> IlMachineState.loadArgument currentThread 0
|
||||
|> IlMachineState.advanceProgramCounter currentThread
|
||||
|> Tuple.withRight WhatWeDid.Executed
|
||||
|> ExecutionResult.Stepped
|
||||
| LdArg1 ->
|
||||
state
|
||||
|> IlMachineState.loadArgument currentThread 1
|
||||
|> IlMachineState.advanceProgramCounter currentThread
|
||||
|> Tuple.withRight WhatWeDid.Executed
|
||||
|> ExecutionResult.Stepped
|
||||
| LdArg2 ->
|
||||
state
|
||||
|> IlMachineState.loadArgument currentThread 2
|
||||
|> IlMachineState.advanceProgramCounter currentThread
|
||||
|> Tuple.withRight WhatWeDid.Executed
|
||||
|> ExecutionResult.Stepped
|
||||
| LdArg3 ->
|
||||
state
|
||||
|> IlMachineState.loadArgument currentThread 3
|
||||
|> IlMachineState.advanceProgramCounter currentThread
|
||||
|> Tuple.withRight WhatWeDid.Executed
|
||||
| Ldloc_0 -> failwith "todo"
|
||||
| Ldloc_1 -> failwith "todo"
|
||||
| Ldloc_2 -> failwith "todo"
|
||||
| Ldloc_3 -> failwith "todo"
|
||||
|> ExecutionResult.Stepped
|
||||
| Ldloc_0 ->
|
||||
let localVar = state.ThreadState.[currentThread].MethodState.LocalVariables.[0]
|
||||
|
||||
state
|
||||
|> IlMachineState.pushToEvalStack localVar currentThread
|
||||
|> IlMachineState.advanceProgramCounter currentThread
|
||||
|> Tuple.withRight WhatWeDid.Executed
|
||||
|> ExecutionResult.Stepped
|
||||
| Ldloc_1 ->
|
||||
let localVar = state.ThreadState.[currentThread].MethodState.LocalVariables.[1]
|
||||
|
||||
state
|
||||
|> IlMachineState.pushToEvalStack localVar currentThread
|
||||
|> IlMachineState.advanceProgramCounter currentThread
|
||||
|> Tuple.withRight WhatWeDid.Executed
|
||||
|> ExecutionResult.Stepped
|
||||
| Ldloc_2 ->
|
||||
let localVar = state.ThreadState.[currentThread].MethodState.LocalVariables.[2]
|
||||
|
||||
state
|
||||
|> IlMachineState.pushToEvalStack localVar currentThread
|
||||
|> IlMachineState.advanceProgramCounter currentThread
|
||||
|> Tuple.withRight WhatWeDid.Executed
|
||||
|> ExecutionResult.Stepped
|
||||
| Ldloc_3 ->
|
||||
let localVar = state.ThreadState.[currentThread].MethodState.LocalVariables.[3]
|
||||
|
||||
state
|
||||
|> IlMachineState.pushToEvalStack localVar currentThread
|
||||
|> IlMachineState.advanceProgramCounter currentThread
|
||||
|> Tuple.withRight WhatWeDid.Executed
|
||||
|> ExecutionResult.Stepped
|
||||
| Pop -> failwith "todo"
|
||||
| Dup -> failwith "todo"
|
||||
| Ret ->
|
||||
let threadStateAtEndOfMethod = state.ThreadState.[currentThread]
|
||||
|
||||
let returnState =
|
||||
match threadStateAtEndOfMethod.MethodState.ReturnState with
|
||||
| None -> failwith "Program finished execution?"
|
||||
| Some returnState -> returnState
|
||||
match threadStateAtEndOfMethod.MethodState.ReturnState with
|
||||
| None -> ExecutionResult.Terminated (state, currentThread)
|
||||
| Some returnState ->
|
||||
|
||||
let state =
|
||||
match returnState.WasInitialising with
|
||||
@@ -868,7 +1042,7 @@ module AbstractMachine =
|
||||
match threadStateAtEndOfMethod.MethodState.EvaluationStack.Values with
|
||||
| [] ->
|
||||
// no return value
|
||||
state, WhatWeDid.Executed
|
||||
(state, WhatWeDid.Executed) |> ExecutionResult.Stepped
|
||||
| [ retVal ] ->
|
||||
let retType =
|
||||
threadStateAtEndOfMethod.MethodState.ExecutingMethod.Signature.ReturnType
|
||||
@@ -876,19 +1050,65 @@ module AbstractMachine =
|
||||
state
|
||||
|> IlMachineState.pushToStackCoerced retVal retType currentThread
|
||||
|> Tuple.withRight WhatWeDid.Executed
|
||||
|> ExecutionResult.Stepped
|
||||
| vals ->
|
||||
failwith
|
||||
"Unexpected interpretation result has a local evaluation stack with more than one element on RET"
|
||||
|
||||
| LdcI4_0 -> failwith "todo"
|
||||
| LdcI4_1 -> failwith "todo"
|
||||
| LdcI4_2 -> failwith "todo"
|
||||
| LdcI4_3 -> failwith "todo"
|
||||
| LdcI4_4 -> failwith "todo"
|
||||
| LdcI4_5 -> failwith "todo"
|
||||
| LdcI4_6 -> failwith "todo"
|
||||
| LdcI4_7 -> failwith "todo"
|
||||
| LdcI4_8 -> failwith "todo"
|
||||
| LdcI4_0 ->
|
||||
state
|
||||
|> IlMachineState.pushToEvalStack (CliObject.Basic (BasicCliObject.Int32 0)) currentThread
|
||||
|> IlMachineState.advanceProgramCounter currentThread
|
||||
|> Tuple.withRight WhatWeDid.Executed
|
||||
|> ExecutionResult.Stepped
|
||||
| LdcI4_1 ->
|
||||
state
|
||||
|> IlMachineState.pushToEvalStack (CliObject.Basic (BasicCliObject.Int32 1)) currentThread
|
||||
|> IlMachineState.advanceProgramCounter currentThread
|
||||
|> Tuple.withRight WhatWeDid.Executed
|
||||
|> ExecutionResult.Stepped
|
||||
| LdcI4_2 ->
|
||||
state
|
||||
|> IlMachineState.pushToEvalStack (CliObject.Basic (BasicCliObject.Int32 2)) currentThread
|
||||
|> IlMachineState.advanceProgramCounter currentThread
|
||||
|> Tuple.withRight WhatWeDid.Executed
|
||||
|> ExecutionResult.Stepped
|
||||
| LdcI4_3 ->
|
||||
state
|
||||
|> IlMachineState.pushToEvalStack (CliObject.Basic (BasicCliObject.Int32 3)) currentThread
|
||||
|> IlMachineState.advanceProgramCounter currentThread
|
||||
|> Tuple.withRight WhatWeDid.Executed
|
||||
|> ExecutionResult.Stepped
|
||||
| LdcI4_4 ->
|
||||
state
|
||||
|> IlMachineState.pushToEvalStack (CliObject.Basic (BasicCliObject.Int32 4)) currentThread
|
||||
|> IlMachineState.advanceProgramCounter currentThread
|
||||
|> Tuple.withRight WhatWeDid.Executed
|
||||
|> ExecutionResult.Stepped
|
||||
| LdcI4_5 ->
|
||||
state
|
||||
|> IlMachineState.pushToEvalStack (CliObject.Basic (BasicCliObject.Int32 5)) currentThread
|
||||
|> IlMachineState.advanceProgramCounter currentThread
|
||||
|> Tuple.withRight WhatWeDid.Executed
|
||||
|> ExecutionResult.Stepped
|
||||
| LdcI4_6 ->
|
||||
state
|
||||
|> IlMachineState.pushToEvalStack (CliObject.Basic (BasicCliObject.Int32 6)) currentThread
|
||||
|> IlMachineState.advanceProgramCounter currentThread
|
||||
|> Tuple.withRight WhatWeDid.Executed
|
||||
|> ExecutionResult.Stepped
|
||||
| LdcI4_7 ->
|
||||
state
|
||||
|> IlMachineState.pushToEvalStack (CliObject.Basic (BasicCliObject.Int32 7)) currentThread
|
||||
|> IlMachineState.advanceProgramCounter currentThread
|
||||
|> Tuple.withRight WhatWeDid.Executed
|
||||
|> ExecutionResult.Stepped
|
||||
| LdcI4_8 ->
|
||||
state
|
||||
|> IlMachineState.pushToEvalStack (CliObject.Basic (BasicCliObject.Int32 8)) currentThread
|
||||
|> IlMachineState.advanceProgramCounter currentThread
|
||||
|> Tuple.withRight WhatWeDid.Executed
|
||||
|> ExecutionResult.Stepped
|
||||
| LdcI4_m1 -> failwith "todo"
|
||||
| LdNull -> failwith "todo"
|
||||
| Ceq -> failwith "todo"
|
||||
@@ -896,10 +1116,30 @@ module AbstractMachine =
|
||||
| Cgt_un -> failwith "todo"
|
||||
| Clt -> failwith "todo"
|
||||
| Clt_un -> failwith "todo"
|
||||
| Stloc_0 -> failwith "todo"
|
||||
| Stloc_1 -> failwith "todo"
|
||||
| Stloc_2 -> failwith "todo"
|
||||
| Stloc_3 -> failwith "todo"
|
||||
| Stloc_0 ->
|
||||
state
|
||||
|> IlMachineState.popFromStackToLocalVariable currentThread 0
|
||||
|> IlMachineState.advanceProgramCounter currentThread
|
||||
|> Tuple.withRight WhatWeDid.Executed
|
||||
|> ExecutionResult.Stepped
|
||||
| Stloc_1 ->
|
||||
state
|
||||
|> IlMachineState.popFromStackToLocalVariable currentThread 1
|
||||
|> IlMachineState.advanceProgramCounter currentThread
|
||||
|> Tuple.withRight WhatWeDid.Executed
|
||||
|> ExecutionResult.Stepped
|
||||
| Stloc_2 ->
|
||||
state
|
||||
|> IlMachineState.popFromStackToLocalVariable currentThread 2
|
||||
|> IlMachineState.advanceProgramCounter currentThread
|
||||
|> Tuple.withRight WhatWeDid.Executed
|
||||
|> ExecutionResult.Stepped
|
||||
| Stloc_3 ->
|
||||
state
|
||||
|> IlMachineState.popFromStackToLocalVariable currentThread 3
|
||||
|> IlMachineState.advanceProgramCounter currentThread
|
||||
|> Tuple.withRight WhatWeDid.Executed
|
||||
|> ExecutionResult.Stepped
|
||||
| Sub -> failwith "todo"
|
||||
| Sub_ovf -> failwith "todo"
|
||||
| Sub_ovf_un -> failwith "todo"
|
||||
@@ -1197,12 +1437,73 @@ module AbstractMachine =
|
||||
|> IlMachineState.advanceProgramCounter thread
|
||||
|> Tuple.withRight WhatWeDid.Executed
|
||||
|
||||
let executeOneStep
|
||||
(loggerFactory : ILoggerFactory)
|
||||
let private executeUnaryConst
|
||||
(state : IlMachineState)
|
||||
(thread : ThreadId)
|
||||
(currentThread : ThreadId)
|
||||
(op : UnaryConstIlOp)
|
||||
: IlMachineState * WhatWeDid
|
||||
=
|
||||
match op with
|
||||
| Stloc s ->
|
||||
state
|
||||
|> IlMachineState.popFromStackToLocalVariable currentThread (int s)
|
||||
|> IlMachineState.advanceProgramCounter currentThread
|
||||
|> Tuple.withRight WhatWeDid.Executed
|
||||
| Stloc_s b ->
|
||||
state
|
||||
|> IlMachineState.popFromStackToLocalVariable currentThread (int b)
|
||||
|> IlMachineState.advanceProgramCounter currentThread
|
||||
|> Tuple.withRight WhatWeDid.Executed
|
||||
| Ldc_I8 int64 -> failwith "todo"
|
||||
| Ldc_I4 i -> failwith "todo"
|
||||
| Ldc_R4 f -> failwith "todo"
|
||||
| Ldc_R8 f -> failwith "todo"
|
||||
| Ldc_I4_s b -> failwith "todo"
|
||||
| Br i -> failwith "todo"
|
||||
| Br_s b ->
|
||||
state
|
||||
|> IlMachineState.advanceProgramCounter currentThread
|
||||
|> 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"
|
||||
| Beq_s b -> failwith "todo"
|
||||
| Blt_s b -> failwith "todo"
|
||||
| Ble_s b -> failwith "todo"
|
||||
| Bgt_s b -> failwith "todo"
|
||||
| Bge_s b -> failwith "todo"
|
||||
| Beq i -> failwith "todo"
|
||||
| Blt i -> failwith "todo"
|
||||
| Ble i -> failwith "todo"
|
||||
| Bgt i -> failwith "todo"
|
||||
| Bge i -> failwith "todo"
|
||||
| Bne_un_s b -> failwith "todo"
|
||||
| Bge_un_s b -> failwith "todo"
|
||||
| Bgt_un_s b -> failwith "todo"
|
||||
| Ble_un_s b -> failwith "todo"
|
||||
| Blt_un_s b -> failwith "todo"
|
||||
| Bne_un i -> failwith "todo"
|
||||
| Bge_un i -> failwith "todo"
|
||||
| Bgt_un i -> failwith "todo"
|
||||
| Ble_un i -> failwith "todo"
|
||||
| Blt_un i -> failwith "todo"
|
||||
| Ldloc_s b -> failwith "todo"
|
||||
| Ldloca_s b -> failwith "todo"
|
||||
| Ldarga s -> failwith "todo"
|
||||
| Ldarg_s b -> failwith "todo"
|
||||
| Ldarga_s b -> failwith "todo"
|
||||
| Leave i -> failwith "todo"
|
||||
| Leave_s b -> failwith "todo"
|
||||
| Starg_s b -> failwith "todo"
|
||||
| Starg s -> failwith "todo"
|
||||
| Unaligned b -> failwith "todo"
|
||||
| Ldloc s -> failwith "todo"
|
||||
| Ldloca s -> failwith "todo"
|
||||
| Ldarg s -> failwith "todo"
|
||||
|
||||
let executeOneStep (loggerFactory : ILoggerFactory) (state : IlMachineState) (thread : ThreadId) : ExecutionResult =
|
||||
let logger = loggerFactory.CreateLogger typeof<Dummy>.DeclaringType
|
||||
let instruction = state.ThreadState.[thread].MethodState
|
||||
|
||||
@@ -1219,9 +1520,11 @@ module AbstractMachine =
|
||||
|
||||
match instruction.ExecutingMethod.Locations.[instruction.IlOpIndex] with
|
||||
| IlOp.Nullary op -> executeNullary state thread op
|
||||
| UnaryConst unaryConstIlOp -> failwith "todo"
|
||||
| UnaryMetadataToken (unaryMetadataTokenIlOp, bytes) ->
|
||||
| IlOp.UnaryConst unaryConstIlOp -> executeUnaryConst state thread unaryConstIlOp |> ExecutionResult.Stepped
|
||||
| IlOp.UnaryMetadataToken (unaryMetadataTokenIlOp, bytes) ->
|
||||
executeUnaryMetadata loggerFactory unaryMetadataTokenIlOp bytes state thread
|
||||
| Switch immutableArray -> failwith "todo"
|
||||
| UnaryStringToken (unaryStringTokenIlOp, stringHandle) ->
|
||||
|> ExecutionResult.Stepped
|
||||
| IlOp.Switch immutableArray -> failwith "todo"
|
||||
| IlOp.UnaryStringToken (unaryStringTokenIlOp, stringHandle) ->
|
||||
executeUnaryStringToken unaryStringTokenIlOp stringHandle state thread
|
||||
|> ExecutionResult.Stepped
|
||||
|
@@ -2,6 +2,7 @@ namespace WoofWare.PawPrint
|
||||
|
||||
#nowarn "9"
|
||||
|
||||
open System
|
||||
open System.Collections.Immutable
|
||||
open System.Reflection
|
||||
open System.Reflection.Metadata
|
||||
@@ -56,7 +57,7 @@ type GenericParameter =
|
||||
|
||||
/// <summary>
|
||||
/// The zero-based index of the generic parameter in the generic parameter list.
|
||||
/// For example, in Dictionary<TKey, TValue>, TKey has index 0 and TValue has index 1.
|
||||
/// For example, in Dictionary<TKey, TValue&rt;, TKey has index 0 and TValue has index 1.
|
||||
/// </summary>
|
||||
SequenceNumber : int
|
||||
}
|
||||
@@ -136,6 +137,8 @@ type MethodInfo =
|
||||
/// </summary>
|
||||
LocalsInit : bool
|
||||
|
||||
LocalVars : ImmutableArray<TypeDefn> option
|
||||
|
||||
/// <summary>
|
||||
/// Whether this method is static (true) or an instance method (false).
|
||||
/// </summary>
|
||||
@@ -150,6 +153,7 @@ module MethodInfo =
|
||||
{
|
||||
Instructions : (IlOp * int) list
|
||||
LocalInit : bool
|
||||
LocalSig : ImmutableArray<TypeDefn> option
|
||||
MaxStackSize : int
|
||||
ExceptionRegions : ImmutableArray<ExceptionRegion>
|
||||
}
|
||||
@@ -171,11 +175,26 @@ module MethodInfo =
|
||||
else
|
||||
LanguagePrimitives.EnumOfValue (uint16 op)
|
||||
|
||||
let private readMethodBody (peReader : PEReader) (methodDef : MethodDefinition) : MethodBody option =
|
||||
let private readMethodBody
|
||||
(peReader : PEReader)
|
||||
(metadataReader : MetadataReader)
|
||||
(methodDef : MethodDefinition)
|
||||
: MethodBody option
|
||||
=
|
||||
if methodDef.RelativeVirtualAddress = 0 then
|
||||
None
|
||||
else
|
||||
let methodBody = peReader.GetMethodBody methodDef.RelativeVirtualAddress
|
||||
|
||||
let localSig =
|
||||
let s = methodBody.LocalSignature |> metadataReader.GetStandaloneSignature
|
||||
// :sob: why are all the useful methods internal
|
||||
try
|
||||
s.Signature |> ignore<BlobHandle> |> Some
|
||||
with :? BadImageFormatException ->
|
||||
None
|
||||
|> Option.map (fun () -> s.DecodeLocalSignature (TypeDefn.typeProvider, ()))
|
||||
|
||||
let ilBytes = methodBody.GetILBytes ()
|
||||
use bytes = fixed ilBytes
|
||||
let mutable reader : BlobReader = BlobReader (bytes, ilBytes.Length)
|
||||
@@ -459,6 +478,7 @@ module MethodInfo =
|
||||
{
|
||||
Instructions = instructions
|
||||
LocalInit = methodBody.LocalVariablesInitialized
|
||||
LocalSig = localSig
|
||||
MaxStackSize = methodBody.MaxStack
|
||||
ExceptionRegions = methodBody.ExceptionRegions
|
||||
}
|
||||
@@ -475,7 +495,7 @@ module MethodInfo =
|
||||
let methodDef = metadataReader.GetMethodDefinition methodHandle
|
||||
let methodName = metadataReader.GetString methodDef.Name
|
||||
let methodSig = methodDef.DecodeSignature (TypeDefn.typeProvider, ())
|
||||
let methodBody = readMethodBody peReader methodDef
|
||||
let methodBody = readMethodBody peReader metadataReader methodDef
|
||||
let declaringType = methodDef.GetDeclaringType ()
|
||||
|
||||
match methodBody with
|
||||
@@ -501,6 +521,7 @@ module MethodInfo =
|
||||
Signature = TypeMethodSignature.make methodSig
|
||||
IsPinvokeImpl = methodDef.Attributes.HasFlag MethodAttributes.PinvokeImpl
|
||||
LocalsInit = methodBody.LocalInit
|
||||
LocalVars = methodBody.LocalSig
|
||||
IsStatic = not methodSig.Header.IsInstance
|
||||
}
|
||||
|> Some
|
||||
|
94
WoofWare.PawPrint/Program.fs
Normal file
94
WoofWare.PawPrint/Program.fs
Normal file
@@ -0,0 +1,94 @@
|
||||
namespace WoofWare.PawPrint
|
||||
|
||||
open System.Collections.Immutable
|
||||
open System.IO
|
||||
open Microsoft.Extensions.Logging
|
||||
|
||||
[<RequireQualifiedAccess>]
|
||||
module Program =
|
||||
/// Returns the pointer to the resulting array on the heap.
|
||||
let allocateArgs (args : string list) (state : IlMachineState) : ManagedHeapAddress * IlMachineState =
|
||||
let argsAllocations, state =
|
||||
(state, args)
|
||||
||> Seq.mapFold (fun state arg -> IlMachineState.allocate (ReferenceType.String arg) state
|
||||
// TODO: set the char values in memory
|
||||
)
|
||||
|
||||
let arrayAllocation, state =
|
||||
IlMachineState.allocate
|
||||
(ReferenceType.Array (args.Length, Type.ReferenceType ReferenceType.ManagedObject))
|
||||
state
|
||||
// TODO: set the length of the array
|
||||
|
||||
let state =
|
||||
((state, 0), argsAllocations)
|
||||
||> Seq.fold (fun (state, i) arg ->
|
||||
let state =
|
||||
IlMachineState.setArrayValue arrayAllocation (CliObject.OfManagedObject arg) i state
|
||||
|
||||
state, i + 1
|
||||
)
|
||||
|> fst
|
||||
|
||||
arrayAllocation, state
|
||||
|
||||
/// Returns the abstract machine's state at the end of execution, together with the thread which
|
||||
/// caused execution to end.
|
||||
let run
|
||||
(loggerFactory : ILoggerFactory)
|
||||
(fileStream : Stream)
|
||||
(dotnetRuntimeDirs : ImmutableArray<string>)
|
||||
(argv : string list)
|
||||
: IlMachineState * ThreadId
|
||||
=
|
||||
let logger = loggerFactory.CreateLogger "Program"
|
||||
|
||||
let dumped = Assembly.read loggerFactory fileStream
|
||||
|
||||
let entryPoint =
|
||||
match dumped.MainMethod with
|
||||
| None -> failwith "No entry point in input DLL"
|
||||
| Some d -> d
|
||||
|
||||
let mainMethod = dumped.Methods.[entryPoint]
|
||||
|
||||
if mainMethod.Signature.GenericParameterCount > 0 then
|
||||
failwith "Refusing to execute generic main method"
|
||||
|
||||
let state = IlMachineState.initial dotnetRuntimeDirs dumped
|
||||
|
||||
let arrayAllocation, state =
|
||||
match mainMethod.Signature.ParameterTypes |> Seq.toList with
|
||||
| [ TypeDefn.OneDimensionalArrayLowerBoundZero (TypeDefn.PrimitiveType PrimitiveType.String) ] ->
|
||||
allocateArgs argv state
|
||||
| _ -> failwith "Main method must take an array of strings; other signatures not yet implemented"
|
||||
|
||||
match mainMethod.Signature.ReturnType with
|
||||
| TypeDefn.PrimitiveType PrimitiveType.Int32 -> ()
|
||||
| _ -> failwith "Main method must return int32; other types not currently supported"
|
||||
|
||||
let state, mainThread =
|
||||
state
|
||||
|> IlMachineState.addThread
|
||||
// TODO: we need to load the main method's class first, and that's a faff with the current layout
|
||||
{ MethodState.Empty mainMethod None with
|
||||
Arguments = ImmutableArray.Create (CliObject.OfManagedObject arrayAllocation)
|
||||
}
|
||||
dumped.Name
|
||||
|
||||
let rec go (state : IlMachineState) =
|
||||
match AbstractMachine.executeOneStep loggerFactory state mainThread with
|
||||
| ExecutionResult.Terminated (state, terminatingThread) -> state, terminatingThread
|
||||
| ExecutionResult.Stepped (state', whatWeDid) ->
|
||||
|
||||
match whatWeDid with
|
||||
| WhatWeDid.Executed -> logger.LogInformation "Executed one step."
|
||||
| WhatWeDid.SuspendedForClassInit ->
|
||||
logger.LogInformation "Suspended execution of current method for class initialisation."
|
||||
| WhatWeDid.NotTellingYou -> logger.LogInformation "(Execution outcome missing.)"
|
||||
| WhatWeDid.BlockedOnClassInit threadBlockingUs ->
|
||||
logger.LogInformation "Unable to execute because class has not yet initialised."
|
||||
|
||||
go state'
|
||||
|
||||
go state
|
@@ -158,7 +158,7 @@ module TypeDefn =
|
||||
| SignatureTypeCode.Pinned -> failwith "todo"
|
||||
| x -> failwith $"Unrecognised type code: {x}"
|
||||
|
||||
let typeProvider =
|
||||
let typeProvider : ISignatureTypeProvider<TypeDefn, unit> =
|
||||
{ new ISignatureTypeProvider<TypeDefn, unit> with
|
||||
member this.GetArrayType (elementType : TypeDefn, shape : ArrayShape) : TypeDefn =
|
||||
TypeDefn.Array (elementType, shape)
|
||||
|
@@ -21,6 +21,7 @@
|
||||
<Compile Include="TypeInfo.fs" />
|
||||
<Compile Include="Assembly.fs" />
|
||||
<Compile Include="AbstractMachine.fs" />
|
||||
<Compile Include="Program.fs" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
@@ -54,6 +54,21 @@
|
||||
"version": "8.0.12",
|
||||
"hash": "sha256-P7gJnRkTZT0+xx+VOjn0MX/CURjk/JdT5ArGC6mQ3Qw="
|
||||
},
|
||||
{
|
||||
"pname": "Microsoft.CodeAnalysis.Analyzers",
|
||||
"version": "3.3.4",
|
||||
"hash": "sha256-qDzTfZBSCvAUu9gzq2k+LOvh6/eRvJ9++VCNck/ZpnE="
|
||||
},
|
||||
{
|
||||
"pname": "Microsoft.CodeAnalysis.Common",
|
||||
"version": "4.8.0",
|
||||
"hash": "sha256-3IEinVTZq6/aajMVA8XTRO3LTIEt0PuhGyITGJLtqz4="
|
||||
},
|
||||
{
|
||||
"pname": "Microsoft.CodeAnalysis.CSharp",
|
||||
"version": "4.8.0",
|
||||
"hash": "sha256-MmOnXJvd/ezs5UPcqyGLnbZz5m+VedpRfB+kFZeeqkU="
|
||||
},
|
||||
{
|
||||
"pname": "Microsoft.CodeCoverage",
|
||||
"version": "17.13.0",
|
||||
@@ -184,6 +199,11 @@
|
||||
"version": "5.0.0",
|
||||
"hash": "sha256-7jZM4qAbIzne3AcdFfMbvbgogqpxvVe6q2S7Ls8xQy0="
|
||||
},
|
||||
{
|
||||
"pname": "System.Collections.Immutable",
|
||||
"version": "7.0.0",
|
||||
"hash": "sha256-9an2wbxue2qrtugYES9awshQg+KfJqajhnhs45kQIdk="
|
||||
},
|
||||
{
|
||||
"pname": "System.Diagnostics.DiagnosticSource",
|
||||
"version": "5.0.0",
|
||||
@@ -198,5 +218,15 @@
|
||||
"pname": "System.Reflection.Metadata",
|
||||
"version": "1.6.0",
|
||||
"hash": "sha256-JJfgaPav7UfEh4yRAQdGhLZF1brr0tUWPl6qmfNWq/E="
|
||||
},
|
||||
{
|
||||
"pname": "System.Reflection.Metadata",
|
||||
"version": "7.0.0",
|
||||
"hash": "sha256-GwAKQhkhPBYTqmRdG9c9taqrKSKDwyUgOEhWLKxWNPI="
|
||||
},
|
||||
{
|
||||
"pname": "System.Runtime.CompilerServices.Unsafe",
|
||||
"version": "6.0.0",
|
||||
"hash": "sha256-bEG1PnDp7uKYz/OgLOWs3RWwQSVYm+AnPwVmAmcgp2I="
|
||||
}
|
||||
]
|
||||
|
Reference in New Issue
Block a user