-
Notifications
You must be signed in to change notification settings - Fork 32
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #138 from Washi1337/feature/type-initializers
Emulated Type Initializers
- Loading branch information
Showing
21 changed files
with
530 additions
and
47 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
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
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
47 changes: 47 additions & 0 deletions
47
...Platforms/Echo.Platforms.AsmResolver/Emulation/Dispatch/ObjectModel/FieldOpCodeHandler.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,47 @@ | ||
using AsmResolver.DotNet; | ||
using AsmResolver.PE.DotNet.Cil; | ||
|
||
namespace Echo.Platforms.AsmResolver.Emulation.Dispatch.ObjectModel; | ||
|
||
/// <summary> | ||
/// Represents a handler that handles opcodes related to field access. | ||
/// </summary> | ||
public abstract class FieldOpCodeHandler : ICilOpCodeHandler | ||
{ | ||
/// <inheritdoc /> | ||
public CilDispatchResult Dispatch(CilExecutionContext context, CilInstruction instruction) | ||
{ | ||
var field = (IFieldDescriptor) instruction.Operand!; | ||
|
||
// Ensure the enclosing type is initialized in the runtime. | ||
if (field.DeclaringType is { } declaringType) | ||
{ | ||
var initResult = context.Machine.TypeManager.HandleInitialization(context.Thread, declaringType); | ||
if (!initResult.IsNoAction) | ||
return initResult.ToDispatchResult(); | ||
} | ||
|
||
// Handle the actual field operation. | ||
var dispatchResult = DispatchInternal(context, instruction, field); | ||
|
||
// We are not inheriting from FallThroughOpCodeHandler because of the type initialization. | ||
// This means we need to manually increase the PC on success. | ||
if (dispatchResult.IsSuccess) | ||
context.CurrentFrame.ProgramCounter += instruction.Size; | ||
|
||
return dispatchResult; | ||
} | ||
|
||
/// <summary> | ||
/// Handles the actual operation on the field. | ||
/// </summary> | ||
/// <param name="context">The context to evaluate the instruction in.</param> | ||
/// <param name="instruction">The instruction to dispatch and evaluate.</param> | ||
/// <param name="field">The field to perform the operation on.</param> | ||
/// <returns>The dispatching result.</returns> | ||
protected abstract CilDispatchResult DispatchInternal( | ||
CilExecutionContext context, | ||
CilInstruction instruction, | ||
IFieldDescriptor field | ||
); | ||
} |
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
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
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
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
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
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
1 change: 1 addition & 0 deletions
1
src/Platforms/Echo.Platforms.AsmResolver/Emulation/Runtime/ClrMockMemory.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 |
---|---|---|
@@ -1,3 +1,4 @@ | ||
|
||
using System; | ||
using AsmResolver.DotNet; | ||
using AsmResolver.DotNet.Signatures; | ||
|
139 changes: 139 additions & 0 deletions
139
src/Platforms/Echo.Platforms.AsmResolver/Emulation/Runtime/RuntimeTypeManager.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,139 @@ | ||
using System.Collections.Concurrent; | ||
using System.Collections.Generic; | ||
using AsmResolver; | ||
using AsmResolver.DotNet; | ||
using AsmResolver.DotNet.Signatures; | ||
|
||
namespace Echo.Platforms.AsmResolver.Emulation.Runtime; | ||
|
||
/// <summary> | ||
/// Provides a mechanism for initialization and management of types residing in a virtual machine. | ||
/// </summary> | ||
public sealed class RuntimeTypeManager | ||
{ | ||
private readonly CilVirtualMachine _machine; | ||
|
||
private readonly ConcurrentDictionary<ITypeDescriptor, TypeInitialization> _initializations | ||
= new(SignatureComparer.Default); | ||
|
||
/// <summary> | ||
/// Creates a new runtime type manager. | ||
/// </summary> | ||
/// <param name="machine">The machine the type is made for.</param> | ||
public RuntimeTypeManager(CilVirtualMachine machine) | ||
{ | ||
_machine = machine; | ||
} | ||
|
||
private TypeInitialization GetInitialization(ITypeDescriptor type) | ||
{ | ||
if (_initializations.TryGetValue(type, out var initialization)) | ||
return initialization; | ||
|
||
var newInitialization = new TypeInitialization(type); | ||
while (!_initializations.TryGetValue(type, out initialization)) | ||
{ | ||
if (_initializations.TryAdd(type, newInitialization)) | ||
{ | ||
initialization = newInitialization; | ||
break; | ||
} | ||
} | ||
|
||
return initialization; | ||
} | ||
|
||
/// <summary> | ||
/// Registers the event that a type has failed to initialize. | ||
/// </summary> | ||
/// <param name="type">The type that failed to initialize.</param> | ||
/// <param name="innerException">The exception object that describes the failure.</param> | ||
/// <returns>The resulting TypeInitializationException instance.</returns> | ||
public ObjectHandle RegisterInitializationException(ITypeDescriptor type, ObjectHandle innerException) | ||
{ | ||
var initialization = GetInitialization(type); | ||
if (!initialization.Exception.IsNull) | ||
return initialization.Exception; | ||
|
||
lock (initialization) | ||
{ | ||
if (initialization.Exception.IsNull) | ||
{ | ||
initialization.Exception = _machine.Heap | ||
.AllocateObject(_machine.ValueFactory.TypeInitializationExceptionType, true) | ||
.AsObjectHandle(_machine); | ||
} | ||
|
||
// TODO: incorporate `innerException`. | ||
} | ||
|
||
return initialization.Exception; | ||
} | ||
|
||
/// <summary> | ||
/// Handles the type initialization on the provided thread. | ||
/// </summary> | ||
/// <param name="thread">The thread the initialization is to be called on.</param> | ||
/// <param name="type">The type to initialize.</param> | ||
/// <returns>The initialization result.</returns> | ||
public TypeInitializerResult HandleInitialization(CilThread thread, ITypeDescriptor type) | ||
{ | ||
var initialization = GetInitialization(type); | ||
|
||
// If we already have an exception cached as a result of a previous type-load failure, rethrow it. | ||
if (!initialization.Exception.IsNull) | ||
return TypeInitializerResult.Exception(initialization.Exception); | ||
|
||
// We only need to call the constructor once. | ||
if (initialization.ConstructorCalled) | ||
return TypeInitializerResult.NoAction(); | ||
|
||
lock (initialization) | ||
{ | ||
// Try check if any thread beat us in the initialization handling. | ||
if (!initialization.Exception.IsNull) | ||
return TypeInitializerResult.Exception(initialization.Exception); | ||
|
||
if (initialization.ConstructorCalled) | ||
return TypeInitializerResult.NoAction(); | ||
|
||
// Try resolve the type that is being initialized. | ||
var definition = type.Resolve(); | ||
if (definition is null) | ||
{ | ||
initialization.Exception = _machine.Heap | ||
.AllocateObject(_machine.ValueFactory.TypeInitializationExceptionType, true) | ||
.AsObjectHandle(_machine); | ||
|
||
return TypeInitializerResult.Exception(initialization.Exception); | ||
} | ||
|
||
// "Call" the constructor. | ||
initialization.ConstructorCalled = true; | ||
|
||
// Actually find the constructor and call it if it is there. | ||
var cctor = definition.GetStaticConstructor(); | ||
if (cctor is not null) | ||
{ | ||
thread.CallStack.Push(cctor); | ||
return TypeInitializerResult.Redirected(); | ||
} | ||
|
||
return TypeInitializerResult.NoAction(); | ||
} | ||
} | ||
|
||
private sealed class TypeInitialization | ||
{ | ||
public TypeInitialization(ITypeDescriptor type) | ||
{ | ||
Type = type; | ||
} | ||
|
||
public ITypeDescriptor Type { get; } | ||
|
||
public bool ConstructorCalled { get; set; } | ||
|
||
public ObjectHandle Exception { get; set; } | ||
} | ||
} |
Oops, something went wrong.