gd_mono.cpp 32 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114
  1. /*************************************************************************/
  2. /* gd_mono.cpp */
  3. /*************************************************************************/
  4. /* This file is part of: */
  5. /* GODOT ENGINE */
  6. /* https://godotengine.org */
  7. /*************************************************************************/
  8. /* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */
  9. /* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */
  10. /* */
  11. /* Permission is hereby granted, free of charge, to any person obtaining */
  12. /* a copy of this software and associated documentation files (the */
  13. /* "Software"), to deal in the Software without restriction, including */
  14. /* without limitation the rights to use, copy, modify, merge, publish, */
  15. /* distribute, sublicense, and/or sell copies of the Software, and to */
  16. /* permit persons to whom the Software is furnished to do so, subject to */
  17. /* the following conditions: */
  18. /* */
  19. /* The above copyright notice and this permission notice shall be */
  20. /* included in all copies or substantial portions of the Software. */
  21. /* */
  22. /* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
  23. /* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
  24. /* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
  25. /* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
  26. /* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
  27. /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
  28. /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
  29. /*************************************************************************/
  30. #include "gd_mono.h"
  31. #include <mono/metadata/exception.h>
  32. #include <mono/metadata/mono-config.h>
  33. #include <mono/metadata/mono-debug.h>
  34. #include <mono/metadata/mono-gc.h>
  35. #include "core/os/dir_access.h"
  36. #include "core/os/file_access.h"
  37. #include "core/os/os.h"
  38. #include "core/os/thread.h"
  39. #include "core/project_settings.h"
  40. #include "../csharp_script.h"
  41. #include "../glue/cs_glue_version.gen.h"
  42. #include "../godotsharp_dirs.h"
  43. #include "../utils/path_utils.h"
  44. #include "gd_mono_class.h"
  45. #include "gd_mono_marshal.h"
  46. #include "gd_mono_utils.h"
  47. #ifdef TOOLS_ENABLED
  48. #include "../editor/godotsharp_editor.h"
  49. #include "main/main.h"
  50. #endif
  51. GDMono *GDMono::singleton = NULL;
  52. namespace {
  53. void setup_runtime_main_args() {
  54. CharString execpath = OS::get_singleton()->get_executable_path().utf8();
  55. List<String> cmdline_args = OS::get_singleton()->get_cmdline_args();
  56. List<CharString> cmdline_args_utf8;
  57. Vector<char *> main_args;
  58. main_args.resize(cmdline_args.size() + 1);
  59. main_args.write[0] = execpath.ptrw();
  60. int i = 1;
  61. for (List<String>::Element *E = cmdline_args.front(); E; E = E->next()) {
  62. CharString &stored = cmdline_args_utf8.push_back(E->get().utf8())->get();
  63. main_args.write[i] = stored.ptrw();
  64. i++;
  65. }
  66. mono_runtime_set_main_args(main_args.size(), main_args.ptrw());
  67. }
  68. #ifdef DEBUG_ENABLED
  69. static bool _wait_for_debugger_msecs(uint32_t p_msecs) {
  70. do {
  71. if (mono_is_debugger_attached())
  72. return true;
  73. int last_tick = OS::get_singleton()->get_ticks_msec();
  74. OS::get_singleton()->delay_usec((p_msecs < 25 ? p_msecs : 25) * 1000);
  75. int tdiff = OS::get_singleton()->get_ticks_msec() - last_tick;
  76. if (tdiff > p_msecs) {
  77. p_msecs = 0;
  78. } else {
  79. p_msecs -= tdiff;
  80. }
  81. } while (p_msecs > 0);
  82. return mono_is_debugger_attached();
  83. }
  84. void gdmono_debug_init() {
  85. mono_debug_init(MONO_DEBUG_FORMAT_MONO);
  86. int da_port = GLOBAL_DEF("mono/debugger_agent/port", 23685);
  87. bool da_suspend = GLOBAL_DEF("mono/debugger_agent/wait_for_debugger", false);
  88. int da_timeout = GLOBAL_DEF("mono/debugger_agent/wait_timeout", 3000);
  89. #ifdef TOOLS_ENABLED
  90. if (Engine::get_singleton()->is_editor_hint() ||
  91. ProjectSettings::get_singleton()->get_resource_path().empty() ||
  92. Main::is_project_manager()) {
  93. return;
  94. }
  95. #endif
  96. CharString da_args = OS::get_singleton()->get_environment("GODOT_MONO_DEBUGGER_AGENT").utf8();
  97. if (da_args.length() == 0) {
  98. da_args = String("--debugger-agent=transport=dt_socket,address=127.0.0.1:" + itos(da_port) +
  99. ",embedding=1,server=y,suspend=" + (da_suspend ? "y,timeout=" + itos(da_timeout) : "n"))
  100. .utf8();
  101. }
  102. // --debugger-agent=help
  103. const char *options[] = {
  104. "--soft-breakpoints",
  105. da_args.get_data()
  106. };
  107. mono_jit_parse_options(2, (char **)options);
  108. }
  109. #endif
  110. } // namespace
  111. void GDMono::add_mono_shared_libs_dir_to_path() {
  112. // By default Mono seems to search shared libraries in the following directories:
  113. // Current working directory, @executable_path@ and PATH
  114. // The parent directory of the image file (assembly where the dllimport method is declared)
  115. // @executable_path@/../lib
  116. // @executable_path@/../Libraries (__MACH__ only)
  117. // This does not work when embedding Mono unless we use the same directory structure.
  118. // To fix this we append the directory containing our shared libraries to PATH.
  119. #if defined(WINDOWS_ENABLED) || defined(UNIX_ENABLED)
  120. String path_var("PATH");
  121. String path_value = OS::get_singleton()->get_environment(path_var);
  122. #ifdef WINDOWS_ENABLED
  123. path_value += ';';
  124. String bundled_bin_dir = GodotSharpDirs::get_data_mono_bin_dir();
  125. path_value += DirAccess::exists(bundled_bin_dir) ? bundled_bin_dir : mono_reg_info.bin_dir;
  126. #else
  127. path_value += ':';
  128. String bundled_lib_dir = GodotSharpDirs::get_data_mono_lib_dir();
  129. if (DirAccess::exists(bundled_lib_dir)) {
  130. path_value += bundled_lib_dir;
  131. } else {
  132. // TODO: Do we need to add the lib dir when using the system installed Mono on Unix platforms?
  133. }
  134. #endif
  135. OS::get_singleton()->set_environment(path_var, path_value);
  136. #endif
  137. }
  138. void GDMono::initialize() {
  139. ERR_FAIL_NULL(Engine::get_singleton());
  140. print_verbose("Mono: Initializing module...");
  141. #ifdef DEBUG_METHODS_ENABLED
  142. _initialize_and_check_api_hashes();
  143. #endif
  144. GDMonoLog::get_singleton()->initialize();
  145. String assembly_rootdir;
  146. String config_dir;
  147. #ifdef TOOLS_ENABLED
  148. #ifdef WINDOWS_ENABLED
  149. mono_reg_info = MonoRegUtils::find_mono();
  150. if (mono_reg_info.assembly_dir.length() && DirAccess::exists(mono_reg_info.assembly_dir)) {
  151. assembly_rootdir = mono_reg_info.assembly_dir;
  152. }
  153. if (mono_reg_info.config_dir.length() && DirAccess::exists(mono_reg_info.config_dir)) {
  154. config_dir = mono_reg_info.config_dir;
  155. }
  156. #elif OSX_ENABLED
  157. const char *c_assembly_rootdir = mono_assembly_getrootdir();
  158. const char *c_config_dir = mono_get_config_dir();
  159. if (!c_assembly_rootdir || !c_config_dir || !DirAccess::exists(c_assembly_rootdir) || !DirAccess::exists(c_config_dir)) {
  160. Vector<const char *> locations;
  161. locations.push_back("/Library/Frameworks/Mono.framework/Versions/Current/");
  162. locations.push_back("/usr/local/var/homebrew/linked/mono/");
  163. for (int i = 0; i < locations.size(); i++) {
  164. String hint_assembly_rootdir = path_join(locations[i], "lib");
  165. String hint_mscorlib_path = path_join(hint_assembly_rootdir, "mono", "4.5", "mscorlib.dll");
  166. String hint_config_dir = path_join(locations[i], "etc");
  167. if (FileAccess::exists(hint_mscorlib_path) && DirAccess::exists(hint_config_dir)) {
  168. assembly_rootdir = hint_assembly_rootdir;
  169. config_dir = hint_config_dir;
  170. break;
  171. }
  172. }
  173. }
  174. #endif
  175. #endif // TOOLS_ENABLED
  176. String bundled_assembly_rootdir = GodotSharpDirs::get_data_mono_lib_dir();
  177. String bundled_config_dir = GodotSharpDirs::get_data_mono_etc_dir();
  178. #ifdef TOOLS_ENABLED
  179. if (DirAccess::exists(bundled_assembly_rootdir) && DirAccess::exists(bundled_config_dir)) {
  180. assembly_rootdir = bundled_assembly_rootdir;
  181. config_dir = bundled_config_dir;
  182. }
  183. #else
  184. // These are always the directories in export templates
  185. assembly_rootdir = bundled_assembly_rootdir;
  186. config_dir = bundled_config_dir;
  187. #endif
  188. // Leak if we call mono_set_dirs more than once
  189. mono_set_dirs(assembly_rootdir.length() ? assembly_rootdir.utf8().get_data() : NULL,
  190. config_dir.length() ? config_dir.utf8().get_data() : NULL);
  191. add_mono_shared_libs_dir_to_path();
  192. GDMonoAssembly::initialize();
  193. #ifdef DEBUG_ENABLED
  194. gdmono_debug_init();
  195. #endif
  196. mono_config_parse(NULL);
  197. mono_install_unhandled_exception_hook(&unhandled_exception_hook, NULL);
  198. #ifdef TOOLS_ENABLED
  199. if (!DirAccess::exists("res://.mono")) {
  200. // 'res://.mono/' is missing so there is nothing to load. We don't need to initialize mono, but
  201. // we still do so unless mscorlib is missing (which is the case for projects that don't use C#).
  202. String mscorlib_fname("mscorlib.dll");
  203. Vector<String> search_dirs;
  204. GDMonoAssembly::fill_search_dirs(search_dirs);
  205. bool found = false;
  206. for (int i = 0; i < search_dirs.size(); i++) {
  207. if (FileAccess::exists(search_dirs[i].plus_file(mscorlib_fname))) {
  208. found = true;
  209. break;
  210. }
  211. }
  212. if (!found)
  213. return; // mscorlib is missing, do not initialize mono
  214. }
  215. #endif
  216. root_domain = mono_jit_init_version("GodotEngine.RootDomain", "v4.0.30319");
  217. ERR_EXPLAIN("Mono: Failed to initialize runtime");
  218. ERR_FAIL_NULL(root_domain);
  219. GDMonoUtils::set_main_thread(GDMonoUtils::get_current_thread());
  220. setup_runtime_main_args(); // Required for System.Environment.GetCommandLineArgs
  221. runtime_initialized = true;
  222. print_verbose("Mono: Runtime initialized");
  223. // mscorlib assembly MUST be present at initialization
  224. ERR_EXPLAIN("Mono: Failed to load mscorlib assembly");
  225. ERR_FAIL_COND(!_load_corlib_assembly());
  226. #ifdef TOOLS_ENABLED
  227. // The tools domain must be loaded here, before the scripts domain.
  228. // Otherwise domain unload on the scripts domain will hang indefinitely.
  229. ERR_EXPLAIN("Mono: Failed to load tools domain");
  230. ERR_FAIL_COND(_load_tools_domain() != OK);
  231. // TODO move to editor init callback, and do it lazily when required before editor init (e.g.: bindings generation)
  232. ERR_EXPLAIN("Mono: Failed to load Editor Tools assembly");
  233. ERR_FAIL_COND(!_load_editor_tools_assembly());
  234. #endif
  235. ERR_EXPLAIN("Mono: Failed to load scripts domain");
  236. ERR_FAIL_COND(_load_scripts_domain() != OK);
  237. #ifdef DEBUG_ENABLED
  238. bool debugger_attached = _wait_for_debugger_msecs(500);
  239. if (!debugger_attached && OS::get_singleton()->is_stdout_verbose())
  240. print_error("Mono: Debugger wait timeout");
  241. #endif
  242. _register_internal_calls();
  243. // The following assemblies are not required at initialization
  244. #ifdef MONO_GLUE_ENABLED
  245. if (_load_api_assemblies()) {
  246. // Everything is fine with the api assemblies, load the project assembly
  247. _load_project_assembly();
  248. } else {
  249. if ((core_api_assembly && (core_api_assembly_out_of_sync || !GDMonoUtils::mono_cache.godot_api_cache_updated))
  250. #ifdef TOOLS_ENABLED
  251. || (editor_api_assembly && editor_api_assembly_out_of_sync)
  252. #endif
  253. ) {
  254. #ifdef TOOLS_ENABLED
  255. // The assembly was successfully loaded, but the full api could not be cached.
  256. // This is most likely an outdated assembly loaded because of an invalid version in the
  257. // metadata, so we invalidate the version in the metadata and unload the script domain.
  258. if (core_api_assembly_out_of_sync) {
  259. ERR_PRINT("The loaded Core API assembly is out of sync");
  260. metadata_set_api_assembly_invalidated(APIAssembly::API_CORE, true);
  261. } else if (!GDMonoUtils::mono_cache.godot_api_cache_updated) {
  262. ERR_PRINT("The loaded Core API assembly is in sync, but the cache update failed");
  263. metadata_set_api_assembly_invalidated(APIAssembly::API_CORE, true);
  264. }
  265. if (editor_api_assembly_out_of_sync) {
  266. ERR_PRINT("The loaded Editor API assembly is out of sync");
  267. metadata_set_api_assembly_invalidated(APIAssembly::API_EDITOR, true);
  268. }
  269. print_line("Mono: Proceeding to unload scripts domain because of invalid API assemblies.");
  270. Error err = _unload_scripts_domain();
  271. if (err != OK) {
  272. WARN_PRINT("Mono: Failed to unload scripts domain");
  273. }
  274. #else
  275. ERR_PRINT("The loaded API assembly is invalid");
  276. CRASH_NOW();
  277. #endif // TOOLS_ENABLED
  278. }
  279. }
  280. #else
  281. print_verbose("Mono: Glue disabled, ignoring script assemblies.");
  282. #endif // MONO_GLUE_ENABLED
  283. print_verbose("Mono: INITIALIZED");
  284. }
  285. #ifdef MONO_GLUE_ENABLED
  286. namespace GodotSharpBindings {
  287. uint64_t get_core_api_hash();
  288. #ifdef TOOLS_ENABLED
  289. uint64_t get_editor_api_hash();
  290. #endif
  291. uint32_t get_bindings_version();
  292. void register_generated_icalls();
  293. } // namespace GodotSharpBindings
  294. #endif
  295. void GDMono::_register_internal_calls() {
  296. #ifdef MONO_GLUE_ENABLED
  297. GodotSharpBindings::register_generated_icalls();
  298. #endif
  299. #ifdef TOOLS_ENABLED
  300. GodotSharpEditor::register_internal_calls();
  301. #endif
  302. }
  303. void GDMono::_initialize_and_check_api_hashes() {
  304. #ifdef MONO_GLUE_ENABLED
  305. if (get_api_core_hash() != GodotSharpBindings::get_core_api_hash()) {
  306. ERR_PRINT("Mono: Core API hash mismatch!");
  307. }
  308. #ifdef TOOLS_ENABLED
  309. if (get_api_editor_hash() != GodotSharpBindings::get_editor_api_hash()) {
  310. ERR_PRINT("Mono: Editor API hash mismatch!");
  311. }
  312. #endif // TOOLS_ENABLED
  313. #endif // MONO_GLUE_ENABLED
  314. }
  315. void GDMono::add_assembly(uint32_t p_domain_id, GDMonoAssembly *p_assembly) {
  316. assemblies[p_domain_id][p_assembly->get_name()] = p_assembly;
  317. }
  318. GDMonoAssembly **GDMono::get_loaded_assembly(const String &p_name) {
  319. MonoDomain *domain = mono_domain_get();
  320. uint32_t domain_id = domain ? mono_domain_get_id(domain) : 0;
  321. return assemblies[domain_id].getptr(p_name);
  322. }
  323. bool GDMono::load_assembly(const String &p_name, GDMonoAssembly **r_assembly, bool p_refonly) {
  324. CRASH_COND(!r_assembly);
  325. MonoAssemblyName *aname = mono_assembly_name_new(p_name.utf8());
  326. bool result = load_assembly(p_name, aname, r_assembly, p_refonly);
  327. mono_assembly_name_free(aname);
  328. mono_free(aname);
  329. return result;
  330. }
  331. bool GDMono::load_assembly(const String &p_name, MonoAssemblyName *p_aname, GDMonoAssembly **r_assembly, bool p_refonly) {
  332. CRASH_COND(!r_assembly);
  333. print_verbose("Mono: Loading assembly " + p_name + (p_refonly ? " (refonly)" : "") + "...");
  334. MonoImageOpenStatus status = MONO_IMAGE_OK;
  335. MonoAssembly *assembly = mono_assembly_load_full(p_aname, NULL, &status, p_refonly);
  336. if (!assembly)
  337. return false;
  338. ERR_FAIL_COND_V(status != MONO_IMAGE_OK, false);
  339. uint32_t domain_id = mono_domain_get_id(mono_domain_get());
  340. GDMonoAssembly **stored_assembly = assemblies[domain_id].getptr(p_name);
  341. ERR_FAIL_COND_V(stored_assembly == NULL, false);
  342. ERR_FAIL_COND_V((*stored_assembly)->get_assembly() != assembly, false);
  343. *r_assembly = *stored_assembly;
  344. print_verbose("Mono: Assembly " + p_name + (p_refonly ? " (refonly)" : "") + " loaded from path: " + (*r_assembly)->get_path());
  345. return true;
  346. }
  347. bool GDMono::load_assembly_from(const String &p_name, const String &p_path, GDMonoAssembly **r_assembly, bool p_refonly) {
  348. CRASH_COND(!r_assembly);
  349. print_verbose("Mono: Loading assembly " + p_name + (p_refonly ? " (refonly)" : "") + "...");
  350. GDMonoAssembly *assembly = GDMonoAssembly::load_from(p_name, p_path, p_refonly);
  351. if (!assembly)
  352. return false;
  353. #ifdef DEBUG_ENABLED
  354. uint32_t domain_id = mono_domain_get_id(mono_domain_get());
  355. GDMonoAssembly **stored_assembly = assemblies[domain_id].getptr(p_name);
  356. ERR_FAIL_COND_V(stored_assembly == NULL, false);
  357. ERR_FAIL_COND_V(*stored_assembly != assembly, false);
  358. #endif
  359. *r_assembly = assembly;
  360. print_verbose("Mono: Assembly " + p_name + (p_refonly ? " (refonly)" : "") + " loaded from path: " + (*r_assembly)->get_path());
  361. return true;
  362. }
  363. APIAssembly::Version APIAssembly::Version::get_from_loaded_assembly(GDMonoAssembly *p_api_assembly, APIAssembly::Type p_api_type) {
  364. APIAssembly::Version api_assembly_version;
  365. const char *nativecalls_name = p_api_type == APIAssembly::API_CORE ?
  366. BINDINGS_CLASS_NATIVECALLS :
  367. BINDINGS_CLASS_NATIVECALLS_EDITOR;
  368. GDMonoClass *nativecalls_klass = p_api_assembly->get_class(BINDINGS_NAMESPACE, nativecalls_name);
  369. if (nativecalls_klass) {
  370. GDMonoField *api_hash_field = nativecalls_klass->get_field("godot_api_hash");
  371. if (api_hash_field)
  372. api_assembly_version.godot_api_hash = GDMonoMarshal::unbox<uint64_t>(api_hash_field->get_value(NULL));
  373. GDMonoField *binds_ver_field = nativecalls_klass->get_field("bindings_version");
  374. if (binds_ver_field)
  375. api_assembly_version.bindings_version = GDMonoMarshal::unbox<uint32_t>(binds_ver_field->get_value(NULL));
  376. GDMonoField *cs_glue_ver_field = nativecalls_klass->get_field("cs_glue_version");
  377. if (cs_glue_ver_field)
  378. api_assembly_version.cs_glue_version = GDMonoMarshal::unbox<uint32_t>(cs_glue_ver_field->get_value(NULL));
  379. }
  380. return api_assembly_version;
  381. }
  382. String APIAssembly::to_string(APIAssembly::Type p_type) {
  383. return p_type == APIAssembly::API_CORE ? "API_CORE" : "API_EDITOR";
  384. }
  385. bool GDMono::_load_corlib_assembly() {
  386. if (corlib_assembly)
  387. return true;
  388. bool success = load_assembly("mscorlib", &corlib_assembly);
  389. if (success)
  390. GDMonoUtils::update_corlib_cache();
  391. return success;
  392. }
  393. bool GDMono::_load_core_api_assembly() {
  394. if (core_api_assembly)
  395. return true;
  396. #ifdef TOOLS_ENABLED
  397. if (metadata_is_api_assembly_invalidated(APIAssembly::API_CORE)) {
  398. print_verbose("Mono: Skipping loading of Core API assembly because it was invalidated");
  399. return false;
  400. }
  401. #endif
  402. String assembly_path = GodotSharpDirs::get_res_assemblies_dir().plus_file(CORE_API_ASSEMBLY_NAME ".dll");
  403. if (!FileAccess::exists(assembly_path))
  404. return false;
  405. bool success = load_assembly_from(CORE_API_ASSEMBLY_NAME,
  406. assembly_path,
  407. &core_api_assembly);
  408. if (success) {
  409. #ifdef MONO_GLUE_ENABLED
  410. APIAssembly::Version api_assembly_ver = APIAssembly::Version::get_from_loaded_assembly(core_api_assembly, APIAssembly::API_CORE);
  411. core_api_assembly_out_of_sync = GodotSharpBindings::get_core_api_hash() != api_assembly_ver.godot_api_hash ||
  412. GodotSharpBindings::get_bindings_version() != api_assembly_ver.bindings_version ||
  413. CS_GLUE_VERSION != api_assembly_ver.cs_glue_version;
  414. if (!core_api_assembly_out_of_sync) {
  415. GDMonoUtils::update_godot_api_cache();
  416. }
  417. #else
  418. GDMonoUtils::update_godot_api_cache();
  419. #endif
  420. }
  421. return success;
  422. }
  423. #ifdef TOOLS_ENABLED
  424. bool GDMono::_load_editor_api_assembly() {
  425. if (editor_api_assembly)
  426. return true;
  427. if (metadata_is_api_assembly_invalidated(APIAssembly::API_EDITOR)) {
  428. print_verbose("Mono: Skipping loading of Editor API assembly because it was invalidated");
  429. return false;
  430. }
  431. String assembly_path = GodotSharpDirs::get_res_assemblies_dir().plus_file(EDITOR_API_ASSEMBLY_NAME ".dll");
  432. if (!FileAccess::exists(assembly_path))
  433. return false;
  434. bool success = load_assembly_from(EDITOR_API_ASSEMBLY_NAME,
  435. assembly_path,
  436. &editor_api_assembly);
  437. if (success) {
  438. #ifdef MONO_GLUE_ENABLED
  439. APIAssembly::Version api_assembly_ver = APIAssembly::Version::get_from_loaded_assembly(editor_api_assembly, APIAssembly::API_EDITOR);
  440. editor_api_assembly_out_of_sync = GodotSharpBindings::get_editor_api_hash() != api_assembly_ver.godot_api_hash ||
  441. GodotSharpBindings::get_bindings_version() != api_assembly_ver.bindings_version ||
  442. CS_GLUE_VERSION != api_assembly_ver.cs_glue_version;
  443. #endif
  444. }
  445. return success;
  446. }
  447. #endif
  448. #ifdef TOOLS_ENABLED
  449. bool GDMono::_load_editor_tools_assembly() {
  450. if (editor_tools_assembly)
  451. return true;
  452. _GDMONO_SCOPE_DOMAIN_(tools_domain)
  453. return load_assembly(EDITOR_TOOLS_ASSEMBLY_NAME, &editor_tools_assembly);
  454. }
  455. #endif
  456. bool GDMono::_load_project_assembly() {
  457. if (project_assembly)
  458. return true;
  459. String name = ProjectSettings::get_singleton()->get("application/config/name");
  460. if (name.empty()) {
  461. name = "UnnamedProject";
  462. }
  463. bool success = load_assembly(name, &project_assembly);
  464. if (success) {
  465. mono_assembly_set_main(project_assembly->get_assembly());
  466. CSharpLanguage::get_singleton()->project_assembly_loaded();
  467. } else {
  468. if (OS::get_singleton()->is_stdout_verbose())
  469. print_error("Mono: Failed to load project assembly");
  470. }
  471. return success;
  472. }
  473. bool GDMono::_load_api_assemblies() {
  474. if (!_load_core_api_assembly()) {
  475. if (OS::get_singleton()->is_stdout_verbose())
  476. print_error("Mono: Failed to load Core API assembly");
  477. return false;
  478. }
  479. if (core_api_assembly_out_of_sync || !GDMonoUtils::mono_cache.godot_api_cache_updated)
  480. return false;
  481. #ifdef TOOLS_ENABLED
  482. if (!_load_editor_api_assembly()) {
  483. if (OS::get_singleton()->is_stdout_verbose())
  484. print_error("Mono: Failed to load Editor API assembly");
  485. return false;
  486. }
  487. if (editor_api_assembly_out_of_sync)
  488. return false;
  489. #endif
  490. return true;
  491. }
  492. #ifdef TOOLS_ENABLED
  493. String GDMono::_get_api_assembly_metadata_path() {
  494. return GodotSharpDirs::get_res_metadata_dir().plus_file("api_assemblies.cfg");
  495. }
  496. void GDMono::metadata_set_api_assembly_invalidated(APIAssembly::Type p_api_type, bool p_invalidated) {
  497. String section = APIAssembly::to_string(p_api_type);
  498. String path = _get_api_assembly_metadata_path();
  499. Ref<ConfigFile> metadata;
  500. metadata.instance();
  501. metadata->load(path);
  502. metadata->set_value(section, "invalidated", p_invalidated);
  503. String assembly_path = GodotSharpDirs::get_res_assemblies_dir()
  504. .plus_file(p_api_type == APIAssembly::API_CORE ?
  505. CORE_API_ASSEMBLY_NAME ".dll" :
  506. EDITOR_API_ASSEMBLY_NAME ".dll");
  507. ERR_FAIL_COND(!FileAccess::exists(assembly_path));
  508. uint64_t modified_time = FileAccess::get_modified_time(assembly_path);
  509. metadata->set_value(section, "invalidated_asm_modified_time", String::num_uint64(modified_time));
  510. String dir = path.get_base_dir();
  511. if (!DirAccess::exists(dir)) {
  512. DirAccessRef da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM);
  513. ERR_FAIL_COND(!da);
  514. Error err = da->make_dir_recursive(ProjectSettings::get_singleton()->globalize_path(dir));
  515. ERR_FAIL_COND(err != OK);
  516. }
  517. Error save_err = metadata->save(path);
  518. ERR_FAIL_COND(save_err != OK);
  519. }
  520. bool GDMono::metadata_is_api_assembly_invalidated(APIAssembly::Type p_api_type) {
  521. String section = APIAssembly::to_string(p_api_type);
  522. Ref<ConfigFile> metadata;
  523. metadata.instance();
  524. metadata->load(_get_api_assembly_metadata_path());
  525. String assembly_path = GodotSharpDirs::get_res_assemblies_dir()
  526. .plus_file(p_api_type == APIAssembly::API_CORE ?
  527. CORE_API_ASSEMBLY_NAME ".dll" :
  528. EDITOR_API_ASSEMBLY_NAME ".dll");
  529. if (!FileAccess::exists(assembly_path))
  530. return false;
  531. uint64_t modified_time = FileAccess::get_modified_time(assembly_path);
  532. uint64_t stored_modified_time = metadata->get_value(section, "invalidated_asm_modified_time", 0);
  533. return metadata->get_value(section, "invalidated", false) && modified_time <= stored_modified_time;
  534. }
  535. #endif
  536. Error GDMono::_load_scripts_domain() {
  537. ERR_FAIL_COND_V(scripts_domain != NULL, ERR_BUG);
  538. print_verbose("Mono: Loading scripts domain...");
  539. scripts_domain = GDMonoUtils::create_domain("GodotEngine.ScriptsDomain");
  540. ERR_EXPLAIN("Mono: Could not create scripts app domain");
  541. ERR_FAIL_NULL_V(scripts_domain, ERR_CANT_CREATE);
  542. mono_domain_set(scripts_domain, true);
  543. return OK;
  544. }
  545. Error GDMono::_unload_scripts_domain() {
  546. ERR_FAIL_NULL_V(scripts_domain, ERR_BUG);
  547. print_verbose("Mono: Unloading scripts domain...");
  548. if (mono_domain_get() != root_domain)
  549. mono_domain_set(root_domain, true);
  550. mono_gc_collect(mono_gc_max_generation());
  551. finalizing_scripts_domain = true;
  552. if (!mono_domain_finalize(scripts_domain, 2000)) {
  553. ERR_PRINT("Mono: Domain finalization timeout");
  554. }
  555. finalizing_scripts_domain = false;
  556. mono_gc_collect(mono_gc_max_generation());
  557. _domain_assemblies_cleanup(mono_domain_get_id(scripts_domain));
  558. core_api_assembly = NULL;
  559. project_assembly = NULL;
  560. #ifdef TOOLS_ENABLED
  561. editor_api_assembly = NULL;
  562. #endif
  563. core_api_assembly_out_of_sync = false;
  564. #ifdef TOOLS_ENABLED
  565. editor_api_assembly_out_of_sync = false;
  566. #endif
  567. MonoDomain *domain = scripts_domain;
  568. scripts_domain = NULL;
  569. MonoException *exc = NULL;
  570. mono_domain_try_unload(domain, (MonoObject **)&exc);
  571. if (exc) {
  572. ERR_PRINT("Exception thrown when unloading scripts domain");
  573. GDMonoUtils::debug_unhandled_exception(exc);
  574. return FAILED;
  575. }
  576. return OK;
  577. }
  578. #ifdef TOOLS_ENABLED
  579. Error GDMono::_load_tools_domain() {
  580. ERR_FAIL_COND_V(tools_domain != NULL, ERR_BUG);
  581. print_verbose("Mono: Loading tools domain...");
  582. tools_domain = GDMonoUtils::create_domain("GodotEngine.ToolsDomain");
  583. ERR_EXPLAIN("Mono: Could not create tools app domain");
  584. ERR_FAIL_NULL_V(tools_domain, ERR_CANT_CREATE);
  585. return OK;
  586. }
  587. #endif
  588. #ifdef GD_MONO_HOT_RELOAD
  589. Error GDMono::reload_scripts_domain() {
  590. ERR_FAIL_COND_V(!runtime_initialized, ERR_BUG);
  591. if (scripts_domain) {
  592. Error err = _unload_scripts_domain();
  593. if (err != OK) {
  594. ERR_PRINT("Mono: Failed to unload scripts domain");
  595. return err;
  596. }
  597. }
  598. Error err = _load_scripts_domain();
  599. if (err != OK) {
  600. ERR_PRINT("Mono: Failed to load scripts domain");
  601. return err;
  602. }
  603. #ifdef MONO_GLUE_ENABLED
  604. if (!_load_api_assemblies()) {
  605. if ((core_api_assembly && (core_api_assembly_out_of_sync || !GDMonoUtils::mono_cache.godot_api_cache_updated))
  606. #ifdef TOOLS_ENABLED
  607. || (editor_api_assembly && editor_api_assembly_out_of_sync)
  608. #endif
  609. ) {
  610. #ifdef TOOLS_ENABLED
  611. // The assembly was successfully loaded, but the full api could not be cached.
  612. // This is most likely an outdated assembly loaded because of an invalid version in the
  613. // metadata, so we invalidate the version in the metadata and unload the script domain.
  614. if (core_api_assembly_out_of_sync) {
  615. ERR_PRINT("The loaded Core API assembly is out of sync");
  616. metadata_set_api_assembly_invalidated(APIAssembly::API_CORE, true);
  617. } else if (!GDMonoUtils::mono_cache.godot_api_cache_updated) {
  618. ERR_PRINT("The loaded Core API assembly is in sync, but the cache update failed");
  619. metadata_set_api_assembly_invalidated(APIAssembly::API_CORE, true);
  620. }
  621. if (editor_api_assembly_out_of_sync) {
  622. ERR_PRINT("The loaded Editor API assembly is out of sync");
  623. metadata_set_api_assembly_invalidated(APIAssembly::API_EDITOR, true);
  624. }
  625. Error err = _unload_scripts_domain();
  626. if (err != OK) {
  627. WARN_PRINT("Mono: Failed to unload scripts domain");
  628. }
  629. return ERR_CANT_RESOLVE;
  630. #else
  631. ERR_PRINT("The loaded API assembly is invalid");
  632. CRASH_NOW();
  633. #endif
  634. } else {
  635. return ERR_CANT_OPEN;
  636. }
  637. }
  638. if (!_load_project_assembly()) {
  639. return ERR_CANT_OPEN;
  640. }
  641. #else
  642. print_verbose("Mono: Glue disabled, ignoring script assemblies.");
  643. #endif // MONO_GLUE_ENABLED
  644. return OK;
  645. }
  646. #endif
  647. Error GDMono::finalize_and_unload_domain(MonoDomain *p_domain) {
  648. CRASH_COND(p_domain == NULL);
  649. String domain_name = mono_domain_get_friendly_name(p_domain);
  650. print_verbose("Mono: Unloading domain `" + domain_name + "`...");
  651. if (mono_domain_get() == p_domain)
  652. mono_domain_set(root_domain, true);
  653. mono_gc_collect(mono_gc_max_generation());
  654. if (!mono_domain_finalize(p_domain, 2000)) {
  655. ERR_PRINT("Mono: Domain finalization timeout");
  656. }
  657. mono_gc_collect(mono_gc_max_generation());
  658. _domain_assemblies_cleanup(mono_domain_get_id(p_domain));
  659. MonoException *exc = NULL;
  660. mono_domain_try_unload(p_domain, (MonoObject **)&exc);
  661. if (exc) {
  662. ERR_PRINTS("Exception thrown when unloading domain `" + domain_name + "`");
  663. GDMonoUtils::debug_unhandled_exception(exc);
  664. return FAILED;
  665. }
  666. return OK;
  667. }
  668. GDMonoClass *GDMono::get_class(MonoClass *p_raw_class) {
  669. MonoImage *image = mono_class_get_image(p_raw_class);
  670. if (image == corlib_assembly->get_image())
  671. return corlib_assembly->get_class(p_raw_class);
  672. uint32_t domain_id = mono_domain_get_id(mono_domain_get());
  673. HashMap<String, GDMonoAssembly *> &domain_assemblies = assemblies[domain_id];
  674. const String *k = NULL;
  675. while ((k = domain_assemblies.next(k))) {
  676. GDMonoAssembly *assembly = domain_assemblies.get(*k);
  677. if (assembly->get_image() == image) {
  678. GDMonoClass *klass = assembly->get_class(p_raw_class);
  679. if (klass)
  680. return klass;
  681. }
  682. }
  683. return NULL;
  684. }
  685. void GDMono::_domain_assemblies_cleanup(uint32_t p_domain_id) {
  686. HashMap<String, GDMonoAssembly *> &domain_assemblies = assemblies[p_domain_id];
  687. const String *k = NULL;
  688. while ((k = domain_assemblies.next(k))) {
  689. memdelete(domain_assemblies.get(*k));
  690. }
  691. assemblies.erase(p_domain_id);
  692. }
  693. void GDMono::unhandled_exception_hook(MonoObject *p_exc, void *) {
  694. // This method will be called by the runtime when a thrown exception is not handled.
  695. // It won't be called when we manually treat a thrown exception as unhandled.
  696. // We assume the exception was already printed before calling this hook.
  697. #ifdef DEBUG_ENABLED
  698. GDMonoUtils::debug_send_unhandled_exception_error((MonoException *)p_exc);
  699. if (ScriptDebugger::get_singleton())
  700. ScriptDebugger::get_singleton()->idle_poll();
  701. #endif
  702. abort();
  703. GD_UNREACHABLE();
  704. }
  705. GDMono::GDMono() {
  706. singleton = this;
  707. gdmono_log = memnew(GDMonoLog);
  708. runtime_initialized = false;
  709. finalizing_scripts_domain = false;
  710. root_domain = NULL;
  711. scripts_domain = NULL;
  712. #ifdef TOOLS_ENABLED
  713. tools_domain = NULL;
  714. #endif
  715. core_api_assembly_out_of_sync = false;
  716. #ifdef TOOLS_ENABLED
  717. editor_api_assembly_out_of_sync = false;
  718. #endif
  719. corlib_assembly = NULL;
  720. core_api_assembly = NULL;
  721. project_assembly = NULL;
  722. #ifdef TOOLS_ENABLED
  723. editor_api_assembly = NULL;
  724. editor_tools_assembly = NULL;
  725. #endif
  726. api_core_hash = 0;
  727. #ifdef TOOLS_ENABLED
  728. api_editor_hash = 0;
  729. #endif
  730. }
  731. GDMono::~GDMono() {
  732. if (is_runtime_initialized()) {
  733. if (scripts_domain) {
  734. Error err = _unload_scripts_domain();
  735. if (err != OK) {
  736. WARN_PRINT("Mono: Failed to unload scripts domain");
  737. }
  738. }
  739. const uint32_t *k = NULL;
  740. while ((k = assemblies.next(k))) {
  741. HashMap<String, GDMonoAssembly *> &domain_assemblies = assemblies.get(*k);
  742. const String *kk = NULL;
  743. while ((kk = domain_assemblies.next(kk))) {
  744. memdelete(domain_assemblies.get(*kk));
  745. }
  746. }
  747. assemblies.clear();
  748. GDMonoUtils::clear_cache();
  749. print_verbose("Mono: Runtime cleanup...");
  750. mono_jit_cleanup(root_domain);
  751. runtime_initialized = false;
  752. }
  753. if (gdmono_log)
  754. memdelete(gdmono_log);
  755. singleton = NULL;
  756. }
  757. _GodotSharp *_GodotSharp::singleton = NULL;
  758. void _GodotSharp::attach_thread() {
  759. GDMonoUtils::attach_current_thread();
  760. }
  761. void _GodotSharp::detach_thread() {
  762. GDMonoUtils::detach_current_thread();
  763. }
  764. int32_t _GodotSharp::get_domain_id() {
  765. MonoDomain *domain = mono_domain_get();
  766. CRASH_COND(!domain); // User must check if runtime is initialized before calling this method
  767. return mono_domain_get_id(domain);
  768. }
  769. int32_t _GodotSharp::get_scripts_domain_id() {
  770. MonoDomain *domain = SCRIPTS_DOMAIN;
  771. CRASH_COND(!domain); // User must check if scripts domain is loaded before calling this method
  772. return mono_domain_get_id(domain);
  773. }
  774. bool _GodotSharp::is_scripts_domain_loaded() {
  775. return GDMono::get_singleton()->is_runtime_initialized() && SCRIPTS_DOMAIN != NULL;
  776. }
  777. bool _GodotSharp::_is_domain_finalizing_for_unload(int32_t p_domain_id) {
  778. return is_domain_finalizing_for_unload(p_domain_id);
  779. }
  780. bool _GodotSharp::is_domain_finalizing_for_unload() {
  781. return is_domain_finalizing_for_unload(mono_domain_get());
  782. }
  783. bool _GodotSharp::is_domain_finalizing_for_unload(int32_t p_domain_id) {
  784. return is_domain_finalizing_for_unload(mono_domain_get_by_id(p_domain_id));
  785. }
  786. bool _GodotSharp::is_domain_finalizing_for_unload(MonoDomain *p_domain) {
  787. if (!p_domain)
  788. return true;
  789. if (p_domain == SCRIPTS_DOMAIN && GDMono::get_singleton()->is_finalizing_scripts_domain())
  790. return true;
  791. return mono_domain_is_unloading(p_domain);
  792. }
  793. bool _GodotSharp::is_runtime_shutting_down() {
  794. return mono_runtime_is_shutting_down();
  795. }
  796. bool _GodotSharp::is_runtime_initialized() {
  797. return GDMono::get_singleton()->is_runtime_initialized();
  798. }
  799. void _GodotSharp::_bind_methods() {
  800. ClassDB::bind_method(D_METHOD("attach_thread"), &_GodotSharp::attach_thread);
  801. ClassDB::bind_method(D_METHOD("detach_thread"), &_GodotSharp::detach_thread);
  802. ClassDB::bind_method(D_METHOD("get_domain_id"), &_GodotSharp::get_domain_id);
  803. ClassDB::bind_method(D_METHOD("get_scripts_domain_id"), &_GodotSharp::get_scripts_domain_id);
  804. ClassDB::bind_method(D_METHOD("is_scripts_domain_loaded"), &_GodotSharp::is_scripts_domain_loaded);
  805. ClassDB::bind_method(D_METHOD("is_domain_finalizing_for_unload", "domain_id"), &_GodotSharp::_is_domain_finalizing_for_unload);
  806. ClassDB::bind_method(D_METHOD("is_runtime_shutting_down"), &_GodotSharp::is_runtime_shutting_down);
  807. ClassDB::bind_method(D_METHOD("is_runtime_initialized"), &_GodotSharp::is_runtime_initialized);
  808. }
  809. _GodotSharp::_GodotSharp() {
  810. singleton = this;
  811. }
  812. _GodotSharp::~_GodotSharp() {
  813. singleton = NULL;
  814. }