diff options
Diffstat (limited to 'modules/mono/mono_gd')
-rw-r--r-- | modules/mono/mono_gd/gd_mono.cpp | 766 | ||||
-rw-r--r-- | modules/mono/mono_gd/gd_mono.h | 226 | ||||
-rw-r--r-- | modules/mono/mono_gd/gd_mono_assembly.cpp | 356 | ||||
-rw-r--r-- | modules/mono/mono_gd/gd_mono_assembly.h | 121 | ||||
-rw-r--r-- | modules/mono/mono_gd/gd_mono_class.cpp | 381 | ||||
-rw-r--r-- | modules/mono/mono_gd/gd_mono_class.h | 124 | ||||
-rw-r--r-- | modules/mono/mono_gd/gd_mono_field.cpp | 362 | ||||
-rw-r--r-- | modules/mono/mono_gd/gd_mono_field.h | 74 | ||||
-rw-r--r-- | modules/mono/mono_gd/gd_mono_header.h | 59 | ||||
-rw-r--r-- | modules/mono/mono_gd/gd_mono_internals.cpp | 66 | ||||
-rw-r--r-- | modules/mono/mono_gd/gd_mono_internals.h | 42 | ||||
-rw-r--r-- | modules/mono/mono_gd/gd_mono_log.cpp | 175 | ||||
-rw-r--r-- | modules/mono/mono_gd/gd_mono_log.h | 61 | ||||
-rw-r--r-- | modules/mono/mono_gd/gd_mono_marshal.cpp | 845 | ||||
-rw-r--r-- | modules/mono/mono_gd/gd_mono_marshal.h | 218 | ||||
-rw-r--r-- | modules/mono/mono_gd/gd_mono_method.cpp | 192 | ||||
-rw-r--r-- | modules/mono/mono_gd/gd_mono_method.h | 81 | ||||
-rw-r--r-- | modules/mono/mono_gd/gd_mono_utils.cpp | 367 | ||||
-rw-r--r-- | modules/mono/mono_gd/gd_mono_utils.h | 182 |
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 |