diff options
Diffstat (limited to 'src/vm/objectclone.cpp')
-rw-r--r-- | src/vm/objectclone.cpp | 3865 |
1 files changed, 0 insertions, 3865 deletions
diff --git a/src/vm/objectclone.cpp b/src/vm/objectclone.cpp deleted file mode 100644 index b4ad314165..0000000000 --- a/src/vm/objectclone.cpp +++ /dev/null @@ -1,3865 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. -// -// File: ObjectClone.cpp -// - -// - - -#include "common.h" - -#ifdef FEATURE_REMOTING -#include "objectclone.h" -#include "frames.h" -#include "assembly.hpp" -#include "field.h" -#include "security.h" -#include "virtualcallstub.h" -#include "crossdomaincalls.h" -#include "callhelpers.h" -#include "jitinterface.h" -#include "typestring.h" -#include "typeparse.h" -#include "runtimehandles.h" -#include "appdomain.inl" - -// Define the following to re-enable object cloner strict mode (where we require source fields for non-optional destination fields -// and don't attempt to load assemblies we can't find via display via partial names instead). -//#define OBJECT_CLONER_STRICT_MODE - -void MakeIDeserializationCallback(OBJECTREF refTarget); - -MethodDesc *GetInterfaceMethodImpl(MethodTable *pMT, MethodTable *pItfMT, WORD wSlot) -{ - CONTRACTL { - THROWS; - GC_TRIGGERS; - } CONTRACTL_END; - - MethodDesc *pMeth = NULL; - DispatchSlot slot(pMT->FindDispatchSlot(pItfMT->GetTypeID(), (UINT32)wSlot)); - CONSISTENCY_CHECK(!slot.IsNull()); - pMeth = slot.GetMethodDesc(); - return pMeth; -} - -// Given a FieldDesc which may be representative and an object which contains said field, return the actual type of the field. This -// works even when called from a different appdomain from which the type was loaded (though naturally it is the caller's -// responsbility to ensure such an appdomain cannot be unloaded during the processing of this method). -TypeHandle LoadExactFieldType(FieldDesc *pFD, OBJECTREF orefParent, AppDomain *pDomain) -{ - CONTRACTL { - THROWS; - GC_TRIGGERS; - MODE_COOPERATIVE; - } CONTRACTL_END; - - MethodTable *pEnclosingMT = orefParent->GetMethodTable(); - - // Set up a field signature with the owning type providing a type context for any type variables. - MetaSig sig(pFD, TypeHandle(pEnclosingMT)); - sig.NextArg(); - - // If the enclosing type is resident to this domain or domain neutral and loaded in this domain then we can simply go get it. - // The logic is trickier (and more expensive to calculate) for generic types, so skip the optimization there. - if (pEnclosingMT->GetDomain() == GetAppDomain() || - (pEnclosingMT->IsDomainNeutral() && - !pEnclosingMT->HasInstantiation() && - pEnclosingMT->GetAssembly()->FindDomainAssembly(GetAppDomain()))) - return sig.GetLastTypeHandleThrowing(); - - TypeHandle retTH; - - // Otherwise we have to do this the expensive way -- switch to the home domain for the type lookup. - ENTER_DOMAIN_PTR(pDomain, ADV_RUNNINGIN); - retTH = sig.GetLastTypeHandleThrowing(); - END_DOMAIN_TRANSITION; - - return retTH; -} - -extern TypeHandle GetTypeByName( _In_opt_z_ LPUTF8 szFullClassName, - BOOL bThrowOnError, - BOOL bIgnoreCase, - StackCrawlMark *stackMark, - BOOL *pbAssemblyIsLoading); - -#ifndef DACCESS_COMPILE -#define CUSTOM_GCPROTECT_BEGIN(context) do { \ - FrameWithCookie<GCSafeCollectionFrame> __gcframe(context); \ - /* work around unreachable code warning */ \ - if (true) { DEBUG_ASSURE_NO_RETURN_BEGIN(GCPROTECT) - -#define CUSTOM_GCPROTECT_END() \ - DEBUG_ASSURE_NO_RETURN_END(GCPROTECT) } \ - __gcframe.Pop(); } while(0) - -#else // #ifndef DACCESS_COMPILE - -#define CUSTOM_GCPROTECT_BEGIN(context) -#define CUSTOM_GCPROTECT_END() - -#endif // #ifndef DACCESS_COMPILE - -int GCSafeObjectHashTable::HasID(OBJECTREF refObj, OBJECTREF *newObj) -{ - CONTRACTL - { - MODE_COOPERATIVE; - THROWS; - GC_NOTRIGGER; - } - CONTRACTL_END - - BOOL seenBefore = FALSE; - *newObj = NULL; - int index = FindElement(refObj, seenBefore); - - if (seenBefore) - { - _ASSERTE(index < (int)m_currArraySize); - *newObj = m_newObjects[index]; - return m_ids[index]; - } - - return -1; -} - -// returns the object id -int GCSafeObjectHashTable::AddObject(OBJECTREF refObj, OBJECTREF newObj) -{ - CONTRACTL - { - MODE_COOPERATIVE; - THROWS; - GC_NOTRIGGER; - } - CONTRACTL_END - - int index = -1; - GCPROTECT_BEGIN(refObj); - GCPROTECT_BEGIN(newObj); - - if (m_count > m_currArraySize / 2) - { - Resize(); - } - - BOOL seenBefore = FALSE; - index = FindElement(refObj, seenBefore); - - _ASSERTE(index >= 0 && index < (int)m_currArraySize); - if (seenBefore) - { - _ASSERTE(!"Adding an object thats already present"); - } - else - { - m_objects[index] = refObj; - m_newObjects[index] = newObj; - m_ids[index] = ++m_count; - } - - GCPROTECT_END(); - GCPROTECT_END(); - - return m_ids[index]; -} - -// returns the object id -int GCSafeObjectHashTable::UpdateObject(OBJECTREF refObj, OBJECTREF newObj) -{ - CONTRACTL - { - MODE_COOPERATIVE; - THROWS; - GC_NOTRIGGER; - } - CONTRACTL_END - - int index = -1; - GCPROTECT_BEGIN(refObj); - GCPROTECT_BEGIN(newObj); - - BOOL seenBefore = FALSE; - index = FindElement(refObj, seenBefore); - - _ASSERTE(index >= 0 && index < (int)m_currArraySize); - if (!seenBefore) - { - _ASSERTE(!"An object has to exist in the table, to update it"); - } - else - { - _ASSERTE(m_objects[index] == refObj); - m_newObjects[index] = newObj; - } - - GCPROTECT_END(); - GCPROTECT_END(); - - return m_ids[index]; -} - -// returns index into array where obj was found or will fit in -int GCSafeObjectHashTable::FindElement(OBJECTREF refObj, BOOL &seenBefore) -{ - CONTRACTL - { - MODE_COOPERATIVE; - THROWS; - GC_NOTRIGGER; - } - CONTRACTL_END - - int currentNumBuckets = m_currArraySize / NUM_SLOTS_PER_BUCKET; - int hashcode = 0; - GCPROTECT_BEGIN(refObj); - hashcode = refObj->GetHashCodeEx(); - GCPROTECT_END(); - - hashcode &= 0x7FFFFFFF; // ignore sign bit - int hashIncrement = (1+((hashcode)%(currentNumBuckets-2))); -#ifdef _DEBUG - int numLoops = 0; -#endif - - do - { - int index = ((unsigned)hashcode % currentNumBuckets) * NUM_SLOTS_PER_BUCKET; - _ASSERTE(index >= 0 && index < (int)m_currArraySize); - for (int i = index; i < index + NUM_SLOTS_PER_BUCKET; i++) - { - if (m_objects[i] == refObj) - { - seenBefore = TRUE; - return i; - } - - if (m_objects[i] == NULL) - { - seenBefore = FALSE; - return i; - } - } - hashcode += hashIncrement; -#ifdef _DEBUG - if (++numLoops > currentNumBuckets) - _ASSERTE(!"Looped too many times, trying to find object in hashtable. If hitting ignore doesnt seem to help, then contact Ashok"); -#endif - }while (true); - - _ASSERTE(!"Not expected to reach here in GCSafeObjectHashTable::FindElement"); - return -1; -} - -void GCSafeObjectHashTable::Resize() -{ - CONTRACTL - { - MODE_COOPERATIVE; - THROWS; - GC_NOTRIGGER; - } - CONTRACTL_END - // Allocate new space - DWORD newSize = m_currArraySize * 2; - for (int i = 0; (DWORD) i < sizeof(g_rgPrimes)/sizeof(DWORD); i++) - { - if (g_rgPrimes[i] > newSize) - { - newSize = g_rgPrimes[i]; - break; - } - } - - newSize *= NUM_SLOTS_PER_BUCKET; - NewArrayHolder<OBJECTREF> refTemp (new OBJECTREF[newSize]); - ZeroMemory((void *)refTemp, sizeof(OBJECTREF) * newSize); - - NewArrayHolder<OBJECTREF> refTempNewObj (new OBJECTREF[newSize]); -#ifdef USE_CHECKED_OBJECTREFS - ZeroMemory((void *)refTempNewObj, sizeof(OBJECTREF) * newSize); -#endif - - NewArrayHolder<int> bTemp (new int[newSize]); - ZeroMemory((void *)bTemp, sizeof(int) * newSize); - - // Copy over objects and data - NewArrayHolder<OBJECTREF> refOldObj (m_objects); - NewArrayHolder<OBJECTREF> refOldNewObj (m_newObjects); - NewArrayHolder<int> oldIds (m_ids); - DWORD oldArrSize = m_currArraySize; - - if (oldIds == (int *)&m_dataOnStack[0]) - { - refOldObj.SuppressRelease(); - refOldNewObj.SuppressRelease(); - oldIds.SuppressRelease(); - } - - refTemp.SuppressRelease(); - refTempNewObj.SuppressRelease(); - bTemp.SuppressRelease(); - - m_ids = bTemp; - m_objects = refTemp; - m_newObjects = refTempNewObj; - m_currArraySize = newSize; - - for (DWORD i = 0; i < oldArrSize; i++) - { - if (refOldObj[i] == NULL) - continue; - - BOOL seenBefore = FALSE; - int newIndex = FindElement(refOldObj[i], seenBefore); - - if (!seenBefore) - { - _ASSERTE(newIndex < (int)m_currArraySize); - m_objects[newIndex] = refOldObj[i]; - m_newObjects[newIndex] = refOldNewObj[i]; - m_ids[newIndex] = oldIds[i]; - } - else - _ASSERTE(!"Object seen twice while rehashing"); - } - -#ifdef USE_CHECKED_OBJECTREFS - for(DWORD i = 0; i < m_currArraySize; i++) - Thread::ObjectRefProtected(&m_objects[i]); - for(DWORD i = 0; i < m_currArraySize; i++) - Thread::ObjectRefProtected(&m_newObjects[i]); -#endif - -} - -void GCSafeObjectTable::Push(OBJECTREF refObj, OBJECTREF refParent, OBJECTREF refAux, QueuedObjectInfo * pQOI) -{ - CONTRACTL - { - THROWS; - MODE_COOPERATIVE; - GC_NOTRIGGER; - } - CONTRACTL_END - _ASSERTE(refObj != NULL); - _ASSERTE(m_QueueType == LIFO_QUEUE); - _ASSERTE(m_head == 0 && m_dataHead == 0); - - // First find the size of the object info - DWORD size = pQOI->GetSize(); - - // Check if resize is needed - EnsureSize(size); - - // Push on the stack, first the objects - DWORD index = m_count; - if (m_Objects1) - m_Objects1[index] = refObj; -#ifdef _DEBUG - else - _ASSERTE(refObj == NULL); -#endif - if (m_Objects2) - m_Objects2[index] = refParent; -#ifdef _DEBUG - else - _ASSERTE(refParent == NULL); -#endif - if (m_Objects3) - m_Objects3[index] = refAux; -#ifdef _DEBUG - else - _ASSERTE(refAux == NULL); -#endif - - // then the info - if (m_dataIndices) - m_dataIndices[index] = m_numDataBytes; - BYTE *pData = &m_data[m_numDataBytes]; - memcpy(pData, (VOID*)pQOI, size); - - m_numDataBytes += size; - m_count++; -} - -OBJECTREF GCSafeObjectTable::Pop(OBJECTREF *refParent, OBJECTREF *refAux, QueuedObjectInfo ** pQOI) -{ - LIMITED_METHOD_CONTRACT; - _ASSERTE(m_QueueType == LIFO_QUEUE); - _ASSERTE(m_head == 0 && m_dataHead == 0); - _ASSERTE(m_dataIndices != NULL); - - *pQOI = NULL; - OBJECTREF refRet = NULL; - *refParent = NULL; - *refAux = NULL; - if (m_count == 0) - return NULL; - - m_count--; - refRet = m_Objects1[m_count]; - if (m_Objects2) - *refParent = m_Objects2[m_count]; - if (m_Objects3) - *refAux = m_Objects3[m_count]; - *pQOI = (QueuedObjectInfo *) &m_data[m_dataIndices[m_count]]; - - m_numDataBytes -= (*pQOI)->GetSize(); - return refRet; -} - -void GCSafeObjectTable::SetAt(DWORD index, OBJECTREF refObj, OBJECTREF refParent, OBJECTREF refAux, QueuedObjectInfo * pQOI) -{ - CONTRACTL - { - THROWS; - MODE_COOPERATIVE; - GC_NOTRIGGER; - } - CONTRACTL_END - _ASSERTE(refObj != NULL); -#ifdef _DEBUG - if (m_QueueType == LIFO_QUEUE) - _ASSERTE(index >= 0 && index < m_count); - else - _ASSERTE(index < m_currArraySize); -#endif - - // First find the size of the object info - DWORD size = pQOI->GetSize(); - - // Push on the stack, first the objects - m_Objects1[index] = refObj; - if (m_Objects2) - m_Objects2[index] = refParent; - if (m_Objects3) - m_Objects3[index] = refAux; - - // then the info - _ASSERTE(m_dataIndices != NULL); - - QueuedObjectInfo *pData = (QueuedObjectInfo *)&m_data[m_dataIndices[index]]; - _ASSERTE(pData->GetSize() == size); - - memcpy(pData, (VOID*)pQOI, size); -} - -OBJECTREF GCSafeObjectTable::GetAt(DWORD index, OBJECTREF *refParent, OBJECTREF *refAux, QueuedObjectInfo ** pQOI) -{ - LIMITED_METHOD_CONTRACT; -#ifdef _DEBUG - if (m_QueueType == LIFO_QUEUE) - _ASSERTE(index >= 0 && index < m_count); - else - _ASSERTE(index < m_currArraySize); -#endif - - OBJECTREF refRet = m_Objects1[index]; - if (m_Objects2) - *refParent = m_Objects2[index]; - else - *refParent = NULL; - if (m_Objects3) - *refAux = m_Objects3[index]; - else - *refAux = NULL; - - _ASSERTE(m_dataIndices != NULL); - - *pQOI = (QueuedObjectInfo *) &m_data[m_dataIndices[index]]; - - return refRet; -} - -void GCSafeObjectTable::Enqueue(OBJECTREF refObj, OBJECTREF refParent, OBJECTREF refAux, QueuedObjectInfo *pQOI) -{ - CONTRACTL - { - THROWS; - MODE_COOPERATIVE; - GC_NOTRIGGER; - } - CONTRACTL_END - - _ASSERTE(refObj != NULL); - _ASSERTE(m_QueueType == FIFO_QUEUE); - - // First find the size of the object info - DWORD size = pQOI ? pQOI->GetSize() : 0; - - // Check if resize is needed - EnsureSize(size); - - // Append to queue, first the objects - DWORD index = (m_head + m_count) % m_currArraySize; - m_Objects1[index] = refObj; - if (m_Objects2) - m_Objects2[index] = refParent; - if (m_Objects3) - m_Objects3[index] = refAux; - - // then the info - if (pQOI) - { - DWORD dataIndex = (m_dataHead + m_numDataBytes) % (m_currArraySize * MAGIC_FACTOR); - BYTE *pData = &m_data[dataIndex]; - memcpy(pData, (VOID*)pQOI, size); - - if (m_dataIndices) - m_dataIndices[index] = dataIndex; - m_numDataBytes += size; - } - - m_count++; -} - -OBJECTREF GCSafeObjectTable::Dequeue(OBJECTREF *refParent, OBJECTREF *refAux, QueuedObjectInfo ** pQOI) -{ - LIMITED_METHOD_CONTRACT; - - _ASSERTE(m_QueueType == FIFO_QUEUE); - - if (pQOI) - *pQOI = NULL; - OBJECTREF refRet = NULL; - *refParent = NULL; - *refAux = NULL; - if (m_count == 0) - return NULL; - - refRet = m_Objects1[m_head]; - if (m_Objects2) - *refParent = m_Objects2[m_head]; - if (m_Objects3) - *refAux = m_Objects3[m_head]; - - if (pQOI) - { - *pQOI = (QueuedObjectInfo *) &m_data[m_dataHead]; - - m_dataHead = (m_dataHead + (*pQOI)->GetSize()) % (m_currArraySize * MAGIC_FACTOR); - - m_numDataBytes -= (*pQOI)->GetSize(); - } - - m_head = (m_head + 1) % m_currArraySize; - m_count--; - return refRet; -} - -OBJECTREF GCSafeObjectTable::Peek(OBJECTREF *refParent, OBJECTREF *refAux, QueuedObjectInfo **pQOI) -{ - CONTRACTL - { - NOTHROW; - GC_NOTRIGGER; - SO_TOLERANT; - MODE_COOPERATIVE; - } - CONTRACTL_END; - - *pQOI = NULL; - *refParent = NULL; - *refAux = NULL; - if (m_count == 0) - return NULL; - - DWORD indexToPeek; - if (m_QueueType == LIFO_QUEUE) - { - indexToPeek = m_count; - return GetAt(indexToPeek, refParent, refAux, pQOI); - } - else - { - indexToPeek = m_head; - if (m_Objects2) - *refParent = m_Objects2[m_head]; - if (m_Objects3) - *refParent = m_Objects3[m_head]; - *pQOI = (QueuedObjectInfo *) &m_data[m_dataHead]; - return m_Objects1[m_head]; - } - -} - -void GCSafeObjectTable::EnsureSize(DWORD requiredDataSize) -{ - CONTRACTL - { - THROWS; - MODE_COOPERATIVE; - GC_NOTRIGGER; - } - CONTRACTL_END - // Check if the object queue is sized enough - if (m_count == m_currArraySize) - { - Resize(); - return; - } - - // Check if the data array size is enough - if (m_numDataBytes + requiredDataSize > m_currArraySize * MAGIC_FACTOR) - { - Resize(); - return; - } - - if (m_QueueType == FIFO_QUEUE) - { - // Will current QueuedObjectInfo go beyond the edge of the array ? - if (m_dataHead + m_numDataBytes + requiredDataSize > m_currArraySize * MAGIC_FACTOR) - { - Resize(); - return; - } - } -} - -void GCSafeObjectTable::Resize() -{ - CONTRACTL - { - THROWS; - MODE_COOPERATIVE; - GC_NOTRIGGER; - } - CONTRACTL_END - // Allocate new space - DWORD newSize = m_currArraySize * 2; - NewArrayHolder<OBJECTREF> refTemp (NULL); - NewArrayHolder<OBJECTREF> refParentTemp (NULL); - NewArrayHolder<OBJECTREF> refAuxTemp (NULL); - - refTemp = new OBJECTREF[newSize]; - if (m_Objects2) - refParentTemp = new OBJECTREF[newSize]; - if (m_Objects3) - refAuxTemp = new OBJECTREF[newSize]; - -#ifdef USE_CHECKED_OBJECTREFS - ZeroMemory((void *)refTemp, sizeof(OBJECTREF) * newSize); - if (m_Objects2) - ZeroMemory((void *)refParentTemp, sizeof(OBJECTREF) * newSize); - if (m_Objects3) - ZeroMemory((void *)refAuxTemp, sizeof(OBJECTREF) * newSize); -#endif - - NewArrayHolder<BYTE> bTemp (NULL); - NewArrayHolder<DWORD> dwIndicesTemp (NULL); - - bTemp = new BYTE[newSize * MAGIC_FACTOR]; - if (m_dataIndices) - dwIndicesTemp = new DWORD[newSize]; - - // Copy over objects and data - if (m_QueueType == LIFO_QUEUE || (m_QueueType == FIFO_QUEUE && m_head == 0)) - { - void *pSrc = (void *)&m_Objects1[0]; - void *pDest = (void *)&refTemp[0]; - memcpyUnsafe(pDest, pSrc, m_count * sizeof(OBJECTREF)); - - if (m_Objects2) - { - pSrc = (void *)&m_Objects2[0]; - pDest = (void *)&refParentTemp[0]; - memcpyUnsafe(pDest, pSrc, m_count * sizeof(OBJECTREF)); - } - - if (m_Objects3) - { - pSrc = (void *)&m_Objects3[0]; - pDest = (void *)&refAuxTemp[0]; - memcpyUnsafe(pDest, pSrc, m_count * sizeof(OBJECTREF)); - } - - pSrc = (void *)&m_data[0]; - pDest = (void *)&bTemp[0]; - memcpyNoGCRefs(pDest, pSrc, m_numDataBytes); - - if (m_dataIndices) - { - pSrc = (void *)&m_dataIndices[0]; - pDest = (void *)&dwIndicesTemp[0]; - memcpyNoGCRefs(pDest, pSrc, m_count * sizeof(DWORD)); - } - - } - else - { - _ASSERTE(m_QueueType == FIFO_QUEUE && m_head != 0); - _ASSERTE(m_currArraySize > m_head); - DWORD numObjRefsToCopy = (m_count > m_currArraySize - m_head ? m_currArraySize - m_head : m_count); - - void *pSrc = (void *)&m_Objects1[m_head]; - void *pDest = (void *)&refTemp[0]; - memcpyUnsafe(pDest, pSrc, numObjRefsToCopy * sizeof(OBJECTREF)); - pSrc = (void *)&m_Objects1[0]; - pDest = (void *)&refTemp[numObjRefsToCopy]; - memcpyUnsafe(pDest, pSrc, (m_count - numObjRefsToCopy) * sizeof(OBJECTREF)); - - if (m_Objects2) - { - pSrc = (void *)&m_Objects2[m_head]; - pDest = (void *)&refParentTemp[0]; - memcpyUnsafe(pDest, pSrc, numObjRefsToCopy * sizeof(OBJECTREF)); - pSrc = (void *)&m_Objects2[0]; - pDest = (void *)&refParentTemp[numObjRefsToCopy]; - memcpyUnsafe(pDest, pSrc, (m_count - numObjRefsToCopy) * sizeof(OBJECTREF)); - } - - if (m_Objects3) - { - pSrc = (void *)&m_Objects3[m_head]; - pDest = (void *)&refAuxTemp[0]; - memcpyUnsafe(pDest, pSrc, numObjRefsToCopy * sizeof(OBJECTREF)); - pSrc = (void *)&m_Objects3[0]; - pDest = (void *)&refAuxTemp[numObjRefsToCopy]; - memcpyUnsafe(pDest, pSrc, (m_count - numObjRefsToCopy) * sizeof(OBJECTREF)); - } - - if (m_dataIndices) - { - pSrc = (void *)&m_dataIndices[m_head]; - pDest = (void *)&dwIndicesTemp[0]; - memcpyUnsafe(pDest, pSrc, numObjRefsToCopy * sizeof(DWORD)); - pSrc = (void *)&m_dataIndices[0]; - pDest = (void *)&dwIndicesTemp[numObjRefsToCopy]; - memcpyUnsafe(pDest, pSrc, (m_count - numObjRefsToCopy) * sizeof(DWORD)); - } - - DWORD numBytesToCopy = (m_numDataBytes > ((m_currArraySize * MAGIC_FACTOR) - m_dataHead) ? ((m_currArraySize * MAGIC_FACTOR) - m_dataHead) : m_numDataBytes);//(m_currArraySize * MAGIC_FACTOR) - m_dataHead; - memcpyNoGCRefs((void *)bTemp, (void *) &m_data[m_dataHead], numBytesToCopy); - memcpyNoGCRefs((void *) &bTemp[numBytesToCopy], (void *)m_data, (m_numDataBytes - numBytesToCopy)); - } - - // Delete old allocation - if (m_usingHeap) - { - delete[] m_data; - delete[] m_Objects1; - delete[] m_Objects2; - delete[] m_Objects3; - delete[] m_dataIndices; - } - - refTemp.SuppressRelease(); - refParentTemp.SuppressRelease(); - refAuxTemp.SuppressRelease(); - dwIndicesTemp.SuppressRelease(); - bTemp.SuppressRelease(); - - m_currArraySize = newSize; - m_Objects1 = refTemp; - m_Objects2 = refParentTemp; - m_Objects3 = refAuxTemp; - m_dataIndices = dwIndicesTemp; - m_data = bTemp; - m_head = 0; - m_dataHead = 0; - - m_usingHeap = TRUE; -#ifdef USE_CHECKED_OBJECTREFS - for(DWORD i = 0; i < m_currArraySize; i++) - { - Thread::ObjectRefProtected(&m_Objects1[i]); - if (m_Objects2) - Thread::ObjectRefProtected(&m_Objects2[i]); - if (m_Objects3) - Thread::ObjectRefProtected(&m_Objects3[i]); - } -#endif -} - - -VOID GCScanRootsInCollection(promote_func *fn, ScanContext* sc, void *context) -{ - STATIC_CONTRACT_SO_TOLERANT; - GCSafeCollection *pObjCollection = (GCSafeCollection *)context; - pObjCollection->ReportGCRefs(fn, sc); -} - -VOID -BeginCloning(ObjectClone *pOC) -{ - pOC->Init(FALSE); -} - -VOID -EndCloning(ObjectClone *pOC) -{ - pOC->Cleanup(FALSE); -} - -typedef Holder<ObjectClone*, BeginCloning, EndCloning> ObjectCloneHolder; - - -OBJECTREF ObjectClone::Clone(OBJECTREF refObj, TypeHandle expectedType, AppDomain* fromDomain, AppDomain* toDomain, OBJECTREF refExecutionContext) -{ - CONTRACTL - { - MODE_COOPERATIVE; - THROWS; - GC_TRIGGERS; - } - CONTRACTL_END - - if (refObj == NULL) - return NULL; - - if (m_context != ObjectFreezer && refObj->GetMethodTable() == g_pStringClass) - return refObj; - - ObjectCloneHolder ocHolder(this); - - m_fromDomain = fromDomain; - m_toDomain = toDomain; - - m_currObject = refObj; - GCPROTECT_BEGIN(m_currObject); - m_topObject = NULL; - GCPROTECT_BEGIN(m_topObject); - m_fromExecutionContext = refExecutionContext; - GCPROTECT_BEGIN(m_fromExecutionContext); - - // Enter the domain we're cloning into, if we're not already there - ENTER_DOMAIN_PTR(toDomain,ADV_RUNNINGIN); - - if (!m_securityChecked) - { - Security::SpecialDemand(SSWT_DEMAND_FROM_NATIVE, SECURITY_SERIALIZATION); - m_securityChecked = TRUE; - } - -#ifdef _DEBUG - DefineFullyQualifiedNameForClass(); - LOG((LF_REMOTING, LL_INFO100, "Clone. Cloning instance of type %s.\n", - GetFullyQualifiedNameForClassNestedAware(m_currObject->GetMethodTable()))); -#endif - - m_newObject = NULL; - GCPROTECT_BEGIN(m_newObject); - PTRARRAYREF refValues = NULL; - GCPROTECT_BEGIN(refValues); - OBJECTREF refParent = NULL; - GCPROTECT_BEGIN(refParent); - - QueuedObjectInfo *currObjFixupInfo = NULL; - // For some dynamically sized stack objects - void *pTempStackSpace = NULL; - DWORD dwCurrStackSpaceSize = 0; - - // Initialize QOM - QueuedObjectInfo topObj; - OBJECTREF dummy1, dummy2; - QOM.Enqueue(m_currObject, NULL, NULL, (QueuedObjectInfo *)&topObj); - - while ((m_currObject = QOM.Dequeue(&dummy1, &dummy2, &currObjFixupInfo)) != NULL) - { - m_newObject = NULL; - MethodTable *newMT = NULL; - - BOOL repeatObject = FALSE; - BOOL isISerializable = FALSE, isIObjRef = FALSE, isBoxed = FALSE; - DWORD ISerializableTSOIndex = (DWORD) -1; - DWORD IObjRefTSOIndex = (DWORD) -1; - DWORD BoxedValTSOIndex = (DWORD) -1; - m_skipFieldScan = FALSE; - - // ALLOCATE PHASE - - // Was currObject seen before ? - int currID = TOS.HasID(m_currObject, &m_newObject); - if (currID != -1) - { - // Yes - repeatObject = TRUE; - m_skipFieldScan = TRUE; - newMT = m_newObject->GetMethodTable(); - - if (m_cbInterface->IsISerializableType(newMT)) - { - currObjFixupInfo->SetIsISerializableInstance(); - isISerializable = TRUE; - ISerializableTSOIndex = FindObjectInTSO(currID, ISerializable); - } - -#ifdef _DEBUG - LOG((LF_REMOTING, LL_INFO1000, "Clone. Object of type %s with id %d seen before.\n", - GetFullyQualifiedNameForClassNestedAware(m_currObject->GetMethodTable()), currID)); -#endif - } - else - { -#ifdef _DEBUG - LOG((LF_REMOTING, LL_INFO1000, "Clone. Object of type %s not seen before.\n", - GetFullyQualifiedNameForClassNestedAware(m_currObject->GetMethodTable()))); -#endif - // No - MethodTable *currMT = m_currObject->GetMethodTable(); - - // Check whether object is serializable - m_cbInterface->ValidateFromType(currMT); - - // Add current object to table of seen objects and get an id - currID = TOS.AddObject(m_currObject, m_newObject); - LOG((LF_REMOTING, LL_INFO1000, "Clone. Current object added to Table of Objects Seen. Given id %d.\n", currID)); - - if ( m_cbInterface->IsRemotedType(currMT, m_fromDomain, m_toDomain)) - { - refValues = AllocateISerializable(currID, TRUE); - isISerializable = TRUE; - ISerializableTSOIndex = TSO.GetCount() - 1; - currObjFixupInfo->SetIsISerializableInstance(); - if (refValues == NULL) - { - // We found a smugglable objref. No field scanning needed - m_skipFieldScan = TRUE; - } - } - else if( m_cbInterface->IsISerializableType(currMT)) - { - InvokeVtsCallbacks(m_currObject, RemotingVtsInfo::VTS_CALLBACK_ON_SERIALIZING, fromDomain); - if (HasVtsCallbacks(m_currObject->GetMethodTable(), RemotingVtsInfo::VTS_CALLBACK_ON_SERIALIZED)) - VSC.Enqueue(m_currObject, NULL, NULL, NULL); - - refValues = AllocateISerializable(currID, FALSE); - isISerializable = TRUE; - ISerializableTSOIndex = TSO.GetCount() - 1; - currObjFixupInfo->SetIsISerializableInstance(); - } - else if (currMT->IsArray()) - { - AllocateArray(); - } - else - { - // This is a regular object - InvokeVtsCallbacks(m_currObject, RemotingVtsInfo::VTS_CALLBACK_ON_SERIALIZING, fromDomain); - if (HasVtsCallbacks(m_currObject->GetMethodTable(), RemotingVtsInfo::VTS_CALLBACK_ON_SERIALIZED)) - VSC.Enqueue(m_currObject, NULL, NULL, NULL); - - AllocateObject(); - - if (m_cbInterface->IsISerializableType(m_newObject->GetMethodTable())) - { - // We have a situation where the serialized instnce was not ISerializable, - // but the target instance is. So we make the from object look like a ISerializable - refValues = MakeObjectLookLikeISerializable(currID); - isISerializable = TRUE; - ISerializableTSOIndex = TSO.GetCount() - 1; - currObjFixupInfo->SetIsISerializableInstance(); - } - } - - _ASSERTE(m_newObject != NULL); - newMT = m_newObject->GetMethodTable(); - - // Check whether new object is serializable - m_cbInterface->ValidateToType(newMT); - - // Update the TOS, to include the new object - int retId; - retId = TOS.UpdateObject(m_currObject, m_newObject); - _ASSERTE(retId == currID); - } - _ASSERTE(m_newObject != NULL); - - // FIXUP PHASE - // Get parent to be fixed up - ParentInfo *parentInfo; - refParent = QOF.Peek(&dummy1, &dummy2, (QueuedObjectInfo **)&parentInfo); - MethodTable *pParentMT = NULL; - - if (refParent == NULL) - { - LOG((LF_REMOTING, LL_INFO1000, "Clone. No parent found. This is the top object.\n")); - // This is the top object - _ASSERTE(m_topObject == NULL); - m_topObject = m_newObject; - } - else - { -#ifdef _DEBUG - LOG((LF_REMOTING, LL_INFO1000, "Clone. Parent is of type %s.\n", - GetFullyQualifiedNameForClassNestedAware(m_currObject->GetMethodTable()))); -#endif - pParentMT = refParent->GetMethodTable(); - } - - if (IsDelayedFixup(newMT, currObjFixupInfo)) - { - // New object is IObjRef or a boxed object - if (m_cbInterface->IsIObjectReferenceType(newMT)) - { - LOG((LF_REMOTING, LL_INFO1000, "Clone. This is an IObjectReference. Delaying fixup.\n")); - DWORD size = sizeof(IObjRefInstanceInfo) + (currObjFixupInfo ? currObjFixupInfo->GetSize() : 0); - if (size > dwCurrStackSpaceSize) - { - pTempStackSpace = _alloca(size); - dwCurrStackSpaceSize = size; - } - IObjRefInstanceInfo *pIORInfo = new (pTempStackSpace) IObjRefInstanceInfo(currID, 0, 0); - if (currObjFixupInfo) - pIORInfo->SetFixupInfo(currObjFixupInfo); - // Check if this instance is ISerializable also - if (isISerializable) - { - LOG((LF_REMOTING, LL_INFO1000, "Clone. This is also an ISerializable type at index %d in TSO.\n", ISerializableTSOIndex)); - _ASSERTE(ISerializableTSOIndex != (DWORD) -1); - pIORInfo->SetISerTSOIndex(ISerializableTSOIndex); - } - - if (repeatObject) - pIORInfo->SetIsRepeatObject(); - - // Add to TSO - TSO.Push(m_newObject, m_currObject, refParent, pIORInfo); - - isIObjRef = TRUE; - IObjRefTSOIndex = TSO.GetCount() - 1; - - LOG((LF_REMOTING, LL_INFO1000, "Clone. Added to TSO at index %d.\n", IObjRefTSOIndex)); - // Any special object parent, would wait till the current object is resolved - if (parentInfo) - { - parentInfo->IncrementSpecialMembers(); - TMappings.Add(IObjRefTSOIndex); - } - - } - if (currObjFixupInfo->NeedsUnboxing()) - { - LOG((LF_REMOTING, LL_INFO1000, "Clone. This is a boxed value type. Delaying fixup.\n")); - DWORD size = sizeof(ValueTypeInfo) + currObjFixupInfo->GetSize(); - if (size > dwCurrStackSpaceSize) - { - pTempStackSpace = _alloca(size); - dwCurrStackSpaceSize = size; - } - ValueTypeInfo *valInfo = new (pTempStackSpace) ValueTypeInfo(currID, currObjFixupInfo); - // If the value type is also ISer or IObj, then it has to wait till those interfaces are addressed - if (isISerializable) - { - LOG((LF_REMOTING, LL_INFO1000, "Clone. This is also an ISerializable type at index %d in TSO.\n", ISerializableTSOIndex)); - valInfo->SetISerTSOIndex(ISerializableTSOIndex); - } - if (isIObjRef) - { - LOG((LF_REMOTING, LL_INFO1000, "Clone. This is also an IObjectReference type at index %d in TSO.\n", IObjRefTSOIndex)); - valInfo->SetIObjRefTSOIndex(IObjRefTSOIndex); - } - - // Add to TSO - TSO.Push(m_newObject, refParent, NULL, valInfo); - - isBoxed = TRUE; - BoxedValTSOIndex = TSO.GetCount() - 1; - - LOG((LF_REMOTING, LL_INFO1000, "Clone. Added to TSO at index %d.\n", BoxedValTSOIndex)); - // An IObjRef parent, or a parent itself boxed, would wait till the current object is resolved - if (parentInfo && (parentInfo->NeedsUnboxing() || parentInfo->IsIObjRefInstance())) - { - parentInfo->IncrementSpecialMembers(); - TMappings.Add(BoxedValTSOIndex); - } - } - } - - if (refParent != NULL) - { - if (!IsDelayedFixup(newMT, currObjFixupInfo)) - Fixup(m_newObject, refParent, currObjFixupInfo); - - // If currObj is ISer, then an IObjRef parent would wait till the current object is resolved - if (currObjFixupInfo->IsISerializableInstance() && - parentInfo->IsIObjRefInstance()) - { - parentInfo->IncrementSpecialMembers(); - TMappings.Add(ISerializableTSOIndex); - } - } - - // If we are done with this parent, remove it from QOF - if (parentInfo && parentInfo->DecrementFixupCount() == 0) - { - LOG((LF_REMOTING, LL_INFO1000, "Clone. All children fixed up. Removing parent from QOF.\n", BoxedValTSOIndex)); - LOG((LF_REMOTING, LL_INFO1000, "Clone. Parent has %d special member objects.\n", parentInfo->GetNumSpecialMembers())); - OBJECTREF refTemp; - ParentInfo *pFITemp; - refTemp = QOF.Dequeue(&dummy1, &dummy2, (QueuedObjectInfo **)&pFITemp); - _ASSERTE(refTemp == refParent); - _ASSERTE(pFITemp == parentInfo); - - // If parent is a special object, then we need to know how many special members it has - if ((parentInfo->IsIObjRefInstance() || - parentInfo->IsISerializableInstance() || - parentInfo->NeedsUnboxing()) - && parentInfo->GetNumSpecialMembers() > 0) - { - // Make a note in TSO that this parent has non-zero special members - DWORD index[3]; - index[0] = parentInfo->GetIObjRefIndexIntoTSO(); - index[1] = parentInfo->GetISerIndexIntoTSO(); - index[2] = parentInfo->GetBoxedValIndexIntoTSO(); - - for (DWORD count = 0; count < 3; count++) - { - OBJECTREF refIser, refNames, refValuesTemp; - SpecialObjectInfo *pISerInfo; - - if (index[count] == (DWORD) -1) - continue; - - refIser = TSO.GetAt(index[count], &refNames, &refValuesTemp, (QueuedObjectInfo **)&pISerInfo); - _ASSERTE(refIser == refParent); - - DWORD numSpecialObjects = parentInfo->GetNumSpecialMembers(); - pISerInfo->SetNumSpecialMembers(numSpecialObjects); - - _ASSERTE(TMappings.GetCount() >= numSpecialObjects); - pISerInfo->SetMappingTableIndex(TMappings.GetCount() - numSpecialObjects); - } - } - } - - // FIELD SCAN PHASE - if (!m_skipFieldScan) - { - if (m_currObject->GetMethodTable()->IsArray()) - ScanArrayMembers(); - else if (isISerializable) - ScanISerializableMembers(IObjRefTSOIndex, ISerializableTSOIndex, BoxedValTSOIndex, refValues); - else - ScanMemberFields(IObjRefTSOIndex, BoxedValTSOIndex); - } - - } // While there are objects in QOM - - // OBJECT COMPLETION PHASE - CompleteSpecialObjects(); - - // Deliver VTS OnDeserialized callbacks. - CompleteVtsOnDeserializedCallbacks(); - - CompleteIDeserializationCallbacks(); - - _ASSERTE(m_topObject != NULL); - // If a type check was requested, see if the returned object is of the expected type - if (!expectedType.IsNull() - && !ObjIsInstanceOf(OBJECTREFToObject(m_topObject), expectedType)) - COMPlusThrow(kArgumentException,W("Arg_ObjObj")); - - GCPROTECT_END(); // refParent - GCPROTECT_END(); // refValues - - GCPROTECT_END(); // m_newObject - - END_DOMAIN_TRANSITION; - - // Deliver VTS OnSerialized callbacks. - CompleteVtsOnSerializedCallbacks(); - - GCPROTECT_END(); // m_fromExecutionContext - GCPROTECT_END(); // m_topObject - GCPROTECT_END(); // m_currObject - - return m_topObject; -} - -// IObjRef and value types boxed by us, need to be fixed up towards the end -BOOL ObjectClone::IsDelayedFixup(MethodTable *newMT, QueuedObjectInfo *pCurrInfo) -{ - CONTRACTL - { - GC_TRIGGERS; - MODE_COOPERATIVE; - THROWS; - } - CONTRACTL_END - if (m_cbInterface->IsIObjectReferenceType(newMT) || - pCurrInfo->NeedsUnboxing()) - return TRUE; - else - return FALSE; -} - -void ObjectClone::Fixup(OBJECTREF newObj, OBJECTREF refParent, QueuedObjectInfo *pFixupInfo) -{ - CONTRACTL - { - GC_TRIGGERS; - MODE_COOPERATIVE; - THROWS; - } - CONTRACTL_END - MethodTable *pParentMT = refParent->GetMethodTable(); - - if (pFixupInfo->IsISerializableMember()) - { - HandleISerializableFixup(refParent, pFixupInfo); - } - else if (pParentMT->IsArray()) - { - HandleArrayFixup(refParent, pFixupInfo); - } - else - { - HandleObjectFixup(refParent, pFixupInfo); - } -} - -PTRARRAYREF ObjectClone::MakeObjectLookLikeISerializable(int objectId) -{ - CONTRACTL - { - GC_TRIGGERS; - THROWS; - MODE_COOPERATIVE; - } - CONTRACTL_END - - _ASSERTE(m_context != ObjectFreezer); - - LOG((LF_REMOTING, LL_INFO1000, "MakeObjectLookLikeISerializable. Target object is ISerializable, so making from object look ISerializable\n")); - MethodTable *pCurrMT = m_currObject->GetMethodTable(); - DWORD numFields = pCurrMT->GetNumInstanceFields(); - - PTRARRAYREF fieldNames = NULL; - PTRARRAYREF fieldValues = NULL; - - GCPROTECT_BEGIN(fieldNames); - GCPROTECT_BEGIN(fieldValues); - - // Go back to from domain - ENTER_DOMAIN_PTR(m_fromDomain,ADV_RUNNINGIN); - - // Reset the execution context to the original state it was in when we first - // left the from domain (this will automatically be popped once we return - // from this domain again). - Thread *pThread = GetThread(); - if (pThread->IsExposedObjectSet()) - { - THREADBASEREF refThread = (THREADBASEREF)pThread->GetExposedObjectRaw(); - refThread->SetExecutionContext(m_fromExecutionContext); - } - - fieldNames = (PTRARRAYREF)AllocateObjectArray(numFields, g_pStringClass, FALSE); - fieldValues = (PTRARRAYREF)AllocateObjectArray(numFields, g_pObjectClass, FALSE); - - DWORD fieldIndex = 0; - while (pCurrMT) - { - - DWORD numInstanceFields = pCurrMT->GetNumIntroducedInstanceFields(); - - FieldDesc *pFields = pCurrMT->GetApproxFieldDescListRaw(); - - for (DWORD i = 0; i < numInstanceFields; i++) - { - if (pFields[i].IsNotSerialized()) - { - LOG((LF_REMOTING, LL_INFO1000, "MakeObjectLookLikeISerializable. Field %s is marked NonSerialized. Skipping.\n", pFields[i].GetName())); - continue; - } - - CorElementType typ = pFields[i].GetFieldType(); - DWORD offset = pFields[i].GetOffset(); - - LPCUTF8 szFieldName = pFields[i].GetName(); - STRINGREF refName = StringObject::NewString(szFieldName); - _ASSERTE(refName != NULL); - - fieldNames->SetAt(fieldIndex, refName); - - switch (typ) - { - case ELEMENT_TYPE_BOOLEAN: - case ELEMENT_TYPE_I1: - case ELEMENT_TYPE_U1: - case ELEMENT_TYPE_I2: - case ELEMENT_TYPE_U2: - case ELEMENT_TYPE_CHAR: - case ELEMENT_TYPE_I4: - case ELEMENT_TYPE_U4: - case ELEMENT_TYPE_I8: - case ELEMENT_TYPE_U8: - case ELEMENT_TYPE_I: - case ELEMENT_TYPE_U: - case ELEMENT_TYPE_R4: - case ELEMENT_TYPE_R8: - { - MethodTable *pFldMT = MscorlibBinder::GetElementType(typ); - void *pData = m_currObject->GetData() + offset; - OBJECTREF refBoxed = pFldMT->Box(pData); - - fieldValues->SetAt(fieldIndex, refBoxed); - break; - } - case ELEMENT_TYPE_VALUETYPE: - case ELEMENT_TYPE_PTR: - case ELEMENT_TYPE_FNPTR: - { - TypeHandle th = LoadExactFieldType(&pFields[i], m_currObject, m_fromDomain); - _ASSERTE(!th.AsMethodTable()->IsByRefLike() && "Field types cannot contain stack pointers."); - - OBJECTREF refBoxed = BoxValueTypeInWrongDomain(m_currObject, offset, th.AsMethodTable()); - - fieldValues->SetAt(fieldIndex, refBoxed); - break; - } - case ELEMENT_TYPE_SZARRAY: // Single Dim - case ELEMENT_TYPE_ARRAY: // General Array - case ELEMENT_TYPE_CLASS: // Class - case ELEMENT_TYPE_OBJECT: - case ELEMENT_TYPE_STRING: // System.String - case ELEMENT_TYPE_VAR: - { - OBJECTREF refField = *((OBJECTREF *) m_currObject->GetData() + offset); - fieldValues->SetAt(fieldIndex, refField); - break; - } - default: - _ASSERTE(!"Unknown element type in MakeObjectLookLikeISerializalbe"); - } - - fieldIndex++; - } - - pCurrMT = pCurrMT->GetParentMethodTable(); - } - - // Back to original domain - END_DOMAIN_TRANSITION; - - // Add object to TSO - ISerializableInstanceInfo iserInfo(objectId, 0); - TSO.Push(m_newObject, fieldNames, NULL, (QueuedObjectInfo *)&iserInfo); - - LOG((LF_REMOTING, LL_INFO1000, "MakeObjectLookLikeISerializable. Added to TSO at index %d.\n", TSO.GetCount() - 1)); - GCPROTECT_END(); - GCPROTECT_END(); - - return fieldValues; -} - -PTRARRAYREF ObjectClone::AllocateISerializable(int objectId, BOOL bIsRemotingObject) -{ - CONTRACTL - { - GC_TRIGGERS; - THROWS; - MODE_COOPERATIVE; - } - CONTRACTL_END - - _ASSERTE(m_context != ObjectFreezer); - - // Go back to from domain - StackSString ssAssemName; - StackSString ssTypeName; - - struct _gc { - STRINGREF typeName; - STRINGREF assemblyName; - PTRARRAYREF fieldNames; - PTRARRAYREF fieldValues; - OBJECTREF refObjRef; - } gc; - ZeroMemory(&gc, sizeof(gc)); - - GCPROTECT_BEGIN(gc); - - ENTER_DOMAIN_PTR(m_fromDomain,ADV_RUNNINGIN); - - // Reset the execution context to the original state it was in when we first - // left the from domain (this will automatically be popped once we return - // from this domain again). - Thread *pThread = GetThread(); - if (pThread->IsExposedObjectSet()) - { - THREADBASEREF refThread = (THREADBASEREF)pThread->GetExposedObjectRaw(); - refThread->SetExecutionContext(m_fromExecutionContext); - } - - // Call GetObjectData on the interface - - LOG((LF_REMOTING, LL_INFO1000, "AllocateISerializable. Instance is ISerializable type. Calling GetObjectData.\n")); - - PREPARE_NONVIRTUAL_CALLSITE(METHOD__OBJECTCLONEHELPER__GET_OBJECT_DATA); - - DECLARE_ARGHOLDER_ARRAY(args, 5); - - args[ARGNUM_0] = OBJECTREF_TO_ARGHOLDER(m_currObject); - args[ARGNUM_1] = PTR_TO_ARGHOLDER(&gc.typeName); - args[ARGNUM_2] = PTR_TO_ARGHOLDER(&gc.assemblyName); - args[ARGNUM_3] = PTR_TO_ARGHOLDER(&gc.fieldNames); - args[ARGNUM_4] = PTR_TO_ARGHOLDER(&gc.fieldValues); - - CATCH_HANDLER_FOUND_NOTIFICATION_CALLSITE; - CALL_MANAGED_METHOD_RETREF(gc.refObjRef, OBJECTREF, args); - - if (!bIsRemotingObject || gc.refObjRef == NULL) - { - ssAssemName.Set(gc.assemblyName->GetBuffer()); - ssTypeName.Set(gc.typeName->GetBuffer()); - } - - // Back to original domain - END_DOMAIN_TRANSITION; - - // if its a remoting object we are dealing with, we may already have the smugglable objref - if (bIsRemotingObject && gc.refObjRef != NULL) - { - m_newObject = gc.refObjRef; - // Add object to TSO. We dont need a ISerializable record, because we are smuggling the ObjRef - // and so, technically the ISerializable ctor can be considered already called. But we still make an entry in - // TSO and mark it "processed", so repeat references to the same remoting object work correctly - ISerializableInstanceInfo iserInfo(objectId, 0); - iserInfo.SetHasBeenProcessed(); - TSO.Push(m_newObject, NULL, NULL, (QueuedObjectInfo *)&iserInfo); - - LOG((LF_REMOTING, LL_INFO1000, "AllocateISerializable. GetObjectData returned smugglable ObjRef. Added dummy record to TSO at index %d.\n", TSO.GetCount() - 1)); - } - else - { - // Find the type (and choke on any exotics such as arrays, function pointers or generic type definitions). - TypeHandle th = GetType(ssTypeName, ssAssemName); - if (th.IsTypeDesc() || th.ContainsGenericVariables()) - { - StackSString ssBeforeTypeName, ssAfterTypeName; - TypeString::AppendType(ssBeforeTypeName, m_currObject->GetTypeHandle(), TypeString::FormatNamespace | TypeString::FormatFullInst); - TypeString::AppendType(ssAfterTypeName, th, TypeString::FormatNamespace | TypeString::FormatFullInst); - COMPlusThrow(kSerializationException, IDS_SERIALIZATION_BAD_ISER_TYPE, ssBeforeTypeName.GetUnicode(), ssAfterTypeName.GetUnicode()); - } - MethodTable *pSrvMT = th.AsMethodTable(); - _ASSERTE(pSrvMT); - -#ifdef _DEBUG - { - DefineFullyQualifiedNameForClass(); - LPCUTF8 __szTypeName = GetFullyQualifiedNameForClassNestedAware(pSrvMT); - LOG((LF_REMOTING, LL_INFO1000, "AllocateISerializable. Allocating instance of type %s.\n", &__szTypeName[0])); - } -#endif - // Allocate the object - m_newObject = m_cbInterface->AllocateObject(m_currObject, pSrvMT); - - // Add object to TSO - ISerializableInstanceInfo iserInfo(objectId, 0); - - // Check if the target object is ISerializable. If not, we need to treat construction of this object differently - if (!m_cbInterface->IsISerializableType(pSrvMT)) - { - iserInfo.SetTargetNotISerializable(); - } - TSO.Push(m_newObject, gc.fieldNames, NULL, (QueuedObjectInfo *)&iserInfo); - - LOG((LF_REMOTING, LL_INFO1000, "AllocateISerializable. Added to TSO at index %d.\n", TSO.GetCount() - 1)); - } - GCPROTECT_END(); - - return gc.fieldValues; -} - -void ObjectClone::AllocateArray() -{ - CONTRACTL - { - MODE_COOPERATIVE; - GC_TRIGGERS; - THROWS; - } - CONTRACTL_END - - LOG((LF_REMOTING, LL_INFO1000, "AllocateArray. Instance is an array type.\n")); - MethodTable *pCurrMT = m_currObject->GetMethodTable(); - _ASSERTE(pCurrMT->IsArray()); - - BASEARRAYREF refArray = (BASEARRAYREF)m_currObject; - GCPROTECT_BEGIN(refArray); - - TypeHandle elemTh = refArray->GetArrayElementTypeHandle(); - CorElementType elemType = refArray->GetArrayElementType(); - DWORD numComponents = refArray->GetNumComponents(); - - TypeHandle __elemTh = GetCorrespondingTypeForTargetDomain(elemTh); - _ASSERTE(!__elemTh.IsNull()); - - unsigned __rank = pCurrMT->GetRank(); - TypeHandle __arrayTh = ClassLoader::LoadArrayTypeThrowing(__elemTh, __rank == 1 ? ELEMENT_TYPE_SZARRAY : ELEMENT_TYPE_ARRAY, __rank); - - DWORD __numArgs = __rank*2; - INT32* __args = (INT32*) _alloca(sizeof(INT32)*__numArgs); - - if (__arrayTh.AsArray()->GetInternalCorElementType() == ELEMENT_TYPE_ARRAY) - { - const INT32* bounds = refArray->GetBoundsPtr(); - const INT32* lowerBounds = refArray->GetLowerBoundsPtr(); - for(unsigned int i=0; i < __rank; i++) - { - __args[2*i] = lowerBounds[i]; - __args[2*i+1] = bounds[i]; - } - } - else - { - __numArgs = 1; - __args[0] = numComponents; - } - m_newObject = m_cbInterface->AllocateArray(m_currObject, __arrayTh, __args, __numArgs, FALSE); - - // Treat pointer as a primitive type (we shallow copy the bits). - if (CorTypeInfo::IsPrimitiveType(elemType) || elemType == ELEMENT_TYPE_PTR) - { - LOG((LF_REMOTING, LL_INFO1000, "AllocateArray. Instance is an array of primitive type. Copying contents.\n")); - // Copy contents. - SIZE_T numBytesToCopy = refArray->GetComponentSize() * numComponents; - I1ARRAYREF refI1Arr = (I1ARRAYREF)m_newObject; - BYTE *pDest = (BYTE *)refI1Arr->GetDirectPointerToNonObjectElements(); - I1ARRAYREF refFromArr = (I1ARRAYREF)refArray; - BYTE *pSrc = (BYTE *)refFromArr->GetDirectPointerToNonObjectElements(); - - memcpyNoGCRefs(pDest, pSrc, numBytesToCopy); - m_skipFieldScan = TRUE; - } - else if (elemType == ELEMENT_TYPE_VALUETYPE) - { - if (!__elemTh.GetMethodTable()->HasFieldsWhichMustBeInited() && RemotableMethodInfo::TypeIsConduciveToBlitting(elemTh.AsMethodTable(), __elemTh.GetMethodTable())) - { - LOG((LF_REMOTING, LL_INFO1000, "AllocateArray. Instance is an array of value type with no embedded GC type. Copying contents.\n")); - // Copy contents. - SIZE_T numBytesToCopy = refArray->GetComponentSize() * numComponents; - I1ARRAYREF refI1Arr = (I1ARRAYREF)m_newObject; - BYTE *pDest = (BYTE *)refI1Arr->GetDirectPointerToNonObjectElements(); - I1ARRAYREF refFromArr = (I1ARRAYREF)refArray; - BYTE *pSrc = (BYTE *)refFromArr->GetDirectPointerToNonObjectElements(); - - memcpyNoGCRefs(pDest, pSrc, numBytesToCopy); - m_skipFieldScan = TRUE; - } - } - GCPROTECT_END(); -} - -void ObjectClone::AllocateObject() -{ - CONTRACTL - { - MODE_COOPERATIVE; - GC_TRIGGERS; - THROWS; - } - CONTRACTL_END - - LOG((LF_REMOTING, LL_INFO1000, "AllocateObject. Instance is a regular object.\n")); - MethodTable *pCurrMT = m_currObject->GetMethodTable(); - _ASSERTE(!pCurrMT->IsArray()); - _ASSERTE(!pCurrMT->IsMarshaledByRef() && !pCurrMT->IsTransparentProxy()); - _ASSERTE(!m_cbInterface->IsISerializableType(pCurrMT)); - - MethodTable *pCorrespondingMT = GetCorrespondingTypeForTargetDomain(pCurrMT); - _ASSERTE(pCorrespondingMT); - - pCorrespondingMT->EnsureInstanceActive(); - - m_newObject = m_cbInterface->AllocateObject(m_currObject, pCorrespondingMT); - - InvokeVtsCallbacks(m_newObject, RemotingVtsInfo::VTS_CALLBACK_ON_DESERIALIZING, m_toDomain); -} - -// Use this wrapper when the type handle can't be represented as a raw MethodTable (i.e. it's a pointer or array type). -TypeHandle ObjectClone::GetCorrespondingTypeForTargetDomain(TypeHandle thCli) -{ - CONTRACTL - { - MODE_COOPERATIVE; - GC_TRIGGERS; - THROWS; - } - CONTRACTL_END - - TypeHandle thBaseType = thCli; - TypeHandle thSrvType; - - // Strip off any pointer information (and record the depth). We'll put this back later (when we've translated the base type). - DWORD dwPointerDepth = 0; - while (thBaseType.IsPointer()) - { - dwPointerDepth++; - thBaseType = thBaseType.AsTypeDesc()->GetTypeParam(); - } - - // If we hit an array then we'll recursively translate the element type then build an array type out of it. - if (thBaseType.IsArray()) - { - ArrayTypeDesc *atd = (ArrayTypeDesc *)thBaseType.AsTypeDesc(); - thSrvType = GetCorrespondingTypeForTargetDomain(atd->GetArrayElementTypeHandle()); - - thSrvType = ClassLoader::LoadArrayTypeThrowing(thSrvType, atd->GetInternalCorElementType(), atd->GetRank()); - } - else - { - // We should have only unshared types if we get here. - _ASSERTE(!thBaseType.IsTypeDesc()); - thSrvType = GetCorrespondingTypeForTargetDomain(thBaseType.AsMethodTable()); - } - - // Match the level of pointer indirection from the original client type. - while (dwPointerDepth--) - { - thSrvType = thSrvType.MakePointer(); - } - - return thSrvType; -} - -MethodTable * ObjectClone::GetCorrespondingTypeForTargetDomain(MethodTable *pCliMT) -{ - CONTRACTL - { - MODE_COOPERATIVE; - GC_TRIGGERS; - THROWS; - } - CONTRACTL_END - - MethodTable *pSrvMT = NULL; - if (m_fromDomain == m_toDomain) - return pCliMT; - - _ASSERTE(m_context != ObjectFreezer); -#ifdef _DEBUG - SString __ssTypeName; - StackScratchBuffer __scratchBuf; - if (pCliMT->IsArray()) - pCliMT->_GetFullyQualifiedNameForClass(__ssTypeName); - else - pCliMT->_GetFullyQualifiedNameForClassNestedAware(__ssTypeName); -#endif - - // Take benefit of shared types. If a type is shared, and its assembly has been loaded - // in the target domain, go ahead and use the same MT ptr. - // The logic is trickier (and more expensive to calculate) for generic types, so skip the optimization there. - if (pCliMT->IsDomainNeutral() && !pCliMT->HasInstantiation()) - { - if (pCliMT->GetAssembly()->FindDomainAssembly(m_toDomain)) - { - LOG((LF_REMOTING, LL_INFO1000, - "GetCorrespondingTypeForTargetDomain. Type %s is shared. Using same MethodTable.\n", __ssTypeName.GetUTF8(__scratchBuf))); - return pCliMT; - } - } - - pSrvMT = CrossDomainTypeMap::GetMethodTableForDomain(pCliMT, m_fromDomain, m_toDomain); - if (pSrvMT) - { - LOG((LF_REMOTING, LL_INFO1000, - "GetCorrespondingTypeForTargetDomain. Found matching type for %s in domain %d from cache.\n", __ssTypeName.GetUTF8(__scratchBuf), m_toDomain)); - return pSrvMT; - } - - // Need to find the name and lookup in target domain - SString ssCliTypeName; - if (pCliMT->IsArray()) - { - pCliMT->_GetFullyQualifiedNameForClass(ssCliTypeName); - } - else if (pCliMT->HasInstantiation()) - { - TypeString::AppendType(ssCliTypeName, TypeHandle(pCliMT), TypeString::FormatNamespace | TypeString::FormatFullInst); - } - else - { - pCliMT->_GetFullyQualifiedNameForClassNestedAware(ssCliTypeName); - } - - - SString ssAssemblyName; - pCliMT->GetAssembly()->GetDisplayName(ssAssemblyName); - - // Get the assembly - TypeHandle th = GetType(ssCliTypeName, ssAssemblyName); - - if (!pCliMT->IsArray()) - { - pSrvMT = th.AsMethodTable(); - } - else - { - _ASSERTE(th.IsArray()); - TypeDesc *td = th.AsTypeDesc(); - pSrvMT = td->GetMethodTable(); - } - CrossDomainTypeMap::SetMethodTableForDomain(pCliMT, m_fromDomain, pSrvMT, m_toDomain); - LOG((LF_REMOTING, LL_INFO1000, - "GetCorrespondingTypeForTargetDomain. Loaded matching type for %s in domain %d. Added to cache.\n", __ssTypeName.GetUTF8(__scratchBuf), m_toDomain)); - return pSrvMT; -} - -TypeHandle ObjectClone::GetType(const SString &ssTypeName, const SString &ssAssemName) -{ - CONTRACTL - { - GC_TRIGGERS; - MODE_COOPERATIVE; - THROWS; - } - CONTRACTL_END; - - Assembly *pAssembly = NULL; - -#ifndef OBJECT_CLONER_STRICT_MODE - EX_TRY -#endif - { - AssemblySpec spec; - StackScratchBuffer scratchBuf; - HRESULT hr = spec.Init(ssAssemName.GetUTF8(scratchBuf)); - if (SUCCEEDED(hr)) - { - pAssembly = spec.LoadAssembly(FILE_ACTIVE); - } - else - { - COMPlusThrowHR(hr); - } - } -#ifndef OBJECT_CLONER_STRICT_MODE - EX_CATCH - { - if (GET_EXCEPTION()->IsTransient()) - { - EX_RETHROW; - } - - DomainAssembly *pDomainAssembly = NULL; -#ifdef FEATURE_FUSION - // If the normal load fails then try loading from a partial assembly name (relaxed serializer rules). - pDomainAssembly = LoadAssemblyFromPartialNameHack((SString*)&ssAssemName, TRUE); -#endif // FEATURE_FUSION - if (pDomainAssembly == NULL) - COMPlusThrow(kSerializationException, IDS_SERIALIZATION_UNRESOLVED_TYPE, - ssTypeName.GetUnicode(), ssAssemName.GetUnicode()); - else - pAssembly = pDomainAssembly->GetAssembly(); - } - EX_END_CATCH(SwallowAllExceptions); -#endif - - _ASSERTE(pAssembly); - - TypeHandle th = TypeName::GetTypeFromAssembly(ssTypeName.GetUnicode(), pAssembly); - - if (th.IsNull()) - { - COMPlusThrow(kSerializationException, IDS_SERIALIZATION_UNRESOLVED_TYPE, - ssTypeName.GetUnicode(), ssAssemName.GetUnicode()); - } - - LOG((LF_REMOTING, LL_INFO1000, "GetType. Loaded type %S from assembly %S in domain %d. \n", - ssTypeName.GetUnicode(), ssAssemName.GetUnicode(), m_toDomain->GetId().m_dwId)); - - return th; -} - -void ObjectClone::HandleISerializableFixup(OBJECTREF refParent, QueuedObjectInfo *currObjFixupInfo) -{ - CONTRACTL - { - NOTHROW; - GC_NOTRIGGER; - SO_TOLERANT; - MODE_COOPERATIVE; - } - CONTRACTL_END; - - _ASSERTE(m_context != ObjectFreezer); - - ISerializableMemberInfo *pIsInfo = (ISerializableMemberInfo *)currObjFixupInfo; - OBJECTREF refNames, refValues; - ISerializableInstanceInfo *dummy; - OBJECTREF parent; - parent = TSO.GetAt(pIsInfo->GetTableIndex(), &refNames, &refValues, (QueuedObjectInfo **)&dummy); - _ASSERTE(parent == refParent); - _ASSERTE(dummy->IsISerializableInstance()); - - PTRARRAYREF refFields = (PTRARRAYREF)refValues; - _ASSERTE(pIsInfo->GetFieldIndex() < refFields->GetNumComponents()); - refFields->SetAt(pIsInfo->GetFieldIndex(), m_newObject); - - LOG((LF_REMOTING, LL_INFO1000, "HandleISerializableFixup. Parent is ISerializable. Added field #%d to TSO record at index %d\n", pIsInfo->GetFieldIndex(), pIsInfo->GetTableIndex())); -} - -void ObjectClone::HandleArrayFixup(OBJECTREF refParent, QueuedObjectInfo *currObjFixupInfo) -{ - CONTRACTL - { - MODE_COOPERATIVE; - THROWS; - GC_TRIGGERS; - } - CONTRACTL_END - - _ASSERTE(refParent->GetMethodTable()->IsArray()); - BASEARRAYREF refParentArray = (BASEARRAYREF) refParent; - GCPROTECT_BEGIN(refParentArray); - - NDimArrayMemberInfo *pArrInfo = (NDimArrayMemberInfo *)currObjFixupInfo; - DWORD *pIndices = pArrInfo->GetIndices(); - - TypeHandle arrayElementType = refParentArray->GetArrayElementTypeHandle(); - MethodTable *pArrayMT = refParentArray->GetMethodTable(); - - DWORD Rank = pArrayMT->GetRank(); - SIZE_T Offset = 0; - SIZE_T Multiplier = 1; - - _ASSERTE(Rank == pArrInfo->GetNumDimensions()); - - for (int i = Rank-1; i >= 0; i--) { - INT32 curIndex = pIndices[i]; - const INT32 *pBoundsPtr = refParentArray->GetBoundsPtr(); - - // Bounds check each index - // Casting to unsigned allows us to use one compare for [0..limit-1] - _ASSERTE((UINT32) curIndex < (UINT32) pBoundsPtr[i]); - - Offset += curIndex * Multiplier; - Multiplier *= pBoundsPtr[i]; - } - - // The follwing code is loosely based on COMArrayInfo::SetValue - - if (!arrayElementType.IsValueType()) - { - if (!ObjIsInstanceOf(OBJECTREFToObject(m_newObject), arrayElementType)) - COMPlusThrow(kInvalidCastException,W("InvalidCast_StoreArrayElement")); - - OBJECTREF* pElem = (OBJECTREF*)(refParentArray->GetDataPtr() + (Offset * pArrayMT->GetComponentSize())); - SetObjectReference(pElem,m_newObject,GetAppDomain()); - } - else - { - // value class or primitive type - OBJECTREF* pElem = (OBJECTREF*)(refParentArray->GetDataPtr() + (Offset * pArrayMT->GetComponentSize())); - if (!arrayElementType.GetMethodTable()->UnBoxInto(pElem, m_newObject)) - COMPlusThrow(kInvalidCastException, W("InvalidCast_StoreArrayElement")); - } - - LOG((LF_REMOTING, LL_INFO1000, "HandleArrayFixup. Parent is an array. Added element at offset %d\n", Offset)); - GCPROTECT_END(); -} - -void ObjectClone::HandleObjectFixup(OBJECTREF refParent, QueuedObjectInfo *currObjFixupInfo) -{ - CONTRACTL - { - MODE_COOPERATIVE; - GC_TRIGGERS; - THROWS; - } - CONTRACTL_END - ObjectMemberInfo *pObjInfo = (ObjectMemberInfo *)currObjFixupInfo; - FieldDesc *pTargetField = pObjInfo->GetFieldDesc(); - DWORD offset = pTargetField->GetOffset(); - -#ifdef _DEBUG - MethodTable *pTemp = refParent->GetMethodTable(); - _ASSERTE(offset < pTemp->GetBaseSize()); -#endif - - GCPROTECT_BEGIN(refParent); - - TypeHandle fldType = LoadExactFieldType(pTargetField, refParent, m_toDomain); - - if (!ObjIsInstanceOf(OBJECTREFToObject(m_newObject), fldType)) - COMPlusThrow(kArgumentException,W("Arg_ObjObj")); - - OBJECTREF *pDest = (OBJECTREF *) (refParent->GetData() + offset); - _ASSERTE(GetAppDomain()==m_toDomain); - SetObjectReference(pDest, m_newObject, GetAppDomain()); - - GCPROTECT_END(); - - LOG((LF_REMOTING, LL_INFO1000, "HandleObjectFixup. Parent is a regular object. Added field at offset %d\n", offset)); -} - -#ifdef OBJECT_CLONER_STRICT_MODE -static void DECLSPEC_NORETURN ThrowMissingFieldException(FieldDesc *pFD) -{ - CONTRACTL - { - MODE_COOPERATIVE; - GC_TRIGGERS; - THROWS; - } - CONTRACTL_END; - - StackSString szField(SString::Utf8, pFD->GetName()); - - StackSString szType; - TypeString::AppendType(szType, TypeHandle(pFD->GetApproxEnclosingMethodTable())); - - COMPlusThrow(kSerializationException, - IDS_SERIALIZATION_MISSING_FIELD, - szField.GetUnicode(), - szType.GetUnicode()); -} -#endif - -void ObjectClone::ScanMemberFields(DWORD IObjRefTSOIndex, DWORD BoxedValTSOIndex) -{ - CONTRACTL - { - MODE_COOPERATIVE; - GC_TRIGGERS; - THROWS; - } - CONTRACTL_END - _ASSERTE(m_currObject != NULL); - _ASSERTE(m_newObject != NULL); - - MethodTable *pMT = m_currObject->GetMethodTable(); - _ASSERTE(!pMT->IsMarshaledByRef() && !pMT->IsTransparentProxy()); - _ASSERTE(!pMT->IsArray()); - MethodTable *pTargetMT = m_newObject->GetMethodTable(); - - DWORD numFixupsNeeded = 0; - - if (RemotableMethodInfo::TypeIsConduciveToBlitting(pMT, pTargetMT)) - { - _ASSERTE(pMT->GetAlignedNumInstanceFieldBytes() == pTargetMT->GetAlignedNumInstanceFieldBytes()); - DWORD numBytes = pMT->GetNumInstanceFieldBytes(); - BYTE *pFrom = m_currObject->GetData(); - BYTE *pTo = m_newObject->GetData(); - memcpyNoGCRefs(pTo, pFrom, numBytes); - LOG((LF_REMOTING, LL_INFO1000, "ScanMemberFields. Object has no reference type fields. Blitting contents.\n")); - } - else if (AreTypesEmittedIdentically(pMT, pTargetMT)) - { - LOG((LF_REMOTING, LL_INFO1000, "ScanMemberFields. Object not blittable but types are layed out for easy cloning .\n")); - MethodTable *pCurrMT = pMT; - MethodTable *pCurrTargetMT = pTargetMT; - while (pCurrMT) - { - DWORD numInstanceFields = pCurrMT->GetNumIntroducedInstanceFields(); - _ASSERTE(pCurrTargetMT->GetNumIntroducedInstanceFields() == numInstanceFields); - - FieldDesc *pFields = pCurrMT->GetApproxFieldDescListRaw(); - FieldDesc *pTargetFields = pCurrTargetMT->GetApproxFieldDescListRaw(); - - for (DWORD i = 0; i < numInstanceFields; i++) - { - if (pFields[i].IsNotSerialized()) - { - LOG((LF_REMOTING, LL_INFO1000, "ScanMemberFields. Field %s is marked NonSerialized. Skipping.\n", pFields[i].GetName())); - continue; - } - - numFixupsNeeded += CloneField(&pFields[i], &pTargetFields[i]); - } - - pCurrMT = pCurrMT->GetParentMethodTable(); - pCurrTargetMT = pCurrTargetMT->GetParentMethodTable(); - } - } - else - { - LOG((LF_REMOTING, LL_INFO1000, "ScanMemberFields. Object type layout is different.\n")); - - // The object types between source and destination have significant differences (some fields may be added, removed or - // re-ordered, the type hierarchy may have had layers added or removed). We can still clone the object if every non-optional - // field in the destination object can be found and serialized in a type with the same name in the source object. We ignore - // fields and entire type layers that have been added in the source object and also any fields or layers that have been - // removed as long as they don't include any fields that are mandatory in the destination object. We allow the fields within - // a type layer to move around (we key the field by name only, the latter stage of cloning will check type equivalency and - // as above we will widen primitive types if necessary). Since it requires significant effort to calculate whether the - // objects can be cloned (and then locate corresponding fields in order to do so) we cache a mapping of source object fields - // to destination object fields. - - // The following call will return such a mapping (it's an array where each entry is a pointer to a source object field desc - // and the entries are in destination field index order, most derived type first, followed by second most derived type - // etc.). If a mapping is impossible the method will throw. - FieldDesc **pFieldMap = CrossDomainFieldMap::LookupOrCreateFieldMapping(pTargetMT, pMT); - DWORD dwMapIndex = 0; - - MethodTable *pDstMT = pTargetMT; - while (pDstMT) - { - FieldDesc *pDstFields = pDstMT->GetApproxFieldDescListRaw(); - DWORD numInstanceFields = pDstMT->GetNumIntroducedInstanceFields(); - - for (DWORD i = 0; i < numInstanceFields; i++) - { - FieldDesc *pSrcField = pFieldMap[dwMapIndex++]; - - // Non-serialized fields in the destination type (or optional fields where the source type doesn't have an - // equivalent) don't have a source field desc. - if (pSrcField == NULL) - continue; - - numFixupsNeeded += CloneField(pSrcField, &pDstFields[i]); - } - - pDstMT = pDstMT->GetParentMethodTable(); - } - - _ASSERTE(dwMapIndex == pTargetMT->GetNumInstanceFields()); - } - - if (numFixupsNeeded > 0) - { - ParentInfo fxInfo(numFixupsNeeded); - if (IObjRefTSOIndex != (DWORD) -1) - { - _ASSERTE(m_cbInterface->IsIObjectReferenceType(pMT)); - fxInfo.SetIsIObjRefInstance(); - fxInfo.SetIObjRefIndexIntoTSO(IObjRefTSOIndex); - } - if (BoxedValTSOIndex != (DWORD) -1) - { - _ASSERTE(pMT->IsValueType()); - fxInfo.SetNeedsUnboxing(); - fxInfo.SetBoxedValIndexIntoTSO(BoxedValTSOIndex); - } - QOF.Enqueue(m_newObject, NULL, NULL, (QueuedObjectInfo *)&fxInfo); - LOG((LF_REMOTING, LL_INFO1000, "ScanMemberFields. Current object had total of %d reference type fields. Adding to QOF.\n", numFixupsNeeded)); - // Delay calling any OnDeserialized callbacks until the end of the cloning operation (it's difficult to tell when all the - // children have been deserialized). - if (HasVtsCallbacks(m_newObject->GetMethodTable(), RemotingVtsInfo::VTS_CALLBACK_ON_DESERIALIZED)) - VDC.Enqueue(m_newObject, NULL, NULL, NULL); - if (m_cbInterface->RequiresDeserializationCallback(m_newObject->GetMethodTable())) - { - LOG((LF_REMOTING, LL_INFO1000, "ScanMemberFields. Adding object to Table of IDeserialization Callbacks\n")); - QueuedObjectInfo noInfo; - TDC.Enqueue(m_newObject, NULL, NULL, &noInfo); - } - } - else - { - // This is effectively a leaf node (no complex children) so if the type has a callback for OnDeserialized we'll deliver it - // now. This fixes callback ordering for a few more edge cases (e.g. VSW 415611) and is reasonably cheap. We can never do a - // perfect job (in the presence of object graph cycles) and a near perfect job (intuitively ordered callbacks for acyclic - // object graphs) is prohibitively expensive; so we're stuck with workarounds like this. - InvokeVtsCallbacks(m_newObject, RemotingVtsInfo::VTS_CALLBACK_ON_DESERIALIZED, m_toDomain); - if (m_cbInterface->RequiresDeserializationCallback(m_newObject->GetMethodTable())) - MakeIDeserializationCallback(m_newObject); - } -} - -DWORD ObjectClone::CloneField(FieldDesc *pSrcField, FieldDesc *pDstField) -{ - CONTRACTL - { - MODE_COOPERATIVE; - GC_TRIGGERS; - THROWS; - } - CONTRACTL_END; - - BOOL bFixupNeeded = FALSE; - - CorElementType srcType = pSrcField->GetFieldType(); - CorElementType dstType = pDstField->GetFieldType(); - DWORD srcOffset = pSrcField->GetOffset(); - DWORD dstOffset = pDstField->GetOffset(); - - BOOL bUseWidenedValue = FALSE; - ARG_SLOT fieldData = 0; - if (srcType != dstType) - { - void *pData = m_currObject->GetData() + srcOffset; - - MethodTable *pSrcFieldMT = NULL; - if (CorTypeInfo::IsPrimitiveType(srcType)) - pSrcFieldMT = MscorlibBinder::GetElementType(srcType); - else - pSrcFieldMT = LoadExactFieldType(pSrcField, m_currObject, m_fromDomain).AsMethodTable(); - - LOG((LF_REMOTING, LL_INFO1000, "ScanMemberFields. Field %s has differing types at source and destination. Will try to convert.\n", pSrcField->GetName())); - fieldData = HandleFieldTypeMismatch(dstType, srcType, pData, pSrcFieldMT); - bUseWidenedValue = TRUE; - } - - switch (dstType) - { - case ELEMENT_TYPE_I1: - case ELEMENT_TYPE_U1: - case ELEMENT_TYPE_BOOLEAN: - { - BYTE *pDest = m_newObject->GetData() + dstOffset; - if (bUseWidenedValue) - *pDest = (unsigned char) fieldData; - else - { - BYTE *pByte = m_currObject->GetData() + srcOffset; - *pDest = *pByte; - } - } - break; - case ELEMENT_TYPE_I2: - case ELEMENT_TYPE_U2: - case ELEMENT_TYPE_CHAR: - { - WORD *pDest = (WORD*)(m_newObject->GetData() + dstOffset); - if (bUseWidenedValue) - *pDest = (short) fieldData; - else - { - WORD *pWord = (WORD*)(m_currObject->GetData() + srcOffset); - *(pDest) = *pWord; - } - } - break; - case ELEMENT_TYPE_I4: - case ELEMENT_TYPE_U4: - case ELEMENT_TYPE_R4: - IN_WIN32(case ELEMENT_TYPE_FNPTR:) - IN_WIN32(case ELEMENT_TYPE_I:) - IN_WIN32(case ELEMENT_TYPE_U:) - { - DWORD *pDest = (DWORD*)(m_newObject->GetData() + dstOffset); - if (bUseWidenedValue) - *pDest = (int) fieldData; - else - { - DWORD *pDword = (DWORD*)(m_currObject->GetData() + srcOffset); - *(pDest) = *pDword; - } - } - break; - case ELEMENT_TYPE_R8: - case ELEMENT_TYPE_I8: - case ELEMENT_TYPE_U8: - IN_WIN64(case ELEMENT_TYPE_FNPTR:) - IN_WIN64(case ELEMENT_TYPE_I:) - IN_WIN64(case ELEMENT_TYPE_U:) - { - INT64 *pDest = (INT64*)(m_newObject->GetData() + dstOffset); - if (bUseWidenedValue) - *pDest = fieldData; - else - { - INT64 *pLong = (INT64*)(m_currObject->GetData() + srcOffset); - *(pDest) = *pLong; - } - } - break; - case ELEMENT_TYPE_PTR: - { - void **pDest = (void**)(m_newObject->GetData() + dstOffset); - void **pPtr = (void**)(m_currObject->GetData() + srcOffset); - *(pDest) = *pPtr; - } - break; - case ELEMENT_TYPE_STRING: - case ELEMENT_TYPE_CLASS: // objectrefs - case ELEMENT_TYPE_OBJECT: - case ELEMENT_TYPE_SZARRAY: // single dim, zero - case ELEMENT_TYPE_ARRAY: // all other arrays - { - OBJECTREF *pSrc = (OBJECTREF *)(m_currObject->GetData() + srcOffset); - OBJECTREF *pDest = (OBJECTREF *)(m_newObject->GetData() + dstOffset); - - if ((*pSrc) == NULL) - break; - - // If no deep copy is required, just copy the reference - if (!m_cbInterface->RequiresDeepCopy(*pSrc)) - { - _ASSERTE(GetAppDomain()==m_toDomain); - SetObjectReference(pDest, *pSrc, GetAppDomain()); - break; - } - - // Special case String - if ((*pSrc)->GetMethodTable() == g_pStringClass) - { - // Better check the destination really expects a string (or maybe an object). - TypeHandle thDstField = LoadExactFieldType(pDstField, m_newObject, m_toDomain); - if (thDstField != TypeHandle(g_pStringClass) && thDstField != TypeHandle(g_pObjectClass)) - COMPlusThrow(kArgumentException, W("Arg_ObjObj")); - - STRINGREF refStr = (STRINGREF) *pSrc; - refStr = m_cbInterface->AllocateString(refStr); - // Get dest addr again, as a GC might have occurred - pDest = (OBJECTREF *)(m_newObject->GetData() + dstOffset); - _ASSERTE(GetAppDomain()==m_toDomain); - SetObjectReference(pDest, refStr, GetAppDomain()); - - break; - } - - // Add the object to QOM - LOG((LF_REMOTING, LL_INFO1000, "ScanMemberFields. Adding object in field %s to Queue of Objects to be Marshalled.\n", pSrcField->GetName())); - ObjectMemberInfo objInfo(pDstField); - bFixupNeeded = TRUE; - QOM.Enqueue(*pSrc, NULL, NULL, (QueuedObjectInfo *)&objInfo); - } - break; - - case ELEMENT_TYPE_VALUETYPE: - { - TypeHandle th = LoadExactFieldType(pSrcField, m_currObject, m_fromDomain); - _ASSERTE(!th.AsMethodTable()->IsByRefLike() && "Field types cannot contain stack pointers."); - - TypeHandle thTarget = LoadExactFieldType(pDstField, m_newObject, m_toDomain); - - MethodTable *pValueClassMT = th.AsMethodTable(); - MethodTable *pValueClassTargetMT = thTarget.AsMethodTable(); - if (!RemotableMethodInfo::TypeIsConduciveToBlitting(pValueClassMT, pValueClassTargetMT)) - { - // Needs marshalling - // We're allocating an object in the "to" domain - // using a type from the "from" domain. - OBJECTREF refTmpBox = BoxValueTypeInWrongDomain(m_currObject, srcOffset, pValueClassMT); - - // Nullable<T> might return null here. In that case we don't need to do anything - // and the null value otherwise confuxes the fixup queue. - if (refTmpBox != NULL) - { - // Add the object to QOM - ObjectMemberInfo objInfo(pDstField); - objInfo.SetNeedsUnboxing(); - bFixupNeeded = TRUE; - QOM.Enqueue(refTmpBox, NULL, NULL, (QueuedObjectInfo *)&objInfo); - LOG((LF_REMOTING, LL_INFO1000, "ScanMemberFields. Value type field %s has reference type contents. Boxing and adding to QOM.\n", pSrcField->GetName())); - } - } - else - { - DWORD numBytesToCopy = th.AsMethodTable()->GetNumInstanceFieldBytes(); - BYTE *pByte = m_currObject->GetData() + srcOffset; - BYTE *pDest = m_newObject->GetData() + dstOffset; - memcpyNoGCRefs(pDest, pByte, numBytesToCopy); - LOG((LF_REMOTING, LL_INFO1000, "ScanMemberFields. Value type field %s has no reference type contents. Blitting.\n", pSrcField->GetName())); - } - } - break; - default: - _ASSERTE(!"Unknown element type seen in ObjectClone::ScanMemberFields"); - break; - } - - return bFixupNeeded ? 1 : 0; -} - -BOOL ObjectClone::AreTypesEmittedIdentically(MethodTable *pMT1, MethodTable *pMT2) -{ - LIMITED_METHOD_CONTRACT; - - // Identical here means that both types have the same hierarchy (depth and names match) and that each level of the hierarchy has - // the same fields (by name) at the same index. - // We're going to be called quite frequently (once per call to ScanMemberFields) so until we're convinced that caching this - // information is worth it we'll just compute the fast cases here and let the rest fall through to the slower technique. The - // fast check is that the types are shared and identical or that they're loaded from the same file (in which case we have to be - // a little more paranoid and check up the hierarchy). - if (pMT1 == pMT2) - return TRUE; - - // While the current level of the type is loaded from the same file... - // Note that we used to check that the assemblies were the same; now we're more paranoid and check the actual modules scoping - // the type are identical. This closes a security hole where identically named types in different modules of the same assembly - // could cause the wrong type to be loaded in the server context allowing violation of the type system. - while (pMT1->GetModule()->GetFile()->Equals(pMT2->GetModule()->GetFile())) - { - // Inspect the parents. - pMT1 = pMT1->GetParentMethodTable(); - pMT2 = pMT2->GetParentMethodTable(); - - // If the parents are the same shared type (e.g. Object), then we've found a match. - if (pMT1 == pMT2) - return TRUE; - - // Else check if one of the hierarchies has run out before the other (and therefore can't be equivalent). - if (pMT1 == NULL || pMT2 == NULL) - return FALSE; - } - - return FALSE; -} - -BOOL AreTypesEquivalent(MethodTable *pMT1, MethodTable *pMT2) -{ - CONTRACTL - { - MODE_COOPERATIVE; - GC_TRIGGERS; - THROWS; - } - CONTRACTL_END; - - // Equivalent here is quite a weak predicate. All it means is that the types have the same (fully assembly qualified) name. The - // derivation hierarchy is not inspected at all. - StackSString szType1; - StackSString szType2; - - TypeString::AppendType(szType1, TypeHandle(pMT1), TypeString::FormatNamespace | - TypeString::FormatFullInst | - TypeString::FormatAssembly | - TypeString::FormatNoVersion); - TypeString::AppendType(szType2, TypeHandle(pMT2), TypeString::FormatNamespace | - TypeString::FormatFullInst | - TypeString::FormatAssembly | - TypeString::FormatNoVersion); - - return szType1.Equals(szType2); -} - -PtrHashMap *CrossDomainFieldMap::s_pFieldMap = NULL; -SimpleRWLock *CrossDomainFieldMap::s_pFieldMapLock = NULL; - -BOOL CrossDomainFieldMap::CompareFieldMapEntry(UPTR val1, UPTR val2) -{ - CONTRACTL { - MODE_ANY; - NOTHROW; - GC_NOTRIGGER; - SO_TOLERANT; - } - CONTRACTL_END; - - CrossDomainFieldMap::FieldMapEntry *pEntry1 = (CrossDomainFieldMap::FieldMapEntry *)(val1 << 1); - CrossDomainFieldMap::FieldMapEntry *pEntry2 = (CrossDomainFieldMap::FieldMapEntry *)val2; - - if (pEntry1->m_pSrcMT == pEntry2->m_pSrcMT && - pEntry1->m_pDstMT == pEntry2->m_pDstMT) - return TRUE; - - return FALSE; -} - -CrossDomainFieldMap::FieldMapEntry::FieldMapEntry(MethodTable *pSrcMT, MethodTable *pDstMT, FieldDesc **pFieldMap) -{ - WRAPPER_NO_CONTRACT; - - m_pSrcMT = pSrcMT; - m_pDstMT = pDstMT; - m_pFieldMap = pFieldMap; - BaseDomain *pSrcDomain = pSrcMT->GetDomain(); - m_dwSrcDomain = pSrcDomain->IsAppDomain() ? ((AppDomain*)pSrcDomain)->GetId() : ADID(0); - BaseDomain *pDstDomain = pDstMT->GetDomain(); - m_dwDstDomain = pDstDomain->IsAppDomain() ? ((AppDomain*)pDstDomain)->GetId() : ADID(0); -} - -static BOOL IsOwnerOfRWLock(LPVOID lock) -{ - // @TODO - SimpleRWLock does not have knowledge of which thread gets the writer - // lock, so no way to verify - return TRUE; -} - -// Remove any entries in the table that refer to an appdomain that is no longer live. -void CrossDomainFieldMap::FlushStaleEntries() -{ - if (s_pFieldMapLock == NULL || s_pFieldMap == NULL) - return; - - SimpleWriteLockHolder swlh(s_pFieldMapLock); - - bool fDeletedEntry = false; - PtrHashMap::PtrIterator iter = s_pFieldMap->begin(); - while (!iter.end()) - { - FieldMapEntry *pEntry = (FieldMapEntry *)iter.GetValue(); - AppDomainFromIDHolder adFrom(pEntry->m_dwSrcDomain, TRUE); - AppDomainFromIDHolder adTo(pEntry->m_dwDstDomain, TRUE); - if (adFrom.IsUnloaded() || - adTo.IsUnloaded()) //we do not use ptr for anything - { -#ifdef _DEBUG - LPVOID pDeletedEntry = -#endif - s_pFieldMap->DeleteValue(pEntry->GetHash(), pEntry); - _ASSERTE(pDeletedEntry == pEntry); - delete pEntry; - fDeletedEntry = true; - } - ++iter; - } - - if (fDeletedEntry) - s_pFieldMap->Compact(); -} - -FieldDesc **CrossDomainFieldMap::LookupOrCreateFieldMapping(MethodTable *pDstMT, MethodTable *pSrcMT) -{ - CONTRACTL - { - MODE_COOPERATIVE; - GC_TRIGGERS; - THROWS; - } - CONTRACTL_END; - - // We lazily allocate the reader/writer lock we synchronize access to the hash with. - if (s_pFieldMapLock == NULL) - { - void *pLockSpace = SystemDomain::GetGlobalLoaderAllocator()->GetLowFrequencyHeap()->AllocMem(S_SIZE_T(sizeof(SimpleRWLock))); - SimpleRWLock *pLock = new (pLockSpace) SimpleRWLock(COOPERATIVE_OR_PREEMPTIVE, LOCK_TYPE_DEFAULT); - - if (FastInterlockCompareExchangePointer(&s_pFieldMapLock, pLock, NULL) != NULL) - // We lost the race, give up our copy. - SystemDomain::GetGlobalLoaderAllocator()->GetLowFrequencyHeap()->BackoutMem(pLockSpace, sizeof(SimpleRWLock)); - } - - // Now we have a lock we can use to synchronize the remainder of the init. - if (s_pFieldMap == NULL) - { - SimpleWriteLockHolder swlh(s_pFieldMapLock); - - if (s_pFieldMap == NULL) - { - PtrHashMap *pMap = new (SystemDomain::GetGlobalLoaderAllocator()->GetLowFrequencyHeap()) PtrHashMap(); - LockOwner lock = {s_pFieldMapLock, IsOwnerOfRWLock}; - pMap->Init(32, CompareFieldMapEntry, TRUE, &lock); - s_pFieldMap = pMap; - } - } - else - { - // Try getting an existing value first. - - FieldMapEntry sEntry(pSrcMT, pDstMT, NULL); - - SimpleReadLockHolder srlh(s_pFieldMapLock); - FieldMapEntry *pFound = (FieldMapEntry *)s_pFieldMap->LookupValue(sEntry.GetHash(), (LPVOID)&sEntry); - if (pFound != (FieldMapEntry *)INVALIDENTRY) - return pFound->m_pFieldMap; - } - - // We couldn't find an existing entry in the hash. Now we must go through the painstaking process of matching fields in the - // destination object to their counterparts in the source object. We build an array of pointers to source field descs ordered by - // destination type field index (all the fields for the most derived type first, then all the fields for the second most derived - // type etc.). - NewArrayHolder<FieldDesc*> pFieldMap(new FieldDesc*[pDstMT->GetNumInstanceFields()]); - DWORD dwMapIndex = 0; - - // We start with the source and destination types for the object (which we know are equivalent at least in type name). For each - // layer of the type hierarchy for the destination object (from the instance type through to Object) we attempt to locate the - // corresponding source type in the hierarchy. This is non-trivial since either source or destination type hierarchies may have - // added or removed layers. We ignore extra type layers in the source hierarchy and just concentrate on destination type layers - // that introduce instance fields that are not marked NotSerializable. For each such layer we first locate the corresponding - // source layer (via fully qualified type name) and then map each serialized (and possibly optional) destination field to the - // corresponding source field (again by name). We don't allow a field to move around the type hierarchy (i.e. a field defined in - // the base class in one version can't move to a derived type in later versions and be recognized as the original field). - // Allowing this would introduce all sorts of ambiguity problems (consider the case of private fields all with the same name - // implemented at every layer of the type hierarchy). - - bool fFirstPass = true; - MethodTable *pCurrDstMT = pDstMT; - MethodTable *pCurrSrcMT = pSrcMT; - while (pCurrDstMT) - { - DWORD numInstanceFields = pCurrDstMT->GetNumIntroducedInstanceFields(); - - // Skip destination types with no instance fields to clone. - if (numInstanceFields == 0) - { - pCurrDstMT = pCurrDstMT->GetParentMethodTable(); - // Only safe to skip the source type as well on the first pass (the source version may have eliminated this level of - // the type hierarchy). - if (fFirstPass) - pCurrSrcMT = pCurrSrcMT->GetParentMethodTable(); - fFirstPass = false; - continue; - } - - // We need to synchronize the source type with the destination type. This means skipping any source types in the - // hierarchy that the destination doesn't know about. - MethodTable *pCandidateMT = pCurrSrcMT; - while (pCandidateMT) - { - if (fFirstPass || pCandidateMT == pCurrDstMT || AreTypesEquivalent(pCandidateMT, pCurrDstMT)) - { - // Skip intermediate source types (the destination type didn't know anything about them, so they're surplus - // to requirements). - pCurrSrcMT = pCandidateMT; - break; - } - - pCandidateMT = pCandidateMT->GetParentMethodTable(); - } - -#ifdef OBJECT_CLONER_STRICT_MODE - // If there's no candidate source type equivalent to the current destination type we need to prove that the destination - // type has no mandatory instance fields or throw an exception (since there's no place to fetch the field values from). - if (pCandidateMT == NULL) - { - FieldDesc *pFields = pCurrDstMT->GetApproxFieldDescListRaw(); - - for (DWORD i = 0; i < numInstanceFields; i++) - { - if (pFields[i].IsNotSerialized() || pFields[i].IsOptionallySerialized()) - { - pFieldMap[dwMapIndex++] = NULL; - continue; - } - - // We've found a field that must be cloned but have no corresponding source-side type to clone it from. Raise an - // exception. - ThrowMissingFieldException(&pFields[i]); - } - - // If we get here we know the current destination type level was effectively a no-op. Move onto the next level. - pCurrDstMT = pCurrDstMT->GetParentMethodTable(); - fFirstPass = false; - continue; - } -#else - // In lax matching mode we can ignore all fields, even those not marked optional. So the lack of an equivalent type in the - // source hierarchy doesn't bother us. Mark all fields as having a default value and then move onto the next level in the - // type hierarchy. - if (pCandidateMT == NULL) - { - for (DWORD i = 0; i < numInstanceFields; i++) - pFieldMap[dwMapIndex++] = NULL; - - pCurrDstMT = pCurrDstMT->GetParentMethodTable(); - fFirstPass = false; - continue; - } -#endif - - // If we get here we have equivalent types in pCurrDstMT and pCurrSrcMT. Now we need to locate the source field desc - // corresponding to every mandatory (and possibly optional) field in the destination type and record it in the field map. - DWORD numSrcFields = pCurrSrcMT->GetNumIntroducedInstanceFields(); - DWORD numDstFields = pCurrDstMT->GetNumIntroducedInstanceFields(); - - FieldDesc *pDstFields = pCurrDstMT->GetApproxFieldDescListRaw(); - FieldDesc *pSrcFields = pCurrSrcMT->GetApproxFieldDescListRaw(); - - for (DWORD i = 0; i < numDstFields; i++) - { - // Non-serialized destination fields aren't filled in from source types. - if (pDstFields[i].IsNotSerialized()) - { - pFieldMap[dwMapIndex++] = NULL; - continue; - } - - // Go look for a field in the source type with the same name. - LPCUTF8 szDstFieldName = pDstFields[i].GetName(); - DWORD j; - for (j = 0; j < numSrcFields; j++) - { - LPCUTF8 szSrcFieldName = pSrcFields[j].GetName(); - if (strcmp(szDstFieldName, szSrcFieldName) == 0) - { - // Check that the field isn't marked NotSerialized (if it is then it's invisible to the cloner). - if (pSrcFields[j].IsNotSerialized()) - j = numSrcFields; - break; - } - } - -#ifdef OBJECT_CLONER_STRICT_MODE - // If we didn't find a corresponding field it might not be fatal; the field could be optionally serializable from the - // destination type's point of view. - if (j == numSrcFields) - { - if (pDstFields[i].IsOptionallySerialized()) - { - pFieldMap[dwMapIndex++] = NULL; - continue; - } - // The field was required. Throw an exception. - ThrowMissingFieldException(&pDstFields[i]); - } -#else - // In lax matching mode we can ignore all fields, even those not marked optional. Simply mark this field as having the - // default value. - if (j == numSrcFields) - { - pFieldMap[dwMapIndex++] = NULL; - continue; - } -#endif - - // Otherwise we found matching fields (in name at least, type processing is done later). - pFieldMap[dwMapIndex++] = &pSrcFields[j]; - } - - pCurrDstMT = pCurrDstMT->GetParentMethodTable(); - pCurrSrcMT = pCurrSrcMT->GetParentMethodTable(); - fFirstPass = false; - } - - _ASSERTE(dwMapIndex == pDstMT->GetNumInstanceFields()); - - // Now we have a field map we should insert it into the hash. - NewHolder<FieldMapEntry> pEntry(new FieldMapEntry(pSrcMT, pDstMT, pFieldMap)); - PREFIX_ASSUME(pEntry != NULL); - pFieldMap.SuppressRelease(); - - SimpleWriteLockHolder swlh(s_pFieldMapLock); - - UPTR key = pEntry->GetHash(); - - FieldMapEntry *pFound = (FieldMapEntry *)s_pFieldMap->LookupValue(key, (LPVOID)pEntry); - if (pFound == (FieldMapEntry *)INVALIDENTRY) - { - s_pFieldMap->InsertValue(key, (LPVOID)pEntry); - pEntry.SuppressRelease(); - return pFieldMap; - } - else - return pFound->m_pFieldMap; -} - -ARG_SLOT ObjectClone::HandleFieldTypeMismatch(CorElementType dstType, CorElementType srcType, void *pData, MethodTable *pSrcMT) -{ - CONTRACTL - { - MODE_COOPERATIVE; - GC_TRIGGERS; - THROWS; - } - CONTRACTL_END - _ASSERTE(m_context != ObjectFreezer); - ARG_SLOT data = 0; - InvokeUtil::CreatePrimitiveValue(dstType, srcType, pData, pSrcMT, &data); - return data; -} - -void ObjectClone::ScanISerializableMembers(DWORD IObjRefTSOIndex, DWORD ISerTSOIndex, DWORD BoxedValTSOIndex, PTRARRAYREF refValues) -{ - CONTRACTL - { - MODE_COOPERATIVE; - GC_TRIGGERS; - THROWS; - } - CONTRACTL_END - - _ASSERTE(m_context != ObjectFreezer); - // Queue the non-primitive types - DWORD numFieldsToBeMarshalled = 0; - PTRARRAYREF refNewValues = NULL; - - LOG((LF_REMOTING, LL_INFO1000, "ScanISerializableMembers. Scanning members of ISerializable type object.\n")); - GCPROTECT_BEGIN(refValues); - - refNewValues = (PTRARRAYREF) AllocateObjectArray(refValues->GetNumComponents(), g_pObjectClass, FALSE); - - _ASSERTE(refNewValues != NULL); - - for (DWORD index = 0; index < refValues->GetNumComponents(); index++) - { - OBJECTREF refField = refValues->GetAt(index); - if (refField == NULL) - continue; - - if (CorTypeInfo::IsPrimitiveType(refField->GetTypeHandle().GetSignatureCorElementType()) || - refField->GetMethodTable() == g_pStringClass) - { - refNewValues->SetAt(index, refField); - continue; - } - - ISerializableMemberInfo isInfo(ISerTSOIndex, index); - QOM.Enqueue(refField, NULL, NULL, (QueuedObjectInfo *) &isInfo); - numFieldsToBeMarshalled++; - refNewValues->SetAt(index, NULL); - LOG((LF_REMOTING, LL_INFO1000, "ScanISerializableMembers. Member at index %d is reference type. Adding to QOM.\n", index)); - } - GCPROTECT_END(); - - // Update TSO - OBJECTREF refNames = NULL, refFields = NULL; - QueuedObjectInfo *pDummy; - OBJECTREF newObj; - newObj = TSO.GetAt(ISerTSOIndex, &refNames, &refFields, &pDummy); - _ASSERTE(newObj == m_newObject); - - TSO.SetAt(ISerTSOIndex, m_newObject, refNames, refNewValues, pDummy); - - if (numFieldsToBeMarshalled > 0) - { - ParentInfo fxInfo(numFieldsToBeMarshalled); - fxInfo.SetIsISerializableInstance(); - fxInfo.SetIObjRefIndexIntoTSO(IObjRefTSOIndex); - fxInfo.SetISerIndexIntoTSO(ISerTSOIndex); - fxInfo.SetBoxedValIndexIntoTSO(BoxedValTSOIndex); - QOF.Enqueue(m_newObject, NULL, NULL, (QueuedObjectInfo *) &fxInfo); - LOG((LF_REMOTING, LL_INFO1000, "ScanISerializableMembers. Current object had total of %d reference type fields. Adding to QOF.\n", numFieldsToBeMarshalled)); - // Delay calling any OnDeserialized callbacks until the end of the cloning operation (it's difficult to tell when all the - // children have been deserialized). - if (HasVtsCallbacks(m_newObject->GetMethodTable(), RemotingVtsInfo::VTS_CALLBACK_ON_DESERIALIZED)) - VDC.Enqueue(m_newObject, NULL, NULL, NULL); - if (m_cbInterface->RequiresDeserializationCallback(m_newObject->GetMethodTable())) - { - LOG((LF_REMOTING, LL_INFO1000, "ScanISerializableMembers. Adding object to Table of IDeserialization Callbacks\n")); - QueuedObjectInfo noInfo; - TDC.Enqueue(m_newObject, NULL, NULL, &noInfo); - } - } - else - { - // This is effectively a leaf node (no complex children) so if the type has a callback for OnDeserialized we'll deliver it - // now. This fixes callback ordering for a few more edge cases (e.g. VSW 415611) and is reasonably cheap. We can never do a - // perfect job (in the presence of object graph cycles) and a near perfect job (intuitively ordered callbacks for acyclic - // object graphs) is prohibitively expensive; so we're stuck with workarounds like this. - InvokeVtsCallbacks(m_newObject, RemotingVtsInfo::VTS_CALLBACK_ON_DESERIALIZED, m_toDomain); - if (m_cbInterface->RequiresDeserializationCallback(m_newObject->GetMethodTable())) - MakeIDeserializationCallback(m_newObject); - } -} - -void ObjectClone::ScanArrayMembers() -{ - CONTRACTL - { - MODE_COOPERATIVE; - GC_TRIGGERS; - THROWS; - } - CONTRACTL_END -#ifdef _DEBUG - MethodTable *pCurrMT = m_currObject->GetMethodTable(); - _ASSERTE(pCurrMT && pCurrMT->IsArray()); - MethodTable *pNewMT = m_newObject->GetMethodTable(); - _ASSERTE(pNewMT && pNewMT->IsArray()); -#endif - - LOG((LF_REMOTING, LL_INFO1000, "ScanArrayMembers. Scanning members of array object.\n")); - BASEARRAYREF refFromArray = (BASEARRAYREF) m_currObject; - BASEARRAYREF refToArray = (BASEARRAYREF) m_newObject; - - GCPROTECT_BEGIN(refFromArray); - GCPROTECT_BEGIN(refToArray); - - TypeHandle toArrayElementType = refToArray->GetArrayElementTypeHandle(); - DWORD numComponents = refFromArray->GetNumComponents(); - MethodTable *pArrayMT = refFromArray->GetMethodTable(); - - DWORD rank = pArrayMT->GetRank(); - DWORD dwOffset = 0; - - DWORD *pIndices = (DWORD*) _alloca(sizeof(DWORD) * rank); - VOID *pTemp = _alloca(sizeof(NDimArrayMemberInfo) + rank * sizeof(DWORD)); - NDimArrayMemberInfo *pArrInfo = new (pTemp) NDimArrayMemberInfo(rank); - - bool boxingObjects = (pArrayMT->GetArrayElementType() == ELEMENT_TYPE_VALUETYPE); - - // Must enter the from domain if we are going to be allocating any non-agile boxes - ENTER_DOMAIN_PTR_PREDICATED(m_fromDomain,ADV_RUNNINGIN,boxingObjects); - - if (boxingObjects) - { - pArrInfo->SetNeedsUnboxing(); - - // We may be required to activate value types of array elements, since we - // are going to box them. Hoist out the required domain transition and - // activation. - - MethodTable *pMT = ((BASEARRAYREF)m_currObject)->GetArrayElementTypeHandle().GetMethodTable(); - pMT->EnsureInstanceActive(); - } - - DWORD numFixupsNeeded = 0; - for (DWORD i = 0; i < numComponents; i++) - { - // The array could be huge. To avoid keeping a pending GC waiting (and maybe timing out) we're going to pulse the GC mode - // every so often. Do this more freqeuntly in debug builds, where each iteration through this loop takes considerably - // longer. -#ifdef _DEBUG -#define COPY_CYCLES 1024 -#else -#define COPY_CYCLES 8192 -#endif - if ((i % COPY_CYCLES) == (COPY_CYCLES - 1)) - GetThread()->PulseGCMode(); - - const INT32 *pBoundsPtr = refFromArray->GetBoundsPtr(); - DWORD findIndices = i; - for (DWORD rankIndex = rank; rankIndex > 0; rankIndex--) - { - DWORD numElementsInDimension = pBoundsPtr[rankIndex - 1]; - DWORD quotient = findIndices / numElementsInDimension; - DWORD remainder = findIndices % numElementsInDimension; - pIndices[rankIndex - 1] = remainder; - findIndices = quotient; - } - - pArrInfo->SetIndices(pIndices); - - Object *rv = GetObjectFromArray((BASEARRAYREF *)&m_currObject, dwOffset); - if (rv != NULL) - { - OBJECTREF oRef = ObjectToOBJECTREF(rv); - - if (oRef->GetMethodTable() == g_pStringClass && m_context != ObjectFreezer) - { - OBJECTREF* pElem = (OBJECTREF*)(refToArray->GetDataPtr() + (dwOffset * pArrayMT->GetComponentSize())); - SetObjectReference(pElem,oRef,GetAppDomain()); - } - else - { - // Add the object to QOM - numFixupsNeeded++; - QOM.Enqueue(oRef, NULL, NULL, pArrInfo); - LOG((LF_REMOTING, LL_INFO1000, "ScanArrayMembers. Element at offset %d is reference type. Adding to QOM.\n", dwOffset)); - } - } - dwOffset ++; - } - - if (numFixupsNeeded > 0) - { - ParentInfo fxInfo(numFixupsNeeded); - QOF.Enqueue(m_newObject, NULL, NULL, (QueuedObjectInfo *)&fxInfo); - LOG((LF_REMOTING, LL_INFO1000, "ScanArrayMembers. Current object had total of %d reference type fields. Adding to QOF.\n", numFixupsNeeded)); - } - - END_DOMAIN_TRANSITION; - - GCPROTECT_END(); - GCPROTECT_END(); -} - -#ifdef _MSC_VER -#pragma warning(push) -#pragma warning(disable:4244) -#endif // _MSC_VER -Object *ObjectClone::GetObjectFromArray(BASEARRAYREF* arrObj, DWORD dwOffset) -{ - CONTRACTL { - THROWS; - if ((*arrObj)->GetArrayElementTypeHandle().GetMethodTable()->IsValueType()) GC_TRIGGERS; else GC_NOTRIGGER; - } CONTRACTL_END; - - // Get the type of the element... - switch ((*arrObj)->GetArrayElementType()) { - - case ELEMENT_TYPE_VOID: - return NULL; - - case ELEMENT_TYPE_CLASS: // Class - case ELEMENT_TYPE_SZARRAY: // Single Dim, Zero - case ELEMENT_TYPE_ARRAY: // General Array - case ELEMENT_TYPE_STRING: - case ELEMENT_TYPE_OBJECT: - { - _ASSERTE((*arrObj)->GetComponentSize() == sizeof(OBJECTREF)); - BYTE* pData = ((BYTE*)(*arrObj)->GetDataPtr()) + (dwOffset * sizeof(OBJECTREF)); - return *(Object **)pData; - } - - case ELEMENT_TYPE_VALUETYPE: - { - MethodTable *pMT = (*arrObj)->GetArrayElementTypeHandle().GetMethodTable(); - WORD wComponentSize = (*arrObj)->GetComponentSize(); - BYTE* pData = ((BYTE*)(*arrObj)->GetDataPtr()) + (dwOffset * wComponentSize); - return OBJECTREFToObject(pMT->Box(pData)); - } - case ELEMENT_TYPE_BOOLEAN: // boolean - case ELEMENT_TYPE_I1: // sbyte - case ELEMENT_TYPE_U1: - case ELEMENT_TYPE_I2: // short - case ELEMENT_TYPE_U2: - case ELEMENT_TYPE_CHAR: // char - case ELEMENT_TYPE_I4: // int - case ELEMENT_TYPE_I: - case ELEMENT_TYPE_U: - case ELEMENT_TYPE_U4: - case ELEMENT_TYPE_I8: // long - case ELEMENT_TYPE_U8: - case ELEMENT_TYPE_R4: // float - case ELEMENT_TYPE_R8: // double - case ELEMENT_TYPE_PTR: - { - // Note that this is a cloned version of the value class case above for performance - - // Watch for GC here. We allocate the object and then - // grab the void* to the data we are going to copy. - MethodTable *pMT = (*arrObj)->GetArrayElementTypeHandle().GetMethodTable(); - OBJECTREF obj = ::AllocateObject(pMT); - WORD wComponentSize = (*arrObj)->GetComponentSize(); - BYTE* pData = ((BYTE*)(*arrObj)->GetDataPtr()) + (dwOffset * wComponentSize); - CopyValueClassUnchecked(obj->UnBox(), pData, (*arrObj)->GetArrayElementTypeHandle().GetMethodTable()); - return OBJECTREFToObject(obj); - } - - case ELEMENT_TYPE_END: - default: - _ASSERTE(!"Unknown array element type"); - } - - _ASSERTE(!"Should never get here"); - return NULL; -} -#ifdef _MSC_VER -#pragma warning(pop) -#endif // _MSC_VER: warning C4244 - - -void ObjectClone::CompleteValueTypeFields(OBJECTREF newObj, OBJECTREF refParent, QueuedObjectInfo *objInfo) -{ - CONTRACTL - { - MODE_COOPERATIVE; - GC_TRIGGERS; - THROWS; - } - CONTRACTL_END - -#ifdef _DEBUG - { - SString ssTypeName; - SString ssParentTypeName; - newObj->GetMethodTable()->_GetFullyQualifiedNameForClassNestedAware(ssTypeName); - refParent->GetMethodTable()->_GetFullyQualifiedNameForClassNestedAware(ssParentTypeName); - LOG((LF_REMOTING, LL_INFO1000, "CompleteValueTypeFields. Fixing up value type field of type %S into parent of type %S.\n", - ssTypeName.GetUnicode(), ssParentTypeName.GetUnicode())); - } -#endif - - ValueTypeInfo *pValTypeInfo = (ValueTypeInfo *)objInfo; - QueuedObjectInfo *pFixupInfo = pValTypeInfo->GetFixupInfo(); - PREFIX_ASSUME(pFixupInfo != NULL); - - _ASSERTE(pFixupInfo->NeedsUnboxing()); - if (pFixupInfo->IsArray()) - { - m_newObject = newObj; - HandleArrayFixup(refParent, pFixupInfo); - } - else - { - GCPROTECT_BEGIN(refParent); - GCPROTECT_BEGIN(newObj); - ObjectMemberInfo *pObjInfo = (ObjectMemberInfo *)pFixupInfo; - FieldDesc *pTargetField = pObjInfo->GetFieldDesc(); - - TypeHandle fldType = LoadExactFieldType(pTargetField, refParent, m_toDomain); - void *pDest = refParent->GetData() + pTargetField->GetOffset(); - _ASSERTE(GetAppDomain()==m_toDomain); - - if (!fldType.GetMethodTable()->UnBoxInto(pDest, newObj)) - COMPlusThrow(kArgumentException,W("Arg_ObjObj")); - - GCPROTECT_END(); - GCPROTECT_END(); - } - pValTypeInfo->SetHasBeenProcessed(); -} - -void ObjectClone::CompleteSpecialObjects() -{ - CONTRACTL - { - THROWS; - GC_TRIGGERS; - MODE_COOPERATIVE; - } - CONTRACTL_END; - - OBJECTREF nextObj = NULL; - OBJECTREF refNames = NULL; - OBJECTREF refValues = NULL; - SpecialObjectInfo *pObjInfo = NULL; - - GCPROTECT_BEGIN(refNames); - GCPROTECT_BEGIN(refValues); - - DWORD skippedObjects = 0; - DWORD numLoops = 0; - - if (TSO.GetCount() == 0) - goto EarlyExit; - - LOG((LF_REMOTING, LL_INFO1000, "CompleteSpecialObjects. Beginning.\n")); - do - { - skippedObjects = 0; - numLoops++; - DWORD index = 0; - TSO.BeginEnumeration(&index); - while((nextObj = TSO.GetNext(&index, &refNames, &refValues, (QueuedObjectInfo **)&pObjInfo)) != NULL) - { - if (pObjInfo->HasBeenProcessed()) - continue; - - if (pObjInfo->IsISerializableInstance()) - { - _ASSERTE(m_context != ObjectFreezer); - - LOG((LF_REMOTING, LL_INFO1000, "CompleteSpecialObjects. ISerializable instance at index %d.\n", index)); - ISerializableInstanceInfo *iserInfo = (ISerializableInstanceInfo *)pObjInfo; - if (iserInfo->GetNumSpecialMembers() > 0) - { - if (CheckForUnresolvedMembers(iserInfo)) - { - LOG((LF_REMOTING, LL_INFO1000, "CompleteSpecialObjects. Skipping ISerializable instance due to unresolved members.\n")); - skippedObjects++; - continue; - } - } - CompleteISerializableObject(nextObj, refNames, refValues, iserInfo); - } - else if (pObjInfo->IsIObjRefInstance()) - { - _ASSERTE(m_context != ObjectFreezer); - - LOG((LF_REMOTING, LL_INFO1000, "CompleteSpecialObjects. IObjectReference instance at index %d.\n", index)); - IObjRefInstanceInfo *iorInfo = (IObjRefInstanceInfo *)pObjInfo; - if (iorInfo->GetNumSpecialMembers() > 0 || - iorInfo->GetISerTSOIndex() != (DWORD) -1) - { - if (CheckForUnresolvedMembers(iorInfo)) - { - LOG((LF_REMOTING, LL_INFO1000, "CompleteSpecialObjects. Skipping IObjectReference instance due to unresolved members.\n")); - skippedObjects++; - continue; - } - } - if (!CompleteIObjRefObject(nextObj, index, iorInfo)) - skippedObjects++; - } - else - { - _ASSERTE(pObjInfo->IsBoxedObject()); - LOG((LF_REMOTING, LL_INFO1000, "CompleteSpecialObjects. Boxed valuetype instance at index %d.\n", index)); - ValueTypeInfo *valTypeInfo = (ValueTypeInfo *)pObjInfo; - if (valTypeInfo->GetNumSpecialMembers() > 0 || - valTypeInfo->GetISerTSOIndex() != (DWORD) -1 || - valTypeInfo->GetIObjRefTSOIndex() != (DWORD) -1) - { - if (CheckForUnresolvedMembers(valTypeInfo)) - { - LOG((LF_REMOTING, LL_INFO1000, "CompleteSpecialObjects. Skipping boxed value instance due to unresolved members.\n")); - skippedObjects++; - continue; - } - } - // If we were waiting on an IObjRef fixup then the target object will have changed. - if (valTypeInfo->GetIObjRefTSOIndex() != (DWORD) -1) - { - OBJECTREF dummy1, dummy2; - QueuedObjectInfo *dummy3; - nextObj = TSO.GetAt(valTypeInfo->GetIObjRefTSOIndex(), &dummy1, &dummy2, &dummy3); - } - CompleteValueTypeFields(nextObj, refNames, valTypeInfo); - } - - }; - } while (skippedObjects > 0 && numLoops < 100); - - if (skippedObjects > 0 && numLoops >= 100) - { - COMPlusThrow(kSerializationException, IDS_SERIALIZATION_UNRESOLVED_SPECIAL_OBJECT); - } -EarlyExit: ; - GCPROTECT_END(); - GCPROTECT_END(); -} - -BOOL ObjectClone::CheckForUnresolvedMembers(SpecialObjectInfo *splInfo) -{ - CONTRACTL - { - NOTHROW; - GC_NOTRIGGER; - SO_TOLERANT; - MODE_COOPERATIVE; - } - CONTRACTL_END; - - BOOL foundUnresolvedMember = FALSE; - - DWORD mappingIndex = splInfo->GetMappingTableIndex(); - for (DWORD count = 0; count < splInfo->GetNumSpecialMembers(); count++) - { - DWORD memberIndex = TMappings.GetAt(mappingIndex++); - SpecialObjectInfo *pMemberInfo; - OBJECTREF dummy1, dummy2, dummy3; - dummy1 = TSO.GetAt(memberIndex, &dummy2, &dummy3, (QueuedObjectInfo **)&pMemberInfo); - // An unresolved IObjRef member is a blocker for any special object parent - if (pMemberInfo->IsIObjRefInstance() && !pMemberInfo->HasBeenProcessed()) - { - LOG((LF_REMOTING, LL_INFO1000, "CheckForUnresolvedMembers. Found unresolved IObjectReference member at index %d.\n", memberIndex)); - foundUnresolvedMember = TRUE; - break; - } - - // An unresolved ISer member is a blocker for IObjRef parent - if (pMemberInfo->IsISerializableInstance() && - !pMemberInfo->HasBeenProcessed() && - splInfo->IsIObjRefInstance()) - { - LOG((LF_REMOTING, LL_INFO1000, "CheckForUnresolvedMembers. Found unresolved ISerializable member at index %d.\n", memberIndex)); - foundUnresolvedMember = TRUE; - break; - } - - // An unresolved boxed object is a blocker for a boxed parent or an IObjRef parent - if (pMemberInfo->IsBoxedObject() && - !pMemberInfo->HasBeenProcessed() && - (splInfo->IsIObjRefInstance() || splInfo->IsBoxedObject())) - { - LOG((LF_REMOTING, LL_INFO1000, "CheckForUnresolvedMembers. Found unresolved boxed valuetype member at index %d.\n", memberIndex)); - foundUnresolvedMember = TRUE; - break; - } - } - - // Done checking members. Now check if this instance itself needs some processing - // If an instance is both ISer and IObj, then ISer should be processed before IObjRef - if (!foundUnresolvedMember && splInfo->IsIObjRefInstance()) - { - IObjRefInstanceInfo *pObjRefInfo = (IObjRefInstanceInfo *)splInfo; - if (pObjRefInfo->GetISerTSOIndex() != (DWORD) -1) - { - // Check if the ISer requirements have been met - SpecialObjectInfo *pMemberInfo; - OBJECTREF dummy1, dummy2, dummy3; - dummy1 = TSO.GetAt(pObjRefInfo->GetISerTSOIndex(), &dummy2, &dummy3, (QueuedObjectInfo **)&pMemberInfo); - if (!pMemberInfo->HasBeenProcessed()) - { - LOG((LF_REMOTING, LL_INFO1000, "CheckForUnresolvedMembers. This instance is also ISerializable at index %d. Not resolved yet.\n", pObjRefInfo->GetISerTSOIndex())); - foundUnresolvedMember = TRUE; - } - } - } - - // If an instance is ISer, IObj and a boxed value type, then ISer,IObj should be processed before unboxing - if (!foundUnresolvedMember && splInfo->IsBoxedObject()) - { - ValueTypeInfo *pValTypeInfo = (ValueTypeInfo *)splInfo; - if (pValTypeInfo->GetISerTSOIndex() != (DWORD) -1) - { - // Check if the ISer requirements have been met - SpecialObjectInfo *pMemberInfo; - OBJECTREF dummy1, dummy2, dummy3; - dummy1 = TSO.GetAt(pValTypeInfo->GetISerTSOIndex(), &dummy2, &dummy3, (QueuedObjectInfo **)&pMemberInfo); - if (!pMemberInfo->HasBeenProcessed()) - { - LOG((LF_REMOTING, LL_INFO1000, "CheckForUnresolvedMembers. This instance is also ISerializable at index %d. Not resolved yet.\n", pValTypeInfo->GetISerTSOIndex())); - foundUnresolvedMember = TRUE; - } - } - if (!foundUnresolvedMember && pValTypeInfo->GetIObjRefTSOIndex() != (DWORD) -1) - { - // Check if the ISer requirements have been met - SpecialObjectInfo *pMemberInfo; - OBJECTREF dummy1, dummy2, dummy3; - dummy1 = TSO.GetAt(pValTypeInfo->GetIObjRefTSOIndex(), &dummy2, &dummy3, (QueuedObjectInfo **)&pMemberInfo); - if (!pMemberInfo->HasBeenProcessed()) - { - LOG((LF_REMOTING, LL_INFO1000, "CheckForUnresolvedMembers. This instance is also IObjectReference at index %d. Not resolved yet.\n", pValTypeInfo->GetIObjRefTSOIndex())); - foundUnresolvedMember = TRUE; - } - } - } - return foundUnresolvedMember; -} - -void ObjectClone::CompleteISerializableObject(OBJECTREF IserObj, OBJECTREF refNames, OBJECTREF refValues, ISerializableInstanceInfo *iserInfo) -{ - CONTRACTL - { - GC_TRIGGERS; - MODE_COOPERATIVE; - THROWS; - } - CONTRACTL_END - - _ASSERTE(m_context != ObjectFreezer); - - struct _gc { - OBJECTREF IserObj; - OBJECTREF refNames; - OBJECTREF refValues; - OBJECTREF refSerInfo; - } gc; - - gc.IserObj = IserObj; - gc.refNames = refNames; - gc.refValues = refValues; - gc.refSerInfo = NULL; - - GCPROTECT_BEGIN(gc); - -#ifdef _DEBUG - { - DefineFullyQualifiedNameForClass(); - LOG((LF_REMOTING, LL_INFO1000, "CompleteISerializableObject. Completing ISerializable object of type %s.\n", - GetFullyQualifiedNameForClassNestedAware(gc.IserObj->GetMethodTable()))); - } -#endif - - BOOL bIsBoxed = gc.IserObj->GetMethodTable()->IsValueType(); - - // StreamingContextData is an out parameter of the managed callback, so it's passed by reference on all platforms. - RuntimeMethodHandle::StreamingContextData context = {0}; - - PREPARE_NONVIRTUAL_CALLSITE(METHOD__OBJECTCLONEHELPER__PREPARE_DATA); - - DECLARE_ARGHOLDER_ARRAY(args, 4); - - args[ARGNUM_0] = OBJECTREF_TO_ARGHOLDER(gc.IserObj); - args[ARGNUM_1] = OBJECTREF_TO_ARGHOLDER(gc.refNames); - args[ARGNUM_2] = OBJECTREF_TO_ARGHOLDER(gc.refValues); - args[ARGNUM_3] = PTR_TO_ARGHOLDER(&context); - - CATCH_HANDLER_FOUND_NOTIFICATION_CALLSITE; - CALL_MANAGED_METHOD_RETREF(gc.refSerInfo, OBJECTREF, args); - - if (iserInfo->IsTargetNotISerializable()) - { - // Prepare data would have constructed the object already - _ASSERTE(gc.refSerInfo == NULL); - } - else - { - _ASSERTE(gc.refSerInfo != NULL); - MethodTable *pMT = gc.IserObj->GetMethodTable(); - _ASSERTE(pMT); - - MethodDesc * pCtor; - -#ifdef FEATURE_IMPERSONATION - // Deal with the WindowsIdentity class specially by calling an internal - // serialization constructor; the public one has a security demand that - // breaks partial trust scenarios and is too expensive to assert for. - if (MscorlibBinder::IsClass(pMT, CLASS__WINDOWS_IDENTITY)) - pCtor = MscorlibBinder::GetMethod(METHOD__WINDOWS_IDENTITY__SERIALIZATION_CTOR); - else -#endif - pCtor = MemberLoader::FindConstructor(pMT, &gsig_IM_SerInfo_StrContext_RetVoid); - - if (pCtor == NULL) - { - DefineFullyQualifiedNameForClassW(); - COMPlusThrow(kSerializationException, IDS_SERIALIZATION_CTOR_NOT_FOUND, - GetFullyQualifiedNameForClassNestedAwareW(pMT)); - } - - MethodDescCallSite ctor(pCtor); - - ARG_SLOT argSlots[3]; - // Nullable<T> does not implement ISerializable. - _ASSERTE(!Nullable::IsNullableType(gc.IserObj->GetMethodTable())); - argSlots[0] = (bIsBoxed ? (ARG_SLOT)(SIZE_T)(gc.IserObj->UnBox()) : ObjToArgSlot(gc.IserObj)); - argSlots[1] = ObjToArgSlot(gc.refSerInfo); -#if defined(_TARGET_X86_) || defined(_TARGET_ARM_) - static_assert_no_msg(sizeof(context) == sizeof(ARG_SLOT)); - argSlots[2] = *(ARG_SLOT*)(&context); // StreamingContext is passed by value on x86 and ARM -#elif defined(_WIN64) - static_assert_no_msg(sizeof(context) > sizeof(ARG_SLOT)); - argSlots[2] = PtrToArgSlot(&context); // StreamingContext is passed by reference on WIN64 -#else // !_TARGET_X86_ && !_WIN64 && !_TARGET_ARM_ - PORTABILITY_ASSERT("ObjectClone::CompleteISerializableObject() - NYI on this platform"); -#endif // !_TARGET_X86_ && !_WIN64 && !_TARGET_ARM_ - ctor.CallWithValueTypes(&argSlots[0]); - } - iserInfo->SetHasBeenProcessed(); - - GCPROTECT_END(); - -} - -// FALSE means the object could not be resolved and need to perform more iterations -BOOL ObjectClone::CompleteIObjRefObject(OBJECTREF IObjRef, DWORD tsoIndex, IObjRefInstanceInfo *iorInfo) -{ - CONTRACTL - { - GC_TRIGGERS; - MODE_COOPERATIVE; - THROWS; - } - CONTRACTL_END - - BOOL bResult = FALSE; - - struct _gc { - OBJECTREF IObjRef; - OBJECTREF newObj; - OBJECTREF refParent; - OBJECTREF refFromObj; - OBJECTREF resolvedObject; - } gc; - - gc.IObjRef = IObjRef; - gc.newObj = NULL; - gc.refParent = NULL; - gc.refFromObj = NULL; - gc.resolvedObject = NULL; - - GCPROTECT_BEGIN(gc); - - _ASSERTE(m_context != ObjectFreezer); - // First check if this is a repeat object - if (iorInfo->IsRepeatObject()) - { - OBJECTREF dummy; - dummy = TSO.GetAt(tsoIndex, &gc.refFromObj, &gc.refParent, (QueuedObjectInfo **)&iorInfo); - PREFIX_ASSUME(gc.refFromObj != NULL); - - // Look in the Table of Seen objects whether this IObjRef has been resolved - int currId; - currId = TOS.HasID(gc.refFromObj, &gc.resolvedObject); - _ASSERTE(currId != -1); - - MethodTable *pResolvedMT = gc.resolvedObject->GetMethodTable(); - if (!pResolvedMT->IsTransparentProxy() && - m_cbInterface->IsIObjectReferenceType(pResolvedMT)) - { - bResult = FALSE; - } - else - { -#ifdef _DEBUG - { - DefineFullyQualifiedNameForClass(); - LOG((LF_REMOTING, LL_INFO1000, "CompleteIObjRefObject. Found IObjectReference object of type %s already resolved.\n", - GetFullyQualifiedNameForClassNestedAware(gc.IObjRef->GetMethodTable()))); - } -#endif - - // Yes, its been resolved. - // Fix the object into its parent (unless it requires unboxing, in which case there's another entry in the TSO ready to - // do that). - QueuedObjectInfo *pFixupInfo = (QueuedObjectInfo *)iorInfo->GetFixupInfo(); - PREFIX_ASSUME(pFixupInfo != NULL); - if (pFixupInfo->NeedsUnboxing()) - { - TSO.SetAt(tsoIndex, gc.resolvedObject, gc.refFromObj, gc.refParent, iorInfo); - iorInfo->SetHasBeenProcessed(); - bResult = TRUE; - } - else - { - if (gc.refParent == NULL) - m_topObject = gc.resolvedObject; - else - { - m_newObject = gc.resolvedObject; - if (pFixupInfo->NeedsUnboxing()) - CompleteValueTypeFields(gc.resolvedObject, gc.refParent, pFixupInfo); - else - Fixup(gc.resolvedObject, gc.refParent, pFixupInfo); - } - iorInfo->SetHasBeenProcessed(); - bResult = TRUE; - } - } - } - else - { - MethodTable *pMT = gc.IObjRef->GetMethodTable(); - _ASSERTE(pMT); - - MethodTable *pItf = MscorlibBinder::GetClass(CLASS__IOBJECTREFERENCE); - MethodDesc *pMeth = GetInterfaceMethodImpl(pMT, pItf, 0); - MethodDescCallSite method(pMeth, &gc.IObjRef); - - // Ensure Streamingcontext type is loaded. Do not delete this line - MethodTable *pMTStreamingContext; - pMTStreamingContext = MscorlibBinder::GetClass(CLASS__STREAMING_CONTEXT); - _ASSERTE(pMTStreamingContext); - - ARG_SLOT arg[2]; - arg[0] = ObjToArgSlot(gc.IObjRef); - - RuntimeMethodHandle::StreamingContextData context = { NULL, GetStreamingContextState() }; -#ifdef _WIN64 - static_assert_no_msg(sizeof(context) > sizeof(ARG_SLOT)); - arg[1] = PtrToArgSlot(&context); -#else - static_assert_no_msg(sizeof(context) <= sizeof(ARG_SLOT)); - arg[1] = *(ARG_SLOT*)(&context); -#endif - - gc.newObj = method.CallWithValueTypes_RetOBJECTREF(&arg[0]); - - INDEBUG(DefineFullyQualifiedNameForClass();) - - _ASSERTE(gc.newObj != NULL); - MethodTable *pNewMT = gc.newObj->GetMethodTable(); - if (!pNewMT->IsTransparentProxy() && - gc.newObj != gc.IObjRef && - m_cbInterface->IsIObjectReferenceType(pNewMT)) - { -#ifdef _DEBUG - LOG((LF_REMOTING, LL_INFO1000, - "CompleteIObjRefObject. GetRealObject on object of type %s returned another IObjectReference. Adding back to TSO.\n", - GetFullyQualifiedNameForClassNestedAware(gc.IObjRef->GetMethodTable()))); -#endif - - // Put this back into the table - OBJECTREF dummy; - dummy = TSO.GetAt(tsoIndex, &gc.refFromObj, &gc.refParent, (QueuedObjectInfo **)&iorInfo); - TSO.SetAt(tsoIndex, gc.newObj, gc.refFromObj, gc.refParent, iorInfo); - bResult = FALSE; - } - else - { -#ifdef _DEBUG - LOG((LF_REMOTING, LL_INFO1000, - "CompleteIObjRefObject. Called GetRealObject on object of type %s. Fixing it up into its parent.\n", - GetFullyQualifiedNameForClassNestedAware(gc.IObjRef->GetMethodTable()))); -#endif - // Fix the object into its parent (unless it requires unboxing, in which case there's another entry in the TSO ready to - // do that). - QueuedObjectInfo *pFixupInfo = (QueuedObjectInfo *)iorInfo->GetFixupInfo(); - OBJECTREF dummy; - dummy = TSO.GetAt(tsoIndex, &gc.refFromObj, &gc.refParent, (QueuedObjectInfo **)&iorInfo); - if (pFixupInfo->NeedsUnboxing()) - { - TSO.SetAt(tsoIndex, gc.newObj, gc.refFromObj, gc.refParent, iorInfo); - iorInfo->SetHasBeenProcessed(); - bResult = TRUE; - } - else - { - if (gc.refParent == NULL) - m_topObject = gc.newObj; - else - { - m_newObject = gc.newObj; - Fixup(gc.newObj, gc.refParent, pFixupInfo); - } - - // Update Table of Seen objects, so that any repeat objects can be updated too - TOS.UpdateObject(gc.refFromObj, gc.newObj); - iorInfo->SetHasBeenProcessed(); - bResult = TRUE; - } - } - } - - GCPROTECT_END(); - return bResult; -} - -void MakeIDeserializationCallback(OBJECTREF refTarget) -{ - CONTRACTL - { - GC_TRIGGERS; - MODE_COOPERATIVE; - THROWS; - } - CONTRACTL_END; - - struct _gc { - OBJECTREF refTarget; - } gc; - gc.refTarget = refTarget; - - GCPROTECT_BEGIN(gc); - - MethodTable *pMT = gc.refTarget->GetMethodTable(); - _ASSERTE(pMT); - - MethodTable *pItf = MscorlibBinder::GetClass(CLASS__IDESERIALIZATIONCB); - MethodDesc *pMeth = GetInterfaceMethodImpl(pMT, pItf, 0); - PCODE pCode = pMeth->GetSingleCallableAddrOfCode(); - - PREPARE_NONVIRTUAL_CALLSITE_USING_CODE(pCode); - - DECLARE_ARGHOLDER_ARRAY(args, 2); - - args[ARGNUM_0] = OBJECTREF_TO_ARGHOLDER(gc.refTarget); - args[ARGNUM_1] = NULL; - - CATCH_HANDLER_FOUND_NOTIFICATION_CALLSITE; - CALL_MANAGED_METHOD_NORET(args); - - GCPROTECT_END(); -} - -void ObjectClone::CompleteIDeserializationCallbacks() -{ - CONTRACTL - { - GC_TRIGGERS; - MODE_COOPERATIVE; - THROWS; - } - CONTRACTL_END - OBJECTREF Dummy1 = NULL, Dummy2 = NULL; - QueuedObjectInfo *pObjInfo = NULL; - - if (TDC.GetCount() == 0) - return; - - LOG((LF_REMOTING, LL_INFO1000, "CompleteIDeserializationCallbacks. Beginning.\n")); - - OBJECTREF nextObj; - while ((nextObj = TDC.Dequeue(&Dummy1, &Dummy2, &pObjInfo)) != NULL) - { - MakeIDeserializationCallback(nextObj); - } -} - -void ObjectClone::CompleteVtsOnDeserializedCallbacks() -{ - CONTRACTL - { - GC_TRIGGERS; - MODE_COOPERATIVE; - THROWS; - } - CONTRACTL_END; - - OBJECTREF nextObj = NULL, Dummy1 = NULL, Dummy2 = NULL; - - if (VDC.GetCount() == 0) - return; - - LOG((LF_REMOTING, LL_INFO1000, "CompleteVtsOnDeserializedCallbacks. Beginning.\n")); - - GCPROTECT_BEGIN(nextObj); - - while ((nextObj = VDC.Dequeue(&Dummy1, &Dummy2, NULL)) != NULL) - InvokeVtsCallbacks(nextObj, RemotingVtsInfo::VTS_CALLBACK_ON_DESERIALIZED, m_toDomain); - - GCPROTECT_END(); -} - -void ObjectClone::CompleteVtsOnSerializedCallbacks() -{ - CONTRACTL - { - GC_TRIGGERS; - MODE_COOPERATIVE; - THROWS; - } - CONTRACTL_END; - - OBJECTREF nextObj = NULL, Dummy1 = NULL, Dummy2 = NULL; - - if (VSC.GetCount() == 0) - return; - - LOG((LF_REMOTING, LL_INFO1000, "CompleteVtsOnSerializedCallbacks. Beginning.\n")); - - GCPROTECT_BEGIN(nextObj); - - while ((nextObj = VSC.Dequeue(&Dummy1, &Dummy2, NULL)) != NULL) - InvokeVtsCallbacks(nextObj, RemotingVtsInfo::VTS_CALLBACK_ON_SERIALIZED, m_fromDomain); - - GCPROTECT_END(); -} - -// Does a binary search to find the object with given id, and record of given kind -DWORD ObjectClone::FindObjectInTSO(int objId, SpecialObjects kind) -{ - CONTRACTL - { - MODE_COOPERATIVE; - GC_NOTRIGGER; - NOTHROW; - } - CONTRACTL_END - - DWORD lowIndex = 0; - DWORD highIndex = TSO.GetCount(); - DWORD midIndex = highIndex / 2; - DWORD firstMatch; - - if (highIndex == 0) - { - _ASSERTE(!"Special Object unexpectedly not found for given object id\n"); - return 0; // throw ? - } - - SpecialObjectInfo *splInfo = NULL; - while (true) - { - OBJECTREF refParent, refFromObj; - OBJECTREF dummy; - dummy = TSO.GetAt(midIndex, &refFromObj, &refParent, (QueuedObjectInfo **)&splInfo); - - if (objId < splInfo->GetObjectId()) - { - highIndex = midIndex; - } - else - { - if (objId == splInfo->GetObjectId()) - break; - lowIndex = midIndex; - } - - DWORD oldIndex = midIndex; - midIndex = lowIndex + (highIndex - lowIndex)/2; - if (oldIndex == midIndex) - { - // Binary search failed. See comments below - goto LinearSearch; - } - } - - // Found match at midIndex - // Find the first record for this obj id - firstMatch = midIndex; - while(midIndex != 0) - { - midIndex -= 1; - SpecialObjectInfo *pTemp; - OBJECTREF refParent, refFromObj; - OBJECTREF dummy; - dummy = TSO.GetAt(midIndex, &refFromObj, &refParent, (QueuedObjectInfo **)&pTemp); - if (pTemp->GetObjectId() != objId) - break; - else - firstMatch = midIndex; - }; - - // Now look for the right kind of record - do - { - OBJECTREF refParent, refFromObj; - OBJECTREF dummy; - dummy = TSO.GetAt(firstMatch, &refFromObj, &refParent, (QueuedObjectInfo **)&splInfo); - - if (splInfo->GetObjectId() == objId) - { - switch(kind) - { - case ISerializable: - if (splInfo->IsISerializableInstance()) - return firstMatch; - break; - case IObjectReference: - if (splInfo->IsIObjRefInstance()) - return firstMatch; - break; - case BoxedValueType: - if (splInfo->IsBoxedObject()) - return firstMatch; - break; - default: - _ASSERTE(!"Unknown enum value in FindObjectInTSO"); - }; - } - - firstMatch++; - - }while(firstMatch < TSO.GetCount()); - -LinearSearch: - // If there are multiple objects that are ISer/IObj, and some of them repeat in a certain fashion, - // then the entries in TSO are not in sorted order. In such a case binary search will fail. Lets do a linear search - // in such a case for now. This is probably reasonable since the TSO should usually be short and in-order (and presumably - // cheaper than trying to keep the list in sorted order at all times). - DWORD currIndex = 0; - for (; currIndex < TSO.GetCount(); currIndex++) - { - OBJECTREF refParent, refFromObj; - OBJECTREF dummy; - dummy = TSO.GetAt(currIndex, &refFromObj, &refParent, (QueuedObjectInfo **)&splInfo); - - SpecialObjects foundKind = ISerializable; - if (splInfo->IsIObjRefInstance()) - foundKind = IObjectReference; - else if (splInfo->IsBoxedObject()) - foundKind = BoxedValueType; - else - _ASSERTE(splInfo->IsISerializableInstance()); - - if (objId == splInfo->GetObjectId() - && kind == foundKind) - return currIndex; - } - - - _ASSERTE(!"Special Object unexpectedly not found for given object id\n"); - return 0; // throw ? -} - -// This function is effectively a replica of MethodTable::Box. Its replicated to avoid "GCPROTECT_INTERIOR" that Box uses -// and causes some leak detection asserts to go off. This is a controlled leak situation, where we know we're leaking stuff -// and dont want the asserts. -OBJECTREF ObjectClone::BoxValueTypeInWrongDomain(OBJECTREF refParent, DWORD offset, MethodTable *pValueTypeMT) -{ - CONTRACTL - { - THROWS; - GC_TRIGGERS; - MODE_COOPERATIVE; - PRECONDITION(pValueTypeMT->IsValueType()); - PRECONDITION(!pValueTypeMT->IsByRefLike()); - } - CONTRACTL_END; - - OBJECTREF ref = NULL; - void* pSrc = refParent->GetData() + offset; - GCPROTECT_BEGININTERIOR(pSrc); - - // We must enter the target domain if we are boxing a non-agile type. This of course has some overhead - // so we want to avoid it if possible. GetLoaderModule() == mscorlib && CanBeBlittedByObjectCloner is a - // conservative first approximation of agile types. - ENTER_DOMAIN_PTR_PREDICATED(m_fromDomain, ADV_RUNNINGIN, - !pValueTypeMT->GetLoaderModule()->IsSystem() || pValueTypeMT->GetClass()->CannotBeBlittedByObjectCloner()); - - ref = pValueTypeMT->FastBox(&pSrc); - - END_DOMAIN_TRANSITION; - - GCPROTECT_END(); - return ref; -} - -// Returns whether or not a given type requires VTS callbacks of the specified kind. -BOOL ObjectClone::HasVtsCallbacks(MethodTable *pMT, RemotingVtsInfo::VtsCallbackType eCallbackType) -{ - CONTRACTL - { - NOTHROW; - GC_NOTRIGGER; - MODE_ANY; - } - CONTRACTL_END; - - while (pMT) - { - if (pMT->HasRemotingVtsInfo()) - { - PTR_RemotingVtsInfo pVtsInfo = pMT->GetRemotingVtsInfo(); - _ASSERTE(pVtsInfo != NULL); - - if (!pVtsInfo->m_pCallbacks[eCallbackType].IsNull()) - return TRUE; - } - pMT = pMT->GetParentMethodTable(); - } - - return FALSE; -} - -// Calls all of the VTS event methods for a given callback type on the object instance provided (starting at the base class). -void ObjectClone::InvokeVtsCallbacks(OBJECTREF refTarget, RemotingVtsInfo::VtsCallbackType eCallbackType, AppDomain* pDomain) -{ - CONTRACTL - { - THROWS; - GC_TRIGGERS; - MODE_COOPERATIVE; - } - CONTRACTL_END; - - GCPROTECT_BEGIN(refTarget); - - // Quickly walk the target's type hierarchy and determine the number of methods we'll need to call. - DWORD cMethods = 0; - MethodDesc *pLastCallback; - MethodTable *pMT = refTarget->GetMethodTable(); - while (pMT) - { - if (pMT->HasRemotingVtsInfo()) - { - PTR_RemotingVtsInfo pVtsInfo = pMT->GetRemotingVtsInfo(); - _ASSERTE(pVtsInfo != NULL); - - if (!pVtsInfo->m_pCallbacks[eCallbackType].IsNull()) - { - cMethods++; - -#ifdef FEATURE_PREJIT - // Might have to restore cross module method pointers. - Module::RestoreMethodDescPointer(&pVtsInfo->m_pCallbacks[eCallbackType]); -#endif - - pLastCallback = pVtsInfo->m_pCallbacks[eCallbackType].GetValue(); - } - } - pMT = pMT->GetParentMethodTable(); - } - - // Maybe there's no work to do. - if (cMethods == 0) - goto Done; - - // Allocate an array to hold the methods to invoke (we do this because the invocation order is the opposite way round from the - // way we can easily scan for the methods). We can easily optimize this for the single callback case though. - MethodDesc **pCallbacks = cMethods == 1 ? &pLastCallback : (MethodDesc**)_alloca(cMethods * sizeof(MethodDesc*)); - - if (cMethods > 1) - { - // Walk the type hierarchy again, and this time fill in the methods to call in the correct slot of our callback table. - DWORD dwSlotIndex = cMethods; - pMT = refTarget->GetMethodTable(); - while (pMT) - { - if (pMT->HasRemotingVtsInfo()) - { - PTR_RemotingVtsInfo pVtsInfo = pMT->GetRemotingVtsInfo(); - _ASSERTE(pVtsInfo != NULL); - - if (!pVtsInfo->m_pCallbacks[eCallbackType].IsNull()) - pCallbacks[--dwSlotIndex] = pVtsInfo->m_pCallbacks[eCallbackType].GetValue(); - } - pMT = pMT->GetParentMethodTable(); - } - _ASSERTE(dwSlotIndex == 0); - } - - bool fSwitchDomains = pDomain != GetAppDomain(); - - ENTER_DOMAIN_PTR(pDomain,ADV_RUNNINGIN); - - // If we're calling back into the from domain then reset the execution context to its original state (this will automatically be - // popped once we return from this domain again). - if (pDomain == m_fromDomain && fSwitchDomains) - { - Thread *pThread = GetThread(); - if (pThread->IsExposedObjectSet()) - { - THREADBASEREF refThread = (THREADBASEREF)pThread->GetExposedObjectRaw(); - refThread->SetExecutionContext(m_fromExecutionContext); - } - } - - // Remember to adjust this pointer for boxed value types. - BOOL bIsBoxed = refTarget->GetMethodTable()->IsValueType(); - - RuntimeMethodHandle::StreamingContextData sContext = { NULL, GetStreamingContextState() }; - - // Ensure Streamingcontext type is loaded. Do not delete this line - MethodTable *pMTStreamingContext; - pMTStreamingContext = MscorlibBinder::GetClass(CLASS__STREAMING_CONTEXT); - _ASSERTE(pMTStreamingContext); - - // Now go and call each method in order. - for (DWORD i = 0; i < cMethods; i++) - { - MethodDescCallSite callback(pCallbacks[i], &refTarget); - - ARG_SLOT argSlots[2]; - - // Nullable<T> does not have any VTS functions - _ASSERTE(!Nullable::IsNullableType(refTarget->GetMethodTable())); - - argSlots[0] = (bIsBoxed ? (ARG_SLOT)(SIZE_T)(refTarget->UnBox()) : ObjToArgSlot(refTarget)); -#if defined(_TARGET_X86_) || defined(_TARGET_ARM_) - static_assert_no_msg(sizeof(sContext) == sizeof(ARG_SLOT)); - argSlots[1] = *(ARG_SLOT*)(&sContext); // StreamingContext is passed by value on x86 and ARM -#elif defined(_WIN64) - static_assert_no_msg(sizeof(sContext) > sizeof(ARG_SLOT)); - argSlots[1] = PtrToArgSlot(&sContext); // StreamingContext is passed by reference on WIN64 -#else // !_TARGET_X86_ && !_WIN64 && !_TARGET_ARM_ - PORTABILITY_ASSERT("ObjectClone::InvokeVtsCallbacks() - NYI on this platform"); -#endif // !_TARGET_X86_ && !_WIN64 && !_TARGET_ARM_ - - callback.CallWithValueTypes(&argSlots[0]); - } - - END_DOMAIN_TRANSITION; - -Done: ; - GCPROTECT_END(); -} - -#endif // FEATURE_REMOTING |