From 15740faf9fe9fe4be08965098bbf2947e096aeeb Mon Sep 17 00:00:00 2001 From: chai Date: Wed, 14 Aug 2019 22:50:43 +0800 Subject: +Unity Runtime code --- Runtime/Mono/MonoScriptCache.cpp | 556 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 556 insertions(+) create mode 100644 Runtime/Mono/MonoScriptCache.cpp (limited to 'Runtime/Mono/MonoScriptCache.cpp') 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 (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 foundMethods; + GetScriptingMethodRegistry().AllMethodsIn(klass, foundMethods, ScriptingMethodRegistry::kInstanceOnly); + + for (std::vector::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;imonoMethod)); + string postfix = "The function will be ignored."; + string message; + for (int i=0;isecond; + 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;imonoMethod)); +#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;imonoMethod, 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 (this); + if (cache->refCount.Release()) + { + UNITY_DELETE(cache, kMemScriptManager); + } +} + +void MonoScriptCache::Retain () const +{ + const_cast (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 -- cgit v1.1-26-g67d0