Enable Invoke and GetValue for ref-returning members #17639
Conversation
https://github.com/dotnet/corefx/issues/15960 Returned magic object is the object pointed to by the ref. If the ref is null, NullReferenceException.
@@ -41,7 +41,7 @@ internal INVOCATION_FLAGS InvocationFlags | |||
// | |||
// first take care of all the NO_INVOKE cases. | |||
if (ContainsGenericParameters || | |||
ReturnType.IsByRef || |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we need IsByRefLike
condition here as well? Or are the ByRefLike returns handled somewhere else?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That gets handled in the "else" branch (RuntimeMethodHandle.GetSecurityFlags(this);
)
@@ -41,7 +41,7 @@ internal INVOCATION_FLAGS InvocationFlags | |||
// | |||
// first take care of all the NO_INVOKE cases. | |||
if (ContainsGenericParameters || | |||
ReturnType.IsByRef || | |||
(ReturnType.IsByRef && ReturnType.GetElementType().IsByRefLike) || |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Update the message that will be thrown for this case in ThrowNoInvokeException ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed.
src/vm/reflectioninvocation.cpp
Outdated
COMPlusThrow(kNullReferenceException, IDS_INVOKE_NULLREF_RETURNED); | ||
} | ||
|
||
// If the target was a value type, that scenario was handled above as part of the code path that copies value types into preallocated boxed objects. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we just call InvokeUtil::CreateObject
here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
InvokeUtil::CreateObject
assumes it's free to allocate objects since the pointer it receives is in non-gc-heap memory. The specific cases we call it for happen not to do that but spreading out the GC restriction over function calls like that leaves the code base in a more fragile state than I'd like to. I went back and forth on this one several times - I decided that the small amount of repetition here was worth paying in order to keep the fragility in that one method.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok, there are no other callers for InvokeUtil::CreateObject
and it was doing things it didn't need to do so I changed the rules for it and made the byref case use it.
src/vm/invokeutil.cpp
Outdated
// CreateObjectAfterInvoke | ||
// This routine will create the specified object from the value returned by the Invoke target. | ||
// | ||
// This does not handle the ELEMENT_TYPE_VALUETYPE case. There is no way for this function to handle that case in a GC-safe way. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There is no way for this function to handle that case in a GC-safe way.
I do not think that this is valid statement. It can be handled by protecting pValue
using GCPROTECT_BEGININTERIOR
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is that also safe when pValue
is a completely unmanaged pointer (i.e. into the stack?)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(I think it is fine for this function to not handle ELEMENT_TYPE_VALUETYPE
.)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok, we'll delete the phrase from the comment, then.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is that also safe when pValue is a completely unmanaged pointer (i.e. into the stack?)
Yes.
|
||
INDEBUG(pValue = (LPVOID)0xcccccccc); // We're about to allocate a GC object - can no longer trust pValue | ||
obj = AllocateObject(pMT); | ||
memcpyNoGCRefs(obj->UnBox(), &capturedValue, size); | ||
} | ||
break; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ELEMENT_TYPE_BYREF
should not be needed anymore. Also, it is referring to resource string that no longer exists.
LGTM otherwise. I assume that there are matching corefx tests coming. |
@@ -666,6 +671,9 @@ OBJECTREF InvokeUtil::CreateObject(TypeHandle th, void * pValue) { | |||
MethodTable *pMT = NULL; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
GetSignatureCorElementType
is a regular element type that you find in metadata signatures, etc.
GetInternalCorElementType
used by the caller is relevant for calling convention - what gets passed in registers, etc.
The bug that broke corefx is when these two differ.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, it broke enums. I'm thinking we should just strip CreateObject
down to handling objects, pointers and function pointers and let the prealloc-and-copy code handle all value types.
* Reapply #17639 * tryagain-wip 4/23/2018 7:27:37 AM - Fix Invoke of enum-returning methods * Assert for refbufargs implying valuetype * Catch ref to void in managed layer
…pes (#13901) CoreCLR allows reflection on methods with ByRef return types under certain conditions. The PR implementing it is dotnet/coreclr#17639. Mono already implemented most of it in the marshalling code but artificially blocked it in `ves_icall_InternalInvoke`. I changed the condition for the exception to match the condition in `emit_invoke_call` (marshal-ilgen.c). It's similar to the condition in CoreCLR, but it may not handle byref-like types correctly. It would have to fixed on both places if it turns out to be the case. The remaining issue is that null references returned in ByRef are not checked and converted to correct exception. It should result in `NullReferenceException` with special message that doesn't get wrapped in `TargetInvocationException`. I put a TODO marker at a place where I think it should be handled, but it's a bit over my head to implement it. /cc @vargaz
…pes (mono#13901) CoreCLR allows reflection on methods with ByRef return types under certain conditions. The PR implementing it is dotnet/coreclr#17639. Mono already implemented most of it in the marshalling code but artificially blocked it in `ves_icall_InternalInvoke`. I changed the condition for the exception to match the condition in `emit_invoke_call` (marshal-ilgen.c). It's similar to the condition in CoreCLR, but it may not handle byref-like types correctly. It would have to fixed on both places if it turns out to be the case. The remaining issue is that null references returned in ByRef are not checked and converted to correct exception. It should result in `NullReferenceException` with special message that doesn't get wrapped in `TargetInvocationException`. I put a TODO marker at a place where I think it should be handled, but it's a bit over my head to implement it. /cc @vargaz
https://github.com/dotnet/corefx/issues/15960
Returned magic object is the object pointed to by
the ref. If the ref is null, NullReferenceException.