Permalink
Fetching contributors…
Cannot retrieve contributors at this time
3245 lines (2597 sloc) 107 KB
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
//
//
#include "common.h"
#include "reflectioninvocation.h"
#include "invokeutil.h"
#include "object.h"
#include "class.h"
#include "method.hpp"
#include "typehandle.h"
#include "field.h"
#include "eeconfig.h"
#include "vars.hpp"
#include "jitinterface.h"
#include "contractimpl.h"
#include "virtualcallstub.h"
#include "comdelegate.h"
#include "generics.h"
#ifdef FEATURE_COMINTEROP
#include "interoputil.h"
#include "runtimecallablewrapper.h"
#endif
#include "dbginterface.h"
#include "argdestination.h"
/**************************************************************************/
/* if the type handle 'th' is a byref to a nullable type, return the
type handle to the nullable type in the byref. Otherwise return
the null type handle */
static TypeHandle NullableTypeOfByref(TypeHandle th) {
CONTRACTL
{
NOTHROW;
GC_NOTRIGGER;
SO_TOLERANT;
MODE_ANY;
}
CONTRACTL_END;
if (th.GetVerifierCorElementType() != ELEMENT_TYPE_BYREF)
return TypeHandle();
TypeHandle subType = th.AsTypeDesc()->GetTypeParam();
if (!Nullable::IsNullableType(subType))
return TypeHandle();
return subType;
}
static void TryCallMethodWorker(MethodDescCallSite* pMethodCallSite, ARG_SLOT* args, Frame* pDebuggerCatchFrame)
{
// Use static contracts b/c we have SEH.
STATIC_CONTRACT_THROWS;
STATIC_CONTRACT_GC_TRIGGERS;
STATIC_CONTRACT_MODE_ANY;
struct Param: public NotifyOfCHFFilterWrapperParam
{
MethodDescCallSite * pMethodCallSite;
ARG_SLOT* args;
} param;
param.pFrame = pDebuggerCatchFrame;
param.pMethodCallSite = pMethodCallSite;
param.args = args;
PAL_TRY(Param *, pParam, &param)
{
pParam->pMethodCallSite->CallWithValueTypes(pParam->args);
}
PAL_EXCEPT_FILTER(NotifyOfCHFFilterWrapper)
{
// Should never reach here b/c handler should always continue search.
_ASSERTE(false);
}
PAL_ENDTRY
}
// Warning: This method has subtle differences from CallDescrWorkerReflectionWrapper
// In particular that one captures watson bucket data and corrupting exception severity,
// then transfers that data to the newly produced TargetInvocationException. This one
// doesn't take those same steps.
//
static void TryCallMethod(MethodDescCallSite* pMethodCallSite, ARG_SLOT* args, bool wrapExceptions) {
CONTRACTL {
THROWS;
GC_TRIGGERS;
MODE_COOPERATIVE;
}
CONTRACTL_END;
if (wrapExceptions)
{
OBJECTREF ppException = NULL;
GCPROTECT_BEGIN(ppException);
// The sole purpose of having this frame is to tell the debugger that we have a catch handler here
// which may swallow managed exceptions. The debugger needs this in order to send a
// CatchHandlerFound (CHF) notification.
FrameWithCookie<DebuggerU2MCatchHandlerFrame> catchFrame;
EX_TRY{
TryCallMethodWorker(pMethodCallSite, args, &catchFrame);
}
EX_CATCH{
ppException = GET_THROWABLE();
_ASSERTE(ppException);
}
EX_END_CATCH(RethrowTransientExceptions)
catchFrame.Pop();
// It is important to re-throw outside the catch block because re-throwing will invoke
// the jitter and managed code and will cause us to use more than the backout stack limit.
if (ppException != NULL)
{
// If we get here we need to throw an TargetInvocationException
OBJECTREF except = InvokeUtil::CreateTargetExcept(&ppException);
COMPlusThrow(except);
}
GCPROTECT_END();
}
else
{
pMethodCallSite->CallWithValueTypes(args);
}
}
FCIMPL5(Object*, RuntimeFieldHandle::GetValue, ReflectFieldObject *pFieldUNSAFE, Object *instanceUNSAFE, ReflectClassBaseObject *pFieldTypeUNSAFE, ReflectClassBaseObject *pDeclaringTypeUNSAFE, CLR_BOOL *pDomainInitialized) {
CONTRACTL {
FCALL_CHECK;
}
CONTRACTL_END;
struct _gc
{
OBJECTREF target;
REFLECTCLASSBASEREF pFieldType;
REFLECTCLASSBASEREF pDeclaringType;
REFLECTFIELDREF refField;
}gc;
gc.target = ObjectToOBJECTREF(instanceUNSAFE);
gc.pFieldType = (REFLECTCLASSBASEREF)ObjectToOBJECTREF(pFieldTypeUNSAFE);
gc.pDeclaringType = (REFLECTCLASSBASEREF)ObjectToOBJECTREF(pDeclaringTypeUNSAFE);
gc.refField = (REFLECTFIELDREF)ObjectToOBJECTREF(pFieldUNSAFE);
if ((gc.pFieldType == NULL) || (gc.refField == NULL))
FCThrowRes(kArgumentNullException, W("Arg_InvalidHandle"));
TypeHandle fieldType = gc.pFieldType->GetType();
TypeHandle declaringType = (gc.pDeclaringType != NULL) ? gc.pDeclaringType->GetType() : TypeHandle();
Assembly *pAssem;
if (declaringType.IsNull())
{
// global field
pAssem = gc.refField->GetField()->GetModule()->GetAssembly();
}
else
{
pAssem = declaringType.GetAssembly();
}
if (pAssem->IsIntrospectionOnly())
FCThrowEx(kInvalidOperationException, IDS_EE_CODEEXECUTION_IN_INTROSPECTIVE_ASSEMBLY, NULL, NULL, NULL);
// We should throw NotSupportedException here.
// But for backward compatibility we are throwing FieldAccessException instead.
if (pAssem->IsDynamic() && !pAssem->HasRunAccess())
FCThrow(kFieldAccessException);
OBJECTREF rv = NULL; // not protected
HELPER_METHOD_FRAME_BEGIN_RET_PROTECT(gc);
// There can be no GC after this until the Object is returned.
rv = InvokeUtil::GetFieldValue(gc.refField->GetField(), fieldType, &gc.target, declaringType, pDomainInitialized);
HELPER_METHOD_FRAME_END();
return OBJECTREFToObject(rv);
}
FCIMPLEND
FCIMPL2(FC_BOOL_RET, ReflectionInvocation::CanValueSpecialCast, ReflectClassBaseObject *pValueTypeUNSAFE, ReflectClassBaseObject *pTargetTypeUNSAFE) {
CONTRACTL {
FCALL_CHECK;
PRECONDITION(CheckPointer(pValueTypeUNSAFE));
PRECONDITION(CheckPointer(pTargetTypeUNSAFE));
}
CONTRACTL_END;
REFLECTCLASSBASEREF refValueType = (REFLECTCLASSBASEREF)ObjectToOBJECTREF(pValueTypeUNSAFE);
REFLECTCLASSBASEREF refTargetType = (REFLECTCLASSBASEREF)ObjectToOBJECTREF(pTargetTypeUNSAFE);
TypeHandle valueType = refValueType->GetType();
TypeHandle targetType = refTargetType->GetType();
// we are here only if the target type is a primitive, an enum or a pointer
CorElementType targetCorElement = targetType.GetVerifierCorElementType();
BOOL ret = TRUE;
HELPER_METHOD_FRAME_BEGIN_RET_2(refValueType, refTargetType);
// the field type is a pointer
if (targetCorElement == ELEMENT_TYPE_PTR || targetCorElement == ELEMENT_TYPE_FNPTR) {
// the object must be an IntPtr or a System.Reflection.Pointer
if (valueType == TypeHandle(MscorlibBinder::GetClass(CLASS__INTPTR))) {
//
// it's an IntPtr, it's good.
}
//
// it's a System.Reflection.Pointer object
// void* assigns to any pointer. Otherwise the type of the pointer must match
else if (!InvokeUtil::IsVoidPtr(targetType)) {
if (!valueType.CanCastTo(targetType))
ret = FALSE;
}
} else {
// the field type is an enum or a primitive. To have any chance of assignement the object type must
// be an enum or primitive as well.
// So get the internal cor element and that must be the same or widen
CorElementType valueCorElement = valueType.GetVerifierCorElementType();
if (InvokeUtil::IsPrimitiveType(valueCorElement))
ret = (InvokeUtil::CanPrimitiveWiden(targetCorElement, valueCorElement)) ? TRUE : FALSE;
else
ret = FALSE;
}
HELPER_METHOD_FRAME_END();
FC_RETURN_BOOL(ret);
}
FCIMPLEND
FCIMPL3(Object*, ReflectionInvocation::AllocateValueType, ReflectClassBaseObject *pTargetTypeUNSAFE, Object *valueUNSAFE, CLR_BOOL fForceTypeChange) {
CONTRACTL {
FCALL_CHECK;
PRECONDITION(CheckPointer(pTargetTypeUNSAFE));
PRECONDITION(CheckPointer(valueUNSAFE, NULL_OK));
}
CONTRACTL_END;
struct _gc
{
REFLECTCLASSBASEREF refTargetType;
OBJECTREF value;
OBJECTREF obj;
}gc;
gc.value = ObjectToOBJECTREF(valueUNSAFE);
gc.obj = gc.value;
gc.refTargetType = (REFLECTCLASSBASEREF)ObjectToOBJECTREF(pTargetTypeUNSAFE);
TypeHandle targetType = gc.refTargetType->GetType();
HELPER_METHOD_FRAME_BEGIN_RET_PROTECT(gc);
CorElementType targetElementType = targetType.GetSignatureCorElementType();
if (InvokeUtil::IsPrimitiveType(targetElementType) || targetElementType == ELEMENT_TYPE_VALUETYPE)
{
MethodTable* allocMT = targetType.AsMethodTable();
if (gc.value != NULL)
{
// ignore the type of the incoming box if fForceTypeChange is set
// and the target type is not nullable
if (!fForceTypeChange || Nullable::IsNullableType(targetType))
allocMT = gc.value->GetMethodTable();
}
// for null Nullable<T> we don't want a default value being created.
// just allow the null value to be passed, as it will be converted to
// a true nullable
if (!(gc.value == NULL && Nullable::IsNullableType(targetType)))
{
// boxed value type are 'read-only' in the sence that you can't
// only the implementor of the value type can expose mutators.
// To insure byrefs don't mutate value classes in place, we make
// a copy (and if we were not given one, we create a null value type
// instance.
gc.obj = allocMT->Allocate();
if (gc.value != NULL)
CopyValueClassUnchecked(gc.obj->UnBox(), gc.value->UnBox(), allocMT);
}
}
HELPER_METHOD_FRAME_END();
return OBJECTREFToObject(gc.obj);
}
FCIMPLEND
FCIMPL7(void, RuntimeFieldHandle::SetValue, ReflectFieldObject *pFieldUNSAFE, Object *targetUNSAFE, Object *valueUNSAFE, ReflectClassBaseObject *pFieldTypeUNSAFE, DWORD attr, ReflectClassBaseObject *pDeclaringTypeUNSAFE, CLR_BOOL *pDomainInitialized) {
CONTRACTL {
FCALL_CHECK;
}
CONTRACTL_END;
struct _gc {
OBJECTREF target;
OBJECTREF value;
REFLECTCLASSBASEREF fieldType;
REFLECTCLASSBASEREF declaringType;
REFLECTFIELDREF refField;
} gc;
gc.target = ObjectToOBJECTREF(targetUNSAFE);
gc.value = ObjectToOBJECTREF(valueUNSAFE);
gc.fieldType= (REFLECTCLASSBASEREF)ObjectToOBJECTREF(pFieldTypeUNSAFE);
gc.declaringType= (REFLECTCLASSBASEREF)ObjectToOBJECTREF(pDeclaringTypeUNSAFE);
gc.refField = (REFLECTFIELDREF)ObjectToOBJECTREF(pFieldUNSAFE);
if ((gc.fieldType == NULL) || (gc.refField == NULL))
FCThrowResVoid(kArgumentNullException, W("Arg_InvalidHandle"));
TypeHandle fieldType = gc.fieldType->GetType();
TypeHandle declaringType = gc.declaringType != NULL ? gc.declaringType->GetType() : TypeHandle();
Assembly *pAssem;
if (declaringType.IsNull())
{
// global field
pAssem = gc.refField->GetField()->GetModule()->GetAssembly();
}
else
{
pAssem = declaringType.GetAssembly();
}
if (pAssem->IsIntrospectionOnly())
FCThrowExVoid(kInvalidOperationException, IDS_EE_CODEEXECUTION_IN_INTROSPECTIVE_ASSEMBLY, NULL, NULL, NULL);
// We should throw NotSupportedException here.
// But for backward compatibility we are throwing FieldAccessException instead.
if (pAssem->IsDynamic() && !pAssem->HasRunAccess())
FCThrowVoid(kFieldAccessException);
FC_GC_POLL_NOT_NEEDED();
FieldDesc* pFieldDesc = gc.refField->GetField();
HELPER_METHOD_FRAME_BEGIN_PROTECT(gc);
//TODO: cleanup this function
InvokeUtil::SetValidField(fieldType.GetSignatureCorElementType(), fieldType, pFieldDesc, &gc.target, &gc.value, declaringType, pDomainInitialized);
HELPER_METHOD_FRAME_END();
}
FCIMPLEND
//A.CI work
FCIMPL1(Object*, RuntimeTypeHandle::Allocate, ReflectClassBaseObject* pTypeUNSAFE)
{
CONTRACTL {
FCALL_CHECK;
PRECONDITION(CheckPointer(pTypeUNSAFE));
}
CONTRACTL_END
REFLECTCLASSBASEREF refType = (REFLECTCLASSBASEREF)ObjectToOBJECTREF(pTypeUNSAFE);
TypeHandle type = refType->GetType();
// Handle the nullable<T> special case
if (Nullable::IsNullableType(type)) {
return OBJECTREFToObject(Nullable::BoxedNullableNull(type));
}
OBJECTREF rv = NULL;
HELPER_METHOD_FRAME_BEGIN_RET_1(refType);
rv = AllocateObject(type.GetMethodTable());
HELPER_METHOD_FRAME_END();
return OBJECTREFToObject(rv);
}//Allocate
FCIMPLEND
FCIMPL5(Object*, RuntimeTypeHandle::CreateInstance, ReflectClassBaseObject* refThisUNSAFE,
CLR_BOOL publicOnly,
CLR_BOOL wrapExceptions,
CLR_BOOL* pbCanBeCached,
MethodDesc** pConstructor) {
CONTRACTL {
FCALL_CHECK;
PRECONDITION(CheckPointer(refThisUNSAFE));
PRECONDITION(CheckPointer(pbCanBeCached));
PRECONDITION(CheckPointer(pConstructor));
PRECONDITION(*pbCanBeCached == false);
PRECONDITION(*pConstructor == NULL);
}
CONTRACTL_END;
if (refThisUNSAFE == NULL)
FCThrow(kNullReferenceException);
MethodDesc* pMeth;
OBJECTREF rv = NULL;
REFLECTCLASSBASEREF refThis = (REFLECTCLASSBASEREF)ObjectToOBJECTREF(refThisUNSAFE);
TypeHandle thisTH = refThis->GetType();
Assembly *pAssem = thisTH.GetAssembly();
if (pAssem->IsIntrospectionOnly())
FCThrowEx(kInvalidOperationException, IDS_EE_CODEEXECUTION_IN_INTROSPECTIVE_ASSEMBLY, NULL, NULL, NULL);
if (pAssem->IsDynamic() && !pAssem->HasRunAccess())
FCThrowRes(kNotSupportedException, W("NotSupported_DynamicAssemblyNoRunAccess"));
HELPER_METHOD_FRAME_BEGIN_RET_2(rv, refThis);
MethodTable* pVMT;
// Get the type information associated with refThis
if (thisTH.IsNull() || thisTH.IsTypeDesc())
COMPlusThrow(kMissingMethodException,W("Arg_NoDefCTor"));
pVMT = thisTH.AsMethodTable();
pVMT->EnsureInstanceActive();
#ifdef FEATURE_COMINTEROP
// If this is __ComObject then create the underlying COM object.
if (IsComObjectClass(refThis->GetType())) {
#ifdef FEATURE_COMINTEROP_UNMANAGED_ACTIVATION
SyncBlock* pSyncBlock = refThis->GetSyncBlock();
void* pClassFactory = (void*)pSyncBlock->GetInteropInfo()->GetComClassFactory();
if (!pClassFactory)
COMPlusThrow(kInvalidComObjectException, IDS_EE_NO_BACKING_CLASS_FACTORY);
// create an instance of the Com Object
rv = ((ComClassFactory*)pClassFactory)->CreateInstance(NULL);
#else // FEATURE_COMINTEROP_UNMANAGED_ACTIVATION
COMPlusThrow(kInvalidComObjectException, IDS_EE_NO_BACKING_CLASS_FACTORY);
#endif // FEATURE_COMINTEROP_UNMANAGED_ACTIVATION
}
else
#endif // FEATURE_COMINTEROP
{
// if this is an abstract class then we will fail this
if (pVMT->IsAbstract()) {
if (pVMT->IsInterface())
COMPlusThrow(kMissingMethodException,W("Acc_CreateInterface"));
else
COMPlusThrow(kMissingMethodException,W("Acc_CreateAbst"));
}
else if (pVMT->ContainsGenericVariables()) {
COMPlusThrow(kArgumentException,W("Acc_CreateGeneric"));
}
if (pVMT->IsByRefLike())
COMPlusThrow(kNotSupportedException, W("NotSupported_ByRefLike"));
if (pVMT->IsSharedByGenericInstantiations())
COMPlusThrow(kNotSupportedException, W("NotSupported_Type"));
if (!pVMT->HasDefaultConstructor())
{
// We didn't find the parameterless constructor,
// if this is a Value class we can simply allocate one and return it
if (!pVMT->IsValueType()) {
COMPlusThrow(kMissingMethodException,W("Arg_NoDefCTor"));
}
// Handle the nullable<T> special case
if (Nullable::IsNullableType(thisTH)) {
rv = Nullable::BoxedNullableNull(thisTH);
}
else
rv = pVMT->Allocate();
if (!pVMT->Collectible())
{
*pbCanBeCached = true;
}
}
else // !pVMT->HasDefaultConstructor()
{
pMeth = pVMT->GetDefaultConstructor();
// Validate the method can be called by this caller
DWORD attr = pMeth->GetAttrs();
if (!IsMdPublic(attr) && publicOnly)
COMPlusThrow(kMissingMethodException, W("Arg_NoDefCTor"));
// We've got the class, lets allocate it and call the constructor
OBJECTREF o;
bool remoting = false;
o = AllocateObject(pVMT);
GCPROTECT_BEGIN(o);
MethodDescCallSite ctor(pMeth, &o);
// Copy "this" pointer
ARG_SLOT arg;
if (pVMT->IsValueType())
arg = PtrToArgSlot(o->UnBox());
else
arg = ObjToArgSlot(o);
// Call the method
TryCallMethod(&ctor, &arg, wrapExceptions);
rv = o;
GCPROTECT_END();
// No need to set these if they cannot be cached. In particular, if the type is a value type with a custom
// parameterless constructor, don't allow caching and have subsequent calls come back here to allocate an object and
// call the constructor.
if (!remoting && !pVMT->Collectible() && !pVMT->IsValueType())
{
*pbCanBeCached = true;
*pConstructor = pMeth;
}
}
}
HELPER_METHOD_FRAME_END();
return OBJECTREFToObject(rv);
}
FCIMPLEND
FCIMPL2(Object*, RuntimeTypeHandle::CreateInstanceForGenericType, ReflectClassBaseObject* pTypeUNSAFE, ReflectClassBaseObject* pParameterTypeUNSAFE) {
FCALL_CONTRACT;
struct _gc
{
OBJECTREF rv;
REFLECTCLASSBASEREF refType;
REFLECTCLASSBASEREF refParameterType;
} gc;
gc.rv = NULL;
gc.refType = (REFLECTCLASSBASEREF)ObjectToOBJECTREF(pTypeUNSAFE);
gc.refParameterType = (REFLECTCLASSBASEREF)ObjectToOBJECTREF(pParameterTypeUNSAFE);
MethodDesc* pMeth;
TypeHandle genericType = gc.refType->GetType();
TypeHandle parameterHandle = gc.refParameterType->GetType();
_ASSERTE (genericType.HasInstantiation());
HELPER_METHOD_FRAME_BEGIN_RET_PROTECT(gc);
TypeHandle instantiatedType = ((TypeHandle)genericType.GetCanonicalMethodTable()).Instantiate(Instantiation(&parameterHandle, 1));
// Get the type information associated with refThis
MethodTable* pVMT = instantiatedType.GetMethodTable();
_ASSERTE (pVMT != 0 && !instantiatedType.IsTypeDesc());
_ASSERTE(!(pVMT->GetAssembly()->IsDynamic() && !pVMT->GetAssembly()->HasRunAccess()));
_ASSERTE( !pVMT->IsAbstract() ||! instantiatedType.ContainsGenericVariables());
_ASSERTE(!pVMT->IsByRefLike() && pVMT->HasDefaultConstructor());
pMeth = pVMT->GetDefaultConstructor();
MethodDescCallSite ctor(pMeth);
// We've got the class, lets allocate it and call the constructor
// Nullables don't take this path, if they do we need special logic to make an instance
_ASSERTE(!Nullable::IsNullableType(instantiatedType));
gc.rv = instantiatedType.GetMethodTable()->Allocate();
ARG_SLOT arg = ObjToArgSlot(gc.rv);
// Call the method
TryCallMethod(&ctor, &arg, true);
HELPER_METHOD_FRAME_END();
return OBJECTREFToObject(gc.rv);
}
FCIMPLEND
NOINLINE FC_BOOL_RET IsInstanceOfTypeHelper(OBJECTREF obj, REFLECTCLASSBASEREF refType)
{
FCALL_CONTRACT;
BOOL canCast = false;
FC_INNER_PROLOG(RuntimeTypeHandle::IsInstanceOfType);
HELPER_METHOD_FRAME_BEGIN_RET_ATTRIB_2(Frame::FRAME_ATTR_EXACT_DEPTH|Frame::FRAME_ATTR_CAPTURE_DEPTH_2, obj, refType);
canCast = ObjIsInstanceOf(OBJECTREFToObject(obj), refType->GetType());
HELPER_METHOD_FRAME_END();
FC_RETURN_BOOL(canCast);
}
FCIMPL2(FC_BOOL_RET, RuntimeTypeHandle::IsInstanceOfType, ReflectClassBaseObject* pTypeUNSAFE, Object *objectUNSAFE) {
FCALL_CONTRACT;
OBJECTREF obj = ObjectToOBJECTREF(objectUNSAFE);
REFLECTCLASSBASEREF refType = (REFLECTCLASSBASEREF)ObjectToOBJECTREF(pTypeUNSAFE);
// Null is not instance of anything in reflection world
if (obj == NULL)
FC_RETURN_BOOL(false);
if (refType == NULL)
FCThrowRes(kArgumentNullException, W("Arg_InvalidHandle"));
switch (ObjIsInstanceOfNoGC(objectUNSAFE, refType->GetType())) {
case TypeHandle::CanCast:
FC_RETURN_BOOL(true);
case TypeHandle::CannotCast:
FC_RETURN_BOOL(false);
default:
// fall through to the slow helper
break;
}
FC_INNER_RETURN(FC_BOOL_RET, IsInstanceOfTypeHelper(obj, refType));
}
FCIMPLEND
/****************************************************************************/
/* boxed Nullable<T> are represented as a boxed T, so there is no unboxed
Nullable<T> inside to point at by reference. Because of this a byref
parameters of type Nullable<T> are copied out of the boxed instance
(to a place on the stack), before the call is made (and this copy is
pointed at). After the call returns, this copy must be copied back to
the original argument array. ByRefToNullable, is a simple linked list
that remembers what copy-backs are needed */
struct ByRefToNullable {
unsigned argNum; // The argument number for this byrefNullable argument
void* data; // The data to copy back to the ByRefNullable. This points to the stack
TypeHandle type; // The type of Nullable for this argument
ByRefToNullable* next; // list of these
ByRefToNullable(unsigned aArgNum, void* aData, TypeHandle aType, ByRefToNullable* aNext) {
argNum = aArgNum;
data = aData;
type = aType;
next = aNext;
}
};
void CallDescrWorkerReflectionWrapper(CallDescrData * pCallDescrData, Frame * pFrame)
{
// Use static contracts b/c we have SEH.
STATIC_CONTRACT_THROWS;
STATIC_CONTRACT_GC_TRIGGERS;
STATIC_CONTRACT_MODE_ANY;
struct Param: public NotifyOfCHFFilterWrapperParam
{
CallDescrData * pCallDescrData;
} param;
param.pFrame = pFrame;
param.pCallDescrData = pCallDescrData;
PAL_TRY(Param *, pParam, &param)
{
CallDescrWorkerWithHandler(pParam->pCallDescrData);
}
PAL_EXCEPT_FILTER(ReflectionInvocationExceptionFilter)
{
// Should never reach here b/c handler should always continue search.
_ASSERTE(false);
}
PAL_ENDTRY
} // CallDescrWorkerReflectionWrapper
OBJECTREF InvokeArrayConstructor(ArrayTypeDesc* arrayDesc, MethodDesc* pMeth, PTRARRAYREF* objs, int argCnt)
{
CONTRACTL {
THROWS;
GC_TRIGGERS;
MODE_COOPERATIVE;
}
CONTRACTL_END;
DWORD i;
// If we're trying to create an array of pointers or function pointers,
// check that the caller has skip verification permission.
CorElementType et = arrayDesc->GetArrayElementTypeHandle().GetVerifierCorElementType();
// Validate the argCnt an the Rank. Also allow nested SZARRAY's.
_ASSERTE(argCnt == (int) arrayDesc->GetRank() || argCnt == (int) arrayDesc->GetRank() * 2 ||
arrayDesc->GetInternalCorElementType() == ELEMENT_TYPE_SZARRAY);
// Validate all of the parameters. These all typed as integers
int allocSize = 0;
if (!ClrSafeInt<int>::multiply(sizeof(INT32), argCnt, allocSize))
COMPlusThrow(kArgumentException, IDS_EE_SIGTOOCOMPLEX);
INT32* indexes = (INT32*) _alloca((size_t)allocSize);
ZeroMemory(indexes, allocSize);
for (i=0; i<(DWORD)argCnt; i++)
{
if (!(*objs)->m_Array[i])
COMPlusThrowArgumentException(W("parameters"), W("Arg_NullIndex"));
MethodTable* pMT = ((*objs)->m_Array[i])->GetMethodTable();
CorElementType oType = TypeHandle(pMT).GetVerifierCorElementType();
if (!InvokeUtil::IsPrimitiveType(oType) || !InvokeUtil::CanPrimitiveWiden(ELEMENT_TYPE_I4,oType))
COMPlusThrow(kArgumentException,W("Arg_PrimWiden"));
memcpy(&indexes[i],(*objs)->m_Array[i]->UnBox(),pMT->GetNumInstanceFieldBytes());
}
return AllocateArrayEx(TypeHandle(arrayDesc), indexes, argCnt);
}
static BOOL IsActivationNeededForMethodInvoke(MethodDesc * pMD)
{
CONTRACTL {
THROWS;
GC_TRIGGERS;
MODE_COOPERATIVE;
}
CONTRACTL_END;
// The activation for non-generic instance methods is covered by non-null "this pointer"
if (!pMD->IsStatic() && !pMD->HasMethodInstantiation() && !pMD->IsInterface())
return FALSE;
// We need to activate each time for domain neutral types
if (pMD->IsDomainNeutral())
return TRUE;
// We need to activate the instance at least once
pMD->EnsureActive();
return FALSE;
}
class ArgIteratorBaseForMethodInvoke
{
protected:
SIGNATURENATIVEREF * m_ppNativeSig;
FORCEINLINE CorElementType GetReturnType(TypeHandle * pthValueType)
{
WRAPPER_NO_CONTRACT;
return (*pthValueType = (*m_ppNativeSig)->GetReturnTypeHandle()).GetInternalCorElementType();
}
FORCEINLINE CorElementType GetNextArgumentType(DWORD iArg, TypeHandle * pthValueType)
{
WRAPPER_NO_CONTRACT;
return (*pthValueType = (*m_ppNativeSig)->GetArgumentAt(iArg)).GetInternalCorElementType();
}
FORCEINLINE void Reset()
{
LIMITED_METHOD_CONTRACT;
}
public:
BOOL HasThis()
{
LIMITED_METHOD_CONTRACT;
return (*m_ppNativeSig)->HasThis();
}
BOOL HasParamType()
{
LIMITED_METHOD_CONTRACT;
// param type methods are not supported for reflection invoke, so HasParamType is always false for them
return FALSE;
}
BOOL IsVarArg()
{
LIMITED_METHOD_CONTRACT;
// vararg methods are not supported for reflection invoke, so IsVarArg is always false for them
return FALSE;
}
DWORD NumFixedArgs()
{
LIMITED_METHOD_CONTRACT;
return (*m_ppNativeSig)->NumFixedArgs();
}
#ifdef FEATURE_INTERPRETER
BYTE CallConv()
{
LIMITED_METHOD_CONTRACT;
return IMAGE_CEE_CS_CALLCONV_DEFAULT;
}
#endif // FEATURE_INTERPRETER
};
class ArgIteratorForMethodInvoke : public ArgIteratorTemplate<ArgIteratorBaseForMethodInvoke>
{
public:
ArgIteratorForMethodInvoke(SIGNATURENATIVEREF * ppNativeSig)
{
m_ppNativeSig = ppNativeSig;
DWORD dwFlags = (*m_ppNativeSig)->GetArgIteratorFlags();
// Use the cached values if they are available
if (dwFlags & SIZE_OF_ARG_STACK_COMPUTED)
{
m_dwFlags = dwFlags;
m_nSizeOfArgStack = (*m_ppNativeSig)->GetSizeOfArgStack();
return;
}
//
// Compute flags and stack argument size, and cache them for next invocation
//
ForceSigWalk();
if (IsActivationNeededForMethodInvoke((*m_ppNativeSig)->GetMethod()))
{
m_dwFlags |= METHOD_INVOKE_NEEDS_ACTIVATION;
}
(*m_ppNativeSig)->SetSizeOfArgStack(m_nSizeOfArgStack);
_ASSERTE((*m_ppNativeSig)->GetSizeOfArgStack() == m_nSizeOfArgStack);
// This has to be last
(*m_ppNativeSig)->SetArgIteratorFlags(m_dwFlags);
_ASSERTE((*m_ppNativeSig)->GetArgIteratorFlags() == m_dwFlags);
}
BOOL IsActivationNeeded()
{
LIMITED_METHOD_CONTRACT;
return (m_dwFlags & METHOD_INVOKE_NEEDS_ACTIVATION) != 0;
}
};
void DECLSPEC_NORETURN ThrowInvokeMethodException(MethodDesc * pMethod, OBJECTREF targetException)
{
CONTRACTL {
THROWS;
GC_TRIGGERS;
MODE_COOPERATIVE;
}
CONTRACTL_END;
GCPROTECT_BEGIN(targetException);
#if defined(_DEBUG) && !defined(FEATURE_PAL)
if (IsWatsonEnabled())
{
if (!CLRException::IsPreallocatedExceptionObject(targetException))
{
// If the exception is not preallocated, we should be having the
// watson buckets in the throwable already.
if(!((EXCEPTIONREF)targetException)->AreWatsonBucketsPresent())
{
// If an exception is raised by the VM (e.g. type load exception by the JIT) and it comes
// across the reflection invocation boundary before CLR's personality routine for managed
// code has been invoked, then no buckets would be available for us at this point.
//
// Since we cannot assert this, better log it for diagnosis if required.
LOG((LF_EH, LL_INFO100, "InvokeImpl - No watson buckets available - regular exception likely raised within VM and not seen by managed code.\n"));
}
}
else
{
// Exception is preallocated.
PTR_EHWatsonBucketTracker pUEWatsonBucketTracker = GetThread()->GetExceptionState()->GetUEWatsonBucketTracker();
if ((IsThrowableThreadAbortException(targetException) && pUEWatsonBucketTracker->CapturedForThreadAbort())||
(pUEWatsonBucketTracker->CapturedAtReflectionInvocation()))
{
// ReflectionInvocationExceptionFilter would have captured
// the watson bucket details for preallocated exceptions
// in the UE watson bucket tracker.
if(pUEWatsonBucketTracker->RetrieveWatsonBuckets() == NULL)
{
// See comment above
LOG((LF_EH, LL_INFO100, "InvokeImpl - No watson buckets available - preallocated exception likely raised within VM and not seen by managed code.\n"));
}
}
}
}
#endif // _DEBUG && !FEATURE_PAL
#ifdef FEATURE_CORRUPTING_EXCEPTIONS
// Get the corruption severity of the exception that came in through reflection invocation.
CorruptionSeverity severity = GetThread()->GetExceptionState()->GetLastActiveExceptionCorruptionSeverity();
// Since we are dealing with an exception, set the flag indicating if the target of Reflection can handle exception or not.
// This flag is used in CEHelper::CanIDispatchTargetHandleException.
GetThread()->GetExceptionState()->SetCanReflectionTargetHandleException(CEHelper::CanMethodHandleException(severity, pMethod));
#endif // FEATURE_CORRUPTING_EXCEPTIONS
OBJECTREF except = InvokeUtil::CreateTargetExcept(&targetException);
#ifndef FEATURE_PAL
if (IsWatsonEnabled())
{
struct
{
OBJECTREF oExcept;
} gcTIE;
ZeroMemory(&gcTIE, sizeof(gcTIE));
GCPROTECT_BEGIN(gcTIE);
gcTIE.oExcept = except;
_ASSERTE(!CLRException::IsPreallocatedExceptionObject(gcTIE.oExcept));
// If the original exception was preallocated, then copy over the captured
// watson buckets to the TargetInvocationException object, if available.
//
// We dont need to do this if the original exception was not preallocated
// since it already contains the watson buckets inside the object.
if (CLRException::IsPreallocatedExceptionObject(targetException))
{
PTR_EHWatsonBucketTracker pUEWatsonBucketTracker = GetThread()->GetExceptionState()->GetUEWatsonBucketTracker();
BOOL fCopyWatsonBuckets = TRUE;
PTR_VOID pBuckets = pUEWatsonBucketTracker->RetrieveWatsonBuckets();
if (pBuckets != NULL)
{
// Copy the buckets to the exception object
CopyWatsonBucketsToThrowable(pBuckets, gcTIE.oExcept);
// Confirm that they are present.
_ASSERTE(((EXCEPTIONREF)gcTIE.oExcept)->AreWatsonBucketsPresent());
}
// Clear the UE watson bucket tracker since the bucketing
// details are now in the TargetInvocationException object.
pUEWatsonBucketTracker->ClearWatsonBucketDetails();
}
// update "except" incase the reference to the object
// was updated by the GC
except = gcTIE.oExcept;
GCPROTECT_END();
}
#endif // !FEATURE_PAL
// Since the original exception is inner of target invocation exception,
// when TIE is seen to be raised for the first time, we will end up
// using the inner exception buckets automatically.
// Since VM is throwing the exception, we set it to use the same corruption severity
// that the original exception came in with from reflection invocation.
COMPlusThrow(except
#ifdef FEATURE_CORRUPTING_EXCEPTIONS
, severity
#endif // FEATURE_CORRUPTING_EXCEPTIONS
);
GCPROTECT_END();
}
FCIMPL5(Object*, RuntimeMethodHandle::InvokeMethod,
Object *target, PTRArray *objs, SignatureNative* pSigUNSAFE,
CLR_BOOL fConstructor, CLR_BOOL fWrapExceptions)
{
FCALL_CONTRACT;
struct {
OBJECTREF target;
PTRARRAYREF args;
SIGNATURENATIVEREF pSig;
OBJECTREF retVal;
} gc;
gc.target = ObjectToOBJECTREF(target);
gc.args = (PTRARRAYREF)objs;
gc.pSig = (SIGNATURENATIVEREF)pSigUNSAFE;
gc.retVal = NULL;
MethodDesc* pMeth = gc.pSig->GetMethod();
TypeHandle ownerType = gc.pSig->GetDeclaringType();
HELPER_METHOD_FRAME_BEGIN_RET_PROTECT(gc);
Assembly *pAssem = pMeth->GetAssembly();
if (pAssem->IsIntrospectionOnly())
COMPlusThrow(kInvalidOperationException, IDS_EE_CODEEXECUTION_IN_INTROSPECTIVE_ASSEMBLY);
// We should throw NotSupportedException here.
// But for backward compatibility we are throwing TargetException instead.
if (pAssem->IsDynamic() && !pAssem->HasRunAccess())
COMPlusThrow(kTargetException);
if (ownerType.IsSharedByGenericInstantiations())
COMPlusThrow(kNotSupportedException, W("NotSupported_Type"));
#ifdef _DEBUG
if (g_pConfig->ShouldInvokeHalt(pMeth))
{
_ASSERTE(!"InvokeHalt");
}
#endif
// Skip the activation optimization for remoting because of remoting proxy is not always activated.
// It would be nice to clean this up and get remoting to always activate methodtable behind the proxy.
BOOL fForceActivationForRemoting = FALSE;
if (fConstructor)
{
// If we are invoking a constructor on an array then we must
// handle this specially. String objects allocate themselves
// so they are a special case.
if (ownerType.IsArray()) {
gc.retVal = InvokeArrayConstructor(ownerType.AsArray(),
pMeth,
&gc.args,
gc.pSig->NumFixedArgs());
goto Done;
}
MethodTable * pMT = ownerType.AsMethodTable();
{
if (pMT != g_pStringClass)
gc.retVal = pMT->Allocate();
}
}
else
{
}
{
ArgIteratorForMethodInvoke argit(&gc.pSig);
if (argit.IsActivationNeeded() || fForceActivationForRemoting)
pMeth->EnsureActive();
CONSISTENCY_CHECK(pMeth->CheckActivated());
UINT nStackBytes = argit.SizeOfFrameArgumentArray();
// Note that SizeOfFrameArgumentArray does overflow checks with sufficient margin to prevent overflows here
SIZE_T nAllocaSize = TransitionBlock::GetNegSpaceSize() + sizeof(TransitionBlock) + nStackBytes;
Thread * pThread = GET_THREAD();
// Make sure we have enough room on the stack for this. Note that we will need the stack amount twice - once to build the stack
// and second time to actually make the call.
INTERIOR_STACK_PROBE_FOR(pThread, 1 + static_cast<UINT>((2 * nAllocaSize) / GetOsPageSize()) + static_cast<UINT>(HOLDER_CODE_NORMAL_STACK_LIMIT));
LPBYTE pAlloc = (LPBYTE)_alloca(nAllocaSize);
LPBYTE pTransitionBlock = pAlloc + TransitionBlock::GetNegSpaceSize();
CallDescrData callDescrData;
callDescrData.pSrc = pTransitionBlock + sizeof(TransitionBlock);
callDescrData.numStackSlots = nStackBytes / STACK_ELEM_SIZE;
#ifdef CALLDESCR_ARGREGS
callDescrData.pArgumentRegisters = (ArgumentRegisters*)(pTransitionBlock + TransitionBlock::GetOffsetOfArgumentRegisters());
#endif
#ifdef CALLDESCR_FPARGREGS
callDescrData.pFloatArgumentRegisters = NULL;
#endif
#ifdef CALLDESCR_REGTYPEMAP
callDescrData.dwRegTypeMap = 0;
#endif
callDescrData.fpReturnSize = argit.GetFPReturnSize();
// This is duplicated logic from MethodDesc::GetCallTarget
PCODE pTarget;
if (pMeth->IsVtableMethod())
{
pTarget = pMeth->GetSingleCallableAddrOfVirtualizedCode(&gc.target, ownerType);
}
else
{
pTarget = pMeth->GetSingleCallableAddrOfCode();
}
callDescrData.pTarget = pTarget;
// Build the arguments on the stack
GCStress<cfg_any>::MaybeTrigger();
FrameWithCookie<ProtectValueClassFrame> *pProtectValueClassFrame = NULL;
ValueClassInfo *pValueClasses = NULL;
ByRefToNullable* byRefToNullables = NULL;
// 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.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()) {
PVOID pThisPtr;
if (fConstructor)
{
// Copy "this" pointer: only unbox if type is value type and method is not unboxing stub
if (ownerType.IsValueType() && !pMeth->IsUnboxingStub()) {
// Note that we create a true boxed nullabe<T> and then convert it to a T below
pThisPtr = gc.retVal->GetData();
}
else
pThisPtr = OBJECTREFToObject(gc.retVal);
}
else
if (!pMeth->GetMethodTable()->IsValueType())
pThisPtr = OBJECTREFToObject(gc.target);
else {
if (pMeth->IsUnboxingStub())
pThisPtr = OBJECTREFToObject(gc.target);
else {
// Create a true boxed Nullable<T> and use that as the 'this' pointer.
// since what is passed in is just a boxed T
MethodTable* pMT = pMeth->GetMethodTable();
if (Nullable::IsNullableType(pMT)) {
OBJECTREF bufferObj = pMT->Allocate();
void* buffer = bufferObj->GetData();
Nullable::UnBox(buffer, gc.target, pMT);
pThisPtr = buffer;
}
else
pThisPtr = gc.target->UnBox();
}
}
*((LPVOID*) (pTransitionBlock + argit.GetThisOffset())) = pThisPtr;
}
// NO GC AFTER THIS POINT. The object references in the method frame are not protected.
//
// We have already copied "this" pointer so we do not want GC to happen even sooner. Unfortunately,
// we may allocate in the process of copying this pointer that makes it hard to express using contracts.
//
// If an exception occurs a gc may happen but we are going to dump the stack anyway and we do
// not need to protect anything.
PVOID pRetBufStackCopy = NULL;
{
BEGINFORBIDGC();
#ifdef _DEBUG
GCForbidLoaderUseHolder forbidLoaderUse;
#endif
// Take care of any return arguments
if (fHasRetBuffArg)
{
// We stack-allocate this ret buff, to preserve the invariant that ret-buffs are always in the
// caller's stack frame. We'll copy into gc.retVal later.
TypeHandle retTH = gc.pSig->GetReturnTypeHandle();
MethodTable* pMT = retTH.GetMethodTable();
if (pMT->IsStructRequiringStackAllocRetBuf())
{
SIZE_T sz = pMT->GetNumInstanceFieldBytes();
pRetBufStackCopy = _alloca(sz);
memset(pRetBufStackCopy, 0, sz);
pValueClasses = new (_alloca(sizeof(ValueClassInfo))) ValueClassInfo(pRetBufStackCopy, pMT, pValueClasses);
*((LPVOID*) (pTransitionBlock + argit.GetRetBuffArgOffset())) = pRetBufStackCopy;
}
else
{
PVOID pRetBuff = gc.retVal->GetData();
*((LPVOID*) (pTransitionBlock + argit.GetRetBuffArgOffset())) = pRetBuff;
}
}
// copy args
UINT nNumArgs = gc.pSig->NumFixedArgs();
for (UINT i = 0 ; i < nNumArgs; i++) {
TypeHandle th = gc.pSig->GetArgumentAt(i);
int ofs = argit.GetNextOffset();
_ASSERTE(ofs != TransitionBlock::InvalidOffset);
#ifdef CALLDESCR_REGTYPEMAP
FillInRegTypeMap(ofs, argit.GetArgType(), (BYTE *)&callDescrData.dwRegTypeMap);
#endif
#ifdef CALLDESCR_FPARGREGS
// Under CALLDESCR_FPARGREGS -ve offsets indicate arguments in floating point registers. If we have at
// least one such argument we point the call worker at the floating point area of the frame (we leave
// it null otherwise since the worker can perform a useful optimization if it knows no floating point
// registers need to be set up).
if (TransitionBlock::HasFloatRegister(ofs, argit.GetArgLocDescForStructInRegs()) &&
(callDescrData.pFloatArgumentRegisters == NULL))
{
callDescrData.pFloatArgumentRegisters = (FloatArgumentRegisters*) (pTransitionBlock +
TransitionBlock::GetOffsetOfFloatArgumentRegisters());
}
#endif
UINT structSize = argit.GetArgSize();
bool needsStackCopy = false;
// A boxed Nullable<T> is represented as boxed T. So to pass a Nullable<T> by reference,
// we have to create a Nullable<T> on stack, copy the T into it, then pass it to the callee and
// after returning from the call, copy the T out of the Nullable<T> back to the boxed T.
TypeHandle nullableType = NullableTypeOfByref(th);
if (!nullableType.IsNull()) {
th = nullableType;
structSize = th.GetSize();
needsStackCopy = true;
}
#ifdef ENREGISTERED_PARAMTYPE_MAXSIZE
else if (argit.IsArgPassedByRef())
{
needsStackCopy = true;
}
#endif
ArgDestination argDest(pTransitionBlock, ofs, argit.GetArgLocDescForStructInRegs());
if(needsStackCopy)
{
MethodTable * pMT = th.GetMethodTable();
_ASSERTE(pMT && pMT->IsValueType());
PVOID pArgDst = argDest.GetDestinationAddress();
PVOID pStackCopy = _alloca(structSize);
*(PVOID *)pArgDst = pStackCopy;
pArgDst = pStackCopy;
if (!nullableType.IsNull())
{
byRefToNullables = new(_alloca(sizeof(ByRefToNullable))) ByRefToNullable(i, pStackCopy, nullableType, byRefToNullables);
}
// save the info into ValueClassInfo
if (pMT->ContainsPointers())
{
pValueClasses = new (_alloca(sizeof(ValueClassInfo))) ValueClassInfo(pStackCopy, pMT, pValueClasses);
}
// We need a new ArgDestination that points to the stack copy
argDest = ArgDestination(pStackCopy, 0, NULL);
}
InvokeUtil::CopyArg(th, &(gc.args->m_Array[i]), &argDest);
}
ENDFORBIDGC();
}
#ifdef FEATURE_CORRUPTING_EXCEPTIONS
// By default, set the flag in TES indicating the reflection target can handle CSE.
// This flag is used in CEHelper::CanIDispatchTargetHandleException.
pThread->GetExceptionState()->SetCanReflectionTargetHandleException(TRUE);
#endif // FEATURE_CORRUPTING_EXCEPTIONS
if (pValueClasses != NULL)
{
pProtectValueClassFrame = new (_alloca (sizeof (FrameWithCookie<ProtectValueClassFrame>)))
FrameWithCookie<ProtectValueClassFrame>(pThread, pValueClasses);
}
// Call the method
bool fExceptionThrown = false;
if (fWrapExceptions)
{
// The sole purpose of having this frame is to tell the debugger that we have a catch handler here
// which may swallow managed exceptions. The debugger needs this in order to send a
// CatchHandlerFound (CHF) notification.
FrameWithCookie<DebuggerU2MCatchHandlerFrame> catchFrame(pThread);
EX_TRY_THREAD(pThread) {
CallDescrWorkerReflectionWrapper(&callDescrData, &catchFrame);
} EX_CATCH{
// Rethrow transient exceptions for constructors for backward compatibility
if (fConstructor && GET_EXCEPTION()->IsTransient())
{
EX_RETHROW;
}
// Abuse retval to store the exception object
gc.retVal = GET_THROWABLE();
_ASSERTE(gc.retVal);
fExceptionThrown = true;
} EX_END_CATCH(SwallowAllExceptions);
catchFrame.Pop(pThread);
}
else
{
CallDescrWorkerWithHandler(&callDescrData);
}
// Now that we are safely out of the catch block, we can create and raise the
// TargetInvocationException.
if (fExceptionThrown)
{
ThrowInvokeMethodException(pMeth, gc.retVal);
}
// It is still illegal to do a GC here. The return type might have/contain GC pointers.
if (fConstructor)
{
// We have a special case for Strings...The object is returned...
if (ownerType == TypeHandle(g_pStringClass)) {
PVOID pReturnValue = &callDescrData.returnValue;
gc.retVal = *(OBJECTREF *)pReturnValue;
}
// If it is a Nullable<T>, box it using Nullable<T> conventions.
// TODO: this double allocates on constructions which is wasteful
gc.retVal = Nullable::NormalizeBox(gc.retVal);
}
else
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.
else if (!fHasRetBuffArg)
{
CopyValueClass(gc.retVal->GetData(), &callDescrData.returnValue, gc.retVal->GetMethodTable(), gc.retVal->GetAppDomain());
}
else if (pRetBufStackCopy)
{
CopyValueClass(gc.retVal->GetData(), pRetBufStackCopy, gc.retVal->GetMethodTable(), gc.retVal->GetAppDomain());
}
// From here on out, it is OK to have GCs since the return object (which may have had
// GC pointers has been put into a GC object and thus protected.
// TODO this creates two objects which is inefficient
// 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::CreateObjectAfterInvoke(retTH, &callDescrData.returnValue);
}
while (byRefToNullables != NULL) {
OBJECTREF obj = Nullable::Box(byRefToNullables->data, byRefToNullables->type.GetMethodTable());
SetObjectReference(&gc.args->m_Array[byRefToNullables->argNum], obj, gc.args->GetAppDomain());
byRefToNullables = byRefToNullables->next;
}
if (pProtectValueClassFrame != NULL)
pProtectValueClassFrame->Pop(pThread);
END_INTERIOR_STACK_PROBE;
}
Done:
HELPER_METHOD_FRAME_END();
return OBJECTREFToObject(gc.retVal);
}
FCIMPLEND
struct SkipStruct {
StackCrawlMark* pStackMark;
MethodDesc* pMeth;
};
// This method is called by the GetMethod function and will crawl backward
// up the stack for integer methods.
static StackWalkAction SkipMethods(CrawlFrame* frame, VOID* data) {
CONTRACTL {
NOTHROW;
GC_NOTRIGGER;
MODE_ANY;
}
CONTRACTL_END;
SkipStruct* pSkip = (SkipStruct*) data;
MethodDesc *pFunc = frame->GetFunction();
/* We asked to be called back only for functions */
_ASSERTE(pFunc);
// The check here is between the address of a local variable
// (the stack mark) and a pointer to the EIP for a frame
// (which is actually the pointer to the return address to the
// function from the previous frame). So we'll actually notice
// which frame the stack mark was in one frame later. This is
// fine since we only implement LookForMyCaller.
_ASSERTE(*pSkip->pStackMark == LookForMyCaller);
if (!frame->IsInCalleesFrames(pSkip->pStackMark))
return SWA_CONTINUE;
if (pFunc->RequiresInstMethodDescArg())
{
pSkip->pMeth = (MethodDesc *) frame->GetParamTypeArg();
if (pSkip->pMeth == NULL)
pSkip->pMeth = pFunc;
}
else
pSkip->pMeth = pFunc;
return SWA_ABORT;
}
// Return the MethodInfo that represents the current method (two above this one)
FCIMPL1(ReflectMethodObject*, RuntimeMethodHandle::GetCurrentMethod, StackCrawlMark* stackMark) {
FCALL_CONTRACT;
REFLECTMETHODREF pRet = NULL;
HELPER_METHOD_FRAME_BEGIN_RET_0();
SkipStruct skip;
skip.pStackMark = stackMark;
skip.pMeth = 0;
StackWalkFunctions(GetThread(), SkipMethods, &skip);
// If C<Foo>.m<Bar> was called, the stack walker returns C<object>.m<object>. We cannot
// get know that the instantiation used Foo or Bar at that point. So the next best thing
// is to return C<T>.m<P> and that's what LoadTypicalMethodDefinition will do for us.
if (skip.pMeth != NULL)
pRet = skip.pMeth->LoadTypicalMethodDefinition()->GetStubMethodInfo();
else
pRet = NULL;
HELPER_METHOD_FRAME_END();
return (ReflectMethodObject*)OBJECTREFToObject(pRet);
}
FCIMPLEND
static OBJECTREF DirectObjectFieldGet(FieldDesc *pField, TypeHandle fieldType, TypeHandle enclosingType, TypedByRef *pTarget, CLR_BOOL *pDomainInitialized) {
CONTRACTL {
THROWS;
GC_TRIGGERS;
MODE_COOPERATIVE;
PRECONDITION(CheckPointer(pField));
}
CONTRACTL_END;
OBJECTREF refRet;
OBJECTREF objref = NULL;
GCPROTECT_BEGIN(objref);
if (!pField->IsStatic()) {
objref = ObjectToOBJECTREF(*((Object**)pTarget->data));
}
InvokeUtil::ValidateObjectTarget(pField, enclosingType, &objref);
refRet = InvokeUtil::GetFieldValue(pField, fieldType, &objref, enclosingType, pDomainInitialized);
GCPROTECT_END();
return refRet;
}
FCIMPL4(Object*, RuntimeFieldHandle::GetValueDirect, ReflectFieldObject *pFieldUNSAFE, ReflectClassBaseObject *pFieldTypeUNSAFE, TypedByRef *pTarget, ReflectClassBaseObject *pDeclaringTypeUNSAFE) {
CONTRACTL {
FCALL_CHECK;
}
CONTRACTL_END;
struct
{
REFLECTCLASSBASEREF refFieldType;
REFLECTCLASSBASEREF refDeclaringType;
REFLECTFIELDREF refField;
}gc;
gc.refFieldType = (REFLECTCLASSBASEREF)ObjectToOBJECTREF(pFieldTypeUNSAFE);
gc.refDeclaringType = (REFLECTCLASSBASEREF)ObjectToOBJECTREF(pDeclaringTypeUNSAFE);
gc.refField = (REFLECTFIELDREF)ObjectToOBJECTREF(pFieldUNSAFE);
if ((gc.refFieldType == NULL) || (gc.refField == NULL))
FCThrowRes(kArgumentNullException, W("Arg_InvalidHandle"));
TypeHandle fieldType = gc.refFieldType->GetType();
FieldDesc *pField = gc.refField->GetField();
Assembly *pAssem = pField->GetModule()->GetAssembly();
if (pAssem->IsIntrospectionOnly())
FCThrowEx(kInvalidOperationException, IDS_EE_CODEEXECUTION_IN_INTROSPECTIVE_ASSEMBLY, NULL, NULL, NULL);
// We should throw NotSupportedException here.
// But for backward compatibility we are throwing FieldAccessException instead.
if (pAssem->IsDynamic() && !pAssem->HasRunAccess())
FCThrow(kFieldAccessException);
OBJECTREF refRet = NULL;
CorElementType fieldElType;
HELPER_METHOD_FRAME_BEGIN_RET_PROTECT(gc);
// Find the Object and its type
TypeHandle targetType = pTarget->type;
_ASSERTE(gc.refDeclaringType == NULL || !gc.refDeclaringType->GetType().IsTypeDesc());
MethodTable *pEnclosingMT = (gc.refDeclaringType != NULL ? gc.refDeclaringType->GetType() : TypeHandle()).AsMethodTable();
// Verify the callee/caller access
if (!pField->IsPublic() || (pEnclosingMT != NULL && !pEnclosingMT->IsExternallyVisible()))
{
bool targetRemoted = false;
RefSecContext sCtx(InvokeUtil::GetInvocationAccessCheckType(targetRemoted));
MethodTable* pInstanceMT = NULL;
if (!pField->IsStatic())
{
if (!targetType.IsTypeDesc())
pInstanceMT = targetType.AsMethodTable();
}
//TODO: missing check that the field is consistent
// Perform the normal access check (caller vs field).
InvokeUtil::CanAccessField(&sCtx,
pEnclosingMT,
pInstanceMT,
pField);
}
CLR_BOOL domainInitialized = FALSE;
if (pField->IsStatic() || !targetType.IsValueType()) {
refRet = DirectObjectFieldGet(pField, fieldType, TypeHandle(pEnclosingMT), pTarget, &domainInitialized);
goto lExit;
}
// Validate that the target type can be cast to the type that owns this field info.
if (!targetType.CanCastTo(TypeHandle(pEnclosingMT)))
COMPlusThrowArgumentException(W("obj"), NULL);
// This is a workaround because from the previous case we may end up with an
// Enum. We want to process it here.
// Get the value from the field
void* p;
fieldElType = fieldType.GetSignatureCorElementType();
switch (fieldElType) {
case ELEMENT_TYPE_VOID:
_ASSERTE(!"Void used as Field Type!");
COMPlusThrow(kInvalidProgramException);
case ELEMENT_TYPE_BOOLEAN: // boolean
case ELEMENT_TYPE_I1: // byte
case ELEMENT_TYPE_U1: // unsigned byte
case ELEMENT_TYPE_I2: // short
case ELEMENT_TYPE_U2: // unsigned short
case ELEMENT_TYPE_CHAR: // char
case ELEMENT_TYPE_I4: // int
case ELEMENT_TYPE_U4: // unsigned int
case ELEMENT_TYPE_I:
case ELEMENT_TYPE_U:
case ELEMENT_TYPE_R4: // float
case ELEMENT_TYPE_I8: // long
case ELEMENT_TYPE_U8: // unsigned long
case ELEMENT_TYPE_R8: // double
case ELEMENT_TYPE_VALUETYPE:
_ASSERTE(!fieldType.IsTypeDesc());
p = ((BYTE*) pTarget->data) + pField->GetOffset();
refRet = fieldType.AsMethodTable()->Box(p);
break;
case ELEMENT_TYPE_OBJECT:
case ELEMENT_TYPE_CLASS:
case ELEMENT_TYPE_SZARRAY: // Single Dim, Zero
case ELEMENT_TYPE_ARRAY: // general array
p = ((BYTE*) pTarget->data) + pField->GetOffset();
refRet = ObjectToOBJECTREF(*(Object**) p);
break;
case ELEMENT_TYPE_PTR:
{
p = ((BYTE*) pTarget->data) + pField->GetOffset();
refRet = InvokeUtil::CreatePointer(fieldType, *(void **)p);
break;
}
default:
_ASSERTE(!"Unknown Type");
// this is really an impossible condition
COMPlusThrow(kNotSupportedException);
}
lExit: ;
HELPER_METHOD_FRAME_END();
return OBJECTREFToObject(refRet);
}
FCIMPLEND
static void DirectObjectFieldSet(FieldDesc *pField, TypeHandle fieldType, TypeHandle enclosingType, TypedByRef *pTarget, OBJECTREF *pValue, CLR_BOOL *pDomainInitialized) {
CONTRACTL {
THROWS;
GC_TRIGGERS;
MODE_COOPERATIVE;
PRECONDITION(CheckPointer(pField));
PRECONDITION(!fieldType.IsNull());
}
CONTRACTL_END;
OBJECTREF objref = NULL;
GCPROTECT_BEGIN(objref);
if (!pField->IsStatic()) {
objref = ObjectToOBJECTREF(*((Object**)pTarget->data));
}
// Validate the target/fld type relationship
InvokeUtil::ValidateObjectTarget(pField, enclosingType, &objref);
InvokeUtil::ValidField(fieldType, pValue);
InvokeUtil::SetValidField(pField->GetFieldType(), fieldType, pField, &objref, pValue, enclosingType, pDomainInitialized);
GCPROTECT_END();
}
FCIMPL5(void, RuntimeFieldHandle::SetValueDirect, ReflectFieldObject *pFieldUNSAFE, ReflectClassBaseObject *pFieldTypeUNSAFE, TypedByRef *pTarget, Object *valueUNSAFE, ReflectClassBaseObject *pContextTypeUNSAFE) {
CONTRACTL {
FCALL_CHECK;
}
CONTRACTL_END;
struct _gc
{
OBJECTREF oValue;
REFLECTCLASSBASEREF pFieldType;
REFLECTCLASSBASEREF pContextType;
REFLECTFIELDREF refField;
}gc;
gc.oValue = ObjectToOBJECTREF(valueUNSAFE);
gc.pFieldType = (REFLECTCLASSBASEREF)ObjectToOBJECTREF(pFieldTypeUNSAFE);
gc.pContextType = (REFLECTCLASSBASEREF)ObjectToOBJECTREF(pContextTypeUNSAFE);
gc.refField = (REFLECTFIELDREF)ObjectToOBJECTREF(pFieldUNSAFE);
if ((gc.pFieldType == NULL) || (gc.refField == NULL))
FCThrowResVoid(kArgumentNullException, W("Arg_InvalidHandle"));
TypeHandle fieldType = gc.pFieldType->GetType();
TypeHandle contextType = (gc.pContextType != NULL) ? gc.pContextType->GetType() : NULL;
FieldDesc *pField = gc.refField->GetField();
Assembly *pAssem = pField->GetModule()->GetAssembly();
if (pAssem->IsIntrospectionOnly())
FCThrowExVoid(kInvalidOperationException, IDS_EE_CODEEXECUTION_IN_INTROSPECTIVE_ASSEMBLY, NULL, NULL, NULL);
// We should throw NotSupportedException here.
// But for backward compatibility we are throwing FieldAccessException instead.
if (pAssem->IsDynamic() && !pAssem->HasRunAccess())
FCThrowVoid(kFieldAccessException);
BYTE *pDst = NULL;
ARG_SLOT value = NULL;
CorElementType fieldElType;
HELPER_METHOD_FRAME_BEGIN_PROTECT(gc);
// Find the Object and its type
TypeHandle targetType = pTarget->type;
MethodTable *pEnclosingMT = contextType.GetMethodTable();
{
// Verify that the value passed can be widened into the target
InvokeUtil::ValidField(fieldType, &gc.oValue);
// Verify that this is not a Final Field
DWORD attr = pField->GetAttributes(); // should we cache?
if (IsFdLiteral(attr))
COMPlusThrow(kFieldAccessException,W("Acc_ReadOnly"));
// Verify the callee/caller access
if (!pField->IsPublic() || (pEnclosingMT != NULL && !pEnclosingMT->IsExternallyVisible()))
{
// security and consistency checks
bool targetRemoted = false;
RefSecContext sCtx(InvokeUtil::GetInvocationAccessCheckType(targetRemoted));
MethodTable* pInstanceMT = NULL;
if (!pField->IsStatic()) {
if (!targetType.IsTypeDesc())
pInstanceMT = targetType.AsMethodTable();
}
//TODO: missing check that the field is consistent
// Perform the normal access check (caller vs field).
InvokeUtil::CanAccessField(&sCtx,
pEnclosingMT,
pInstanceMT,
pField);
}
}
CLR_BOOL domainInitialized = FALSE;
if (pField->IsStatic() || !targetType.IsValueType()) {
DirectObjectFieldSet(pField, fieldType, TypeHandle(pEnclosingMT), pTarget, &gc.oValue, &domainInitialized);
goto lExit;
}
if (gc.oValue == NULL && fieldType.IsValueType() && !Nullable::IsNullableType(fieldType))
COMPlusThrowArgumentNull(W("value"));
// Validate that the target type can be cast to the type that owns this field info.
if (!targetType.CanCastTo(TypeHandle(pEnclosingMT)))
COMPlusThrowArgumentException(W("obj"), NULL);
// Set the field
fieldElType = fieldType.GetInternalCorElementType();
if (ELEMENT_TYPE_BOOLEAN <= fieldElType && fieldElType <= ELEMENT_TYPE_R8) {
CorElementType objType = gc.oValue->GetTypeHandle().GetInternalCorElementType();
if (objType != fieldElType)
InvokeUtil::CreatePrimitiveValue(fieldElType, objType, gc.oValue, &value);
else
value = *(ARG_SLOT*)gc.oValue->UnBox();
}
pDst = ((BYTE*) pTarget->data) + pField->GetOffset();
switch (fieldElType) {
case ELEMENT_TYPE_VOID:
_ASSERTE(!"Void used as Field Type!");
COMPlusThrow(kInvalidProgramException);
case ELEMENT_TYPE_BOOLEAN: // boolean
case ELEMENT_TYPE_I1: // byte
case ELEMENT_TYPE_U1: // unsigned byte
VolatileStore((UINT8*)pDst, *(UINT8*)&value);
break;
case ELEMENT_TYPE_I2: // short
case ELEMENT_TYPE_U2: // unsigned short
case ELEMENT_TYPE_CHAR: // char
VolatileStore((UINT16*)pDst, *(UINT16*)&value);
break;
case ELEMENT_TYPE_I4: // int
case ELEMENT_TYPE_U4: // unsigned int
case ELEMENT_TYPE_R4: // float
VolatileStore((UINT32*)pDst, *(UINT32*)&value);
break;
case ELEMENT_TYPE_I8: // long
case ELEMENT_TYPE_U8: // unsigned long
case ELEMENT_TYPE_R8: // double
VolatileStore((UINT64*)pDst, *(UINT64*)&value);
break;
case ELEMENT_TYPE_I:
{
INT_PTR valuePtr = (INT_PTR) InvokeUtil::GetIntPtrValue(gc.oValue);
VolatileStore((INT_PTR*) pDst, valuePtr);
}
break;
case ELEMENT_TYPE_U:
{
UINT_PTR valuePtr = (UINT_PTR) InvokeUtil::GetIntPtrValue(gc.oValue);
VolatileStore((UINT_PTR*) pDst, valuePtr);
}
break;
case ELEMENT_TYPE_PTR: // pointers
if (gc.oValue != 0) {
value = 0;
if (MscorlibBinder::IsClass(gc.oValue->GetMethodTable(), CLASS__POINTER)) {
value = (size_t) InvokeUtil::GetPointerValue(gc.oValue);
#ifdef _MSC_VER
#pragma warning(disable: 4267) //work-around for compiler
#endif
VolatileStore((size_t*) pDst, (size_t) value);
#ifdef _MSC_VER
#pragma warning(default: 4267)
#endif
break;
}
}
// drop through
case ELEMENT_TYPE_FNPTR:
{
value = 0;
if (gc.oValue != 0) {
CorElementType objType = gc.oValue->GetTypeHandle().GetInternalCorElementType();
InvokeUtil::CreatePrimitiveValue(objType, objType, gc.oValue, &value);
}
#ifdef _MSC_VER
#pragma warning(disable: 4267) //work-around for compiler
#endif
VolatileStore((size_t*) pDst, (size_t) value);
#ifdef _MSC_VER
#pragma warning(default: 4267)
#endif
}
break;
case ELEMENT_TYPE_SZARRAY: // Single Dim, Zero
case ELEMENT_TYPE_ARRAY: // General Array
case ELEMENT_TYPE_CLASS:
case ELEMENT_TYPE_OBJECT:
SetObjectReferenceUnchecked((OBJECTREF*)pDst, gc.oValue);
break;
case ELEMENT_TYPE_VALUETYPE:
{
_ASSERTE(!fieldType.IsTypeDesc());
MethodTable* pMT = fieldType.AsMethodTable();
// If we have a null value then we must create an empty field
if (gc.oValue == 0)
InitValueClass(pDst, pMT);
else {
pMT->UnBoxIntoUnchecked(pDst, gc.oValue);
}
}
break;
default:
_ASSERTE(!"Unknown Type");
// this is really an impossible condition
COMPlusThrow(kNotSupportedException);
}
lExit: ;
HELPER_METHOD_FRAME_END();
}
FCIMPLEND
void QCALLTYPE ReflectionInvocation::CompileMethod(MethodDesc * pMD)
{
QCALL_CONTRACT;
// Argument is checked on the managed side
PRECONDITION(pMD != NULL);
if (!pMD->IsPointingToPrestub())
return;
BEGIN_QCALL;
pMD->DoPrestub(NULL);
END_QCALL;
}
// This method triggers the class constructor for a give type
FCIMPL1(void, ReflectionInvocation::RunClassConstructor, ReflectClassBaseObject *pTypeUNSAFE)
{
FCALL_CONTRACT;
REFLECTCLASSBASEREF refType = (REFLECTCLASSBASEREF)ObjectToOBJECTREF(pTypeUNSAFE);
if (refType == NULL)
FCThrowArgumentVoidEx(kArgumentException, NULL, W("InvalidOperation_HandleIsNotInitialized"));
TypeHandle typeHnd = refType->GetType();
if (typeHnd.IsTypeDesc())
return;
MethodTable *pMT = typeHnd.AsMethodTable();
Assembly *pAssem = pMT->GetAssembly();
if (pAssem->IsIntrospectionOnly())
FCThrowExVoid(kInvalidOperationException, IDS_EE_CODEEXECUTION_IN_INTROSPECTIVE_ASSEMBLY, NULL, NULL, NULL);
if (pAssem->IsDynamic() && !pAssem->HasRunAccess())
{
FCThrowResVoid(kNotSupportedException, W("NotSupported_DynamicAssemblyNoRunAccess"));
}
if (!pMT->IsClassInited())
{
HELPER_METHOD_FRAME_BEGIN_1(refType);
// We perform the access check only on CoreCLR for backward compatibility.
RefSecContext sCtx(InvokeUtil::GetInvocationAccessCheckType());
InvokeUtil::CanAccessClass(&sCtx, pMT);
pMT->CheckRestore();
pMT->EnsureInstanceActive();
pMT->CheckRunClassInitThrowing();
HELPER_METHOD_FRAME_END();
}
}
FCIMPLEND
// This method triggers the module constructor for a give module
FCIMPL1(void, ReflectionInvocation::RunModuleConstructor, ReflectModuleBaseObject *pModuleUNSAFE) {
FCALL_CONTRACT;
REFLECTMODULEBASEREF refModule = (REFLECTMODULEBASEREF)ObjectToOBJECTREF(pModuleUNSAFE);
if(refModule == NULL)
FCThrowArgumentVoidEx(kArgumentException, NULL, W("InvalidOperation_HandleIsNotInitialized"));
Module *pModule = refModule->GetModule();
Assembly *pAssem = pModule->GetAssembly();
if (pAssem->IsIntrospectionOnly())
FCThrowExVoid(kInvalidOperationException, IDS_EE_CODEEXECUTION_IN_INTROSPECTIVE_ASSEMBLY, NULL, NULL, NULL);
if (pAssem->IsDynamic() && !pAssem->HasRunAccess())
FCThrowResVoid(kNotSupportedException, W("NotSupported_DynamicAssemblyNoRunAccess"));
DomainFile *pDomainFile = pModule->FindDomainFile(GetAppDomain());
if (pDomainFile==NULL || !pDomainFile->IsActive())
{
HELPER_METHOD_FRAME_BEGIN_1(refModule);
if(pDomainFile==NULL)
pDomainFile=pModule->GetDomainFile();
pDomainFile->EnsureActive();
HELPER_METHOD_FRAME_END();
}
}
FCIMPLEND
static void PrepareMethodHelper(MethodDesc * pMD)
{
CONTRACTL
{
THROWS;
GC_TRIGGERS;
MODE_ANY;
}
CONTRACTL_END;
GCX_PREEMP();
if (pMD->IsPointingToPrestub())
pMD->DoPrestub(NULL);
if (pMD->IsWrapperStub())
{
pMD = pMD->GetWrappedMethodDesc();
if (pMD->IsPointingToPrestub())
pMD->DoPrestub(NULL);
}
}
// This method triggers a given method to be jitted. CoreCLR implementation of this method triggers jiting of the given method only.
// It does not walk a subset of callgraph to provide CER guarantees.
FCIMPL3(void, ReflectionInvocation::PrepareMethod, ReflectMethodObject* pMethodUNSAFE, TypeHandle *pInstantiation, UINT32 cInstantiation)
{
CONTRACTL {
FCALL_CHECK;
PRECONDITION(CheckPointer(pMethodUNSAFE, NULL_OK));
PRECONDITION(CheckPointer(pInstantiation, NULL_OK));
}
CONTRACTL_END;
REFLECTMETHODREF refMethod = (REFLECTMETHODREF)ObjectToOBJECTREF(pMethodUNSAFE);
HELPER_METHOD_FRAME_BEGIN_1(refMethod);
if (refMethod == NULL)
COMPlusThrow(kArgumentException, W("InvalidOperation_HandleIsNotInitialized"));
MethodDesc *pMD = refMethod->GetMethod();
if (pMD->IsAbstract())
COMPlusThrow(kArgumentException, W("Argument_CannotPrepareAbstract"));
MethodTable * pExactMT = pMD->GetMethodTable();
if (pInstantiation != NULL)
{
// We were handed an instantiation, check that the method expects it and the right number of types has been provided (the
// caller supplies one array containing the class instantiation immediately followed by the method instantiation).
if (cInstantiation != (pMD->GetNumGenericMethodArgs() + pMD->GetNumGenericClassArgs()))
COMPlusThrow(kArgumentException, W("Argument_InvalidGenericInstantiation"));
// Check we've got a reasonable looking instantiation.
if (!Generics::CheckInstantiation(Instantiation(pInstantiation, cInstantiation)))
COMPlusThrow(kArgumentException, W("Argument_InvalidGenericInstantiation"));
for (ULONG i = 0; i < cInstantiation; i++)
if (pInstantiation[i].ContainsGenericVariables())
COMPlusThrow(kArgumentException, W("Argument_InvalidGenericInstantiation"));
TypeHandle thExactType = ClassLoader::LoadGenericInstantiationThrowing(pMD->GetModule(),
pMD->GetMethodTable()->GetCl(),
Instantiation(pInstantiation, pMD->GetNumGenericClassArgs()));
pExactMT = thExactType.AsMethodTable();
pMD = MethodDesc::FindOrCreateAssociatedMethodDesc(pMD,
pExactMT,
FALSE,
Instantiation(&pInstantiation[pMD->GetNumGenericClassArgs()], pMD->GetNumGenericMethodArgs()),
FALSE);
}
if (pMD->ContainsGenericVariables())
COMPlusThrow(kArgumentException, W("Argument_InvalidGenericInstantiation"));
PrepareMethodHelper(pMD);
HELPER_METHOD_FRAME_END();
}
FCIMPLEND
// This method triggers target of a given method to be jitted. CoreCLR implementation of this method triggers jiting
// of the given method only. It does not walk a subset of callgraph to provide CER guarantees.
// In the case of a multi-cast delegate, we rely on the fact that each individual component
// was prepared prior to the Combine.
FCIMPL1(void, ReflectionInvocation::PrepareDelegate, Object* delegateUNSAFE)
{
CONTRACTL {
FCALL_CHECK;
PRECONDITION(CheckPointer(delegateUNSAFE, NULL_OK));
}
CONTRACTL_END;
if (delegateUNSAFE == NULL)
return;
OBJECTREF delegate = ObjectToOBJECTREF(delegateUNSAFE);
HELPER_METHOD_FRAME_BEGIN_1(delegate);
MethodDesc *pMD = COMDelegate::GetMethodDesc(delegate);
PrepareMethodHelper(pMD);
HELPER_METHOD_FRAME_END();
}
FCIMPLEND
// This method checks to see if there is sufficient stack to execute the average Framework method.
// If there is not, then it throws System.InsufficientExecutionStackException. The limit for each
// thread is precomputed when the thread is created.
FCIMPL0(void, ReflectionInvocation::EnsureSufficientExecutionStack)
{
FCALL_CONTRACT;
Thread *pThread = GetThread();
// We use the address of a local variable as our "current stack pointer", which is
// plenty close enough for the purposes of this method.
UINT_PTR current = reinterpret_cast<UINT_PTR>(&pThread);
UINT_PTR limit = pThread->GetCachedStackSufficientExecutionLimit();
if (current < limit)
{
FCThrowVoid(kInsufficientExecutionStackException);
}
}
FCIMPLEND
// As with EnsureSufficientExecutionStack, this method checks and returns whether there is
// sufficient stack to execute the average Framework method, but rather than throwing,
// it simply returns a Boolean: true for sufficient stack space, otherwise false.
FCIMPL0(FC_BOOL_RET, ReflectionInvocation::TryEnsureSufficientExecutionStack)
{
FCALL_CONTRACT;
Thread *pThread = GetThread();
// Same logic as EnsureSufficientExecutionStack
UINT_PTR current = reinterpret_cast<UINT_PTR>(&pThread);
UINT_PTR limit = pThread->GetCachedStackSufficientExecutionLimit();
FC_RETURN_BOOL(current >= limit);
}
FCIMPLEND
struct ECWGCFContext
{
BOOL fHandled;
Frame *pStartFrame;
};
// Crawl the stack looking for Thread Abort related information (whether we're executing inside a CER or an error handling clauses
// of some sort).
StackWalkAction ECWGCFCrawlCallBack(CrawlFrame* pCf, void* data)
{
CONTRACTL {
NOTHROW;
GC_NOTRIGGER;
}
CONTRACTL_END;
ECWGCFContext *pData = (ECWGCFContext *)data;
Frame *pFrame = pCf->GetFrame();
if (pFrame && pFrame->GetFunction() != NULL && pFrame != pData->pStartFrame)
{
// We walk through a transition frame, but it is not our start frame.
// This means ExecuteCodeWithGuarantee is not at the bottom of stack.
pData->fHandled = TRUE;
return SWA_ABORT;
}
MethodDesc *pMD = pCf->GetFunction();
// Non-method frames don't interest us.
if (pMD == NULL)
return SWA_CONTINUE;
if (!pMD->GetModule()->IsSystem())
{
// We walk through some user code. This means that ExecuteCodeWithGuarantee is not at the bottom of stack.
pData->fHandled = TRUE;
return SWA_ABORT;
}
return SWA_CONTINUE;
}
struct ECWGC_Param
{
BOOL fExceptionThrownInTryCode;
BOOL fStackOverflow;
struct ECWGC_GC *gc;
ECWGC_Param()
{
fExceptionThrownInTryCode = FALSE;
fStackOverflow = FALSE;
}
};
LONG SODetectionFilter(EXCEPTION_POINTERS *ep, void* pv)
{
WRAPPER_NO_CONTRACT;
DefaultCatchFilterParam param(COMPLUS_EXCEPTION_EXECUTE_HANDLER);
if (DefaultCatchFilter(ep, &param) == EXCEPTION_CONTINUE_EXECUTION)
{
return EXCEPTION_CONTINUE_EXECUTION;
}
// Record the fact that an exception occurred while running the try code.
ECWGC_Param *pParam= (ECWGC_Param *)pv;
pParam->fExceptionThrownInTryCode = TRUE;
// We unwind the stack only in the case of a stack overflow.
if (ep->ExceptionRecord->ExceptionCode == STATUS_STACK_OVERFLOW)
{
pParam->fStackOverflow = TRUE;
return EXCEPTION_EXECUTE_HANDLER;
}
return EXCEPTION_CONTINUE_SEARCH;
}
struct ECWGC_GC
{
DELEGATEREF codeDelegate;
DELEGATEREF backoutDelegate;
OBJECTREF userData;
};
void ExecuteCodeWithGuaranteedCleanupBackout(ECWGC_GC *gc, BOOL fExceptionThrownInTryCode)
{
// We need to prevent thread aborts from occuring for the duration of the call to the backout code.
// Once we enter managed code, the CER will take care of it as well; however without this holder,
// MethodDesc::Call would raise a thread abort exception if the thread is currently requesting one.
ThreadPreventAbortHolder preventAbort;
#ifdef _DEBUG
// We have prevented abort on this thread. Normally we don't allow
// a thread to enter managed code if abort is prevented. But here the code
// requires the thread not be aborted.
Thread::DisableAbortCheckHolder dach;
#endif
GCX_COOP();
PREPARE_NONVIRTUAL_CALLSITE_USING_METHODDESC(g_pExecuteBackoutCodeHelperMethod);
DECLARE_ARGHOLDER_ARRAY(args, 3);
args[ARGNUM_0] = OBJECTREF_TO_ARGHOLDER(gc->backoutDelegate);
args[ARGNUM_1] = OBJECTREF_TO_ARGHOLDER(gc->userData);
args[ARGNUM_2] = DWORD_TO_ARGHOLDER(fExceptionThrownInTryCode);
CRITICAL_CALLSITE;
CALL_MANAGED_METHOD_NORET(args);
}
void ExecuteCodeWithGuaranteedCleanupHelper (ECWGC_GC *gc)
{
STATIC_CONTRACT_THROWS;
STATIC_CONTRACT_MODE_COOPERATIVE;
ECWGC_Param param;
param.gc = gc;
PAL_TRY(ECWGC_Param *, pParamOuter, &param)
{
PAL_TRY(ECWGC_Param *, pParam, pParamOuter)
{
PREPARE_NONVIRTUAL_CALLSITE_USING_CODE(pParam->gc->codeDelegate->GetMethodPtr());
DECLARE_ARGHOLDER_ARRAY(args, 2);
args[ARGNUM_0] = OBJECTREF_TO_ARGHOLDER(pParam->gc->codeDelegate->GetTarget());
args[ARGNUM_1] = OBJECTREF_TO_ARGHOLDER(pParam->gc->userData);
CALL_MANAGED_METHOD_NORET(args);
}
PAL_EXCEPT_FILTER(SODetectionFilter)
{
}
PAL_ENDTRY;
if (pParamOuter->fStackOverflow)
{
GCX_COOP_NO_DTOR();
}
}
PAL_FINALLY
{
ExecuteCodeWithGuaranteedCleanupBackout(gc, param.fExceptionThrownInTryCode);
}
PAL_ENDTRY;
#ifdef FEATURE_STACK_PROBE
if (param.fStackOverflow)
COMPlusThrowSO();
#else
//This will not be set as clr to managed transition code will terminate the
//process if there is an SO before SODetectionFilter() is called.
_ASSERTE(!param.fStackOverflow);
#endif
}
//
// ExecuteCodeWithGuaranteedCleanup ensures that we will call the backout code delegate even if an SO occurs. We do this by calling the
// try delegate from within an EX_TRY/EX_CATCH block that will catch any thrown exceptions and thus cause the stack to be unwound. This
// guarantees that the backout delegate is called with at least DEFAULT_ENTRY_PROBE_SIZE pages of stack. After the backout delegate is called,
// we re-raise any exceptions that occurred inside the try delegate. Note that any CER that uses large or arbitrary amounts of stack in
// it's try block must use ExecuteCodeWithGuaranteedCleanup.
//
// ExecuteCodeWithGuaranteedCleanup also guarantees that the backount code will be run before any filters higher up on the stack. This
// is important to prevent security exploits.
//
FCIMPL3(void, ReflectionInvocation::ExecuteCodeWithGuaranteedCleanup, Object* codeDelegateUNSAFE, Object* backoutDelegateUNSAFE, Object* userDataUNSAFE)
{
CONTRACTL {
FCALL_CHECK;
PRECONDITION(CheckPointer(codeDelegateUNSAFE, NULL_OK));
PRECONDITION(CheckPointer(backoutDelegateUNSAFE, NULL_OK));
PRECONDITION(CheckPointer(userDataUNSAFE, NULL_OK));
}
CONTRACTL_END;
ECWGC_GC gc;
gc.codeDelegate = (DELEGATEREF)ObjectToOBJECTREF(codeDelegateUNSAFE);
gc.backoutDelegate = (DELEGATEREF)ObjectToOBJECTREF(backoutDelegateUNSAFE);
gc.userData = ObjectToOBJECTREF(userDataUNSAFE);
HELPER_METHOD_FRAME_BEGIN_PROTECT(gc);
if (gc.codeDelegate == NULL)
COMPlusThrowArgumentNull(W("code"));
if (gc.backoutDelegate == NULL)
COMPlusThrowArgumentNull(W("backoutCode"));
ExecuteCodeWithGuaranteedCleanupHelper(&gc);
HELPER_METHOD_FRAME_END();
}
FCIMPLEND
FCIMPL4(void, ReflectionInvocation::MakeTypedReference, TypedByRef * value, Object* targetUNSAFE, ArrayBase* fldsUNSAFE, ReflectClassBaseObject *pFieldTypeUNSAFE)
{
CONTRACTL {
FCALL_CHECK;
PRECONDITION(CheckPointer(targetUNSAFE));
PRECONDITION(CheckPointer(fldsUNSAFE));
}
CONTRACTL_END;
DWORD offset = 0;
struct _gc
{
OBJECTREF target;
BASEARRAYREF flds;
REFLECTCLASSBASEREF refFieldType;
} gc;
gc.target = (OBJECTREF) targetUNSAFE;
gc.flds = (BASEARRAYREF) fldsUNSAFE;
gc.refFieldType = (REFLECTCLASSBASEREF)ObjectToOBJECTREF(pFieldTypeUNSAFE);
TypeHandle fieldType = gc.refFieldType->GetType();
HELPER_METHOD_FRAME_BEGIN_PROTECT(gc);
GCPROTECT_BEGININTERIOR (value)
DWORD cnt = gc.flds->GetNumComponents();
FieldDesc** fields = (FieldDesc**)gc.flds->GetDataPtr();
for (DWORD i = 0; i < cnt; i++) {
FieldDesc* pField = fields[i];
offset += pField->GetOffset();
}
// Fields already are prohibted from having ArgIterator and RuntimeArgumentHandles
_ASSERTE(!gc.target->GetTypeHandle().GetMethodTable()->IsByRefLike());
// Create the ByRef
value->data = ((BYTE *)(gc.target->GetAddress() + offset)) + sizeof(Object);
value->type = fieldType;
GCPROTECT_END();
HELPER_METHOD_FRAME_END();
}
FCIMPLEND
FCIMPL2(void, ReflectionInvocation::SetTypedReference, TypedByRef * target, Object* objUNSAFE) {
FCALL_CONTRACT;
// <TODO>@TODO: We fixed serious bugs in this method very late in the endgame
// for V1 RTM. So it was decided to disable this API (nobody would seem to
// be using it anyway). If this API is enabled again, the implementation should
// be similar to COMArrayInfo::SetValue.
// </TODO>
HELPER_METHOD_FRAME_BEGIN_0();
COMPlusThrow(kNotSupportedException);
HELPER_METHOD_FRAME_END();
}
FCIMPLEND
// This is an internal helper function to TypedReference class.
// It extracts the object from the typed reference.
FCIMPL1(Object*, ReflectionInvocation::TypedReferenceToObject, TypedByRef * value) {
FCALL_CONTRACT;
OBJECTREF Obj = NULL;
TypeHandle th(value->type);
if (th.IsNull())
FCThrowRes(kArgumentNullException, W("ArgumentNull_TypedRefType"));
MethodTable* pMT = th.GetMethodTable();
PREFIX_ASSUME(NULL != pMT);
if (pMT->IsValueType())
{
// value->data is protected by the caller
HELPER_METHOD_FRAME_BEGIN_RET_1(Obj);
Obj = pMT->Box(value->data);
HELPER_METHOD_FRAME_END();
}
else {
Obj = ObjectToOBJECTREF(*((Object**)value->data));
}
return OBJECTREFToObject(Obj);
}
FCIMPLEND
#ifdef _DEBUG
FCIMPL1(FC_BOOL_RET, ReflectionInvocation::IsAddressInStack, void * ptr)
{
FCALL_CONTRACT;
FC_RETURN_BOOL(Thread::IsAddressInCurrentStack(ptr));
}
FCIMPLEND
#endif
FCIMPL2_IV(Object*, ReflectionInvocation::CreateEnum, ReflectClassBaseObject *pTypeUNSAFE, INT64 value) {
FCALL_CONTRACT;
REFLECTCLASSBASEREF refType = (REFLECTCLASSBASEREF)ObjectToOBJECTREF(pTypeUNSAFE);
TypeHandle typeHandle = refType->GetType();
_ASSERTE(typeHandle.IsEnum());
OBJECTREF obj = NULL;
HELPER_METHOD_FRAME_BEGIN_RET_1(refType);
MethodTable *pEnumMT = typeHandle.AsMethodTable();
obj = pEnumMT->Box(ArgSlotEndianessFixup ((ARG_SLOT*)&value,
pEnumMT->GetNumInstanceFieldBytes()));
HELPER_METHOD_FRAME_END();
return OBJECTREFToObject(obj);
}
FCIMPLEND
#ifdef FEATURE_COMINTEROP
static void TryGetClassFromProgID(STRINGREF className, STRINGREF server, OBJECTREF* pRefClass, DWORD bThrowOnError) {
CONTRACTL {
THROWS;
GC_TRIGGERS;
MODE_COOPERATIVE;
}
CONTRACTL_END;
EX_TRY
{
// NOTE: this call enables GC
GetComClassFromProgID(className, server, pRefClass);
}
EX_CATCH
{
if (bThrowOnError)
{
EX_RETHROW;
}
}
EX_END_CATCH(SwallowAllExceptions)
}
// GetClassFromProgID
// This method will return a Class object for a COM Classic object based
// upon its ProgID. The COM Classic object is found and a wrapper object created
FCIMPL3(Object*, ReflectionInvocation::GetClassFromProgID, StringObject* classNameUNSAFE,
StringObject* serverUNSAFE,
CLR_BOOL bThrowOnError) {
FCALL_CONTRACT;
REFLECTCLASSBASEREF refClass = NULL;
STRINGREF className = (STRINGREF) classNameUNSAFE;
STRINGREF server = (STRINGREF) serverUNSAFE;
HELPER_METHOD_FRAME_BEGIN_RET_2(className, server);
GCPROTECT_BEGIN(refClass)
// Since we will be returning a type that represents a COM component, we need
// to make sure COM is started before we return it.
EnsureComStarted();
// Make sure a prog id was provided
if (className == NULL)
COMPlusThrowArgumentNull(W("progID"),W("ArgumentNull_String"));
TryGetClassFromProgID(className, server, (OBJECTREF*) &refClass, bThrowOnError);
GCPROTECT_END();
HELPER_METHOD_FRAME_END();
return OBJECTREFToObject(refClass);
}
FCIMPLEND
static void TryGetClassFromCLSID(GUID clsid, STRINGREF server, OBJECTREF* pRefClass, DWORD bThrowOnError) {
CONTRACTL {
THROWS;
GC_TRIGGERS;
MODE_COOPERATIVE;
}
CONTRACTL_END;
EX_TRY
{
// NOTE: this call enables GC
GetComClassFromCLSID(clsid, server, pRefClass);
}
EX_CATCH
{
if (bThrowOnError)
{
EX_RETHROW;
}
}
EX_END_CATCH(SwallowAllExceptions)
}
// GetClassFromCLSID
// This method will return a Class object for a COM Classic object based
// upon its ProgID. The COM Classic object is found and a wrapper object created
FCIMPL3(Object*, ReflectionInvocation::GetClassFromCLSID, GUID clsid, StringObject* serverUNSAFE, CLR_BOOL bThrowOnError) {
FCALL_CONTRACT;
struct _gc {
REFLECTCLASSBASEREF refClass;
STRINGREF server;
} gc;
gc.refClass = NULL;
gc.server = (STRINGREF) serverUNSAFE;
HELPER_METHOD_FRAME_BEGIN_RET_PROTECT(gc.server);
// Since we will be returning a type that represents a COM component, we need
// to make sure COM is started before we return it.
EnsureComStarted();
TryGetClassFromCLSID(clsid, gc.server, (OBJECTREF*) &gc.refClass, bThrowOnError);
HELPER_METHOD_FRAME_END();
return OBJECTREFToObject(gc.refClass);
}
FCIMPLEND
FCIMPL8(Object*, ReflectionInvocation::InvokeDispMethod, ReflectClassBaseObject* refThisUNSAFE,
StringObject* nameUNSAFE,
INT32 invokeAttr,
Object* targetUNSAFE,
PTRArray* argsUNSAFE,
PTRArray* byrefModifiersUNSAFE,
LCID lcid,
PTRArray* namedParametersUNSAFE) {
FCALL_CONTRACT;
struct _gc
{
REFLECTCLASSBASEREF refThis;
STRINGREF name;
OBJECTREF target;
PTRARRAYREF args;
PTRARRAYREF byrefModifiers;
PTRARRAYREF namedParameters;
OBJECTREF RetObj;
} gc;
gc.refThis = (REFLECTCLASSBASEREF) refThisUNSAFE;
gc.name = (STRINGREF) nameUNSAFE;
gc.target = (OBJECTREF) targetUNSAFE;
gc.args = (PTRARRAYREF) argsUNSAFE;
gc.byrefModifiers = (PTRARRAYREF) byrefModifiersUNSAFE;
gc.namedParameters = (PTRARRAYREF) namedParametersUNSAFE;
gc.RetObj = NULL;
HELPER_METHOD_FRAME_BEGIN_RET_PROTECT(gc);
_ASSERTE(gc.target != NULL);
_ASSERTE(gc.target->GetMethodTable()->IsComObjectType());
WORD flags = 0;
if (invokeAttr & BINDER_InvokeMethod)
flags |= DISPATCH_METHOD;
if (invokeAttr & BINDER_GetProperty)
flags |= DISPATCH_PROPERTYGET;
if (invokeAttr & BINDER_SetProperty)
flags = DISPATCH_PROPERTYPUT | DISPATCH_PROPERTYPUTREF;
if (invokeAttr & BINDER_PutDispProperty)
flags = DISPATCH_PROPERTYPUT;
if (invokeAttr & BINDER_PutRefDispProperty)
flags = DISPATCH_PROPERTYPUTREF;
if (invokeAttr & BINDER_CreateInstance)
flags = DISPATCH_CONSTRUCT;
IUInvokeDispMethod(&gc.refThis,
&gc.target,
(OBJECTREF*)&gc.name,
NULL,
(OBJECTREF*)&gc.args,
(OBJECTREF*)&gc.byrefModifiers,
(OBJECTREF*)&gc.namedParameters,
&gc.RetObj,
lcid,
flags,
invokeAttr & BINDER_IgnoreReturn,
invokeAttr & BINDER_IgnoreCase);
HELPER_METHOD_FRAME_END();
return OBJECTREFToObject(gc.RetObj);
}
FCIMPLEND
#endif // FEATURE_COMINTEROP
FCIMPL2(void, ReflectionInvocation::GetGUID, ReflectClassBaseObject* refThisUNSAFE, GUID * result) {
FCALL_CONTRACT;
REFLECTCLASSBASEREF refThis = (REFLECTCLASSBASEREF) refThisUNSAFE;
HELPER_METHOD_FRAME_BEGIN_1(refThis);
GCPROTECT_BEGININTERIOR (result);
if (result == NULL || refThis == NULL)
COMPlusThrow(kNullReferenceException);
TypeHandle type = refThis->GetType();
if (type.IsTypeDesc()) {
memset(result,0,sizeof(GUID));
goto lExit;
}
#ifdef FEATURE_COMINTEROP
if (IsComObjectClass(type))
{
SyncBlock* pSyncBlock = refThis->GetSyncBlock();
#ifdef FEATURE_COMINTEROP_UNMANAGED_ACTIVATION
ComClassFactory* pComClsFac = pSyncBlock->GetInteropInfo()->GetComClassFactory();
if (pComClsFac)
{
memcpyNoGCRefs(result, &pComClsFac->m_rclsid, sizeof(GUID));
}
else
#endif // FEATURE_COMINTEROP_UNMANAGED_ACTIVATION
{
memset(result, 0, sizeof(GUID));
}
goto lExit;
}
#endif // FEATURE_COMINTEROP
GUID guid;
type.AsMethodTable()->GetGuid(&guid, TRUE);
memcpyNoGCRefs(result, &guid, sizeof(GUID));
lExit: ;
GCPROTECT_END();
HELPER_METHOD_FRAME_END();
}
FCIMPLEND
//*************************************************************************************************
//*************************************************************************************************
//*************************************************************************************************
// ReflectionSerialization
//*************************************************************************************************
//*************************************************************************************************
//*************************************************************************************************
FCIMPL1(Object*, ReflectionSerialization::GetUninitializedObject, ReflectClassBaseObject* objTypeUNSAFE) {
FCALL_CONTRACT;
OBJECTREF retVal = NULL;
REFLECTCLASSBASEREF objType = (REFLECTCLASSBASEREF) objTypeUNSAFE;
HELPER_METHOD_FRAME_BEGIN_RET_NOPOLL();
if (objType == NULL) {
COMPlusThrowArgumentNull(W("type"), W("ArgumentNull_Type"));
}
TypeHandle type = objType->GetType();
// Don't allow arrays, pointers, byrefs or function pointers.
if (type.IsTypeDesc())
COMPlusThrow(kArgumentException, W("Argument_InvalidValue"));
MethodTable *pMT = type.GetMethodTable();
PREFIX_ASSUME(pMT != NULL);
//We don't allow unitialized strings.
if (pMT == g_pStringClass) {
COMPlusThrow(kArgumentException, W("Argument_NoUninitializedStrings"));
}
// if this is an abstract class or an interface type then we will
// fail this
if (pMT->IsAbstract()) {
COMPlusThrow(kMemberAccessException,W("Acc_CreateAbst"));
}
if (pMT->ContainsGenericVariables()) {
COMPlusThrow(kMemberAccessException,W("Acc_CreateGeneric"));
}
if (pMT->IsByRefLike()) {
COMPlusThrow(kNotSupportedException, W("NotSupported_ByRefLike"));
}
// Never allow allocation of generics actually instantiated over __Canon
if (pMT->IsSharedByGenericInstantiations()) {
COMPlusThrow(kNotSupportedException, W("NotSupported_Type"));
}
// Never allow the allocation of an unitialized ContextBoundObject derived type, these must always be created with a paired
// transparent proxy or the jit will get confused.
#ifdef FEATURE_COMINTEROP
// Also do not allow allocation of uninitialized RCWs (COM objects).
if (pMT->IsComObjectType())
COMPlusThrow(kNotSupportedException, W("NotSupported_ManagedActivation"));
#endif // FEATURE_COMINTEROP
// If it is a nullable, return the underlying type instead.
if (Nullable::IsNullableType(pMT))
pMT = pMT->GetInstantiation()[0].GetMethodTable();
retVal = pMT->Allocate();
HELPER_METHOD_FRAME_END();
return OBJECTREFToObject(retVal);
}
FCIMPLEND
//*************************************************************************************************
//*************************************************************************************************
//*************************************************************************************************
// ReflectionEnum
//*************************************************************************************************
//*************************************************************************************************
//*************************************************************************************************
FCIMPL1(Object *, ReflectionEnum::InternalGetEnumUnderlyingType, ReflectClassBaseObject *target) {
FCALL_CONTRACT;
VALIDATEOBJECT(target);
TypeHandle th = target->GetType();
if (!th.IsEnum())
FCThrowArgument(NULL, NULL);
OBJECTREF result = NULL;
HELPER_METHOD_FRAME_BEGIN_RET_0();
MethodTable *pMT = MscorlibBinder::GetElementType(th.AsMethodTable()->GetInternalCorElementType());
result = pMT->GetManagedClassObject();
HELPER_METHOD_FRAME_END();
return OBJECTREFToObject(result);
}
FCIMPLEND
FCIMPL1(INT32, ReflectionEnum::InternalGetCorElementType, Object *pRefThis) {
FCALL_CONTRACT;
VALIDATEOBJECT(pRefThis);
if (pRefThis == NULL)
FCThrowArgumentNull(NULL);
return pRefThis->GetMethodTable()->GetInternalCorElementType();
}
FCIMPLEND
//*******************************************************************************
struct TempEnumValue
{
LPCUTF8 name;
UINT64 value;
};
//*******************************************************************************
class TempEnumValueSorter : public CQuickSort<TempEnumValue>
{
public:
TempEnumValueSorter(TempEnumValue *pArray, SSIZE_T iCount)
: CQuickSort<TempEnumValue>(pArray, iCount) { LIMITED_METHOD_CONTRACT; }
int Compare(TempEnumValue *pFirst, TempEnumValue *pSecond)
{
LIMITED_METHOD_CONTRACT;
if (pFirst->value == pSecond->value)
return 0;
if (pFirst->value > pSecond->value)
return 1;
else
return -1;
}
};
void QCALLTYPE ReflectionEnum::GetEnumValuesAndNames(EnregisteredTypeHandle pEnumType, QCall::ObjectHandleOnStack pReturnValues, QCall::ObjectHandleOnStack pReturnNames, BOOL fGetNames)
{
QCALL_CONTRACT;
BEGIN_QCALL;
TypeHandle th = TypeHandle::FromPtr(pEnumType);
if (!th.IsEnum())
COMPlusThrow(kArgumentException, W("Arg_MustBeEnum"));
MethodTable *pMT = th.AsMethodTable();
IMDInternalImport *pImport = pMT->GetMDImport();
StackSArray<TempEnumValue> temps;
UINT64 previousValue = 0;
HENUMInternalHolder fieldEnum(pImport);
fieldEnum.EnumInit(mdtFieldDef, pMT->GetCl());
//
// Note that we're fine treating signed types as unsigned, because all we really
// want to do is sort them based on a convenient strong ordering.
//
BOOL sorted = TRUE;
CorElementType type = pMT->GetInternalCorElementType();
mdFieldDef field;
while (pImport->EnumNext(&fieldEnum, &field))
{
DWORD dwFlags;
IfFailThrow(pImport->GetFieldDefProps(field, &dwFlags));
if (IsFdStatic(dwFlags))
{
TempEnumValue temp;
if (fGetNames)
IfFailThrow(pImport->GetNameOfFieldDef(field, &temp.name));
UINT64 value = 0;
MDDefaultValue defaultValue;
IfFailThrow(pImport->GetDefaultValue(field, &defaultValue));
// The following code assumes that the address of all union members is the same.
static_assert_no_msg(offsetof(MDDefaultValue, m_byteValue) == offsetof(MDDefaultValue, m_usValue));
static_assert_no_msg(offsetof(MDDefaultValue, m_ulValue) == offsetof(MDDefaultValue, m_ullValue));
PVOID pValue = &defaultValue.m_byteValue;
switch (type) {
case ELEMENT_TYPE_I1:
value = *((INT8 *)pValue);
break;
case ELEMENT_TYPE_U1:
case ELEMENT_TYPE_BOOLEAN:
value = *((UINT8 *)pValue);
break;
case ELEMENT_TYPE_I2:
value = *((INT16 *)pValue);
break;
case ELEMENT_TYPE_U2:
case ELEMENT_TYPE_CHAR:
value = *((UINT16 *)pValue);
break;
case ELEMENT_TYPE_I4:
IN_WIN32(case ELEMENT_TYPE_I:)
value = *((INT32 *)pValue);
break;
case ELEMENT_TYPE_U4:
IN_WIN32(case ELEMENT_TYPE_U:)
value = *((UINT32 *)pValue);
break;
case ELEMENT_TYPE_I8:
case ELEMENT_TYPE_U8:
IN_WIN64(case ELEMENT_TYPE_I:)
IN_WIN64(case ELEMENT_TYPE_U:)
value = *((INT64 *)pValue);
break;
default:
break;
}
temp.value = value;
//
// Check to see if we are already sorted. This may seem extraneous, but is
// actually probably the normal case.
//
if (previousValue > value)
sorted = FALSE;
previousValue = value;
temps.Append(temp);
}
}
TempEnumValue * pTemps = &(temps[0]);
DWORD cFields = temps.GetCount();
if (!sorted)
{
TempEnumValueSorter sorter(pTemps, cFields);
sorter.Sort();
}
{
GCX_COOP();
struct gc {
I8ARRAYREF values;
PTRARRAYREF names;
} gc;
gc.values = NULL;
gc.names = NULL;
GCPROTECT_BEGIN(gc);
{
gc.values = (I8ARRAYREF) AllocatePrimitiveArray(ELEMENT_TYPE_U8, cFields);
INT64 *pToValues = gc.values->GetDirectPointerToNonObjectElements();
for (DWORD i = 0; i < cFields; i++) {
pToValues[i] = pTemps[i].value;
}
pReturnValues.Set(gc.values);
}
if (fGetNames)
{
gc.names = (PTRARRAYREF) AllocateObjectArray(cFields, g_pStringClass);
for (DWORD i = 0; i < cFields; i++) {
STRINGREF str = StringObject::NewString(pTemps[i].name);
gc.names->SetAt(i, str);
}
pReturnNames.Set(gc.names);
}
GCPROTECT_END();
}
END_QCALL;
}
FCIMPL2_IV(Object*, ReflectionEnum::InternalBoxEnum, ReflectClassBaseObject* target, INT64 value) {
FCALL_CONTRACT;
VALIDATEOBJECT(target);
OBJECTREF ret = NULL;
MethodTable* pMT = target->GetType().AsMethodTable();
HELPER_METHOD_FRAME_BEGIN_RET_0();
ret = pMT->Box(ArgSlotEndianessFixup((ARG_SLOT*)&value, pMT->GetNumInstanceFieldBytes()));
HELPER_METHOD_FRAME_END();
return OBJECTREFToObject(ret);
}
FCIMPLEND
//*************************************************************************************************
//*************************************************************************************************
//*************************************************************************************************
// ReflectionBinder
//*************************************************************************************************
//*************************************************************************************************
//*************************************************************************************************
FCIMPL2(FC_BOOL_RET, ReflectionBinder::DBCanConvertPrimitive, ReflectClassBaseObject* source, ReflectClassBaseObject* target) {
FCALL_CONTRACT;
VALIDATEOBJECT(source);
VALIDATEOBJECT(target);
CorElementType tSRC = source->GetType().GetSignatureCorElementType();
CorElementType tTRG = target->GetType().GetSignatureCorElementType();
FC_RETURN_BOOL(InvokeUtil::IsPrimitiveType(tTRG) && InvokeUtil::CanPrimitiveWiden(tTRG, tSRC));
}
FCIMPLEND
FCIMPL2(FC_BOOL_RET, ReflectionBinder::DBCanConvertObjectPrimitive, Object* sourceObj, ReflectClassBaseObject* target) {
FCALL_CONTRACT;
VALIDATEOBJECT(sourceObj);
VALIDATEOBJECT(target);
if (sourceObj == 0)
FC_RETURN_BOOL(true);
TypeHandle th(sourceObj->GetMethodTable());
CorElementType tSRC = th.GetVerifierCorElementType();
CorElementType tTRG = target->GetType().GetSignatureCorElementType();
FC_RETURN_BOOL(InvokeUtil::IsPrimitiveType(tTRG) && InvokeUtil::CanPrimitiveWiden(tTRG, tSRC));
}
FCIMPLEND
FCIMPL2(FC_BOOL_RET, ReflectionEnum::InternalEquals, Object *pRefThis, Object* pRefTarget)
{
FCALL_CONTRACT;
VALIDATEOBJECT(pRefThis);
BOOL ret = false;
if (pRefTarget == NULL) {
FC_RETURN_BOOL(ret);
}
if( pRefThis == pRefTarget)
FC_RETURN_BOOL(true);
//Make sure we are comparing same type.
MethodTable* pMTThis = pRefThis->GetMethodTable();
_ASSERTE(!pMTThis->IsArray()); // bunch of assumptions about arrays wrong.
if ( pMTThis != pRefTarget->GetMethodTable()) {
FC_RETURN_BOOL(ret);
}
void * pThis = pRefThis->UnBox();
void * pTarget = pRefTarget->UnBox();
switch (pMTThis->GetNumInstanceFieldBytes()) {
case 1:
ret = (*(UINT8*)pThis == *(UINT8*)pTarget);
break;
case 2:
ret = (*(UINT16*)pThis == *(UINT16*)pTarget);
break;
case 4:
ret = (*(UINT32*)pThis == *(UINT32*)pTarget);
break;
case 8:
ret = (*(UINT64*)pThis == *(UINT64*)pTarget);
break;
default:
// should not reach here.
UNREACHABLE_MSG("Incorrect Enum Type size!");
break;
}
FC_RETURN_BOOL(ret);
}
FCIMPLEND
// preform (this & flags) != flags
FCIMPL2(FC_BOOL_RET, ReflectionEnum::InternalHasFlag, Object *pRefThis, Object* pRefFlags)
{
FCALL_CONTRACT;
VALIDATEOBJECT(pRefThis);
BOOL cmp = false;
_ASSERTE(pRefFlags != NULL); // Enum.cs would have thrown ArgumentNullException before calling into InternalHasFlag
VALIDATEOBJECT(pRefFlags);
void * pThis = pRefThis->UnBox();
void * pFlags = pRefFlags->UnBox();
MethodTable* pMTThis = pRefThis->GetMethodTable();
_ASSERTE(!pMTThis->IsArray()); // bunch of assumptions about arrays wrong.
_ASSERTE(pMTThis->GetNumInstanceFieldBytes() == pRefFlags->GetMethodTable()->GetNumInstanceFieldBytes()); // Enum.cs verifies that the types are Equivalent
switch (pMTThis->GetNumInstanceFieldBytes()) {
case 1:
cmp = ((*(UINT8*)pThis & *(UINT8*)pFlags) == *(UINT8*)pFlags);
break;
case 2:
cmp = ((*(UINT16*)pThis & *(UINT16*)pFlags) == *(UINT16*)pFlags);
break;
case 4:
cmp = ((*(UINT32*)pThis & *(UINT32*)pFlags) == *(UINT32*)pFlags);
break;
case 8:
cmp = ((*(UINT64*)pThis & *(UINT64*)pFlags) == *(UINT64*)pFlags);
break;
default:
// should not reach here.
UNREACHABLE_MSG("Incorrect Enum Type size!");
break;
}
FC_RETURN_BOOL(cmp);
}
FCIMPLEND
// compare two boxed enums using their underlying enum type
FCIMPL2(int, ReflectionEnum::InternalCompareTo, Object *pRefThis, Object* pRefTarget)
{
FCALL_CONTRACT;
const int retIncompatibleMethodTables = 2; // indicates that the method tables did not match
const int retInvalidEnumType = 3; // indicates that the enum was of an unknown/unsupported unerlying type
VALIDATEOBJECT(pRefThis);
if (pRefTarget == NULL) {
return 1; // all values are greater than null
}
if( pRefThis == pRefTarget)
return 0;
VALIDATEOBJECT(pRefTarget);
//Make sure we are comparing same type.
MethodTable* pMTThis = pRefThis->GetMethodTable();
_ASSERTE(pMTThis->IsEnum());
if ( pMTThis != pRefTarget->GetMethodTable()) {
return retIncompatibleMethodTables; // error case, types incompatible
}
void * pThis = pRefThis->UnBox();
void * pTarget = pRefTarget->UnBox();
#define CMPEXPR(x1,x2) ((x1) == (x2)) ? 0 : ((x1) < (x2)) ? -1 : 1
switch (pMTThis->GetInternalCorElementType()) {
case ELEMENT_TYPE_I1:
{
INT8 i1 = *(INT8*)pThis;
INT8 i2 = *(INT8*)pTarget;
return CMPEXPR(i1,i2);
}
break;
case ELEMENT_TYPE_I2:
{
INT16 i1 = *(INT16*)pThis;
INT16 i2 = *(INT16*)pTarget;
return CMPEXPR(i1,i2);
}
break;
case ELEMENT_TYPE_I4:
IN_WIN32(case ELEMENT_TYPE_I:)
{
INT32 i1 = *(INT32*)pThis;
INT32 i2 = *(INT32*)pTarget;
return CMPEXPR(i1,i2);
}
break;
case ELEMENT_TYPE_I8:
IN_WIN64(case ELEMENT_TYPE_I:)
{
INT64 i1 = *(INT64*)pThis;
INT64 i2 = *(INT64*)pTarget;
return CMPEXPR(i1,i2);
}
break;
case ELEMENT_TYPE_BOOLEAN:
{
bool b1 = !!*(UINT8 *)pThis;
bool b2 = !!*(UINT8 *)pTarget;
return CMPEXPR(b1,b2);
}
break;
case ELEMENT_TYPE_U1:
{
UINT8 u1 = *(UINT8 *)pThis;
UINT8 u2 = *(UINT8 *)pTarget;
return CMPEXPR(u1,u2);
}
break;
case ELEMENT_TYPE_U2:
case ELEMENT_TYPE_CHAR:
{
UINT16 u1 = *(UINT16 *)pThis;
UINT16 u2 = *(UINT16 *)pTarget;
return CMPEXPR(u1,u2);
}
break;
case ELEMENT_TYPE_U4:
IN_WIN32(case ELEMENT_TYPE_U:)
{
UINT32 u1 = *(UINT32 *)pThis;
UINT32 u2 = *(UINT32 *)pTarget;
return CMPEXPR(u1,u2);
}
break;
case ELEMENT_TYPE_U8:
IN_WIN64(case ELEMENT_TYPE_U:)
{
UINT64 u1 = *(UINT64*)pThis;
UINT64 u2 = *(UINT64*)pTarget;
return CMPEXPR(u1,u2);
}
break;
case ELEMENT_TYPE_R4:
{
static_assert_no_msg(sizeof(float) == 4);
float f1 = *(float*)pThis;
float f2 = *(float*)pTarget;
return CMPEXPR(f1,f2);
}
break;
case ELEMENT_TYPE_R8:
{
static_assert_no_msg(sizeof(double) == 8);
double d1 = *(double*)pThis;
double d2 = *(double*)pTarget;
return CMPEXPR(d1,d2);
}
break;
default:
break;
}
return retInvalidEnumType; // second error case -- unsupported enum type
}
FCIMPLEND