summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/dlls/mscorrc/mscorrc.rc1
-rw-r--r--src/dlls/mscorrc/resource.h2
-rw-r--r--src/mscorlib/Resources/Strings.resx7
-rw-r--r--src/mscorlib/src/System/Reflection/RuntimeMethodInfo.cs18
-rw-r--r--src/vm/invokeutil.cpp57
-rw-r--r--src/vm/invokeutil.h2
-rw-r--r--src/vm/reflectioninvocation.cpp49
7 files changed, 84 insertions, 52 deletions
diff --git a/src/dlls/mscorrc/mscorrc.rc b/src/dlls/mscorrc/mscorrc.rc
index 61f5c9af1b..42c0be4f83 100644
--- a/src/dlls/mscorrc/mscorrc.rc
+++ b/src/dlls/mscorrc/mscorrc.rc
@@ -1538,6 +1538,7 @@ BEGIN
IDS_EE_TORNSTATE "Unexpected change made to file '%1'."
+ IDS_INVOKE_NULLREF_RETURNED "The target method returned a null reference."
END
// These strings are generated from within the EE for streams
diff --git a/src/dlls/mscorrc/resource.h b/src/dlls/mscorrc/resource.h
index b8e1b2b2a9..49f1d0e69d 100644
--- a/src/dlls/mscorrc/resource.h
+++ b/src/dlls/mscorrc/resource.h
@@ -898,3 +898,5 @@
#define IDS_EE_NDIRECT_LOADLIB_MAC 0x263f
#define IDS_EE_NDIRECT_GETPROCADDRESS_UNIX 0x2640
#define IDS_EE_ERROR_COM 0x2641
+
+#define IDS_INVOKE_NULLREF_RETURNED 0x2642
diff --git a/src/mscorlib/Resources/Strings.resx b/src/mscorlib/Resources/Strings.resx
index 8ec81b99df..6f25d779db 100644
--- a/src/mscorlib/Resources/Strings.resx
+++ b/src/mscorlib/Resources/Strings.resx
@@ -2938,8 +2938,11 @@
<data name="NotSupported_ByRefLikeArray" xml:space="preserve">
<value>Cannot create arrays of ByRef-like values.</value>
</data>
- <data name="NotSupported_ByRefReturn" xml:space="preserve">
- <value>ByRef return value not supported in reflection invocation.</value>
+ <data name="NotSupported_ByRefToByRefLikeReturn" xml:space="preserve">
+ <value>ByRef to ByRefLike return values not supported in reflection invocation.</value>
+ </data>
+ <data name="NotSupported_ByRefToVoidReturn" xml:space="preserve">
+ <value>ByRef to void return values not supported in reflection invocation.</value>
</data>
<data name="NotSupported_CallToVarArg" xml:space="preserve">
<value>Vararg calling convention not supported.</value>
diff --git a/src/mscorlib/src/System/Reflection/RuntimeMethodInfo.cs b/src/mscorlib/src/System/Reflection/RuntimeMethodInfo.cs
index 1c23f165e4..8bebfb3e70 100644
--- a/src/mscorlib/src/System/Reflection/RuntimeMethodInfo.cs
+++ b/src/mscorlib/src/System/Reflection/RuntimeMethodInfo.cs
@@ -41,7 +41,7 @@ namespace System.Reflection
//
// first take care of all the NO_INVOKE cases.
if (ContainsGenericParameters ||
- ReturnType.IsByRef ||
+ IsDisallowedByRefType(ReturnType) ||
(declaringType != null && declaringType.ContainsGenericParameters) ||
((CallingConvention & CallingConventions.VarArgs) == CallingConventions.VarArgs))
{
@@ -61,6 +61,15 @@ namespace System.Reflection
return m_invocationFlags;
}
}
+
+ private bool IsDisallowedByRefType(Type type)
+ {
+ if (!type.IsByRef)
+ return false;
+
+ Type elementType = type.GetElementType();
+ return elementType.IsByRefLike || elementType == typeof(void);
+ }
#endregion
#region Constructor
@@ -444,10 +453,13 @@ namespace System.Reflection
{
throw new MemberAccessException();
}
- // ByRef return are not allowed in reflection
else if (ReturnType.IsByRef)
{
- throw new NotSupportedException(SR.NotSupported_ByRefReturn);
+ Type elementType = ReturnType.GetElementType();
+ if (elementType.IsByRefLike)
+ throw new NotSupportedException(SR.NotSupported_ByRefToByRefLikeReturn);
+ if (elementType == typeof(void))
+ throw new NotSupportedException(SR.NotSupported_ByRefToVoidReturn);
}
throw new TargetException();
diff --git a/src/vm/invokeutil.cpp b/src/vm/invokeutil.cpp
index 170b0da6d6..a45e81b262 100644
--- a/src/vm/invokeutil.cpp
+++ b/src/vm/invokeutil.cpp
@@ -649,9 +649,14 @@ void InvokeUtil::ValidField(TypeHandle th, OBJECTREF* value)
COMPlusThrow(kArgumentException,W("Arg_ObjObj"));
}
-// InternalCreateObject
-// This routine will create the specified object from the value
-OBJECTREF InvokeUtil::CreateObject(TypeHandle th, void * pValue) {
+//
+// 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. The caller must preallocate the box object and
+// copy the value type into it afterward.
+//
+OBJECTREF InvokeUtil::CreateObjectAfterInvoke(TypeHandle th, void * pValue) {
CONTRACTL {
THROWS;
GC_TRIGGERS;
@@ -663,9 +668,11 @@ OBJECTREF InvokeUtil::CreateObject(TypeHandle th, void * pValue) {
CONTRACTL_END;
CorElementType type = th.GetSignatureCorElementType();
- MethodTable *pMT = NULL;
OBJECTREF obj = NULL;
+ // WARNING: pValue can be an inner reference into a managed object and it is not protected from GC. You must do nothing that
+ // triggers a GC until the all the data it points to has been captured in a GC-protected location.
+
// Handle the non-table types
switch (type) {
case ELEMENT_TYPE_VOID:
@@ -677,18 +684,6 @@ OBJECTREF InvokeUtil::CreateObject(TypeHandle th, void * pValue) {
break;
}
- case ELEMENT_TYPE_FNPTR:
- pMT = MscorlibBinder::GetElementType(ELEMENT_TYPE_I);
- goto PrimitiveType;
-
- case ELEMENT_TYPE_VALUETYPE:
- {
- _ASSERTE(!th.IsTypeDesc());
- pMT = th.AsMethodTable();
- obj = pMT->Box(pValue);
- break;
- }
-
case ELEMENT_TYPE_CLASS: // Class
case ELEMENT_TYPE_SZARRAY: // Single Dim, Zero
case ELEMENT_TYPE_ARRAY: // General Array
@@ -698,35 +693,15 @@ OBJECTREF InvokeUtil::CreateObject(TypeHandle th, void * pValue) {
obj = *(OBJECTREF *)pValue;
break;
- case ELEMENT_TYPE_BOOLEAN: // boolean
- case ELEMENT_TYPE_I1: // byte
- case ELEMENT_TYPE_U1:
- case ELEMENT_TYPE_I2: // short
- case ELEMENT_TYPE_U2:
- case ELEMENT_TYPE_CHAR: // char
- case ELEMENT_TYPE_I4: // int
- case ELEMENT_TYPE_U4:
- case ELEMENT_TYPE_I8: // long
- case ELEMENT_TYPE_U8:
- case ELEMENT_TYPE_R4: // float
- case ELEMENT_TYPE_R8: // double
- case ELEMENT_TYPE_I:
- case ELEMENT_TYPE_U:
- _ASSERTE(!th.IsTypeDesc());
- pMT = th.AsMethodTable();
- PrimitiveType:
+ case ELEMENT_TYPE_FNPTR:
{
- // Don't use MethodTable::Box here for perf reasons
- PREFIX_ASSUME(pMT != NULL);
- obj = AllocateObject(pMT);
- DWORD size = pMT->GetNumInstanceFieldBytes();
- memcpyNoGCRefs(obj->UnBox(), pValue, size);
+ LPVOID capturedValue = *(LPVOID*)pValue;
+ INDEBUG(pValue = (LPVOID)0xcccccccc); // We're about to allocate a GC object - can no longer trust pValue
+ obj = AllocateObject(MscorlibBinder::GetElementType(ELEMENT_TYPE_I));
+ *(LPVOID*)(obj->UnBox()) = capturedValue;
}
break;
- case ELEMENT_TYPE_BYREF:
- COMPlusThrow(kNotSupportedException, W("NotSupported_ByRefReturn"));
- case ELEMENT_TYPE_END:
default:
_ASSERTE(!"Unknown Type");
COMPlusThrow(kNotSupportedException);
diff --git a/src/vm/invokeutil.h b/src/vm/invokeutil.h
index 80db62251c..79ded1d76b 100644
--- a/src/vm/invokeutil.h
+++ b/src/vm/invokeutil.h
@@ -105,7 +105,7 @@ public:
// Given a type, this routine will convert an return value representing that
// type into an ObjectReference. If the type is a primitive, the
// value is wrapped in one of the Value classes.
- static OBJECTREF CreateObject(TypeHandle th, void * pValue);
+ static OBJECTREF CreateObjectAfterInvoke(TypeHandle th, void * pValue);
// This is a special purpose Exception creation function. It
// creates the TargetInvocationExeption placing the passed
diff --git a/src/vm/reflectioninvocation.cpp b/src/vm/reflectioninvocation.cpp
index d4f5a9902f..6dea9b760c 100644
--- a/src/vm/reflectioninvocation.cpp
+++ b/src/vm/reflectioninvocation.cpp
@@ -1092,12 +1092,30 @@ FCIMPL5(Object*, RuntimeMethodHandle::InvokeMethod,
// if we have the magic Value Class return, we need to allocate that class
// and place a pointer to it on the stack.
+ BOOL hasRefReturnAndNeedsBoxing = FALSE; // Indicates that the method has a BYREF return type and the target type needs to be copied into a preallocated boxed object.
+
TypeHandle retTH = gc.pSig->GetReturnTypeHandle();
+
+ TypeHandle refReturnTargetTH; // Valid only if retType == ELEMENT_TYPE_BYREF. Caches the TypeHandle of the byref target.
BOOL fHasRetBuffArg = argit.HasRetBuffArg();
- CorElementType retType = retTH.GetInternalCorElementType();
- if (retType == ELEMENT_TYPE_VALUETYPE || fHasRetBuffArg) {
+ CorElementType retType = retTH.GetSignatureCorElementType();
+ BOOL hasValueTypeReturn = retTH.IsValueType() && retType != ELEMENT_TYPE_VOID;
+ _ASSERTE(hasValueTypeReturn || !fHasRetBuffArg); // only valuetypes are returned via a return buffer.
+ if (hasValueTypeReturn) {
gc.retVal = retTH.GetMethodTable()->Allocate();
}
+ else if (retType == ELEMENT_TYPE_BYREF)
+ {
+ refReturnTargetTH = retTH.AsTypeDesc()->GetTypeParam();
+
+ // If the target of the byref is a value type, we need to preallocate a boxed object to hold the managed return value.
+ if (refReturnTargetTH.IsValueType())
+ {
+ _ASSERTE(refReturnTargetTH.GetSignatureCorElementType() != ELEMENT_TYPE_VOID); // Managed Reflection layer has a bouncer for "ref void" returns.
+ hasRefReturnAndNeedsBoxing = TRUE;
+ gc.retVal = refReturnTargetTH.GetMethodTable()->Allocate();
+ }
+ }
// Copy "this" pointer
if (!pMeth->IsStatic()) {
@@ -1323,13 +1341,23 @@ FCIMPL5(Object*, RuntimeMethodHandle::InvokeMethod,
gc.retVal = Nullable::NormalizeBox(gc.retVal);
}
else
- if (retType == ELEMENT_TYPE_VALUETYPE)
+ if (hasValueTypeReturn || hasRefReturnAndNeedsBoxing)
{
_ASSERTE(gc.retVal != NULL);
+ if (hasRefReturnAndNeedsBoxing)
+ {
+ // Method has BYREF return and the target type is one that needs boxing. We need to copy into the boxed object we have allocated for this purpose.
+ LPVOID pReturnedReference = *(LPVOID*)&callDescrData.returnValue;
+ if (pReturnedReference == NULL)
+ {
+ COMPlusThrow(kNullReferenceException, IDS_INVOKE_NULLREF_RETURNED);
+ }
+ CopyValueClass(gc.retVal->GetData(), pReturnedReference, gc.retVal->GetMethodTable(), gc.retVal->GetAppDomain());
+ }
// if the structure is returned by value, then we need to copy in the boxed object
// we have allocated for this purpose.
- if (!fHasRetBuffArg)
+ else if (!fHasRetBuffArg)
{
CopyValueClass(gc.retVal->GetData(), &callDescrData.returnValue, gc.retVal->GetMethodTable(), gc.retVal->GetAppDomain());
}
@@ -1344,9 +1372,20 @@ FCIMPL5(Object*, RuntimeMethodHandle::InvokeMethod,
// If the return type is a Nullable<T> box it into the correct form
gc.retVal = Nullable::NormalizeBox(gc.retVal);
}
+ else if (retType == ELEMENT_TYPE_BYREF)
+ {
+ // WARNING: pReturnedReference is an unprotected inner reference so we must not trigger a GC until the referenced value has been safely captured.
+ LPVOID pReturnedReference = *(LPVOID*)&callDescrData.returnValue;
+ if (pReturnedReference == NULL)
+ {
+ COMPlusThrow(kNullReferenceException, IDS_INVOKE_NULLREF_RETURNED);
+ }
+
+ gc.retVal = InvokeUtil::CreateObjectAfterInvoke(refReturnTargetTH, pReturnedReference);
+ }
else
{
- gc.retVal = InvokeUtil::CreateObject(retTH, &callDescrData.returnValue);
+ gc.retVal = InvokeUtil::CreateObjectAfterInvoke(retTH, &callDescrData.returnValue);
}
while (byRefToNullables != NULL) {