Closed Miista closed 8 months ago
As of right now, no, we cannot call Enum.IsDefined
from within PoseContext.Isolate
. I get the following stacktrace:
System.Reflection.TargetInvocationException: Exception has been thrown by the target of an invocation.
---> System.NotSupportedException: Cannot create boxed ByRef-like values.
at System.Runtime.CompilerServices.RuntimeHelpers.GetUninitializedObject(Type type)
at stub_newobj_System.ReadOnlySpan`1[System.Char]_.ctor(Char[])
at impl_System.ReadOnlySpan`1[System.Char]_op_Implicit(Char[])
at stub_call_System.ReadOnlySpan`1[System.Char]_op_Implicit(Char[])
at impl_System.IO.StreamWriter_Write(StreamWriter, Char[])
at stub_callvirt_System.IO.TextWriter_Write(TextWriter, Char[])
at impl_System.IO.TextWriter_WriteLine(TextWriter)
at stub_callvirt_System.IO.TextWriter_WriteLine(TextWriter)
at impl_System.IO.TextWriter_WriteLine(TextWriter, Boolean)
at stub_callvirt_System.IO.TextWriter_WriteLine(TextWriter, Boolean)
at impl_System.IO.TextWriter+SyncTextWriter_WriteLine(SyncTextWriter, Boolean)
at stub_callvirt_System.IO.TextWriter_WriteLine(TextWriter, Boolean)
at impl_System.Console_WriteLine(Boolean)
at stub_call_System.Console_WriteLine(Boolean)
at impl_Pose.Sandbox.Program+<>c_<Main>b__0_2(<>c)
at System.RuntimeMethodHandle.InvokeMethod(Object target, Void** arguments, Signature sig, Boolean isConstructor)
at System.Reflection.MethodBaseInvoker.InvokeDirectByRefWithFewArgs(Object obj, Span`1 copyOfArgs, BindingFlags invokeAttr)
--- End of inner exception stack trace ---
at System.Reflection.MethodBaseInvoker.InvokeDirectByRefWithFewArgs(Object obj, Span`1 copyOfArgs, BindingFlags invokeAttr)
at System.Reflection.MethodBaseInvoker.InvokeWithOneArg(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture)
at System.Delegate.DynamicInvokeImpl(Object[] args)
at Pose.PoseContext.Isolate(Action entryPoint, Shim[] shims) in C:\Rider\pose\src\Pose\PoseContext.cs:line 31
at Pose.Sandbox.Program.Main(String[] args) in C:\Rider\pose\src\Sandbox\Program.cs:line 26
Specifically the following line hints at the culprit:
System.NotSupportedException: Cannot create boxed ByRef-like values.
at System.Runtime.CompilerServices.RuntimeHelpers.GetUninitializedObject(Type type)
My investigation shows that this is caused by attempting to treat Span<T>
as a reference type.
The first line in GenerateStubForObjectInitialization
calls MakeByRefType
if the incoming type is a value type. This causes the remaining code to treat the type as a reference type. Unfortunately, this also causes the wrong IL to be emitted.
If I change the constructor stub generation to emit code as if Span<T>
was a value type (as it is!) everything works.
Concern: Does this impact all value type constructors?
No, it doesn't seem like it impacts all value type constructors. I attempted to call new DateTime(2004, 1, 1)
from within Isolate
. It compiled and executed without issues. So it would seem that this issue is solely caused by the call to new Span<T>
. This doesn't change the fact that we need to handle this as there is no guarantee that we won't encounter similar issues for other types in the future.
I found the issue. It turns out that it was completely self-inflicted. During my clean up of Stubs.cs
I accidently swapped constructor.DeclaringType.IsValueType
for thisType.IsValueType
.
TL;DR; Change thisType.IsValueType
to constructor.IsValueType
.
In the following, the line if (thisType.IsValueType)
is what's causing the exception.
public static DynamicMethod GenerateStubForObjectInitialization(ConstructorInfo constructor)
{
var thisType = constructor.DeclaringType ?? throw new Exception($"Method {constructor.Name} does not have a {nameof(MethodBase.DeclaringType)}");
if (thisType.IsValueType)
{
thisType = thisType.MakeByRefType();
}
if (thisType.IsValueType) // <-- THIS LINE IS CAUSING THE EXCEPTION
{
}
}
In the example, the line just above the offending line transforms the value type into a by-ref value type. This means that we never enter the if
statement. It is literally dead code.
The original code, however, used the incoming constructor
which does not change into a by-ref value type, thus entering the if
statement.
public static DynamicMethod GenerateStubForObjectInitialization(ConstructorInfo constructor)
{
var thisType = constructor.DeclaringType ?? throw new Exception($"Method {constructor.Name} does not have a {nameof(MethodBase.DeclaringType)}");
if (thisType.IsValueType)
{
thisType = thisType.MakeByRefType();
}
if (constructor.IsValueType) // <-- THIS LINE MAKES EVERYTHING WORK AGAIN!
{
}
}
I'm including this in a separate patch release. This means that it will go into v2.0.1.
Please refer to tonerdo/pose#26