summaryrefslogtreecommitdiff
path: root/src/vm/reflectioninvocation.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/vm/reflectioninvocation.cpp')
-rw-r--r--src/vm/reflectioninvocation.cpp3890
1 files changed, 3890 insertions, 0 deletions
diff --git a/src/vm/reflectioninvocation.cpp b/src/vm/reflectioninvocation.cpp
new file mode 100644
index 0000000000..de50514682
--- /dev/null
+++ b/src/vm/reflectioninvocation.cpp
@@ -0,0 +1,3890 @@
+// 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 "security.h"
+#ifdef FEATURE_REMOTING
+#include "remoting.h"
+#endif
+#include "eeconfig.h"
+#include "vars.hpp"
+#include "jitinterface.h"
+#include "contractimpl.h"
+#include "virtualcallstub.h"
+#include "comdelegate.h"
+#include "constrainedexecutionregion.h"
+#include "generics.h"
+
+#ifdef FEATURE_COMINTEROP
+#include "interoputil.h"
+#include "runtimecallablewrapper.h"
+#endif
+
+#include "dbginterface.h"
+#include "argdestination.h"
+
+// these flags are defined in XXXInfo.cs and only those that are used are replicated here
+#define INVOCATION_FLAGS_UNKNOWN 0x00000000
+#define INVOCATION_FLAGS_INITIALIZED 0x00000001
+
+// it's used for both method and field to signify that no access is allowed
+#define INVOCATION_FLAGS_NO_INVOKE 0x00000002
+
+#define INVOCATION_FLAGS_NEED_SECURITY 0x00000004
+
+// because field and method are different we can reuse the same bits
+//method
+#define INVOCATION_FLAGS_IS_CTOR 0x00000010
+#define INVOCATION_FLAGS_RISKY_METHOD 0x00000020
+#define INVOCATION_FLAGS_W8P_API 0x00000040
+#define INVOCATION_FLAGS_IS_DELEGATE_CTOR 0x00000080
+#define INVOCATION_FLAGS_CONTAINS_STACK_POINTERS 0x00000100
+// field
+#define INVOCATION_FLAGS_SPECIAL_FIELD 0x00000010
+#define INVOCATION_FLAGS_FIELD_SPECIAL_CAST 0x00000020
+
+// temporary flag used for flagging invocation of method vs ctor
+#define INVOCATION_FLAGS_CONSTRUCTOR_INVOKE 0x10000000
+
+/**************************************************************************/
+/* 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 TryDemand(DWORD whatPermission, RuntimeExceptionKind reKind, LPCWSTR wszTag) {
+ CONTRACTL {
+ THROWS;
+ GC_TRIGGERS;
+ MODE_COOPERATIVE;
+ }
+ CONTRACTL_END;
+
+
+ EX_TRY {
+ Security::SpecialDemand(SSWT_LATEBOUND_LINKDEMAND, whatPermission);
+ }
+ EX_CATCH {
+ COMPlusThrow(reKind, wszTag);
+ }
+ EX_END_CATCH_UNREACHABLE
+}
+
+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) {
+ CONTRACTL {
+ THROWS;
+ GC_TRIGGERS;
+ MODE_COOPERATIVE;
+ }
+ CONTRACTL_END;
+
+ 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();
+}
+
+
+
+
+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
+
+FCIMPL5(void, ReflectionInvocation::PerformVisibilityCheckOnField, FieldDesc *pFieldDesc, Object *target, ReflectClassBaseObject *pDeclaringTypeUNSAFE, DWORD attr, DWORD invocationFlags) {
+ CONTRACTL {
+ FCALL_CHECK;
+ PRECONDITION(CheckPointer(pFieldDesc));
+ PRECONDITION(CheckPointer(pDeclaringTypeUNSAFE));
+ }
+ CONTRACTL_END;
+
+#ifndef FEATURE_CORECLR
+ // Security checks are expensive as they involve stack walking. Avoid them if we can.
+ // In immersive we don't allow private reflection to framework code. So we need to perform
+ // the access check even if all the domains on the stack are fully trusted.
+ if (Security::AllDomainsOnStackFullyTrusted() && !AppX::IsAppXProcess())
+ return;
+#endif
+
+ REFLECTCLASSBASEREF refDeclaringType = (REFLECTCLASSBASEREF)ObjectToOBJECTREF(pDeclaringTypeUNSAFE);
+
+ TypeHandle declaringType = refDeclaringType->GetType();
+ OBJECTREF targetObj = ObjectToOBJECTREF(target);
+
+ HELPER_METHOD_FRAME_BEGIN_2(targetObj, refDeclaringType);
+
+ if ((invocationFlags & INVOCATION_FLAGS_SPECIAL_FIELD) != 0) {
+ // Verify that this is not a Final Field
+ if (IsFdInitOnly(attr))
+ TryDemand(SECURITY_SERIALIZATION, kFieldAccessException, W("Acc_ReadOnly"));
+ if (IsFdHasFieldRVA(attr))
+ TryDemand(SECURITY_SKIP_VER, kFieldAccessException, W("Acc_RvaStatic"));
+ }
+
+ if ((invocationFlags & INVOCATION_FLAGS_NEED_SECURITY) != 0) {
+ // Verify the callee/caller access
+
+ bool targetRemoted = FALSE;
+
+#ifndef FEATURE_CORECLR
+ targetRemoted = targetObj != NULL && InvokeUtil::IsTargetRemoted(pFieldDesc, targetObj->GetMethodTable());
+#endif //FEATURE_CORECLR
+
+ RefSecContext sCtx(InvokeUtil::GetInvocationAccessCheckType(targetRemoted));
+
+ MethodTable* pInstanceMT = NULL;
+ if (targetObj != NULL && !pFieldDesc->IsStatic()) {
+ TypeHandle targetType = targetObj->GetTypeHandle();
+ if (!targetType.IsTypeDesc())
+ pInstanceMT = targetType.AsMethodTable();
+ }
+
+ // Perform the normal access check (caller vs field).
+ InvokeUtil::CanAccessField(&sCtx,
+ declaringType.GetMethodTable(),
+ pInstanceMT,
+ pFieldDesc);
+ }
+ HELPER_METHOD_FRAME_END();
+}
+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. Demand SkipVerification and proceed
+
+ Security::SpecialDemand(SSWT_LATEBOUND_LINKDEMAND, SECURITY_SKIP_VER);
+ }
+ //
+ // 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
+ // demand SkipVerification and proceed
+ Security::SpecialDemand(SSWT_LATEBOUND_LINKDEMAND, SECURITY_SKIP_VER);
+ }
+ else
+ // demand SkipVerification and proceed
+ Security::SpecialDemand(SSWT_LATEBOUND_LINKDEMAND, SECURITY_SKIP_VER);
+ } 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
+
+FCIMPL6(Object*, RuntimeTypeHandle::CreateInstance, ReflectClassBaseObject* refThisUNSAFE,
+ CLR_BOOL publicOnly,
+ CLR_BOOL securityOff,
+ CLR_BOOL* pbCanBeCached,
+ MethodDesc** pConstructor,
+ CLR_BOOL *pbNeedSecurityCheck) {
+ CONTRACTL {
+ FCALL_CHECK;
+ PRECONDITION(CheckPointer(refThisUNSAFE));
+ PRECONDITION(CheckPointer(pbCanBeCached));
+ PRECONDITION(CheckPointer(pbNeedSecurityCheck));
+ PRECONDITION(CheckPointer(pConstructor));
+ PRECONDITION(*pbCanBeCached == false);
+ PRECONDITION(*pConstructor == NULL);
+ PRECONDITION(*pbNeedSecurityCheck == true);
+ }
+ 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;
+ bool bNeedAccessCheck;
+
+ // Get the type information associated with refThis
+ if (thisTH.IsNull() || thisTH.IsTypeDesc())
+ COMPlusThrow(kMissingMethodException,W("Arg_NoDefCTor"));
+
+ pVMT = thisTH.AsMethodTable();
+
+ pVMT->EnsureInstanceActive();
+
+ bNeedAccessCheck = false;
+
+#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);
+
+ // Check for the required permissions (SecurityPermission.UnmanagedCode),
+ // since arbitrary unmanaged code in the class factory will execute below).
+ Security::SpecialDemand(SSWT_LATEBOUND_LINKDEMAND, SECURITY_UNMANAGED_CODE);
+
+ // 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 we are creating a COM object which has backing metadata we still
+ // need to ensure that the caller has unmanaged code access permission.
+ if (pVMT->IsComObjectType())
+ Security::SpecialDemand(SSWT_LATEBOUND_LINKDEMAND, SECURITY_UNMANAGED_CODE);
+
+ // 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"));
+ }
+
+ if (!securityOff)
+ {
+#ifndef FEATURE_CORECLR
+ // Security checks are expensive as they involve stack walking. Avoid them if we can.
+ // In immersive we don't allow private reflection to framework code. So we need to perform
+ // the access check even if all the domains on the stack are fully trusted.
+ if (Security::AllDomainsOnStackFullyTrusted() && !AppX::IsAppXProcess())
+ {
+ bNeedAccessCheck = false;
+ }
+ else
+#endif //FEATURE_CORECLR
+ {
+ // Public critical types cannot be accessed by transparent callers
+ bNeedAccessCheck = !pVMT->IsExternallyVisible() || Security::TypeRequiresTransparencyCheck(pVMT);
+ }
+
+ if (bNeedAccessCheck)
+ {
+ RefSecContext sCtx(InvokeUtil::GetInvocationAccessCheckType());
+ InvokeUtil::CanAccessClass(&sCtx, pVMT, TRUE);
+ }
+ }
+
+ // Handle the nullable<T> special case
+ if (Nullable::IsNullableType(thisTH)) {
+ rv = Nullable::BoxedNullableNull(thisTH);
+ }
+ else
+ rv = pVMT->Allocate();
+
+ // Since no security checks will be performed on cached value types without default ctors,
+ // we cannot cache those types that require access checks.
+ // In fact, we don't even need to set pbNeedSecurityCheck to false here.
+ if (!pVMT->Collectible() && !bNeedAccessCheck)
+ {
+ *pbCanBeCached = true;
+ *pbNeedSecurityCheck = false;
+ }
+ }
+ 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"));
+
+ if (!securityOff)
+ {
+ // If the type is critical or the constructor we're using is critical, we need to ensure that
+ // the caller is allowed to invoke it.
+ bool needsTransparencyCheck = Security::TypeRequiresTransparencyCheck(pVMT) ||
+ (Security::IsMethodCritical(pMeth) && !Security::IsMethodSafeCritical(pMeth));
+
+ // We also need to do a check if the method or type is not public
+ bool needsVisibilityCheck = !IsMdPublic(attr) || !pVMT->IsExternallyVisible();
+
+ // If the visiblity, transparency, or legacy LinkDemands on the type or constructor dictate that
+ // we need to check the caller, then do that now.
+ bNeedAccessCheck = needsTransparencyCheck ||
+ needsVisibilityCheck ||
+ pMeth->RequiresLinktimeCheck();
+
+ if (bNeedAccessCheck)
+ {
+ // this security context will be used in cast checking as well
+ RefSecContext sCtx(InvokeUtil::GetInvocationAccessCheckType());
+ InvokeUtil::CanAccessMethod(pMeth, pVMT, NULL, &sCtx);
+ }
+ }
+
+ // We've got the class, lets allocate it and call the constructor
+ OBJECTREF o;
+ bool remoting = false;
+
+#ifdef FEATURE_REMOTING
+ if (pVMT->IsTransparentProxy())
+ COMPlusThrow(kMissingMethodException,W("NotSupported_Constructor"));
+
+ if (pVMT->MayRequireManagedActivation())
+ {
+ o = CRemotingServices::CreateProxyOrObject(pVMT);
+ remoting = true;
+ }
+ else
+ o = AllocateObject(pVMT);
+
+#else
+ o = AllocateObject(pVMT);
+#endif
+ 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);
+
+ rv = o;
+ GCPROTECT_END();
+
+ // No need to set these if they cannot be cached
+ if (!remoting && !pVMT->Collectible())
+ {
+ *pbCanBeCached = true;
+ *pConstructor = pMeth;
+ *pbNeedSecurityCheck = bNeedAccessCheck;
+ }
+ }
+ }
+
+ 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
+#ifdef FEATURE_REMOTING
+ _ASSERTE(!pVMT->IsTransparentProxy());
+ _ASSERTE(!pVMT->MayRequireManagedActivation());
+#endif
+
+ // 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);
+
+ 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
+
+FCIMPL1(DWORD, ReflectionInvocation::GetSpecialSecurityFlags, ReflectMethodObject *pMethodUNSAFE) {
+ CONTRACTL {
+ FCALL_CHECK;
+ }
+ CONTRACTL_END;
+
+ DWORD dwFlags = 0;
+
+ struct
+ {
+ REFLECTMETHODREF refMethod;
+ }
+ gc;
+
+ gc.refMethod = (REFLECTMETHODREF)ObjectToOBJECTREF(pMethodUNSAFE);
+
+ if (!gc.refMethod)
+ FCThrowRes(kArgumentNullException, W("Arg_InvalidHandle"));
+
+ MethodDesc* pMethod = gc.refMethod->GetMethod();
+
+ HELPER_METHOD_FRAME_BEGIN_RET_PROTECT(gc);
+
+ // this is an information that is critical for ctors, otherwise is not important
+ // we get it here anyway to simplify code
+ MethodTable *pMT = pMethod->GetMethodTable();
+ _ASSERTE(pMT);
+
+ // We should also check the return type here.
+ // Is there an easier way to get the return type of a method?
+ MetaSig metaSig(pMethod);
+ TypeHandle retTH = metaSig.GetRetTypeHandleThrowing();
+ MethodTable *pRetMT = retTH.GetMethodTable();
+
+ // If either the declaring type or the return type contains stack pointers (ByRef or typedbyref),
+ // the type cannot be boxed and thus cannot be invoked through reflection invocation.
+ if ( pMT->IsByRefLike() || (pRetMT != NULL && pRetMT->IsByRefLike()) )
+ dwFlags |= INVOCATION_FLAGS_CONTAINS_STACK_POINTERS;
+
+ // Is this a call to a potentially dangerous method? (If so, we're going
+ // to demand additional permission).
+ if (InvokeUtil::IsDangerousMethod(pMethod))
+ dwFlags |= INVOCATION_FLAGS_RISKY_METHOD;
+
+ // Is there a link demand?
+ if (pMethod->RequiresLinktimeCheck()) {
+ dwFlags |= INVOCATION_FLAGS_NEED_SECURITY;
+ }
+ else
+ if (Security::IsMethodCritical(pMethod) && !Security::IsMethodSafeCritical(pMethod)) {
+ dwFlags |= INVOCATION_FLAGS_NEED_SECURITY;
+ }
+
+ HELPER_METHOD_FRAME_END();
+ return dwFlags;
+}
+FCIMPLEND
+
+#ifndef FEATURE_CORECLR
+
+// Can not inline this function.
+#ifdef _MSC_VER
+__declspec(noinline)
+#endif
+void PerformSecurityCheckHelper(Object *targetUnsafe, MethodDesc *pMeth, MethodTable* pParentMT, DWORD dwFlags)
+{
+ CONTRACTL {
+ THROWS;
+ GC_TRIGGERS;
+ MODE_COOPERATIVE;
+
+ PRECONDITION(CheckPointer(pMeth));
+ }
+ CONTRACTL_END;
+
+ OBJECTREF target (targetUnsafe);
+ GCPROTECT_BEGIN (target);
+ FrameWithCookie<DebuggerSecurityCodeMarkFrame> __dbgSecFrame;
+
+ bool targetRemoted = false;
+
+#ifndef FEATURE_CORECLR
+ targetRemoted = target != NULL && InvokeUtil::IsTargetRemoted(pMeth, target->GetMethodTable());
+#endif //FEATURE_CORECLR
+
+ RefSecContext sCtx(InvokeUtil::GetInvocationAccessCheckType(targetRemoted));
+
+ MethodTable* pInstanceMT = NULL;
+ if (target != NULL) {
+ if (!target->GetTypeHandle().IsTypeDesc())
+ pInstanceMT = target->GetTypeHandle().AsMethodTable();
+ }
+
+#ifdef FEATURE_CORECLR
+ if (dwFlags & (INVOCATION_FLAGS_RISKY_METHOD|INVOCATION_FLAGS_IS_DELEGATE_CTOR))
+ {
+ // On CoreCLR we assert that "dangerous" methods (see IsDangerousMethods) can only
+ // be reflection-invoked by platform code (C or SC).
+
+ // Also, for delegates, in desktop we used to demand unmanaged
+ // code permission for this since it's hard to validate the target address.
+ // Here we just restrict access to Critical code.
+ MethodDesc *pCallerMD = sCtx.GetCallerMethod();
+
+ if (pCallerMD && Security::IsMethodTransparent(pCallerMD))
+ {
+ ThrowMethodAccessException(pCallerMD, pMeth, FALSE, IDS_E_TRANSPARENT_REFLECTION);
+ }
+ }
+
+ if (dwFlags & (INVOCATION_FLAGS_NEED_SECURITY|INVOCATION_FLAGS_CONSTRUCTOR_INVOKE))
+#endif
+ {
+
+ if (dwFlags & INVOCATION_FLAGS_CONSTRUCTOR_INVOKE)
+ InvokeUtil::CanAccessMethod(pMeth,
+ pParentMT,
+ pInstanceMT,
+ &sCtx,
+ TRUE /*fCriticalToFullDemand*/);
+ else
+ InvokeUtil::CanAccessMethod(pMeth,
+ pParentMT,
+ pInstanceMT,
+ &sCtx,
+ TRUE /*fCriticalToFullDemand*/,
+ (dwFlags & INVOCATION_FLAGS_IS_CTOR) != 0 /*checkSkipVer*/);
+ }
+
+ __dbgSecFrame.Pop();
+ GCPROTECT_END();
+}
+
+FCIMPL4(void, ReflectionInvocation::PerformSecurityCheck, Object *target, MethodDesc *pMeth, ReflectClassBaseObject *pParentUNSAFE, DWORD dwFlags) {
+ CONTRACTL {
+ FCALL_CHECK;
+ PRECONDITION(CheckPointer(pMeth));
+ }
+ CONTRACTL_END;
+
+#ifndef FEATURE_CORECLR
+ // Security checks are expensive as they involve stack walking. Avoid them if we can.
+ // In immersive we don't allow private reflection to framework code. So we need to perform
+ // the access check even if all the domains on the stack are fully trusted.
+ if (Security::AllDomainsOnStackFullyTrusted() && !AppX::IsAppXProcess())
+ return;
+#endif
+
+ REFLECTCLASSBASEREF refParent = (REFLECTCLASSBASEREF)ObjectToOBJECTREF(pParentUNSAFE);
+
+ HELPER_METHOD_FRAME_BEGIN_1(refParent);
+ //CAUTION: PerformSecurityCheckHelper could trigger GC!
+
+ TypeHandle parent = refParent != NULL ? refParent->GetType() : TypeHandle();
+ PerformSecurityCheckHelper(target,pMeth,parent.GetMethodTable(),dwFlags);
+
+ HELPER_METHOD_FRAME_END();
+}
+FCIMPLEND
+
+#endif // FEATURE_CORECLR
+
+/****************************************************************************/
+/* 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();
+ if (et == ELEMENT_TYPE_PTR || et == ELEMENT_TYPE_FNPTR)
+ Security::SpecialDemand(SSWT_LATEBOUND_LINKDEMAND, SECURITY_SKIP_VER);
+
+ // 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)
+#ifdef FEATURE_CORECLR
+ if (IsWatsonEnabled())
+#endif // FEATURE_CORECLR
+ {
+ 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
+#ifdef FEATURE_CORECLR
+ if (IsWatsonEnabled())
+#endif // FEATURE_CORECLR
+ {
+ 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();
+}
+
+FCIMPL4(Object*, RuntimeMethodHandle::InvokeMethod,
+ Object *target, PTRArray *objs, SignatureNative* pSigUNSAFE, CLR_BOOL fConstructor)
+{
+ 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();
+
+#ifdef FEATURE_REMOTING
+ if (pMT->MayRequireManagedActivation())
+ {
+ gc.retVal = CRemotingServices::CreateProxyOrObject(pMT);
+ fForceActivationForRemoting = TRUE;
+ }
+ else
+#endif
+ {
+ if (pMT != g_pStringClass)
+ gc.retVal = pMT->Allocate();
+ }
+ }
+ else
+ {
+#ifdef FEATURE_REMOTING
+ if (gc.target != NULL)
+ {
+ fForceActivationForRemoting = gc.target->IsTransparentProxy();
+ }
+#endif
+ }
+
+ {
+ 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) / OS_PAGE_SIZE) + 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.
+
+ TypeHandle retTH = gc.pSig->GetReturnTypeHandle();
+ BOOL fHasRetBuffArg = argit.HasRetBuffArg();
+ CorElementType retType = retTH.GetInternalCorElementType();
+ if (retType == ELEMENT_TYPE_VALUETYPE || fHasRetBuffArg) {
+ gc.retVal = retTH.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);
+ }
+
+ // 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);
+
+ // Call the method
+ bool fExceptionThrown = false;
+ 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);
+
+ // 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 (retType == ELEMENT_TYPE_VALUETYPE)
+ {
+ _ASSERTE(gc.retVal != NULL);
+
+ // if the structure is returned by value, then we need to copy in the boxed object
+ // we have allocated for this purpose.
+ 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
+ {
+ gc.retVal = InvokeUtil::CreateObject(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
+
+#ifdef FEATURE_SERIALIZATION
+FCIMPL4(void, RuntimeMethodHandle::SerializationInvoke,
+ ReflectMethodObject *pMethodUNSAFE, Object* targetUNSAFE, Object* serializationInfoUNSAFE, struct StreamingContextData * pContext) {
+ FCALL_CONTRACT;
+
+ struct _gc
+ {
+ OBJECTREF target;
+ OBJECTREF serializationInfo;
+ REFLECTMETHODREF refMethod;
+ } gc;
+
+ gc.target = (OBJECTREF) targetUNSAFE;
+ gc.serializationInfo = (OBJECTREF) serializationInfoUNSAFE;
+ gc.refMethod = (REFLECTMETHODREF)ObjectToOBJECTREF(pMethodUNSAFE);
+
+ MethodDesc* pMethod = pMethodUNSAFE->GetMethod();
+
+ Assembly *pAssem = pMethod->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"));
+
+ HELPER_METHOD_FRAME_BEGIN_PROTECT(gc);
+
+ {
+ ARG_SLOT newArgs[3];
+
+ // Nullable<T> does not support the ISerializable constructor, so we should never get here.
+ _ASSERTE(!Nullable::IsNullableType(gc.target->GetMethodTable()));
+
+ if (pMethod == MscorlibBinder::GetMethod(METHOD__WINDOWS_IDENTITY__SERIALIZATION_CTOR))
+ {
+ // WindowsIdentity.ctor takes only one argument
+ MethodDescCallSite method(pMethod, &gsig_IM_SerInfo_RetVoid, &gc.target);
+
+ // NO GC AFTER THIS POINT
+ // Copy "this" pointer: only unbox if type is value type and method is not unboxing stub
+ if (pMethod->GetMethodTable()->IsValueType() && !pMethod->IsUnboxingStub())
+ newArgs[0] = PtrToArgSlot(gc.target->UnBox());
+ else
+ newArgs[0] = ObjToArgSlot(gc.target);
+
+ newArgs[1] = ObjToArgSlot(gc.serializationInfo);
+
+ TryCallMethod(&method, newArgs);
+ }
+ else
+ {
+ //
+ // Use hardcoded sig for performance
+ //
+ MethodDescCallSite method(pMethod, &gsig_IM_SerInfo_StrContext_RetVoid, &gc.target);
+
+ // NO GC AFTER THIS POINT
+ // Copy "this" pointer: only unbox if type is value type and method is not unboxing stub
+ if (pMethod->GetMethodTable()->IsValueType() && !pMethod->IsUnboxingStub())
+ newArgs[0] = PtrToArgSlot(gc.target->UnBox());
+ else
+ newArgs[0] = ObjToArgSlot(gc.target);
+
+ newArgs[1] = ObjToArgSlot(gc.serializationInfo);
+
+#ifdef _WIN64
+ //
+ // on win64 the struct does not fit in an ARG_SLOT, so we pass it by reference
+ //
+ static_assert_no_msg(sizeof(*pContext) > sizeof(ARG_SLOT));
+ newArgs[2] = PtrToArgSlot(pContext);
+#else // _WIN64
+ //
+ // on x86 the struct fits in an ARG_SLOT, so we pass it by value
+ //
+ static_assert_no_msg(sizeof(*pContext) == sizeof(ARG_SLOT));
+ newArgs[2] = *(ARG_SLOT*)pContext;
+#endif // _WIN64
+
+ TryCallMethod(&method, newArgs);
+ }
+ }
+
+ HELPER_METHOD_FRAME_END_POLL();
+}
+FCIMPLEND
+#endif // FEATURE_SERIALIZATION
+
+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;
+
+#ifndef FEATURE_CORECLR
+ targetRemoted = !targetType.IsNull() && InvokeUtil::IsTargetRemoted(pField, targetType.AsMethodTable());
+#endif //FEATURE_CORECLR
+
+ 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 (IsFdInitOnly(attr)) {
+ TryDemand(SECURITY_SERIALIZATION, kFieldAccessException, W("Acc_ReadOnly"));
+ }
+ if (IsFdHasFieldRVA(attr)) {
+ TryDemand(SECURITY_SKIP_VER, kFieldAccessException, W("Acc_RvaStatic"));
+ }
+ 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;
+#ifndef FEATURE_CORECLR
+ targetRemoted = targetType.IsNull() && InvokeUtil::IsTargetRemoted(pField, targetType.AsMethodTable());
+#endif //FEATURE_CORECLR
+
+ 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.
+#ifdef FEATURE_CORECLR
+ RefSecContext sCtx(InvokeUtil::GetInvocationAccessCheckType());
+ InvokeUtil::CanAccessClass(&sCtx, pMT);
+#endif //FEATURE_CORECLR
+
+ 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
+
+#ifndef FEATURE_CORECLR
+// This method triggers a given method to be jitted
+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);
+
+ if (refMethod == NULL)
+ FCThrowArgumentVoidEx(kArgumentException, NULL, W("InvalidOperation_HandleIsNotInitialized"));
+
+ MethodDesc *pMD = refMethod->GetMethod();
+
+ HELPER_METHOD_FRAME_BEGIN_1(refMethod);
+
+ if (pMD->IsAbstract())
+ COMPlusThrowArgumentNull(W("method"), W("Argument_CannotPrepareAbstract"));
+
+ pMD->CheckRestore();
+
+ 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"));
+
+ // We need to find the actual class and/or method instantiations, even though we've been passed them. This is an issue of
+ // lifetime -- the instantiation passed in will go away at some point whereas preparation of the method has the potential to
+ // persist a copy of the instantiation pointer. By finding the actual instantiation we get a stable pointer whose lifetime
+ // is at least as long as the data generated by preparation.
+
+ // 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"));
+
+ // Load the exact type of the method if it needs to be instantiated (because it's a generic type definition, e.g. C<T>, or a
+ // shared type instantiation, e.g. C<Object>).
+ if (pExactMT->IsGenericTypeDefinition() || pExactMT->IsSharedByGenericInstantiations())
+ {
+ TypeHandle thExactType = ClassLoader::LoadGenericInstantiationThrowing(pMD->GetModule(),
+ pMD->GetMethodTable()->GetCl(),
+ Instantiation(pInstantiation, pMD->GetNumGenericClassArgs()));
+ pExactMT = thExactType.AsMethodTable();
+ }
+
+ // As for the class we might need to find a method desc with an exact instantiation if the one we have is too vague.
+ // Note: IsGenericMethodDefinition implies ContainsGenericVariables so there's no need to check it separately.
+ if (pMD->IsSharedByGenericInstantiations() || pMD->ContainsGenericVariables())
+ pMD = MethodDesc::FindOrCreateAssociatedMethodDesc(pMD,
+ pExactMT,
+ FALSE,
+ Instantiation(&pInstantiation[pMD->GetNumGenericClassArgs()], pMD->GetNumGenericMethodArgs()),
+ FALSE);
+ }
+ else
+ {
+ // No instantiation provided, the method better not be expecting one.
+
+ // Methods that are generic definitions (e.g. C.Foo<U>) and those that are shared (e.g. C<Object>.Foo, C.Foo<Object>) need
+ // extra instantiation data.
+ // Note: IsGenericMethodDefinition implies ContainsGenericVariables so there's no need to check it separately.
+ if (pMD->IsSharedByGenericInstantiations() || pMD->ContainsGenericVariables())
+ COMPlusThrow(kArgumentException, W("Argument_InvalidGenericInstantiation"));
+
+ // The rest of the cases (non-generics related methods, instantiating stubs, methods instantiated over non-shared types
+ // etc.) should be able to provide their instantiation for us as necessary.
+ }
+
+ // Go prepare the method at the specified instantiation.
+ PrepareMethodDesc(pMD, pExactMT->GetInstantiation(), pMD->GetMethodInstantiation());
+
+ HELPER_METHOD_FRAME_END();
+}
+FCIMPLEND
+
+// This method triggers a given delegate to be prepared. This involves preparing the
+// delegate's Invoke method and preparing the target of that Invoke. In the case of
+// a multi-cast delegate, we rely on the fact that each individual component was prepared
+// prior to the Combine. If our event sinks perform the Combine, this is always true.
+// If the client calls Combine himself, he is responsible for his own preparation.
+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);
+
+ PrepareDelegateHelper(&delegate, FALSE);
+
+ HELPER_METHOD_FRAME_END();
+}
+FCIMPLEND
+#endif // !FEATURE_CORECLR
+
+FCIMPL1(void, ReflectionInvocation::PrepareContractedDelegate, 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);
+
+ PrepareDelegateHelper(&delegate, TRUE);
+
+ HELPER_METHOD_FRAME_END();
+}
+FCIMPLEND
+
+void ReflectionInvocation::PrepareDelegateHelper(OBJECTREF *pDelegate, BOOL onlyContractedMethod)
+{
+ CONTRACTL {
+ THROWS;
+ GC_TRIGGERS;
+ MODE_COOPERATIVE;
+ PRECONDITION(CheckPointer(pDelegate));
+ PRECONDITION(CheckPointer(OBJECTREFToObject(*pDelegate)));
+ }
+ CONTRACTL_END;
+
+ // Make sure the delegate subsystem itself is prepared.
+ // Force the immediate creation of any global stubs required. This is platform specific.
+#ifdef _TARGET_X86_
+ {
+ GCX_PREEMP();
+ COMDelegate::TheDelegateInvokeStub();
+ }
+#endif
+
+ MethodDesc *pMDTarget = COMDelegate::GetMethodDesc(*pDelegate);
+ MethodDesc *pMDInvoke = COMDelegate::FindDelegateInvokeMethod((*pDelegate)->GetMethodTable());
+
+ // If someone does give us a multicast delegate, then both MDs will be the same -- they
+ // will both be the Delegate's Invoke member. Normally, pMDTarget points at the method
+ // the delegate is wrapping, of course.
+ if (pMDTarget == pMDInvoke)
+ {
+ pMDTarget->CheckRestore();
+
+ // The invoke method itself is never generic, but the delegate class itself might be.
+ PrepareMethodDesc(pMDInvoke,
+ pMDInvoke->GetExactClassInstantiation((*pDelegate)->GetTypeHandle()),
+ Instantiation(),
+ onlyContractedMethod);
+ }
+ else
+ {
+ pMDTarget->CheckRestore();
+ pMDInvoke->CheckRestore();
+
+ // Prepare the eventual target method first.
+
+ // Load the exact type of the method if it needs to be instantiated (because it's a generic type definition, e.g. C<T>, or a
+ // shared type instantiation, e.g. C<Object>).
+ MethodTable *pExactMT = pMDTarget->GetMethodTable();
+ if (pExactMT->IsGenericTypeDefinition() || pExactMT->IsSharedByGenericInstantiations())
+ {
+ OBJECTREF targetObj = COMDelegate::GetTargetObject(*pDelegate);
+
+#ifdef FEATURE_REMOTING
+ // We prepare the delegate for the sole purpose of reliability (CER).
+ // If the target is a transparent proxy, we cannot guarantee reliability anyway.
+ if (CRemotingServices::IsTransparentProxy(OBJECTREFToObject(targetObj)))
+ return;
+#endif //FEATURE_REMOTING
+
+ pExactMT = targetObj->GetMethodTable();
+ }
+
+
+ // For delegates with generic target methods it must be the case that we are passed an instantiating stub -- there's no
+ // other way the necessary method instantiation information can be passed to us.
+ // The target MD may be shared by generic instantiations as long as it does not require extra instantiation arguments.
+ // We have the actual target object so we can extract the exact class instantiation from it.
+ _ASSERTE(!pMDTarget->RequiresInstArg() &&
+ !pMDTarget->ContainsGenericVariables());
+
+ PrepareMethodDesc(pMDTarget,
+ pMDTarget->GetExactClassInstantiation(TypeHandle(pExactMT)),
+ pMDTarget->GetMethodInstantiation(),
+ onlyContractedMethod);
+
+ // Now prepare the delegate invoke method.
+ // The invoke method itself is never generic, but the delegate class itself might be.
+ PrepareMethodDesc(pMDInvoke,
+ pMDInvoke->GetExactClassInstantiation((*pDelegate)->GetTypeHandle()),
+ Instantiation(),
+ onlyContractedMethod);
+ }
+}
+
+FCIMPL0(void, ReflectionInvocation::ProbeForSufficientStack)
+{
+ FCALL_CONTRACT;
+
+#ifdef FEATURE_STACK_PROBE
+ // probe for our entry point amount and throw if not enough stack
+ RetailStackProbe(ADJUST_PROBE(DEFAULT_ENTRY_PROBE_AMOUNT));
+#else
+ FCUnique(0x69);
+#endif
+
+}
+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
+
+#ifdef FEATURE_CORECLR
+// 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
+#endif // FEATURE_CORECLR
+
+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 arbitraty 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"));
+
+ if (!IsCompilationProcess())
+ {
+ // Delegates are prepared as part of the ngen process, so only prepare the backout
+ // delegate for non-ngen processes.
+ PrepareDelegateHelper((OBJECTREF *)&gc.backoutDelegate, FALSE);
+
+ // Make sure the managed backout code helper function has been prepared before we
+ // attempt to run the backout code.
+ PrepareMethodDesc(g_pExecuteBackoutCodeHelperMethod, Instantiation(), Instantiation(), FALSE, TRUE);
+ }
+
+ 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());
+
+ // Unless security is turned off, we need to validate that the calling code
+ // has unmanaged code access privilege.
+ Security::SpecialDemand(SSWT_LATEBOUND_LINKDEMAND, SECURITY_UNMANAGED_CODE);
+
+ 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"));
+ }
+ else if (pMT->ContainsGenericVariables()) {
+ COMPlusThrow(kMemberAccessException,W("Acc_CreateGeneric"));
+ }
+ // Never allow allocation of generics actually instantiated over __Canon
+ else 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_REMOTING
+ if (pMT->IsContextful())
+ COMPlusThrow(kNotSupportedException, W("NotSupported_ManagedActivation"));
+#endif
+
+#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
+
+FCIMPL1(Object*, ReflectionSerialization::GetSafeUninitializedObject, ReflectClassBaseObject* objTypeUNSAFE) {
+ FCALL_CONTRACT;
+
+ OBJECTREF retVal = NULL;
+ REFLECTCLASSBASEREF objType = (REFLECTCLASSBASEREF) objTypeUNSAFE;
+
+ HELPER_METHOD_FRAME_BEGIN_RET_1(objType);
+
+ 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"));
+ else if (pMT->ContainsGenericVariables()) {
+ COMPlusThrow(kMemberAccessException,W("Acc_CreateGeneric"));
+ }
+
+ // 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_REMOTING
+ if (pMT->IsContextful())
+ COMPlusThrow(kNotSupportedException, W("NotSupported_ManagedActivation"));
+#endif
+
+#ifdef FEATURE_COMINTEROP
+ // Also do not allow allocation of uninitialized RCWs (COM objects).
+ if (pMT->IsComObjectType())
+ COMPlusThrow(kNotSupportedException, W("NotSupported_ManagedActivation"));
+#endif // FEATURE_COMINTEROP
+
+#ifdef FEATURE_APTCA
+ if (!pMT->GetAssembly()->AllowUntrustedCaller()) {
+ OBJECTREF permSet = NULL;
+ Security::GetPermissionInstance(&permSet, SECURITY_FULL_TRUST);
+ Security::DemandSet(SSWT_LATEBOUND_LINKDEMAND, permSet);
+ }
+#endif // FEATURE_APTCA
+
+#ifdef FEATURE_CAS_POLICY
+ if (pMT->GetClass()->RequiresLinktimeCheck()) {
+ OBJECTREF refClassNonCasDemands = NULL;
+ OBJECTREF refClassCasDemands = NULL;
+
+ refClassCasDemands = TypeSecurityDescriptor::GetLinktimePermissions(pMT, &refClassNonCasDemands);
+
+ if (refClassCasDemands != NULL)
+ Security::DemandSet(SSWT_LATEBOUND_LINKDEMAND, refClassCasDemands);
+
+ }
+#endif // FEATURE_CAS_POLICY
+
+ // 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
+
+FCIMPL0(FC_BOOL_RET, ReflectionSerialization::GetEnableUnsafeTypeForwarders)
+{
+ FCALL_CONTRACT;
+ FC_RETURN_BOOL(CLRConfig::GetConfigValue(CLRConfig::EXTERNAL_Serialization_UnsafeTypeForwarding));
+}
+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
+