-
Notifications
You must be signed in to change notification settings - Fork 33
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add default shims for Unsafe, RuntimeHelpers and MemoryMarshal.
- Loading branch information
Showing
5 changed files
with
306 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
39 changes: 39 additions & 0 deletions
39
src/Platforms/Echo.Platforms.AsmResolver/Emulation/Invocation/IntrinsicsInvoker.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
using System.Collections.Generic; | ||
using AsmResolver.DotNet; | ||
using Echo.Memory; | ||
using Echo.Platforms.AsmResolver.Emulation.Dispatch; | ||
|
||
namespace Echo.Platforms.AsmResolver.Emulation.Invocation; | ||
|
||
/// <summary> | ||
/// Implements a method invoker that shims methods in the <c>System.Runtime.Intrinsics</c> namespace. | ||
/// </summary> | ||
public class IntrinsicsInvoker : IMethodInvoker | ||
{ | ||
/// <summary> | ||
/// Gets the singleton instance of the <see cref="IntrinsicsInvoker"/> class. | ||
/// </summary> | ||
public static IntrinsicsInvoker Instance { get; } = new(); | ||
|
||
/// <inheritdoc /> | ||
public InvocationResult Invoke(CilExecutionContext context, IMethodDescriptor method, IList<BitVector> arguments) | ||
{ | ||
if (method is not { DeclaringType: {} declaringType, Name: {} name }) | ||
return InvocationResult.Inconclusive(); | ||
|
||
if (declaringType.Namespace != "System.Runtime.Intrinsics") | ||
return InvocationResult.Inconclusive(); | ||
|
||
return name.Value switch | ||
{ | ||
"get_IsHardwareAccelerated" => InvokeIsHardwareAccelerated(context), | ||
_ => InvocationResult.Inconclusive() | ||
}; | ||
} | ||
|
||
private static InvocationResult InvokeIsHardwareAccelerated(CilExecutionContext context) | ||
{ | ||
// We assume no hardware acceleration. | ||
return InvocationResult.StepOver(context.Machine.ValueFactory.RentBoolean(false)); | ||
} | ||
} |
40 changes: 40 additions & 0 deletions
40
src/Platforms/Echo.Platforms.AsmResolver/Emulation/Invocation/MemoryMarshalInvoker.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
using System.Collections.Generic; | ||
using AsmResolver.DotNet; | ||
using Echo.Memory; | ||
using Echo.Platforms.AsmResolver.Emulation.Dispatch; | ||
|
||
namespace Echo.Platforms.AsmResolver.Emulation.Invocation; | ||
|
||
/// <summary> | ||
/// Implements a method invoker that shims methods in the <see cref="System.Runtime.InteropServices.MemoryMarshal"/> class. | ||
/// </summary> | ||
public class MemoryMarshalInvoker : IMethodInvoker | ||
{ | ||
/// <summary> | ||
/// Gets the singleton instance of the <see cref="MemoryMarshalInvoker"/> class. | ||
/// </summary> | ||
public static MemoryMarshalInvoker Instance { get; } = new(); | ||
|
||
/// <inheritdoc /> | ||
public InvocationResult Invoke(CilExecutionContext context, IMethodDescriptor method, IList<BitVector> arguments) | ||
{ | ||
if (method is not { DeclaringType: { } declaringType, Name: { } name }) | ||
return InvocationResult.Inconclusive(); | ||
|
||
if (!declaringType.IsTypeOf("System.Runtime.InteropServices", "MemoryMarshal")) | ||
return InvocationResult.Inconclusive(); | ||
|
||
return name.Value switch | ||
{ | ||
"GetArrayDataReference" => InvokeGetArrayDataReference(context, arguments), | ||
_ => InvocationResult.Inconclusive() | ||
}; | ||
} | ||
|
||
private static InvocationResult InvokeGetArrayDataReference(CilExecutionContext context, IList<BitVector> arguments) | ||
{ | ||
var arrayObject = arguments[0].AsObjectHandle(context.Machine); | ||
long result = arrayObject.Address + context.Machine.ValueFactory.ArrayHeaderSize; | ||
return InvocationResult.StepOver(context.Machine.ValueFactory.RentNativeInteger(result)); | ||
} | ||
} |
68 changes: 68 additions & 0 deletions
68
src/Platforms/Echo.Platforms.AsmResolver/Emulation/Invocation/RuntimeHelpersInvoker.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,68 @@ | ||
using System.Collections.Generic; | ||
using System.Runtime.CompilerServices; | ||
using AsmResolver; | ||
using AsmResolver.DotNet; | ||
using Echo.Memory; | ||
using Echo.Platforms.AsmResolver.Emulation.Dispatch; | ||
|
||
namespace Echo.Platforms.AsmResolver.Emulation.Invocation; | ||
|
||
/// <summary> | ||
/// Implements a method invoker that shims methods from the <see cref="RuntimeHelpers"/> class. | ||
/// </summary> | ||
public class RuntimeHelpersInvoker : IMethodInvoker | ||
{ | ||
/// <summary> | ||
/// Gets the singleton instance for the <see cref="RuntimeHelpersInvoker"/> class. | ||
/// </summary> | ||
public static RuntimeHelpersInvoker Instance { get; } = new(); | ||
|
||
/// <inheritdoc /> | ||
public InvocationResult Invoke(CilExecutionContext context, IMethodDescriptor method, IList<BitVector> arguments) | ||
{ | ||
if (method is not {DeclaringType: {} declaringType, Name: {} name}) | ||
return InvocationResult.Inconclusive(); | ||
|
||
if (!declaringType.IsTypeOf("System.Runtime.CompilerServices", "RuntimeHelpers")) | ||
return InvocationResult.Inconclusive(); | ||
|
||
return name.Value switch | ||
{ | ||
"IsReferenceOrContainsReferences" => InvokeIsReferenceOrContainsReferences(context, method), | ||
"InitializeArray" => InvokeInitializeArray(context, arguments), | ||
_ => InvocationResult.Inconclusive() | ||
}; | ||
} | ||
|
||
private static InvocationResult InvokeIsReferenceOrContainsReferences(CilExecutionContext context, IMethodDescriptor method) | ||
{ | ||
if (method is not MethodSpecification { Signature.TypeArguments: { Count: 1 } typeArguments }) | ||
return InvocationResult.Inconclusive(); | ||
|
||
// TODO: This is inaccurate (feature-blocked by https://github.com/Washi1337/AsmResolver/issues/530). | ||
bool result = !typeArguments[0].IsValueType; | ||
|
||
return InvocationResult.StepOver(context.Machine.ValueFactory.RentBoolean(result)); | ||
} | ||
|
||
private static InvocationResult InvokeInitializeArray(CilExecutionContext context, IList<BitVector> arguments) | ||
{ | ||
// Read parameters. | ||
var array = arguments[0].AsObjectHandle(context.Machine); | ||
var fieldHandle = arguments[1].AsStructHandle(context.Machine); | ||
|
||
// Resolve the field handle to a field descriptor. | ||
if (!context.Machine.ValueFactory.ClrMockMemory.Fields.TryGetObject(fieldHandle.Address, out var field)) | ||
return InvocationResult.Inconclusive(); | ||
|
||
// Resole the field behind the field descriptor. | ||
var definition = field!.Resolve(); | ||
|
||
// Read the data. | ||
if (definition?.FieldRva is not IReadableSegment segment) | ||
return InvocationResult.Inconclusive(); | ||
array.WriteArrayData(segment.ToArray()); | ||
|
||
return InvocationResult.StepOver(null); | ||
} | ||
} |
128 changes: 128 additions & 0 deletions
128
src/Platforms/Echo.Platforms.AsmResolver/Emulation/Invocation/UnsafeInvoker.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,128 @@ | ||
using System.Collections.Generic; | ||
using AsmResolver.DotNet; | ||
using Echo.Memory; | ||
using Echo.Platforms.AsmResolver.Emulation.Dispatch; | ||
|
||
namespace Echo.Platforms.AsmResolver.Emulation.Invocation; | ||
|
||
/// <summary> | ||
/// Implements a method invoker that shims methods from the <see cref="System.Runtime.CompilerServices.Unsafe"/> class. | ||
/// </summary> | ||
public class UnsafeInvoker : IMethodInvoker | ||
{ | ||
/// <summary> | ||
/// Gets the singleton instance of the <see cref="UnsafeInvoker"/> class. | ||
/// </summary> | ||
public static UnsafeInvoker Instance { get; } = new(); | ||
|
||
/// <inheritdoc /> | ||
public InvocationResult Invoke(CilExecutionContext context, IMethodDescriptor method, IList<BitVector> arguments) | ||
{ | ||
if (method is not { Name: { } name, DeclaringType: { } declaringType, Signature: { } signature }) | ||
return InvocationResult.Inconclusive(); | ||
|
||
if (!declaringType.IsTypeOf("System.Runtime.CompilerServices", "Unsafe") | ||
&& !declaringType.IsTypeOf("Internal.Runtime.CompilerServices", "Unsafe")) | ||
{ | ||
return InvocationResult.Inconclusive(); | ||
} | ||
|
||
// TODO: Add handlers for the remaining methods in the Unsafe class. | ||
switch (signature.ParameterTypes.Count) | ||
{ | ||
case 1: | ||
return name.Value switch | ||
{ | ||
"As" => InvokeAsOrAsRef(context, arguments), | ||
"AsRef" => InvokeAsOrAsRef(context, arguments), | ||
"Read" => InvokeReadUnaligned(context, method, arguments), | ||
"ReadUnaligned" => InvokeReadUnaligned(context, method, arguments), | ||
_ => InvocationResult.Inconclusive() | ||
}; | ||
|
||
case 2: | ||
return name.Value switch | ||
{ | ||
"AreSame" => InvokeAreSame(context, arguments), | ||
"Add" => InvokeAdd(context, method, arguments), | ||
"AddByteOffset" => InvokeAddByteOffset(context, arguments), | ||
_ => InvocationResult.Inconclusive() | ||
}; | ||
|
||
default: | ||
return InvocationResult.Inconclusive(); | ||
} | ||
} | ||
|
||
private static InvocationResult InvokeAsOrAsRef(CilExecutionContext context, IList<BitVector> arguments) | ||
{ | ||
// We don't do any GC tracking, thus returning the same input reference suffices. | ||
return InvocationResult.StepOver(arguments[0].Clone(context.Machine.ValueFactory.BitVectorPool)); | ||
} | ||
|
||
private static InvocationResult InvokeAreSame(CilExecutionContext context, IList<BitVector> arguments) | ||
{ | ||
var comparison = arguments[0].AsSpan().IsEqualTo(arguments[1]); | ||
var result = context.Machine.ValueFactory.RentBoolean(comparison); | ||
return InvocationResult.StepOver(result); | ||
} | ||
|
||
private static InvocationResult InvokeAddByteOffset(CilExecutionContext context, IList<BitVector> arguments) | ||
{ | ||
var result = arguments[0].Clone(context.Machine.ValueFactory.BitVectorPool); | ||
result.AsSpan().IntegerAdd(arguments[1]); | ||
return InvocationResult.StepOver(result); | ||
} | ||
|
||
private static InvocationResult InvokeAdd(CilExecutionContext context, IMethodDescriptor method, IList<BitVector> arguments) | ||
{ | ||
if (method is not MethodSpecification { Signature.TypeArguments: { Count: 1 } typeArguments }) | ||
throw new CilEmulatorException("Expected a method specification with a single type argument."); | ||
|
||
var valueFactory = context.Machine.ValueFactory; | ||
var pool = valueFactory.BitVectorPool; | ||
|
||
// Determine the size of a single element. | ||
uint elementSize = valueFactory.GetTypeValueMemoryLayout(typeArguments[0]).Size; | ||
var elementSizeVector = valueFactory.RentNativeInteger(elementSize); | ||
|
||
// We need to resize the offset as Unsafe.Add<T> accepts both a nint and an int32 as offset. | ||
var elementOffset = arguments[1].Resize((int)(valueFactory.PointerSize * 8), true, pool); | ||
|
||
// The offset is the index multiplied by its element size. | ||
elementOffset.AsSpan().IntegerMultiply(elementSizeVector); | ||
|
||
// Add the offset to the start address. | ||
var source = arguments[0].Clone(pool); | ||
source.AsSpan().IntegerAdd(elementOffset); | ||
|
||
// Return temporary vectors. | ||
pool.Return(elementSizeVector); | ||
pool.Return(elementOffset); | ||
|
||
return InvocationResult.StepOver(source); | ||
} | ||
|
||
private static InvocationResult InvokeReadUnaligned( | ||
CilExecutionContext context, | ||
IMethodDescriptor method, | ||
IList<BitVector> arguments) | ||
{ | ||
// Should be a generic instance method. | ||
if (method is not MethodSpecification { Signature: { } signature }) | ||
return InvocationResult.Inconclusive(); | ||
|
||
// Pointer should be known to be able to read from it. | ||
if (!arguments[0].IsFullyKnown) | ||
return InvocationResult.Inconclusive(); | ||
|
||
// Concretize pointer. | ||
long resolvedAddress = arguments[0].AsSpan().ReadNativeInteger(context.Machine.Is32Bit); | ||
|
||
// Read from pointer. | ||
var result = context.Machine.ValueFactory.CreateValue(signature.TypeArguments[0], false); | ||
context.Machine.Memory.Read(resolvedAddress, result); | ||
|
||
return InvocationResult.StepOver(result); | ||
} | ||
} |