Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Enum.IsDefined cannot be called from within PoseContext.Isolate #26

Closed
Miista opened this issue Jan 24, 2024 · 4 comments · Fixed by #40
Closed

Enum.IsDefined cannot be called from within PoseContext.Isolate #26

Miista opened this issue Jan 24, 2024 · 4 comments · Fixed by #40
Labels
bug Something isn't working priority: low The issue has low priority
Milestone

Comments

@Miista
Copy link
Owner

Miista commented Jan 24, 2024

Please refer to tonerdo/pose#26

@Miista Miista added the question/investigation Further information or investigation is required label Jan 24, 2024
@Miista
Copy link
Owner Author

Miista commented Jan 25, 2024

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)

@Miista Miista added bug Something isn't working priority: low The issue has low priority and removed question/investigation Further information or investigation is required labels Jan 28, 2024
@Miista Miista changed the title Can Enum.IsDefined be called from within PoseContext.Isolate? Enum.IsDefined cannot be called from within PoseContext.Isolate` Jan 29, 2024
@Miista Miista changed the title Enum.IsDefined cannot be called from within PoseContext.Isolate` Enum.IsDefined cannot be called from within PoseContext.Isolate Jan 29, 2024
@Miista
Copy link
Owner Author

Miista commented Feb 1, 2024

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.

@Miista
Copy link
Owner Author

Miista commented Feb 2, 2024

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.

Detailed example

In the following, the line if (thisType.IsValueType) is what's causing the exception.

The cleaned up code

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.

The original code

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!
    {
    }
}

@Miista Miista modified the milestones: v2.1, v2.0.1 Feb 2, 2024
@Miista
Copy link
Owner Author

Miista commented Feb 2, 2024

I'm including this in a separate patch release. This means that it will go into v2.0.1.

Miista pushed a commit that referenced this issue Feb 2, 2024
@Miista Miista closed this as completed in #40 Feb 2, 2024
Miista added a commit that referenced this issue Feb 2, 2024
…from-within-posecontextisolate

#26: Enum.IsDefined cannot be called from within PoseContext.Isolate
Miista added a commit that referenced this issue May 2, 2024
Miista added a commit that referenced this issue May 2, 2024
…from-within-posecontextisolate

#26: Enum.IsDefined cannot be called from within PoseContext.Isolate
Miista added a commit that referenced this issue May 2, 2024
Miista added a commit that referenced this issue May 2, 2024
…from-within-posecontextisolate

#26: Enum.IsDefined cannot be called from within PoseContext.Isolate
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working priority: low The issue has low priority
Projects
None yet
Development

Successfully merging a pull request may close this issue.

1 participant