summaryrefslogtreecommitdiff
path: root/src/vm/objectclone.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/vm/objectclone.cpp')
-rw-r--r--src/vm/objectclone.cpp3865
1 files changed, 3865 insertions, 0 deletions
diff --git a/src/vm/objectclone.cpp b/src/vm/objectclone.cpp
new file mode 100644
index 0000000000..b4ad314165
--- /dev/null
+++ b/src/vm/objectclone.cpp
@@ -0,0 +1,3865 @@
+// 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