gd_mono_class.cpp 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550
  1. /*************************************************************************/
  2. /* gd_mono_class.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_class.h"
  31. #include <mono/metadata/attrdefs.h>
  32. #include "gd_mono_assembly.h"
  33. #include "gd_mono_marshal.h"
  34. String GDMonoClass::get_full_name(MonoClass *p_mono_class) {
  35. // mono_type_get_full_name is not exposed to embedders, but this seems to do the job
  36. MonoReflectionType *type_obj = mono_type_get_object(mono_domain_get(), get_mono_type(p_mono_class));
  37. MonoException *exc = NULL;
  38. MonoString *str = GDMonoUtils::object_to_string((MonoObject *)type_obj, &exc);
  39. UNLIKELY_UNHANDLED_EXCEPTION(exc);
  40. return GDMonoMarshal::mono_string_to_godot(str);
  41. }
  42. MonoType *GDMonoClass::get_mono_type(MonoClass *p_mono_class) {
  43. return mono_class_get_type(p_mono_class);
  44. }
  45. String GDMonoClass::get_full_name() const {
  46. return get_full_name(mono_class);
  47. }
  48. MonoType *GDMonoClass::get_mono_type() {
  49. // Careful, you cannot compare two MonoType*.
  50. // There is mono_metadata_type_equal, how is this different from comparing two MonoClass*?
  51. return get_mono_type(mono_class);
  52. }
  53. uint32_t GDMonoClass::get_flags() const {
  54. return mono_class_get_flags(mono_class);
  55. }
  56. bool GDMonoClass::is_static() const {
  57. uint32_t static_class_flags = MONO_TYPE_ATTR_ABSTRACT | MONO_TYPE_ATTR_SEALED;
  58. return (get_flags() & static_class_flags) == static_class_flags;
  59. }
  60. bool GDMonoClass::is_assignable_from(GDMonoClass *p_from) const {
  61. return mono_class_is_assignable_from(mono_class, p_from->mono_class);
  62. }
  63. GDMonoClass *GDMonoClass::get_parent_class() {
  64. if (assembly) {
  65. MonoClass *parent_mono_class = mono_class_get_parent(mono_class);
  66. if (parent_mono_class) {
  67. return GDMono::get_singleton()->get_class(parent_mono_class);
  68. }
  69. }
  70. return NULL;
  71. }
  72. #ifdef TOOLS_ENABLED
  73. Vector<MonoClassField *> GDMonoClass::get_enum_fields() {
  74. bool class_is_enum = mono_class_is_enum(mono_class);
  75. ERR_FAIL_COND_V(!class_is_enum, Vector<MonoClassField *>());
  76. Vector<MonoClassField *> enum_fields;
  77. void *iter = NULL;
  78. MonoClassField *raw_field = NULL;
  79. while ((raw_field = mono_class_get_fields(get_mono_ptr(), &iter)) != NULL) {
  80. uint32_t field_flags = mono_field_get_flags(raw_field);
  81. // Enums have an instance field named value__ which holds the value of the enum.
  82. // Enum constants are static, so we will use this to ignore the value__ field.
  83. if (field_flags & MONO_FIELD_ATTR_PUBLIC && field_flags & MONO_FIELD_ATTR_STATIC) {
  84. enum_fields.push_back(raw_field);
  85. }
  86. }
  87. return enum_fields;
  88. }
  89. #endif
  90. bool GDMonoClass::has_attribute(GDMonoClass *p_attr_class) {
  91. #ifdef DEBUG_ENABLED
  92. ERR_FAIL_NULL_V(p_attr_class, false);
  93. #endif
  94. if (!attrs_fetched)
  95. fetch_attributes();
  96. if (!attributes)
  97. return false;
  98. return mono_custom_attrs_has_attr(attributes, p_attr_class->get_mono_ptr());
  99. }
  100. MonoObject *GDMonoClass::get_attribute(GDMonoClass *p_attr_class) {
  101. #ifdef DEBUG_ENABLED
  102. ERR_FAIL_NULL_V(p_attr_class, NULL);
  103. #endif
  104. if (!attrs_fetched)
  105. fetch_attributes();
  106. if (!attributes)
  107. return NULL;
  108. return mono_custom_attrs_get_attr(attributes, p_attr_class->get_mono_ptr());
  109. }
  110. void GDMonoClass::fetch_attributes() {
  111. ERR_FAIL_COND(attributes != NULL);
  112. attributes = mono_custom_attrs_from_class(get_mono_ptr());
  113. attrs_fetched = true;
  114. }
  115. void GDMonoClass::fetch_methods_with_godot_api_checks(GDMonoClass *p_native_base) {
  116. CRASH_COND(!CACHED_CLASS(GodotObject)->is_assignable_from(this));
  117. if (methods_fetched)
  118. return;
  119. void *iter = NULL;
  120. MonoMethod *raw_method = NULL;
  121. while ((raw_method = mono_class_get_methods(get_mono_ptr(), &iter)) != NULL) {
  122. StringName name = mono_method_get_name(raw_method);
  123. // get_method implicitly fetches methods and adds them to this->methods
  124. GDMonoMethod *method = get_method(raw_method, name);
  125. ERR_CONTINUE(!method);
  126. if (method->get_name() != name) {
  127. #ifdef DEBUG_ENABLED
  128. String fullname = method->get_ret_type_full_name() + " " + name + "(" + method->get_signature_desc(true) + ")";
  129. WARN_PRINTS("Method `" + fullname + "` is hidden by Godot API method. Should be `" +
  130. method->get_full_name_no_class() + "`. In class `" + namespace_name + "." + class_name + "`.");
  131. #endif
  132. continue;
  133. }
  134. #ifdef DEBUG_ENABLED
  135. // For debug builds, we also fetched from native base classes as well before if this is not a native base class.
  136. // This allows us to warn the user here if he is using snake_case by mistake.
  137. if (p_native_base != this) {
  138. GDMonoClass *native_top = p_native_base;
  139. while (native_top) {
  140. GDMonoMethod *m = native_top->get_method(name, method->get_parameters_count());
  141. if (m && m->get_name() != name) {
  142. // found
  143. String fullname = m->get_ret_type_full_name() + " " + name + "(" + m->get_signature_desc(true) + ")";
  144. WARN_PRINTS("Method `" + fullname + "` should be `" + m->get_full_name_no_class() +
  145. "`. In class `" + namespace_name + "." + class_name + "`.");
  146. break;
  147. }
  148. if (native_top == CACHED_CLASS(GodotObject))
  149. break;
  150. native_top = native_top->get_parent_class();
  151. }
  152. }
  153. #endif
  154. uint32_t flags = mono_method_get_flags(method->mono_method, NULL);
  155. if (!(flags & MONO_METHOD_ATTR_VIRTUAL))
  156. continue;
  157. // Virtual method of Godot Object derived type, let's try to find GodotMethod attribute
  158. GDMonoClass *top = p_native_base;
  159. while (top) {
  160. GDMonoMethod *base_method = top->get_method(name, method->get_parameters_count());
  161. if (base_method && base_method->has_attribute(CACHED_CLASS(GodotMethodAttribute))) {
  162. // Found base method with GodotMethod attribute.
  163. // We get the original API method name from this attribute.
  164. // This name must point to the virtual method.
  165. MonoObject *attr = base_method->get_attribute(CACHED_CLASS(GodotMethodAttribute));
  166. StringName godot_method_name = CACHED_FIELD(GodotMethodAttribute, methodName)->get_string_value(attr);
  167. #ifdef DEBUG_ENABLED
  168. CRASH_COND(godot_method_name == StringName());
  169. #endif
  170. MethodKey key = MethodKey(godot_method_name, method->get_parameters_count());
  171. GDMonoMethod **existing_method = methods.getptr(key);
  172. if (existing_method)
  173. memdelete(*existing_method); // Must delete old one
  174. methods.set(key, method);
  175. break;
  176. }
  177. if (top == CACHED_CLASS(GodotObject))
  178. break;
  179. top = top->get_parent_class();
  180. }
  181. }
  182. methods_fetched = true;
  183. }
  184. GDMonoMethod *GDMonoClass::get_fetched_method_unknown_params(const StringName &p_name) {
  185. ERR_FAIL_COND_V(!methods_fetched, NULL);
  186. const MethodKey *k = NULL;
  187. while ((k = methods.next(k))) {
  188. if (k->name == p_name)
  189. return methods.get(*k);
  190. }
  191. return NULL;
  192. }
  193. bool GDMonoClass::has_fetched_method_unknown_params(const StringName &p_name) {
  194. return get_fetched_method_unknown_params(p_name) != NULL;
  195. }
  196. bool GDMonoClass::implements_interface(GDMonoClass *p_interface) {
  197. return mono_class_implements_interface(mono_class, p_interface->get_mono_ptr());
  198. }
  199. GDMonoMethod *GDMonoClass::get_method(const StringName &p_name, int p_params_count) {
  200. MethodKey key = MethodKey(p_name, p_params_count);
  201. GDMonoMethod **match = methods.getptr(key);
  202. if (match)
  203. return *match;
  204. if (methods_fetched)
  205. return NULL;
  206. MonoMethod *raw_method = mono_class_get_method_from_name(mono_class, String(p_name).utf8().get_data(), p_params_count);
  207. if (raw_method) {
  208. GDMonoMethod *method = memnew(GDMonoMethod(p_name, raw_method));
  209. methods.set(key, method);
  210. return method;
  211. }
  212. return NULL;
  213. }
  214. GDMonoMethod *GDMonoClass::get_method(MonoMethod *p_raw_method) {
  215. MonoMethodSignature *sig = mono_method_signature(p_raw_method);
  216. int params_count = mono_signature_get_param_count(sig);
  217. StringName method_name = mono_method_get_name(p_raw_method);
  218. return get_method(p_raw_method, method_name, params_count);
  219. }
  220. GDMonoMethod *GDMonoClass::get_method(MonoMethod *p_raw_method, const StringName &p_name) {
  221. MonoMethodSignature *sig = mono_method_signature(p_raw_method);
  222. int params_count = mono_signature_get_param_count(sig);
  223. return get_method(p_raw_method, p_name, params_count);
  224. }
  225. GDMonoMethod *GDMonoClass::get_method(MonoMethod *p_raw_method, const StringName &p_name, int p_params_count) {
  226. ERR_FAIL_NULL_V(p_raw_method, NULL);
  227. MethodKey key = MethodKey(p_name, p_params_count);
  228. GDMonoMethod **match = methods.getptr(key);
  229. if (match)
  230. return *match;
  231. GDMonoMethod *method = memnew(GDMonoMethod(p_name, p_raw_method));
  232. methods.set(key, method);
  233. return method;
  234. }
  235. GDMonoMethod *GDMonoClass::get_method_with_desc(const String &p_description, bool p_include_namespace) {
  236. MonoMethodDesc *desc = mono_method_desc_new(p_description.utf8().get_data(), p_include_namespace);
  237. MonoMethod *method = mono_method_desc_search_in_class(desc, mono_class);
  238. mono_method_desc_free(desc);
  239. ERR_FAIL_COND_V(mono_method_get_class(method) != mono_class, NULL);
  240. return get_method(method);
  241. }
  242. void *GDMonoClass::get_method_thunk(const StringName &p_name, int p_params_count) {
  243. GDMonoMethod *method = get_method(p_name, p_params_count);
  244. return method ? method->get_thunk() : NULL;
  245. }
  246. GDMonoField *GDMonoClass::get_field(const StringName &p_name) {
  247. Map<StringName, GDMonoField *>::Element *result = fields.find(p_name);
  248. if (result)
  249. return result->value();
  250. if (fields_fetched)
  251. return NULL;
  252. MonoClassField *raw_field = mono_class_get_field_from_name(mono_class, String(p_name).utf8().get_data());
  253. if (raw_field) {
  254. GDMonoField *field = memnew(GDMonoField(raw_field, this));
  255. fields.insert(p_name, field);
  256. return field;
  257. }
  258. return NULL;
  259. }
  260. const Vector<GDMonoField *> &GDMonoClass::get_all_fields() {
  261. if (fields_fetched)
  262. return fields_list;
  263. void *iter = NULL;
  264. MonoClassField *raw_field = NULL;
  265. while ((raw_field = mono_class_get_fields(mono_class, &iter)) != NULL) {
  266. StringName name = mono_field_get_name(raw_field);
  267. Map<StringName, GDMonoField *>::Element *match = fields.find(name);
  268. if (match) {
  269. fields_list.push_back(match->get());
  270. } else {
  271. GDMonoField *field = memnew(GDMonoField(raw_field, this));
  272. fields.insert(name, field);
  273. fields_list.push_back(field);
  274. }
  275. }
  276. fields_fetched = true;
  277. return fields_list;
  278. }
  279. GDMonoProperty *GDMonoClass::get_property(const StringName &p_name) {
  280. Map<StringName, GDMonoProperty *>::Element *result = properties.find(p_name);
  281. if (result)
  282. return result->value();
  283. if (properties_fetched)
  284. return NULL;
  285. MonoProperty *raw_property = mono_class_get_property_from_name(mono_class, String(p_name).utf8().get_data());
  286. if (raw_property) {
  287. GDMonoProperty *property = memnew(GDMonoProperty(raw_property, this));
  288. properties.insert(p_name, property);
  289. return property;
  290. }
  291. return NULL;
  292. }
  293. const Vector<GDMonoProperty *> &GDMonoClass::get_all_properties() {
  294. if (properties_fetched)
  295. return properties_list;
  296. void *iter = NULL;
  297. MonoProperty *raw_property = NULL;
  298. while ((raw_property = mono_class_get_properties(mono_class, &iter)) != NULL) {
  299. StringName name = mono_property_get_name(raw_property);
  300. Map<StringName, GDMonoProperty *>::Element *match = properties.find(name);
  301. if (match) {
  302. properties_list.push_back(match->get());
  303. } else {
  304. GDMonoProperty *property = memnew(GDMonoProperty(raw_property, this));
  305. properties.insert(name, property);
  306. properties_list.push_back(property);
  307. }
  308. }
  309. properties_fetched = true;
  310. return properties_list;
  311. }
  312. const Vector<GDMonoClass *> &GDMonoClass::get_all_delegates() {
  313. if (delegates_fetched)
  314. return delegates_list;
  315. void *iter = NULL;
  316. MonoClass *raw_class = NULL;
  317. while ((raw_class = mono_class_get_nested_types(mono_class, &iter)) != NULL) {
  318. if (mono_class_is_delegate(raw_class)) {
  319. StringName name = mono_class_get_name(raw_class);
  320. Map<StringName, GDMonoClass *>::Element *match = delegates.find(name);
  321. if (match) {
  322. delegates_list.push_back(match->get());
  323. } else {
  324. GDMonoClass *delegate = memnew(GDMonoClass(mono_class_get_namespace(raw_class), mono_class_get_name(raw_class), raw_class, assembly));
  325. delegates.insert(name, delegate);
  326. delegates_list.push_back(delegate);
  327. }
  328. }
  329. }
  330. delegates_fetched = true;
  331. return delegates_list;
  332. }
  333. const Vector<GDMonoMethod *> &GDMonoClass::get_all_methods() {
  334. if (!method_list_fetched) {
  335. void *iter = NULL;
  336. MonoMethod *raw_method = NULL;
  337. while ((raw_method = mono_class_get_methods(get_mono_ptr(), &iter)) != NULL) {
  338. method_list.push_back(memnew(GDMonoMethod(mono_method_get_name(raw_method), raw_method)));
  339. }
  340. method_list_fetched = true;
  341. }
  342. return method_list;
  343. }
  344. GDMonoClass::GDMonoClass(const StringName &p_namespace, const StringName &p_name, MonoClass *p_class, GDMonoAssembly *p_assembly) {
  345. namespace_name = p_namespace;
  346. class_name = p_name;
  347. mono_class = p_class;
  348. assembly = p_assembly;
  349. attrs_fetched = false;
  350. attributes = NULL;
  351. methods_fetched = false;
  352. method_list_fetched = false;
  353. fields_fetched = false;
  354. properties_fetched = false;
  355. delegates_fetched = false;
  356. }
  357. GDMonoClass::~GDMonoClass() {
  358. if (attributes) {
  359. mono_custom_attrs_free(attributes);
  360. }
  361. for (Map<StringName, GDMonoField *>::Element *E = fields.front(); E; E = E->next()) {
  362. memdelete(E->value());
  363. }
  364. for (Map<StringName, GDMonoProperty *>::Element *E = properties.front(); E; E = E->next()) {
  365. memdelete(E->value());
  366. }
  367. {
  368. // Ugly workaround...
  369. // We may have duplicated values, because we redirect snake_case methods to PascalCasel (only Godot API methods).
  370. // This way, we end with both the snake_case name and the PascalCasel name paired with the same method.
  371. // Therefore, we must avoid deleting the same pointer twice.
  372. int offset = 0;
  373. Vector<GDMonoMethod *> deleted_methods;
  374. deleted_methods.resize(methods.size());
  375. const MethodKey *k = NULL;
  376. while ((k = methods.next(k))) {
  377. GDMonoMethod *method = methods.get(*k);
  378. if (method) {
  379. for (int i = 0; i < offset; i++) {
  380. if (deleted_methods[i] == method) {
  381. // Already deleted
  382. goto already_deleted;
  383. }
  384. }
  385. deleted_methods.write[offset] = method;
  386. ++offset;
  387. memdelete(method);
  388. }
  389. already_deleted:;
  390. }
  391. methods.clear();
  392. }
  393. for (int i = 0; i < method_list.size(); ++i) {
  394. memdelete(method_list[i]);
  395. }
  396. }