diff options
author | chai <chaifix@163.com> | 2019-08-14 22:50:43 +0800 |
---|---|---|
committer | chai <chaifix@163.com> | 2019-08-14 22:50:43 +0800 |
commit | 15740faf9fe9fe4be08965098bbf2947e096aeeb (patch) | |
tree | a730ec236656cc8cab5b13f088adfaed6bb218fb /Runtime/Mono |
Diffstat (limited to 'Runtime/Mono')
40 files changed, 13710 insertions, 0 deletions
diff --git a/Runtime/Mono/Coroutine.cpp b/Runtime/Mono/Coroutine.cpp new file mode 100644 index 0000000..887eafc --- /dev/null +++ b/Runtime/Mono/Coroutine.cpp @@ -0,0 +1,379 @@ +#include "UnityPrefix.h" + +#include "Coroutine.h" + +#if ENABLE_SCRIPTING + +#include "Runtime/Misc/AsyncOperation.h" +#include "Runtime/Mono/MonoBehaviour.h" +#include "Runtime/Mono/MonoScript.h" +#include "Runtime/Mono/MonoIncludes.h" +#include "Runtime/Mono/MonoManager.h" +#include "Runtime/GameCode/CallDelayed.h" +#include "Runtime/Export/WWW.h" +#include "Runtime/Scripting/ScriptingUtility.h" +#include "Runtime/Scripting/Backend/ScriptingBackendApi.h" +#include "Runtime/Scripting/ScriptingObjectWithIntPtrField.h" + +#if UNITY_IPHONE_API +# include "Runtime/Input/OnScreenKeyboard.h" +#endif + +#include "Runtime/Scripting/Backend/ScriptingBackendApi.h" + +// copied from MonoBehaviour.cpp +// if they should be synced - blame the author not me ;-) +#define DEBUG_COROUTINE 0 +#define DEBUG_COROUTINE_LEAK 0 + +Coroutine::Coroutine() + : m_DoneRunning(false) +{ + #if DEBUG_COROUTINE + static int coroutineCounter = 0; + coroutineCounter++; + printf_console("Allocating coroutine %d --- %d : 0x%x\n", this, coroutineCounter, &m_RefCount); + #endif +} + +Coroutine::~Coroutine() +{ + Assert(m_CoroutineEnumeratorGCHandle == 0); + + #if DEBUG_COROUTINE + printf_console("Deconstructor coroutine %d\n", this); + #endif +} + +void Coroutine::SetMoveNextMethod(ScriptingMethodPtr method) +{ + m_MoveNext = method; +} + +void Coroutine::SetCurrentMethod(ScriptingMethodPtr method) +{ + m_Current = method; +} + +void Coroutine::ContinueCoroutine (Object* o, void* userData) +{ + Coroutine* coroutine = (Coroutine*)userData; + Assert(coroutine->m_RefCount > 0 && coroutine->m_RefCount < 1000000); + + if ((Object*)coroutine->m_Behaviour != o) + { + ErrorString("Coroutine continue failure"); + #if DEBUG_COROUTINE + if ((Object*)coroutine->m_Behaviour != o) + { + printf_console ("continue Coroutine fuckup %d refcount: %d behaviour: %d \n", coroutine, coroutine->m_RefCount, coroutine->m_Behaviour); + printf_console ("continue Coroutine fuckup name: %s methodname\n", ((MonoBehaviour*)(o))->GetScript()->GetName()); + if (coroutine->m_CoroutineMethod) + printf_console ("continue Coroutine methodname: %s\n", scripting_method_get_name(coroutine->m_CoroutineMethod)); + } + #endif + return; + } + + coroutine->Run (); +} + +void Coroutine::CleanupCoroutine (void* userData) +{ + Coroutine* coroutine = (Coroutine*)userData; + AssertIf(coroutine->m_RefCount <= 0); + AssertIf(coroutine->m_RefCount > 1000000); + coroutine->m_RefCount--; + + #if DEBUG_COROUTINE + printf_console("decrease refcount %d - active: %d \n", coroutine, coroutine->m_RefCount); + #endif + + if (coroutine->m_RefCount > 0) + return; + + coroutine->m_DoneRunning = true; + + #if DEBUG_COROUTINE + printf_console ("CleanupCoroutine %d\n", coroutine); + if (coroutine->m_Behaviour && GetDelayedCallManager().HasDelayedCall(coroutine->m_Behaviour, Coroutine::ContinueCoroutine, CompareCoroutineMethodName, coroutine)) + { + printf_console ("FUCKUP is still in delayed call manager%d!\n", coroutine->m_Behaviour); + } + #endif + + if (coroutine->m_ContinueWhenFinished) + { + CleanupCoroutine (coroutine->m_ContinueWhenFinished); + coroutine->m_ContinueWhenFinished = NULL; + } + + if (coroutine->m_WaitingFor) + coroutine->m_WaitingFor->m_ContinueWhenFinished = NULL; + + coroutine->RemoveFromList(); + + if (coroutine->m_AsyncOperation) + { + coroutine->m_AsyncOperation->SetCoroutineCallback(NULL, NULL, NULL, NULL); + coroutine->m_AsyncOperation->Release(); + coroutine->m_AsyncOperation = NULL; + } + + Assert(coroutine->m_CoroutineEnumeratorGCHandle != 0); + + scripting_gchandle_free (coroutine->m_CoroutineEnumeratorGCHandle); + + coroutine->m_CoroutineEnumeratorGCHandle = 0; + + if (!coroutine->m_IsReferencedByMono) + { + delete coroutine; + + #if DEBUG_COROUTINE_LEAK + gCoroutineCounter--; + #endif + } +} + +void Coroutine::CleanupCoroutineGC (void* userData) +{ + Coroutine* coroutine = (Coroutine*)userData; + if (!coroutine->m_IsReferencedByMono) + return; + + if (coroutine->m_RefCount != 0) + { + coroutine->m_IsReferencedByMono = false; + return; + } + + ErrorIf(coroutine->IsInList()); + + delete coroutine; + + #if DEBUG_COROUTINE + printf_console ("GC free coroutine: %d\n", coroutine); + #endif + + #if DEBUG_COROUTINE_LEAK + gCoroutineCounter--; + #endif +} + + +bool Coroutine::CompareCoroutineMethodName (void* callBackUserData, void* cancelUserdata) +{ + Coroutine* coroutine = (Coroutine*)callBackUserData; + if (!coroutine->m_CoroutineMethod) + return false; + + return strcmp(scripting_method_get_name(coroutine->m_CoroutineMethod), (const char*)cancelUserdata) == 0; +} + +bool Coroutine::InvokeMoveNext(ScriptingExceptionPtr* exception) +{ + ScriptingInvocation invocation(m_MoveNext); + invocation.object = m_CoroutineEnumerator; + invocation.classContextForProfiler = m_Behaviour->GetClass(); + invocation.objectInstanceIDContextForException = m_Behaviour->GetInstanceID(); + bool result = invocation.Invoke<bool>(exception); + return result && *exception==NULL; +} + + +void Coroutine::Run () +{ + Assert(m_RefCount != 0); + Assert(m_Behaviour != NULL); + + #if DEBUG_COROUTINE + AssertIf(GetDelayedCallManager().HasDelayedCall(m_Behaviour, Coroutine::ContinueCoroutine, CompareCoroutineMethodName, this)); + if (m_Behaviour == NULL) + { + printf_console ("Coroutine fuckup %d refcount: %d behaviour%d\n", this, m_RefCount, m_Behaviour); + } + #endif + + // - Call MoveNext (This processes the function until the next yield!) + // - Call Current (This returns condition when to continue the coroutine next.) + // -> Queue it based on the continue condition + + // Temporarily increase refcount so the object will not get destroyed during the m_MoveNext call + m_RefCount++; + ScriptingExceptionPtr exception = NULL; + bool keepLooping = InvokeMoveNext(&exception); + AssertIf(m_RefCount <= 0 || m_RefCount > 10000000); + + bool coroutineWasDestroyedDuringMoveNext = m_RefCount == 1; + // Decrease temporary refcount so the object will not get destroyed during the m_MoveNext call + CleanupCoroutine(this); + + // The coroutine has been destroyed in the mean time, probably due to a call to StopAllCoroutines, stop executing further + if (coroutineWasDestroyedDuringMoveNext) + { + Assert(m_ContinueWhenFinished == NULL); + return; + } + + if (exception != NULL) + return; + + // Are we done with this coroutine? + if (!keepLooping) + { + // If there is a coroutine waiting for this one to finish Run it! + if (m_ContinueWhenFinished) + { + AssertIf (this != m_ContinueWhenFinished->m_WaitingFor); + Coroutine* continueWhenFinished = m_ContinueWhenFinished; + m_ContinueWhenFinished->m_WaitingFor = NULL; + m_ContinueWhenFinished = NULL; + // The coroutine might have been stopped inside of the last coroutine invokation + if (continueWhenFinished->m_Behaviour) + continueWhenFinished->Run (); + CleanupCoroutine (continueWhenFinished); + } + + return; + } + + if (m_Behaviour == NULL) + return; + + ProcessCoroutineCurrent(); +} + +void Coroutine::ProcessCoroutineCurrent() +{ + ScriptingExceptionPtr exception = NULL; +#if !UNITY_FLASH + ScriptingInvocation invocation(m_Current); + invocation.object = m_CoroutineEnumerator; + invocation.objectInstanceIDContextForException = m_Behaviour->GetInstanceID(); + invocation.classContextForProfiler = m_Behaviour->GetClass(); + ScriptingObjectPtr monoWait = invocation.Invoke(&exception); +#else + ScriptingObjectPtr monoWait = Ext_Flash_getProperty(m_CoroutineEnumerator,"IEnumerator_Current"); +#endif + + AssertIf(m_RefCount <= 0 || m_RefCount > 10000000); + + if (exception != NULL) + return; + + if (monoWait == SCRIPTING_NULL) + { + m_RefCount++; + CallDelayed (ContinueCoroutine, m_Behaviour, 0.0F, this, 0.0F, CleanupCoroutine, DelayedCallManager::kRunDynamicFrameRate | DelayedCallManager::kWaitForNextFrame); + return; + } + + HandleIEnumerableCurrentReturnValue(monoWait); +} + +void Coroutine::HandleIEnumerableCurrentReturnValue(ScriptingObjectPtr monoWait) +{ + AsyncOperation* async = NULL; + ScriptingClassPtr waitClass = scripting_object_get_class (monoWait, GetScriptingTypeRegistry()); + const CommonScriptingClasses& classes = GetMonoManager ().GetCommonClasses (); + + // Continue the coroutine in 'wait' seconds + if (scripting_class_is_subclass_of (waitClass, classes.waitForSeconds)) + { + m_RefCount++; + + float wait; + MarshallManagedStructIntoNative(monoWait,&wait); + CallDelayed (ContinueCoroutine, m_Behaviour, wait, this, 0.0F, CleanupCoroutine, DelayedCallManager::kRunDynamicFrameRate | DelayedCallManager::kWaitForNextFrame); + return; + } + + // Continue the coroutine on the next fixed update + if (scripting_class_is_subclass_of (waitClass, classes.waitForFixedUpdate)) + { + m_RefCount++; + CallDelayed (ContinueCoroutine, m_Behaviour, 0.0F, this, 0.0F, CleanupCoroutine, DelayedCallManager::kRunFixedFrameRate); + return; + } + + // Continue the coroutine at the end of frame + if (scripting_class_is_subclass_of (waitClass, classes.waitForEndOfFrame)) + { + m_RefCount++; + CallDelayed (ContinueCoroutine, m_Behaviour, 0.0F, this, 0.0F, CleanupCoroutine, DelayedCallManager::kEndOfFrame); + return; + } + + // Continue after another coroutine is finished + if (scripting_class_is_subclass_of (waitClass, classes.coroutine)) + { + Coroutine* waitForCoroutine; + MarshallManagedStructIntoNative(monoWait,&waitForCoroutine); + if (waitForCoroutine->m_DoneRunning) + { + // continue executing. + ContinueCoroutine(m_Behaviour, this); + return; + } + + if (waitForCoroutine->m_ContinueWhenFinished != NULL) + { + LogStringObject ("Another coroutine is already waiting for this coroutine!\nCurrently only one coroutine can wait for another coroutine!", m_Behaviour); + return; + } + + m_RefCount++; + waitForCoroutine->m_ContinueWhenFinished = this; + m_WaitingFor = waitForCoroutine; + return; + } + +#if ENABLE_WWW + // Continue after fetching an www object is done + + if (classes.www && scripting_class_is_subclass_of (waitClass, classes.www )) + { + WWW* wwwptr; + MarshallManagedStructIntoNative(monoWait,&wwwptr); + if(wwwptr != NULL) + { + m_RefCount++; + wwwptr->CallWhenDone (ContinueCoroutine, m_Behaviour, this, CleanupCoroutine); + } + return; + } +#endif + // Continue after fetching an www object is done + if ((scripting_class_is_subclass_of (waitClass, classes.asyncOperation)) && (async = ScriptingObjectWithIntPtrField<AsyncOperation> (monoWait).GetPtr()) != NULL) + { + m_RefCount++; + + if (async->IsDone()) + { + CallDelayed (ContinueCoroutine, m_Behaviour, 0.0F, this, 0.0F, CleanupCoroutine, DelayedCallManager::kRunDynamicFrameRate | DelayedCallManager::kRunDynamicFrameRate | DelayedCallManager::kWaitForNextFrame); + return; + } + + // Use AysncOperation ContinueCoroutine - default path + if (async->HasCoroutineCallback ()) + { + ////@TODO: Throw exception? + ErrorString("This asynchronous operation is already being yielded from another coroutine. An asynchronous operation can only be yielded once."); + CallDelayed (ContinueCoroutine, m_Behaviour, 0.0F, this, 0.0F, CleanupCoroutine, DelayedCallManager::kRunDynamicFrameRate | DelayedCallManager::kRunDynamicFrameRate | DelayedCallManager::kWaitForNextFrame); + return; + } + + async->SetCoroutineCallback(ContinueCoroutine, m_Behaviour, this, CleanupCoroutine); + m_AsyncOperation = async; + m_AsyncOperation->Retain(); + + return; + } + + // Continue the coroutine on the next dynamic frame update + m_RefCount++; + CallDelayed (ContinueCoroutine, m_Behaviour, 0.0F, this, 0.0F, CleanupCoroutine, DelayedCallManager::kRunDynamicFrameRate | DelayedCallManager::kRunDynamicFrameRate | DelayedCallManager::kWaitForNextFrame); + //Ext_MarshalMap_Release_ScriptingObject(monoWait);//RH TODO : RELEASE THE MONOWAIT OBJECTS SOMEWHERE +} +#endif diff --git a/Runtime/Mono/Coroutine.h b/Runtime/Mono/Coroutine.h new file mode 100644 index 0000000..f82a13d --- /dev/null +++ b/Runtime/Mono/Coroutine.h @@ -0,0 +1,45 @@ +#ifndef _COROUTINE_H_ +#define _COROUTINE_H_ +#if ENABLE_SCRIPTING +#include "Runtime/Scripting/Backend/ScriptingTypes.h" +#include "Runtime/Scripting/ScriptingUtility.h" +#include "Runtime/Utilities/LinkedList.h" + +class AsyncOperation; +class MonoBehaviour; +class Object; + + +struct Coroutine : public ListElement +{ + ScriptingObjectPtr m_CoroutineEnumerator; + int m_CoroutineEnumeratorGCHandle; + ScriptingMethodPtr m_CoroutineMethod; + ScriptingMethodPtr m_MoveNext; + ScriptingMethodPtr m_Current; + MonoBehaviour* m_Behaviour; + int m_RefCount; + int m_IsReferencedByMono; + bool m_DoneRunning; + Coroutine* m_ContinueWhenFinished; + Coroutine* m_WaitingFor; + AsyncOperation* m_AsyncOperation; + Coroutine (); + ~Coroutine(); + + void Run (); + + void SetMoveNextMethod(ScriptingMethodPtr method); + void SetCurrentMethod(ScriptingMethodPtr method); + static void ContinueCoroutine (Object* o, void* userData); + static void CleanupCoroutine (void* userData); + static void CleanupCoroutineGC (void* userData); + static bool CompareCoroutineMethodName (void* callBackUserData, void* cancelUserdata); + +private: + bool InvokeMoveNext(ScriptingExceptionPtr* exception); + void ProcessCoroutineCurrent(); + void HandleIEnumerableCurrentReturnValue(ScriptingObjectPtr); +}; +#endif //ENABLE_SCRIPTING +#endif diff --git a/Runtime/Mono/MonoAttributeHelpers.cpp b/Runtime/Mono/MonoAttributeHelpers.cpp new file mode 100644 index 0000000..cde1054 --- /dev/null +++ b/Runtime/Mono/MonoAttributeHelpers.cpp @@ -0,0 +1,191 @@ +#include "UnityPrefix.h" +#include "Configuration/UnityConfigure.h" // include before anything, to get Prof_ENABLED if that is defined +#if UNITY_EDITOR && ENABLE_MONO +#include "MonoManager.h" +#include "MonoAttributeHelpers.h" +#include <stdlib.h> + +using namespace std; + + +static string RemoveStringFromString (string str, string remove) +{ + while (true) + { + int pos = str.find (remove); + if (pos == string::npos) + break; + str.erase (pos, remove.size()); + } + return str; +} + +static std::string SignatureToString (MonoMethodSignature* signature) +{ + MonoType* methodType; + void* iter = NULL; + std::string parameters; + while ((methodType = mono_signature_get_params (signature, &iter))) + { + if (!parameters.empty()) + parameters += ", "; + parameters += mono_type_get_name(methodType); + } + + string returnType = mono_type_get_name( mono_signature_get_return_type (signature) ); + string result = Format ("%s Func(%s)", returnType.c_str(), parameters.c_str()); + return RemoveStringFromString (result, "System."); +} + + + +void GetMethodsWithAttribute (ScriptingClass* attributeClass, MonoMethod* comparedParams, std::vector<MonoMethod*>& resultList) +{ + MonoMethodSignature* pCompareSig = NULL; + if (comparedParams) + pCompareSig = mono_method_signature( comparedParams ); + + for (int i = MonoManager::kEngineAssembly; i < GetMonoManager().GetAssemblyCount(); i++) + { + MonoAssembly* assembly = GetMonoManager().GetAssembly(i); + if (!assembly) + continue; + + MonoImage* pMonoImage = mono_assembly_get_image( assembly ); + if (!pMonoImage) + continue; + + // stolen from #include... + const int MONO_TABLE_TYPEDEF = 2; // mono/metadata/blob.h + const int MONO_TOKEN_TYPE_DEF = 0x02000000; // mono/metadata/tokentype.h + + int numFields = mono_image_get_table_rows( pMonoImage, MONO_TABLE_TYPEDEF ); + for (int typeIdx = 1; typeIdx < numFields; ++typeIdx) + { + guint32 token = MONO_TOKEN_TYPE_DEF | (typeIdx + 1); + MonoClass* klass = mono_class_get (pMonoImage, token); + gpointer iter = NULL; + MonoMethod* method; + + if (klass == NULL) { + // We need to call these two methods to clear the thread local + // loader error status in mono. If not we'll randomly process the error + // the next time it's checked. + void* last_error = mono_loader_get_last_error (); + mono_loader_error_prepare_exception (last_error); + continue; + } + + while ((method = mono_class_get_methods(klass, &iter))) + { + MonoMethodSignature* sig = mono_method_signature (method); + + if (!sig) + continue; + + MonoCustomAttrInfo* attribInfo = mono_custom_attrs_from_method(method); + if (!attribInfo) + continue; + + if (!mono_custom_attrs_has_attr (attribInfo, attributeClass)) + { + mono_custom_attrs_free(attribInfo); + continue; + } + + // Get static methods only + bool isInstanceMethod = mono_signature_is_instance (sig); + if (isInstanceMethod) + { + mono_custom_attrs_free(attribInfo); + ErrorString (Format("UnityException: An [%s] attributed method is not static", mono_class_get_name(attributeClass))); + continue; + } + + if (pCompareSig) + { + if (!mono_metadata_signature_equal(sig, pCompareSig)) + { + mono_custom_attrs_free(attribInfo); + ErrorString (Format("UnityException: An [%s] attributed method has incorrect signature: %s. Correct signature: %s", mono_class_get_name(attributeClass), SignatureToString(sig).c_str(), SignatureToString(pCompareSig).c_str())); + continue; + } + } + else + { + if (mono_signature_get_param_count (sig) > 0) + { + mono_custom_attrs_free(attribInfo); + ErrorString (Format("UnityException: An [%s] attributed method parameter count should be 0 but signature is: %s", mono_class_get_name(attributeClass), SignatureToString(sig).c_str())); + continue; + } + } + + resultList.push_back(method); + mono_custom_attrs_free(attribInfo); + } + } + } +} + +bool AttributeSorter( MonoMethod* methodA, MonoMethod* methodB ) +{ +#if UNITY_EDITOR + MonoCustomAttrInfo* attribAInfo = mono_custom_attrs_from_method(methodA); + MonoCustomAttrInfo* attribBInfo = mono_custom_attrs_from_method(methodB); + MonoObject* objA = mono_custom_attrs_get_attr( attribAInfo, MONO_COMMON.callbackOrderAttribute ); + MonoObject* objB = mono_custom_attrs_get_attr( attribBInfo, MONO_COMMON.callbackOrderAttribute ); + MonoClass* klassA = mono_object_get_class(objA); + MonoClass* klassB = mono_object_get_class(objB); + MonoProperty* propertyA = mono_class_get_property_from_name(klassA, "callbackOrder"); + MonoProperty* propertyB = mono_class_get_property_from_name(klassB, "callbackOrder"); + MonoMethod* getterA = mono_property_get_get_method(propertyA); + MonoMethod* getterB = mono_property_get_get_method(propertyB); + MonoException* monoException = NULL; + MonoObject* resA = mono_runtime_invoke(getterA, objA, NULL, &monoException); + MonoObject* resB = mono_runtime_invoke(getterB, objB, NULL, &monoException); + int x = ExtractMonoObjectData<signed int>(resA); + int y = ExtractMonoObjectData<signed int>(resB); + + return x < y; +#else + return false; +#endif +} + +void CallMethodsWithAttribute (ScriptingClass* attributeClass, ScriptingArguments& arguments, MonoMethod* comparedParams) +{ + vector<MonoMethod*> resultList; + + GetMethodsWithAttribute(attributeClass, comparedParams, resultList); + sort (resultList.begin(), resultList.end(), AttributeSorter); + + for (vector<MonoMethod*>::iterator iter = resultList.begin(); iter != resultList.end(); ++iter) + { + ScriptingInvocation invocation(*iter); + invocation.Arguments() = arguments; + invocation.Invoke(); + } +} + +bool CallMethodsWithAttributeAndReturnTrueIfUsed (ScriptingClass* attributeClass, ScriptingArguments& arguments, MonoMethod* comparedParams) +{ + vector<MonoMethod*> resultList; + + GetMethodsWithAttribute(attributeClass, comparedParams, resultList); + sort (resultList.begin(), resultList.end(), AttributeSorter); + + for (vector<MonoMethod*>::iterator iter = resultList.begin(); iter != resultList.end(); ++iter) + { + ScriptingInvocation invocation(*iter); + invocation.Arguments() = arguments; + ScriptingObjectPtr returnValueObj = invocation.Invoke(); + if (ExtractMonoObjectData<bool>(returnValueObj)) + return true; + } + + return false; +} + + +#endif //UNITY_EDITOR && ENABLE_MONO diff --git a/Runtime/Mono/MonoAttributeHelpers.h b/Runtime/Mono/MonoAttributeHelpers.h new file mode 100644 index 0000000..cc2b8c8 --- /dev/null +++ b/Runtime/Mono/MonoAttributeHelpers.h @@ -0,0 +1,19 @@ +#ifndef MONOATTRIBUTEHELPERS_H +#define MONOATTRIBUTEHELPERS_H + +#include "MonoIncludes.h" +#include "Configuration/UnityConfigure.h" +#if UNITY_EDITOR && ENABLE_MONO +#include "Runtime/Scripting/ScriptingUtility.h" +#include "Runtime/Scripting/Backend/ScriptingTypes.h" +#include <vector> + +struct ScriptingArguments; + +//void GetMethodsWithAttribute (ScriptingClass* attributeClass, void** requiredParameters, MonoMethod* comparedParams, std::vector<MonoMethod*>& resultList); +void CallMethodsWithAttribute (ScriptingClass* attributeClass, ScriptingArguments& arguments, MonoMethod* comparedParams); +bool CallMethodsWithAttributeAndReturnTrueIfUsed (ScriptingClass* attributeClass, ScriptingArguments& arguments, MonoMethod* comparedParams); + +#endif //UNITY_EDITOR && ENABLE_MONO + +#endif //MONOATTRIBUTEHELPERS_H diff --git a/Runtime/Mono/MonoBehaviour.cpp b/Runtime/Mono/MonoBehaviour.cpp new file mode 100644 index 0000000..3c79901 --- /dev/null +++ b/Runtime/Mono/MonoBehaviour.cpp @@ -0,0 +1,1894 @@ +#include "UnityPrefix.h" +#if ENABLE_SCRIPTING +#include "MonoBehaviour.h" +#include "MonoBehaviourAnimationBinding.h" +#include "Runtime/Mono/Coroutine.h" +#include "Runtime/Serialize/TransferFunctions/SerializeTransfer.h" +#include "Runtime/Serialize/TransferFunctions/TransferNameConversions.h" +#include "Runtime/Serialize/FileCache.h" +#include "Runtime/Scripting/ScriptingObjectWithIntPtrField.h" +#include "MonoScript.h" +#include "MonoScriptCache.h" +#include "MonoTypeSignatures.h" +#include "MonoManager.h" +#include "Runtime/BaseClasses/IsPlaying.h" +#include "Runtime/BaseClasses/MessageIdentifiers.h" +#include "Runtime/GameCode/CallDelayed.h" +#include "Runtime/BaseClasses/MessageHandler.h" +#include "tabledefs.h" +#include "Runtime/Utilities/Word.h" +#include "Runtime/IMGUI/GUIState.h" +#include "Runtime/Serialize/IterateTypeTree.h" +#include "Runtime/BaseClasses/SupportedMessageOptimization.h" +#include "Runtime/Misc/MessageParameters.h" +#include "Runtime/Camera/Camera.h" +#include "Runtime/Camera/Renderable.h" +#include "Runtime/Math/AnimationCurve.h" +#include "Runtime/Misc/AsyncOperation.h" +#include "Runtime/Camera/RenderManager.h" +#include "Runtime/Scripting/ScriptingUtility.h" +#include "Runtime/Threads/ThreadSpecificValue.h" +#include "Runtime/Audio/AudioCustomFilter.h" +#include "Runtime/Misc/BuildSettings.h" +#include "Runtime/Scripting/Backend/ScriptingArguments.h" +#include "Runtime/Scripting/Backend/ScriptingMethodRegistry.h" +#include "Runtime/Physics2D/CollisionListener2D.h" +#include "Runtime/Interfaces/IPhysics.h" + +#if ENABLE_WWW +#include "Runtime/Export/WWW.h" +#endif +#include "Runtime/Serialize/TransferUtility.h" +#include "Runtime/BaseClasses/RefCounted.h" +#include "Runtime/Misc/InputEvent.h" +#include "Runtime/IMGUI/GUIManager.h" +#include "Runtime/Profiler/ExternalGraphicsProfiler.h" +#if UNITY_EDITOR +#include "Runtime/Serialize/PersistentManager.h" +#include "Runtime/Graphics/Transform.h" +#include "Editor/Src/Gizmos/GizmoRenderer.h" +#include "Editor/Src/Gizmos/GizmoUtil.h" +#include "Editor/Src/Application.h" +#include "Editor/Src/AssetPipeline/LogicGraphCompilationPipeline.h" +#endif + +#include "Runtime/Scripting/Backend/ScriptingBackendApi.h" +#include "Runtime/Scripting/Scripting.h" + +#include "Runtime/Interfaces/IAudio.h" + +IMPLEMENT_CLASS_HAS_INIT (MonoBehaviour) + +using namespace std; +#define DEBUG_COROUTINE 0 +#define DEBUG_COROUTINE_LEAK 0 +#if DEBUG_COROUTINE_LEAK +int gCoroutineCounter = 0; +#endif + + +PROFILER_INFORMATION(gMonoImageFxProfile, "Camera.ImageEffect", kProfilerRender); + + +bool IsInstanceValid (ScriptingObjectPtr target); + +#if UNITY_EDITOR +BackupState::BackupState () +: yamlState(NULL) +, inYamlFormat (false) +, loadedFromDisk (false) +{} + +BackupState::~BackupState () +{ + delete yamlState; +} + +#endif + +MonoBehaviour::MonoBehaviour (MemLabelId label, ObjectCreationMode mode) +: Super(label, mode) +, m_UpdateNode(this) +, m_FixedUpdateNode(this) +, m_LateUpdateNode(this) +, m_GUINode(this) +, m_OnRenderObjectNode(this) +{ + m_Methods = NULL; + m_ScriptCache = NULL; + m_DidAwake = m_DidStart = m_IsDestroying = false; + m_UseGUILayout = true; + m_GUIState = NULL; + #if UNITY_EDITOR + m_EditorHideFlags = 0; + m_Backup = NULL; + #endif + #if ENABLE_AUDIO_FMOD + m_AudioCustomFilter = NULL; + #endif +} + +MonoBehaviour::~MonoBehaviour() +{ + Assert(m_ActiveCoroutines.empty()); + + LockObjectCreation(); + ReleaseMonoInstance (); + StopAllCoroutines (); + UnlockObjectCreation(); + +#if UNITY_EDITOR + delete m_Backup; +#endif +#if ENABLE_UNITYGUI + delete m_GUIState; +#endif + +#if ENABLE_AUDIO_FMOD + delete m_AudioCustomFilter; +#endif +} + +bool MonoBehaviour::IsScriptableObject() +{ + if (m_ScriptCache != NULL) + return m_ScriptCache->scriptType == kScriptTypeScriptableObjectDerived || m_ScriptCache->scriptType == kScriptTypeEditorScriptableObjectDerived; + else + return false; +} + +#if ENABLE_UNITYGUI +ObjectGUIState& MonoBehaviour::GetObjectGUIState() +{ + if (m_GUIState) + return *m_GUIState; + else + { + m_GUIState = new ObjectGUIState (); + return *m_GUIState; + } +} +#endif + +bool MonoBehaviour::IsPlayingOrAllowExecuteInEditMode() const +{ + #if UNITY_EDITOR + return IsWorldPlaying () || GetRunInEditMode(); + #else + return true; + #endif +} + +bool MonoBehaviour::WillUnloadScriptableObject () +{ + MonoScript* script = m_Script; + if (GetInstance() == SCRIPTING_NULL || script == NULL) + return true; + + ///@TODO: SHould this check for m_DidAwake? Can this happen in any way? + + ScriptingObjectPtr instance = GetInstance(); + if (IsScriptableObject()) + { + ScriptingMethodPtr method = m_Methods[MonoScriptCache::kRemoveFromManager]; + if (method != SCRIPTING_NULL) + CallMethodInactive(method); + + if (IsInstanceValid(instance)) + { + method = m_Methods[MonoScriptCache::kRemoveFromManagerInternal]; + if (method != SCRIPTING_NULL) + CallMethodInactive(method); + } + } + return IsInstanceValid(instance); +} + + +// IsInstanceValid is used to determine if the C# side destroyed the object during the script callback using DestroyImmediate. +// For example Awake and OnEnable are called from the same C++ code. If the object is destroyed by Awake, we must not call OnEnable and not touch MonoBehaviour member variables. +bool IsInstanceValid (ScriptingObjectPtr target) +{ + if (target == SCRIPTING_NULL) + return false; + ScriptingObjectOfType<Object> wrapper(target); + return wrapper.GetCachedPtr() != NULL; +} + +void MonoBehaviour::DeprecatedAddToManager () +{ + ScriptingObjectPtr instance = GetInstance(); + if (instance && IsPlayingOrAllowExecuteInEditMode ()) + { + MonoScript* script = m_Script; + int executionOrder = script ? script->GetExecutionOrder() : 0; + ///Try removing this + if ((IsInstanceValid (instance) && m_Methods[MonoScriptCache::kCoroutineStart]) || m_Methods[MonoScriptCache::kCoroutineMain]) + CallDelayed (DelayedStartCall, this, -10, NULL, 0.0F, NULL, DelayedCallManager::kRunDynamicFrameRate | DelayedCallManager::kRunFixedFrameRate | DelayedCallManager::kRunStartupFrame); + if (IsInstanceValid (instance) && m_Methods[MonoScriptCache::kUpdate]) + GetBehaviourManager().AddBehaviour (m_UpdateNode, executionOrder); + if (IsInstanceValid (instance) && m_Methods[MonoScriptCache::kFixedUpdate]) + GetFixedBehaviourManager().AddBehaviour (m_FixedUpdateNode, executionOrder); + if (IsInstanceValid (instance) && m_Methods[MonoScriptCache::kLateUpdate]) + GetLateBehaviourManager().AddBehaviour (m_LateUpdateNode, executionOrder); + + if (IsInstanceValid (instance) && m_Methods[MonoScriptCache::kRenderObject]) + GetRenderManager().AddOnRenderObject (m_OnRenderObjectNode); + + if (IsInstanceValid (instance) && m_Methods[MonoScriptCache::kAddToManager]) + { + // @TODO: This is only for backwards compatiblity + SetupAwake (); + + // If we got disabled or destroyed from a call to Awake, do not proceed calling OnEnable and so on. + // Just quit, we are removed from all managers by a call to RemoveFromManager already. + if( !IsInstanceValid (instance) || !GetEnabled() ) + return; + CallMethodIfAvailable (MonoScriptCache::kAddToManager); + } + +#if ENABLE_IMAGEEFFECTS + if (IsInstanceValid (instance) && m_Methods[MonoScriptCache::kRenderImageFilter]) + { + Camera *camera = QueryComponent (Camera); + if (camera) + { + bool afterOpaque = scripting_method_has_attribute(m_Methods[MonoScriptCache::kRenderImageFilter], MONO_COMMON.imageEffectOpaque); + bool transformsToLDR = scripting_method_has_attribute(m_Methods[MonoScriptCache::kRenderImageFilter], MONO_COMMON.imageEffectTransformsToLDR); + + ImageFilter filter (this, &RenderImageFilter, transformsToLDR, afterOpaque); + camera->AddImageFilter (filter); + } + } +#endif + +#if ENABLE_UNITYGUI + if (IsInstanceValid (instance) && m_Methods[MonoScriptCache::kGUI]) + GetGUIManager().AddGUIScript (m_GUINode); +#endif + + if ( IsInstanceValid (instance) ) + SetByPassOnDSP(false); + } +} + +void MonoBehaviour::WillDestroyComponent () +{ + Super::WillDestroyComponent(); + + if (m_IsDestroying) + { + ErrorString("DestroyImmediate should not be called on the same game object when destroying a MonoBehaviour"); + return; + } + + m_IsDestroying = true; + ScriptingMethodPtr method; + + ScriptingObjectPtr instance = GetInstance(); + if (instance && m_DidAwake) + { + // OnDisable must be called for scriptable objects since we never get a Deactivate call there. + if (IsScriptableObject()) + { + method = m_Methods[MonoScriptCache::kRemoveFromManager]; + if (method) + CallMethodInactive (method); + + if (IsInstanceValid(instance)) + { + method = m_Methods[MonoScriptCache::kRemoveFromManagerInternal]; + if (method) + CallMethodInactive (method); + } + } + + // OnDestroy + if (IsInstanceValid(instance)) + { + method = m_Methods[MonoScriptCache::kOnDestroy]; + if (method != SCRIPTING_NULL) + CallMethodInactive(method); + } + } +} + + +void MonoBehaviour::RemoveFromManager () +{ + m_UpdateNode.RemoveFromList(); + m_FixedUpdateNode.RemoveFromList(); + m_LateUpdateNode.RemoveFromList(); + m_GUINode.RemoveFromList(); + m_OnRenderObjectNode.RemoveFromList(); + +#if ENABLE_IMAGEEFFECTS + if (GetInstance() && m_Methods[MonoScriptCache::kRenderImageFilter]) + { + Camera *camera = QueryComponent (Camera); + if (camera) + { + ImageFilter filter (this, &RenderImageFilter, false, false); + camera->RemoveImageFilter (filter); + } + } +#endif + + if (IsPlayingOrAllowExecuteInEditMode() && GetCachedScriptingObject()) + { + ScriptingObjectPtr instance = GetInstance(); + + if (IsInstanceValid(instance) && m_Methods[MonoScriptCache::kRemoveFromManager] && m_DidAwake) + CallMethodInactive(m_Methods[MonoScriptCache::kRemoveFromManager]); + if (IsInstanceValid(instance) && m_Methods[MonoScriptCache::kRemoveFromManagerInternal] && m_DidAwake) + CallMethodInactive(m_Methods[MonoScriptCache::kRemoveFromManagerInternal]); + //@TODO: Try removing this + if (IsInstanceValid(instance) && (m_Methods[MonoScriptCache::kCoroutineStart] || m_Methods[MonoScriptCache::kCoroutineMain])) + GetDelayedCallManager().CancelCallDelayed (this, DelayedStartCall, NULL, NULL); + + if ( IsInstanceValid(instance) ) + SetByPassOnDSP(true); + } +} + +bool MonoBehaviour::CallMethodInactive (ScriptingMethodPtr method) +{ + AssertIf (GetInstance() == SCRIPTING_NULL); + AssertIf (method == SCRIPTING_NULL); + + ScriptingObjectPtr instance = GetInstance(); + ScriptingInvocation invocation(method); + invocation.object = instance; + invocation.logException = true; + invocation.objectInstanceIDContextForException = Scripting::GetInstanceIDFromScriptingWrapper(instance); + invocation.AdjustArgumentsToMatchMethod(); + invocation.InvokeChecked(); + return (NULL == invocation.exception); +} + +bool MonoBehaviour::CallMethodInactive (const char* methodName) +{ + ScriptingMethodPtr method = GetScriptingMethodRegistry().GetMethod(GetClass(),methodName,ScriptingMethodRegistry::kWithoutArguments); + + if (!method) + return false; + + CallMethodInactive (method); + + return true; +} + +bool MonoBehaviour::DoGUI (MonoBehaviour::GUILayoutType layoutType, int skin) +{ + if (GetInstance () == SCRIPTING_NULL) + return false; + ScriptingMethodPtr method = GetMethod (MonoScriptCache::kGUI); + if (method == SCRIPTING_NULL) + return false; + Start (); + + PPtr<MonoBehaviour> behaviourPPtr = this; + ScriptingObjectPtr monoObject = GetInstance (); + int instanceID = GetInstanceID(); + GUIState &guiState = GetGUIState (); + guiState.m_CanvasGUIState.m_GUIClipState.BeginOnGUI (*guiState.m_CurrentEvent); + + guiState.BeginOnGUI(GetObjectGUIState()); + + int allowGUILayoutParam = layoutType; + + ScriptingInvocation invocation; + invocation.AddInt(skin); + invocation.AddInt(instanceID); + invocation.AddInt(allowGUILayoutParam); + invocation.method = MONO_COMMON.beginGUI; + invocation.Invoke(); + +#if UNITY_EDITOR + ScriptingInvocation invocation2; + invocation2.method = MONO_COMMON.clearUndoSnapshotTarget; + invocation2.Invoke(); +#endif + + ScriptingExceptionPtr exception = NULL; + ScriptingInvocationNoArgs userOnGUIInvocation; + userOnGUIInvocation.method = method; + userOnGUIInvocation.object = monoObject; + userOnGUIInvocation.logException = false; + userOnGUIInvocation.Invoke(&exception); + + // Display exception (except for ExitGUIException which exists only to successfully exit out of a GUI loop) + if (exception) + { +#if ENABLE_MONO + void* excparams[] = {exception}; + MonoObject* res = CallStaticMonoMethod("GUIUtility", "EndGUIFromException", excparams); + guiState.m_CanvasGUIState.m_GUIClipState.EndThroughException(); + if (MonoObjectToBool(res)) + { + guiState.EndOnGUI (); + return guiState.m_CurrentEvent->type == InputEvent::kUsed; + } + else + { + guiState.EndOnGUI (); + ::Scripting::LogException(exception, behaviourPPtr.GetInstanceID()); + return false; + } +#endif + } + else + { + // Mono is not happy about receiving bools from internal calls, + // especially since bools are not exactly specified on how many bytes they contain on the C++ side + + ScriptingInvocation endGUIInvocation; + endGUIInvocation.method = MONO_COMMON.endGUI; + endGUIInvocation.AddInt((int)layoutType); + endGUIInvocation.Invoke(); + + guiState.EndOnGUI (); + guiState.m_CanvasGUIState.m_GUIClipState.EndOnGUI (*guiState.m_CurrentEvent); + } + return guiState.m_CurrentEvent->type == InputEvent::kUsed; +} + +#if 0 +// IM GUI in retained GUI callbacks. not used right now. +bool MonoBehaviour::DoCustomGUI (Rectf &position, const ColorRGBAf &guiColor) +{ + MonoObject *instance = GetInstance (); + MonoMethod* method = GetMethod(MonoScriptCache::kCustomGUI); + if (instance == NULL || method == NULL) + return false; + Start (); + + GUIState &guiState = GetGUIState (); + guiState.SetObjectGUIState (GetObjectGUIState()); + guiState.BeginOnGUI(); + guiState.m_OnGUIState.m_Color = guiColor; + + MonoException* exception; + + void* params[] = { &position }; + bool retval = mono_runtime_invoke_profiled(method, instance, params, &exception); + + // TODO: Handle GUI exceptions + AssertIf (exception); + + guiState.EndOnGUI (); + return retval; +} +#endif + +void MonoBehaviour::SetUseGUILayout (bool use) +{ + m_UseGUILayout = use; +} + +bool MonoBehaviour::GetUseGUILayout () +{ + return m_UseGUILayout; +} + +#if ENABLE_AUDIO_FMOD + +FMOD::DSP* MonoBehaviour::GetOrCreateDSP() +{ + IAudio* audio = GetIAudio(); + if (!audio) + return NULL; + + if (!m_AudioCustomFilter) + { + if (m_Methods && m_Methods[MonoScriptCache::kAudioFilterRead] && IsActive()) + m_AudioCustomFilter = audio->CreateAudioCustomFilter(this); + else + return NULL; +} + + return audio->GetOrCreateDSPFromCustomFilter(m_AudioCustomFilter); +} + +FMOD::DSP* MonoBehaviour::GetDSP() const +{ + IAudio* audio = GetIAudio(); + if (!audio) + return NULL; + + if (m_AudioCustomFilter) + return audio->GetDSPFromAudioCustomFilter(m_AudioCustomFilter); + else + return NULL; +} +#endif + +bool MonoBehaviour::HaveAudioCallback() const +{ + return m_Methods ? m_Methods[MonoScriptCache::kAudioFilterRead] != SCRIPTING_NULL : false; +} + +inline void MonoBehaviour::CallMethodIfAvailable (int methodIndex) +{ + AssertIf (methodIndex < 0 || methodIndex >= MonoScriptCache::kMethodCount); + ScriptingMethodPtr method = m_Methods[methodIndex]; + if (method == SCRIPTING_NULL) + { + return; + } + + AssertIf (GetInstance() == SCRIPTING_NULL); + AssertIf (!m_DidAwake); + + if (!IsActive ()) + return; + + ScriptingInvocationNoArgs invocation(method); + invocation.objectInstanceIDContextForException = GetInstanceID(); + invocation.object = GetInstance(); + invocation.Invoke(); +} + +inline void MonoBehaviour::Start () +{ + DebugAssertIf (!m_DidAwake); + + + #if UNITY_EDITOR + AssertIf (!IsActive () && !GetRunInEditMode()); + #else + AssertIf (!IsActive ()); + #endif + + if (m_DidStart) + return; + + AssertIf (GetInstance() == SCRIPTING_NULL); + + m_DidStart = true; + + ScriptingMethodPtr method; + method = m_Methods[MonoScriptCache::kCoroutineMain]; + + if (method) + InvokeMethodOrCoroutineChecked (method, SCRIPTING_NULL); + + method = m_Methods[MonoScriptCache::kCoroutineStart]; + if (method) + InvokeMethodOrCoroutineChecked (method, SCRIPTING_NULL); +} + + +#if UNITY_EDITOR +void MonoBehaviour::RestartExecuteInEditModeScripts () +{ + std::vector<SInt32> behaviours; + Object::FindAllDerivedObjects (ClassID(MonoBehaviour), &behaviours); + + for (std::vector<SInt32>::iterator it = behaviours.begin(), itEnd = behaviours.end(); it != itEnd; ++it) + { + MonoBehaviour* beh = dynamic_pptr_cast<MonoBehaviour*> (Object::IDToPointer(*it)); + if (beh == NULL || !beh->GetRunInEditMode() || !beh->IsActive() || !beh->GetEnabled()) + continue; + + // disable and enable the script (careful to not call SetDirty) + beh->SetEnabledNoDirty (false); + beh->SetEnabledNoDirty (true); + // and Start it again + beh->m_DidStart = false; + beh->Start (); + } +} + +bool MonoBehaviour::GetRunInEditMode () const +{ + if (m_ScriptCache) + return m_ScriptCache->runInEditMode; + else + return false; +} + +#endif + + +void MonoBehaviour::CallUpdateMethod(int methodIndex) +{ + AssertIf (!IsPlayingOrAllowExecuteInEditMode ()); + AssertIf (!IsActive ()); + AssertIf (!GetEnabled ()); + ScriptingObjectPtr instance = GetInstance(); + + if (instance == SCRIPTING_NULL) + return; + // Ensure Start has been called + Start (); + + // We might be getting destroyed in Start + if (!IsInstanceValid(instance)) + return; + + // Call Update + CallMethodIfAvailable (methodIndex); +} + +void MonoBehaviour::Update () +{ + CallUpdateMethod (MonoScriptCache::kUpdate); +} + +void MonoBehaviour::LateUpdate () +{ + CallUpdateMethod (MonoScriptCache::kLateUpdate); +} + +void MonoBehaviour::FixedUpdate () +{ + CallUpdateMethod (MonoScriptCache::kFixedUpdate); +} + +ScriptingMethodPtr MonoBehaviour::FindMethod (const char* name) +{ + if (GetInstance() == SCRIPTING_NULL) + return SCRIPTING_NULL; + + const MethodCache* cache = GetMethodCache(); + + MethodCache::const_iterator i = cache->find (name); + if (i != cache->end ()) + return i->second; + else + return SCRIPTING_NULL; +} + + +#if ENABLE_MONO || UNITY_WINRT +void MonoBehaviour::InvokeOnRenderObject () +{ + DebugAssertIf (!IsPlayingOrAllowExecuteInEditMode()); + AssertIf (!GetEnabled ()); + AssertIf (!IsActive ()); + if (GetInstance()) + { + Start (); + CallMethodIfAvailable (MonoScriptCache::kRenderObject); + } +} +#endif //ENABLE_MONO || UNITY_WINRT + +#if ENABLE_IMAGEEFFECTS +void MonoBehaviour::RenderImageFilter (Unity::Component* component, RenderTexture *source, RenderTexture *destination) +{ + MonoBehaviour* self = static_cast<MonoBehaviour*>(component); + + AssertIf (!self->GetEnabled ()); + AssertIf (!self->IsActive ()); + if (self->IsPlayingOrAllowExecuteInEditMode () && self->GetInstance()) + { + self->Start (); + + ScriptingMethodPtr method = self->m_Methods[MonoScriptCache::kRenderImageFilter]; + // Early out message not supported + if (method == SCRIPTING_NULL) + return; + + PROFILER_AUTO_GFX(gMonoImageFxProfile, self) + + ScriptingObjectPtr instance = self->GetInstance(); + ScriptingInvocation invocation(method); + invocation.object = instance; + invocation.AddObject(Scripting::ScriptingWrapperFor (source)); + invocation.AddObject(Scripting::ScriptingWrapperFor (destination)); + invocation.objectInstanceIDContextForException = self->GetInstanceID(); + invocation.Invoke(); + } +} +#endif // ENABLE_IMAGEEFFECTS + + + +#if ENABLE_MONO +static MonoObject* ConvertBuiltinMonoValue( MonoObject* value, int wantedType ) +{ + #define SUPPORT_CONVERSION(srctype,dsttype,srccode,dstclass) \ + case srccode: \ + res = mono_object_new( domain, classes.dstclass ); \ + ExtractMonoObjectData<dsttype>(res) = static_cast<dsttype>( ExtractMonoObjectData<srctype>(value) ); \ + return res + + MonoClass* valueClass = mono_object_get_class (value); + int type = mono_type_get_type( mono_class_get_type (valueClass) ); + // types match exactly - return input object + if( type == wantedType ) + return value; + + MonoDomain* domain = mono_domain_get(); + const CommonScriptingClasses& classes = GetMonoManager().GetCommonClasses(); + MonoObject* res; + + if( wantedType == MONO_TYPE_I4 ) + { + switch( type ) { + SUPPORT_CONVERSION(float, int,MONO_TYPE_R4,int_32); + SUPPORT_CONVERSION(double,int,MONO_TYPE_R8,int_32); + } + } + else if( wantedType == MONO_TYPE_R4 ) + { + switch( type ) { + SUPPORT_CONVERSION(int, float,MONO_TYPE_I4,floatSingle); + SUPPORT_CONVERSION(double,float,MONO_TYPE_R8,floatSingle); + } + } + else if( wantedType == MONO_TYPE_R8 ) + { + switch( type ) { + SUPPORT_CONVERSION(int, double,MONO_TYPE_I4,floatDouble); + SUPPORT_CONVERSION(float,double,MONO_TYPE_R4,floatDouble); + } + } + + #undef SUPPORT_CONVERSION + + // can't convert + return NULL; +} +#endif //ENABLE_MONO + + +static bool CompareCoroutine (void* callBackUserData, void* cancelUserdata) +{ + return callBackUserData == cancelUserdata; +} + +Coroutine* MonoBehaviour::CreateCoroutine(ScriptingObjectPtr userCoroutine, ScriptingMethodPtr method) +{ + ScriptingMethodPtr moveNext = scripting_object_get_virtual_method(userCoroutine, MONO_COMMON.IEnumerator_MoveNext, GetScriptingMethodRegistry()); + +#if !UNITY_FLASH + ScriptingMethodPtr current = scripting_object_get_virtual_method(userCoroutine, MONO_COMMON.IEnumerator_Current, GetScriptingMethodRegistry()); +#else + //todo: make flash use generic path. set a bogus value for flash here right now so it passes current != NULL check, flash path will never use this value for now. + ScriptingMethodPtr current = (ScriptingMethodPtr)1; +#endif + + if (current == SCRIPTING_NULL || moveNext == SCRIPTING_NULL) + { + std::string message = (method != SCRIPTING_NULL) ? Format ("Coroutine '%s' couldn't be started!", scripting_method_get_name(method)) : "Coroutine couldn't be started!"; + LogStringObject (message, this); + return NULL; + } + + Coroutine* coroutine = new Coroutine (); + + coroutine->m_CoroutineEnumeratorGCHandle = scripting_gchandle_new (userCoroutine); + coroutine->m_CoroutineEnumerator = userCoroutine; + coroutine->m_CoroutineMethod = method; + coroutine->SetMoveNextMethod(moveNext); + coroutine->SetCurrentMethod(current); + coroutine->m_Behaviour = this; + coroutine->m_ContinueWhenFinished = NULL; + coroutine->m_WaitingFor = NULL; + coroutine->m_AsyncOperation = NULL; + coroutine->m_RefCount = 1; + coroutine->m_IsReferencedByMono = 0; + #if DEBUG_COROUTINE + printf_console ("Allocate coroutine %d\n", coroutine); + AssertIf(GetDelayedCallManager().HasDelayedCall(coroutine->m_Behaviour, Coroutine::ContinueCoroutine, CompareCoroutine, coroutine)); + #endif + + #if DEBUG_COROUTINE_LEAK + printf_console ("Active coroutines %d\n", gCoroutineCounter); + gCoroutineCounter++; + #endif + + m_ActiveCoroutines.push_back (*coroutine); + AssertIf(&m_ActiveCoroutines.back() != coroutine); + m_ActiveCoroutines.back ().Run (); + + AssertIf(coroutine->m_RefCount == 0); + if (coroutine->m_RefCount <= 1) + { + Coroutine::CleanupCoroutine(coroutine); + return NULL; + } + + Coroutine::CleanupCoroutine(coroutine); + return coroutine; +} + + +static ScriptingObjectPtr CreateManagedWrapperForCoroutine(Coroutine* coroutine) +{ + if (coroutine == NULL) return SCRIPTING_NULL; + Assert(!coroutine->m_IsReferencedByMono); + coroutine->m_IsReferencedByMono = true; + ScriptingObjectWithIntPtrField<Coroutine> wrapper = scripting_object_new(GetMonoManager ().GetCommonClasses ().coroutine); + wrapper.SetPtr(coroutine, Coroutine::CleanupCoroutineGC); + return wrapper.object; +} + +ScriptingObjectPtr MonoBehaviour::StartCoroutineManaged (const char* name, ScriptingObjectPtr value) +{ + Coroutine* coroutine = StartCoroutine (name, value); + return CreateManagedWrapperForCoroutine(coroutine); +} + +ScriptingObjectPtr MonoBehaviour::StartCoroutineManaged2 (ScriptingObjectPtr enumerator) +{ + if (!IsActive ()) + { + ErrorStringObject (Format ("Coroutine couldn't be started because the the game object '%s' is inactive!", GetName()), this); + return SCRIPTING_NULL; + } + + Coroutine* coroutine = CreateCoroutine(enumerator, SCRIPTING_NULL); + return CreateManagedWrapperForCoroutine(coroutine); +} + +static bool DoesMethodHaveIEnumeratorReturnType(ScriptingMethodPtr method) +{ + ScriptingClassPtr iEnumerator = GetMonoManager ().GetCommonClasses ().iEnumerator; + ScriptingClassPtr returnType = scripting_method_get_returntype(method, GetScriptingTypeRegistry()); + return returnType == iEnumerator; +} + +Coroutine* MonoBehaviour::HandleCoroutineReturnValue (ScriptingMethodPtr method, ScriptingObjectPtr returnValue) +{ + if (!DoesMethodHaveIEnumeratorReturnType(method)) + return NULL; + + return CreateCoroutine(returnValue, method); +} + +ScriptingObjectPtr MonoBehaviour::InvokeMethodOrCoroutineChecked(ScriptingMethodPtr scriptingMethod, ScriptingObjectPtr value, ScriptingExceptionPtr* exception) +{ + #define PARAMETER_ERROR(x) \ + { \ + string error = Format ("Failed to call function %s of class %s\n", scripting_method_get_name (scriptingMethod), GetScript ()->GetScriptClassName ().c_str ()); \ + error += x; \ + ErrorStringObject (error, this); \ + return SCRIPTING_NULL; \ + } +#if ENABLE_MONO + + + MonoObject* instance = GetInstance(); + int argCount = scripting_method_get_argument_count (scriptingMethod, GetScriptingTypeRegistry()); + + // Fast path - method takes no arguments + if (argCount == 0) + return mono_runtime_invoke_profiled_fast(scriptingMethod, instance, exception, NULL); + + // One argument, one value + if (argCount != 1 || value == SCRIPTING_NULL) + { + if (value == NULL) + { + PARAMETER_ERROR (Format ("Calling function %s with no parameters but the function requires %d.", scripting_method_get_name (scriptingMethod), argCount)); + } + else + { + PARAMETER_ERROR (Format ("Calling function %s with 1 parameter but the function requires %d.", scripting_method_get_name (scriptingMethod), argCount)); + } + + return NULL; + } + + MonoClass* inParamClass = mono_object_get_class (value); + + void* iterator = NULL; + + MonoMethodSignature* sig = mono_method_signature (scriptingMethod->monoMethod); + MonoType* methodType = mono_signature_get_params (sig, &iterator); + MonoClass* methodClass = mono_class_from_mono_type (methodType); + int methodTypeType = mono_type_get_type (methodType); + + void* argumentList[1] = { NULL }; + + // Pass builtin type + if (IsMonoBuiltinType (methodTypeType)) + { + MonoObject* converted = ConvertBuiltinMonoValue(value, methodTypeType); + if( converted ) + { + argumentList[0] = ExtractMonoObjectDataPtr<void> (converted); + } + } + // Pass by value + else if (methodTypeType == MONO_TYPE_VALUETYPE) + { + if (inParamClass == methodClass) + { + argumentList[0] = ExtractMonoObjectDataPtr<void> (value); + } + } + // Pass by class + else if (methodTypeType == MONO_TYPE_CLASS) + { + if (mono_class_is_subclass_of (inParamClass, methodClass, false)) + { + argumentList[0] = value; + } + } + // Passing string + else if (methodTypeType == MONO_TYPE_STRING && methodTypeType == mono_type_get_type (mono_class_get_type (inParamClass))) + { + argumentList[0] = value; + } + else if (methodTypeType == MONO_TYPE_OBJECT) + { + argumentList[0] = value; + } + + if (argumentList[0]) + return mono_runtime_invoke_profiled (scriptingMethod->monoMethod, instance, argumentList, exception); + + void* invokeargs[3] = { instance, mono_string_new_wrapper (scripting_method_get_name (scriptingMethod)), value }; + return mono_runtime_invoke_profiled (GetMonoManager().GetCommonClasses().invokeMember->monoMethod, NULL, invokeargs, exception); + +#else + // ToDo: make full params type check, like we do it with mono + int argCount = scripting_method_get_argument_count(scriptingMethod, GetScriptingTypeRegistry()); + ScriptingInvocation invocation(scriptingMethod); + invocation.object = GetInstance(); + + if (argCount == 0) + return invocation.Invoke(exception); + + if (argCount != 1 || value == SCRIPTING_NULL) + { + if (value == SCRIPTING_NULL) + { + PARAMETER_ERROR (Format ("Calling function %s with no parameters but the function requires %d.", scripting_method_get_name (scriptingMethod), argCount)); + } + else + { + PARAMETER_ERROR (Format ("Calling function %s with 1 parameter but the function requires %d.", scripting_method_get_name (scriptingMethod), argCount)); + } + return SCRIPTING_NULL; + } + +#if UNITY_WP8 + ScriptingInvocation invokeMember(MONO_COMMON.invokeMember); + invokeMember.AddObject(GetInstance()); + invokeMember.AddString(scripting_method_get_name(scriptingMethod)); + invokeMember.AddObject(value); + return invokeMember.Invoke(exception); +#else + invocation.AddObject(value); + return invocation.Invoke(exception, true); +#endif + +#endif + #undef PARAMETER_ERROR +} + +Coroutine* MonoBehaviour::InvokeMethodOrCoroutineChecked (ScriptingMethodPtr method, ScriptingObjectPtr value) +{ + + AssertIf (!IsPlayingOrAllowExecuteInEditMode ()); + + ScriptingObjectPtr instance = GetInstance(); + AssertIf (instance == SCRIPTING_NULL); + + ScriptingExceptionPtr exception = NULL; + ScriptingObjectPtr returnValue = InvokeMethodOrCoroutineChecked(method,value,&exception); + + if (returnValue != SCRIPTING_NULL && exception == NULL) + return HandleCoroutineReturnValue (method, returnValue); + + if (exception != NULL) + Scripting::LogException(exception, Scripting::GetInstanceIDFromScriptingWrapper(instance)); + + return NULL; +} + +Coroutine* MonoBehaviour::StartCoroutine (const char* name, ScriptingObjectPtr value) +{ + AssertIf (!IsPlayingOrAllowExecuteInEditMode ()); + AssertIf (GetInstance() == SCRIPTING_NULL); + + if (!IsActive ()) + { + ErrorStringObject (Format ("Coroutine '%s' couldn't be started because the the game object '%s' is inactive!", name, GetName()), this); + return NULL; + } + + ScriptingMethodPtr method = FindMethod (name); + if (method == NULL) + { + ErrorStringObject (Format ("Coroutine '%s' couldn't be started!", name), this); + return NULL; + } + + return InvokeMethodOrCoroutineChecked (method, value); +} + +static void DoStopCoroutine (Coroutine *coroutine) +{ + coroutine->RemoveFromList (); + + // We clear the behaviour reference prior to cleaning up the coroutine because otherwise CleanupCoroutine might remove + // it from underneath m_ActiveCoroutines + coroutine->m_Behaviour = NULL; + + if (coroutine->m_WaitingFor) + { + AssertIf (coroutine->m_WaitingFor->m_ContinueWhenFinished != coroutine); + coroutine->m_WaitingFor->m_ContinueWhenFinished = NULL; + coroutine->m_WaitingFor = NULL; + Coroutine::CleanupCoroutine (coroutine); + } + else if (coroutine->m_AsyncOperation) + Coroutine::CleanupCoroutine (coroutine); +} + +void MonoBehaviour::StopCoroutine (const char* name) +{ + #if DEBUG_COROUTINE + printf_console("StopCoroutine %s. %d - %s", name, this, GetName()); + #endif + GetDelayedCallManager ().CancelCallDelayed (this, Coroutine::ContinueCoroutine, Coroutine::CompareCoroutineMethodName, (void*)name); + for (List<Coroutine>::iterator i = m_ActiveCoroutines.begin (); + i != m_ActiveCoroutines.end (); ++i) { + if (i->m_CoroutineMethod != NULL && !strcmp (name, scripting_method_get_name (i->m_CoroutineMethod))) { + DoStopCoroutine (&(*i)); + break; + } + } +} + +void MonoBehaviour::StopAllCoroutines () +{ + if(m_ActiveCoroutines.empty ()) + { + return; + } + + #if DEBUG_COROUTINE + printf_console("StopAllCoroutines! %d - %s", this, GetName()); + #endif + + DelayedCall* wwwCallback = NULL; + #if ENABLE_WWW + wwwCallback = WWWDelayCall::Callback; + #endif + + // Remove all coroutines in this behaviour from delayed call/ + // This will call CleanupCoroutine and decrease the retain count. + // In turn this might remove coroutines from the active coroutine list, if no one else is referencing them. + GetDelayedCallManager ().CancelCallDelayed2 (this, Coroutine::ContinueCoroutine, wwwCallback); + + /// 1. We need to go through the remaining coroutines (Those that are still referenced from mono or other coroutines are yielding to it from another game object) + + while (!m_ActiveCoroutines.empty()) + { + Coroutine *coroutine = &m_ActiveCoroutines.front(); + DoStopCoroutine (coroutine); + #if DEBUG_COROUTINE + printf_console ("Cleaning up Stop ALL %d\n", coroutine); + #endif + } + + // This assert is useful when you think we might leak coroutines. + Assert (m_ActiveCoroutines.empty ()); +} + +#if UNITY_EDITOR +void MonoBehaviour::CheckConsistency () +{ + Super::CheckConsistency(); + + if (!GetInstance()) + return; + + bool canDestroy = GetDisableImmediateDestruction (); + SetDisableImmediateDestruction (true); + + // Validate properties + + ScriptingMethodPtr method = m_Methods[MonoScriptCache::kValidateProperties]; + if (method && !(m_ScriptCache->scriptType == kScriptTypeMonoBehaviourDerived && GetGameObjectPtr() == NULL)) + { + CallMethodInactive (method); + } + + SetDisableImmediateDestruction (canDestroy); +} +#endif + +void MonoBehaviour::SmartReset () +{ + Super::SmartReset (); + // Only in edit mode for more speed + // and to be nice to scripters we call Reset only if we are active + if (GetInstance() && !IsWorldPlaying () ) + CallMethodInactive("Reset"); +} + +void MonoBehaviour::InitializeClass () +{ + GameObject::RegisterAllMessagesHandler (ClassID (MonoBehaviour), &MonoBehaviour::HandleNotifications, &MonoBehaviour::CanHandleNotifications); + + RegisterAllowNameConversion ("GUISkin", "customStyles", "m_CustomStyles"); + + InitializeMonoBehaviourAnimationBindingInterface (); +} + +void MonoBehaviour::CleanupClass () +{ + CleanupMonoBehaviourAnimationBindingInterface (); +} + +UInt32 MonoBehaviour::CalculateSupportedMessages () +{ + if (!GetInstance()) + return 0; + + int mask = 0; + if (m_Methods[MonoScriptCache::kMethodCount + kEnterContact.messageID]) + mask |= kHasCollisionEnterExit; + if (m_Methods[MonoScriptCache::kMethodCount + kExitContact.messageID]) + mask |= kHasCollisionEnterExit; + if (m_Methods[MonoScriptCache::kMethodCount + kStayContact.messageID]) + mask |= kHasCollisionStay; + if (m_Methods[MonoScriptCache::kMethodCount + kOnWillRenderObject.messageID]) + mask |= kHasOnWillRenderObject; + if (m_Methods[MonoScriptCache::kMethodCount + kAnimatorMove.messageID]) + mask |= kHasOnAnimatorMove; + if (m_Methods[MonoScriptCache::kMethodCount + kAnimatorIK.messageID]) + mask |= kHasOnAnimatorIK; + if (m_Methods[MonoScriptCache::kMethodCount + kCollisionEnter2D.messageID]) + mask |= kHasCollision2D; + if (m_Methods[MonoScriptCache::kMethodCount + kCollisionExit2D.messageID]) + mask |= kHasCollision2D; + if (m_Methods[MonoScriptCache::kMethodCount + kCollisionStay2D.messageID]) + mask |= kHasCollision2D; + return mask; +} + +bool MonoBehaviour::CanHandleNotifications (void* receiver, int messageIndex, MessageData& data) { + DebugAssertIf (dynamic_pptr_cast<MonoBehaviour*> (static_cast<MonoBehaviour*> (receiver)) == NULL); + MonoBehaviour* behaviour = static_cast<MonoBehaviour*> (receiver); + return behaviour->GetInstance() && behaviour->m_Methods[MonoScriptCache::kMethodCount + messageIndex]; +} + +static bool SetupArgumentsForMessageInvocation(ScriptingArguments& arguments, MessageData& data, ScriptingMethodPtr receivingMethod, MonoBehaviour& behaviour) +{ + switch (data.type) + { + case 0: + return true; + case ClassID(int): + arguments.AddInt((int)data.GetGenericDataRef()); + return true; + case ClassID(float): + arguments.AddFloat((float)data.GetGenericDataRef()); + return true; + case ClassID(bool): + arguments.AddBoolean((int)data.GetGenericDataRef() != 0); + return true; + case ClassID(Collision): + arguments.AddObject(GetIPhysics()->ConvertContactToMono (data.GetData<Collision*> ())); + return true; + #if ENABLE_2D_PHYSICS + case ClassID(Collision2D): + arguments.AddObject(ConvertCollision2DToScripting (data.GetData<Collision2D*> ())); + return true; + #endif + case ClassID(MonoObject): + { + ScriptingObjectPtr parameter = data.GetScriptingObjectData (); + arguments.AddObject(parameter); + + if (!parameter) + return true; + + ScriptingClassPtr klass_of_receivingmethodargument = scripting_method_get_nth_argumenttype(receivingMethod,0,GetScriptingTypeRegistry()); + if (klass_of_receivingmethodargument == NULL) + return true; + ScriptingClassPtr klass_of_payload = scripting_object_get_class(parameter,GetScriptingTypeRegistry()); + + if (!scripting_class_is_subclass_of(klass_of_payload, klass_of_receivingmethodargument)) + { + std::string message = Format("%s couldn't be called because the expected parameter %s doesn't match %s.", scripting_method_get_name(receivingMethod), scripting_class_get_name(klass_of_receivingmethodargument),scripting_class_get_name(klass_of_payload)); + ErrorStringObject(message, &behaviour); + return false; + } + + return true; + } + default: + // Otherwise its a Object derived class (The heavy typechecking is done in MonoScript) + arguments.AddObject(Scripting::ScriptingWrapperFor (data.GetData<Object*> ())); + return true; + } +} + +static bool ShouldMessageBeSentToDisabledGameObjects(int messageIndex) +{ + if( !IS_CONTENT_NEWER_OR_SAME(kUnityVersion4_2_a1) && (messageIndex == kAnimatorMove.messageID || messageIndex == kAnimatorIK.messageID)) + return true; + + MessageIdentifier msg = GameObject::GetMessageHandler ().MessageIDToMessageIdentifier (messageIndex); + + return !(msg.options & MessageIdentifier::kDontSendToDisabled); +} + +void MonoBehaviour::HandleNotifications (void* receiver, int messageIndex, MessageData& data) { + DebugAssertIf (dynamic_pptr_cast<MonoBehaviour*> (static_cast<MonoBehaviour*> (receiver)) == NULL); + MonoBehaviour* behaviour = static_cast<MonoBehaviour*> (receiver); + + #if UNITY_FLASH + if(behaviour->m_Methods == NULL) + return; + #endif + + if (!behaviour->IsPlayingOrAllowExecuteInEditMode ()) + return; + + if (!behaviour->GetInstance()) + return; + + ScriptingMethodPtr method = behaviour->m_Methods[MonoScriptCache::kMethodCount + messageIndex]; + + if (method == SCRIPTING_NULL) + return; + + if (!behaviour->GetEnabled () && !ShouldMessageBeSentToDisabledGameObjects(messageIndex)) + return; + + ScriptingInvocation invocation(method); + invocation.object = behaviour->GetInstance(); + invocation.objectInstanceIDContextForException = behaviour->GetInstanceID(); + + if (!SetupArgumentsForMessageInvocation(invocation.Arguments(),data,method,*behaviour)) + return; + + ScriptingExceptionPtr exception = NULL; + ScriptingObjectPtr returnValue = invocation.Invoke(&exception); + + if (exception == NULL && returnValue) + behaviour->HandleCoroutineReturnValue (method, returnValue); +} + +void MonoBehaviour::SetupAwake () +{ + if (IsPlayingOrAllowExecuteInEditMode () && !m_DidAwake && GetInstance() && IsActive ()) + { + m_DidAwake = true; + CallMethodIfAvailable (MonoScriptCache::kAwake); + } +} + +void MonoBehaviour::DeprecatedDelayedAwakeCall (Object* o, void* userData) +{ + static_cast<MonoBehaviour*> (o)->SetupAwake (); +} + +void MonoBehaviour::DelayedStartCall (Object* o, void* userData) +{ + static_cast<MonoBehaviour*> (o)->Start (); +} + +void MonoBehaviour::DeprecatedAwakeFromLoadCodePath (AwakeFromLoadMode awakeMode) +{ + // We must cache the reference to other objects here + // since Behaviour::AwakeFromLoad might destroy the object itself + ScriptingObjectPtr instance = GetInstance(); + + // Potential Call to AddToManager / RemoveFromManager with various C# callbacks + Super::AwakeFromLoad (awakeMode); + + if (IsPlayingOrAllowExecuteInEditMode () && IsInstanceValid(instance) && !m_DidAwake && IsActive ()) + { + if (awakeMode & (kInstantiateOrCreateFromCodeAwakeFromLoad | kActivateAwakeFromLoad)) + SetupAwake(); + else + // @TODO: Do we really need this? Build this into Proper AwakeFromLoadQueue or whatever + CallDelayed (DeprecatedDelayedAwakeCall, this, -1000, NULL, 0.0F, NULL, DelayedCallManager::kRunDynamicFrameRate | DelayedCallManager::kRunFixedFrameRate | DelayedCallManager::kRunStartupFrame); + } + else if (!m_DidAwake && IsInstanceValid(instance) && IsScriptableObject()) + { + m_DidAwake = true; + + ScriptingMethodPtr method = m_Methods[MonoScriptCache::kAddToManager]; + if (method) + CallMethodInactive (method); + + method = m_Methods[MonoScriptCache::kAwake]; + if (method) + CallMethodInactive (method); + } + + if ((awakeMode & kDidLoadFromDisk) == 0 && IsInstanceValid(instance)) + { + ScriptingMethodPtr method = m_Methods[MonoScriptCache::kValidateProperties]; + if (method && !(m_ScriptCache->scriptType == kScriptTypeMonoBehaviourDerived && GetGameObjectPtr() == NULL)) + CallMethodInactive (method); + } +} + + +#define USE_DEPRECATED_AWAKE !IS_CONTENT_NEWER_OR_SAME(kUnityVersion4_0_a1) +//#define USE_DEPRECATED_AWAKE 1 + + + +// * Awake +// * OnEnable +// * ----- For all scripts +// * Start for all scripts +void MonoBehaviour::AwakeFromLoad (AwakeFromLoadMode awakeMode) +{ +#if UNITY_EDITOR && UNITY_LOGIC_GRAPH + LoadLogicGraphInEditor (); +#endif + + if (GetGameObjectPtr()) + GetGameObjectPtr()->SetSupportedMessagesDirty (); + + // Deprecated 3.x codepath + if (USE_DEPRECATED_AWAKE) + { + DeprecatedAwakeFromLoadCodePath (awakeMode); + return; + } + + // We must cache the reference to other objects here + // since Behaviour::AwakeFromLoad might destroy the object itself + // and we need a way to check that this happened. + ScriptingObjectPtr instance = GetInstance(); + + // The managed MonoBehaviour could not be created. Give control to the Behaviour and move on. + if (instance == SCRIPTING_NULL) + { + Super::AwakeFromLoad (awakeMode); + return; + } + + // CallDelayed->Start must be called before Awake otherwise objects created inside of Awake will get their Start call + // before the Start of the Behaviour that created them. + bool willCallAddToManager = IsPlayingOrAllowExecuteInEditMode() && GetEnabled() && IsActive(); + if (willCallAddToManager) + { + Super::AwakeFromLoad (awakeMode); + return; + } + + #define RETURN_IF_INSTANCE_DESTROYED if (!IsInstanceValid(instance)) return; + + bool isMonoBehaviourRequiringAwake = IsPlayingOrAllowExecuteInEditMode () && !m_DidAwake && IsActive (); + bool isScriptableObjectRequiringAwakeAndEnable = !m_DidAwake && IsScriptableObject(); + + // Awake for monobehaviours and scriptable objects + if (isMonoBehaviourRequiringAwake || isScriptableObjectRequiringAwakeAndEnable) + { + CallAwake(); + RETURN_IF_INSTANCE_DESTROYED + } + + // OnEnable for scriptable object + if (isScriptableObjectRequiringAwakeAndEnable) + { + ScriptingMethodPtr enableMethod = m_Methods[MonoScriptCache::kAddToManager]; + if (enableMethod) + { + CallMethodInactive (enableMethod); + RETURN_IF_INSTANCE_DESTROYED + } + } + + // Potential Call to AddToManager / RemoveFromManager with various C# callbacks + Super::AwakeFromLoad (awakeMode); + + #undef RETURN_IF_INSTANCE_DESTROYED +} + +void MonoBehaviour::CallAwake () +{ + m_DidAwake = true; + ScriptingMethodPtr awakeMethod = m_Methods[MonoScriptCache::kAwake]; + if (awakeMethod) + if (!CallMethodInactive (awakeMethod)) + SetEnabled (false); +} + + + +void MonoBehaviour::AddExternalDependencyCallbacksToManagers () +{ +#if ENABLE_IMAGEEFFECTS + ///@TODO: It doesn't look like the order of image effects is determined by the component order. + // Instead by the rather random from the users perspective Awake order. + if (m_Methods[MonoScriptCache::kRenderImageFilter]) + { + Camera *camera = QueryComponent (Camera); + if (camera) + { + bool afterOpaque = scripting_method_has_attribute(m_Methods[MonoScriptCache::kRenderImageFilter], MONO_COMMON.imageEffectOpaque); + bool transformsToLDR = scripting_method_has_attribute(m_Methods[MonoScriptCache::kRenderImageFilter], MONO_COMMON.imageEffectTransformsToLDR); + + ImageFilter filter (this, &RenderImageFilter, transformsToLDR, afterOpaque); + camera->AddImageFilter (filter); + } + } +#endif + + SetByPassOnDSP(false); +} + +void MonoBehaviour::SetByPassOnDSP(bool state) +{ +#if ENABLE_AUDIO_FMOD + IAudio* audio = GetIAudio(); + if (!audio) return; + + FMOD::DSP* dsp = GetOrCreateDSP(); + if (dsp) + audio->SetBypassOnDSP(dsp,state); +#endif +} + +void MonoBehaviour::AddBehaviourCallbacksToManagers () +{ + MonoScript* script = m_Script; + int executionOrder = script ? script->GetExecutionOrder() : 0; + + if (m_Methods[MonoScriptCache::kUpdate]) + GetBehaviourManager().AddBehaviour (m_UpdateNode, executionOrder); + if (m_Methods[MonoScriptCache::kFixedUpdate]) + GetFixedBehaviourManager().AddBehaviour (m_FixedUpdateNode, executionOrder); + if (m_Methods[MonoScriptCache::kLateUpdate]) + GetLateBehaviourManager().AddBehaviour (m_LateUpdateNode, executionOrder); + + if (m_Methods[MonoScriptCache::kRenderObject]) + GetRenderManager().AddOnRenderObject (m_OnRenderObjectNode); + +#if ENABLE_UNITYGUI + if (m_Methods[MonoScriptCache::kGUI]) + GetGUIManager().AddGUIScript (m_GUINode); +#endif +} + + +/* TODO: Tests + * Destruction during OnEnable / Awake / Start (Maybe integration tests cover this already??) + * Ensure that image effects & image filters are setup if their camera is created in OnEnable... +*/ + +#define RETURN_IF_DESTROYED_OR_DISABLED if (!IsInstanceValid(instance) || !GetEnabled()) return; + +void MonoBehaviour::AddToManager () +{ + if (USE_DEPRECATED_AWAKE) + { + DeprecatedAddToManager (); + return; + } + + + ScriptingObjectPtr instance = GetInstance(); + if (instance == SCRIPTING_NULL || !IsPlayingOrAllowExecuteInEditMode ()) + return; + + if (m_Methods[MonoScriptCache::kCoroutineStart] || m_Methods[MonoScriptCache::kCoroutineMain]) + CallDelayed (DelayedStartCall, this, -10, NULL, 0.0F, NULL, DelayedCallManager::kRunDynamicFrameRate | DelayedCallManager::kRunFixedFrameRate | DelayedCallManager::kRunStartupFrame); + + + // Behaviour callbacks are registered before OnEnable. + // If an object is created in OnEnable, the order will be this script, then the created script. + AddBehaviourCallbacksToManagers (); + + // We must call Awake here. + // CallDelayed->Start must be called before Awake otherwise objects created inside of Awake will get their Start call + // before the Start of the Behaviour that created them. + if (!m_DidAwake) + { + CallAwake (); + RETURN_IF_DESTROYED_OR_DISABLED + } + + if (m_Methods[MonoScriptCache::kAddToManager]) + { + CallMethodIfAvailable (MonoScriptCache::kAddToManager); + RETURN_IF_DESTROYED_OR_DISABLED + } + + // External dependencies might get created by OnEnable. + // Thus we hook them up after OnEnable + AddExternalDependencyCallbacksToManagers (); +} + +void MonoBehaviour::Deactivate (DeactivateOperation operation) +{ + // When loading a new level we don't want coroutines to stop running, so just ignore it. + if (operation != kDeprecatedDeactivateToggleForLevelLoad) + StopAllCoroutines (); + + Super::Deactivate (operation); +} + + +void MonoBehaviour::ReleaseMonoInstance () +{ + Assert(m_ActiveCoroutines.empty()); + + ScriptingObjectPtr instance = GetCachedScriptingObject(); + if (instance) + { + // In the player we protect against null reference exceptions by setting the instance id of the mono representation to 0. + // This is necessary because mono classes might stick around for a bit longer until the GC cleans it up. + // By setting the id to 0 it is impossible for another mono behaviour instance to load it from disk or other weird things. + + // In the editor we don't want to do this as it will cause references to scripts to + // get lost when importing packages containing the referenced script. + // ReleaseMonoInstance is called in that case because the script is getting deleted. + + + #if !UNITY_EDITOR + ScriptingObjectOfType<Object> wrapper(instance); + wrapper.SetInstanceID(0); + wrapper.SetCachedPtr(NULL); + #endif + + SetCachedScriptingObject(SCRIPTING_NULL); + } + + Assert(GetCachedScriptingObject() == SCRIPTING_NULL); + + m_Methods = NULL; + if (m_ScriptCache != NULL) + { + const_cast<MonoScriptCache*> (m_ScriptCache)->Release(); + m_ScriptCache = NULL; + } +} + +#if UNITY_EDITOR +#define LogScriptError(x,script) DebugStringToFile (x, 0, __FILE__, __LINE__, kLog | kScriptCompileError, script, GetMonoManager().GetInstanceID ()); +#endif + +static UNITY_TLS_VALUE(int) s_MonoBehaviourInConstructorCounter; +int GetMonoBehaviourInConstructor() +{ + return s_MonoBehaviourInConstructorCounter; +} + +static std::string SafeGetScriptFileName(MonoScript* script, ScriptingClassPtr klass) +{ + if (script) + return script->GetName(); + + if (klass) + return scripting_class_get_name(klass); + + return ""; +} + +/////@TODO: THIS IS NOT REALLY THREAD SAFE. ALL MONO SCRIPT FUNCTIONS ARE NOT THREAD SAFE??? +//// MONO SCRIPT LOADING SHOULD NOT BE DONE IN A DIFFERENT THREAD +/// ALL FUNCTIONS MODIFYING SCRIPT CONTENT MUST BE THREAD SAFE +void MonoBehaviour::RebuildMonoInstance (ScriptingObjectPtr instance) +{ + ReleaseMonoInstance (); + + MonoScript* script = 0; + MonoScriptType type = kScriptTypeNotInitialized; + +#if UNITY_EDITOR + if (m_ScriptCache == NULL && !m_EditorClassIdentifier.empty()) + { + std::string assembly; + std::string ns; + std::string klass; + GetScriptClassIdComponents(m_EditorClassIdentifier, assembly, ns, klass); + + ScriptingClass* sc = GetMonoManager().GetMonoClassWithAssemblyName(klass, ns, assembly); + m_ScriptCache = CreateMonoScriptCache(sc, true, this); + if (m_ScriptCache != NULL) + { + m_ScriptCache->Retain(); + type = m_ScriptCache->scriptType; + } + } +#endif + + if (m_ScriptCache == NULL) + { + script = dynamic_pptr_cast<MonoScript*> (InstanceIDToObjectThreadSafe(m_Script.GetInstanceID())); + if (script) + { + m_ScriptCache = script->GetScriptCache (); + if (m_ScriptCache != NULL) + { + m_ScriptCache->Retain(); + type = m_ScriptCache->scriptType; + } + } + else + type = kScriptTypeScriptMissing; + } + + // We want a warning to be printed only once, that is when entering play mode and not when returning back + if (IsWorldPlaying() && !IsValidScriptType(type)) + WarningStringObject (FormatScriptTypeError(type, SafeGetScriptFileName(script, GetClass())), this); + + if (!IsValidScriptType(type)) + return; + + AssertIf(GetCachedScriptingObject() != SCRIPTING_NULL); + Assert(m_ScriptCache != NULL && m_ScriptCache->klass != NULL); + + if (instance == SCRIPTING_NULL) + { + ScriptingObjectPtr newInstance; +#if ENABLE_MONO + SET_ALLOC_OWNER(s_MonoDomainContainer); +#endif + // Instantiate Mono class, handle exception + newInstance = scripting_object_new (m_ScriptCache->klass); + if (newInstance == SCRIPTING_NULL) + { + if (IsWorldPlaying()) + WarningStringObject (Format("The script behaviour '%s' could not be instantiated!", script->GetScriptClassName().c_str()), this); + return; + } + + /// Setup Mono object pointers + Scripting::ConnectScriptingWrapperToObject(newInstance, this); + + // Prevent certain functions inside constructor + int constructorCount = s_MonoBehaviourInConstructorCounter; + constructorCount++; + s_MonoBehaviourInConstructorCounter = constructorCount; + + ScriptingExceptionPtr exception = NULL; + + scripting_object_invoke_default_constructor(GetInstance(), &exception); + + DebugAssertIf(constructorCount != s_MonoBehaviourInConstructorCounter); + constructorCount--; + s_MonoBehaviourInConstructorCounter = constructorCount; + + if (exception) + Scripting::LogException(exception, Scripting::GetInstanceIDFromScriptingWrapper(newInstance)); + } + else + { + Scripting::ConnectScriptingWrapperToObject (instance, this); + } + + // Get methods array from script + m_Methods = m_ScriptCache->methods.begin(); + + #if UNITY_EDITOR + m_EditorHideFlags |= (script && script->IsBuiltinScript()) ? kHideScriptPPtr : 0; + #endif +} + +void MonoBehaviour::RebuildMonoInstanceFromScriptChange (ScriptingObjectPtr instance) +{ + if (IsAddedToManager ()) + RemoveFromManager (); + + RebuildMonoInstance (instance); +#if UNITY_EDITOR + if (GetInstance() != NULL) + SetBackup(NULL); +#endif + + if (IsAddedToManager ()) + AddToManager (); +} + +void MonoBehaviour::SetScript (const PPtr<MonoScript>& newScript, ScriptingObjectPtr instance) +{ + if (m_Script != newScript) + { + //LockPlayerLoop(); + + m_Script = newScript; + + RebuildMonoInstanceFromScriptChange(instance); + + //UnlockPlayerLoop(); + } + else if (IsWorldPlaying () && newScript == PPtr<MonoScript> (0)) + { + WarningStringObject ("The referenced script on this Behaviour is missing!", this); + } +} + +#if UNITY_EDITOR + +void MonoBehaviour::SetClassIdentifier (const std::string& id) +{ + if (m_EditorClassIdentifier != id) + { + //LockPlayerLoop(); + + m_EditorClassIdentifier = id; + + RebuildMonoInstanceFromScriptChange(NULL); + + //UnlockPlayerLoop(); + } + //m_EditorClassIdentifier = id; +} + +bool MonoBehaviour::CanAssignMonoVariable (const char* propertyType, Object* object) +{ + return CanAssignMonoVariableStatic(propertyType, object); +} + +bool MonoBehaviour::CanAssignMonoVariableStatic (const char* propertyType, Object* object) +{ + MonoObject* instance = Scripting::ScriptingWrapperFor(object); + if (!instance) + return false; + + MonoClass* assignmentClass = mono_object_get_class(instance); + while (assignmentClass != NULL) + { + if (strcmp(mono_class_get_name(assignmentClass), propertyType) == 0) + return true; + + assignmentClass = mono_class_get_parent(assignmentClass); + } + + return false; +} + +#if UNITY_LOGIC_GRAPH +void MonoBehaviour::LoadLogicGraphInEditor () +{ + // At the moment we compile LogicGraphs just in time when loading scenes. + // MonoBehaviour instances are currently created on the loading thread -> Before logic graphs are compiled + // Thus we need this hack in the editor to ensure that the logic graph is active using the latest graph data + MonoScript* script = m_Script; + if (script != NULL && script->GetEditorGraphData() != NULL && IsWorldPlaying()) + { + script->Rebuild(CompileAndLoadLogicGraph(*script, false)); + RebuildMonoInstance(NULL); + } +} +#endif + + +void MonoBehaviour::DidReloadDomain () +{ +#if ENABLE_AUDIO_FMOD + // release custom filter + delete m_AudioCustomFilter; + m_AudioCustomFilter = NULL; +#endif + + if (IsAddedToManager()) + AddToManager(); + else + { + MonoScript* script = m_Script; + if (GetInstance() == NULL || script == NULL) + return; + + if (!IsScriptableObject()) + return; + + ScriptingMethodPtr method = m_Methods[MonoScriptCache::kAddToManager]; + if (method != NULL) + CallMethodInactive(method); + } +} + +bool MonoBehaviour::ShouldDisplayEnabled () +{ + if (m_EditorHideFlags & kHideEnabled) + return false; + if (GetInstance()) + { + return m_Methods[MonoScriptCache::kUpdate] || m_Methods[MonoScriptCache::kFixedUpdate] || + m_Methods[MonoScriptCache::kLateUpdate] || + m_Methods[MonoScriptCache::kRenderImageFilter] || + m_Methods[MonoScriptCache::kCoroutineStart] || m_Methods[MonoScriptCache::kCoroutineMain] || + m_Methods[MonoScriptCache::kGUI] || + m_Methods[MonoScriptCache::kAddToManager] || m_Methods[MonoScriptCache::kRemoveFromManager] || + m_Methods[MonoScriptCache::kAudioFilterRead]; + } + else + return true; +} + + + +#endif // UNITY_EDITOR + +char const* MonoBehaviour::GetName () const +{ + const GameObject* go = GetGameObjectPtr(); + if (go) + return go->GetName(); + else + return m_Name.c_str (); +} + +void MonoBehaviour::SetName (char const* name) +{ + GameObject* go = GetGameObjectPtr(); + if (go) + return go->SetName(name); + else + { + m_Name = name; + SetDirty(); + } +} + +ScriptingClassPtr MonoBehaviour::GetClass () +{ + if (m_ScriptCache != NULL) + return m_ScriptCache->klass; + else + return NULL; +} + +const MonoBehaviour::MethodCache* MonoBehaviour::GetMethodCache () +{ + if (m_ScriptCache != NULL) + return &m_ScriptCache->methodCache; + else + return NULL; +} + + +std::string MonoBehaviour::GetScriptClassName () +{ + MonoScript* script = m_Script; + if (script) + return script->GetScriptClassName(); + else + return string(); +} + +std::string MonoBehaviour::GetScriptFullClassName () +{ + MonoScript* script = m_Script; + if (script) + return script->GetScriptFullClassName(); + else + return string(); +} + +#if ENABLE_MONO || UNITY_WINRT +ScriptingArrayPtr RequiredComponentsOf(ScriptingClassPtr klass) +{ + // Extract component requirements + ScriptingObjectPtr typeObject = scripting_class_get_object(klass); + ScriptingInvocation invocation(GetMonoManager().GetCommonClasses().extractRequiredComponents); + invocation.AddObject(typeObject); + return scripting_cast_object_to_array(invocation.Invoke()); +} + +ScriptingArrayPtr RequiredComponentsOf(MonoBehaviour* script) +{ + return RequiredComponentsOf(script->GetClass()); +} +#endif + +void ResetAndApplyDefaultReferencesOnNewMonoBehaviour(MonoBehaviour& behaviour) +{ + +#if UNITY_EDITOR + MonoScript* script = behaviour.GetScript(); + if (script && (script->GetScriptType() == kScriptTypeEditorScriptableObjectDerived || !IsWorldPlaying())) + ApplyDefaultReferences(behaviour, script->GetDefaultReferences()); +#endif + + behaviour.Reset(); + behaviour.SmartReset(); + + behaviour.AwakeFromLoad(kInstantiateOrCreateFromCodeAwakeFromLoad); +} + +#undef LogScriptError +#endif diff --git a/Runtime/Mono/MonoBehaviour.h b/Runtime/Mono/MonoBehaviour.h new file mode 100644 index 0000000..9972c51 --- /dev/null +++ b/Runtime/Mono/MonoBehaviour.h @@ -0,0 +1,373 @@ +#ifndef MONOBEHAVIOUR_H +#define MONOBEHAVIOUR_H + +#include "Runtime/GameCode/Behaviour.h" +#include "MonoIncludes.h" +#include "Runtime/Serialize/TypeTree.h" +#include "Runtime/Utilities/CStringHash.h" +#include "Runtime/Scripting/Backend/ScriptingTypes.h" +#include "Runtime/Utilities/LinkedList.h" +#include "Runtime/Utilities/dynamic_array.h" +#include "Runtime/Scripting/ScriptingUtility.h" +#include "Runtime/Scripting/Backend/ScriptingBackendApi.h" + + +class MonoScript; +class MonoBehaviour; +typedef ListNode<MonoBehaviour> MonoBehaviourListNode; +class MonoManager; +struct ObjectGUIState; +struct Coroutine; +class RenderTexture; +struct MonoScriptCache; + +#if ENABLE_AUDIO_FMOD +class AudioCustomFilter; +namespace FMOD { + class DSP; +} +#endif + +class TerrainInstance; + + +#if UNITY_EDITOR +#include "Editor/Src/Utility/YAMLNode.h" + +struct BackupState +{ + BackupState (); + ~BackupState (); + + TypeTree typeTree; + dynamic_array<UInt8> state; + bool inYamlFormat; + bool loadedFromDisk; + YAMLNode* yamlState; + + + void SetYamlBackup (YAMLNode* root) { delete yamlState; inYamlFormat = true; yamlState = root; } + bool IsYaml () const { return inYamlFormat; } + bool HasYamlData () const { return yamlState != NULL; } + + private: + + // Preventy copy constructor + BackupState (const BackupState& preventCopy); + void operator = (const BackupState& preventCopy); +}; +#endif + + +class MonoBehaviour : public Behaviour +{ +public: + + REGISTER_DERIVED_CLASS (MonoBehaviour, Behaviour) + DECLARE_OBJECT_SERIALIZE (MonoBehaviour) + + MonoBehaviour (MemLabelId label, ObjectCreationMode mode); + // virtual ~MonoBehaviour (); declared-by-macro + + // Tag class as sealed, this makes QueryComponent faster. + static bool IsSealedClass () { return true; } + + /// Returns the MonoObject representing this MonoBehaviour + ScriptingObjectPtr GetInstance () + { + #if ENABLE_SCRIPTING + return GetCachedScriptingObject(); + #else + return SCRIPTING_NULL; + #endif + } + ScriptingClassPtr GetClass (); + + void RebuildMonoInstanceFromScriptChange (ScriptingObjectPtr instance); + /// Changes the used script to newScript + /// It is critical you call Awake + void SetScript (const PPtr<MonoScript>& newScript, ScriptingObjectPtr instance = SCRIPTING_NULL); + PPtr<MonoScript> GetScript () const { return m_Script; } + + Coroutine* InvokeMethodOrCoroutineChecked (ScriptingMethodPtr method, ScriptingObjectPtr value = SCRIPTING_NULL); + + /// Starts a coroutine + /// If value is not null the value will be passed into the object. + /// If the method is not a coroutine, it will just be invoked + Coroutine* CreateCoroutine(ScriptingObjectPtr enumerator, ScriptingMethodPtr method); + Coroutine* StartCoroutine (const char* name, ScriptingObjectPtr value = SCRIPTING_NULL); + ScriptingObjectPtr StartCoroutineManaged (const char* name, ScriptingObjectPtr value = SCRIPTING_NULL); + ScriptingObjectPtr StartCoroutineManaged2 (ScriptingObjectPtr enumerator); + + std::string GetScriptClassName (); + std::string GetScriptFullClassName (); + + /// Calls a method with methodName if the method is implemented by the class + bool CallMethodInactive (const char* methodName); + + /// Calls a method + bool CallMethodInactive (ScriptingMethodPtr method); + +#if UNITY_EDITOR + // Check the consistency of the mono behavior... (validate fields ect in mono land) + virtual void CheckConsistency (); +#endif + + /// Forwards Reset to the MonoClass in edit mode only. + virtual void SmartReset (); + + enum GUILayoutType {kNoLayout = 0, kGameLayout = 1, kEditorWindowLayout = 2 }; +#if ENABLE_UNITYGUI + /// Caller for the GUI functions. + virtual bool DoGUI (GUILayoutType layoutType, int skin); +#endif + + // ImageFilter + #if ENABLE_IMAGEEFFECTS + static void RenderImageFilter (Unity::Component* component, RenderTexture *source, RenderTexture *destination); + #endif + + virtual void Deactivate (DeactivateOperation operation); + virtual void AwakeFromLoad (AwakeFromLoadMode mode); + + virtual UInt32 CalculateSupportedMessages (); + + ScriptingMethodPtr FindMethod (const char* name); + + void InvokeOnRenderObject (); + + #if UNITY_EDITOR + enum { kHideScriptPPtr = 1 << 0, kHideEnabled = 1 << 2 }; + void SetEditorHideFlags(int flags) { m_EditorHideFlags = flags; } + + void SetClassIdentifier(const std::string& id); + std::string GetClassIdentifier() const { return m_EditorClassIdentifier; } + #endif + + void StopCoroutine (const char* name); + void StopAllCoroutines (); + + virtual char const* GetName () const; + virtual void SetName (char const* name); + + typedef std::map<const char*, ScriptingMethodPtr, compare_cstring> MethodCache; + const MethodCache* GetMethodCache (); + + // SetupAwake is called immediately by Activate. + // It is delayed to make sure that all objects in the scene are setup + // when Awake gets called on the MonoObject + void SetupAwake (); + + bool GetUseGUILayout (); + void SetUseGUILayout (bool use); + + bool WillUnloadScriptableObject (); + +private: + + static void DeprecatedDelayedAwakeCall (Object* o, void* userData); + static void DelayedStartCall (Object* o, void* userData); + + /// Calls Awake on the MonoObject if it implements the Awake method + static void DelayedAwakeMonoBehaviour (Object* o, void* userData); + + // Registers the MonoBehaviour for the Update/FixedUpdate/LateUpdate method. + // Update/FixedUpdate/LateUpdate is called if: + // - We are in play mode + // - Any of the Update, FixedUpdate, LateUpdate is implemented in the MonoClass. + // Before invoking the Update method, + // the Start method is invoked if it hasnt been invoked yet. + virtual void AddToManager (); + virtual void RemoveFromManager (); + virtual void Update (); + virtual void FixedUpdate (); + virtual void LateUpdate (); + inline void Start (); + + void DeprecatedAwakeFromLoadCodePath (AwakeFromLoadMode awakeMode); + + #if UNITY_EDITOR + friend void DrawMonoGizmo (Object& object, int options, void*); + friend bool CanDrawMonoGizmo (Object& object, int options, void*); + #endif + + inline void CallMethodIfAvailable (int methodIndex); + void CallUpdateMethod(int methodIndex); + + ScriptingObjectPtr InvokeMethodOrCoroutineChecked(ScriptingMethodPtr method, ScriptingObjectPtr value, ScriptingExceptionPtr* exception); + + // Depending on which script we use the serialized typetree changes + // thus the typetree might change while the editor is running + virtual bool GetNeedsPerObjectTypeTree () const { return true; } + + static void HandleNotifications (void* receiver, int messageIndex, MessageData& data); + static bool CanHandleNotifications (void* receiver, int messageIndex, MessageData& data); + + #if UNITY_EDITOR + bool CanAssignMonoVariable (const char* property, Object* object); + void DidReloadDomain (); + #endif + + virtual void WillDestroyComponent (); + + template<bool kSwap> + void VirtualRedirectTransferStreamedBinaryRead(StreamedBinaryRead<kSwap>& transfer); +public: + + // Don't call this if GetInstance() == NULL + ScriptingMethodPtr GetMethod (int index) const { return m_Methods[index]; } + + #if UNITY_EDITOR + static bool CanAssignMonoVariableStatic (const char* property, Object* object); + #endif + + // RebuildMonoInstance should only be called by MonoScript and MonoManager + // Anulls the old instance + // And creates a new Mono representation if the script class is availible + // Resets m_Methods to the new class + void RebuildMonoInstance (ScriptingObjectPtr instance); + void ReleaseMonoInstance (); + + #if UNITY_EDITOR + void SetBackup (BackupState* state) { delete m_Backup; m_Backup = state; } + BackupState* GetBackup () { return m_Backup; } + static void ExtractBackupFromInstance (MonoObject* instance, MonoClass* scriptClass, BackupState& backup, int flags); + static void ExtractYAMLBackupFromInstance (MonoObject* instance, MonoClass* scriptClass, BackupState& backup, int flags); + void RestoreInstanceStateFromBackup (BackupState& backup, int flags); + template<class TransferFunctor> + void ProcessBackupStateWhileReading (TransferFunctor& transfer); + #endif + + void SetInstanceNULLAndCreateBackup (); + Coroutine* HandleCoroutineReturnValue (ScriptingMethodPtr method, ScriptingObjectPtr returnValue); + + // Registers the notification receiver function + static void InitializeClass (); + static void CleanupClass (); + + #if UNITY_EDITOR + virtual bool ShouldDisplayEnabled (); + static void RestartExecuteInEditModeScripts (); + #endif + + void TransferSafeBinaryInstanceOnly (dynamic_array<UInt8>& data, const TypeTree& typeTree, int options); + + ObjectGUIState& GetObjectGUIState(); + + bool IsScriptableObject(); + bool IsDestroying() { return m_IsDestroying; } + + List<Coroutine>& GetActiveCoroutines () { return m_ActiveCoroutines; } + +public: + bool HaveAudioCallback() const; + void SetByPassOnDSP(bool state); +#if ENABLE_AUDIO_FMOD + FMOD::DSP* GetDSP() const; + FMOD::DSP* GetOrCreateDSP(); + AudioCustomFilter* GetAudioCustomFilter() { return m_AudioCustomFilter; } +#endif + +private: + + void AddBehaviourCallbacksToManagers (); + void AddExternalDependencyCallbacksToManagers (); + void CallAwake (); + void DeprecatedAddToManager (); + + bool GetRunInEditMode ()const; + bool IsPlayingOrAllowExecuteInEditMode() const; + std::string GetDebugDescription(); + + template<class TransferFunction> + PPtr<MonoScript> TransferEngineData (TransferFunction& transfer); + + template<class TransferFunction> + void TransferMonoData (TransferFunction& transfer); + + template<class TransferFunction> + static void TransferWithInstance (TransferFunction& transfer, ScriptingObjectPtr instance, ScriptingClassPtr klass); + template<class TransferFunction> + void TransferWithInstance (TransferFunction& transfer); + +#if ENABLE_SERIALIZATION_BY_CODEGENERATION + void DoLivenessCheck(RemapPPtrTransfer& transfer); +#endif + + template<class TransferFunction> + void TransferEngineAndInstance (TransferFunction& transfer); + +#if UNITY_EDITOR + static void ExtractBackup (class SafeBinaryRead& transfer, struct BackupState& backup); + static void ExtractBackup (YAMLRead& transfer, BackupState& backup); +#if UNITY_LOGIC_GRAPH + void LoadLogicGraphInEditor (); +#endif +#endif + + PPtr<MonoScript> m_Script; + + UnityStr m_Name; + + const MonoScriptCache* m_ScriptCache; + const ScriptingMethodPtr* m_Methods; + + List<Coroutine> m_ActiveCoroutines; + + BehaviourListNode m_UpdateNode; + BehaviourListNode m_FixedUpdateNode; + BehaviourListNode m_LateUpdateNode; + MonoBehaviourListNode m_GUINode; + MonoBehaviourListNode m_OnRenderObjectNode; + + // Per-monobehaviour GUI State. + ObjectGUIState *m_GUIState; + + bool m_DidAwake; + bool m_DidStart; + bool m_UseGUILayout; + bool m_IsDestroying; + +private: +#if ENABLE_AUDIO_FMOD + AudioCustomFilter* m_AudioCustomFilter; +#endif + + #if UNITY_EDITOR + BackupState* m_Backup; + UInt32 m_EditorHideFlags; + UnityStr m_EditorClassIdentifier; + #endif + + friend struct Coroutine; + friend class MonoManager; +}; + +#if UNITY_EDITOR +void BuildScriptPopupMenus (MonoBehaviour& behaviour, std::map<std::string, std::map<int, std::string> >& popups); +void ApplyDefaultReferences (MonoBehaviour& behaviour, const std::map<UnityStr, PPtr<Object> >& data); +#endif + +void ResetAndApplyDefaultReferencesOnNewMonoBehaviour(MonoBehaviour& behaviour); + +EXPORT_COREMODULE int GetMonoBehaviourInConstructor(); + +#if ENABLE_MONO || UNITY_WINRT +ScriptingArrayPtr RequiredComponentsOf(ScriptingClassPtr klass); +ScriptingArrayPtr RequiredComponentsOf(MonoBehaviour* script); +#endif + +////@TODO: THIS SHOULD BE REMOVED AND DONE WITH THREAD_SAFE / CONSTRUCTOR_SAFE tags instead! + +/// DISALLOW_IN_CONSTRUCTOR Raises an exception when executed from inside a MonoBehaviour constructor. +/// eg. GetComponent uses this to make sure no one uses it in a constructor +#if UNITY_EDITOR +#define DISALLOW_IN_CONSTRUCTOR { \ + if (GetMonoBehaviourInConstructor() == 0) ; else { \ + Scripting::RaiseMonoException("You are not allowed to call this function when declaring a variable.\nMove it to the line after without a variable declaration.\nIf you are using C# don't use this function in the constructor or field initializers, Instead move initialization to the Awake or Start function."); } \ + } +#else +#define DISALLOW_IN_CONSTRUCTOR { } +#endif + +#endif diff --git a/Runtime/Mono/MonoBehaviourAnimationBinding.cpp b/Runtime/Mono/MonoBehaviourAnimationBinding.cpp new file mode 100644 index 0000000..d077acd --- /dev/null +++ b/Runtime/Mono/MonoBehaviourAnimationBinding.cpp @@ -0,0 +1,66 @@ +#include "UnityPrefix.h" +#include "Runtime/Animation/GenericAnimationBindingCache.h" +#include "Runtime/Animation/AnimationClipBindings.h" +#include "Runtime/Animation/BoundCurve.h" +#include "MonoBehaviour.h" +#include "Runtime/Interfaces/IAnimationBinding.h" +#include "MonoScript.h" + +static const char* kEnabledStr = "m_Enabled"; + +class MonoBehaviourPropertyBinding : public IAnimationBinding +{ +public: + +#if UNITY_EDITOR + virtual void GetAllAnimatableProperties (Object& targetObject, std::vector<EditorCurveBinding>& outProperties) const + { + MonoBehaviour* beh = reinterpret_cast<MonoBehaviour *> (&targetObject); + MonoScript& script = *beh->GetScript (); + + outProperties.push_back(EditorCurveBinding ("", ClassID(MonoBehaviour), &script, kEnabledStr, false)); + } +#endif + + virtual float GetFloatValue (const UnityEngine::Animation::BoundCurve& bind) const + { + MonoBehaviour* mono = reinterpret_cast<MonoBehaviour*>(bind.targetObject); + + return UnityEngine::Animation::AnimationBoolToFloat(mono->GetEnabled()); + } + + virtual void SetFloatValue (const UnityEngine::Animation::BoundCurve& bind, float value) const + { + MonoBehaviour* mono = reinterpret_cast<MonoBehaviour*>(bind.targetObject); + + mono->SetEnabled(UnityEngine::Animation::AnimationFloatToBool(value)); + } + + virtual void SetPPtrValue (const UnityEngine::Animation::BoundCurve& bound, SInt32 value) const { } + + virtual SInt32 GetPPtrValue (const UnityEngine::Animation::BoundCurve& bound) const { return 0;} + + virtual bool GenerateBinding (const UnityStr& attribute, bool pptrCurve, UnityEngine::Animation::GenericBinding& outputBinding) const + { + return attribute == kEnabledStr && !pptrCurve; + } + + virtual ClassIDType BindValue (Object& target, const UnityEngine::Animation::GenericBinding& inputBinding, UnityEngine::Animation::BoundCurve& bound) const + { + return ClassID(bool); + } +}; + +static MonoBehaviourPropertyBinding* gBinding = NULL; + +void InitializeMonoBehaviourAnimationBindingInterface () +{ + gBinding = UNITY_NEW (MonoBehaviourPropertyBinding, kMemAnimation); + UnityEngine::Animation::GetGenericAnimationBindingCache ().RegisterIAnimationBinding (ClassID(MonoBehaviour), UnityEngine::Animation::kMonoBehaviourPropertyBinding, gBinding); +} + +void CleanupMonoBehaviourAnimationBindingInterface () +{ + UNITY_DELETE (gBinding, kMemAnimation); +} + diff --git a/Runtime/Mono/MonoBehaviourAnimationBinding.h b/Runtime/Mono/MonoBehaviourAnimationBinding.h new file mode 100644 index 0000000..e417cbe --- /dev/null +++ b/Runtime/Mono/MonoBehaviourAnimationBinding.h @@ -0,0 +1,4 @@ +#pragma once + +void InitializeMonoBehaviourAnimationBindingInterface (); +void CleanupMonoBehaviourAnimationBindingInterface (); diff --git a/Runtime/Mono/MonoBehaviourSerialization.cpp b/Runtime/Mono/MonoBehaviourSerialization.cpp new file mode 100644 index 0000000..3c21123 --- /dev/null +++ b/Runtime/Mono/MonoBehaviourSerialization.cpp @@ -0,0 +1,1745 @@ +#include "UnityPrefix.h" +#if ENABLE_SCRIPTING +#include "MonoBehaviour.h" +#include "Runtime/Serialize/TransferFunctions/SerializeTransfer.h" +#include "Runtime/Serialize/FileCache.h" +#include "Runtime/Serialize/IterateTypeTree.h" +#include "Runtime/Serialize/SerializedFile.h" +#include "MonoScript.h" +#include "MonoTypeSignatures.h" +#include "MonoManager.h" +#include "Runtime/Filters/Misc/Font.h" +#include "Runtime/Math/Quaternion.h" +#include "Runtime/Math/Color.h" +#include "Runtime/Math/Vector4.h" +#include "Runtime/Math/Vector2.h" +#include "Runtime/Math/Rect.h" +#include "Runtime/Geometry/AABB.h" +#include "Runtime/Math/AnimationCurve.h" +#include "Runtime/Math/Gradient.h" +#include "Runtime/IMGUI/GUIStyle.h" +#include "tabledefs.h" +#include "Runtime/Mono/MonoBehaviourSerialization.h" +#include "Runtime/Scripting/ScriptingUtility.h" +#include "Runtime/Serialize/PersistentManager.h" +#include "Runtime/Scripting/CommonScriptingClasses.h" +#include "Runtime/Scripting/Backend/ScriptingBackendApi.h" +#include "Runtime/Scripting/Scripting.h" +#include "Runtime/Scripting/ScriptingObjectWithIntPtrField.h" + +using namespace std; + +//Exotic inclusion of .h file that contains a lot of function bodies. +//Only way I could figure out how to split up this 2500 line file into multiple parts. +//splitting it up normally doesn't work, because all invocations of a template function +//need to be in the same compilation unit, and this serializationcode is all template functions +//calling other template functions. +#include "Runtime/Mono/MonoBehaviourSerialization_Array.h" + +// Shoudl not be used!!! +#include "Runtime/GameCode/CallDelayed.h" + +#if UNITY_EDITOR +#include "Editor/Src/GUIDPersistentManager.h" +#include "Editor/Src/Application.h" +#include "Editor/Src/Utility/RuntimeClassHashing.h" +#endif + +struct TransferScriptInstance; + +template<class TransferFunction> +void TransferScriptData (TransferScriptInstance& info, TransferFunction& transfer); + +#if ENABLE_MONO +const char* CalculateMonoPPtrTypeString (char* buffer, MonoClass* klass) +{ + AssertIf(buffer == NULL); + + // Potential buffer overflow + char* c = buffer; + *c++ = 'P'; + *c++ = 'P'; + *c++ = 't'; + *c++ = 'r'; + *c++ = '<'; + *c++ = '$'; + + const char* className = mono_class_get_name(klass); + while (*className) + { + *c = *className; + c++; + className++; + } + + *c++ = '>'; + *c++ = '\0'; + return buffer; +} + +#if SUPPORT_TEXT_SERIALIZATION +YAMLNode* ConvertBackupToYAML (BackupState& binary); + +template<> +class YAMLSerializeTraits<MonoPPtr> : public YAMLSerializeTraits<PPtr<Object> > +{ +}; +#endif + +template<> +class SerializeTraits<MonoPPtr> : public SerializeTraitsBase<MonoPPtr> +{ +public: + + typedef MonoPPtr value_type; + + inline static const char* GetTypeString (void* data) + { + MonoPPtr* ptr = reinterpret_cast<MonoPPtr*> (data); + AssertIf(ptr == NULL); + // Needed for arrays (Could write custom Array & Traits class but thats a lot of work) + if (ptr->m_Buffer == NULL) + return "PPtr<$>"; + + return CalculateMonoPPtrTypeString(ptr->m_Buffer, ptr->m_Class); + } + + inline static bool IsAnimationChannel () { return false; } + inline static bool MightContainPPtr () { return true; } + inline static bool AllowTransferOptimization () { return false; } + + template<class TransferFunction> inline + static void Transfer (value_type& data, TransferFunction& transfer) + { + data.Transfer (transfer); + } +}; + +template<> +struct RemapPPtrTraits<MonoPPtr> +{ + static bool IsPPtr () { return true; } + static int GetInstanceIDFromPPtr (const MonoPPtr& data) { return data.GetInstanceID (); } + static void SetInstanceIDOfPPtr (MonoPPtr& data, SInt32 instanceID) { data.SetInstanceID (instanceID); } +}; + +template<class TransferFunction> inline +void TransferPPtr (MonoObject* instance, MonoClassField* field, const char* name, MonoClass* klass, int classID, TransferFunction& transfer, TransferMetaFlags metaFlags) +{ + MonoPPtr pptr; + if (transfer.IsWritingPPtr ()) + { + MonoObject* referencedObject = NULL; + mono_field_get_value (instance, field, &referencedObject); + if (referencedObject != NULL) + pptr.SetInstanceID (Scripting::GetInstanceIDFromScriptingWrapper (referencedObject)); + } + + // Potential buffer overflow + char buffer[128]; + pptr.m_Buffer = buffer; + pptr.m_Class = klass; + + transfer.Transfer (pptr, name, metaFlags); + + if (transfer.DidReadLastPPtrProperty ()) + { + MonoObject* newValue = NULL; + newValue = TransferPPtrToMonoObject (pptr.GetInstanceID (), klass, classID, field, instance, transfer.GetFlags() & kThreadedSerialization); + mono_field_set_value (instance, field, newValue); + } +} + +template<class T> +void TransferWithProxyElement (T& t, vector<MonoPPtr>& arr, MonoPPtr&proxy, const char* name, TransferMetaFlags metaflags) +{ + t.Transfer(arr, name, metaflags); +} + +template<> +void TransferWithProxyElement<ProxyTransfer> (ProxyTransfer& t, vector<MonoPPtr>& arr, MonoPPtr& proxy, const char* name, TransferMetaFlags metaflags) +{ + t.BeginTransfer (name, SerializeTraits<vector<MonoPPtr> >::GetTypeString (&arr), (char*)&arr, metaflags); + t.TransferSTLStyleArrayWithElement(proxy, kNoTransferFlags); + t.EndTransfer (); +} + +template<class T, class TransferFunction> inline +void TransferBuiltins (MonoObject* instance, MonoClassField* field, const char* name, TransferFunction& transfer, TransferMetaFlags metaFlags) +{ + T* value = reinterpret_cast<T*> (reinterpret_cast<UInt8*> (instance) + mono_field_get_offset(field)); + transfer.Transfer (*value, name, metaFlags); +} + +template<class TransferFunction> inline +void TransferString (MonoObject* instance, MonoClassField* field, const char* name, TransferFunction& transfer, TransferMetaFlags metaFlags) +{ + UnityStr stdString; + + if (transfer.IsWriting ()) + { + MonoString *strval; + mono_field_get_value (instance, field, &strval); + char *p = mono_string_to_utf8 (strval); + if (p) + stdString = p; + else + stdString.clear (); + g_free (p); + } + + transfer.Transfer (stdString, name, metaFlags); + + if (transfer.DidReadLastProperty ()) + { + ScriptingStringPtr monoString = scripting_string_new(stdString); + mono_field_set_value (instance, field, monoString); + } +} + + +static bool CalculateTransferPrivateVariables (MonoClass* klass) +{ + MonoCustomAttrInfo* attr = mono_custom_attrs_from_class(klass); + bool hasPrivate = attr != NULL && mono_custom_attrs_has_attr (attr, MONO_COMMON.serializePrivateVariables); + if (attr != NULL) + mono_custom_attrs_free (attr); + return hasPrivate; +} + +#if MONO_QUALITY_ERRORS +static void ReplacePrivateWithNULLWrapper (MonoObject* instance, MonoClassField* field) +{ + // This is necessary since proxy transfer will not give us an instance for array elements + if (instance == NULL) + return; + + // This way Mono won't throw an exception when an object is NULL instead the C++ side can handle it. + MonoType* monoType = mono_field_get_type (field); + int type = mono_type_get_type (monoType); + + if (type == MONO_TYPE_CLASS) + { + MonoClass* referencedClass = mono_type_get_class_or_element_class (monoType); + int classID = Scripting::GetClassIDFromScriptingClass (referencedClass); + if (classID != ClassID (MonoBehaviour) && classID != -1) + { + MonoObject* referencedObject; + mono_field_get_value (instance, field, &referencedObject); + if (referencedObject == NULL) + { + referencedObject = Scripting::ScriptingObjectNULL (referencedClass); + mono_field_set_value (instance, field, referencedObject); + } + } + } +} + +static MonoObject* PrepareRefcountedTransfer (MonoClassField* field, MonoClass* referencedClass, MonoObject* instance) +{ + MonoObject* referencedObject = NULL; + if (instance != NULL) + mono_field_get_value (instance, field, &referencedObject); + + if (referencedObject == NULL) + { + referencedObject = ScriptingInstantiateObject (referencedClass); + mono_runtime_object_init_log_exception (referencedObject); + if (instance != NULL) + mono_field_set_value (instance, field, referencedObject); + } + + return referencedObject; +} +#endif + +static MonoObject* PrepareTransfer (ScriptingClass* scriptingClass, MonoClassField* field, MonoObject* instance) +{ + MonoObject* value = NULL; + if (instance) + { + mono_field_get_value (instance, field, &value); + if (value == NULL) + { + value = ScriptingInstantiateObject (scriptingClass); mono_runtime_object_init_log_exception (value); + mono_field_set_value (instance, field, value); + } + } + return value; +} + +static AnimationCurve* PrepareAnimationCurveTransfer (MonoClassField* field, MonoObject* instance) + { + MonoObject* monoObject = PrepareTransfer (MONO_COMMON.animationCurve, field, instance); + if (monoObject) + return ExtractMonoObjectData<AnimationCurve*>(monoObject); + return NULL; + } + + +static GradientNEW* PrepareGradientTransfer (MonoClassField* field, MonoObject* instance) +{ + MonoObject* monoObject = PrepareTransfer (MONO_COMMON.gradient, field, instance); + if (monoObject) + return ExtractMonoObjectData<GradientNEW*>(monoObject); + return NULL; +} + +static RectOffset* PrepareRectOffsetTransfer (MonoClassField* field, MonoObject* instance) +{ + MonoObject* monoObject = PrepareTransfer (MONO_COMMON.rectOffset, field, instance); + if (monoObject) + return ExtractMonoObjectData<RectOffset*>(monoObject); + return NULL; +} + +static GUIStyle* PrepareGUIStyleTransfer (MonoClassField* field, MonoObject* instance) +{ + MonoObject* monoObject = PrepareTransfer (MONO_COMMON.guiStyle, field, instance); + if (monoObject) + return ExtractMonoObjectData<GUIStyle*>(monoObject); + return NULL; +} + +/// Returns only true on serializable classes. +/// But only if we are in the same assembly. +static bool PrepareTransferEmbeddedClass (MonoClassField* field, MonoClass* referencedClass, MonoObject* instance, TransferScriptInstance& output, int currentDepth) +{ + if (!PrepareTransferEmbeddedClassCommonChecks(referencedClass)) + return false; + + MonoObject* referencedObject = NULL; + if (instance) + { + mono_field_get_value (instance, field, &referencedObject); + + if (referencedObject == NULL) + { + referencedObject = ScriptingInstantiateObject (referencedClass); + mono_runtime_object_init_log_exception (referencedObject); + mono_field_set_value (instance, field, referencedObject); + } + } + else + { + referencedObject = ScriptingInstantiateObject (referencedClass); + } + + output.instance = referencedObject; + output.klass = referencedClass; + output.transferPrivate = CalculateTransferPrivateVariables(referencedClass); + output.commonClasses = &GetMonoManager().GetCommonClasses(); + output.depthCounter = currentDepth + 1; + Assert(currentDepth >= 0); + + return referencedObject != NULL; +} + +static bool HasAttribute(MonoClass* klass, MonoClassField *field, ScriptingClass* attributeClass) +{ + MonoCustomAttrInfo* attr = mono_custom_attrs_from_field (klass, field); + + if (attr == NULL) + return false; + + bool has = mono_custom_attrs_has_attr (attr, attributeClass); + mono_custom_attrs_free(attr); + return has; +} + +// Want to keep this logging ability around for some time, say until 2013 +//#define TF_LOG(...) printf_console (__VA_ARGS__) +#define TF_LOG(...) + +#if UNITY_EDITOR + +// Return true if this field should be skipped and not serialized +static bool ProcessClassFields (char const* name, MonoType* monoType, cil::SerializeTracker& targetFields) +{ + char* typeName = mono_type_get_name_full (monoType, MONO_TYPE_NAME_FORMAT_IL); + + // In case target doesn't have any more fields, skip the current one + if (!targetFields.IsFieldValid ()) + { + g_free (typeName); + return false; + } + + // The field doesn't match the current in the target structure, skip + if (!targetFields.IsCurrent (typeName, name)) + { + // It can also be that target has more fields than source, but we catch that before + // we start to serialize, so this shouldn't happen. + if (targetFields.HasField (typeName, name)) + { + while (targetFields.IsFieldValid () && !targetFields.IsCurrent (typeName, name)) + { + cil::TypeDB::Field const& dfield = targetFields.CurrentField(); + + // Should not happen! + WarningString (Format ("Unable to properly serialize object for the player of class '%s' because of extra field '%s' of type '%s' (expecting '%s' '%s')", typeName, dfield.name.c_str(), dfield.typeName.c_str(), typeName, name + )); + TF_LOG (" - %s %s [EXTRA IN TARGET!]\n", dfield.typeName.c_str(), dfield.name.c_str()); + ++targetFields; + } + } + else + { + TF_LOG (" - %s %s [SKIPPED!]\n", typeName, name); + g_free (typeName); + return false; + } + } + else + ++targetFields; + + TF_LOG (" - %s %s\n", typeName, name); + + g_free (typeName); + return true; +} + +#endif // UNITY_EDITOR + +// Keep the traversal behaviour in sync with HashValueTypes (ScriptingTypePtr klass) +template<class TransferFunction> +void TransferScriptData (TransferScriptInstance& info, TransferFunction& transfer) +{ + Assert(info.depthCounter >= 0); + + MonoObject* instance = info.instance; + MonoClass* klass = info.klass; + const CommonScriptingClasses& commonClasses = *info.commonClasses; + + if (transfer.IsSerializingForGameRelease ()) + { + TF_LOG ("Serializing class: %s\n", klass ? mono_class_get_name(klass) : "NULL"); + } + + // Recurse into parent classes stop when we reach the monobehaviour class + MonoClass* parentClass = mono_class_get_parent (klass); + if (parentClass && parentClass != commonClasses.monoBehaviour && parentClass != commonClasses.scriptableObject) + { + TransferScriptInstance parent = info; + parent.klass = parentClass; + parent.transferPrivate = CalculateTransferPrivateVariables(parentClass); + TransferScriptData (parent, transfer); + } + +#if UNITY_EDITOR + cil::SerializeTracker targetFields (cil::g_CurrentTargetTypeDB, klass); +#endif + + MonoClassField *field; + void* iter = NULL; + while ((field = mono_class_get_fields (klass, &iter))) + { + TransferMetaFlags metaFlags = kSimpleEditorMask; + // Exclude const attributes + int flags = mono_field_get_flags (field); + if (flags & (FIELD_ATTRIBUTE_STATIC | FIELD_ATTRIBUTE_INIT_ONLY | FIELD_ATTRIBUTE_NOT_SERIALIZED)) + continue; + + if ( ((flags & (FIELD_ATTRIBUTE_PRIVATE)) || (flags & 0xF) == FIELD_ATTRIBUTE_FAMILY) && !info.transferPrivate && + !HasAttribute(klass, field, commonClasses.serializeField)) + { +#if UNITY_EDITOR + if (transfer.GetFlags () & kSerializeDebugProperties) + metaFlags = kDebugPropertyMask | kNotEditableMask; + else + { + // @todo: This code should be removed once mono has better null ptr exception handling. + // We set all private ptr's that are NULL to be Object wrappers with instanceID 0. + // This way Mono won't throw an exception when an object is NULL instead the C++ side can handle it. + ReplacePrivateWithNULLWrapper (instance, field); + continue; + } +#else + continue; +#endif + } + + /// FINALLY MAKE THE SERIALIZATION CODE UNDERSTANDABLE!!!!!!!!! + /// I would like to be able to check if we actually need the name!!!!!!!!!!! + MonoType* monoType = mono_field_get_type (field); + int type = mono_type_get_type (monoType); + const char* name = mono_field_get_name (field); + +#if UNITY_EDITOR + // Skip fields that are not available in the 'player' script + if (transfer.IsSerializingForGameRelease () && targetFields.IsClassValid ()) + if (!ProcessClassFields (name, monoType, targetFields)) + continue; +#endif + +#if UNITY_EDITOR + if (transfer.NeedNonCriticalMetaFlags () && HasAttribute(klass, field, commonClasses.hideInInspector)) + metaFlags |= kHideInEditorMask; + // Make sure serialized property name does not contain any '.' characters. + // This can happen when serializing internal backing values of C# implicit + // properties, to which mono will add the namespace path (only relevant for + // the debug inspector). + std::string strippedName; + if (strchr(name, '.')) + { + const char *ch = name; + while (*ch != '\0') + { + strippedName += (*ch != '.')?*ch:'_'; + ch++; + } + name = strippedName.c_str(); + } + + if (transfer.NeedNonCriticalMetaFlags () && HasAttribute(klass, field, commonClasses.hideInInspector)) + metaFlags |= kHideInEditorMask; +#endif + + switch (type) + { + case MONO_TYPE_STRING: + TransferString (instance, field, name, transfer, metaFlags); + break; + case MONO_TYPE_I4: + TransferBuiltins<SInt32> (instance, field, name, transfer, metaFlags); + break; + case MONO_TYPE_R4: + TransferBuiltins<float> (instance, field, name, transfer, metaFlags); + break; + case MONO_TYPE_BOOLEAN: + TransferBuiltins<UInt8> (instance, field, name, transfer, metaFlags | kEditorDisplaysCheckBoxMask); + transfer.Align(); + break; + case MONO_TYPE_U1: + TransferBuiltins<UInt8> (instance, field, name, transfer, metaFlags); + transfer.Align(); + break; + case MONO_TYPE_R8: + TransferBuiltins<double> (instance, field, name, transfer, metaFlags); + break; + + + case MONO_TYPE_VALUETYPE: + { + MonoClass* structClass = mono_type_get_class_or_element_class (monoType); + if (structClass == commonClasses.vector3) + TransferBuiltins<Vector3f> (instance, field, name, transfer, metaFlags); + else if (structClass == commonClasses.vector2) + TransferBuiltins<Vector2f> (instance, field, name, transfer, metaFlags); + else if (structClass == commonClasses.vector4) + TransferBuiltins<Vector4f> (instance, field, name, transfer, metaFlags); + else if (structClass == commonClasses.rect) + TransferBuiltins<Rectf> (instance, field, name, transfer, metaFlags); + else if (structClass == commonClasses.quaternion) + TransferBuiltins<Quaternionf> (instance, field, name, transfer, metaFlags); + else if (structClass == commonClasses.matrix4x4) + TransferBuiltins<Matrix4x4f> (instance, field, name, transfer, metaFlags); + else if (structClass == commonClasses.bounds) + TransferBuiltins<AABB> (instance, field, name, transfer, metaFlags); + else if (structClass == commonClasses.color) + TransferBuiltins<ColorRGBAf> (instance, field, name, transfer, metaFlags); + else if (structClass == commonClasses.color32) + TransferBuiltins<ColorRGBA32> (instance, field, name, transfer, metaFlags); + else if (structClass == commonClasses.layerMask) + TransferBuiltins<BitField> (instance, field, name, transfer, metaFlags); + else if (mono_class_is_enum (structClass)) + { + MonoType* enumMonoType = mono_class_enum_basetype (structClass); + int enumType = mono_type_get_type (enumMonoType); + switch (enumType) + { + case MONO_TYPE_I4: + TransferBuiltins<SInt32> (instance, field, name, transfer, metaFlags); + break; + case MONO_TYPE_U1: + TransferBuiltins<UInt8> (instance, field, name, transfer, metaFlags); + transfer.Align(); + break; + default: + ErrorString (ErrorMessageForUnsupportedEnumField(monoType, mono_class_get_type(klass), name)); + break; + } + } +#if UNITY_EDITOR + else if ((transfer.GetFlags () & kSerializeMonoReload) && structClass == commonClasses.monoReloadableIntPtr) + { + TransferBuiltins<UIntPtr> (instance, field, name, transfer, metaFlags); + } + else if ((transfer.GetFlags () & kSerializeMonoReload) && structClass == commonClasses.monoReloadableIntPtrClear) + { + TransferBuiltins<UIntPtr> (instance, field, name, transfer, metaFlags); + // Clear value after writing, so that objects referencing it won't try to delete the C++ object + if (transfer.IsWriting()) + { + void** reloadValue = reinterpret_cast<void**> (reinterpret_cast<UInt8*> (instance) + mono_field_get_offset(field)); + *reloadValue = NULL; + } + } +#endif + } + break; + + case MONO_TYPE_CLASS: + { + // Serialize pptr + MonoClass* referencedClass = mono_type_get_class_or_element_class (monoType); + + // Do not serialize delegates + // Most delegates are not serializable, but Boo generates delegate classes + // that are marked as serializable + if (mono_class_is_subclass_of (referencedClass, MONO_COMMON.multicastDelegate, false)) + continue; + + int classID = Scripting::GetClassIDFromScriptingClass (referencedClass); + if (classID != -1) + { + TransferPPtr (instance, field, name, referencedClass, classID, transfer, metaFlags); + } + else if (referencedClass == commonClasses.animationCurve) + { + AnimationCurve* curve = PrepareAnimationCurveTransfer(field, instance); + transfer.Transfer (*curve, name, metaFlags); + } + else if (referencedClass == commonClasses.gradient) + { + GradientNEW* gradient = PrepareGradientTransfer (field, instance); + transfer.Transfer (*gradient, name, metaFlags); + } + else if (referencedClass == commonClasses.rectOffset) + { + RectOffset* offset = PrepareRectOffsetTransfer(field, instance); + transfer.Transfer (*offset, name, metaFlags); + } + else if (referencedClass == commonClasses.guiStyle) + { + GUIStyle* style = PrepareGUIStyleTransfer(field, instance); + transfer.Transfer (*style, name, metaFlags); + } + // Serialize embedded class, but only if it has attribute serializable + else + { + // Unfortunately we don't support cycles. + if (referencedClass == klass) + break; + + // Make sure we don't endless loop. Put a hard cap limit on the level of nested types until we have a better general solution. + if (info.depthCounter > kClassSerializationDepthLimit) + return; + + TransferScriptInstance transferScriptInstance; + if (PrepareTransferEmbeddedClass (field, referencedClass, instance, transferScriptInstance, info.depthCounter)) + { + transfer.TransferWithTypeString(transferScriptInstance, name, mono_class_get_name(referencedClass), metaFlags); + } + } + } + break; + + case MONO_TYPE_GENERICINST: + case MONO_TYPE_SZARRAY: + { + TransferFieldOfTypeArray(instance, klass, field, name, info, transfer, monoType, type, metaFlags); + } + break; + + default: + + // printf_console ("unsupported type"); + + break; + + }; + } + + if (transfer.IsReading() || transfer.IsReadingPPtr()) + ApplyScriptDataModified(info); +} +#endif //ENABLE_MONO +/// Mono Backup +/// Problem: Sometimes the dll is not loadable or a script class can't be loaded +/// We don't want to lose the data when loading from disk so we store it in a temporary backup until the class appears again. + +/// We now directly get the backup from the serialized information if we can't store it directly into the mono representation +/// This makes the code hard because we need to individually support every single transfer function. +/// Also we need to manually remap pptrs into instance id's +/// + +template<class TransferFunction> +PPtr<MonoScript> MonoBehaviour::TransferEngineData (TransferFunction& transfer) +{ + Super::Transfer (transfer); + PPtr<MonoScript> newScript = m_Script; +#if UNITY_EDITOR + if (!transfer.IsSerializingForGameRelease()) + transfer.Transfer(m_EditorHideFlags, "m_EditorHideFlags", kHideInEditorMask); + + if (SerializePrefabIgnoreProperties(transfer)) + { + TransferMetaFlags mask = kNoTransferFlags; + if (m_EditorHideFlags & kHideScriptPPtr) + mask |= kHideInEditorMask; + transfer.Transfer (newScript, "m_Script", mask); + } +#else + transfer.Transfer (newScript, "m_Script"); +#endif + + transfer.Transfer(m_Name, "m_Name", kHideInEditorMask); + TRANSFER_EDITOR_ONLY_HIDDEN(m_EditorClassIdentifier); + return newScript; +} + + +std::string MonoBehaviour::GetDebugDescription() +{ + return Format("id:%d, script:%s",GetInstanceID(), GetScriptClassName().c_str()); +} + +#if ENABLE_MONO && !ENABLE_SERIALIZATION_BY_CODEGENERATION +template<class TransferFunction> +inline void MonoBehaviour::TransferWithInstance (TransferFunction& transfer) +{ + TransferWithInstance (transfer, GetInstance (), GetClass()); +} + +template<class TransferFunction> +void MonoBehaviour::TransferWithInstance (TransferFunction& transfer, ScriptingObjectPtr instance, ScriptingClassPtr klass) +{ + SET_ALLOC_OWNER(s_MonoDomainContainer); + + AssertIf (instance == NULL); + AssertIf (klass == NULL); + + TransferScriptInstance referencedInstance; + referencedInstance.instance = instance; + referencedInstance.klass = klass; + referencedInstance.commonClasses = &GetMonoManager ().GetCommonClasses (); + referencedInstance.transferPrivate = CalculateTransferPrivateVariables(klass); + referencedInstance.depthCounter = 0; + + TransferScriptData (referencedInstance, transfer); +} +#endif + +template<class TransferFunction> +void MonoBehaviour::TransferEngineAndInstance (TransferFunction& transfer) +{ + PPtr<MonoScript> newScript = TransferEngineData (transfer); + if (transfer.IsReadingPPtr ()) + SetScript (newScript); + if (GetInstance()) + { + TransferWithInstance (transfer); + } +} + + +void MonoBehaviour::SetInstanceNULLAndCreateBackup () +{ + if (GetInstance()) + { + GetDelayedCallManager().CancelAllCallDelayed( this ); + +#if UNITY_EDITOR + SetBackup (new BackupState ()); + ExtractBackupFromInstance (GetInstance(), GetClass(), *m_Backup, 0); +#endif + + ReleaseMonoInstance (); + } +} + +/// Simple Player specific serialization +#if !UNITY_EDITOR + +#if SUPPORT_SERIALIZED_TYPETREES +void MonoBehaviour::VirtualRedirectTransfer (SafeBinaryRead& transfer) +{ + SET_ALLOC_OWNER(this); + transfer.BeginTransfer ("Base", MonoBehaviour::GetTypeString(), NULL); + + PPtr<MonoScript> newScript = TransferEngineData (transfer); + if (transfer.IsReadingPPtr ()) + SetScript (newScript); + + if (GetInstance()) + { + transfer.OverrideRootTypeName(scripting_class_get_name(GetClass())); + TransferWithInstance (transfer); + } + + transfer.EndTransfer (); +} + +void MonoBehaviour::VirtualRedirectTransfer (StreamedBinaryRead<true>& transfer) +{ + SET_ALLOC_OWNER(this); + TransferEngineAndInstance(transfer); +} + +#endif + +void MonoBehaviour::VirtualRedirectTransfer (StreamedBinaryWrite<false>& transfer) +{ + TransferEngineAndInstance(transfer); +} + +void MonoBehaviour::VirtualRedirectTransfer (StreamedBinaryRead<false>& transfer) +{ + SET_ALLOC_OWNER(this); + TransferEngineAndInstance(transfer); +} + + +void MonoBehaviour::VirtualRedirectTransfer (ProxyTransfer& transfer) +{ +#if !UNITY_FLASH && !UNITY_WINRT && !ENABLE_SERIALIZATION_BY_CODEGENERATION + transfer.BeginTransfer ("Base", MonoBehaviour::GetTypeString(), NULL, kNoTransferFlags); + + TransferEngineAndInstance(transfer); + + transfer.EndTransfer (); +#endif +} + + +void MonoBehaviour::VirtualRedirectTransfer (RemapPPtrTransfer& transfer) +{ + TransferEngineAndInstance (transfer); +} + + +/// Unity Editor specific serialization +#else // #if UNITY_EDITOR + +void ApplyDefaultReferences (MonoBehaviour& behaviour, const map<UnityStr, PPtr<Object> >& data) +{ + MonoObject* instance = behaviour.GetInstance(); + if (instance == NULL) + return; + + const CommonScriptingClasses& commonClasses = MONO_COMMON; + MonoClass* klass = mono_object_get_class(behaviour.GetInstance()); + while (klass && klass != commonClasses.monoBehaviour && klass != commonClasses.scriptableObject) + { + MonoClassField *field; + void* iter = NULL; + while ((field = mono_class_get_fields (klass, &iter))) + { + int flags = mono_field_get_flags (field); + + // Ignore static / nonserialized + if (flags & (FIELD_ATTRIBUTE_STATIC | FIELD_ATTRIBUTE_INIT_ONLY | FIELD_ATTRIBUTE_NOT_SERIALIZED)) + continue; + + // only public fields, or marked as serialize field + if ( ((flags & (FIELD_ATTRIBUTE_PRIVATE)) || (flags & 0xF) == FIELD_ATTRIBUTE_FAMILY) && !HasAttribute(klass, field, commonClasses.serializeField)) + continue; + + // Only pptrs on the root level + MonoType* monoType = mono_field_get_type (field); + int type = mono_type_get_type (monoType); + if (type == MONO_TYPE_CLASS) + { + MonoClass* referencedClass = mono_type_get_class_or_element_class (monoType); + const char* name = mono_field_get_name (field); + map<UnityStr, PPtr<Object> >::const_iterator found = data.find(name); + if (found != data.end()) + { + // Check if the target variable inherits from the class + MonoObject* target = Scripting::ScriptingWrapperFor(found->second); + if (target && mono_class_is_subclass_of(mono_object_get_class(target), referencedClass, false)) + { + // Never override pptr values + MonoObject* oldTarget; + mono_field_get_value (instance, field, &oldTarget); + if (Scripting::GetInstanceIDFromScriptingWrapper(oldTarget) == 0) + mono_field_set_value (instance, field, target); + } + } + } + } + + klass = mono_class_get_parent (klass); + } +} + +struct FileToMemoryID +{ + bool operator () (const TypeTree& typeTree, dynamic_array<UInt8>& data, int bytePosition) + { + if (!IsTypeTreePPtr (typeTree)) + return true; + + if (typeTree.m_ByteSize == 12) + { + LocalSerializedObjectIdentifier* pptrData = reinterpret_cast<LocalSerializedObjectIdentifier*> (&data[bytePosition]); + SInt32 instanceID = 0; + LocalSerializedObjectIdentifierToInstanceID (*pptrData, instanceID); + pptrData->localSerializedFileIndex = instanceID; + pptrData->localIdentifierInFile = 0; + } + else + { + SInt32* pptrData = reinterpret_cast<SInt32*> (&data[bytePosition]); + SInt32 instanceID = 0; + LocalSerializedObjectIdentifier identifier; + identifier.localSerializedFileIndex = pptrData[0]; + identifier.localIdentifierInFile = pptrData[1]; + + LocalSerializedObjectIdentifierToInstanceID (identifier, instanceID); + + pptrData[0] = instanceID; + pptrData[1] = 0; + + } + return true; + } +}; + +struct MemoryIDToFileID +{ + bool operator () (const TypeTree& typeTree, dynamic_array<UInt8>& data, int bytePosition) + { + if (!IsTypeTreePPtr (typeTree)) + return true; + + if (typeTree.m_ByteSize == 12) + { + LocalSerializedObjectIdentifier* pptrData = reinterpret_cast<LocalSerializedObjectIdentifier*> (&data[bytePosition]); + SInt32 instanceID = pptrData->localSerializedFileIndex; + InstanceIDToLocalSerializedObjectIdentifier (instanceID, *pptrData); + } + else + { + SInt32* pptrData = reinterpret_cast<SInt32*> (&data[bytePosition]); + + LocalSerializedObjectIdentifier identifier; + InstanceIDToLocalSerializedObjectIdentifier (pptrData[0], identifier); + + pptrData[0] = identifier.localSerializedFileIndex; + pptrData[1] = identifier.localIdentifierInFile; + } + return true; + } +}; + +static int SkipString(CachedReader &cache, int basePos, int byteOffset, bool swapEndian, const TypeTree& type) +{ + UInt32 nameLength = 0; + cache.SetPosition(basePos + byteOffset); + cache.Read(&nameLength, sizeof(nameLength)); + if (swapEndian) + SwapEndianBytes(nameLength); + + byteOffset += sizeof(nameLength)+nameLength; + if (type.m_MetaFlag & kAlignBytesFlag || type.m_Children.back().m_MetaFlag & kAlignBytesFlag) + byteOffset = Align4(byteOffset); + + return byteOffset; +} + + +static bool IsTypeTreeProperMonoBehaviour (const TypeTree& typeTree, int baseSize, int* byteOffset, int basePos, int* numberOfEngineChildren, CachedReader& cache, bool swapEndian) +{ + *byteOffset = 0; + *numberOfEngineChildren = 0; + + TypeTree::const_iterator i = typeTree.m_Children.begin (); + + if (i != typeTree.end() && i->m_Name == "m_ObjectHideFlags") + { + *byteOffset += i->m_ByteSize; + *numberOfEngineChildren += 1; + i++; + } + + if (i != typeTree.end() && i->m_Name == "m_ExtensionPtr") + { + *byteOffset += i->m_ByteSize; + *numberOfEngineChildren += 1; + i++; + } + + if (i != typeTree.end() && i->m_Name == "m_PrefabParentObject") + { + *byteOffset += i->m_ByteSize; + *numberOfEngineChildren += 1; + i++; + } + + if (i != typeTree.end() && i->m_Name == "m_PrefabInternal") + { + *byteOffset += i->m_ByteSize; + *numberOfEngineChildren += 1; + i++; + } + + if (i != typeTree.end() && i->m_Name == "m_GameObject") + { + *byteOffset += i->m_ByteSize; + *numberOfEngineChildren += 1; + i++; + } + else + return false; + + if (i != typeTree.end() && i->m_Name == "m_Enabled") + { + *byteOffset += i->m_ByteSize; + if (i->m_MetaFlag & kAlignBytesFlag) + *byteOffset = Align4(*byteOffset); + *numberOfEngineChildren += 1; + i++; + } + else + return false; + + if (i != typeTree.end() && i->m_Name == "m_EditorHideFlags") + { + *byteOffset += i->m_ByteSize; + *numberOfEngineChildren += 1; + i++; + } + + if (i != typeTree.end() && i->m_Name == "m_Script") + { + *byteOffset += i->m_ByteSize; + *numberOfEngineChildren += 1; + i++; + } + else + return false; + + if (i != typeTree.end() && i->m_Name == "m_Name") + { + *byteOffset = SkipString(cache, basePos, *byteOffset, swapEndian, *i); + + *numberOfEngineChildren += 1; + i++; + } + +#if UNITY_EDITOR + if (i != typeTree.end() && i->m_Name == "m_EditorClassIdentifier") + { + *byteOffset = SkipString(cache, basePos, *byteOffset, swapEndian, *i); + + *numberOfEngineChildren += 1; + i++; + } +#endif + + return baseSize >= *byteOffset; +} + +void MonoBehaviour::ExtractBackup (SafeBinaryRead& transfer, BackupState& backup) +{ + backup.loadedFromDisk = true; + + const TypeTree& sourceType = *transfer.m_OldBaseType; + int byteOffset; + int numberOfEngineChildren; + if (IsTypeTreeProperMonoBehaviour (sourceType, transfer.m_BaseByteSize, &byteOffset, transfer.m_BaseBytePosition, &numberOfEngineChildren, transfer.m_Cache, transfer.ConvertEndianess())) + { + dynamic_array<UInt8>& sourceData = backup.state; + + // Extract typetree, remove all engine serialized data (m_Enabled, m_Script, m_GameObject etc) + backup.typeTree = sourceType; + TypeTree::iterator begin = backup.typeTree.m_Children.begin (); + TypeTree::iterator end = begin; + advance (end, numberOfEngineChildren); + RemoveFromTypeTree(backup.typeTree, begin, end); + + // Extract data + sourceData.resize_uninitialized (transfer.m_BaseByteSize - byteOffset); + transfer.m_Cache.SetPosition (byteOffset + transfer.m_BaseBytePosition); + transfer.m_Cache.Read (sourceData.begin (), sourceData.size ()); + + // case 565490 + // Remove m_EditorClassIdentifier if it is in the backup, it should not be there + // Remove the data from the sourceData and remove the data type from the type tree + // If m_EditorClassIdentifier is in the backup it will always be the first type + if (backup.typeTree.m_Children.size() > 0 && StrCmp(backup.typeTree.begin()->m_Name, "m_EditorClassIdentifier") == 0) + { + int skipped = 0; + WalkTypeTree(*backup.typeTree.m_Children.begin(), sourceData.begin(), &skipped); + sourceData.erase(sourceData.begin(), sourceData.begin() + skipped); + RemoveFromTypeTree(backup.typeTree, backup.typeTree.begin(), ++backup.typeTree.begin()); + } + + + // Swap bytes + if (transfer.ConvertEndianess()) + ByteSwapGeneric(backup.typeTree, sourceData); + + if (transfer.NeedsInstanceIDRemapping ()) + { + int pos = 0; + FileToMemoryID remap; + IterateTypeTree (backup.typeTree, sourceData, &pos, remap); + } + } + else + { + string error = "Monobehaviour has unknown format!: \n"; + sourceType.DebugPrint (error); + ErrorString (error); + } +} + +void MonoBehaviour::ExtractBackupFromInstance (MonoObject* instance, MonoClass* scriptClass, BackupState& backup, int flags) +{ + SET_ALLOC_OWNER(s_MonoDomainContainer); + + AssertIf (instance == NULL || scriptClass == NULL); + // Generate type tree from instance + backup.typeTree = TypeTree (); + ProxyTransfer proxy (backup.typeTree, flags, NULL, 0); + proxy.BeginTransfer ("Base", mono_class_get_name(scriptClass), NULL, kNoTransferFlags); + + TransferScriptInstance transferData; + transferData.instance = instance; + transferData.klass = scriptClass; + transferData.commonClasses = &GetMonoManager ().GetCommonClasses (); + transferData.transferPrivate = CalculateTransferPrivateVariables(scriptClass); + transferData.depthCounter = 0; + + TransferScriptData (transferData, proxy); + proxy.EndTransfer (); + + // Generate state vector + backup.state.clear (); + MemoryCacheWriter memoryCache (backup.state); + StreamedBinaryWrite<false> writeStream; + CachedWriter& writeCache = writeStream.Init (flags, BuildTargetSelection::NoTarget()); + writeCache.InitWrite (memoryCache); + + TransferScriptData (transferData, writeStream); + + writeCache.CompleteWriting (); +} + +void MonoBehaviour::RestoreInstanceStateFromBackup (BackupState& backup, int flags) +{ + if (backup.IsYaml ()) + { + if (backup.HasYamlData ()) + { + yaml_document_t ydoc; + yaml_document_initialize (&ydoc, NULL, NULL, NULL, 1, 1); + backup.yamlState->PopulateDocument (&ydoc); + + YAMLRead transfer (&ydoc, flags); + TransferWithInstance (transfer); + yaml_document_delete (&ydoc); + } + } + else + TransferSafeBinaryInstanceOnly (backup.state, backup.typeTree, flags); +} + +void MonoBehaviour::TransferSafeBinaryInstanceOnly (dynamic_array<UInt8>& data, const TypeTree& typeTree, int options) +{ + MemoryCacheReader memoryCache (data); + SafeBinaryRead readStream; + CachedReader& readCache = readStream.Init (typeTree, 0, data.size (), options); + readCache.InitRead (memoryCache, 0, data.size ()); + + AssertIf(GetInstance() == NULL); + readStream.BeginTransfer ("Base", mono_class_get_name(GetClass()), NULL); + TransferWithInstance (readStream); + readStream.EndTransfer (); + + readCache.End (); +} + + +void MonoBehaviour::VirtualRedirectTransfer (SafeBinaryRead& transfer) +{ + SET_ALLOC_OWNER(this); + transfer.BeginTransfer ("Base", MonoBehaviour::GetTypeString(), NULL); + + // AssertIf (GetInstance() != NULL && m_Backup != NULL); + + PPtr<MonoScript> newScript = TransferEngineData (transfer); + if (transfer.IsReadingPPtr ()) + { + if ((transfer.GetFlags() & kAutoreplaceEditorWindow) && !newScript.IsValid()) + newScript = dynamic_instanceID_cast<MonoScript*> (GetPersistentManager().GetInstanceIDFromPathAndFileID("Library/unity default resources", 12059)); + + SetScript (newScript); + } + + if (GetInstance()) + { + // Override the root name to ensure that it is the same as we will be loading, + // since the user might have changed it in the mean time. + transfer.OverrideRootTypeName(mono_class_get_name(GetClass())); + + TransferWithInstance (transfer); + } + else + ProcessBackupStateWhileReading (transfer); + + transfer.EndTransfer (); +} + +template <bool kSwap> +void MonoBehaviour::VirtualRedirectTransferStreamedBinaryRead(StreamedBinaryRead<kSwap>& transfer) +{ + PPtr<MonoScript> newScript = TransferEngineData (transfer); + if (transfer.IsReadingPPtr ()) + SetScript (newScript); + + if (GetInstance()) + TransferWithInstance (transfer); +} + +void MonoBehaviour::VirtualRedirectTransfer (StreamedBinaryRead<false>& transfer) +{ + SET_ALLOC_OWNER(this); + VirtualRedirectTransferStreamedBinaryRead(transfer); +} + +void MonoBehaviour::VirtualRedirectTransfer (StreamedBinaryRead<true>& transfer) +{ + SET_ALLOC_OWNER(this); + VirtualRedirectTransferStreamedBinaryRead(transfer); +} + +void MonoBehaviour::VirtualRedirectTransfer (ProxyTransfer& transfer) +{ + transfer.BeginTransfer ("Base", MonoBehaviour::GetTypeString(), NULL, kNoTransferFlags); + TransferEngineData (transfer); + + if (GetInstance()) + TransferWithInstance (transfer); + else if (m_Backup && !transfer.IsSerializingForGameRelease()) + { + AppendTypeTree (transfer.m_TypeTree, m_Backup->typeTree.begin (), m_Backup->typeTree.end ()); + } + + transfer.EndTransfer (); +} + +static void ShowScriptMissingWarning(MonoBehaviour& behaviour) +{ + if (behaviour.GetInstance () == NULL) + { + WarningString (Format( "Script attached to '%s' in scene '%s' is missing or no valid script is attached.", behaviour.GetName(), GetApplication().GetCurrentScene().c_str() )); + } +} + +void MonoBehaviour::VirtualRedirectTransfer (StreamedBinaryWrite<false>& transfer) +{ + TransferEngineData (transfer); + + if (GetInstance()) + TransferWithInstance (transfer); + else if (m_Backup && !transfer.IsSerializingForGameRelease() && !m_Backup->IsYaml ()) + { + if (transfer.NeedsInstanceIDRemapping ()) + { + dynamic_array<UInt8> sourceData = m_Backup->state; + int pos = 0; + MemoryIDToFileID remap; + IterateTypeTree (m_Backup->typeTree, sourceData, &pos, remap); + transfer.m_Cache.Write (sourceData.begin (), sourceData.size ()); + } + else + transfer.m_Cache.Write (m_Backup->state.begin (), m_Backup->state.size ()); + } + + if (transfer.IsSerializingForGameRelease()) + ShowScriptMissingWarning(*this); +} + +void MonoBehaviour::VirtualRedirectTransfer (StreamedBinaryWrite<true>& transfer) +{ + TransferEngineAndInstance (transfer); + + if (transfer.IsSerializingForGameRelease()) + ShowScriptMissingWarning(*this); +} + +template<class TransferFunctor> +void MonoBehaviour::ProcessBackupStateWhileReading (TransferFunctor& transfer) +{ + SetBackup (new BackupState ()); + ExtractBackup (transfer, *m_Backup); +} + +char const* kRemoveNodes[] = +{ + "m_ObjectHideFlags", "m_ExtensionPtr", "m_PrefabParentObject", "m_PrefabInternal", + "m_GameObject", "m_Enabled", "m_EditorHideFlags", "m_Script", "m_Name" + +#if UNITY_EDITOR + , "m_EditorClassIdentifier" +#endif +}; + +static const size_t kRemoveNodesCount = sizeof(kRemoveNodes) / sizeof(kRemoveNodes[0]); + + +bool GetInstanceIDDirectlyIfAvailable (YAMLMapping* map, SInt32& instanceId) +{ + if (YAMLScalar* scalar = dynamic_cast<YAMLScalar*> (map->Get ("instanceID"))) + { + instanceId = *scalar; + return true; + } + + return false; +} + +bool GetInstanceIDFromFileIDIfAvailable (YAMLMapping* map, SInt32& instanceId) +{ + int fileID = 0; + + if (YAMLScalar* yFileId = dynamic_cast<YAMLScalar*>(map->Get("fileID"))) + fileID = (int)*yFileId; + else + return false; + + if (YAMLScalar* yGuid = dynamic_cast<YAMLScalar*>(map->Get("guid"))) + { + int type = 0; + if (YAMLScalar* yType = dynamic_cast<YAMLScalar*>(map->Get("type"))) + type = (int)*yType; + + // GUID is present, PPtr is referencing an object from another file + UnityGUID guid = (UnityGUID)*yGuid; + std::string pathName = GetPathNameFromGUIDAndType (guid, type); + + if (pathName.empty ()) + return false; + + instanceId = GetGUIDPersistentManager ().GetInstanceIDFromPathAndFileID(pathName, fileID); + } + else + { + // PPtr is referencing an object from the file we're reading + LocalSerializedObjectIdentifier localId; + localId.localIdentifierInFile = fileID; + GetPersistentManager ().LocalSerializedObjectIdentifierToInstanceIDInternal (localId, instanceId); + } + + return true; +} + +template<class FunctorT> +void IterateYAMLTree (YAMLNode* node, FunctorT func) +{ + if (YAMLMapping* map = dynamic_cast<YAMLMapping*> (node)) + { + if (func (map)) + { + for (YAMLMapping::const_iterator it = map->begin (), end = map->end (); it != end; ++it) + IterateYAMLTree (it->second, func); + } + } else if (YAMLSequence* seq = dynamic_cast<YAMLSequence*> (node)) + { + if (func (seq)) + { + for (YAMLSequence::const_iterator it = seq->begin (), end = seq->end (); it != end; ++it) + IterateYAMLTree (*it, func); + } + } else if (YAMLScalar* scalar = dynamic_cast<YAMLScalar*> (node)) + { + func (scalar); + } +} + +struct ConvertYAMLFileIDToInstanceID +{ + bool operator() (YAMLNode*) const { return true; } + + // We're only interested in mappings + bool operator() (YAMLMapping* map) const + { + SInt32 instanceId = 0; + + if (GetInstanceIDFromFileIDIfAvailable (map, instanceId)) + { + // Modify the mapping, changing fileID into InstanceID + map->Clear (); + + map->Append ("instanceID", instanceId); + + // We do not want to iterate this mapping anymore + return false; + } + + return true; + } +}; + +struct ConvertYAMLInstanceIDToFileID +{ + bool m_AllowLocalIdentifierInFile; + ConvertYAMLInstanceIDToFileID (bool allowLocalIdentifierInFile) : m_AllowLocalIdentifierInFile (allowLocalIdentifierInFile) {} + + bool operator() (YAMLNode*) const { return true; } + + // We're only interested in mappings + bool operator() (YAMLMapping* map) const + { + SInt32 instanceID = 0; + + if (GetInstanceIDDirectlyIfAvailable (map, instanceID)) + { + LocalSerializedObjectIdentifier localIdentifier; + InstanceIDToLocalSerializedObjectIdentifier (instanceID, localIdentifier); + + // Modify the mapping, changing fileID into InstanceID + map->Clear(); + + if (m_AllowLocalIdentifierInFile && localIdentifier.localSerializedFileIndex == 0) + { + map->Append ("fileID", localIdentifier.localIdentifierInFile); + } + else + { + GUIDPersistentManager& pm = GetGUIDPersistentManager(); + pm.Lock(); + SerializedObjectIdentifier identifier; + if (pm.InstanceIDToSerializedObjectIdentifier(instanceID, identifier)) + { + FileIdentifier id = pm.PathIDToFileIdentifierInternal(identifier.serializedFileIndex); + map->Append ("fileID", identifier.localIdentifierInFile); + map->Append ("guid", id.guid); + map->Append ("type", id.type); + } + pm.Unlock(); + } + + // We do not want to iterate this mapping anymore + return false; + } + + return true; + } +}; + + +void YAMLConvertFileIDToInstanceID (YAMLMapping* map) +{ + ConvertYAMLFileIDToInstanceID convertor; + IterateYAMLTree (map, convertor); +} + +void YAMLConvertInstanceIDToFileID (YAMLMapping* map, bool allowLocalIdentifierInFile) +{ + ConvertYAMLInstanceIDToFileID convertor (allowLocalIdentifierInFile); + IterateYAMLTree (map, convertor); +} + +void MonoBehaviour::ExtractBackup (YAMLRead& transfer, BackupState& backup) +{ + backup.loadedFromDisk = true; + // Node graph for current object + YAMLNode* yroot = transfer.GetCurrentNode (); + + if (YAMLMapping* monoBehaviour = dynamic_cast<YAMLMapping*> (yroot)) + { + // Remove engine values + for (int i=0; i<kRemoveNodesCount; ++i) + monoBehaviour->Remove (kRemoveNodes[i]); + + // case 565490 + // Remove any duplicates of m_EditorClassIdentifier, caused by a (now fixed) bug in the backup code + YAMLNode* duplicateNode = NULL; + while ((duplicateNode = monoBehaviour->Get("m_EditorClassIdentifier")) != NULL) + { + monoBehaviour->Remove("m_EditorClassIdentifier"); + } + + // Convert PPtr from guid/fileId to instanceID + if (transfer.NeedsInstanceIDRemapping ()) + YAMLConvertFileIDToInstanceID (monoBehaviour); + } + + backup.SetYamlBackup (yroot); +} + +void AppendYAMLMappingToCurrentNode (YAMLWrite& transfer, YAMLMapping* map) +{ + for (YAMLMapping::const_iterator it = map->begin (), end = map->end (); it != end; ++it) + { + int keyId = it->first->PopulateDocument (transfer.GetDocument ()); + int valueId = it->second->PopulateDocument (transfer.GetDocument ()); + yaml_document_append_mapping_pair(transfer.GetDocument (), transfer.GetCurrentNodeIndex (), keyId, valueId); + } +} + +void MonoBehaviour::VirtualRedirectTransfer (YAMLWrite& transfer) +{ + transfer.BeginMetaGroup (MonoBehaviour::GetTypeString()); + TransferEngineData (transfer); + + if (GetInstance()) + TransferWithInstance (transfer); + else if (m_Backup && !transfer.IsSerializingForGameRelease()) + { + YAMLNode* backup = NULL; + + if (m_Backup->IsYaml () && m_Backup->HasYamlData ()) + { + if (transfer.NeedsInstanceIDRemapping ()) + { + // We need to make a temp copy of backup state for MemoryId->FileID remapping. + // This is not very nice, but this code path is hit rarely and we convert through string + // instead of adding code to be able to clone YAMLNode. + std::string data = m_Backup->yamlState->EmitYAMLString (); + backup = ParseYAMLString (data); + } + else + { + backup = m_Backup->yamlState; + } + } + else if (!m_Backup->IsYaml ()) + { + backup = ConvertBackupToYAML (*m_Backup); + } + + if (YAMLMapping* monoBehaviour = dynamic_cast<YAMLMapping*> (backup)) + { + if (transfer.NeedsInstanceIDRemapping ()) + { + bool allowLocalIdentifier = (transfer.GetFlags () & kYamlGlobalPPtrReference) == 0; + YAMLConvertInstanceIDToFileID (monoBehaviour, allowLocalIdentifier); + } + + AppendYAMLMappingToCurrentNode (transfer, monoBehaviour); + } + + if (backup != m_Backup->yamlState) + delete backup; + } + + if (transfer.IsSerializingForGameRelease() && GetInstance () == NULL) + { + WarningString (Format( "Script attached to '%s' in scene '%s' is missing or no valid script is attached.", GetName(), GetApplication().GetCurrentScene().c_str() )); + } + + transfer.EndMetaGroup (); +} + +void MonoBehaviour::VirtualRedirectTransfer (YAMLRead& transfer) +{ + SET_ALLOC_OWNER(this); + transfer.BeginMetaGroup (MonoBehaviour::GetTypeString()); + + PPtr<MonoScript> newScript = TransferEngineData (transfer); + if (transfer.IsReadingPPtr ()) + { + if ((transfer.GetFlags() & kAutoreplaceEditorWindow) && !newScript.IsValid()) + newScript = dynamic_instanceID_cast<MonoScript*> (GetPersistentManager().GetInstanceIDFromPathAndFileID("Library/unity default resources", 12059)); + + SetScript (newScript); + } + + if (GetInstance()) + TransferWithInstance (transfer); + else + ProcessBackupStateWhileReading (transfer); + + transfer.EndMetaGroup (); +} + +// NOTE: This functor does not assign the newly generated InstaceID over the old one! +struct RemapPPtrFromYAMLBackup +{ + GenerateIDFunctor* m_GenerateIDFunctor; + + RemapPPtrFromYAMLBackup (GenerateIDFunctor* functor) : m_GenerateIDFunctor (functor) {} + + bool operator() (YAMLNode*) const { return true; } + + // We're only interested in mappings + bool operator() (YAMLMapping* map) const + { + SInt32 oldInstanceID = 0; + + if (GetInstanceIDDirectlyIfAvailable (map, oldInstanceID)) + { + if (/*YAMLScalar* scalar =*/ dynamic_cast<YAMLScalar*> (map->Get( "instanceID"))) + { + m_GenerateIDFunctor->GenerateInstanceID (oldInstanceID, kNoTransferFlags); + return false; + } + } + + return true; + } +}; + +struct RemapPPtrFromBackup +{ + GenerateIDFunctor* idfunctor; + + bool operator () (const TypeTree& typeTree, dynamic_array<UInt8>& data, int bytePosition) + { + if (!IsTypeTreePPtr (typeTree)) + return true; + + SInt32* pptrData = reinterpret_cast<SInt32*> (&data[bytePosition]); + + SInt32 oldInstanceID = *pptrData; + SInt32 newInstanceID = idfunctor->GenerateInstanceID (oldInstanceID, typeTree.m_MetaFlag); + *pptrData = newInstanceID; + return true; + } +}; + +void MonoBehaviour::VirtualRedirectTransfer (RemapPPtrTransfer& transfer) +{ + PPtr<MonoScript> newScript = TransferEngineData (transfer); + if (transfer.IsReadingPPtr ()) + SetScript (newScript); + + if (GetInstance()) + { + TransferWithInstance (transfer); + return; + } + + if (!m_Backup) + return; + + if (transfer.NeedsInstanceIDRemapping ()) + { + AssertString("Impossible"); + return; + } + + if (!m_Backup->IsYaml ()) + { + dynamic_array<UInt8> sourceData = m_Backup->state; + int pos = 0; + RemapPPtrFromBackup remap; + remap.idfunctor = transfer.GetGenerateIDFunctor(); + IterateTypeTree (m_Backup->typeTree, sourceData, &pos, remap); + return; + } + + if (!m_Backup->HasYamlData ()) + return; + + RemapPPtrFromYAMLBackup remap (transfer.GetGenerateIDFunctor()); + IterateYAMLTree (m_Backup->yamlState, remap); +} + +#endif // UNITY_EDITOR + +ScriptingObjectPtr TransferPPtrToMonoObject(int instanceID, ScriptingClassPtr klass, int classID, ScriptingFieldPtr field, ScriptingObjectPtr parentInstance, bool threadedLoading) +{ +#if ENABLE_MONO && MONO_QUALITY_ERRORS + Object* obj = NULL; + MonoObject* instance = NULL; + int objClassID = 0; + if (!threadedLoading) + { + obj = PPtr<Object>(instanceID); + if (obj) + { + instance = Scripting::ScriptingWrapperFor(obj); + objClassID = obj->GetClassID(); + } + } + else + { + LockObjectCreation(); + + obj = Object::IDToPointerNoThreadCheck(instanceID); + if (obj != NULL) + { + instance = Scripting::ScriptingWrapperFor(obj); + objClassID = obj->GetClassID(); + UnlockObjectCreation(); + } + else + { + UnlockObjectCreation(); + obj = GetPersistentManager ().ReadObjectThreaded (instanceID); + if (obj != NULL) + { + instance = Scripting::ScriptingWrapperFor(obj); + objClassID = obj->GetClassID(); + } + } + } + + if (obj) + { + // If we got an instance and it's the right type, just return it + if (instance != NULL && mono_class_is_subclass_of(mono_object_get_class(instance), klass, false)) + return instance; + + // No cached object available + if (objClassID == ClassID (MonoBehaviour)) + { + // Doesn't derive from klass, so we can use the real MonoObject + if (instance) + return NULL; + + // Probably couldn't be loaded temporarily. Create fake wrapper + // There used by some problems with duplicating objects, but i can't think of a real world reason for this. + // We can probably remove it + if (!mono_unity_class_is_abstract(klass)) + instance = scripting_object_new(klass); + + if (!instance) + { + //if this happens, the klass was unable to initialize, often because of an exception in the klass' static constructor. + return NULL; + } + + ScriptingObjectOfType<Object>(instance).SetInstanceID(instanceID); + return instance; + } + } + + if (classID == ClassID (MonoBehaviour)) + return NULL; + + if (instanceID == 0) + { + MonoObject* obj = MonoObjectNULL(klass, UnassignedReferenceString(parentInstance, classID, field, instanceID)); + return obj; + } + + ScriptingObjectOfType<Object> object = mono_object_new (mono_domain_get(), klass); + object.SetInstanceID(instanceID); + object.SetError(UnassignedReferenceString(parentInstance, classID, field, instanceID)); + return object.GetScriptingObject(); + +#endif + + return TransferPPtrToMonoObjectUnChecked(instanceID, threadedLoading); +} + +ScriptingObjectPtr TransferPPtrToMonoObjectUnChecked (int instanceID, bool threadedLoading) +{ + if (!threadedLoading) // Non-Threaded loading path + return Scripting::GetScriptingWrapperForInstanceID(instanceID); + + if (instanceID == 0) + return SCRIPTING_NULL; + + LockObjectCreation(); + + Object* obj = Object::IDToPointerNoThreadCheck(instanceID); + + UnlockObjectCreation(); + + if(!obj) + obj = GetPersistentManager().ReadObjectThreaded(instanceID); + + return Scripting::ScriptingWrapperFor(obj); + +} + +#endif // ENABLE_SCRIPTING diff --git a/Runtime/Mono/MonoBehaviourSerialization.h b/Runtime/Mono/MonoBehaviourSerialization.h new file mode 100644 index 0000000..1e7fadb --- /dev/null +++ b/Runtime/Mono/MonoBehaviourSerialization.h @@ -0,0 +1,51 @@ +#pragma once + +#include "Runtime/Scripting/Backend/ScriptingTypes.h" + +static bool CalculateTransferPrivateVariables (MonoClass* klass); +struct BackupState; +YAMLNode* ConvertBackupToYAML (BackupState& binary); + +enum { kClassSerializationDepthLimit = 7 }; + + +struct MonoPPtr : PPtr<Object> +{ + MonoPPtr () { m_Buffer = NULL; m_Class = SCRIPTING_NULL; } + + char* m_Buffer; + ScriptingTypePtr m_Class; +}; + +struct TransferScriptInstance; + +template<class TransferFunction> +void TransferScriptData (TransferScriptInstance& info, TransferFunction& transfer); + +struct TransferScriptInstance +{ + inline static const char* GetTypeString () { return "Generic Mono"; } + inline static bool IsAnimationChannel () { return false; } + inline static bool MightContainPPtr () { return true; } + inline static bool AllowTransferOptimization () { return false; } + + template<class TransferFunction> inline + void Transfer (TransferFunction& transfer) + { + TransferScriptData (*this, transfer); + } + + TransferScriptInstance() + { + depthCounter = -9999; + } + + ScriptingObjectPtr instance; + ScriptingTypePtr klass; + bool transferPrivate; + const CommonScriptingClasses* commonClasses; + int depthCounter; +}; + +ScriptingObjectPtr TransferPPtrToMonoObjectUnChecked(int instanceID, bool threadedLoading); +ScriptingObjectPtr TransferPPtrToMonoObject(int instanceID, ScriptingClassPtr klass, int classID, ScriptingFieldPtr field, ScriptingObjectPtr parentInstance, bool threadedLoading); diff --git a/Runtime/Mono/MonoBehaviourSerialization_Array.h b/Runtime/Mono/MonoBehaviourSerialization_Array.h new file mode 100644 index 0000000..b029a8c --- /dev/null +++ b/Runtime/Mono/MonoBehaviourSerialization_Array.h @@ -0,0 +1,583 @@ +//This is being included by a headerfile. A bit akwardly done, but at the time it seemed like the most sane approach to be able to move serialization of arrays into its own files, to declutter the rest of monobehaviour serializationcode. + +#include "Runtime/Scripting/Scripting.h" + +#if ENABLE_MONO && !ENABLE_SERIALIZATION_BY_CODEGENERATION + +///@TODO: investigate if we can't get rid of the duplication of code between array transfers and non-array transfers. + +namespace +{ + template<typename ElementType> + struct TransferArrayHelper + { + MonoArray* array; + MonoObject* instance; + MonoClass* klass; + MonoClassField* field; + int length; + int elementSize; + unsigned* forcedSize; + + vector<ElementType> vec; + + TransferArrayHelper (MonoObject* instance, MonoClassField* field, MonoClass* klass, unsigned* forcedSize) + : array (0), + length (0), + elementSize (0), + forcedSize (forcedSize), + instance (instance), + field (field), + klass (klass) + { + } + + void SetupWriting () + { + mono_field_get_value (instance, field, &array); + if (array) + { + length = forcedSize != NULL ? *forcedSize : mono_array_length(array); + AssertIf(forcedSize && *forcedSize > mono_array_length(array)); + elementSize = mono_class_array_element_size (klass); + } + vec.resize (length); + } + + void SetupReading () + { + if (array == NULL || length != vec.size()) + array = mono_array_new (mono_domain_get (), klass, vec.size ()); + elementSize = mono_class_array_element_size (klass); + } + + void FinishReading () + { + mono_field_set_value (instance, field, array); + if (forcedSize != NULL) + *forcedSize = vec.size(); + } + + template<typename T> + T& Get (int index) + { + return Scripting::GetScriptingArrayElement<T> (array, index); + } + + template<typename T> + void Put (int index, const T& value) + { + Scripting::SetScriptingArrayElement (array, index, value); + } + }; +} + + +/// TODO: Optimize away where typestring is not required +template<class TransferFunction> inline +void TransferArrayPPtr (MonoObject* instance, MonoClassField* field, const char* name, MonoClass* klass, int classID, TransferFunction& transfer, unsigned* forcedSize, TransferMetaFlags metaFlags) +{ + TransferArrayHelper<MonoPPtr> helper (instance, field, klass, forcedSize); + + // Potential buffer overflow + char buffer[128]; + MonoPPtr proxy; + proxy.m_Buffer = buffer; + proxy.m_Class = klass; + + if (transfer.IsWritingPPtr()) + { + helper.SetupWriting (); + + for (int i=0;i<helper.length;i++) + { + MonoObject* mono = helper.Get<MonoObject*> (i); + if (mono) + helper.vec[i].SetInstanceID(Scripting::GetInstanceIDFromScriptingWrapper(mono)); + } + } + + TransferWithProxyElement (transfer, helper.vec, proxy, name, metaFlags); + + if (transfer.DidReadLastPPtrProperty ()) + { + helper.SetupReading (); + + const int count = helper.vec.size(); + for (int i=0;i<count;i++) + { + helper.Put (i, TransferPPtrToMonoObject(helper.vec[i].GetInstanceID(), klass, classID, field, instance, transfer.GetFlags() & kThreadedSerialization)); + } + + helper.FinishReading (); + } +} + + + +template<class TransferFunction> inline +void TransferArrayString (MonoObject* instance, MonoClassField* field, const char* name, MonoClass* klass, TransferFunction& transfer, unsigned* forcedSize, TransferMetaFlags metaFlags) +{ + TransferArrayHelper<UnityStr> helper (instance, field, klass, forcedSize); + + if (transfer.IsWriting()) + { + helper.SetupWriting (); + + for (int i=0;i<helper.length;i++) + { + char *p = mono_string_to_utf8 (helper.Get<MonoString*> (i)); + if (p) + helper.vec[i] = p; + else + helper.vec[i].clear (); + g_free (p); + } + } + + transfer.Transfer (helper.vec, name, metaFlags); + + if (transfer.DidReadLastProperty ()) + { + helper.SetupReading (); + + const int count = helper.vec.size(); + for (int i=0;i<count;i++) + { + helper.Put (i, MonoStringNew (helper.vec[i])); + } + + helper.FinishReading (); + } +} + + +/// @TODO: THIS CAN BE OPTIMIZED FOR BOTH SPACE AND SPEED. DONT CONVERT TO STL vectors. + +template<class T, class TransferFunction> inline +void TransferArrayBuiltins (MonoObject* instance, MonoClassField* field, const char* name, MonoClass* klass, TransferFunction& transfer, unsigned* forcedSize, TransferMetaFlags metaFlags) +{ + TransferArrayHelper<T> helper (instance, field, klass, forcedSize); + + if (transfer.IsWriting()) + { + helper.SetupWriting (); + + for (int i=0;i<helper.length;i++) + { + helper.vec[i] = helper.template Get<T> (i); + } + } + + transfer.Transfer (helper.vec, name, metaFlags); + + if (transfer.IsReading()) + { + helper.SetupReading (); + + const int count = helper.vec.size(); + for (int i=0;i<count;i++) + { + helper.Put (i, helper.vec[i]); + } + + helper.FinishReading (); + } +} + +template<typename T, class TransferFunction> inline +void TransferArrayOfT (MonoObject* instance, MonoClassField* field, const char* name, MonoClass* klass, TransferFunction& transfer, unsigned* forcedSize, TransferMetaFlags metaFlags) +{ + TransferArrayHelper<T> helper (instance, field, klass, forcedSize); + + if (transfer.IsWriting() || transfer.IsWritingPPtr()) + { + helper.SetupWriting (); + + for (int i=0;i<helper.length;i++) + { + MonoObject* element = helper.template Get<MonoObject*> (i); + if (element != NULL) + helper.vec[i] = *ExtractMonoObjectData<T*> (element); + } + } + + transfer.Transfer (helper.vec, name, metaFlags); + + if (transfer.DidReadLastProperty () || transfer.DidReadLastPPtrProperty ()) + { + helper.SetupReading (); + + const int count = helper.vec.size(); + for (int i=0;i<count;i++) + { + MonoObject*& element = helper.template Get<MonoObject*> (i); + + // Make sure the element is allocated + if (element == NULL) + { + element = ScriptingInstantiateObject (klass); + mono_runtime_object_init_log_exception (element); + } + + // Replace the contents of the element + T* monoElementRepresentation = ExtractMonoObjectData<T*> (element); + *monoElementRepresentation = helper.vec[i]; + } + + helper.FinishReading (); + } +} + + + + +struct TransferArrayScriptInstance +{ + MonoArray* array; + MonoClass* elementClass; + const CommonScriptingClasses* commonClasses; + MonoClassField* field; + MonoObject* instanceObject; + unsigned* forcedSize; + int depthCounter; + + TransferArrayScriptInstance() + { + depthCounter = -9999; + } + + typedef TransferScriptInstance value_type; + + struct iterator + { + MonoArray* array; + int index; + TransferScriptInstance transfer_instance; + + friend bool operator != (const iterator& lhs, const iterator& rhs) { return lhs.index != rhs.index; } + void operator++() { index++; } + + TransferScriptInstance& operator * () + { + MonoObject* instance = GetMonoArrayElement<MonoObject*> (array, index); + + if (instance == NULL) + { + instance = ScriptingInstantiateObject (transfer_instance.klass); + mono_runtime_object_init_log_exception (instance); + Scripting::SetScriptingArrayElement<MonoObject*> (array, index, instance); + } + + transfer_instance.instance = instance; + + return transfer_instance; + } + }; + + inline int size () const + { + AssertIf (forcedSize != NULL && *forcedSize > mono_array_length_safe(array)); + return forcedSize != NULL ? *forcedSize : mono_array_length_safe(array); + } + + inline void ResizeGenericList (size_t size) + { + if (forcedSize != NULL) + *forcedSize = size; + } + + inline bool IsGenericList () const + { + return forcedSize != NULL; + } + + iterator begin () + { + iterator i; + i.array = array; + i.index = 0; + i.transfer_instance.klass = elementClass; + i.transfer_instance.commonClasses = commonClasses; + i.transfer_instance.transferPrivate = CalculateTransferPrivateVariables(elementClass); + i.transfer_instance.depthCounter = depthCounter; + return i; + } + + iterator end () + { + iterator i; + i.index = size(); + return i; + } +}; + + +template<> +class SerializeTraits<TransferArrayScriptInstance> : public SerializeTraitsBase<TransferArrayScriptInstance> +{ +public: + + typedef TransferArrayScriptInstance value_type ; + inline static const char* GetTypeString (void *ptr = NULL) { return "Array"; } \ + inline static bool IsAnimationChannel () { return false; } \ + inline static bool MightContainPPtr () { return true; } \ + inline static bool AllowTransferOptimization () { return false; } + +#if SUPPORT_SERIALIZED_TYPETREES + static void Transfer (value_type& data, SafeBinaryRead& transfer) { transfer.TransferSTLStyleArray (data); } + static void Transfer (value_type& data, StreamedBinaryRead<true>& transfer) { transfer.TransferSTLStyleArray (data); } + static void Transfer (value_type& data, StreamedBinaryWrite<true>& transfer) { transfer.TransferSTLStyleArray (data); } +#endif + static void Transfer (value_type& data, StreamedBinaryRead<false>& transfer) { transfer.TransferSTLStyleArray (data); } + static void Transfer (value_type& data, StreamedBinaryWrite<false>& transfer) { transfer.TransferSTLStyleArray (data); } + static void Transfer (value_type& data, RemapPPtrTransfer& transfer) { transfer.TransferSTLStyleArray (data); } +#if SUPPORT_TEXT_SERIALIZATION + static void Transfer (value_type& data, YAMLRead& transfer) { transfer.TransferSTLStyleArray (data); } + static void Transfer (value_type& data, YAMLWrite& transfer) { transfer.TransferSTLStyleArray (data); } +#endif + + //template + static void Transfer (value_type& data, ProxyTransfer& transfer) + { + TransferScriptInstance instance; + instance.instance = NULL; + instance.klass = data.elementClass; + instance.commonClasses = data.commonClasses; + instance.transferPrivate = CalculateTransferPrivateVariables(instance.klass); + instance.depthCounter = data.depthCounter; + transfer.TransferSTLStyleArrayWithElement(instance, kNoTransferFlags); + } + + static bool IsContinousMemoryArray () { return false; } + + static void ResizeSTLStyleArray (value_type& data, int rs) + { + bool needsResize; + // When we deal an List<> we only need to reserve enough memory + // We also should reallocate if we are decreasing the actual array size because in that case otherwise GC memory + // that is in the unused part of the array must be cleared for which we currently have no simple function. + if (data.IsGenericList ()) + needsResize = data.array == NULL || rs > mono_array_length_safe(data.array) || rs < data.size (); + // When we deal with an array we must ensure the array size matches exactly + else + needsResize = data.array == NULL || rs != mono_array_length_safe(data.array); + + // Create the array data + if( needsResize ) + { + MonoArray* array = mono_array_new (mono_domain_get (), data.elementClass, rs); + mono_field_set_value (data.instanceObject, data.field, array); + data.array = array; + } + + // Synchronize List<> data to + data.ResizeGenericList(rs); + } +}; + + +static bool PrepareTransferEmbeddedClassCommonChecks (MonoClass* referencedClass) +{ + int flags = mono_class_get_flags(referencedClass); + if ((flags & TYPE_ATTRIBUTE_SERIALIZABLE) == 0) + return false; + + if (mono_unity_class_is_abstract (referencedClass) || mono_unity_class_is_interface (referencedClass)) + return false; + + MonoImage* image = mono_class_get_image(referencedClass); + if (image == mono_get_corlib()) + return false; + else if (GetMonoManager().GetAssemblyIndexFromImage(image) == -1) + return false; + + return true; +} +/// Returns only true on serializable classes. +/// But only if we are in the same assembly. +static bool PrepareTransferEmbeddedArrayClass (MonoClassField* field, MonoClass* referencedClass, MonoObject* instance, unsigned* forcedSize, TransferArrayScriptInstance& output, int currentDepth) +{ + if (!PrepareTransferEmbeddedClassCommonChecks(referencedClass)) + return false; + + MonoArray* referencedObject = NULL; + if (instance) + mono_field_get_value (instance, field, &referencedObject); + + output.array = referencedObject; + output.elementClass = referencedClass; + output.commonClasses = &GetMonoManager().GetCommonClasses(); + output.field = field; + output.instanceObject = instance; + output.forcedSize = forcedSize; + output.depthCounter = currentDepth + 1; + Assert(currentDepth >= 0); + + return true; +} + +void ApplyScriptDataModified (TransferScriptInstance& info) +{ + + if (info.klass != info.commonClasses->guiStyle) + return; + + if (!info.instance) + return; + + //this needs to be kept in sinc with SerializedStateReader.as for flash. + ScriptingInvocation invocation("UnityEngine", "GUIStyle", "Apply"); + invocation.object = info.instance; + invocation.Invoke(); +} + + + +struct GenericListData +{ + MonoArray* array; + unsigned size; + int version; +}; + +bool PrepareArrayTransfer(int type, MonoType* monoType, MonoClassField*& field, MonoObject*& instance, MonoClass*& elementClass, int& elementClassType, unsigned*& forcedSize) +{ + if (type != MONO_TYPE_GENERICINST) + { + elementClass = mono_type_get_class_or_element_class(monoType); + elementClassType = mono_type_get_type(mono_class_get_type (elementClass)); + return true; + } + else + { + MonoClassField* arrayField = GetMonoArrayFieldFromList(type, monoType, field); + if (arrayField == NULL) + return false; + + // Grab the current list class if it doesn't exist, create it + MonoObject* list = NULL; + MonoClass *klass = mono_class_from_mono_type (monoType); + + if (instance) + { + mono_field_get_value (instance, field, &list); + if (list == NULL) + { + list = ScriptingInstantiateObject (klass); + mono_runtime_object_init_log_exception (list); + } + } + else + { + // Mono seems to crash if we dont create an instance of the object prior to accessing the fields + ScriptingInstantiateObject (klass); + } + if (list) + forcedSize = &ExtractMonoObjectData<GenericListData>(list).size; + + MonoType* arrayType = mono_field_get_type(arrayField); + + instance = list; + field = arrayField; + elementClass = mono_type_get_class_or_element_class(arrayType); + elementClassType = mono_type_get_type(mono_class_get_type (elementClass)); + return true; + } +} + + +template<class TransferFunction> +void TransferFieldOfTypeArray(ScriptingObjectPtr instance, MonoClass* klass, MonoClassField* field, const char* name, TransferScriptInstance& info, TransferFunction& transfer, MonoType* monoType, int type, TransferMetaFlags metaFlags) +{ + MonoClass* elementClass; + int elementClassType; + MonoObject* tempInstance = instance; + MonoClassField* arrayField = field; + const CommonScriptingClasses& commonClasses = *info.commonClasses; + unsigned* forcedSize = NULL; + if (!PrepareArrayTransfer(type, monoType, arrayField, tempInstance, elementClass, elementClassType, forcedSize)) + return; + + if (elementClassType == MONO_TYPE_I4) + TransferArrayBuiltins<int> (tempInstance, arrayField, name, elementClass, transfer, forcedSize, metaFlags); + else if (elementClassType == MONO_TYPE_R4) + TransferArrayBuiltins<float> (tempInstance, arrayField, name, elementClass, transfer, forcedSize, metaFlags); + else if (elementClassType == MONO_TYPE_U1) + TransferArrayBuiltins<UInt8> (tempInstance, arrayField, name, elementClass, transfer, forcedSize, metaFlags); + else if (elementClassType == MONO_TYPE_STRING) + TransferArrayString (tempInstance, arrayField, name, elementClass, transfer, forcedSize, metaFlags); + else if (elementClass == commonClasses.vector3) + TransferArrayBuiltins<Vector3f> (tempInstance, arrayField, name, elementClass, transfer, forcedSize, metaFlags); + else if (elementClass == commonClasses.vector2) + TransferArrayBuiltins<Vector2f> (tempInstance, arrayField, name, elementClass, transfer, forcedSize, metaFlags); + else if (elementClass == commonClasses.vector4) + TransferArrayBuiltins<Vector4f> (tempInstance, arrayField, name, elementClass, transfer, forcedSize, metaFlags); + else if (elementClass == commonClasses.quaternion) + TransferArrayBuiltins<Quaternionf> (tempInstance, arrayField, name, elementClass, transfer, forcedSize, metaFlags); + else if (elementClass == commonClasses.matrix4x4) + TransferArrayBuiltins<Matrix4x4f> (tempInstance, arrayField, name, elementClass, transfer, forcedSize, metaFlags); + else if (elementClass == commonClasses.color) + TransferArrayBuiltins<ColorRGBAf> (tempInstance, arrayField, name, elementClass, transfer, forcedSize, metaFlags); + else if (elementClass == commonClasses.color32) + TransferArrayBuiltins<ColorRGBA32> (tempInstance, arrayField, name, elementClass, transfer, forcedSize, metaFlags); + else if (elementClass == commonClasses.rect) + TransferArrayBuiltins<Rectf> (tempInstance, arrayField, name, elementClass, transfer, forcedSize, metaFlags); + else if (elementClass == commonClasses.layerMask) + TransferArrayBuiltins<BitField> (tempInstance, arrayField, name, elementClass, transfer, forcedSize, metaFlags); + else if (elementClassType == MONO_TYPE_BOOLEAN) + TransferArrayBuiltins<UInt8> (tempInstance, arrayField, name, elementClass, transfer, forcedSize, metaFlags | kEditorDisplaysCheckBoxMask); + else if (elementClass == commonClasses.rectOffset) + TransferArrayOfT<RectOffset> (tempInstance, arrayField, name, elementClass, transfer, forcedSize, metaFlags); + else if (elementClass == commonClasses.guiStyle) + TransferArrayOfT<GUIStyle> (tempInstance, arrayField, name, elementClass, transfer, forcedSize, metaFlags); + else if (mono_class_is_enum (elementClass)) + { + MonoType* enumMonoType = mono_class_enum_basetype (elementClass); + int enumType = mono_type_get_type (enumMonoType); + switch (enumType) + { + case MONO_TYPE_I4: + TransferArrayBuiltins<int> (tempInstance, arrayField, name, elementClass, transfer, forcedSize, metaFlags); + break; + case MONO_TYPE_U1: + TransferArrayBuiltins<UInt8> (tempInstance, arrayField, name, elementClass, transfer, forcedSize, metaFlags); + break; + default: + ErrorString (ErrorMessageForUnsupportedEnumField(mono_class_get_type(elementClass), mono_class_get_type(klass), name)); + return; + } + } + else + { + int elementClassID = Scripting::GetClassIDFromScriptingClass (elementClass); + + if (elementClassID != -1) + TransferArrayPPtr (tempInstance, arrayField, name, elementClass, elementClassID, transfer, forcedSize, metaFlags); + else + { + if (elementClassType != MONO_TYPE_CLASS) + return; + + // Make sure we don't endless loop. Put a hard cap limit on the level of nested types until we have a better general solution. + if (info.depthCounter > kClassSerializationDepthLimit) + return; + + TransferArrayScriptInstance transferArrayInstance; + if (PrepareTransferEmbeddedArrayClass (arrayField, elementClass, tempInstance, forcedSize, transferArrayInstance, info.depthCounter)) + { + transfer.TransferWithTypeString(transferArrayInstance, name, mono_class_get_name(elementClass), metaFlags); + } + else + { + return; + } + } + } + + // Assign List<> to member variable. + // Need to do it after serialization because only here we know if we can actually serialize the List<> + if (field != arrayField && instance) + mono_field_set_value(instance, field, tempInstance); +} +#endif diff --git a/Runtime/Mono/MonoBehaviourSerialization_ByCodeGeneration.cpp b/Runtime/Mono/MonoBehaviourSerialization_ByCodeGeneration.cpp new file mode 100644 index 0000000..c589319 --- /dev/null +++ b/Runtime/Mono/MonoBehaviourSerialization_ByCodeGeneration.cpp @@ -0,0 +1,269 @@ +#include "UnityPrefix.h" + +#include "MonoBehaviour.h" +#include "Runtime/Serialize/TransferFunctions/SerializeTransfer.h" +#include "Runtime/Serialize/FileCache.h" +#include "Runtime/Serialize/IterateTypeTree.h" +#include "MonoScript.h" +#include "MonoManager.h" +#include "Runtime/Mono/MonoBehaviourSerialization.h" +#include "Runtime/Scripting/ScriptingUtility.h" +#include "Runtime/Scripting/CommonScriptingClasses.h" +#include "Runtime/Scripting/Backend/ScriptingTypeRegistry.h" +#include <stack> + +#include "MonoBehaviourSerialization_ByCodeGeneration.h" + +#if ENABLE_SCRIPTING + +static StreamedBinaryRead<false>* currentTransferRead; +static CachedReader* currentCachedReader; +static std::stack< StreamedBinaryRead<false>* > currentTranferReadStack; +static StreamedBinaryWrite<false>* currentTransferWrite; +static RemapPPtrTransfer* currentRemapPPTRTransfer; + +// ToDo: On windows standalone, we're getting : +//(Filename: E:/Projects/win8-new-serialization/Runtime/ExportGenerated/StandalonePlayer/UnityEngineDebug.cpp Line: 54) +// +//ptr == NULL || GET_CURRENT_ALLOC_OWNER() == GET_DEFAULT_OWNER() || GET_CURRENT_ALLOC_OWNER() == s_MonoDomainContainer + +int SERIALIZATION_SCRIPT_CALL_CONVENTION GetCurrentSerializationStreamPosition() +{ + return (int)currentCachedReader->GetAbsoluteMemoryPosition(); +} + +SCRIPTINGOBJECTWRAPPER SERIALIZATION_SCRIPT_CALL_CONVENTION NativeExt_MonoBehaviourSerialization_GetNewInstanceToReplaceOldInstance(int oldInstanceID) +{ + SInt32 newInstanceID = currentRemapPPTRTransfer->GetNewInstanceIDforOldInstanceID(oldInstanceID); + return Scripting::GetScriptingWrapperForInstanceID(newInstanceID); +} + +#if ENABLE_SERIALIZATION_BY_CODEGENERATION + +template<> +void MonoBehaviour::TransferWithInstance<StreamedBinaryWrite<false> >(StreamedBinaryWrite<false>& transfer) +{ + AssertIf (GetInstance() == SCRIPTING_NULL); + + //TODO: find a way to serialize a monobehaviour that is both fast and does not have a fixed buffersize. + currentTransferWrite = &transfer; +#if UNITY_WINRT + static ScriptingMethodPtr method = GetScriptingMethodRegistry().GetMethod(GetScriptingTypeRegistry().GetType("UnityEngine", "IUnitySerializable"), "Unity_Serialize"); +#else + // Don't know why above code doesn't work on Mono, says something about invalid header, but this code works + ScriptingMethodPtr method = GetScriptingMethodRegistry().GetMethod(GetClass(), "Serialize"); +#endif + ScriptingInvocationNoArgs invoke(method); + invoke.object = GetInstance(); + invoke.Invoke(); +} + +template<> +void MonoBehaviour::TransferWithInstance<StreamedBinaryRead<false> >(StreamedBinaryRead<false> & transfer) +{ + AssertIf (GetInstance() == SCRIPTING_NULL); + + currentTranferReadStack.push(&transfer); + + currentTransferRead = currentTranferReadStack.top(); + if(currentTransferRead != NULL){ + currentCachedReader = ¤tTransferRead->GetCachedReader(); + } + +#if UNITY_WINRT + static ScriptingMethodPtr method = GetScriptingMethodRegistry().GetMethod(GetScriptingTypeRegistry().GetType("UnityEngine", "IUnitySerializable"), "Unity_Deserialize"); +#else + // Don't know why above code doesn't work on Mono, says something about invalid header, but this code works + ScriptingMethodPtr method = GetScriptingMethodRegistry().GetMethod(GetClass(), "Deserialize"); +#endif + ScriptingInvocationNoArgs invoke(method); + invoke.object = GetInstance(); + invoke.Invoke(); + + Assert(currentTransferRead == &transfer); + currentTranferReadStack.pop(); + + if (!currentTranferReadStack.empty()){ + currentTransferRead = currentTranferReadStack.top(); + currentCachedReader = ¤tTransferRead->GetCachedReader(); + } +} + +template<> +void MonoBehaviour::TransferWithInstance<RemapPPtrTransfer>(RemapPPtrTransfer& transfer) +{ + AssertIf (GetInstance() == SCRIPTING_NULL ); + + currentRemapPPTRTransfer = &transfer; +#if UNITY_WINRT + static ScriptingMethodPtr method = GetScriptingMethodRegistry().GetMethod(GetScriptingTypeRegistry().GetType("UnityEngine", "IUnitySerializable"), "Unity_RemapPPtrs"); +#else + // Don't know why above code doesn't work on Mono, says something about invalid header, but this code works + ScriptingMethodPtr method = GetScriptingMethodRegistry().GetMethod(GetClass(), "RemapPPtrs"); +#endif + ScriptingInvocationNoArgs invoke(method); + invoke.object = GetInstance(); + invoke.Invoke(); +} + +#if ENABLE_SERIALIZATION_BY_CODEGENERATION +void MonoBehaviour::DoLivenessCheck(RemapPPtrTransfer& transfer) +{ + if (GetInstance() == SCRIPTING_NULL) return; + + currentRemapPPTRTransfer = &transfer; + + static ScriptingMethodPtr method = GetScriptingMethodRegistry().GetMethod(GetScriptingTypeRegistry().GetType("UnityEngine", "IUnityAssetsReferenceHolder"), "Unity_LivenessCheck"); + + ScriptingInvocationNoArgs invoke(method); + invoke.object = GetInstance(); + invoke.Invoke(); +} +#endif + +#endif + +unsigned char SERIALIZATION_SCRIPT_CALL_CONVENTION NativeExt_MonoBehaviourSerialization_ReadByte() +{ + unsigned char retVal = 0; + currentCachedReader->Read(&retVal,1); + return retVal; +} + +char SERIALIZATION_SCRIPT_CALL_CONVENTION NativeExt_MonoBehaviourSerialization_ReadBool() +{ + return NativeExt_MonoBehaviourSerialization_ReadByte(); +} + +int SERIALIZATION_SCRIPT_CALL_CONVENTION NativeExt_MonoBehaviourSerialization_ReadInt() +{ + int retVal = 0; + currentCachedReader->Read(&retVal,4); + return retVal; +} + +float SERIALIZATION_SCRIPT_CALL_CONVENTION NativeExt_MonoBehaviourSerialization_ReadFloat() +{ + float retVal = 0; + currentCachedReader->Read(&retVal,4); + return retVal; +} + +double SERIALIZATION_SCRIPT_CALL_CONVENTION NativeExt_MonoBehaviourSerialization_ReadDouble() +{ + double retVal = 0; + currentCachedReader->Read(&retVal,8); + return retVal; +} +SCRIPT_BINDINGS_EXPORT_DECL void SERIALIZATION_SCRIPT_CALL_CONVENTION NativeExt_MonoBehaviourSerialization_ReadBuffer(int ptr, int size) +{ + currentCachedReader->Read((void*)ptr, size); +} +ScriptingStringPtr SERIALIZATION_SCRIPT_CALL_CONVENTION NativeExt_MonoBehaviourSerialization_ReadString() +{ + UnityStr stdString; + currentTransferRead->Transfer(stdString, "does_not_matter", kNoTransferFlags); + return scripting_string_new(stdString); +} + +SCRIPTINGOBJECTWRAPPER SERIALIZATION_SCRIPT_CALL_CONVENTION NativeExt_MonoBehaviourSerialization_ReadUnityEngineObject() +{ + PPtr<Object> pptr; + currentTransferRead->Transfer (pptr, "does_not_matter", kNoTransferFlags); + return TransferPPtrToMonoObjectUnChecked(pptr.GetInstanceID(), currentTransferRead->GetFlags() & kThreadedSerialization); +} + +void SERIALIZATION_SCRIPT_CALL_CONVENTION NativeExt_MonoBehaviourSerialization_ReadGUIStyle(GUIStyle* style) +{ + currentTransferRead->Transfer(*style,"does_not_matter",kNoTransferFlags); +} + +void SERIALIZATION_SCRIPT_CALL_CONVENTION NativeExt_MonoBehaviourSerialization_ReadGradient(GradientNEW* gradient) +{ + currentTransferRead->Transfer(*gradient,"does_not_matter",kNoTransferFlags); +} + +void SERIALIZATION_SCRIPT_CALL_CONVENTION NativeExt_MonoBehaviourSerialization_ReadAnimationCurve(AnimationCurve* animation_curve) +{ + currentTransferRead->Transfer(*animation_curve,"does_not_matter",kNoTransferFlags); +} + +void SERIALIZATION_SCRIPT_CALL_CONVENTION NativeExt_MonoBehaviourSerialization_ReadRectOffset(RectOffset* offset) +{ + currentTransferRead->Transfer(*offset,"does_not_matter",kNoTransferFlags); +} + +void SERIALIZATION_SCRIPT_CALL_CONVENTION NativeExt_MonoBehaviourSerialization_ReaderAlign() +{ + currentCachedReader->Align4Read(); +} + +void SERIALIZATION_SCRIPT_CALL_CONVENTION NativeExt_MonoBehaviourSerialization_WriterAlign() +{ + currentTransferWrite->GetCachedWriter().Align4Write(); +} + +void SERIALIZATION_SCRIPT_CALL_CONVENTION NativeExt_MonoBehaviourSerialization_WriteByte(unsigned char value) +{ + currentTransferWrite->GetCachedWriter().Write(value); +} + +void SERIALIZATION_SCRIPT_CALL_CONVENTION NativeExt_MonoBehaviourSerialization_WriteBool(int value) +{ + currentTransferWrite->GetCachedWriter().Write((char)(value ? 1 : 0)); +} + +void SERIALIZATION_SCRIPT_CALL_CONVENTION NativeExt_MonoBehaviourSerialization_WriteInt(int value) +{ + currentTransferWrite->GetCachedWriter().Write(value); +} + +void SERIALIZATION_SCRIPT_CALL_CONVENTION NativeExt_MonoBehaviourSerialization_WriteFloat(float value) +{ + currentTransferWrite->GetCachedWriter().Write(value); +} + +void SERIALIZATION_SCRIPT_CALL_CONVENTION NativeExt_MonoBehaviourSerialization_WriteDouble(double value) +{ + currentTransferWrite->GetCachedWriter().Write(value); +} + +void SERIALIZATION_SCRIPT_CALL_CONVENTION NativeExt_MonoBehaviourSerialization_WriteBuffer(int ptr, int size) +{ + currentTransferWrite->GetCachedWriter().Write((void*)ptr, size); +} + +void SERIALIZATION_SCRIPT_CALL_CONVENTION NativeExt_MonoBehaviourSerialization_WriteString(char* value, int size) +{ + // TODO: optimize this, probably creating a new UnityStr is not needed + UnityStr tmpStr(value, size); + currentTransferWrite->Transfer(tmpStr, "does_not_matter", kNoTransferFlags); +} + +void SERIALIZATION_SCRIPT_CALL_CONVENTION NativeExt_MonoBehaviourSerialization_WriteUnityEngineObject(int instance_id) +{ + PPtr<Object> pptr; + pptr.SetInstanceID(instance_id); + currentTransferWrite->Transfer(pptr, "does_not_matter", kNoTransferFlags); +} + +void SERIALIZATION_SCRIPT_CALL_CONVENTION NativeExt_MonoBehaviourSerialization_WriteGradient(GradientNEW* value) +{ + currentTransferWrite->Transfer(*value,"does_not_matter",kNoTransferFlags); +} + +void SERIALIZATION_SCRIPT_CALL_CONVENTION NativeExt_MonoBehaviourSerialization_WriteAnimationCurve(AnimationCurve* value) +{ + currentTransferWrite->Transfer(*value,"does_not_matter",kNoTransferFlags); +} + +void SERIALIZATION_SCRIPT_CALL_CONVENTION NativeExt_MonoBehaviourSerialization_WriteGUIStyle(GUIStyle* value) +{ + currentTransferWrite->Transfer(*value,"does_not_matter",kNoTransferFlags); +} + +void SERIALIZATION_SCRIPT_CALL_CONVENTION NativeExt_MonoBehaviourSerialization_WriteRectOffset(RectOffset* value) +{ + currentTransferWrite->Transfer(*value,"does_not_matter",kNoTransferFlags); +} +#endif
\ No newline at end of file diff --git a/Runtime/Mono/MonoBehaviourSerialization_ByCodeGeneration.h b/Runtime/Mono/MonoBehaviourSerialization_ByCodeGeneration.h new file mode 100644 index 0000000..a4c58cb --- /dev/null +++ b/Runtime/Mono/MonoBehaviourSerialization_ByCodeGeneration.h @@ -0,0 +1,58 @@ +#pragma once + +#include "UnityPrefix.h" + +#include "MonoBehaviour.h" +#include "MonoScript.h" +#include "MonoManager.h" +#include "Runtime/Scripting/ScriptingUtility.h" +#include "Runtime/Scripting/CommonScriptingClasses.h" +#include "Runtime/Math/AnimationCurve.h" +#include "Runtime/Math/Gradient.h" +#include "Runtime/IMGUI/GUIStyle.h" + +#if UNITY_WINRT +#define SCRIPTINGOBJECTWRAPPER long long +#define SERIALIZATION_SCRIPT_CALL_CONVENTION WINAPI +#else +#define SCRIPTINGOBJECTWRAPPER ScriptingObjectPtr +#define SERIALIZATION_SCRIPT_CALL_CONVENTION +#endif + + +#if ENABLE_SERIALIZATION_BY_CODEGENERATION + +SCRIPT_BINDINGS_EXPORT_DECL int SERIALIZATION_SCRIPT_CALL_CONVENTION GetCurrentSerializationStreamPosition(); +SCRIPT_BINDINGS_EXPORT_DECL SCRIPTINGOBJECTWRAPPER SERIALIZATION_SCRIPT_CALL_CONVENTION NativeExt_MonoBehaviourSerialization_GetNewInstanceToReplaceOldInstance(int oldInstanceID); + +// Reader +SCRIPT_BINDINGS_EXPORT_DECL void SERIALIZATION_SCRIPT_CALL_CONVENTION NativeExt_MonoBehaviourSerialization_ReaderAlign(); +SCRIPT_BINDINGS_EXPORT_DECL unsigned char SERIALIZATION_SCRIPT_CALL_CONVENTION NativeExt_MonoBehaviourSerialization_ReadByte(); +SCRIPT_BINDINGS_EXPORT_DECL char SERIALIZATION_SCRIPT_CALL_CONVENTION NativeExt_MonoBehaviourSerialization_ReadBool(); +SCRIPT_BINDINGS_EXPORT_DECL int SERIALIZATION_SCRIPT_CALL_CONVENTION NativeExt_MonoBehaviourSerialization_ReadInt(); +SCRIPT_BINDINGS_EXPORT_DECL float SERIALIZATION_SCRIPT_CALL_CONVENTION NativeExt_MonoBehaviourSerialization_ReadFloat(); +SCRIPT_BINDINGS_EXPORT_DECL double SERIALIZATION_SCRIPT_CALL_CONVENTION NativeExt_MonoBehaviourSerialization_ReadDouble(); +SCRIPT_BINDINGS_EXPORT_DECL void SERIALIZATION_SCRIPT_CALL_CONVENTION NativeExt_MonoBehaviourSerialization_ReadBuffer(int ptr, int size); +SCRIPT_BINDINGS_EXPORT_DECL ScriptingStringPtr SERIALIZATION_SCRIPT_CALL_CONVENTION NativeExt_MonoBehaviourSerialization_ReadString(); +SCRIPT_BINDINGS_EXPORT_DECL SCRIPTINGOBJECTWRAPPER SERIALIZATION_SCRIPT_CALL_CONVENTION NativeExt_MonoBehaviourSerialization_ReadUnityEngineObject(); +SCRIPT_BINDINGS_EXPORT_DECL void SERIALIZATION_SCRIPT_CALL_CONVENTION NativeExt_MonoBehaviourSerialization_ReadGUIStyle(GUIStyle* style); +SCRIPT_BINDINGS_EXPORT_DECL void SERIALIZATION_SCRIPT_CALL_CONVENTION NativeExt_MonoBehaviourSerialization_ReadGradient(GradientNEW* gradient); +SCRIPT_BINDINGS_EXPORT_DECL void SERIALIZATION_SCRIPT_CALL_CONVENTION NativeExt_MonoBehaviourSerialization_ReadAnimationCurve(AnimationCurve* animation_curve); +SCRIPT_BINDINGS_EXPORT_DECL void SERIALIZATION_SCRIPT_CALL_CONVENTION NativeExt_MonoBehaviourSerialization_ReadRectOffset(RectOffset* offset); + +// Writer +SCRIPT_BINDINGS_EXPORT_DECL void SERIALIZATION_SCRIPT_CALL_CONVENTION NativeExt_MonoBehaviourSerialization_WriterAlign(); +SCRIPT_BINDINGS_EXPORT_DECL void SERIALIZATION_SCRIPT_CALL_CONVENTION NativeExt_MonoBehaviourSerialization_WriteByte(unsigned char value); +SCRIPT_BINDINGS_EXPORT_DECL void SERIALIZATION_SCRIPT_CALL_CONVENTION NativeExt_MonoBehaviourSerialization_WriteBool(int value); +SCRIPT_BINDINGS_EXPORT_DECL void SERIALIZATION_SCRIPT_CALL_CONVENTION NativeExt_MonoBehaviourSerialization_WriteInt(int value); +SCRIPT_BINDINGS_EXPORT_DECL void SERIALIZATION_SCRIPT_CALL_CONVENTION NativeExt_MonoBehaviourSerialization_WriteFloat(float value); +SCRIPT_BINDINGS_EXPORT_DECL void SERIALIZATION_SCRIPT_CALL_CONVENTION NativeExt_MonoBehaviourSerialization_WriteDouble(double value); +SCRIPT_BINDINGS_EXPORT_DECL void SERIALIZATION_SCRIPT_CALL_CONVENTION NativeExt_MonoBehaviourSerialization_WriteBuffer(int ptr, int size); +SCRIPT_BINDINGS_EXPORT_DECL void SERIALIZATION_SCRIPT_CALL_CONVENTION NativeExt_MonoBehaviourSerialization_WriteString(char* value, int size); +SCRIPT_BINDINGS_EXPORT_DECL void SERIALIZATION_SCRIPT_CALL_CONVENTION NativeExt_MonoBehaviourSerialization_WriteUnityEngineObject(int instance_id); +SCRIPT_BINDINGS_EXPORT_DECL void SERIALIZATION_SCRIPT_CALL_CONVENTION NativeExt_MonoBehaviourSerialization_WriteGUIStyle(GUIStyle* style); +SCRIPT_BINDINGS_EXPORT_DECL void SERIALIZATION_SCRIPT_CALL_CONVENTION NativeExt_MonoBehaviourSerialization_WriteGradient(GradientNEW* gradient); +SCRIPT_BINDINGS_EXPORT_DECL void SERIALIZATION_SCRIPT_CALL_CONVENTION NativeExt_MonoBehaviourSerialization_WriteAnimationCurve(AnimationCurve* animation_curve); +SCRIPT_BINDINGS_EXPORT_DECL void SERIALIZATION_SCRIPT_CALL_CONVENTION NativeExt_MonoBehaviourSerialization_WriteRectOffset(RectOffset* offset); + +#endif diff --git a/Runtime/Mono/MonoBehaviourSerialization_YamlBackup.cpp b/Runtime/Mono/MonoBehaviourSerialization_YamlBackup.cpp new file mode 100644 index 0000000..9ea4681 --- /dev/null +++ b/Runtime/Mono/MonoBehaviourSerialization_YamlBackup.cpp @@ -0,0 +1,201 @@ +#include "UnityPrefix.h" + +#if ENABLE_SCRIPTING +#include "MonoBehaviour.h" +#include "Runtime/Serialize/TransferFunctions/SerializeTransfer.h" +#include "Runtime/Serialize/SerializedFile.h" +#include "Runtime/Serialize/IterateTypeTree.h" +#include "MonoScript.h" +#include "MonoTypeSignatures.h" +#include "MonoManager.h" +#include "Runtime/Math/Quaternion.h" +#include "Runtime/Math/Color.h" +#include "Runtime/Math/Vector4.h" +#include "Runtime/Math/Vector2.h" +#include "Runtime/Math/Rect.h" +#include "Runtime/Scripting/ScriptingUtility.h" + +using namespace std; + +#if SUPPORT_TEXT_SERIALIZATION +struct ArrayAsString +{ + char const* name; + bool serializeAsString; +}; + +ArrayAsString g_ArrayAsString[] = +{ +#define YAML_DECLARE_WRITE_ARRAY_AS_STRING(x) \ +{ #x, YAMLSerializeTraits<x>::ShouldSerializeArrayAsCompactString () } + + YAML_DECLARE_WRITE_ARRAY_AS_STRING(int), + YAML_DECLARE_WRITE_ARRAY_AS_STRING(char), + YAML_DECLARE_WRITE_ARRAY_AS_STRING(SInt32), +}; + +static const size_t g_ArrayAsStringCount = sizeof (g_ArrayAsString) / sizeof (g_ArrayAsString[0]); + +bool ConvertArrayAsString (std::string const& typeName) +{ + for (int i=0; i<g_ArrayAsStringCount; ++i) + { + if (g_ArrayAsString[i].name == typeName) + return g_ArrayAsString[i].serializeAsString; + } + + return false; +} + +struct YAMLConverterContext +{ + dynamic_array<UInt8>& m_Data; + YAMLWrite& m_Writer; + + template<class T> + T* ExtractAndAdvance (int* bytePosition) + { + T* ptr = reinterpret_cast<T*> (&m_Data[*bytePosition]); + *bytePosition += sizeof (T); + return ptr; + } + + YAMLConverterContext (dynamic_array<UInt8>& data, YAMLWrite& writer) + : m_Data (data), m_Writer (writer) + {} + + void EmitBasicData (TypeTree const& typeTree, int* bytePosition) + { +#define HANDLE_COMPLEX_TYPE(T) \ +if (typeTree.m_Type == #T) { \ +T* data = ExtractAndAdvance<T> (bytePosition); \ +m_Writer.Transfer (*data, typeTree.m_Name.c_str()); \ +} else + +#define HANDLE_BASIC_TYPE(T) \ +if (typeTree.m_Type == #T) { \ +T data = *reinterpret_cast<T*> (&m_Data[*bytePosition]); \ +m_Writer.Transfer (data, typeTree.m_Name.c_str()); \ +} else + + HANDLE_BASIC_TYPE (int) + HANDLE_BASIC_TYPE (SInt32) + HANDLE_BASIC_TYPE (bool) + HANDLE_BASIC_TYPE (float) + HANDLE_BASIC_TYPE (double) + HANDLE_BASIC_TYPE (UInt8) + { + ErrorStringMsg ("Binary to YAML conversion: type %s is unsupported\n", typeTree.m_Type.c_str()); + } + } + + void ConvertDataToYAML (TypeTree const& typeTree) + { + int bytePosition = 0; + + for (TypeTree::TypeTreeList::const_iterator i = typeTree.m_Children.begin (); i != typeTree.m_Children.end ();++i) + { + ConvertDataToYAML (*i, &bytePosition); + } + } + + void ConvertDataToYAML (TypeTree const& typeTree, int* bytePosition) + { + if (typeTree.IsBasicDataType()) + { + // TypeTrees can contain $initialized_XXX$ member for Mono structs. We don't want to appear in YAML + bool skip = !typeTree.m_Name.empty () && typeTree.m_Name[0] == '$' && typeTree.m_Name[typeTree.m_Name.size ()-1] == '$'; + if (!skip) + EmitBasicData (typeTree, bytePosition); + } + else if (IsTypeTreePPtr (typeTree)) + { + m_Writer.PushMetaFlag (kTransferUsingFlowMappingStyle); + + m_Writer.BeginMetaGroup (typeTree.m_Name); + SInt32 instanceID = *reinterpret_cast<SInt32*> (&m_Data[*bytePosition]); + m_Writer.Transfer (instanceID, "instanceID"); + *bytePosition += typeTree.m_ByteSize; + + m_Writer.EndMetaGroup (); + m_Writer.PopMetaFlag (); + } + else if (typeTree.m_IsArray) + { + SInt32 arraySize = *ExtractAndAdvance<SInt32> (bytePosition); + Assert (typeTree.m_Children.size () == 2); + TypeTree::const_iterator sizeIt = typeTree.begin (); + TypeTree::const_iterator dataIt = sizeIt; ++dataIt; + TypeTree const& elementTypeTree = *dataIt; + + if (elementTypeTree.IsBasicDataType () && ConvertArrayAsString (elementTypeTree.m_Type)) + { + std::string str; + size_t elementSize = elementTypeTree.m_ByteSize; + size_t numBytes = arraySize * elementSize; + str.resize (numBytes*2); + + for (size_t i=0; i<arraySize; ++i, *bytePosition += elementSize) + { + void* dataPtr = &m_Data[*bytePosition]; + BytesToHexString (dataPtr, elementSize, &str[i*2*elementSize]); + } + + m_Writer.TransferStringData (str); + } + else + { + m_Writer.StartSequence (); + + for (size_t i = 0; i<arraySize; ++i) + ConvertDataToYAML (elementTypeTree, bytePosition); + } + } + else + { + HANDLE_COMPLEX_TYPE (Vector2f) + HANDLE_COMPLEX_TYPE (Vector3f) + HANDLE_COMPLEX_TYPE (Vector4f) + HANDLE_COMPLEX_TYPE (Quaternionf) + HANDLE_COMPLEX_TYPE (Matrix4x4f) + { + m_Writer.BeginMetaGroup (typeTree.m_Name); + + for (TypeTree::TypeTreeList::const_iterator i = typeTree.m_Children.begin (); i != typeTree.m_Children.end ();++i) + ConvertDataToYAML (*i, bytePosition); + + m_Writer.EndMetaGroup (); + } + } + + if (typeTree.IsBasicDataType ()) + *bytePosition += typeTree.m_ByteSize; + + if (typeTree.m_MetaFlag & kAlignBytesFlag) + *bytePosition = Align4_Iterate (*bytePosition); + } +}; + +YAMLNode* ConvertBackupToYAML (BackupState& binary) +{ + YAMLWrite writer (0); + YAMLConverterContext ctx(binary.state, writer); + + ctx.ConvertDataToYAML (binary.typeTree); + + yaml_document_t* yaml = writer.GetDocument(); + YAMLNode* node = YAMLDocNodeToNode (yaml, yaml_document_get_root_node(yaml)); + +#if 0 + if (node) + { + std::string str = node->EmitYAMLString(); + printf_console ("CONVERTED BINARY TO YAML:\n%s<< END\n", str.c_str()); + } +#endif + + return node; +} +#endif // SUPPORT_TEXT_SERIALIZATION + +#endif // ENABLE_SCRIPTING diff --git a/Runtime/Mono/MonoBehaviourSerialization_flash.cpp b/Runtime/Mono/MonoBehaviourSerialization_flash.cpp new file mode 100644 index 0000000..55910b9 --- /dev/null +++ b/Runtime/Mono/MonoBehaviourSerialization_flash.cpp @@ -0,0 +1,239 @@ +#include "UnityPrefix.h" + +#include "MonoBehaviour.h" +#include "Runtime/Math/AnimationCurve.h" +#include "Runtime/Math/Gradient.h" +#include "Runtime/Serialize/TransferFunctions/SerializeTransfer.h" +#include "Runtime/Serialize/FileCache.h" +#include "Runtime/Serialize/IterateTypeTree.h" +#include "MonoScript.h" +#include "MonoManager.h" +#include "Runtime/Mono/MonoBehaviourSerialization.h" +#include "Runtime/Scripting/ScriptingUtility.h" +#include "Runtime/Scripting/CommonScriptingClasses.h" +#include "Runtime/IMGUI/GUIStyle.h" +#include <stack> + +#if UNITY_FLASH + +#define FLASH_SERIALIZATION_STRING_BUFFERSIZE 4*1024 + +static StreamedBinaryRead<false>* currentTransferRead; +static CachedReader* currentCachedReader; +static std::stack< StreamedBinaryRead<false>* > currentTranferReadStack; +static StreamedBinaryWrite<false>* currentTransferWrite; +static RemapPPtrTransfer* currentRemapPPTRTransfer; + +static unsigned char temporaryStringBuffer[FLASH_SERIALIZATION_STRING_BUFFERSIZE]; + +//since there is so much code involved in reading a PPtr<T> from a serializedstream, and getting the ScriptingObject +//belonging to that, we do not do that deserialization in as3, but in c instead. as3 will call this function +//when it wants to deserialize something that enherits from UnityEngine.Object +extern "C" ScriptingObjectPtr ReadPPtrAndReturnScriptingWrapper() +{ + PPtr<Object> pptr; + currentTransferRead->Transfer (pptr, "does_not_matter", kNoTransferFlags); + return TransferPPtrToMonoObjectUnChecked(pptr.GetInstanceID(), currentTransferRead->GetFlags() & kThreadedSerialization); +} + +extern "C" GradientNEW* ReadGradientAndReturnPtr() +{ + GradientNEW* gradient = new GradientNEW(); + currentTransferRead->Transfer (*gradient, "does_not_matter", kNoTransferFlags); + return gradient; +} + +extern "C" AnimationCurve* ReadAnimationCurveAndReturnPtr() +{ + AnimationCurve* curve = new AnimationCurve(); + currentTransferRead->Transfer(*curve, "does_not_matter", kNoTransferFlags); + return curve; +} + +extern "C" int GetCurrentSerializationStreamPosition() +{ + return (int)currentCachedReader->GetAbsoluteMemoryPosition(); +} + +extern "C" void WriteGradient(GradientNEW* gradient) +{ + currentTransferWrite->Transfer(*gradient, "doesntmatter", kNoTransferFlags); +} + +extern "C" void WriteAnimationCurve(AnimationCurve* curve) +{ + currentTransferWrite->Transfer(*curve, "doesntmatter", kNoTransferFlags); +} + +extern "C" void WritePPtrWithInstanceID(int instanceid) +{ + PPtr<Object> pptr; + pptr.SetInstanceID(instanceid); + currentTransferWrite->Transfer(pptr, "doesntmatter", kNoTransferFlags); +} + +extern "C" ScriptingObjectPtr GetNewInstanceToReplaceOldInstance(int oldInstanceID) +{ + SInt32 newInstanceID = currentRemapPPTRTransfer->GetNewInstanceIDforOldInstanceID(oldInstanceID); + return Scripting::GetScriptingWrapperForInstanceID(newInstanceID); +} + +template<> +void MonoBehaviour::TransferWithInstance<StreamedBinaryWrite<false> > (StreamedBinaryWrite<false>& transfer) +{ + AssertIf (GetInstance() == NULL); + currentTransferWrite = &transfer; + Ext_SerializeMonoBehaviour(GetInstance()); +} + +template<> +void MonoBehaviour::TransferWithInstance<StreamedBinaryRead<false> > (StreamedBinaryRead<false> & transfer) +{ + AssertIf (GetInstance() == NULL); + + currentTranferReadStack.push(&transfer); + + currentTransferRead = currentTranferReadStack.top(); + if(currentTransferRead != NULL){ + currentCachedReader = ¤tTransferRead->GetCachedReader(); + } + + Ext_DeserializeMonoBehaviour(GetInstance()); + + Assert(currentTransferRead == &transfer); + currentTranferReadStack.pop(); + + if(!currentTranferReadStack.empty()){ + currentTransferRead = currentTranferReadStack.top(); + currentCachedReader = ¤tTransferRead->GetCachedReader(); + } +} + +template<> +void MonoBehaviour::TransferWithInstance<RemapPPtrTransfer> (RemapPPtrTransfer& transfer) +{ + AssertIf (GetInstance() == NULL); + currentRemapPPTRTransfer = &transfer; + Ext_RemapPPtrs(GetInstance()); +} + +extern "C" unsigned char NativeExt_MonoBehaviourSerialization_ReadByte() +{ + unsigned char retVal = 0; + currentCachedReader->Read(&retVal,1); + return retVal; +} + +extern "C" unsigned char NativeExt_MonoBehaviourSerialization_WriteByte(unsigned char value) +{ + currentTransferWrite->GetCachedWriter().Write(value); +} + +extern "C" char NativeExt_MonoBehaviourSerialization_ReadBool() +{ + return NativeExt_MonoBehaviourSerialization_ReadByte(); +} + +extern "C" char NativeExt_MonoBehaviourSerialization_WriteBool(bool value) +{ + currentTransferWrite->GetCachedWriter().Write((char)(value ? 1 : 0)); +} + +extern "C" int NativeExt_MonoBehaviourSerialization_ReadInt() +{ + int retVal = 0; + currentCachedReader->Read(&retVal,4); + return retVal; +} + +extern "C" char NativeExt_MonoBehaviourSerialization_WriteInt(int value) +{ + currentTransferWrite->GetCachedWriter().Write(value); +} + +extern "C" float NativeExt_MonoBehaviourSerialization_ReadFloat() +{ + float retVal = 0; + currentCachedReader->Read(&retVal,4); + return retVal; +} + +extern "C" char NativeExt_MonoBehaviourSerialization_WriteFloat(float value) +{ + currentTransferWrite->GetCachedWriter().Write(value); +} + +extern "C" double NativeExt_MonoBehaviourSerialization_ReadDouble() +{ + double retVal = 0; + currentCachedReader->Read(&retVal,8); + return retVal; +} + +extern "C" char NativeExt_MonoBehaviourSerialization_WriteDouble(double value) +{ + currentTransferWrite->GetCachedWriter().Write(value); +} + +extern "C" void NativeExt_MonoBehaviourSerialization_ReadString() +{ + int length = 0; + currentCachedReader->Read(&length, 4);//Length of the string + void* tempBuffer; + bool usesLocalBuffer; + usesLocalBuffer = !(length < FLASH_SERIALIZATION_STRING_BUFFERSIZE); + if(usesLocalBuffer) + tempBuffer = malloc(length); + else + tempBuffer = temporaryStringBuffer; + + + currentCachedReader->Read(tempBuffer,length); + + __asm __volatile__("var bpos:int = heap.position;"); + __asm __volatile__("heap.position = %0;"::"r"(tempBuffer)); + __asm __volatile__("string_g0 = heap.readUTFBytes(%0);"::"r"(length)); + __asm __volatile__("heap.position = bpos;"); + + if(usesLocalBuffer) + free(tempBuffer); + + currentCachedReader->Align4Read(); +} + +extern "C" void NativeExt_MonoBehaviourSerialization_WriteString(char* value, int size) +{ + UnityStr tmpStr(value, size); + currentTransferWrite->Transfer(tmpStr, "does_not_matter", kNoTransferFlags); +} + +extern "C" void NativeExt_MonoBehaviourSerialization_ReadGUIStyle(GUIStyle* style) +{ + currentTransferRead->Transfer(*style,"does_not_matter",kNoTransferFlags); +} + +extern "C" void NativeExt_MonoBehaviourSerialization_WriteGUIStyle(GUIStyle* style) +{ + currentTransferWrite->Transfer(*style,"does_not_matter",kNoTransferFlags); +} + +extern "C" void NativeExt_MonoBehaviourSerialization_ReadRectOffset(RectOffset* offset) +{ + currentTransferRead->Transfer(*offset,"does_not_matter",kNoTransferFlags); +} + +extern "C" void NativeExt_MonoBehaviourSerialization_WriteRectOffset(RectOffset* offset) +{ + currentTransferWrite->Transfer(*offset,"does_not_matter",kNoTransferFlags); +} + +extern "C" void NativeExt_MonoBehaviourSerialization_WriteAlign() +{ + currentTransferWrite->GetCachedWriter().Align4Write(); +} + +extern "C" void NativeExt_MonoBehaviourSerialization_ReadAlign() +{ + currentCachedReader->Align4Read(); +} +#endif diff --git a/Runtime/Mono/MonoBehaviourSerialization_metro.cpp b/Runtime/Mono/MonoBehaviourSerialization_metro.cpp new file mode 100644 index 0000000..c00026f --- /dev/null +++ b/Runtime/Mono/MonoBehaviourSerialization_metro.cpp @@ -0,0 +1,178 @@ +#include "UnityPrefix.h" + +#include "MonoBehaviour.h" +#include "Runtime/Math/AnimationCurve.h" +#include "Runtime/Serialize/TransferFunctions/SerializeTransfer.h" +#include "Runtime/Serialize/FileCache.h" +#include "Runtime/Serialize/IterateTypeTree.h" +#include "MonoScript.h" +#include "MonoManager.h" +#include "Runtime/Mono/MonoBehaviourSerialization.h" +#include "Runtime/Scripting/ScriptingUtility.h" +#include "Runtime/Scripting/CommonScriptingClasses.h" +#include "Runtime/IMGUI/GUIStyle.h" + +#if ENABLE_SCRIPTING && UNITY_WINRT && !ENABLE_SERIALIZATION_BY_CODEGENERATION +std::stack<StreamedBinaryWrite<false>*> currentTransferWrite; +std::stack<StreamedBinaryRead<false>*> currentTransferRead; +RemapPPtrTransfer* currentRemapper = NULL; + +static void SerializationReader_Read(int destination, int size, bool align) +{ + StreamedBinaryRead<false>* read = currentTransferRead.top(); + read->ReadDirect((void*)destination, size); + if (align) + read->Align(); +} +static long long SerializationReader_ReadPPtr() +{ + StreamedBinaryRead<false>* transfer = currentTransferRead.top(); + + PPtr<Object> pptr; + transfer->Transfer (pptr, "does_not_matter", kNoTransferFlags); + + return TransferPPtrToMonoObjectUnChecked(pptr.GetInstanceID(), transfer->GetFlags() & kThreadedSerialization); +} + + +void SerializationReader_ReadGUIStyle(int dstPtr) +{ + StreamedBinaryRead<false>* transfer = currentTransferRead.top(); + transfer->Transfer(*(GUIStyle*)dstPtr, "does_not_matter", kNoTransferFlags); +} +void SerializationReader_ReadRectOffset(int dstPtr) +{ + StreamedBinaryRead<false>* transfer = currentTransferRead.top(); + transfer->Transfer(*(RectOffset*)dstPtr, "does_not_matter", kNoTransferFlags); +} +int SerializationReader_ReadAnimationCurve() +{ + StreamedBinaryRead<false>* transfer = currentTransferRead.top(); + AnimationCurve* curve = new AnimationCurve(); + transfer->Transfer (*curve, "does_not_matter", kNoTransferFlags); + return (int)curve; +} + + +static void SetupReader() +{ + static BridgeInterface::ISerializationReader^ reader = nullptr; + if (reader == nullptr) + { + reader = s_WinRTBridge->CreateSerializationReader( + ref new BridgeInterface::SerializationReader_Read(SerializationReader_Read), + ref new BridgeInterface::SerializationReader_ReadPPtr(SerializationReader_ReadPPtr), + ref new BridgeInterface::SerializationReader_ReadStruct(SerializationReader_ReadGUIStyle), + ref new BridgeInterface::SerializationReader_ReadStruct(SerializationReader_ReadRectOffset), + ref new BridgeInterface::SerializationReader_ReadAnimationCurve(SerializationReader_ReadAnimationCurve)); + GetWinRTMonoBehaviourSerialization()->SetNativeReader(reader); + } +} + + +static void SerializationWriter_Write(int source, int size, bool align) +{ + StreamedBinaryWrite<false>* writer = currentTransferWrite.top(); + writer->GetCachedWriter().Write((void*)source, size); + if (align) + writer->Align(); +} + +void SerializationWriter_WriteGUIStyle(int srcPtr) +{ + currentTransferWrite.top()->Transfer(*(GUIStyle*)srcPtr, "does_not_matter", kNoTransferFlags); +} +void SerializationWriter_WriteRectOffset(int srcPtr) +{ + currentTransferWrite.top()->Transfer(*(RectOffset*)srcPtr, "does_not_matter", kNoTransferFlags); +} +void SerializationWriter_WriteAnimationCurve(int srcAnimationCurvePtr) +{ + AnimationCurve* curve = (AnimationCurve*)srcAnimationCurvePtr; + currentTransferWrite.top()->Transfer(*curve,"doesntmatter", kNoTransferFlags); +} + + +static void SerializationWriter_WritePPtr(int instanceID) +{ + StreamedBinaryWrite<false>* transfer = currentTransferWrite.top(); + PPtr<Object> pptr; + pptr.SetInstanceID(instanceID); + currentTransferWrite.top()->Transfer(pptr, "doesntmatter", kNoTransferFlags); +} + +static void SetupWriter() +{ + static BridgeInterface::ISerializationWriter^ writer = nullptr; + if (writer == nullptr) + { + writer = s_WinRTBridge->CreateSerializationWriter( + ref new BridgeInterface::SerializationWriter_Write(SerializationWriter_Write), + ref new BridgeInterface::SerializationWriter_WritePPtr(SerializationWriter_WritePPtr), + ref new BridgeInterface::SerializationWriter_WriteStruct(SerializationWriter_WriteGUIStyle), + ref new BridgeInterface::SerializationWriter_WriteStruct(SerializationWriter_WriteRectOffset), + ref new BridgeInterface::SerializationWriter_WriteAnimationCurve(SerializationWriter_WriteAnimationCurve)); + GetWinRTMonoBehaviourSerialization()->SetNativeWriter(writer); + } +} + + +template<> +void MonoBehaviour::TransferWithInstance<StreamedBinaryWrite<false> > (StreamedBinaryWrite<false>& transfer) +{ + AssertIf (GetInstance() == SCRIPTING_NULL); + currentTransferWrite.push(&transfer); + + SetupWriter(); + GetWinRTMonoBehaviourSerialization()->Serialize(GetInstance()); + + StreamedBinaryWrite<false>* remove = currentTransferWrite.top(); + Assert(remove == &transfer); + currentTransferWrite.pop(); +} + +template<> +void MonoBehaviour::TransferWithInstance<StreamedBinaryRead<false> > (StreamedBinaryRead<false> & transfer) +{ + AssertIf (GetInstance() == SCRIPTING_NULL); + + currentTransferRead.push(&transfer); + SetupReader(); + GetWinRTMonoBehaviourSerialization()->Deserialize(GetInstance()); + + StreamedBinaryRead<false>* remove = currentTransferRead.top(); + Assert(remove == &transfer); + currentTransferRead.pop(); +} + +static long long SerializationRemapper_GetScriptingWrapper(int oldInstanceID) +{ + SInt32 newInstanceID = currentRemapper->GetNewInstanceIDforOldInstanceID(oldInstanceID); + //printf_console("GNITRO called. old instanceid: %i, new instanceid: %i", oldInstanceID, newInstanceID); + return Scripting::GetScriptingWrapperForInstanceID(newInstanceID); +} + +static void SetupRemapper() +{ + static BridgeInterface::SerializationRemapper_GetScriptingWrapper^ remapper = nullptr; + if (remapper == nullptr) + { + remapper = ref new BridgeInterface::SerializationRemapper_GetScriptingWrapper(SerializationRemapper_GetScriptingWrapper); + GetWinRTMonoBehaviourSerialization()->SetNativeRemapper(remapper); + } + +} + +template<> +void MonoBehaviour::TransferWithInstance<RemapPPtrTransfer> (RemapPPtrTransfer& transfer) +{ + AssertIf (GetInstance() == SCRIPTING_NULL); + currentRemapper = &transfer; + SetupRemapper(); + GetWinRTMonoBehaviourSerialization()->Remap(GetInstance()); +} + +void MonoBehaviour::DoLivenessCheck(RemapPPtrTransfer& transfer) +{ +} +#endif diff --git a/Runtime/Mono/MonoExportUtility.cpp b/Runtime/Mono/MonoExportUtility.cpp new file mode 100644 index 0000000..71e4451 --- /dev/null +++ b/Runtime/Mono/MonoExportUtility.cpp @@ -0,0 +1,49 @@ +#include "UnityPrefix.h" +#if ENABLE_MONO +#include "MonoExportUtility.h" +#include "MonoTypeSignatures.h" +#include "MonoScript.h" +#include "MonoScriptCache.h" +#include "Runtime/Scripting/Backend/ScriptingTypeRegistry.h" +#include "Runtime/Scripting/ScriptingUtility.h" + + +#if !GAMERELEASE +#include "Editor/Src/Utility/ObjectNames.h" +#endif + +using namespace std; + +#if UNITY_EDITOR +static ScriptingClassPtr ClassIDToScriptingClassIncludingBasicTypes (int classID) +{ + if (classID == ClassID (Undefined)) + return NULL; + else if (classID <= kLargestEditorClassID) + return Scripting::ClassIDToScriptingType (classID); + else if (classID == ClassID (int)) + return mono_get_int32_class (); + else if (classID == ClassID (bool)) + return mono_get_boolean_class(); + else if (classID == ClassID (float)) + return mono_get_single_class(); + else + { + AssertString("Unsupported classID value"); + return NULL; + } +} + +MonoObject* ClassIDToScriptingTypeObjectIncludingBasicTypes (int classID) +{ + ScriptingClassPtr klass = ClassIDToScriptingClassIncludingBasicTypes (classID); + if (klass == NULL) + return NULL; + + return mono_type_get_object(mono_domain_get(), mono_class_get_type(klass)); +} + +#endif + + +#endif //ENABLE_MONO diff --git a/Runtime/Mono/MonoExportUtility.h b/Runtime/Mono/MonoExportUtility.h new file mode 100644 index 0000000..f6db714 --- /dev/null +++ b/Runtime/Mono/MonoExportUtility.h @@ -0,0 +1,14 @@ +#ifndef MONOEXPORTUTILITY_H +#define MONOEXPORTUTILITY_H + +#include "Runtime/Scripting/ScriptingUtility.h" + +#if ENABLE_MONO +#include "MonoManager.h" + +#if UNITY_EDITOR +ScriptingObjectPtr ClassIDToScriptingTypeObjectIncludingBasicTypes (int classID); +#endif + +#endif +#endif diff --git a/Runtime/Mono/MonoFunctions.h b/Runtime/Mono/MonoFunctions.h new file mode 100644 index 0000000..e93a298 --- /dev/null +++ b/Runtime/Mono/MonoFunctions.h @@ -0,0 +1,418 @@ +#ifndef DO_API_NO_RETURN +#define DO_API_NO_RETURN(a,b,c) DO_API(a,b,c) +#endif + +// If you add functions to this file you also need to expose them in MonoBundle.exp +// Otherwise they wont be exported in the web plugin! +DO_API(void,mono_thread_suspend_all_other_threads, ()) +DO_API(void,mono_thread_pool_cleanup, ()) +DO_API(void,mono_threads_set_shutting_down, ()) +DO_API(void,mono_runtime_set_shutting_down, ()) +DO_API(gboolean,mono_runtime_is_shutting_down, ()) +DO_API(gboolean,mono_domain_finalize, (MonoDomain *domain, int timeout)) +DO_API(void,mono_runtime_cleanup, (MonoDomain *domain)) +DO_API(MonoMethod*,mono_object_get_virtual_method, (MonoObject *obj, MonoMethod *method)) + +DO_API(void,mono_verifier_set_mode,(MiniVerifierMode) ); +DO_API(void,mono_security_set_mode,(MonoSecurityMode) ); +DO_API(void,mono_add_internal_call,(const char *name, gconstpointer method)) +DO_API(void,mono_jit_cleanup,(MonoDomain *domain)) +DO_API(MonoDomain*,mono_jit_init,(const char *file)) +DO_API(MonoDomain*,mono_jit_init_version,(const char *file, const char* runtime_version)) +DO_API(int, mono_jit_exec, (MonoDomain *domain, MonoAssembly *assembly, int argc, char *argv[])) +DO_API(MonoClass *,mono_class_from_name,(MonoImage *image, const char* name_space, const char *name)) +DO_API(MonoClass *,mono_class_from_name_case,(MonoImage *image, const char* name_space, const char *name)) +DO_API(MonoAssembly *,mono_domain_assembly_open,(MonoDomain *domain, const char *name)) +DO_API(MonoDomain *, mono_domain_create_appdomain,(const char *domainname, const char* configfile)) +DO_API(void, mono_domain_unload, (MonoDomain* domain)) +DO_API(MonoObject*,mono_object_new,(MonoDomain *domain, MonoClass *klass)) +DO_API(void,mono_runtime_object_init,(MonoObject *this_obj)) +DO_API(MonoObject*,mono_runtime_invoke,(MonoMethod *method, void *obj, void **params, MonoException **exc)) +DO_API(void,mono_field_set_value,(MonoObject *obj, MonoClassField *field, void *value)) +DO_API(void,mono_field_get_value,(MonoObject *obj, MonoClassField *field, void *value)) +DO_API(int,mono_field_get_offset,(MonoClassField *field)) +DO_API(MonoClassField*,mono_class_get_fields,(MonoClass* klass, gpointer *iter)) +DO_API(MonoMethod*,mono_class_get_methods,(MonoClass* klass, gpointer *iter)) +DO_API(MonoDomain*,mono_domain_get,()) +DO_API(MonoDomain*,mono_get_root_domain,()) +DO_API(gint32,mono_domain_get_id,(MonoDomain *domain)) +DO_API(void,mono_assembly_foreach,(GFunc func, gpointer user_data)) +DO_API(void,mono_image_close,(MonoImage *image)) +DO_API(void,mono_unity_socket_security_enabled_set,(gboolean)) +//DO_API(void,mono_set_unhandled_exception_handler,(void* function)) +DO_API(const char*, mono_image_get_name, (MonoImage *image)) +DO_API(MonoClass*, mono_get_object_class, ()) +#if UNITY_WIN || UNITY_OSX || UNITY_ANDROID || UNITY_BB10 || UNITY_TIZEN +DO_API(void,mono_set_signal_chaining, (bool)) +#endif +#if UNITY_WIN +DO_API(long, mono_unity_seh_handler, (EXCEPTION_POINTERS*)) +DO_API(void, mono_unity_set_unhandled_exception_handler, (void*)) +#endif +DO_API(void, mono_set_commandline_arguments, (int, const char* argv[], const char*)) +#if USE_MONO_AOT +DO_API(void*, mono_aot_get_method,(MonoDomain *domain, MonoMethod *method)) +#endif +//DO_API(MonoMethod*, mono_marshal_get_xappdomain_invoke, (MonoMethod*)) + +#if UNITY_EDITOR +// Type-safe way of looking up methods based on method signatures +DO_API(MonoObject*,mono_runtime_invoke_array,(MonoMethod *method, void *obj, MonoArray *params, MonoException **exc)) +DO_API(MonoMethodDesc*, mono_method_desc_new, (const char *name, gboolean include_namespace)) +DO_API(MonoMethod*, mono_method_desc_search_in_class, (MonoMethodDesc* desc, MonoClass* klass)) +DO_API(void, mono_method_desc_free, (MonoMethodDesc* desc)) +DO_API(char*,mono_type_get_name_full,(MonoType *type, MonoTypeNameFormat format)) +#endif + +#if UNITY_WIN || UNITY_XENON +DO_API(gunichar2*,mono_string_to_utf16,(MonoString *string_obj)) +#endif + +DO_API(const char*,mono_field_get_name,(MonoClassField *field)) +DO_API(MonoType*,mono_field_get_type,(MonoClassField *field)) +DO_API(int,mono_type_get_type,(MonoType *type)) +DO_API(const char*,mono_method_get_name,(MonoMethod *method)) +DO_API(MonoImage*,mono_assembly_get_image,(MonoAssembly *assembly)) +DO_API(MonoClass* ,mono_method_get_class,(MonoMethod *method)) +DO_API(MonoClass*,mono_object_get_class,(MonoObject *obj)) +DO_API(gboolean,mono_class_is_valuetype,(MonoClass *klass)) +DO_API(guint32,mono_signature_get_param_count,(MonoMethodSignature *sig)) +DO_API(char*,mono_string_to_utf8,(MonoString *string_obj)) +DO_API(MonoString*,mono_string_new_wrapper,(const char* text)) +DO_API(MonoString*,mono_string_new_len,(MonoDomain *domain, const char *text, guint32 length)) +DO_API(MonoString*,mono_string_from_utf16,(const gunichar2* text)) +DO_API(MonoClass*,mono_class_get_parent,(MonoClass *klass)) +DO_API(const char*,mono_class_get_namespace,(MonoClass *klass)) +DO_API(gboolean,mono_class_is_subclass_of,(MonoClass *klass, MonoClass *klassc, gboolean check_interfaces)) +DO_API(const char*,mono_class_get_name,(MonoClass *klass)) +DO_API(char*,mono_type_get_name,(MonoType *type)) +DO_API(MonoClass*,mono_type_get_class,(MonoType *type)) +DO_API(MonoException *,mono_exception_from_name_msg,(MonoImage *image, const char *name_space, const char *name, const char *msg)) +DO_API_NO_RETURN(void,mono_raise_exception,(MonoException *ex)) +DO_API(MonoClass*,mono_get_exception_class,()) +DO_API(MonoClass*,mono_get_array_class,()) +DO_API(MonoClass*,mono_get_string_class,()) +DO_API(MonoClass*,mono_get_boolean_class,()) +DO_API(MonoClass*,mono_get_byte_class,()) +DO_API(MonoClass*,mono_get_char_class,()) +DO_API(MonoClass*,mono_get_int16_class,()) +DO_API(MonoClass*,mono_get_int32_class,()) +DO_API(MonoClass*,mono_get_int64_class,()) +DO_API(MonoClass*,mono_get_single_class,()) +DO_API(MonoClass*,mono_get_double_class,()) +DO_API(MonoArray*,mono_array_new,(MonoDomain *domain, MonoClass *eclass, guint32 n)) +//DO_API(MonoArray *, mono_array_new_specific, (MonoVTable *vtable, guint32 n)) +DO_API(MonoArray*, mono_array_new_full, (MonoDomain *domain, MonoClass *array_class, guint32 *lengths, guint32 *lower_bounds)) + +DO_API(MonoClass *, mono_array_class_get, (MonoClass *eclass, guint32 rank)) + +DO_API(gint32,mono_class_array_element_size,(MonoClass *ac)) +DO_API(MonoObject*, mono_type_get_object, (MonoDomain *domain, MonoType *type)) +DO_API(gboolean, mono_class_is_generic, (MonoClass* klass)) +DO_API(gboolean, mono_class_is_inflated, (MonoClass* klass)) + +DO_API(MonoThread *,mono_thread_attach,(MonoDomain *domain)) + +DO_API(void, mono_thread_detach,(MonoThread *thread)) +DO_API(MonoThread *,mono_thread_exit,()) + +DO_API(MonoThread *,mono_thread_current,(void)) +DO_API(void,mono_thread_set_main,(MonoThread* thread)) +DO_API(void,mono_set_find_plugin_callback,(gconstpointer method)) +DO_API(void,mono_security_enable_core_clr, ()) + +typedef bool (*MonoCoreClrPlatformCB) (const char *image_name); +DO_API(bool,mono_security_set_core_clr_platform_callback, (MonoCoreClrPlatformCB)) + +#if MONO_2_12 +DO_API(void,mono_security_core_clr_set_options, (MonoSecurityCoreCLROptions)) +#endif + +DO_API(void, mono_runtime_unhandled_exception_policy_set, (MonoRuntimeUnhandledExceptionPolicy policy)) + +#if UNITY_OSX +//DO_API(void,SetNativeSigsegvHandler,(gconstpointer ptr)) +#endif +#if UNITY_WIN +//DO_API(void,SetNativeSigsegvHandlerWin,(gconstpointer ptr)) +// @TODO: move this out of windows specific +//DO_API(void,unity_mono_redirect_output,(const char* fout, const char* ferr)) +//DO_API(void,unity_mono_close_output,()) +#endif + +DO_API(MonoClass*,mono_class_get_nesting_type,(MonoClass *klass)) +DO_API(MonoVTable* ,mono_class_vtable,(MonoDomain *domain, MonoClass *klass)) +DO_API(MonoReflectionMethod* ,mono_method_get_object,(MonoDomain *domain, MonoMethod *method, MonoClass *refclass)) + +DO_API(MonoMethodSignature* ,mono_method_signature,(MonoMethod *method)) +DO_API(MonoType*,mono_signature_get_params,(MonoMethodSignature *sig, gpointer *iter)) +DO_API(MonoType*,mono_signature_get_return_type,(MonoMethodSignature *sig)) +DO_API(MonoType*,mono_class_get_type,(MonoClass *klass)) +DO_API(void,mono_set_ignore_version_and_key_when_finding_assemblies_already_loaded,(gboolean value)) +DO_API (void,mono_debug_init ,(int format)) + +DO_API (void,mono_debug_open_image_from_memory,(MonoImage *image, const char *raw_contents, int size)) +DO_API(guint32,mono_field_get_flags,(MonoClassField *field)) +DO_API(MonoImage*,mono_image_open_from_data_full,(const void *data, guint32 data_len, gboolean need_copy, int *status, gboolean ref_only)) +DO_API(MonoImage*,mono_image_open_from_data_with_name,(char *data, guint32 data_len, gboolean need_copy, int *status, gboolean refonly, const char *name)) +DO_API(MonoAssembly *,mono_assembly_load_from,(MonoImage *image, const char*fname, int *status)) +DO_API(bool, mono_assembly_fill_assembly_name, (MonoImage *image, MonoAssemblyName *aname)) +DO_API(char* , mono_stringify_assembly_name, (MonoAssemblyName *aname)) +DO_API(int, mono_assembly_name_parse,(const char* name, MonoAssemblyName *assembly)) +DO_API(MonoAssembly*, mono_assembly_loaded, (MonoAssemblyName *aname)) +DO_API(int, mono_image_get_table_rows, (MonoImage *image, int table_id)) +DO_API(MonoClass*, mono_class_get, (MonoImage *image, guint32 type_token)) +DO_API(gboolean, mono_metadata_signature_equal, (MonoMethodSignature *sig1, MonoMethodSignature *sig2)) + +DO_API(MonoObject *,mono_value_box,(MonoDomain *domain, MonoClass *klass, gpointer val)) +DO_API(MonoImage*,mono_class_get_image,(MonoClass *klass)) +DO_API(char,mono_signature_is_instance,(MonoMethodSignature *signature)) +DO_API(MonoMethod*,mono_method_get_last_managed,()) +DO_API(MonoClass*,mono_get_enum_class,()) +DO_API(MonoType*,mono_class_get_byref_type,(MonoClass *klass)) + +DO_API (void,mono_field_static_get_value,(MonoVTable *vt, MonoClassField *field, void *value)) +DO_API(void,mono_unity_set_embeddinghostname,(const char* name)) +DO_API(void,mono_set_assemblies_path,(const char* name)) + +DO_API (guint32, mono_gchandle_new, (MonoObject *obj, gboolean pinned)) +DO_API (MonoObject*, mono_gchandle_get_target, (guint32 gchandle)) + +DO_API (guint32, mono_gchandle_new_weakref, (MonoObject *obj, gboolean track_resurrection)) + +DO_API (gboolean, mono_gchandle_is_in_domain, (guint32 gchandle, MonoDomain *domain)) +DO_API (MonoObject*, mono_assembly_get_object, (MonoDomain *domain, MonoAssembly *assembly)) + +DO_API (void,mono_gchandle_free, (guint32 gchandle)) + +typedef gboolean (*MonoStackWalk) (MonoMethod *method, gint32 native_offset, gint32 il_offset, gboolean managed, gpointer data); +DO_API (void, mono_stack_walk, (MonoStackWalk func, gpointer user_data)); +DO_API (char*, mono_pmip, (void *ip)); +DO_API (MonoObject*, mono_runtime_delegate_invoke, (MonoObject *delegate, void **params, MonoException **exc)) + +//DO_API (void,GC_free,(void* p)) +//DO_API(void*,GC_malloc_uncollectable,(int size)) +DO_API(MonoProperty*,mono_class_get_properties,(MonoClass* klass, gpointer *iter)) +DO_API(MonoMethod*,mono_property_get_get_method,(MonoProperty *prop)) +DO_API(MonoObject *,mono_object_new_alloc_specific,(MonoVTable *vtable)) +DO_API(MonoObject *,mono_object_new_specific,(MonoVTable *vtable)) + +DO_API (void,mono_gc_collect,(int generation)) +DO_API(int,mono_gc_max_generation,()) + +DO_API(gint64,mono_gc_get_used_size,()) +DO_API(gint64,mono_gc_get_heap_size,()) + +DO_API(MonoAssembly*,mono_image_get_assembly,(MonoImage *image)) +DO_API(MonoAssembly*,mono_assembly_open,(const char *filename, int *status)) + +DO_API(gboolean,mono_class_is_enum,(MonoClass *klass)) +DO_API(MonoType* ,mono_class_enum_basetype, (MonoClass *klass)) +DO_API(gint32,mono_class_instance_size,(MonoClass *klass)) +DO_API(guint32,mono_object_get_size,(MonoObject *obj)) +DO_API(const char*,mono_image_get_filename,(MonoImage *image)) +DO_API(MonoAssembly*,mono_assembly_load_from_full,(MonoImage *image, const char *fname,int *status,gboolean refonly)) +DO_API(MonoClass*,mono_class_get_interfaces,(MonoClass* klass, gpointer *iter)) +DO_API (void,mono_assembly_close,(MonoAssembly *assembly)) +DO_API(MonoProperty*,mono_class_get_property_from_name,(MonoClass *klass, const char *name)) +DO_API(MonoMethod*,mono_class_get_method_from_name,(MonoClass *klass, const char *name, int param_count)) +DO_API(MonoClass*,mono_class_from_mono_type,(MonoType *image)) +DO_API(int, mono_class_get_rank, (MonoClass *klass)); +DO_API(MonoClass*, mono_class_get_element_class, (MonoClass *klass)); +DO_API(bool,mono_unity_class_is_interface,(MonoClass* klass)) +DO_API(bool,mono_unity_class_is_abstract,(MonoClass* klass)) + +DO_API (gboolean,mono_domain_set,(MonoDomain *domain, gboolean force)) +DO_API (void,mono_thread_push_appdomain_ref,(MonoDomain *domain)) +DO_API (void,mono_thread_pop_appdomain_ref,()) + +DO_API (int, mono_runtime_exec_main, (MonoMethod *method, MonoArray *args, MonoObject **exc)) + +DO_API (MonoImage*,mono_get_corlib,()) +DO_API (MonoClassField*, mono_class_get_field_from_name, (MonoClass *klass, const char *name)) +DO_API (guint32, mono_class_get_flags, (MonoClass *klass)) + +DO_API(int, mono_parse_default_optimizations, (const char* p)) +DO_API (void, mono_set_defaults, (int verbose_level, guint32 opts)) +DO_API(void, mono_config_parse, (const char *filename)) +DO_API(void, mono_set_dirs, (const char *assembly_dir, const char *config_dir)) +//DO_API(void,ves_icall_System_AppDomain_InternalUnload,(int domain_id)) +//DO_API(MonoObject*,ves_icall_System_AppDomain_createDomain,(MonoString *friendly_name, MonoObject *setup)) +DO_API(void,mono_jit_parse_options,(int argc, char * argv[])) +DO_API(gpointer, mono_object_unbox, (MonoObject* o)) + +DO_API(MonoObject*, mono_custom_attrs_get_attr, (MonoCustomAttrInfo *ainfo, MonoClass *attr_klass)) + +DO_API(gboolean, mono_custom_attrs_has_attr, (MonoCustomAttrInfo *ainfo, MonoClass *attr_klass)) +DO_API(MonoCustomAttrInfo*, mono_custom_attrs_from_field, (MonoClass *klass, MonoClassField *field)) +DO_API(MonoCustomAttrInfo*, mono_custom_attrs_from_method, (MonoMethod *method)) +DO_API(MonoCustomAttrInfo*, mono_custom_attrs_from_class, (MonoClass *klass)) +DO_API(void, mono_custom_attrs_free, (MonoCustomAttrInfo* attr)) + +#if UNITY_STANDALONE || UNITY_EDITOR +// DllImport fallback handling to load native libraries from custom locations +typedef void* (*MonoDlFallbackLoad) (const char *name, int flags, char **err, void *user_data); +typedef void* (*MonoDlFallbackSymbol) (void *handle, const char *name, char **err, void *user_data); +typedef void* (*MonoDlFallbackClose) (void *handle, void *user_data); +DO_API(MonoDlFallbackHandler*, mono_dl_fallback_register, (MonoDlFallbackLoad load_func, MonoDlFallbackSymbol symbol_func, MonoDlFallbackClose close_func, void *user_data)) +DO_API(void, mono_dl_fallback_unregister, (MonoDlFallbackHandler *handler)) +#endif + +#if UNITY_EDITOR +typedef void (*vprintf_func)(const char* msg, va_list args); +DO_API(void, mono_unity_set_vprintf_func, (vprintf_func func)) +DO_API(void*, mono_loader_get_last_error, (void)) +DO_API(MonoException*, mono_loader_error_prepare_exception, (void *error)) +#endif + +typedef void (*register_object_callback)(gpointer* arr, int size, void* userdata); +DO_API(void*, mono_unity_liveness_calculation_begin, (MonoClass* filter, int max_object_count, register_object_callback callback, void* userdata)) +DO_API(void, mono_unity_liveness_calculation_end, (void* state)) +DO_API(void, mono_unity_liveness_calculation_from_root, (MonoObject* root, void* state)) +DO_API(void, mono_unity_liveness_calculation_from_statics, (void* state)) + + +///@TODO add this as an optimization when upgrading mono, used by MonoStringNewLength: +/// mono_string_new_len (MonoDomain *domain, const char *text, guint length); + +// Profiler +#if ENABLE_MONO_MEMORY_PROFILER || ENABLE_MONO_HEAPSHOT +typedef void (*MonoProfileFunc) (void *prof); +typedef void (*MonoProfileMethodFunc) (void *prof, MonoMethod *method); +typedef void (*MonoProfileExceptionFunc) (void *prof, MonoObject *object); +typedef void (*MonoProfileExceptionClauseFunc) (void *prof, MonoMethod *method, int clause_type, int clause_num); +typedef void (*MonoProfileGCFunc) (void *prof, int event, int generation); +typedef void (*MonoProfileGCResizeFunc) (void *prof, SInt64 new_size); +typedef void (*MonoProfileAllocFunc) (void *prof, MonoObject *obj, MonoClass *klass); +typedef void (*MonoProfileStatCallChainFunc) (void *prof, int call_chain_depth, guchar **ip, void *context); +typedef void (*MonoProfileStatFunc) (void *prof, guchar *ip, void *context); + + +DO_API(void, mono_profiler_install, (void *prof, MonoProfileFunc shutdown_callback)) +DO_API(void, mono_profiler_set_events, (int events)) +DO_API(void, mono_profiler_install_enter_leave, (MonoProfileMethodFunc enter, MonoProfileMethodFunc fleave)) +DO_API(void, mono_profiler_install_gc, (MonoProfileGCFunc callback, MonoProfileGCResizeFunc heap_resize_callback)) +DO_API(void, mono_profiler_install_allocation, (MonoProfileAllocFunc callback)) +//DO_API(void, mono_gc_base_init, ()) +//DO_API(void, mono_profiler_install_statistical, (MonoProfileStatFunc callback)) +//DO_API(void, mono_profiler_install_statistical_call_chain, (MonoProfileStatCallChainFunc callback, int call_chain_depth)) +DO_API(void, mono_profiler_install_exception, (MonoProfileExceptionFunc throw_callback, MonoProfileMethodFunc exc_method_leave, MonoProfileExceptionClauseFunc clause_callback)) + +# if ENABLE_MONO_HEAPSHOT + typedef void (*MonoProfileClassFunc) (void *prof, MonoClass* klass); + typedef void (*MonoProfileClassResult) (void *prof, MonoClass* klass,int result); + + DO_API(void, mono_profiler_install_class, (MonoProfileClassFunc start_load, MonoProfileClassResult end_load, MonoProfileClassFunc start_unload, MonoProfileClassFunc end_unload)) + DO_API(gboolean, mono_type_is_reference, (MonoType *type)); + DO_API(gint32, mono_class_value_size, (MonoClass *klass, guint32 *align)); + DO_API(char*, mono_array_addr_with_size, (MonoArray *array, int size, uintptr_t idx)); + #define mono_array_addr(array,type,index) ((type*)(void*) mono_array_addr_with_size (array, sizeof (type), index)) + #define mono_array_get(array,type,index) ( *(type*)mono_array_addr ((array), type, (index)) ) + DO_API(gint32, mono_array_element_size, (MonoClass *ac)); + DO_API(char*, mono_type_full_name, (MonoType *type)); + typedef void (*MonoDomainFunc) (MonoDomain *domain, void* user_data); + DO_API(void, mono_domain_foreach, (MonoDomainFunc func, void* user_data)); + DO_API(gboolean, mono_object_is_alive, (MonoObject* obj)); +# endif +#endif + +#if UNITY_IPHONE || UNITY_PEPPER +DO_API(void, mono_aot_register_module, (void *aot_info)) +#endif + +// GLib functions +#if UNITY_IPHONE || UNITY_PEPPER // iPhone uses eglib which just calls free +#define g_free free +#elif MONO_2_12 +#define g_free mono_unity_g_free +DO_API(void,mono_unity_g_free,(void* p)) +#else +DO_API(void,g_free,(void* p)) +#endif +/* +#if UNITY_PS3 || UNITY_XENON || UNITY_ANDROID +static inline char *g_strdup (const char *str) { if (str) {return strdup (str);} return NULL; } +#define g_mem_set_vtable(x) +#else +DO_API(char*,g_strdup,(const char *image)) +DO_API(void,g_mem_set_vtable,(gpointer vtable)) +#endif*/ + + +//DO_API(void,macosx_register_exception_handler,()) + +DO_API(void, mono_trace_set_level_string, (const char *value)) +DO_API(void, mono_trace_set_mask_string, (const char *value)) + +#if UNITY_WII +DO_API(char*, g_strdup_d, (char const* str)) + +// memory and string functions +DO_API(void*, console_g_malloc, ( size_t size )) +DO_API(void*, console_g_calloc, ( size_t size, size_t elemSize )) +DO_API(void*, console_g_realloc, (void *memblock, size_t size)) +DO_API(void, console_g_free, ( void* p )) + +// specific allocations +DO_API(void*, console_g_malloc_image, (size_t size)) +DO_API(void, console_g_free_image, (void* p)) + +DO_API(void*, console_g_malloc0_handles, (size_t size)) +DO_API(void, console_g_free_handles, (void* p)) + +#define g_free console_g_free +//#define g_malloc_d(x) console_g_malloc(x) +//#define g_calloc_d(obj,size) console_g_calloc(obj,size) +#define g_free_d(x) console_g_free(x) +#define g_strdup_d(x) g_strdup(x) + +DO_API(void*, g_memdup, (void const* mem, unsigned int byte_size)) +DO_API(char*, g_strdup, (const char *str)) + +#endif + +#if UNITY_XENON || UNITY_PS3 +// memory and string functions +DO_API(void*, console_g_malloc, ( size_t size )) +DO_API(void*, console_g_calloc, ( size_t size, size_t elemSize )) +DO_API(void*, console_g_realloc, (void *memblock, size_t size)) +DO_API(void, console_g_free, ( void* p )) + +#define g_free console_g_free +#endif + +#if UNITY_PEPPER +DO_API(void, mono_set_corlib_data, (void *data, size_t size)) +DO_API(void, mono_runtime_set_no_exec, (gboolean val)) +DO_API(void, mono_jit_set_aot_only, (gboolean val)) +typedef MonoAssembly *(*MonoAssemblySearchFunc) (char **aname, void* user_data); + +DO_API(void, mono_install_assembly_postload_search_hook, (MonoAssemblySearchFunc func, gpointer user_data)) +#endif + +#if UNITY_PS3 || UNITY_XENON +DO_API(char*, g_strdup_d, (char const* str)) +#define g_strdup_d(x) g_strdup(x) +DO_API(void*, g_memdup, (void const* mem, unsigned int byte_size)) +//DO_API(char*, g_strdup, (const char *str)) + +#endif + +#if UNITY_XENON +DO_API(void, mono_xenon_dl_set_min_version, (DWORD min_version)) +DO_API(void, mono_runtime_set_main_args, (const char** args, int num_args)) +#endif + +#if UNITY_PLUGINS_AVAILABLE && UNITY_XENON +DO_API(MonoDl*, mono_dl_open, (const char* name, int flags, char** error_msg)) +DO_API(char*, mono_dl_symbol, (void* MonoDl, const char* name, void** symbol)) +DO_API(void, mono_dl_close, (void* MonoDl)) +DO_API(MonoDl*, cached_module_load, (const char* name, int flags, char** error_msg)) +#endif + +#if UNITY_OSX +DO_API(int,mono_backtrace_from_context, (void* context, void* array[], int count)) +#endif + +#undef DO_API +#undef DO_API_NO_RETURN diff --git a/Runtime/Mono/MonoHeapShot.cpp b/Runtime/Mono/MonoHeapShot.cpp new file mode 100644 index 0000000..65b4f14 --- /dev/null +++ b/Runtime/Mono/MonoHeapShot.cpp @@ -0,0 +1,510 @@ +#include "UnityPrefix.h" +#include "Configuration/UnityConfigure.h" +#include "Runtime/Threads/Mutex.h" + +#include "MonoHeapShot.h" + +#if ENABLE_MONO_HEAPSHOT + +#if defined(SN_TARGET_PS3) +#ifndef FALSE +#define FALSE 0 +#endif +#ifndef TRUE +#define TRUE 1 +#endif +#endif + +#include "MonoHeapShotWriter.h" +#include "Runtime/Misc/PlayerSettings.h" +#include "Runtime/Network/PlayerCommunicator/PlayerConnection.h" + +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ + +/* + * heap-shot.c + * + * Copyright (C) 2005 Novell, Inc. + * + * This profiler is unsafe: it does in signal context almost everything + * that must not be done there, like taking locks, running a GC etc. + * It takes a lock in heap_shot_gc_func(), which can cause a deadlock. + * It doesn't deal with moving objects. + */ + +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of version 2 of the GNU General Public + * License as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + * USA. + */ + +/* +#include <string.h> +#include <glib.h> +#include <pthread.h> +#include <mono/metadata/assembly.h> +#include <mono/metadata/class.h> +#include <mono/metadata/debug-helpers.h> +#include <mono/metadata/object.h> +#include <mono/metadata/profiler.h> +#include <mono/metadata/mono-gc.h> +#include <mono/metadata/metadata.h> +#include <mono/metadata/debug-helpers.h> +#include <unistd.h> +#include <time.h> + +*/ + + +/* +typedef pthread_mutex_t mono_mutex_t; +#define mono_mutex_init(mutex,attr) pthread_mutex_init (mutex, attr) +#define mono_mutex_lock(mutex) pthread_mutex_lock (mutex) +#define mono_mutex_unlock(mutex) pthread_mutex_unlock (mutex) +*/ +//#define MONO_CONSOLES (UNITY_XENON || UNITY_PS3) + +//#if MONO_CONSOLES +//#define MONOPROFILER MonoProfiler +//#define MONOGCEVENT MonoGCEvent +//#else +#define MONOPROFILER void +#define MONOGCEVENT int +//#endif + +#if UNITY_WII +typedef OSMutex mono_mutex_t; +#define mono_mutex_init(mutex,attr) OSInitMutex(mutex); +#define mono_mutex_lock(mutex) OSLockMutex(mutex) +#define mono_mutex_unlock(mutex) OSUnlockMutex(mutex); +#else +typedef Mutex* mono_mutex_t; +#define mono_mutex_init(mutex,attr) *mutex = new Mutex() +#define mono_mutex_lock(mutex) (*mutex)->Lock() +#define mono_mutex_unlock(mutex) (*mutex)->Unlock() +#endif + + +struct _MonoProfiler +{ + mono_mutex_t lock; + mono_mutex_t dump_lock; + ObjectsHash *objects_hash; + ObjectsHash *work_objects_hash; + + ClassHash *class_hash; + ClassHash *exclude_class_hash; + ClassHash *work_class_hash; + MonoHeapShotWriter dumpfile_writer; + const char *out_file_name; + int dump_count; +}; +typedef _MonoProfiler MonoProfiler; + +static MonoProfiler* s_MonoProfiler = NULL; + +MonoProfiler* HeapShotCreateMonoProfiler (const char *outfilename) +{ + //struct sigaction sa; + MonoProfiler* p = new MonoProfiler(); + s_MonoProfiler = p; + + mono_mutex_init (&p->lock, NULL); + mono_mutex_init (&p->dump_lock, NULL); + + p->objects_hash = new ObjectsHash(); + p->class_hash = new ClassHash(); + p->exclude_class_hash = new ClassHash(); + p->out_file_name = outfilename; + p->dump_count = 0; + + // Sets the PROF signal + //sa.sa_handler = profiler_signal_handler; + //sigemptyset (&sa.sa_mask); + //sa.sa_flags = 0; + //g_assert (sigaction (SIGPROF, &sa, NULL) != -1); + + return p; +} + +static gboolean heap_scan_object (MonoProfiler *p, MonoObject *obj, MonoClass *klass, MonoClassField *parent_field) +{ + gpointer iter; + MonoClassField *field; + gboolean has_refs = FALSE; + MonoClass *cur_klass = klass; + + do { + iter = NULL; + while ((field = mono_class_get_fields (cur_klass, &iter)) != NULL) { + MonoType* field_type = mono_field_get_type (field); + // Skip static fields + if (mono_field_get_flags (field) & 0x0010 /*FIELD_ATTRIBUTE_STATIC*/) + continue; + + if (mono_type_is_reference (field_type)) { + // Dump the object reference + MonoObject* ref; + has_refs = true; + mono_field_get_value (obj, field, &ref); + if (ref && p->work_objects_hash->find(ref) != p->work_objects_hash->end()) + p->dumpfile_writer.DumpReference (ref, parent_field ? parent_field : field); + } + else { + MonoClass *fclass = mono_class_from_mono_type (field_type); + if (fclass && mono_class_is_valuetype (fclass)) { + if (p->exclude_class_hash->find(fclass) != p->exclude_class_hash->end()) + continue; + // It's a value type. Check if the class is big enough to hold references + int size = mono_class_value_size (fclass, NULL); + if (size >= sizeof(gpointer) && fclass != cur_klass) { + // Get the object value and scan it + char* vop = (char*)malloc (size); + mono_field_get_value (obj, field, vop); + // Recursively scan the object + if (heap_scan_object (p, (MonoObject*)(vop - sizeof(MonoObject)), fclass, parent_field)) + has_refs = true; + free (vop); + } + } + } + } + cur_klass = mono_class_get_parent (cur_klass); + } while (cur_klass); + + // If the class doesn't contain references, register in the exclude_class_hash table, + // so it won't be scanned again. + if (!has_refs && p->exclude_class_hash->find(klass) == p->exclude_class_hash->end()) + { + (*p->exclude_class_hash)[klass] = klass; + //g_hash_table_insert (p->exclude_class_hash, klass, klass); + } + return has_refs; +} + +static void heap_scan_array (MonoProfiler *p, MonoObject *obj, MonoClass *klass) +{ + MonoArray *array = (MonoArray *) obj; + MonoClass *eklass = mono_class_get_element_class (klass); + gboolean has_refs = FALSE; + + if (!mono_class_is_valuetype (eklass)) + { + // It's an array of object references, write all of them in the output file + int n; + for (n=0; n<mono_array_length (array); n++) { + MonoObject *ref = mono_array_get (array, MonoObject*, n); + if (ref && p->work_objects_hash->find(ref) != p->work_objects_hash->end()) + p->dumpfile_writer.DumpReference (ref, NULL); + } + has_refs = true; + } + //else if (!g_hash_table_lookup (p->exclude_class_hash, eklass)) + else if (p->exclude_class_hash->find(eklass) == p->exclude_class_hash->end()) + { + // It's an array of value type objects. Each object will be scanned + // by recursively calling heap_scan_object for each member + int n; + gint32 esize = mono_array_element_size (klass); + if (esize >= sizeof(gpointer)) { + // The type is big enough to contain references. + // Scan the array. + for (n=0; n<mono_array_length (array); n++) { + char *ref = (char *) mono_array_addr_with_size (array, esize, n); + ref -= sizeof (MonoObject); + if (heap_scan_object (p, (MonoObject *) ref, eklass, NULL)) + has_refs = true; + else + // The class has no fields, it makes no sense to continue + break; + } + } + } + // If the class doesn't contain references, register in the exclude_class_hash table, + // so it won't be scanned again. + if (!has_refs && p->exclude_class_hash->find(klass) == p->exclude_class_hash->end()) + { + (*p->exclude_class_hash)[klass] = klass; + //g_hash_table_insert (p->exclude_class_hash, klass, klass); + } +} + +static void heap_scan_fn (gpointer key, gpointer value, gpointer user_data) +{ + MonoProfiler *p = (MonoProfiler*)user_data; + MonoObject *obj = (MonoObject*)key; + MonoClass *klass = (MonoClass*)value; + + // Write the object header + p->dumpfile_writer.BeginObjectDump (obj, klass); + + // If the type is registered as not having reference fields, just return + //if (g_hash_table_lookup (p->exclude_class_hash, klass)) + if (p->exclude_class_hash->find(klass) != p->exclude_class_hash->end()) + { + p->dumpfile_writer.EndObjectDump (); + return; + } + + if (mono_class_get_rank (klass)) { + // It's an array + heap_scan_array (p, obj, klass); + } + else { + heap_scan_object (p, obj, klass, NULL); + } + + // Write the object end marker + p->dumpfile_writer.EndObjectDump (); +} + +static void dump_static_fields_fn (gpointer key, gpointer value, gpointer user_data) +{ + MonoClassField *field; + gpointer iter = NULL; + gboolean added = FALSE; + MonoClass *klass = (MonoClass*)key; + MonoProfiler *p = (MonoProfiler*)((gpointer*)user_data)[0]; + MonoDomain *domain = (MonoDomain*)((gpointer*)user_data)[1]; + MonoVTable *vtable = NULL; + gpointer field_value; + + if (strstr (mono_type_full_name (mono_class_get_type (klass)), "`")) + return; + + while ((field = mono_class_get_fields (klass, &iter)) != NULL) { + if (mono_field_get_flags (field) & 0x0010 /*FIELD_ATTRIBUTE_STATIC*/) { + // Dump the class only if it has static fields + if (!added) { + p->dumpfile_writer.BeginObjectDump (NULL, klass); + vtable = mono_class_vtable (domain, klass); + added = true; + } + MonoType* field_type = mono_field_get_type (field); + + if (mono_type_is_reference (field_type)) { + mono_field_static_get_value (vtable, field, &field_value); + if (field_value) { + p->dumpfile_writer.DumpReference (field_value, field); + } + } else { + MonoClass *fclass = mono_class_from_mono_type (field_type); + if (fclass && mono_class_is_valuetype (fclass)) { + //if (g_hash_table_lookup (p->exclude_class_hash, fclass)) + if (p->exclude_class_hash->find(fclass) != p->exclude_class_hash->end()) + continue; + int size = mono_class_value_size (fclass, NULL); + if (size >= sizeof(gpointer) && fclass != klass) { + // Get the object value and scan it + char* vop = (char*)malloc (size); + mono_field_static_get_value (vtable, field, vop); + // Recursively scan the object + heap_scan_object (p, (MonoObject*)(vop - sizeof(MonoObject)), fclass, field); + free (vop); + } + } + } + } + } + if (added) + p->dumpfile_writer.EndObjectDump (); +} + +static void dump_domain_static_fields_fn (MonoDomain *domain, gpointer user_data) +{ + MonoProfiler *p = (MonoProfiler*)user_data; + gpointer data [2]; + data [0] = p; + data [1] = domain; + //g_hash_table_foreach (p->work_class_hash, dump_static_fields_fn, &data); + for (ClassHash::iterator item = p->work_class_hash->begin(); + item != p->work_class_hash->end(); + item++) + { + dump_static_fields_fn(item->first, item->second, &data); + } +} + +void HeapShotDumpObjectMap (HeapShotData& data) +{ + if (s_MonoProfiler == NULL) + { + printf_console("Mono HeapShot is disabled? Do you have Mono Debugger enabled?"); + return; + } + MonoProfiler *p = s_MonoProfiler; + + mono_gc_collect (0); + mono_mutex_lock (&p->dump_lock); + + // Make a copy of the hashtables which collect object and type data, + // to avoid deadlocks while inspecting the data + mono_mutex_lock (&p->lock); + + p->work_objects_hash = new ObjectsHash(); + p->work_class_hash = new ClassHash(); + *p->work_objects_hash = *p->objects_hash; + //g_hash_table_foreach (p->objects_hash, clone_hash_table_fn, p->work_objects_hash); + //g_hash_table_foreach (p->class_hash, clone_hash_table_fn, p->work_class_hash); + *p->work_class_hash = *p->class_hash; + + mono_mutex_unlock (&p->lock); + + char fileName[255]; + sprintf(fileName, "%s_%d.omap", p->out_file_name, p->dump_count); + printf_console ("Dumping object map to file '%s'\n", fileName); + p->dumpfile_writer.Start(&data); + + // Dump object information + //g_hash_table_foreach (p->work_objects_hash, heap_scan_fn, p); + for (ObjectsHash::iterator item = p->work_objects_hash->begin(); + item != p->work_objects_hash->end(); + item++) + { + heap_scan_fn(item->first, item->second, p); + } + + p->dump_count++; + + // Dump static field references for each domain + // This can cause new object allocations + mono_domain_foreach (dump_domain_static_fields_fn, p); + p->dumpfile_writer.DumpObjectsReferencedByUnity(); + p->dumpfile_writer.End(); + + //g_hash_table_destroy (p->work_objects_hash); + delete p->work_objects_hash; + delete p->work_class_hash; + + printf_console ("done\n"); + + mono_mutex_unlock (&p->dump_lock); +} + +void HeapShotAllocFunc (MonoProfiler *p, MonoObject *obj, MonoClass *klass) +{ + mono_mutex_lock (&p->lock); + //g_hash_table_insert (p->objects_hash, obj, klass); + (*p->objects_hash)[obj] = klass; + mono_mutex_unlock (&p->lock); +} + +static gboolean post_gc_clean_objects_fn (gpointer key, gpointer value, gpointer user_data) +{ + MonoObject *obj = (MonoObject*)key; + return !mono_object_is_alive (obj); +} + +void HeapShotGCFunc (MonoProfiler *p, MonoGCEvent e, int gen) +{ + if (e != MONO_GC_EVENT_MARK_END) + return; + + mono_mutex_lock (&p->lock); + //g_hash_table_foreach_remove (p->objects_hash, post_gc_clean_objects_fn, NULL); + for (ObjectsHash::iterator item = p->objects_hash->begin(); + item != p->objects_hash->end(); + ) + { + if (post_gc_clean_objects_fn(item->first, item->second, NULL)) + { + ObjectsHash::iterator toDelete = item; + item++; + p->objects_hash->erase(toDelete); + } + else + { + item++; + } + } + mono_mutex_unlock (&p->lock); +} + +void HeapShotLoadClassFunc (MonoProfiler *p, MonoClass *klass, int result) +{ + mono_mutex_lock (&p->lock); + //g_hash_table_insert (p->class_hash, klass, klass); + (*p->class_hash)[klass] = klass; + mono_mutex_unlock (&p->lock); +} + + void HeapShotUnloadClassFunc (MonoProfiler *p, MonoClass *klass) +{ + mono_mutex_lock (&p->lock); + //g_hash_table_remove (p->class_hash, klass); + p->class_hash->erase(klass); + mono_mutex_unlock (&p->lock); +} + +static void GCMonoCallback (MONOPROFILER* prof, MONOGCEVENT event, int generation) +{ + HeapShotGCFunc((MonoProfiler*)prof, (MonoGCEvent)event, generation); +} + +static void GCMonoResizeCallback (MONOPROFILER* prof, SInt64 new_size) +{ + //printf("GCMonoResizeCallback\n"); +} + +static void GCMonoAllocationCallback (MONOPROFILER* prof, MonoObject *obj, MonoClass *klass) +{ + //printf("GCMonoAllocationCallback: Allocating %d bytes for %s\n", mono_object_get_size(obj), mono_class_get_name(klass)); + HeapShotAllocFunc((MonoProfiler*)prof, obj, klass); +} +static void MonoLoadClassFunc (MONOPROFILER *p, MonoClass *klass, int result) +{ + //printf("MonoLoadClassFunc: %s %d\n", mono_class_get_name(klass), result); + HeapShotLoadClassFunc((MonoProfiler*)p, klass, result); +} +static void MonoUnloadClassFunc (MONOPROFILER *p, MonoClass *klass) +{ + HeapShotUnloadClassFunc((MonoProfiler*)p, klass); +} +void InstallMonoHeapshot() +{ +#if UNITY_WII + if (GetPlayerSettingsPtr() && GetPlayerSettings().wiiHio2Usage != 1) return; +#endif + +#if ENABLE_PLAYERCONNECTION + if (PlayerConnection::ms_Instance && PlayerConnection::Get().AllowDebugging()) + { + printf_console("\nCan not initialize mono heapshot when script debugging is on.\n"); + return; + } +#endif + + printf_console("\nInitializing mono heapshot\n"); + s_MonoProfiler = HeapShotCreateMonoProfiler ("test.txt"); + + mono_profiler_install (s_MonoProfiler, NULL); + mono_profiler_install_gc(GCMonoCallback, GCMonoResizeCallback); + mono_profiler_install_allocation(GCMonoAllocationCallback); + //mono_profiler_install_enter_leave (EnterMonoMethod, LeaveMonoMethod); + //mono_profiler_install_jit_compile (EnterJITCompileMonoMethod, LeaveJITCompileMonoMethod); + //mono_profiler_install_gc_roots (GCHandleCallback, GCRootCallback); + mono_profiler_install_class (NULL, MonoLoadClassFunc, MonoUnloadClassFunc, NULL); + + int flags = + MONO_PROFILE_GC | + MONO_PROFILE_ALLOCATIONS | + //MONO_PROFILE_ENTER_LEAVE | + //MONO_PROFILE_JIT_COMPILATION | + //MONO_PROFILE_GC_ROOTS | + MONO_PROFILE_CLASS_EVENTS; + + mono_profiler_set_events ((MonoProfileFlags)flags); +} + + +#endif diff --git a/Runtime/Mono/MonoHeapShot.h b/Runtime/Mono/MonoHeapShot.h new file mode 100644 index 0000000..bea44f2 --- /dev/null +++ b/Runtime/Mono/MonoHeapShot.h @@ -0,0 +1,81 @@ +#ifndef MONO_HEAPSHOT_H +#define MONO_HEAPSHOT_H + + +#if ENABLE_MONO_HEAPSHOT + +#include "Runtime/Mono/MonoTypes.h" +#include "Runtime/Scripting/ScriptingUtility.h" + +extern "C" +{ + typedef enum + { + MONO_GC_EVENT_START, + MONO_GC_EVENT_MARK_START, + MONO_GC_EVENT_MARK_END, + MONO_GC_EVENT_RECLAIM_START, + MONO_GC_EVENT_RECLAIM_END, + MONO_GC_EVENT_END, + MONO_GC_EVENT_PRE_STOP_WORLD, + MONO_GC_EVENT_POST_STOP_WORLD, + MONO_GC_EVENT_PRE_START_WORLD, + MONO_GC_EVENT_POST_START_WORLD + } MonoGCEvent; + + typedef struct _MonoProfiler MonoProfiler; + /* + typedef gint32 mono_bool; + + extern mono_bool mono_type_is_reference (MonoType *type); + extern gint32 mono_class_value_size (MonoClass *klass, guint32 *align); + extern MonoClass* mono_class_get_element_class (MonoClass *klass); + + extern char* mono_array_addr_with_size (MonoArray *array, int size, uintptr_t idx); + #define mono_array_addr(array,type,index) ((type*)(void*) mono_array_addr_with_size (array, sizeof (type), index)) + #define mono_array_get(array,type,index) ( *(type*)mono_array_addr ((array), type, (index)) ) + + extern gint32 mono_array_element_size (MonoClass *ac); + extern int mono_class_get_rank (MonoClass *klass); + extern char* mono_type_full_name (MonoType *type); + typedef void (*MonoDomainFunc) (MonoDomain *domain, void* user_data); + extern void mono_domain_foreach (MonoDomainFunc func, void* user_data); + extern mono_bool mono_object_is_alive (MonoObject* obj); + + // Note: theser are different than the ones defined in MonoFunctions.h + typedef void (*MonoProfileFunc) (MonoProfiler *prof); + typedef void (*MonoProfileGCFunc) (MonoProfiler *prof, MonoGCEvent event, int generation); + typedef void (*MonoProfileGCResizeFunc) (MonoProfiler *prof, gint64 new_size); + typedef void (*MonoProfileClassFunc) (MonoProfiler *prof, MonoClass *klass); + typedef void (*MonoProfileAllocFunc) (MonoProfiler *prof, MonoObject *obj, MonoClass *klass); + typedef void (*MonoProfileClassResult) (MonoProfiler *prof, MonoClass *klass, int result); + + extern void mono_profiler_install (MonoProfiler *prof, MonoProfileFunc shutdown_callback); + extern void mono_profiler_install_allocation (MonoProfileAllocFunc callback); + extern void mono_profiler_install_class (MonoProfileClassFunc start_load, MonoProfileClassResult end_load, MonoProfileClassFunc start_unload, MonoProfileClassFunc end_unload); + extern void mono_profiler_install_gc (MonoProfileGCFunc callback, MonoProfileGCResizeFunc heap_resize_callback); + extern void mono_profiler_set_events (MonoProfileFlags events); + */ + + struct MonoThreadsSync; + struct MonoObject + { + MonoVTable *vtable; + MonoThreadsSync *synchronisation; + } ; +} + +typedef std::map<MonoObject*, MonoClass*> ObjectsHash; +typedef std::map<MonoClass*, MonoClass*> ClassHash; +typedef std::vector<UInt8> HeapShotData; + +MonoProfiler* HeapShotCreateMonoProfiler (const char *outfilename); +void HeapShotAllocFunc (MonoProfiler *p, MonoObject *obj, MonoClass *klass); +void HeapShotGCFunc (MonoProfiler *p, MonoGCEvent e, int gen); +void HeapShotLoadClassFunc (MonoProfiler *p, MonoClass *klass, int result); +void HeapShotUnloadClassFunc (MonoProfiler *p, MonoClass *klass); +void HeapShotDumpObjectMap (HeapShotData& data); +void InstallMonoHeapshot(); +#endif + +#endif diff --git a/Runtime/Mono/MonoHeapShotWriter.cpp b/Runtime/Mono/MonoHeapShotWriter.cpp new file mode 100644 index 0000000..ae615c0 --- /dev/null +++ b/Runtime/Mono/MonoHeapShotWriter.cpp @@ -0,0 +1,267 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- */ + +/* + * outfile-writer.c + * + * Copyright (C) 2005 Novell, Inc. + * + */ + +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of version 2 of the GNU General Public + * License as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + * USA. + */ +#include "UnityPrefix.h" + +#include "MonoHeapShotWriter.h" + +#if ENABLE_MONO_HEAPSHOT +#include "Runtime/Serialize/SwapEndianBytes.h" +#include "Runtime/Scripting/ScriptingUtility.h" +#include "Runtime/Allocator/LinearAllocator.h" +#include "Runtime/Mono/MonoBehaviour.h" + +#define MAGIC_NUMBER 0x4eabfdd1 +#define FILE_FORMAT_VERSION 6 +#define FILE_LABEL "heap-shot logfile" +#define TAG_TYPE 0x01 +#define TAG_OBJECT 0x02 +#define TAG_UNITY 0x03 +#define TAG_EOS 0xff + + + +extern "C" +{ +// extern char* mono_type_full_name (MonoType *type); + void mono_free (void* p) + { + g_free(p); + } +} + + +MonoHeapShotWriter::MonoHeapShotWriter() +{ + m_Data = NULL; + m_SeenItems = new ClassHash(); +} +MonoHeapShotWriter::~MonoHeapShotWriter() +{ + delete m_SeenItems; +} +void MonoHeapShotWriter::Start(HeapShotData* data) +{ + m_Data = data; + m_Data->clear(); + m_SeenItems->clear(); + WriteUInt32 (MAGIC_NUMBER); + WriteInt32 (FILE_FORMAT_VERSION); + WriteString (FILE_LABEL); + m_SavedOutfileOffset = m_Data->size(); + // we update these after dumping all objects + WriteInt32 (0); // total # of types + WriteInt32 (0); // total # of objects + WriteInt32 (0); // total # of references + WriteInt32 (0); // total # of fields +} + +void MonoHeapShotWriter::End() +{ + // Write out the end-of-stream tag. + WriteByte (TAG_EOS); + + memcpy(&(*m_Data)[m_SavedOutfileOffset], &m_TypeCount, sizeof(SInt32)); + memcpy(&(*m_Data)[m_SavedOutfileOffset + sizeof(SInt32)], &m_ObjecCount, sizeof(SInt32)); + memcpy(&(*m_Data)[m_SavedOutfileOffset + sizeof(SInt32) * 2], &m_ReferenceCount, sizeof(SInt32)); + memcpy(&(*m_Data)[m_SavedOutfileOffset + sizeof(SInt32) * 3], &m_FieldCount, sizeof(SInt32)); +} +void MonoHeapShotWriter::BeginObjectDump(MonoObject* obj, MonoClass* klass) +{ + char *name; + + /* First, add this type if we haven't seen it before. */ + //if (g_hash_table_lookup (m_SeenItems, klass) == NULL) + if (m_SeenItems->find(klass) == m_SeenItems->end()) + { + MonoClassField *field; + MonoClass *cur_klass = klass; + void* iter; + + name = mono_type_full_name (mono_class_get_type (klass)); + WriteByte (TAG_TYPE); + WritePointer (klass); + WriteString (name); + mono_free (name); + (*m_SeenItems)[klass] = klass; + //g_hash_table_insert (m_SeenItems, klass, klass); + ++m_TypeCount; + + // Write every field + do + { + iter = NULL; + while ((field = mono_class_get_fields (cur_klass, &iter)) != NULL) { + WritePointer (field); + WriteString (mono_field_get_name (field)); + m_FieldCount++; + } + cur_klass = mono_class_get_parent (cur_klass); + } + while (cur_klass); + WritePointer (NULL); + } + + + WriteByte (TAG_OBJECT); + if (obj) + { + WritePointer ((void*)obj); // id of the object + WritePointer (klass); // class + WriteInt32 ((SInt32)mono_object_get_size (obj)); + } + else + { + // Used to register references from static class members + WritePointer ((void*)klass); + WritePointer (klass); + WriteInt32 ((SInt32)0); + } + m_ObjecCount++; + +} + +void MonoHeapShotWriter::EndObjectDump() +{ + WritePointer (NULL); // no more references +} + +void MonoHeapShotWriter::DumpReference(void* ref, void* field) +{ + WritePointer (ref); + WritePointer (field); + m_ReferenceCount++; +} +void MonoHeapShotWriter::DumpObjectsReferencedByUnity() +{ + FindMode mode = Scripting::kFindAssets; + MonoClass* compareKlass = GetMonoManager().GetBuiltinMonoClass ("Object", false); + if (compareKlass == NULL) + { + ErrorString ("DumpObjectsReferencedByUnity: Invalid Type"); + return ; + } + + int classID = GetClassIDFromScriptingClass (compareKlass); + + if (classID == -1) + { + string klassName = mono_class_get_name (compareKlass); + ErrorString ("DumpObjectsReferencedByUnity: The type has to be derived from UnityEngine.Object. Type is " + klassName + "."); + return ; + } + + // Gather the derived objects + std::vector<SInt32> objects; + Object::FindAllDerivedObjects (classID, &objects); + + sort(objects.begin(), objects.end()); + + // We might need to ignore some objects which are not derived from the mono class but from MonoBehaviour + // so we store them in a buffer and then copy them into the mono array + MonoObject** monoObjects; + ALLOC_TEMP(monoObjects, MonoObject*, objects.size(), kMemProfiler); + + + int count = 0; + for (int i=0;i<objects.size ();i++) + { + Object& object = *PPtr<Object> (objects[i]); + if (object.TestHideFlag (Object::kDontSave) && mode != Scripting::kFindAnything) + continue; + + + MonoObject* mono = ScriptingWrapperFor (&object); + if (mono) + { + if (object.IsDerivedFrom (ClassID (MonoBehaviour))) + { + MonoClass* klass = mono_object_get_class (mono); + if (mono_class_is_subclass_of (klass, compareKlass, true)) + monoObjects[count++] = mono; + } + else + monoObjects[count++] = mono; + } + } + WriteByte (TAG_UNITY); + for (int i = 0; i < count; i++) + { + WritePointer ((void*)monoObjects[i]); + } + WritePointer(0); +} +void MonoHeapShotWriter::WriteByte (UInt8 x) +{ + m_Data->push_back (x); +} + +void MonoHeapShotWriter::WritePointer (void* x) +{ + UInt32 y = (UInt32) (x); + WriteUInt32 (y); +} + +void MonoHeapShotWriter::WriteInt32 (SInt32 x) +{ + SwapEndianBytesLittleToNative(x); + int o = m_Data->size(); + m_Data->resize(o + sizeof(UInt32)); + memcpy(&(*m_Data)[o], &x, sizeof(SInt32)); +} + +void MonoHeapShotWriter::WriteUInt32 (UInt32 x) +{ + SwapEndianBytesLittleToNative(x); + int o = m_Data->size(); + m_Data->resize(o + sizeof(UInt32)); + memcpy(&(*m_Data)[o], &x, sizeof(UInt32)); +} + +void MonoHeapShotWriter::WriteVInt (UInt32 x) +{ + UInt8 y; + do + { + y = (UInt8) (x & 0x7f); + x = x >> 7; + if (x != 0) y |= 0x80; + WriteByte (y); + } + while (x != 0); +} + +void MonoHeapShotWriter::WriteString (const char *str) +{ + int len = strlen (str); + WriteVInt ((UInt32) len); + if (len > 0) + { + int o = m_Data->size(); + m_Data->resize(o + len); + memcpy(&(*m_Data)[o], str, len); + } +} + +#endif diff --git a/Runtime/Mono/MonoHeapShotWriter.h b/Runtime/Mono/MonoHeapShotWriter.h new file mode 100644 index 0000000..0e86996 --- /dev/null +++ b/Runtime/Mono/MonoHeapShotWriter.h @@ -0,0 +1,64 @@ +#ifndef MONOHEAPSHOTWRITER_H +#define MONOHEAPSHOTWRITER_H + +#include "Runtime/Mono/MonoTypes.h" +#include "Runtime/Scripting/ScriptingUtility.h" +#include "MonoHeapShot.h" + +#if ENABLE_MONO_HEAPSHOT + + +class MonoHeapShotWriter +{ +public: + MonoHeapShotWriter(); + ~MonoHeapShotWriter(); + + void Start(HeapShotData* data); + + void End(); + + void DumpObjectsReferencedByUnity(); + + void BeginObjectDump(MonoObject* obj, MonoClass* klass); + + void EndObjectDump(); + + void DumpReference(gpointer ref, gpointer field); + + inline const UInt8* GetData() const + { + return &(*m_Data)[0]; + } + inline UInt32 GetDataSize() const + { + return m_Data->size(); + } + +private: + void WriteByte (UInt8 x); + + void WritePointer (void* x); + + void WriteInt32 (SInt32 x); + + void WriteUInt32 (UInt32 x); + + void WriteVInt (UInt32 x); + + void WriteString (const char *str); + + typedef std::vector<UInt8> Data; + HeapShotData* m_Data; + ClassHash* m_SeenItems; + int m_TypeCount; + int m_ObjecCount; + int m_ReferenceCount; + int m_FieldCount; + long m_SavedOutfileOffset; +}; + +#endif + +#endif + diff --git a/Runtime/Mono/MonoIncludes.h b/Runtime/Mono/MonoIncludes.h new file mode 100644 index 0000000..4b69b9d --- /dev/null +++ b/Runtime/Mono/MonoIncludes.h @@ -0,0 +1,40 @@ +#ifndef MONOINCLUDES_H +#define MONOINCLUDES_H + +#include "Configuration/UnityConfigure.h" +#include "MonoTypes.h" +#include "MonoTypeSignatures.h" + +#ifndef __cplusplus +#error somewhat unexpected +#endif + + +extern "C" +{ + +// on windows and linux, we always load mono functions dynamically + +#if ENABLE_MONO + +#define mono_string_chars(s) ((gunichar2*)&((s)->firstCharacter)) +#define mono_string_length(s) ((s)->length) + +#if (WEBPLUG || UNITY_WIN || UNITY_LINUX) && !UNITY_PEPPER + +#define DO_API(r,n,p) extern EXPORT_COREMODULE r (*n) p; +#include "MonoFunctions.h" + +#else // WEBPLUG || UNITY_WIN || UNITY_LINUX + +#define DO_API(r,n,p) r n p; +#define DO_API_NO_RETURN(r,n,p) DOES_NOT_RETURN r n p; +#include "MonoFunctions.h" + +#endif // WEBPLUG || UNITY_WIN || UNITY_LINUX + +#endif // ENABLE_MONO + +} + +#endif diff --git a/Runtime/Mono/MonoManager.cpp b/Runtime/Mono/MonoManager.cpp new file mode 100644 index 0000000..871b9d0 --- /dev/null +++ b/Runtime/Mono/MonoManager.cpp @@ -0,0 +1,2456 @@ +#include "UnityPrefix.h" +#include "Configuration/UnityConfigure.h" // include before anything, to get Prof_ENABLED if that is defined + +#if ENABLE_MONO +#include "MonoManager.h" +#include "MonoScript.h" +#include "MonoBehaviour.h" +#include "Runtime/BaseClasses/ManagerContext.h" +#include "Runtime/Serialize/TransferFunctions/SerializeTransfer.h" +#include "MonoIncludes.h" +#include "Runtime/Serialize/PersistentManager.h" +#include "Runtime/Serialize/TransferUtility.h" +#include "Runtime/Serialize/TypeTree.h" +#include "Runtime/BaseClasses/IsPlaying.h" +#include "Runtime/Utilities/PathNameUtility.h" +#include "Runtime/Export/MonoICallRegistration.h" +#include "Runtime/Utilities/Word.h" +#include <stdlib.h> +#include "Runtime/Misc/Plugins.h" +#include "Runtime/Threads/Mutex.h" +#include "Runtime/Utilities/File.h" +#include "Runtime/Utilities/FileUtilities.h" +#include "Runtime/Serialize/FileCache.h" +#include "Runtime/Profiler/ProfilerImpl.h" +#include "Runtime/Misc/PreloadManager.h" +#include "Runtime/Misc/BuildSettings.h" +#include "Runtime/Utilities/PlayerPrefs.h" +#include "Runtime/Scripting/CommonScriptingClasses.h" +#include "Runtime/Scripting/ScriptingUtility.h" +#include "Runtime/Audio/AudioManager.h" +#include "Runtime/Utilities/Stacktrace.h" +#include "Runtime/Profiler/TimeHelper.h" +#include "Runtime/Misc/DeveloperConsole.h" +#include "Runtime/Scripting/ScriptingExportUtility.h" +#include "Runtime/Scripting/Backend/ScriptingInvocation.h" +#include "Runtime/Scripting/Backend/Mono/ScriptingMethodFactory_Mono.h" +#include "Runtime/Scripting/Backend/ScriptingMethodRegistry.h" +#include "Runtime/Scripting/Backend/ScriptingTypeRegistry.h" +#include "Runtime/Core/Callbacks/GlobalCallbacks.h" +#include "Runtime/Scripting/Scripting.h" + +#if UNITY_EDITOR +#include "Editor/Src/EditorModules.h" +#endif + +#ifndef USE_ASSEMBLY_PREPROCESSOR +#define USE_ASSEMBLY_PREPROCESSOR 0 +#endif + +#if WEBPLUG +#include "PlatformDependent/CommonWebPlugin/CompressedFileStream.h" +#include "Runtime/Utilities/ErrorExit.h" +#define PLAYER_DATA_FOLDER "" +#endif +#if USE_ASSEMBLY_PREPROCESSOR +#include "AssemblyModifier.h" +#include "AssemblyModifierOnDisk.h" +#endif + +#if UNITY_EDITOR +#include "Editor/Src/EditorSettings.h" +#include "Editor/Src/EditorHelper.h" +#include "Editor/Platform/Interface/BugReportingTools.h" +#include "Runtime/Utilities/Argv.h" +#include "Editor/Platform/Interface/EditorUtility.h" +#include "Editor/Src/EditorUserBuildSettings.h" +#define PLAYER_DATA_FOLDER "" +#endif +#if UNITY_OSX || UNITY_LINUX +#include <dlfcn.h> +#endif +#if UNITY_WIN +#include "Configuration/UnityConfigureOther.h" +#include "PlatformDependent/Win/PathUnicodeConversion.h" +#include "PlatformDependent/Win/WinUtils.h" +#include <signal.h> +#endif +#if UNITY_WII +#include "PlatformDependent/wii/WiiUtility.h" +#endif +#if UNITY_OSX +#include <mach/task.h> +#include <sys/stat.h> +#endif +#ifdef _MSC_VER +#define va_copy(a,z) ((void)((a)=(z))) +#endif + +#if ENABLE_PLAYERCONNECTION +#include "Runtime/Network/PlayerCommunicator/PlayerConnection.h" +#endif + +#ifndef PLAYER_DATA_FOLDER +#include "Runtime/Misc/Player.h" +#define PLAYER_DATA_FOLDER SelectDataFolder() +#endif + +#define SUPPORT_MDB_FILES (UNITY_EDITOR || WEBPLUG) +#define USE_MONO_DOMAINS (UNITY_EDITOR || WEBPLUG) && !UNITY_PEPPER +// Turn off the extra domain for now. It doesn't buy us anything until +// we get some sharing in the runtime. Right now, it loads the assembly +// into the extra domain but the code will be jitted again in the child domain. +#define USE_TWO_MONO_DOMAINS 0 + +// Editor and development console players always load mdbs +#define ALWAYS_LOAD_MDBS (UNITY_EDITOR || ((UNITY_XENON || UNITY_WII || UNITY_PS3) && !MASTER_BUILD) || ENABLE_PROFILER) + +#if UNITY_EDITOR +extern "C" +{ + void debug_mono_images_leak (); +} + +static const char* kEditorAssembliesPath = "Library/ScriptAssemblies"; + +#endif // UNITY_EDITOR + +#if ENABLE_MONO_MEMORY_PROFILER +void mono_profiler_startup (); +#endif + +int* s_MonoDomainContainer = NULL; + +UNITY_TLS_VALUE(bool) MonoManager::m_IsMonoBehaviourInConstructor; + +#if UNITY_WIN && UNITY_WIN_ENABLE_CRASHHANDLER && !UNITY_WINRT + +#define USE_WIN_CRASH_HANDLER 1 +// We do cunning things with crash handler and mono - we disable +// it when mono is initialized (because mono installs its own handler), +// but setup a callback so that we can still launch bug reporter. +#include "../../Tools/BugReporterWin/lib/CrashHandler.h" +extern CrashHandler* gUnityCrashHandler; + +#else +#define USE_WIN_CRASH_HANDLER 0 +#endif + +static void UnloadDomain (); +static MonoDomain* CreateAndSetChildDomain(); +static void UnhandledExceptionHandler (MonoObject* object); +double GetTimeSinceStartup (); + +using namespace std; + +static const char* kEngineAssemblyName = "UnityEngine.dll"; +static const char* kEditorInternalNameSpace = "UnityEditorInternal"; +#if MONO_2_12 +const char* kMonoClasslibsProfile = "4.5"; +#else +const char* kMonoClasslibsProfile = "2.0"; +#endif + +#if UNITY_STANDALONE || UNITY_EDITOR +static const char* gNativeLibsDir = NULL; +#endif + +static MonoVTable** gClassIDToVTable = NULL; + +ScriptsDidChangeCallback* gUnloadDomainCallback = NULL; + +static bool gDebuggerEnabled = false; + +#if UNITY_PEPPER +void *gCorLibMemory = NULL; +#endif + +struct DomainReloadingData { + std::vector<SInt32> m_SavedBehaviours; + PPtr<MonoBehaviour> m_SavedScriptReloadProperties; + ABSOLUTE_TIME reloadStart; +}; + +void RegisterUnloadDomainCallback (ScriptsDidChangeCallback* call) +{ + AssertIf(gUnloadDomainCallback != NULL); + gUnloadDomainCallback = call; +} + +static void ExtractMonoStacktrace (const std::string& condition, std::string& processedStackTrace, std::string& stackTrace, int errorNum, string& file, int* line, int type, int targetInstanceID); + +#if UNITY_OSX +void HandleSignal (int i, __siginfo* info, void* p); +#define UNITY_SA_DISABLE 0x0004 // disable taking signals on alternate stack +#endif +#if UNITY_WIN +int HandleSignal( EXCEPTION_POINTERS* ep ); +void HandleAbort (int signal); +#endif + +#if UNITY_EDITOR +static std::string GetBuildToolsEngineDllPathIfExists (BuildTargetPlatform target) +{ + string buildToolsDirectory = GetBuildToolsDirectory(target, false); + if ( IsDirectoryCreated(buildToolsDirectory) ) + { + string engineDLLPath = AppendPathName(buildToolsDirectory, kEngineAssemblyName); + if (IsFileCreated(engineDLLPath)) + return engineDLLPath; + } + return ""; +} +#endif // UNITY_EDITOR + +#if UNITY_STANDALONE || UNITY_EDITOR +static void * mono_fallback_dlopen (const char* name, int flags, char **err, void *user_data) +{ + if (IsAbsoluteFilePath (name)) + return NULL; + + void* handle = NULL; + string fullPath = AppendPathName (gNativeLibsDir, name); + + #if (UNITY_WIN && !UNITY_WINRT) + std::wstring widePath; + ConvertUnityPathName (fullPath, widePath); + handle = (void *) LoadLibraryW (widePath.c_str ()); + + #elif UNITY_OSX + handle = dlopen (fullPath.c_str (), RTLD_NOW); + + #elif UNITY_XENON + handle = cached_module_load (fullPath.c_str (), MONO_DL_LAZY, 0); + #endif + + if (!handle && err) { + printf_console ("Fallback handler could not load library %s\n", fullPath.c_str()); + } + return handle; +} + +static void* mono_fallback_lookup_symbol (void *handle, const char *name, char **err, void *user_data) +{ + void* symbol = NULL; + #if (UNITY_WIN && !UNITY_WINRT) + symbol = GetProcAddress ((HMODULE)handle, name); + + #elif UNITY_OSX + symbol = dlsym (handle, name); + + #elif UNITY_XENON + char* ret = mono_dl_symbol ((MonoDl*)handle, name, &symbol); + if (!symbol && err) + *err = ret; + #endif + + #if !UNITY_XENON + if (!symbol && err) { + printf_console ("Fallback handler could not load symbol %s\n", name); + } + #endif + + return symbol; +} + +static void* mono_fallback_close (void *handle, void *user_data) +{ + #if (UNITY_WIN && !UNITY_METRO) + FreeLibrary ((HMODULE) handle); + + #elif UNITY_OSX + dlclose (handle); + + #endif + + return NULL; +} +#endif + +MonoManager::MonoManager (MemLabelId label, ObjectCreationMode mode) +: ScriptingManager(label, mode, this, UNITY_NEW(ScriptingMethodFactory_Mono(), kMemManager)) +{ + m_AssemblyReferencingDomain = NULL; + + #if UNITY_PLUGINS_AVAILABLE + mono_set_find_plugin_callback ((gconstpointer)FindAndLoadUnityPlugin); + #endif + + #if UNITY_EDITOR + m_LogAssemblyReload = false; + #endif + + #if UNITY_STANDALONE || UNITY_EDITOR + gNativeLibsDir = strdup(GetApplicationNativeLibsPath().c_str ()); + mono_dl_fallback_register (mono_fallback_dlopen, mono_fallback_lookup_symbol, mono_fallback_close, NULL); + #endif + + m_HasCompileErrors = false; + + CleanupClassIDMaps(); +} + +MonoManager::~MonoManager () +{ + gClassIDToVTable = NULL; + gClassIDToClass = NULL; + RegisterLogPreprocessor (NULL); +} + +AssemblyMask MonoManager::GetSystemAssemblyMask (bool load) +{ + AssemblyMask assemblies(kScriptAssemblies); + assemblies[kEngineAssembly] = load; + #if UNITY_EDITOR + assemblies[kEditorAssembly] = load; + assemblies[kLocatorAssembly] = load; + #endif + + return assemblies; +} + + +AssemblyMask MonoManager::GetAvailableDllAssemblyMask () +{ + AssemblyMask assemblies(GetAssemblyCount()); + assemblies[kEngineAssembly]=true; + #if UNITY_EDITOR + assemblies[kEditorAssembly]=true; + assemblies[kLocatorAssembly]=true; + #endif + + for (int i=kScriptAssemblies;i<GetAssemblyCount();i++) + { + string path = GetAssemblyPath (i); + #if UNITY_XENON + path += ".mono"; + #endif + #if !UNITY_PEPPER + if (IsFileCreated (path)) + assemblies[i] = true; + #endif + + #if WEBPLUG + if (CompressedFileStream::Get().Find(GetLastPathNameComponent(path))) + assemblies[i] = true; + #if UNITY_PEPPER + if (CompressedFileStream::GetResources().Find(GetLastPathNameComponent(path))) + assemblies[i] = true; + #endif + #endif + } + + return assemblies; +} + +void MonoManager::AwakeFromLoad (AwakeFromLoadMode awakeMode) +{ + Super::AwakeFromLoad (awakeMode); + + // Load assemblies in player + // In editor MonoImporter Reloads the assemblies + #if GAMERELEASE + ReloadAssembly (GetAvailableDllAssemblyMask()); + #endif + +#if ENABLE_SERIALIZATION_BY_CODEGENERATION + ScriptingInvocation initManagedAnalysis(GetScriptingMethodRegistry().GetMethod("UnityEngine.Serialization","ManagedLivenessAnalysis","Init")); + initManagedAnalysis.Invoke(); + + ScriptingInvocation initWriter(GetScriptingMethodRegistry().GetMethod("UnityEngine.Serialization","SerializedStateWriter","Init")); + initWriter.Invoke(); + + ScriptingInvocation initReader(GetScriptingMethodRegistry().GetMethod("UnityEngine.Serialization","SerializedStateReader","Init")); + initReader.Invoke(); + + ScriptingInvocation initRemapper(GetScriptingMethodRegistry().GetMethod("UnityEngine.Serialization","PPtrRemapper","Init")); + initRemapper.Invoke(); +#endif + + // Disable Mono stacktrace in non-development (release) player builds + #if UNITY_EDITOR || UNITY_DEVELOPER_BUILD + RegisterLogPreprocessor (ExtractMonoStacktrace); + #endif +} + +#if !UNITY_RELEASE +void MonoManager::AssertInvalidAssembly (MonoClass* klass) +{ + if (klass == NULL) + return; + + MonoImage* image = mono_class_get_image(klass); + if (image == mono_get_corlib()) + return; + + for (int i=0;i<m_ScriptImages.size();i++) + { +// if (m_ScriptImages[i]) +// printf_console("compare against image file %s\n", mono_image_get_filename(m_ScriptImages[i])); + + if (m_ScriptImages[i] == image) + return; + } + printf_console("with error class %p \n", klass); + printf_console("with name %s \n", mono_class_get_name(klass)); + printf_console("with image %p \n", image); + printf_console("will image file name %s\n", mono_image_get_filename(image)); + printf_console("Mono class %s is in an invalid assembly %s ! BUG", mono_class_get_name(klass), mono_image_get_filename(image)); + #if UNITY_EDITOR + ErrorString("Invalid assembly loaded"); + #endif + printf_console("\n\n\n\n"); +} +#endif + +#if UNITY_EDITOR + +void MonoManager::ResizeAssemblyNames(int max) +{ + m_AssemblyNames.clear(); + m_AssemblyNames.resize(max); +} + +int MonoManager::InsertAssemblyName(const std::string& assemblyName) +{ + int index = GetAssemblyIndexFromAssemblyName(assemblyName); + if (index != -1) + return index; + + index = GetAssemblyCount(); + m_AssemblyNames.push_back(assemblyName); + return index; +} + +void MonoManager::SetAssemblyName (unsigned assemblyIndex, const string& name) +{ + Assert(assemblyIndex <= m_AssemblyNames.size ()); + m_AssemblyNames[assemblyIndex] = name; +} + + +void MonoManager::SetCustomDllPathLocation (const std::string& name, const std::string& path) +{ + if (m_CustomDllLocation[name] != path) + { + ////@TODO: Create warnings for duplicate names + m_CustomDllLocation[name] = path; + } +} + +/* +std::string MonoManager::AddCustomDll (const std::string& path) +{ + if (!DetectDotNetDll(path)) + return ""; + + m_CustomDlls.insert(path); + + set<std::string> names; + + // Warn if a .dll name exists twice + for (set<std::string>::iterator i=m_CustomDlls.begin();i != m_CustomDlls.end();i++) + { + const std::string& dllpath = *i; + if (!IsFileCreated (dllpath)) + continue; + + if(!DetectDotNetDll(dllpath)) + continue; + + string name = GetLastPathNameComponent(dllpath); + if (!names.insert (name).second) + return Format("A .dll with name %s exists two times. The dll '%s' will be ignored.", name.c_str(), dllpath.c_str()); + } + + return ""; +} +*/ + +void MonoManager::SetHasCompileErrors (bool compileErrors) +{ + m_HasCompileErrors = compileErrors; +} + +#endif + +#if UNITY_EDITOR +static int gMonoPathsIndexOfManagedFolder = 0; +//#else +//static int gMonoPathsIndexOfManagedFolder = 1; +#endif + + +namespace MonoPathContainer +{ + std::vector<std::string>* g_MonoPaths = NULL; + void StaticInitialize(){ g_MonoPaths = UNITY_NEW(std::vector<std::string>,kMemMono);} + void StaticDestroy(){UNITY_DELETE(g_MonoPaths,kMemMono);} + std::vector<std::string>& GetMonoPaths() {return *g_MonoPaths;} + void SetMonoPaths(const std::vector<std::string>& paths){*g_MonoPaths = paths;} + void AppendMonoPath (const string& path) + { +#if UNITY_WIN && !UNITY_WINRT + wchar_t widePath[kDefaultPathBufferSize]; + ConvertUnityPathName(path.c_str(), widePath, kDefaultPathBufferSize); + + wchar_t fullPath[kDefaultPathBufferSize]; + GetFullPathNameW(widePath, kDefaultPathBufferSize, fullPath, NULL); + + string unityPath; + ConvertWindowsPathName(fullPath, unityPath); +#else + string unityPath(path); +#endif + MonoPathContainer::GetMonoPaths().push_back(unityPath); + } +}; + +string MonoManager::GetAssemblyPath (int index) +{ + AssertIf (index < 0); + +#if UNITY_EDITOR + if (index == kEngineAssembly) + { + // Check if target specific assembly exists + std::string engineDLLPath = GetBuildToolsEngineDllPathIfExists (GetEditorUserBuildSettings().GetActiveBuildTarget()); + if (!engineDLLPath.empty ()) + { + return engineDLLPath; + } + else + { + return AppendPathName(MonoPathContainer::GetMonoPaths()[gMonoPathsIndexOfManagedFolder], m_AssemblyNames[index]); + } + } + else if (index < kScriptAssemblies) + { + return AppendPathName(MonoPathContainer::GetMonoPaths()[gMonoPathsIndexOfManagedFolder], m_AssemblyNames[index]); + } + else if (index < m_AssemblyNames.size ()) + { + CustomDllLocation::iterator found = m_CustomDllLocation.find(m_AssemblyNames[index]); + if (found != m_CustomDllLocation.end()) + return found->second; + else + return AppendPathName (kEditorAssembliesPath, m_AssemblyNames[index]); + } + else + return ""; +#elif WEBPLUG +#if !UNITY_PEPPER + if (index < kScriptAssemblies) + return AppendPathName(MonoPathContainer::GetMonoPaths()[0], m_AssemblyNames[index]); + else +#endif + if (index < m_AssemblyNames.size ()) + return m_AssemblyNames[index]; + else + return ""; +#else + return AppendPathName("Managed", m_AssemblyNames[index]); +#endif +} + +MonoAssembly* MonoManager::GetAssembly (int index) +{ + AssertIf (index < 0); + if (index < m_ScriptImages.size ()) + { + MonoImage* image = m_ScriptImages[index]; + if (image) + return mono_image_get_assembly (image); + } + return NULL; +} + +BackendNativeType MonoManager::NativeTypeFor(const char* namespaze, const char* className) +{ + return GetMonoClass(className,namespaze); +} + +ScriptingTypePtr MonoManager::Provide(BackendNativeType nativePtr) +{ + return (ScriptingTypePtr) nativePtr; +} + +ScriptingTypePtr Provide(void* nativeType); + +void MonoManager::Release(ScriptingTypePtr klass) +{ +} + +// If namespace is NULL then search is in any namespace +MonoClass* MonoManager::GetMonoClassCaseInsensitive (const char* className, const char* theNameSpace /*=NULL*/) +{ + for (ScriptImages::iterator i=m_ScriptImages.begin ();i != m_ScriptImages.end ();i++) + { + MonoImage* curImage = *i; + if (!curImage) + continue; + + MonoClass* klass = mono_class_from_name_case (curImage, theNameSpace, className); + if (klass) + return klass; + } + + return NULL; +} + +// If namespace is NULL then search is in any namespace +MonoClass* MonoManager::GetMonoClass (const char* className, const char* theNameSpace /*=NULL*/) +{ + MonoClass* klass = NULL; + MonoImage* curImage; + + klass = mono_class_from_name(mono_get_corlib(), theNameSpace, className); + + ///@todo: give compile error when classes with the same name are defined in different dll's + for (ScriptImages::iterator i=m_ScriptImages.begin ();i != m_ScriptImages.end () && klass == NULL;i++) + { + curImage = *i; + if (!curImage) + continue; + + klass = mono_class_from_name (curImage, theNameSpace, className); + } + + return klass; +} + +int MonoManager::GetAssemblyIndexFromAssemblyName (const string& name) +{ + vector<UnityStr>::iterator found = find (m_AssemblyNames.begin (), m_AssemblyNames.end (), name); + if (found == m_AssemblyNames.end ()) + return -1; + + return distance (m_AssemblyNames.begin (), found); +} + +MonoClass* MonoManager::GetMonoClassWithAssemblyName (const std::string& className, const string& nameSpace, const string& assemblyName) +{ + int index = GetAssemblyIndexFromAssemblyName (assemblyName); + MonoImage* image = 0; + if (index == -1) + { + MonoAssemblyName aname; +#if !UNITY_PEPPER || UNITY_NACL_WEBPLAYER + if (!mono_assembly_name_parse (assemblyName.c_str(),&aname)) + return NULL; +#endif + MonoAssembly* assembly = mono_assembly_loaded (&aname); + if (!assembly) return NULL; + image = mono_assembly_get_image(assembly); + } + else + { + if (index >= m_ScriptImages.size ()) return NULL; + image = m_ScriptImages[index]; + } + + if (!image) return NULL; + + return mono_class_from_name (image, nameSpace.c_str(), className.c_str ()); +} + +string MonoManager::GetAssemblyIdentifierFromImage(MonoImage* image) +{ + for (int i=0;i<m_ScriptImages.size ();i++) + { + if (m_ScriptImages[i] == image) + return m_AssemblyNames[i]; + } + return ""; +} + +int MonoManager::GetAssemblyIndexFromImage(MonoImage* image) +{ + for (int i=0;i<m_ScriptImages.size ();i++) + { + if (m_ScriptImages[i] == image) + return i; + } + return -1; +} + +#if UNITY_OSX +bool SameFileSystemEntry(const struct stat& s1, const struct stat& s2) +{ + return (s1.st_ino == s2.st_ino) && (s1.st_dev == s2.st_dev); +} +#endif + +bool IsPlatformPath(const std::string& path) +{ + std::vector<string>& monoPaths = MonoPathContainer::GetMonoPaths(); +#if UNITY_OSX + // On OSX we want to take symlinks into account + struct stat pathStat; + if (stat(path.c_str(), &pathStat) != 0) + return false; + + for (std::vector<string>::iterator i = monoPaths.begin(); i != monoPaths.end(); ++i) + { + struct stat platformStat; + if (stat((*i).c_str(), &platformStat) == 0 && SameFileSystemEntry(pathStat, platformStat)) + return true; + } + return false; +#elif UNITY_WIN + #if !UNITY_WINRT + string unityPath = ""; + + if (!path.empty()) + { + wchar_t widePath[kDefaultPathBufferSize]; + ConvertUnityPathName(path.c_str(), widePath, kDefaultPathBufferSize); + + wchar_t fullPath[kDefaultPathBufferSize]; + if (!GetFullPathNameW(widePath, kDefaultPathBufferSize, fullPath, NULL)) + fullPath[0] = L'\0'; + + ConvertWindowsPathName(fullPath, unityPath); + } + + std::vector<string>::iterator pos = std::find(monoPaths.begin(), monoPaths.end(), unityPath); + return pos != monoPaths.end(); + #else + #pragma message("todo: implement") // ?!- + return false; // ?!- + #endif +#else + std::vector<string>::iterator pos = std::find(monoPaths.begin(), monoPaths.end(), path); + return pos != monoPaths.end(); +#endif +} + +bool isPlatformCodeCallback(const char* image_name) +{ + std::string name(image_name); + ConvertSeparatorsToUnity(name); + + bool result = IsPlatformPath(DeleteLastPathNameComponent(name)); + printf_console(result ? "Platform assembly: %s (this message is harmless)\n" : "Non platform assembly: %s (this message is harmless)\n", image_name); + return result; +} + +static MonoAssembly* LoadAssemblyAndSymbolsWrapper(const string& absolutePath) +{ + MonoAssembly* assembly = mono_domain_assembly_open (mono_domain_get (), absolutePath.c_str()); + if (assembly) + { + #if USE_MONO_DEBUGGER + #if !ALWAYS_LOAD_MDBS + if (gDebuggerEnabled) + #endif + { + mono_debug_open_image_from_memory(mono_assembly_get_image(assembly), 0, 0); + } + #endif + } + return assembly; +} + +static MonoAssembly* LoadAssemblyWrapper (const void* data, size_t size, const char* name) +{ + #if (UNITY_PS3 || UNITY_XENON) + Assert(data == NULL); + string absolutePath = PathToAbsolutePath (name); + ConvertSeparatorsToPlatform (absolutePath); + + #if UNITY_XENON + // Try loading the .DLL.MONO from UPDATE: drive first. + // If it succeeds, mono will also load dependencies from the same path. + Assert(absolutePath.substr(0, 5) == "game:"); + string absoluteUpdatePath = "update" + absolutePath.substr(4); + MonoAssembly* updateAssembly = LoadAssemblyAndSymbolsWrapper(absoluteUpdatePath); + if (updateAssembly) + return updateAssembly; + #endif + + MonoAssembly* assembly = LoadAssemblyAndSymbolsWrapper(absolutePath); + return assembly; + + #else + + + // We can't use mono_image_open_file because we continously replace the file when compiling new dll's. + // But mono will keep the file locked, so we need to load it into memory. + InputString dataString; + + string absolutePath = PathToAbsolutePath(name); + ConvertSeparatorsToPlatform(absolutePath); + + if (data == NULL) + { + #if WEBPLUG + return mono_domain_assembly_open (mono_domain_get (), absolutePath.c_str()); + #endif + + if (!ReadStringFromFile (&dataString, absolutePath)) + return NULL; + + data = &dataString[0]; + size = dataString.size (); + } + + int status = 0; + MonoImage* image = mono_image_open_from_data_with_name ((char*)data, size, /*Copy data*/true, &status, false /* ref only*/, absolutePath.c_str()); + if (status != 0 || image == NULL) + { + printf_console("Failed loading assembly %s\n", name); + return NULL; + } + + #if USE_MONO_DEBUGGER + #if !ALWAYS_LOAD_MDBS + if (gDebuggerEnabled) + #endif + { + string absolutePathMdb = PathToAbsolutePath(MdbFile(name)); + ConvertSeparatorsToPlatform(absolutePathMdb); + + #if !UNITY_PEPPER + if (ReadStringFromFile(&dataString, absolutePathMdb)) + { + mono_debug_open_image_from_memory(image, &dataString[0], dataString.size ()); + } + #endif + } + #endif + + /// try out true as last argument (loads reflection only should be faster) + printf_console ("Loading %s into Unity Child Domain\n", absolutePath.c_str ()); + MonoAssembly* assembly = mono_assembly_load_from_full (image, absolutePath.c_str (), &status, false/* ref only*/); + if (status != 0 || assembly == NULL) + { + // clear reference added by mono_image_open_from_data_with_name + mono_image_close (image); + + printf_console("Failed loading assembly '%s'\n", name); + return NULL; + } + + // We are loading a new assembly and it is already loaded???? + if (mono_assembly_get_image(assembly) != image) + { + #if !UNITY_RELEASE + LogString(Format("Already loaded assembly '%s'", mono_image_get_name(mono_assembly_get_image(assembly)))); + #endif + } + + // clear reference added by mono_image_open_from_data_with_name + mono_image_close (image); + + return assembly; + #endif + +} + +void SetSecurityMode() +{ + bool enableCoreClr = false; + bool enableVerifier = false; + bool enableSocketSecurity = false; + +#if WEBPLUG && !UNITY_PEPPER + enableSocketSecurity = true; + enableCoreClr = true; + enableVerifier = true; +#endif + +#if UNITY_EDITOR + if (DoesBuildTargetUseSecuritySandbox(GetEditorUserBuildSettings().GetActiveBuildTarget ())) + { + enableSocketSecurity = true; + enableVerifier = true; + } +#endif + +#if MONO_2_12 + mono_security_core_clr_set_options ((MonoSecurityCoreCLROptions)(MONO_SECURITY_CORE_CLR_OPTIONS_RELAX_REFLECTION | MONO_SECURITY_CORE_CLR_OPTIONS_RELAX_DELEGATE)); +#endif + mono_security_set_core_clr_platform_callback (&isPlatformCodeCallback); + +#if !UNITY_PEPPER + if (enableCoreClr) + { + mono_security_set_mode (MONO_SECURITY_MODE_CORE_CLR); + } else { + mono_security_set_mode (MONO_SECURITY_MODE_NONE); + } + + if (enableVerifier) + mono_verifier_set_mode (MONO_VERIFIER_MODE_VERIFIABLE); + else + mono_verifier_set_mode (MONO_VERIFIER_MODE_OFF); +#endif + + #if !UNITY_XENON && !UNITY_PS3 && !UNITY_PEPPER && !UNITY_WII + mono_unity_socket_security_enabled_set (enableSocketSecurity); + #endif +} + + +#define LogAssemblyError(x) DebugStringToFile (x, 0, __FILE__, __LINE__, kLog | kAssetImportError, 0, GetInstanceID ()); +#if WEBPLUG + + + +MonoAssembly *LoadAssemblyWebPlug (string path) +{ + MonoAssembly *assembly; + CompressedFileStream::Data* data = CompressedFileStream::Get().Find(GetLastPathNameComponent(path)); +#if UNITY_PEPPER + if (!data) + data = CompressedFileStream::GetResources().Find(GetLastPathNameComponent(path)); +#endif + if (data) + { + MemoryCacherReadBlocks cacher (data->blocks, data->end, kCacheBlockSize); + UInt8* dataCpy; + ALLOC_TEMP(dataCpy, UInt8, data->GetSize()); + cacher.DirectRead(dataCpy, data->offset, data->GetSize()); +#if USE_ASSEMBLY_PREPROCESSOR + bool modify = true; + if (modify) + { + AssemblyModifier am; + InputString output; + bool success = am.PreProcessAssembly(dataCpy, data->GetSize(), output); + if (success) + assembly = LoadAssemblyWrapper (&output[0], output.size(), path.c_str ()); + } + if (assembly==NULL) +#endif + assembly = LoadAssemblyWrapper (dataCpy, data->GetSize(), path.c_str ()); + if (!assembly) return NULL; +#if !ALWAYS_LOAD_MDBS + if (gDebuggerEnabled) +#endif + { + //see if there is an mdb too + CompressedFileStream::Data* mdbdata = CompressedFileStream::Get().Find(GetLastPathNameComponent(MdbFile(path))); + #if UNITY_PEPPER + if (!mdbdata) + mdbdata = CompressedFileStream::GetResources().Find(GetLastPathNameComponent(MdbFile(path))); + #endif + if (mdbdata) + { + MemoryCacherReadBlocks mdbcacher (mdbdata->blocks, mdbdata->end, kCacheBlockSize); + UInt8* mdbdataCpy; + ALLOC_TEMP(mdbdataCpy, UInt8, mdbdata->GetSize()); + mdbcacher.DirectRead(mdbdataCpy, mdbdata->offset, mdbdata->GetSize()); + + MonoImage* image = mono_assembly_get_image(assembly); + mono_debug_open_image_from_memory(image, (const char*)mdbdataCpy, mdbdata->GetSize()); + } + } + } + else + assembly = LoadAssemblyWrapper (NULL, 0, path.c_str ()); + return assembly; +} + +MonoAssembly *AssemblyLoadHook (char **name, void* user_data) +{ + return LoadAssemblyWebPlug(string(*name)+".dll"); +} +#endif + +bool MonoManager::IsThisFileAnAssemblyThatCouldChange(std::string& path) +{ + std::string abspath = PathToAbsolutePath(path); + ConvertSeparatorsToUnity(abspath); + int startingScriptIndex = kScriptAssemblies; + +#if UNITY_EDITOR + if (IsDeveloperBuild()) + startingScriptIndex = kEngineAssembly; +#endif + + for (int i=startingScriptIndex; i<GetAssemblyCount(); i++) + { + std::string assemblypath = PathToAbsolutePath(GetAssemblyPath (i)); + ConvertSeparatorsToUnity(assemblypath); + + if (abspath == assemblypath) + return true; + } + return false; +} + + + +bool MonoManager::LoadAssemblies (AssemblyMask allAssembliesMask) +{ + bool firstLoad = false; + bool failedLoadingSomeAssemblies = false; + + // Load assemblies + for (int i = 0; i < GetAssemblyCount() && i < allAssembliesMask.size (); i++) + { + + // Does the assembly have to be reloaded? + if (allAssembliesMask[i]) + { + if (m_ScriptImages.empty () || m_ScriptImages.size () <= i) + m_ScriptImages.resize (max<int> (i + 1, m_ScriptImages.size ())); + + if (i >= kScriptAssemblies || m_ScriptImages[i] == NULL) + { + if (i < kScriptAssemblies) + firstLoad = true; + + string path = GetAssemblyPath (i); + m_ScriptImages[i] = NULL; + + // Load the script assembly, the script assembly might not be there + // if we dont have scripts this is valid + MonoAssembly* assembly = NULL; + #if WEBPLUG + assembly = LoadAssemblyWebPlug (path); + #elif UNITY_EDITOR + if ( i < kScriptAssemblies && !IsDeveloperBuild()) + { + string s = PathToAbsolutePath (path); + assembly = mono_domain_assembly_open (mono_domain_get (), s.c_str()); + } + else + assembly = LoadAssemblyWrapper (NULL, 0, path.c_str ()); + #else + assembly = LoadAssemblyWrapper (NULL, 0, path.c_str ()); + #endif + + MonoImage* newImage = assembly ? mono_assembly_get_image (assembly) : NULL; + + m_ScriptImages[i] = newImage; + if (newImage == NULL) + { + failedLoadingSomeAssemblies = true; + LogAssemblyError ("Loading script assembly \"" + path + "\" failed!"); + } + } + } + // Should we unload the assembly? + else if (i < m_ScriptImages.size ()) + { + m_ScriptImages[i] = NULL; + } + } + + if (firstLoad) + { + +#if !UNITY_PS3 && !UNITY_XENON && !UNITY_IPHONE + for (int i = 0; i < m_ScriptImages.size (); i++) + { + if (m_ScriptImages[i]) + { +#if USE_ANCIENT_MONO + if (!mono_assembly_preload_references (m_ScriptImages[i])) + { + printf_console ("Failed loading references %s\n", GetAssemblyPath (i).c_str ()); + m_ScriptImages[i] = NULL; + } +#endif + } + } + ScriptingInvocation ("UnityEngine","ClassLibraryInitializer","Init").Invoke (); +#endif +#if UNITY_IOS + CallStaticMonoMethod ("UnhandledExceptionHandler", "RegisterUECatcher"); +#endif + + } + + return failedLoadingSomeAssemblies; +} + +#if UNITY_EDITOR +void MonoManager::SetupLoadedEditorAssemblies () +{ + // Setup editor assemblies + MonoClass* editorAssemblies = GetMonoClass("EditorAssemblies", "UnityEditor"); + if (editorAssemblies) + { + MonoClass* assemblyClass = mono_class_from_name (mono_get_corlib (), "System.Reflection", "Assembly"); + + vector<MonoAssembly*> assemblies; + assemblies.reserve(64); + + for (int i=0;i<GetAssemblyCount();i++) + { + if (GetAssembly(i) && i != kEngineAssembly) + assemblies.push_back(GetAssembly(i)); + } + + MonoArray* array = mono_array_new (mono_domain_get (), assemblyClass, assemblies.size()); + for (int i=0;i<assemblies.size();i++) + { + Scripting::SetScriptingArrayElement(array, i, mono_assembly_get_object(mono_domain_get(), assemblies[i])); + } + + ScriptingInvocation invocation(editorAssemblies,"SetLoadedEditorAssemblies"); + invocation.AddArray(array); + invocation.Invoke(); + } +} +#endif + + +MonoManager::AssemblyLoadFailure MonoManager::ReloadAssembly (AssemblyMask allAssembliesMask) +{ + DomainReloadingData savedData; + MonoManager::AssemblyLoadFailure initialLoad = BeginReloadAssembly (savedData); + if (initialLoad == kFailedLoadingEngineOrEditorAssemblies) + return initialLoad; + +#if UNITY_EDITOR + GlobalCallbacks::Get().initialDomainReloadingComplete.Invoke(); + allAssembliesMask = GetAvailableDllAssemblyMask(); +#endif + + return EndReloadAssembly (savedData, allAssembliesMask); +} + +MonoManager::AssemblyLoadFailure MonoManager::BeginReloadAssembly (DomainReloadingData& savedData) +{ + printf_console ("Begin MonoManager ReloadAssembly\n"); + + // Make sure there are no preload operations queued up before we reload the domain. + GetPreloadManager().WaitForAllAsyncOperationsToComplete(); + StopPreloadManager(); + +#if UNITY_EDITOR + // if there's no script images on the list, no point in trying to stop, nothing has been loaded yet + if (!m_ScriptImages.empty()) + ShutdownManagedModuleManager (); +#endif + + savedData.reloadStart = START_TIME; + + RemoveErrorWithIdentifierFromConsole (GetInstanceID ()); + + #if GAMERELEASE + if (!m_ScriptImages.empty()) + ErrorString("Reload Assembly may not be called multiple times in the player"); + #endif + + #if DEBUGMODE + ///@TODO: Also assert when the preload manager is running, write a test for it + if (mono_method_get_last_managed ()) + { + ErrorString("Reload Assembly called from managed code directly. This will cause a crash. You should never refresh assets in synchronous mode or enter playmode synchronously from script code."); + return kFailedLoadingEngineOrEditorAssemblies; + } + #endif + + // Find and backup all Monobehaviours to the EditorExtensionImpl + // If we are in play mode we use kForceSerializeAllProperties which will serialize private and public properties. + // This makes it possible to reload script while in playmode + // Otherwise we use the normal backup mechanism which doesnt persist private properties. + + Object::FindAllDerivedObjects (ClassID(MonoBehaviour), &savedData.m_SavedBehaviours, false); + // sort all behaviours by script execution order (if set) and then instance ID, just like the persistent manager does + std::sort (savedData.m_SavedBehaviours.begin (), savedData.m_SavedBehaviours.end (), AwakeFromLoadQueue::SortBehaviourByExecutionOrderAndInstanceID); + MonoBehaviour* behaviour; + + #if UNITY_EDITOR + + // Allow static editor classes to store their state + if (GetAssembly(kEditorAssembly) != NULL) + CallStaticMonoMethod ("AssemblyReloadEvents", "OnBeforeAssemblyReload"); + // Store script reload properties (For example text editing state.) + // Only store them if there are any other monobehaviours around, because when loading scripts for the first + // time we don't want this because the resource manager has not yet registered the ScriptReloadProperties + if (GetAssembly(kEditorAssembly) != NULL && !savedData.m_SavedBehaviours.empty ()) + { + savedData.m_SavedScriptReloadProperties = ScriptingObjectToObject<MonoBehaviour> (CallStaticMonoMethod ("ScriptReloadProperties", "Store")); + if (savedData.m_SavedScriptReloadProperties.IsValid ()) + savedData.m_SavedBehaviours.push_back(savedData.m_SavedScriptReloadProperties.GetInstanceID ()); + } + + for (int i = 0; i < savedData.m_SavedBehaviours.size (); i++) + { + behaviour = PPtr<MonoBehaviour> (savedData.m_SavedBehaviours[i]); + if (behaviour == NULL) + continue; + + // Take backup just so that if we can load after reloading the assembly: Then we load the backup + // If we can't load after reloading the assembly, We just keep the backup on the behviour.. + if (behaviour->GetInstance ()) + { + Assert(behaviour->GetBackup() == NULL); + + int flags = kSerializeDebugProperties | kSerializeMonoReload; + + BackupState* backup = new BackupState(); + // Due to serialization to YAML not being super-fast, we backup all MonoBehaviours in binary mode + // and we convert (when saving) the backup to YAML only when reload of a script fails. + MonoBehaviour::ExtractBackupFromInstance (behaviour->GetInstance (), behaviour->GetClass (), *backup, flags); + behaviour->SetBackup(backup); + } + + // Release and unload. + // Calling RemoveFromManager here makes sure + if (behaviour->IsAddedToManager ()) + behaviour->RemoveFromManager (); + else + behaviour->WillUnloadScriptableObject(); + } + + for (int i = 0; i < savedData.m_SavedBehaviours.size (); i++) + { + behaviour = PPtr<MonoBehaviour> (savedData.m_SavedBehaviours[i]); + if (behaviour == NULL) + continue; + + behaviour->StopAllCoroutines(); + } + + #endif //#if UNITY_EDITOR + + // Release all mono behaviours + for (int i = 0; i < savedData.m_SavedBehaviours.size (); i++) + { + behaviour = PPtr<MonoBehaviour> (savedData.m_SavedBehaviours[i]); + if (behaviour == NULL) + continue; + +// #if !UNITY_EDITOR +// ErrorString(Format("MonoBehaviour not unloaded in web player unload %s instance: %d", behaviour->GetName().c_str(), behaviour->GetInstance())); +// #endif + + // Take backup just so that if we can load after reloading the assembly: Then we load the backup + behaviour->ReleaseMonoInstance (); + } + + // Release all Monohandles + vector<Object*> allObjects; + Object::FindObjectsOfType (ClassID(Object), &allObjects); + for (int i=0;i<allObjects.size();i++) + { + if (allObjects[i]->GetCachedScriptingObject() != NULL) + allObjects[i]->SetCachedScriptingObject(NULL); + } + + // Clear common scripting classes + ClearCommonScriptingClasses (m_CommonScriptingClasses); + m_ScriptingMethodRegistry->InvalidateCache(); + m_ScriptingTypeRegistry->InvalidateCache(); + + #if ENABLE_PROFILER + UnityProfiler::Get().CleanupMonoMethodCaches(); + #endif + +#if !USE_TWO_MONO_DOMAINS + AssemblyMask unloadMask = GetSystemAssemblyMask (false); + LoadAssemblies (unloadMask); +#endif + + #if USE_MONO_DOMAINS + + PopulateAssemblyReferencingDomain(); + + if (CreateAndSetChildDomain() == NULL) + { + return kFailedLoadingEngineOrEditorAssemblies; + } + +#if USE_TWO_MONO_DOMAINS + if (m_AssemblyReferencingDomain == NULL) + m_AssemblyReferencingDomain = mono_domain_create_appdomain("AssemblyReferencingDomain",NULL); +#endif + +#endif + + SetSecurityMode(); + + #if ENABLE_MONO_MEMORY_PROFILER + UnityProfiler::Get().SetupProfilerEvents (); + #endif + + // debug_mono_images_leak(); + + AssemblyMask loadMask = GetSystemAssemblyMask (true); + bool failedLoadingSomeAssemblies = LoadAssemblies(loadMask); + + mono_gc_collect (mono_gc_max_generation ()); + + if (failedLoadingSomeAssemblies) + { + for (int i=0;i<m_ScriptImages.size();i++) + m_ScriptImages[i] = NULL; + CleanupClassIDMaps(); + } + + return failedLoadingSomeAssemblies ? kFailedLoadingEngineOrEditorAssemblies : kEverythingLoaded; +} + +MonoManager::AssemblyLoadFailure MonoManager::EndReloadAssembly (const DomainReloadingData& savedData, AssemblyMask allAssembliesMask) +{ + MonoBehaviour* behaviour; + + bool failedLoadingSomeAssemblies = LoadAssemblies(allAssembliesMask); + + // Rebuild the m_ClassIDToMonoClass lookup table + RebuildClassIDToScriptingClass (); + RebuildCommonMonoClasses (); + +#if UNITY_EDITOR + + // Rebuild all MonoScript classes cached info + vector<MonoScript*> scripts; + Object::FindObjectsOfType (&scripts); + for (int i=0;i<scripts.size ();i++) + { + MonoScript& script = *scripts[i]; + MonoClass* klass = GetMonoClassWithAssemblyName (script.GetScriptClassName (), script.GetNameSpace(), script.GetAssemblyName ()); + #if !UNITY_RELEASE + AssertInvalidAssembly(klass); + #endif + + script.Rebuild (klass); + } + + // Rebuild all mono instances! + for (int i = 0; i < savedData.m_SavedBehaviours.size (); i++) + { + behaviour = PPtr<MonoBehaviour>(savedData.m_SavedBehaviours[i]); + if (behaviour == NULL) + continue; + behaviour->RebuildMonoInstance (SCRIPTING_NULL); + + if (behaviour->GetGameObjectPtr()) + behaviour->GetGameObjectPtr ()->SetSupportedMessagesDirty (); + } + + // Reload all MonoBehaviours from the Backup + for (int i = 0; i < savedData.m_SavedBehaviours.size (); i++) + { + behaviour = PPtr<MonoBehaviour>(savedData.m_SavedBehaviours[i]); + if (behaviour == NULL) + continue; + + BackupState* backup = behaviour->GetBackup (); + + // We now have an instance + // So read the state from the behaviours own backup + if (backup != NULL && behaviour->GetInstance ()) + { + int flags = 0; + if (!backup->loadedFromDisk) + flags = kSerializeDebugProperties | kSerializeMonoReload; + + behaviour->RestoreInstanceStateFromBackup (*backup, flags); + behaviour->SetBackup (NULL); + } + + AssertIf (behaviour->GetInstance () != NULL && behaviour->GetBackup () != NULL); + } + + + // Add to manager and Awake from load comes last because we want everything to have the correct backup already loaded and ready. + for (int i = 0; i < savedData.m_SavedBehaviours.size (); i++) + { + behaviour = PPtr<MonoBehaviour> (savedData.m_SavedBehaviours[i]); + if (behaviour == NULL) + continue; + + behaviour->CheckConsistency (); + behaviour->AwakeFromLoad (kDefaultAwakeFromLoad); + + behaviour->DidReloadDomain(); + } + + /////////@TODO: Switch this to didReloadMonoDomain + // Notify AudioScriptBufferManager to reload buffers + if (GetAudioManager().GetScriptBufferManagerPtr()) + GetAudioManager().GetScriptBufferManager().DidReloadDomain(); + + SetupLoadedEditorAssemblies (); + + GlobalCallbacks::Get().didReloadMonoDomain.Invoke(); + + if (m_LogAssemblyReload) + { + LogString("Mono: successfully reloaded assembly\n"); + } + else + { + printf_console ("Mono: successfully reloaded assembly\n"); + } + #else + ErrorIf(Object::FindAllDerivedObjects (ClassID(MonoBehaviour), NULL) != 0); + ErrorIf(Object::FindAllDerivedObjects (ClassID(MonoScript), NULL) != 0); + #endif + + #if UNITY_EDITOR + + // Allow static editor classes to reconstruct their state + if (GetAssembly(kEditorAssembly) != NULL) + CallStaticMonoMethod ("AssemblyReloadEvents", "OnAfterAssemblyReload"); + + if (savedData.m_SavedScriptReloadProperties.IsValid ()) + { + void* arg[] = { Scripting::ScriptingWrapperFor(savedData.m_SavedScriptReloadProperties) }; + CallStaticMonoMethod("ScriptReloadProperties", "Load", arg); + + DestroySingleObject (savedData.m_SavedScriptReloadProperties); + } + #endif + + printf_console("- Completed reload, in %6.3f seconds\n", GetElapsedTimeInSeconds(savedData.reloadStart)); + + return failedLoadingSomeAssemblies ? kFailedLoadingScriptAssemblies : kEverythingLoaded; +} + +void MonoManager::RebuildClassIDToScriptingClass () +{ + ScriptingManager::RebuildClassIDToScriptingClass(); + int size = m_ClassIDToMonoClass.size(); + m_ClassIDToVTable.clear(); + m_ClassIDToVTable.resize (size,NULL); + gClassIDToVTable = &m_ClassIDToVTable[0]; + + for(int i=0; i!=size; i++) + { + if (m_ClassIDToMonoClass[i]==NULL) continue; + m_ClassIDToVTable[i] = mono_class_vtable(mono_domain_get(), m_ClassIDToMonoClass[i]); + + AssertIf ((m_ClassIDToMonoClass[i] == NULL) != (m_ClassIDToVTable[i] == NULL)); + } +} + +void MonoManager::CleanupClassIDMaps() +{ + m_ClassIDToMonoClass.clear (); + m_ClassIDToVTable.clear (); + gClassIDToVTable = &m_ClassIDToVTable[0]; + gClassIDToClass = SCRIPTING_NULL; + + memset(&m_CommonScriptingClasses, 0, sizeof(m_CommonScriptingClasses) ); +} + +MonoClass* MonoManager::GetBuiltinMonoClass (const char* name, bool optional) +{ + MonoClass* klass = NULL; + if (m_ScriptImages[kEngineAssembly]) + klass = mono_class_from_name (m_ScriptImages[kEngineAssembly], "UnityEngine", name); + + if (klass) + return klass; + else if (!optional) + { + ErrorString (Format ("Mono Class %s couldn't be found! This might lead to random crashes later on!", name)); + return NULL; + } + else + { + return NULL; + } +} + + +#if UNITY_EDITOR +MonoClass* MonoManager::GetBuiltinEditorMonoClass (const char* name) +{ + MonoClass* klass = NULL; + if (m_ScriptImages[kEditorAssembly]) + klass = mono_class_from_name (m_ScriptImages[kEditorAssembly], "UnityEditor", name); + + if (klass) + return klass; + else + { + ErrorString (Format ("Mono Class %s couldn't be found! This might lead to random crashes later on!", name)); + return NULL; + } +} +#endif + +void MonoManager::RebuildCommonMonoClasses () +{ + FillCommonScriptingClasses(m_CommonScriptingClasses); + + ScriptingMethodPtr setProject = m_CommonScriptingClasses.stackTraceUtilitySetProjectFolder; + if (setProject) + { + MonoException* exception; + std::string projectFolder = File::GetCurrentDirectory (); + if( !projectFolder.empty() ) + projectFolder += kPathNameSeparator; + ConvertSeparatorsToPlatform( projectFolder ); + + void* values[1] = { MonoStringNew (projectFolder) }; + mono_runtime_invoke_profiled (setProject->monoMethod, NULL, values, &exception); + if (exception) + Scripting::LogException (exception, 0); + } +} + + +MonoObject* MonoInstantiateScriptingWrapperForClassID(int classID) +{ + MonoVTable* vtable = gClassIDToVTable[classID]; + if (!vtable) + return NULL; + + return mono_object_new_alloc_specific (vtable); +} +DOES_NOT_RETURN void RaiseDotNetExceptionImpl (const char* ns, const char* type, const char* format, va_list list) +{ + va_list ap; + va_copy (ap, list); + + char buffer[1024 * 5]; + vsnprintf (buffer, 1024 * 5, format, ap); + va_end (ap); + + MonoException* exception = mono_exception_from_name_msg (mono_get_corlib (), ns, type, buffer); + mono_raise_exception (exception); + +} + +DOES_NOT_RETURN void RaiseSystemExceptionImpl (const char* type, const char* format, va_list list) +{ + va_list ap; + va_copy (ap, list); + RaiseDotNetExceptionImpl("System",type,format,ap); +} + +void RaiseManagedException (const char *ns, const char *type, const char *format, ...) +{ + va_list va; + va_start (va, format); + RaiseDotNetExceptionImpl (ns, type, format, va); +} + +void RaiseMonoException (const char* format, ...) +{ + va_list va; + va_start( va, format ); + + char buffer[1024 * 5]; + vsnprintf (buffer, 1024 * 5, format, va); + + MonoException* exception = mono_exception_from_name_msg (GetMonoManager ().GetEngineImage (), kEngineNameSpace, "UnityException", buffer); + mono_raise_exception (exception); +} + +void RaiseOutOfRangeException (const char* format, ...) +{ + va_list va; + va_start( va, format ); + RaiseSystemExceptionImpl("IndexOutOfRangeException", format, va); +} + +void RaiseNullException (const char* format, ...) +{ + va_list va; + va_start( va, format ); + RaiseSystemExceptionImpl("NullReferenceException", format, va); +} + +void RaiseArgumentException (const char* format, ...) +{ + va_list va; + va_start( va, format ); + RaiseSystemExceptionImpl("ArgumentException", format, va); +} + +void RaiseInvalidOperationException (const char* format, ...) +{ + va_list va; + va_start( va, format ); + RaiseSystemExceptionImpl("InvalidOperationException", format, va); +} + +void RaiseSecurityException (const char* format, ...) +{ + va_list va; + va_start( va, format ); + RaiseDotNetExceptionImpl("System.Security","SecurityException", format, va); +} + +void RaiseNullExceptionObject (MonoObject* object) +{ + #if MONO_QUALITY_ERRORS + if (object) + { + MonoClass* klass = mono_object_get_class(object); + if (mono_class_is_subclass_of (mono_object_get_class(object), GetMonoManager().ClassIDToScriptingClass(ClassID(Object)), false)) + { + UnityEngineObjectMemoryLayout& data = ExtractMonoObjectData<UnityEngineObjectMemoryLayout>(object); + string error = MonoStringToCpp(data.error); + + // The object was destroyed underneath us - but if we hit a MissingReferenceException then we show that instead! + if (data.instanceID != 0 && error.find ("MissingReferenceException:") != 0) + { + error = Format("The object of type '%s' has been destroyed but you are still trying to access it.\n" + "Your script should either check if it is null or you should not destroy the object.", mono_class_get_name(klass)); + + MonoException* exception = mono_exception_from_name_msg (GetMonoManager().GetEngineImage(), "UnityEngine", "MissingReferenceException", error.c_str()); + mono_raise_exception (exception); + } + + // We got an error message, parse it and throw it + if (data.error) + { + error = MonoStringToCpp(data.error); + string::size_type exceptionTypePos = error.find(':'); + if (exceptionTypePos != string::npos) + { + string type = string(error.begin(), error.begin() + exceptionTypePos); + error.erase(error.begin(), error.begin()+exceptionTypePos + 1); + + MonoException* exception = mono_exception_from_name_msg (GetMonoManager().GetEngineImage(), "UnityEngine", type.c_str(), error.c_str ()); + mono_raise_exception (exception); + } + } + } + } + + MonoException* exception = mono_exception_from_name_msg (mono_get_corlib (), "System", "NullReferenceException", ""); + mono_raise_exception (exception); + #else + MonoException* exception = mono_exception_from_name_msg (mono_get_corlib (), "System", "NullReferenceException", ""); + mono_raise_exception (exception); + #endif +} + +MonoObject* InstantiateScriptingWrapperForClassID(int classID) +{ + MonoVTable* vtable = gClassIDToVTable[classID]; + if (!vtable) + return NULL; + + return mono_object_new_alloc_specific (vtable); +} + +#if UNITY_EDITOR +bool AmIBeingDebugged(); +#endif + + +#if UNITY_EDITOR +extern "C" { + void *mono_trace_parse_options (char *options); + extern void *mono_jit_trace_calls; +} +#endif + +#if USE_MONO_DOMAINS + +void MonoManager::UnloadAllAssembliesOnNextDomainReload() +{ +#if USE_TWO_MONO_DOMAINS + if (m_AssemblyReferencingDomain != NULL) + mono_domain_unload(m_AssemblyReferencingDomain); + m_AssemblyReferencingDomain = NULL; +#endif +} + +void MonoManager::PopulateAssemblyReferencingDomain() +{ +#if USE_TWO_MONO_DOMAINS + //if unityeditor isn't loaded yet (first time), return, since we depend on some managed code to do our work. + if (m_ScriptImages.empty()) + return; + + if (m_AssemblyReferencingDomain == NULL) + { + printf_console("Not loading any assemblies in assemblyreferencingdomain, since the domain doesn't exist\n"); + return; + } + + MonoException* exc = NULL; + printf_console("- GetNamesOfAssembliesLoadedInCurrentDomain\n"); + ScriptingArrayPtr result = (ScriptingArrayPtr) CallStaticMonoMethod("AssemblyHelper","GetNamesOfAssembliesLoadedInCurrentDomain",NULL,&exc); + + if (exc!=NULL) + { + ErrorString("Failed calling GetNamesOfAssembliesLoadedInCurrentDomain"); + return; + } + + for (int i=0; i!= GetScriptingArraySize(result); i++) + { + string filename(ScriptingStringToCpp(Scripting::GetScriptingArrayElement<MonoString*>(result,i))); + + if (IsThisFileAnAssemblyThatCouldChange(filename)) + continue; + + printf_console("- loading assembly into m_AssemblyReferencingDomain: %s\n",filename.c_str()); + MonoAssembly* a = mono_domain_assembly_open(m_AssemblyReferencingDomain,filename.c_str()); + if (a == NULL) + ErrorString(Format("Failed to load assembly: %s into temporary domain\n",filename.c_str())); + } +#endif +} + +MonoDomain* CreateAndSetChildDomain() +{ + MonoDomain* old_domain = mono_domain_get(); + + // Unload the old domain + UnloadDomain (); + + //there should only ever be one child domain, so if we're making a new one, + //we better be in the rootdomain. + AssertIf(mono_get_root_domain() != mono_domain_get()); + + MonoDomain* newDomain = mono_domain_create_appdomain("Unity Child Domain", NULL); + + AssertIf(mono_get_root_domain() != mono_domain_get()); + + // Activate the domain! + if (newDomain) + { + mono_thread_push_appdomain_ref(newDomain); + + if (!mono_domain_set (newDomain, false)) + { + printf_console("Exception setting domain\n"); + return NULL; + } + } + else + { + printf_console("Failed to create domain\n"); + return NULL; + } + + + AssertIf(mono_domain_get () == mono_get_root_domain()); + AssertIf(mono_domain_get () == NULL); + AssertIf(mono_domain_get () == old_domain); + UNUSED(old_domain); + + MonoDomainIncrementUniqueId(); + + return mono_domain_get (); +} + + +static void UnloadDomain () +{ + if (gUnloadDomainCallback) + gUnloadDomainCallback(); + + ClearLogCallback (); + + //make sure to be in the domain you want to unload. + MonoDomain* domainToUnload = mono_domain_get(); + + //never unload the rootdomain + if (domainToUnload && domainToUnload != mono_get_root_domain()) + { + // you can only unload a domain when it's not the active domain, so we're going to switch to the rootdomain, + // so we can kill the childdomain. + if (!mono_domain_set(mono_get_root_domain(), false)) + { + printf_console("Exception setting domain\n"); + } + mono_thread_pop_appdomain_ref(); + mono_domain_unload(domainToUnload); + } + + //unloading a domain is also a nice point in time to have the GC run. + mono_gc_collect(mono_gc_max_generation()); + +#if UNITY_PLUGINS_AVAILABLE + //@TODO: Have Mono release its DLL handles (see case 373275). Until that is the case, + // this is disabled as it results in a change of behavior (plugin functions can get called multiple + // times in the editor which as long as we don't actually release the DLLs doesn't really make + // sense). + //UnloadAllPlugins (); +#endif +} +#endif + +#if UNITY_OSX && !UNITY_IPHONE && !UNITY_PEPPER +void SetupSignalHandler(int signal) +{ + struct sigaction action; + memset(&action, 0, sizeof(action)); + action.sa_sigaction = HandleSignal; + action.sa_flags = SA_SIGINFO; + if(sigaction(signal, &action, NULL) < 0) + printf_console("Error setting signal handler for signal %d\n",signal); + } +#endif + +void SetupSignalHandlers() +{ + +#if UNITY_OSX +# if WEBPLUG + //Remove possible existing mach exception handlers which interfere with our exception handling (ie Firefox) + mach_port_t current_task = mach_task_self(); + exception_mask_t exceptionMask = EXC_MASK_BAD_ACCESS | EXC_MASK_BAD_INSTRUCTION | EXC_MASK_ARITHMETIC | EXC_MASK_BREAKPOINT; + task_set_exception_ports(current_task, exceptionMask, 0, EXCEPTION_DEFAULT, THREAD_STATE_NONE); +# endif + +# if !UNITY_IPHONE && !UNITY_PEPPER + SetupSignalHandler(SIGSEGV); + SetupSignalHandler(SIGBUS); + SetupSignalHandler(SIGFPE); + SetupSignalHandler(SIGILL); +#endif +#endif + +// Make sure abort() calls get passed to the crash handler +#if UNITY_WIN + _set_abort_behavior (0, _WRITE_ABORT_MSG); + signal (SIGABRT, HandleAbort); +#endif + +#if UNITY_WIN || UNITY_OSX || UNITY_ANDROID || UNITY_BB10 || UNITY_TIZEN + //this causes mono to forward the signal to us, if the signal is not mono related. (i.e. not a breakpoint, or a managed nullref exception, etc) + mono_set_signal_chaining(1); +#endif +} + +bool ShouldGiveDebuggerChanceToAttach() +{ +#if SUPPORT_ENVIRONMENT_VARIABLES + if (getenv("UNITY_GIVE_CHANCE_TO_ATTACH_DEBUGGER")) + return true; +#endif + return false; +} + +void GiveDebuggerChanceToAttachIfRequired() +{ + if (ShouldGiveDebuggerChanceToAttach()) + { +#if UNITY_WIN && !UNITY_XENON && !UNITY_WINRT + MessageBoxW(0,L"You can attach a debugger now if you want",L"Debug",MB_OK); +#endif +#if UNITY_OSX + CFStringRef msg = CFSTR ("You can attach a debugger now if you want"); + CFOptionFlags responseFlags; + CFUserNotificationDisplayAlert (0.0, kCFUserNotificationPlainAlertLevel, NULL, NULL, NULL, msg, NULL, NULL, NULL, NULL, &responseFlags); +#endif + } +} + +void StoreGlobalMonoPaths (const std::vector<string>& monoPaths) +{ +#if UNITY_WIN && !UNITY_WINRT + + MonoPathContainer::GetMonoPaths().reserve(monoPaths.size()); + + for (vector<string>::const_iterator it = monoPaths.begin(); it != monoPaths.end(); ++it) + { + wchar_t widePath[kDefaultPathBufferSize]; + ConvertUnityPathName(it->c_str(), widePath, kDefaultPathBufferSize); + + wchar_t fullPath[kDefaultPathBufferSize]; + GetFullPathNameW(widePath, kDefaultPathBufferSize, fullPath, NULL); + + string unityPath; + ConvertWindowsPathName(fullPath, unityPath); + + MonoPathContainer::GetMonoPaths().push_back(unityPath); + } + +#else + MonoPathContainer::SetMonoPaths(monoPaths); +#endif +} + +static std::string BuildMonoPath (const std::vector<string>& monoPaths) +{ +#if UNITY_WIN + const char* separator = ";"; +#else + const char* separator = ":"; +#endif + + string monoPath; + for (int i=0;i!=monoPaths.size();i++) + { + if (i!=0) monoPath.append(separator); + monoPath.append(monoPaths[i]); + } + + return monoPath; +} + +static void SetupMonoPaths (const std::vector<string>& monoPaths, const std::string& monoConfigPath) +{ + // Mono will not look for the loaded Boo assembly if the version is different. + // It will look in the mono path and next to the loading dll. + // In web player Boo.Lang.dll is in different location than the rest of mono, + // hence we setup multiple assembly paths. + for (int i = 0; i < monoPaths.size(); i++) + printf_console("Mono path[%d] = '%s'\n", i, monoPaths[i].c_str()); + printf_console ("Mono config path = '%s'\n", monoConfigPath.c_str ()); + mono_set_dirs (monoPaths[0].c_str(), monoConfigPath.c_str()); + +#if !UNITY_PEPPER + mono_set_assemblies_path(BuildMonoPath (monoPaths).c_str ()); +#endif + + StoreGlobalMonoPaths (monoPaths); +} + +static void InitializeMonoDebugger (bool forceEnable=false) +{ + // Set up debugger + string monoOptions; + +#if MONO_2_12 + const string defaultDebuggerOptions = "--debugger-agent=transport=dt_socket,embedding=1,server=y,suspend=n"; +#else + const string defaultDebuggerOptions = "--debugger-agent=transport=dt_socket,embedding=1,defer=y"; +#endif + +#if UNITY_EDITOR + if (EditorPrefs::GetBool ("AllowAttachedDebuggingOfEditor", true)) + { + // Editor always listens on default port + monoOptions = defaultDebuggerOptions; + } +#endif + +#if SUPPORT_ENVIRONMENT_VARIABLES + // MONO_ARGUMENTS overrides everything + char* args = getenv("MONO_ARGUMENTS"); + if (args!=0) + { + monoOptions = args; + } +#endif +#if ENABLE_PLAYERCONNECTION && !UNITY_FLASH + // Allow attaching using player connection discovery + if (monoOptions.empty ()) + { + PlayerConnection::Initialize (PLAYER_DATA_FOLDER, forceEnable); + if (PlayerConnection::Get ().AllowDebugging ()) { + // Must be synchronized with Unity SDB addin + unsigned debuggerPort = 56000 + (PlayerConnection::Get ().GetLocalGuid () % 1000); + monoOptions = defaultDebuggerOptions + Format (",address=0.0.0.0:%u", debuggerPort); + } + } +#endif + + + if (!monoOptions.empty ()) + { + char *optionArray[] = { (char*)monoOptions.c_str () }; + printf_console ("Using monoOptions %s\n", monoOptions.c_str ()); + mono_jit_parse_options(1, optionArray); + gDebuggerEnabled = true; + } + +#if !ALWAYS_LOAD_MDBS + if (gDebuggerEnabled) +#endif + { + //this is not so much about debugging (although also required for debugging), but it's about being able to load .mdb files, which you need for nice stacktraces, lineinfo, etc. + mono_debug_init (1); + } +} + +void MonoPrintfRedirect(const char* msg, va_list list) +{ + va_list args; + va_copy (args, list); + printf_consolev(LogType_Debug,msg,args); + va_end (args); +} + +#if UNITY_NACL_WEBPLAYER +extern "C" { + void mono_set_pthread_suspend(int (*_pthread_suspend)(pthread_t thread, int sig)); + int pthreadsuspend_initialize(void); + int pthread_suspend(pthread_t thread, int sig); +} +#endif + +bool InitializeMonoFromMain (const std::vector<string>& monoPaths, string monoConfigPath, int argc, const char** argv, bool enableDebugger) +{ + s_MonoDomainContainer = UNITY_NEW_AS_ROOT(int,kMemMono, "MonoDomain", ""); + SET_ALLOC_OWNER(s_MonoDomainContainer); + + MonoPathContainer::StaticInitialize(); + + const char *emptyarg = ""; + GiveDebuggerChanceToAttachIfRequired(); + + + #if DEBUGMODE + printf_console( "Initialize mono\n" ); + #endif + +// Debug helpers +#if 0 + mono_unity_set_vprintf_func(MonoPrintfRedirect); + + //setenvvar ("MONO_DEBUG", "abort-on-sigsegv"); + //setenvvar ("MONO_LOG_MASK", "all"); + //setenvvar ("MONO_LOG_LEVEL", "debug"); + //setenvvar("MONO_TRACE","Console.Out:++++ "); + + // Tracing mask + #if UNITY_EDITOR + mono_trace_set_mask_string("all"); // "asm", "type", "dll", "gc", "cfg", "aot", "all", + mono_trace_set_level_string("debug");// "error", "critical", "warning", "message", "info", "debug" + //mono_jit_trace_calls = mono_trace_parse_options ("Console.Out:++++ "); + #endif +#endif + + SetupMonoPaths (monoPaths, monoConfigPath); + + mono_config_parse (NULL); + + SetupSignalHandlers(); + + mono_set_defaults (0, mono_parse_default_optimizations (NULL)); + +#if USE_MONO_DEBUGGER + InitializeMonoDebugger (enableDebugger); +#endif + + if (argv==NULL) argv = &emptyarg; +#if !UNITY_XENON && ! UNITY_PS3 && !UNITY_IPHONE && !UNITY_PEPPER && !UNITY_WII + mono_set_commandline_arguments(argc,argv, NULL); +#endif + +#if UNITY_NACL +#if UNITY_NACL_WEBPLAYER + pthreadsuspend_initialize (); + + mono_set_pthread_suspend (pthread_suspend); +#endif + + CompressedFileStream::Data* data = CompressedFileStream::GetResources().Find("mscorlib.dll"); + if (data == NULL) + { + ErrorString ("Could not find mscorlib in web stream!"); + return false; + } + + MemoryCacherReadBlocks cacher (data->blocks, data->end, kCacheBlockSize); + gCorLibMemory = (UInt8*)UNITY_ALLOC(kMemMono, data->GetSize(), 32); + cacher.DirectRead(gCorLibMemory, data->offset, data->GetSize()); + mono_set_corlib_data (gCorLibMemory, data->GetSize()); + + mono_install_assembly_postload_search_hook (AssemblyLoadHook, NULL); + + mono_jit_parse_options(argc, (char**)argv); +#endif + + +// mono_runtime_set_no_exec (true); +#if MONO_2_12 + MonoDomain* domain = mono_jit_init_version ("Unity Root Domain", "v4.0.30319"); +#else + MonoDomain* domain = mono_jit_init_version ("Unity Root Domain", "v2.0.50727"); +#endif + if (domain == NULL) + return false; + +#if WEBPLUG && (!UNITY_PEPPER || UNITY_NACL_WEBPLAYER) + mono_set_ignore_version_and_key_when_finding_assemblies_already_loaded(true); +#endif + + // The soft debugger needs this + mono_thread_set_main (mono_thread_current ()); + +#if ENABLE_MONO_MEMORY_PROFILER + //mono_gc_base_init (); ask joachim why we need this. + + mono_profiler_startup (); +#endif + +#if !UNITY_XENON && !UNITY_PS3 && !UNITY_IPHONE && !UNITY_PEPPER && !UNITY_WII + mono_unity_set_embeddinghostname("Unity"); +#endif + +#if !UNITY_PEPPER + mono_runtime_unhandled_exception_policy_set (MONO_UNHANDLED_POLICY_LEGACY); +#endif + + RegisterAllInternalCalls(); + + return true; +} + +#if !UNITY_WII && !UNITY_PS3 && !UNITY_XENON && !UNITY_IPHONE && !UNITY_ANDROID && !UNITY_PEPPER && !UNITY_LINUX && !UNITY_BB10 && !UNITY_TIZEN + +#if UNITY_OSX + +#include <dlfcn.h> +#include <cxxabi.h> + +void PrintStackTraceOSX (void* context) +{ + void *array[256]; + size_t size; + char **strings; + size_t i; + + size = mono_backtrace_from_context (context, array, 256); + + printf_console ("Obtained %zu stack frames.\n", size); + + for (i = 0; i < size; i++) + { + const char* symbolName = mono_pmip(array[i]); + if (!symbolName) + { + Dl_info dlinfo; + dladdr(array[i], &dlinfo); + symbolName = dlinfo.dli_sname; + if (symbolName) + { + int status = 0; + char* cname = abi::__cxa_demangle(symbolName, 0, 0, &status); + if (status == 0 && cname) + symbolName = cname; + } + } + + printf_console ("#%-3d%016p in %s\n", i, array[i], symbolName); + } + + free (strings); +} + + +void HandleSignal (int i, __siginfo* info, void* p) +#endif +#if UNITY_WIN +int __cdecl HandleSignal( EXCEPTION_POINTERS* ep ) +#endif +{ + printf_console("Receiving unhandled NULL exception\n"); + + #if UNITY_EDITOR + + // ---- editor + + #if UNITY_OSX + printf_console("Launching bug reporter\n"); + PrintStackTraceOSX(p); + LaunchBugReporter (kCrashbug); + + #elif UNITY_WIN + + winutils::ProcessInternalCrash(ep, false); + + if( gUnityCrashHandler ) + { + printf_console( "unity: Launch crash handler\n" ); + return gUnityCrashHandler->ProcessCrash( ep ); + } + return EXCEPTION_EXECUTE_HANDLER; + + #else + #error "Unknown platform" + #endif + + #else + // ---- player + + // We are hitting this from inside a mono method, so lets just keep going! + if (mono_thread_current() != NULL && mono_method_get_last_managed() != NULL) + { + #if WEBPLUG + ExitWithErrorCode(kErrorFatalException); + #endif + + #if UNITY_OSX + abort(); + #endif + + #if UNITY_WIN + return EXCEPTION_EXECUTE_HANDLER; + #endif + + } + else + { + // we have a serious exception - launch crash reporter + #if UNITY_WIN && USE_WIN_CRASH_HANDLER + + winutils::ProcessInternalCrash(ep, false); + + if( gUnityCrashHandler ) { + printf_console( "unity: Launch crash handler\n" ); + return gUnityCrashHandler->ProcessCrash( ep ); + } + #endif + + #if WEBPLUG + ExitWithErrorCode(kErrorFatalException); + #endif + + #if UNITY_OSX + abort(); + #endif + + #if UNITY_WIN + return EXCEPTION_EXECUTE_HANDLER; + #endif + } + #endif +} + +#endif // !UNITY_WII && !UNITY_PS3 && !UNITY_XENON && !UNITY_IPHONE && !UNITY_ANDROID && !UNITY_PEPPER && !UNITY_LINUX + +#if UNITY_WIN +void __cdecl HandleAbort (int signal) +{ + printf_console ("Received abort signal from the operating system\n"); + #if UNITY_EDITOR + LaunchBugReporter (kFatalError); + #endif +} +#endif + +void UnhandledExceptionHandler (MonoObject* object) +{ + printf_console("Receiving unhandled exception\n"); + + #if UNITY_EDITOR + LaunchBugReporter (kFatalError); + #elif WEBPLUG + + #else + #endif +} + +#if USE_MONO_DOMAINS +bool CleanupMonoReloadable () +{ + + UnloadDomain(); + return true; +} +#endif + +void CleanupMono () +{ + #if DEBUGMODE + printf_console( "Cleanup mono\n" ); + #endif + + RegisterLogPreprocessor (NULL); + +#if USE_MONO_DOMAINS + UnloadDomain(); +#endif + + mono_runtime_set_shutting_down (); +#if !UNITY_PEPPER + mono_threads_set_shutting_down (); + mono_thread_pool_cleanup (); + mono_thread_suspend_all_other_threads (); +#endif + mono_jit_cleanup(mono_get_root_domain()); + + #if UNITY_PEPPER + UNITY_FREE (kMemMono, gCorLibMemory); + #endif + + #if UNITY_OSX + struct sigaction sa; + sa.sa_sigaction = NULL; + sigemptyset (&sa.sa_mask); + sa.sa_flags = UNITY_SA_DISABLE; + sigaction (SIGSEGV, &sa, NULL); + + sa.sa_sigaction = NULL; + sigemptyset (&sa.sa_mask); + sa.sa_flags = UNITY_SA_DISABLE; + sigaction (SIGABRT, &sa, NULL); + #endif + + MonoPathContainer::StaticDestroy(); + + UNITY_DELETE(s_MonoDomainContainer,kMemMono); +} + +void PostprocessStacktrace(const char* stackTrace, std::string& processedStackTrace) +{ + if (GetMonoManagerPtr () && GetMonoManager ().GetCommonClasses ().postprocessStacktrace) + { + MonoException* exception = NULL; + int stripEngineInternalInformation = UNITY_RELEASE; + void* arg[] = { MonoStringNew(stackTrace), &stripEngineInternalInformation }; + + + MonoString* msTrace = (MonoString*)mono_runtime_invoke_profiled (GetMonoManager ().GetCommonClasses ().postprocessStacktrace->monoMethod, NULL, arg, &exception); + if (exception) + { + printf_console ("Failed to postprocess stacktrace\n"); + return; + } + processedStackTrace = MonoStringToCpp (msTrace); + } +} + +static void ExtractMonoStacktrace (const std::string& condition, std::string& processedStackTrace, std::string& stackTrace, int errorNum, string& file, int* line, int type, int targetInstanceID) +{ +#if UNITY_WII + // When debugger is running on Wii, mono stack extraction fails for unknown reasons + // But if running without debugger it works fine + if (wii::IsDebuggerPresent()) return; +#endif + +#if SUPPORT_THREADS + const bool isMainThread = Thread::EqualsCurrentThreadID(Thread::mainThreadId); +#else + const bool isMainThread = true; +#endif + if (isMainThread && mono_thread_current() != NULL && mono_method_get_last_managed () != NULL && (type & kDontExtractStacktrace) == 0) + { + MonoClass* stackTraceUtil = GetMonoManager ().GetMonoClass ("StackTraceUtility", kEngineNameSpace); + if (stackTraceUtil) + { + void* iter = NULL; + MonoMethod* method; + while ((method = mono_class_get_methods (stackTraceUtil, &iter))) { + const char* curName = mono_method_get_name (method); + if (strcmp ("ExtractStackTrace", curName) == 0) + break; + } + + if (method) + { + MonoException* exception = NULL; + MonoString* msTrace = (MonoString*)mono_runtime_invoke_profiled (method, NULL, NULL, &exception); + if (exception) + { + printf_console ("Failed to extract mono stacktrace from Log message\n"); + return; + } + + stackTrace = MonoStringToCpp (msTrace); + if (!stackTrace.empty ()) + { + int oldLine = *line; + string oldFile = file; + ExceptionToLineAndPath (stackTrace, *line, file); + if (!(type & kMayIgnoreLineNumber)) + stackTrace = Format ("%s\n[%s line %d]", stackTrace.c_str (), oldFile.c_str(), oldLine); + + PostprocessStacktrace(stackTrace.c_str(), processedStackTrace); + } + } + } + } +} + +MonoMethod* FindStaticMonoMethod (MonoImage* image, const char* className, const char* nameSpace, const char* methodName) +{ + MonoClass* klass = mono_class_from_name(image,nameSpace,className); + if (!klass) return NULL; + + MonoMethod* method = mono_class_get_method_from_name(klass,methodName,-1); + if (!method) return NULL; + + return method; +} + +MonoMethod* FindStaticMonoMethod (const char* nameSpace, const char* className, const char* methodName) +{ + MonoClass* klass = GetMonoManager ().GetMonoClass (className, nameSpace); + if (!klass) + return NULL; + return mono_class_get_method_from_name (klass, methodName, -1); +} + +MonoMethod* FindStaticMonoMethod (const char* className, const char* methodName) +{ + MonoClass* klass = GetMonoManager ().GetMonoClass (className, kEngineNameSpace); + if (!klass) + klass = GetMonoManager ().GetMonoClass (className, kEditorNameSpace); + if (!klass) + klass = GetMonoManager ().GetMonoClass (className, kEditorInternalNameSpace); + if (!klass) + return NULL; + + return mono_class_get_method_from_name (klass, methodName, -1); +} + +std::string MdbFile (const std::string& path) +{ + return AppendPathNameExtension (path, "mdb"); +} +std::string PdbFile (const std::string& path) +{ + int f = path.find(".dll"); + return AppendPathNameExtension (f != -1 ? path.substr(0, f) : path, "pdb"); +} + +void ClearLogCallback () +{ +#if UNITY_HAS_DEVELOPER_CONSOLE + // By default redirect log output to the developer console + RegisterLogCallback(DeveloperConsole_HandleLogFunction, true); +#else + RegisterLogCallback(NULL, false); +#endif // UNITY_HAS_DEVELOPER_CONSOLE +} + +template<class TransferFunction> +void MonoManager::Transfer (TransferFunction& transfer) +{ + Super::Transfer (transfer); + transfer.SetVersion(2); + + #if UNITY_EDITOR + if (transfer.IsSerializingForGameRelease ()) + { + transfer.Transfer (m_MonoScriptManager.m_RuntimeScripts, "m_Scripts"); + + //flash exports monomanager in a special way, it only requires m_Scripts + if (transfer.IsWritingGameReleaseData () && transfer.GetBuildingTarget().platform==kBuildFlash) + return; + + transfer.Transfer (m_AssemblyNames, "m_AssemblyNames"); + } + else if (transfer.GetFlags() & kPerformUnloadDependencyTracking) + { + transfer.Transfer (m_MonoScriptManager.m_RuntimeScripts, "m_Scripts"); + } + + #else + transfer.Transfer (m_MonoScriptManager.m_RuntimeScripts, "m_Scripts"); + transfer.Transfer (m_AssemblyNames, "m_AssemblyNames"); + + if (transfer.IsOldVersion (1)) + { + if (m_AssemblyNames.size() >= kScriptAssemblies) + { + m_AssemblyNames[0] = kEngineAssemblyName; + for (int i = 1; i < kScriptAssemblies; i++) + m_AssemblyNames[i] = ""; + } + + set<UnityStr> m_CustomDlls; + TRANSFER(m_CustomDlls); + for (std::set<UnityStr>::iterator i=m_CustomDlls.begin();i!=m_CustomDlls.end();i++) + { + if (find(m_AssemblyNames.begin(), m_AssemblyNames.end(), *i) == m_AssemblyNames.end()) + m_AssemblyNames.push_back(*i); + } + } + #endif +} + + +IMPLEMENT_OBJECT_SERIALIZE (MonoManager) +IMPLEMENT_CLASS (MonoManager) +GET_MANAGER (MonoManager) +GET_MANAGER_PTR (MonoManager) + +#endif //ENABLE_MONO diff --git a/Runtime/Mono/MonoManager.h b/Runtime/Mono/MonoManager.h new file mode 100644 index 0000000..b7c9b4b --- /dev/null +++ b/Runtime/Mono/MonoManager.h @@ -0,0 +1,240 @@ +#ifndef MONOMANAGER_H +#define MONOMANAGER_H + +#include "MonoIncludes.h" +#include "Configuration/UnityConfigure.h" +#include "Runtime/Modules/ExportModules.h" + +#if UNITY_FLASH +# include "Runtime/Scripting/MonoManager_Flash.h" +#elif UNITY_WINRT +# include "Runtime/Scripting/MonoManager_WinRT.h" +#endif + +#if !ENABLE_MONO +struct MonoAssembly; +struct MonoVTable; +struct MonoException; +struct MonoClassField; +struct MonoDomain; +#endif + +#if ENABLE_MONO + +#include "Runtime/BaseClasses/GameManager.h" +#include <set> +#include <list> +#include "Runtime/Utilities/DateTime.h" +#include "Runtime/Utilities/dynamic_bitset.h" +#include "Runtime/Threads/ThreadSpecificValue.h" +#include "Runtime/Scripting/ScriptingUtility.h" +#include "Runtime/Scripting/Backend/ScriptingTypes.h" +#include "Runtime/Mono/MonoScriptManager.h" +#include "Runtime/Scripting/ScriptingManager.h" +#include "Runtime/Threads/Thread.h" +#include "Runtime/Scripting/Backend/IScriptingTypeProvider.h" + +namespace Unity { class Component; class GameObject; } +extern const char* kUnityEngine; +extern const char* kMonoClasslibsProfile; +struct GUIState; +struct DomainReloadingData; + +typedef dynamic_bitset AssemblyMask; + +class MonoScript; +class MonoBehaviour; + + +/* + The Mono Scripting runtime System consists of 3 classes. + + MonoManager + MonoScript + MonoBehaviour + + The MonoManager contains all information about the Assemblys it uses. + (An assembly is an DLL or Exe containing the classes, metadata, and IL assembly code) + + When a MonoScript is loaded or rebuilt because the script has changed, + the MonoManager is asked to lookup the MonoClass given by the script name. + + The MonoManager also keeps a lookup of ClassIDToMonoClass which is a precalculated list + of classID's to their MonoClass* (The lookup respects inheritance so that when the C++ class is not availible as a MonoClass its parent Class is used instead) + + ---------- continue..... + + MonoExport.cpp is used to wrap all the C++ objects eg. Transform +*/ + + +typedef void ScriptsDidChangeCallback (); + +// TODO: remove +MonoObject* MonoInstantiateScriptingWrapperForClassID(int classID); + +class EXPORT_COREMODULE MonoManager : public ScriptingManager, public IScriptingTypeProvider +{ + public: + REGISTER_DERIVED_CLASS (MonoManager, GlobalGameManager) + DECLARE_OBJECT_SERIALIZE (MonoManager) + + MonoManager (MemLabelId label, ObjectCreationMode mode); + // virtual ~MonoManager (); declared-by-macro + + virtual void AwakeFromLoad (AwakeFromLoadMode awakeMode); + + // If 'theNameSpace' is NULL then search is in any namespace + MonoClass* GetMonoClassCaseInsensitive (const char* className, const char* theNameSpace = NULL); + // If 'theNameSpace' is NULL then search is in any namespace + MonoClass* GetMonoClass (const char* className, const char* theNameSpace = NULL); + + // Returns the class with className in the assembly defined by identifier (normally the pathname extension of the script) + MonoClass* GetMonoClassWithAssemblyName (const std::string& className, const string& nameSpace, const string& identifier); + + /// Reloads all assemblies with assemblymask. If the assembly can be loaded, the bit for that assembly is cleared. + /// Returns which assemblies could be loaded + +#if UNITY_EDITOR + enum { kEngineAssembly = 0, kEditorAssembly = 1, kLocatorAssembly = 2, kScriptAssemblies = 3 }; +#else + enum { kEngineAssembly = 0, kEditorAssembly = 1, kScriptAssemblies = 2 }; +#endif + enum AssemblyLoadFailure { kEverythingLoaded = 0, kFailedLoadingScriptAssemblies = 1, kFailedLoadingEngineOrEditorAssemblies = 2 }; + AssemblyLoadFailure ReloadAssembly (AssemblyMask allAssembliesMask); + + AssemblyMask GetSystemAssemblyMask (bool load); + AssemblyMask GetAvailableDllAssemblyMask (); + + MonoImage* GetEngineImage () { return m_ScriptImages[kEngineAssembly]; } + + std::string GetAssemblyPath (int index); + MonoAssembly* GetAssembly (int index); + + std::string GetAssemblyIdentifierFromImage(MonoImage* image); + int GetAssemblyIndexFromImage(MonoImage* image); + + void AssertInvalidAssembly (MonoClass* klass); + + MonoClass* GetBuiltinMonoClass (const char* name, bool optional = false); + + int GetAssemblyCount() const {return m_AssemblyNames.size(); } + int GetAssemblyIndexFromAssemblyName (const string& identifier); + + void UnloadAllAssembliesOnNextDomainReload(); + + #if UNITY_EDITOR + + void ResizeAssemblyNames(int max); + + // The assembly name should never be changed when mono dll's have already been loaded. + void SetAssemblyName (unsigned index, const string& name); + + void RegisterScriptsChanged (ScriptsDidChangeCallback* func); + + // Called when the domain and UnityEngine/UnityEditor have been reloaded but before anything else is, + // to give a chance for other dlls to be loaded before anything else (i.e., package manager) + void SetLogAssemblyReload (bool reload) { m_LogAssemblyReload = reload; } + + MonoClass* GetBuiltinEditorMonoClass (const char* name); + + int InsertAssemblyName(const std::string& assemblyName); + void SetCustomDllPathLocation (const std::string& name, const std::string& path); + + bool HasCompileErrors () { return m_HasCompileErrors; } + void SetHasCompileErrors (bool compileErrors); + + std::vector<UnityStr>& GetRawAssemblyNames () { return m_AssemblyNames; } + + #else + bool HasCompileErrors () { return false; } + #endif + + + //implementation of IScriptingTypeProvider + BackendNativeType NativeTypeFor(const char* namespaze, const char* className); + ScriptingTypePtr Provide(BackendNativeType nativeType); + void Release(ScriptingTypePtr klass); + + private: + + void SetupLoadedEditorAssemblies (); + AssemblyLoadFailure BeginReloadAssembly (DomainReloadingData& savedData); + AssemblyLoadFailure EndReloadAssembly (const DomainReloadingData& savedData, AssemblyMask allAssembliesMask); + + // Rebuilds the m_ClassIDToMonoClass lookup table. + // m_ClassIDToMonoClass maps from the classID to the MonoClass it is best represented by. + // If a MonoClass can't be found for the exact type the next parent class is used instead + void RebuildClassIDToScriptingClass (); + void CleanupClassIDMaps(); + // Initializes in the CommonMonoTypes struct + virtual void RebuildCommonMonoClasses (); + + bool LoadAssemblies (AssemblyMask allAssembliesMask); + void PopulateAssemblyReferencingDomain(); + + + typedef std::vector<MonoImage*> ScriptImages; + + ScriptImages m_ScriptImages; + + std::vector<UnityStr> m_AssemblyNames; + + std::vector<MonoVTable*> m_ClassIDToVTable; + + bool m_HasCompileErrors; + static UNITY_TLS_VALUE(bool) m_IsMonoBehaviourInConstructor; + + MonoDomain* m_AssemblyReferencingDomain; + bool IsThisFileAnAssemblyThatCouldChange(std::string& path); + + #if UNITY_EDITOR + DateTime m_EngineDllModDate; + + typedef std::map<std::string, std::string> CustomDllLocation; + CustomDllLocation m_CustomDllLocation; + bool m_LogAssemblyReload; + string m_ManagedEditorAssembliesBasePath; + #endif +}; + +EXPORT_COREMODULE MonoManager& GetMonoManager (); +MonoManager* GetMonoManagerPtr (); + + +namespace MonoPathContainer +{ + std::vector<std::string>& GetMonoPaths(); + void AppendMonoPath (const string& path); +}; + +MonoMethod* FindStaticMonoMethod (MonoImage* image, const char* className, const char* nameSpace, const char* methodName); +MonoMethod* FindStaticMonoMethod (const char* className, const char* methodName); +MonoMethod* FindStaticMonoMethod (const char* nameSpace, const char* className, const char* methodName); + +/// This has to be called from main to initialize mono +bool InitializeMonoFromMain (const std::vector<string>& monoPaths, string monoConfigPath, int argc, const char** argv, bool enableDebugger=false); +void CleanupMono (); +bool CleanupMonoReloadable (); + +#if UNITY_RELEASE +#define AssertInvalidClass(x) +#else +#define AssertInvalidClass(x) GetMonoManager().AssertInvalidAssembly(x); +#endif + +std::string MdbFile (const string& path); +std::string PdbFile (const string& path); + +void DisableLoadSeperateDomain (); + +MonoDomain* CreateDomain (); + +void RegisterUnloadDomainCallback (ScriptsDidChangeCallback* call); + +void PostprocessStacktrace(const char* stackTrace, std::string& processedStackTrace); + +void ClearLogCallback (); + +#endif //ENABLE_SCRIPTING +#endif diff --git a/Runtime/Mono/MonoScopedThreadAttach.cpp b/Runtime/Mono/MonoScopedThreadAttach.cpp new file mode 100644 index 0000000..7cead89 --- /dev/null +++ b/Runtime/Mono/MonoScopedThreadAttach.cpp @@ -0,0 +1,72 @@ +/* + * MonoScopedThreadAttach.cpp + * AllTargets.workspace + * + * Created by Søren Christiansen on 8/23/11. + * Copyright 2011 Unity Technologies. All rights reserved. + * + */ +#include "UnityPrefix.h" +#include "MonoScopedThreadAttach.h" +#include "MonoIncludes.h" +#include "Runtime/Threads/Thread.h" +#include "Runtime/Threads/Mutex.h" + +#if SUPPORT_MONO_THREADS + +static attached_thread m_AttachedThreads[MAX_ATTACHED_THREADS] = {{0,0,0}, {0,0,0}, {0,0,0}, {0,0,0}}; +static Mutex mutex; + + +// call this from the thread you want to attach! +Thread::ThreadID AttachMonoThread(MonoDomain* domain) +{ + Assert(!Thread::CurrentThreadIsMainThread()); + Mutex::AutoLock lock( mutex ); + + for (int i=0; i < MAX_ATTACHED_THREADS; ++i) + { + if (m_AttachedThreads[i].threadID == Thread::GetCurrentThreadID()) + { + m_AttachedThreads[i].refCount++; + return m_AttachedThreads[i].threadID; + } + else + if (m_AttachedThreads[i].threadID == 0) + { + m_AttachedThreads[i].threadID = Thread::GetCurrentThreadID(); + m_AttachedThreads[i].thread = mono_thread_attach(domain); + m_AttachedThreads[i].refCount = 1; + return m_AttachedThreads[i].threadID; + } + } + + return 0; +} + +// call this from the thread you want to detach! +bool DetachMonoThread(Thread::ThreadID threadID) +{ + Assert(!Thread::CurrentThreadIsMainThread()); + Mutex::AutoLock lock( mutex ); + + for (int i=0; i < MAX_ATTACHED_THREADS; ++i) + { + if (m_AttachedThreads[i].threadID == Thread::GetCurrentThreadID()) + { + m_AttachedThreads[i].refCount--; + if (m_AttachedThreads[i].refCount == 0) + { + mono_thread_detach(m_AttachedThreads[i].thread); + m_AttachedThreads[i].threadID = 0; + m_AttachedThreads[i].thread = 0; + m_AttachedThreads[i].refCount = 0; + } + return true; + } + } + + return false; +} + +#endif // SUPPORT_MONO_THREADS diff --git a/Runtime/Mono/MonoScopedThreadAttach.h b/Runtime/Mono/MonoScopedThreadAttach.h new file mode 100644 index 0000000..7549bac --- /dev/null +++ b/Runtime/Mono/MonoScopedThreadAttach.h @@ -0,0 +1,52 @@ +/* + * MonoScopedThreadAttach.h + * AllTargets.workspace + * + * Created by Søren Christiansen on 8/23/11. + * Copyright 2011 Unity Technologies. All rights reserved. + * + */ +#ifndef __UNITY_MONOSCOPEDTHREADATTACH_H__ +#define __UNITY_MONOSCOPEDTHREADATTACH_H__ + +#if SUPPORT_MONO_THREADS + +#include "Runtime/Threads/Thread.h" + +#define MAX_ATTACHED_THREADS 4 + +struct MonoDomain; +struct MonoThread; + +Thread::ThreadID AttachMonoThread(MonoDomain* domain); +bool DetachMonoThread(Thread::ThreadID threadID); + +struct attached_thread +{ + Thread::ThreadID threadID; + MonoThread* thread; + int refCount; +}; + +struct ScopedThreadAttach +{ + ScopedThreadAttach(MonoDomain* domain) : threadID(0) + { + Assert(domain); + if (!Thread::CurrentThreadIsMainThread()) + threadID = AttachMonoThread(domain); + } + ~ScopedThreadAttach() + { + if (threadID) + DetachMonoThread(threadID); + } + +private: + ScopedThreadAttach(const ScopedThreadAttach& other); + void operator=(const ScopedThreadAttach& other); + Thread::ThreadID threadID; +}; + +#endif // SUPPORT_MONO_THREADS +#endif // __UNITY_MONOSCOPEDTHREADATTACH_H__ diff --git a/Runtime/Mono/MonoScript.cpp b/Runtime/Mono/MonoScript.cpp new file mode 100644 index 0000000..75b7c30 --- /dev/null +++ b/Runtime/Mono/MonoScript.cpp @@ -0,0 +1,443 @@ +#include "UnityPrefix.h" +#if ENABLE_SCRIPTING +#include "MonoScript.h" +#include "Runtime/Serialize/TransferFunctions/SerializeTransfer.h" +#include "Runtime/Serialize/TransferUtility.h" +#include "MonoManager.h" +#include "Runtime/Scripting/Backend/ScriptingBackendApi.h" +#include "MonoScriptCache.h" +#include "MonoBehaviour.h" + +#if UNITY_FLASH || UNITY_WINRT +#include "Runtime/Scripting/Backend/ScriptingTypeRegistry.h" +#endif + +const char* kBuildLogicGraphDllFile = "Temp/StagingArea/Data/Managed/LogicGraphs.dll"; + +string GetGeneratedLogicGraphsDllName() +{ + return GetLastPathNameComponent(kBuildLogicGraphDllFile); +} + + +using namespace std; + + +#if UNITY_EDITOR +static UInt32 ComputeTypeTreeHashForScriptClass (MonoScript* script) +{ + if (script->GetClass () == NULL + || !IsValidScriptType (script->GetScriptType ()) + || script->IsEditorScript ()) + return 0; + + // We don't want to call the constructor as that may run user code and + // may have all kinds of side-effects. All we need is an instance for + // running the TypeTree generation/serialization code on. + ScriptingObjectPtr instance = scripting_object_new (script->GetClass ()); + if (!instance) + return 0; + + // Create a temporary MonoBehaviour that we can run ProxyTransfer on. + MonoBehaviour* dummyInstance = NEW_OBJECT (MonoBehaviour); + dummyInstance->Reset (); + dummyInstance->HackSetAwakeWasCalled (); + + // Connect MonoBehaviour to script class and to the object instance + // we have created. + dummyInstance->SetScript (script, instance); + + // Generate type tree. + TypeTree typeTree; + GenerateTypeTree (*dummyInstance, &typeTree); + DestroySingleObject (dummyInstance); + + // Compute hash of tree. + return HashTypeTree (typeTree); +} +#endif // UNITY_EDITOR + + +MonoScript::MonoScript (MemLabelId label, ObjectCreationMode mode) +: Super(label, mode) +{ + m_PropertiesHash = 0; + + m_ScriptCache = NULL; + m_IsEditorScript = false; + m_ExecutionOrder = 0; +} + +MonoScript::~MonoScript () +{ + CleanupScriptCache(); +} + +UInt32 MonoScript::GetPropertiesHash () +{ + // In the editor, compute the property hash on demand based on the type + // tree generated by the MonoBehaviour serialization code. + #if UNITY_EDITOR + if (m_PropertiesHash == 0) + m_PropertiesHash = ComputeTypeTreeHashForScriptClass (this); + #endif + + return m_PropertiesHash; +} + +UnityStr MonoScript::GetScriptFullClassName () const +{ + if (m_Namespace.empty()) + return m_ClassName; + else + return m_Namespace + "." + m_ClassName; +} + +ScriptingClassPtr MonoScript::GetClass () +{ + if (m_ScriptCache) + return m_ScriptCache->klass; + else + return NULL; +} + +ScriptingMethodPtr MonoScript::FindMethod (const char* name) +{ + if (m_ScriptCache) + return ::FindMethod(*m_ScriptCache, name); + else + return NULL; +} + +MonoScriptType MonoScript::GetScriptType() const +{ + if (m_ScriptCache == NULL) + return kScriptTypeNotInitialized; + else + return m_ScriptCache->scriptType; +} + + +void MonoScript::CleanupScriptCache () +{ + if (m_ScriptCache != NULL) + { + const_cast<MonoScriptCache*> (m_ScriptCache)->Release(); + m_ScriptCache = NULL; + } +} + +#if ENABLE_SCRIPTING + +void MonoScript::Rebuild (ScriptingTypePtr klass) +{ + CleanupScriptCache (); + + #if UNITY_EDITOR + m_PropertiesHash = 0; + #endif + + m_ScriptCache = CreateMonoScriptCache (klass, m_IsEditorScript, this); +} +#endif + +void MonoScript::RebuildFromAwake() +{ +#if ENABLE_MONO + Rebuild (GetMonoManager().GetMonoClassWithAssemblyName (GetScriptClassName (), GetNameSpace(), GetAssemblyName ())); +#elif UNITY_FLASH || UNITY_WINRT + ScriptingTypePtr t = GetScriptingTypeRegistry().GetType(GetNameSpace().c_str(), GetScriptClassName().c_str()); + Rebuild(t); +#endif + +} + +// Used by WinRT & Flash +UnityStr MonoScript::GetScriptName() +{ + UnityStr s = GetNameSpace(); + if (s.size() > 0) + s += "." + GetScriptClassName(); + else + s = GetScriptClassName(); + return s; +} + +void MonoScript::AwakeFromLoad (AwakeFromLoadMode awakeMode) +{ + Super::AwakeFromLoad (awakeMode); + if ((awakeMode & kDidLoadThreaded) == 0) + { + RebuildFromAwake(); + } +} + +void MonoScript::AwakeFromLoadThreaded () +{ + Super::AwakeFromLoadThreaded(); + RebuildFromAwake(); +} + +template<class TransferFunction> +void MonoScript::TransferPropertiesHash (TransferFunction& transfer) +{ + #if UNITY_EDITOR + if (transfer.IsWritingGameReleaseData()) + { + // Force building the properties hash + GetPropertiesHash(); + } + #endif + + // When reading or writing for game release + // Transfer properties hash + if (transfer.IsSerializingForGameRelease ()) + transfer.Transfer (m_PropertiesHash, "m_PropertiesHash", kNotEditableMask); +} + +#if UNITY_EDITOR +// Temporarily (When building the player during the write operation). +// Sets the correct assembly of the MonoScript, and restores it afterwards. +struct RemapAssemblyDuringBuild +{ + MonoScript* m_Script; + string m_AssemblyName; + string m_Namespace; + string m_ClassName; + + RemapAssemblyDuringBuild (MonoScript& inScript, bool logicGraphRelease) + : m_Script (NULL) + { + if (!logicGraphRelease) + return; + + m_Script = &inScript; + + m_AssemblyName = m_Script->GetAssemblyName(); + m_Namespace = m_Script->GetNameSpace(); + m_ClassName = m_Script->GetScriptClassName(); + + m_Script->m_AssemblyName = GetGeneratedLogicGraphsDllName(); + m_Script->m_Namespace = ""; + + m_Script->m_ClassName = m_Script->m_EditorGraphData->GetName(); + } + + ~RemapAssemblyDuringBuild () + { + if (m_Script == NULL) + return; + + m_Script->m_AssemblyName = m_AssemblyName; + m_Script->m_Namespace = m_Namespace; + m_Script->m_ClassName = m_ClassName; + } +}; +#endif + +template<class TransferFunction> +void MonoScript::Transfer (TransferFunction& transfer) +{ + // Don't transfer script class since we already transfer the script property ourselves. + Super::Super::Transfer (transfer); + + transfer.SetVersion(4); + + #if UNITY_EDITOR + + bool logicGraphRelease = transfer.IsWritingGameReleaseData() && !m_EditorGraphData.IsNull(); + RemapAssemblyDuringBuild remap (*this, logicGraphRelease); + + if (!transfer.IsSerializingForGameRelease ()) + { + transfer.Transfer (m_Script, "m_Script", kHideInEditorMask); + transfer.Transfer (m_DefaultReferences, "m_DefaultReferences", kHideInEditorMask); + transfer.Transfer (m_Icon, "m_Icon", kNoTransferFlags); + TRANSFER (m_EditorGraphData); + } + + #endif + + + transfer.Transfer (m_ExecutionOrder, "m_ExecutionOrder", kNotEditableMask); + TransferPropertiesHash (transfer); + + transfer.Transfer (m_ClassName, "m_ClassName", kNotEditableMask); + transfer.Transfer (m_Namespace, "m_Namespace", kNotEditableMask); + transfer.Transfer (m_AssemblyName, "m_AssemblyName", kNotEditableMask); + transfer.Transfer (m_IsEditorScript, "m_IsEditorScript", kHideInEditorMask); + + // AssemblyIdentifier has been removed and is now simply the dll name + if (transfer.IsVersionSmallerOrEqual(1)) + { + transfer.Transfer (m_AssemblyName, "m_AssemblyIdentifier", kNotEditableMask); + + if (m_AssemblyName == "Unity Engine Special") + { + m_AssemblyName = "UnityEngine.dll"; + m_Namespace = "UnityEngine"; + } + else if (m_AssemblyName == "Unity Editor Special") + { + m_AssemblyName = "UnityEditor.dll"; + m_Namespace = "UnityEditor"; + } + else + { + m_AssemblyName = "Assembly - " + m_AssemblyName + ".dll"; + } + } + + #if UNITY_EDITOR + // In 2.x Assemblies compiled by Unity had spaces in the name. + // In 3.0 we are removing all spaces, since this makes loading assemblies easier and fixes issue on xbox where the filename is too long. + // ("Assembly - UnityScript.dll" -> "Assembly-UnityScript.dll") + if (transfer.IsVersionSmallerOrEqual(2)) + { + if (BeginsWith(m_AssemblyName, "Assembly - ")) + replace_string(m_AssemblyName, " ", ""); + } + #endif +} + +bool MonoScript::ShouldIgnoreInGarbageDependencyTracking () +{ + // MonoScript can have referenced + // But it's ok to ignore them eg. references to default references, icons and editor graph + return true; +} + +void MonoScript::Init (const ScriptString& script, const string& className, const std::string& nameSpace, const string& identifier, bool isEditorScript) +{ + SetScript(script); + m_ClassName = className; + m_Namespace = nameSpace; + m_AssemblyName = identifier; + m_PropertiesHash = 0; + m_IsEditorScript = isEditorScript; + + SetDirty (); +} + +#if ENABLE_SCRIPTING + +void MonoScript::Init (MonoClass* scriptType) +{ + m_ClassName = scripting_class_get_name(scriptType); + m_Namespace = scripting_class_get_namespace(scriptType); + m_PropertiesHash = 0; + #if ENABLE_MONO + MonoImage* image = mono_class_get_image(scriptType); + MonoAssemblyName name; + bool success = mono_assembly_fill_assembly_name(image, &name); + if (success) + m_AssemblyName = mono_stringify_assembly_name(&name); + #endif + m_IsEditorScript = 0; + SetDirty (); +} +#endif + +std::string MonoScript::GetNameSpace ()const +{ + return m_Namespace; +} + +#if ENABLE_SCRIPTING +MonoScript* CreateMonoScriptFromScriptingType(ScriptingTypePtr klass) +{ + MonoScript* result = NEW_OBJECT(MonoScript); + result->Reset (); + result->Init(klass); + GetMonoScriptManager().RegisterRuntimeScript (*result); + result->AwakeFromLoad(kInstantiateOrCreateFromCodeAwakeFromLoad); + return result; +} +#endif + +void MonoScript::SetExecutionOrder (SInt32 executionOrder) +{ + m_ExecutionOrder = executionOrder; + SetDirty(); +} + +#if UNITY_EDITOR +bool MonoScript::IsBuiltinScript() const +{ + return m_AssemblyName == "UnityEngine.dll" || m_AssemblyName == "UnityEditor.dll"; +} + +void MonoScript::SetIcon (PPtr<Object> icon) +{ + m_Icon = icon; + // We do not call SetDirty() since MonoScript is generated data (MonoImporter holds the icon state) +} + +PPtr<Object> MonoScript::GetIcon () const +{ + return m_Icon; +} + +void MonoScript::SetEditorGraphData(Object* data) +{ + if (m_EditorGraphData != PPtr<Object> (data)) + { + m_EditorGraphData = data; + SetDirty(); + } +} + +PPtr<Object> MonoScript::GetEditorGraphData() +{ + return m_EditorGraphData; +} + +bool MonoScript::GetScriptTypeWasJustCreatedFromComponentMenu () +{ + if (m_ScriptCache) + return m_ScriptCache->scriptTypeWasJustCreatedFromComponentMenu; + else + return false; +} + +void MonoScript::SetScriptTypeWasJustCreatedFromComponentMenu () +{ + if (m_ScriptCache == NULL) + Rebuild(NULL); + + const_cast<MonoScriptCache*> (m_ScriptCache)->scriptTypeWasJustCreatedFromComponentMenu = true; +} + +std::string BuildScriptClassId(const std::string& assembly, const std::string& ns, const std::string& klass) +{ + return assembly + ":" + ns + ":" + klass; +} + +void GetScriptClassIdComponents(const std::string& scriptClassId, std::string& assembly, std::string& ns, std::string& klass) +{ + std::vector<std::string> parts; + Split(scriptClassId, ":", parts); + + assembly = parts[0]; + AssertIf(parts.size() != 2 && parts.size() != 3); + // empty namespace + if (parts.size() == 2) + { + ns.clear(); + klass = parts[1]; + } + else if (parts.size() == 3) + { + ns = parts[1]; + klass = parts[2]; + } +} + +#endif + +#if ENABLE_SCRIPTING +IMPLEMENT_CLASS (MonoScript) +IMPLEMENT_OBJECT_SERIALIZE (MonoScript) +#endif + +#endif diff --git a/Runtime/Mono/MonoScript.h b/Runtime/Mono/MonoScript.h new file mode 100644 index 0000000..c4f72ca --- /dev/null +++ b/Runtime/Mono/MonoScript.h @@ -0,0 +1,124 @@ +#ifndef MONOSCRIPT_H +#define MONOSCRIPT_H + +#include "Runtime/Scripting/Backend/ScriptingTypes.h" +#include "Runtime/Scripting/TextAsset.h" +#include "MonoScriptType.h" + +class MonoBehaviour; +class MonoManager; +struct CommonScriptingClasses; +struct MonoScriptCache; + +/* + The MonoScript wraps a MonoClass and caches eg. some commonly used methods for fast access. +*/ +class MonoScript : public TextAsset +{ + public: + REGISTER_DERIVED_CLASS (MonoScript, TextAsset) + DECLARE_OBJECT_SERIALIZE (MonoScript) + + MonoScript (MemLabelId label, ObjectCreationMode mode); + // virtual ~MonoScript (); declared-by-macro + + void Init (ScriptingClassPtr scriptType); + void Init (const ScriptString& script, const std::string& className, const std::string& nameSpace, const std::string& assemblyIdentifier, bool isEditorScript); + + const MonoScriptCache* GetScriptCache () const { return m_ScriptCache; } + + // Returns the MonoClass with name GetScriptClassName () + // Returns NULL if the MonoClass can't be found. + ScriptingClassPtr GetClass (); + + MonoScriptType GetScriptType() const; + + bool IsEditorScript () const { return m_IsEditorScript; } + + + ScriptingMethodPtr FindMethod (const char* name); + + // Returns the className of the script + virtual const UnityStr& GetScriptClassName () const { return m_ClassName; } + UnityStr GetScriptFullClassName () const; + + std::string GetNameSpace ()const; + + // Rebuilds the cached information about the class + // This function should only be called from MonoManager + void Rebuild (ScriptingTypePtr klass); + + const UnityStr& GetAssemblyName () const { return m_AssemblyName; } + + + /// Calls Rebuild to initialize the cached information about the class + void AwakeFromLoad(AwakeFromLoadMode mode); + void AwakeFromLoadThreaded (); + virtual bool ShouldIgnoreInGarbageDependencyTracking (); + + #if UNITY_EDITOR + + bool IsBuiltinScript() const; + const std::map<UnityStr, PPtr<Object> > GetDefaultReferences () { return m_DefaultReferences; } + void SetDefaultReferences (const std::map<UnityStr, PPtr<Object> >& references) { m_DefaultReferences = references; } + + void SetIcon (PPtr<Object> icon); + PPtr<Object> GetIcon () const; + + void SetEditorGraphData(Object* data); + PPtr<Object> GetEditorGraphData(); + + bool GetScriptTypeWasJustCreatedFromComponentMenu (); + void SetScriptTypeWasJustCreatedFromComponentMenu (); + + #endif + + int GetExecutionOrder () const { return m_ExecutionOrder; } + void SetExecutionOrder (SInt32 executionOrder); + + UnityStr GetScriptName(); + + UInt32 GetPropertiesHash (); + +private: + + template<class TransferFunction> + void TransferPropertiesHash (TransferFunction& transfer); + + void CleanupScriptCache (); + + + SInt32 m_ExecutionOrder; + UInt32 m_PropertiesHash; + + const MonoScriptCache* m_ScriptCache; + + UnityStr m_ClassName; + UnityStr m_Namespace; + UnityStr m_AssemblyName; + bool m_IsEditorScript; + #if UNITY_EDITOR + std::map<UnityStr, PPtr<Object> > m_DefaultReferences; + PPtr<Object> m_Icon; + PPtr<Object> m_EditorGraphData; + #endif + + friend struct RemapAssemblyDuringBuild; + + void RebuildFromAwake(); +}; + +typedef MonoScript* MonoScriptPtr; +typedef PPtr<MonoScript> MonoScriptPPtr; + +MonoScript* CreateMonoScriptFromScriptingType(ScriptingClassPtr klass); +const char* GetNameOfMethodByIndex(int index); + +#if UNITY_EDITOR +std::string BuildScriptClassId(const std::string& assembly, const std::string& ns, const std::string& klass); +void GetScriptClassIdComponents(const std::string& scriptClassId, std::string& assembly, std::string& ns, std::string& klass); +#endif + +extern const char* kBuildLogicGraphDllFile; + +#endif diff --git a/Runtime/Mono/MonoScriptCache.cpp b/Runtime/Mono/MonoScriptCache.cpp new file mode 100644 index 0000000..a1397c7 --- /dev/null +++ b/Runtime/Mono/MonoScriptCache.cpp @@ -0,0 +1,556 @@ +#include "UnityPrefix.h" +#include "MonoScriptCache.h" + +#if ENABLE_SCRIPTING + +#include "Runtime/BaseClasses/MessageHandler.h" +#include "Runtime/BaseClasses/GameObject.h" +#include "Runtime/Misc/MessageParameters.h" +#include "Runtime/Scripting/Backend/ScriptingMethodRegistry.h" +#include "Runtime/Scripting/Backend/ScriptingTypeRegistry.h" +#include "Runtime/Scripting/Backend/ScriptingBackendApi.h" +#include "MonoManager.h" + +const char* kMethodNames[] = { + "Update", + "LateUpdate", + "FixedUpdate", + "Awake", + "Start", + "Main", + "OnRenderObject", + "OnEnable", + "OnDisable", + "OnDisableINTERNAL", + "Start", + "Main", + "OnRenderImage", + "OnDrawGizmos", + "OnGUI", + "OnValidate", + "OnSerializeNetworkView", + "OnNetworkInstantiate", + "OnDestroy", + "OnAudioFilterRead", + NULL +}; + + +typedef MonoScriptCache::MethodCache MethodCache; +static void ClearMethodCache (MonoScriptCache::MethodCache& methods) +{ + for (MonoScriptCache::MethodCache::iterator i=methods.begin ();i != methods.end ();i++) + delete[] const_cast<char*> (i->first); + methods.clear (); +} + + +// These errors get removed as soon as the assembly gets reloaded. (MonoManager::ReloadAssemblies) +#define LogScriptError(x,script) DebugStringToFile (x, 0, __FILE__, __LINE__, kLog | kScriptCompileError, script ? script->GetInstanceID() : 0, manager.GetInstanceID ()); + +static bool Check2MethodParameters (ScriptingMethodPtr method, MonoClass* klass0, MonoClass* klass1, Object* script, MonoManager& manager); +static bool Check1MethodParameters (ScriptingMethodPtr method, MonoClass* klass0, Object* script, MonoManager& manager); + +static RegisterMonoRPCCallback* gRegisterMonoRPC = NULL; + +void RegisterMonoRPC (RegisterMonoRPCCallback* callback) +{ + gRegisterMonoRPC = callback; +} + +#if UNITY_FLASH +static MethodCache* methodCacheToInsertInto; + +extern "C" void Ext_InsertAllMethodsInMethodCacheForType(ScriptingClassPtr klass); + +void BuildMethodCache (MethodCache& methods, ScriptingTypePtr klass, bool staticMethod) +{ + AssertIf (klass == NULL); + methodCacheToInsertInto = &methods; + Ext_InsertAllMethodsInMethodCacheForType(klass); +} + +extern "C" void InsertMethodInMethodCache(const char* name, const char* mappedName, ScriptingClassPtr klass) +{ + const char* namecpy = strcpy(new char[strlen(name) + 1],name); + ScriptingMethodPtr method = new ScriptingMethod(namecpy,mappedName,"",klass); + methodCacheToInsertInto->insert (std::make_pair (namecpy, method)); +} +#endif + +#if !UNITY_FLASH +static void BuildMethodCache (MethodCache& methods, ScriptingClassPtr klass, bool staticMethod) +{ + AssertIf (klass == NULL); + + std::vector<ScriptingMethodPtr> foundMethods; + GetScriptingMethodRegistry().AllMethodsIn(klass, foundMethods, ScriptingMethodRegistry::kInstanceOnly); + + for (std::vector<ScriptingMethodPtr>::iterator methodIterator = foundMethods.begin(); methodIterator != foundMethods.end(); methodIterator++) + { + ScriptingMethodPtr method = *methodIterator; + + std::string curName = scripting_method_get_name (method); + if (methods.find (curName.c_str()) != methods.end ()) + continue; + + methods.insert (std::make_pair (strcpy (new char[curName.length() + 1], curName.c_str()), method)); + } +} +#endif + +#if ENABLE_MONO +static bool IsCoroutine (MonoMethod* method, const CommonScriptingClasses& common) +{ + MonoType* returnType = mono_signature_get_return_type (mono_method_signature (method)); + if (returnType == NULL) + return false; + + MonoClass* returnClass = mono_class_from_mono_type (returnType); + + // C# iterators return iEnumerator + return returnClass == common.iEnumerator; +} + +static bool CheckMethodParameters (ScriptingMethodPtr method, MonoClass* klass, MonoClass** klassArray, unsigned numParams, Object* errorContext, MonoManager& manager) +{ + MonoMethodSignature* signature = mono_method_signature (method->monoMethod); + int paramCount = mono_signature_get_param_count (signature); + if (paramCount != numParams) + { + const char* foundClass = mono_class_get_name (klass); + string prefix = Format ("Script error (%s): %s.\n", foundClass, mono_method_get_name (method->monoMethod)); + string postfix = "The function will be ignored."; + string message = Format("%sThe function must have exactly %i parameters.\n%s", prefix.c_str(), numParams, postfix.c_str()); + LogScriptError (message, errorContext); + return false; + } + + void* iterator = NULL; + + bool success = true; + + for (int i=0;i<numParams;++i) + { + MonoClass* monoParameterClass = mono_class_from_mono_type (mono_signature_get_params (signature, &iterator)); + + if (monoParameterClass != mono_get_object_class () && !mono_class_is_subclass_of (klassArray[i], monoParameterClass, true)) + { + success = false; + break; + } + } + + if (!success) + { + const char* foundClass = mono_class_get_name (klass); + string prefix = Format ("Script error(%s): %s.\n", foundClass, mono_method_get_name (method->monoMethod)); + string postfix = "The function will be ignored."; + string message; + for (int i=0;i<numParams;++i) + { + message += mono_class_get_name (klassArray[i]); + message += (i<numParams-1)?" and ":"."; + } + LogScriptError (prefix + "The function parameters have to be of type: " + message + "\n" + postfix, errorContext); + return false; + } + else + return true; +} + +static bool Check1MethodParameters (ScriptingMethodPtr method, MonoClass* klass, MonoClass* klass0, Object* errorContext, MonoManager& manager) +{ + MonoClass* klassArray[1]; + klassArray[0] = klass0; + return CheckMethodParameters(method, klass, klassArray, 1, errorContext, manager); +} + +static bool Check2MethodParameters (ScriptingMethodPtr method, MonoClass* klass, MonoClass* klass0, MonoClass* klass1, Object* errorContext, MonoManager& manager) +{ + MonoClass* klassArray[2]; + klassArray[0] = klass0; + klassArray[1] = klass1; + return CheckMethodParameters(method, klass, klassArray, 2, errorContext, manager); +} + +// A messages has to: +// - return either void or bool. +// - have 1 or zero arguments +// - the argument has to be derived from Object or be a builtin type +// otherwise the method gets ignored as a message receiver +// This is done to simplify the MessageHandling at runtime. +static bool CheckMessageParameters (MonoMethod* method, int messageIndex, Object* errorContext, MonoManager& manager) +{ + MessageHandler& msgHandler = GameObject::GetMessageHandler (); + string messageName = msgHandler.MessageIDToName (messageIndex); + + string prefix = "Script error: " + string (messageName) + "\n"; + string postfix = "The message will be ignored."; + + + // Needs to have zero or one parameter + MonoMethodSignature* signature = mono_method_signature (method); + int paramCount = mono_signature_get_param_count (signature); + if (paramCount != 0 && paramCount != 1) + { + LogScriptError (prefix + "The message must have 0 or 1 parameters.\n" + postfix, errorContext); + return false; + } + + MonoClass* monoObjectClass = mono_get_object_class (); + + MonoImage* engineImage = manager.GetEngineImage(); + + if (paramCount == 1) + { + // Passed msg needs to have a parameter + if (msgHandler.MessageIDToParameter (messageIndex) == 0) + { + LogScriptError (prefix + "The message may not have any parameters.\n" + postfix, errorContext); + return false; + } + + void* iterator = NULL; + MonoType* monoParameterType = mono_signature_get_params (signature, &iterator); + MonoClass* monoParameterClass = mono_class_from_mono_type (monoParameterType); + int typeType = mono_type_get_type (monoParameterType); + MessageIdentifier msg = msgHandler.MessageIDToMessageIdentifier (messageIndex); + + const char* formattedMsgParameter = ""; + + // Specific c to mono remap + if (msg.scriptParameterName) + { + MonoClass* scriptParameterClass = mono_class_from_name(engineImage, "UnityEngine", msg.scriptParameterName); + AssertIf(scriptParameterClass == NULL); + if (monoParameterClass == scriptParameterClass && scriptParameterClass) + return true; + else + formattedMsgParameter = msg.scriptParameterName; + } + + if (msg.parameterClassId != -1) // Check when derived from Object + { + // javascript style + if (monoParameterClass == monoObjectClass) + return true; + + // Check custom data passing. + // Generalize this! + if (msg.parameterClassId == ClassID(Collision)) + { + if (monoParameterClass == manager.GetCommonClasses().collision) + return true; + formattedMsgParameter = "Collision"; + } + else if (msg.parameterClassId == ClassID(Collision2D)) + { + if (monoParameterClass == manager.GetCommonClasses().collision2D) + return true; + formattedMsgParameter = "Collision2D"; + } + else + { + // Allow anything that is a super class + if (monoParameterClass) + { + int monoParameterClassID = Scripting::GetClassIDFromScriptingClass (monoParameterClass); + + if (monoParameterClassID != -1 && Object::IsDerivedFromClassID (msg.parameterClassId, monoParameterClassID)) + return true; + } + + // Format the error nicer + formattedMsgParameter = Object::ClassIDToString (msg.parameterClassId).c_str (); + } + } + + // Check for MonoObject passed directly + ///@TODO: REMOVE THIS AND DO A PROPER SendMessage for the character controller + if (msg.parameterClassId == ClassID(MonoObject)) + return true; + + // Check built in types + if (typeType == MONO_TYPE_BOOLEAN && msg.parameterClassId == ClassID (bool)) + return true; + if (typeType == MONO_TYPE_I4 && msg.parameterClassId == ClassID (int)) + return true; + if (typeType == MONO_TYPE_R4 && msg.parameterClassId == ClassID (float)) + return true; + + // Should have already returned if everything was OK... + LogScriptError (prefix + "This message parameter has to be of type: " + formattedMsgParameter + "\n" + postfix, errorContext); + return false; + } + return true; +} + +#endif//ENABLE_MONO + +ScriptingMethodPtr FindMethod (const MonoScriptCache& cache, const char* name) +{ + MonoScriptCache::MethodCache::const_iterator found = cache.methodCache.find (name); + if (found != cache.methodCache.end ()) + return found->second; + else + return SCRIPTING_NULL; +} + + +static void PopulateMethods(MonoScriptCache& cache, MonoClass* klass, Object* errorContext) +{ + int messageCount = GameObject::GetMessageHandler ().GetMessageCount (); + cache.methods.resize_initialized (MonoScriptCache::kMethodCount + messageCount, SCRIPTING_NULL); + + // Check the methods we support (Eg. Update, Render ...) + DebugAssertIf (kMethodNames[MonoScriptCache::kMethodCount] != NULL); + for (int i=0;i<MonoScriptCache::kMethodCount;i++) + { + DebugAssertIf(kMethodNames[i] == NULL); + + ScriptingMethodPtr method = FindMethod (cache, kMethodNames[i]); +#if ENABLE_MONO + MonoManager& manager = GetMonoManager(); + const CommonScriptingClasses& common = manager.GetCommonClasses(); + + if (method) + { + int parameterCount = mono_signature_get_param_count (mono_method_signature (method->monoMethod)); +#if ENABLE_NETWORK + if (i == MonoScriptCache::kSerializeNetView) + { + if (parameterCount == 1) + { + if (!Check1MethodParameters (method, klass, common.bitStream, errorContext, manager)) + method = NULL; + } + else + { + if (!Check2MethodParameters (method, klass, common.bitStream, common.networkMessageInfo, errorContext, manager)) + method = NULL; + } + } + else if (i == MonoScriptCache::kNetworkInstantiate) + { + if (!Check1MethodParameters (method, klass, common.networkMessageInfo, errorContext, manager)) + method = NULL; + } + else +#endif + if (i == MonoScriptCache::kRenderImageFilter) + { + if (!Check2MethodParameters (method, klass, common.renderTexture, common.renderTexture, errorContext, manager)) + method = NULL; + } + else if (i == MonoScriptCache::kAudioFilterRead) + { + if (!Check2MethodParameters (method, klass, common.floatSingleArray, common.int_32, errorContext, manager)) + method = NULL; + } + else if (parameterCount != 0) + { + method = NULL; + const char* foundClass = mono_class_get_name (klass); + LogScriptError (string ("Script error (") + foundClass + "): " + kMethodNames[i] + "() can not take parameters.", errorContext); + } + else if (IsCoroutine (method->monoMethod, common)) + { + if (i == MonoScriptCache::kStart || i == MonoScriptCache::kMain) + method = NULL; + else if (i != MonoScriptCache::kCoroutineStart && i != MonoScriptCache::kCoroutineMain) + { + method = NULL; + const char* foundClass = mono_class_get_name (klass); + LogScriptError (string ("Script error (") + foundClass + "): " + kMethodNames[i] + "() can not be a coroutine.", errorContext); + } + } + } +#endif + cache.methods[i] = method; + } + + // Check which messages we support + for (int i=0;i<messageCount;i++) + { + MessageHandler& msgHandler = GameObject::GetMessageHandler (); + if ((msgHandler.MessageIDToMessageIdentifier (i).options & MessageIdentifier::kSendToScripts) == 0) + continue; + + const char* messageName = msgHandler.MessageIDToName (i); + ScriptingMethodPtr method = FindMethod (cache, messageName); + +#if ENABLE_MONO + if (method) + { + if (!CheckMessageParameters (method->monoMethod, i, errorContext, GetMonoManager())) + method = NULL; + } + +#endif + cache.methods[i + MonoScriptCache::kMethodCount] = method; + } + +} + +static void RegisterNetworkRPC(MonoScriptCache& cache, const CommonScriptingClasses& common) +{ +#if ENABLE_NETWORK + if (gRegisterMonoRPC && common.RPC) + { + for (MethodCache::iterator m=cache.methodCache.begin();m != cache.methodCache.end();m++) + { + MonoMethod* meth = m->second->monoMethod; + MonoCustomAttrInfo* attr = mono_custom_attrs_from_method(meth); + if (attr != NULL) + { + if (mono_custom_attrs_has_attr (attr, common.RPC)) + gRegisterMonoRPC (mono_method_get_name(meth)); + mono_custom_attrs_free(attr); + } + } + } +#endif +} + +MonoScriptCache::MonoScriptCache () +{ + scriptType = kScriptTypeNotInitialized; + klass = SCRIPTING_NULL; + className = NULL; + #if UNITY_EDITOR + scriptTypeWasJustCreatedFromComponentMenu = false; + runInEditMode = false; + #endif +} + + +MonoScriptCache::~MonoScriptCache () +{ +#if ENABLE_SCRIPTING + ClearMethodCache (methodCache); +#endif +} + +void MonoScriptCache::Release () const +{ + MonoScriptCache* cache = const_cast<MonoScriptCache*> (this); + if (cache->refCount.Release()) + { + UNITY_DELETE(cache, kMemScriptManager); + } +} + +void MonoScriptCache::Retain () const +{ + const_cast<AtomicRefCounter&> (refCount).Retain(); +} + +MonoScriptCache* CreateMonoScriptCache (ScriptingTypePtr klass, bool isEditorScript, Object* errorContext) +{ + MonoScriptCache* cache = UNITY_NEW (MonoScriptCache, kMemScriptManager); + + // Class still needs to be assigned, even on things not derived from MonoBehaviour or ScriptableObject (e.g. for + // interfaces etc.). So that any GetComponent(string) calls will be able to find us. + cache->klass = klass; + + if (klass == NULL) + { + cache->scriptType = kScriptTypeClassNotFound; + return cache; + } + + MonoManager& manager = GetMonoManager(); + const CommonScriptingClasses& common = manager.GetCommonClasses(); + cache->className = scripting_class_get_name (klass); + + #if ENABLE_MONO + if (mono_class_get_flags (klass) & MONO_TYPE_ATTR_ABSTRACT) + { + cache->scriptType = kScriptTypeClassIsAbstract; + return cache; + } + + #endif + + #if ENABLE_MONO && (!UNITY_PEPPER) + // @TODO: NACL should support mono_class_is_generic + if (mono_class_is_generic (klass) || mono_class_is_inflated (klass)) + { + cache->scriptType = kScriptTypeClassIsGeneric; + return cache; + } + #endif + + + if (scripting_class_is_subclass_of(klass, common.monoBehaviour)) + cache->scriptType = kScriptTypeMonoBehaviourDerived; + else if (scripting_class_is_subclass_of (klass, common.scriptableObject)) + cache->scriptType = kScriptTypeScriptableObjectDerived; + else + { + cache->scriptType = kScriptTypeNothingDerived; + return cache; + } + + #if UNITY_EDITOR + if (isEditorScript) + { + if (cache->scriptType == kScriptTypeScriptableObjectDerived) + { + cache->scriptType = kScriptTypeEditorScriptableObjectDerived; + } + else + { + cache->scriptType = kScriptTypeNothingDerived; + return cache; + } + } + #endif + + ClearMethodCache (cache->methodCache); + BuildMethodCache (cache->methodCache, cache->klass, false); + + PopulateMethods(*cache, cache->klass, errorContext); + RegisterNetworkRPC (*cache, common); + + #if UNITY_EDITOR + // Is this an edit mode script? + MonoObject* monoScriptObject = mono_type_get_object(mono_domain_get(), mono_class_get_type(klass)); + ScriptingInvocation invocation(common.checkIsEditMode); + invocation.AddObject(monoScriptObject); + + cache->runInEditMode = MonoObjectToBool(invocation.Invoke()) || isEditorScript; + #endif + + return cache; +} + +bool IsValidScriptType(MonoScriptType type) +{ + return (type == kScriptTypeMonoBehaviourDerived || + type == kScriptTypeEditorScriptableObjectDerived || + type == kScriptTypeScriptableObjectDerived); +} + +std::string FormatScriptTypeError(MonoScriptType type, const std::string& fileName) +{ + if (type == kScriptTypeClassNotFound) + return Format("The class defined in script file named '%s' does not match the file name!", fileName.c_str()); + if (type == kScriptTypeNothingDerived) + return Format("The class defined in the script file named '%s' is not derived from MonoBehaviour or ScriptableObject!", fileName.c_str()); + if (type == kScriptTypeClassIsAbstract) + return Format("The class in script file named '%s' is abstract. The script class can't be abstract!", fileName.c_str()); + if (type == kScriptTypeClassIsInterface) + return Format("The class in script file named '%s' is an interface. The script can't be an interface!", fileName.c_str()); + if (type == kScriptTypeClassIsGeneric) + return Format("The class in script file named '%s' is generic. Generic MonoBehaviours are not supported!", fileName.c_str()); + if (type == kScriptTypeNotInitialized) + return Format("The class in script file named '%s' is not yet initialized!", fileName.c_str()); + if (type == kScriptTypeScriptMissing) + return "The referenced script on this Behaviour is missing!"; + + return ""; +} + +#endif
\ No newline at end of file diff --git a/Runtime/Mono/MonoScriptCache.h b/Runtime/Mono/MonoScriptCache.h new file mode 100644 index 0000000..04271ae --- /dev/null +++ b/Runtime/Mono/MonoScriptCache.h @@ -0,0 +1,50 @@ +#pragma once + +#include "Runtime/Scripting/Backend/ScriptingTypes.h" +#include "Runtime/Threads/AtomicRefCounter.h" +#include "Runtime/Utilities/CStringHash.h" +#include "Runtime/Utilities/dynamic_array.h" +#include "MonoScriptType.h" + +struct CommonScriptingClasses; + + +// A refcounted constant method cache for the MonoBehaviour. +// When creating the MonoScript it precomputes all available methods etc. +// The data is shared in this class. +// It is refcounted, in case the MonoSript is deleted before the MonoBehaviour is destroyed. + +struct MonoScriptCache +{ + typedef std::map<const char*, ScriptingMethodPtr, compare_cstring> MethodCache; + + enum { kUpdate = 0, kLateUpdate, kFixedUpdate, kAwake, kStart, kMain, kRenderObject, kAddToManager, kRemoveFromManager, kRemoveFromManagerInternal, kCoroutineStart, kCoroutineMain, kRenderImageFilter, kDrawGizmos, kGUI, kValidateProperties, kSerializeNetView, kNetworkInstantiate, kOnDestroy, kAudioFilterRead, kMethodCount }; + + AtomicRefCounter refCount; + ScriptingClassPtr klass; + dynamic_array<ScriptingMethodPtr> methods; + MethodCache methodCache; + MonoScriptType scriptType; + const char* className; + + #if UNITY_EDITOR + bool scriptTypeWasJustCreatedFromComponentMenu; + bool runInEditMode; + #endif + + + MonoScriptCache (); + ~MonoScriptCache (); + + void Release () const; + void Retain () const; +}; + +ScriptingMethodPtr FindMethod (const MonoScriptCache& cache, const char* name); + +typedef void RegisterMonoRPCCallback (const char* name); +void RegisterMonoRPC (RegisterMonoRPCCallback* callback); +MonoScriptCache* CreateMonoScriptCache (ScriptingTypePtr klass, bool isEditorScript, Object* errorContext); + +bool IsValidScriptType(MonoScriptType type); +std::string FormatScriptTypeError(MonoScriptType type, const std::string& fileName); diff --git a/Runtime/Mono/MonoScriptManager.cpp b/Runtime/Mono/MonoScriptManager.cpp new file mode 100644 index 0000000..4b1c3c6 --- /dev/null +++ b/Runtime/Mono/MonoScriptManager.cpp @@ -0,0 +1,150 @@ +#include "UnityPrefix.h" +#include "Runtime/Mono/MonoScript.h" +#include "Runtime/Mono/MonoScriptManager.h" +#include "Runtime/Threads/Thread.h" + +#if ENABLE_SCRIPTING +template<typename T> MonoScript* FindScript(MonoScriptManager::Scripts& scripts, T& filter) +{ + MonoScriptManager::Scripts::iterator i, next; + for (i= scripts.begin ();i != scripts.end ();i=next) + { + next = i; next++; + MonoScript* script = *i; + if (script == NULL) + { + scripts.erase (i); + } + else if (filter.Match(script)) + return script; + } + return NULL; +} + +struct MatchScriptByNameFilter +{ + const char* name; + bool Match(MonoScript* ms) { return ms->GetScriptClassName() == name; } +}; + +struct MatchScriptByClassFilter +{ + ScriptingClassPtr klass; + bool Match(MonoScript* ms) { return ms->GetClass() == klass; } +}; + +struct MatchScriptByClassNamespaceAssemblyFilter +{ + // hold by const ref to minimize copying + std::string const& scriptClassName, nameSpace, assemblyName; + + MatchScriptByClassNamespaceAssemblyFilter (std::string const& scriptName, std::string const& ns, std::string const& assembly) + : scriptClassName (scriptName), nameSpace (ns), assemblyName (assembly) + {} + + bool Match(MonoScript* ms) + { + return ms->GetScriptClassName () == scriptClassName + && ms->GetNameSpace () == nameSpace + && ms->GetAssemblyName () == assemblyName; + } +}; + +static MonoScript* FindScript(ScriptingClassPtr klass, MonoScriptManager::Scripts& scripts) +{ + MatchScriptByClassFilter filter; + filter.klass = klass; + return FindScript(scripts,filter); +} + +static void AddScriptsToList(MonoScriptManager::Scripts& scripts, MonoScriptManager::AllScripts& addtothis) +{ + MonoScriptManager::Scripts::iterator i, next; + + for (i= scripts.begin ();i != scripts.end ();i=next) + { + next = i; next++; + MonoScript* script = *i; + if (script) + addtothis.insert (script); + else + { + scripts.erase (i); + } + } +} + +MonoScriptManager::AllScripts MonoScriptManager::GetAllRuntimeScripts () +{ + ASSERT_RUNNING_ON_MAIN_THREAD + + AllScripts scripts; + AddScriptsToList(m_RuntimeScripts,scripts); + return scripts; +} + +MonoScript* MonoScriptManager::FindRuntimeScript (const string& className) +{ + ASSERT_RUNNING_ON_MAIN_THREAD + + MatchScriptByNameFilter filter; + filter.name = className.c_str(); + return FindScript(m_RuntimeScripts,filter); +} + +MonoScript* MonoScriptManager::FindRuntimeScript (const string& className, string const& nameSpace, string const& assembly) +{ + ASSERT_RUNNING_ON_MAIN_THREAD + + MatchScriptByClassNamespaceAssemblyFilter filter (className, nameSpace, assembly); + return FindScript (m_RuntimeScripts, filter); +} + +MonoScript* MonoScriptManager::FindRuntimeScript (ScriptingClassPtr klass) +{ + ASSERT_RUNNING_ON_MAIN_THREAD + return FindScript(klass,m_RuntimeScripts); +} + +void MonoScriptManager::RegisterRuntimeScript (MonoScript& script) +{ + ASSERT_RUNNING_ON_MAIN_THREAD + m_RuntimeScripts.insert (&script); +} + +#if UNITY_EDITOR +MonoScript* MonoScriptManager::FindEditorScript (MonoClass* klass) +{ + ASSERT_RUNNING_ON_MAIN_THREAD + return FindScript(klass,m_EditorScripts); +} + +MonoScript* MonoScriptManager::FindEditorScript (const string& className) +{ + ASSERT_RUNNING_ON_MAIN_THREAD + + MatchScriptByNameFilter filter; + filter.name = className.c_str(); + return FindScript(m_EditorScripts,filter); +} + +void MonoScriptManager::RegisterEditorScript (MonoScript& script) +{ + ASSERT_RUNNING_ON_MAIN_THREAD + m_EditorScripts.insert (&script); +} + +MonoScriptManager::AllScripts MonoScriptManager::GetAllRuntimeAndEditorScripts () +{ + ASSERT_RUNNING_ON_MAIN_THREAD + + AllScripts scripts; + + AddScriptsToList(m_RuntimeScripts,scripts); + AddScriptsToList(m_EditorScripts,scripts); + + return scripts; +} + +#endif +#endif diff --git a/Runtime/Mono/MonoScriptManager.h b/Runtime/Mono/MonoScriptManager.h new file mode 100644 index 0000000..fc05127 --- /dev/null +++ b/Runtime/Mono/MonoScriptManager.h @@ -0,0 +1,40 @@ +#ifndef _MONOSCRIPTMANAGER_H_ +#define _MONOSCRIPTMANAGER_H_ + +class MonoScript; +class MonoManager; + +class MonoScriptManager +{ +public: + void RegisterRuntimeScript (MonoScript& script); + void RegisterEditorScript (MonoScript& script); + + typedef UNITY_SET(kMemScriptManager, MonoScript*) AllScripts; + typedef UNITY_SET(kMemScriptManager, PPtr<MonoScript>) Scripts; + + MonoScript* FindRuntimeScript (const std::string& className); + MonoScript* FindRuntimeScript (const std::string& className, std::string const& nameSpace, std::string const& assembly); + MonoScript* FindRuntimeScript (ScriptingClassPtr klass); + AllScripts GetAllRuntimeScripts (); + +#if UNITY_EDITOR + AllScripts GetAllRuntimeAndEditorScripts (); + MonoScript* FindEditorScript (MonoClass* klass); + MonoScript* FindEditorScript (const std::string& className); +#else + MonoScript* FindEditorScript (ScriptingClassPtr klass) { return NULL; } +#endif + + +private: + + Scripts m_RuntimeScripts; +#if UNITY_EDITOR + Scripts m_EditorScripts; +#endif + + friend class MonoManager; +}; + +#endif diff --git a/Runtime/Mono/MonoScriptType.h b/Runtime/Mono/MonoScriptType.h new file mode 100644 index 0000000..eefe952 --- /dev/null +++ b/Runtime/Mono/MonoScriptType.h @@ -0,0 +1,19 @@ +#ifndef MONOSCRIPTTYPE_H +#define MONOSCRIPTTYPE_H + +enum MonoScriptType +{ + kScriptTypeMonoBehaviourDerived = 0, + kScriptTypeScriptableObjectDerived = 1, + kScriptTypeEditorScriptableObjectDerived = 2, + + kScriptTypeNotInitialized = -1, + kScriptTypeNothingDerived = -2, + kScriptTypeClassNotFound = -3, + kScriptTypeClassIsAbstract = -4, + kScriptTypeClassIsInterface = -5, + kScriptTypeClassIsGeneric = -6, + kScriptTypeScriptMissing = -7 +}; + +#endif // !MONOSCRIPTTYPE_H diff --git a/Runtime/Mono/MonoTypeSignatures.h b/Runtime/Mono/MonoTypeSignatures.h new file mode 100644 index 0000000..d3a7149 --- /dev/null +++ b/Runtime/Mono/MonoTypeSignatures.h @@ -0,0 +1,170 @@ +/* + * blob.h: Definitions used to pull information out of the Blob + * + */ +#ifndef _MONO_METADATA_BLOB_H_ +#define _MONO_METADATA_BLOB_H_ + +#define SIGNATURE_HAS_THIS 0x20 +#define SIGNATURE_EXPLICIT_THIS 0x40 +#define SIGNATURE_VARARG 0x05 + +/* + * Encoding for type signatures used in the Metadata + */ +typedef enum { + MONO_TYPE_END = 0x00, /* End of List */ + MONO_TYPE_VOID = 0x01, + MONO_TYPE_BOOLEAN = 0x02, + MONO_TYPE_CHAR = 0x03, + MONO_TYPE_I1 = 0x04, + MONO_TYPE_U1 = 0x05, + MONO_TYPE_I2 = 0x06, + MONO_TYPE_U2 = 0x07, + MONO_TYPE_I4 = 0x08, + MONO_TYPE_U4 = 0x09, + MONO_TYPE_I8 = 0x0a, + MONO_TYPE_U8 = 0x0b, + MONO_TYPE_R4 = 0x0c, + MONO_TYPE_R8 = 0x0d, + MONO_TYPE_STRING = 0x0e, + MONO_TYPE_PTR = 0x0f, /* arg: <type> token */ + MONO_TYPE_BYREF = 0x10, /* arg: <type> token */ + MONO_TYPE_VALUETYPE = 0x11, /* arg: <type> token */ + MONO_TYPE_CLASS = 0x12, /* arg: <type> token */ + MONO_TYPE_VAR = 0x13, /* number */ + MONO_TYPE_ARRAY = 0x14, /* type, rank, boundsCount, bound1, loCount, lo1 */ + MONO_TYPE_GENERICINST= 0x15, /* <type> <type-arg-count> <type-1> \x{2026} <type-n> */ + MONO_TYPE_TYPEDBYREF = 0x16, + MONO_TYPE_I = 0x18, + MONO_TYPE_U = 0x19, + MONO_TYPE_FNPTR = 0x1b, /* arg: full method signature */ + MONO_TYPE_OBJECT = 0x1c, + MONO_TYPE_SZARRAY = 0x1d, /* 0-based one-dim-array */ + MONO_TYPE_MVAR = 0x1e, /* number */ + MONO_TYPE_CMOD_REQD = 0x1f, /* arg: typedef or typeref token */ + MONO_TYPE_CMOD_OPT = 0x20, /* optional arg: typedef or typref token */ + MONO_TYPE_INTERNAL = 0x21, /* CLR internal type */ + + MONO_TYPE_MODIFIER = 0x40, /* Or with the following types */ + MONO_TYPE_SENTINEL = 0x41, /* Sentinel for varargs method signature */ + MONO_TYPE_PINNED = 0x45 /* Local var that points to pinned object */ +} MonoTypeEnum; + +typedef enum { + MONO_PROFILE_NONE = 0, + MONO_PROFILE_APPDOMAIN_EVENTS = 1 << 0, + MONO_PROFILE_ASSEMBLY_EVENTS = 1 << 1, + MONO_PROFILE_MODULE_EVENTS = 1 << 2, + MONO_PROFILE_CLASS_EVENTS = 1 << 3, + MONO_PROFILE_JIT_COMPILATION = 1 << 4, + MONO_PROFILE_INLINING = 1 << 5, + MONO_PROFILE_EXCEPTIONS = 1 << 6, + MONO_PROFILE_ALLOCATIONS = 1 << 7, + MONO_PROFILE_GC = 1 << 8, + MONO_PROFILE_THREADS = 1 << 9, + MONO_PROFILE_REMOTING = 1 << 10, + MONO_PROFILE_TRANSITIONS = 1 << 11, + MONO_PROFILE_ENTER_LEAVE = 1 << 12, + MONO_PROFILE_COVERAGE = 1 << 13, + MONO_PROFILE_INS_COVERAGE = 1 << 14, + MONO_PROFILE_STATISTICAL = 1 << 15 +} MonoProfileFlags; + +/* + * Type Attributes (23.1.15). + */ +enum { + MONO_TYPE_ATTR_VISIBILITY_MASK = 0x00000007, + MONO_TYPE_ATTR_NOT_PUBLIC = 0x00000000, + MONO_TYPE_ATTR_PUBLIC = 0x00000001, + MONO_TYPE_ATTR_NESTED_PUBLIC = 0x00000002, + MONO_TYPE_ATTR_NESTED_PRIVATE = 0x00000003, + MONO_TYPE_ATTR_NESTED_FAMILY = 0x00000004, + MONO_TYPE_ATTR_NESTED_ASSEMBLY = 0x00000005, + MONO_TYPE_ATTR_NESTED_FAM_AND_ASSEM = 0x00000006, + MONO_TYPE_ATTR_NESTED_FAM_OR_ASSEM = 0x00000007, + + MONO_TYPE_ATTR_LAYOUT_MASK = 0x00000018, + MONO_TYPE_ATTR_AUTO_LAYOUT = 0x00000000, + MONO_TYPE_ATTR_SEQUENTIAL_LAYOUT = 0x00000008, + MONO_TYPE_ATTR_EXPLICIT_LAYOUT = 0x00000010, + + MONO_TYPE_ATTR_CLASS_SEMANTIC_MASK = 0x00000020, + MONO_TYPE_ATTR_CLASS = 0x00000000, + MONO_TYPE_ATTR_INTERFACE = 0x00000020, + + MONO_TYPE_ATTR_ABSTRACT = 0x00000080, + MONO_TYPE_ATTR_SEALED = 0x00000100, + MONO_TYPE_ATTR_SPECIAL_NAME = 0x00000400, + + MONO_TYPE_ATTR_IMPORT = 0x00001000, + MONO_TYPE_ATTR_SERIALIZABLE = 0x00002000, + + MONO_TYPE_ATTR_STRING_FORMAT_MASK = 0x00030000, + MONO_TYPE_ATTR_ANSI_CLASS = 0x00000000, + MONO_TYPE_ATTR_UNICODE_CLASS = 0x00010000, + MONO_TYPE_ATTR_AUTO_CLASS = 0x00020000, + MONO_TYPE_ATTR_CUSTOM_CLASS = 0x00030000, + MONO_TYPE_ATTR_CUSTOM_MASK = 0x00c00000, + + MONO_TYPE_ATTR_BEFORE_FIELD_INIT = 0x00100000, + MONO_TYPE_ATTR_FORWARDER = 0x00200000, + + MONO_TYPE_ATTR_RESERVED_MASK = 0x00040800, + MONO_TYPE_ATTR_RT_SPECIAL_NAME = 0x00000800, + MONO_TYPE_ATTR_HAS_SECURITY = 0x00040000 +}; + + +/* + * Method Attributes (22.1.9) + */ +enum { + METHOD_IMPL_ATTRIBUTE_CODE_TYPE_MASK = 0x0003, + METHOD_IMPL_ATTRIBUTE_IL = 0x0000, + METHOD_IMPL_ATTRIBUTE_NATIVE = 0x0001, + METHOD_IMPL_ATTRIBUTE_OPTIL = 0x0002, + METHOD_IMPL_ATTRIBUTE_RUNTIME = 0x0003, + + METHOD_IMPL_ATTRIBUTE_MANAGED_MASK = 0x0004, + METHOD_IMPL_ATTRIBUTE_UNMANAGED = 0x0004, + METHOD_IMPL_ATTRIBUTE_MANAGED = 0x0000, + + METHOD_IMPL_ATTRIBUTE_FORWARD_REF = 0x0010, + METHOD_IMPL_ATTRIBUTE_PRESERVE_SIG = 0x0080, + METHOD_IMPL_ATTRIBUTE_INTERNAL_CALL = 0x1000, + METHOD_IMPL_ATTRIBUTE_SYNCHRONIZED = 0x0020, + METHOD_IMPL_ATTRIBUTE_NOINLINING = 0x0008, + METHOD_IMPL_ATTRIBUTE_MAX_METHOD_IMPL_VAL = 0xffff, + + METHOD_ATTRIBUTE_MEMBER_ACCESS_MASK = 0x0007, + METHOD_ATTRIBUTE_COMPILER_CONTROLLED = 0x0000, + METHOD_ATTRIBUTE_PRIVATE = 0x0001, + METHOD_ATTRIBUTE_FAM_AND_ASSEM = 0x0002, + METHOD_ATTRIBUTE_ASSEM = 0x0003, + METHOD_ATTRIBUTE_FAMILY = 0x0004, + METHOD_ATTRIBUTE_FAM_OR_ASSEM = 0x0005, + METHOD_ATTRIBUTE_PUBLIC = 0x0006, + + METHOD_ATTRIBUTE_STATIC = 0x0010, + METHOD_ATTRIBUTE_FINAL = 0x0020, + METHOD_ATTRIBUTE_VIRTUAL = 0x0040, + METHOD_ATTRIBUTE_HIDE_BY_SIG = 0x0080, + + METHOD_ATTRIBUTE_VTABLE_LAYOUT_MASK = 0x0100, + METHOD_ATTRIBUTE_REUSE_SLOT = 0x0000, + METHOD_ATTRIBUTE_NEW_SLOT = 0x0100, + + METHOD_ATTRIBUTE_ABSTRACT = 0x0400, + METHOD_ATTRIBUTE_SPECIAL_NAME = 0x0800, + + METHOD_ATTRIBUTE_PINVOKE_IMPL = 0x2000, + METHOD_ATTRIBUTE_UNMANAGED_EXPORT = 0x0008, +}; + + + +inline bool IsMonoBuiltinType (int type) { return type >= MONO_TYPE_BOOLEAN && type <= MONO_TYPE_R8; } + +#endif diff --git a/Runtime/Mono/MonoTypes.h b/Runtime/Mono/MonoTypes.h new file mode 100644 index 0000000..9fa5b87 --- /dev/null +++ b/Runtime/Mono/MonoTypes.h @@ -0,0 +1,128 @@ +#ifndef MONOTYPES_H +#define MONOTYPES_H + +//TODO use mono headers directly, so we don't get burned when the struct definitions in this file +//go out of sync with mono's. +//this is not done yet, because it's tricky, as the mono headers define symbols that we also define in UnityFunctions.h, +//so we'd need to find some way to either remove those defines from the mono headers, or somehow mangle them. +#if ENABLE_MONO + +struct MonoException; +struct MonoAssembly; +struct MonoObject; +struct MonoClassField; +struct MonoClass; +struct MonoDomain; +struct MonoImage; +struct MonoType; +struct MonoMethodSignature; +struct MonoArray; +struct MonoThread; +struct MonoVTable; +struct MonoProperty; +struct MonoReflectionAssembly; +struct MonoReflectionMethod; +struct MonoAppDomain; +struct MonoCustomAttrInfo; +struct MonoDl; +#if UNITY_STANDALONE || UNITY_EDITOR +struct MonoDlFallbackHandler; +#endif + +#if UNITY_EDITOR +struct MonoMethodDesc; +#endif + +typedef const void* gconstpointer; +typedef void* gpointer; +typedef int gboolean; +typedef unsigned int guint32; +typedef int gint32; +typedef unsigned long gulong; +#if UNITY_WII + typedef signed long long gint64; +#else + typedef long gint64; +#endif +typedef unsigned char guchar; +typedef UInt16 gunichar2; +struct MonoString +{ + void* monoObjectPart1; + void* monoObjectPart2; + gint32 length; + gunichar2 firstCharacter; +}; + +struct MonoMethod { + UInt16 flags; + UInt16 iflags; +}; + +struct GPtrArray { + gpointer *pdata; + guint32 len; +}; + +typedef enum +{ + MONO_VERIFIER_MODE_OFF, + MONO_VERIFIER_MODE_VALID, + MONO_VERIFIER_MODE_VERIFIABLE, + MONO_VERIFIER_MODE_STRICT +} MiniVerifierMode; + +typedef enum { + MONO_SECURITY_MODE_NONE, + MONO_SECURITY_MODE_CORE_CLR, + MONO_SECURITY_MODE_CAS, + MONO_SECURITY_MODE_SMCS_HACK +} MonoSecurityMode; + +typedef enum { + MONO_TYPE_NAME_FORMAT_IL, + MONO_TYPE_NAME_FORMAT_REFLECTION, + MONO_TYPE_NAME_FORMAT_FULL_NAME, + MONO_TYPE_NAME_FORMAT_ASSEMBLY_QUALIFIED +} MonoTypeNameFormat; + +typedef struct { + const char *name; + const char *culture; + const char *hash_value; + const UInt8* public_key; + // string of 16 hex chars + 1 NULL + guchar public_key_token [17]; + guint32 hash_alg; + guint32 hash_len; + guint32 flags; + UInt16 major, minor, build, revision; +#if MONO_2_10 || MONO_2_12 + UInt16 arch; +#endif +} MonoAssemblyName; + +typedef void GFuncRef (void*, void*); +typedef GFuncRef* GFunc; + +typedef enum { + MONO_UNHANDLED_POLICY_LEGACY, + MONO_UNHANDLED_POLICY_CURRENT +} MonoRuntimeUnhandledExceptionPolicy; + +typedef enum { + MONO_DL_LAZY = 1, + MONO_DL_LOCAL = 2, + MONO_DL_MASK = 3 +} MonoDynamicLibraryFlag; + +#if MONO_2_12 +typedef enum { + MONO_SECURITY_CORE_CLR_OPTIONS_DEFAULT = 0, + MONO_SECURITY_CORE_CLR_OPTIONS_RELAX_REFLECTION = 1, + MONO_SECURITY_CORE_CLR_OPTIONS_RELAX_DELEGATE = 2 +} MonoSecurityCoreCLROptions; +#endif + +#endif //ENABLE_MONO +#endif diff --git a/Runtime/Mono/MonoUtility.cpp b/Runtime/Mono/MonoUtility.cpp new file mode 100644 index 0000000..1e0a4e0 --- /dev/null +++ b/Runtime/Mono/MonoUtility.cpp @@ -0,0 +1,685 @@ +#include "UnityPrefix.h" +#include "MonoIncludes.h" +#include "Runtime/Utilities/File.h" +#include "Runtime/BaseClasses/RefCounted.h" +#include "Runtime/BaseClasses/GameObject.h" +#include "Runtime/Utilities/Utility.h" +#include "Runtime/Utilities/PathNameUtility.h" +#include "Runtime/Scripting/ScriptingUtility.h" +#include "Runtime/Scripting/Scripting.h" + +#if UNITY_EDITOR +static UNITY_TLS_VALUE(void*) gStackLimit; + +static void* GetStackLimit() +{ +#if UNITY_WIN + MEMORY_BASIC_INFORMATION mbi; + VirtualQuery(&mbi, &mbi, sizeof(mbi)); + + return mbi.AllocationBase; +#elif UNITY_OSX + pthread_t self = pthread_self(); + void* addr = pthread_get_stackaddr_np(self); + size_t size = pthread_get_stacksize_np(self); + return (void*)((char*)addr - (char*)size); +#elif UNITY_LINUX + pthread_attr_t attr; + size_t stacksize = 0; + void *stackaddr = NULL; + pthread_t self = pthread_self (); + int ret = pthread_getattr_np (self, &attr); + + if (ret != 0) + { + printf_console ("pthread_getattr_np ret=%d\n", ret); + return 0; + } + + ret = pthread_attr_getstack (&attr, &stackaddr, &stacksize); + + if (ret != 0) + { + printf_console ("pthread_attr_getstack ret=%d\n", ret); + return 0; + } + + return (void*)((char*)stackaddr - (char*)stacksize); +#else +#error Platform does not have stack checking implemented. +#endif +} + +#define REQUIRED_SCRIPTING_STACK_SIZE (16*1024) + + +// Functions to check whether we have REQUIRED_SCRIPTING_STACK_SIZE (64K) of stack space available +// before calling into native code. This ensures a StackOverflowException will *not* occur in mono runtime +// or engine code. +bool IsStackLargeEnough () +{ + // Note, we assume stack grows DOWN + void* stackLimit = gStackLimit; + if (stackLimit == NULL) + gStackLimit = stackLimit = GetStackLimit(); + + if (((char*)&stackLimit-(char*)stackLimit) < REQUIRED_SCRIPTING_STACK_SIZE) + return false; + else + return true; +} + +void AssertStackLargeEnough () +{ + if (!IsStackLargeEnough ()) + { + Scripting::RaiseManagedException ("System", "StackOverflowException", ""); + } +} + +#endif + +std::string ErrorMessageForUnsupportedEnumField(MonoType* enumType, MonoType* classType, const char * fieldName) +{ + char* enumTypeName = mono_type_get_name (enumType); + char* classTypeName = mono_type_get_name (classType); + + std::string message = Format("Unsupported enum type '%s' used for field '%s' in class '%s'", + enumTypeName, + fieldName, + classTypeName); + + g_free(enumTypeName); + g_free(classTypeName); + + return message; +} + +#if MONO_QUALITY_ERRORS +MonoObject* MonoObjectNULL (ScriptingClassPtr klass, ScriptingStringPtr error) +{ + AssertMsg (klass, "NULL scripting class!"); + if (NULL == klass) + return NULL; + + if (mono_class_is_subclass_of (klass, GetScriptingManager ().GetCommonClasses ().monoBehaviour, 0)) + return NULL; + if (mono_class_is_subclass_of (klass, GetScriptingManager ().GetCommonClasses ().scriptableObject, 0)) + return NULL; + + if (!mono_class_is_subclass_of (klass, GetScriptingManager ().GetCommonClasses ().unityEngineObject, 0)) + return NULL; + + if (mono_unity_class_is_abstract (klass) || mono_unity_class_is_interface (klass)) + return NULL; + + ScriptingObjectPtr scriptingobject = mono_object_new (mono_domain_get (), klass); + if (scriptingobject == NULL) + return NULL; + + ScriptingObjectOfType<Object> object (scriptingobject); + object.SetInstanceID (0); + + if (error != NULL) + object.SetError (error); + + return scriptingobject; +} + +MonoObject* MonoObjectNULL (int classID, MonoString* error) +{ + AssertIf (classID == -1); + if (classID == ClassID (MonoBehaviour)) + return NULL; + + ScriptingObjectPtr scriptingobject = Scripting::InstantiateScriptingWrapperForClassID(classID); + if (scriptingobject == NULL) + return NULL; + + ScriptingObjectOfType<Object> object(scriptingobject); + object.SetInstanceID(0); + + if (error != NULL) + object.SetError(error); + + return scriptingobject; +} + +MonoString* MissingComponentString (GameObject& go, const char* klassName) +{ + return MonoStringFormat( + "MissingComponentException:There is no '%s' attached to the \"%s\" game object, but a script is trying to access it.\n" + "You probably need to add a %s to the game object \"%s\". Or your script needs to check if the component is attached before using it.", + klassName, go.GetName(), klassName, go.GetName()); +} + +MonoString* MissingComponentString (GameObject& go, int classID) +{ + const string& className = Object::ClassIDToString(classID); + return MissingComponentString(go,className.c_str()); +} + +MonoString* MissingComponentString (GameObject& go, ScriptingTypePtr klass) +{ + return MissingComponentString(go,scripting_class_get_name(klass)); +} + +#endif + +int mono_array_length (MonoArray* array) +{ + char* raw = sizeof(uintptr_t)*3 + (char*)array; + return *reinterpret_cast<uintptr_t*> (raw); +} + +int mono_array_length_safe (MonoArray* array) +{ + if (array) + { + char* raw = sizeof(uintptr_t)*3 + (char*)array; + return *reinterpret_cast<uintptr_t*> (raw); + } + else + { + return 0; + } +} + +ScriptingClassPtr GetBuiltinScriptingClass(const char* name,bool optional) +{ + return GetMonoManager().GetBuiltinMonoClass(name,optional); +} + + +#if USE_MONO_AOT && !(UNITY_XENON || UNITY_PS3) + +// Flag defined in mono, when AOT libraries are built with -ficall option +// But that is not available in mono/consoles +extern "C" int mono_ficall_flag; + +void* ResolveMonoMethodPointer(MonoDomain* domain, MonoMethod* method) +{ + return mono_ficall_flag && method ? mono_aot_get_method(domain, method) : NULL; +} +#else +void* ResolveMonoMethodPointer(MonoDomain* domain, MonoMethod* method) +{ + return NULL; +} +#endif + +void mono_runtime_object_init_exception (MonoObject *thiss, MonoException** exception) +{ + MonoClass *klass = mono_object_get_class (thiss); + + MonoMethod *method; + void* iter = NULL; + while ((method = mono_class_get_methods (klass, &iter))) { + MonoMethodSignature *signature = mono_method_signature (method); + if (!signature) { + ErrorString (Format ("Error looking up signature for method %s.%s", mono_class_get_name (klass), mono_method_get_name (method))); + continue; + } + int paramCount = mono_signature_get_param_count (signature); + if (!strcmp (".ctor", mono_method_get_name (method)) && signature && paramCount == 0) + break; + } + + if (method) + { + AssertIf (mono_class_is_valuetype (mono_method_get_class (method))); + mono_runtime_invoke_profiled (method, thiss, NULL, exception); + } + else + { + *exception = NULL; + } +} + +void mono_runtime_object_init_log_exception (MonoObject *thiss) +{ + if (!thiss) + return; + MonoException* exc = NULL; + mono_runtime_object_init_exception(thiss, &exc); + if (exc) + ::Scripting::LogException(exc, 0); +} + +/* +mono_enumerator_next (MonoObject* enumerable, gconstpointer pointer) +{ + +}*/ + +bool IsUtf16InAsciiRange( gunichar2 const* str, int length ) +{ + gunichar2 const* strEnd = str + length; + while( str != strEnd ) + { //length-- ) { + if( (*str & ~((gunichar2)0x7f)) != 0 ) + return false; + ++str; + } + return true; +} + +bool FastTestAndConvertUtf16ToAscii( char* destination, gunichar2 const* str, int length ) +{ + gunichar2 const* strEnd = str + length; + while( str != strEnd ) { //length-- ) { + if( (*str & ~((gunichar2)0x7f)) != 0 ) + return false; + *destination = (char)*str; + ++destination; + ++str; + } + return true; +} + +// converts symbols in the range 0x00-0x7f from unicode16 to ascii (excluding the terminating 0 character) +void fastUtf16ToAscii( char* destination, gunichar2 const* str, int length ) +{ + gunichar2 const* strEnd = str + length; + while( str != strEnd ) { + *destination = (char)*str; + ++destination; + ++str; + } +} + +#if UNITY_WIN || UNITY_XENON +std::wstring MonoStringToWideCpp (MonoString* monoString) +{ + if (monoString) + { + wchar_t* buf = (wchar_t*)mono_string_to_utf16(monoString); + std::wstring temp (buf); + g_free (buf); + return temp; + } + else + return std::wstring (); +} +#endif + +std::string MonoStringToCpp (MonoString* monoString) +{ + if (!monoString) + return string (); + + char buff[256]; + if(monoString->length <= 256 && FastTestAndConvertUtf16ToAscii (buff,mono_string_chars(monoString), mono_string_length(monoString)) ) + return string((char const*)buff,mono_string_length (monoString)); + + char* buf = mono_string_to_utf8 (monoString); + string temp (buf); + g_free (buf); + return temp; +} + +MonoArray *mono_array_new_2d (int size0, int size1, MonoClass *klass) { + guint32 sizes[] = {size0, size1}; + MonoClass* ac = mono_array_class_get (klass, 2); + + return mono_array_new_full(mono_domain_get (), ac, sizes, NULL); +} + +MonoArray *mono_array_new_3d (int size0, int size1, int size2, MonoClass *klass) { + guint32 sizes[] = {size0, size1, size2}; + MonoClass* ac = mono_array_class_get (klass, 3); + + return mono_array_new_full(mono_domain_get (), ac, sizes, NULL); +} + +std::string MonoStringToCppChecked (MonoObject* monoString) +{ + if (monoString && mono_type_get_type(mono_class_get_type(mono_object_get_class(monoString))) == MONO_TYPE_STRING) + { + char* buf = mono_string_to_utf8 ((MonoString*)monoString); + string temp (buf); + g_free (buf); + return temp; + } + else + return string (); +} + +inline bool ExtractLineAndPath (const string& exception, string::size_type& pathBegin, int& line, string& path) +{ + // Extract line and path from exception ... + // Format is: in [0x00031] (at /Users/.../filename.cs:51) + + pathBegin = exception.find ("(at ", pathBegin); + + if (pathBegin != string::npos) + { + pathBegin += 4; + + // On Windows, there's a ':' right at the beginning as part of drive + #if UNITY_WIN && UNITY_EDITOR + std::string::size_type pathEnd = exception.find (':', exception.size() > pathBegin+2 ? pathBegin+2 : pathBegin); + #else + std::string::size_type pathEnd = exception.find (':', pathBegin); + #endif + if (pathEnd != string::npos) + { + path.assign (exception.begin () + pathBegin, exception.begin () + pathEnd); + line = atoi (exception.c_str () + pathEnd + 1); + pathBegin = pathEnd; + ConvertSeparatorsToUnity(path); + return true; + } + } + return false; +} + +inline bool IsScriptAssetPath (const string& path) +{ + const string& projectDir = File::GetCurrentDirectory (); + // C# returns absolute path names + if (path.find (projectDir) == 0) + return true; + // Boo returns relative path names + if (!IsAbsoluteFilePath(path) ) + return true; + return false; +} + +bool ExceptionToLineAndPath (const string& stackTrace, int& line, string& path) +{ + // Extract line and path from exception... + // We want the topmost exception function trace that is in the project folder. + // If there is nothing in the project folder we return the topmost. + // Format is: in [0x00031] (at /Users/.../filename.cs:51) + string::size_type searchStart = 0; + + if (ExtractLineAndPath (stackTrace, searchStart, line, path) && !IsScriptAssetPath (path)) + { + string tempPath; + int tempLine; + while (ExtractLineAndPath (stackTrace, searchStart, tempLine, tempPath)) + { + if (!IsAbsoluteFilePath(tempPath)) + { + path = tempPath; + line = tempLine; + break; + } + } + return true; + } + else + return false; +} + + +string SimpleGetExceptionString(MonoException* exception) +{ + MonoClass* klass = mono_object_get_class((MonoObject*)exception); + if (!klass) + return ""; + + MonoMethod* toString = mono_class_get_method_from_name(mono_get_exception_class(), "ToString", 0); + if (!toString) + return ""; + + MonoString* exceptionString = (MonoString*)mono_runtime_invoke_profiled(toString, (MonoObject*)exception, NULL, NULL); + if (!exceptionString) + return ""; + + return mono_string_to_utf8(exceptionString); +} + +MonoString* MonoStringNew (const std::string& in) +{ + return MonoStringNew (in.c_str ()); +} + +MonoString* MonoStringNew (const char* in) +{ + Assert (in != NULL); + MonoString* mono = mono_string_new_wrapper (in); + if (mono != NULL) + return mono; + else + { + // This can happen when conversion fails eg. converting utf8 to ascii or something i guess. + mono = mono_string_new_wrapper (""); + Assert (mono != NULL); + return mono; + } +} + +MonoString* MonoStringNewUTF16 (const wchar_t* in) +{ + Assert (in != NULL); + MonoString* mono = mono_string_from_utf16 ( (const gunichar2*)in ); + if (mono != NULL) + return mono; + else + { + // See MonoStringNew + mono = mono_string_new_wrapper (""); + Assert (mono != NULL); + return mono; + } +} + +MonoString* MonoStringNewLength (const char* in, int length) +{ + Assert (in != NULL); + Assert (length >= 0); + MonoDomain* domain = mono_domain_get (); + Assert (domain != NULL); + MonoString* mono = mono_string_new_len (domain, in, length); + if (mono != NULL) + return mono; + else + { + // This can happen when conversion fails eg. converting utf8 to ascii or something i guess. + mono = mono_string_new_wrapper (""); + Assert (mono != NULL); + return mono; + } +} + +bool MonoSetObjectField(MonoObject* target, const char* fieldname, MonoObject* value) +{ + MonoClass* klass = mono_object_get_class(target); + MonoClassField* field = mono_class_get_field_from_name(klass,fieldname); + if (!field) return false; + mono_field_set_value(target,field,value); + return true; +} + +bool MonoObjectToBool (MonoObject* value) +{ + if (value && mono_type_get_type (mono_class_get_type (mono_object_get_class (value))) == MONO_TYPE_BOOLEAN) + return ExtractMonoObjectData<char> (value); + else + return false; +} + +int MonoObjectToInt (MonoObject* value) +{ + if (value && mono_type_get_type (mono_class_get_type (mono_object_get_class (value))) == MONO_TYPE_I4) + return ExtractMonoObjectData<int> (value); + else + return -1; +} + +MonoAssembly* mono_load_assembly_from_any_monopath(const char* assemblyname) +{ + MonoDomain* domain = mono_domain_get(); + std::vector<string>& monoPaths = MonoPathContainer::GetMonoPaths(); + for (int i=0; i!=monoPaths.size(); i++) + { + MonoAssembly* ass = mono_domain_assembly_open(domain, AppendPathName(monoPaths[i],assemblyname).c_str()); + if (ass) return ass; + } + return NULL; +} + +MonoMethod* mono_unity_find_method(const char* assemblyname, const char* ns, const char* klass, const char* methodname) +{ +//todo: be less stupid about always trying to load an assembly that will be already loaded 99% of the time + MonoAssembly* ass = mono_load_assembly_from_any_monopath(assemblyname); + if (!ass) return NULL; + MonoImage* img = mono_assembly_get_image(ass); + if (!img) return NULL; + MonoMethod* method = FindStaticMonoMethod(img,klass,ns,methodname); + if (!method) return NULL; + return method; +} + +MonoMethod* mono_reflection_method_get_method (MonoObject* ass) +{ + return ExtractMonoObjectData<MonoMethod*>(ass); +} + +MonoString* MonoStringFormat (const char* format, ...) +{ + using namespace std; + va_list vl; + va_start( vl, format ); + char buffer[1024 * 5]; + vsnprintf (buffer, 1024 * 5, format, vl); + va_end (vl); + return mono_string_new_wrapper(buffer); +} + +void StringMonoArrayToVector (MonoArray* arr, std::vector<UnityStr>& container) +{ + container.resize(mono_array_length_safe(arr)); + for (int i=0;i<container.size();i++) + { + container[i] = MonoStringToCpp(GetMonoArrayElement<MonoString*> (arr, i)); + } +} + +void StringMonoArrayToVector (MonoArray* arr, std::vector<std::string>& container) +{ + container.resize(mono_array_length_safe(arr)); + for (int i=0;i<container.size();i++) + { + container[i] = MonoStringToCpp(GetMonoArrayElement<MonoString*> (arr, i)); + } +} + + +void SetReferenceDataOnScriptingWrapper(MonoObject* wrapper, const UnityEngineObjectMemoryLayout& data) +{ + UnityEngineObjectMemoryLayout* wrapperdata = reinterpret_cast<UnityEngineObjectMemoryLayout*> (((char*)wrapper) + kMonoObjectOffset); + memcpy(wrapperdata,&data,sizeof(UnityEngineObjectMemoryLayout)); +} + +MonoObject* mono_class_get_object (MonoClass* klass) +{ + if (klass == NULL) + return NULL; + + MonoType* type = mono_class_get_type(klass); + if (type) + return mono_type_get_object (mono_domain_get(), type); + else + return NULL; +} + +MonoClass* mono_type_get_class_or_element_class (MonoType* type) +{ +#if MONO_2_12 + MonoClass* klass = mono_class_from_mono_type (type); + if (mono_class_get_rank (klass) > 0) + { + klass = mono_class_get_element_class (klass); + } + + return klass; +#else + return mono_type_get_class (type); +#endif +} + +int mono_array_length_safe_wrapper(MonoArray* array) +{ + return mono_array_length_safe(array); +} + +#if MONO_QUALITY_ERRORS +MonoString* UnassignedReferenceString (MonoObject* instance, int classID, MonoClassField* field, int instanceID) +{ + MonoClass* klass = NULL; + if (instance == NULL) + return NULL; + // Transfer sometimes provides us with non-object derived instances so we simply ignore those + klass = mono_object_get_class(instance); + if (!mono_class_is_subclass_of(klass, GetMonoManager().GetCommonClasses().unityEngineObject, false)) + return NULL; + + const char* fieldName = mono_field_get_name(field); + const char* klassName = mono_class_get_name(mono_object_get_class(instance)); + + if (instanceID == 0) + { + return MonoStringFormat( + "UnassignedReferenceException:The variable %s of '%s' has not been assigned.\n" + "You probably need to assign the %s variable of the %s script in the inspector.", + fieldName, klassName, fieldName, klassName); + } + else + { + return MonoStringFormat( + "MissingReferenceException:The variable %s of '%s' doesn't exist anymore.\n" + "You probably need to reassign the %s variable of the '%s' script in the inspector.", + fieldName, klassName, fieldName, klassName); + } +} +#endif + +MonoClassField* GetMonoArrayFieldFromList (int type, MonoType* monoType, MonoClassField* field) +{ + if (type != MONO_TYPE_GENERICINST) + return NULL; + + MonoClass* elementClass = mono_class_from_mono_type(monoType); + + // Check that we have a Generic List class + const char* className = mono_class_get_name(elementClass); + if (strcmp(className, "List`1") != 0 || mono_class_get_image(elementClass) != mono_get_corlib()) + return NULL; + + MonoClassField *arrayField; + void* iter_list = NULL; + + // List<> first element is something called Default Capacity + // Second is the actual array + // But, Mono 2.12 reordered the fields +#if !MONO_2_12 + mono_class_get_fields (elementClass, &iter_list); +#endif + arrayField = mono_class_get_fields (elementClass, &iter_list); + +#if !UNITY_RELEASE + AssertIf(strcmp(mono_field_get_name(arrayField), "_items") != 0); + AssertIf(mono_field_get_offset(arrayField) != kMonoObjectOffset); + + MonoClassField* sizeField = mono_class_get_fields (elementClass, &iter_list); + AssertIf(strcmp(mono_field_get_name(sizeField), "_size") != 0); + AssertIf(mono_field_get_offset(sizeField) != kMonoObjectOffset + sizeof(intptr_t)); +#endif + + return arrayField; +} + +static int currentDomainId = 0; + +int MonoDomainGetUniqueId() +{ + return currentDomainId; +} + +void MonoDomainIncrementUniqueId() +{ + currentDomainId++; +} diff --git a/Runtime/Mono/MonoUtility.h b/Runtime/Mono/MonoUtility.h new file mode 100644 index 0000000..147d1ec --- /dev/null +++ b/Runtime/Mono/MonoUtility.h @@ -0,0 +1,556 @@ +#ifndef MONOUTILITY_H +#define MONOUTILITY_H + +#if !defined(SCRIPTINGUTILITY_H) +#error "Don't include MonoUtility.h, include ScriptingUtility.h instead" +#endif + +#include "Configuration/UnityConfigure.h" +#include "Runtime/BaseClasses/BaseObject.h" +#include "Runtime/Mono/MonoManager.h" +#include "Runtime/Profiler/Profiler.h" +#include "Runtime/Scripting/Backend/ScriptingTypes.h" +#include "Runtime/Utilities/dynamic_array.h" +#include "Runtime/Scripting/Backend/ScriptingBackendApi.h" +#include "Runtime/Scripting/Scripting.h" +#include "Runtime/Scripting/ScriptingManager.h" + +//Unity messes around deep in mono internals, bypassing mono's API to do certain things. One of those things is we have c++ +//structs that we assume are identical to c# structs, and when a mono method calls into the unity runtime, we make the assumption +//that we can access the data stored in the c# struct directly. When a MonoObject* gets passed to us by mono, and we know that the +//object it represents is a struct, we take the MonoObject*, add 8 bytes, and assume the data of the c# struct is stored there in memory. +//When you update to a newer version of mono, and get weird crashes, the assumptions that these offsets make are a good one to verify. +enum { kMonoObjectOffset = sizeof(void*) * 2 }; +enum { kMonoArrayOffset = sizeof(void*) * 4 }; + +#if UNITY_EDITOR +bool IsStackLargeEnough (); +void AssertStackLargeEnough (); +#undef SCRIPTINGAPI_STACK_CHECK +#define SCRIPTINGAPI_STACK_CHECK(NAME) AssertStackLargeEnough(); +#endif + +// TODO: move +struct UnityEngineObjectMemoryLayout +{ + int instanceID; + void* cachedPtr; + +#if MONO_QUALITY_ERRORS + MonoString* error; +#endif +}; + +struct MonoObject; +struct MonoClass; +struct MonoException; +struct MonoArray; +class TrackedReferenceBase; + + +#if UNITY_WII +#define SCRIPTINGAPI_DEFINE_REF_ARG(t, n) MonoObject* _ ## n ## __mObject +#define SCRIPTINGAPI_FIX_REF_ARG(t, n) t n; n.object = _ ## n ## __mObject; +#else +#define SCRIPTINGAPI_DEFINE_REF_ARG(t, n) t n +#define SCRIPTINGAPI_FIX_REF_ARG(t, n) +#endif + +/* + Every MonoObject* that wraps an Object class contains the Reference::Data struct. + It contains the instanceID and a cachedPtr. + Only DEPLOY_OPTIMIZED mode (Player in release build) uses the cached ptr + + Normally we play safe and always dereference the pptr and also explicitly check + for null so we catch null ptr's before dereferencing. + + Referenced objects can of course be destroyed, resulting in stable cachedPtr's + Why does this work? + + If the script code expects that a reference might become null it needs to check against null. + if (transform == null) + If the script code does not check and the referenced is deleted, + then a null exception will be thrown in the editor. + In the DEPLOY_OPTIZMIZED mode no exception will be thrown but the program will just crash. + This means that a game has to ship with no null exception thrown in the editor or debug player. +*/ + +template<class T> +inline T* ExtractMonoObjectDataPtr (MonoObject* object) +{ + return reinterpret_cast<T*> (reinterpret_cast<char*> (object) + kMonoObjectOffset); +} + +template<class T> +inline T& ExtractMonoObjectData (MonoObject* object) +{ + return *reinterpret_cast<T*> (reinterpret_cast<char*> (object) + kMonoObjectOffset); +} + + +inline ScriptingObjectPtr ScriptingInstantiateObject(ScriptingClassPtr klass) +{ +#if UNITY_EDITOR + if (mono_unity_class_is_abstract (klass)) { + // Cannot instantiate abstract class + return SCRIPTING_NULL; + } +#endif + return mono_object_new(mono_domain_get(),klass); +} + +template<class T> inline +void MarshallManagedStructIntoNative(ScriptingObjectPtr so, T* dest) +{ + *dest = ExtractMonoObjectData<T>(so); +} + +template<class T> inline +void MarshallNativeStructIntoManaged(const T& src, ScriptingObjectPtr dest) +{ + ExtractMonoObjectData<T>(dest) = src; +} + + +// If MonoClass is derived from any UnityEngine class returns its classID +// otherwise returns -1. + +void mono_runtime_object_init_exception (MonoObject *thiss, MonoException** exception); +void mono_runtime_object_init_log_exception (MonoObject *thiss); + +template<class T> +inline T& GetMonoArrayElement (MonoArray* array, int i) +{ + return Scripting::GetScriptingArrayElement<T>(array,i); +} + + +inline void* GetMonoArrayPtr(MonoArray* array) +{ + return (void*) (((char*) array) + kMonoArrayOffset); +} + +template<class T> +inline T* GetMono2DArrayData (MonoArray* array) +{ + char* raw = kMonoArrayOffset + sizeof(guint32) + (char*)array; + return (T*)raw; +} + + +template<class T> +inline T* GetMono3DArrayData (MonoArray* array) +{ + char* raw = kMonoArrayOffset + sizeof(guint32)*2 + (char*)array; + return (T*)raw; +} + +int MonoDomainGetUniqueId(); +void MonoDomainIncrementUniqueId(); + +int mono_array_length (MonoArray* array); +int mono_array_length_safe (MonoArray* array); + +std::string MonoStringToCpp (MonoString* monoString); +std::string MonoStringToCppChecked (MonoObject* monoString); + +#if UNITY_WIN || UNITY_XENON +std::wstring MonoStringToWideCpp (MonoString* monoString); +#endif + +MonoString* MonoStringNew (const std::string& in); +MonoString* MonoStringNew (const char* in); +MonoString* MonoStringNewLength (const char* in, int length); +MonoString* MonoStringNewUTF16 (const wchar_t* in); +ScriptingStringPtr MonoStringFormat (const char* format, ...); + +bool ExceptionToLineAndPath (const std::string& exception, int& line, std::string& path); +bool MonoObjectToBool (MonoObject* value); +int MonoObjectToInt (MonoObject* value); +MonoMethod* mono_reflection_method_get_method (MonoObject* ass); +MonoString* MonoStringFormat (const char* format, ...); +MonoArray *mono_array_new_3d (int size0, int size1, int size2, MonoClass *klass); +MonoArray *mono_array_new_2d (int size0, int size1, MonoClass *klass); +MonoAssembly* mono_load_assembly_from_any_monopath(const char* assemblyname); +MonoMethod* mono_unity_find_method(const char* assemblyname, const char* ns, const char* klass, const char* methodname); +MonoObject* mono_class_get_object (MonoClass* klass); +MonoClass* mono_type_get_class_or_element_class (MonoType* type); +MonoString* UnassignedReferenceString (MonoObject* instance, int classID, MonoClassField* field, int instanceID); + +bool IsUtf16InAsciiRange( gunichar2 const* str, int length ); +bool FastTestAndConvertUtf16ToAscii( char* dest, gunichar2 const* str, int length ); +// str must contain only ascii characters +void FastUtf16ToAscii( char* destination, gunichar2 const* str, int length ); + +// Helper functions to go over a C++ vector<arbitrary struct> & create a MonoArray of it. +// it runs a template function on it in order to do the actual conversion of each object +/* Example usage: +C++RAW +struct MonoTreePrototype { + MonoObject *prefab; +}; + +void TreePrototypeToMono (TreePrototype &src, MonoTreePrototype &dest) { + dest.prefab = ObjectToScriptingObject (src.prefab); +} + +void TreePrototypeToCpp (MonoTreePrototype &src, TreePrototype &dest) { + dest.prefab = MonoObjectToObject<GameObject> (src.prefab); +} + + +CUSTOM_PROP TreePrototype[] treePrototypes + { return VectorToMonoStructArray<TreePrototype, MonoTreePrototype> (self->GetTreePrototypes(), MONO_COMMON.treePrototype, TreePrototypeToMono); } + { MonoStructArrayToVector<TreePrototype, MonoTreePrototype> (value, self->GetTreePrototypes(), TreePrototypeToCpp); } +*/ + +template<class T, typename Alloc> +void MonoObjectArrayToSet (MonoArray *array, std::set<T*, std::less<T*>, Alloc >& dest) { + Scripting::RaiseIfNull (array); + int len = mono_array_length_safe(array); + dest.clear(); + for (int i = 0; i < len;i++) + { + int instanceID = Scripting::GetInstanceIDFromScriptingWrapper(GetMonoArrayElement<MonoObject*>(array, i)); + T* objectPtr = dynamic_instanceID_cast<T*> (instanceID); + if (objectPtr) + dest.insert(objectPtr); + } +} + +template<class T> +void MonoArrayToSet(MonoArray* array, std::set<T>& dest) +{ + Scripting::RaiseIfNull (array); + int len = mono_array_length_safe(array); + dest.clear(); + for (int i = 0; i < len;i++) + { + dest.insert(GetMonoArrayElement<T>(array, i)); + } +} + +template<typename T> +MonoArray* SetToMonoArray(const std::set<T>& src, MonoClass* klass) +{ + MonoArray* array = mono_array_new (mono_domain_get (), klass, src.size()); + + typename std::set<T>::const_iterator j = src.begin(); + for (int i=0;i<src.size();i++, j++) + { + Scripting::SetScriptingArrayElement(array, i, *j); + } + + return array; +} + +template<class T> +void MonoArrayToVector(MonoArray* array, std::vector<T>& dest) +{ + Scripting::RaiseIfNull (array); + int len = mono_array_length_safe(array); + dest.resize (len); + for (int i = 0; i < len; i++) + dest[i] = GetMonoArrayElement<T>(array, i); +} + + +template<class T, class T2, class U, class TConverter> +MonoArray *VectorToMonoStructArray (const U &source, MonoClass *klass, TConverter converter) { + MonoArray *arr = mono_array_new (mono_domain_get (), klass, source.size()); + for (int i = 0; i < source.size();i++) + converter (source[i], GetMonoArrayElement<T2> (arr, i)); + return arr; +} + +template<class T, class T2> +void MonoStructArrayToVector (MonoArray *source, std::vector<T> &dest, void (*converter) (T2 &source, T &dest)) { + Scripting::RaiseIfNull (source); + int len = mono_array_length(source); + dest.resize (len); + for (int i = 0; i < len;i++) + converter (GetMonoArrayElement<T2> (source, i), dest[i]); +} + +template<class T, class T2> +std::vector<T> MonoStructArrayToVector (MonoArray *source, void (*converter) (T2 &source, T &dest)) { + std::vector<T> dest; + MonoStructArrayToVector<T, T2> (source, dest, converter); + return dest; +} + +template<class T, class T2, class U> +MonoArray *VectorToMonoClassArray (const U &source, MonoClass *klass, void (*converter) (const T &source, T2 &dest)) { + MonoArray *arr = mono_array_new (mono_domain_get (), klass, source.size()); + for (int i = 0; i < source.size();i++) { + MonoObject *obj = ScriptingInstantiateObject (klass); + GetMonoArrayElement<MonoObject*> (arr,i) = obj; + converter (source[i], ExtractMonoObjectData<T2> (obj)); + } + return arr; +} + +template<class T, class T2> +MonoArray *SetToMonoClassArray (const std::set<T> &source, MonoClass *klass, void (*converter) (const T &source, T2 &dest)) { + MonoArray *arr = mono_array_new (mono_domain_get (), klass, source.size()); + int idx = 0; + for (typename std::set<T>::const_iterator i = source.begin(); i != source.end();i++) { + MonoObject *obj = ScriptingInstantiateObject (klass); + GetMonoArrayElement<MonoObject*> (arr,idx++) = obj; + converter (*i, ExtractMonoObjectData<T2> (obj)); + } + return arr; +} + +template<typename mapType, class T, class T2, class T3> +MonoArray *MapToMonoClassArray (const mapType &source, MonoClass *klass, void (*converter) (const T &first, const T2 &second, T3 &dest)) { + MonoArray *arr = mono_array_new (mono_domain_get (), klass, source.size()); + int idx = 0; + for (typename mapType::const_iterator i = source.begin(); i != source.end();i++) { + MonoObject *obj = ScriptingInstantiateObject (klass); + GetMonoArrayElement<MonoObject*> (arr,idx++) = obj; + converter (i->first, i->second, ExtractMonoObjectData<T3> (obj)); + } + return arr; +} + +template<class T, class T2> +void MonoClassArrayToVector (MonoArray *source, std::vector<T> &dest, void (*converter) (T2 &source, T &dest)) { + Scripting::RaiseIfNull (source); + int len = mono_array_length(source); + dest.resize (len); + for (int i = 0; i < len;i++) { + MonoObject *obj = GetMonoArrayElement<MonoObject*> (source, i); + Scripting::RaiseIfNull (obj); + converter (ExtractMonoObjectData<T2> (obj), dest[i]); + } +} + +template<class T, class T2> +std::vector<T> MonoClassArrayToVector (MonoArray *source, void (*converter) (T2 &source, T &dest)) { + std::vector<T> dest; + MonoClassArrayToVector<T, T2> (source, dest, converter); + return dest; +} + +void StringMonoArrayToVector (MonoArray* arr, std::vector<std::string>& container); +void StringMonoArrayToVector (MonoArray* arr, std::vector<UnityStr>& container); + +inline ScriptingClassPtr GetScriptingTypeOfScriptingObject(ScriptingObjectPtr object) +{ + return mono_object_get_class(object); +} + +inline bool ScriptingClassIsSubclassOf(ScriptingClassPtr c1, ScriptingClassPtr c2) +{ + return mono_class_is_subclass_of(c1,c2,true); +} + +ScriptingClassPtr GetBuiltinScriptingClass(const char* name,bool optional=false); + +inline int GetScriptingArraySize(ScriptingArrayPtr a) +{ + return mono_array_length_safe(a); +} + + +/* + Code for switching between mono_runtime_invoke and mono_aot_get_method + (later mono_method_get_unmanaged_thunk also will be included) + */ + +void* ResolveMonoMethodPointer(MonoDomain *domain, MonoMethod *method); +bool MonoSetObjectField(MonoObject* target, const char* fieldname, MonoObject* value); +#if UNITY_EDITOR +bool IsStackLargeEnough (); +#endif + +// Ensure current thread is attached to mono, +// error and return otherwise +#define MONO_RUNTIME_INVOKE_THREAD_CHECK_WITH_RET(Ret) \ +do{ \ + if (!mono_thread_current ()) { \ + ErrorStringWithoutStacktrace ("Thread is not attached to scripting runtime"); \ + return Ret; \ + } \ +} while (0) + +#define MONO_RUNTIME_INVOKE_THREAD_CHECK MONO_RUNTIME_INVOKE_THREAD_CHECK_WITH_RET(NULL) + +// Ensure we have enough stack for a reasonable invocation +#if UNITY_EDITOR +#define MONO_RUNTIME_INVOKE_STACK_CHECK_WITH_RET(Ret) \ +do { \ + if (!IsStackLargeEnough ()) { \ + *exc = mono_exception_from_name_msg (mono_get_corlib (), "System", "StackOverflowException", ""); \ + return Ret; \ + } \ +} while (0) + +#define MONO_RUNTIME_INVOKE_STACK_CHECK MONO_RUNTIME_INVOKE_STACK_CHECK_WITH_RET(NULL) +#else +#define MONO_RUNTIME_INVOKE_STACK_CHECK_WITH_RET(Ret) do {} while (0) +#define MONO_RUNTIME_INVOKE_STACK_CHECK do {} while (0) +#endif + +inline MonoObject* mono_runtime_invoke_profiled_fast (ScriptingMethodPtr method, MonoObject* obj, MonoException** exc, MonoClass* profileClassForCoroutine) +{ + MONO_RUNTIME_INVOKE_THREAD_CHECK; + MONO_RUNTIME_INVOKE_STACK_CHECK; + + MONO_PROFILER_BEGIN(method,profileClassForCoroutine, obj); + + MonoObject* ret; +#if USE_MONO_AOT + if (method->fastMonoMethod) + ret = method->fastMonoMethod(obj, exc); + else +#endif + ret = mono_runtime_invoke(method->monoMethod, obj, NULL, exc); + + MONO_PROFILER_END; + + return ret; +} + + +/// returns false if an exception was thrown +inline bool mono_runtime_invoke_profiled_fast_bool (MonoMethod* method, FastMonoMethod fastMethod, MonoObject* obj, MonoException** exc, MonoClass* profileClassForCoroutine) +{ + Assert(USE_MONO_AOT || fastMethod == NULL ); + Assert(exc != NULL); + + MONO_RUNTIME_INVOKE_THREAD_CHECK_WITH_RET(false); + MONO_RUNTIME_INVOKE_STACK_CHECK_WITH_RET(false); + + MONO_PROFILER_BEGIN(GetScriptingMethodRegistry().GetMethod(method), profileClassForCoroutine, obj); + + bool result = false; +#if USE_MONO_AOT + if (fastMethod) + result = fastMethod(obj, exc); + else +#endif + { + MonoObject* _tmpres = mono_runtime_invoke(method, obj, NULL, exc); + if (_tmpres != NULL && *exc == NULL) + result = ExtractMonoObjectData<char>(_tmpres); + } + + MONO_PROFILER_END; + + return result; +} + +/// Never call mono_runtime_invoke directly, otherwise the profiler will not be able to pick it up! +inline MonoObject* mono_runtime_invoke_profiled (MonoMethod *method, MonoObject *obj, void **params, MonoException **exc, MonoClass* classContextForProfiler=NULL) +{ + MONO_RUNTIME_INVOKE_THREAD_CHECK; + MONO_RUNTIME_INVOKE_STACK_CHECK; + + MONO_PROFILER_BEGIN (GetScriptingMethodRegistry().GetMethod(method), classContextForProfiler, obj); + MonoObject* ret = mono_runtime_invoke(method, obj, params, exc); + MONO_PROFILER_END; + + return ret; +} + +template<class T> +ScriptingObjectPtr CreateScriptingObjectFromNativeStruct(ScriptingClassPtr klass, T& thestruct) +{ + ScriptingObjectPtr mono = ScriptingInstantiateObject (klass); + T& destStruct = ExtractMonoObjectData<T> (mono); + destStruct = thestruct; + return mono; +} + +inline +char* ScriptingStringToAllocatedChars(const ICallString& str) +{ + return mono_string_to_utf8(str.str); +} + +MonoClassField* GetMonoArrayFieldFromList (int type, MonoType* monoType, MonoClassField* field); + +int EXPORT_COREMODULE mono_array_length_safe_wrapper(MonoArray* array); + +inline void ScriptingStringToAllocatedChars_Free(const char* str) +{ + g_free((void*)str); +} + +template<class T> +struct ScriptingObjectOfType; + +template<class T> +inline T* ScriptingObjectToObject(ScriptingObjectPtr so) +{ + ScriptingObjectOfType<T> ref(so); + return ref.GetPtr(); +} + +MonoClassField* GetMonoArrayFieldFromList (int type, MonoType* monoType, MonoClassField* field); + +/// ToDo: remove these (MonoObjectArrayToVector, MonoObjectArrayToPPtrVector) later, or change it to more unified version +/// vector<GameObject*> gos; +/// gos = MonoObjectArrayToVector(MonoArray*); +template<class T> +void MonoObjectArrayToVector (MonoArray *source, std::vector<T*>& dest) { + Scripting::RaiseIfNull (source); + int len = mono_array_length(source); + dest.resize (len); + for (int i = 0; i < len;i++) + dest[i] = ScriptingObjectToObject<T> (GetMonoArrayElement<MonoObject*> (source, i)); +} + +/// vector<GameObject*> gos; +/// gos = MonoObjectArrayToVector(MonoArray*); +template<class T> +void MonoObjectArrayToPPtrVector (MonoArray *source, std::vector<PPtr<T> >& dest) { + Scripting::RaiseIfNull (source); + int len = mono_array_length(source); + dest.resize (len); + for (int i = 0; i < len;i++) + dest[i] = ScriptingObjectToObject<T> (GetMonoArrayElement<MonoObject*> (source, i)); +} + +template<class T> +inline ScriptingObjectPtr ScriptingGetObjectReference(PPtr<T> object) +{ + return ObjectToScriptingObject(object); +} + +template<class T> +inline ScriptingObjectPtr ScriptingGetObjectReference(T* object) +{ + return Scripting::ScriptingWrapperFor(object); +} + +#define CreateScriptingParams(VariableName, ParamCount) ScriptingParams VariableName[ParamCount]; +// Don't use if Value is ScriptingObjectPtr!!! +#define SetScriptingParam(VariableName, Index, Value) VariableName[Index] = &Value; +#define GetSafeString(ClassName, Getter) Getter + +std::string ErrorMessageForUnsupportedEnumField(MonoType* enumType, MonoType* classType, const char * fieldName); + +MonoObject* MonoObjectNULL (int classID, MonoString* error); + +namespace Unity { class GameObject; class Component;} +class Object; + +#if MONO_QUALITY_ERRORS +// Wrap null ptr with pseudo null object +ScriptingObjectPtr MonoObjectNULL (ScriptingClassPtr klass, ScriptingStringPtr error); +inline ScriptingObjectPtr MonoObjectNULL (ScriptingClassPtr klass){ return MonoObjectNULL (klass, SCRIPTING_NULL); } + +// Component missing string error +MonoString* MissingComponentString (Unity::GameObject& go, int classID); +MonoString* MissingComponentString (Unity::GameObject& go, ScriptingTypePtr klass); + +#else + +inline ScriptingObjectPtr MonoObjectNULL (ScriptingClassPtr klass) { return SCRIPTING_NULL; } + +#endif + +#endif//ENABLE_MONO diff --git a/Runtime/Mono/tabledefs.h b/Runtime/Mono/tabledefs.h new file mode 100644 index 0000000..cfe9543 --- /dev/null +++ b/Runtime/Mono/tabledefs.h @@ -0,0 +1,231 @@ +/* + * tabledefs.h: This file contains the various definitions for constants + * found on the metadata tables + * + * Author: + * Miguel de Icaza (miguel@ximian.com) + * + * (C) 2001 Ximian, Inc. + * + * From the ECMA documentation + */ + +#ifndef _MONO_METADATA_TABLEDEFS_H_ +#define _MONO_METADATA_TABLEDEFS_H_ + +/* + * 22.1.1 Values for AssemblyHashAlgorithm + */ + +enum { + ASSEMBLY_HASH_NONE, + ASSEMBLY_HASH_MD5 = 0x8003, + ASSEMBLY_HASH_SHA1 = 0x8004 +}; + +/* + * 22.1.4 Flags for Event.EventAttributes + */ + +enum { + EVENT_SPECIALNAME = 0x0200, + EVENT_RTSPECIALNAME = 0x0400 +}; + +/* + * 22.1.6 Flags for FileAttributes + */ + +enum { + FILE_CONTAINS_METADATA = 0, + FILE_CONTAINS_NO_METADATA = 1 +}; + +enum { + SECURITY_ACTION_DEMAND = 2, + SECURITY_ACTION_ASSERT = 3, + SECURITY_ACTION_DENY = 4, + SECURITY_ACTION_PERMITONLY = 5, + SECURITY_ACTION_LINKDEMAND = 6, + SECURITY_ACTION_INHERITDEMAND = 7, + SECURITY_ACTION_REQMIN = 8, + SECURITY_ACTION_REQOPT = 9, + SECURITY_ACTION_REQREFUSE = 10 +}; + +/* + * Field Attributes (21.1.5). + */ + +#define FIELD_ATTRIBUTE_FIELD_ACCESS_MASK 0x0007 +#define FIELD_ATTRIBUTE_COMPILER_CONTROLLED 0x0000 +#define FIELD_ATTRIBUTE_PRIVATE 0x0001 +#define FIELD_ATTRIBUTE_FAM_AND_ASSEM 0x0002 +#define FIELD_ATTRIBUTE_ASSEMBLY 0x0003 +#define FIELD_ATTRIBUTE_FAMILY 0x0004 +#define FIELD_ATTRIBUTE_FAM_OR_ASSEM 0x0005 +#define FIELD_ATTRIBUTE_PUBLIC 0x0006 + +#define FIELD_ATTRIBUTE_STATIC 0x0010 +#define FIELD_ATTRIBUTE_INIT_ONLY 0x0020 +#define FIELD_ATTRIBUTE_LITERAL 0x0040 +#define FIELD_ATTRIBUTE_NOT_SERIALIZED 0x0080 +#define FIELD_ATTRIBUTE_SPECIAL_NAME 0x0200 +#define FIELD_ATTRIBUTE_PINVOKE_IMPL 0x2000 + +/* For runtime use only */ +#define FIELD_ATTRIBUTE_RESERVED_MASK 0x9500 +#define FIELD_ATTRIBUTE_RT_SPECIAL_NAME 0x0400 +#define FIELD_ATTRIBUTE_HAS_FIELD_MARSHAL 0x1000 +#define FIELD_ATTRIBUTE_HAS_DEFAULT 0x8000 +#define FIELD_ATTRIBUTE_HAS_FIELD_RVA 0x0100 + +/* + * Type Attributes (21.1.13). + */ +#define TYPE_ATTRIBUTE_VISIBILITY_MASK 0x00000007 +#define TYPE_ATTRIBUTE_NOT_PUBLIC 0x00000000 +#define TYPE_ATTRIBUTE_PUBLIC 0x00000001 +#define TYPE_ATTRIBUTE_NESTED_PUBLIC 0x00000002 +#define TYPE_ATTRIBUTE_NESTED_PRIVATE 0x00000003 +#define TYPE_ATTRIBUTE_NESTED_FAMILY 0x00000004 +#define TYPE_ATTRIBUTE_NESTED_ASSEMBLY 0x00000005 +#define TYPE_ATTRIBUTE_NESTED_FAM_AND_ASSEM 0x00000006 +#define TYPE_ATTRIBUTE_NESTED_FAM_OR_ASSEM 0x00000007 + +#define TYPE_ATTRIBUTE_LAYOUT_MASK 0x00000018 +#define TYPE_ATTRIBUTE_AUTO_LAYOUT 0x00000000 +#define TYPE_ATTRIBUTE_SEQUENTIAL_LAYOUT 0x00000008 +#define TYPE_ATTRIBUTE_EXPLICIT_LAYOUT 0x00000010 + +#define TYPE_ATTRIBUTE_CLASS_SEMANTIC_MASK 0x00000020 +#define TYPE_ATTRIBUTE_CLASS 0x00000000 +#define TYPE_ATTRIBUTE_INTERFACE 0x00000020 + +#define TYPE_ATTRIBUTE_ABSTRACT 0x00000080 +#define TYPE_ATTRIBUTE_SEALED 0x00000100 +#define TYPE_ATTRIBUTE_SPECIAL_NAME 0x00000400 + +#define TYPE_ATTRIBUTE_IMPORT 0x00001000 +#define TYPE_ATTRIBUTE_SERIALIZABLE 0x00002000 + +#define TYPE_ATTRIBUTE_STRING_FORMAT_MASK 0x00030000 +#define TYPE_ATTRIBUTE_ANSI_CLASS 0x00000000 +#define TYPE_ATTRIBUTE_UNICODE_CLASS 0x00010000 +#define TYPE_ATTRIBUTE_AUTO_CLASS 0x00020000 + +#define TYPE_ATTRIBUTE_BEFORE_FIELD_INIT 0x00100000 + +#define TYPE_ATTRIBUTE_RESERVED_MASK 0x00040800 +#define TYPE_ATTRIBUTE_RT_SPECIAL_NAME 0x00000800 +#define TYPE_ATTRIBUTE_HAS_SECURITY 0x00040000 + +/* + * Method Attributes (22.1.9) + */ + +#define METHOD_IMPL_ATTRIBUTE_CODE_TYPE_MASK 0x0003 +#define METHOD_IMPL_ATTRIBUTE_IL 0x0000 +#define METHOD_IMPL_ATTRIBUTE_NATIVE 0x0001 +#define METHOD_IMPL_ATTRIBUTE_OPTIL 0x0002 +#define METHOD_IMPL_ATTRIBUTE_RUNTIME 0x0003 + +#define METHOD_IMPL_ATTRIBUTE_MANAGED_MASK 0x0004 +#define METHOD_IMPL_ATTRIBUTE_UNMANAGED 0x0004 +#define METHOD_IMPL_ATTRIBUTE_MANAGED 0x0000 + +#define METHOD_IMPL_ATTRIBUTE_FORWARD_REF 0x0010 +#define METHOD_IMPL_ATTRIBUTE_PRESERVE_SIG 0x0080 +#define METHOD_IMPL_ATTRIBUTE_INTERNAL_CALL 0x1000 +#define METHOD_IMPL_ATTRIBUTE_SYNCHRONIZED 0x0020 +#define METHOD_IMPL_ATTRIBUTE_NOINLINING 0x0008 +#define METHOD_IMPL_ATTRIBUTE_MAX_METHOD_IMPL_VAL 0xffff + +#define METHOD_ATTRIBUTE_MEMBER_ACCESS_MASK 0x0007 +#define METHOD_ATTRIBUTE_COMPILER_CONTROLLED 0x0000 +#define METHOD_ATTRIBUTE_PRIVATE 0x0001 +#define METHOD_ATTRIBUTE_FAM_AND_ASSEM 0x0002 +#define METHOD_ATTRIBUTE_ASSEM 0x0003 +#define METHOD_ATTRIBUTE_FAMILY 0x0004 +#define METHOD_ATTRIBUTE_FAM_OR_ASSEM 0x0005 +#define METHOD_ATTRIBUTE_PUBLIC 0x0006 + +#define METHOD_ATTRIBUTE_STATIC 0x0010 +#define METHOD_ATTRIBUTE_FINAL 0x0020 +#define METHOD_ATTRIBUTE_VIRTUAL 0x0040 +#define METHOD_ATTRIBUTE_HIDE_BY_SIG 0x0080 + +#define METHOD_ATTRIBUTE_VTABLE_LAYOUT_MASK 0x0100 +#define METHOD_ATTRIBUTE_REUSE_SLOT 0x0000 +#define METHOD_ATTRIBUTE_NEW_SLOT 0x0100 + +#define METHOD_ATTRIBUTE_ABSTRACT 0x0400 +#define METHOD_ATTRIBUTE_SPECIAL_NAME 0x0800 + +#define METHOD_ATTRIBUTE_PINVOKE_IMPL 0x2000 +#define METHOD_ATTRIBUTE_UNMANAGED_EXPORT 0x0008 + +/* + * For runtime use only + */ +#define METHOD_ATTRIBUTE_RESERVED_MASK 0xd000 +#define METHOD_ATTRIBUTE_RT_SPECIAL_NAME 0x1000 +#define METHOD_ATTRIBUTE_HAS_SECURITY 0x4000 +#define METHOD_ATTRIBUTE_REQUIRE_SEC_OBJECT 0x8000 + + +/* + * Method Semantics ([MethodSemanticAttributes]) 22.1.10 + */ + +#define METHOD_SEMANTIC_SETTER 0x0001 +#define METHOD_SEMANTIC_GETTER 0x0002 +#define METHOD_SEMANTIC_OTHER 0x0004 +#define METHOD_SEMANTIC_ADD_ON 0x0008 +#define METHOD_SEMANTIC_REMOVE_ON 0x0010 +#define METHOD_SEMANTIC_FIRE 0x0020 + +/* + * Flags for Params (22.1.11) + */ +#define PARAM_ATTRIBUTE_IN 0x0001 +#define PARAM_ATTRIBUTE_OUT 0x0002 +#define PARAM_ATTRIBUTE_OPTIONAL 0x0004 +#define PARAM_ATTRIBUTE_RESERVED_MASK 0xf000 +#define PARAM_ATTRIBUTE_HAS_DEFAULT 0x1000 +#define PARAM_ATTRIBUTE_HAS_FIELD_MARSHAL 0x2000 +#define PARAM_ATTRIBUTE_UNUSED 0xcfe0 + +/* + * 22.1.12 PropertyAttributes + */ +#define PROPERTY_ATTRIBUTE_SPECIAL_NAME 0x0200 +#define PROPERTY_ATTRIBUTE_RESERVED_MASK 0xf400 +#define PROPERTY_ATTRIBUTE_RT_SPECIAL_NAME 0x0400 +#define PROPERTY_ATTRIBUTE_HAS_DEFAULT 0x1000 +#define PROPERTY_ATTRIBUTE_UNUSED 0xe9ff + +/* + * 22.1.7 Flags for ImplMap [PInvokeAttributes] + */ +#define PINVOKE_ATTRIBUTE_NO_MANGLE 0x0001 +#define PINVOKE_ATTRIBUTE_CHAR_SET_MASK 0x0006 +#define PINVOKE_ATTRIBUTE_CHAR_SET_NOT_SPEC 0x0000 +#define PINVOKE_ATTRIBUTE_CHAR_SET_ANSI 0x0002 +#define PINVOKE_ATTRIBUTE_CHAR_SET_UNICODE 0x0004 +#define PINVOKE_ATTRIBUTE_CHAR_SET_AUTO 0x0006 +#define PINVOKE_ATTRIBUTE_SUPPORTS_LAST_ERROR 0x0040 +#define PINVOKE_ATTRIBUTE_CALL_CONV_MASK 0x0700 +#define PINVOKE_ATTRIBUTE_CALL_CONV_WINAPI 0x0100 +#define PINVOKE_ATTRIBUTE_CALL_CONV_CDECL 0x0200 +#define PINVOKE_ATTRIBUTE_CALL_CONV_STDCALL 0x0300 +#define PINVOKE_ATTRIBUTE_CALL_CONV_THISCALL 0x0400 +#define PINVOKE_ATTRIBUTE_CALL_CONV_FASTCALL 0x0500 +#define PINVOKE_ATTRIBUTE_CALL_CONV_GENERIC 0x0010 +#define PINVOKE_ATTRIBUTE_CALL_CONV_GENERICINST 0x000a + +/** + * 21.5 AssemblyRefs + */ +#define ASSEMBLYREF_FULL_PUBLIC_KEY_FLAG 0x00000001 +#endif |