Skip to content
This repository was 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
masterfrom
unknown repository
Apr 18, 2018
Merged

Enable Invoke and GetValue for ref-returning members#17639
4 commits merged into
masterfrom
unknown repository

Conversation

@ghost

@ghost ghost commented Apr 18, 2018

Copy link
Copy Markdown

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
//
// first take care of all the NO_INVOKE cases.
if (ContainsGenericParameters ||
ReturnType.IsByRef ||

Copy link
Copy Markdown
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
Copy Markdown
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);)

// first take care of all the NO_INVOKE cases.
if (ContainsGenericParameters ||
ReturnType.IsByRef ||
(ReturnType.IsByRef && ReturnType.GetElementType().IsByRefLike) ||

@jkotas jkotas Apr 18, 2018

Copy link
Copy Markdown
Member

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
Copy Markdown
Author

Choose a reason for hiding this comment

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

Fixed.

Comment thread 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.

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

Comment thread 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.

@jkotas jkotas Apr 18, 2018

Copy link
Copy Markdown
Member

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
Copy Markdown
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
Copy Markdown
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
Copy Markdown
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
Copy Markdown
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.

Comment thread src/vm/invokeutil.cpp
obj = AllocateObject(pMT);
memcpyNoGCRefs(obj->UnBox(), &capturedValue, size);
}
break;

Copy link
Copy Markdown
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

jkotas commented Apr 18, 2018

Copy link
Copy Markdown
Member

LGTM otherwise. I assume that there are matching corefx tests coming.

Comment thread src/vm/invokeutil.cpp
@@ -666,6 +671,9 @@ OBJECTREF InvokeUtil::CreateObject(TypeHandle th, void * pValue) {
MethodTable *pMT = NULL;

Copy link
Copy Markdown
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
Copy Markdown
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
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

Development

Successfully merging this pull request may close these issues.

3 participants