summaryrefslogtreecommitdiff
path: root/modules/mono/mono_gd
diff options
context:
space:
mode:
Diffstat (limited to 'modules/mono/mono_gd')
-rw-r--r--modules/mono/mono_gd/gd_mono.cpp766
-rw-r--r--modules/mono/mono_gd/gd_mono.h226
-rw-r--r--modules/mono/mono_gd/gd_mono_assembly.cpp356
-rw-r--r--modules/mono/mono_gd/gd_mono_assembly.h121
-rw-r--r--modules/mono/mono_gd/gd_mono_class.cpp381
-rw-r--r--modules/mono/mono_gd/gd_mono_class.h124
-rw-r--r--modules/mono/mono_gd/gd_mono_field.cpp362
-rw-r--r--modules/mono/mono_gd/gd_mono_field.h74
-rw-r--r--modules/mono/mono_gd/gd_mono_header.h59
-rw-r--r--modules/mono/mono_gd/gd_mono_internals.cpp66
-rw-r--r--modules/mono/mono_gd/gd_mono_internals.h42
-rw-r--r--modules/mono/mono_gd/gd_mono_log.cpp175
-rw-r--r--modules/mono/mono_gd/gd_mono_log.h61
-rw-r--r--modules/mono/mono_gd/gd_mono_marshal.cpp845
-rw-r--r--modules/mono/mono_gd/gd_mono_marshal.h218
-rw-r--r--modules/mono/mono_gd/gd_mono_method.cpp192
-rw-r--r--modules/mono/mono_gd/gd_mono_method.h81
-rw-r--r--modules/mono/mono_gd/gd_mono_utils.cpp367
-rw-r--r--modules/mono/mono_gd/gd_mono_utils.h182
19 files changed, 4698 insertions, 0 deletions
diff --git a/modules/mono/mono_gd/gd_mono.cpp b/modules/mono/mono_gd/gd_mono.cpp
new file mode 100644
index 0000000000..2c88832998
--- /dev/null
+++ b/modules/mono/mono_gd/gd_mono.cpp
@@ -0,0 +1,766 @@
+/*************************************************************************/
+/* gd_mono.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+#include "gd_mono.h"
+
+#include <mono/metadata/mono-config.h>
+#include <mono/metadata/mono-debug.h>
+#include <mono/metadata/mono-gc.h>
+
+#include "os/dir_access.h"
+#include "os/file_access.h"
+#include "os/os.h"
+#include "os/thread.h"
+#include "project_settings.h"
+
+#include "../csharp_script.h"
+#include "../utils/path_utils.h"
+#include "gd_mono_utils.h"
+
+#ifdef TOOLS_ENABLED
+#include "../editor/godotsharp_editor.h"
+#endif
+
+#ifdef MONO_PRINT_HANDLER_ENABLED
+void gdmono_MonoPrintCallback(const char *string, mono_bool is_stdout) {
+
+ if (is_stdout) {
+ OS::get_singleton()->print(string);
+ } else {
+ OS::get_singleton()->printerr(string);
+ }
+}
+#endif
+
+GDMono *GDMono::singleton = NULL;
+
+#ifdef DEBUG_ENABLED
+static bool _wait_for_debugger_msecs(uint32_t p_msecs) {
+
+ do {
+ if (mono_is_debugger_attached())
+ return true;
+
+ int last_tick = OS::get_singleton()->get_ticks_msec();
+
+ OS::get_singleton()->delay_usec((p_msecs < 25 ? p_msecs : 25) * 1000);
+
+ int tdiff = OS::get_singleton()->get_ticks_msec() - last_tick;
+
+ if (tdiff > p_msecs) {
+ p_msecs = 0;
+ } else {
+ p_msecs -= tdiff;
+ }
+ } while (p_msecs > 0);
+
+ return mono_is_debugger_attached();
+}
+#endif
+
+#ifdef TOOLS_ENABLED
+// temporary workaround. should be provided from Main::setup/setup2 instead
+bool _is_project_manager_requested() {
+
+ List<String> cmdline_args = OS::get_singleton()->get_cmdline_args();
+ for (List<String>::Element *E = cmdline_args.front(); E; E = E->next()) {
+ const String &arg = E->get();
+ if (arg == "-p" || arg == "--project-manager")
+ return true;
+ }
+
+ return false;
+}
+#endif
+
+#ifdef DEBUG_ENABLED
+void gdmono_debug_init() {
+
+ mono_debug_init(MONO_DEBUG_FORMAT_MONO);
+
+ int da_port = GLOBAL_DEF("mono/debugger_agent/port", 23685);
+ bool da_suspend = GLOBAL_DEF("mono/debugger_agent/wait_for_debugger", false);
+ int da_timeout = GLOBAL_DEF("mono/debugger_agent/wait_timeout", 3000);
+
+#ifdef TOOLS_ENABLED
+ if (Engine::get_singleton()->is_editor_hint() ||
+ ProjectSettings::get_singleton()->get_resource_path().empty() ||
+ _is_project_manager_requested()) {
+ return;
+ }
+#endif
+
+ CharString da_args = String("--debugger-agent=transport=dt_socket,address=127.0.0.1:" + itos(da_port) +
+ ",embedding=1,server=y,suspend=" + (da_suspend ? "y,timeout=" + itos(da_timeout) : "n"))
+ .utf8();
+ // --debugger-agent=help
+ const char *options[] = {
+ "--soft-breakpoints",
+ da_args.get_data()
+ };
+ mono_jit_parse_options(2, (char **)options);
+}
+#endif
+
+void GDMono::initialize() {
+
+ ERR_FAIL_NULL(Engine::get_singleton());
+
+ OS::get_singleton()->print("Initializing mono...\n");
+
+#ifdef DEBUG_METHODS_ENABLED
+ _initialize_and_check_api_hashes();
+#endif
+
+ GDMonoLog::get_singleton()->initialize();
+
+#ifdef MONO_PRINT_HANDLER_ENABLED
+ mono_trace_set_print_handler(gdmono_MonoPrintCallback);
+ mono_trace_set_printerr_handler(gdmono_MonoPrintCallback);
+#endif
+
+#ifdef WINDOWS_ENABLED
+ mono_reg_info = MonoRegUtils::find_mono();
+
+ CharString assembly_dir;
+ CharString config_dir;
+
+ if (mono_reg_info.assembly_dir.length() && DirAccess::exists(mono_reg_info.assembly_dir)) {
+ assembly_dir = mono_reg_info.assembly_dir.utf8();
+ }
+
+ if (mono_reg_info.config_dir.length() && DirAccess::exists(mono_reg_info.config_dir)) {
+ config_dir = mono_reg_info.config_dir.utf8();
+ }
+
+ mono_set_dirs(assembly_dir.length() ? assembly_dir.get_data() : NULL,
+ config_dir.length() ? config_dir.get_data() : NULL);
+#else
+ mono_set_dirs(NULL, NULL);
+#endif
+
+ GDMonoAssembly::initialize();
+
+#ifdef DEBUG_ENABLED
+ gdmono_debug_init();
+#endif
+
+ mono_config_parse(NULL);
+
+ root_domain = mono_jit_init_version("GodotEngine.RootDomain", "v4.0.30319");
+
+ ERR_EXPLAIN("Mono: Failed to initialize runtime");
+ ERR_FAIL_NULL(root_domain);
+
+ GDMonoUtils::set_main_thread(GDMonoUtils::get_current_thread());
+
+ runtime_initialized = true;
+
+ OS::get_singleton()->print("Mono: Runtime initialized\n");
+
+ // mscorlib assembly MUST be present at initialization
+ ERR_EXPLAIN("Mono: Failed to load mscorlib assembly");
+ ERR_FAIL_COND(!_load_corlib_assembly());
+
+#ifdef TOOLS_ENABLED
+ // The tools domain must be loaded here, before the scripts domain.
+ // Otherwise domain unload on the scripts domain will hang indefinitely.
+
+ ERR_EXPLAIN("Mono: Failed to load tools domain");
+ ERR_FAIL_COND(_load_tools_domain() != OK);
+
+ // TODO move to editor init callback, and do it lazily when required before editor init (e.g.: bindings generation)
+ ERR_EXPLAIN("Mono: Failed to load Editor Tools assembly");
+ ERR_FAIL_COND(!_load_editor_tools_assembly());
+#endif
+
+ ERR_EXPLAIN("Mono: Failed to load scripts domain");
+ ERR_FAIL_COND(_load_scripts_domain() != OK);
+
+#ifdef DEBUG_ENABLED
+ bool debugger_attached = _wait_for_debugger_msecs(500);
+ if (!debugger_attached && OS::get_singleton()->is_stdout_verbose())
+ OS::get_singleton()->printerr("Mono: Debugger wait timeout\n");
+#endif
+
+ _register_internal_calls();
+
+ // The following assemblies are not required at initialization
+ _load_all_script_assemblies();
+
+ OS::get_singleton()->print("Mono: EVERYTHING OK\n");
+}
+
+#ifndef MONO_GLUE_DISABLED
+namespace GodotSharpBindings {
+
+uint64_t get_core_api_hash();
+uint64_t get_editor_api_hash();
+
+void register_generated_icalls();
+} // namespace GodotSharpBindings
+#endif
+
+void GDMono::_register_internal_calls() {
+#ifndef MONO_GLUE_DISABLED
+ GodotSharpBindings::register_generated_icalls();
+#endif
+
+#ifdef TOOLS_ENABLED
+ GodotSharpBuilds::_register_internal_calls();
+#endif
+}
+
+#ifdef DEBUG_METHODS_ENABLED
+void GDMono::_initialize_and_check_api_hashes() {
+
+ api_core_hash = ClassDB::get_api_hash(ClassDB::API_CORE);
+
+#ifndef MONO_GLUE_DISABLED
+ if (api_core_hash != GodotSharpBindings::get_core_api_hash()) {
+ ERR_PRINT("Mono: Core API hash mismatch!");
+ }
+#endif
+
+#ifdef TOOLS_ENABLED
+ api_editor_hash = ClassDB::get_api_hash(ClassDB::API_EDITOR);
+
+#ifndef MONO_GLUE_DISABLED
+ if (api_editor_hash != GodotSharpBindings::get_editor_api_hash()) {
+ ERR_PRINT("Mono: Editor API hash mismatch!");
+ }
+#endif
+
+#endif // TOOLS_ENABLED
+}
+#endif // DEBUG_METHODS_ENABLED
+
+void GDMono::add_assembly(uint32_t p_domain_id, GDMonoAssembly *p_assembly) {
+
+ assemblies[p_domain_id][p_assembly->get_name()] = p_assembly;
+}
+
+GDMonoAssembly **GDMono::get_loaded_assembly(const String &p_name) {
+
+ MonoDomain *domain = mono_domain_get();
+ uint32_t domain_id = domain ? mono_domain_get_id(domain) : 0;
+ return assemblies[domain_id].getptr(p_name);
+}
+
+bool GDMono::_load_assembly(const String &p_name, GDMonoAssembly **r_assembly) {
+
+ CRASH_COND(!r_assembly);
+
+ if (OS::get_singleton()->is_stdout_verbose())
+ OS::get_singleton()->print((String() + "Mono: Loading assembly " + p_name + "...\n").utf8());
+
+ MonoImageOpenStatus status = MONO_IMAGE_OK;
+ MonoAssemblyName *aname = mono_assembly_name_new(p_name.utf8());
+ MonoAssembly *assembly = mono_assembly_load_full(aname, NULL, &status, false);
+ mono_assembly_name_free(aname);
+
+ ERR_FAIL_NULL_V(assembly, false);
+
+ uint32_t domain_id = mono_domain_get_id(mono_domain_get());
+
+ GDMonoAssembly **stored_assembly = assemblies[domain_id].getptr(p_name);
+
+ ERR_FAIL_COND_V(status != MONO_IMAGE_OK, false);
+ ERR_FAIL_COND_V(stored_assembly == NULL, false);
+
+ ERR_FAIL_COND_V((*stored_assembly)->get_assembly() != assembly, false);
+ *r_assembly = *stored_assembly;
+
+ if (OS::get_singleton()->is_stdout_verbose())
+ OS::get_singleton()->print(String("Mono: Assembly " + p_name + " loaded from path: " + (*r_assembly)->get_path() + "\n").utf8());
+
+ return true;
+}
+
+bool GDMono::_load_corlib_assembly() {
+
+ if (corlib_assembly)
+ return true;
+
+ bool success = _load_assembly("mscorlib", &corlib_assembly);
+
+ if (success)
+ GDMonoUtils::update_corlib_cache();
+
+ return success;
+}
+
+bool GDMono::_load_core_api_assembly() {
+
+ if (api_assembly)
+ return true;
+
+ bool success = _load_assembly(API_ASSEMBLY_NAME, &api_assembly);
+
+ if (success)
+ GDMonoUtils::update_godot_api_cache();
+
+ return success;
+}
+
+#ifdef TOOLS_ENABLED
+bool GDMono::_load_editor_api_assembly() {
+
+ if (editor_api_assembly)
+ return true;
+
+ return _load_assembly(EDITOR_API_ASSEMBLY_NAME, &editor_api_assembly);
+}
+#endif
+
+#ifdef TOOLS_ENABLED
+bool GDMono::_load_editor_tools_assembly() {
+
+ if (editor_tools_assembly)
+ return true;
+
+ _GDMONO_SCOPE_DOMAIN_(tools_domain)
+
+ return _load_assembly(EDITOR_TOOLS_ASSEMBLY_NAME, &editor_tools_assembly);
+}
+#endif
+
+bool GDMono::_load_project_assembly() {
+
+ if (project_assembly)
+ return true;
+
+ String project_assembly_name = ProjectSettings::get_singleton()->get("application/config/name");
+
+ bool success = _load_assembly(project_assembly_name, &project_assembly);
+
+ if (success)
+ mono_assembly_set_main(project_assembly->get_assembly());
+
+ return success;
+}
+
+bool GDMono::_load_all_script_assemblies() {
+
+#ifndef MONO_GLUE_DISABLED
+ if (!_load_core_api_assembly()) {
+ if (OS::get_singleton()->is_stdout_verbose())
+ OS::get_singleton()->printerr("Mono: Failed to load Core API assembly\n");
+ return false;
+ } else {
+#ifdef TOOLS_ENABLED
+ if (!_load_editor_api_assembly()) {
+ if (OS::get_singleton()->is_stdout_verbose())
+ OS::get_singleton()->printerr("Mono: Failed to load Editor API assembly\n");
+ return false;
+ }
+#endif
+ }
+
+ if (!_load_project_assembly()) {
+ if (OS::get_singleton()->is_stdout_verbose())
+ OS::get_singleton()->printerr("Mono: Failed to load project assembly\n");
+ return false;
+ }
+
+ return true;
+#else
+ if (OS::get_singleton()->is_stdout_verbose())
+ OS::get_singleton()->print("Mono: Glue disbled, ignoring script assemblies\n");
+
+ return true;
+#endif
+}
+
+Error GDMono::_load_scripts_domain() {
+
+ ERR_FAIL_COND_V(scripts_domain != NULL, ERR_BUG);
+
+ if (OS::get_singleton()->is_stdout_verbose()) {
+ OS::get_singleton()->print("Mono: Loading scripts domain...\n");
+ }
+
+ scripts_domain = GDMonoUtils::create_domain("GodotEngine.ScriptsDomain");
+
+ ERR_EXPLAIN("Mono: Could not create scripts app domain");
+ ERR_FAIL_NULL_V(scripts_domain, ERR_CANT_CREATE);
+
+ mono_domain_set(scripts_domain, true);
+
+ return OK;
+}
+
+Error GDMono::_unload_scripts_domain() {
+
+ ERR_FAIL_NULL_V(scripts_domain, ERR_BUG);
+
+ if (OS::get_singleton()->is_stdout_verbose()) {
+ OS::get_singleton()->print("Mono: Unloading scripts domain...\n");
+ }
+
+ _GodotSharp::get_singleton()->_dispose_callback();
+
+ if (mono_domain_get() != root_domain)
+ mono_domain_set(root_domain, true);
+
+ mono_gc_collect(mono_gc_max_generation());
+
+ finalizing_scripts_domain = true;
+ mono_domain_finalize(scripts_domain, 2000);
+ finalizing_scripts_domain = false;
+
+ mono_gc_collect(mono_gc_max_generation());
+
+ _domain_assemblies_cleanup(mono_domain_get_id(scripts_domain));
+
+ api_assembly = NULL;
+ project_assembly = NULL;
+#ifdef TOOLS_ENABLED
+ editor_api_assembly = NULL;
+#endif
+
+ MonoDomain *domain = scripts_domain;
+ scripts_domain = NULL;
+
+ _GodotSharp::get_singleton()->_dispose_callback();
+
+ MonoObject *ex = NULL;
+ mono_domain_try_unload(domain, &ex);
+
+ if (ex) {
+ ERR_PRINT("Exception thrown when unloading scripts domain:");
+ mono_print_unhandled_exception(ex);
+ return FAILED;
+ }
+
+ return OK;
+}
+
+#ifdef TOOLS_ENABLED
+Error GDMono::_load_tools_domain() {
+
+ ERR_FAIL_COND_V(tools_domain != NULL, ERR_BUG);
+
+ if (OS::get_singleton()->is_stdout_verbose()) {
+ OS::get_singleton()->print("Mono: Loading tools domain...\n");
+ }
+
+ tools_domain = GDMonoUtils::create_domain("GodotEngine.ToolsDomain");
+
+ ERR_EXPLAIN("Mono: Could not create tools app domain");
+ ERR_FAIL_NULL_V(tools_domain, ERR_CANT_CREATE);
+
+ return OK;
+}
+#endif
+
+#ifdef TOOLS_ENABLED
+Error GDMono::reload_scripts_domain() {
+
+ ERR_FAIL_COND_V(!runtime_initialized, ERR_BUG);
+
+ if (scripts_domain) {
+ Error err = _unload_scripts_domain();
+ if (err != OK) {
+ ERR_PRINT("Mono: Failed to unload scripts domain");
+ return err;
+ }
+ }
+
+ Error err = _load_scripts_domain();
+ if (err != OK) {
+ ERR_PRINT("Mono: Failed to load scripts domain");
+ return err;
+ }
+
+ if (!_load_all_script_assemblies()) {
+ if (OS::get_singleton()->is_stdout_verbose())
+ OS::get_singleton()->printerr("Mono: Failed to load script assemblies\n");
+ return ERR_CANT_OPEN;
+ }
+
+ return OK;
+}
+#endif
+
+GDMonoClass *GDMono::get_class(MonoClass *p_raw_class) {
+
+ MonoImage *image = mono_class_get_image(p_raw_class);
+
+ if (image == corlib_assembly->get_image())
+ return corlib_assembly->get_class(p_raw_class);
+
+ uint32_t domain_id = mono_domain_get_id(mono_domain_get());
+ HashMap<String, GDMonoAssembly *> &domain_assemblies = assemblies[domain_id];
+
+ const String *k = NULL;
+ while ((k = domain_assemblies.next(k))) {
+ GDMonoAssembly *assembly = domain_assemblies.get(*k);
+ if (assembly->get_image() == image) {
+ GDMonoClass *klass = assembly->get_class(p_raw_class);
+
+ if (klass)
+ return klass;
+ }
+ }
+
+ return NULL;
+}
+
+void GDMono::_domain_assemblies_cleanup(uint32_t p_domain_id) {
+
+ HashMap<String, GDMonoAssembly *> &domain_assemblies = assemblies[p_domain_id];
+
+ const String *k = NULL;
+ while ((k = domain_assemblies.next(k))) {
+ memdelete(domain_assemblies.get(*k));
+ }
+
+ assemblies.erase(p_domain_id);
+}
+
+GDMono::GDMono() {
+
+ singleton = this;
+
+ gdmono_log = memnew(GDMonoLog);
+
+ runtime_initialized = false;
+ finalizing_scripts_domain = false;
+
+ root_domain = NULL;
+ scripts_domain = NULL;
+#ifdef TOOLS_ENABLED
+ tools_domain = NULL;
+#endif
+
+ corlib_assembly = NULL;
+ api_assembly = NULL;
+ project_assembly = NULL;
+#ifdef TOOLS_ENABLED
+ editor_api_assembly = NULL;
+ editor_tools_assembly = NULL;
+#endif
+
+#ifdef DEBUG_METHODS_ENABLED
+ api_core_hash = 0;
+#ifdef TOOLS_ENABLED
+ api_editor_hash = 0;
+#endif
+#endif
+}
+
+GDMono::~GDMono() {
+
+ if (runtime_initialized) {
+
+ if (scripts_domain) {
+
+ Error err = _unload_scripts_domain();
+ if (err != OK) {
+ WARN_PRINT("Mono: Failed to unload scripts domain");
+ }
+ }
+
+ const uint32_t *k = NULL;
+ while ((k = assemblies.next(k))) {
+ HashMap<String, GDMonoAssembly *> &domain_assemblies = assemblies.get(*k);
+
+ const String *kk = NULL;
+ while ((kk = domain_assemblies.next(kk))) {
+ memdelete(domain_assemblies.get(*kk));
+ }
+ }
+ assemblies.clear();
+
+ GDMonoUtils::clear_cache();
+
+ OS::get_singleton()->print("Mono: Runtime cleanup...\n");
+
+ runtime_initialized = false;
+ mono_jit_cleanup(root_domain);
+ }
+
+ if (gdmono_log)
+ memdelete(gdmono_log);
+}
+
+_GodotSharp *_GodotSharp::singleton = NULL;
+
+void _GodotSharp::_dispose_object(Object *p_object) {
+
+ if (p_object->get_script_instance()) {
+ CSharpInstance *cs_instance = CAST_CSHARP_INSTANCE(p_object->get_script_instance());
+ if (cs_instance) {
+ cs_instance->mono_object_disposed();
+ return;
+ }
+ }
+
+ // Unsafe refcount decrement. The managed instance also counts as a reference.
+ // See: CSharpLanguage::alloc_instance_binding_data(Object *p_object)
+ if (Object::cast_to<Reference>(p_object)->unreference()) {
+ memdelete(p_object);
+ }
+}
+
+void _GodotSharp::_dispose_callback() {
+
+#ifndef NO_THREADS
+ queue_mutex->lock();
+#endif
+
+ for (List<Object *>::Element *E = obj_delete_queue.front(); E; E = E->next()) {
+ _dispose_object(E->get());
+ }
+
+ for (List<NodePath *>::Element *E = np_delete_queue.front(); E; E = E->next()) {
+ memdelete(E->get());
+ }
+
+ for (List<RID *>::Element *E = rid_delete_queue.front(); E; E = E->next()) {
+ memdelete(E->get());
+ }
+
+ obj_delete_queue.clear();
+ np_delete_queue.clear();
+ rid_delete_queue.clear();
+ queue_empty = true;
+
+#ifndef NO_THREADS
+ queue_mutex->unlock();
+#endif
+}
+
+void _GodotSharp::attach_thread() {
+
+ GDMonoUtils::attach_current_thread();
+}
+
+void _GodotSharp::detach_thread() {
+
+ GDMonoUtils::detach_current_thread();
+}
+
+bool _GodotSharp::is_finalizing_domain() {
+
+ return GDMono::get_singleton()->is_finalizing_scripts_domain();
+}
+
+bool _GodotSharp::is_domain_loaded() {
+
+ return GDMono::get_singleton()->get_scripts_domain() != NULL;
+}
+
+#define ENQUEUE_FOR_DISPOSAL(m_queue, m_inst) \
+ m_queue.push_back(m_inst); \
+ if (queue_empty) { \
+ queue_empty = false; \
+ call_deferred("_dispose_callback"); \
+ }
+
+void _GodotSharp::queue_dispose(Object *p_object) {
+
+ if (Thread::get_main_id() == Thread::get_caller_id() && !GDMono::get_singleton()->is_finalizing_scripts_domain()) {
+ _dispose_object(p_object);
+ } else {
+#ifndef NO_THREADS
+ queue_mutex->lock();
+#endif
+
+ ENQUEUE_FOR_DISPOSAL(obj_delete_queue, p_object);
+
+#ifndef NO_THREADS
+ queue_mutex->unlock();
+#endif
+ }
+}
+
+void _GodotSharp::queue_dispose(NodePath *p_node_path) {
+
+ if (Thread::get_main_id() == Thread::get_caller_id() && !GDMono::get_singleton()->is_finalizing_scripts_domain()) {
+ memdelete(p_node_path);
+ } else {
+#ifndef NO_THREADS
+ queue_mutex->lock();
+#endif
+
+ ENQUEUE_FOR_DISPOSAL(np_delete_queue, p_node_path);
+
+#ifndef NO_THREADS
+ queue_mutex->unlock();
+#endif
+ }
+}
+
+void _GodotSharp::queue_dispose(RID *p_rid) {
+
+ if (Thread::get_main_id() == Thread::get_caller_id() && !GDMono::get_singleton()->is_finalizing_scripts_domain()) {
+ memdelete(p_rid);
+ } else {
+#ifndef NO_THREADS
+ queue_mutex->lock();
+#endif
+
+ ENQUEUE_FOR_DISPOSAL(rid_delete_queue, p_rid);
+
+#ifndef NO_THREADS
+ queue_mutex->unlock();
+#endif
+ }
+}
+
+void _GodotSharp::_bind_methods() {
+
+ ClassDB::bind_method(D_METHOD("attach_thread"), &_GodotSharp::attach_thread);
+ ClassDB::bind_method(D_METHOD("detach_thread"), &_GodotSharp::detach_thread);
+
+ ClassDB::bind_method(D_METHOD("is_finalizing_domain"), &_GodotSharp::is_finalizing_domain);
+ ClassDB::bind_method(D_METHOD("is_domain_loaded"), &_GodotSharp::is_domain_loaded);
+
+ ClassDB::bind_method(D_METHOD("_dispose_callback"), &_GodotSharp::_dispose_callback);
+}
+
+_GodotSharp::_GodotSharp() {
+
+ singleton = this;
+ queue_empty = true;
+#ifndef NO_THREADS
+ queue_mutex = Mutex::create();
+#endif
+}
+
+_GodotSharp::~_GodotSharp() {
+
+ singleton = NULL;
+
+ if (queue_mutex) {
+ memdelete(queue_mutex);
+ }
+}
diff --git a/modules/mono/mono_gd/gd_mono.h b/modules/mono/mono_gd/gd_mono.h
new file mode 100644
index 0000000000..b188c0730a
--- /dev/null
+++ b/modules/mono/mono_gd/gd_mono.h
@@ -0,0 +1,226 @@
+/*************************************************************************/
+/* gd_mono.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+#ifndef GD_MONO_H
+#define GD_MONO_H
+
+#include "../godotsharp_defs.h"
+#include "gd_mono_assembly.h"
+#include "gd_mono_log.h"
+
+#ifdef WINDOWS_ENABLED
+#include "../utils/mono_reg_utils.h"
+#endif
+
+#define SCRIPTS_DOMAIN GDMono::get_singleton()->get_scripts_domain()
+#ifdef TOOLS_ENABLED
+#define TOOLS_DOMAIN GDMono::get_singleton()->get_tools_domain()
+#endif
+
+class GDMono {
+
+ bool runtime_initialized;
+ bool finalizing_scripts_domain;
+
+ MonoDomain *root_domain;
+ MonoDomain *scripts_domain;
+#ifdef TOOLS_ENABLED
+ MonoDomain *tools_domain;
+#endif
+
+ GDMonoAssembly *corlib_assembly;
+ GDMonoAssembly *api_assembly;
+ GDMonoAssembly *project_assembly;
+#ifdef TOOLS_ENABLED
+ GDMonoAssembly *editor_api_assembly;
+ GDMonoAssembly *editor_tools_assembly;
+#endif
+
+ HashMap<uint32_t, HashMap<String, GDMonoAssembly *> > assemblies;
+
+ void _domain_assemblies_cleanup(uint32_t p_domain_id);
+
+ bool _load_corlib_assembly();
+ bool _load_core_api_assembly();
+#ifdef TOOLS_ENABLED
+ bool _load_editor_api_assembly();
+ bool _load_editor_tools_assembly();
+#endif
+ bool _load_project_assembly();
+
+ bool _load_all_script_assemblies();
+
+ void _register_internal_calls();
+
+ Error _load_scripts_domain();
+ Error _unload_scripts_domain();
+
+#ifdef TOOLS_ENABLED
+ Error _load_tools_domain();
+#endif
+
+#ifdef DEBUG_METHODS_ENABLED
+ uint64_t api_core_hash;
+#ifdef TOOLS_ENABLED
+ uint64_t api_editor_hash;
+#endif
+ void _initialize_and_check_api_hashes();
+#endif
+
+ bool _load_assembly(const String &p_name, GDMonoAssembly **r_assembly);
+
+ GDMonoLog *gdmono_log;
+
+#ifdef WINDOWS_ENABLED
+ MonoRegInfo mono_reg_info;
+#endif
+
+protected:
+ static GDMono *singleton;
+
+public:
+#ifdef DEBUG_METHODS_ENABLED
+ uint64_t get_api_core_hash() { return api_core_hash; }
+#ifdef TOOLS_ENABLED
+ uint64_t get_api_editor_hash() { return api_editor_hash; }
+#endif
+#endif
+
+ enum MemberVisibility {
+ PRIVATE,
+ PROTECTED_AND_INTERNAL, // FAM_AND_ASSEM
+ INTERNAL, // ASSEMBLY
+ PROTECTED, // FAMILY
+ PUBLIC
+ };
+
+ static GDMono *get_singleton() { return singleton; }
+
+ // Do not use these, unless you know what you're doing
+ void add_assembly(uint32_t p_domain_id, GDMonoAssembly *p_assembly);
+ GDMonoAssembly **get_loaded_assembly(const String &p_name);
+
+ _FORCE_INLINE_ bool is_runtime_initialized() const { return runtime_initialized; }
+ _FORCE_INLINE_ bool is_finalizing_scripts_domain() const { return finalizing_scripts_domain; }
+
+ _FORCE_INLINE_ MonoDomain *get_scripts_domain() { return scripts_domain; }
+#ifdef TOOLS_ENABLED
+ _FORCE_INLINE_ MonoDomain *get_tools_domain() { return tools_domain; }
+#endif
+
+ _FORCE_INLINE_ GDMonoAssembly *get_corlib_assembly() const { return corlib_assembly; }
+ _FORCE_INLINE_ GDMonoAssembly *get_api_assembly() const { return api_assembly; }
+ _FORCE_INLINE_ GDMonoAssembly *get_project_assembly() const { return project_assembly; }
+#ifdef TOOLS_ENABLED
+ _FORCE_INLINE_ GDMonoAssembly *get_editor_api_assembly() const { return editor_api_assembly; }
+ _FORCE_INLINE_ GDMonoAssembly *get_editor_tools_assembly() const { return editor_tools_assembly; }
+#endif
+
+#ifdef WINDOWS_ENABLED
+ const MonoRegInfo &get_mono_reg_info() { return mono_reg_info; }
+#endif
+
+ GDMonoClass *get_class(MonoClass *p_raw_class);
+
+#ifdef TOOLS_ENABLED
+ Error reload_scripts_domain();
+#endif
+
+ void initialize();
+
+ GDMono();
+ ~GDMono();
+};
+
+class GDMonoScopeDomain {
+
+ MonoDomain *prev_domain;
+
+public:
+ GDMonoScopeDomain(MonoDomain *p_domain) {
+ MonoDomain *prev_domain = mono_domain_get();
+ if (prev_domain != p_domain) {
+ this->prev_domain = prev_domain;
+ mono_domain_set(p_domain, false);
+ } else {
+ this->prev_domain = NULL;
+ }
+ }
+
+ ~GDMonoScopeDomain() {
+ if (prev_domain)
+ mono_domain_set(prev_domain, false);
+ }
+};
+
+#define _GDMONO_SCOPE_DOMAIN_(m_mono_domain) \
+ GDMonoScopeDomain __gdmono__scope__domain__(m_mono_domain); \
+ (void)__gdmono__scope__domain__;
+
+class _GodotSharp : public Object {
+ GDCLASS(_GodotSharp, Object)
+
+ friend class GDMono;
+
+ void _dispose_object(Object *p_object);
+
+ void _dispose_callback();
+
+ List<Object *> obj_delete_queue;
+ List<NodePath *> np_delete_queue;
+ List<RID *> rid_delete_queue;
+
+ bool queue_empty;
+
+#ifndef NO_THREADS
+ Mutex *queue_mutex;
+#endif
+
+protected:
+ static _GodotSharp *singleton;
+ static void _bind_methods();
+
+public:
+ static _GodotSharp *get_singleton() { return singleton; }
+
+ void attach_thread();
+ void detach_thread();
+
+ bool is_finalizing_domain();
+ bool is_domain_loaded();
+
+ void queue_dispose(Object *p_object);
+ void queue_dispose(NodePath *p_node_path);
+ void queue_dispose(RID *p_rid);
+
+ _GodotSharp();
+ ~_GodotSharp();
+};
+
+#endif // GD_MONO_H
diff --git a/modules/mono/mono_gd/gd_mono_assembly.cpp b/modules/mono/mono_gd/gd_mono_assembly.cpp
new file mode 100644
index 0000000000..4b370295f3
--- /dev/null
+++ b/modules/mono/mono_gd/gd_mono_assembly.cpp
@@ -0,0 +1,356 @@
+/*************************************************************************/
+/* gd_mono_assembly.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+#include "gd_mono_assembly.h"
+
+#include <mono/metadata/mono-debug.h>
+#include <mono/metadata/tokentype.h>
+
+#include "list.h"
+#include "os/file_access.h"
+#include "os/os.h"
+
+#include "../godotsharp_dirs.h"
+#include "gd_mono_class.h"
+
+bool GDMonoAssembly::no_search = false;
+Vector<String> GDMonoAssembly::search_dirs;
+
+MonoAssembly *GDMonoAssembly::_search_hook(MonoAssemblyName *aname, void *user_data) {
+
+ (void)user_data; // UNUSED
+
+ String name = mono_assembly_name_get_name(aname);
+ bool has_extension = name.ends_with(".dll") || name.ends_with(".exe");
+
+ if (no_search)
+ return NULL;
+
+ GDMonoAssembly **loaded_asm = GDMono::get_singleton()->get_loaded_assembly(has_extension ? name.get_basename() : name);
+ if (loaded_asm)
+ return (*loaded_asm)->get_assembly();
+
+ no_search = true; // Avoid the recursion madness
+
+ String path;
+ MonoAssembly *res = NULL;
+
+ for (int i = 0; i < search_dirs.size(); i++) {
+ const String &search_dir = search_dirs[i];
+
+ if (has_extension) {
+ path = search_dir.plus_file(name);
+ if (FileAccess::exists(path)) {
+ res = _load_assembly_from(name.get_basename(), path);
+ break;
+ }
+ } else {
+ path = search_dir.plus_file(name + ".dll");
+ if (FileAccess::exists(path)) {
+ res = _load_assembly_from(name, path);
+ break;
+ }
+
+ path = search_dir.plus_file(name + ".exe");
+ if (FileAccess::exists(path)) {
+ res = _load_assembly_from(name, path);
+ break;
+ }
+ }
+ }
+
+ no_search = false;
+
+ return res;
+}
+
+MonoAssembly *GDMonoAssembly::_preload_hook(MonoAssemblyName *aname, char **assemblies_path, void *user_data) {
+
+ (void)user_data; // UNUSED
+
+ if (search_dirs.empty()) {
+ search_dirs.push_back(GodotSharpDirs::get_res_temp_assemblies_dir());
+ search_dirs.push_back(GodotSharpDirs::get_res_assemblies_dir());
+ search_dirs.push_back(OS::get_singleton()->get_resource_dir());
+ search_dirs.push_back(OS::get_singleton()->get_executable_path().get_base_dir());
+
+ const char *rootdir = mono_assembly_getrootdir();
+ if (rootdir) {
+ search_dirs.push_back(String(rootdir).plus_file("mono").plus_file("4.5"));
+ }
+
+ while (assemblies_path) {
+ if (*assemblies_path)
+ search_dirs.push_back(*assemblies_path);
+ ++assemblies_path;
+ }
+ }
+
+ return NULL;
+}
+
+MonoAssembly *GDMonoAssembly::_load_assembly_from(const String &p_name, const String &p_path) {
+
+ GDMonoAssembly *assembly = memnew(GDMonoAssembly(p_name, p_path));
+
+ MonoDomain *domain = mono_domain_get();
+
+ Error err = assembly->load(domain);
+
+ if (err != OK) {
+ memdelete(assembly);
+ ERR_FAIL_V(NULL);
+ }
+
+ GDMono::get_singleton()->add_assembly(domain ? mono_domain_get_id(domain) : 0, assembly);
+
+ return assembly->get_assembly();
+}
+
+void GDMonoAssembly::initialize() {
+
+ // TODO refonly as well?
+ mono_install_assembly_preload_hook(&GDMonoAssembly::_preload_hook, NULL);
+ mono_install_assembly_search_hook(&GDMonoAssembly::_search_hook, NULL);
+}
+
+Error GDMonoAssembly::load(MonoDomain *p_domain) {
+
+ ERR_FAIL_COND_V(loaded, ERR_FILE_ALREADY_IN_USE);
+
+ uint64_t last_modified_time = FileAccess::get_modified_time(path);
+
+ Vector<uint8_t> data = FileAccess::get_file_as_array(path);
+ ERR_FAIL_COND_V(data.empty(), ERR_FILE_CANT_READ);
+
+ String image_filename(path);
+
+ MonoImageOpenStatus status;
+
+ image = mono_image_open_from_data_with_name(
+ (char *)&data[0], data.size(),
+ true, &status, false,
+ image_filename.utf8().get_data());
+
+ ERR_FAIL_COND_V(status != MONO_IMAGE_OK || image == NULL, ERR_FILE_CANT_OPEN);
+
+#ifdef DEBUG_ENABLED
+ String pdb_path(path + ".pdb");
+
+ if (!FileAccess::exists(pdb_path)) {
+ pdb_path = path.get_basename() + ".pdb"; // without .dll
+
+ if (!FileAccess::exists(pdb_path))
+ goto no_pdb;
+ }
+
+ pdb_data.clear();
+ pdb_data = FileAccess::get_file_as_array(pdb_path);
+ mono_debug_open_image_from_memory(image, &pdb_data[0], pdb_data.size());
+
+no_pdb:
+
+#endif
+
+ assembly = mono_assembly_load_from_full(image, image_filename.utf8().get_data(), &status, false);
+
+ ERR_FAIL_COND_V(status != MONO_IMAGE_OK || assembly == NULL, ERR_FILE_CANT_OPEN);
+
+ if (p_domain && mono_image_get_entry_point(image)) {
+ // TODO should this be removed? do we want to call main? what other effects does this have?
+ mono_jit_exec(p_domain, assembly, 0, NULL);
+ }
+
+ loaded = true;
+ modified_time = last_modified_time;
+
+ return OK;
+}
+
+Error GDMonoAssembly::wrapper_for_image(MonoImage *p_image) {
+
+ ERR_FAIL_COND_V(loaded, ERR_FILE_ALREADY_IN_USE);
+
+ assembly = mono_image_get_assembly(p_image);
+ ERR_FAIL_NULL_V(assembly, FAILED);
+
+ image = p_image;
+
+ mono_image_addref(image);
+
+ loaded = true;
+
+ return OK;
+}
+
+void GDMonoAssembly::unload() {
+
+ ERR_FAIL_COND(!loaded);
+
+#ifdef DEBUG_ENABLED
+ if (pdb_data.size()) {
+ mono_debug_close_image(image);
+ pdb_data.clear();
+ }
+#endif
+
+ for (Map<MonoClass *, GDMonoClass *>::Element *E = cached_raw.front(); E; E = E->next()) {
+ memdelete(E->value());
+ }
+
+ cached_classes.clear();
+ cached_raw.clear();
+
+ mono_image_close(image);
+
+ assembly = NULL;
+ image = NULL;
+ loaded = false;
+}
+
+GDMonoClass *GDMonoAssembly::get_class(const StringName &p_namespace, const StringName &p_name) {
+
+ ERR_FAIL_COND_V(!loaded, NULL);
+
+ ClassKey key(p_namespace, p_name);
+
+ GDMonoClass **match = cached_classes.getptr(key);
+
+ if (match)
+ return *match;
+
+ MonoClass *mono_class = mono_class_from_name(image, String(p_namespace).utf8(), String(p_name).utf8());
+
+ if (!mono_class)
+ return NULL;
+
+ GDMonoClass *wrapped_class = memnew(GDMonoClass(p_namespace, p_name, mono_class, this));
+
+ cached_classes[key] = wrapped_class;
+ cached_raw[mono_class] = wrapped_class;
+
+ return wrapped_class;
+}
+
+GDMonoClass *GDMonoAssembly::get_class(MonoClass *p_mono_class) {
+
+ ERR_FAIL_COND_V(!loaded, NULL);
+
+ Map<MonoClass *, GDMonoClass *>::Element *match = cached_raw.find(p_mono_class);
+
+ if (match)
+ return match->value();
+
+ StringName namespace_name = mono_class_get_namespace(p_mono_class);
+ StringName class_name = mono_class_get_name(p_mono_class);
+
+ GDMonoClass *wrapped_class = memnew(GDMonoClass(namespace_name, class_name, p_mono_class, this));
+
+ cached_classes[ClassKey(namespace_name, class_name)] = wrapped_class;
+ cached_raw[p_mono_class] = wrapped_class;
+
+ return wrapped_class;
+}
+
+GDMonoClass *GDMonoAssembly::get_object_derived_class(const StringName &p_class) {
+
+ GDMonoClass *match = NULL;
+
+ if (gdobject_class_cache_updated) {
+ Map<StringName, GDMonoClass *>::Element *result = gdobject_class_cache.find(p_class);
+
+ if (result)
+ match = result->get();
+ } else {
+ List<GDMonoClass *> nested_classes;
+
+ int rows = mono_image_get_table_rows(image, MONO_TABLE_TYPEDEF);
+
+ for (int i = 1; i < rows; i++) {
+ MonoClass *mono_class = mono_class_get(image, (i + 1) | MONO_TOKEN_TYPE_DEF);
+
+ if (!mono_class_is_assignable_from(CACHED_CLASS_RAW(GodotObject), mono_class))
+ continue;
+
+ GDMonoClass *current = get_class(mono_class);
+
+ if (!current)
+ continue;
+
+ nested_classes.push_back(current);
+
+ if (!match && current->get_name() == p_class)
+ match = current;
+
+ while (!nested_classes.empty()) {
+ GDMonoClass *current_nested = nested_classes.front()->get();
+ nested_classes.pop_back();
+
+ void *iter = NULL;
+
+ while (true) {
+ MonoClass *raw_nested = mono_class_get_nested_types(current_nested->get_raw(), &iter);
+
+ if (!raw_nested)
+ break;
+
+ GDMonoClass *nested_class = get_class(raw_nested);
+
+ if (nested_class) {
+ gdobject_class_cache.insert(nested_class->get_name(), nested_class);
+ nested_classes.push_back(nested_class);
+ }
+ }
+ }
+
+ gdobject_class_cache.insert(current->get_name(), current);
+ }
+
+ gdobject_class_cache_updated = true;
+ }
+
+ return match;
+}
+
+GDMonoAssembly::GDMonoAssembly(const String &p_name, const String &p_path) {
+
+ loaded = false;
+ gdobject_class_cache_updated = false;
+ name = p_name;
+ path = p_path;
+ modified_time = 0;
+ assembly = NULL;
+ image = NULL;
+}
+
+GDMonoAssembly::~GDMonoAssembly() {
+
+ if (loaded)
+ unload();
+}
diff --git a/modules/mono/mono_gd/gd_mono_assembly.h b/modules/mono/mono_gd/gd_mono_assembly.h
new file mode 100644
index 0000000000..710b674622
--- /dev/null
+++ b/modules/mono/mono_gd/gd_mono_assembly.h
@@ -0,0 +1,121 @@
+/*************************************************************************/
+/* gd_mono_assembly.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+#ifndef GD_MONO_ASSEMBLY_H
+#define GD_MONO_ASSEMBLY_H
+
+#include <mono/jit/jit.h>
+#include <mono/metadata/assembly.h>
+
+#include "gd_mono_utils.h"
+#include "hash_map.h"
+#include "map.h"
+#include "ustring.h"
+
+class GDMonoAssembly {
+
+ struct ClassKey {
+ struct Hasher {
+ static _FORCE_INLINE_ uint32_t hash(const ClassKey &p_key) {
+ uint32_t hash = 0;
+
+ GDMonoUtils::hash_combine(hash, p_key.namespace_name.hash());
+ GDMonoUtils::hash_combine(hash, p_key.class_name.hash());
+
+ return hash;
+ }
+ };
+
+ _FORCE_INLINE_ bool operator==(const ClassKey &p_a) const {
+ return p_a.class_name == class_name && p_a.namespace_name == namespace_name;
+ }
+
+ ClassKey() {}
+
+ ClassKey(const StringName &p_namespace_name, const StringName &p_class_name) {
+ namespace_name = p_namespace_name;
+ class_name = p_class_name;
+ }
+
+ StringName namespace_name;
+ StringName class_name;
+ };
+
+ MonoAssembly *assembly;
+ MonoImage *image;
+
+ bool loaded;
+
+ String name;
+ String path;
+ uint64_t modified_time;
+
+ HashMap<ClassKey, GDMonoClass *, ClassKey::Hasher> cached_classes;
+ Map<MonoClass *, GDMonoClass *> cached_raw;
+
+ bool gdobject_class_cache_updated;
+ Map<StringName, GDMonoClass *> gdobject_class_cache;
+
+#ifdef DEBUG_ENABLED
+ Vector<uint8_t> pdb_data;
+#endif
+
+ static bool no_search;
+ static Vector<String> search_dirs;
+
+ static MonoAssembly *_search_hook(MonoAssemblyName *aname, void *user_data);
+ static MonoAssembly *_preload_hook(MonoAssemblyName *aname, char **assemblies_path, void *user_data);
+
+ static MonoAssembly *_load_assembly_from(const String &p_name, const String &p_path);
+
+ friend class GDMono;
+ static void initialize();
+
+public:
+ Error load(MonoDomain *p_domain);
+ Error wrapper_for_image(MonoImage *p_image);
+ void unload();
+
+ _FORCE_INLINE_ bool is_loaded() const { return loaded; }
+ _FORCE_INLINE_ MonoImage *get_image() const { return image; }
+ _FORCE_INLINE_ MonoAssembly *get_assembly() const { return assembly; }
+ _FORCE_INLINE_ String get_name() const { return name; }
+ _FORCE_INLINE_ String get_path() const { return path; }
+ _FORCE_INLINE_ uint64_t get_modified_time() const { return modified_time; }
+
+ GDMonoClass *get_class(const StringName &p_namespace, const StringName &p_class);
+ GDMonoClass *get_class(MonoClass *p_mono_class);
+
+ GDMonoClass *get_object_derived_class(const StringName &p_class);
+
+ GDMonoAssembly(const String &p_name, const String &p_path = String());
+ ~GDMonoAssembly();
+};
+
+#endif // GD_MONO_ASSEMBLY_H
diff --git a/modules/mono/mono_gd/gd_mono_class.cpp b/modules/mono/mono_gd/gd_mono_class.cpp
new file mode 100644
index 0000000000..0134ace5d7
--- /dev/null
+++ b/modules/mono/mono_gd/gd_mono_class.cpp
@@ -0,0 +1,381 @@
+/*************************************************************************/
+/* gd_mono_class.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+#include "gd_mono_class.h"
+
+#include <mono/metadata/attrdefs.h>
+
+#include "gd_mono_assembly.h"
+
+MonoType *GDMonoClass::get_raw_type(GDMonoClass *p_class) {
+
+ return mono_class_get_type(p_class->get_raw());
+}
+
+bool GDMonoClass::is_assignable_from(GDMonoClass *p_from) const {
+
+ return mono_class_is_assignable_from(mono_class, p_from->mono_class);
+}
+
+GDMonoClass *GDMonoClass::get_parent_class() {
+
+ if (assembly) {
+ MonoClass *parent_mono_class = mono_class_get_parent(mono_class);
+
+ if (parent_mono_class) {
+ return GDMono::get_singleton()->get_class(parent_mono_class);
+ }
+ }
+
+ return NULL;
+}
+
+bool GDMonoClass::has_method(const StringName &p_name) {
+
+ return get_method(p_name) != NULL;
+}
+
+bool GDMonoClass::has_attribute(GDMonoClass *p_attr_class) {
+
+#ifdef DEBUG_ENABLED
+ ERR_FAIL_NULL_V(p_attr_class, false);
+#endif
+
+ if (!attrs_fetched)
+ fetch_attributes();
+
+ if (!attributes)
+ return false;
+
+ return mono_custom_attrs_has_attr(attributes, p_attr_class->get_raw());
+}
+
+MonoObject *GDMonoClass::get_attribute(GDMonoClass *p_attr_class) {
+
+#ifdef DEBUG_ENABLED
+ ERR_FAIL_NULL_V(p_attr_class, NULL);
+#endif
+
+ if (!attrs_fetched)
+ fetch_attributes();
+
+ if (!attributes)
+ return NULL;
+
+ return mono_custom_attrs_get_attr(attributes, p_attr_class->get_raw());
+}
+
+void GDMonoClass::fetch_attributes() {
+
+ ERR_FAIL_COND(attributes != NULL);
+
+ attributes = mono_custom_attrs_from_class(get_raw());
+ attrs_fetched = true;
+}
+
+void GDMonoClass::fetch_methods_with_godot_api_checks(GDMonoClass *p_native_base) {
+
+ CRASH_COND(!CACHED_CLASS(GodotObject)->is_assignable_from(this));
+
+ if (methods_fetched)
+ return;
+
+ void *iter = NULL;
+ MonoMethod *raw_method = NULL;
+ while ((raw_method = mono_class_get_methods(get_raw(), &iter)) != NULL) {
+ StringName name = mono_method_get_name(raw_method);
+
+ GDMonoMethod *method = get_method(raw_method, name);
+ ERR_CONTINUE(!method);
+
+ if (method->get_name() != name) {
+
+#ifdef DEBUG_ENABLED
+ String fullname = method->get_ret_type_full_name() + " " + name + "(" + method->get_signature_desc(true) + ")";
+ WARN_PRINTS("Method `" + fullname + "` is hidden by Godot API method. Should be `" +
+ method->get_full_name_no_class() + "`. In class `" + namespace_name + "." + class_name + "`.");
+#endif
+ continue;
+ }
+
+#ifdef DEBUG_ENABLED
+ // For debug builds, we also fetched from native base classes as well before if this is not a native base class.
+ // This allows us to warn the user here if he is using snake_case by mistake.
+
+ if (p_native_base != this) {
+
+ GDMonoClass *native_top = p_native_base;
+ while (native_top) {
+ GDMonoMethod *m = native_top->get_method(name, method->get_parameters_count());
+
+ if (m && m->get_name() != name) {
+ // found
+ String fullname = m->get_ret_type_full_name() + " " + name + "(" + m->get_signature_desc(true) + ")";
+ WARN_PRINTS("Method `" + fullname + "` should be `" + m->get_full_name_no_class() +
+ "`. In class `" + namespace_name + "." + class_name + "`.");
+ break;
+ }
+
+ if (native_top == CACHED_CLASS(GodotObject))
+ break;
+
+ native_top = native_top->get_parent_class();
+ }
+ }
+#endif
+
+ uint32_t flags = mono_method_get_flags(method->mono_method, NULL);
+
+ if (!(flags & MONO_METHOD_ATTR_VIRTUAL))
+ continue;
+
+ // Virtual method of Godot Object derived type, let's try to find GodotMethod attribute
+
+ GDMonoClass *top = p_native_base;
+
+ while (top) {
+ GDMonoMethod *base_method = top->get_method(name, method->get_parameters_count());
+
+ if (base_method && base_method->has_attribute(CACHED_CLASS(GodotMethodAttribute))) {
+ // Found base method with GodotMethod attribute.
+ // We get the original API method name from this attribute.
+ // This name must point to the virtual method.
+
+ MonoObject *attr = base_method->get_attribute(CACHED_CLASS(GodotMethodAttribute));
+
+ StringName godot_method_name = CACHED_FIELD(GodotMethodAttribute, methodName)->get_string_value(attr);
+#ifdef DEBUG_ENABLED
+ CRASH_COND(godot_method_name == StringName());
+#endif
+ MethodKey key = MethodKey(godot_method_name, method->get_parameters_count());
+ GDMonoMethod **existing_method = methods.getptr(key);
+ if (existing_method)
+ memdelete(*existing_method); // Must delete old one
+ methods.set(key, method);
+
+ break;
+ }
+
+ if (top == CACHED_CLASS(GodotObject))
+ break;
+
+ top = top->get_parent_class();
+ }
+ }
+
+ methods_fetched = true;
+}
+
+GDMonoMethod *GDMonoClass::get_method(const StringName &p_name) {
+
+ ERR_FAIL_COND_V(!methods_fetched, NULL);
+
+ const MethodKey *k = NULL;
+
+ while ((k = methods.next(k))) {
+ if (k->name == p_name)
+ return methods.get(*k);
+ }
+
+ return NULL;
+}
+
+GDMonoMethod *GDMonoClass::get_method(const StringName &p_name, int p_params_count) {
+
+ MethodKey key = MethodKey(p_name, p_params_count);
+
+ GDMonoMethod **match = methods.getptr(key);
+
+ if (match)
+ return *match;
+
+ if (methods_fetched)
+ return NULL;
+
+ MonoMethod *raw_method = mono_class_get_method_from_name(mono_class, String(p_name).utf8().get_data(), p_params_count);
+
+ if (raw_method) {
+ GDMonoMethod *method = memnew(GDMonoMethod(p_name, raw_method));
+ methods.set(key, method);
+
+ return method;
+ }
+
+ return NULL;
+}
+
+GDMonoMethod *GDMonoClass::get_method(MonoMethod *p_raw_method) {
+
+ MonoMethodSignature *sig = mono_method_signature(p_raw_method);
+
+ int params_count = mono_signature_get_param_count(sig);
+ StringName method_name = mono_method_get_name(p_raw_method);
+
+ return get_method(p_raw_method, method_name, params_count);
+}
+
+GDMonoMethod *GDMonoClass::get_method(MonoMethod *p_raw_method, const StringName &p_name) {
+
+ MonoMethodSignature *sig = mono_method_signature(p_raw_method);
+ int params_count = mono_signature_get_param_count(sig);
+ return get_method(p_raw_method, p_name, params_count);
+}
+
+GDMonoMethod *GDMonoClass::get_method(MonoMethod *p_raw_method, const StringName &p_name, int p_params_count) {
+
+ ERR_FAIL_NULL_V(p_raw_method, NULL);
+
+ MethodKey key = MethodKey(p_name, p_params_count);
+
+ GDMonoMethod **match = methods.getptr(key);
+
+ if (match)
+ return *match;
+
+ GDMonoMethod *method = memnew(GDMonoMethod(p_name, p_raw_method));
+ methods.set(key, method);
+
+ return method;
+}
+
+GDMonoMethod *GDMonoClass::get_method_with_desc(const String &p_description, bool p_include_namespace) {
+
+ MonoMethodDesc *desc = mono_method_desc_new(p_description.utf8().get_data(), p_include_namespace);
+ MonoMethod *method = mono_method_desc_search_in_class(desc, mono_class);
+ mono_method_desc_free(desc);
+
+ return get_method(method);
+}
+
+GDMonoField *GDMonoClass::get_field(const StringName &p_name) {
+
+ Map<StringName, GDMonoField *>::Element *result = fields.find(p_name);
+
+ if (result)
+ return result->value();
+
+ if (fields_fetched)
+ return NULL;
+
+ MonoClassField *raw_field = mono_class_get_field_from_name(mono_class, String(p_name).utf8().get_data());
+
+ if (raw_field) {
+ GDMonoField *field = memnew(GDMonoField(raw_field, this));
+ fields.insert(p_name, field);
+
+ return field;
+ }
+
+ return NULL;
+}
+
+const Vector<GDMonoField *> &GDMonoClass::get_all_fields() {
+
+ if (fields_fetched)
+ return fields_list;
+
+ void *iter = NULL;
+ MonoClassField *raw_field = NULL;
+ while ((raw_field = mono_class_get_fields(get_raw(), &iter)) != NULL) {
+ StringName name = mono_field_get_name(raw_field);
+
+ Map<StringName, GDMonoField *>::Element *match = fields.find(name);
+
+ if (match) {
+ fields_list.push_back(match->get());
+ } else {
+ GDMonoField *field = memnew(GDMonoField(raw_field, this));
+ fields.insert(name, field);
+ fields_list.push_back(field);
+ }
+ }
+
+ fields_fetched = true;
+
+ return fields_list;
+}
+
+GDMonoClass::GDMonoClass(const StringName &p_namespace, const StringName &p_name, MonoClass *p_class, GDMonoAssembly *p_assembly) {
+
+ namespace_name = p_namespace;
+ class_name = p_name;
+ mono_class = p_class;
+ assembly = p_assembly;
+
+ attrs_fetched = false;
+ attributes = NULL;
+
+ methods_fetched = false;
+ fields_fetched = false;
+}
+
+GDMonoClass::~GDMonoClass() {
+
+ if (attributes) {
+ mono_custom_attrs_free(attributes);
+ }
+
+ for (Map<StringName, GDMonoField *>::Element *E = fields.front(); E; E = E->next()) {
+ memdelete(E->value());
+ }
+
+ {
+ // Ugly workaround...
+ // We may have duplicated values, because we redirect snake_case methods to PascalCasel (only Godot API methods).
+ // This way, we end with both the snake_case name and the PascalCasel name paired with the same method.
+ // Therefore, we must avoid deleting the same pointer twice.
+
+ int offset = 0;
+ Vector<GDMonoMethod *> deleted_methods;
+ deleted_methods.resize(methods.size());
+
+ const MethodKey *k = NULL;
+ while ((k = methods.next(k))) {
+ GDMonoMethod *method = methods.get(*k);
+
+ if (method) {
+ for (int i = 0; i < offset; i++) {
+ if (deleted_methods[i] == method) {
+ // Already deleted
+ goto already_deleted;
+ }
+ }
+
+ deleted_methods[offset] = method;
+ ++offset;
+
+ memdelete(method);
+ }
+
+ already_deleted:;
+ }
+
+ methods.clear();
+ }
+}
diff --git a/modules/mono/mono_gd/gd_mono_class.h b/modules/mono/mono_gd/gd_mono_class.h
new file mode 100644
index 0000000000..1e72553879
--- /dev/null
+++ b/modules/mono/mono_gd/gd_mono_class.h
@@ -0,0 +1,124 @@
+/*************************************************************************/
+/* gd_mono_class.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+#ifndef GD_MONO_CLASS_H
+#define GD_MONO_CLASS_H
+
+#include <mono/metadata/debug-helpers.h>
+
+#include "map.h"
+#include "ustring.h"
+
+#include "gd_mono_field.h"
+#include "gd_mono_header.h"
+#include "gd_mono_method.h"
+#include "gd_mono_utils.h"
+
+class GDMonoClass {
+ struct MethodKey {
+ struct Hasher {
+ static _FORCE_INLINE_ uint32_t hash(const MethodKey &p_key) {
+ uint32_t hash = 0;
+
+ GDMonoUtils::hash_combine(hash, p_key.name.hash());
+ GDMonoUtils::hash_combine(hash, HashMapHasherDefault::hash(p_key.params_count));
+
+ return hash;
+ }
+ };
+
+ _FORCE_INLINE_ bool operator==(const MethodKey &p_a) const {
+ return p_a.params_count == params_count && p_a.name == name;
+ }
+
+ MethodKey() {}
+
+ MethodKey(const StringName &p_name, int p_params_count) {
+ name = p_name;
+ params_count = p_params_count;
+ }
+
+ StringName name;
+ int params_count;
+ };
+
+ StringName namespace_name;
+ StringName class_name;
+
+ MonoClass *mono_class;
+ GDMonoAssembly *assembly;
+
+ bool attrs_fetched;
+ MonoCustomAttrInfo *attributes;
+
+ bool methods_fetched;
+ HashMap<MethodKey, GDMonoMethod *, MethodKey::Hasher> methods;
+
+ bool fields_fetched;
+ Map<StringName, GDMonoField *> fields;
+ Vector<GDMonoField *> fields_list;
+
+ friend class GDMonoAssembly;
+ GDMonoClass(const StringName &p_namespace, const StringName &p_name, MonoClass *p_class, GDMonoAssembly *p_assembly);
+
+public:
+ static MonoType *get_raw_type(GDMonoClass *p_class);
+
+ bool is_assignable_from(GDMonoClass *p_from) const;
+
+ _FORCE_INLINE_ StringName get_namespace() const { return namespace_name; }
+ _FORCE_INLINE_ StringName get_name() const { return class_name; }
+
+ _FORCE_INLINE_ MonoClass *get_raw() const { return mono_class; }
+ _FORCE_INLINE_ const GDMonoAssembly *get_assembly() const { return assembly; }
+
+ GDMonoClass *get_parent_class();
+
+ bool has_method(const StringName &p_name);
+
+ bool has_attribute(GDMonoClass *p_attr_class);
+ MonoObject *get_attribute(GDMonoClass *p_attr_class);
+
+ void fetch_attributes();
+ void fetch_methods_with_godot_api_checks(GDMonoClass *p_native_base);
+
+ GDMonoMethod *get_method(const StringName &p_name);
+ GDMonoMethod *get_method(const StringName &p_name, int p_params_count);
+ GDMonoMethod *get_method(MonoMethod *p_raw_method);
+ GDMonoMethod *get_method(MonoMethod *p_raw_method, const StringName &p_name);
+ GDMonoMethod *get_method(MonoMethod *p_raw_method, const StringName &p_name, int p_params_count);
+ GDMonoMethod *get_method_with_desc(const String &p_description, bool p_includes_namespace);
+
+ GDMonoField *get_field(const StringName &p_name);
+ const Vector<GDMonoField *> &get_all_fields();
+
+ ~GDMonoClass();
+};
+
+#endif // GD_MONO_CLASS_H
diff --git a/modules/mono/mono_gd/gd_mono_field.cpp b/modules/mono/mono_gd/gd_mono_field.cpp
new file mode 100644
index 0000000000..c2d8eeaa32
--- /dev/null
+++ b/modules/mono/mono_gd/gd_mono_field.cpp
@@ -0,0 +1,362 @@
+/*************************************************************************/
+/* gd_mono_field.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+#include "gd_mono_field.h"
+
+#include <mono/metadata/attrdefs.h>
+
+#include "gd_mono_class.h"
+#include "gd_mono_marshal.h"
+
+void GDMonoField::set_value_raw(MonoObject *p_object, void *p_ptr) {
+ mono_field_set_value(p_object, mono_field, &p_ptr);
+}
+
+void GDMonoField::set_value(MonoObject *p_object, const Variant &p_value) {
+#define SET_FROM_STRUCT_AND_BREAK(m_type) \
+ { \
+ const m_type &val = p_value.operator m_type(); \
+ MARSHALLED_OUT(m_type, val, raw); \
+ mono_field_set_value(p_object, mono_field, raw); \
+ break; \
+ }
+
+#define SET_FROM_PRIMITIVE(m_type) \
+ { \
+ m_type val = p_value.operator m_type(); \
+ mono_field_set_value(p_object, mono_field, &val); \
+ }
+
+#define SET_FROM_ARRAY_AND_BREAK(m_type) \
+ { \
+ MonoArray *managed = GDMonoMarshal::m_type##_to_mono_array(p_value.operator m_type()); \
+ mono_field_set_value(p_object, mono_field, &managed); \
+ break; \
+ }
+
+ switch (type.type_encoding) {
+ case MONO_TYPE_BOOLEAN: {
+ SET_FROM_PRIMITIVE(bool);
+ } break;
+
+ case MONO_TYPE_I1: {
+ SET_FROM_PRIMITIVE(signed char);
+ } break;
+ case MONO_TYPE_I2: {
+ SET_FROM_PRIMITIVE(signed short);
+ } break;
+ case MONO_TYPE_I4: {
+ SET_FROM_PRIMITIVE(signed int);
+ } break;
+ case MONO_TYPE_I8: {
+ SET_FROM_PRIMITIVE(int64_t);
+ } break;
+
+ case MONO_TYPE_U1: {
+ SET_FROM_PRIMITIVE(unsigned char);
+ } break;
+ case MONO_TYPE_U2: {
+ SET_FROM_PRIMITIVE(unsigned short);
+ } break;
+ case MONO_TYPE_U4: {
+ SET_FROM_PRIMITIVE(unsigned int);
+ } break;
+ case MONO_TYPE_U8: {
+ SET_FROM_PRIMITIVE(uint64_t);
+ } break;
+
+ case MONO_TYPE_R4: {
+ SET_FROM_PRIMITIVE(float);
+ } break;
+
+ case MONO_TYPE_R8: {
+ SET_FROM_PRIMITIVE(double);
+ } break;
+
+ case MONO_TYPE_STRING: {
+ MonoString *mono_string = GDMonoMarshal::mono_string_from_godot(p_value);
+ mono_field_set_value(p_object, mono_field, mono_string);
+ } break;
+
+ case MONO_TYPE_VALUETYPE: {
+ GDMonoClass *tclass = type.type_class;
+
+ if (tclass == CACHED_CLASS(Vector2))
+ SET_FROM_STRUCT_AND_BREAK(Vector2);
+
+ if (tclass == CACHED_CLASS(Rect2))
+ SET_FROM_STRUCT_AND_BREAK(Rect2);
+
+ if (tclass == CACHED_CLASS(Transform2D))
+ SET_FROM_STRUCT_AND_BREAK(Transform2D);
+
+ if (tclass == CACHED_CLASS(Vector3))
+ SET_FROM_STRUCT_AND_BREAK(Vector3);
+
+ if (tclass == CACHED_CLASS(Basis))
+ SET_FROM_STRUCT_AND_BREAK(Basis);
+
+ if (tclass == CACHED_CLASS(Quat))
+ SET_FROM_STRUCT_AND_BREAK(Quat);
+
+ if (tclass == CACHED_CLASS(Transform))
+ SET_FROM_STRUCT_AND_BREAK(Transform);
+
+ if (tclass == CACHED_CLASS(Rect3))
+ SET_FROM_STRUCT_AND_BREAK(Rect3);
+
+ if (tclass == CACHED_CLASS(Color))
+ SET_FROM_STRUCT_AND_BREAK(Color);
+
+ if (tclass == CACHED_CLASS(Plane))
+ SET_FROM_STRUCT_AND_BREAK(Plane);
+
+ ERR_EXPLAIN(String() + "Attempted to set the value of a field of unmarshallable type: " + tclass->get_name());
+ ERR_FAIL();
+ } break;
+
+ case MONO_TYPE_ARRAY:
+ case MONO_TYPE_SZARRAY: {
+ MonoArrayType *array_type = mono_type_get_array_type(GDMonoClass::get_raw_type(type.type_class));
+
+ if (array_type->eklass == CACHED_CLASS_RAW(MonoObject))
+ SET_FROM_ARRAY_AND_BREAK(Array);
+
+ if (array_type->eklass == CACHED_CLASS_RAW(uint8_t))
+ SET_FROM_ARRAY_AND_BREAK(PoolByteArray);
+
+ if (array_type->eklass == CACHED_CLASS_RAW(int32_t))
+ SET_FROM_ARRAY_AND_BREAK(PoolIntArray);
+
+ if (array_type->eklass == REAL_T_MONOCLASS)
+ SET_FROM_ARRAY_AND_BREAK(PoolRealArray);
+
+ if (array_type->eklass == CACHED_CLASS_RAW(String))
+ SET_FROM_ARRAY_AND_BREAK(PoolStringArray);
+
+ if (array_type->eklass == CACHED_CLASS_RAW(Vector2))
+ SET_FROM_ARRAY_AND_BREAK(PoolVector2Array);
+
+ if (array_type->eklass == CACHED_CLASS_RAW(Vector3))
+ SET_FROM_ARRAY_AND_BREAK(PoolVector3Array);
+
+ if (array_type->eklass == CACHED_CLASS_RAW(Color))
+ SET_FROM_ARRAY_AND_BREAK(PoolColorArray);
+
+ ERR_EXPLAIN(String() + "Attempted to convert Variant to a managed array of unmarshallable element type.");
+ ERR_FAIL();
+ } break;
+
+ case MONO_TYPE_CLASS: {
+ GDMonoClass *type_class = type.type_class;
+
+ // GodotObject
+ if (CACHED_CLASS(GodotObject)->is_assignable_from(type_class)) {
+ MonoObject *managed = GDMonoUtils::unmanaged_get_managed(p_value.operator Object *());
+ mono_field_set_value(p_object, mono_field, &managed);
+ break;
+ }
+
+ if (CACHED_CLASS(NodePath) == type_class) {
+ MonoObject *managed = GDMonoUtils::create_managed_from(p_value.operator NodePath());
+ mono_field_set_value(p_object, mono_field, &managed);
+ break;
+ }
+
+ if (CACHED_CLASS(RID) == type_class) {
+ MonoObject *managed = GDMonoUtils::create_managed_from(p_value.operator RID());
+ mono_field_set_value(p_object, mono_field, &managed);
+ break;
+ }
+
+ ERR_EXPLAIN(String() + "Attempted to set the value of a field of unmarshallable type: " + type_class->get_name());
+ ERR_FAIL();
+ } break;
+
+ case MONO_TYPE_OBJECT: {
+ GDMonoClass *type_class = type.type_class;
+
+ // Variant
+ switch (p_value.get_type()) {
+ case Variant::BOOL: {
+ SET_FROM_PRIMITIVE(bool);
+ } break;
+ case Variant::INT: {
+ SET_FROM_PRIMITIVE(int);
+ } break;
+ case Variant::REAL: {
+#ifdef REAL_T_IS_DOUBLE
+ SET_FROM_PRIMITIVE(double);
+#else
+ SET_FROM_PRIMITIVE(float);
+#endif
+ } break;
+ case Variant::STRING: {
+ MonoString *mono_string = GDMonoMarshal::mono_string_from_godot(p_value);
+ mono_field_set_value(p_object, mono_field, mono_string);
+ } break;
+ case Variant::VECTOR2: SET_FROM_STRUCT_AND_BREAK(Vector2);
+ case Variant::RECT2: SET_FROM_STRUCT_AND_BREAK(Rect2);
+ case Variant::VECTOR3: SET_FROM_STRUCT_AND_BREAK(Vector3);
+ case Variant::TRANSFORM2D: SET_FROM_STRUCT_AND_BREAK(Transform2D);
+ case Variant::PLANE: SET_FROM_STRUCT_AND_BREAK(Plane);
+ case Variant::QUAT: SET_FROM_STRUCT_AND_BREAK(Quat);
+ case Variant::RECT3: SET_FROM_STRUCT_AND_BREAK(Rect3);
+ case Variant::BASIS: SET_FROM_STRUCT_AND_BREAK(Basis);
+ case Variant::TRANSFORM: SET_FROM_STRUCT_AND_BREAK(Transform);
+ case Variant::COLOR: SET_FROM_STRUCT_AND_BREAK(Color);
+ case Variant::NODE_PATH: {
+ MonoObject *managed = GDMonoUtils::create_managed_from(p_value.operator NodePath());
+ mono_field_set_value(p_object, mono_field, &managed);
+ } break;
+ case Variant::_RID: {
+ MonoObject *managed = GDMonoUtils::create_managed_from(p_value.operator RID());
+ mono_field_set_value(p_object, mono_field, &managed);
+ } break;
+ case Variant::OBJECT: {
+ MonoObject *managed = GDMonoUtils::unmanaged_get_managed(p_value.operator Object *());
+ mono_field_set_value(p_object, mono_field, managed);
+ break;
+ }
+ case Variant::DICTIONARY: {
+ MonoObject *managed = GDMonoMarshal::Dictionary_to_mono_object(p_value.operator Dictionary());
+ mono_field_set_value(p_object, mono_field, &managed);
+ } break;
+ case Variant::ARRAY: SET_FROM_ARRAY_AND_BREAK(Array);
+ case Variant::POOL_BYTE_ARRAY: SET_FROM_ARRAY_AND_BREAK(PoolByteArray);
+ case Variant::POOL_INT_ARRAY: SET_FROM_ARRAY_AND_BREAK(PoolIntArray);
+ case Variant::POOL_REAL_ARRAY: SET_FROM_ARRAY_AND_BREAK(PoolRealArray);
+ case Variant::POOL_STRING_ARRAY: SET_FROM_ARRAY_AND_BREAK(PoolStringArray);
+ case Variant::POOL_VECTOR2_ARRAY: SET_FROM_ARRAY_AND_BREAK(PoolVector2Array);
+ case Variant::POOL_VECTOR3_ARRAY: SET_FROM_ARRAY_AND_BREAK(PoolVector3Array);
+ case Variant::POOL_COLOR_ARRAY: SET_FROM_ARRAY_AND_BREAK(PoolColorArray);
+#undef SET_FROM_ARRAY_AND_BREAK
+ default: break;
+ }
+ } break;
+
+ case MONO_TYPE_GENERICINST: {
+ if (CACHED_RAW_MONO_CLASS(Dictionary) == type.type_class->get_raw()) {
+ MonoObject *managed = GDMonoMarshal::Dictionary_to_mono_object(p_value.operator Dictionary());
+ mono_field_set_value(p_object, mono_field, &managed);
+ break;
+ }
+ } break;
+
+ default: {
+ ERR_PRINTS(String() + "Attempted to set the value of a field of unexpected type encoding: " + itos(type.type_encoding));
+ } break;
+ }
+
+#undef SET_FROM_STRUCT_AND_BREAK
+#undef SET_FROM_PRIMITIVE
+}
+
+bool GDMonoField::get_bool_value(MonoObject *p_object) {
+ return (bool)GDMonoMarshal::unbox<MonoBoolean>(get_value(p_object));
+}
+
+int GDMonoField::get_int_value(MonoObject *p_object) {
+ return GDMonoMarshal::unbox<int32_t>(get_value(p_object));
+}
+
+String GDMonoField::get_string_value(MonoObject *p_object) {
+ MonoObject *val = get_value(p_object);
+ return val ? GDMonoMarshal::mono_string_to_godot((MonoString *)val) : String();
+}
+
+bool GDMonoField::has_attribute(GDMonoClass *p_attr_class) {
+ ERR_FAIL_NULL_V(p_attr_class, false);
+
+ if (!attrs_fetched)
+ fetch_attributes();
+
+ if (!attributes)
+ return false;
+
+ return mono_custom_attrs_has_attr(attributes, p_attr_class->get_raw());
+}
+
+MonoObject *GDMonoField::get_attribute(GDMonoClass *p_attr_class) {
+ ERR_FAIL_NULL_V(p_attr_class, NULL);
+
+ if (!attrs_fetched)
+ fetch_attributes();
+
+ if (!attributes)
+ return NULL;
+
+ return mono_custom_attrs_get_attr(attributes, p_attr_class->get_raw());
+}
+
+void GDMonoField::fetch_attributes() {
+ ERR_FAIL_COND(attributes != NULL);
+ attributes = mono_custom_attrs_from_field(owner->get_raw(), get_raw());
+ attrs_fetched = true;
+}
+
+bool GDMonoField::is_static() {
+ return mono_field_get_flags(mono_field) & MONO_FIELD_ATTR_STATIC;
+}
+
+GDMono::MemberVisibility GDMonoField::get_visibility() {
+ switch (mono_field_get_flags(mono_field) & MONO_FIELD_ATTR_FIELD_ACCESS_MASK) {
+ case MONO_FIELD_ATTR_PRIVATE:
+ return GDMono::PRIVATE;
+ case MONO_FIELD_ATTR_FAM_AND_ASSEM:
+ return GDMono::PROTECTED_AND_INTERNAL;
+ case MONO_FIELD_ATTR_ASSEMBLY:
+ return GDMono::INTERNAL;
+ case MONO_FIELD_ATTR_FAMILY:
+ return GDMono::PROTECTED;
+ case MONO_FIELD_ATTR_PUBLIC:
+ return GDMono::PUBLIC;
+ default:
+ ERR_FAIL_V(GDMono::PRIVATE);
+ }
+}
+
+GDMonoField::GDMonoField(MonoClassField *p_raw_field, GDMonoClass *p_owner) {
+ owner = p_owner;
+ mono_field = p_raw_field;
+ name = mono_field_get_name(mono_field);
+ MonoType *field_type = mono_field_get_type(mono_field);
+ type.type_encoding = mono_type_get_type(field_type);
+ MonoClass *field_type_class = mono_class_from_mono_type(field_type);
+ type.type_class = GDMono::get_singleton()->get_class(field_type_class);
+
+ attrs_fetched = false;
+ attributes = NULL;
+}
+
+GDMonoField::~GDMonoField() {
+ if (attributes) {
+ mono_custom_attrs_free(attributes);
+ }
+}
diff --git a/modules/mono/mono_gd/gd_mono_field.h b/modules/mono/mono_gd/gd_mono_field.h
new file mode 100644
index 0000000000..b7e1942d71
--- /dev/null
+++ b/modules/mono/mono_gd/gd_mono_field.h
@@ -0,0 +1,74 @@
+/*************************************************************************/
+/* gd_mono_field.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+#ifndef GDMONOFIELD_H
+#define GDMONOFIELD_H
+
+#include "gd_mono.h"
+#include "gd_mono_header.h"
+
+class GDMonoField {
+ GDMonoClass *owner;
+ MonoClassField *mono_field;
+
+ String name;
+ ManagedType type;
+
+ bool attrs_fetched;
+ MonoCustomAttrInfo *attributes;
+
+public:
+ _FORCE_INLINE_ String get_name() const { return name; }
+ _FORCE_INLINE_ ManagedType get_type() const { return type; }
+
+ _FORCE_INLINE_ MonoClassField *get_raw() const { return mono_field; }
+
+ void set_value_raw(MonoObject *p_object, void *p_ptr);
+ void set_value(MonoObject *p_object, const Variant &p_value);
+
+ _FORCE_INLINE_ MonoObject *get_value(MonoObject *p_object) {
+ return mono_field_get_value_object(mono_domain_get(), mono_field, p_object);
+ }
+
+ bool get_bool_value(MonoObject *p_object);
+ int get_int_value(MonoObject *p_object);
+ String get_string_value(MonoObject *p_object);
+
+ bool has_attribute(GDMonoClass *p_attr_class);
+ MonoObject *get_attribute(GDMonoClass *p_attr_class);
+ void fetch_attributes();
+
+ bool is_static();
+ GDMono::MemberVisibility get_visibility();
+
+ GDMonoField(MonoClassField *p_raw_field, GDMonoClass *p_owner);
+ ~GDMonoField();
+};
+
+#endif // GDMONOFIELD_H
diff --git a/modules/mono/mono_gd/gd_mono_header.h b/modules/mono/mono_gd/gd_mono_header.h
new file mode 100644
index 0000000000..803d394f96
--- /dev/null
+++ b/modules/mono/mono_gd/gd_mono_header.h
@@ -0,0 +1,59 @@
+/*************************************************************************/
+/* gd_mono_header.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+#ifndef GD_MONO_HEADER_H
+#define GD_MONO_HEADER_H
+
+#include "int_types.h"
+
+class GDMonoAssembly;
+class GDMonoClass;
+class GDMonoMethod;
+class GDMonoField;
+
+struct ManagedType {
+ int type_encoding;
+ GDMonoClass *type_class;
+
+ ManagedType() {
+ type_class = 0;
+ }
+};
+
+typedef union {
+ uint32_t _uint32;
+ float _float;
+} mono_float;
+
+typedef union {
+ uint64_t _uint64;
+ float _double;
+} mono_double;
+
+#endif // GD_MONO_HEADER_H
diff --git a/modules/mono/mono_gd/gd_mono_internals.cpp b/modules/mono/mono_gd/gd_mono_internals.cpp
new file mode 100644
index 0000000000..cfe2148b80
--- /dev/null
+++ b/modules/mono/mono_gd/gd_mono_internals.cpp
@@ -0,0 +1,66 @@
+/*************************************************************************/
+/* godotsharp_internals.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+#include "gd_mono_internals.h"
+
+#include "../csharp_script.h"
+#include "../mono_gc_handle.h"
+#include "gd_mono_utils.h"
+
+namespace GDMonoInternals {
+
+void tie_managed_to_unmanaged(MonoObject *managed, Object *unmanaged) {
+
+ // This method should not fail
+
+ CRASH_COND(!unmanaged);
+
+ // All mono objects created from the managed world (e.g.: `new Player()`)
+ // need to have a CSharpScript in order for their methods to be callable from the unmanaged side
+
+ Reference *ref = Object::cast_to<Reference>(unmanaged);
+
+ GDMonoClass *klass = GDMonoUtils::get_object_class(managed);
+
+ CRASH_COND(!klass);
+
+ Ref<MonoGCHandle> gchandle = ref ? MonoGCHandle::create_weak(managed) :
+ MonoGCHandle::create_strong(managed);
+
+ Ref<CSharpScript> script = CSharpScript::create_for_managed_type(klass);
+
+ CRASH_COND(script.is_null());
+
+ ScriptInstance *si = CSharpInstance::create_for_managed_type(unmanaged, script.ptr(), gchandle);
+
+ unmanaged->set_script_and_instance(script.get_ref_ptr(), si);
+
+ return;
+}
+}
diff --git a/modules/mono/mono_gd/gd_mono_internals.h b/modules/mono/mono_gd/gd_mono_internals.h
new file mode 100644
index 0000000000..6bdf4a6c46
--- /dev/null
+++ b/modules/mono/mono_gd/gd_mono_internals.h
@@ -0,0 +1,42 @@
+/*************************************************************************/
+/* godotsharp_internals.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+#ifndef GD_MONO_INTERNALS_H
+#define GD_MONO_INTERNALS_H
+
+#include <mono/jit/jit.h>
+
+#include "core/object.h"
+
+namespace GDMonoInternals {
+
+void tie_managed_to_unmanaged(MonoObject *managed, Object *unmanaged);
+}
+
+#endif // GD_MONO_INTERNALS_H
diff --git a/modules/mono/mono_gd/gd_mono_log.cpp b/modules/mono/mono_gd/gd_mono_log.cpp
new file mode 100644
index 0000000000..e473348897
--- /dev/null
+++ b/modules/mono/mono_gd/gd_mono_log.cpp
@@ -0,0 +1,175 @@
+/*************************************************************************/
+/* gd_mono_log.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+#include "gd_mono_log.h"
+
+#include <mono/utils/mono-logger.h>
+#include <stdlib.h> // abort
+
+#include "os/dir_access.h"
+#include "os/os.h"
+
+#include "../godotsharp_dirs.h"
+
+static int log_level_get_id(const char *p_log_level) {
+
+ const char *valid_log_levels[] = { "error", "critical", "warning", "message", "info", "debug", NULL };
+
+ int i = 0;
+ while (valid_log_levels[i]) {
+ if (!strcmp(valid_log_levels[i], p_log_level))
+ return i;
+ i++;
+ }
+
+ return -1;
+}
+
+void gdmono_MonoLogCallback(const char *log_domain, const char *log_level, const char *message, mono_bool fatal, void *user_data) {
+
+ FileAccess *f = GDMonoLog::get_singleton()->get_log_file();
+
+ if (GDMonoLog::get_singleton()->get_log_level_id() >= log_level_get_id(log_level)) {
+ String text(message);
+ text += " (in domain ";
+ text += log_domain;
+ if (log_level) {
+ text += ", ";
+ text += log_level;
+ }
+ text += ")\n";
+
+ f->seek_end();
+ f->store_string(text);
+ }
+
+ if (fatal) {
+ ERR_PRINTS("Mono: FALTAL ERROR, ABORTING! Logfile: " + GDMonoLog::get_singleton()->get_log_file_path() + "\n");
+ abort();
+ }
+}
+
+GDMonoLog *GDMonoLog::singleton = NULL;
+
+bool GDMonoLog::_try_create_logs_dir(const String &p_logs_dir) {
+
+ if (!DirAccess::exists(p_logs_dir)) {
+ DirAccessRef diraccess = DirAccess::create(DirAccess::ACCESS_FILESYSTEM);
+ ERR_FAIL_COND_V(!diraccess, false);
+ Error logs_mkdir_err = diraccess->make_dir_recursive(p_logs_dir);
+ ERR_EXPLAIN("Failed to create mono logs directory");
+ ERR_FAIL_COND_V(logs_mkdir_err != OK, false);
+ }
+
+ return true;
+}
+
+void GDMonoLog::_open_log_file(const String &p_file_path) {
+
+ log_file = FileAccess::open(p_file_path, FileAccess::WRITE);
+
+ ERR_EXPLAIN("Failed to create log file");
+ ERR_FAIL_COND(!log_file);
+}
+
+void GDMonoLog::_delete_old_log_files(const String &p_logs_dir) {
+
+ static const uint64_t MAX_SECS = 5 * 86400;
+
+ DirAccessRef da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM);
+ ERR_FAIL_COND(!da);
+
+ Error err = da->change_dir(p_logs_dir);
+ ERR_FAIL_COND(err != OK);
+
+ ERR_FAIL_COND(da->list_dir_begin() != OK);
+
+ String current;
+ while ((current = da->get_next()).length()) {
+ if (da->current_is_dir())
+ continue;
+ if (!current.ends_with(".txt"))
+ continue;
+
+ String name = current.get_basename();
+ uint64_t unixtime = (uint64_t)name.to_int64();
+
+ if (OS::get_singleton()->get_unix_time() - unixtime > MAX_SECS) {
+ da->remove(current);
+ }
+ }
+
+ da->list_dir_end();
+}
+
+void GDMonoLog::initialize() {
+
+#ifdef DEBUG_ENABLED
+ const char *log_level = "debug";
+#else
+ const char *log_level = "warning";
+#endif
+
+ String logs_dir = GodotSharpDirs::get_mono_logs_dir();
+
+ if (_try_create_logs_dir(logs_dir)) {
+ _delete_old_log_files(logs_dir);
+
+ log_file_path = logs_dir.plus_file(String::num_int64(OS::get_singleton()->get_unix_time()) + ".txt");
+ _open_log_file(log_file_path);
+ }
+
+ mono_trace_set_level_string(log_level);
+ log_level_id = log_level_get_id(log_level);
+
+ if (log_file) {
+ if (OS::get_singleton()->is_stdout_verbose())
+ OS::get_singleton()->print(String("Mono: Logfile is " + log_file_path + "\n").utf8());
+ mono_trace_set_log_handler(gdmono_MonoLogCallback, this);
+ } else {
+ OS::get_singleton()->printerr("Mono: No log file, using default log handler\n");
+ }
+}
+
+GDMonoLog::GDMonoLog() {
+
+ singleton = this;
+
+ log_level_id = -1;
+}
+
+GDMonoLog::~GDMonoLog() {
+
+ singleton = NULL;
+
+ if (log_file) {
+ log_file->close();
+ memdelete(log_file);
+ }
+}
diff --git a/modules/mono/mono_gd/gd_mono_log.h b/modules/mono/mono_gd/gd_mono_log.h
new file mode 100644
index 0000000000..497f1e5317
--- /dev/null
+++ b/modules/mono/mono_gd/gd_mono_log.h
@@ -0,0 +1,61 @@
+/*************************************************************************/
+/* gd_mono_log.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+#ifndef GD_MONO_LOG_H
+#define GD_MONO_LOG_H
+
+#include "os/file_access.h"
+
+class GDMonoLog {
+
+ int log_level_id;
+
+ FileAccess *log_file;
+ String log_file_path;
+
+ bool _try_create_logs_dir(const String &p_logs_dir);
+ void _open_log_file(const String &p_file_path);
+ void _delete_old_log_files(const String &p_logs_dir);
+
+ static GDMonoLog *singleton;
+
+public:
+ _FORCE_INLINE_ static GDMonoLog *get_singleton() { return singleton; }
+
+ void initialize();
+
+ _FORCE_INLINE_ FileAccess *get_log_file() { return log_file; }
+ _FORCE_INLINE_ String get_log_file_path() { return log_file_path; }
+ _FORCE_INLINE_ int get_log_level_id() { return log_level_id; }
+
+ GDMonoLog();
+ ~GDMonoLog();
+};
+
+#endif // GD_MONO_LOG_H
diff --git a/modules/mono/mono_gd/gd_mono_marshal.cpp b/modules/mono/mono_gd/gd_mono_marshal.cpp
new file mode 100644
index 0000000000..b64915109f
--- /dev/null
+++ b/modules/mono/mono_gd/gd_mono_marshal.cpp
@@ -0,0 +1,845 @@
+/*************************************************************************/
+/* gd_mono_marshal.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+#include "gd_mono_marshal.h"
+
+#include "gd_mono.h"
+#include "gd_mono_class.h"
+
+namespace GDMonoMarshal {
+
+#define RETURN_BOXED_STRUCT(m_t, m_var_in) \
+ { \
+ const m_t &m_in = m_var_in->operator m_t(); \
+ MARSHALLED_OUT(m_t, m_in, raw); \
+ return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(m_t), raw); \
+ }
+
+#define RETURN_UNBOXED_STRUCT(m_t, m_var_in) \
+ { \
+ float *raw = (float *)mono_object_unbox(m_var_in); \
+ MARSHALLED_IN(m_t, raw, ret); \
+ return ret; \
+ }
+
+Variant::Type managed_to_variant_type(const ManagedType &p_type) {
+ switch (p_type.type_encoding) {
+ case MONO_TYPE_BOOLEAN:
+ return Variant::BOOL;
+
+ case MONO_TYPE_I1:
+ return Variant::INT;
+ case MONO_TYPE_I2:
+ return Variant::INT;
+ case MONO_TYPE_I4:
+ return Variant::INT;
+ case MONO_TYPE_I8:
+ return Variant::INT;
+
+ case MONO_TYPE_U1:
+ return Variant::INT;
+ case MONO_TYPE_U2:
+ return Variant::INT;
+ case MONO_TYPE_U4:
+ return Variant::INT;
+ case MONO_TYPE_U8:
+ return Variant::INT;
+
+ case MONO_TYPE_R4:
+ return Variant::REAL;
+ case MONO_TYPE_R8:
+ return Variant::REAL;
+
+ case MONO_TYPE_STRING: {
+ return Variant::STRING;
+ } break;
+
+ case MONO_TYPE_VALUETYPE: {
+ GDMonoClass *tclass = p_type.type_class;
+
+ if (tclass == CACHED_CLASS(Vector2))
+ return Variant::VECTOR2;
+
+ if (tclass == CACHED_CLASS(Rect2))
+ return Variant::RECT2;
+
+ if (tclass == CACHED_CLASS(Transform2D))
+ return Variant::TRANSFORM2D;
+
+ if (tclass == CACHED_CLASS(Vector3))
+ return Variant::VECTOR3;
+
+ if (tclass == CACHED_CLASS(Basis))
+ return Variant::BASIS;
+
+ if (tclass == CACHED_CLASS(Quat))
+ return Variant::QUAT;
+
+ if (tclass == CACHED_CLASS(Transform))
+ return Variant::TRANSFORM;
+
+ if (tclass == CACHED_CLASS(Rect3))
+ return Variant::RECT3;
+
+ if (tclass == CACHED_CLASS(Color))
+ return Variant::COLOR;
+
+ if (tclass == CACHED_CLASS(Plane))
+ return Variant::PLANE;
+ } break;
+
+ case MONO_TYPE_ARRAY:
+ case MONO_TYPE_SZARRAY: {
+ MonoArrayType *array_type = mono_type_get_array_type(GDMonoClass::get_raw_type(p_type.type_class));
+
+ if (array_type->eklass == CACHED_CLASS_RAW(MonoObject))
+ return Variant::ARRAY;
+
+ if (array_type->eklass == CACHED_CLASS_RAW(uint8_t))
+ return Variant::POOL_BYTE_ARRAY;
+
+ if (array_type->eklass == CACHED_CLASS_RAW(int32_t))
+ return Variant::POOL_INT_ARRAY;
+
+ if (array_type->eklass == REAL_T_MONOCLASS)
+ return Variant::POOL_REAL_ARRAY;
+
+ if (array_type->eklass == CACHED_CLASS_RAW(String))
+ return Variant::POOL_STRING_ARRAY;
+
+ if (array_type->eklass == CACHED_CLASS_RAW(Vector2))
+ return Variant::POOL_VECTOR2_ARRAY;
+
+ if (array_type->eklass == CACHED_CLASS_RAW(Vector3))
+ return Variant::POOL_VECTOR3_ARRAY;
+
+ if (array_type->eklass == CACHED_CLASS_RAW(Color))
+ return Variant::POOL_COLOR_ARRAY;
+ } break;
+
+ case MONO_TYPE_CLASS: {
+ GDMonoClass *type_class = p_type.type_class;
+
+ // GodotObject
+ if (CACHED_CLASS(GodotObject)->is_assignable_from(type_class)) {
+ return Variant::OBJECT;
+ }
+
+ if (CACHED_CLASS(NodePath) == type_class) {
+ return Variant::NODE_PATH;
+ }
+
+ if (CACHED_CLASS(RID) == type_class) {
+ return Variant::_RID;
+ }
+ } break;
+
+ case MONO_TYPE_GENERICINST: {
+ if (CACHED_RAW_MONO_CLASS(Dictionary) == p_type.type_class->get_raw()) {
+ return Variant::DICTIONARY;
+ }
+ } break;
+ }
+
+ // No error, the caller will decide what to do in this case
+ return Variant::NIL;
+}
+
+String mono_to_utf8_string(MonoString *p_mono_string) {
+ MonoError error;
+ char *utf8 = mono_string_to_utf8_checked(p_mono_string, &error);
+
+ ERR_EXPLAIN("Conversion of MonoString to UTF8 failed.");
+ ERR_FAIL_COND_V(!mono_error_ok(&error), String());
+
+ String ret = String::utf8(utf8);
+
+ mono_free(utf8);
+
+ return ret;
+}
+
+String mono_to_utf16_string(MonoString *p_mono_string) {
+ int len = mono_string_length(p_mono_string);
+ String ret;
+
+ if (len == 0)
+ return ret;
+
+ ret.resize(len + 1);
+ ret.set(len, 0);
+
+ CharType *src = (CharType *)mono_string_chars(p_mono_string);
+ CharType *dst = &(ret.operator[](0));
+
+ for (int i = 0; i < len; i++) {
+ dst[i] = src[i];
+ }
+
+ return ret;
+}
+
+MonoObject *variant_to_mono_object(const Variant *p_var) {
+ ManagedType type;
+
+ type.type_encoding = MONO_TYPE_OBJECT;
+
+ return variant_to_mono_object(p_var, type);
+}
+
+MonoObject *variant_to_mono_object(const Variant *p_var, const ManagedType &p_type) {
+ switch (p_type.type_encoding) {
+ case MONO_TYPE_BOOLEAN: {
+ MonoBoolean val = p_var->operator bool();
+ return BOX_BOOLEAN(val);
+ }
+
+ case MONO_TYPE_I1: {
+ char val = p_var->operator signed char();
+ return BOX_INT8(val);
+ }
+ case MONO_TYPE_I2: {
+ short val = p_var->operator signed short();
+ return BOX_INT16(val);
+ }
+ case MONO_TYPE_I4: {
+ int val = p_var->operator signed int();
+ return BOX_INT32(val);
+ }
+ case MONO_TYPE_I8: {
+ int64_t val = p_var->operator int64_t();
+ return BOX_INT64(val);
+ }
+
+ case MONO_TYPE_U1: {
+ char val = p_var->operator unsigned char();
+ return BOX_UINT8(val);
+ }
+ case MONO_TYPE_U2: {
+ short val = p_var->operator unsigned short();
+ return BOX_UINT16(val);
+ }
+ case MONO_TYPE_U4: {
+ int val = p_var->operator unsigned int();
+ return BOX_UINT32(val);
+ }
+ case MONO_TYPE_U8: {
+ uint64_t val = p_var->operator uint64_t();
+ return BOX_UINT64(val);
+ }
+
+ case MONO_TYPE_R4: {
+ float val = p_var->operator float();
+ return BOX_FLOAT(val);
+ }
+ case MONO_TYPE_R8: {
+ double val = p_var->operator double();
+ return BOX_DOUBLE(val);
+ }
+
+ case MONO_TYPE_STRING: {
+ return (MonoObject *)mono_string_from_godot(p_var->operator String());
+ } break;
+
+ case MONO_TYPE_VALUETYPE: {
+ GDMonoClass *tclass = p_type.type_class;
+
+ if (tclass == CACHED_CLASS(Vector2))
+ RETURN_BOXED_STRUCT(Vector2, p_var);
+
+ if (tclass == CACHED_CLASS(Rect2))
+ RETURN_BOXED_STRUCT(Rect2, p_var);
+
+ if (tclass == CACHED_CLASS(Transform2D))
+ RETURN_BOXED_STRUCT(Transform2D, p_var);
+
+ if (tclass == CACHED_CLASS(Vector3))
+ RETURN_BOXED_STRUCT(Vector3, p_var);
+
+ if (tclass == CACHED_CLASS(Basis))
+ RETURN_BOXED_STRUCT(Basis, p_var);
+
+ if (tclass == CACHED_CLASS(Quat))
+ RETURN_BOXED_STRUCT(Quat, p_var);
+
+ if (tclass == CACHED_CLASS(Transform))
+ RETURN_BOXED_STRUCT(Transform, p_var);
+
+ if (tclass == CACHED_CLASS(Rect3))
+ RETURN_BOXED_STRUCT(Rect3, p_var);
+
+ if (tclass == CACHED_CLASS(Color))
+ RETURN_BOXED_STRUCT(Color, p_var);
+
+ if (tclass == CACHED_CLASS(Plane))
+ RETURN_BOXED_STRUCT(Plane, p_var);
+ } break;
+
+ case MONO_TYPE_ARRAY:
+ case MONO_TYPE_SZARRAY: {
+ MonoArrayType *array_type = mono_type_get_array_type(GDMonoClass::get_raw_type(p_type.type_class));
+
+ if (array_type->eklass == CACHED_CLASS_RAW(MonoObject))
+ return (MonoObject *)Array_to_mono_array(p_var->operator Array());
+
+ if (array_type->eklass == CACHED_CLASS_RAW(uint8_t))
+ return (MonoObject *)PoolByteArray_to_mono_array(p_var->operator PoolByteArray());
+
+ if (array_type->eklass == CACHED_CLASS_RAW(int32_t))
+ return (MonoObject *)PoolIntArray_to_mono_array(p_var->operator PoolIntArray());
+
+ if (array_type->eklass == REAL_T_MONOCLASS)
+ return (MonoObject *)PoolRealArray_to_mono_array(p_var->operator PoolRealArray());
+
+ if (array_type->eklass == CACHED_CLASS_RAW(String))
+ return (MonoObject *)PoolStringArray_to_mono_array(p_var->operator PoolStringArray());
+
+ if (array_type->eklass == CACHED_CLASS_RAW(Vector2))
+ return (MonoObject *)PoolVector2Array_to_mono_array(p_var->operator PoolVector2Array());
+
+ if (array_type->eklass == CACHED_CLASS_RAW(Vector3))
+ return (MonoObject *)PoolVector3Array_to_mono_array(p_var->operator PoolVector3Array());
+
+ if (array_type->eklass == CACHED_CLASS_RAW(Color))
+ return (MonoObject *)PoolColorArray_to_mono_array(p_var->operator PoolColorArray());
+
+ ERR_EXPLAIN(String() + "Attempted to convert Variant to a managed array of unmarshallable element type.");
+ ERR_FAIL_V(NULL);
+ } break;
+
+ case MONO_TYPE_CLASS: {
+ GDMonoClass *type_class = p_type.type_class;
+
+ // GodotObject
+ if (CACHED_CLASS(GodotObject)->is_assignable_from(type_class)) {
+ return GDMonoUtils::unmanaged_get_managed(p_var->operator Object *());
+ }
+
+ if (CACHED_CLASS(NodePath) == type_class) {
+ return GDMonoUtils::create_managed_from(p_var->operator NodePath());
+ }
+
+ if (CACHED_CLASS(RID) == type_class) {
+ return GDMonoUtils::create_managed_from(p_var->operator RID());
+ }
+ } break;
+ case MONO_TYPE_OBJECT: {
+ // Variant
+ switch (p_var->get_type()) {
+ case Variant::BOOL: {
+ MonoBoolean val = p_var->operator bool();
+ return BOX_BOOLEAN(val);
+ }
+ case Variant::INT: {
+ int val = p_var->operator signed int();
+ return BOX_INT32(val);
+ }
+ case Variant::REAL: {
+#ifdef REAL_T_IS_DOUBLE
+ double val = p_var->operator double();
+ return BOX_DOUBLE(val);
+#else
+ float val = p_var->operator float();
+ return BOX_FLOAT(val);
+#endif
+ }
+ case Variant::STRING:
+ return (MonoObject *)mono_string_from_godot(p_var->operator String());
+ case Variant::VECTOR2:
+ RETURN_BOXED_STRUCT(Vector2, p_var);
+ case Variant::RECT2:
+ RETURN_BOXED_STRUCT(Rect2, p_var);
+ case Variant::VECTOR3:
+ RETURN_BOXED_STRUCT(Vector3, p_var);
+ case Variant::TRANSFORM2D:
+ RETURN_BOXED_STRUCT(Transform2D, p_var);
+ case Variant::PLANE:
+ RETURN_BOXED_STRUCT(Plane, p_var);
+ case Variant::QUAT:
+ RETURN_BOXED_STRUCT(Quat, p_var);
+ case Variant::RECT3:
+ RETURN_BOXED_STRUCT(Rect3, p_var);
+ case Variant::BASIS:
+ RETURN_BOXED_STRUCT(Basis, p_var);
+ case Variant::TRANSFORM:
+ RETURN_BOXED_STRUCT(Transform, p_var);
+ case Variant::COLOR:
+ RETURN_BOXED_STRUCT(Color, p_var);
+ case Variant::NODE_PATH:
+ return GDMonoUtils::create_managed_from(p_var->operator NodePath());
+ case Variant::_RID:
+ return GDMonoUtils::create_managed_from(p_var->operator RID());
+ case Variant::OBJECT: {
+ return GDMonoUtils::unmanaged_get_managed(p_var->operator Object *());
+ }
+ case Variant::DICTIONARY:
+ return Dictionary_to_mono_object(p_var->operator Dictionary());
+ case Variant::ARRAY:
+ return (MonoObject *)Array_to_mono_array(p_var->operator Array());
+ case Variant::POOL_BYTE_ARRAY:
+ return (MonoObject *)PoolByteArray_to_mono_array(p_var->operator PoolByteArray());
+ case Variant::POOL_INT_ARRAY:
+ return (MonoObject *)PoolIntArray_to_mono_array(p_var->operator PoolIntArray());
+ case Variant::POOL_REAL_ARRAY:
+ return (MonoObject *)PoolRealArray_to_mono_array(p_var->operator PoolRealArray());
+ case Variant::POOL_STRING_ARRAY:
+ return (MonoObject *)PoolStringArray_to_mono_array(p_var->operator PoolStringArray());
+ case Variant::POOL_VECTOR2_ARRAY:
+ return (MonoObject *)PoolVector2Array_to_mono_array(p_var->operator PoolVector2Array());
+ case Variant::POOL_VECTOR3_ARRAY:
+ return (MonoObject *)PoolVector3Array_to_mono_array(p_var->operator PoolVector3Array());
+ case Variant::POOL_COLOR_ARRAY:
+ return (MonoObject *)PoolColorArray_to_mono_array(p_var->operator PoolColorArray());
+ default:
+ return NULL;
+ }
+ break;
+ case MONO_TYPE_GENERICINST: {
+ if (CACHED_RAW_MONO_CLASS(Dictionary) == p_type.type_class->get_raw()) {
+ return Dictionary_to_mono_object(p_var->operator Dictionary());
+ }
+ } break;
+ } break;
+ }
+
+ ERR_EXPLAIN(String() + "Attempted to convert Variant to an unmarshallable managed type. Name: \'" +
+ p_type.type_class->get_name() + "\' Encoding: " + itos(p_type.type_encoding));
+ ERR_FAIL_V(NULL);
+}
+
+Variant mono_object_to_variant(MonoObject *p_obj) {
+ if (!p_obj)
+ return Variant();
+
+ GDMonoClass *tclass = GDMono::get_singleton()->get_class(mono_object_get_class(p_obj));
+ ERR_FAIL_COND_V(!tclass, Variant());
+
+ MonoType *raw_type = tclass->get_raw_type(tclass);
+
+ ManagedType type;
+
+ type.type_encoding = mono_type_get_type(raw_type);
+ type.type_class = tclass;
+
+ return mono_object_to_variant(p_obj, type);
+}
+
+Variant mono_object_to_variant(MonoObject *p_obj, const ManagedType &p_type) {
+ switch (p_type.type_encoding) {
+ case MONO_TYPE_BOOLEAN:
+ return (bool)unbox<MonoBoolean>(p_obj);
+
+ case MONO_TYPE_I1:
+ return unbox<int8_t>(p_obj);
+ case MONO_TYPE_I2:
+ return unbox<int16_t>(p_obj);
+ case MONO_TYPE_I4:
+ return unbox<int32_t>(p_obj);
+ case MONO_TYPE_I8:
+ return unbox<int64_t>(p_obj);
+
+ case MONO_TYPE_U1:
+ return unbox<uint8_t>(p_obj);
+ case MONO_TYPE_U2:
+ return unbox<uint16_t>(p_obj);
+ case MONO_TYPE_U4:
+ return unbox<uint32_t>(p_obj);
+ case MONO_TYPE_U8:
+ return unbox<uint64_t>(p_obj);
+
+ case MONO_TYPE_R4:
+ return unbox<float>(p_obj);
+ case MONO_TYPE_R8:
+ return unbox<double>(p_obj);
+
+ case MONO_TYPE_STRING: {
+ String str = mono_string_to_godot((MonoString *)p_obj);
+ return str;
+ } break;
+
+ case MONO_TYPE_VALUETYPE: {
+ GDMonoClass *tclass = p_type.type_class;
+
+ if (tclass == CACHED_CLASS(Vector2))
+ RETURN_UNBOXED_STRUCT(Vector2, p_obj);
+
+ if (tclass == CACHED_CLASS(Rect2))
+ RETURN_UNBOXED_STRUCT(Rect2, p_obj);
+
+ if (tclass == CACHED_CLASS(Transform2D))
+ RETURN_UNBOXED_STRUCT(Transform2D, p_obj);
+
+ if (tclass == CACHED_CLASS(Vector3))
+ RETURN_UNBOXED_STRUCT(Vector3, p_obj);
+
+ if (tclass == CACHED_CLASS(Basis))
+ RETURN_UNBOXED_STRUCT(Basis, p_obj);
+
+ if (tclass == CACHED_CLASS(Quat))
+ RETURN_UNBOXED_STRUCT(Quat, p_obj);
+
+ if (tclass == CACHED_CLASS(Transform))
+ RETURN_UNBOXED_STRUCT(Transform, p_obj);
+
+ if (tclass == CACHED_CLASS(Rect3))
+ RETURN_UNBOXED_STRUCT(Rect3, p_obj);
+
+ if (tclass == CACHED_CLASS(Color))
+ RETURN_UNBOXED_STRUCT(Color, p_obj);
+
+ if (tclass == CACHED_CLASS(Plane))
+ RETURN_UNBOXED_STRUCT(Plane, p_obj);
+ } break;
+
+ case MONO_TYPE_ARRAY:
+ case MONO_TYPE_SZARRAY: {
+ MonoArrayType *array_type = mono_type_get_array_type(GDMonoClass::get_raw_type(p_type.type_class));
+
+ if (array_type->eklass == CACHED_CLASS_RAW(MonoObject))
+ return mono_array_to_Array((MonoArray *)p_obj);
+
+ if (array_type->eklass == CACHED_CLASS_RAW(uint8_t))
+ return mono_array_to_PoolByteArray((MonoArray *)p_obj);
+
+ if (array_type->eklass == CACHED_CLASS_RAW(int32_t))
+ return mono_array_to_PoolIntArray((MonoArray *)p_obj);
+
+ if (array_type->eklass == REAL_T_MONOCLASS)
+ return mono_array_to_PoolRealArray((MonoArray *)p_obj);
+
+ if (array_type->eklass == CACHED_CLASS_RAW(String))
+ return mono_array_to_PoolStringArray((MonoArray *)p_obj);
+
+ if (array_type->eklass == CACHED_CLASS_RAW(Vector2))
+ return mono_array_to_PoolVector2Array((MonoArray *)p_obj);
+
+ if (array_type->eklass == CACHED_CLASS_RAW(Vector3))
+ return mono_array_to_PoolVector3Array((MonoArray *)p_obj);
+
+ if (array_type->eklass == CACHED_CLASS_RAW(Color))
+ return mono_array_to_PoolColorArray((MonoArray *)p_obj);
+
+ ERR_EXPLAIN(String() + "Attempted to convert a managed array of unmarshallable element type to Variant.");
+ ERR_FAIL_V(Variant());
+ } break;
+
+ case MONO_TYPE_CLASS: {
+ GDMonoClass *type_class = p_type.type_class;
+
+ // GodotObject
+ if (CACHED_CLASS(GodotObject)->is_assignable_from(type_class)) {
+ Object *ptr = unbox<Object *>(CACHED_FIELD(GodotObject, ptr)->get_value(p_obj));
+ return ptr ? Variant(ptr) : Variant();
+ }
+
+ if (CACHED_CLASS(NodePath) == type_class) {
+ NodePath *ptr = unbox<NodePath *>(CACHED_FIELD(NodePath, ptr)->get_value(p_obj));
+ return ptr ? Variant(*ptr) : Variant();
+ }
+
+ if (CACHED_CLASS(RID) == type_class) {
+ RID *ptr = unbox<RID *>(CACHED_FIELD(RID, ptr)->get_value(p_obj));
+ return ptr ? Variant(*ptr) : Variant();
+ }
+ } break;
+
+ case MONO_TYPE_GENERICINST: {
+ if (CACHED_RAW_MONO_CLASS(Dictionary) == p_type.type_class->get_raw()) {
+ return mono_object_to_Dictionary(p_obj);
+ }
+ } break;
+ }
+
+ ERR_EXPLAIN(String() + "Attempted to convert an unmarshallable managed type to Variant. Name: \'" +
+ p_type.type_class->get_name() + "\' Encoding: " + itos(p_type.type_encoding));
+ ERR_FAIL_V(Variant());
+}
+
+MonoArray *Array_to_mono_array(const Array &p_array) {
+ MonoArray *ret = mono_array_new(mono_domain_get(), CACHED_CLASS_RAW(MonoObject), p_array.size());
+
+ for (int i = 0; i < p_array.size(); i++) {
+ MonoObject *boxed = variant_to_mono_object(p_array[i]);
+ mono_array_set(ret, MonoObject *, i, boxed);
+ }
+
+ return ret;
+}
+
+Array mono_array_to_Array(MonoArray *p_array) {
+ Array ret;
+ int length = mono_array_length(p_array);
+
+ for (int i = 0; i < length; i++) {
+ MonoObject *elem = mono_array_get(p_array, MonoObject *, i);
+ ret.push_back(mono_object_to_variant(elem));
+ }
+
+ return ret;
+}
+
+// TODO Optimize reading/writing from/to PoolArrays
+
+MonoArray *PoolIntArray_to_mono_array(const PoolIntArray &p_array) {
+ MonoArray *ret = mono_array_new(mono_domain_get(), CACHED_CLASS_RAW(int32_t), p_array.size());
+
+ for (int i = 0; i < p_array.size(); i++) {
+ mono_array_set(ret, int32_t, i, p_array[i]);
+ }
+
+ return ret;
+}
+
+PoolIntArray mono_array_to_PoolIntArray(MonoArray *p_array) {
+ PoolIntArray ret;
+ int length = mono_array_length(p_array);
+
+ for (int i = 0; i < length; i++) {
+ int32_t elem = mono_array_get(p_array, int32_t, i);
+ ret.push_back(elem);
+ }
+
+ return ret;
+}
+
+MonoArray *PoolByteArray_to_mono_array(const PoolByteArray &p_array) {
+ MonoArray *ret = mono_array_new(mono_domain_get(), CACHED_CLASS_RAW(uint8_t), p_array.size());
+
+ for (int i = 0; i < p_array.size(); i++) {
+ mono_array_set(ret, uint8_t, i, p_array[i]);
+ }
+
+ return ret;
+}
+
+PoolByteArray mono_array_to_PoolByteArray(MonoArray *p_array) {
+ PoolByteArray ret;
+ int length = mono_array_length(p_array);
+
+ for (int i = 0; i < length; i++) {
+ uint8_t elem = mono_array_get(p_array, uint8_t, i);
+ ret.push_back(elem);
+ }
+
+ return ret;
+}
+
+MonoArray *PoolRealArray_to_mono_array(const PoolRealArray &p_array) {
+ MonoArray *ret = mono_array_new(mono_domain_get(), REAL_T_MONOCLASS, p_array.size());
+
+ for (int i = 0; i < p_array.size(); i++) {
+ mono_array_set(ret, real_t, i, p_array[i]);
+ }
+
+ return ret;
+}
+
+PoolRealArray mono_array_to_PoolRealArray(MonoArray *p_array) {
+ PoolRealArray ret;
+ int length = mono_array_length(p_array);
+
+ for (int i = 0; i < length; i++) {
+ real_t elem = mono_array_get(p_array, real_t, i);
+ ret.push_back(elem);
+ }
+
+ return ret;
+}
+
+MonoArray *PoolStringArray_to_mono_array(const PoolStringArray &p_array) {
+ MonoArray *ret = mono_array_new(mono_domain_get(), CACHED_CLASS_RAW(String), p_array.size());
+
+ for (int i = 0; i < p_array.size(); i++) {
+ MonoString *boxed = mono_string_from_godot(p_array[i]);
+ mono_array_set(ret, MonoString *, i, boxed);
+ }
+
+ return ret;
+}
+
+PoolStringArray mono_array_to_PoolStringArray(MonoArray *p_array) {
+ PoolStringArray ret;
+ int length = mono_array_length(p_array);
+
+ for (int i = 0; i < length; i++) {
+ MonoString *elem = mono_array_get(p_array, MonoString *, i);
+ ret.push_back(mono_string_to_godot(elem));
+ }
+
+ return ret;
+}
+
+MonoArray *PoolColorArray_to_mono_array(const PoolColorArray &p_array) {
+ MonoArray *ret = mono_array_new(mono_domain_get(), CACHED_CLASS_RAW(Color), p_array.size());
+
+ for (int i = 0; i < p_array.size(); i++) {
+#ifdef YOLOCOPY
+ mono_array_set(ret, Color, i, p_array[i]);
+#else
+ real_t *raw = (real_t *)mono_array_addr_with_size(ret, sizeof(real_t) * 4, i);
+ const Color &elem = p_array[i];
+ raw[0] = elem.r;
+ raw[4] = elem.g;
+ raw[8] = elem.b;
+ raw[12] = elem.a;
+#endif
+ }
+
+ return ret;
+}
+
+PoolColorArray mono_array_to_PoolColorArray(MonoArray *p_array) {
+ PoolColorArray ret;
+ int length = mono_array_length(p_array);
+
+ for (int i = 0; i < length; i++) {
+ real_t *raw_elem = mono_array_get(p_array, real_t *, i);
+ MARSHALLED_IN(Color, raw_elem, elem);
+ ret.push_back(elem);
+ }
+
+ return ret;
+}
+
+MonoArray *PoolVector2Array_to_mono_array(const PoolVector2Array &p_array) {
+ MonoArray *ret = mono_array_new(mono_domain_get(), CACHED_CLASS_RAW(Vector2), p_array.size());
+
+ for (int i = 0; i < p_array.size(); i++) {
+#ifdef YOLOCOPY
+ mono_array_set(ret, Vector2, i, p_array[i]);
+#else
+ real_t *raw = (real_t *)mono_array_addr_with_size(ret, sizeof(real_t) * 2, i);
+ const Vector2 &elem = p_array[i];
+ raw[0] = elem.x;
+ raw[4] = elem.y;
+#endif
+ }
+
+ return ret;
+}
+
+PoolVector2Array mono_array_to_PoolVector2Array(MonoArray *p_array) {
+ PoolVector2Array ret;
+ int length = mono_array_length(p_array);
+
+ for (int i = 0; i < length; i++) {
+ real_t *raw_elem = mono_array_get(p_array, real_t *, i);
+ MARSHALLED_IN(Vector2, raw_elem, elem);
+ ret.push_back(elem);
+ }
+
+ return ret;
+}
+
+MonoArray *PoolVector3Array_to_mono_array(const PoolVector3Array &p_array) {
+ MonoArray *ret = mono_array_new(mono_domain_get(), CACHED_CLASS_RAW(Vector3), p_array.size());
+
+ for (int i = 0; i < p_array.size(); i++) {
+#ifdef YOLOCOPY
+ mono_array_set(ret, Vector3, i, p_array[i]);
+#else
+ real_t *raw = (real_t *)mono_array_addr_with_size(ret, sizeof(real_t) * 3, i);
+ const Vector3 &elem = p_array[i];
+ raw[0] = elem.x;
+ raw[4] = elem.y;
+ raw[8] = elem.z;
+#endif
+ }
+
+ return ret;
+}
+
+PoolVector3Array mono_array_to_PoolVector3Array(MonoArray *p_array) {
+ PoolVector3Array ret;
+ int length = mono_array_length(p_array);
+
+ for (int i = 0; i < length; i++) {
+ real_t *raw_elem = mono_array_get(p_array, real_t *, i);
+ MARSHALLED_IN(Vector3, raw_elem, elem);
+ ret.push_back(elem);
+ }
+
+ return ret;
+}
+
+MonoObject *Dictionary_to_mono_object(const Dictionary &p_dict) {
+ MonoArray *keys = mono_array_new(mono_domain_get(), CACHED_CLASS_RAW(MonoObject), p_dict.size());
+ MonoArray *values = mono_array_new(mono_domain_get(), CACHED_CLASS_RAW(MonoObject), p_dict.size());
+
+ int i = 0;
+ const Variant *dkey = NULL;
+ while ((dkey = p_dict.next(dkey))) {
+ mono_array_set(keys, MonoObject *, i, variant_to_mono_object(dkey));
+ mono_array_set(values, MonoObject *, i, variant_to_mono_object(p_dict[*dkey]));
+ i++;
+ }
+
+ GDMonoUtils::MarshalUtils_ArraysToDict arrays_to_dict = CACHED_METHOD_THUNK(MarshalUtils, ArraysToDictionary);
+
+ MonoObject *ex = NULL;
+ MonoObject *ret = arrays_to_dict(keys, values, &ex);
+
+ if (ex) {
+ mono_print_unhandled_exception(ex);
+ ERR_FAIL_V(NULL);
+ }
+
+ return ret;
+}
+
+Dictionary mono_object_to_Dictionary(MonoObject *p_dict) {
+ Dictionary ret;
+
+ GDMonoUtils::MarshalUtils_DictToArrays dict_to_arrays = CACHED_METHOD_THUNK(MarshalUtils, DictionaryToArrays);
+
+ MonoArray *keys = NULL;
+ MonoArray *values = NULL;
+ MonoObject *ex = NULL;
+ dict_to_arrays(p_dict, &keys, &values, &ex);
+
+ if (ex) {
+ mono_print_unhandled_exception(ex);
+ ERR_FAIL_V(Dictionary());
+ }
+
+ int length = mono_array_length(keys);
+
+ for (int i = 0; i < length; i++) {
+ MonoObject *key_obj = mono_array_get(keys, MonoObject *, i);
+ MonoObject *value_obj = mono_array_get(values, MonoObject *, i);
+
+ Variant key = key_obj ? mono_object_to_variant(key_obj) : Variant();
+ Variant value = value_obj ? mono_object_to_variant(value_obj) : Variant();
+
+ ret[key] = value;
+ }
+
+ return ret;
+}
+}
diff --git a/modules/mono/mono_gd/gd_mono_marshal.h b/modules/mono/mono_gd/gd_mono_marshal.h
new file mode 100644
index 0000000000..38dd22357d
--- /dev/null
+++ b/modules/mono/mono_gd/gd_mono_marshal.h
@@ -0,0 +1,218 @@
+/*************************************************************************/
+/* gd_mono_marshal.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+#ifndef GDMONOMARSHAL_H
+#define GDMONOMARSHAL_H
+
+#include "gd_mono.h"
+#include "gd_mono_utils.h"
+#include "variant.h"
+
+namespace GDMonoMarshal {
+
+template <typename T>
+T unbox(MonoObject *p_obj) {
+ return *(T *)mono_object_unbox(p_obj);
+}
+
+#define BOX_DOUBLE(x) mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(double), &x)
+#define BOX_FLOAT(x) mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(float), &x)
+#define BOX_INT64(x) mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(int64_t), &x)
+#define BOX_INT32(x) mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(int32_t), &x)
+#define BOX_INT16(x) mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(int16_t), &x)
+#define BOX_INT8(x) mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(int8_t), &x)
+#define BOX_UINT64(x) mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(uint64_t), &x)
+#define BOX_UINT32(x) mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(uint32_t), &x)
+#define BOX_UINT16(x) mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(uint16_t), &x)
+#define BOX_UINT8(x) mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(uint8_t), &x)
+#define BOX_BOOLEAN(x) mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(bool), &x)
+#define BOX_PTR(x) mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(IntPtr), x)
+
+Variant::Type managed_to_variant_type(const ManagedType &p_type);
+
+// String
+
+String mono_to_utf8_string(MonoString *p_mono_string);
+String mono_to_utf16_string(MonoString *p_mono_string);
+
+_FORCE_INLINE_ String mono_string_to_godot(MonoString *p_mono_string) {
+ if (sizeof(CharType) == 2)
+ return mono_to_utf16_string(p_mono_string);
+
+ return mono_to_utf8_string(p_mono_string);
+}
+
+_FORCE_INLINE_ MonoString *mono_from_utf8_string(const String &p_string) {
+ return mono_string_new(mono_domain_get(), p_string.utf8().get_data());
+}
+
+_FORCE_INLINE_ MonoString *mono_from_utf16_string(const String &p_string) {
+ return mono_string_from_utf16((mono_unichar2 *)p_string.c_str());
+}
+
+_FORCE_INLINE_ MonoString *mono_string_from_godot(const String &p_string) {
+ if (sizeof(CharType) == 2)
+ return mono_from_utf16_string(p_string);
+
+ return mono_from_utf8_string(p_string);
+}
+
+// Variant
+
+MonoObject *variant_to_mono_object(const Variant *p_var, const ManagedType &p_type);
+MonoObject *variant_to_mono_object(const Variant *p_var);
+
+_FORCE_INLINE_ MonoObject *variant_to_mono_object(Variant p_var) {
+ return variant_to_mono_object(&p_var);
+}
+
+Variant mono_object_to_variant(MonoObject *p_obj);
+Variant mono_object_to_variant(MonoObject *p_obj, const ManagedType &p_type);
+
+// Array
+
+MonoArray *Array_to_mono_array(const Array &p_array);
+Array mono_array_to_Array(MonoArray *p_array);
+
+// PoolIntArray
+
+MonoArray *PoolIntArray_to_mono_array(const PoolIntArray &p_array);
+PoolIntArray mono_array_to_PoolIntArray(MonoArray *p_array);
+
+// PoolByteArray
+
+MonoArray *PoolByteArray_to_mono_array(const PoolByteArray &p_array);
+PoolByteArray mono_array_to_PoolByteArray(MonoArray *p_array);
+
+// PoolRealArray
+
+MonoArray *PoolRealArray_to_mono_array(const PoolRealArray &p_array);
+PoolRealArray mono_array_to_PoolRealArray(MonoArray *p_array);
+
+// PoolStringArray
+
+MonoArray *PoolStringArray_to_mono_array(const PoolStringArray &p_array);
+PoolStringArray mono_array_to_PoolStringArray(MonoArray *p_array);
+
+// PoolColorArray
+
+MonoArray *PoolColorArray_to_mono_array(const PoolColorArray &p_array);
+PoolColorArray mono_array_to_PoolColorArray(MonoArray *p_array);
+
+// PoolVector2Array
+
+MonoArray *PoolVector2Array_to_mono_array(const PoolVector2Array &p_array);
+PoolVector2Array mono_array_to_PoolVector2Array(MonoArray *p_array);
+
+// PoolVector3Array
+
+MonoArray *PoolVector3Array_to_mono_array(const PoolVector3Array &p_array);
+PoolVector3Array mono_array_to_PoolVector3Array(MonoArray *p_array);
+
+// Dictionary
+
+MonoObject *Dictionary_to_mono_object(const Dictionary &p_dict);
+Dictionary mono_object_to_Dictionary(MonoObject *p_dict);
+
+#ifdef YOLO_COPY
+#define MARSHALLED_OUT(m_t, m_in, m_out) m_t *m_out = (m_t *)&m_in;
+#define MARSHALLED_IN(m_t, m_in, m_out) m_t m_out = *reinterpret_cast<m_t *>(m_in);
+#else
+
+// Expects m_in to be of type float*
+
+#define MARSHALLED_OUT(m_t, m_in, m_out) MARSHALLED_OUT_##m_t(m_in, m_out)
+#define MARSHALLED_IN(m_t, m_in, m_out) MARSHALLED_IN_##m_t(m_in, m_out)
+
+// Vector2
+
+#define MARSHALLED_OUT_Vector2(m_in, m_out) real_t m_out[2] = { m_in.x, m_in.y };
+#define MARSHALLED_IN_Vector2(m_in, m_out) Vector2 m_out(m_in[0], m_in[1]);
+
+// Rect2
+
+#define MARSHALLED_OUT_Rect2(m_in, m_out) real_t m_out[4] = { m_in.position.x, m_in.position.y, m_in.size.width, m_in.size.height };
+#define MARSHALLED_IN_Rect2(m_in, m_out) Rect2 m_out(m_in[0], m_in[1], m_in[2], m_in[3]);
+
+// Transform2D
+
+#define MARSHALLED_OUT_Transform2D(m_in, m_out) real_t m_out[6] = { m_in[0].x, m_in[0].y, m_in[1].x, m_in[1].y, m_in[2].x, m_in[2].y };
+#define MARSHALLED_IN_Transform2D(m_in, m_out) Transform2D m_out(m_in[0], m_in[1], m_in[2], m_in[3], m_in[4], m_in[5]);
+
+// Vector3
+
+#define MARSHALLED_OUT_Vector3(m_in, m_out) real_t m_out[3] = { m_in.x, m_in.y, m_in.z };
+#define MARSHALLED_IN_Vector3(m_in, m_out) Vector3 m_out(m_in[0], m_in[1], m_in[2]);
+
+// Basis
+
+#define MARSHALLED_OUT_Basis(m_in, m_out) real_t m_out[9] = { \
+ m_in[0].x, m_in[0].y, m_in[0].z, \
+ m_in[1].x, m_in[1].y, m_in[1].z, \
+ m_in[2].x, m_in[2].y, m_in[2].z \
+};
+#define MARSHALLED_IN_Basis(m_in, m_out) Basis m_out(m_in[0], m_in[1], m_in[2], m_in[3], m_in[4], m_in[5], m_in[6], m_in[7], m_in[8]);
+
+// Quat
+
+#define MARSHALLED_OUT_Quat(m_in, m_out) real_t m_out[4] = { m_in.x, m_in.y, m_in.z, m_in.w };
+#define MARSHALLED_IN_Quat(m_in, m_out) Quat m_out(m_in[0], m_in[1], m_in[2], m_in[3]);
+
+// Transform
+
+#define MARSHALLED_OUT_Transform(m_in, m_out) real_t m_out[12] = { \
+ m_in.basis[0].x, m_in.basis[0].y, m_in.basis[0].z, \
+ m_in.basis[1].x, m_in.basis[1].y, m_in.basis[1].z, \
+ m_in.basis[2].x, m_in.basis[2].y, m_in.basis[2].z, \
+ m_in.origin.x, m_in.origin.y, m_in.origin.z \
+};
+#define MARSHALLED_IN_Transform(m_in, m_out) Transform m_out( \
+ Basis(m_in[0], m_in[1], m_in[2], m_in[3], m_in[4], m_in[5], m_in[6], m_in[7], m_in[8]), \
+ Vector3(m_in[9], m_in[10], m_in[11]));
+
+// Rect3
+
+#define MARSHALLED_OUT_Rect3(m_in, m_out) real_t m_out[6] = { m_in.position.x, m_in.position.y, m_in.position.z, m_in.size.x, m_in.size.y, m_in.size.z };
+#define MARSHALLED_IN_Rect3(m_in, m_out) Rect3 m_out(Vector3(m_in[0], m_in[1], m_in[2]), Vector3(m_in[3], m_in[4], m_in[5]));
+
+// Color
+
+#define MARSHALLED_OUT_Color(m_in, m_out) real_t m_out[4] = { m_in.r, m_in.g, m_in.b, m_in.a };
+#define MARSHALLED_IN_Color(m_in, m_out) Color m_out(m_in[0], m_in[1], m_in[2], m_in[3]);
+
+// Plane
+
+#define MARSHALLED_OUT_Plane(m_in, m_out) real_t m_out[4] = { m_in.normal.x, m_in.normal.y, m_in.normal.z, m_in.d };
+#define MARSHALLED_IN_Plane(m_in, m_out) Plane m_out(m_in[0], m_in[1], m_in[2], m_in[3]);
+
+#endif
+
+} // GDMonoMarshal
+
+#endif // GDMONOMARSHAL_H
diff --git a/modules/mono/mono_gd/gd_mono_method.cpp b/modules/mono/mono_gd/gd_mono_method.cpp
new file mode 100644
index 0000000000..6468e0d3d9
--- /dev/null
+++ b/modules/mono/mono_gd/gd_mono_method.cpp
@@ -0,0 +1,192 @@
+/*************************************************************************/
+/* gd_mono_method.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+#include "gd_mono_method.h"
+
+#include "gd_mono_class.h"
+#include "gd_mono_marshal.h"
+
+void GDMonoMethod::_update_signature() {
+ // Apparently MonoMethodSignature needs not to be freed.
+ // mono_method_signature caches the result, we don't need to cache it ourselves.
+
+ MonoMethodSignature *method_sig = mono_method_signature(mono_method);
+ _update_signature(method_sig);
+}
+
+void GDMonoMethod::_update_signature(MonoMethodSignature *p_method_sig) {
+ is_instance = mono_signature_is_instance(p_method_sig);
+ params_count = mono_signature_get_param_count(p_method_sig);
+
+ MonoType *ret_type = mono_signature_get_return_type(p_method_sig);
+ if (ret_type) {
+ return_type.type_encoding = mono_type_get_type(ret_type);
+
+ if (return_type.type_encoding != MONO_TYPE_VOID) {
+ MonoClass *ret_type_class = mono_class_from_mono_type(ret_type);
+ return_type.type_class = GDMono::get_singleton()->get_class(ret_type_class);
+ }
+ }
+
+ void *iter = NULL;
+ MonoType *param_raw_type;
+ while ((param_raw_type = mono_signature_get_params(p_method_sig, &iter)) != NULL) {
+ ManagedType param_type;
+
+ param_type.type_encoding = mono_type_get_type(param_raw_type);
+
+ if (param_type.type_encoding != MONO_TYPE_VOID) {
+ MonoClass *param_type_class = mono_class_from_mono_type(param_raw_type);
+ param_type.type_class = GDMono::get_singleton()->get_class(param_type_class);
+ }
+
+ param_types.push_back(param_type);
+ }
+}
+
+void *GDMonoMethod::get_thunk() {
+ return mono_method_get_unmanaged_thunk(mono_method);
+}
+
+MonoObject *GDMonoMethod::invoke(MonoObject *p_object, const Variant **p_params, MonoObject **r_exc) {
+ if (get_return_type().type_encoding != MONO_TYPE_VOID || get_parameters_count() > 0) {
+ MonoArray *params = mono_array_new(mono_domain_get(), CACHED_CLASS_RAW(MonoObject), get_parameters_count());
+
+ for (int i = 0; i < params_count; i++) {
+ MonoObject *boxed_param = GDMonoMarshal::variant_to_mono_object(p_params[i], param_types[i]);
+ mono_array_set(params, MonoObject *, i, boxed_param);
+ }
+
+ return mono_runtime_invoke_array(mono_method, p_object, params, r_exc);
+ } else {
+ mono_runtime_invoke(mono_method, p_object, NULL, r_exc);
+ return NULL;
+ }
+}
+
+MonoObject *GDMonoMethod::invoke(MonoObject *p_object, MonoObject **r_exc) {
+ ERR_FAIL_COND_V(get_parameters_count() > 0, NULL);
+ return invoke_raw(p_object, NULL, r_exc);
+}
+
+MonoObject *GDMonoMethod::invoke_raw(MonoObject *p_object, void **p_params, MonoObject **r_exc) {
+ return mono_runtime_invoke(mono_method, p_object, p_params, r_exc);
+}
+
+bool GDMonoMethod::has_attribute(GDMonoClass *p_attr_class) {
+ ERR_FAIL_NULL_V(p_attr_class, false);
+
+ if (!attrs_fetched)
+ fetch_attributes();
+
+ if (!attributes)
+ return false;
+
+ return mono_custom_attrs_has_attr(attributes, p_attr_class->get_raw());
+}
+
+MonoObject *GDMonoMethod::get_attribute(GDMonoClass *p_attr_class) {
+ ERR_FAIL_NULL_V(p_attr_class, NULL);
+
+ if (!attrs_fetched)
+ fetch_attributes();
+
+ if (!attributes)
+ return NULL;
+
+ return mono_custom_attrs_get_attr(attributes, p_attr_class->get_raw());
+}
+
+void GDMonoMethod::fetch_attributes() {
+ ERR_FAIL_COND(attributes != NULL);
+ attributes = mono_custom_attrs_from_method(mono_method);
+ attrs_fetched = true;
+}
+
+String GDMonoMethod::get_full_name(bool p_signature) const {
+ char *res = mono_method_full_name(mono_method, p_signature);
+ String full_name(res);
+ mono_free(res);
+ return full_name;
+}
+
+String GDMonoMethod::get_full_name_no_class() const {
+ String res;
+
+ MonoMethodSignature *method_sig = mono_method_signature(mono_method);
+
+ char *ret_str = mono_type_full_name(mono_signature_get_return_type(method_sig));
+ res += ret_str;
+ mono_free(ret_str);
+
+ res += " ";
+ res += name;
+ res += "(";
+
+ char *sig_desc = mono_signature_get_desc(method_sig, true);
+ res += sig_desc;
+ mono_free(sig_desc);
+
+ res += ")";
+
+ return res;
+}
+
+String GDMonoMethod::get_ret_type_full_name() const {
+ MonoMethodSignature *method_sig = mono_method_signature(mono_method);
+ char *ret_str = mono_type_full_name(mono_signature_get_return_type(method_sig));
+ String res = ret_str;
+ mono_free(ret_str);
+ return res;
+}
+
+String GDMonoMethod::get_signature_desc(bool p_namespaces) const {
+ MonoMethodSignature *method_sig = mono_method_signature(mono_method);
+ char *sig_desc = mono_signature_get_desc(method_sig, p_namespaces);
+ String res = sig_desc;
+ mono_free(sig_desc);
+ return res;
+}
+
+GDMonoMethod::GDMonoMethod(StringName p_name, MonoMethod *p_method) {
+ name = p_name;
+
+ mono_method = p_method;
+
+ attrs_fetched = false;
+ attributes = NULL;
+
+ _update_signature();
+}
+
+GDMonoMethod::~GDMonoMethod() {
+ if (attributes) {
+ mono_custom_attrs_free(attributes);
+ }
+}
diff --git a/modules/mono/mono_gd/gd_mono_method.h b/modules/mono/mono_gd/gd_mono_method.h
new file mode 100644
index 0000000000..ea4bc8e707
--- /dev/null
+++ b/modules/mono/mono_gd/gd_mono_method.h
@@ -0,0 +1,81 @@
+/*************************************************************************/
+/* gd_mono_method.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+#ifndef GD_MONO_METHOD_H
+#define GD_MONO_METHOD_H
+
+#include "gd_mono.h"
+#include "gd_mono_header.h"
+
+class GDMonoMethod {
+
+ StringName name;
+
+ bool is_instance;
+ int params_count;
+ ManagedType return_type;
+ Vector<ManagedType> param_types;
+
+ bool attrs_fetched;
+ MonoCustomAttrInfo *attributes;
+
+ void _update_signature();
+ void _update_signature(MonoMethodSignature *p_method_sig);
+
+ friend class GDMonoClass;
+
+ MonoMethod *mono_method;
+
+public:
+ _FORCE_INLINE_ StringName get_name() { return name; }
+
+ _FORCE_INLINE_ bool is_static() { return !is_instance; }
+ _FORCE_INLINE_ int get_parameters_count() { return params_count; }
+ _FORCE_INLINE_ ManagedType get_return_type() { return return_type; }
+
+ void *get_thunk();
+
+ MonoObject *invoke(MonoObject *p_object, const Variant **p_params, MonoObject **r_exc = NULL);
+ MonoObject *invoke(MonoObject *p_object, MonoObject **r_exc = NULL);
+ MonoObject *invoke_raw(MonoObject *p_object, void **p_params, MonoObject **r_exc = NULL);
+
+ bool has_attribute(GDMonoClass *p_attr_class);
+ MonoObject *get_attribute(GDMonoClass *p_attr_class);
+ void fetch_attributes();
+
+ String get_full_name(bool p_signature = false) const;
+ String get_full_name_no_class() const;
+ String get_ret_type_full_name() const;
+ String get_signature_desc(bool p_namespaces = false) const;
+
+ GDMonoMethod(StringName p_name, MonoMethod *p_method);
+ ~GDMonoMethod();
+};
+
+#endif // GD_MONO_METHOD_H
diff --git a/modules/mono/mono_gd/gd_mono_utils.cpp b/modules/mono/mono_gd/gd_mono_utils.cpp
new file mode 100644
index 0000000000..5deca8e64d
--- /dev/null
+++ b/modules/mono/mono_gd/gd_mono_utils.cpp
@@ -0,0 +1,367 @@
+/*************************************************************************/
+/* gd_mono_utils.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+#include "gd_mono_utils.h"
+
+#include "os/dir_access.h"
+#include "project_settings.h"
+#include "reference.h"
+
+#include "../csharp_script.h"
+#include "gd_mono.h"
+#include "gd_mono_class.h"
+#include "gd_mono_marshal.h"
+
+namespace GDMonoUtils {
+
+MonoCache mono_cache;
+
+#define CACHE_AND_CHECK(m_var, m_val) \
+ { \
+ m_var = m_val; \
+ if (!m_var) ERR_PRINT("Mono Cache: Member " #m_var " is null. This is really bad!"); \
+ }
+
+#define CACHE_CLASS_AND_CHECK(m_class, m_val) CACHE_AND_CHECK(GDMonoUtils::mono_cache.class_##m_class, m_val)
+#define CACHE_NS_CLASS_AND_CHECK(m_ns, m_class, m_val) CACHE_AND_CHECK(GDMonoUtils::mono_cache.class_##m_ns##_##m_class, m_val)
+#define CACHE_RAW_MONO_CLASS_AND_CHECK(m_class, m_val) CACHE_AND_CHECK(GDMonoUtils::mono_cache.rawclass_##m_class, m_val)
+#define CACHE_FIELD_AND_CHECK(m_class, m_field, m_val) CACHE_AND_CHECK(GDMonoUtils::mono_cache.field_##m_class##_##m_field, m_val)
+#define CACHE_METHOD_THUNK_AND_CHECK(m_class, m_method, m_val) CACHE_AND_CHECK(GDMonoUtils::mono_cache.methodthunk_##m_class##_##m_method, m_val)
+
+void MonoCache::clear_members() {
+
+ class_MonoObject = NULL;
+ class_bool = NULL;
+ class_int8_t = NULL;
+ class_int16_t = NULL;
+ class_int32_t = NULL;
+ class_int64_t = NULL;
+ class_uint8_t = NULL;
+ class_uint16_t = NULL;
+ class_uint32_t = NULL;
+ class_uint64_t = NULL;
+ class_float = NULL;
+ class_double = NULL;
+ class_String = NULL;
+ class_IntPtr = NULL;
+
+ rawclass_Dictionary = NULL;
+
+ class_Vector2 = NULL;
+ class_Rect2 = NULL;
+ class_Transform2D = NULL;
+ class_Vector3 = NULL;
+ class_Basis = NULL;
+ class_Quat = NULL;
+ class_Transform = NULL;
+ class_Rect3 = NULL;
+ class_Color = NULL;
+ class_Plane = NULL;
+ class_NodePath = NULL;
+ class_RID = NULL;
+ class_GodotObject = NULL;
+ class_Node = NULL;
+ class_Control = NULL;
+ class_Spatial = NULL;
+ class_WeakRef = NULL;
+ class_MarshalUtils = NULL;
+
+ class_ExportAttribute = NULL;
+ field_ExportAttribute_hint = NULL;
+ field_ExportAttribute_hint_string = NULL;
+ field_ExportAttribute_usage = NULL;
+ class_ToolAttribute = NULL;
+ class_RemoteAttribute = NULL;
+ class_SyncAttribute = NULL;
+ class_MasterAttribute = NULL;
+ class_SlaveAttribute = NULL;
+ class_GodotMethodAttribute = NULL;
+ field_GodotMethodAttribute_methodName = NULL;
+
+ field_GodotObject_ptr = NULL;
+ field_NodePath_ptr = NULL;
+ field_Image_ptr = NULL;
+ field_RID_ptr = NULL;
+
+ methodthunk_MarshalUtils_DictionaryToArrays = NULL;
+ methodthunk_MarshalUtils_ArraysToDictionary = NULL;
+ methodthunk_GodotObject__AwaitedSignalCallback = NULL;
+ methodthunk_SignalAwaiter_FailureCallback = NULL;
+ methodthunk_GodotTaskScheduler_Activate = NULL;
+
+ task_scheduler_handle = Ref<MonoGCHandle>();
+}
+
+#define GODOT_API_CLASS(m_class) (GDMono::get_singleton()->get_api_assembly()->get_class(BINDINGS_NAMESPACE, #m_class))
+
+void update_corlib_cache() {
+
+ CACHE_CLASS_AND_CHECK(MonoObject, GDMono::get_singleton()->get_corlib_assembly()->get_class(mono_get_object_class()));
+ CACHE_CLASS_AND_CHECK(bool, GDMono::get_singleton()->get_corlib_assembly()->get_class(mono_get_boolean_class()));
+ CACHE_CLASS_AND_CHECK(int8_t, GDMono::get_singleton()->get_corlib_assembly()->get_class(mono_get_sbyte_class()));
+ CACHE_CLASS_AND_CHECK(int16_t, GDMono::get_singleton()->get_corlib_assembly()->get_class(mono_get_int16_class()));
+ CACHE_CLASS_AND_CHECK(int32_t, GDMono::get_singleton()->get_corlib_assembly()->get_class(mono_get_int32_class()));
+ CACHE_CLASS_AND_CHECK(int64_t, GDMono::get_singleton()->get_corlib_assembly()->get_class(mono_get_int64_class()));
+ CACHE_CLASS_AND_CHECK(uint8_t, GDMono::get_singleton()->get_corlib_assembly()->get_class(mono_get_byte_class()));
+ CACHE_CLASS_AND_CHECK(uint16_t, GDMono::get_singleton()->get_corlib_assembly()->get_class(mono_get_uint16_class()));
+ CACHE_CLASS_AND_CHECK(uint32_t, GDMono::get_singleton()->get_corlib_assembly()->get_class(mono_get_uint32_class()));
+ CACHE_CLASS_AND_CHECK(uint64_t, GDMono::get_singleton()->get_corlib_assembly()->get_class(mono_get_uint64_class()));
+ CACHE_CLASS_AND_CHECK(float, GDMono::get_singleton()->get_corlib_assembly()->get_class(mono_get_single_class()));
+ CACHE_CLASS_AND_CHECK(double, GDMono::get_singleton()->get_corlib_assembly()->get_class(mono_get_double_class()));
+ CACHE_CLASS_AND_CHECK(String, GDMono::get_singleton()->get_corlib_assembly()->get_class(mono_get_string_class()));
+ CACHE_CLASS_AND_CHECK(IntPtr, GDMono::get_singleton()->get_corlib_assembly()->get_class(mono_get_intptr_class()));
+}
+
+void update_godot_api_cache() {
+
+ CACHE_CLASS_AND_CHECK(Vector2, GODOT_API_CLASS(Vector2));
+ CACHE_CLASS_AND_CHECK(Rect2, GODOT_API_CLASS(Rect2));
+ CACHE_CLASS_AND_CHECK(Transform2D, GODOT_API_CLASS(Transform2D));
+ CACHE_CLASS_AND_CHECK(Vector3, GODOT_API_CLASS(Vector3));
+ CACHE_CLASS_AND_CHECK(Basis, GODOT_API_CLASS(Basis));
+ CACHE_CLASS_AND_CHECK(Quat, GODOT_API_CLASS(Quat));
+ CACHE_CLASS_AND_CHECK(Transform, GODOT_API_CLASS(Transform));
+ CACHE_CLASS_AND_CHECK(Rect3, GODOT_API_CLASS(Rect3));
+ CACHE_CLASS_AND_CHECK(Color, GODOT_API_CLASS(Color));
+ CACHE_CLASS_AND_CHECK(Plane, GODOT_API_CLASS(Plane));
+ CACHE_CLASS_AND_CHECK(NodePath, GODOT_API_CLASS(NodePath));
+ CACHE_CLASS_AND_CHECK(RID, GODOT_API_CLASS(NodePath));
+ CACHE_CLASS_AND_CHECK(GodotObject, GODOT_API_CLASS(Object));
+ CACHE_CLASS_AND_CHECK(Node, GODOT_API_CLASS(Node));
+ CACHE_CLASS_AND_CHECK(Control, GODOT_API_CLASS(Control));
+ CACHE_CLASS_AND_CHECK(Spatial, GODOT_API_CLASS(Spatial));
+ CACHE_CLASS_AND_CHECK(WeakRef, GODOT_API_CLASS(WeakRef));
+ CACHE_CLASS_AND_CHECK(MarshalUtils, GODOT_API_CLASS(MarshalUtils));
+
+ // Attributes
+ CACHE_CLASS_AND_CHECK(ExportAttribute, GODOT_API_CLASS(ExportAttribute));
+ CACHE_FIELD_AND_CHECK(ExportAttribute, hint, CACHED_CLASS(ExportAttribute)->get_field("hint"));
+ CACHE_FIELD_AND_CHECK(ExportAttribute, hint_string, CACHED_CLASS(ExportAttribute)->get_field("hint_string"));
+ CACHE_FIELD_AND_CHECK(ExportAttribute, usage, CACHED_CLASS(ExportAttribute)->get_field("usage"));
+ CACHE_CLASS_AND_CHECK(ToolAttribute, GODOT_API_CLASS(ToolAttribute));
+ CACHE_CLASS_AND_CHECK(RemoteAttribute, GODOT_API_CLASS(RemoteAttribute));
+ CACHE_CLASS_AND_CHECK(SyncAttribute, GODOT_API_CLASS(SyncAttribute));
+ CACHE_CLASS_AND_CHECK(MasterAttribute, GODOT_API_CLASS(MasterAttribute));
+ CACHE_CLASS_AND_CHECK(SlaveAttribute, GODOT_API_CLASS(SlaveAttribute));
+ CACHE_CLASS_AND_CHECK(GodotMethodAttribute, GODOT_API_CLASS(GodotMethodAttribute));
+ CACHE_FIELD_AND_CHECK(GodotMethodAttribute, methodName, CACHED_CLASS(GodotMethodAttribute)->get_field("methodName"));
+
+ CACHE_FIELD_AND_CHECK(GodotObject, ptr, CACHED_CLASS(GodotObject)->get_field(BINDINGS_PTR_FIELD));
+ CACHE_FIELD_AND_CHECK(NodePath, ptr, CACHED_CLASS(NodePath)->get_field(BINDINGS_PTR_FIELD));
+ CACHE_FIELD_AND_CHECK(RID, ptr, CACHED_CLASS(RID)->get_field(BINDINGS_PTR_FIELD));
+
+ CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, DictionaryToArrays, (MarshalUtils_DictToArrays)CACHED_CLASS(MarshalUtils)->get_method("DictionaryToArrays", 3)->get_thunk());
+ CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, ArraysToDictionary, (MarshalUtils_ArraysToDict)CACHED_CLASS(MarshalUtils)->get_method("ArraysToDictionary", 2)->get_thunk());
+ CACHE_METHOD_THUNK_AND_CHECK(GodotObject, _AwaitedSignalCallback, (GodotObject__AwaitedSignalCallback)CACHED_CLASS(GodotObject)->get_method("_AwaitedSignalCallback", 2)->get_thunk());
+ CACHE_METHOD_THUNK_AND_CHECK(SignalAwaiter, FailureCallback, (SignalAwaiter_FailureCallback)GODOT_API_CLASS(SignalAwaiter)->get_method("FailureCallback", 0)->get_thunk());
+ CACHE_METHOD_THUNK_AND_CHECK(GodotTaskScheduler, Activate, (GodotTaskScheduler_Activate)GODOT_API_CLASS(GodotTaskScheduler)->get_method("Activate", 0)->get_thunk());
+
+ {
+ /*
+ * TODO Right now we only support Dictionary<object, object>.
+ * It would be great if we could support other key/value types
+ * without forcing the user to copy the entries.
+ */
+ GDMonoMethod *method_get_dict_type = CACHED_CLASS(MarshalUtils)->get_method("GetDictionaryType", 0);
+ ERR_FAIL_NULL(method_get_dict_type);
+ MonoReflectionType *dict_refl_type = (MonoReflectionType *)method_get_dict_type->invoke(NULL);
+ ERR_FAIL_NULL(dict_refl_type);
+ MonoType *dict_type = mono_reflection_type_get_type(dict_refl_type);
+ ERR_FAIL_NULL(dict_type);
+
+ CACHE_RAW_MONO_CLASS_AND_CHECK(Dictionary, mono_class_from_mono_type(dict_type));
+ }
+
+ MonoObject *task_scheduler = mono_object_new(SCRIPTS_DOMAIN, GODOT_API_CLASS(GodotTaskScheduler)->get_raw());
+ mono_runtime_object_init(task_scheduler);
+ mono_cache.task_scheduler_handle = MonoGCHandle::create_strong(task_scheduler);
+}
+
+void clear_cache() {
+ mono_cache.cleanup();
+ mono_cache.clear_members();
+}
+
+MonoObject *unmanaged_get_managed(Object *unmanaged) {
+ if (unmanaged) {
+ if (unmanaged->get_script_instance()) {
+ CSharpInstance *cs_instance = CAST_CSHARP_INSTANCE(unmanaged->get_script_instance());
+
+ if (cs_instance) {
+ return cs_instance->get_mono_object();
+ }
+ }
+
+ // Only called if the owner does not have a CSharpInstance
+ void *data = unmanaged->get_script_instance_binding(CSharpLanguage::get_singleton()->get_language_index());
+
+ if (data) {
+ return ((Map<Object *, Ref<MonoGCHandle> >::Element *)data)->value()->get_target();
+ }
+ }
+
+ return NULL;
+}
+
+void set_main_thread(MonoThread *p_thread) {
+ mono_thread_set_main(p_thread);
+}
+
+void attach_current_thread() {
+ ERR_FAIL_COND(!GDMono::get_singleton()->is_runtime_initialized());
+ MonoThread *mono_thread = mono_thread_attach(SCRIPTS_DOMAIN);
+ ERR_FAIL_NULL(mono_thread);
+}
+
+void detach_current_thread() {
+ ERR_FAIL_COND(!GDMono::get_singleton()->is_runtime_initialized());
+ MonoThread *mono_thread = mono_thread_current();
+ ERR_FAIL_NULL(mono_thread);
+ mono_thread_detach(mono_thread);
+}
+
+MonoThread *get_current_thread() {
+ return mono_thread_current();
+}
+
+GDMonoClass *get_object_class(MonoObject *p_object) {
+ return GDMono::get_singleton()->get_class(mono_object_get_class(p_object));
+}
+
+GDMonoClass *type_get_proxy_class(const StringName &p_type) {
+ String class_name = p_type;
+
+ if (class_name[0] == '_')
+ class_name = class_name.substr(1, class_name.length());
+
+ GDMonoClass *klass = GDMono::get_singleton()->get_api_assembly()->get_class(BINDINGS_NAMESPACE, class_name);
+
+#ifdef TOOLS_ENABLED
+ if (!klass) {
+ return GDMono::get_singleton()->get_editor_api_assembly()->get_class(BINDINGS_NAMESPACE, class_name);
+ }
+#endif
+
+ return klass;
+}
+
+GDMonoClass *get_class_native_base(GDMonoClass *p_class) {
+ GDMonoClass *klass = p_class;
+
+ do {
+ const GDMonoAssembly *assembly = klass->get_assembly();
+ if (assembly == GDMono::get_singleton()->get_api_assembly())
+ return klass;
+#ifdef TOOLS_ENABLED
+ if (assembly == GDMono::get_singleton()->get_editor_api_assembly())
+ return klass;
+#endif
+ } while ((klass = klass->get_parent_class()) != NULL);
+
+ return NULL;
+}
+
+MonoObject *create_managed_for_godot_object(GDMonoClass *p_class, const StringName &p_native, Object *p_object) {
+ String object_type = p_object->get_class_name();
+
+ if (object_type[0] == '_')
+ object_type = object_type.substr(1, object_type.length());
+
+ if (!ClassDB::is_parent_class(object_type, p_native)) {
+ ERR_EXPLAIN("Type inherits from native type '" + p_native + "', so it can't be instanced in object of type: '" + p_object->get_class() + "'");
+ ERR_FAIL_V(NULL);
+ }
+
+ MonoObject *mono_object = mono_object_new(SCRIPTS_DOMAIN, p_class->get_raw());
+ ERR_FAIL_NULL_V(mono_object, NULL);
+
+ CACHED_FIELD(GodotObject, ptr)->set_value_raw(mono_object, p_object);
+
+ // Construct
+ mono_runtime_object_init(mono_object);
+
+ return mono_object;
+}
+
+MonoObject *create_managed_from(const NodePath &p_from) {
+ MonoObject *mono_object = mono_object_new(SCRIPTS_DOMAIN, CACHED_CLASS_RAW(NodePath));
+ ERR_FAIL_NULL_V(mono_object, NULL);
+
+ // Construct
+ mono_runtime_object_init(mono_object);
+
+ CACHED_FIELD(NodePath, ptr)->set_value_raw(mono_object, memnew(NodePath(p_from)));
+
+ return mono_object;
+}
+
+MonoObject *create_managed_from(const RID &p_from) {
+ MonoObject *mono_object = mono_object_new(SCRIPTS_DOMAIN, CACHED_CLASS_RAW(RID));
+ ERR_FAIL_NULL_V(mono_object, NULL);
+
+ // Construct
+ mono_runtime_object_init(mono_object);
+
+ CACHED_FIELD(RID, ptr)->set_value_raw(mono_object, memnew(RID(p_from)));
+
+ return mono_object;
+}
+
+MonoDomain *create_domain(const String &p_friendly_name) {
+ MonoDomain *domain = mono_domain_create_appdomain((char *)p_friendly_name.utf8().get_data(), NULL);
+
+ if (domain) {
+ // Workaround to avoid this exception:
+ // System.Configuration.ConfigurationErrorsException: Error Initializing the configuration system.
+ // ---> System.ArgumentException: The 'ExeConfigFilename' argument cannot be null.
+ mono_domain_set_config(domain, ".", "");
+ }
+
+ return domain;
+}
+
+String get_exception_name_and_message(MonoObject *p_ex) {
+ String res;
+
+ MonoClass *klass = mono_object_get_class(p_ex);
+ MonoType *type = mono_class_get_type(klass);
+
+ char *full_name = mono_type_full_name(type);
+ res += full_name;
+ mono_free(full_name);
+
+ res += ": ";
+
+ MonoProperty *prop = mono_class_get_property_from_name(klass, "Message");
+ MonoString *msg = (MonoString *)mono_property_get_value(prop, p_ex, NULL, NULL);
+ res += GDMonoMarshal::mono_string_to_godot(msg);
+
+ return res;
+}
+}
diff --git a/modules/mono/mono_gd/gd_mono_utils.h b/modules/mono/mono_gd/gd_mono_utils.h
new file mode 100644
index 0000000000..f97f048aa9
--- /dev/null
+++ b/modules/mono/mono_gd/gd_mono_utils.h
@@ -0,0 +1,182 @@
+/*************************************************************************/
+/* gd_mono_utils.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+#ifndef GD_MONOUTILS_H
+#define GD_MONOUTILS_H
+
+#include <mono/metadata/threads.h>
+
+#include "../mono_gc_handle.h"
+#include "gd_mono_header.h"
+
+#include "object.h"
+#include "reference.h"
+
+namespace GDMonoUtils {
+
+typedef MonoObject *(*MarshalUtils_DictToArrays)(MonoObject *, MonoArray **, MonoArray **, MonoObject **);
+typedef MonoObject *(*MarshalUtils_ArraysToDict)(MonoArray *, MonoArray *, MonoObject **);
+typedef MonoObject *(*GodotObject__AwaitedSignalCallback)(MonoObject *, MonoArray **, MonoObject *, MonoObject **);
+typedef MonoObject *(*SignalAwaiter_FailureCallback)(MonoObject *, MonoObject **);
+typedef MonoObject *(*GodotTaskScheduler_Activate)(MonoObject *, MonoObject **);
+
+struct MonoCache {
+ // Format for cached classes in the Godot namespace: class_<Class>
+ // Macro: CACHED_CLASS(<Class>)
+
+ // Format for cached classes in a different namespace: class_<Namespace>_<Class>
+ // Macro: CACHED_NS_CLASS(<Namespace>, <Class>)
+
+ // -----------------------------------------------
+ // corlib classes
+
+ // Let's use the no-namespace format for these too
+ GDMonoClass *class_MonoObject;
+ GDMonoClass *class_bool;
+ GDMonoClass *class_int8_t;
+ GDMonoClass *class_int16_t;
+ GDMonoClass *class_int32_t;
+ GDMonoClass *class_int64_t;
+ GDMonoClass *class_uint8_t;
+ GDMonoClass *class_uint16_t;
+ GDMonoClass *class_uint32_t;
+ GDMonoClass *class_uint64_t;
+ GDMonoClass *class_float;
+ GDMonoClass *class_double;
+ GDMonoClass *class_String;
+ GDMonoClass *class_IntPtr;
+
+ MonoClass *rawclass_Dictionary;
+ // -----------------------------------------------
+
+ GDMonoClass *class_Vector2;
+ GDMonoClass *class_Rect2;
+ GDMonoClass *class_Transform2D;
+ GDMonoClass *class_Vector3;
+ GDMonoClass *class_Basis;
+ GDMonoClass *class_Quat;
+ GDMonoClass *class_Transform;
+ GDMonoClass *class_Rect3;
+ GDMonoClass *class_Color;
+ GDMonoClass *class_Plane;
+ GDMonoClass *class_NodePath;
+ GDMonoClass *class_RID;
+ GDMonoClass *class_GodotObject;
+ GDMonoClass *class_Node;
+ GDMonoClass *class_Control;
+ GDMonoClass *class_Spatial;
+ GDMonoClass *class_WeakRef;
+ GDMonoClass *class_MarshalUtils;
+
+ GDMonoClass *class_ExportAttribute;
+ GDMonoField *field_ExportAttribute_hint;
+ GDMonoField *field_ExportAttribute_hint_string;
+ GDMonoField *field_ExportAttribute_usage;
+ GDMonoClass *class_ToolAttribute;
+ GDMonoClass *class_RemoteAttribute;
+ GDMonoClass *class_SyncAttribute;
+ GDMonoClass *class_MasterAttribute;
+ GDMonoClass *class_SlaveAttribute;
+ GDMonoClass *class_GodotMethodAttribute;
+ GDMonoField *field_GodotMethodAttribute_methodName;
+
+ GDMonoField *field_GodotObject_ptr;
+ GDMonoField *field_NodePath_ptr;
+ GDMonoField *field_Image_ptr;
+ GDMonoField *field_RID_ptr;
+
+ MarshalUtils_DictToArrays methodthunk_MarshalUtils_DictionaryToArrays;
+ MarshalUtils_ArraysToDict methodthunk_MarshalUtils_ArraysToDictionary;
+ GodotObject__AwaitedSignalCallback methodthunk_GodotObject__AwaitedSignalCallback;
+ SignalAwaiter_FailureCallback methodthunk_SignalAwaiter_FailureCallback;
+ GodotTaskScheduler_Activate methodthunk_GodotTaskScheduler_Activate;
+
+ Ref<MonoGCHandle> task_scheduler_handle;
+
+ void clear_members();
+ void cleanup() {}
+
+ MonoCache() {
+ clear_members();
+ }
+};
+
+extern MonoCache mono_cache;
+
+void update_corlib_cache();
+void update_godot_api_cache();
+void clear_cache();
+
+_FORCE_INLINE_ void hash_combine(uint32_t &p_hash, const uint32_t &p_with_hash) {
+ p_hash ^= p_with_hash + 0x9e3779b9 + (p_hash << 6) + (p_hash >> 2);
+}
+
+/**
+ * If the object has a csharp script, returns the target of the gchandle stored in the script instance
+ * Otherwise returns a newly constructed MonoObject* which is attached to the object
+ * Returns NULL on error
+ */
+MonoObject *unmanaged_get_managed(Object *unmanaged);
+
+void set_main_thread(MonoThread *p_thread);
+void attach_current_thread();
+void detach_current_thread();
+MonoThread *get_current_thread();
+
+GDMonoClass *get_object_class(MonoObject *p_object);
+GDMonoClass *type_get_proxy_class(const StringName &p_type);
+GDMonoClass *get_class_native_base(GDMonoClass *p_class);
+
+MonoObject *create_managed_for_godot_object(GDMonoClass *p_class, const StringName &p_native, Object *p_object);
+
+MonoObject *create_managed_from(const NodePath &p_from);
+MonoObject *create_managed_from(const RID &p_from);
+
+MonoDomain *create_domain(const String &p_friendly_name);
+
+String get_exception_name_and_message(MonoObject *p_ex);
+
+} // GDMonoUtils
+
+#define NATIVE_GDMONOCLASS_NAME(m_class) (GDMonoMarshal::mono_string_to_godot((MonoString *)m_class->get_field("nativeName")->get_value(NULL)))
+
+#define CACHED_CLASS(m_class) (GDMonoUtils::mono_cache.class_##m_class)
+#define CACHED_CLASS_RAW(m_class) (GDMonoUtils::mono_cache.class_##m_class->get_raw())
+#define CACHED_NS_CLASS(m_ns, m_class) (GDMonoUtils::mono_cache.class_##m_ns##_##m_class)
+#define CACHED_RAW_MONO_CLASS(m_class) (GDMonoUtils::mono_cache.rawclass_##m_class)
+#define CACHED_FIELD(m_class, m_field) (GDMonoUtils::mono_cache.field_##m_class##_##m_field)
+#define CACHED_METHOD_THUNK(m_class, m_method) (GDMonoUtils::mono_cache.methodthunk_##m_class##_##m_method)
+
+#ifdef REAL_T_IS_DOUBLE
+#define REAL_T_MONOCLASS CACHED_CLASS_RAW(double)
+#else
+#define REAL_T_MONOCLASS CACHED_CLASS_RAW(float)
+#endif
+
+#endif // GD_MONOUTILS_H