diff options
Diffstat (limited to 'src/vm/remoting.cpp')
-rw-r--r-- | src/vm/remoting.cpp | 3773 |
1 files changed, 0 insertions, 3773 deletions
diff --git a/src/vm/remoting.cpp b/src/vm/remoting.cpp deleted file mode 100644 index 1b65323bb6..0000000000 --- a/src/vm/remoting.cpp +++ /dev/null @@ -1,3773 +0,0 @@ -// 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. -// -// File: remoting.cpp -// - -// -// Purpose: Defines various remoting related objects such as -// proxies -// - -// - - -#include "common.h" - -#ifdef FEATURE_REMOTING -#include "virtualcallstub.h" -#include "excep.h" -#include "comdelegate.h" -#include "remoting.h" -#include "field.h" -#include "siginfo.hpp" -#include "stackbuildersink.h" -#include "eehash.h" -#include "profilepriv.h" -#include "message.h" -#include "eeconfig.h" -#include "comcallablewrapper.h" -#include "interopconverter.h" -#include "asmconstants.h" -#include "crossdomaincalls.h" -#include "contractimpl.h" -#include "typestring.h" -#include "generics.h" -#include "appdomain.inl" -#include "dbginterface.h" - -#ifndef DACCESS_COMPILE - -// These hold label offsets into non-virtual thunks. They are used by -// CNonVirtualThunkMgr::DoTraceStub and ::TraceManager to help the -// debugger figure out where the thunk is going to go. -DWORD g_dwNonVirtualThunkRemotingLabelOffset = 0; -DWORD g_dwNonVirtualThunkReCheckLabelOffset = 0; - -// Statics - -MethodTable *CRemotingServices::s_pMarshalByRefObjectClass; -MethodTable *CRemotingServices::s_pServerIdentityClass; - -MethodDesc *CRemotingServices::s_pRPPrivateInvoke; -MethodDesc *CRemotingServices::s_pRPInvokeStatic; -MethodDesc *CRemotingServices::s_pWrapMethodDesc; -MethodDesc *CRemotingServices::s_pIsCurrentContextOK; -MethodDesc *CRemotingServices::s_pCheckCast; -MethodDesc *CRemotingServices::s_pFieldSetterDesc; -MethodDesc *CRemotingServices::s_pFieldGetterDesc; -MethodDesc *CRemotingServices::s_pObjectGetTypeDesc; -MethodDesc *CRemotingServices::s_pGetTypeDesc; -MethodDesc *CRemotingServices::s_pProxyForDomainDesc; -MethodDesc *CRemotingServices::s_pServerContextForProxyDesc; -MethodDesc *CRemotingServices::s_pServerDomainIdForProxyDesc; -DWORD CRemotingServices::s_dwServerOffsetInRealProxy; -DWORD CRemotingServices::s_dwSrvIdentityOffsetInRealProxy; -DWORD CRemotingServices::s_dwIdOffset; -DWORD CRemotingServices::s_dwTPOrObjOffsetInIdentity; -DWORD CRemotingServices::s_dwMBRIDOffset; -DWORD CRemotingServices::s_dwLeaseOffsetInIdentity; -DWORD CRemotingServices::s_dwURIOffsetInIdentity; -CrstStatic CRemotingServices::s_RemotingCrst; -BOOL CRemotingServices::s_fRemotingStarted; -MethodDesc *CRemotingServices::s_pRenewLeaseOnCallDesc; - - -#ifdef FEATURE_COMINTEROP -MethodDesc *CRemotingServices::s_pCreateObjectForCom; -#endif - -// CTPMethodTable Statics -DWORD CTPMethodTable::s_dwCommitedTPSlots; -DWORD CTPMethodTable::s_dwReservedTPSlots; -DWORD CTPMethodTable::s_dwReservedTPIndirectionSlotSize; -DWORD CTPMethodTable::s_dwGCInfoBytes; -DWORD CTPMethodTable::s_dwMTDataSlots; -MethodTable *CTPMethodTable::s_pRemotingProxyClass; -CrstStatic CTPMethodTable::s_TPMethodTableCrst; -EEThunkHashTable *CTPMethodTable::s_pThunkHashTable; -BOOL CTPMethodTable::s_fTPTableFieldsInitialized; - -#endif // !DACCESS_COMPILE - - -SPTR_IMPL(MethodTable, CTPMethodTable, s_pThunkTable); - -#ifndef DACCESS_COMPILE - -// CVirtualThunks statics -CVirtualThunks *CVirtualThunks::s_pVirtualThunks; - -// CVirtualThunkMgr statics -CVirtualThunkMgr *CVirtualThunkMgr::s_pVirtualThunkMgr; - -#ifndef HAS_REMOTING_PRECODE -// CNonVirtualThunk statics -CNonVirtualThunk *CNonVirtualThunk::s_pNonVirtualThunks; -SimpleRWLock* CNonVirtualThunk::s_pNonVirtualThunksListLock; - -// CNonVirtualThunkMgr statics -CNonVirtualThunkMgr *CNonVirtualThunkMgr::s_pNonVirtualThunkMgr; -#endif - -//+---------------------------------------------------------------------------- -// -// Method: CRemotingServices::Initialize public -// -// Synopsis: Initialized remoting state -// -//+---------------------------------------------------------------------------- -VOID CRemotingServices::Initialize() -{ - STANDARD_VM_CONTRACT; - - // Initialize the remoting services critical section - s_RemotingCrst.Init(CrstRemoting, CrstFlags(CRST_REENTRANCY|CRST_HOST_BREAKABLE)); - - CTPMethodTable::Initialize(); -} - -INT32 CRemotingServices::IsTransparentProxy(Object* orTP) -{ - CONTRACTL - { - NOTHROW; - GC_NOTRIGGER; - MODE_COOPERATIVE; - SO_TOLERANT; - } - CONTRACTL_END; - - INT32 fIsTPMT = FALSE; - - if(orTP != NULL) - { - // Check if the supplied object has transparent proxy method table - MethodTable *pMT = orTP->GetMethodTable(); - fIsTPMT = pMT->IsTransparentProxy() ? TRUE : FALSE; - } - - LOG((LF_REMOTING, LL_EVERYTHING, "!IsTransparentProxyEx(0x%x) returning %s", - orTP, fIsTPMT ? "TRUE" : "FALSE")); - - return(fIsTPMT); -} - - -Object* CRemotingServices::GetRealProxy(Object* objTP) -{ - CONTRACTL - { - NOTHROW; - GC_NOTRIGGER; - MODE_COOPERATIVE; - SO_TOLERANT; - } - CONTRACTL_END; - - OBJECTREF rv = NULL; - - if ((objTP != NULL) && (IsTransparentProxy(objTP))) - { - _ASSERTE(s_fRemotingStarted); - rv = CTPMethodTable::GetRP(OBJECTREF(objTP)); - } - - LOG((LF_REMOTING, LL_INFO100, "!GetRealProxy(0x%x) returning 0x%x\n", objTP, OBJECTREFToObject(rv))); - - return OBJECTREFToObject(rv); -} - - -//+---------------------------------------------------------------------------- -// -// Method: CRemotingServices::EnsureRemotingStarted -// -// Synopsis: Startup the remoting services. -// -// -//+---------------------------------------------------------------------------- -VOID CRemotingServices::EnsureRemotingStarted() -{ - CONTRACTL - { - THROWS; - GC_TRIGGERS; - MODE_ANY; - } - CONTRACTL_END; - - if (!CRemotingServices::s_fRemotingStarted) - CRemotingServices::StartRemoting(); - - if (!CTPMethodTable::s_fTPTableFieldsInitialized) - CTPMethodTable::EnsureFieldsInitialized(); -} - -//+---------------------------------------------------------------------------- -// -// Method: CRemotingServices::StartRemoting private -// -// Synopsis: Initialize the static fields of CRemotingServices class -// -// -//+---------------------------------------------------------------------------- -VOID CRemotingServices::StartRemoting() -{ - CONTRACTL - { - THROWS; - GC_TRIGGERS; - MODE_ANY; - } - CONTRACTL_END; - - // Acquire the remoting lock before initializing fields - GCX_PREEMP(); - - CrstHolder ch(&s_RemotingCrst); - - // Make sure that no other thread has initialized the fields - if (!s_fRemotingStarted) - { - InitActivationServicesClass(); - InitRealProxyClass(); - InitRemotingProxyClass(); - InitIdentityClass(); - InitServerIdentityClass(); - InitMarshalByRefObjectClass(); - InitRemotingServicesClass(); - InitObjectClass(); - InitLeaseClass(); - - // ********* NOTE ************ - // This must always be the last statement in this block to prevent races - // - VolatileStore(&s_fRemotingStarted, TRUE); - // ********* END NOTE ************ - } -} - -//+---------------------------------------------------------------------------- -// -// Method: CRemotingServices::InitActivationServicesClass private -// -// Synopsis: Extract the method descriptors and fields of ActivationServices class -// -// -//+---------------------------------------------------------------------------- -VOID CRemotingServices::InitActivationServicesClass() -{ - STANDARD_VM_CONTRACT; - - s_pIsCurrentContextOK = MscorlibBinder::GetMethod(METHOD__ACTIVATION_SERVICES__IS_CURRENT_CONTEXT_OK); -#ifdef FEATURE_COMINTEROP - s_pCreateObjectForCom = MscorlibBinder::GetMethod(METHOD__ACTIVATION_SERVICES__CREATE_OBJECT_FOR_COM); -#endif -} - -//+---------------------------------------------------------------------------- -// -// Method: CRemotingServices::InitRealProxyClass private -// -// Synopsis: Extract the method descriptors and fields of Real Proxy class -// -// -//+---------------------------------------------------------------------------- -VOID CRemotingServices::InitRealProxyClass() -{ - STANDARD_VM_CONTRACT; - - // Now store the methoddesc of the PrivateInvoke method on the RealProxy class - s_pRPPrivateInvoke = MscorlibBinder::GetMethod(METHOD__REAL_PROXY__PRIVATE_INVOKE); - - // Now find the offset to the _identity field inside the - // RealProxy class - s_dwIdOffset = RealProxyObject::GetOffsetOfIdentity() - Object::GetOffsetOfFirstField(); - - s_dwServerOffsetInRealProxy = RealProxyObject::GetOffsetOfServerObject() - Object::GetOffsetOfFirstField(); - - s_dwSrvIdentityOffsetInRealProxy = RealProxyObject::GetOffsetOfServerIdentity() - Object::GetOffsetOfFirstField(); - - return; -} - -//+---------------------------------------------------------------------------- -// -// Method: CRemotingServices::InitRemotingProxyClass private -// -// Synopsis: Extract the method descriptors and fields of RemotingProxy class -// -// -//+---------------------------------------------------------------------------- -VOID CRemotingServices::InitRemotingProxyClass() -{ - STANDARD_VM_CONTRACT; - - s_pRPInvokeStatic = MscorlibBinder::GetMethod(METHOD__REMOTING_PROXY__INVOKE); - - // Note: We cannot do this inside TPMethodTable::InitializeFields .. - // that causes recursions if in some situation only the latter is called - // If you do this you will see Asserts when running any process under CorDbg - // This is because jitting of NV methods on MBR objects calls - // InitializeFields and when actually doing that we should not need to - // JIT another NV method on some MBR object. - CTPMethodTable::s_pRemotingProxyClass = MscorlibBinder::GetClass(CLASS__REMOTING_PROXY); - _ASSERTE(CTPMethodTable::s_pRemotingProxyClass); -} - -//+---------------------------------------------------------------------------- -// -// Method: CRemotingServices::InitServerIdentityClass private -// -// Synopsis: Extract the method descriptors and fields of ServerIdentity class -// -// -//+---------------------------------------------------------------------------- -VOID CRemotingServices::InitServerIdentityClass() -{ - STANDARD_VM_CONTRACT; - - s_pServerIdentityClass = MscorlibBinder::GetClass(CLASS__SERVER_IDENTITY); -} - -//+---------------------------------------------------------------------------- -// -// Method: CRemotingServices::InitIdentityClass private -// -// Synopsis: Extract the method descriptors and fields of Identity class -// -// -//+---------------------------------------------------------------------------- -VOID CRemotingServices::InitIdentityClass() -{ - STANDARD_VM_CONTRACT; - - s_dwTPOrObjOffsetInIdentity = MscorlibBinder::GetFieldOffset(FIELD__IDENTITY__TP_OR_OBJECT); - - s_dwLeaseOffsetInIdentity = MscorlibBinder::GetFieldOffset(FIELD__IDENTITY__LEASE); - - s_dwURIOffsetInIdentity = MscorlibBinder::GetFieldOffset(FIELD__IDENTITY__OBJURI); -} - -//+---------------------------------------------------------------------------- -// -// Method: CRemotingServices::InitMarshalByRefObjectClass private -// -// Synopsis: Extract the method descriptors and fields of MarshalByRefObject class -// -// -//+---------------------------------------------------------------------------- -VOID CRemotingServices::InitMarshalByRefObjectClass() -{ - STANDARD_VM_CONTRACT; - - s_pMarshalByRefObjectClass = MscorlibBinder::GetClass(CLASS__MARSHAL_BY_REF_OBJECT); - s_dwMBRIDOffset = MarshalByRefObjectBaseObject::GetOffsetOfServerIdentity() - Object::GetOffsetOfFirstField(); -} - -//+---------------------------------------------------------------------------- -// -// Method: CRemotingServices::InitRemotingServicesClass private -// -// Synopsis: Extract the method descriptors and fields of RemotingServices class -// -// -//+---------------------------------------------------------------------------- -VOID CRemotingServices::InitRemotingServicesClass() -{ - STANDARD_VM_CONTRACT; - - s_pCheckCast = MscorlibBinder::GetMethod(METHOD__REMOTING_SERVICES__CHECK_CAST); - - // Need these to call wrap/unwrap from the VM (message.cpp). - // Also used by JIT helpers to wrap/unwrap - s_pWrapMethodDesc = MscorlibBinder::GetMethod(METHOD__REMOTING_SERVICES__WRAP); - s_pProxyForDomainDesc = MscorlibBinder::GetMethod(METHOD__REMOTING_SERVICES__CREATE_PROXY_FOR_DOMAIN); - s_pServerContextForProxyDesc = MscorlibBinder::GetMethod(METHOD__REMOTING_SERVICES__GET_SERVER_CONTEXT_FOR_PROXY); - s_pServerDomainIdForProxyDesc = MscorlibBinder::GetMethod(METHOD__REMOTING_SERVICES__GET_SERVER_DOMAIN_ID_FOR_PROXY); - s_pGetTypeDesc = MscorlibBinder::GetMethod(METHOD__REMOTING_SERVICES__GET_TYPE); -} - -//+---------------------------------------------------------------------------- -// -// Method: CRemotingServices::InitObjectClass private -// -// Synopsis: Extract the method descriptors and fields of Object class -// -// -//+---------------------------------------------------------------------------- -VOID CRemotingServices::InitObjectClass() -{ - STANDARD_VM_CONTRACT; - - s_pFieldSetterDesc = MscorlibBinder::GetMethod(METHOD__OBJECT__FIELD_SETTER); - s_pFieldGetterDesc = MscorlibBinder::GetMethod(METHOD__OBJECT__FIELD_GETTER); - s_pObjectGetTypeDesc = MscorlibBinder::GetMethod(METHOD__OBJECT__GET_TYPE); -} - -VOID CRemotingServices::InitLeaseClass() -{ - STANDARD_VM_CONTRACT; - - s_pRenewLeaseOnCallDesc = MscorlibBinder::GetMethod(METHOD__LEASE__RENEW_ON_CALL); -} - -//+---------------------------------------------------------------------------- -// -// Method: CRemotingServices::RequiresManagedActivation private -// -// Synopsis: Determine if a config file has been parsed or if there -// are any attributes on the class that would require us -// to go into the managed activation codepath. -// -// -// Note: Called by CreateProxyOrObject (JIT_NewCrossContext) -// -//+---------------------------------------------------------------------------- -ManagedActivationType __stdcall CRemotingServices::RequiresManagedActivation(TypeHandle ty) -{ - CONTRACTL - { - THROWS; - GC_NOTRIGGER; - MODE_ANY; - SO_TOLERANT; - PRECONDITION(!ty.IsNull()); - } - CONTRACTL_END; - - MethodTable* pMT = ty.GetMethodTable(); - - PREFIX_ASSUME(pMT != NULL); - if (!pMT->MayRequireManagedActivation()) - return NoManagedActivation; - -#ifdef _DEBUG - - ManagedActivationType bManaged = NoManagedActivation; - if (pMT->IsRemotingConfigChecked()) - { - // We have done work to figure this out in the past ... - // use the cached result - bManaged = pMT->RequiresManagedActivation() ? ManagedActivation : NoManagedActivation; - } - else if (pMT->IsContextful() || pMT->GetClass()->HasRemotingProxyAttribute()) - { - // Contextful and classes that have a remoting proxy attribute - // (whether they are MarshalByRef or ContextFul) always take the slow - // path of managed activation - bManaged = ManagedActivation; - } - else - { - // If we have parsed a config file that might have configured - // this Type to be activated remotely - if (GetAppDomain()->IsRemotingConfigured()) - { - bManaged = ManagedActivation; - // We will remember if the activation is actually going - // remote based on if the managed call to IsContextOK returned us - // a proxy or not - } - -#ifdef FEATURE_COMINTEROP - else if (pMT->IsComObjectType()) - { - bManaged = ComObjectType; - } -#endif // FEATURE_COMINTEROP - - } - -#endif // _DEBUG - - if (pMT->RequiresManagedActivation()) - { - // Contextful and classes that have a remoting proxy attribute - // (whether they are MarshalByRef or ContextFul) always take the slow - // path of managed activation - _ASSERTE(bManaged == ManagedActivation); - return ManagedActivation; - } - - ManagedActivationType bMng = NoManagedActivation; - if (!pMT->IsRemotingConfigChecked()) - { - g_IBCLogger.LogMethodTableAccess(pMT); - - // If we have parsed a config file that might have configured - // this Type to be activated remotely - if (GetAppDomain()->IsRemotingConfigured()) - { - bMng = ManagedActivation; - // We will remember if the activation is actually going - // remote based on if the managed call to IsContextOK returned us - // a proxy or not - } - -#ifdef FEATURE_COMINTEROP - else if (pMT->IsComObjectType()) - { - bMng = ComObjectType; - } -#endif // FEATURE_COMINTEROP - - if (bMng == NoManagedActivation) - { - pMT->TrySetRemotingConfigChecked(); - } - } - - _ASSERTE(bManaged == bMng); - return bMng; -} - -//+---------------------------------------------------------------------------- -// -// Method: CRemotingServices::CreateProxyOrObject public -// -// Synopsis: Determine if the current context is appropriate -// for activation. If the current context is OK then it creates -// an object else it creates a proxy. -// -// -// Note: Called by JIT_NewCrossContext -// -//+---------------------------------------------------------------------------- -OBJECTREF CRemotingServices::CreateProxyOrObject(MethodTable* pMT, - BOOL fIsCom /*default:FALSE*/, BOOL fIsNewObj /*default:FALSE*/) - /* fIsCom == Did we come here through CoCreateInstance */ - /* fIsNewObj == Did we come here through Jit_NewCrossContext (newObj) */ -{ - CONTRACTL - { - THROWS; - GC_TRIGGERS; - MODE_COOPERATIVE; - PRECONDITION(CheckPointer(pMT)); - PRECONDITION(!pMT->IsTransparentProxy()); - - // By the time we reach here, we have already checked that the class may require - // managed activation. This check is made either through the JIT_NewCrossContext helper - // or Activator.CreateInstance codepath. - PRECONDITION(pMT->MayRequireManagedActivation()); - } - CONTRACTL_END; - - // Ensure remoting has been started. - EnsureRemotingStarted(); - - // Get the address of IsCurrentContextOK in managed code - MethodDesc* pTargetMD = NULL; - Object *pServer = NULL; - -#ifdef FEATURE_COMINTEROP - if(fIsCom) - { - pTargetMD = CRemotingServices::MDofCreateObjectForCom(); - } - else -#endif // FEATURE_COMINTEROP - { - pTargetMD = CRemotingServices::MDofIsCurrentContextOK(); - } - - // Arrays are not created by JIT_NewCrossContext - _ASSERTE(!pMT->IsArray()); - - // Get the type seen by reflection - REFLECTCLASSBASEREF reflectType = (REFLECTCLASSBASEREF) pMT->GetManagedClassObject(); - LPVOID pvType = NULL; - *(REFLECTCLASSBASEREF *)&pvType = reflectType; - - // This will return either an uninitialized object or a proxy - pServer = (Object *)CTPMethodTable::CallTarget(pTargetMD, pvType, NULL, (LPVOID)(size_t)(fIsNewObj?1:0)); - - if (!pMT->IsContextful() && !pMT->IsComObjectType()) - { - // Cache the result of the activation attempt ... - // if a strictly MBR class is not configured for remote - // activation we will not go - // through this slow path next time! - // (see RequiresManagedActivation) - if (IsTransparentProxy(pServer)) - { - // Set the flag that this class is remote activate - // which means activation will go to managed code. - pMT->SetRequiresManagedActivation(); - } - else - { - // Set only the flag that no managed checks are required - // for this class next time. - pMT->SetRemotingConfigChecked(); - } - } - - LOG((LF_REMOTING, LL_INFO1000, "CreateProxyOrObject returning 0x%p\n", pServer)); - if (pMT->IsContextful()) - { - COUNTER_ONLY(GetPerfCounters().m_Context.cObjAlloc++); - } - return ObjectToOBJECTREF(pServer); -} - - -#ifndef HAS_REMOTING_PRECODE -//+---------------------------------------------------------------------------- -// -// Method: CRemotingServices::GetStubForNonVirtualMethod public -// -// Synopsis: Get a stub for a non virtual method. -// -// -//+---------------------------------------------------------------------------- -Stub* CRemotingServices::GetStubForNonVirtualMethod(MethodDesc* pMD, LPVOID pvAddrOfCode, Stub* pInnerStub) -{ - CONTRACT (Stub*) - { - THROWS; - GC_TRIGGERS; - MODE_ANY; - PRECONDITION(CheckPointer(pMD)); - PRECONDITION(CheckPointer(pvAddrOfCode)); - PRECONDITION(CheckPointer(pInnerStub, NULL_OK)); - POSTCONDITION(CheckPointer(RETVAL)); - } - CONTRACT_END; - - CPUSTUBLINKER sl; - Stub* pStub = CTPMethodTable::CreateStubForNonVirtualMethod(pMD, &sl, pvAddrOfCode, pInnerStub); - - RETURN pStub; -} -#endif // HAS_REMOTING_PRECODE - -//+---------------------------------------------------------------------------- -// -// Method: CRemotingServices::GetNonVirtualEntryPointForVirtualMethod public -// -// Synopsis: Get a thunk for a non-virtual call to a virtual method. -// Virtual methods do not normally get thunked in the vtable. This -// is because virtual calls use the object's vtable, and proxied objects -// would use the proxy's vtable. Hence local object (which would -// have the real vtable) can make virtual calls without going through -// the thunk. -// However, if the virtual function is called non-virtually, we have -// a problem (since this would bypass the proxy's vtable). Since this -// is not a common case, we fix it by using a stub in such cases. -// -// -//+---------------------------------------------------------------------------- -PCODE CRemotingServices::GetNonVirtualEntryPointForVirtualMethod(MethodDesc* pMD) -{ - CONTRACT (PCODE) - { - THROWS; - GC_TRIGGERS; - MODE_ANY; - PRECONDITION(CheckPointer(pMD)); - PRECONDITION(pMD->IsRemotingInterceptedViaVirtualDispatch()); - POSTCONDITION(RETVAL != NULL); - } - CONTRACT_END; - -#ifdef HAS_REMOTING_PRECODE - RETURN pMD->GetLoaderAllocator()->GetFuncPtrStubs()->GetFuncPtrStub(pMD, PRECODE_REMOTING); -#else - GCX_PREEMP(); - RETURN *CTPMethodTable::GetOrCreateNonVirtualSlotForVirtualMethod(pMD); -#endif -} - -#ifndef HAS_REMOTING_PRECODE -//+---------------------------------------------------------------------------- -// -// Method: CRemotingServices::DestroyThunk public -// -// Synopsis: Destroy the thunk for the non virtual method. -// -// -//+---------------------------------------------------------------------------- -void CRemotingServices::DestroyThunk(MethodDesc* pMD) -{ - WRAPPER_NO_CONTRACT; - - // Delegate to a helper routine - CTPMethodTable::DestroyThunk(pMD); -} -#endif // HAS_REMOTING_PRECODE - -//+---------------------------------------------------------------------------- -// -// Method: CRemotingServices::GetDispatchInterfaceHelper public -// -// Synopsis: Returns helper for dispatching interface call into the remoting system -// with exact MethodDesc. Used for remoting of calls on generic interfaces. -// The returned helper has MethodDesc calling convention -//+---------------------------------------------------------------------------- -PCODE CRemotingServices::GetDispatchInterfaceHelper(MethodDesc* pMD) -{ - WRAPPER_NO_CONTRACT; - - return GetEEFuncEntryPoint(CRemotingServices__DispatchInterfaceCall); -} - -//+---------------------------------------------------------------------------- -// -// Method: CRemotingServices::CheckCast public -// -// Synopsis: Checks either -// (1) If the object type supports the given interface OR -// (2) If the given type is present in the hierarchy of the -// object type -// -//+---------------------------------------------------------------------------- -BOOL CRemotingServices::CheckCast(OBJECTREF orTP, TypeHandle objTy, TypeHandle ty) -{ - CONTRACTL - { - THROWS; - GC_TRIGGERS; - MODE_COOPERATIVE; - PRECONDITION(orTP != NULL); - PRECONDITION(!objTy.IsNull()); - PRECONDITION(!ty.IsNull()); - - // Object class can never be an interface. We use a separate cached - // entry for storing interfaces that the proxy supports. - PRECONDITION(!objTy.IsInterface()); - } - CONTRACTL_END; - - // Early out if someone's trying to cast us to a type desc (such as a byref, - // array or function pointer). - if (ty.IsTypeDesc()) - return FALSE; - - BOOL fCastOK = FALSE; - - // (1) We are trying to cast to an interface - if (ty.IsInterface()) - { - // Do a quick check for interface cast by comparing it against the - // cached entry - MethodTable *pItfMT = ((TRANSPARENTPROXYREF)orTP)->GetInterfaceMethodTable(); - if (NULL != pItfMT) - { - if(pItfMT == ty.GetMethodTable()) - fCastOK = TRUE; - else - fCastOK = pItfMT->CanCastToInterface(ty.GetMethodTable()); - } - - if(!fCastOK) - fCastOK = objTy.GetMethodTable()->CanCastToInterface(ty.GetMethodTable()); - } - // (2) Everything else... - else - { - // Walk up the class hierarchy and find a matching class - while (ty != objTy) - { - if (objTy.IsNull()) - { - // Oh-oh, the cast did not succeed. Maybe we have to refine - // the proxy to match the clients view - break; - } - - // Continue searching - objTy = objTy.GetParent(); - } - - if(objTy == ty) - fCastOK = TRUE; - } - - return fCastOK; -} - -//+---------------------------------------------------------------------------- -// -// Method: CRemotingServices::CheckCast public -// -// Synopsis: Refine the type hierarchy that the proxy represents to match -// the client view. If the client is trying to cast the proxy -// to a type not supported by the server object then we -// return NULL -// -// -//+---------------------------------------------------------------------------- -BOOL CRemotingServices::CheckCast(OBJECTREF orTP, TypeHandle ty) -{ - CONTRACTL - { - THROWS; - GC_TRIGGERS; - MODE_COOPERATIVE; - PRECONDITION(orTP != NULL); - PRECONDITION(!ty.IsNull()); - } - CONTRACTL_END; - - BOOL fCastOK = FALSE; - - GCPROTECT_BEGIN(orTP); - - // Make sure the type being cast to has been restored. - ty.CheckRestore(); - - MethodTable *pMT = orTP->GetMethodTable(); - - // Make sure that we have a transparent proxy - _ASSERTE(pMT->IsTransparentProxy()); - - pMT = orTP->GetTrueMethodTable(); - - // Do a cast check without taking a lock - fCastOK = CheckCast(orTP, TypeHandle(pMT), ty); - - if (!fCastOK && !ty.IsTypeDesc()) - { - // We reach here only if any of the types in the current type hierarchy - // represented by the proxy does not match the given type. - // Call a helper routine in managed RemotingServices to find out - // whether the server object supports the given type - MethodDesc* pTargetMD = MDofCheckCast(); - fCastOK = CTPMethodTable::CheckCast(pTargetMD, (TRANSPARENTPROXYREF)orTP, ty); - } - - if (fCastOK) - { - // Do the type equivalence tests - CRealProxy::UpdateOptFlags(orTP); - } - - GCPROTECT_END(); - - LOG((LF_REMOTING, LL_INFO100, "CheckCast returning %s for object 0x%x and class 0x%x \n", (fCastOK ? "TRUE" : "FALSE"))); - - return (fCastOK); -} - -//+---------------------------------------------------------------------------- -// -// Method: CRemotingServices::FieldAccessor public -// -// Synopsis: Sets/Gets the value of the field given an instance or a proxy -// -//+---------------------------------------------------------------------------- -void CRemotingServices::FieldAccessor(FieldDesc* pFD, OBJECTREF o, LPVOID pVal, BOOL fIsGetter) -{ - CONTRACTL - { - THROWS; - GC_TRIGGERS; - MODE_COOPERATIVE; - PRECONDITION(CheckPointer(pFD)); - PRECONDITION(o != NULL); - PRECONDITION(CheckPointer(pVal, NULL_OK)); - PRECONDITION(o->IsTransparentProxy() || o->GetMethodTable()->IsMarshaledByRef()); - } - CONTRACTL_END; - - MethodTable *pMT = o->GetMethodTable(); - TypeHandle fldClass; - TypeHandle thRealObjectType; - - GCPROTECT_BEGIN(o); - GCPROTECT_BEGININTERIOR(pVal); - - // If the field descriptor type is not exact (i.e. it's a representative - // descriptor for a generic field) then we need to be more careful - // determining the properties of the field. - if (pFD->IsSharedByGenericInstantiations()) - { - // We need to resolve the field type in the context of the actual object - // it belongs to. If we've been handed a proxy we have to go grab the - // proxied type for this to work. - thRealObjectType = o->GetTrueTypeHandle(); - - // Evaluate the field signature in the type context of the parent object. - MetaSig sig(pFD, thRealObjectType); - sig.NextArg(); - fldClass = sig.GetLastTypeHandleThrowing(); - } - else - { - fldClass = pFD->GetFieldTypeHandleThrowing(); - } - - GCPROTECT_END(); - GCPROTECT_END(); - - CorElementType fieldType = fldClass.GetSignatureCorElementType(); - UINT cbSize = GetSizeForCorElementType(fieldType); - BOOL fIsGCRef = CorTypeInfo::IsObjRef(fieldType); - BOOL fIsByValue = fieldType == ELEMENT_TYPE_VALUETYPE; - - if(pMT->IsMarshaledByRef()) - { - GCX_FORBID(); - - _ASSERTE(!o->IsTransparentProxy()); - - // This is a reference to a real object. Get/Set the field value - // and return - LPVOID pFieldAddress = pFD->GetAddress((LPVOID)OBJECTREFToObject(o)); - LPVOID pDest = (fIsGetter ? pVal : pFieldAddress); - LPVOID pSrc = (fIsGetter ? pFieldAddress : pVal); - if(fIsGCRef && !fIsGetter) - { - SetObjectReference((OBJECTREF*)pDest, ObjectToOBJECTREF(*(Object **)pSrc), o->GetAppDomain()); - } - else if(fIsByValue) - { - CopyValueClass(pDest, pSrc, fldClass.AsMethodTable(), o->GetAppDomain()); - } - else - { - CopyDestToSrc(pDest, pSrc, cbSize); - } - } - else - { - // Call the managed code to start the field access call - CallFieldAccessor(pFD, o, pVal, fIsGetter, fIsByValue, fIsGCRef, thRealObjectType, fldClass, fieldType, cbSize); - } -} - -//+---------------------------------------------------------------------------- -// -// Method: CRemotingServices::CopyDestToSrc private -// -// Synopsis: Copies the specified number of bytes from the src to dest -// -// -//+---------------------------------------------------------------------------- -VOID CRemotingServices::CopyDestToSrc(LPVOID pDest, LPVOID pSrc, UINT cbSize) -{ - CONTRACTL - { - NOTHROW; - GC_NOTRIGGER; - MODE_ANY; - PRECONDITION(CheckPointer(pDest)); - PRECONDITION(CheckPointer(pSrc)); - } - CONTRACTL_END; - - switch (cbSize) - { - case 1: - VolatileStore((INT8*)pDest, *(INT8*)pSrc); - break; - - case 2: - VolatileStore((INT16*)pDest, *(INT16*)pSrc); - break; - - case 4: - VolatileStore((INT32*)pDest, *(INT32*)pSrc); - break; - - case 8: - VolatileStore((INT64*)pDest, *(INT64*)pSrc); - break; - - default: - UNREACHABLE(); - break; - } -} - -//+---------------------------------------------------------------------------- -// -// Method: CRemotingServices::CallFieldAccessor private -// -// Synopsis: Sets up the arguments and calls RealProxy::FieldAccessor -// -// -//+---------------------------------------------------------------------------- -VOID CRemotingServices::CallFieldAccessor(FieldDesc* pFD, - OBJECTREF o, - VOID* pVal, - BOOL fIsGetter, - BOOL fIsByValue, - BOOL fIsGCRef, - TypeHandle ty, - TypeHandle fldTy, - CorElementType fieldType, - UINT cbSize) -{ - CONTRACTL - { - THROWS; - GC_TRIGGERS; - MODE_COOPERATIVE; - PRECONDITION(CheckPointer(pFD)); - PRECONDITION(o != NULL); - PRECONDITION(CheckPointer(pVal)); - } - CONTRACTL_END; - - //****************************WARNING****************************** - // GC Protect all non-primitive variables - //***************************************************************** - - FieldArgs fieldArgs; - fieldArgs.obj = NULL; - fieldArgs.val = NULL; - fieldArgs.typeName = NULL; - fieldArgs.fieldName = NULL; - - GCPROTECT_BEGIN(fieldArgs); - GCPROTECT_BEGININTERIOR(pVal); - - fieldArgs.obj = o; - - // protect the field value if it is a gc-ref type - if(fIsGCRef) - fieldArgs.val = ObjectToOBJECTREF(*(Object **)pVal); - - - // Set up the arguments - - // Argument 1: String typeName - // Argument 2: String fieldName - // Get the type name and field name strings - GetTypeAndFieldName(&fieldArgs, pFD, ty); - - // Argument 3: Object val - OBJECTREF val = NULL; - if(!fIsGetter) - { - // If we are setting a field value then we create a variant data - // structure to hold the field value - // Extract the field from the gc protected structure if it is an object - // else use the value passed to the function - LPVOID pvFieldVal = (fIsGCRef ? (LPVOID)&(fieldArgs.val) : pVal); - // <REVISIT_TODO>: This can cause a GC. We need some way to protect the variant - // data</REVISIT_TODO> - OBJECTREF *lpVal = &val; - GCPROTECT_BEGININTERIOR (pvFieldVal); - CMessage::GetObjectFromStack(lpVal, &pvFieldVal, fieldType, fldTy, TRUE); - GCPROTECT_END (); - } - - // Get the method descriptor of the call - MethodDesc *pMD = (fIsGetter ? MDofFieldGetter() : MDofFieldSetter()); - - // Call the field accessor function - //////////////////////////////// GETTER /////////////////////////////////// - if(fIsGetter) - { - // Set up the return value - OBJECTREF oRet = NULL; - - GCPROTECT_BEGIN (oRet); - CRemotingServices__CallFieldGetter(pMD, - (LPVOID)OBJECTREFToObject(fieldArgs.obj), - (LPVOID)OBJECTREFToObject(fieldArgs.typeName), - (LPVOID)OBJECTREFToObject(fieldArgs.fieldName), - (LPVOID)&(oRet)); - - // If we are getting a field value then extract the field value - // based on the type of the field - if(fIsGCRef) - { - // Do a check cast to ensure that the field type and the - // return value are compatible - OBJECTREF orRet = oRet; - OBJECTREF orSaved = orRet; - if(IsTransparentProxy(OBJECTREFToObject(orRet))) - { - GCPROTECT_BEGIN(orRet); - - if(!CheckCast(orRet, fldTy)) - COMPlusThrow(kInvalidCastException, W("Arg_ObjObj")); - - orSaved = orRet; - - GCPROTECT_END(); - } - - *(OBJECTREF *)pVal = orSaved; - } - else if (fIsByValue) - { - // Copy from the source to the destination - if (oRet != NULL) - { - fldTy.GetMethodTable()->UnBoxIntoUnchecked(pVal, oRet); - } - } - else - { - if (oRet != NULL) - CopyDestToSrc(pVal, oRet->UnBox(), cbSize); - } - GCPROTECT_END (); - } - ///////////////////////// SETTER ////////////////////////////////////////// - else - { - CRemotingServices__CallFieldSetter(pMD, - (LPVOID)OBJECTREFToObject(fieldArgs.obj), - (LPVOID)OBJECTREFToObject(fieldArgs.typeName), - (LPVOID)OBJECTREFToObject(fieldArgs.fieldName), - (LPVOID)OBJECTREFToObject(val)); - } - - GCPROTECT_END(); // pVal - GCPROTECT_END(); // fieldArgs -} - -//+---------------------------------------------------------------------------- -// -// Method: CRemotingServices::GetTypeAndFieldName private -// -// Synopsis: Get the type name and field name of the -// -// -//+---------------------------------------------------------------------------- -VOID CRemotingServices::GetTypeAndFieldName(FieldArgs *pArgs, FieldDesc *pFD, TypeHandle thEnclosingClass) -{ - CONTRACTL - { - THROWS; - GC_TRIGGERS; - MODE_COOPERATIVE; - PRECONDITION(CheckPointer(pArgs)); - PRECONDITION(CheckPointer(pFD)); - } - CONTRACTL_END; - - TypeHandle thDeclaringType = !thEnclosingClass.IsNull() ? - pFD->GetExactDeclaringType(thEnclosingClass.AsMethodTable()) : pFD->GetEnclosingMethodTable(); - _ASSERTE(!thDeclaringType.IsNull()); - - // Extract the type name and field name string - // <REVISIT_TODO>FUTURE: Put this in the reflection data structure cache TarunA 11/26/00</REVISIT_TODO> - StackSString ss; - TypeString::AppendType(ss, thDeclaringType, TypeString::FormatNamespace | TypeString::FormatFullInst); - pArgs->typeName = StringObject::NewString(ss); - - pArgs->fieldName = StringObject::NewString(pFD->GetName()); -} - -//+---------------------------------------------------------------------------- -// -// Method: CRemotingServices::MatchField private -// -// Synopsis: Find out whether the given field name is the same as the name -// of the field descriptor field name. -// -// -//+---------------------------------------------------------------------------- -BOOL CRemotingServices::MatchField(FieldDesc* pCurField, LPCUTF8 szFieldName) -{ - CONTRACTL - { - NOTHROW; - GC_NOTRIGGER; - MODE_ANY; - PRECONDITION(CheckPointer(pCurField)); - PRECONDITION(CheckPointer(szFieldName)); - } - CONTRACTL_END; - - // Get the name of the field - LPCUTF8 szCurFieldName; - if (FAILED(pCurField->GetName_NoThrow(&szCurFieldName))) - { - return FALSE; - } - - return strcmp(szCurFieldName, szFieldName) == 0; -} - -//+---------------------------------------------------------------------------- -// -// Method: CRemotingServices::Wrap public -// -// Synopsis: Wrap a contextful object to create a proxy -// Delegates to a helper method to do the actual work -// -// -//+---------------------------------------------------------------------------- -OBJECTREF CRemotingServices::Wrap(OBJECTREF obj) -{ - CONTRACTL - { - THROWS; - GC_TRIGGERS; - MODE_COOPERATIVE; - } - CONTRACTL_END; - - // Basic sanity check - VALIDATEOBJECTREF(obj); - - // ******************* WARNING ******************************************** - // Do not throw any exceptions or provoke GC without setting up a frame. - // At present its the callers responsibility to setup a frame that can - // handle exceptions. - // ************************************************************************ - OBJECTREF orProxy = obj; - if(obj != NULL && (obj->GetMethodTable()->IsContextful())) - { - if(!IsTransparentProxy(OBJECTREFToObject(obj))) - { - // See if we can extract the proxy from the object - orProxy = GetProxyFromObject(obj); - if(orProxy == NULL) - { - // ask the remoting services to wrap the object - orProxy = CRemotingServices::WrapHelper(obj); - } - } - } - - return orProxy; -} - -//+---------------------------------------------------------------------------- -// -// Method: CRemotingServices::WrapHelper public -// -// Synopsis: Wrap an object to return a proxy. This function assumes that -// a fcall frame is already setup. -// -//+---------------------------------------------------------------------------- -OBJECTREF CRemotingServices::WrapHelper(OBJECTREF obj) -{ - // Basic sanity check - VALIDATEOBJECTREF(obj); - - CONTRACTL - { - THROWS; - GC_TRIGGERS; - MODE_COOPERATIVE; - PRECONDITION(obj != NULL); - PRECONDITION(!IsTransparentProxy(OBJECTREFToObject(obj))); - PRECONDITION(obj->GetMethodTable()->IsContextful()); - } - CONTRACTL_END; - - - // Default return value indicates an error - OBJECTREF newobj = NULL; - MethodDesc* pTargetMD = NULL; - - // Ensure remoting has been started. - EnsureRemotingStarted(); - - // Get the address of wrap in managed code - pTargetMD = CRemotingServices::MDofWrap(); - - // call the managed method to wrap - newobj = ObjectToOBJECTREF( (Object *)CTPMethodTable::CallTarget(pTargetMD, - (LPVOID)OBJECTREFToObject(obj), - NULL)); - - return newobj; -} - -//+---------------------------------------------------------------------------- -// -// Method: CRemotingServices::GetProxyFromObject public -// -// Synopsis: Extract the proxy from the field in the -// ContextBoundObject class -// -// -//+---------------------------------------------------------------------------- -OBJECTREF CRemotingServices::GetProxyFromObject(OBJECTREF obj) -{ - CONTRACTL - { - NOTHROW; - GC_NOTRIGGER; - MODE_COOPERATIVE; - PRECONDITION(obj != NULL); - } - CONTRACTL_END; - - // Basic sanity check - VALIDATEOBJECTREF(obj); - - // We can derive a proxy for contextful types only. - _ASSERTE(obj->GetMethodTable()->IsContextful()); - - OBJECTREF srvID = (OBJECTREF)(Object*)obj->GetPtrOffset(s_dwMBRIDOffset); - OBJECTREF orProxy = NULL; - - if (srvID != NULL) - orProxy = (OBJECTREF)(Object*)srvID->GetPtrOffset(s_dwTPOrObjOffsetInIdentity); - - // This should either be null or a proxy type - _ASSERTE((orProxy == NULL) || IsTransparentProxy(OBJECTREFToObject(orProxy))); - - return orProxy; -} - -//+---------------------------------------------------------------------------- -// -// Method: CRemotingServices::IsProxyToRemoteObject public -// -// Synopsis: Check if the proxy is to a remote object -// (1) TRUE : if object is non local (ie outside this PROCESS) otherwise -// (2) FALSE -// -//+---------------------------------------------------------------------------- -BOOL CRemotingServices::IsProxyToRemoteObject(OBJECTREF obj) -{ - CONTRACTL - { - THROWS; - GC_TRIGGERS; - MODE_COOPERATIVE; - PRECONDITION(obj != NULL); - } - CONTRACTL_END; - - // Basic sanity check - VALIDATEOBJECTREF(obj); - - // If remoting is not started, for now let us just return FALSE - if(!s_fRemotingStarted) - return FALSE; - - if(!obj->IsTransparentProxy()) - return FALSE; - - // so it is a transparent proxy - AppDomain *pDomain = GetServerDomainForProxy(obj); - if(pDomain != NULL) - return TRUE; - - return FALSE; -} - -//+---------------------------------------------------------------------------- -// -// Method: CRemotingServices::GetObjectFromProxy public -// -// Synopsis: Extract the object given a proxy. -// -// -//+---------------------------------------------------------------------------- -OBJECTREF CRemotingServices::GetObjectFromProxy(OBJECTREF obj) -{ - CONTRACTL - { - NOTHROW; - GC_NOTRIGGER; - MODE_COOPERATIVE; - PRECONDITION(obj != NULL); - PRECONDITION(s_fRemotingStarted); - PRECONDITION(IsTransparentProxy(OBJECTREFToObject(obj))); - SO_TOLERANT; - } - CONTRACTL_END; - - // Basic sanity check - VALIDATEOBJECTREF(obj); - - OBJECTREF oref = NULL; - if (CTPMethodTable__GenericCheckForContextMatch(OBJECTREFToObject(obj))) - { - OBJECTREF objRef = ObjectToOBJECTREF(GetRealProxy(OBJECTREFToObject(obj))); - oref = (OBJECTREF)(Object*)objRef->GetPtrOffset(s_dwServerOffsetInRealProxy); - if (oref != NULL) - obj = oref; - } - - return obj; -} - -//+---------------------------------------------------------------------------- -// -// Method: CRemotingServices::GetServerIdentityFromProxy private -// -// Synopsis: Gets the server identity (if one exists) from a proxy -// -// -// -// -//+---------------------------------------------------------------------------- -OBJECTREF CRemotingServices::GetServerIdentityFromProxy(OBJECTREF obj) -{ - CONTRACTL - { - THROWS; - GC_TRIGGERS; - MODE_COOPERATIVE; - PRECONDITION(obj != NULL); - PRECONDITION(IsTransparentProxy(OBJECTREFToObject(obj))); - } - CONTRACTL_END; - - - // Extract the real proxy underlying the transparent proxy - OBJECTREF pObj = ObjectToOBJECTREF(GetRealProxy(OBJECTREFToObject(obj))); - - OBJECTREF id = NULL; - - // Extract the identity object - pObj = (OBJECTREF)(Object*)pObj->GetPtrOffset(s_dwIdOffset); - - // Extract the _identity from the real proxy only if it is an instance of - // remoting proxy - if((pObj != NULL) && IsInstanceOfServerIdentity(pObj->GetMethodTable())) - id = pObj; - - return id; -} - -//+---------------------------------------------------------------------------- -// -// Method: CRemotingServices::GetServerDomainForProxy public -// -// Synopsis: Returns the AppDomain corresponding to the server -// if the proxy and the server are in the same process. -// -// -//+---------------------------------------------------------------------------- -AppDomain *CRemotingServices::GetServerDomainForProxy(OBJECTREF proxy) -{ - CONTRACT (AppDomain*) - { - THROWS; - GC_TRIGGERS; - MODE_COOPERATIVE; - PRECONDITION(proxy != NULL); - POSTCONDITION(CheckPointer(RETVAL, NULL_OK)); - } - CONTRACT_END; - - // call the managed method - Context *pContext = (Context *)GetServerContextForProxy(proxy); - if (pContext) - RETURN pContext->GetDomain(); - else - RETURN NULL; -} - -//+---------------------------------------------------------------------------- -// -// Method: CRemotingServices::GetServerDomainIdForProxy public -// -// Synopsis: Returns the AppDomain ID corresponding to the server -// if the proxy and the server are in the same process. -// Returns 0 if it cannot determine. -// -// -//+---------------------------------------------------------------------------- -int CRemotingServices::GetServerDomainIdForProxy(OBJECTREF proxy) -{ - CONTRACTL - { - THROWS; - GC_TRIGGERS; - MODE_COOPERATIVE; - PRECONDITION(proxy != NULL); - PRECONDITION(IsTransparentProxy(OBJECTREFToObject(proxy))); - } - CONTRACTL_END; - - // Get the address of GetDomainIdForProxy in managed code - MethodDesc* pTargetMD = CRemotingServices::MDofGetServerDomainIdForProxy(); - - // This will just read the appDomain ID from the marshaled data - // for the proxy. It returns 0 if the proxy is to a server in another - // process. It may also return 0 if it cannot determine the server - // domain ID (eg. for Well Known Object proxies). - - // call the managed method - // <REVISIT_TODO>This cast to Int32 actually causes a potential loss - // of data.</REVISIT_TODO> - return (int)(INT_PTR)CTPMethodTable::CallTarget( - pTargetMD, - (LPVOID)OBJECTREFToObject(proxy), - NULL); -} - - -//+---------------------------------------------------------------------------- -// -// Method: CRemotingServices::GetServerContextForProxy public -// -// Synopsis: Returns the AppDomain corresponding to the server -// if the proxy and the server are in the same process. -// -// -//+---------------------------------------------------------------------------- -Context *CRemotingServices::GetServerContextForProxy(OBJECTREF proxy) -{ - CONTRACT (Context*) - { - THROWS; - GC_TRIGGERS; - MODE_COOPERATIVE; - PRECONDITION(proxy != NULL); - PRECONDITION(IsTransparentProxy(OBJECTREFToObject(proxy))); - POSTCONDITION(CheckPointer(RETVAL, NULL_OK)); - } - CONTRACT_END; - - // Get the address of GetAppDomainForProxy in managed code - MethodDesc* pTargetMD = CRemotingServices::MDofGetServerContextForProxy(); - - // This will return the correct VM Context object for the server if - // the proxy is true cross domain proxy to a server in another domain - // in the same process. The managed method will Assert if called on a proxy - // which is either half-built or does not have an ObjRef ... which may - // happen for eg. if the proxy and the server are in the same appdomain. - - // we return NULL if the server object for the proxy is in another - // process or if the appDomain for the server is invalid or if we cannot - // determine the context (eg. well known object proxies). - - // call the managed method - RETURN (Context *)CTPMethodTable::CallTarget( - pTargetMD, - (LPVOID)OBJECTREFToObject(proxy), - NULL); -} - -//+---------------------------------------------------------------------------- -// -// Method: CRemotingServices::CreateProxyForDomain public -// -// Synopsis: Create a proxy for the app domain object by calling marshal -// inside the newly created domain and unmarshaling in the old -// domain -// -// -//+---------------------------------------------------------------------------- -OBJECTREF CRemotingServices::CreateProxyForDomain(AppDomain* pDomain) -{ - CONTRACTL - { - THROWS; - GC_TRIGGERS; - MODE_COOPERATIVE; - PRECONDITION(CheckPointer(pDomain)); - } - CONTRACTL_END; - - // Ensure remoting has been started. - EnsureRemotingStarted(); - - MethodDesc* pTargetMD = MDOfCreateProxyForDomain(); - - // Call the managed method which will marshal and unmarshal the - // appdomain object to create the proxy - - // We pass the ContextID of the default context of the new appDomain - // object. This helps the boot-strapping! (i.e. entering the new domain - // to marshal itself out). - - Object *proxy = (Object *)CTPMethodTable::CallTarget( - pTargetMD, - (LPVOID)(DWORD_PTR)pDomain->GetId().m_dwId, - (LPVOID)pDomain->GetDefaultContext()); - return ObjectToOBJECTREF(proxy); -} - -//+---------------------------------------------------------------------------- -// -// Method: CRemotingServices::GetClass public -// -// Synopsis: Extract the true class of the object whose proxy is given. -// -// -// -//+---------------------------------------------------------------------------- -REFLECTCLASSBASEREF CRemotingServices::GetClass(OBJECTREF pThis) -{ - CONTRACTL - { - THROWS; - GC_TRIGGERS; - MODE_COOPERATIVE; - INJECT_FAULT(COMPlusThrowOM()); - PRECONDITION(pThis != NULL); - } - CONTRACTL_END; - - REFLECTCLASSBASEREF refClass = NULL; - MethodTable *pMT = NULL; - - GCPROTECT_BEGIN(pThis); - - // For proxies to objects in the same appdomain, we always know the - // correct type - if(GetServerIdentityFromProxy(pThis) != NULL) - { - pMT = pThis->GetTrueMethodTable(); - } - else - { - // For everything else either we have refined the proxy to its correct type - // or we have to consult the objref to get the true type - - MethodDesc* pTargetMD = CRemotingServices::MDofGetType(); - - refClass = (REFLECTCLASSBASEREF)(ObjectToOBJECTREF((Object *)CTPMethodTable::CallTarget(pTargetMD, - (LPVOID)OBJECTREFToObject(pThis), NULL))); - - if(refClass == NULL) - { - // There was no objref associated with the proxy or it is a proxy - // that we do not understand. - // In this case, we return the class that is stored in the proxy - pMT = pThis->GetTrueMethodTable(); - } - - _ASSERTE(refClass != NULL || pMT != NULL); - - // Refine the proxy to the class just retrieved - if(refClass != NULL) - { - CTPMethodTable::RefineProxy((TRANSPARENTPROXYREF)pThis, refClass->GetType()); - } - } - - if (refClass == NULL) - { - PREFIX_ASSUME(pMT != NULL); - refClass = (REFLECTCLASSBASEREF)pMT->GetManagedClassObject(); - } - - GCPROTECT_END(); - - _ASSERTE(refClass != NULL); - return refClass; -} - -//+---------------------------------------------------------------------------- -// -// Method: CRealProxy::SetStubData public -// -// Synopsis: Set the stub data in the transparent proxy -// -//+---------------------------------------------------------------------------- -FCIMPL2(VOID, CRealProxy::SetStubData, Object* orRPUNSAFE, Object* orStubDataUNSAFE) -{ - CONTRACTL - { - FCALL_CHECK; - } - CONTRACTL_END; - - BOOL fThrow = FALSE; - REALPROXYREF orRP = (REALPROXYREF)ObjectToOBJECTREF(orRPUNSAFE); - OBJECTREF orStubData = ObjectToOBJECTREF(orStubDataUNSAFE); - - if (orRP != NULL && orStubData != NULL) - { - TRANSPARENTPROXYREF orTP = orRP->GetTransparentProxy(); - if (orTP != NULL) - { - orTP->SetStubData(orStubData); - } - else - { - fThrow = TRUE; - } - } - else - { - fThrow = TRUE; - } - - if(fThrow) - FCThrowVoid(kArgumentNullException); -} -FCIMPLEND - -//+---------------------------------------------------------------------------- -// -// Method: CRealProxy::GetStubData public -// -// Synopsis: Get the stub data in the transparent proxy -// -//+---------------------------------------------------------------------------- -FCIMPL1(Object*, CRealProxy::GetStubData, Object* orRPUNSAFE) -{ - CONTRACTL - { - FCALL_CHECK; - } - CONTRACTL_END; - - BOOL fThrow = FALSE; - REALPROXYREF orRP = (REALPROXYREF)ObjectToOBJECTREF(orRPUNSAFE); - OBJECTREF orRet = NULL; - - if (orRP != NULL) - { - TRANSPARENTPROXYREF orTP = orRP->GetTransparentProxy(); - if (orTP != NULL) - orRet = orTP->GetStubData(); - else - fThrow = TRUE; - } - else - { - fThrow = TRUE; - } - - if(fThrow) - FCThrow(kArgumentNullException); - - return OBJECTREFToObject(orRet); -} -FCIMPLEND - -//+---------------------------------------------------------------------------- -// -// Method: CRealProxy::GetDefaultStub public -// -// Synopsis: Get the default stub implemented by us which matches contexts -// -//+---------------------------------------------------------------------------- -FCIMPL0(LPVOID, CRealProxy::GetDefaultStub) -{ - FCALL_CONTRACT; - - return (LPVOID)CRemotingServices__CheckForContextMatch; -} -FCIMPLEND - -//+---------------------------------------------------------------------------- -// -// Method: CRealProxy::GetStub public -// -// Synopsis: Get the stub pointer in the transparent proxy -// -//+---------------------------------------------------------------------------- -FCIMPL1(LPVOID, CRealProxy::GetStub, Object* orRPUNSAFE) -{ - CONTRACTL - { - FCALL_CHECK; - PRECONDITION(CheckPointer(orRPUNSAFE)); - } - CONTRACTL_END; - - REALPROXYREF orRP = (REALPROXYREF)ObjectToOBJECTREF(orRPUNSAFE); - TRANSPARENTPROXYREF orTP = orRP->GetTransparentProxy(); - - return orTP->GetStub(); -} -FCIMPLEND - -//+---------------------------------------------------------------------------- -// -// Method: CRealProxy::GetProxiedType public -// -// Synopsis: Get the type that is represented by the transparent proxy -// -//+---------------------------------------------------------------------------- -FCIMPL1(Object*, CRealProxy::GetProxiedType, Object* orRPUNSAFE) -{ - FCALL_CONTRACT; - - REFLECTCLASSBASEREF refClass = NULL; - REALPROXYREF orRP = (REALPROXYREF)ObjectToOBJECTREF(orRPUNSAFE); - HELPER_METHOD_FRAME_BEGIN_RET_1(orRP); - - TRANSPARENTPROXYREF orTP = orRP->GetTransparentProxy(); - - refClass = CRemotingServices::GetClass(orTP); - _ASSERTE(refClass != NULL); - - HELPER_METHOD_FRAME_END(); - return OBJECTREFToObject(refClass); -} -FCIMPLEND - -//+---------------------------------------------------------------------------- -// -// Method: CTPMethodTable::Initialize public -// -// Synopsis: Initialized data structures needed for managing tranparent -// proxies -// -//+---------------------------------------------------------------------------- -VOID CTPMethodTable::Initialize() -{ - STANDARD_VM_CONTRACT; - - s_TPMethodTableCrst.Init(CrstTPMethodTable); -} - -//+---------------------------------------------------------------------------- - -PCODE CTPMethodTable::GetTPStubEntryPoint() -{ - LIMITED_METHOD_CONTRACT; - return GetEEFuncEntryPoint(TransparentProxyStub); -} - -PCODE CTPMethodTable::GetDelegateStubEntryPoint() -{ - LIMITED_METHOD_CONTRACT; - return GetEEFuncEntryPoint(TransparentProxyStub_CrossContext); -} - -//+---------------------------------------------------------------------------- -// -// Method: CTPMethodTable::EnsureFieldsInitialized private -// -// Synopsis: Initialize the static fields of CTPMethodTable class -// and the thunk manager classes -// -// -//+---------------------------------------------------------------------------- -void CTPMethodTable::EnsureFieldsInitialized() -{ - CONTRACT_VOID - { - THROWS; - GC_TRIGGERS; - MODE_ANY; - POSTCONDITION(s_fTPTableFieldsInitialized); - } - CONTRACT_END; - - if (!s_fTPTableFieldsInitialized) - { - GCX_PREEMP(); - - // Load Tranparent proxy class (do this before we enter the critical section) - MethodTable* pTPMT = MscorlibBinder::GetClass(CLASS__TRANSPARENT_PROXY); - _ASSERTE(pTPMT->IsTransparentProxy()); - - CrstHolder ch(&s_TPMethodTableCrst); - - if(!s_fTPTableFieldsInitialized) - { - // Obtain size of GCInfo stored above the method table - CGCDesc *pGCDesc = CGCDesc::GetCGCDescFromMT(pTPMT); - BYTE *pGCTop = (BYTE *) pGCDesc->GetLowestSeries(); - s_dwGCInfoBytes = (DWORD)(((BYTE *) pTPMT) - pGCTop); - _ASSERTE((s_dwGCInfoBytes & 3) == 0); - - // Obtain the number of bytes to be copied for creating the TP - // method tables containing thunks - _ASSERTE(((s_dwGCInfoBytes + sizeof(MethodTable)) & (sizeof(PCODE)-1)) == 0); - s_dwMTDataSlots = ((s_dwGCInfoBytes + sizeof(MethodTable)) / sizeof(PCODE)); - _ASSERTE(sizeof(MethodTable) == MethodTable::GetVtableOffset()); - - // We rely on the number of interfaces implemented by the - // Transparent proxy being 0, so that InterfaceInvoke hints - // fail and trap to InnerFailStub which also fails and - // in turn traps to FailStubWorker. In FailStubWorker, we - // determine the class being proxied and return correct slot. - _ASSERTE(pTPMT->GetNumInterfaces() == 0); - - CVirtualThunkMgr::InitVirtualThunkManager(); - - // Create the global thunk table and set the cycle between - // the transparent proxy class and the global thunk table - CreateTPMethodTable(pTPMT); - -#ifdef HAS_REMOTING_PRECODE - // Activate the remoting precode helper - ActivatePrecodeRemotingThunk(); -#endif // HAS_REMOTING_PRECODE - - // NOTE: This must always be the last statement in this block - // to prevent races - // Load Tranparent proxy class - s_fTPTableFieldsInitialized = TRUE; - } - } - - RETURN; -} - -//+---------------------------------------------------------------------------- -// -// Method: CTPMethodTable::GetRP public -// -// Synopsis: Get the real proxy backing the transparent proxy -// -//+---------------------------------------------------------------------------- -REALPROXYREF CTPMethodTable::GetRP(OBJECTREF orTP) -{ - CONTRACTL - { - NOTHROW; - GC_NOTRIGGER; - MODE_COOPERATIVE; - PRECONDITION(orTP != NULL); - PRECONDITION(orTP->IsTransparentProxy()); - SO_TOLERANT; - } - CONTRACTL_END; - - return (REALPROXYREF)(((TRANSPARENTPROXYREF)orTP)->GetRealProxy()); -} - -//+---------------------------------------------------------------------------- -// -// Method: CTPMethodTable::GetMethodTableBeingProxied public -// -// Synopsis: Get the real type backing the transparent proxy -// -//+---------------------------------------------------------------------------- -MethodTable * CTPMethodTable::GetMethodTableBeingProxied(OBJECTREF orTP) -{ - CONTRACTL - { - NOTHROW; - GC_NOTRIGGER; - MODE_COOPERATIVE; - SO_TOLERANT; - PRECONDITION(orTP != NULL); - PRECONDITION(orTP->IsTransparentProxy()); - } - CONTRACTL_END; - - return ((TRANSPARENTPROXYREF)orTP)->GetMethodTableBeingProxied(); -} - -#define PAGE_ROUND_UP(cb) (((cb) + g_SystemInfo.dwAllocationGranularity) & ~(g_SystemInfo.dwAllocationGranularity - 1)) - -//+---------------------------------------------------------------------------- -// -// Method: CTPMethodTable::CreateTPMethodTable private -// -// Synopsis: (1) Reserves a transparent proxy method table that is large -// enough to support the largest vtable -// (2) Commits memory for the GC info of the global thunk table and -// sets the cycle between the transparent proxy class and the -// globale thunk table. -// -//+---------------------------------------------------------------------------- - -void CTPMethodTable::CreateTPMethodTable(MethodTable* pTPMT) -{ - CONTRACT_VOID { - THROWS; - GC_NOTRIGGER; - MODE_ANY; - POSTCONDITION(CheckPointer(s_pThunkTable)); - } CONTRACT_END; - - // The largest possible vtable size 64K - DWORD dwMaxSlots = 64*1024; - - // Allocate virtual memory that is big enough to hold a method table - // of the maximum possible size - DWORD dwReserveSize = 0; - DWORD dwMethodTableReserveSize = (DWORD)(s_dwMTDataSlots * sizeof(PCODE)); - s_dwReservedTPIndirectionSlotSize = MethodTable::GetNumVtableIndirections(dwMaxSlots) * sizeof(PTR_PCODE); - dwMethodTableReserveSize += s_dwReservedTPIndirectionSlotSize; - - dwMethodTableReserveSize += (DWORD)(dwMaxSlots * sizeof(PCODE)); - dwReserveSize = PAGE_ROUND_UP(dwMethodTableReserveSize); - - void *pAlloc = ::ClrVirtualAlloc(0, dwReserveSize, MEM_RESERVE | MEM_TOP_DOWN, PAGE_EXECUTE_READWRITE); - - if (pAlloc) - { - BOOL bFailed = TRUE; - - // Make sure that we have not created the one and only - // transparent proxy method table before - _ASSERTE(NULL == s_pThunkTable); - - // Commit the required amount of memory - DWORD dwCommitSize = 0; - - // MethodTable memory - DWORD dwMethodTableCommitSize = (s_dwMTDataSlots) * sizeof(PCODE); - if (!ClrSafeInt<DWORD>::addition(0, dwMethodTableCommitSize, dwCommitSize)) - { - COMPlusThrowHR(COR_E_OVERFLOW); - } - - if (::ClrVirtualAlloc(pAlloc, dwCommitSize, MEM_COMMIT, PAGE_EXECUTE_READWRITE)) - { - // Copy the fixed portion from the true TP Method Table - memcpy(pAlloc,MTToAlloc(pTPMT, s_dwGCInfoBytes), (dwMethodTableCommitSize)); - - // Initialize the transparent proxy method table - InitThunkTable(0, dwMaxSlots, AllocToMT((BYTE *) pAlloc, s_dwGCInfoBytes)); - - // At this point the transparent proxy class points to the - // the true TP Method Table and not the transparent - // proxy method table. We do not use the true method table - // any more. Instead we use the transparent proxy method table - // for allocating transparent proxies. So, we have to make the - // transparent proxy class point to the one and only transparent - // proxy method table - pTPMT->GetClass()->SetMethodTableForTransparentProxy(s_pThunkTable); - - // Allocate the slots of the Object class method table because - // we can reflect on the __Transparent proxy class even though - // we never intend to use remoting. - _ASSERTE(NULL != g_pObjectClass); - _ASSERTE(0 == GetCommitedTPSlots()); - if(ExtendCommitedSlots(g_pObjectClass->GetNumMethods())) - bFailed = FALSE; - } - else - { - ClrVirtualFree(pAlloc, 0, MEM_RELEASE); - } - - if(bFailed) - DestroyThunkTable(); - } - else { - if (pAlloc != NULL) - ::ClrVirtualFree(pAlloc, 0, MEM_RELEASE); - } - - // Note that the thunk table is set to null on any failure path - // via DestroyThunkTable - if (!s_pThunkTable) - COMPlusThrowOM(); - - RETURN; -} - -//+---------------------------------------------------------------------------- -// -// Method: CTPMethodTable::ExtendCommitedSlots private -// -// Synopsis: Extends the commited slots of transparent proxy method table to -// the desired number -// -//+---------------------------------------------------------------------------- -BOOL CTPMethodTable::ExtendCommitedSlots(_In_range_(1,64*1024) DWORD dwSlots) -{ - CONTRACTL - { - NOTHROW; - GC_NOTRIGGER; - MODE_ANY; - INJECT_FAULT(return FALSE); - PRECONDITION(s_dwCommitedTPSlots <= dwSlots); - PRECONDITION(dwSlots <= s_dwReservedTPSlots); - PRECONDITION((CVirtualThunks::GetVirtualThunks() == NULL) || - (s_dwCommitedTPSlots == CVirtualThunks::GetVirtualThunks()->_dwCurrentThunk)); - - // Either we have initialized everything or we are asked to allocate - // some slots during initialization - PRECONDITION(s_fTPTableFieldsInitialized || (0 == s_dwCommitedTPSlots)); - } - CONTRACTL_END; - - // Commit memory for TPMethodTable - BOOL bAlloc = FALSE; - void *pAlloc = MTToAlloc(s_pThunkTable, s_dwGCInfoBytes); - ClrSafeInt<DWORD> dwCommitSize; - dwCommitSize += s_dwMTDataSlots * sizeof(PCODE); - dwCommitSize += MethodTable::GetNumVtableIndirections(dwSlots) * sizeof(PTR_PCODE); - - DWORD dwLastIndirectionSlot = s_pThunkTable->GetIndexOfVtableIndirection(s_pThunkTable->GetNumVirtuals() - 1); - DWORD dwSlotsCommitSize = dwSlots * sizeof(PCODE); - PCODE *pAllocSlots = (PCODE*)(((BYTE*)s_pThunkTable) + s_dwMTDataSlots * sizeof(PCODE) + s_dwReservedTPIndirectionSlotSize); - - if (dwCommitSize.IsOverflow()) - { - return FALSE; // error condition - } - - if (::ClrVirtualAlloc(pAlloc, dwCommitSize.Value(), MEM_COMMIT, PAGE_EXECUTE_READWRITE) && - ::ClrVirtualAlloc(pAllocSlots, dwSlotsCommitSize, MEM_COMMIT, PAGE_EXECUTE_READWRITE)) - { - _ASSERTE(FitsIn<WORD>(dwSlots)); - s_pThunkTable->SetNumVirtuals((WORD)dwSlots); - - MethodTable::VtableIndirectionSlotIterator it = s_pThunkTable->IterateVtableIndirectionSlotsFrom(dwLastIndirectionSlot); - do - { - it.SetIndirectionSlot(&pAllocSlots[it.GetStartSlot()]); - } - while (it.Next()); - - bAlloc = AllocateThunks(dwSlots, dwCommitSize.Value()); - } - - return bAlloc; -} - -//+---------------------------------------------------------------------------- -// -// Method: CTPMethodTable::AllocateThunks private -// -// Synopsis: Allocates the desired number of thunks for virtual methods -// -//+---------------------------------------------------------------------------- -BOOL CTPMethodTable::AllocateThunks(DWORD dwSlots, DWORD dwCommitSize) -{ - CONTRACTL - { - NOTHROW; - GC_NOTRIGGER; - MODE_ANY; - } - CONTRACTL_END; - // Check for existing thunks - DWORD dwCommitThunks = 0; - DWORD dwAllocThunks = dwSlots; - MethodTable *pThunkTable = s_pThunkTable; - - CVirtualThunks* pThunks = CVirtualThunks::GetVirtualThunks(); - if (pThunks) - { - // Compute the sizes of memory to be commited and allocated - BOOL fCommit; - if (dwSlots < pThunks->_dwReservedThunks) - { - fCommit = TRUE; - dwCommitThunks = dwSlots; - dwAllocThunks = 0; - } - else - { - fCommit = (pThunks->_dwCurrentThunk != pThunks->_dwReservedThunks); - dwCommitThunks = pThunks->_dwReservedThunks; - dwAllocThunks = dwSlots - pThunks->_dwReservedThunks; - } - - // Commit memory if needed - if (fCommit) - { - DWORD dwCommitSizeTmp = (sizeof(CVirtualThunks) - ConstVirtualThunkSize) + - ((dwCommitThunks - pThunks->_dwStartThunk) * ConstVirtualThunkSize); - - if (!::ClrVirtualAlloc(pThunks, dwCommitSizeTmp, MEM_COMMIT, PAGE_EXECUTE_READWRITE)) - return(NULL); - - // Generate thunks that push slot number and jump to TP stub - DWORD dwStartSlot = pThunks->_dwStartThunk; - DWORD dwCurrentSlot = pThunks->_dwCurrentThunk; - while (dwCurrentSlot < dwCommitThunks) - { - PCODE pCode = CreateThunkForVirtualMethod(dwCurrentSlot, (BYTE *)&pThunks->ThunkCode[dwCurrentSlot-dwStartSlot]); - pThunkTable->SetSlot(dwCurrentSlot, pCode); - ++dwCurrentSlot; - } - - ClrFlushInstructionCache(&pThunks->ThunkCode[pThunks->_dwCurrentThunk-dwStartSlot], - (dwCommitThunks-pThunks->_dwCurrentThunk)*ConstVirtualThunkSize); - - s_dwCommitedTPSlots = dwCommitThunks; - pThunks->_dwCurrentThunk = dwCommitThunks; - } - } - - // <REVISIT_TODO> - // Check for the avialability of a TP method table that is no longer being - // reused </REVISIT_TODO> - - // Allocate memory if necessary - if (dwAllocThunks) - { - DWORD dwReserveSize = ((sizeof(CVirtualThunks) - ConstVirtualThunkSize) + - ((dwAllocThunks << 1) * ConstVirtualThunkSize) + - g_SystemInfo.dwAllocationGranularity) & ~((size_t) g_SystemInfo.dwAllocationGranularity - 1); - - void *pAlloc = ::ClrVirtualAlloc(0, dwReserveSize, - MEM_RESERVE | MEM_TOP_DOWN, - PAGE_EXECUTE_READWRITE); - if (pAlloc) - { - // Commit the required amount of memory - DWORD dwCommitSizeTmp = (sizeof(CVirtualThunks) - ConstVirtualThunkSize) + - (dwAllocThunks * ConstVirtualThunkSize); - - if (::ClrVirtualAlloc(pAlloc, dwCommitSizeTmp, MEM_COMMIT, PAGE_EXECUTE_READWRITE)) - { - ((CVirtualThunks *) pAlloc)->_pNext = pThunks; - pThunks = CVirtualThunks::SetVirtualThunks((CVirtualThunks *) pAlloc); - pThunks->_dwReservedThunks = (dwReserveSize - - (sizeof(CVirtualThunks) - ConstVirtualThunkSize)) / - ConstVirtualThunkSize; - pThunks->_dwStartThunk = dwCommitThunks; - pThunks->_dwCurrentThunk = dwCommitThunks; - - // Generate thunks that push slot number and jump to TP stub - DWORD dwStartSlot = pThunks->_dwStartThunk; - DWORD dwCurrentSlot = pThunks->_dwCurrentThunk; - while (dwCurrentSlot < dwSlots) - { - PCODE pCode = CreateThunkForVirtualMethod(dwCurrentSlot, (BYTE *)&pThunks->ThunkCode[dwCurrentSlot-dwStartSlot]); - pThunkTable->SetSlot(dwCurrentSlot, pCode); - ++dwCurrentSlot; - } - - ClrFlushInstructionCache(&pThunks->ThunkCode[pThunks->_dwCurrentThunk-dwStartSlot], - (dwSlots-pThunks->_dwCurrentThunk)*ConstVirtualThunkSize); - - s_dwCommitedTPSlots = dwSlots; - pThunks->_dwCurrentThunk = dwSlots; - } - else - { - ::ClrVirtualFree(pAlloc, 0, MEM_RELEASE); - return FALSE; - } - } - else - { - return FALSE; - } - } - - return TRUE; -} - -//+---------------------------------------------------------------------------- -// -// Method: CTPMethodTable::CreateTPOfClassForRP private -// -// Synopsis: Creates a transparent proxy that behaves as an object of the -// supplied class -// -//+---------------------------------------------------------------------------- -void CTPMethodTable::CreateTPOfClassForRP(TypeHandle ty, REALPROXYREF *pRP, TRANSPARENTPROXYREF *pTP) -{ - CONTRACT_VOID - { - THROWS; - GC_TRIGGERS; - MODE_COOPERATIVE; - INJECT_FAULT(COMPlusThrowOM()); - PRECONDITION(!ty.IsNull()); - PRECONDITION(pRP != NULL); - PRECONDITION(*pRP != NULL); - PRECONDITION(pTP != NULL); - POSTCONDITION(*pTP != NULL); - } - CONTRACT_END; - - // Ensure remoting is started. - EnsureFieldsInitialized(); - - MethodTable * pMT = ty.GetMethodTable(); - - // Get the size of the VTable for the class to proxy - DWORD dwSlots = pMT->GetNumVirtuals(); - - if (dwSlots == 0) - dwSlots = 1; - - // The global thunk table must have been initialized - _ASSERTE(s_pThunkTable != NULL); - - // Check for the need to extend existing TP method table - if (dwSlots > GetCommitedTPSlots()) - { - CrstHolder ch(&s_TPMethodTableCrst); - - if (dwSlots > GetCommitedTPSlots()) - { - if (!ExtendCommitedSlots(dwSlots)) - COMPlusThrowOM(); - } - } - - // Create a TP Object - IfNullThrow(*pTP = (TRANSPARENTPROXYREF) AllocateObject(GetMethodTable())); - - // Create the cycle between TP and RP - (*pRP)->SetTransparentProxy(*pTP); - - // Make the TP behave as an object of supplied class - (*pTP)->SetRealProxy(*pRP); - - // If we are creating a proxy for an interface then the class - // is the object class else it is the class supplied - if (pMT->IsInterface()) - { - _ASSERTE(NULL != g_pObjectClass); - - (*pTP)->SetMethodTableBeingProxied(CRemotingServices::GetMarshalByRefClass()); - - // Set the cached interface method table to the given interface - // method table - (*pTP)->SetInterfaceMethodTable(pMT); - } - else - { - (*pTP)->SetMethodTableBeingProxied(pMT); - } - - RETURN; -} - -Signature InitMessageData(messageData *msgData, - FramedMethodFrame *pFrame, - Module **ppModule, - SigTypeContext *pTypeContext) -{ - CONTRACTL - { - THROWS; - GC_TRIGGERS; - MODE_COOPERATIVE; - PRECONDITION(CheckPointer(msgData)); - PRECONDITION(CheckPointer(pFrame)); - PRECONDITION(CheckPointer(ppModule)); - PRECONDITION(CheckPointer(pTypeContext)); - } - CONTRACTL_END; - - msgData->pFrame = pFrame; - msgData->iFlags = 0; - - MethodDesc *pMD = pFrame->GetFunction(); - _ASSERTE(!pMD->ContainsGenericVariables()); - _ASSERTE(pMD->IsRuntimeMethodHandle()); - - TypeHandle thGoverningType; - BOOL fIsDelegate = pMD->GetMethodTable()->IsDelegate(); - - // We want to calculate and store a governing type for the method since - // sometimes the parent method table might be representative. We get the - // exact type context from the this reference we're calling on (adjusting - // for the fact it's a TP). - - // But cope with the common cases first for speed: - // * If the method is not on a generic type and this is not the async - // delegate case (which requires us to unwrap the delegate and have a - // look) then we know the method desc's parent method table will be exact. - // * We require method descs to be exact for the interface case as well (since - // the target object doesn't help us resolve the interface type at all). - // * COM interop can use this code path, but that doesn't support generics so - // we can use the quick logic for that too. - if ((!pMD->HasClassInstantiation() && !fIsDelegate) || - pMD->IsInterface() || - pMD->IsComPlusCall()) - { - thGoverningType = TypeHandle(pMD->GetMethodTable()); - } - else - { - MethodDesc *pTargetMD; - MethodTable *pTargetMT; - if (fIsDelegate) - { - // Async delegates are also handled differently in that the method and the - // this are delegate wrappers round the real method and target. - pTargetMD = COMDelegate::GetMethodDesc(pFrame->GetThis()); - - // Delegates on static methods don't have a useful target instance. - // But in that case the target method is guaranteed to have exact - // type information. - if (pTargetMD->IsStatic()) - pTargetMT = pTargetMD->GetMethodTable(); - else - { - OBJECTREF refDelegateTarget = COMDelegate::GetTargetObject(pFrame->GetThis()); - pTargetMT = refDelegateTarget->GetTrueMethodTable(); - } - } - else - { - pTargetMD = pMD; - pTargetMT = CTPMethodTable::GetMethodTableBeingProxied(pFrame->GetThis()); - } - - // One last check to see if we can optimize the delegate case now we've - // unwrapped it. - if (fIsDelegate && !pTargetMD->HasClassInstantiation() && !pTargetMT->IsDelegate()) - { - thGoverningType = TypeHandle(pTargetMD->GetMethodTable()); - } - else - { - // Not quite done yet, we need to get the type that declares the method, - // which may be a superclass of the type we're calling on. - MethodTable *pDeclaringMT = pTargetMD->GetMethodTable(); - thGoverningType = ClassLoader::LoadGenericInstantiationThrowing(pDeclaringMT->GetModule(), - pDeclaringMT->GetCl(), - pTargetMD->GetExactClassInstantiation(TypeHandle(pTargetMT))); - } - } - - msgData->thGoverningType = thGoverningType; - - if (fIsDelegate) - { - DelegateEEClass* delegateCls = (DelegateEEClass*) pMD->GetMethodTable()->GetClass(); - - _ASSERTE(pFrame->GetThis()->GetMethodTable()->IsDelegate()); - - msgData->pDelegateMD = pMD; - msgData->pMethodDesc = COMDelegate::GetMethodDesc(pFrame->GetThis()); - - _ASSERTE(msgData->pMethodDesc != NULL); - _ASSERTE(!msgData->pMethodDesc->ContainsGenericVariables()); - _ASSERTE(msgData->pMethodDesc->IsRuntimeMethodHandle()); - - if (pMD == delegateCls->m_pBeginInvokeMethod) - { - msgData->iFlags |= MSGFLG_BEGININVOKE; - } - else - { - _ASSERTE(pMD == delegateCls->m_pEndInvokeMethod); - msgData->iFlags |= MSGFLG_ENDINVOKE; - } - } - else - { - msgData->pDelegateMD = NULL; - msgData->pMethodDesc = pMD; - _ASSERTE(msgData->pMethodDesc->IsRuntimeMethodHandle()); - } - - if (msgData->pMethodDesc->IsOneWay()) - { - msgData->iFlags |= MSGFLG_ONEWAY; - } - - if (msgData->pMethodDesc->IsCtor()) - { - msgData->iFlags |= MSGFLG_CTOR; - } - - Signature signature; - Module *pModule; - - if (msgData->pDelegateMD) - { - signature = msgData->pDelegateMD->GetSignature(); - pModule = msgData->pDelegateMD->GetModule(); - - // If the delegate is generic, pDelegateMD may not represent the exact instantiation so we recover it from 'this'. - SigTypeContext::InitTypeContext(pFrame->GetThis()->GetMethodTable()->GetInstantiation(), Instantiation(), pTypeContext); - } - else if (msgData->pMethodDesc->IsVarArg()) - { - VASigCookie *pVACookie = pFrame->GetVASigCookie(); - signature = pVACookie->signature; - pModule = pVACookie->pModule; - SigTypeContext::InitTypeContext(pTypeContext); - - } - else - { - signature = msgData->pMethodDesc->GetSignature(); - pModule = msgData->pMethodDesc->GetModule(); - SigTypeContext::InitTypeContext(msgData->pMethodDesc, thGoverningType, pTypeContext); - } - - *ppModule = pModule; - return signature; -} - -VOID CRealProxy::UpdateOptFlags(OBJECTREF refTP) -{ - CONTRACTL - { - GC_TRIGGERS; - MODE_COOPERATIVE; - THROWS; - } - CONTRACTL_END - - DWORD hierarchyDepth = 0; - REALPROXYREF refRP = CTPMethodTable::GetRP(refTP); - - OBJECTHANDLE hServerIdentity = (OBJECTHANDLE)refRP->GetPtrOffset(CRemotingServices::GetOffsetOfSrvIdentityInRP()); - if (hServerIdentity == NULL) - return; - - // Check if the proxy has already been marked as not equivalent. - // In which case, it can never get marked as anything else - RealProxyObject *rpTemp = (RealProxyObject *)OBJECTREFToObject(refRP); - - DWORD domainID = rpTemp->GetDomainID(); - AppDomainFromIDHolder ad((ADID)domainID, TRUE); - if (domainID == 0 || ad.IsUnloaded()) //we do not use ptr - return; // The appdomain the server belongs to, has been unloaded - ad.Release(); - DWORD optFlag = rpTemp->GetOptFlags(); - if ((optFlag & OPTIMIZATION_FLAG_INITTED) && - !(optFlag & OPTIMIZATION_FLAG_PROXY_EQUIVALENT)) - return; - - OBJECTREF refSrvIdentity = ObjectFromHandle(hServerIdentity); - // Is this a disconnected proxy ? - if (refSrvIdentity == NULL) - return; - - OBJECTREF refSrvObject = ObjectToOBJECTREF((Object *)refSrvIdentity->GetPtrOffset(CRemotingServices::GetOffsetOfTPOrObjInIdentity())); - - MethodTable *pCliMT = CTPMethodTable::GetMethodTableBeingProxied(refTP); - - BOOL bProxyQualifies = FALSE; - BOOL bCastToSharedType = FALSE; - - // Check if modules are physically the same - - // Check the inheritance hierarchy of the server object, to find the type - // that corresponds to the type the proxy is being cast to - // @TODO - If being cast to an interface, currently the proxy doesnt get marked equivalent - // @TODO - Need to check equivalency of the interface being cast to, and then reuse interface slot # on other side - LPCUTF8 szCliTypeName, szCliNameSpace; - szCliTypeName = pCliMT->GetFullyQualifiedNameInfo(&szCliNameSpace); - PREFIX_ASSUME(szCliTypeName != NULL); - - MethodTable *pSrvHierarchy = refSrvObject->GetMethodTable(); - - GCPROTECT_BEGIN(refRP); - while (pSrvHierarchy) - { - LPCUTF8 szSrvTypeName, szSrvNameSpace; - szSrvTypeName = pSrvHierarchy->GetFullyQualifiedNameInfo(&szSrvNameSpace); - PREFIX_ASSUME(szSrvNameSpace != NULL); - - if (!strcmp(szCliTypeName, szSrvTypeName) && !strcmp(szCliNameSpace, szSrvNameSpace)) - { - // Check if the types are shared. If they are, no further check neccesary - if (pSrvHierarchy == pCliMT) - { - bProxyQualifies = TRUE; - bCastToSharedType = TRUE; - } - else - { - bProxyQualifies = CRealProxy::ProxyTypeIdentityCheck(pCliMT, pSrvHierarchy); - } - break; - } - - pSrvHierarchy = pSrvHierarchy->GetParentMethodTable(); - hierarchyDepth++; - } - GCPROTECT_END(); - - optFlag = 0; - if (bProxyQualifies && hierarchyDepth < OPTIMIZATION_FLAG_DEPTH_MASK) - { - optFlag = OPTIMIZATION_FLAG_INITTED | OPTIMIZATION_FLAG_PROXY_EQUIVALENT; - if (bCastToSharedType) - optFlag |= OPTIMIZATION_FLAG_PROXY_SHARED_TYPE; - optFlag |= (hierarchyDepth & OPTIMIZATION_FLAG_DEPTH_MASK); - } - else - optFlag = OPTIMIZATION_FLAG_INITTED; - - RealProxyObject *rpUNSAFE = (RealProxyObject *)OBJECTREFToObject(refRP); - rpUNSAFE->SetOptFlags(optFlag); -} - -BOOL CRealProxy::ProxyTypeIdentityCheck(MethodTable *pCliHierarchy, MethodTable *pSrvHierarchy) -{ - CONTRACTL - { - GC_TRIGGERS; - MODE_COOPERATIVE; - THROWS; - } - CONTRACTL_END - // We have found the server side type that corresponds to the most derived type - // on client side, that the proxy is cast to - // Now do identity check on the server type hierarchy to see if there is an exact match - - BOOL bProxyQualifies = FALSE; - do - { - LPCUTF8 szCliTypeName, szCliNameSpace; - LPCUTF8 szSrvTypeName, szSrvNameSpace; - szCliTypeName = pCliHierarchy->GetFullyQualifiedNameInfo(&szCliNameSpace); - szSrvTypeName = pSrvHierarchy->GetFullyQualifiedNameInfo(&szSrvNameSpace); - PREFIX_ASSUME(szCliTypeName != NULL); - PREFIX_ASSUME(szSrvNameSpace != NULL); - - // If type names are different, there is no match - if (strcmp(szCliTypeName, szSrvTypeName) || - strcmp(szCliNameSpace, szSrvNameSpace)) - { - bProxyQualifies = FALSE; - return bProxyQualifies; - } - - PEAssembly *pClientPE = pCliHierarchy->GetAssembly()->GetManifestFile(); - PEAssembly *pServerPE = pSrvHierarchy->GetAssembly()->GetManifestFile(); - // If the PE files are different, there is no match - if (!pClientPE->Equals(pServerPE)) - { - bProxyQualifies = FALSE; - return bProxyQualifies; - } - - // If the number of interfaces implemented are different, there is no match - if (pSrvHierarchy->GetNumInterfaces() != pCliHierarchy->GetNumInterfaces()) - { - bProxyQualifies = FALSE; - return bProxyQualifies; - } - - MethodTable::InterfaceMapIterator srvItfIt = pSrvHierarchy->IterateInterfaceMap(); - MethodTable::InterfaceMapIterator cliItfIt = pCliHierarchy->IterateInterfaceMap(); - while (srvItfIt.Next()) - { - BOOL succeeded; - succeeded = cliItfIt.Next(); - CONSISTENCY_CHECK(succeeded); - if (!ProxyTypeIdentityCheck(srvItfIt.GetInterface(), cliItfIt.GetInterface())) - { - bProxyQualifies = FALSE; - return bProxyQualifies; - } - } - - pSrvHierarchy = pSrvHierarchy->GetParentMethodTable(); - pCliHierarchy = pCliHierarchy->GetParentMethodTable(); - } - while (pSrvHierarchy && pCliHierarchy); - - if (pSrvHierarchy || pCliHierarchy) - { - bProxyQualifies = FALSE; - return bProxyQualifies; - } - - bProxyQualifies = TRUE; - return bProxyQualifies; - -} - -ProfilerRemotingClientCallbackHolder::ProfilerRemotingClientCallbackHolder() -{ -#ifdef PROFILING_SUPPORTED - // If profiling is active, notify it that remoting stuff is kicking in - BEGIN_PIN_PROFILER(CORProfilerTrackRemoting()); - GCX_PREEMP(); - g_profControlBlock.pProfInterface->RemotingClientInvocationStarted(); - END_PIN_PROFILER(); -#endif // PROFILING_SUPPORTED -} - -ProfilerRemotingClientCallbackHolder::~ProfilerRemotingClientCallbackHolder() -{ -#ifdef PROFILING_SUPPORTED - // If profiling is active, tell profiler we've made the call, received the - // return value, done any processing necessary, and now remoting is done. - BEGIN_PIN_PROFILER(CORProfilerTrackRemoting()); - GCX_PREEMP(); - g_profControlBlock.pProfInterface->RemotingClientInvocationFinished(); - END_PIN_PROFILER(); -#endif // PROFILING_SUPPORTED -} - -enum -{ - CALLTYPE_INVALIDCALL = 0x0, // Important:: sync this with RealProxy.cs - CALLTYPE_METHODCALL = 0x1, // Important:: sync this with RealProxy.cs - CALLTYPE_CONSTRUCTORCALL = 0x2 // Important:: sync this with RealProxy.cs -}; - -extern "C" void STDCALL TransparentProxyStubPatch(); - -//+---------------------------------------------------------------------------- -// -// Method: TransparentProxyStubWorker -// -// Synopsis: This function gets control in two situations -// (1) When a call is made on the transparent proxy it delegates to -// PrivateInvoke method on the real proxy -// (2) When a call is made on the constructor it again delegates to the -// PrivateInvoke method on the real proxy. -// -// -//+---------------------------------------------------------------------------- -extern "C" UINT32 STDCALL TransparentProxyStubWorker(TransitionBlock * pTransitionBlock, TADDR pMethodDescOrSlot) -{ - CONTRACTL - { - THROWS; - GC_TRIGGERS; - MODE_COOPERATIVE; - ENTRY_POINT; - PRECONDITION(CheckPointer(pTransitionBlock)); - } - CONTRACTL_END; - - UINT fpRetSize = 0; - - FrameWithCookie<TPMethodFrame> frame(pTransitionBlock); - TPMethodFrame * pFrame = &frame; - - //we need to zero out the return value buffer because we will report it during GC -#ifdef ENREGISTERED_RETURNTYPE_MAXSIZE - ZeroMemory (pFrame->GetReturnValuePtr(), ENREGISTERED_RETURNTYPE_MAXSIZE); -#else - *(ARG_SLOT *)pFrame->GetReturnValuePtr() = 0; -#endif - - // For virtual calls the slot number is pushed but for - // non virtual calls/interface invoke the method descriptor is already - // pushed - MethodDesc * pMD; - if ((pMethodDescOrSlot >> 16) == 0) - { - // The frame is not completly setup at this point. - // Do not throw exceptions or provoke GC - MethodTable* pMT = CTPMethodTable::GetMethodTableBeingProxied(pFrame->GetThis()); - _ASSERTE(pMT); - - // Replace the slot number with the method descriptor on the stack - pMD = pMT->GetMethodDescForSlot((WORD)pMethodDescOrSlot); - } - else - { - pMD = dac_cast<PTR_MethodDesc>(pMethodDescOrSlot); - } - pFrame->SetFunction(pMD); - - pFrame->Push(); - - // Give debugger opportunity to stop here now that we know the MethodDesc * - TransparentProxyStubPatch(); - - INSTALL_UNWIND_AND_CONTINUE_HANDLER; - - if (g_pConfig->UseNewCrossDomainRemoting()) - { - BOOL bOptSuccess = FALSE; - CrossDomainChannel cdc; - bOptSuccess = cdc.CheckCrossDomainCall(pFrame); - if (bOptSuccess) - { - fpRetSize = cdc.GetFPReturnSize(); - goto Done; - } - } - - { - messageData msgData; - Module *pModule = NULL; - SigTypeContext inst; - Signature signature = InitMessageData(&msgData, pFrame, &pModule, &inst); - - _ASSERTE(!signature.IsEmpty() && pModule); - - // Allocate metasig on the stack - MetaSig mSig(signature, pModule, &inst); - msgData.pSig = &mSig; - - MethodDesc *pMD = pFrame->GetFunction(); - if (pMD->GetMethodTable()->IsDelegate()) - { - // check that there is only one target - if (COMDelegate::IsTrueMulticastDelegate(pFrame->GetThis())) - { - COMPlusThrow(kArgumentException, W("Remoting_Delegate_TooManyTargets")); - } - } - - { - ProfilerRemotingClientCallbackHolder profilerHolder; - - OBJECTREF pThisPointer = NULL; - - if (pMD->GetMethodTable()->IsDelegate()) - { - // this is an async call - _ASSERTE(pFrame->GetThis()->GetMethodTable()->IsDelegate()); - - pThisPointer = COMDelegate::GetTargetObject(pFrame->GetThis()); - } - else - { - pThisPointer = pFrame->GetThis(); - } - - OBJECTREF firstParameter; - MethodDesc* pTargetMD = NULL; - size_t callType = CALLTYPE_INVALIDCALL; - - // We are invoking either the constructor or a method on the object - if(pMD->IsCtor()) - { - // Get the address of PrivateInvoke in managed code - pTargetMD = CRemotingServices::MDofPrivateInvoke(); - _ASSERTE(pThisPointer->IsTransparentProxy()); - - firstParameter = CTPMethodTable::GetRP(pThisPointer); - - // Set a field to indicate that it is a constructor call - callType = CALLTYPE_CONSTRUCTORCALL; - } - else - { - // Set a field to indicate that it is a method call - callType = CALLTYPE_METHODCALL; - - if (pThisPointer->IsTransparentProxy()) - { - // Extract the real proxy underlying the transparent proxy - firstParameter = CTPMethodTable::GetRP(pThisPointer); - - // Get the address of PrivateInvoke in managed code - pTargetMD = CRemotingServices::MDofPrivateInvoke(); - _ASSERTE(pTargetMD); - } - else - { - // must be async if this is not a TP - _ASSERTE(pMD->GetMethodTable()->IsDelegate()); - firstParameter = NULL; - - // Get the address of PrivateInvoke in managed code - pTargetMD = CRemotingServices::MDofInvokeStatic(); - } - - // Go ahead and call PrivateInvoke on Real proxy. There is no need to - // catch exceptions thrown by it - // See RealProxy.cs - } - - _ASSERTE(pTargetMD); - - // Call the appropriate target - CTPMethodTable::CallTarget(pTargetMD, (LPVOID)OBJECTREFToObject(firstParameter), (LPVOID)&msgData, (LPVOID)callType); - - // Check for the need to trip thread - if (GetThread()->CatchAtSafePointOpportunistic()) - { - // There is no need to GC protect the return object as - // TPFrame is GC protecting it - CommonTripThread(); - } - } // ProfilerClientCallbackHolder - - { - mSig.Reset(); - - ArgIterator argit(&mSig); - -#ifdef _TARGET_X86_ - // Set the number of bytes to pop for x86 - pFrame->SetCbStackPop(argit.CbStackPop()); -#endif // _TARGET_X86_ - - fpRetSize = argit.GetFPReturnSize(); - } - } - -Done: ; - - pFrame->Pop(); - - UNINSTALL_UNWIND_AND_CONTINUE_HANDLER; - - return fpRetSize; -} - - -// Helper due to inability to combine SEH with anything interesting. -BOOL CTPMethodTable::CheckCastHelper(MethodDesc* pTargetMD, LPVOID pFirst, LPVOID pSecond) -{ - CONTRACTL - { - THROWS; - GC_TRIGGERS; - MODE_COOPERATIVE; - PRECONDITION(CheckPointer(pTargetMD)); - PRECONDITION(CheckPointer(pFirst, NULL_OK)); - PRECONDITION(CheckPointer(pSecond, NULL_OK)); - } - CONTRACTL_END; - - // Actual return type is a managed 'bool', so only look at a CLR_BOOL-sized - // result. The high bits are undefined on AMD64. (Note that a narrowing - // cast to CLR_BOOL will not work since it is the same as checking the - // size_t result != 0.) - LPVOID ret = CallTarget(pTargetMD, pFirst, pSecond); - return *(CLR_BOOL*)StackElemEndianessFixup(&ret, sizeof(CLR_BOOL)); -} - - - -//+---------------------------------------------------------------------------- -// -// Method: CTPMethodTable::CheckCast private -// -// Synopsis: Call the managed checkcast method to determine whether the -// server type can be cast to the given type -// -// -// -//+---------------------------------------------------------------------------- -BOOL CTPMethodTable::CheckCast(MethodDesc* pTargetMD, TRANSPARENTPROXYREF orTP, TypeHandle ty) -{ - CONTRACTL - { - THROWS; - GC_TRIGGERS; - MODE_COOPERATIVE; - PRECONDITION(CheckPointer(pTargetMD)); - PRECONDITION(orTP != NULL); - PRECONDITION(!ty.IsNull()); - } - CONTRACTL_END; - - REFLECTCLASSBASEREF reflectType = NULL; - LPVOID pvType = NULL; - BOOL fCastOK = FALSE; - - typedef struct _GCStruct - { - TRANSPARENTPROXYREF orTP; - REALPROXYREF orRP; - } GCStruct; - - GCStruct gcValues; - gcValues.orTP = orTP; - gcValues.orRP = GetRP(orTP); - - GCPROTECT_BEGIN (gcValues); - - reflectType = (REFLECTCLASSBASEREF) ty.GetMethodTable()->GetManagedClassObject(); - *(REFLECTCLASSBASEREF *)&pvType = reflectType; - - fCastOK = CheckCastHelper(pTargetMD, - (LPVOID)OBJECTREFToObject(gcValues.orRP), - pvType); - - if (fCastOK) - { - _ASSERTE(s_fTPTableFieldsInitialized); - - // The cast succeeded. Replace the current type in the proxy - // with the given type. - - CrstHolder ch(&s_TPMethodTableCrst); - - if (ty.IsInterface()) - { - // We replace the cached interface method table with the interface - // method table that we are trying to cast to. This will ensure that - // casts to this interface, which are likely to happen, will succeed. - gcValues.orTP->SetInterfaceMethodTable(ty.GetMethodTable()); - } - else - { - MethodTable *pCurrent = gcValues.orTP->GetMethodTableBeingProxied(); - - BOOL fDerivedClass = FALSE; - // Check whether this class derives from the current class - fDerivedClass = CRemotingServices::CheckCast(gcValues.orTP, ty, - TypeHandle(pCurrent)); - // We replace the current method table only if we cast to a more - // derived class - if (fDerivedClass) - { - // Set the method table in the proxy to the given method table - RefineProxy(gcValues.orTP, ty); - } - } - } - - GCPROTECT_END(); - return fCastOK; -} - -//+---------------------------------------------------------------------------- -// -// Method: CTPMethodTable::RefineProxy public -// -// Synopsis: Set the method table in the proxy to the given class' method table. -// Additionally, expand the TP method table to the required number of slots. -// -// -//+---------------------------------------------------------------------------- -void CTPMethodTable::RefineProxy(TRANSPARENTPROXYREF orTP, TypeHandle ty) -{ - CONTRACTL - { - THROWS; - GC_TRIGGERS; - MODE_COOPERATIVE; - INJECT_FAULT(COMPlusThrowOM()); - PRECONDITION(orTP != NULL); - PRECONDITION(!ty.IsNull()); - } - CONTRACTL_END; - - // Do the expansion only if necessary - MethodTable *pMT = ty.GetMethodTable(); - - if (pMT != orTP->GetMethodTableBeingProxied()) - { - orTP->SetMethodTableBeingProxied(pMT); - - // Extend the vtable if necessary - DWORD dwSlots = pMT->GetNumVirtuals(); - - if (dwSlots == 0) - dwSlots = 1; - - if((dwSlots > GetCommitedTPSlots()) && !ExtendCommitedSlots(dwSlots)) - { - // We failed to extend the committed slots. Out of memory. - COMPlusThrowOM(); - } - - } -} - -#ifndef HAS_REMOTING_PRECODE -//+---------------------------------------------------------------------------- -// -// Method: CTPMethodTable::GetOrCreateNonVirtualSlotForVirtualMethod private -// -// Synopsis: Get a slot for a non-virtual call to a virtual method. -// -//+---------------------------------------------------------------------------- -PTR_PCODE CTPMethodTable::GetOrCreateNonVirtualSlotForVirtualMethod(MethodDesc* pMD) -{ - CONTRACT (PTR_PCODE) - { - THROWS; - GC_TRIGGERS; - MODE_ANY; - PRECONDITION(CheckPointer(pMD)); - PRECONDITION(pMD->IsRemotingInterceptedViaVirtualDispatch()); - POSTCONDITION(CheckPointer(RETVAL)); - } - CONTRACT_END; - - // Ensure the TP MethodTable's fields have been initialized. - EnsureFieldsInitialized(); - - PTR_PCODE pSlot; - - { - // Create the thunk in a thread safe manner - CrstHolder ch(&s_TPMethodTableCrst); - - // NOTE: CNonVirtualThunk::SetNonVirtualThunks() depends on the lock being initialized - CNonVirtualThunk::InitializeListLock(); - - // Create hash table if we do not have one yet - if (s_pThunkHashTable == NULL) - { - NewHolder <EEThunkHashTable> pTempHash(new EEThunkHashTable()); - - LockOwner lock = {&s_TPMethodTableCrst, IsOwnerOfCrst}; - IfNullThrow(pTempHash->Init(23,&lock)); - - s_pThunkHashTable = pTempHash.Extract(); - } - - if (!s_pThunkHashTable->GetValue(pMD, (HashDatum *)&pSlot)) - { - PCODE pThunkCode = CreateNonVirtualThunkForVirtualMethod(pMD); - - _ASSERTE(CNonVirtualThunkMgr::IsThunkByASM(pThunkCode)); - _ASSERTE(CNonVirtualThunkMgr::GetMethodDescByASM(pThunkCode)); - - // Set the generated thunk once and for all.. - CNonVirtualThunk *pThunk = CNonVirtualThunk::SetNonVirtualThunks((BYTE*)pThunkCode); - - // Remember the thunk address in a hash table - // so that we dont generate it again - pSlot = (PTR_PCODE)pThunk->GetAddrOfCode(); - s_pThunkHashTable->InsertValue(pMD, (HashDatum)pSlot); - } - } - - RETURN pSlot; -} - -//+---------------------------------------------------------------------------- -// -// Method: CTPMethodTable::DestroyThunk public -// -// Synopsis: Destroy the thunk for the non virtual method. -// -// -//+---------------------------------------------------------------------------- -void CTPMethodTable::DestroyThunk(MethodDesc* pMD) -{ - CONTRACTL - { - NOTHROW; - GC_TRIGGERS; - MODE_ANY; - PRECONDITION(CheckPointer(pMD)); - } - CONTRACTL_END; - - if(s_pThunkHashTable) - { - CrstHolder ch(&s_TPMethodTableCrst); - - LPVOID pvCode = NULL; - s_pThunkHashTable->GetValue(pMD, (HashDatum *)&pvCode); - CNonVirtualThunk *pThunk = NULL; - if(NULL != pvCode) - { - pThunk = CNonVirtualThunk::AddrToThunk(pvCode); - delete pThunk; - s_pThunkHashTable->DeleteValue(pMD); - } - } -} -#endif // HAS_REMOTING_PRECODE - -static LPVOID CallTargetWorker1(MethodDesc* pTargetMD, - LPVOID pvFirst, - LPVOID pvSecond) -{ - STATIC_CONTRACT_THROWS; - STATIC_CONTRACT_GC_TRIGGERS; - STATIC_CONTRACT_MODE_COOPERATIVE; - STATIC_CONTRACT_SO_INTOLERANT; - - LPVOID ret = NULL; - PCODE pTarget = pTargetMD->GetSingleCallableAddrOfCode(); - -#if defined(DEBUGGING_SUPPORTED) - if (CORDebuggerTraceCall()) - { - g_pDebugInterface->TraceCall((const BYTE*)pTarget); - } -#endif // DEBUGGING_SUPPORTED - - - BEGIN_CALL_TO_MANAGED(); - - ret = CTPMethodTable__CallTargetHelper2((const BYTE*)pTarget, pvFirst, pvSecond); - - END_CALL_TO_MANAGED(); - - return ret; -} - - -//+---------------------------------------------------------------------------- -// -// Method: CTPMethodTable::CallTarget private -// -// Synopsis: Calls the target method on the given object -// -//+---------------------------------------------------------------------------- -LPVOID __stdcall CTPMethodTable::CallTarget (MethodDesc* pTargetMD, - LPVOID pvFirst, - LPVOID pvSecond) -{ - CONTRACTL - { - THROWS; - GC_TRIGGERS; - MODE_COOPERATIVE; - SO_INTOLERANT; - PRECONDITION(CheckPointer(pTargetMD)); - PRECONDITION(CheckPointer(pvFirst, NULL_OK)); - PRECONDITION(CheckPointer(pvSecond, NULL_OK)); - } - CONTRACTL_END; - -#ifdef _DEBUG - - Thread* curThread = GetThread(); - - Object* ObjRefTable[OBJREF_TABSIZE]; - - if (curThread) - memcpy(ObjRefTable, curThread->dangerousObjRefs, sizeof(curThread->dangerousObjRefs)); - -#endif // _DEBUG - - LPVOID ret = CallTargetWorker1(pTargetMD, pvFirst, pvSecond); - -#ifdef _DEBUG - // Restore dangerousObjRefs when we return back to EE after call - if (curThread) - memcpy(curThread->dangerousObjRefs, ObjRefTable, sizeof(curThread->dangerousObjRefs)); - - ENABLESTRESSHEAP (); -#endif // _DEBUG - - return ret; -} - - -static LPVOID CallTargetWorker2(MethodDesc* pTargetMD, - LPVOID pvFirst, - LPVOID pvSecond, - LPVOID pvThird) -{ - STATIC_CONTRACT_THROWS; - STATIC_CONTRACT_GC_TRIGGERS; - STATIC_CONTRACT_MODE_COOPERATIVE; - STATIC_CONTRACT_SO_INTOLERANT; - - LPVOID ret = NULL; - PCODE pTarget = pTargetMD->GetSingleCallableAddrOfCode(); - -#if defined(DEBUGGING_SUPPORTED) - if (CORDebuggerTraceCall()) - { - g_pDebugInterface->TraceCall((const BYTE*)pTarget); - } -#endif // DEBUGGING_SUPPORTED - - BEGIN_CALL_TO_MANAGED(); - - ret = CTPMethodTable__CallTargetHelper3((const BYTE*)pTarget, pvFirst, pvSecond, pvThird); - - END_CALL_TO_MANAGED(); - return ret; - -} - -LPVOID __stdcall CTPMethodTable::CallTarget (MethodDesc* pTargetMD, - LPVOID pvFirst, - LPVOID pvSecond, - LPVOID pvThird) -{ - CONTRACTL - { - THROWS; - GC_TRIGGERS; - MODE_COOPERATIVE; - SO_INTOLERANT; - PRECONDITION(CheckPointer(pTargetMD)); - PRECONDITION(CheckPointer(pvFirst, NULL_OK)); - PRECONDITION(CheckPointer(pvSecond, NULL_OK)); - PRECONDITION(CheckPointer(pvThird, NULL_OK)); - } - CONTRACTL_END; - -#ifdef _DEBUG - Thread* curThread = GetThread(); - - Object* ObjRefTable[OBJREF_TABSIZE]; - if (curThread) - memcpy(ObjRefTable, curThread->dangerousObjRefs, sizeof(curThread->dangerousObjRefs)); - -#endif // _DEBUG - - LPVOID ret = CallTargetWorker2(pTargetMD, pvFirst, pvSecond, pvThird); - -#ifdef _DEBUG - // Restore dangerousObjRefs when we return back to EE after call - if (curThread) - memcpy(curThread->dangerousObjRefs, ObjRefTable, sizeof(curThread->dangerousObjRefs)); - - ENABLESTRESSHEAP (); -#endif // _DEBUG - - return ret; -} - - -#ifndef HAS_REMOTING_PRECODE -//+---------------------------------------------------------------------------- -// -// Method: CNonVirtualThunk::SetNextThunk public -// -// Synopsis: Creates a thunk for the given address and adds it to the global -// list -// -//+---------------------------------------------------------------------------- -CNonVirtualThunk* CNonVirtualThunk::SetNonVirtualThunks(const BYTE* pbCode) -{ - CONTRACT (CNonVirtualThunk*) - { - THROWS; - GC_TRIGGERS; - MODE_ANY; - INJECT_FAULT(COMPlusThrowOM()); - PRECONDITION(CheckPointer(pbCode)); - POSTCONDITION(CheckPointer(RETVAL)); - } - CONTRACT_END; - - CNonVirtualThunk *pThunk = new CNonVirtualThunk(pbCode); - - // Put the generated thunk in a global list - // Note: this is called when a NV thunk is being created .. - // The TPMethodTable critsec is held at this point - pThunk->SetNextThunk(); - - // Set up the stub manager if necessary - CNonVirtualThunkMgr::InitNonVirtualThunkManager(); - - RETURN pThunk; -} - -//+---------------------------------------------------------------------------- -// -// Method: CNonVirtualThunk::~CNonVirtualThunk public -// -// Synopsis: Deletes the thunk from the global list of thunks -// -// -//+---------------------------------------------------------------------------- -CNonVirtualThunk::~CNonVirtualThunk() -{ - CONTRACTL - { - NOTHROW; - GC_NOTRIGGER; - MODE_ANY; - PRECONDITION(CheckPointer(s_pNonVirtualThunks)); - } - CONTRACTL_END; - - CNonVirtualThunk* pCurr = s_pNonVirtualThunks; - CNonVirtualThunk* pPrev = NULL; - BOOL found = FALSE; - - // Note: This is called with the TPMethodTable critsec held - while(!found && (NULL != pCurr)) - { - if(pCurr == this) - { - found = TRUE; - SimpleRWLock::SimpleWriteLockHolder swlh(s_pNonVirtualThunksListLock); - - // Unlink from the chain - if(NULL != pPrev) - { - pPrev->_pNext = pCurr->_pNext; - } - else - { - // First entry needs to be deleted - s_pNonVirtualThunks = pCurr->_pNext; - } - } - pPrev = pCurr; - pCurr = pCurr->_pNext; - } - - _ASSERTE(found); -} -#endif // HAS_REMOTING_PRECODE - -//+---------------------------------------------------------------------------- -// -// Method: CVirtualThunkMgr::InitVirtualThunkManager public -// -// Synopsis: Adds the stub manager to aid debugger in stepping into calls -// -// -//+---------------------------------------------------------------------------- -void CVirtualThunkMgr::InitVirtualThunkManager() -{ - CONTRACTL - { - THROWS; - GC_TRIGGERS; - MODE_ANY; - INJECT_FAULT(COMPlusThrowOM()); - } - CONTRACTL_END; - - // This is function is already threadsafe since this method is called from within a - // critical section - if(NULL == s_pVirtualThunkMgr) - { - // Add the stub manager for vtable calls - s_pVirtualThunkMgr = new CVirtualThunkMgr(); - - StubManager::AddStubManager(s_pVirtualThunkMgr); - } - -} - -#endif // !DACCESS_COMPILE - -//+---------------------------------------------------------------------------- -// -// Method: CVirtualThunkMgr::CheckIsStub_Internal public -// -// Synopsis: Returns TRUE if the given address is the starting address of -// the transparent proxy stub -// -//+---------------------------------------------------------------------------- -BOOL CVirtualThunkMgr::CheckIsStub_Internal(PCODE stubStartAddress) -{ - CONTRACTL - { - NOTHROW; - GC_NOTRIGGER; - MODE_ANY; - SUPPORTS_DAC; - } - CONTRACTL_END; - - BOOL bIsStub = FALSE; - -#ifndef DACCESS_COMPILE - if (!IsThunkByASM(stubStartAddress)) - return FALSE; - if(NULL != FindThunk((const BYTE *) stubStartAddress)) - bIsStub = TRUE; -#endif // !DACCESS_COMPILE - - return bIsStub; -} - -#ifndef DACCESS_COMPILE - -//+---------------------------------------------------------------------------- -// -// Method: CVirtualThunkMgr::Entry2MethodDesc public -// -// Synopsis: Convert a starting address to a MethodDesc -// -//+---------------------------------------------------------------------------- -MethodDesc *CVirtualThunkMgr::Entry2MethodDesc(PCODE StubStartAddress, MethodTable *pMT) -{ - CONTRACT (MethodDesc*) - { - NOTHROW; - GC_NOTRIGGER; - MODE_ANY; - PRECONDITION(CheckPointer(pMT, NULL_OK)); - POSTCONDITION(CheckPointer(RETVAL, NULL_OK)); - } - CONTRACT_END; - - if (s_pVirtualThunkMgr == NULL) - RETURN NULL; - - if (!pMT) - RETURN NULL; - - if (!s_pVirtualThunkMgr->CheckIsStub_Internal(StubStartAddress)) - RETURN NULL; - - RETURN GetMethodDescByASM(StubStartAddress, pMT); -} - -//+---------------------------------------------------------------------------- -// -// Method: CVirtualThunkMgr::FindThunk private -// -// Synopsis: Finds a thunk that matches the given starting address -// -//+---------------------------------------------------------------------------- -LPBYTE CVirtualThunkMgr::FindThunk(const BYTE *stubStartAddress) -{ - CONTRACT (LPBYTE) - { - NOTHROW; - GC_NOTRIGGER; - MODE_ANY; - PRECONDITION(CheckPointer(stubStartAddress, NULL_OK)); - POSTCONDITION(CheckPointer(RETVAL, NULL_OK)); - SO_TOLERANT; - } - CONTRACT_END; - - CVirtualThunks* pThunks = CVirtualThunks::GetVirtualThunks(); - LPBYTE pThunkAddr = NULL; - - while(NULL != pThunks) - { - DWORD dwStartSlot = pThunks->_dwStartThunk; - DWORD dwCurrSlot = pThunks->_dwStartThunk; - DWORD dwMaxSlot = pThunks->_dwCurrentThunk; - while (dwCurrSlot < dwMaxSlot) - { - LPBYTE pStartAddr = pThunks->ThunkCode[dwCurrSlot-dwStartSlot].pCode; - if((stubStartAddress >= pStartAddr) && - (stubStartAddress < (pStartAddr + ConstVirtualThunkSize))) - { - pThunkAddr = pStartAddr; - break; - } - ++dwCurrSlot; - } - - pThunks = pThunks->GetNextThunk(); - } - - RETURN pThunkAddr; -} - -#endif // !DACCESS_COMPILE - -#ifndef HAS_REMOTING_PRECODE - -#ifndef DACCESS_COMPILE - -//+---------------------------------------------------------------------------- -// -// Method: CNonVirtualThunkMgr::InitNonVirtualThunkManager public -// -// Synopsis: Adds the stub manager to aid debugger in stepping into calls -// -// -//+---------------------------------------------------------------------------- -void CNonVirtualThunkMgr::InitNonVirtualThunkManager() -{ - CONTRACTL - { - THROWS; - GC_TRIGGERS; - MODE_ANY; - INJECT_FAULT(COMPlusThrowOM()); - } - CONTRACTL_END; - - // This function is already thread safe since this method is called from within a - // critical section - if(NULL == s_pNonVirtualThunkMgr) - { - // Add the stub manager for non vtable calls - s_pNonVirtualThunkMgr = new CNonVirtualThunkMgr(); - - StubManager::AddStubManager(s_pNonVirtualThunkMgr); - } -} - -#endif // !DACCESS_COMPILE - -//+---------------------------------------------------------------------------- -// -// Method: CNonVirtualThunkMgr::CheckIsStub_Internal public -// -// Synopsis: Returns TRUE if the given address is the starting address of -// one of our thunks -// -//+---------------------------------------------------------------------------- -BOOL CNonVirtualThunkMgr::CheckIsStub_Internal(PCODE stubStartAddress) -{ - WRAPPER_NO_CONTRACT; - - BOOL bIsStub = FALSE; - -#ifndef DACCESS_COMPILE - if (!IsThunkByASM(stubStartAddress)) - return FALSE; - if(NULL != FindThunk((const BYTE *) stubStartAddress)) - bIsStub = TRUE; -#endif // !DACCESS_COMPILE - - return bIsStub; -} - -#ifndef DACCESS_COMPILE - -//+---------------------------------------------------------------------------- -// -// Method: CNonVirtualThunkMgr::Entry2MethodDesc public -// -// Synopsis: Convert a starting address to a MethodDesc -// -//+---------------------------------------------------------------------------- -MethodDesc *CNonVirtualThunkMgr::Entry2MethodDesc(PCODE StubStartAddress, MethodTable *pMT) -{ - CONTRACT (MethodDesc*) - { - NOTHROW; - GC_NOTRIGGER; - MODE_ANY; - PRECONDITION(CheckPointer(pMT, NULL_OK)); - POSTCONDITION(CheckPointer(RETVAL, NULL_OK)); - } - CONTRACT_END; - - if (s_pNonVirtualThunkMgr == NULL) - RETURN NULL; - - if (!s_pNonVirtualThunkMgr->CheckIsStub_Internal(StubStartAddress)) - RETURN NULL; - - RETURN GetMethodDescByASM(StubStartAddress); -} - -//+---------------------------------------------------------------------------- -// -// Method: CNonVirtualThunkMgr::FindThunk private -// -// Synopsis: Finds a thunk that matches the given starting address -// -//+---------------------------------------------------------------------------- -CNonVirtualThunk* CNonVirtualThunkMgr::FindThunk(const BYTE *stubStartAddress) -{ - CONTRACT (CNonVirtualThunk*) - { - NOTHROW; - GC_NOTRIGGER; - MODE_ANY; - PRECONDITION(CheckPointer(stubStartAddress, NULL_OK)); - POSTCONDITION(CheckPointer(RETVAL, NULL_OK)); - SO_TOLERANT; - } - CONTRACT_END; - - SimpleRWLock::SimpleReadLockHolder srlh(CNonVirtualThunk::GetThunksListLock()); - CNonVirtualThunk* pThunk = CNonVirtualThunk::GetNonVirtualThunks(); - - while(NULL != pThunk) - { - if(stubStartAddress == pThunk->GetThunkCode()) - break; - - pThunk = pThunk->GetNextThunk(); - } - - RETURN pThunk; -} - -#endif // !DACCESS_COMPILE - -#endif // HAS_REMOTING_PRECODE - - -#ifndef DACCESS_COMPILE - -//+---------------------------------------------------------------------------- -//+- HRESULT MethodDescDispatchHelper(MethodDesc* pMD, ARG_SLOT[] args, ARG_SLOT *pret) -//+---------------------------------------------------------------------------- -HRESULT MethodDescDispatchHelper(MethodDescCallSite* pMethodCallSite, ARG_SLOT args[], ARG_SLOT *pret) -{ - CONTRACTL - { - NOTHROW; - GC_TRIGGERS; - MODE_COOPERATIVE; - PRECONDITION(CheckPointer(pMethodCallSite)); - PRECONDITION(CheckPointer(args)); - PRECONDITION(CheckPointer(pret)); - } - CONTRACTL_END; - - HRESULT hr = S_OK; - EX_TRY - { - *pret = pMethodCallSite->Call_RetArgSlot(args); - } - EX_CATCH_HRESULT(hr); - - return hr; -} - - -#ifdef FEATURE_COMINTEROP - -//+---------------------------------------------------------------------------- -// -// Method: VOID CRemotingServices::CallSetDCOMProxy(OBJECTREF realProxy, IUnknown* pUnk) -// -//+---------------------------------------------------------------------------- - -VOID CRemotingServices::CallSetDCOMProxy(OBJECTREF realProxy, IUnknown* pUnk) -{ - CONTRACTL - { - THROWS; - GC_TRIGGERS; - MODE_COOPERATIVE; - PRECONDITION(realProxy != NULL); - PRECONDITION(CheckPointer(pUnk, NULL_OK)); - } - CONTRACTL_END; - - GCPROTECT_BEGIN(realProxy); - - MethodDescCallSite setDCOMProxy(METHOD__REAL_PROXY__SETDCOMPROXY, &realProxy); - - ARG_SLOT args[] = - { - ObjToArgSlot(realProxy), - (ARG_SLOT)pUnk - }; - - ARG_SLOT ret; - MethodDescDispatchHelper(&setDCOMProxy, args, &ret); - - GCPROTECT_END(); -} - - -BOOL CRemotingServices::CallSupportsInterface(OBJECTREF realProxy, REFIID iid, ARG_SLOT* pret) -{ - CONTRACTL - { - THROWS; - GC_TRIGGERS; - MODE_COOPERATIVE; - PRECONDITION(realProxy != NULL); - PRECONDITION(CheckPointer(pret)); - } - CONTRACTL_END; - - BOOL fResult = TRUE; - - GCPROTECT_BEGIN(realProxy); - - MethodDescCallSite supportsInterface(METHOD__REAL_PROXY__SUPPORTSINTERFACE, &realProxy); - - ARG_SLOT args[] = - { - ObjToArgSlot(realProxy), - (ARG_SLOT)&iid - }; - - HRESULT hr = MethodDescDispatchHelper(&supportsInterface, args, pret); - - // It is allowed for the managed code to return a NULL interface pointer without returning - // a failure HRESULT. This is done for performance to avoid having to throw an exception. - // If this occurs, we need to return E_NOINTERFACE. - if ((*(IUnknown**)pret) == NULL) - hr = E_NOINTERFACE; - - if (FAILED(hr)) - fResult = FALSE; - - GCPROTECT_END(); - return fResult; -} -#endif // FEATURE_COMINTEROP - -//+---------------------------------------------------------------------------- -// -// Method: CRemotingServices::GetStubForInterfaceMethod -// -// Synopsis: Given the exact interface method we wish to invoke on, return -// the entry point of a stub that will correctly transition into -// the remoting system passing it this method. -// The stubs is just another kind of precode. They are cached -// in per appdomain hash. -// -// -//+---------------------------------------------------------------------------- -PCODE CRemotingServices::GetStubForInterfaceMethod(MethodDesc *pItfMD) -{ - CONTRACTL - { - THROWS; - GC_TRIGGERS; - MODE_ANY; - PRECONDITION(CheckPointer(pItfMD)); - PRECONDITION(pItfMD->IsInterface() && !pItfMD->IsStatic()); - } - CONTRACTL_END; - - return pItfMD->GetLoaderAllocator()->GetFuncPtrStubs()->GetFuncPtrStub(pItfMD, PRECODE_STUB); -} - -#endif // !DACCESS_COMPILE -#endif // FEATURE_REMOTING |