//
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license 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);
// : This can cause a GC. We need some way to protect the variant
// data
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
// FUTURE: Put this in the reflection data structure cache TarunA 11/26/00
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
// This cast to Int32 actually causes a potential loss
// of data.
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::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 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(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;
}
}
//
// Check for the avialability of a TP method table that is no longer being
// reused
// 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 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(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 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