Bläddra i källkod

Bugfix: More major fixes to the scripting runtime
- Fixed a managed memory corruption issue caused by managed dictionary serialization
- Managed serializable objects now all use GC handles

BearishSun 7 år sedan
förälder
incheckning
e8c7e6fb74

+ 2 - 1
Source/BansheeMono/BsMonoManager.cpp

@@ -108,6 +108,7 @@ namespace bs
 		const char* options[] = {
 			"--soft-breakpoints",
 			"--debugger-agent=transport=dt_socket,address=127.0.0.1:17615,embedding=1,server=y,suspend=n",
+			"--debug-domain-unload",
 
 			// GC options:
 			// check-remset-consistency: Makes sure that write barriers are properly issued in native code, and therefore
@@ -118,7 +119,7 @@ namespace bs
 			// xdomain-checks: Makes sure that no references are left when a domain is unloaded.
 			"--gc-debug=check-remset-consistency,verify-before-collections,xdomain-checks"
 		};
-		mono_jit_parse_options(3, (char**)options);
+		mono_jit_parse_options(4, (char**)options);
 		mono_trace_set_level_string("warning"); // Note: Switch to "debug" for detailed output, disabled for now due to spam
 #else
 		mono_trace_set_level_string("warning");

+ 1 - 1
Source/BansheeMono/BsMonoUtil.cpp

@@ -340,4 +340,4 @@ namespace bs
 			LOGERR(msg);
 		}
 	}
-}
+}

+ 2 - 2
Source/SBansheeEditor/BsManagedEditorCommand.cpp

@@ -83,7 +83,7 @@ namespace bs
 		if (!mInUndoRedoStack)
 		{
 			MonoObject* obj = MonoUtil::getObjectFromGCHandle(mGCHandle);
-			mGCHandle = MonoUtil::newGCHandle(obj);
+			mGCHandle = MonoUtil::newGCHandle(obj, false);
 			mInUndoRedoStack = true;
 		}
 	}
@@ -123,7 +123,7 @@ namespace bs
 		}
 
 		MonoObject* instance = currentObjInfo->mMonoClass->createInstance(construct);
-		mGCHandle = MonoUtil::newGCHandle(instance);
+		mGCHandle = MonoUtil::newGCHandle(instance, false);
 
 		mTypeMissing = false;
 		return instance;

+ 1 - 5
Source/SBansheeEditor/Wrappers/BsScriptProjectLibrary.cpp

@@ -553,10 +553,6 @@ namespace bs
 			return nullptr;
 
 		SPtr<ManagedSerializableObject> userDataObj = std::static_pointer_cast<ManagedSerializableObject>(userData);
-		userDataObj->deserialize();
-		MonoObject* instance = userDataObj->getManagedInstance();
-		userDataObj->serialize();
-
-		return instance;
+		return userDataObj->deserialize();
 	}
 }

+ 3 - 0
Source/SBansheeEditor/Wrappers/BsScriptSerializedDiff.cpp

@@ -29,6 +29,9 @@ namespace bs
 		if (oldSerializedObject == nullptr || newSerializedObject == nullptr)
 			return nullptr;
 
+		oldSerializedObject->serialize();
+		newSerializedObject->serialize();
+
 		SPtr<ManagedSerializableDiff> diff = ManagedSerializableDiff::create(oldSerializedObject, newSerializedObject);
 
 		MonoObject* instance = metaData.scriptClass->createInstance();

+ 1 - 7
Source/SBansheeEditor/Wrappers/BsScriptSerializedObject.cpp

@@ -88,12 +88,6 @@ namespace bs
 		if (serializedObject == nullptr)
 			return nullptr;
 
-		serializedObject->deserialize();
-		MonoObject* managedInstance = serializedObject->getManagedInstance();
-
-		// Note: Technically I could just clear managed data without a full serialization since I know nothing has changed
-		serializedObject->serialize(); 
-
-		return managedInstance;
+		return serializedObject->deserialize();
 	}
 }

+ 3 - 0
Source/SBansheeEngine/BsScriptObjectManager.cpp

@@ -39,6 +39,9 @@ namespace bs
 		// Make sure any managed game objects are properly destroyed so their OnDestroy callbacks fire before unloading the domain
 		GameObjectManager::instance().destroyQueuedObjects();
 
+		// Make sure all objects that are finalized due to reasons other than assembly refreshed are destroyed
+		processFinalizedObjects(false);
+
 		for (auto& scriptObject : mScriptObjects)
 			backupData[scriptObject] = scriptObject->beginRefresh();
 

+ 1 - 2
Source/SBansheeEngine/RTTI/BsManagedResourceRTTI.h

@@ -47,11 +47,10 @@ namespace bs
 		{
 			ManagedResource* mr = static_cast<ManagedResource*>(obj);
 			SPtr<ManagedSerializableObject> serializableObject = any_cast<SPtr<ManagedSerializableObject>>(mr->mRTTIData);
-			serializableObject->deserialize();
 
 			SPtr<Resource> mrPtr = std::static_pointer_cast<Resource>(mr->getThisPtr());
 			HManagedResource handle = static_resource_cast<ManagedResource>(gResources()._createResourceHandle(mrPtr));
-			mr->setHandle(serializableObject->getManagedInstance(), handle);
+			mr->setHandle(serializableObject->deserialize(), handle);
 			mr->mRTTIData = nullptr;
 		}
 

+ 60 - 35
Source/SBansheeEngine/Serialization/BsManagedSerializableArray.cpp

@@ -12,17 +12,18 @@
 namespace bs
 {
 	ManagedSerializableArray::ManagedSerializableArray(const ConstructPrivately& dummy)
-		:mManagedInstance(nullptr), mElementMonoClass(nullptr), mCopyMethod(nullptr), mElemSize(0)
 	{
 
 	}
 
-	ManagedSerializableArray::ManagedSerializableArray(const ConstructPrivately& dummy, const SPtr<ManagedSerializableTypeInfoArray>& typeInfo, MonoObject* managedInstance)
-		: mManagedInstance(managedInstance), mElementMonoClass(nullptr), mCopyMethod(nullptr), mArrayTypeInfo(typeInfo)
-		, mElemSize(0)
+	ManagedSerializableArray::ManagedSerializableArray(const ConstructPrivately& dummy, 
+		const SPtr<ManagedSerializableTypeInfoArray>& typeInfo, MonoObject* managedInstance)
+		: mArrayTypeInfo(typeInfo)
 		
 	{
-		ScriptArray scriptArray((MonoArray*)mManagedInstance);
+		mGCHandle = MonoUtil::newGCHandle(managedInstance, false);
+
+		ScriptArray scriptArray((MonoArray*)managedInstance);
 		mElemSize = scriptArray.elementSize();
 
 		initMonoObjects();
@@ -32,7 +33,17 @@ namespace bs
 			mNumElements[i] = getLengthInternal(i);
 	}
 
-	SPtr<ManagedSerializableArray> ManagedSerializableArray::createFromExisting(MonoObject* managedInstance, const SPtr<ManagedSerializableTypeInfoArray>& typeInfo)
+	ManagedSerializableArray::~ManagedSerializableArray()
+	{
+		if(mGCHandle != 0)
+		{
+			MonoUtil::freeGCHandle(mGCHandle);
+			mGCHandle = 0;
+		}
+	}
+
+	SPtr<ManagedSerializableArray> ManagedSerializableArray::createFromExisting(MonoObject* managedInstance, 
+		const SPtr<ManagedSerializableTypeInfoArray>& typeInfo)
 	{
 		if(managedInstance == nullptr)
 			return nullptr;
@@ -70,17 +81,20 @@ namespace bs
 		return createInstance->invoke(nullptr, params);
 	}
 
+	MonoObject* ManagedSerializableArray::getManagedInstance() const
+	{
+		if(mGCHandle != 0)
+			return MonoUtil::getObjectFromGCHandle(mGCHandle);
+
+		return nullptr;
+	}
+
 	void ManagedSerializableArray::setFieldData(UINT32 arrayIdx, const SPtr<ManagedSerializableFieldData>& val)
 	{
-		if (mManagedInstance != nullptr)
+		if (mGCHandle != 0)
 		{
-			if (MonoUtil::isValueType(mElementMonoClass))
-				setValueInternal(arrayIdx, val->getValue(mArrayTypeInfo->mElementType));
-			else
-			{
-				MonoObject* ptrToObj = (MonoObject*)val->getValue(mArrayTypeInfo->mElementType);
-				setValueInternal(arrayIdx, &ptrToObj);
-			}
+			MonoArray* array = (MonoArray*)MonoUtil::getObjectFromGCHandle(mGCHandle);
+			setFieldData(array, arrayIdx, val);
 		}
 		else
 		{
@@ -88,11 +102,22 @@ namespace bs
 		}
 	}
 
+	void ManagedSerializableArray::setFieldData(MonoArray* obj, UINT32 arrayIdx, const SPtr<ManagedSerializableFieldData>& val)
+	{
+		if (MonoUtil::isValueType(mElementMonoClass))
+			setValueInternal(obj, arrayIdx, val->getValue(mArrayTypeInfo->mElementType));
+		else
+		{
+			MonoObject* ptrToObj = (MonoObject*)val->getValue(mArrayTypeInfo->mElementType);
+			setValueInternal(obj, arrayIdx, &ptrToObj);
+		}
+	}
+
 	SPtr<ManagedSerializableFieldData> ManagedSerializableArray::getFieldData(UINT32 arrayIdx)
 	{
-		if (mManagedInstance != nullptr)
+		if (mGCHandle != 0)
 		{
-			MonoArray* array = (MonoArray*)mManagedInstance;
+			MonoArray* array = (MonoArray*)MonoUtil::getObjectFromGCHandle(mGCHandle);
 			ScriptArray scriptArray(array);
 
 			UINT32 numElems = scriptArray.size();
@@ -118,7 +143,7 @@ namespace bs
 
 	void ManagedSerializableArray::serialize()
 	{
-		if (mManagedInstance == nullptr)
+		if(mGCHandle == 0)
 			return;
 
 		mNumElements.resize(mArrayTypeInfo->mRank);
@@ -135,20 +160,18 @@ namespace bs
 		for (auto& fieldEntry : mCachedEntries)
 			fieldEntry->serialize();
 
-		mManagedInstance = nullptr;
+		MonoUtil::freeGCHandle(mGCHandle);
+		mGCHandle = 0;
 	}
 
-	void ManagedSerializableArray::deserialize()
+	MonoObject* ManagedSerializableArray::deserialize()
 	{
-		mManagedInstance = createManagedInstance(mArrayTypeInfo, mNumElements);
+		MonoObject* managedInstance = createManagedInstance(mArrayTypeInfo, mNumElements);
 
-		if (mManagedInstance == nullptr)
-		{
-			mCachedEntries.clear();
-			return;
-		}
+		if (managedInstance == nullptr)
+			return nullptr;
 
-		ScriptArray scriptArray((MonoArray*)mManagedInstance);
+		ScriptArray scriptArray((MonoArray*)managedInstance);
 		mElemSize = scriptArray.elementSize();
 
 		initMonoObjects();
@@ -160,18 +183,16 @@ namespace bs
 		UINT32 idx = 0;
 		for (auto& arrayEntry : mCachedEntries)
 		{
-			setFieldData(idx, arrayEntry);
+			setFieldData((MonoArray*)managedInstance, idx, arrayEntry);
 			idx++;
 		}
 
-		mCachedEntries.clear();
+		return managedInstance;
 	}
 	
-	void ManagedSerializableArray::setValueInternal(UINT32 arrayIdx, void* val)
+	void ManagedSerializableArray::setValueInternal(MonoArray* obj, UINT32 arrayIdx, void* val)
 	{
-		MonoArray* array = (MonoArray*)mManagedInstance;
-
-		ScriptArray scriptArray(array);
+		ScriptArray scriptArray(obj);
 		UINT32 numElems = (UINT32)scriptArray.size();
 		assert(arrayIdx < numElems);
 	
@@ -211,7 +232,7 @@ namespace bs
 
 	void ManagedSerializableArray::resize(const Vector<UINT32>& newSizes)
 	{
-		if (mManagedInstance != nullptr)
+		if (mGCHandle != 0)
 		{
 			assert(mArrayTypeInfo->mRank == (UINT32)newSizes.size());
 
@@ -234,7 +255,9 @@ namespace bs
 
 			mCopyMethod->invoke(nullptr, params);
 
-			mManagedInstance = newArray;
+			MonoUtil::freeGCHandle(mGCHandle);
+			mGCHandle = MonoUtil::newGCHandle(newArray, false);
+
 			mNumElements = newSizes;
 		}
 		else
@@ -246,11 +269,13 @@ namespace bs
 
 	UINT32 ManagedSerializableArray::getLengthInternal(UINT32 dimension) const
 	{
+		MonoObject* managedInstace = MonoUtil::getObjectFromGCHandle(mGCHandle);
+
 		MonoClass* systemArray = ScriptAssemblyManager::instance().getSystemArrayClass();
 		MonoMethod* getLength = systemArray->getMethod("GetLength", 1);
 
 		void* params[1] = { &dimension };
-		MonoObject* returnObj = getLength->invoke(mManagedInstance, params);
+		MonoObject* returnObj = getLength->invoke(managedInstace, params);
 
 		return *(UINT32*)MonoUtil::unbox(returnObj);
 	}

+ 32 - 20
Source/SBansheeEngine/Serialization/BsManagedSerializableArray.h

@@ -17,12 +17,15 @@ namespace bs
 	 * @note	
 	 * This class can be in two states:
 	 *	 - Linked - When the object has a link to a managed object. This is the default state when a new instance
-	 *				of ManagedSerializableObject is created. Any operations during this state will operate directly
-	 *				on the linked managed object.
+	 *				of ManagedSerializableArray is created. Any operations during this state will operate directly
+	 *				on the linked managed object. A GC handle will be kept to the linked managed object. The handle can
+	 *				be freed by transfering to serialized mode or by destroying this object.
 	 *	 - Serialized - When the object has no link to the managed object but instead just contains cached object
 	 *					and field data that may be used for initializing a managed object. Any operations during
 	 *					this state will operate only on the cached internal data.
-	 * You can transfer between these states by calling serialize(linked->serialized) & deserialize (serialized->linked).
+	 *					
+	 * You can transfer an object in linked state to serialized state by calling serialize(). If an object is in serialized
+	 * state you can call deserialize() to populated a managed object from the cached data. 	
 	 *	
 	 */
 	class BS_SCR_BE_EXPORT ManagedSerializableArray : public IReflectable
@@ -33,17 +36,20 @@ namespace bs
 	public:
 		ManagedSerializableArray(const ConstructPrivately& dummy, const SPtr<ManagedSerializableTypeInfoArray>& typeInfo, MonoObject* managedInstance);
 		ManagedSerializableArray(const ConstructPrivately& dummy);
+		~ManagedSerializableArray();
 
 		/**
 		 * Returns the internal managed instance of the array. This will return null if the object is in serialized mode.
 		 */
-		MonoObject* getManagedInstance() const { return mManagedInstance; }
+		MonoObject* getManagedInstance() const;
 
 		/**	Returns the type information for the internal array. */
 		SPtr<ManagedSerializableTypeInfoArray> getTypeInfo() const { return mArrayTypeInfo; }
 
 		/**
-		 * Changes the size of the array. Operates on managed object if in linked state, or on cached data otherwise.
+		 * Changes the size of the array. Operates on managed object if in linked state, or on cached data otherwise. If
+		 * in linked state the new instance of the managed object will be created and must be retrieved by
+		 * getManagedInstance().
 		 *
 		 * @param[in]	newSizes	Array of sizes, one per array dimension. Number of sizes must match number of array
 		 *							dimensions as specified by its type.
@@ -88,21 +94,18 @@ namespace bs
 
 		/**
 		 * Serializes the internal managed object into a set of cached data that can be saved in memory/disk and can be
-		 * deserialized later. Does nothing if object is already is serialized mode. When in serialized mode the reference
-		 * to the managed instance will be lost.
+		 * deserialized later. The internal managed object will be freed (if no other references to it). Calling serialize()
+		 * again will have no result.
 		 */
 		void serialize();
 
 		/**
 		 * Deserializes a set of cached data into a managed object. This action may fail in case the cached data contains a
-		 * type that no longer exists. You may check if it completely successfully if getManagedInstance() returns non-null
-		 * after.
+		 * type that no longer exists in which case null is returned.
 		 *
-		 * This action transfers the object into linked mode. All further operations will operate directly on the managed
-		 * instance and the cached data will be cleared. If you call this method on an already linked object the old object
-		 * will be replaced and initialized with empty data (since cached data does not exist).
+		 * @return		Newly created object initialized with the cached data.
 		 */
-		void deserialize();
+		MonoObject* deserialize();
 
 		/**
 		 * Creates a managed serializable array that references an existing managed array. Created object will be in linked
@@ -142,20 +145,29 @@ namespace bs
 		/** Returns the size of the specified dimension of the array. Operates on the internal managed object. */
 		UINT32 getLengthInternal(UINT32 dimension) const;
 
-		/**	Sets a value at the specified index in the array. Operates on the internal managed object. */
-		void setValueInternal(UINT32 arrayIdx, void* val);
+		/**
+		 * Sets a new element value at the specified array index. Operates on the provided managed instance.
+		 * 
+		 * @param[in]	obj			Managed instance in which to set the data in.
+		 * @param[in]	arrayIdx	Index at which to set the value.
+		 * @param[in]	val			Wrapper around the value to store in the array. Must be of the array element type.
+		 */
+		void setFieldData(MonoArray* obj, UINT32 arrayIdx, const SPtr<ManagedSerializableFieldData>& val);
+
+		/**	Sets a value at the specified index in the array. Operates on the provided managed object. */
+		void setValueInternal(MonoArray* obj, UINT32 arrayIdx, void* val);
 
 		/** Converts a multi-dimensional array index into a sequential one-dimensional index. */
 		UINT32 toSequentialIdx(const Vector<UINT32>& idx) const;
 
-		MonoObject* mManagedInstance;
-		::MonoClass* mElementMonoClass;
-		MonoMethod* mCopyMethod;
+		uint32_t mGCHandle = 0;
+		::MonoClass* mElementMonoClass = nullptr;
+		MonoMethod* mCopyMethod = nullptr;
 
 		SPtr<ManagedSerializableTypeInfoArray> mArrayTypeInfo;
 		Vector<SPtr<ManagedSerializableFieldData>> mCachedEntries;
 		Vector<UINT32> mNumElements;
-		UINT32 mElemSize;
+		UINT32 mElemSize = 0;
 
 		/************************************************************************/
 		/* 								RTTI		                     		*/
@@ -167,7 +179,7 @@ namespace bs
 	public:
 		friend class ManagedSerializableArrayRTTI;
 		static RTTITypeBase* getRTTIStatic();
-		virtual RTTITypeBase* getRTTI() const override;
+		RTTITypeBase* getRTTI() const override;
 	};
 
 	/** @} */

+ 210 - 63
Source/SBansheeEngine/Serialization/BsManagedSerializableDictionary.cpp

@@ -39,17 +39,131 @@ namespace bs
 		return a->equals(b);
 	}
 
-	ManagedSerializableDictionary::Enumerator::Enumerator(MonoObject* instance, const ManagedSerializableDictionary* parent)
-		:mInstance(instance), mCurrent(nullptr), mIteratorInitialized(false), mParent(parent)
-	{ }
+	ManagedSerializableDictionary::Enumerator::Enumerator(const ManagedSerializableDictionary* parent)
+		: mIteratorInitialized(false), mParent(parent)
+	{
+		MonoArray* keysArray = nullptr;
+		MonoArray* valuesArray = nullptr;
+		if(parent->mGCHandle != 0)
+		{
+			MonoObject* managedInstance = MonoUtil::getObjectFromGCHandle(parent->mGCHandle);
+
+			mNumEntries = *(UINT32*)MonoUtil::unbox(parent->mCountProp->get(managedInstance));
+			MonoObject* keyCollection = parent->mKeysProp->get(managedInstance);
+			MonoObject* valueCollection = parent->mValuesProp->get(managedInstance);
+
+			mKeyType = parent->mDictionaryTypeInfo->mKeyType->getMonoClass();
+			mValueType = parent->mDictionaryTypeInfo->mValueType->getMonoClass();
+
+			ScriptArray keys(mKeyType, mNumEntries);
+			ScriptArray values(mValueType, mNumEntries);
+
+			UINT32 offset = 0;
+			void* keyParams[2] = { keys.getInternal(), &offset };
+			parent->mKeysCopyTo->invoke(keyCollection, keyParams);
+
+			void* valueParams[2] = { values.getInternal(), &offset };
+			parent->mValuesCopyTo->invoke(valueCollection, valueParams);
+
+			keysArray = keys.getInternal();
+			valuesArray = values.getInternal();
+		}
+		else
+			mNumEntries = (UINT32)parent->mCachedEntries.size();
+
+		// Note: Handle needed since Enumerator will be on the stack? meaning the GC should be able to find the references.
+		if(keysArray && valuesArray)
+		{
+			mKeysArrayHandle = MonoUtil::newGCHandle((MonoObject*)keysArray, false);
+			mValuesArrayHandle = MonoUtil::newGCHandle((MonoObject*)valuesArray, false);
+		}
+	}
+
+	ManagedSerializableDictionary::Enumerator::Enumerator(const Enumerator& other)
+		: mNumEntries(other.mNumEntries), mIteratorInitialized(false), mParent(other.mParent)
+	{
+		if(other.mKeysArrayHandle != 0 && other.mValuesArrayHandle != 0)
+		{
+			MonoObject* keysArray = MonoUtil::getObjectFromGCHandle(other.mKeysArrayHandle);
+			mKeysArrayHandle = MonoUtil::newGCHandle(keysArray, false);
+
+			MonoObject* valuesArray = MonoUtil::getObjectFromGCHandle(other.mValuesArrayHandle);
+			mValuesArrayHandle = MonoUtil::newGCHandle(valuesArray, false);
+
+			mKeyType = other.mKeyType;
+			mValueType = other.mValueType;
+		}
+	}
+
+	ManagedSerializableDictionary::Enumerator::~Enumerator()
+	{
+		if(mKeysArrayHandle != 0)
+			MonoUtil::freeGCHandle(mKeysArrayHandle);
+
+		if(mValuesArrayHandle != 0)
+			MonoUtil::freeGCHandle(mValuesArrayHandle);
+	}
+
+	ManagedSerializableDictionary::Enumerator& 
+		ManagedSerializableDictionary::Enumerator::operator=(const Enumerator& other)
+	{
+		mNumEntries = other.mNumEntries;
+		mIteratorInitialized = false;
+		mParent = other.mParent;
+		mKeyType = nullptr;
+		mValueType = nullptr;
+
+		if(mKeysArrayHandle != 0)
+		{
+			MonoUtil::freeGCHandle(mKeysArrayHandle);
+			mKeysArrayHandle = 0;
+		}
+
+		if(mValuesArrayHandle != 0)
+		{
+			MonoUtil::freeGCHandle(mValuesArrayHandle);
+			mValuesArrayHandle = 0;
+		}
+
+		if(other.mKeysArrayHandle != 0 && other.mValuesArrayHandle != 0)
+		{
+			MonoObject* keysArray = MonoUtil::getObjectFromGCHandle(other.mKeysArrayHandle);
+			mKeysArrayHandle = MonoUtil::newGCHandle(keysArray, false);
+
+			MonoObject* valuesArray = MonoUtil::getObjectFromGCHandle(other.mValuesArrayHandle);
+			mValuesArrayHandle = MonoUtil::newGCHandle(valuesArray, false);
+
+			mKeyType = other.mKeyType;
+			mValueType = other.mValueType;
+		}
+
+		return *this;
+	}
 
 	SPtr<ManagedSerializableFieldData> ManagedSerializableDictionary::Enumerator::getKey() const
 	{
-		if (mInstance != nullptr)
+		if (mKeysArrayHandle != 0)
 		{
-			MonoObject* obj = mParent->mKeyProp->get(mCurrent);
+			MonoArray* keysArray = (MonoArray*)MonoUtil::getObjectFromGCHandle(mKeysArrayHandle);
+			ScriptArray keys(keysArray);
 
-			return ManagedSerializableFieldData::create(mParent->mDictionaryTypeInfo->mKeyType, obj);
+			if(mCurrentIdx != (UINT32)-1)
+			{
+				void* val = (void*)keys.getRaw(mCurrentIdx, keys.elementSize());
+
+				MonoObject* obj = nullptr;
+				if (MonoUtil::isValueType(mKeyType))
+				{
+					if (val != nullptr)
+						obj = MonoUtil::box(mKeyType, val);
+				}
+				else
+					obj = *(MonoObject**)val;
+
+				return ManagedSerializableFieldData::create(mParent->mDictionaryTypeInfo->mKeyType, obj);
+			}
+			else
+				return nullptr;
 		}
 		else
 		{
@@ -59,11 +173,28 @@ namespace bs
 
 	SPtr<ManagedSerializableFieldData> ManagedSerializableDictionary::Enumerator::getValue() const
 	{
-		if (mInstance != nullptr)
+		if (mValuesArrayHandle != 0)
 		{
-			MonoObject* obj = mParent->mValueProp->get(mCurrent);
+			MonoArray* valuesArray = (MonoArray*)MonoUtil::getObjectFromGCHandle(mValuesArrayHandle);
+			ScriptArray values(valuesArray);
 
-			return ManagedSerializableFieldData::create(mParent->mDictionaryTypeInfo->mValueType, obj);
+			if(mCurrentIdx != (UINT32)-1)
+			{
+				void* val = (void*)values.getRaw(mCurrentIdx, values.elementSize());
+
+				MonoObject* obj = nullptr;
+				if (MonoUtil::isValueType(mValueType))
+				{
+					if (val != nullptr)
+						obj = MonoUtil::box(mValueType, val);
+				}
+				else
+					obj = *(MonoObject**)val;
+
+				return ManagedSerializableFieldData::create(mParent->mDictionaryTypeInfo->mValueType, obj);
+			}
+			else
+				return nullptr;
 		}
 		else
 		{
@@ -73,17 +204,15 @@ namespace bs
 
 	bool ManagedSerializableDictionary::Enumerator::moveNext()
 	{
-		if (mInstance != nullptr)
+		if (mKeysArrayHandle != 0 && mValuesArrayHandle != 0)
 		{
-			MonoObject* returnVal = mParent->mEnumMoveNext->invoke(mInstance, nullptr);
-			bool isValid = *(bool*)MonoUtil::unbox(returnVal);
-
-			if (isValid)
-				mCurrent = (MonoObject*)MonoUtil::unbox(mParent->mEnumCurrentProp->get(mInstance));
-			else
-				mCurrent = nullptr;
+			if((mCurrentIdx + 1) < mNumEntries)
+			{
+				mCurrentIdx++;
+				return true;
+			}
 
-			return isValid;
+			return false;
 		}
 		else
 		{
@@ -100,16 +229,13 @@ namespace bs
 	}
 
 	ManagedSerializableDictionary::ManagedSerializableDictionary(const ConstructPrivately& dummy)
-		: mManagedInstance(nullptr), mAddMethod(nullptr), mRemoveMethod(nullptr), mTryGetValueMethod(nullptr)
-		, mContainsKeyMethod(nullptr), mGetEnumerator(nullptr), mEnumMoveNext(nullptr), mEnumCurrentProp(nullptr)
-		, mKeyProp(nullptr), mValueProp(nullptr)
 	{ }
 
 	ManagedSerializableDictionary::ManagedSerializableDictionary(const ConstructPrivately& dummy, const SPtr<ManagedSerializableTypeInfoDictionary>& typeInfo, MonoObject* managedInstance)
-		: mManagedInstance(managedInstance), mAddMethod(nullptr), mRemoveMethod(nullptr), mTryGetValueMethod(nullptr)
-		, mContainsKeyMethod(nullptr), mGetEnumerator(nullptr), mEnumMoveNext(nullptr), mEnumCurrentProp(nullptr)
-		, mKeyProp(nullptr), mValueProp(nullptr), mDictionaryTypeInfo(typeInfo)
+		: mDictionaryTypeInfo(typeInfo)
 	{
+		mGCHandle = MonoUtil::newGCHandle(managedInstance, false);
+
 		MonoClass* dictClass = MonoManager::instance().findClass(MonoUtil::getClass(managedInstance));
 		if (dictClass == nullptr)
 			return;
@@ -117,7 +243,17 @@ namespace bs
 		initMonoObjects(dictClass);
 	}
 
-	SPtr<ManagedSerializableDictionary> ManagedSerializableDictionary::createFromExisting(MonoObject* managedInstance, const SPtr<ManagedSerializableTypeInfoDictionary>& typeInfo)
+	ManagedSerializableDictionary::~ManagedSerializableDictionary()
+	{
+		if(mGCHandle != 0)
+		{
+			MonoUtil::freeGCHandle(mGCHandle);
+			mGCHandle = 0;
+		}
+	}
+
+	SPtr<ManagedSerializableDictionary> ManagedSerializableDictionary::createFromExisting(MonoObject* managedInstance, 
+		const SPtr<ManagedSerializableTypeInfoDictionary>& typeInfo)
 	{
 		if(managedInstance == nullptr)
 			return nullptr;
@@ -157,12 +293,21 @@ namespace bs
 		return bs_shared_ptr_new<ManagedSerializableDictionary>(ConstructPrivately());
 	}
 
+	MonoObject* ManagedSerializableDictionary::getManagedInstance() const
+	{
+		if(mGCHandle != 0)
+			return MonoUtil::getObjectFromGCHandle(mGCHandle);
+
+		return nullptr;
+	}
+
 	void ManagedSerializableDictionary::serialize()
 	{
-		if (mManagedInstance == nullptr)
+		if (mGCHandle == 0)
 			return;
 
-		MonoClass* dictionaryClass = MonoManager::instance().findClass(MonoUtil::getClass(mManagedInstance));
+		MonoObject* managedInstance = MonoUtil::getObjectFromGCHandle(mGCHandle);
+		MonoClass* dictionaryClass = MonoManager::instance().findClass(MonoUtil::getClass(managedInstance));
 		if (dictionaryClass == nullptr)
 			return;
 
@@ -184,23 +329,20 @@ namespace bs
 			fieldEntry.second->serialize();
 		}
 
-		mManagedInstance = nullptr;
+		MonoUtil::freeGCHandle(mGCHandle);
+		mGCHandle = 0;
 	}
 
-	void ManagedSerializableDictionary::deserialize()
+	MonoObject* ManagedSerializableDictionary::deserialize()
 	{
-		mManagedInstance = createManagedInstance(mDictionaryTypeInfo);
-
-		if (mManagedInstance == nullptr)
-		{
-			mCachedEntries.clear();
-			return;
-		}
+		MonoObject* managedInstance = createManagedInstance(mDictionaryTypeInfo);
+		if (managedInstance == nullptr)
+			return nullptr;
 
 		::MonoClass* dictionaryMonoClass = mDictionaryTypeInfo->getMonoClass();
 		MonoClass* dictionaryClass = MonoManager::instance().findClass(dictionaryMonoClass);
 		if (dictionaryClass == nullptr)
-			return;
+			return nullptr;
 
 		initMonoObjects(dictionaryClass);
 
@@ -214,16 +356,16 @@ namespace bs
 		UINT32 idx = 0;
 		for (auto& entry : mCachedEntries)
 		{
-			setFieldData(entry.first, entry.second);
+			setFieldData(managedInstance, entry.first, entry.second);
 			idx++;
 		}
 
-		mCachedEntries.clear();
+		return managedInstance;
 	}
 
 	SPtr<ManagedSerializableFieldData> ManagedSerializableDictionary::getFieldData(const SPtr<ManagedSerializableFieldData>& key)
 	{
-		if (mManagedInstance != nullptr)
+		if (mGCHandle != 0)
 		{
 			MonoObject* value = nullptr;
 
@@ -231,7 +373,8 @@ namespace bs
 			params[0] = key->getValue(mDictionaryTypeInfo->mKeyType);
 			params[1] = &value;
 
-			mTryGetValueMethod->invoke(mManagedInstance, params);
+			MonoObject* managedInstance = MonoUtil::getObjectFromGCHandle(mGCHandle);
+			mTryGetValueMethod->invoke(managedInstance, params);
 
 			MonoObject* boxedValue = value;
 			::MonoClass* valueTypeClass = mDictionaryTypeInfo->mValueType->getMonoClass();
@@ -251,13 +394,10 @@ namespace bs
 
 	void ManagedSerializableDictionary::setFieldData(const SPtr<ManagedSerializableFieldData>& key, const SPtr<ManagedSerializableFieldData>& val)
 	{
-		if (mManagedInstance != nullptr)
+		if (mGCHandle != 0)
 		{
-			void* params[2];
-			params[0] = key->getValue(mDictionaryTypeInfo->mKeyType);
-			params[1] = val->getValue(mDictionaryTypeInfo->mValueType);
-
-			mAddMethod->invoke(mManagedInstance, params);
+			MonoObject* managedInstance = MonoUtil::getObjectFromGCHandle(mGCHandle);
+			setFieldData(managedInstance, key, val);
 		}
 		else
 		{
@@ -265,14 +405,24 @@ namespace bs
 		}
 	}
 
+	void ManagedSerializableDictionary::setFieldData(MonoObject* obj, const SPtr<ManagedSerializableFieldData>& key, const SPtr<ManagedSerializableFieldData>& val)
+	{
+		void* params[2];
+		params[0] = key->getValue(mDictionaryTypeInfo->mKeyType);
+		params[1] = val->getValue(mDictionaryTypeInfo->mValueType);
+
+		mAddMethod->invoke(obj, params);
+	}
+
 	void ManagedSerializableDictionary::removeFieldData(const SPtr<ManagedSerializableFieldData>& key)
 	{
-		if (mManagedInstance != nullptr)
+		if (mGCHandle != 0)
 		{
 			void* params[1];
 			params[0] = key->getValue(mDictionaryTypeInfo->mKeyType);
 
-			mRemoveMethod->invoke(mManagedInstance, params);
+			MonoObject* managedInstance = MonoUtil::getObjectFromGCHandle(mGCHandle);
+			mRemoveMethod->invoke(managedInstance, params);
 		}
 		else
 		{
@@ -284,12 +434,13 @@ namespace bs
 
 	bool ManagedSerializableDictionary::contains(const SPtr<ManagedSerializableFieldData>& key) const
 	{
-		if (mManagedInstance != nullptr)
+		if (mGCHandle != 0)
 		{
 			void* params[1];
 			params[0] = key->getValue(mDictionaryTypeInfo->mKeyType);
 
-			MonoObject* returnVal = mContainsKeyMethod->invoke(mManagedInstance, params);
+			MonoObject* managedInstance = MonoUtil::getObjectFromGCHandle(mGCHandle);
+			MonoObject* returnVal = mContainsKeyMethod->invoke(managedInstance, params);
 			return *(bool*)MonoUtil::unbox(returnVal);
 		}
 		else
@@ -298,7 +449,7 @@ namespace bs
 
 	ManagedSerializableDictionary::Enumerator ManagedSerializableDictionary::getEnumerator() const
 	{
-		return Enumerator((MonoObject*)MonoUtil::unbox(mGetEnumerator->invoke(mManagedInstance, nullptr)), this);
+		return Enumerator(this);
 	}
 
 	void ManagedSerializableDictionary::initMonoObjects(MonoClass* dictionaryClass)
@@ -307,19 +458,15 @@ namespace bs
 		mRemoveMethod = dictionaryClass->getMethod("Remove", 1);
 		mTryGetValueMethod = dictionaryClass->getMethod("TryGetValue", 2);
 		mContainsKeyMethod = dictionaryClass->getMethod("ContainsKey", 1);
-		mGetEnumerator = dictionaryClass->getMethod("GetEnumerator");
-
-		MonoClass* enumeratorClass = mGetEnumerator->getReturnType();
-		assert(enumeratorClass != nullptr);
-
-		mEnumMoveNext = enumeratorClass->getMethod("MoveNext");
-		mEnumCurrentProp = enumeratorClass->getProperty("Current");
+		mCountProp = dictionaryClass->getProperty("Count");
+		mKeysProp = dictionaryClass->getProperty("Keys");
+		mValuesProp = dictionaryClass->getProperty("Values");
 
-		MonoClass* keyValuePairClass = mEnumCurrentProp->getReturnType();
-		assert(keyValuePairClass != nullptr);
+		MonoClass* keyCollectionClass = mKeysProp->getReturnType();
+		mKeysCopyTo = keyCollectionClass->getMethod("CopyTo", 2);
 
-		mKeyProp = keyValuePairClass->getProperty("Key");
-		mValueProp = keyValuePairClass->getProperty("Value");
+		MonoClass* valueCollectionClass = mValuesProp->getReturnType();
+		mValuesCopyTo = valueCollectionClass->getMethod("CopyTo", 2);
 	}
 
 	RTTITypeBase* ManagedSerializableDictionary::getRTTIStatic()

+ 56 - 34
Source/SBansheeEngine/Serialization/BsManagedSerializableDictionary.h

@@ -4,6 +4,7 @@
 
 #include "BsScriptEnginePrerequisites.h"
 #include "Reflection/BsIReflectable.h"
+#include "BsMonoArray.h"
 
 namespace bs
 {
@@ -27,7 +28,7 @@ namespace bs
 	public:
 		friend class ManagedSerializableDictionaryKeyValueRTTI;
 		static RTTITypeBase* getRTTIStatic();
-		virtual RTTITypeBase* getRTTI() const override;
+		RTTITypeBase* getRTTI() const override;
 	};
 
 	/**
@@ -37,12 +38,15 @@ namespace bs
 	 * @note
 	 * This class can be in two states:
 	 *	 - Linked - When the object has a link to a managed object. This is the default state when a new instance
-	 *				of ManagedSerializableObject is created. Any operations during this state will operate directly
-	 *				on the linked managed object.
+	 *				of ManagedSerializableDictionary is created. Any operations during this state will operate directly
+	 *				on the linked managed object. A GC handle will be kept to the linked managed object. The handle can
+	 *				be freed by transfering to serialized mode or by destroying this object.
 	 *	 - Serialized - When the object has no link to the managed object but instead just contains cached object
 	 *					and field data that may be used for initializing a managed object. Any operations during
 	 *					this state will operate only on the cached internal data.
-	 * You can transfer between these states by calling serialize(linked->serialized) & deserialize (serialized->linked).
+	 *					
+	 * You can transfer an object in linked state to serialized state by calling serialize(). If an object is in serialized
+	 * state you can call deserialize() to populated a managed object from the cached data. 	
 	 */
 	class BS_SCR_BE_EXPORT ManagedSerializableDictionary : public IReflectable
 	{
@@ -72,12 +76,15 @@ namespace bs
 		{
 		public:
 			/**
-			 * Constructs a new enumerator for the provided managed object.
+			 * Constructs a new enumerator for a managed dictionary.
 			 *
-			 * @param[in]	instance	Managed instance of type System.Collections.Generic.Dictionary.
-			 * @param[in]	parent		Serializable parent of the managed instance.
+			 * @param[in]	parent		Owning dictionary object.
 			 */
-			Enumerator(MonoObject* instance, const ManagedSerializableDictionary* parent);
+			Enumerator(const ManagedSerializableDictionary* parent);
+			Enumerator(const Enumerator& other);
+			~Enumerator();
+
+			Enumerator& operator=(const Enumerator& other);
 
 			/**
 			 * Returns the wrapped key data at the current enumerator position. Only valid to call this if enumerator is
@@ -101,8 +108,13 @@ namespace bs
 			bool moveNext();
 
 		private:
-			MonoObject* mInstance;
-			MonoObject* mCurrent;
+			uint32_t mKeysArrayHandle = 0;
+			uint32_t mValuesArrayHandle = 0;
+			UINT32 mNumEntries = 0;
+			UINT32 mCurrentIdx = (UINT32)-1;
+			::MonoClass* mKeyType = nullptr;
+			::MonoClass* mValueType = nullptr;
+
 			CachedEntriesMap::const_iterator mCachedIter;
 			bool mIteratorInitialized;
 
@@ -110,14 +122,16 @@ namespace bs
 		};
 
 	public:
-		ManagedSerializableDictionary(const ConstructPrivately& dummy, const SPtr<ManagedSerializableTypeInfoDictionary>& typeInfo, MonoObject* managedInstance);
+		ManagedSerializableDictionary(const ConstructPrivately& dummy, 
+			const SPtr<ManagedSerializableTypeInfoDictionary>& typeInfo, MonoObject* managedInstance);
 		ManagedSerializableDictionary(const ConstructPrivately& dummy);
+		~ManagedSerializableDictionary();
 
 		/**
 		 * Returns the internal managed instance of the dictionary. This will return null if the object is in serialized
 		 * mode.
 		 */
-		MonoObject* getManagedInstance() const { return mManagedInstance; }
+		MonoObject* getManagedInstance() const;
 
 		/**	Returns the type information for the internal dictionary. */
 		SPtr<ManagedSerializableTypeInfoDictionary> getTypeInfo() const { return mDictionaryTypeInfo; }
@@ -161,21 +175,18 @@ namespace bs
 
 		/**
 		 * Serializes the internal managed object into a set of cached data that can be saved in memory/disk and can be
-		 * deserialized later. Does nothing if object is already is serialized mode. When in serialized mode the reference
-		 * to the managed instance will be lost.
+		 * deserialized later. The internal managed object will be freed (if no other references to it). Calling serialize()
+		 * again will have no result.
 		 */
 		void serialize();
 
 		/**
-		 * Deserializes a set of cached data into a managed object. This action may fail in case the cached	data contains a
-		 * type that no longer exists. You may check if it completely successfully if getManagedInstance() returns non-null
-		 * after.
+		 * Deserializes a set of cached data into a managed object. This action may fail in case the cached data contains a
+		 * type that no longer exists in which case null is returned.
 		 *
-		 * This action transfers the object into linked mode. All further operations will operate directly on the managed
-		 * instance and the cached data will be cleared. If you call this method on an already linked object the old object
-		 * will be replaced and initialized with empty data (since cached data does not exist).
+		 * @return		Newly created object initialized with the cached data.
 		 */
-		void deserialize();
+		MonoObject* deserialize();
 
 		/**
 		 * Creates a managed serializable dictionary that references an existing managed dictionary. Created object will be
@@ -185,7 +196,8 @@ namespace bs
 		 *									correspond with the provided type info.
 		 * @param[in]	typeInfo			Type information for the dictionary and its key/value pair.
 		 */
-		static SPtr<ManagedSerializableDictionary> createFromExisting(MonoObject* managedInstance, const SPtr<ManagedSerializableTypeInfoDictionary>& typeInfo);
+		static SPtr<ManagedSerializableDictionary> createFromExisting(MonoObject* managedInstance, 
+			const SPtr<ManagedSerializableTypeInfoDictionary>& typeInfo);
 
 		/**
 		 * Creates a managed serializable dictionary that creates and references a brand new managed dictionary instance.
@@ -208,17 +220,27 @@ namespace bs
 		 */
 		void initMonoObjects(MonoClass* dictionaryClass);
 
-		MonoObject* mManagedInstance;
+		/**
+		 * Sets the dictionary value at the specified key. Operates on the provided managed object.
+		 *
+		 * @param[in]	obj		Managed object to which to assign the data.
+		 * @param[in]	key		Wrapper around the key data at which to set the value.
+		 * @param[in]	val		Wrapper around the value to set at the specified key.
+		 */
+		void setFieldData(MonoObject* obj, const SPtr<ManagedSerializableFieldData>& key, 
+			const SPtr<ManagedSerializableFieldData>& val);
+
+		uint32_t mGCHandle = 0;
 
-		MonoMethod* mAddMethod;
-		MonoMethod* mRemoveMethod;
-		MonoMethod* mTryGetValueMethod;
-		MonoMethod* mContainsKeyMethod;
-		MonoMethod* mGetEnumerator;
-		MonoMethod* mEnumMoveNext;
-		MonoProperty* mEnumCurrentProp;
-		MonoProperty* mKeyProp;
-		MonoProperty* mValueProp;
+		MonoMethod* mAddMethod = nullptr;
+		MonoMethod* mRemoveMethod = nullptr;
+		MonoMethod* mTryGetValueMethod = nullptr;
+		MonoMethod* mContainsKeyMethod = nullptr;
+		MonoProperty* mCountProp = nullptr;
+		MonoProperty* mKeysProp = nullptr;
+		MonoMethod* mKeysCopyTo = nullptr;
+		MonoProperty* mValuesProp = nullptr;
+		MonoMethod* mValuesCopyTo = nullptr;
 
 		SPtr<ManagedSerializableTypeInfoDictionary> mDictionaryTypeInfo;
 		CachedEntriesMap mCachedEntries;
@@ -233,8 +255,8 @@ namespace bs
 	public:
 		friend class ManagedSerializableDictionaryRTTI;
 		static RTTITypeBase* getRTTIStatic();
-		virtual RTTITypeBase* getRTTI() const override;
+		RTTITypeBase* getRTTI() const override;
 	};
 
 	/** @} */
-}
+}

+ 2 - 1
Source/SBansheeEngine/Serialization/BsManagedSerializableDiff.cpp

@@ -152,7 +152,8 @@ namespace bs
 	{
 	}
 
-	SPtr<ManagedSerializableDiff> ManagedSerializableDiff::create(const SPtr<ManagedSerializableObject>& oldObj, const SPtr<ManagedSerializableObject>& newObj)
+	SPtr<ManagedSerializableDiff> ManagedSerializableDiff::create(const SPtr<ManagedSerializableObject>& oldObj, 
+		const SPtr<ManagedSerializableObject>& newObj)
 	{
 		assert(oldObj != nullptr && newObj != nullptr);
 

+ 16 - 4
Source/SBansheeEngine/Serialization/BsManagedSerializableField.cpp

@@ -1076,7 +1076,10 @@ namespace bs
 	void ManagedSerializableFieldDataObject::deserialize()
 	{
 		if (value != nullptr)
-			value->deserialize();
+		{
+			MonoObject* managedInstance = value->deserialize();
+			value = ManagedSerializableObject::createFromExisting(managedInstance);
+		}
 	}
 
 	void ManagedSerializableFieldDataArray::serialize()
@@ -1088,7 +1091,10 @@ namespace bs
 	void ManagedSerializableFieldDataArray::deserialize()
 	{
 		if (value != nullptr)
-			value->deserialize();
+		{
+			MonoObject* managedInstance = value->deserialize();
+			value = ManagedSerializableArray::createFromExisting(managedInstance, value->getTypeInfo());
+		}
 	}
 
 	void ManagedSerializableFieldDataList::serialize()
@@ -1100,7 +1106,10 @@ namespace bs
 	void ManagedSerializableFieldDataList::deserialize()
 	{
 		if (value != nullptr)
-			value->deserialize();
+		{
+			MonoObject* managedInstance = value->deserialize();
+			value = ManagedSerializableList::createFromExisting(managedInstance, value->getTypeInfo());
+		}
 	}
 
 	void ManagedSerializableFieldDataDictionary::serialize()
@@ -1112,7 +1121,10 @@ namespace bs
 	void ManagedSerializableFieldDataDictionary::deserialize()
 	{
 		if (value != nullptr)
-			value->deserialize();
+		{
+			MonoObject* managedInstance = value->deserialize();
+			value = ManagedSerializableDictionary::createFromExisting(managedInstance, value->getTypeInfo());
+		}
 	}
 
 	RTTITypeBase* ManagedSerializableFieldKey::getRTTIStatic()

+ 52 - 24
Source/SBansheeEngine/Serialization/BsManagedSerializableList.cpp

@@ -13,16 +13,16 @@
 namespace bs
 {
 	ManagedSerializableList::ManagedSerializableList(const ConstructPrivately& dummy)
-		: mManagedInstance(nullptr), mAddMethod(nullptr), mAddRangeMethod(nullptr), mClearMethod(nullptr)
-		, mCopyToMethod(nullptr), mItemProp(nullptr), mCountProp(nullptr), mNumElements(0)
 	{
 
 	}
 
-	ManagedSerializableList::ManagedSerializableList(const ConstructPrivately& dummy, const SPtr<ManagedSerializableTypeInfoList>& typeInfo, MonoObject* managedInstance)
-		: mManagedInstance(managedInstance), mAddMethod(nullptr), mAddRangeMethod(nullptr), mClearMethod(nullptr)
-		, mCopyToMethod(nullptr), mItemProp(nullptr), mCountProp(nullptr), mListTypeInfo(typeInfo), mNumElements(0)
+	ManagedSerializableList::ManagedSerializableList(const ConstructPrivately& dummy, 
+		const SPtr<ManagedSerializableTypeInfoList>& typeInfo, MonoObject* managedInstance)
+		:mListTypeInfo(typeInfo)
 	{
+		mGCHandle = MonoUtil::newGCHandle(managedInstance, false);
+
 		MonoClass* listClass = MonoManager::instance().findClass(MonoUtil::getClass(managedInstance));
 		if(listClass == nullptr)
 			return;
@@ -32,7 +32,17 @@ namespace bs
 		mNumElements = getLengthInternal();
 	}
 
-	SPtr<ManagedSerializableList> ManagedSerializableList::createFromExisting(MonoObject* managedInstance, const SPtr<ManagedSerializableTypeInfoList>& typeInfo)
+	ManagedSerializableList::~ManagedSerializableList()
+	{
+		if(mGCHandle != 0)
+		{
+			MonoUtil::freeGCHandle(mGCHandle);
+			mGCHandle = 0;
+		}
+	}
+
+	SPtr<ManagedSerializableList> ManagedSerializableList::createFromExisting(MonoObject* managedInstance, 
+		const SPtr<ManagedSerializableTypeInfoList>& typeInfo)
 	{
 		if(managedInstance == nullptr)
 			return nullptr;
@@ -81,26 +91,45 @@ namespace bs
 		return bs_shared_ptr_new<ManagedSerializableList>(ConstructPrivately());
 	}
 
+	MonoObject* ManagedSerializableList::getManagedInstance() const
+	{
+		if(mGCHandle != 0)
+			return MonoUtil::getObjectFromGCHandle(mGCHandle);
+
+		return nullptr;
+	}
+
 	void ManagedSerializableList::setFieldData(UINT32 arrayIdx, const SPtr<ManagedSerializableFieldData>& val)
 	{
-		if (mManagedInstance != nullptr)
-			mItemProp->setIndexed(mManagedInstance, arrayIdx, val->getValue(mListTypeInfo->mElementType));
+		if (mGCHandle != 0)
+		{
+			MonoObject* managedInstance = MonoUtil::getObjectFromGCHandle(mGCHandle);
+			setFieldData(managedInstance, arrayIdx, val);
+		}
 		else
 			mCachedEntries[arrayIdx] = val;
 	}
 
+	void ManagedSerializableList::setFieldData(MonoObject* obj, UINT32 arrayIdx, const SPtr<ManagedSerializableFieldData>& val)
+	{
+		mItemProp->setIndexed(obj, arrayIdx, val->getValue(mListTypeInfo->mElementType));
+	}
+
 	void ManagedSerializableList::addFieldDataInternal(const SPtr<ManagedSerializableFieldData>& val)
 	{
+		MonoObject* managedInstance = MonoUtil::getObjectFromGCHandle(mGCHandle);
+
 		void* params[1];
 		params[0] = val->getValue(mListTypeInfo->mElementType);
-		mAddMethod->invoke(mManagedInstance, params);
+		mAddMethod->invoke(managedInstance, params);
 	}
 
 	SPtr<ManagedSerializableFieldData> ManagedSerializableList::getFieldData(UINT32 arrayIdx)
 	{
-		if (mManagedInstance != nullptr)
+		if (mGCHandle != 0)
 		{
-			MonoObject* obj = mItemProp->getIndexed(mManagedInstance, arrayIdx);
+			MonoObject* managedInstance = MonoUtil::getObjectFromGCHandle(mGCHandle);
+			MonoObject* obj = mItemProp->getIndexed(managedInstance, arrayIdx);
 
 			return ManagedSerializableFieldData::create(mListTypeInfo->mElementType, obj);
 		}
@@ -110,7 +139,7 @@ namespace bs
 
 	void ManagedSerializableList::resize(UINT32 newSize)
 	{
-		if (mManagedInstance != nullptr)
+		if (mGCHandle != 0)
 		{
 			ScriptArray tempArray(mListTypeInfo->mElementType->getMonoClass(), newSize);
 
@@ -139,7 +168,7 @@ namespace bs
 
 	void ManagedSerializableList::serialize()
 	{
-		if (mManagedInstance == nullptr)
+		if (mGCHandle == 0)
 			return;
 
 		mNumElements = getLengthInternal();
@@ -152,18 +181,16 @@ namespace bs
 		for (auto& fieldEntry : mCachedEntries)
 			fieldEntry->serialize();
 
-		mManagedInstance = nullptr;
+		MonoUtil::freeGCHandle(mGCHandle);
+		mGCHandle = 0;
 	}
 
-	void ManagedSerializableList::deserialize()
+	MonoObject* ManagedSerializableList::deserialize()
 	{
-		mManagedInstance = createManagedInstance(mListTypeInfo, mNumElements);
+		MonoObject* managedInstance = createManagedInstance(mListTypeInfo, mNumElements);
 
-		if (mManagedInstance == nullptr)
-		{
-			mCachedEntries.clear();
-			return;
-		}
+		if (managedInstance == nullptr)
+			return nullptr;
 
 		MonoClass* listClass = MonoManager::instance().findClass(mListTypeInfo->getMonoClass());
 		initMonoObjects(listClass);
@@ -175,16 +202,17 @@ namespace bs
 		UINT32 idx = 0;
 		for (auto& entry : mCachedEntries)
 		{
-			setFieldData(idx, entry);
+			setFieldData(managedInstance, idx, entry);
 			idx++;
 		}
 
-		mCachedEntries.clear();
+		return managedInstance;
 	}
 
 	UINT32 ManagedSerializableList::getLengthInternal() const
 	{
-		MonoObject* length = mCountProp->get(mManagedInstance);
+		MonoObject* managedInstance = MonoUtil::getObjectFromGCHandle(mGCHandle);
+		MonoObject* length = mCountProp->get(managedInstance);
 
 		if(length == nullptr)
 			return 0;

+ 33 - 22
Source/SBansheeEngine/Serialization/BsManagedSerializableList.h

@@ -17,12 +17,15 @@ namespace bs
 	 * @note
 	 * This class can be in two states:
 	 *	 - Linked - When the object has a link to a managed object. This is the default state when a new instance
-	 *				of ManagedSerializableObject is created. Any operations during this state will operate directly
-	 *				on the linked managed object.
+	 *				of ManagedSerializableList is created. Any operations during this state will operate directly
+	 *				on the linked managed object. A GC handle will be kept to the linked managed object. The handle can
+	 *				be freed by transfering to serialized mode or by destroying this object.
 	 *	 - Serialized - When the object has no link to the managed object but instead just contains cached object
 	 *					and field data that may be used for initializing a managed object. Any operations during
 	 *					this state will operate only on the cached internal data.
-	 * You can transfer between these states by calling serialize(linked->serialized) & deserialize (serialized->linked).
+	 *					
+	 * You can transfer an object in linked state to serialized state by calling serialize(). If an object is in serialized
+	 * state you can call deserialize() to populated a managed object from the cached data. 	
 	 */
 	class BS_SCR_BE_EXPORT ManagedSerializableList : public IReflectable
 	{
@@ -30,11 +33,13 @@ namespace bs
 		struct ConstructPrivately {};
 
 	public:
-		ManagedSerializableList(const ConstructPrivately& dummy, const SPtr<ManagedSerializableTypeInfoList>& typeInfo, MonoObject* managedInstance);
+		ManagedSerializableList(const ConstructPrivately& dummy, const SPtr<ManagedSerializableTypeInfoList>& typeInfo, 
+			MonoObject* managedInstance);
 		ManagedSerializableList(const ConstructPrivately& dummy);
+		~ManagedSerializableList();
 
 		/** Returns the internal managed instance of the list. This will return null if the object is in serialized mode. */
-		MonoObject* getManagedInstance() const { return mManagedInstance; }
+		MonoObject* getManagedInstance() const;
 
 		/**	Returns the type information for the internal list. */
 		SPtr<ManagedSerializableTypeInfoList> getTypeInfo() const { return mListTypeInfo; }
@@ -65,21 +70,18 @@ namespace bs
 
 		/**
 		 * Serializes the internal managed object into a set of cached data that can be saved in memory/disk and can be
-		 * deserialized later. Does nothing if object is already is serialized mode. When in serialized mode the reference
-		 * to the managed instance will be lost.
+		 * deserialized later. The internal managed object will be freed (if no other references to it). Calling serialize()
+		 * again will have no result.
 		 */
 		void serialize();
 
 		/**
 		 * Deserializes a set of cached data into a managed object. This action may fail in case the cached data contains a
-		 * type that no longer exists. You may check if it completely successfully if getManagedInstance() returns non-null
-		 * after.
+		 * type that no longer exists in which case null is returned.
 		 *
-		 * This action transfers the object into linked mode. All further operations will operate directly on the managed
-		 * instance and the cached data will be cleared. If you call this method on an already linked object the old object
-		 * will be replaced and initialized with empty data (since cached data does not exist).
+		 * @return		Newly created object initialized with the cached data.
 		 */
-		void deserialize();
+		MonoObject* deserialize();
 
 		/**
 		 * Creates a managed serializable list that references an existing managed list. Created object will be in linked
@@ -118,21 +120,30 @@ namespace bs
 		/**	Returns the size of the list. Operates on the internal managed object. */
 		UINT32 getLengthInternal() const;
 
+		/**
+		 * Sets a new element value at the specified array index. Operates on the provided managed instance.
+		 * 
+		 * @param[in]	obj			Managed instance in which to set the data in.
+		 * @param[in]	arrayIdx	Index at which to set the value.
+		 * @param[in]	val			Wrapper around the value to store in the array. Must be of the array element type.
+		 */
+		void setFieldData(MonoObject* obj, UINT32 arrayIdx, const SPtr<ManagedSerializableFieldData>& val);
+
 		/** Appends data to the end of the list. Operates on the internal managed object. */
 		void addFieldDataInternal(const SPtr<ManagedSerializableFieldData>& val);
 
-		MonoObject* mManagedInstance;
+		uint32_t mGCHandle = 0;
 
-		MonoMethod* mAddMethod;
-		MonoMethod* mAddRangeMethod;
-		MonoMethod* mClearMethod;
-		MonoMethod* mCopyToMethod;
-		MonoProperty* mItemProp;
-		MonoProperty* mCountProp;
+		MonoMethod* mAddMethod = nullptr;
+		MonoMethod* mAddRangeMethod = nullptr;
+		MonoMethod* mClearMethod = nullptr;
+		MonoMethod* mCopyToMethod = nullptr;
+		MonoProperty* mItemProp = nullptr;
+		MonoProperty* mCountProp = nullptr;
 
 		SPtr<ManagedSerializableTypeInfoList> mListTypeInfo;
 		Vector<SPtr<ManagedSerializableFieldData>> mCachedEntries;
-		UINT32 mNumElements;
+		UINT32 mNumElements = 0;
 
 		/************************************************************************/
 		/* 								RTTI		                     		*/
@@ -144,7 +155,7 @@ namespace bs
 	public:
 		friend class ManagedSerializableListRTTI;
 		static RTTITypeBase* getRTTIStatic();
-		virtual RTTITypeBase* getRTTI() const override;
+		RTTITypeBase* getRTTI() const override;
 	};
 
 	/** @} */

+ 39 - 24
Source/SBansheeEngine/Serialization/BsManagedSerializableObject.cpp

@@ -26,15 +26,23 @@ namespace bs
 	}
 
 	ManagedSerializableObject::ManagedSerializableObject(const ConstructPrivately& dummy)
-		:mManagedInstance(nullptr)
 	{
 
 	}
 
 	ManagedSerializableObject::ManagedSerializableObject(const ConstructPrivately& dummy, SPtr<ManagedSerializableObjectInfo> objInfo, MonoObject* managedInstance)
-		:mManagedInstance(managedInstance), mObjInfo(objInfo)
+		:mObjInfo(objInfo)
 	{
+		mGCHandle = MonoUtil::newGCHandle(managedInstance, false);
+	}
 
+	ManagedSerializableObject::~ManagedSerializableObject()
+	{
+		if(mGCHandle != 0)
+		{
+			MonoUtil::freeGCHandle(mGCHandle);
+			mGCHandle = 0;
+		}		
 	}
 
 	SPtr<ManagedSerializableObject> ManagedSerializableObject::createFromExisting(MonoObject* managedInstance)
@@ -82,9 +90,17 @@ namespace bs
 		return bs_shared_ptr_new<ManagedSerializableObject>(ConstructPrivately());
 	}
 
+	MonoObject* ManagedSerializableObject::getManagedInstance() const
+	{
+		if(mGCHandle != 0)
+			return MonoUtil::getObjectFromGCHandle(mGCHandle);
+
+		return nullptr;
+	}
+
 	void ManagedSerializableObject::serialize()
 	{
-		if (mManagedInstance == nullptr)
+		if(mGCHandle == 0)
 			return;
 
 		mCachedData.clear();
@@ -108,32 +124,30 @@ namespace bs
 		for (auto& fieldEntry : mCachedData)
 			fieldEntry.second->serialize();
 
-		mManagedInstance = nullptr;
+		MonoUtil::freeGCHandle(mGCHandle);
+		mGCHandle = 0;
 	}
 
-	void ManagedSerializableObject::deserialize()
+	MonoObject* ManagedSerializableObject::deserialize()
 	{
 		// See if this type even still exists
 		SPtr<ManagedSerializableObjectInfo> currentObjInfo = nullptr;
-		if (!ScriptAssemblyManager::instance().getSerializableObjectInfo(mObjInfo->mTypeInfo->mTypeNamespace, mObjInfo->mTypeInfo->mTypeName, currentObjInfo))
+		if (!ScriptAssemblyManager::instance().getSerializableObjectInfo(mObjInfo->mTypeInfo->mTypeNamespace, 
+			mObjInfo->mTypeInfo->mTypeName, currentObjInfo))
 		{
-			mManagedInstance = nullptr;
-			mCachedData.clear();
-			return;
+			return nullptr;
 		}
 
-		deserialize(createManagedInstance(currentObjInfo->mTypeInfo), currentObjInfo);
+		MonoObject* managedInstance = createManagedInstance(currentObjInfo->mTypeInfo);
+		deserialize(managedInstance, currentObjInfo);
+
+		return managedInstance;
 	}
 
 	void ManagedSerializableObject::deserialize(MonoObject* instance, const SPtr<ManagedSerializableObjectInfo>& objInfo)
 	{
-		mManagedInstance = instance;
-
-		if (mManagedInstance == nullptr)
-		{
-			mCachedData.clear();
+		if (instance == nullptr)
 			return;
-		}
 
 		// Deserialize children
 		for (auto& fieldEntry : mCachedData)
@@ -155,7 +169,7 @@ namespace bs
 
 					SPtr<ManagedSerializableMemberInfo> matchingFieldInfo = objInfo->findMatchingField(field.second, curType->mTypeInfo);
 					if (matchingFieldInfo != nullptr)
-						setFieldData(matchingFieldInfo, mCachedData[key]);
+						matchingFieldInfo->setValue(instance, mCachedData[key]->getValue(matchingFieldInfo->mTypeInfo));
 
 					i++;
 				}
@@ -163,15 +177,15 @@ namespace bs
 
 			curType = curType->mBaseClass;
 		}
-
-		mObjInfo = objInfo;
-		mCachedData.clear();
 	}
 
 	void ManagedSerializableObject::setFieldData(const SPtr<ManagedSerializableMemberInfo>& fieldInfo, const SPtr<ManagedSerializableFieldData>& val)
 	{
-		if (mManagedInstance != nullptr)
-			fieldInfo->setValue(mManagedInstance, val->getValue(fieldInfo->mTypeInfo));
+		if (mGCHandle != 0)
+		{
+			MonoObject* managedInstance = MonoUtil::getObjectFromGCHandle(mGCHandle);
+			fieldInfo->setValue(managedInstance, val->getValue(fieldInfo->mTypeInfo));
+		}
 		else
 		{
 			ManagedSerializableFieldKey key(fieldInfo->mParentTypeId, fieldInfo->mFieldId);
@@ -181,9 +195,10 @@ namespace bs
 
 	SPtr<ManagedSerializableFieldData> ManagedSerializableObject::getFieldData(const SPtr<ManagedSerializableMemberInfo>& fieldInfo) const
 	{
-		if (mManagedInstance != nullptr)
+		if (mGCHandle != 0)
 		{
-			MonoObject* fieldValue = fieldInfo->getValue(mManagedInstance);
+			MonoObject* managedInstance = MonoUtil::getObjectFromGCHandle(mGCHandle);
+			MonoObject* fieldValue = fieldInfo->getValue(managedInstance);
 
 			return ManagedSerializableFieldData::create(fieldInfo->mTypeInfo, fieldValue);
 		}

+ 16 - 21
Source/SBansheeEngine/Serialization/BsManagedSerializableObject.h

@@ -20,12 +20,14 @@ namespace bs
 	 * This class can be in two states:
 	 *	 - Linked - When the object has a link to a managed object. This is the default state when a new instance
 	 *				of ManagedSerializableObject is created. Any operations during this state will operate directly
-	 *				on the linked managed object.
+	 *				on the linked managed object. A GC handle will be kept to the linked managed object. The handle can
+	 *				be freed by transfering to serialized mode or by destroying this object.
 	 *	 - Serialized - When the object has no link to the managed object but instead just contains cached object
 	 *					and field data that may be used for initializing a managed object. Any operations during
 	 *					this state will operate only on the cached internal data.
-	 * You can transfer between these states by calling serialize(linked->serialized) & deserialize (serialized->linked).
-	 *	
+	 *					
+	 * You can transfer an object in linked state to serialized state by calling serialize(). If an object is in serialized
+	 * state you can call deserialize() to populated a managed object from the cached data. 	
 	 */
 	class BS_SCR_BE_EXPORT ManagedSerializableObject : public IReflectable
 	{
@@ -47,11 +49,12 @@ namespace bs
 	public:
 		ManagedSerializableObject(const ConstructPrivately& dummy, SPtr<ManagedSerializableObjectInfo> objInfo, MonoObject* managedInstance);
 		ManagedSerializableObject(const ConstructPrivately& dummy);
+		~ManagedSerializableObject();
 
 		/**
 		 * Returns the internal managed instance of the object. This will return null if the object is in serialized mode.
 		 */
-		MonoObject* getManagedInstance() const { return mManagedInstance; }
+		MonoObject* getManagedInstance() const;
 
 		/**	Returns the type information for the internal object. */
 		SPtr<ManagedSerializableObjectInfo> getObjectInfo() const { return mObjInfo; }
@@ -78,30 +81,23 @@ namespace bs
 
 		/**
 		 * Serializes the internal managed object into a set of cached data that can be saved in memory/disk and can be
-		 * deserialized later. Does nothing if object is already is serialized mode. When in serialized mode the reference
-		 * to the managed instance will be lost.
+		 * deserialized later. The internal managed object will be freed (if no other references to it). Calling serialize()
+		 * again will have no result.
 		 */
 		void serialize();
 
 		/**
 		 * Deserializes a set of cached data into a managed object. This action may fail in case the cached data contains a
-		 * type that no longer exists. You may check if it completely successfully if getManagedInstance() returns non-null
-		 * after.
+		 * type that no longer exists in which case null is returned.
 		 *
-		 * This action transfers the object into linked mode. All further operations will operate directly on the managed
-		 * instance and the cached data will be cleared. If you call this method on an already linked object the old object
-		 * will be replaced and initialized with empty data (since cached data does not exist).
+		 * @return		Newly created object initialized with the cached data.
 		 */
-		void deserialize();
+		MonoObject* deserialize();
 
 		/**
 		 * Deserializes a set of cached data into an existing managed object. Caller must ensure the provided object is of
 		 * proper type.
 		 *
-		 * This action transfers the object into linked mode. All further operations will operate directly on the managed
-		 * instance and the cached data will be cleared. If you call this method on an already linked object the old object
-		 * will be replaced and initialized with empty data (since cached data does not exist).
-		 *
 		 * @param[in]	instance	Existing managed instance of the same type this serializable object represents.
 		 * @param[in]	objInfo		Serializable object info for the managed object type.
 		 */
@@ -110,13 +106,13 @@ namespace bs
 		/**
 		 * Creates a managed serializable object that references an existing managed object. Created object will be in
 		 * linked mode.
-		 *
+		 * 
 		 * @param[in]	managedInstance		Constructed managed instance of the object to link with.
 		 */
 		static SPtr<ManagedSerializableObject> createFromExisting(MonoObject* managedInstance);
 
 		/**
-		 * Creates a managed serializable object that creates and references a brand new managed object instance.
+		 * Creates a managed serializable object that creates and references a brand new managed object instance. 	
 		 *
 		 * @param[in]	type	Type of the object to create.
 		 */
@@ -129,8 +125,7 @@ namespace bs
 		 */
 		static MonoObject* createManagedInstance(const SPtr<ManagedSerializableTypeInfoObject>& type);
 	protected:
-		MonoObject* mManagedInstance;
-
+		uint32_t mGCHandle = 0;
 		SPtr<ManagedSerializableObjectInfo> mObjInfo;
 		UnorderedMap<ManagedSerializableFieldKey, SPtr<ManagedSerializableFieldData>, Hash, Equals> mCachedData;
 
@@ -144,7 +139,7 @@ namespace bs
 	public:
 		friend class ManagedSerializableObjectRTTI;
 		static RTTITypeBase* getRTTIStatic();
-		virtual RTTITypeBase* getRTTI() const override;
+		RTTITypeBase* getRTTI() const override;
 	};
 
 	/** @} */