// 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; 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, ¶m) { 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 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(); } 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 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) CopyValueClass(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(); } FC_GC_POLL_NOT_NEEDED(); FieldDesc* pFieldDesc = gc.refField->GetField(); HELPER_METHOD_FRAME_BEGIN_PROTECT(gc); // Verify we're not trying to set the value of a static initonly field // once the class has been initialized. if (pFieldDesc->IsStatic()) { MethodTable* pEnclosingMT = pFieldDesc->GetEnclosingMethodTable(); if (pEnclosingMT->IsClassInited() && IsFdInitOnly(pFieldDesc->GetAttributes())) { DefineFullyQualifiedNameForClassW(); SString ssFieldName(SString::Utf8, pFieldDesc->GetName()); COMPlusThrow(kFieldAccessException, IDS_EE_CANNOT_SET_INITONLY_STATIC_FIELD, ssFieldName.GetUnicode(), GetFullyQualifiedNameForClassW(pEnclosingMT)); } } //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 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 FCIMPL6(Object*, RuntimeTypeHandle::CreateInstance, ReflectClassBaseObject* refThisUNSAFE, CLR_BOOL publicOnly, CLR_BOOL wrapExceptions, CLR_BOOL* pbCanBeCached, MethodDesc** pConstructor, CLR_BOOL* pbHasNoDefaultCtor) { CONTRACTL { FCALL_CHECK; PRECONDITION(CheckPointer(refThisUNSAFE)); PRECONDITION(CheckPointer(pbCanBeCached)); PRECONDITION(CheckPointer(pConstructor)); PRECONDITION(CheckPointer(pbHasNoDefaultCtor)); PRECONDITION(*pbCanBeCached == false); PRECONDITION(*pConstructor == NULL); PRECONDITION(*pbHasNoDefaultCtor == false); } 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(); HELPER_METHOD_FRAME_BEGIN_RET_2(rv, refThis); MethodTable* pVMT; // Get the type information associated with refThis if (thisTH.IsNull() || thisTH.IsTypeDesc()) { *pbHasNoDefaultCtor = true; goto DoneCreateInstance; } 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()) { *pbHasNoDefaultCtor = true; goto DoneCreateInstance; } // Handle the nullable special case if (Nullable::IsNullableType(thisTH)) { rv = Nullable::BoxedNullableNull(thisTH); } else rv = pVMT->Allocate(); *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) { *pbHasNoDefaultCtor = true; goto DoneCreateInstance; } // We've got the class, lets allocate it and call the constructor OBJECTREF o; 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 (!pVMT->IsValueType()) { *pbCanBeCached = true; *pConstructor = pMeth; } } } DoneCreateInstance: ; 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(¶meterHandle, 1)); // Get the type information associated with refThis MethodTable* pVMT = instantiatedType.GetMethodTable(); _ASSERTE (pVMT != 0 && !instantiatedType.IsTypeDesc()); _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 are represented as a boxed T, so there is no unboxed Nullable inside to point at by reference. Because of this a byref parameters of type Nullable 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, ¶m) { 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::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 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; } FORCEINLINE BOOL IsRegPassedStruct(MethodTable* pMT) { return pMT->IsRegPassedStruct(); } 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 { 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 (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; BOOL fCtorOfVariableSizedObject = 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(); { fCtorOfVariableSizedObject = pMT->HasComponentSize(); if (!fCtorOfVariableSizedObject) 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(); 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_RETBUFFARGREG callDescrData.pRetBuffArg = (UINT64*)(pTransitionBlock + TransitionBlock::GetOffsetOfRetBuffArgReg()); #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::MaybeTrigger(); FrameWithCookie *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 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 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 is represented as boxed T. So to pass a Nullable by reference, // we have to create a Nullable 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 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))) FrameWithCookie(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 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) #ifdef FEATURE_UTF8STRING || ownerType == TypeHandle(g_pUtf8StringClass) #endif // FEATURE_UTF8STRING ) { PVOID pReturnValue = &callDescrData.returnValue; gc.retVal = *(OBJECTREF *)pReturnValue; } // If it is a Nullable, box it using Nullable 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()); } // 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()); } else if (pRetBufStackCopy) { CopyValueClass(gc.retVal->GetData(), pRetBufStackCopy, gc.retVal->GetMethodTable()); } // 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 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); byRefToNullables = byRefToNullables->next; } if (pProtectValueClassFrame != NULL) pProtectValueClassFrame->Pop(pThread); } 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.m was called, the stack walker returns C.m. We cannot // get know that the instantiation used Foo or Bar at that point. So the next best thing // is to return C.m

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(); 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(); 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 we're not trying to set the value of a static initonly field // once the class has been initialized. if (pField->IsStatic() && pEnclosingMT->IsClassInited() && IsFdInitOnly(pField->GetAttributes())) { DefineFullyQualifiedNameForClassW(); SString ssFieldName(SString::Utf8, pField->GetName()); COMPlusThrow(kFieldAccessException, IDS_EE_CANNOT_SET_INITONLY_STATIC_FIELD, ssFieldName.GetUnicode(), GetFullyQualifiedNameForClassW(pEnclosingMT)); } // 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: SetObjectReference((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 (!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(); DomainFile *pDomainFile = pModule->GetDomainFile(); 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(); pMD->EnsureActive(); 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(&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(&pThread); UINT_PTR limit = pThread->GetCachedStackSufficientExecutionLimit(); FC_RETURN_BOOL(current >= limit); } FCIMPLEND 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, ¶m) == 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, ¶m) { 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; //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); } // // 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: 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. // 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 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(); 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 or Utf8Strings. if (pMT == g_pStringClass #ifdef FEATURE_UTF8STRING || pMT == g_pUtf8StringClass #endif // FEATURE_UTF8STRING ) { 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 { public: TempEnumValueSorter(TempEnumValue *pArray, SSIZE_T iCount) : CQuickSort(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(QCall::TypeHandle pEnumType, QCall::ObjectHandleOnStack pReturnValues, QCall::ObjectHandleOnStack pReturnNames, BOOL fGetNames) { QCALL_CONTRACT; BEGIN_QCALL; TypeHandle th = pEnumType.AsTypeHandle(); if (!th.IsEnum()) COMPlusThrow(kArgumentException, W("Arg_MustBeEnum")); MethodTable *pMT = th.AsMethodTable(); IMDInternalImport *pImport = pMT->GetMDImport(); StackSArray 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 //************************************************************************************************* //************************************************************************************************* //************************************************************************************************* // ReflectionEnum //************************************************************************************************* //************************************************************************************************* //************************************************************************************************* 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