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