Skip to content

Commit

Permalink
Add object allocator interfaces.
Browse files Browse the repository at this point in the history
  • Loading branch information
Washi1337 committed Mar 10, 2024
1 parent 807be04 commit c240669
Show file tree
Hide file tree
Showing 14 changed files with 321 additions and 30 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,8 @@
</ItemGroup>

<ItemGroup>
<PackageReference Include="AsmResolver.DotNet" Version="5.2.0" />
<PackageReference Include="Nullable" Version="1.3.0">
<PackageReference Include="AsmResolver.DotNet" Version="5.5.1" />
<PackageReference Include="Nullable" Version="1.3.1">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,15 @@ public CilDispatcher Dispatcher
get;
}

/// <summary>
/// Gets the service that is responsible for allocating new objects.
/// </summary>
public IObjectAllocator Allocator
{
get;
set;
} = DefaultAllocators.VirtualHeap;

/// <summary>
/// Gets the service that is responsible for invoking external functions or methods.
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ protected IList<BitVector> GetArguments(CilExecutionContext context, IMethodDesc
return result;
}

private BitVector GetInstancePointer(CilExecutionContext context, IMethodDescriptor method)
private static BitVector GetInstancePointer(CilExecutionContext context, IMethodDescriptor method)
{
var factory = context.Machine.ValueFactory;
var stack = context.CurrentFrame.EvaluationStack;
Expand Down Expand Up @@ -161,7 +161,11 @@ private static CilDispatchResult Invoke(CilExecutionContext context, IMethodDesc
case InvocationResultType.StepOver:
// Method was fully handled by the invoker, push result if it produced any.
if (result.Value is not null)
context.CurrentFrame.EvaluationStack.Push(result.Value, method.Signature!.ReturnType, true);
{
var genericContext = GenericContext.FromMethod(method);
var returnType = method.Signature!.ReturnType.InstantiateGenericTypes(genericContext);
context.CurrentFrame.EvaluationStack.Push(result.Value, returnType, true);
}

return CilDispatchResult.Success();

Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
using System;
using System.Collections.Generic;
using AsmResolver.DotNet;
using AsmResolver.PE.DotNet.Cil;
using Echo.Memory;
using Echo.Platforms.AsmResolver.Emulation.Invocation;

namespace Echo.Platforms.AsmResolver.Emulation.Dispatch.ObjectModel
{
Expand All @@ -15,27 +17,43 @@ public class NewObjHandler : CallHandlerBase
public override CilDispatchResult Dispatch(CilExecutionContext context, CilInstruction instruction)
{
var stack = context.CurrentFrame.EvaluationStack;
var factory = context.Machine.ValueFactory;

// Allocate the new object.
var constructor = (IMethodDescriptor) instruction.Operand!;
var instanceType = constructor.DeclaringType!.ToTypeSignature();
var address = factory.CreateNativeInteger(context.Machine.Heap.AllocateObject(instanceType, true));

var arguments = GetArguments(context, constructor);
try
{
// Insert the allocated "this" pointer into the arguments.
arguments.Insert(0, address);

// Call the constructor.
var result = HandleCall(context, instruction, arguments);
var allocation = context.Machine.Allocator.Allocate(context, constructor, arguments);
switch (allocation.ResultType)
{
case AllocationResultType.Inconclusive:
throw new CilEmulatorException($"Allocation of object of type {instanceType} was inconclusive");

case AllocationResultType.Allocated:
// Insert the allocated "this" pointer into the arguments and call constructor.
arguments.Insert(0, allocation.Address!);
var result = HandleCall(context, instruction, arguments);

// If successful, push the resulting object onto the stack.
if (result.IsSuccess)
stack.Push(address, instanceType);
// If successful, push the resulting object onto the stack.
if (result.IsSuccess)
stack.Push(allocation.Address!, instanceType);

return result;

case AllocationResultType.FullyConstructed:
// Fully constructed objects do not have to be post-processed.
stack.Push(allocation.Address!, instanceType);
context.CurrentFrame.ProgramCounter += instruction.Size;
return CilDispatchResult.Success();

case AllocationResultType.Exception:
return CilDispatchResult.Exception(allocation.ExceptionObject);

return result;
default:
throw new ArgumentOutOfRangeException();
}
}
finally
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -183,7 +183,10 @@ public static BitVectorSpan SliceStringLength(this BitVectorSpan span, ValueFact
/// <returns>The slice that contains the raw characters of the string.</returns>
public static BitVectorSpan SliceStringData(this BitVectorSpan span, ValueFactory factory)
{
return span.Slice((int) (factory.StringHeaderSize * 8));
return span.Slice(
(int)(factory.StringHeaderSize * 8),
(int)((span.ByteCount - 2 - factory.StringHeaderSize) * 8)
);
}

/// <summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,9 @@ public long AllocateString(int length, bool initialize)
// Set string length field.
chunkSpan.SliceStringLength(_factory).Write(length);

// Write null-terminator.
chunkSpan.Slice(chunkSpan.Count - 16).Write((ushort) 0);

return address;
}

Expand All @@ -148,6 +151,9 @@ public long AllocateString(BitVector contents)

// Write string contents.
chunkSpan.SliceStringData(_factory).Write(contents);

// Write null-terminator.
chunkSpan.Slice(chunkSpan.Count - 16 - 1).U16 = 0;

return address;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using Echo.Memory;

namespace Echo.Platforms.AsmResolver.Emulation.Invocation;

/// <summary>
/// Describes the result of an allocation of an object.
/// </summary>
[DebuggerDisplay("{Tag,nq}({DebuggerDisplay})")]
public readonly struct AllocationResult
{
private AllocationResult(AllocationResultType resultType, BitVector? address, ObjectHandle exceptionObject)
{
ResultType = resultType;
Address = address;
ExceptionObject = exceptionObject;
}

/// <summary>
/// Gets the type of result this object contains.
/// </summary>
public AllocationResultType ResultType { get; }

/// <summary>
/// Gets a value indicating whether the invocation was inconclusive and not handled yet.
/// </summary>
public bool IsInconclusive => ResultType is AllocationResultType.Inconclusive;

/// <summary>
/// Determines whether the invocation was successful.
/// </summary>
[MemberNotNullWhen(true, nameof(Address))]
public bool IsSuccess => ResultType is AllocationResultType.Allocated or AllocationResultType.FullyConstructed;

/// <summary>
/// When <see cref="ResultType"/> is <see cref="InvocationResultType.StepOver"/>, gets the address of the object
/// that was allocated or constructed.
/// </summary>
public BitVector? Address { get; }

/// <summary>
/// When <see cref="ResultType"/> is <see cref="InvocationResultType.Exception"/>, gets the handle to the
/// exception object that was thrown.
/// </summary>
public ObjectHandle ExceptionObject { get; }

[DebuggerBrowsable(DebuggerBrowsableState.Never)]
internal string Tag => ResultType.ToString();

[DebuggerBrowsable(DebuggerBrowsableState.Never)]
internal object? DebuggerDisplay => IsSuccess ? Address : ExceptionObject;

/// <summary>
/// Constructs a new inconclusive allocation result, where the allocation was not handled yet.
/// </summary>
public static AllocationResult Inconclusive() => new(AllocationResultType.Inconclusive, null, default);

/// <summary>
/// Constructs a new conclusive allocation result, where the object was successfully allocated but not yet
/// initialized yet.
/// </summary>
/// <param name="address">The address of the allocated object.</param>
public static AllocationResult Allocated(BitVector address) => new(AllocationResultType.Allocated, address, default);

/// <summary>
/// Constructs a new conclusive allocation result, where the object was successfully allocated and is also fully
/// initialized by a constructor.
/// </summary>
/// <param name="address">The address of the allocated object.</param>
public static AllocationResult FullyConstructed(BitVector address) => new(AllocationResultType.FullyConstructed, address, default);

/// <summary>
/// Constructs a new failed allocation result with the provided pointer to an exception object describing the
/// error that occurred.
/// </summary>
/// <param name="exceptionObject">The handle to the exception object that was thrown.</param>
public static AllocationResult Exception(ObjectHandle exceptionObject) =>
new(AllocationResultType.Exception, null, exceptionObject);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
namespace Echo.Platforms.AsmResolver.Emulation.Invocation;

/// <summary>
/// Provides members describing the different types of allocation results that can be produced during an object
/// allocation in a CIL virtual machine.
/// </summary>
public enum AllocationResultType
{
/// <summary>
/// Indicates the allocation was not handled yet.
/// </summary>
Inconclusive,

/// <summary>
/// Indicates the object was allocated but not initialized yet and a constructor should be called.
/// </summary>
Allocated,

/// <summary>
/// Indicates the object was allocated and also initialized.
/// </summary>
FullyConstructed,

/// <summary>
/// Indicates the allocation failed with an exception.
/// </summary>
Exception
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
namespace Echo.Platforms.AsmResolver.Emulation.Invocation;

/// <summary>
/// Provides methods for constructing object allocators using a set of default allocators implementations.
/// </summary>
public static class DefaultAllocators
{
/// <summary>
/// Gets an object allocator that returns unknown addresses.
/// </summary>
public static UnknownAddressAllocator UnknownAddress => UnknownAddressAllocator.Instance;

/// <summary>
/// Gets an allocator that allocates objects in the virtualalized heap of the underlying virtual machine.
/// </summary>
public static VirtualHeapAllocator VirtualHeap => VirtualHeapAllocator.Instance;

/// <summary>
/// Chains the first object allocator with the provided object allocator in such a way that if the result of the
/// first allocator is inconclusive, the second allocator will be used as a fallback allocator.
/// </summary>
/// <param name="self">The first object allocator</param>
/// <param name="other">The fallback object allocator</param>
/// <returns>The constructed allocator chain.</returns>
public static ObjectAllocatorChain WithFallback(this IObjectAllocator self, IObjectAllocator other)
{
if (self is not ObjectAllocatorChain chain)
{
chain = new ObjectAllocatorChain();
chain.Allocators.Add(self);
}

chain.Allocators.Add(other);
return chain;
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
using System.Collections.Generic;
using AsmResolver.DotNet;
using Echo.Memory;
using Echo.Platforms.AsmResolver.Emulation.Dispatch;

namespace Echo.Platforms.AsmResolver.Emulation.Invocation;

/// <summary>
/// Provides members for emulating object allocation.
/// </summary>
public interface IObjectAllocator
{
/// <summary>
/// Allocates a new object with the provided constructor and arguments.
/// </summary>
/// <param name="context">The execution context the call originates from.</param>
/// <param name="ctor">The constructor to invoke after allocation.</param>
/// <param name="arguments">The arguments to invoke the constructor with.</param>
/// <returns>The result</returns>
AllocationResult Allocate(CilExecutionContext context, IMethodDescriptor ctor, IList<BitVector> arguments);
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@

using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using Echo.Memory;

namespace Echo.Platforms.AsmResolver.Emulation.Invocation
Expand All @@ -20,10 +21,7 @@ private InvocationResult(InvocationResultType resultType, BitVector? value, Obje
/// <summary>
/// Gets the type of result this object contains.
/// </summary>
public InvocationResultType ResultType
{
get;
}
public InvocationResultType ResultType { get; }

/// <summary>
/// Gets a value indicating whether the invocation was inconclusive and not handled yet.
Expand All @@ -33,25 +31,20 @@ public InvocationResultType ResultType
/// <summary>
/// Determines whether the invocation was successful.
/// </summary>
[MemberNotNullWhen(true, nameof(Value))]
public bool IsSuccess => ResultType is InvocationResultType.StepIn or InvocationResultType.StepOver;

/// <summary>
/// When <see cref="ResultType"/> is <see cref="InvocationResultType.StepOver"/>, gets the value that the
/// method returned (if available).
/// </summary>
public BitVector? Value
{
get;
}
public BitVector? Value { get; }

/// <summary>
/// When <see cref="ResultType"/> is <see cref="InvocationResultType.Exception"/>, gets the handle to the
/// exception object that was thrown.
/// </summary>
public ObjectHandle ExceptionObject
{
get;
}
public ObjectHandle ExceptionObject { get; }

[DebuggerBrowsable(DebuggerBrowsableState.Never)]
internal string Tag => ResultType.ToString();
Expand All @@ -65,12 +58,12 @@ public ObjectHandle ExceptionObject
public static InvocationResult Inconclusive() => new(InvocationResultType.Inconclusive, null, default);

/// <summary>
/// Constructs a new inconclusive invocation result, where the invocation was handled as a step-in action.
/// Constructs a new conclusive invocation result, where the invocation was handled as a step-in action.
/// </summary>
public static InvocationResult StepIn() => new(InvocationResultType.StepIn, null, default);

/// <summary>
/// Constructs a new inconclusive invocation result, where the invocation was fully handled by the invoker and
/// Constructs a new conclusive invocation result, where the invocation was fully handled by the invoker and
/// a result was produced.
/// </summary>
/// <param name="value">
Expand Down
Loading

0 comments on commit c240669

Please sign in to comment.