Преглед изворни кода

C# single-dimensional array serialization works

Marko Pintera пре 11 година
родитељ
комит
ce8a58d25b

+ 1 - 0
BansheeMono/Include/BsMonoAssembly.h

@@ -31,6 +31,7 @@ namespace BansheeEngine
 		virtual ~MonoAssembly();
 
 		MonoClass* getClass(const CM::String& namespaceName, const CM::String& name) const;
+		MonoClass* getClass(::MonoClass* rawMonoClass) const;
 		const CM::Vector<MonoClass*>::type& getAllClasses() const;
 
 		void invoke(const CM::String& functionName);

+ 7 - 0
BansheeMono/Include/BsMonoClass.h

@@ -38,6 +38,13 @@ namespace BansheeEngine
 		MonoObject* getAttribute(MonoClass* monoClass) const;
 		MonoClass* getBaseClass() const;
 
+		/**
+		 * @brief	Retrieves a method, expects exact method name with parameters.
+		 *
+		 * @note	Example: Name = "CreateInstance", Signature = "type,int[]"
+		 */
+		MonoMethod* getMethodExact(const CM::String& name, const CM::String& signature);
+
 		/**
 		 * @brief	Returns all fields belonging to this class.
 		 *

+ 5 - 0
BansheeMono/Include/BsMonoManager.h

@@ -24,6 +24,11 @@ namespace BansheeEngine
 		 */
 		MonoClass* findClass(const CM::String& ns, const CM::String& typeName);
 
+		/**
+		 * @brief	Searches all loaded assemblies for the specified class.
+		 */
+		MonoClass* findClass(::MonoClass* rawMonoClass);
+
 		/**
 		 * @brief	Returns the type name of the provided object, with namespace.
 		 *

+ 23 - 0
BansheeMono/Source/BsMonoAssembly.cpp

@@ -138,6 +138,29 @@ namespace BansheeEngine
 		return newClass;
 	}
 
+	MonoClass* MonoAssembly::getClass(::MonoClass* rawMonoClass) const
+	{
+		if(!mIsLoaded)
+			CM_EXCEPT(InvalidStateException, "Trying to use an unloaded assembly.");
+
+		if(rawMonoClass == nullptr)
+			return nullptr;
+
+		String ns = mono_class_get_namespace(rawMonoClass);
+		String typeName = mono_class_get_name(rawMonoClass);
+
+		MonoAssembly::ClassId classId(ns, typeName);
+		auto iterFind = mClasses.find(classId);
+
+		if(iterFind != mClasses.end())
+			return iterFind->second;
+
+		MonoClass* newClass = new (cm_alloc<MonoClass>()) MonoClass(ns, typeName, rawMonoClass, this);
+		mClasses[classId] = newClass;
+
+		return newClass;
+	}
+
 	const CM::Vector<MonoClass*>::type& MonoAssembly::getAllClasses() const
 	{
 		if(mHaveCachedClassList)

+ 31 - 0
BansheeMono/Source/BsMonoClass.cpp

@@ -6,6 +6,7 @@
 #include "BsMonoManager.h"
 #include "CmUtil.h"
 #include "CmException.h"
+#include <mono/metadata/debug-helpers.h>
 
 using namespace CamelotFramework;
 
@@ -80,6 +81,36 @@ namespace BansheeEngine
 		return *newMethod;
 	}
 
+	MonoMethod* MonoClass::getMethodExact(const CM::String& name, const CM::String& signature)
+	{
+		MethodId mehodId(name + "(" + signature + ")", 0);
+		auto iterFind = mMethods.find(mehodId);
+		if(iterFind != mMethods.end())
+			return iterFind->second;
+
+		::MonoMethod* method;
+		void* iter = nullptr;
+
+		const char* rawName = name.c_str();
+		const char* rawSig = signature.c_str();
+		while ((method = mono_class_get_methods(mClass, &iter)))
+		{
+			if (strcmp(rawName, mono_method_get_name(method)) == 0)
+			{
+				const char* curSig = mono_signature_get_desc(mono_method_signature(method), false);
+				if(strcmp(rawSig, curSig) == 0)
+				{
+					MonoMethod* newMethod = new (cm_alloc<MonoMethod>()) MonoMethod(method);
+					mMethods[mehodId] = newMethod;
+
+					return newMethod;
+				}
+			}
+		}
+
+		return nullptr;
+	}
+
 	bool MonoClass::hasField(const String& name) const
 	{
 		MonoClassField* field = mono_class_get_field_from_name(mClass, name.c_str());

+ 1 - 4
BansheeMono/Source/BsMonoField.cpp

@@ -24,10 +24,7 @@ namespace BansheeEngine
 		if(fieldClass == nullptr)
 			return nullptr;	
 		
-		String ns = mono_class_get_namespace(fieldClass);
-		String typeName = mono_class_get_name(fieldClass);
-
-		mFieldType = MonoManager::instance().findClass(ns, typeName);
+		mFieldType = MonoManager::instance().findClass(fieldClass);
 
 		return mFieldType;
 	}

+ 13 - 0
BansheeMono/Source/BsMonoManager.cpp

@@ -140,6 +140,19 @@ namespace BansheeEngine
 		return nullptr;
 	}
 
+	MonoClass* MonoManager::findClass(::MonoClass* rawMonoClass)
+	{
+		MonoClass* monoClass = nullptr;
+		for(auto& assembly : mAssemblies)
+		{
+			monoClass = assembly.second->getClass(rawMonoClass);
+			if(monoClass != nullptr)
+				return monoClass;
+		}
+
+		return nullptr;
+	}
+
 	String MonoManager::getFullTypeName(MonoObject* obj)
 	{
 		if(obj == nullptr)

+ 6 - 2
CamelotUtility/Source/CmBinarySerializer.cpp

@@ -570,7 +570,9 @@ namespace CamelotFramework
 
 								if(findObj == mObjectMap.end())
 								{
-									LOGWRN("When deserializing, object ID: " + toString(objectId) + " was found but no such object was contained in the file.");
+									if(objectId != 0)
+										LOGWRN("When deserializing, object ID: " + toString(objectId) + " was found but no such object was contained in the file.");
+
 									curField->setArrayValue(object.get(), i, nullptr);
 								}
 								else
@@ -671,7 +673,9 @@ namespace CamelotFramework
 
 							if(findObj == mObjectMap.end())
 							{
-								LOGWRN("When deserializing, object ID: " + toString(objectId) + " was found but no such object was contained in the file.");
+								if(objectId != 0)
+									LOGWRN("When deserializing, object ID: " + toString(objectId) + " was found but no such object was contained in the file.");
+
 								curField->setValue(object.get(), nullptr);
 							}
 							else

+ 25 - 3
GameObjectSerialization.txt

@@ -6,6 +6,14 @@ TODO
 
 IMMEDIATE:
 
+Create a wrapper around MonoArray to make it easier to use?
+ - With support for multirank
+Create a similar wrapper for ICollection
+ - Expose mono_class_bind_generic_parameters
+ - Expose mono_reflection_bind_generic_method_parameters
+   - MonoReflectionMethod->method to get MonoMethod
+
+
 setValue in ScriptSerializableObject & ScriptSerializableArray should check if value is null and if the field type isn't a value type (can't be null)
 
 LOW PRIORITY
@@ -37,8 +45,6 @@ In ScriptSerializableObject & ScriptSerializableArray add:
  Testing:
 Test arrays
 Test arrays of arrays
-Test references to Components
-Test references to Resources
 Test how structs work
 Test how things work when class gets removed
 
@@ -62,4 +68,20 @@ TODO - When I destroy a Component, how will I refresh the inspector to let it kn
   -------------------------------------------------------
 
   Other:
-  Instantiating mono generic classes: http://stackoverflow.com/questions/17628411/get-generic-type-using-mono-embedded
+  Instantiating mono generic classes: http://stackoverflow.com/questions/17628411/get-generic-type-using-mono-embedded
+
+ * 		/*MonoClass* kvpClass = mscorlib->getClass("System.Collections.Generic", "KeyValuePair`2");
+*
+*		MonoType* kvpTypes[2];
+*		kvpTypes[0] = mono_class_get_type(mono_get_string_class());
+*		kvpTypes[1] = mono_class_get_type(mono_get_int32_class());
+*		::MonoClass* kvpStrIntClass = mono_class_bind_generic_parameters(kvpClass->_getInternalClass(), 2, kvpTypes, false);
+*
+*		MonoObject* kvpStrIntInstance = mono_object_new(MonoManager::instance().getDomain(), kvpStrIntClass);
+*		::MonoProperty* prop = mono_class_get_property_from_name(kvpStrIntClass, "Key");
+*
+*		int propDummy = 7;
+*		void* propParams[1] = { &propDummy };
+*		mono_property_set_value(prop, kvpStrIntInstance, propParams, nullptr);
+*
+*		int propVal = *(int*)mono_property_get_value(prop, kvpStrIntInstance, nullptr, nullptr);*/

+ 1 - 0
MBansheeEngine/DbgComponent.cs

@@ -11,5 +11,6 @@ namespace BansheeEngine
         public DbgSerzObj complex = new DbgSerzObj();
         public DbgComponent2 otherComponent;
         public SceneObject otherSO;
+        public int[] zeArray;
     }
 }

+ 4 - 2
MBansheeEngine/Program.cs

@@ -22,13 +22,15 @@ namespace BansheeEngine
             dbgComponent.complex.anotherValue = "AnotherValue";
             dbgComponent.otherComponent = dbgComponent2;
             dbgComponent.otherSO = otherSO;
+            dbgComponent.zeArray = new int[5];
+            dbgComponent.zeArray[4] = 129;
 
             dbgTestComponentClone(so);
 
             for (int i = 0; i < so.GetNumChildren(); i++)
             {
                 SceneObject childSO = so.GetChild(i);
-                reportDbgValue(childSO.GetComponent<DbgComponent>().otherComponent.a2, typeof(DbgComponent));
+                reportDbgValue(childSO.GetComponent<DbgComponent>().otherComponent.a2, childSO.GetComponent<DbgComponent>().zeArray[4], typeof(DbgComponent));
             }
 
             //Color newColor = Color.red;
@@ -42,6 +44,6 @@ namespace BansheeEngine
         private static extern void dbgTestComponentClone(SceneObject so);
 
         [MethodImpl(MethodImplOptions.InternalCall)]
-        private static extern void reportDbgValue(int dbgVal, Type type);
+        private static extern void reportDbgValue(int dbgVal, int dbgVal2, Type type);
     }
 }

+ 14 - 0
MonoIntegrationGuide.txt

@@ -84,6 +84,20 @@ guint8 *stackBottom = (guint8*)tib->StackLimit;
   - Build "mono" project. 
   - You should end up with mono-2.0.dll, mono-2.0.lib, MonoPosixHelper.dll and mono.exe and we are done compiling
 
+-------------------------------Special Banshee-specific changes--------------------------------------------
+
+Move & modify:
+  MonoClass* mono_class_bind_generic_parameters (MonoClass *klass, int type_argc, MonoType **types, gboolean is_dynamic) MONO_INTERNAL; 
+from object-internals.h
+to:
+  MONO_API MonoClass* mono_class_bind_generic_parameters (MonoClass *klass, int type_argc, MonoType **types, mono_bool is_dynamic);
+in object.h
+
+In reflection.c change:
+  MonoClass* mono_class_bind_generic_parameters (MonoClass *klass, int type_argc, MonoType **types, gboolean is_dynamic)
+to:
+  MonoClass* mono_class_bind_generic_parameters (MonoClass *klass, int type_argc, MonoType **types, mono_bool is_dynamic)
+
 
 --------------------------------Integrating Mono into Banshee----------------------------------------------
  - Add mono-2.0.dll to (BansheeRootDir)/bin/(Platform)/(Configuration)

+ 8 - 3
SBansheeEngine/Include/BsScriptSerializableArray.h

@@ -20,11 +20,13 @@ namespace BansheeEngine
 		static ScriptSerializableArrayPtr create(MonoObject* managedInstance, const ScriptSerializableTypeInfoArrayPtr& typeInfo);
 
 	protected:
+		MonoObject* mManagedInstance;
+
 		ScriptSerializableTypeInfoArrayPtr mArrayTypeInfo;
 		CM::Vector<ScriptSerializableFieldDataPtr>::type mArrayEntries;
-		MonoObject* mManagedInstance;
-		::MonoClass* mClass;
-		CM::UINT32 mNumElements;
+		
+		CM::Vector<CM::UINT32>::type mNumElements;
+		CM::UINT32 mElemSize;
 
 		/**
 		 * @brief	Populates internal field data based on currently active managed instance.
@@ -42,6 +44,9 @@ namespace BansheeEngine
 		void setValue(CM::UINT32 arrayIdx, void* val);
 		void* getValue(CM::UINT32 arrayIdx);
 
+		CM::UINT32 toSequentialIdx(const CM::Vector<CM::UINT32>::type& idx) const;
+		CM::UINT32 getLength(CM::UINT32 dimension) const;
+
 		/************************************************************************/
 		/* 								RTTI		                     		*/
 		/************************************************************************/

+ 28 - 6
SBansheeEngine/Include/BsScriptSerializableArrayRTTI.h

@@ -23,14 +23,34 @@ namespace BansheeEngine
 			obj->mArrayTypeInfo = val;
 		}
 
-		CM::UINT32& getNumElements(ScriptSerializableArray* obj)
+		CM::UINT32& getElementSize(ScriptSerializableArray* obj)
 		{
-			return (CM::UINT32)obj->mNumElements;
+			return (CM::UINT32)obj->mElemSize;
 		}
 
-		void setNumElements(ScriptSerializableArray* obj, CM::UINT32& numElements)
+		void setElementSize(ScriptSerializableArray* obj, CM::UINT32& numElements)
 		{
-			obj->mNumElements = numElements;
+			obj->mElemSize = numElements;
+		}
+
+		CM::UINT32& getNumElements(ScriptSerializableArray* obj, CM::UINT32 arrayIdx)
+		{
+			return (CM::UINT32)obj->mNumElements[arrayIdx];
+		}
+
+		void setNumElements(ScriptSerializableArray* obj, CM::UINT32 arrayIdx, CM::UINT32& numElements)
+		{
+			obj->mNumElements[arrayIdx] = numElements;
+		}
+
+		CM::UINT32 getNumElementsNumEntries(ScriptSerializableArray* obj)
+		{
+			return (CM::UINT32)obj->mNumElements.size();
+		}
+
+		void setNumElementsNumEntries(ScriptSerializableArray* obj, CM::UINT32 numEntries)
+		{
+			obj->mNumElements.resize(numEntries);
 		}
 
 		ScriptSerializableFieldDataPtr getArrayEntry(ScriptSerializableArray* obj, CM::UINT32 arrayIdx)
@@ -57,8 +77,10 @@ namespace BansheeEngine
 		ScriptSerializableArrayRTTI()
 		{
 			addReflectablePtrField("mArrayTypeInfo", 0, &ScriptSerializableArrayRTTI::getTypeInfo, &ScriptSerializableArrayRTTI::setTypeInfo);
-			addPlainField("mNumElements", 1, &ScriptSerializableArrayRTTI::getNumElements, &ScriptSerializableArrayRTTI::setNumElements);
-			addReflectablePtrArrayField("mArrayEntries", 2, &ScriptSerializableArrayRTTI::getArrayEntry, &ScriptSerializableArrayRTTI::getNumArrayEntries, 
+			addPlainField("mElementSize", 1, &ScriptSerializableArrayRTTI::getElementSize, &ScriptSerializableArrayRTTI::setElementSize);
+			addPlainArrayField("mNumElements", 2, &ScriptSerializableArrayRTTI::getNumElements, &ScriptSerializableArrayRTTI::getNumElementsNumEntries, 
+				&ScriptSerializableArrayRTTI::setNumElements, &ScriptSerializableArrayRTTI::setNumElementsNumEntries);
+			addReflectablePtrArrayField("mArrayEntries", 3, &ScriptSerializableArrayRTTI::getArrayEntry, &ScriptSerializableArrayRTTI::getNumArrayEntries, 
 				&ScriptSerializableArrayRTTI::setArrayEntry, &ScriptSerializableArrayRTTI::setNumArrayEntries);
 		}
 

+ 2 - 5
SBansheeEngine/Source/BsRuntimeScriptObjects.cpp

@@ -286,6 +286,7 @@ namespace BansheeEngine
 			}
 
 			break;
+		case MONO_TYPE_SZARRAY:
 		case MONO_TYPE_ARRAY:
 			{
 				std::shared_ptr<ScriptSerializableTypeInfoArray> typeInfo = cm_shared_ptr<ScriptSerializableTypeInfoArray>();
@@ -294,13 +295,9 @@ namespace BansheeEngine
 				if(elementClass != nullptr)
 				{
 					monoType = mono_class_get_type(elementClass);
-					monoPrimitiveType = mono_type_get_type(monoType);
-
 					::MonoClass* elementClass = mono_type_get_class(monoType);
-					String elementNs = mono_class_get_namespace(elementClass);
-					String elementTypeName = mono_class_get_name(elementClass);
 
-					MonoClass* monoElementClass = MonoManager::instance().findClass(elementNs, elementTypeName);
+					MonoClass* monoElementClass = MonoManager::instance().findClass(elementClass);
 					if(monoElementClass != nullptr)
 						typeInfo->mElementType = determineType(monoElementClass);
 				}

+ 1 - 1
SBansheeEngine/Source/BsScriptEnginePlugin.cpp

@@ -23,7 +23,7 @@ namespace BansheeEngine
 		cloneSO->setParent(SO);
 	}
 
-	void reportDbgValue(int dbgValue, MonoReflectionType* type)
+	void reportDbgValue(int dbgValue, int dbgValue2, MonoReflectionType* type)
 	{
 		::MonoClass* monoClass = mono_type_get_class(mono_reflection_type_get_type(type));
 

+ 72 - 27
SBansheeEngine/Source/BsScriptSerializableArray.cpp

@@ -4,12 +4,21 @@
 #include "BsRuntimeScriptObjects.h"
 #include "BsScriptSerializableField.h"
 
+// DEBUG ONLY
+#include "BsMonoAssembly.h"
+#include "BsMonoClass.h"
+#include "BsMonoMethod.h"
+#include <mono/metadata/object.h>
+#include <mono/metadata/debug-helpers.h>
+#include <mono/metadata/metadata.h>
+#include "CmDebug.h"
+
 using namespace CamelotFramework;
 
 namespace BansheeEngine
 {
 	ScriptSerializableArray::ScriptSerializableArray(const ConstructPrivately& dummy)
-		:mManagedInstance(nullptr), mNumElements(0), mClass(nullptr)
+		:mManagedInstance(nullptr), mElemSize(0)
 	{
 
 	}
@@ -17,14 +26,12 @@ namespace BansheeEngine
 	ScriptSerializableArray::ScriptSerializableArray(const ConstructPrivately& dummy, const ScriptSerializableTypeInfoArrayPtr& typeInfo, MonoObject* managedInstance)
 		:mArrayTypeInfo(typeInfo), mManagedInstance(managedInstance), mNumElements(0)
 	{
-		mClass = mono_object_get_class(mManagedInstance);
-		mNumElements = (UINT32)mono_array_length((MonoArray*)mManagedInstance);
+		::MonoClass* monoClass = mono_object_get_class(mManagedInstance);
+		mElemSize = mono_array_element_size(monoClass);
 
-		mArrayEntries.resize(mNumElements);
-		for(UINT32 i = 0; i < mNumElements; i++)
-		{
-			mArrayEntries[i] = getFieldData(i);
-		}
+		mNumElements.resize(typeInfo->mRank);
+		for(UINT32 i = 0; i < typeInfo->mRank; i++)
+			mNumElements[i] = getLength(i);
 	}
 
 	ScriptSerializableArrayPtr ScriptSerializableArray::create(MonoObject* managedInstance, const ScriptSerializableTypeInfoArrayPtr& typeInfo)
@@ -42,8 +49,14 @@ namespace BansheeEngine
 
 	void ScriptSerializableArray::serializeManagedInstance()
 	{
-		mArrayEntries.resize(mNumElements);
-		for(UINT32 i = 0; i < mNumElements; i++)
+		UINT32 totalNumElements = 0;
+		for(auto& numElems : mNumElements)
+		{
+			totalNumElements += numElems;
+		}
+
+		mArrayEntries.resize(totalNumElements);
+		for(UINT32 i = 0; i < totalNumElements; i++)
 		{
 			mArrayEntries[i] = getFieldData(i);
 		}
@@ -54,17 +67,27 @@ namespace BansheeEngine
 		if(!mArrayTypeInfo->isTypeLoaded())
 			return;
 
-		uint32_t lengths[1] = { mNumElements };
+		MonoClass* arrayClass = RuntimeScriptObjects::instance().getSystemArrayClass();
 
-		MonoArray* newArray = mono_array_new_full(MonoManager::instance().getDomain(), 
-			mArrayTypeInfo->getMonoClass(), (uintptr_t*)lengths, nullptr); 
+		MonoMethod* createInstance = arrayClass->getMethodExact("CreateInstance", "Type,int[]");
+		MonoArray* lengthArray = mono_array_new(MonoManager::instance().getDomain(), mono_get_int32_class(), (UINT32)mNumElements.size());
 
-		mManagedInstance = (MonoObject*)newArray;
+		for(UINT32 i = 0; i < (UINT32)mNumElements.size(); i++)
+		{
+			void* elemAddr = mono_array_addr_with_size(lengthArray, sizeof(int), i);
+			memcpy(elemAddr, &mNumElements[i], sizeof(int));
+		}
+
+		void* params[2] = { 
+			mono_type_get_object(MonoManager::instance().getDomain(), mono_class_get_type(mArrayTypeInfo->getMonoClass())), lengthArray };
+
+		mManagedInstance = createInstance->invoke(nullptr, params);
 
 		CM::UINT32 idx = 0;
 		for(auto& arrayEntry : mArrayEntries)
 		{
 			setFieldData(idx, arrayEntry);
+			idx++;
 		}
 	}
 
@@ -80,31 +103,53 @@ namespace BansheeEngine
 	
 	void ScriptSerializableArray::setValue(CM::UINT32 arrayIdx, void* val)
 	{
-		if(arrayIdx >= mNumElements)
-			CM_EXCEPT(InvalidParametersException, "Array index out of range: " + toString(arrayIdx) + ". Valid range is [0, " + toString(mNumElements) + ")");
-
 		MonoArray* array = (MonoArray*)mManagedInstance;
-		UINT32 elemSize = mono_array_element_size(mClass);
-	
+
 		UINT32 numElems = (UINT32)mono_array_length(array);
 		assert(arrayIdx < numElems);
 	
-		void* elemAddr = mono_array_addr_with_size(array, elemSize, arrayIdx);
-		memcpy(elemAddr, val, elemSize);
+		void* elemAddr = mono_array_addr_with_size(array, mElemSize, arrayIdx);
+		memcpy(elemAddr, val, mElemSize);
 	}
 	
 	void* ScriptSerializableArray::getValue(CM::UINT32 arrayIdx)
 	{
-		if(arrayIdx >= mNumElements)
-			CM_EXCEPT(InvalidParametersException, "Array index out of range: " + toString(arrayIdx) + ". Valid range is [0, " + toString(mNumElements) + ")");
-
 		MonoArray* array = (MonoArray*)mManagedInstance;
-		UINT32 elemSize = mono_array_element_size(mClass);
-	
+
 		UINT32 numElems = (UINT32)mono_array_length(array);
 		assert(arrayIdx < numElems);
 	
-		return mono_array_addr_with_size(array, elemSize, arrayIdx);
+		return mono_array_addr_with_size(array, mElemSize, arrayIdx);
+	}
+
+	UINT32 ScriptSerializableArray::toSequentialIdx(const CM::Vector<CM::UINT32>::type& idx) const
+	{
+		// TODO - Never actually tested if it works, IDX calculation might work differently
+		if(idx.size() != (UINT32)mNumElements.size())
+			CM_EXCEPT(InvalidParametersException, "Provided index doesn't have the correct number of dimensions");
+
+		UINT32 curIdx = 0;
+		UINT32 prevDimensionSize = 1;
+		
+		for(UINT32 i = 0; i < (UINT32)mNumElements.size(); i++)
+		{
+			curIdx += idx[i] * prevDimensionSize;
+
+			prevDimensionSize *= mNumElements[i];
+		}
+
+		return curIdx;
+	}
+
+	UINT32 ScriptSerializableArray::getLength(UINT32 dimension) const
+	{
+		MonoClass* systemArray = RuntimeScriptObjects::instance().getSystemArrayClass();
+		MonoMethod& getLength = systemArray->getMethod("GetLength", 1);
+
+		void* params[1] = { &dimension };
+		MonoObject* returnObj = getLength.invoke(mManagedInstance, params);
+
+		return *(UINT32*)mono_object_unbox(returnObj);
 	}
 
 	RTTITypeBase* ScriptSerializableArray::getRTTIStatic()