Skip to content
This repository has been archived by the owner on Jan 23, 2023. It is now read-only.

Enable Invoke and GetValue for ref-returning members #17639

Merged
4 commits merged into from Apr 18, 2018
Merged

Enable Invoke and GetValue for ref-returning members #17639

4 commits merged into from Apr 18, 2018

Conversation

ghost
Copy link

@ghost ghost commented Apr 18, 2018

https://github.com/dotnet/corefx/issues/15960

Returned magic object is the object pointed to by
the ref. If the ref is null, NullReferenceException.

https://github.com/dotnet/corefx/issues/15960

Returned magic object is the object pointed to by
the ref. If the ref is null, NullReferenceException.
@ghost ghost self-assigned this Apr 18, 2018
@ghost ghost requested a review from jkotas April 18, 2018 14:16
@@ -41,7 +41,7 @@ internal INVOCATION_FLAGS InvocationFlags
//
// first take care of all the NO_INVOKE cases.
if (ContainsGenericParameters ||
ReturnType.IsByRef ||
Copy link
Member

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?

Copy link
Author

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) ||
Copy link
Member

@jkotas jkotas Apr 18, 2018

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 ?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed.

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.
Copy link
Member

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?

Copy link
Author

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.

Copy link
Author

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.

// 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.
Copy link
Member

@jkotas jkotas Apr 18, 2018

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.

Copy link
Author

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?)

Copy link
Member

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.)

Copy link
Author

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.

Copy link
Member

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;
Copy link
Member

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.

@jkotas
Copy link
Member

jkotas commented Apr 18, 2018

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;
Copy link
Member

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.

Copy link
Author

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.

ghost pushed a commit that referenced this pull request Apr 19, 2018
ghost pushed a commit that referenced this pull request Apr 23, 2018
* 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
marek-safar pushed a commit to mono/mono that referenced this pull request Apr 9, 2019
…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
filipnavara added a commit to filipnavara/mono that referenced this pull request Apr 11, 2019
…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
This pull request was closed.
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
3 participants