gd_mono_assembly.cpp 9.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359
  1. /*************************************************************************/
  2. /* gd_mono_assembly.cpp */
  3. /*************************************************************************/
  4. /* This file is part of: */
  5. /* GODOT ENGINE */
  6. /* https://godotengine.org */
  7. /*************************************************************************/
  8. /* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */
  9. /* Copyright (c) 2014-2017 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_assembly.h"
  31. #include <mono/metadata/mono-debug.h>
  32. #include <mono/metadata/tokentype.h>
  33. #include "list.h"
  34. #include "os/file_access.h"
  35. #include "os/os.h"
  36. #include "../godotsharp_dirs.h"
  37. #include "gd_mono_class.h"
  38. bool GDMonoAssembly::no_search = false;
  39. Vector<String> GDMonoAssembly::search_dirs;
  40. MonoAssembly *GDMonoAssembly::_search_hook(MonoAssemblyName *aname, void *user_data) {
  41. (void)user_data; // UNUSED
  42. String name = mono_assembly_name_get_name(aname);
  43. bool has_extension = name.ends_with(".dll") || name.ends_with(".exe");
  44. if (no_search)
  45. return NULL;
  46. GDMonoAssembly **loaded_asm = GDMono::get_singleton()->get_loaded_assembly(has_extension ? name.get_basename() : name);
  47. if (loaded_asm)
  48. return (*loaded_asm)->get_assembly();
  49. no_search = true; // Avoid the recursion madness
  50. String path;
  51. MonoAssembly *res = NULL;
  52. for (int i = 0; i < search_dirs.size(); i++) {
  53. const String &search_dir = search_dirs[i];
  54. if (has_extension) {
  55. path = search_dir.plus_file(name);
  56. if (FileAccess::exists(path)) {
  57. res = _load_assembly_from(name.get_basename(), path);
  58. break;
  59. }
  60. } else {
  61. path = search_dir.plus_file(name + ".dll");
  62. if (FileAccess::exists(path)) {
  63. res = _load_assembly_from(name, path);
  64. break;
  65. }
  66. path = search_dir.plus_file(name + ".exe");
  67. if (FileAccess::exists(path)) {
  68. res = _load_assembly_from(name, path);
  69. break;
  70. }
  71. }
  72. }
  73. no_search = false;
  74. return res;
  75. }
  76. MonoAssembly *GDMonoAssembly::_preload_hook(MonoAssemblyName *aname, char **assemblies_path, void *user_data) {
  77. (void)user_data; // UNUSED
  78. if (search_dirs.empty()) {
  79. #ifdef TOOLS_DOMAIN
  80. search_dirs.push_back(GodotSharpDirs::get_res_temp_assemblies_dir());
  81. #endif
  82. search_dirs.push_back(GodotSharpDirs::get_res_assemblies_dir());
  83. search_dirs.push_back(OS::get_singleton()->get_resource_dir());
  84. search_dirs.push_back(OS::get_singleton()->get_executable_path().get_base_dir());
  85. const char *rootdir = mono_assembly_getrootdir();
  86. if (rootdir) {
  87. search_dirs.push_back(String(rootdir).plus_file("mono").plus_file("4.5"));
  88. }
  89. if (assemblies_path) {
  90. while (*assemblies_path) {
  91. search_dirs.push_back(*assemblies_path);
  92. ++assemblies_path;
  93. }
  94. }
  95. }
  96. return NULL;
  97. }
  98. MonoAssembly *GDMonoAssembly::_load_assembly_from(const String &p_name, const String &p_path) {
  99. GDMonoAssembly *assembly = memnew(GDMonoAssembly(p_name, p_path));
  100. MonoDomain *domain = mono_domain_get();
  101. Error err = assembly->load(domain);
  102. if (err != OK) {
  103. memdelete(assembly);
  104. ERR_FAIL_V(NULL);
  105. }
  106. GDMono::get_singleton()->add_assembly(domain ? mono_domain_get_id(domain) : 0, assembly);
  107. return assembly->get_assembly();
  108. }
  109. void GDMonoAssembly::initialize() {
  110. // TODO refonly as well?
  111. mono_install_assembly_preload_hook(&GDMonoAssembly::_preload_hook, NULL);
  112. mono_install_assembly_search_hook(&GDMonoAssembly::_search_hook, NULL);
  113. }
  114. Error GDMonoAssembly::load(MonoDomain *p_domain) {
  115. ERR_FAIL_COND_V(loaded, ERR_FILE_ALREADY_IN_USE);
  116. uint64_t last_modified_time = FileAccess::get_modified_time(path);
  117. Vector<uint8_t> data = FileAccess::get_file_as_array(path);
  118. ERR_FAIL_COND_V(data.empty(), ERR_FILE_CANT_READ);
  119. String image_filename(path);
  120. MonoImageOpenStatus status;
  121. image = mono_image_open_from_data_with_name(
  122. (char *)&data[0], data.size(),
  123. true, &status, false,
  124. image_filename.utf8().get_data());
  125. ERR_FAIL_COND_V(status != MONO_IMAGE_OK || image == NULL, ERR_FILE_CANT_OPEN);
  126. #ifdef DEBUG_ENABLED
  127. String pdb_path(path + ".pdb");
  128. if (!FileAccess::exists(pdb_path)) {
  129. pdb_path = path.get_basename() + ".pdb"; // without .dll
  130. if (!FileAccess::exists(pdb_path))
  131. goto no_pdb;
  132. }
  133. pdb_data.clear();
  134. pdb_data = FileAccess::get_file_as_array(pdb_path);
  135. mono_debug_open_image_from_memory(image, &pdb_data[0], pdb_data.size());
  136. no_pdb:
  137. #endif
  138. assembly = mono_assembly_load_from_full(image, image_filename.utf8().get_data(), &status, false);
  139. ERR_FAIL_COND_V(status != MONO_IMAGE_OK || assembly == NULL, ERR_FILE_CANT_OPEN);
  140. if (p_domain && mono_image_get_entry_point(image)) {
  141. // TODO should this be removed? do we want to call main? what other effects does this have?
  142. mono_jit_exec(p_domain, assembly, 0, NULL);
  143. }
  144. loaded = true;
  145. modified_time = last_modified_time;
  146. return OK;
  147. }
  148. Error GDMonoAssembly::wrapper_for_image(MonoImage *p_image) {
  149. ERR_FAIL_COND_V(loaded, ERR_FILE_ALREADY_IN_USE);
  150. assembly = mono_image_get_assembly(p_image);
  151. ERR_FAIL_NULL_V(assembly, FAILED);
  152. image = p_image;
  153. mono_image_addref(image);
  154. loaded = true;
  155. return OK;
  156. }
  157. void GDMonoAssembly::unload() {
  158. ERR_FAIL_COND(!loaded);
  159. #ifdef DEBUG_ENABLED
  160. if (pdb_data.size()) {
  161. mono_debug_close_image(image);
  162. pdb_data.clear();
  163. }
  164. #endif
  165. for (Map<MonoClass *, GDMonoClass *>::Element *E = cached_raw.front(); E; E = E->next()) {
  166. memdelete(E->value());
  167. }
  168. cached_classes.clear();
  169. cached_raw.clear();
  170. mono_image_close(image);
  171. assembly = NULL;
  172. image = NULL;
  173. loaded = false;
  174. }
  175. GDMonoClass *GDMonoAssembly::get_class(const StringName &p_namespace, const StringName &p_name) {
  176. ERR_FAIL_COND_V(!loaded, NULL);
  177. ClassKey key(p_namespace, p_name);
  178. GDMonoClass **match = cached_classes.getptr(key);
  179. if (match)
  180. return *match;
  181. MonoClass *mono_class = mono_class_from_name(image, String(p_namespace).utf8(), String(p_name).utf8());
  182. if (!mono_class)
  183. return NULL;
  184. GDMonoClass *wrapped_class = memnew(GDMonoClass(p_namespace, p_name, mono_class, this));
  185. cached_classes[key] = wrapped_class;
  186. cached_raw[mono_class] = wrapped_class;
  187. return wrapped_class;
  188. }
  189. GDMonoClass *GDMonoAssembly::get_class(MonoClass *p_mono_class) {
  190. ERR_FAIL_COND_V(!loaded, NULL);
  191. Map<MonoClass *, GDMonoClass *>::Element *match = cached_raw.find(p_mono_class);
  192. if (match)
  193. return match->value();
  194. StringName namespace_name = mono_class_get_namespace(p_mono_class);
  195. StringName class_name = mono_class_get_name(p_mono_class);
  196. GDMonoClass *wrapped_class = memnew(GDMonoClass(namespace_name, class_name, p_mono_class, this));
  197. cached_classes[ClassKey(namespace_name, class_name)] = wrapped_class;
  198. cached_raw[p_mono_class] = wrapped_class;
  199. return wrapped_class;
  200. }
  201. GDMonoClass *GDMonoAssembly::get_object_derived_class(const StringName &p_class) {
  202. GDMonoClass *match = NULL;
  203. if (gdobject_class_cache_updated) {
  204. Map<StringName, GDMonoClass *>::Element *result = gdobject_class_cache.find(p_class);
  205. if (result)
  206. match = result->get();
  207. } else {
  208. List<GDMonoClass *> nested_classes;
  209. int rows = mono_image_get_table_rows(image, MONO_TABLE_TYPEDEF);
  210. for (int i = 1; i < rows; i++) {
  211. MonoClass *mono_class = mono_class_get(image, (i + 1) | MONO_TOKEN_TYPE_DEF);
  212. if (!mono_class_is_assignable_from(CACHED_CLASS_RAW(GodotObject), mono_class))
  213. continue;
  214. GDMonoClass *current = get_class(mono_class);
  215. if (!current)
  216. continue;
  217. nested_classes.push_back(current);
  218. if (!match && current->get_name() == p_class)
  219. match = current;
  220. while (!nested_classes.empty()) {
  221. GDMonoClass *current_nested = nested_classes.front()->get();
  222. nested_classes.pop_back();
  223. void *iter = NULL;
  224. while (true) {
  225. MonoClass *raw_nested = mono_class_get_nested_types(current_nested->get_raw(), &iter);
  226. if (!raw_nested)
  227. break;
  228. GDMonoClass *nested_class = get_class(raw_nested);
  229. if (nested_class) {
  230. gdobject_class_cache.insert(nested_class->get_name(), nested_class);
  231. nested_classes.push_back(nested_class);
  232. }
  233. }
  234. }
  235. gdobject_class_cache.insert(current->get_name(), current);
  236. }
  237. gdobject_class_cache_updated = true;
  238. }
  239. return match;
  240. }
  241. GDMonoAssembly::GDMonoAssembly(const String &p_name, const String &p_path) {
  242. loaded = false;
  243. gdobject_class_cache_updated = false;
  244. name = p_name;
  245. path = p_path;
  246. modified_time = 0;
  247. assembly = NULL;
  248. image = NULL;
  249. }
  250. GDMonoAssembly::~GDMonoAssembly() {
  251. if (loaded)
  252. unload();
  253. }