BsMonoManager.cpp 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275
  1. #include "BsMonoManager.h"
  2. #include "BsException.h"
  3. #include "BsScriptMeta.h"
  4. #include "BsMonoAssembly.h"
  5. #include "BsMonoClass.h"
  6. #include "BsMonoUtil.h"
  7. #include <mono/metadata/assembly.h>
  8. #include <mono/metadata/mono-config.h>
  9. #include <mono/metadata/mono-gc.h>
  10. #include <mono/metadata/mono-debug.h>
  11. namespace BansheeEngine
  12. {
  13. const String MonoManager::MONO_LIB_DIR = "..\\..\\Mono\\lib";
  14. const String MonoManager::MONO_ETC_DIR = "..\\..\\Mono\\etc";
  15. MonoManager::MonoManager()
  16. :mRootDomain(nullptr), mScriptDomain(nullptr), mIsCoreLoaded(false)
  17. {
  18. mono_set_dirs(MONO_LIB_DIR.c_str(), MONO_ETC_DIR.c_str());
  19. #if BS_DEBUG_MODE
  20. mono_set_signal_chaining(true);
  21. mono_debug_init(MONO_DEBUG_FORMAT_MONO);
  22. #endif
  23. mono_config_parse(nullptr);
  24. mRootDomain = mono_jit_init_version("BansheeMono", "v4.0.30319"); // TODO: Allow user-defined version here?
  25. if (mRootDomain == nullptr)
  26. BS_EXCEPT(InternalErrorException, "Cannot initialize Mono runtime.");
  27. }
  28. MonoManager::~MonoManager()
  29. {
  30. for (auto& entry : mAssemblies)
  31. {
  32. bs_delete(entry.second);
  33. }
  34. mAssemblies.clear();
  35. unloadScriptDomain();
  36. if (mRootDomain != nullptr)
  37. {
  38. mono_jit_cleanup(mRootDomain);
  39. mRootDomain = nullptr;
  40. }
  41. }
  42. MonoAssembly& MonoManager::loadAssembly(const String& path, const String& name)
  43. {
  44. MonoAssembly* assembly = nullptr;
  45. if (mScriptDomain == nullptr)
  46. {
  47. mScriptDomain = mono_domain_create_appdomain(const_cast<char *>(path.c_str()), nullptr);
  48. mono_domain_set(mScriptDomain, false);
  49. if (mScriptDomain == nullptr)
  50. {
  51. BS_EXCEPT(InternalErrorException, "Cannot create script app domain.");
  52. }
  53. }
  54. auto iterFind = mAssemblies.find(name);
  55. if(iterFind != mAssemblies.end())
  56. {
  57. assembly = iterFind->second;
  58. }
  59. else
  60. {
  61. assembly = new (bs_alloc<MonoAssembly>()) MonoAssembly(path, name);
  62. mAssemblies[name] = assembly;
  63. }
  64. initializeAssembly(*assembly);
  65. return *assembly;
  66. }
  67. void MonoManager::unloadAssembly(MonoAssembly& assembly)
  68. {
  69. ::MonoAssembly* monoAssembly = assembly.mMonoAssembly;
  70. assembly.unload();
  71. if(monoAssembly)
  72. mono_assembly_close(monoAssembly);
  73. }
  74. void MonoManager::initializeAssembly(MonoAssembly& assembly)
  75. {
  76. if (!assembly.mIsLoaded)
  77. {
  78. assembly.load(mScriptDomain);
  79. // Fully initialize all types that use this assembly
  80. Vector<ScriptMeta*>& mTypeMetas = getTypesToInitialize()[assembly.mName];
  81. for (auto& meta : mTypeMetas)
  82. {
  83. meta->scriptClass = assembly.getClass(meta->ns, meta->name);
  84. if (meta->scriptClass == nullptr)
  85. BS_EXCEPT(InvalidParametersException, "Unable to find class of type: \"" + meta->ns + "::" + meta->name + "\"");
  86. if (meta->scriptClass->hasField("mCachedPtr"))
  87. meta->thisPtrField = meta->scriptClass->getField("mCachedPtr");
  88. else
  89. meta->thisPtrField = nullptr;
  90. meta->initCallback();
  91. }
  92. }
  93. if (!mIsCoreLoaded)
  94. {
  95. mIsCoreLoaded = true;
  96. MonoAssembly* corlib = nullptr;
  97. auto iterFind = mAssemblies.find("corlib");
  98. if (iterFind == mAssemblies.end())
  99. {
  100. corlib = new (bs_alloc<MonoAssembly>()) MonoAssembly("corlib", "corlib");
  101. mAssemblies["corlib"] = corlib;
  102. }
  103. else
  104. corlib = iterFind->second;
  105. corlib->loadFromImage(mono_get_corlib());
  106. }
  107. }
  108. MonoAssembly* MonoManager::getAssembly(const String& name) const
  109. {
  110. auto iterFind = mAssemblies.find(name);
  111. if(iterFind != mAssemblies.end())
  112. return iterFind->second;
  113. return nullptr;
  114. }
  115. void MonoManager::registerScriptType(ScriptMeta* metaData)
  116. {
  117. Vector<ScriptMeta*>& mMetas = getTypesToInitialize()[metaData->assembly];
  118. mMetas.push_back(metaData);
  119. }
  120. MonoClass* MonoManager::findClass(const String& ns, const String& typeName)
  121. {
  122. MonoClass* monoClass = nullptr;
  123. for(auto& assembly : mAssemblies)
  124. {
  125. monoClass = assembly.second->getClass(ns, typeName);
  126. if(monoClass != nullptr)
  127. return monoClass;
  128. }
  129. return nullptr;
  130. }
  131. MonoClass* MonoManager::findClass(::MonoClass* rawMonoClass)
  132. {
  133. MonoClass* monoClass = nullptr;
  134. for(auto& assembly : mAssemblies)
  135. {
  136. monoClass = assembly.second->getClass(rawMonoClass);
  137. if(monoClass != nullptr)
  138. return monoClass;
  139. }
  140. return nullptr;
  141. }
  142. String MonoManager::getFullTypeName(MonoObject* obj)
  143. {
  144. if(obj == nullptr)
  145. BS_EXCEPT(InvalidParametersException, "Object cannot be null.");
  146. ::MonoClass* monoClass = mono_object_get_class(obj);
  147. const char* nameSpaceChars = mono_class_get_namespace(monoClass);
  148. String namespaceStr;
  149. if(nameSpaceChars != nullptr)
  150. namespaceStr = nameSpaceChars;
  151. const char* typeNameChars = mono_class_get_name(monoClass);
  152. String typeNameStr;
  153. if(typeNameChars != nullptr)
  154. typeNameStr = typeNameChars;
  155. return namespaceStr + "." + typeNameStr;
  156. }
  157. String MonoManager::getNamespace(MonoObject* obj)
  158. {
  159. if(obj == nullptr)
  160. BS_EXCEPT(InvalidParametersException, "Object cannot be null.");
  161. ::MonoClass* monoClass = mono_object_get_class(obj);
  162. const char* nameSpaceChars = mono_class_get_namespace(monoClass);
  163. String namespaceStr;
  164. if(nameSpaceChars != nullptr)
  165. namespaceStr = nameSpaceChars;
  166. return namespaceStr;
  167. }
  168. String MonoManager::getTypeName(MonoObject* obj)
  169. {
  170. if(obj == nullptr)
  171. BS_EXCEPT(InvalidParametersException, "Object cannot be null.");
  172. ::MonoClass* monoClass = mono_object_get_class(obj);
  173. const char* typeNameChars = mono_class_get_name(monoClass);
  174. String typeNameStr;
  175. if(typeNameChars != nullptr)
  176. typeNameStr = typeNameChars;
  177. return typeNameStr;
  178. }
  179. void MonoManager::unloadScriptDomain()
  180. {
  181. if (mScriptDomain != nullptr)
  182. {
  183. mono_domain_set(mono_get_root_domain(), false);
  184. mono_domain_finalize(mScriptDomain, 2000);
  185. MonoObject* exception = nullptr;
  186. mono_domain_try_unload(mScriptDomain, &exception);
  187. if (exception != nullptr)
  188. MonoUtil::throwIfException(exception);
  189. mono_gc_collect(mono_gc_max_generation());
  190. mScriptDomain = nullptr;
  191. }
  192. for (auto& assemblyEntry : mAssemblies)
  193. assemblyEntry.second->unload();
  194. mIsCoreLoaded = true;
  195. }
  196. void MonoManager::loadScriptDomain()
  197. {
  198. if (mScriptDomain != nullptr)
  199. unloadScriptDomain();
  200. if (mScriptDomain == nullptr)
  201. {
  202. mScriptDomain = mono_domain_create_appdomain("ScriptDomain", nullptr);
  203. mono_domain_set(mScriptDomain, false);
  204. if (mScriptDomain == nullptr)
  205. {
  206. BS_EXCEPT(InternalErrorException, "Cannot create script app domain.");
  207. }
  208. }
  209. if (mScriptDomain != nullptr)
  210. {
  211. for (auto& assemblyEntry : mAssemblies)
  212. initializeAssembly(*assemblyEntry.second);
  213. }
  214. onDomainReload();
  215. }
  216. }