diff --git a/src/Core/Echo/Memory/VirtualMemory.cs b/src/Core/Echo/Memory/VirtualMemory.cs
index cdd907c0..6fc52f23 100644
--- a/src/Core/Echo/Memory/VirtualMemory.cs
+++ b/src/Core/Echo/Memory/VirtualMemory.cs
@@ -49,12 +49,18 @@ public AddressRange AddressRange
/// Occurs when the address was already in use.
public void Map(long address, IMemorySpace space)
{
+ if (space.AddressRange.Length == 0)
+ throw new ArgumentException("Cannot map an empty memory space.");
+
if (!AddressRange.Contains(address))
- throw new ArgumentException($"Address {address:X8} does not fall within the virtual memory.");
+ throw new ArgumentException($"Address 0x{address:X8} does not fall within the virtual memory.");
+
+ if (!AddressRange.Contains(address + space.AddressRange.Length - 1))
+ throw new ArgumentException($"Insufficient space available at address 0x{address:X8} to map {space.AddressRange.Length} bytes within the virtual memory.");
int index = GetMemorySpaceIndex(address);
if (index != -1)
- throw new ArgumentException($"Address {address:X8} is already in use.");
+ throw new ArgumentException($"Address 0x{address:X8} is already in use.");
// Insertion sort to ensure _spaces remains sorted.
int i = 0;
diff --git a/src/Platforms/Echo.Platforms.AsmResolver/Emulation/CilThread.cs b/src/Platforms/Echo.Platforms.AsmResolver/Emulation/CilThread.cs
new file mode 100644
index 00000000..bf398c42
--- /dev/null
+++ b/src/Platforms/Echo.Platforms.AsmResolver/Emulation/CilThread.cs
@@ -0,0 +1,292 @@
+using System;
+using System.Linq;
+using System.Reflection;
+using System.Threading;
+using AsmResolver.DotNet;
+using AsmResolver.DotNet.Signatures;
+using AsmResolver.PE.DotNet.Cil;
+using Echo.Memory;
+using Echo.Platforms.AsmResolver.Emulation.Dispatch;
+using Echo.Platforms.AsmResolver.Emulation.Stack;
+
+namespace Echo.Platforms.AsmResolver.Emulation
+{
+ ///
+ /// Represents a single execution thread in a virtualized .NET process.
+ ///
+ public class CilThread
+ {
+ private CilExecutionContext? _singleStepContext;
+
+ internal CilThread(CilVirtualMachine machine, CallStack callStack)
+ {
+ Machine = machine;
+ CallStack = callStack;
+ IsAlive = true;
+ }
+
+ ///
+ /// Gets the parent machine the thread is running in.
+ ///
+ public CilVirtualMachine Machine
+ {
+ get;
+ }
+
+ ///
+ /// Gets the current state of the call stack.
+ ///
+ ///
+ /// The call stack is also addressable from .
+ ///
+ public CallStack CallStack
+ {
+ get;
+ }
+
+ ///
+ /// Gets a value indicating whether the thread is alive and present in the parent machine.
+ ///
+ public bool IsAlive
+ {
+ get;
+ internal set;
+ }
+
+ ///
+ /// Runs the virtual machine until it halts.
+ ///
+ public void Run() => Run(CancellationToken.None);
+
+ ///
+ /// Runs the virtual machine until it halts.
+ ///
+ /// A token that can be used for canceling the emulation.
+ public void Run(CancellationToken cancellationToken)
+ {
+ StepWhile(cancellationToken, context => !context.CurrentFrame.IsRoot);
+ }
+
+ ///
+ /// Calls the provided method in the context of the virtual machine.
+ ///
+ /// The method to call.
+ /// The arguments.
+ /// The return value, or null if the provided method does not return a value.
+ ///
+ /// This method is blocking until the emulation of the call completes.
+ ///
+ public BitVector? Call(IMethodDescriptor method, object[] arguments)
+ {
+ // Short circuit before we do expensive marshalling...
+ if (arguments.Length != method.Signature!.GetTotalParameterCount())
+ throw new TargetParameterCountException();
+
+ var marshalled = arguments.Select(x => Machine.ObjectMarshaller.ToBitVector(x)).ToArray();
+ return Call(method, CancellationToken.None, marshalled);
+ }
+
+ ///
+ /// Calls the provided method in the context of the virtual machine.
+ ///
+ /// The method to call.
+ /// The arguments.
+ /// The return value, or null if the provided method does not return a value.
+ ///
+ /// This method is blocking until the emulation of the call completes.
+ ///
+ public BitVector? Call(IMethodDescriptor method, BitVector[] arguments)
+ {
+ return Call(method, CancellationToken.None, arguments);
+ }
+
+ ///
+ /// Calls the provided method in the context of the virtual machine.
+ ///
+ /// The method to call.
+ /// A token that can be used for canceling the emulation.
+ /// The arguments.
+ /// The return value, or null if the provided method does not return a value.
+ ///
+ /// This method is blocking until the emulation of the call completes or the emulation is canceled.
+ ///
+ public BitVector? Call(IMethodDescriptor method, CancellationToken cancellationToken, BitVector[] arguments)
+ {
+ if (arguments.Length != method.Signature!.GetTotalParameterCount())
+ throw new TargetParameterCountException();
+
+ var pool = Machine.ValueFactory.BitVectorPool;
+
+ // Instantiate any generic types if available.
+ var context = GenericContext.FromMethod(method);
+ var signature = method.Signature.InstantiateGenericTypes(context);
+
+ // Set up callee frame.
+ var frame = CallStack.Push(method);
+ for (int i = 0; i < arguments.Length; i++)
+ {
+ var slot = Machine.ValueFactory.Marshaller.ToCliValue(arguments[i], signature.ParameterTypes[i]);
+ frame.WriteArgument(i, slot.Contents);
+ pool.Return(slot.Contents);
+ }
+
+ // Run until we return.
+ StepOut(cancellationToken);
+
+ // If void, then we don't have anything else to do.
+ if (!signature.ReturnsValue)
+ return null;
+
+ // If we produced a return value, return a copy of it to the caller.
+ // As the return value may be a rented bit vector, we should copy it to avoid unwanted side-effects.
+ var callResult = CallStack.Peek().EvaluationStack.Pop(signature.ReturnType);
+ var result = callResult.Clone();
+ pool.Return(callResult);
+
+ return result;
+ }
+
+ ///
+ /// Continues execution of the virtual machine while the provided predicate returns true.
+ ///
+ /// A token that can be used for canceling the emulation.
+ ///
+ /// A predicate that is evaluated on every step of the emulation, determining whether execution should continue.
+ ///
+ public void StepWhile(CancellationToken cancellationToken, Predicate condition)
+ {
+ var context = new CilExecutionContext(this, cancellationToken);
+
+ do
+ {
+ Step(context);
+ cancellationToken.ThrowIfCancellationRequested();
+ } while (condition(context));
+ }
+
+ ///
+ /// Performs a single step in the virtual machine. If the current instruction performs a call, the emulation
+ /// is treated as a single instruction.
+ ///
+ public void StepOver() => StepOver(CancellationToken.None);
+
+ ///
+ /// Performs a single step in the virtual machine. If the current instruction performs a call, the emulation
+ /// is treated as a single instruction.
+ ///
+ /// A token that can be used for canceling the emulation.
+ public void StepOver(CancellationToken cancellationToken)
+ {
+ int stackDepth = CallStack.Count;
+ StepWhile(cancellationToken, context => context.Thread.CallStack.Count > stackDepth);
+ }
+
+ ///
+ /// Continues execution of the virtual machine until the current call frame is popped from the stack.
+ ///
+ public void StepOut() => StepOut(CancellationToken.None);
+
+ ///
+ /// Continues execution of the virtual machine until the current call frame is popped from the stack.
+ ///
+ /// A token that can be used for canceling the emulation.
+ public void StepOut(CancellationToken cancellationToken)
+ {
+ int stackDepth = CallStack.Count;
+ StepWhile(cancellationToken, context => context.Thread.CallStack.Count >= stackDepth);
+ }
+
+ ///
+ /// Performs a single step in the virtual machine.
+ ///
+ public void Step()
+ {
+ _singleStepContext ??= new CilExecutionContext(this, CancellationToken.None);
+ Step(_singleStepContext);
+ }
+
+ ///
+ /// Performs a single step in the virtual machine.
+ ///
+ /// A token that can be used for canceling the emulation.
+ public void Step(CancellationToken cancellationToken) => Step(new CilExecutionContext(this, cancellationToken));
+
+ private void Step(CilExecutionContext context)
+ {
+ if (!IsAlive)
+ throw new CilEmulatorException("The thread is not alive.");
+
+ if (CallStack.Peek().IsRoot)
+ throw new CilEmulatorException("No method is currently being executed.");
+
+ var currentFrame = CallStack.Peek();
+ if (currentFrame.Body is not { } body)
+ throw new CilEmulatorException("Emulator only supports managed method bodies.");
+
+ // Determine the next instruction to dispatch.
+ int pc = currentFrame.ProgramCounter;
+ var instruction = body.Instructions.GetByOffset(pc);
+ if (instruction is null)
+ throw new CilEmulatorException($"Invalid program counter in {currentFrame}.");
+
+ // Are we entering any protected regions?
+ UpdateExceptionHandlerStack();
+
+ // Dispatch the instruction.
+ var result = Machine.Dispatcher.Dispatch(context, instruction);
+
+ if (!result.IsSuccess)
+ {
+ var exceptionObject = result.ExceptionObject;
+ if (exceptionObject.IsNull)
+ throw new CilEmulatorException("A null exception object was thrown.");
+
+ // If there were any errors thrown after dispatching, it may trigger the execution of one of the
+ // exception handlers in the entire call stack.
+ if (!UnwindCallStack(exceptionObject))
+ throw new EmulatedException(exceptionObject);
+ }
+ }
+
+ private void UpdateExceptionHandlerStack()
+ {
+ var currentFrame = CallStack.Peek();
+
+ int pc = currentFrame.ProgramCounter;
+ var availableHandlers = currentFrame.ExceptionHandlers;
+ var activeHandlers = currentFrame.ExceptionHandlerStack;
+
+ for (int i = 0; i < availableHandlers.Count; i++)
+ {
+ var handler = availableHandlers[i];
+ if (handler.ProtectedRange.Contains(pc) && handler.Enter() && !activeHandlers.Contains(handler))
+ activeHandlers.Push(handler);
+ }
+ }
+
+ private bool UnwindCallStack(ObjectHandle exceptionObject)
+ {
+ while (!CallStack.Peek().IsRoot)
+ {
+ var currentFrame = CallStack.Peek();
+
+ var result = currentFrame.ExceptionHandlerStack.RegisterException(exceptionObject);
+ if (result.IsSuccess)
+ {
+ // We found a handler that needs to be called. Jump to it.
+ currentFrame.ProgramCounter = result.NextOffset;
+
+ // Push the exception on the stack.
+ currentFrame.EvaluationStack.Clear();
+ currentFrame.EvaluationStack.Push(exceptionObject);
+
+ return true;
+ }
+
+ CallStack.Pop();
+ }
+
+ return false;
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Platforms/Echo.Platforms.AsmResolver/Emulation/CilVirtualMachine.cs b/src/Platforms/Echo.Platforms.AsmResolver/Emulation/CilVirtualMachine.cs
index 268d5726..c958d7b2 100644
--- a/src/Platforms/Echo.Platforms.AsmResolver/Emulation/CilVirtualMachine.cs
+++ b/src/Platforms/Echo.Platforms.AsmResolver/Emulation/CilVirtualMachine.cs
@@ -1,10 +1,8 @@
using System;
+using System.Collections.Generic;
+using System.Collections.ObjectModel;
using System.Linq;
-using System.Reflection;
-using System.Threading;
using AsmResolver.DotNet;
-using AsmResolver.DotNet.Signatures;
-using AsmResolver.PE.DotNet.Cil;
using Echo.Memory;
using Echo.Platforms.AsmResolver.Emulation.Dispatch;
using Echo.Platforms.AsmResolver.Emulation.Heap;
@@ -19,7 +17,18 @@ namespace Echo.Platforms.AsmResolver.Emulation
///
public class CilVirtualMachine
{
- private CilExecutionContext? _singleStepContext;
+ ///
+ /// Fires when a new thread was created.
+ ///
+ public event EventHandler? ThreadCreated;
+
+ ///
+ /// Fires when a thread was destroyed.
+ ///
+ public event EventHandler? ThreadDestroyed;
+
+ private readonly List _threads = new();
+ private readonly CallStackMemory _callStackMemory;
///
/// Creates a new CIL virtual machine.
@@ -34,25 +43,26 @@ public CilVirtualMachine(ModuleDefinition contextModule, bool is32Bit)
ValueFactory = new ValueFactory(contextModule, is32Bit);
ObjectMapMemory = new ObjectMapMemory(this, 0x1000_0000);
ObjectMarshaller = new ObjectMarshaller(this);
-
+
if (is32Bit)
{
Memory.Map(0x1000_0000, Heap = new ManagedObjectHeap(0x0100_0000, ValueFactory));
Memory.Map(0x6000_0000, ObjectMapMemory);
Memory.Map(0x7000_0000, StaticFields = new StaticFieldStorage(ValueFactory, 0x0100_0000));
Memory.Map(0x7100_0000, ValueFactory.ClrMockMemory);
- Memory.Map(0x7fe0_0000, CallStack = new CallStack(0x10_0000, ValueFactory));
+ Memory.Map(0x7f00_0000, _callStackMemory = new CallStackMemory(0x100_0000, ValueFactory));
}
else
{
Memory.Map(0x0000_0100_0000_0000, Heap = new ManagedObjectHeap(0x01000_0000, ValueFactory));
- Memory.Map(0x0000_7ffe_0000_0000, ObjectMapMemory);
- Memory.Map(0x0000_7fff_0000_0000, StaticFields = new StaticFieldStorage(ValueFactory, 0x1000_0000));
- Memory.Map(0x0000_7fff_1000_0000, ValueFactory.ClrMockMemory);
- Memory.Map(0x0000_7fff_8000_0000, CallStack = new CallStack(0x100_0000, ValueFactory));
+ Memory.Map(0x0000_7ffd_0000_0000, ObjectMapMemory);
+ Memory.Map(0x0000_7ffe_0000_0000, StaticFields = new StaticFieldStorage(ValueFactory, 0x1000_0000));
+ Memory.Map(0x0000_7ffe_1000_0000, ValueFactory.ClrMockMemory);
+ Memory.Map(0x0000_7ffe_8000_0000, _callStackMemory = new CallStackMemory(0x1000_0000, ValueFactory));
}
Dispatcher = new CilDispatcher();
+ Threads = new ReadOnlyCollection(_threads);
}
///
@@ -87,17 +97,6 @@ public StaticFieldStorage StaticFields
get;
}
- ///
- /// Gets the current state of the call stack.
- ///
- ///
- /// The call stack is also addressable from .
- ///
- public CallStack CallStack
- {
- get;
- }
-
///
/// Gets the memory manager that embeds managed objects into virtual memory.
///
@@ -161,239 +160,50 @@ public IObjectMarshaller ObjectMarshaller
get;
set;
}
-
- ///
- /// Runs the virtual machine until it halts.
- ///
- public void Run() => Run(CancellationToken.None);
-
- ///
- /// Runs the virtual machine until it halts.
- ///
- /// A token that can be used for canceling the emulation.
- public void Run(CancellationToken cancellationToken)
- {
- StepWhile(cancellationToken, context => !context.CurrentFrame.IsRoot);
- }
///
- /// Calls the provided method in the context of the virtual machine.
+ /// Gets a collection of threads that are currently active in the machine.
///
- /// The method to call.
- /// The arguments.
- /// The return value, or null if the provided method does not return a value.
- ///
- /// This method is blocking until the emulation of the call completes.
- ///
- public BitVector? Call(IMethodDescriptor method, object[] arguments)
+ public IReadOnlyList Threads
{
- // Short circuit before we do expensive marshalling...
- if (arguments.Length != method.Signature!.GetTotalParameterCount())
- throw new TargetParameterCountException();
-
- var marshalled = arguments.Select(x => ObjectMarshaller.ToBitVector(x)).ToArray();
- return Call(method, CancellationToken.None, marshalled);
+ get;
}
-
+
///
- /// Calls the provided method in the context of the virtual machine.
+ /// Creates a new thread in the machine.
///
- /// The method to call.
- /// The arguments.
- /// The return value, or null if the provided method does not return a value.
- ///
- /// This method is blocking until the emulation of the call completes.
- ///
- public BitVector? Call(IMethodDescriptor method, BitVector[] arguments)
+ /// The amount of memory to allocate for the thread's stack.
+ /// The created thread.
+ public CilThread CreateThread(uint stackSize = 0x0010_0000)
{
- return Call(method, CancellationToken.None, arguments);
+ var stack = _callStackMemory.Allocate(stackSize);
+ var thread = new CilThread(this, stack);
+ _threads.Add(thread);
+ ThreadCreated?.Invoke(this, thread);
+ return thread;
}
///
- /// Calls the provided method in the context of the virtual machine.
+ /// Removes a thread and its stack from the machine.
///
- /// The method to call.
- /// A token that can be used for canceling the emulation.
- /// The arguments.
- /// The return value, or null if the provided method does not return a value.
+ /// The thread to remove.
///
- /// This method is blocking until the emulation of the call completes or the emulation is canceled.
+ /// This does not gracefully terminate a thread. Any code that is still running will remain executing, and may
+ /// have unwanted side-effects. Therefore, be sure to only call this method only when it is certain that no code
+ /// is running.
///
- public BitVector? Call(IMethodDescriptor method, CancellationToken cancellationToken, BitVector[] arguments)
- {
- if (arguments.Length != method.Signature!.GetTotalParameterCount())
- throw new TargetParameterCountException();
-
- var pool = ValueFactory.BitVectorPool;
-
- // Instantiate any generic types if available.
- var context = GenericContext.FromMethod(method);
- var signature = method.Signature.InstantiateGenericTypes(context);
-
- // Set up callee frame.
- var frame = CallStack.Push(method);
- for (int i = 0; i < arguments.Length; i++)
- {
- var slot = ValueFactory.Marshaller.ToCliValue(arguments[i], signature.ParameterTypes[i]);
- frame.WriteArgument(i, slot.Contents);
- pool.Return(slot.Contents);
- }
-
- // Run until we return.
- StepOut(cancellationToken);
-
- // If void, then we don't have anything else to do.
- if (!signature.ReturnsValue)
- return null;
-
- // If we produced a return value, return a copy of it to the caller.
- // As the return value may be a rented bit vector, we should copy it to avoid unwanted side-effects.
- var callResult = CallStack.Peek().EvaluationStack.Pop(signature.ReturnType);
- var result = callResult.Clone();
- pool.Return(callResult);
-
- return result;
- }
-
- ///
- /// Continues execution of the virtual machine while the provided predicate returns true.
- ///
- /// A token that can be used for canceling the emulation.
- ///
- /// A predicate that is evaluated on every step of the emulation, determining whether execution should continue.
- ///
- public void StepWhile(CancellationToken cancellationToken, Predicate condition)
- {
- var context = new CilExecutionContext(this, cancellationToken);
-
- do
- {
- Step(context);
- cancellationToken.ThrowIfCancellationRequested();
- } while (condition(context));
- }
-
- ///
- /// Performs a single step in the virtual machine. If the current instruction performs a call, the emulation
- /// is treated as a single instruction.
- ///
- public void StepOver() => StepOver(CancellationToken.None);
-
- ///
- /// Performs a single step in the virtual machine. If the current instruction performs a call, the emulation
- /// is treated as a single instruction.
- ///
- /// A token that can be used for canceling the emulation.
- public void StepOver(CancellationToken cancellationToken)
+ public void DestroyThread(CilThread thread)
{
- int stackDepth = CallStack.Count;
- StepWhile(cancellationToken, context => context.Machine.CallStack.Count > stackDepth);
- }
-
- ///
- /// Continues execution of the virtual machine until the current call frame is popped from the stack.
- ///
- public void StepOut() => StepOut(CancellationToken.None);
-
- ///
- /// Continues execution of the virtual machine until the current call frame is popped from the stack.
- ///
- /// A token that can be used for canceling the emulation.
- public void StepOut(CancellationToken cancellationToken)
- {
- int stackDepth = CallStack.Count;
- StepWhile(cancellationToken, context => context.Machine.CallStack.Count >= stackDepth);
- }
-
- ///
- /// Performs a single step in the virtual machine.
- ///
- public void Step()
- {
- _singleStepContext ??= new CilExecutionContext(this, CancellationToken.None);
- Step(_singleStepContext);
- }
-
- ///
- /// Performs a single step in the virtual machine.
- ///
- /// A token that can be used for canceling the emulation.
- public void Step(CancellationToken cancellationToken) => Step(new CilExecutionContext(this, cancellationToken));
-
- private void Step(CilExecutionContext context)
- {
- if (CallStack.Peek().IsRoot)
- throw new CilEmulatorException("No method is currently being executed.");
-
- var currentFrame = CallStack.Peek();
- if (currentFrame.Body is not { } body)
- throw new CilEmulatorException("Emulator only supports managed method bodies.");
+ if (thread.Machine != this)
+ throw new ArgumentException("Cannot remove a thread from a different machine.");
+ if (!thread.IsAlive)
+ throw new ArgumentException("Cannot destroy a thread that is already destroyed.");
- // Determine the next instruction to dispatch.
- int pc = currentFrame.ProgramCounter;
- var instruction = body.Instructions.GetByOffset(pc);
- if (instruction is null)
- throw new CilEmulatorException($"Invalid program counter in {currentFrame}.");
-
- // Are we entering any protected regions?
- UpdateExceptionHandlerStack();
-
- // Dispatch the instruction.
- var result = Dispatcher.Dispatch(context, instruction);
-
- if (!result.IsSuccess)
- {
- var exceptionObject = result.ExceptionObject;
- if (exceptionObject.IsNull)
- throw new CilEmulatorException("A null exception object was thrown.");
-
- // If there were any errors thrown after dispatching, it may trigger the execution of one of the
- // exception handlers in the entire call stack.
- if (!UnwindCallStack(exceptionObject))
- throw new EmulatedException(exceptionObject);
- }
- }
-
- private void UpdateExceptionHandlerStack()
- {
- var currentFrame = CallStack.Peek();
-
- int pc = currentFrame.ProgramCounter;
- var availableHandlers = currentFrame.ExceptionHandlers;
- var activeHandlers = currentFrame.ExceptionHandlerStack;
-
- for (int i = 0; i < availableHandlers.Count; i++)
- {
- var handler = availableHandlers[i];
- if (handler.ProtectedRange.Contains(pc) && handler.Enter() && !activeHandlers.Contains(handler))
- activeHandlers.Push(handler);
- }
- }
-
- private bool UnwindCallStack(ObjectHandle exceptionObject)
- {
- while (!CallStack.Peek().IsRoot)
- {
- var currentFrame = CallStack.Peek();
-
- var result = currentFrame.ExceptionHandlerStack.RegisterException(exceptionObject);
- if (result.IsSuccess)
- {
- // We found a handler that needs to be called. Jump to it.
- currentFrame.ProgramCounter = result.NextOffset;
-
- // Push the exception on the stack.
- currentFrame.EvaluationStack.Clear();
- currentFrame.EvaluationStack.Push(exceptionObject);
-
- return true;
- }
-
- CallStack.Pop();
- }
+ thread.IsAlive = false;
+ _threads.Remove(thread);
+ _callStackMemory.Free(thread.CallStack);
- return false;
+ ThreadDestroyed?.Invoke(this, thread);
}
-
}
}
\ No newline at end of file
diff --git a/src/Platforms/Echo.Platforms.AsmResolver/Emulation/Dispatch/CilExecutionContext.cs b/src/Platforms/Echo.Platforms.AsmResolver/Emulation/Dispatch/CilExecutionContext.cs
index b95ac837..001842da 100644
--- a/src/Platforms/Echo.Platforms.AsmResolver/Emulation/Dispatch/CilExecutionContext.cs
+++ b/src/Platforms/Echo.Platforms.AsmResolver/Emulation/Dispatch/CilExecutionContext.cs
@@ -13,24 +13,26 @@ public class CilExecutionContext
///
/// The parent machine the instruction is executed on.
/// A token used for canceling the emulation.
- public CilExecutionContext(CilVirtualMachine machine, CancellationToken cancellationToken)
+ public CilExecutionContext(CilThread thread, CancellationToken cancellationToken)
{
- Machine = machine;
+ Thread = thread;
CancellationToken = cancellationToken;
}
- ///
- /// Gets the parent machine the instruction is executed on.
- ///
- public CilVirtualMachine Machine
+ public CilThread Thread
{
get;
}
+ ///
+ /// Gets the parent machine the instruction is executed on.
+ ///
+ public CilVirtualMachine Machine => Thread.Machine;
+
///
/// Gets the current active stack frame.
///
- public CallFrame CurrentFrame => Machine.CallStack.Peek();
+ public CallFrame CurrentFrame => Thread.CallStack.Peek();
///
/// Gets a token used for canceling the emulation.
diff --git a/src/Platforms/Echo.Platforms.AsmResolver/Emulation/Dispatch/ControlFlow/RetHandler.cs b/src/Platforms/Echo.Platforms.AsmResolver/Emulation/Dispatch/ControlFlow/RetHandler.cs
index c0461809..68d53019 100644
--- a/src/Platforms/Echo.Platforms.AsmResolver/Emulation/Dispatch/ControlFlow/RetHandler.cs
+++ b/src/Platforms/Echo.Platforms.AsmResolver/Emulation/Dispatch/ControlFlow/RetHandler.cs
@@ -13,7 +13,7 @@ public class RetHandler : ICilOpCodeHandler
///
public CilDispatchResult Dispatch(CilExecutionContext context, CilInstruction instruction)
{
- var frame = context.Machine.CallStack.Pop();
+ var frame = context.Thread.CallStack.Pop();
var genericContext = GenericContext.FromMethod(frame.Method);
if (frame.Method.Signature!.ReturnsValue)
{
diff --git a/src/Platforms/Echo.Platforms.AsmResolver/Emulation/Dispatch/ObjectModel/CallHandlerBase.cs b/src/Platforms/Echo.Platforms.AsmResolver/Emulation/Dispatch/ObjectModel/CallHandlerBase.cs
index f44fcef1..c78e8c1c 100644
--- a/src/Platforms/Echo.Platforms.AsmResolver/Emulation/Dispatch/ObjectModel/CallHandlerBase.cs
+++ b/src/Platforms/Echo.Platforms.AsmResolver/Emulation/Dispatch/ObjectModel/CallHandlerBase.cs
@@ -155,7 +155,7 @@ private static CilDispatchResult Invoke(CilExecutionContext context, IMethodDesc
for (int i = 0; i < arguments.Count; i++)
frame.WriteArgument(i, arguments[i]);
- context.Machine.CallStack.Push(frame);
+ context.Thread.CallStack.Push(frame);
return CilDispatchResult.Success();
case InvocationResultType.StepOver:
diff --git a/src/Platforms/Echo.Platforms.AsmResolver/Emulation/Stack/CallStack.cs b/src/Platforms/Echo.Platforms.AsmResolver/Emulation/Stack/CallStack.cs
index 8bd4a845..76f6b1bb 100644
--- a/src/Platforms/Echo.Platforms.AsmResolver/Emulation/Stack/CallStack.cs
+++ b/src/Platforms/Echo.Platforms.AsmResolver/Emulation/Stack/CallStack.cs
@@ -30,7 +30,7 @@ public class CallStack : IndexableStack, IMemorySpace
///
/// The maximum number of bytes the stack can hold.
/// The service responsible for managing types.
- public CallStack(int maxSize, ValueFactory factory)
+ public CallStack(uint maxSize, ValueFactory factory)
{
_factory = factory;
AddressRange = new AddressRange(0, maxSize);
diff --git a/src/Platforms/Echo.Platforms.AsmResolver/Emulation/Stack/CallStackMemory.cs b/src/Platforms/Echo.Platforms.AsmResolver/Emulation/Stack/CallStackMemory.cs
new file mode 100644
index 00000000..680944ba
--- /dev/null
+++ b/src/Platforms/Echo.Platforms.AsmResolver/Emulation/Stack/CallStackMemory.cs
@@ -0,0 +1,55 @@
+using System;
+using Echo.Memory;
+
+namespace Echo.Platforms.AsmResolver.Emulation.Stack
+{
+ internal class CallStackMemory : IMemorySpace
+ {
+ private readonly VirtualMemory _mapping;
+ private readonly ValueFactory _factory;
+
+ public CallStackMemory(uint totalSize, ValueFactory factory)
+ {
+ _mapping = new VirtualMemory(totalSize);
+ _factory = factory;
+ }
+
+ ///
+ public AddressRange AddressRange => _mapping.AddressRange;
+
+ ///
+ public bool IsValidAddress(long address) => _mapping.IsValidAddress(address);
+
+ ///
+ public void Rebase(long baseAddress) => _mapping.Rebase(baseAddress);
+
+ ///
+ public void Read(long address, BitVectorSpan buffer) => _mapping.Read(address, buffer);
+
+ ///
+ public void Write(long address, BitVectorSpan buffer) => _mapping.Write(address, buffer);
+
+ ///
+ public void Write(long address, ReadOnlySpan buffer) => _mapping.Write(address, buffer);
+
+ public CallStack Allocate(uint size)
+ {
+ var result = new CallStack(size, _factory);
+
+ long last = AddressRange.Start;
+ foreach (var range in _mapping.GetMappedRanges())
+ {
+ long available = last - range.Start;
+ if (available >= size)
+ break;
+
+ last = range.End;
+ }
+
+ _mapping.Map(last, result);
+ return result;
+ }
+
+ public void Free(CallStack stack) => _mapping.Unmap(stack.AddressRange.Start);
+ }
+}
\ No newline at end of file
diff --git a/test/Platforms/Echo.Platforms.AsmResolver.Tests/Emulation/CilVirtualMachineTest.cs b/test/Platforms/Echo.Platforms.AsmResolver.Tests/Emulation/CilVirtualMachineTest.cs
index 9d623f77..3c698ae9 100644
--- a/test/Platforms/Echo.Platforms.AsmResolver.Tests/Emulation/CilVirtualMachineTest.cs
+++ b/test/Platforms/Echo.Platforms.AsmResolver.Tests/Emulation/CilVirtualMachineTest.cs
@@ -27,12 +27,29 @@ public class CilVirtualMachineTest : IClassFixture
private readonly MockModuleFixture _fixture;
private readonly ITestOutputHelper _testOutputHelper;
private readonly CilVirtualMachine _vm;
+ private readonly CilThread _mainThread;
public CilVirtualMachineTest(MockModuleFixture fixture, ITestOutputHelper testOutputHelper)
{
_fixture = fixture;
_testOutputHelper = testOutputHelper;
_vm = new CilVirtualMachine(fixture.MockModule, false);
+ _mainThread = _vm.CreateThread();
+ }
+
+ [Fact]
+ public void CreateSingleThread()
+ {
+ Assert.Contains(_mainThread, _vm.Threads);
+ }
+
+ [Fact]
+ public void CreateSecondaryThread()
+ {
+ var thread = _vm.CreateThread();
+ Assert.Contains(_mainThread, _vm.Threads);
+ Assert.False(thread.CallStack.AddressRange.Contains(_mainThread.CallStack.AddressRange.Start));
+ Assert.False(thread.CallStack.AddressRange.Contains(_mainThread.CallStack.AddressRange.End - 1));
}
[Fact]
@@ -52,20 +69,20 @@ public void SingleStep()
dummyMethod.CilMethodBody = body;
// Push frame on stack.
- _vm.CallStack.Push(dummyMethod);
+ _mainThread.CallStack.Push(dummyMethod);
// Execute all nops.
for (int i = 0; i < 100; i++)
- _vm.Step();
+ _mainThread.Step();
// Check if we're still in the dummy method.
- Assert.Equal(2, _vm.CallStack.Count);
+ Assert.Equal(2, _mainThread.CallStack.Count);
// Execute return.
- _vm.Step();
+ _mainThread.Step();
// Check if we exited.
- Assert.True(Assert.Single(_vm.CallStack).IsRoot);
+ Assert.True(Assert.Single(_mainThread.CallStack).IsRoot);
}
[Fact(Timeout = 5000)]
@@ -85,12 +102,12 @@ public void RunShouldTerminate()
dummyMethod.CilMethodBody = body;
// Push frame on stack.
- _vm.CallStack.Push(dummyMethod);
+ _mainThread.CallStack.Push(dummyMethod);
- _vm.Run();
+ _mainThread.Run();
// Check if we exited.
- Assert.True(Assert.Single(_vm.CallStack).IsRoot);
+ Assert.True(Assert.Single(_mainThread.CallStack).IsRoot);
}
[Fact(Timeout = 5000)]
@@ -111,7 +128,7 @@ public void CancelShouldThrow()
dummyMethod.CilMethodBody = body;
// Push frame on stack.
- _vm.CallStack.Push(dummyMethod);
+ _mainThread.CallStack.Push(dummyMethod);
var tokenSource = new CancellationTokenSource();
@@ -123,7 +140,7 @@ public void CancelShouldThrow()
tokenSource.Cancel();
};
- Assert.Throws(() => _vm.Run(tokenSource.Token));;
+ Assert.Throws(() => _mainThread.Run(tokenSource.Token));;
}
[Fact]
@@ -151,10 +168,10 @@ public void StepMultiple()
dummyMethod.CilMethodBody = body;
// Push frame on stack.
- var frame = _vm.CallStack.Push(dummyMethod);
+ var frame = _mainThread.CallStack.Push(dummyMethod);
for (int i = 0; i < 5; i++)
- _vm.Step();
+ _mainThread.Step();
var result = frame.EvaluationStack.Peek();
Assert.Equal((3 + 4) * 5, result.Contents.AsSpan().I32);
@@ -180,13 +197,13 @@ public void StepOverNonCallsShouldStepOnce()
dummyMethod.CilMethodBody.Instructions.CalculateOffsets();
// Step into method.
- _vm.CallStack.Push(dummyMethod);
+ _mainThread.CallStack.Push(dummyMethod);
// Step over first instruction.
- _vm.StepOver();
+ _mainThread.StepOver();
// We expect to just have moved to the second instruction.
- Assert.Equal(dummyMethod.CilMethodBody.Instructions[1].Offset, _vm.CallStack.Peek().ProgramCounter);
+ Assert.Equal(dummyMethod.CilMethodBody.Instructions[1].Offset, _mainThread.CallStack.Peek().ProgramCounter);
}
[Fact]
@@ -224,15 +241,15 @@ public void StepCallShouldContinueInFunction()
bar.CilMethodBody.Instructions.CalculateOffsets();
// Step into method.
- _vm.CallStack.Push(foo);
+ _mainThread.CallStack.Push(foo);
// Single-step instruction.
_vm.Invoker = DefaultInvokers.StepIn;
- _vm.Step();
+ _mainThread.Step();
// We expect to have completed "Bar" in its entirety, and moved to the second instruction.
- Assert.Equal(bar, _vm.CallStack.Peek().Method);
- Assert.Equal(bar.CilMethodBody.Instructions[0].Offset, _vm.CallStack.Peek().ProgramCounter);
+ Assert.Equal(bar, _mainThread.CallStack.Peek().Method);
+ Assert.Equal(bar.CilMethodBody.Instructions[0].Offset, _mainThread.CallStack.Peek().ProgramCounter);
}
[Fact]
@@ -270,14 +287,14 @@ public void StepOverCallShouldContinueUntilInstructionAfter()
bar.CilMethodBody.Instructions.CalculateOffsets();
// Step into method.
- _vm.CallStack.Push(foo);
+ _mainThread.CallStack.Push(foo);
// Step over first instruction.
- _vm.StepOver();
+ _mainThread.StepOver();
// We expect to have completed "Bar" in its entirety, and moved to the second instruction.
- Assert.Equal(foo, _vm.CallStack.Peek().Method);
- Assert.Equal(foo.CilMethodBody.Instructions[1].Offset, _vm.CallStack.Peek().ProgramCounter);
+ Assert.Equal(foo, _mainThread.CallStack.Peek().Method);
+ Assert.Equal(foo.CilMethodBody.Instructions[1].Offset, _mainThread.CallStack.Peek().ProgramCounter);
}
[Fact]
@@ -304,14 +321,14 @@ public void StepOverBranchShouldContinueAtBranchTarget()
label.Instruction = foo.CilMethodBody.Instructions[^1];
// Step into method.
- _vm.CallStack.Push(foo);
+ _mainThread.CallStack.Push(foo);
// Step over first instruction.
- _vm.StepOver();
+ _mainThread.StepOver();
// We expect to have jumped.
- Assert.Equal(foo, _vm.CallStack.Peek().Method);
- Assert.Equal(label.Offset, _vm.CallStack.Peek().ProgramCounter);
+ Assert.Equal(foo, _mainThread.CallStack.Peek().Method);
+ Assert.Equal(label.Offset, _mainThread.CallStack.Peek().ProgramCounter);
}
[Fact]
@@ -378,7 +395,7 @@ public void CallFunctionWithLoop()
arraySpan.SliceArrayElement(_vm.ValueFactory, factory.Int32, i).Write(100 + i);
// Call Sum.
- var returnValue = _vm.Call(sum, new BitVector[] { arrayAddress });
+ var returnValue = _mainThread.Call(sum, new BitVector[] { arrayAddress });
Assert.NotNull(returnValue);
Assert.Equal(Enumerable.Range(100, 10).Sum(), returnValue!.AsSpan().I32);
}
@@ -432,7 +449,7 @@ public void CallNestedFunction()
_vm.Invoker = DefaultInvokers.StepIn;
// Call Foo.
- var returnValue = _vm.Call(foo, Array.Empty());
+ var returnValue = _mainThread.Call(foo, Array.Empty());
Assert.NotNull(returnValue);
Assert.Equal((3 + 4) * 5, returnValue!.AsSpan().I32);
}
@@ -486,7 +503,7 @@ public void CallWithReflection()
// Call it with a real string builder.
var builder = new StringBuilder();
- var returnValue = _vm.Call(foo, new object[] { builder, "John Doe" });
+ var returnValue = _mainThread.Call(foo, new object[] { builder, "John Doe" });
// Check result.
Assert.Null(returnValue);
@@ -498,7 +515,7 @@ public void CallTryFinallyNoException()
{
var method = _fixture.GetTestMethod(nameof(TestClass.TryFinally));
- var result = _vm.Call(method, new object[] { false });
+ var result = _mainThread.Call(method, new object[] { false });
Assert.NotNull(result);
Assert.Equal(101, result!.AsSpan().I32);
}
@@ -508,7 +525,7 @@ public void CallTryFinallyException()
{
var method = _fixture.GetTestMethod(nameof(TestClass.TryFinally));
- var result = Assert.Throws(() => _vm.Call(method, new object[] {true}));
+ var result = Assert.Throws(() => _mainThread.Call(method, new object[] {true}));
Assert.Equal("System.Exception", result.ExceptionObject.GetObjectType().FullName);
}
@@ -537,7 +554,7 @@ public void CallAndThrowHandledException(string methodName, object parameter)
var reflectionMethod = typeof(TestClass).GetMethod(methodName);
int expectedResult = (int) reflectionMethod!.Invoke(null, new[] {parameter})!;
- var result = _vm.Call(method, new[] {parameter});
+ var result = _mainThread.Call(method, new[] {parameter});
Assert.NotNull(result);
Assert.Equal(expectedResult, result!.AsSpan().I32);
@@ -566,7 +583,7 @@ public void CallAndThrowUnhandledException(string methodName, object parameter)
expectedException = ex.InnerException!;
}
- var result = Assert.Throws(() => _vm.Call(method, new[] {parameter}));
+ var result = Assert.Throws(() => _mainThread.Call(method, new[] {parameter}));
Assert.Equal(expectedException.GetType().FullName, result.ExceptionObject.GetObjectType().FullName);
}
diff --git a/test/Platforms/Echo.Platforms.AsmResolver.Tests/Emulation/Dispatch/CilDispatcherTest.cs b/test/Platforms/Echo.Platforms.AsmResolver.Tests/Emulation/Dispatch/CilDispatcherTest.cs
index d71ee0f0..29a6b101 100644
--- a/test/Platforms/Echo.Platforms.AsmResolver.Tests/Emulation/Dispatch/CilDispatcherTest.cs
+++ b/test/Platforms/Echo.Platforms.AsmResolver.Tests/Emulation/Dispatch/CilDispatcherTest.cs
@@ -29,9 +29,10 @@ public CilDispatcherTest(MockModuleFixture fixture)
body.Instructions.Add(CilOpCodes.Ret);
var vm = new CilVirtualMachine(fixture.MockModule, false);
- vm.CallStack.Push(dummyMethod);
+ var thread = vm.CreateThread();
+ thread.CallStack.Push(dummyMethod);
- _context = new CilExecutionContext(vm, CancellationToken.None);
+ _context = new CilExecutionContext(thread, CancellationToken.None);
}
[Fact]
diff --git a/test/Platforms/Echo.Platforms.AsmResolver.Tests/Emulation/Dispatch/CilOpCodeHandlerTestBase.cs b/test/Platforms/Echo.Platforms.AsmResolver.Tests/Emulation/Dispatch/CilOpCodeHandlerTestBase.cs
index d8e4ade2..67533130 100644
--- a/test/Platforms/Echo.Platforms.AsmResolver.Tests/Emulation/Dispatch/CilOpCodeHandlerTestBase.cs
+++ b/test/Platforms/Echo.Platforms.AsmResolver.Tests/Emulation/Dispatch/CilOpCodeHandlerTestBase.cs
@@ -26,9 +26,10 @@ protected CilOpCodeHandlerTestBase(MockModuleFixture fixture)
body.Instructions.Add(CilOpCodes.Ret);
var vm = new CilVirtualMachine(fixture.MockModule, false);
- vm.CallStack.Push(dummyMethod);
+ var thread = vm.CreateThread();
+ thread.CallStack.Push(dummyMethod);
- Context = new CilExecutionContext(vm, CancellationToken.None);
+ Context = new CilExecutionContext(thread, CancellationToken.None);
Dispatcher = new CilDispatcher();
}
diff --git a/test/Platforms/Echo.Platforms.AsmResolver.Tests/Emulation/Dispatch/ControlFlow/RetHandlerTest.cs b/test/Platforms/Echo.Platforms.AsmResolver.Tests/Emulation/Dispatch/ControlFlow/RetHandlerTest.cs
index 34eaabd9..4f3e73eb 100644
--- a/test/Platforms/Echo.Platforms.AsmResolver.Tests/Emulation/Dispatch/ControlFlow/RetHandlerTest.cs
+++ b/test/Platforms/Echo.Platforms.AsmResolver.Tests/Emulation/Dispatch/ControlFlow/RetHandlerTest.cs
@@ -18,13 +18,13 @@ public RetHandlerTest(MockModuleFixture fixture)
[Fact]
public void RetFromVoidShouldPopFromCallStack()
{
- int currentFrameCount = Context.Machine.CallStack.Count;
+ int currentFrameCount = Context.Thread.CallStack.Count;
var instruction = new CilInstruction(CilOpCodes.Ret);
var result = Dispatcher.Dispatch(Context, instruction);
Assert.True(result.IsSuccess);
- Assert.Equal(currentFrameCount - 1, Context.Machine.CallStack.Count);
+ Assert.Equal(currentFrameCount - 1, Context.Thread.CallStack.Count);
}
[Fact]
@@ -32,7 +32,7 @@ public void ReturnShouldPushValueOntoStackMarshalled()
{
var method = new MethodDefinition("Dummy", MethodAttributes.Static,
MethodSignature.CreateStatic(ModuleFixture.MockModule.CorLibTypeFactory.Int32));
- var frame = Context.Machine.CallStack.Push(method);
+ var frame = Context.Thread.CallStack.Push(method);
frame.EvaluationStack.Push(new StackSlot(0x0123456789abcdef, StackSlotTypeHint.Integer));
var calleeFrame = Context.CurrentFrame;
diff --git a/test/Platforms/Echo.Platforms.AsmResolver.Tests/Emulation/Dispatch/ObjectModel/CallHandlerTest.cs b/test/Platforms/Echo.Platforms.AsmResolver.Tests/Emulation/Dispatch/ObjectModel/CallHandlerTest.cs
index 887a4fa5..41f4ce7e 100644
--- a/test/Platforms/Echo.Platforms.AsmResolver.Tests/Emulation/Dispatch/ObjectModel/CallHandlerTest.cs
+++ b/test/Platforms/Echo.Platforms.AsmResolver.Tests/Emulation/Dispatch/ObjectModel/CallHandlerTest.cs
@@ -162,7 +162,7 @@ public void ReturnShouldPushValueOntoStack()
{
var method = new MethodDefinition("Dummy", MethodAttributes.Static,
MethodSignature.CreateStatic(ModuleFixture.MockModule.CorLibTypeFactory.Int32));
- var frame = Context.Machine.CallStack.Push(method);
+ var frame = Context.Thread.CallStack.Push(method);
frame.EvaluationStack.Push(new StackSlot(0x1337, StackSlotTypeHint.Integer));
var calleeFrame = Context.CurrentFrame;
diff --git a/test/Platforms/Echo.Platforms.AsmResolver.Tests/Emulation/Dispatch/ObjectModel/CastHandlerTest.cs b/test/Platforms/Echo.Platforms.AsmResolver.Tests/Emulation/Dispatch/ObjectModel/CastHandlerTest.cs
index 831e8d4c..d9949267 100644
--- a/test/Platforms/Echo.Platforms.AsmResolver.Tests/Emulation/Dispatch/ObjectModel/CastHandlerTest.cs
+++ b/test/Platforms/Echo.Platforms.AsmResolver.Tests/Emulation/Dispatch/ObjectModel/CastHandlerTest.cs
@@ -195,7 +195,7 @@ public void CastGenericTypeReturnsInt16()
var methodSpecification = genericMethod.MakeGenericInstanceMethod(ModuleFixture.MockModule.CorLibTypeFactory.Int16);
- Context.Machine.CallStack.Push(methodSpecification);
+ Context.Thread.CallStack.Push(methodSpecification);
var value = 32000;
@@ -225,7 +225,7 @@ public void CastGenericTypeReturnsInt32()
var methodSpecification = genericMethod.MakeGenericInstanceMethod(ModuleFixture.MockModule.CorLibTypeFactory.Int32);
- Context.Machine.CallStack.Push(methodSpecification);
+ Context.Thread.CallStack.Push(methodSpecification);
var value = 214000000;
@@ -254,7 +254,7 @@ public void CastGenericTypeReturnsInt64()
var methodSpecification = genericMethod.MakeGenericInstanceMethod(ModuleFixture.MockModule.CorLibTypeFactory.Int64);
- Context.Machine.CallStack.Push(methodSpecification);
+ Context.Thread.CallStack.Push(methodSpecification);
var value = 922000000000000000L;
diff --git a/test/Platforms/Echo.Platforms.AsmResolver.Tests/Emulation/Dispatch/ObjectModel/LdFldHandlerTest.cs b/test/Platforms/Echo.Platforms.AsmResolver.Tests/Emulation/Dispatch/ObjectModel/LdFldHandlerTest.cs
index 3d491c81..43d92506 100644
--- a/test/Platforms/Echo.Platforms.AsmResolver.Tests/Emulation/Dispatch/ObjectModel/LdFldHandlerTest.cs
+++ b/test/Platforms/Echo.Platforms.AsmResolver.Tests/Emulation/Dispatch/ObjectModel/LdFldHandlerTest.cs
@@ -114,7 +114,7 @@ public void ReadInstanceFieldFromStructureByReference()
LocalVariables = {new CilLocalVariable(structType.ToTypeSignature())}
};
- Context.Machine.CallStack.Push(method);
+ Context.Thread.CallStack.Push(method);
// Initialize variable with field set to 1337.
var instance = factory.CreateValue(structType.ToTypeSignature(), true);
diff --git a/test/Platforms/Echo.Platforms.AsmResolver.Tests/Emulation/Dispatch/ObjectModel/LdFldaHandlerTest.cs b/test/Platforms/Echo.Platforms.AsmResolver.Tests/Emulation/Dispatch/ObjectModel/LdFldaHandlerTest.cs
index d4dfd645..0f8626ea 100644
--- a/test/Platforms/Echo.Platforms.AsmResolver.Tests/Emulation/Dispatch/ObjectModel/LdFldaHandlerTest.cs
+++ b/test/Platforms/Echo.Platforms.AsmResolver.Tests/Emulation/Dispatch/ObjectModel/LdFldaHandlerTest.cs
@@ -60,7 +60,7 @@ public void GetInstanceFieldFromStructureByReference()
LocalVariables = {new CilLocalVariable(structType.ToTypeSignature())}
};
- Context.Machine.CallStack.Push(method);
+ Context.Thread.CallStack.Push(method);
// Push address to struct.
var stack = Context.CurrentFrame.EvaluationStack;
diff --git a/test/Platforms/Echo.Platforms.AsmResolver.Tests/Emulation/Dispatch/ObjectModel/StFldHandlerTest.cs b/test/Platforms/Echo.Platforms.AsmResolver.Tests/Emulation/Dispatch/ObjectModel/StFldHandlerTest.cs
index 979aa68f..53a4a1ca 100644
--- a/test/Platforms/Echo.Platforms.AsmResolver.Tests/Emulation/Dispatch/ObjectModel/StFldHandlerTest.cs
+++ b/test/Platforms/Echo.Platforms.AsmResolver.Tests/Emulation/Dispatch/ObjectModel/StFldHandlerTest.cs
@@ -87,7 +87,7 @@ public void WriteInstanceFieldFromStructureByReference()
LocalVariables = {new CilLocalVariable(structType.ToTypeSignature())}
};
- Context.Machine.CallStack.Push(method);
+ Context.Thread.CallStack.Push(method);
// Push address to struct.
var stack = Context.CurrentFrame.EvaluationStack;
diff --git a/test/Platforms/Echo.Platforms.AsmResolver.Tests/Emulation/Dispatch/Variables/ArgumentsTest.cs b/test/Platforms/Echo.Platforms.AsmResolver.Tests/Emulation/Dispatch/Variables/ArgumentsTest.cs
index 8aba0b7d..12ba25b5 100644
--- a/test/Platforms/Echo.Platforms.AsmResolver.Tests/Emulation/Dispatch/Variables/ArgumentsTest.cs
+++ b/test/Platforms/Echo.Platforms.AsmResolver.Tests/Emulation/Dispatch/Variables/ArgumentsTest.cs
@@ -18,7 +18,7 @@ public ArgumentsTest(MockModuleFixture fixture)
{
}
- private void PrepareMethodWithArgument(int localCount, TypeSignature localType)
+ private void PrepareMethodWithArgument(int localCount, TypeSignature localType)
{
var factory = ModuleFixture.MockModule.CorLibTypeFactory;
var method = new MethodDefinition("DummyMethod", MethodAttributes.Static,
@@ -31,7 +31,7 @@ private void PrepareMethodWithArgument(int localCount, TypeSignature localType)
var body = new CilMethodBody(method);
method.CilMethodBody = body;
- Context.Machine.CallStack.Push(method);
+ Context.Thread.CallStack.Push(method);
}
[Theory]
diff --git a/test/Platforms/Echo.Platforms.AsmResolver.Tests/Emulation/Dispatch/Variables/LocalsTest.cs b/test/Platforms/Echo.Platforms.AsmResolver.Tests/Emulation/Dispatch/Variables/LocalsTest.cs
index 3a993bf8..81cdf290 100644
--- a/test/Platforms/Echo.Platforms.AsmResolver.Tests/Emulation/Dispatch/Variables/LocalsTest.cs
+++ b/test/Platforms/Echo.Platforms.AsmResolver.Tests/Emulation/Dispatch/Variables/LocalsTest.cs
@@ -29,7 +29,7 @@ private void PrepareMethodWithLocal(int localCount, TypeSignature localType)
body.LocalVariables.Add(new CilLocalVariable(localType));
method.CilMethodBody = body;
- Context.Machine.CallStack.Push(method);
+ Context.Thread.CallStack.Push(method);
}
[Theory]
diff --git a/test/Platforms/Echo.Platforms.AsmResolver.Tests/Emulation/Invocation/MethodInvokerTest.cs b/test/Platforms/Echo.Platforms.AsmResolver.Tests/Emulation/Invocation/MethodInvokerTest.cs
index a18ccb12..897a0dc7 100644
--- a/test/Platforms/Echo.Platforms.AsmResolver.Tests/Emulation/Invocation/MethodInvokerTest.cs
+++ b/test/Platforms/Echo.Platforms.AsmResolver.Tests/Emulation/Invocation/MethodInvokerTest.cs
@@ -22,8 +22,9 @@ public MethodInvokerTest(MockModuleFixture fixture)
_fixture = fixture;
var machine = new CilVirtualMachine(fixture.MockModule, false);
- _context = new CilExecutionContext(machine, CancellationToken.None);
- _context.Machine.CallStack.Push(fixture.MockModule.GetOrCreateModuleConstructor());
+ var thread = machine.CreateThread();
+ _context = new CilExecutionContext(thread, CancellationToken.None);
+ _context.Thread.CallStack.Push(fixture.MockModule.GetOrCreateModuleConstructor());
}
[Fact]
diff --git a/test/Platforms/Echo.Platforms.AsmResolver.Tests/Emulation/Stack/ExceptionHandlerFrameTest.cs b/test/Platforms/Echo.Platforms.AsmResolver.Tests/Emulation/Stack/ExceptionHandlerFrameTest.cs
index d18dbb5f..ef0d3846 100644
--- a/test/Platforms/Echo.Platforms.AsmResolver.Tests/Emulation/Stack/ExceptionHandlerFrameTest.cs
+++ b/test/Platforms/Echo.Platforms.AsmResolver.Tests/Emulation/Stack/ExceptionHandlerFrameTest.cs
@@ -15,16 +15,18 @@ public class ExceptionHandlerFrameTest : IClassFixture
{
private readonly MockModuleFixture _fixture;
private readonly CilVirtualMachine _vm;
+ private readonly CilThread _mainThread;
public ExceptionHandlerFrameTest(MockModuleFixture fixture)
{
_fixture = fixture;
_vm = new CilVirtualMachine(_fixture.MockModule, false);
+ _mainThread = _vm.CreateThread();
}
private CallFrame GetMockFrame(string methodName)
{
- return _vm.CallStack.Push(_fixture.GetTestMethod(methodName));
+ return _mainThread.CallStack.Push(_fixture.GetTestMethod(methodName));
}
[Fact]
diff --git a/test/Platforms/Echo.Platforms.AsmResolver.Tests/Emulation/Stack/ExceptionHandlerStackTest.cs b/test/Platforms/Echo.Platforms.AsmResolver.Tests/Emulation/Stack/ExceptionHandlerStackTest.cs
index be9b844d..e425407b 100644
--- a/test/Platforms/Echo.Platforms.AsmResolver.Tests/Emulation/Stack/ExceptionHandlerStackTest.cs
+++ b/test/Platforms/Echo.Platforms.AsmResolver.Tests/Emulation/Stack/ExceptionHandlerStackTest.cs
@@ -20,16 +20,18 @@ public class ExceptionHandlerStackTest : IClassFixture
{
private readonly MockModuleFixture _fixture;
private readonly CilVirtualMachine _vm;
+ private readonly CilThread _mainThread;
public ExceptionHandlerStackTest(MockModuleFixture fixture)
{
_fixture = fixture;
_vm = new CilVirtualMachine(_fixture.MockModule, false);
+ _mainThread = _vm.CreateThread();
}
private CallFrame GetMockFrame(string methodName)
{
- return _vm.CallStack.Push(_fixture.GetTestMethod(methodName));
+ return _mainThread.CallStack.Push(_fixture.GetTestMethod(methodName));
}
private ObjectHandle GetMockException(Type type)