diff options
Diffstat (limited to 'modules/mono')
21 files changed, 1435 insertions, 206 deletions
diff --git a/modules/mono/csharp_script.cpp b/modules/mono/csharp_script.cpp index 62a6b96bb5..996e73a4bb 100644 --- a/modules/mono/csharp_script.cpp +++ b/modules/mono/csharp_script.cpp @@ -121,7 +121,7 @@ void CSharpLanguage::init() { #ifdef TOOLS_ENABLED EditorNode::add_init_callback(&gdsharp_editor_init_callback); - GLOBAL_DEF("mono/export/include_scripts_content", true); + GLOBAL_DEF("mono/export/include_scripts_content", false); #endif } @@ -1609,7 +1609,7 @@ void CSharpScript::load_script_signals(GDMonoClass *p_class, GDMonoClass *p_nati bool CSharpScript::_get_signal(GDMonoClass *p_class, GDMonoClass *p_delegate, Vector<Argument> ¶ms) { if (p_delegate->has_attribute(CACHED_CLASS(SignalAttribute))) { - MonoType *raw_type = GDMonoClass::get_raw_type(p_delegate); + MonoType *raw_type = p_delegate->get_mono_type(); if (mono_type_get_type(raw_type) == MONO_TYPE_CLASS) { // Arguments are accessibles as arguments of .Invoke method diff --git a/modules/mono/csharp_script.h b/modules/mono/csharp_script.h index df597ba776..7f9732c297 100644 --- a/modules/mono/csharp_script.h +++ b/modules/mono/csharp_script.h @@ -292,7 +292,7 @@ public: virtual Ref<Script> get_template(const String &p_class_name, const String &p_base_class_name) const; virtual bool is_using_templates(); virtual void make_template(const String &p_class_name, const String &p_base_class_name, Ref<Script> &p_script); - /* TODO */ virtual bool validate(const String &p_script, int &r_line_error, int &r_col_error, String &r_test_error, const String &p_path, List<String> *r_functions) const { return true; } + /* TODO */ virtual bool validate(const String &p_script, int &r_line_error, int &r_col_error, String &r_test_error, const String &p_path, List<String> *r_functions, Set<int> *r_safe_lines = NULL) const { return true; } virtual String validate_path(const String &p_path) const; virtual Script *create_script() const; virtual bool has_named_classes() const; diff --git a/modules/mono/editor/bindings_generator.cpp b/modules/mono/editor/bindings_generator.cpp index 6fa317ee70..307a7d3e94 100644 --- a/modules/mono/editor/bindings_generator.cpp +++ b/modules/mono/editor/bindings_generator.cpp @@ -100,8 +100,6 @@ #define C_METHOD_MONOSTR_FROM_GODOT C_NS_MONOMARSHAL "::mono_string_from_godot" #define C_METHOD_MONOARRAY_TO(m_type) C_NS_MONOMARSHAL "::mono_array_to_" #m_type #define C_METHOD_MONOARRAY_FROM(m_type) C_NS_MONOMARSHAL "::" #m_type "_to_mono_array" -#define C_METHOD_MANAGED_TO_DICT C_NS_MONOMARSHAL "::mono_object_to_Dictionary" -#define C_METHOD_MANAGED_FROM_DICT C_NS_MONOMARSHAL "::Dictionary_to_mono_object" #define BINDINGS_GENERATOR_VERSION UINT32_C(2) @@ -1338,7 +1336,6 @@ Error BindingsGenerator::_generate_cs_method(const BindingsGenerator::TypeInterf } else if (return_type->cs_out.empty()) { p_output.push_back("return " + im_call + ";\n"); } else { - p_output.push_back(INDENT3); p_output.push_back(sformat(return_type->cs_out, im_call, return_type->cs_type, return_type->im_type_out)); p_output.push_back("\n"); } @@ -2344,7 +2341,6 @@ void BindingsGenerator::_populate_builtin_type_interfaces() { #define INSERT_ARRAY(m_type, m_proxy_t) INSERT_ARRAY_FULL(m_type, m_type, m_proxy_t) - INSERT_ARRAY(Array, object); INSERT_ARRAY(PoolIntArray, int); INSERT_ARRAY_FULL(PoolByteArray, PoolByteArray, byte); @@ -2362,20 +2358,36 @@ void BindingsGenerator::_populate_builtin_type_interfaces() { #undef INSERT_ARRAY + // Array + itype = TypeInterface(); + itype.name = "Array"; + itype.cname = itype.name; + itype.proxy_name = "Array"; + itype.c_out = "\treturn memnew(Array(%1));\n"; + itype.c_type = itype.name; + itype.c_type_in = itype.c_type + "*"; + itype.c_type_out = itype.c_type + "*"; + itype.cs_type = itype.proxy_name; + itype.cs_in = "%0." CS_SMETHOD_GETINSTANCE "()"; + itype.cs_out = "return new Array(%0);"; + itype.im_type_in = "IntPtr"; + itype.im_type_out = "IntPtr"; + builtin_types.insert(itype.cname, itype); + // Dictionary itype = TypeInterface(); itype.name = "Dictionary"; itype.cname = itype.name; - itype.proxy_name = "Dictionary<object, object>"; - itype.c_in = "\t%0 %1_in = " C_METHOD_MANAGED_TO_DICT "(%1);\n"; - itype.c_out = "\treturn " C_METHOD_MANAGED_FROM_DICT "(%1);\n"; - itype.c_arg_in = "&%s_in"; + itype.proxy_name = "Dictionary"; + itype.c_out = "\treturn memnew(Dictionary(%1));\n"; itype.c_type = itype.name; - itype.c_type_in = "MonoObject*"; - itype.c_type_out = "MonoObject*"; + itype.c_type_in = itype.c_type + "*"; + itype.c_type_out = itype.c_type + "*"; itype.cs_type = itype.proxy_name; - itype.im_type_in = itype.proxy_name; - itype.im_type_out = itype.proxy_name; + itype.cs_in = "%0." CS_SMETHOD_GETINSTANCE "()"; + itype.cs_out = "return new Dictionary(%0);"; + itype.im_type_in = "IntPtr"; + itype.im_type_out = "IntPtr"; builtin_types.insert(itype.cname, itype); // void (fictitious type to represent the return type of methods that do not return anything) diff --git a/modules/mono/editor/godotsharp_editor.cpp b/modules/mono/editor/godotsharp_editor.cpp index 998da8bda3..faeb58e5a7 100644 --- a/modules/mono/editor/godotsharp_editor.cpp +++ b/modules/mono/editor/godotsharp_editor.cpp @@ -278,13 +278,11 @@ GodotSharpEditor::GodotSharpEditor(EditorNode *p_editor) { about_label->set_autowrap(true); String about_text = String("C# support in Godot Engine is a brand new feature and a work in progress.\n") + - "It is at the alpha stage and thus not suitable for use in production.\n\n" + - "As of Godot 3.0, C# support is not feature-complete and may crash in some situations. " + - "Bugs and usability issues will be addressed gradually over 3.0.x and 3.x releases, " + + "It is currently in an alpha stage and is not suitable for use in production.\n\n" + + "As of Godot 3.1, C# support is not feature-complete and may crash in some situations. " + + "Bugs and usability issues will be addressed gradually over future 3.x releases, " + "including compatibility breaking changes as new features are implemented for a better overall C# experience.\n\n" + - "The main missing feature is the ability to export games using C# assemblies - you will therefore be able to develop and run games in the editor, " + - "but not to share them as standalone binaries yet. This feature is of course high on the priority list and should be available as soon as possible.\n\n" + - "If you experience issues with this Mono build, please report them on Godot's issue tracker with details about your system, Mono version, IDE, etc.:\n\n" + + "If you experience issues with this Mono build, please report them on Godot's issue tracker with details about your system, Mono version, IDE, etc:\n\n" + " https://github.com/godotengine/godot/issues\n\n" + "Your critical feedback at this stage will play a great role in shaping the C# support in future releases, so thank you!"; about_label->set_text(about_text); diff --git a/modules/mono/glue/collections_glue.cpp b/modules/mono/glue/collections_glue.cpp new file mode 100644 index 0000000000..0551c1991a --- /dev/null +++ b/modules/mono/glue/collections_glue.cpp @@ -0,0 +1,240 @@ +/*************************************************************************/ +/* collections_glue.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 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 "collections_glue.h" + +#include <mono/metadata/exception.h> + +#include "../mono_gd/gd_mono_class.h" + +Array *godot_icall_Array_Ctor() { + return memnew(Array); +} + +void godot_icall_Array_Dtor(Array *ptr) { + memdelete(ptr); +} + +MonoObject *godot_icall_Array_At(Array *ptr, int index) { + if (index < 0 || index > ptr->size()) { + GDMonoUtils::set_pending_exception(mono_get_exception_index_out_of_range()); + return NULL; + } + return GDMonoMarshal::variant_to_mono_object(ptr->operator[](index)); +} + +void godot_icall_Array_SetAt(Array *ptr, int index, MonoObject *value) { + if (index < 0 || index > ptr->size()) { + GDMonoUtils::set_pending_exception(mono_get_exception_index_out_of_range()); + return; + } + ptr->operator[](index) = GDMonoMarshal::mono_object_to_variant(value); +} + +int godot_icall_Array_Count(Array *ptr) { + return ptr->size(); +} + +void godot_icall_Array_Add(Array *ptr, MonoObject *item) { + ptr->append(GDMonoMarshal::mono_object_to_variant(item)); +} + +void godot_icall_Array_Clear(Array *ptr) { + ptr->clear(); +} + +bool godot_icall_Array_Contains(Array *ptr, MonoObject *item) { + return ptr->find(GDMonoMarshal::mono_object_to_variant(item)) != -1; +} + +void godot_icall_Array_CopyTo(Array *ptr, MonoArray *array, int array_index) { + int count = ptr->size(); + + if (mono_array_length(array) < (array_index + count)) { + MonoException *exc = mono_get_exception_argument("", "Destination array was not long enough. Check destIndex and length, and the array's lower bounds."); + GDMonoUtils::set_pending_exception(exc); + return; + } + + for (int i = 0; i < count; i++) { + MonoObject *boxed = GDMonoMarshal::variant_to_mono_object(ptr->operator[](i)); + mono_array_setref(array, array_index, boxed); + array_index++; + } +} + +int godot_icall_Array_IndexOf(Array *ptr, MonoObject *item) { + return ptr->find(GDMonoMarshal::mono_object_to_variant(item)); +} + +void godot_icall_Array_Insert(Array *ptr, int index, MonoObject *item) { + if (index < 0 || index > ptr->size()) { + GDMonoUtils::set_pending_exception(mono_get_exception_index_out_of_range()); + return; + } + ptr->insert(index, GDMonoMarshal::mono_object_to_variant(item)); +} + +bool godot_icall_Array_Remove(Array *ptr, MonoObject *item) { + int idx = ptr->find(GDMonoMarshal::mono_object_to_variant(item)); + if (idx >= 0) { + ptr->remove(idx); + return true; + } + return false; +} + +void godot_icall_Array_RemoveAt(Array *ptr, int index) { + if (index < 0 || index > ptr->size()) { + GDMonoUtils::set_pending_exception(mono_get_exception_index_out_of_range()); + return; + } + ptr->remove(index); +} + +Dictionary *godot_icall_Dictionary_Ctor() { + return memnew(Dictionary); +} + +void godot_icall_Dictionary_Dtor(Dictionary *ptr) { + memdelete(ptr); +} + +MonoObject *godot_icall_Dictionary_GetValue(Dictionary *ptr, MonoObject *key) { + Variant *ret = ptr->getptr(GDMonoMarshal::mono_object_to_variant(key)); + if (ret == NULL) { + MonoObject *exc = mono_object_new(mono_domain_get(), CACHED_CLASS(KeyNotFoundException)->get_mono_ptr()); +#ifdef DEBUG_ENABLED + CRASH_COND(!exc); +#endif + GDMonoUtils::runtime_object_init(exc); + GDMonoUtils::set_pending_exception((MonoException *)exc); + return NULL; + } + return GDMonoMarshal::variant_to_mono_object(ret); +} + +void godot_icall_Dictionary_SetValue(Dictionary *ptr, MonoObject *key, MonoObject *value) { + ptr->operator[](GDMonoMarshal::mono_object_to_variant(key)) = GDMonoMarshal::mono_object_to_variant(value); +} + +Array *godot_icall_Dictionary_Keys(Dictionary *ptr) { + return memnew(Array(ptr->keys())); +} + +Array *godot_icall_Dictionary_Values(Dictionary *ptr) { + return memnew(Array(ptr->values())); +} + +int godot_icall_Dictionary_Count(Dictionary *ptr) { + return ptr->size(); +} + +void godot_icall_Dictionary_Add(Dictionary *ptr, MonoObject *key, MonoObject *value) { + Variant varKey = GDMonoMarshal::mono_object_to_variant(key); + Variant *ret = ptr->getptr(varKey); + if (ret != NULL) { + GDMonoUtils::set_pending_exception(mono_get_exception_argument("key", "An element with the same key already exists")); + return; + } + ptr->operator[](varKey) = GDMonoMarshal::mono_object_to_variant(value); +} + +void godot_icall_Dictionary_Clear(Dictionary *ptr) { + ptr->clear(); +} + +bool godot_icall_Dictionary_Contains(Dictionary *ptr, MonoObject *key, MonoObject *value) { + // no dupes + Variant *ret = ptr->getptr(GDMonoMarshal::mono_object_to_variant(key)); + return ret != NULL && *ret == GDMonoMarshal::mono_object_to_variant(value); +} + +bool godot_icall_Dictionary_ContainsKey(Dictionary *ptr, MonoObject *key) { + return ptr->has(GDMonoMarshal::mono_object_to_variant(key)); +} + +bool godot_icall_Dictionary_RemoveKey(Dictionary *ptr, MonoObject *key) { + return ptr->erase_checked(GDMonoMarshal::mono_object_to_variant(key)); +} + +bool godot_icall_Dictionary_Remove(Dictionary *ptr, MonoObject *key, MonoObject *value) { + Variant varKey = GDMonoMarshal::mono_object_to_variant(key); + + // no dupes + Variant *ret = ptr->getptr(varKey); + if (ret != NULL && *ret == GDMonoMarshal::mono_object_to_variant(value)) { + ptr->erase_checked(varKey); + return true; + } + + return false; +} + +bool godot_icall_Dictionary_TryGetValue(Dictionary *ptr, MonoObject *key, MonoObject **value) { + Variant *ret = ptr->getptr(GDMonoMarshal::mono_object_to_variant(key)); + if (ret == NULL) { + *value = NULL; + return false; + } + *value = GDMonoMarshal::variant_to_mono_object(ret); + return true; +} + +void godot_register_collections_icalls() { + mono_add_internal_call("Godot.Array::godot_icall_Array_Ctor", (void *)godot_icall_Array_Ctor); + mono_add_internal_call("Godot.Array::godot_icall_Array_Dtor", (void *)godot_icall_Array_Dtor); + mono_add_internal_call("Godot.Array::godot_icall_Array_At", (void *)godot_icall_Array_At); + mono_add_internal_call("Godot.Array::godot_icall_Array_SetAt", (void *)godot_icall_Array_SetAt); + mono_add_internal_call("Godot.Array::godot_icall_Array_Count", (void *)godot_icall_Array_Count); + mono_add_internal_call("Godot.Array::godot_icall_Array_Add", (void *)godot_icall_Array_Add); + mono_add_internal_call("Godot.Array::godot_icall_Array_Clear", (void *)godot_icall_Array_Clear); + mono_add_internal_call("Godot.Array::godot_icall_Array_Contains", (void *)godot_icall_Array_Contains); + mono_add_internal_call("Godot.Array::godot_icall_Array_CopyTo", (void *)godot_icall_Array_CopyTo); + mono_add_internal_call("Godot.Array::godot_icall_Array_IndexOf", (void *)godot_icall_Array_IndexOf); + mono_add_internal_call("Godot.Array::godot_icall_Array_Insert", (void *)godot_icall_Array_Insert); + mono_add_internal_call("Godot.Array::godot_icall_Array_Remove", (void *)godot_icall_Array_Remove); + mono_add_internal_call("Godot.Array::godot_icall_Array_RemoveAt", (void *)godot_icall_Array_RemoveAt); + + mono_add_internal_call("Godot.Dictionary::godot_icall_Dictionary_Ctor", (void *)godot_icall_Dictionary_Ctor); + mono_add_internal_call("Godot.Dictionary::godot_icall_Dictionary_Dtor", (void *)godot_icall_Dictionary_Dtor); + mono_add_internal_call("Godot.Dictionary::godot_icall_Dictionary_GetValue", (void *)godot_icall_Dictionary_GetValue); + mono_add_internal_call("Godot.Dictionary::godot_icall_Dictionary_SetValue", (void *)godot_icall_Dictionary_SetValue); + mono_add_internal_call("Godot.Dictionary::godot_icall_Dictionary_Keys", (void *)godot_icall_Dictionary_Keys); + mono_add_internal_call("Godot.Dictionary::godot_icall_Dictionary_Values", (void *)godot_icall_Dictionary_Values); + mono_add_internal_call("Godot.Dictionary::godot_icall_Dictionary_Count", (void *)godot_icall_Dictionary_Count); + mono_add_internal_call("Godot.Dictionary::godot_icall_Dictionary_Add", (void *)godot_icall_Dictionary_Add); + mono_add_internal_call("Godot.Dictionary::godot_icall_Dictionary_Clear", (void *)godot_icall_Dictionary_Clear); + mono_add_internal_call("Godot.Dictionary::godot_icall_Dictionary_Contains", (void *)godot_icall_Dictionary_Contains); + mono_add_internal_call("Godot.Dictionary::godot_icall_Dictionary_ContainsKey", (void *)godot_icall_Dictionary_ContainsKey); + mono_add_internal_call("Godot.Dictionary::godot_icall_Dictionary_RemoveKey", (void *)godot_icall_Dictionary_RemoveKey); + mono_add_internal_call("Godot.Dictionary::godot_icall_Dictionary_Remove", (void *)godot_icall_Dictionary_Remove); + mono_add_internal_call("Godot.Dictionary::godot_icall_Dictionary_TryGetValue", (void *)godot_icall_Dictionary_TryGetValue); +} diff --git a/modules/mono/glue/collections_glue.h b/modules/mono/glue/collections_glue.h new file mode 100644 index 0000000000..eb5ecfb725 --- /dev/null +++ b/modules/mono/glue/collections_glue.h @@ -0,0 +1,100 @@ +/*************************************************************************/ +/* collections_glue.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 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 COLLECTIONS_GLUE_H +#define COLLECTIONS_GLUE_H + +#include "core/array.h" + +#include "../mono_gd/gd_mono_marshal.h" + +// Array + +Array *godot_icall_Array_Ctor(); + +void godot_icall_Array_Dtor(Array *ptr); + +MonoObject *godot_icall_Array_At(Array *ptr, int index); + +void godot_icall_Array_SetAt(Array *ptr, int index, MonoObject *value); + +int godot_icall_Array_Count(Array *ptr); + +void godot_icall_Array_Add(Array *ptr, MonoObject *item); + +void godot_icall_Array_Clear(Array *ptr); + +bool godot_icall_Array_Contains(Array *ptr, MonoObject *item); + +void godot_icall_Array_CopyTo(Array *ptr, MonoArray *array, int array_index); + +int godot_icall_Array_IndexOf(Array *ptr, MonoObject *item); + +void godot_icall_Array_Insert(Array *ptr, int index, MonoObject *item); + +bool godot_icall_Array_Remove(Array *ptr, MonoObject *item); + +void godot_icall_Array_RemoveAt(Array *ptr, int index); + +// Dictionary + +Dictionary *godot_icall_Dictionary_Ctor(); + +void godot_icall_Dictionary_Dtor(Dictionary *ptr); + +MonoObject *godot_icall_Dictionary_GetValue(Dictionary *ptr, MonoObject *key); + +void godot_icall_Dictionary_SetValue(Dictionary *ptr, MonoObject *key, MonoObject *value); + +Array *godot_icall_Dictionary_Keys(Dictionary *ptr); + +Array *godot_icall_Dictionary_Values(Dictionary *ptr); + +int godot_icall_Dictionary_Count(Dictionary *ptr); + +void godot_icall_Dictionary_Add(Dictionary *ptr, MonoObject *key, MonoObject *value); + +void godot_icall_Dictionary_Clear(Dictionary *ptr); + +bool godot_icall_Dictionary_Contains(Dictionary *ptr, MonoObject *key, MonoObject *value); + +bool godot_icall_Dictionary_ContainsKey(Dictionary *ptr, MonoObject *key); + +bool godot_icall_Dictionary_RemoveKey(Dictionary *ptr, MonoObject *key); + +bool godot_icall_Dictionary_Remove(Dictionary *ptr, MonoObject *key, MonoObject *value); + +bool godot_icall_Dictionary_TryGetValue(Dictionary *ptr, MonoObject *key, MonoObject **value); + +// Register internal calls + +void godot_register_collections_icalls(); + +#endif // COLLECTIONS_GLUE_H diff --git a/modules/mono/glue/cs_files/Array.cs b/modules/mono/glue/cs_files/Array.cs new file mode 100644 index 0000000000..51f57daef4 --- /dev/null +++ b/modules/mono/glue/cs_files/Array.cs @@ -0,0 +1,335 @@ +using System; +using System.Collections.Generic; +using System.Collections; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +namespace Godot +{ + class ArraySafeHandle : SafeHandle + { + public ArraySafeHandle(IntPtr handle) : base(IntPtr.Zero, true) + { + this.handle = handle; + } + + public override bool IsInvalid + { + get + { + return handle == IntPtr.Zero; + } + } + + protected override bool ReleaseHandle() + { + Array.godot_icall_Array_Dtor(handle); + return true; + } + } + + public class Array : IList<object>, ICollection<object>, IEnumerable<object>, IDisposable + { + [MethodImpl(MethodImplOptions.InternalCall)] + internal extern static IntPtr godot_icall_Array_Ctor(); + + [MethodImpl(MethodImplOptions.InternalCall)] + internal extern static void godot_icall_Array_Dtor(IntPtr ptr); + + [MethodImpl(MethodImplOptions.InternalCall)] + internal extern static object godot_icall_Array_At(IntPtr ptr, int index); + + [MethodImpl(MethodImplOptions.InternalCall)] + internal extern static void godot_icall_Array_SetAt(IntPtr ptr, int index, object value); + + [MethodImpl(MethodImplOptions.InternalCall)] + internal extern static int godot_icall_Array_Count(IntPtr ptr); + + [MethodImpl(MethodImplOptions.InternalCall)] + internal extern static void godot_icall_Array_Add(IntPtr ptr, object item); + + [MethodImpl(MethodImplOptions.InternalCall)] + internal extern static void godot_icall_Array_Clear(IntPtr ptr); + + [MethodImpl(MethodImplOptions.InternalCall)] + internal extern static bool godot_icall_Array_Contains(IntPtr ptr, object item); + + [MethodImpl(MethodImplOptions.InternalCall)] + internal extern static void godot_icall_Array_CopyTo(IntPtr ptr, object[] array, int arrayIndex); + + [MethodImpl(MethodImplOptions.InternalCall)] + internal extern static int godot_icall_Array_IndexOf(IntPtr ptr, object item); + + [MethodImpl(MethodImplOptions.InternalCall)] + internal extern static void godot_icall_Array_Insert(IntPtr ptr, int index, object item); + + [MethodImpl(MethodImplOptions.InternalCall)] + internal extern static bool godot_icall_Array_Remove(IntPtr ptr, object item); + + [MethodImpl(MethodImplOptions.InternalCall)] + internal extern static void godot_icall_Array_RemoveAt(IntPtr ptr, int index); + + ArraySafeHandle safeHandle; + bool disposed = false; + + public Array() + { + safeHandle = new ArraySafeHandle(godot_icall_Array_Ctor()); + } + + internal Array(ArraySafeHandle handle) + { + safeHandle = handle; + } + + internal Array(IntPtr handle) + { + safeHandle = new ArraySafeHandle(handle); + } + + internal IntPtr GetPtr() + { + return safeHandle.DangerousGetHandle(); + } + + public void Dispose() + { + Dispose(true); + } + + protected virtual void Dispose(bool disposing) + { + if (disposed) + return; + + if (safeHandle != null) + { + safeHandle.Dispose(); + safeHandle = null; + } + + disposed = true; + } + + public object this[int index] + { + get + { + return godot_icall_Array_At(GetPtr(), index); + } + set + { + godot_icall_Array_SetAt(GetPtr(), index, value); + } + } + + public int Count + { + get + { + return godot_icall_Array_Count(GetPtr()); + } + } + + public bool IsReadOnly + { + get + { + return false; + } + } + + public void Add(object item) + { + godot_icall_Array_Add(GetPtr(), item); + } + + public void Clear() + { + godot_icall_Array_Clear(GetPtr()); + } + + public bool Contains(object item) + { + return godot_icall_Array_Contains(GetPtr(), item); + } + + public void CopyTo(object[] array, int arrayIndex) + { + if (array == null) + throw new ArgumentNullException(nameof(array), "Value cannot be null."); + + if (arrayIndex < 0) + throw new ArgumentOutOfRangeException(nameof(arrayIndex), "Number was less than the array's lower bound in the first dimension."); + + // Internal call may throw ArgumentException + godot_icall_Array_CopyTo(GetPtr(), array, arrayIndex); + } + + public IEnumerator<object> GetEnumerator() + { + int count = Count; + + for (int i = 0; i < count; i++) + { + yield return godot_icall_Array_At(GetPtr(), i); + } + } + + public int IndexOf(object item) + { + return godot_icall_Array_IndexOf(GetPtr(), item); + } + + public void Insert(int index, object item) + { + godot_icall_Array_Insert(GetPtr(), index, item); + } + + public bool Remove(object item) + { + return godot_icall_Array_Remove(GetPtr(), item); + } + + public void RemoveAt(int index) + { + godot_icall_Array_RemoveAt(GetPtr(), index); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + } + + public class Array<T> : IList<T>, ICollection<T>, IEnumerable<T> + { + Array objectArray; + + public Array() + { + objectArray = new Array(); + } + + public Array(Array array) + { + objectArray = array; + } + + internal Array(IntPtr handle) + { + objectArray = new Array(handle); + } + + internal Array(ArraySafeHandle handle) + { + objectArray = new Array(handle); + } + + public static explicit operator Array(Array<T> from) + { + return from.objectArray; + } + + public T this[int index] + { + get + { + return (T)objectArray[index]; + } + set + { + objectArray[index] = value; + } + } + + public int Count + { + get + { + return objectArray.Count; + } + } + + public bool IsReadOnly + { + get + { + return objectArray.IsReadOnly; + } + } + + public void Add(T item) + { + objectArray.Add(item); + } + + public void Clear() + { + objectArray.Clear(); + } + + public bool Contains(T item) + { + return objectArray.Contains(item); + } + + public void CopyTo(T[] array, int arrayIndex) + { + if (array == null) + throw new ArgumentNullException(nameof(array), "Value cannot be null."); + + if (arrayIndex < 0) + throw new ArgumentOutOfRangeException(nameof(arrayIndex), "Number was less than the array's lower bound in the first dimension."); + + // TODO This may be quite slow because every element access is an internal call. + // It could be moved entirely to an internal call if we find out how to do the cast there. + + int count = objectArray.Count; + + if (array.Length < (arrayIndex + count)) + throw new ArgumentException("Destination array was not long enough. Check destIndex and length, and the array's lower bounds."); + + for (int i = 0; i < count; i++) + { + array[arrayIndex] = (T)objectArray[i]; + arrayIndex++; + } + } + + public IEnumerator<T> GetEnumerator() + { + int count = objectArray.Count; + + for (int i = 0; i < count; i++) + { + yield return (T)objectArray[i]; + } + } + + public int IndexOf(T item) + { + return objectArray.IndexOf(item); + } + + public void Insert(int index, T item) + { + objectArray.Insert(index, item); + } + + public bool Remove(T item) + { + return objectArray.Remove(item); + } + + public void RemoveAt(int index) + { + objectArray.RemoveAt(index); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + } +} diff --git a/modules/mono/glue/cs_files/Dictionary.cs b/modules/mono/glue/cs_files/Dictionary.cs new file mode 100644 index 0000000000..57a1960ad9 --- /dev/null +++ b/modules/mono/glue/cs_files/Dictionary.cs @@ -0,0 +1,401 @@ +using System; +using System.Collections.Generic; +using System.Collections; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +namespace Godot +{ + class DictionarySafeHandle : SafeHandle + { + public DictionarySafeHandle(IntPtr handle) : base(IntPtr.Zero, true) + { + this.handle = handle; + } + + public override bool IsInvalid + { + get + { + return handle == IntPtr.Zero; + } + } + + protected override bool ReleaseHandle() + { + Dictionary.godot_icall_Dictionary_Dtor(handle); + return true; + } + } + + public class Dictionary : + IDictionary<object, object>, + ICollection<KeyValuePair<object, object>>, + IEnumerable<KeyValuePair<object, object>>, + IDisposable + { + [MethodImpl(MethodImplOptions.InternalCall)] + internal extern static IntPtr godot_icall_Dictionary_Ctor(); + + [MethodImpl(MethodImplOptions.InternalCall)] + internal extern static void godot_icall_Dictionary_Dtor(IntPtr ptr); + + [MethodImpl(MethodImplOptions.InternalCall)] + internal extern static object godot_icall_Dictionary_GetValue(IntPtr ptr, object key); + + [MethodImpl(MethodImplOptions.InternalCall)] + internal extern static void godot_icall_Dictionary_SetValue(IntPtr ptr, object key, object value); + + [MethodImpl(MethodImplOptions.InternalCall)] + internal extern static IntPtr godot_icall_Dictionary_Keys(IntPtr ptr); + + [MethodImpl(MethodImplOptions.InternalCall)] + internal extern static IntPtr godot_icall_Dictionary_Values(IntPtr ptr); + + [MethodImpl(MethodImplOptions.InternalCall)] + internal extern static int godot_icall_Dictionary_Count(IntPtr ptr); + + [MethodImpl(MethodImplOptions.InternalCall)] + internal extern static void godot_icall_Dictionary_Add(IntPtr ptr, object key, object value); + + [MethodImpl(MethodImplOptions.InternalCall)] + internal extern static void godot_icall_Dictionary_Clear(IntPtr ptr); + + [MethodImpl(MethodImplOptions.InternalCall)] + internal extern static bool godot_icall_Dictionary_Contains(IntPtr ptr, object key, object value); + + [MethodImpl(MethodImplOptions.InternalCall)] + internal extern static bool godot_icall_Dictionary_ContainsKey(IntPtr ptr, object key); + + [MethodImpl(MethodImplOptions.InternalCall)] + internal extern static bool godot_icall_Dictionary_RemoveKey(IntPtr ptr, object key); + + [MethodImpl(MethodImplOptions.InternalCall)] + internal extern static bool godot_icall_Dictionary_Remove(IntPtr ptr, object key, object value); + + [MethodImpl(MethodImplOptions.InternalCall)] + internal extern static bool godot_icall_Dictionary_TryGetValue(IntPtr ptr, object key, out object value); + + DictionarySafeHandle safeHandle; + bool disposed = false; + + public Dictionary() + { + safeHandle = new DictionarySafeHandle(godot_icall_Dictionary_Ctor()); + } + + internal Dictionary(DictionarySafeHandle handle) + { + safeHandle = handle; + } + + internal Dictionary(IntPtr handle) + { + safeHandle = new DictionarySafeHandle(handle); + } + + internal IntPtr GetPtr() + { + return safeHandle.DangerousGetHandle(); + } + + public void Dispose() + { + Dispose(true); + } + + protected virtual void Dispose(bool disposing) + { + if (disposed) + return; + + if (safeHandle != null) + { + safeHandle.Dispose(); + safeHandle = null; + } + + disposed = true; + } + + public object this[object key] + { + get + { + return godot_icall_Dictionary_GetValue(GetPtr(), key); + } + set + { + godot_icall_Dictionary_SetValue(GetPtr(), key, value); + } + } + + public ICollection<object> Keys + { + get + { + IntPtr handle = godot_icall_Dictionary_Keys(GetPtr()); + return new Array(new ArraySafeHandle(handle)); + } + } + + public ICollection<object> Values + { + get + { + IntPtr handle = godot_icall_Dictionary_Values(GetPtr()); + return new Array(new ArraySafeHandle(handle)); + } + } + + public int Count + { + get + { + return godot_icall_Dictionary_Count(GetPtr()); + } + } + + public bool IsReadOnly + { + get + { + return false; + } + } + + public void Add(object key, object value) + { + godot_icall_Dictionary_Add(GetPtr(), key, value); + } + + public void Add(KeyValuePair<object, object> item) + { + Add(item.Key, item.Value); + } + + public void Clear() + { + godot_icall_Dictionary_Clear(GetPtr()); + } + + public bool Contains(KeyValuePair<object, object> item) + { + return godot_icall_Dictionary_Contains(GetPtr(), item.Key, item.Value); + } + + public bool ContainsKey(object key) + { + return godot_icall_Dictionary_ContainsKey(GetPtr(), key); + } + + public void CopyTo(KeyValuePair<object, object>[] array, int arrayIndex) + { + // TODO 3 internal calls, can reduce to 1 + Array keys = (Array)Keys; + Array values = (Array)Values; + int count = Count; + + for (int i = 0; i < count; i++) + { + // TODO 2 internal calls, can reduce to 1 + array[arrayIndex] = new KeyValuePair<object, object>(keys[i], values[i]); + arrayIndex++; + } + } + + public IEnumerator<KeyValuePair<object, object>> GetEnumerator() + { + // TODO 3 internal calls, can reduce to 1 + Array keys = (Array)Keys; + Array values = (Array)Values; + int count = Count; + + for (int i = 0; i < count; i++) + { + // TODO 2 internal calls, can reduce to 1 + yield return new KeyValuePair<object, object>(keys[i], values[i]); + } + } + + public bool Remove(object key) + { + return godot_icall_Dictionary_RemoveKey(GetPtr(), key); + } + + public bool Remove(KeyValuePair<object, object> item) + { + return godot_icall_Dictionary_Remove(GetPtr(), item.Key, item.Value); + } + + public bool TryGetValue(object key, out object value) + { + object retValue; + bool found = godot_icall_Dictionary_TryGetValue(GetPtr(), key, out retValue); + value = found ? retValue : default(object); + return found; + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + } + + + public class Dictionary<TKey, TValue> : + IDictionary<TKey, TValue>, + ICollection<KeyValuePair<TKey, TValue>>, + IEnumerable<KeyValuePair<TKey, TValue>> + { + Dictionary objectDict; + + public Dictionary() + { + objectDict = new Dictionary(); + } + + public Dictionary(Dictionary dictionary) + { + objectDict = dictionary; + } + + internal Dictionary(IntPtr handle) + { + objectDict = new Dictionary(handle); + } + + internal Dictionary(DictionarySafeHandle handle) + { + objectDict = new Dictionary(handle); + } + + public static explicit operator Dictionary(Dictionary<TKey, TValue> from) + { + return from.objectDict; + } + + public TValue this[TKey key] + { + get + { + return (TValue)objectDict[key]; + } + set + { + objectDict[key] = value; + } + } + + public ICollection<TKey> Keys + { + get + { + IntPtr handle = Dictionary.godot_icall_Dictionary_Keys(objectDict.GetPtr()); + return new Array<TKey>(new ArraySafeHandle(handle)); + } + } + + public ICollection<TValue> Values + { + get + { + IntPtr handle = Dictionary.godot_icall_Dictionary_Values(objectDict.GetPtr()); + return new Array<TValue>(new ArraySafeHandle(handle)); + } + } + + public int Count + { + get + { + return objectDict.Count; + } + } + + public bool IsReadOnly + { + get + { + return objectDict.IsReadOnly; + } + } + + public void Add(TKey key, TValue value) + { + objectDict.Add(key, value); + } + + public void Add(KeyValuePair<TKey, TValue> item) + { + objectDict.Add(item.Key, item.Value); + } + + public void Clear() + { + objectDict.Clear(); + } + + public bool Contains(KeyValuePair<TKey, TValue> item) + { + return objectDict.Contains(new KeyValuePair<object, object>(item.Key, item.Value)); + } + + public bool ContainsKey(TKey key) + { + return objectDict.ContainsKey(key); + } + + public void CopyTo(KeyValuePair<TKey, TValue>[] array, int arrayIndex) + { + // TODO 3 internal calls, can reduce to 1 + Array<TKey> keys = (Array<TKey>)Keys; + Array<TValue> values = (Array<TValue>)Values; + int count = Count; + + for (int i = 0; i < count; i++) + { + // TODO 2 internal calls, can reduce to 1 + array[arrayIndex] = new KeyValuePair<TKey, TValue>(keys[i], values[i]); + arrayIndex++; + } + } + + public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator() + { + // TODO 3 internal calls, can reduce to 1 + Array<TKey> keys = (Array<TKey>)Keys; + Array<TValue> values = (Array<TValue>)Values; + int count = Count; + + for (int i = 0; i < count; i++) + { + // TODO 2 internal calls, can reduce to 1 + yield return new KeyValuePair<TKey, TValue>(keys[i], values[i]); + } + } + + public bool Remove(TKey key) + { + return objectDict.Remove(key); + } + + public bool Remove(KeyValuePair<TKey, TValue> item) + { + return objectDict.Remove(new KeyValuePair<object, object>(item.Key, item.Value)); + } + + public bool TryGetValue(TKey key, out TValue value) + { + object retValue; + bool found = objectDict.TryGetValue(key, out retValue); + value = found ? (TValue)retValue : default(TValue); + return found; + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + } +} diff --git a/modules/mono/glue/cs_files/GD.cs b/modules/mono/glue/cs_files/GD.cs index ec1534cb9a..e2457ff98b 100644 --- a/modules/mono/glue/cs_files/GD.cs +++ b/modules/mono/glue/cs_files/GD.cs @@ -1,4 +1,9 @@ using System; +#if REAL_T_IS_DOUBLE +using real_t = System.Double; +#else +using real_t = System.Single; +#endif // TODO: Add comments describing what this class does. It is not obvious. @@ -16,22 +21,22 @@ namespace Godot return NativeCalls.godot_icall_Godot_convert(what, type); } - public static float Db2Linear(float db) + public static real_t Db2Linear(real_t db) { - return (float)Math.Exp(db * 0.11512925464970228420089957273422); + return (real_t)Math.Exp(db * 0.11512925464970228420089957273422); } - public static float Dectime(float value, float amount, float step) + public static real_t DecTime(real_t value, real_t amount, real_t step) { - float sgn = value < 0 ? -1.0f : 1.0f; - float val = Mathf.Abs(value); + real_t sgn = Mathf.Sign(value); + real_t val = Mathf.Abs(value); val -= amount * step; - if (val < 0.0f) - val = 0.0f; + if (val < 0) + val = 0; return val * sgn; } - public static FuncRef Funcref(Object instance, string funcname) + public static FuncRef FuncRef(Object instance, string funcname) { var ret = new FuncRef(); ret.SetInstance(instance); @@ -49,9 +54,9 @@ namespace Godot return NativeCalls.godot_icall_Godot_instance_from_id(instanceId); } - public static double Linear2Db(double linear) + public static real_t Linear2Db(real_t linear) { - return Math.Log(linear) * 8.6858896380650365530225783783321; + return (real_t)(Math.Log(linear) * 8.6858896380650365530225783783321); } public static Resource Load(string path) @@ -69,22 +74,22 @@ namespace Godot Print(System.Environment.StackTrace); } - public static void Printerr(params object[] what) + public static void PrintErr(params object[] what) { NativeCalls.godot_icall_Godot_printerr(what); } - public static void Printraw(params object[] what) + public static void PrintRaw(params object[] what) { NativeCalls.godot_icall_Godot_printraw(what); } - public static void Prints(params object[] what) + public static void PrintS(params object[] what) { NativeCalls.godot_icall_Godot_prints(what); } - public static void Printt(params object[] what) + public static void PrintT(params object[] what) { NativeCalls.godot_icall_Godot_printt(what); } @@ -183,7 +188,7 @@ namespace Godot return NativeCalls.godot_icall_Godot_var2str(var); } - public static WeakRef Weakref(Object obj) + public static WeakRef WeakRef(Object obj) { return NativeCalls.godot_icall_Godot_weakref(Object.GetPtr(obj)); } diff --git a/modules/mono/glue/cs_files/MarshalUtils.cs b/modules/mono/glue/cs_files/MarshalUtils.cs index ff4477cc6c..6ad4b3dcb2 100644 --- a/modules/mono/glue/cs_files/MarshalUtils.cs +++ b/modules/mono/glue/cs_files/MarshalUtils.cs @@ -1,36 +1,17 @@ using System; -using System.Collections.Generic; namespace Godot { - internal static class MarshalUtils + static class MarshalUtils { - private static Dictionary<object, object> ArraysToDictionary(object[] keys, object[] values) + static bool IsArrayGenericType(Type type) { - var ret = new Dictionary<object, object>(); - - for (int i = 0; i < keys.Length; i++) - { - ret.Add(keys[i], values[i]); - } - - return ret; - } - - private static void DictionaryToArrays(Dictionary<object, object> from, out object[] keysTo, out object[] valuesTo) - { - var keys = from.Keys; - keysTo = new object[keys.Count]; - keys.CopyTo(keysTo, 0); - - var values = from.Values; - valuesTo = new object[values.Count]; - values.CopyTo(valuesTo, 0); + return type.GetGenericTypeDefinition() == typeof(Array<>); } - private static Type GetDictionaryType() + static bool IsDictionaryGenericType(Type type) { - return typeof(Dictionary<object, object>); + return type.GetGenericTypeDefinition() == typeof(Dictionary<, >); } } } diff --git a/modules/mono/glue/cs_files/VERSION.txt b/modules/mono/glue/cs_files/VERSION.txt index b8626c4cff..7ed6ff82de 100755 --- a/modules/mono/glue/cs_files/VERSION.txt +++ b/modules/mono/glue/cs_files/VERSION.txt @@ -1 +1 @@ -4 +5 diff --git a/modules/mono/glue/glue_header.h b/modules/mono/glue/glue_header.h index cedc8e9992..6a6f3062b4 100644 --- a/modules/mono/glue/glue_header.h +++ b/modules/mono/glue/glue_header.h @@ -29,6 +29,7 @@ /*************************************************************************/ #include "builtin_types_glue.h" +#include "collections_glue.h" #include "../csharp_script.h" #include "../mono_gd/gd_mono_class.h" @@ -308,4 +309,5 @@ MonoObject *godot_icall_Godot_weakref(Object *p_obj) { void godot_register_header_icalls() { godot_register_builtin_type_icalls(); + godot_register_collections_icalls(); } diff --git a/modules/mono/mono_gd/gd_mono_class.cpp b/modules/mono/mono_gd/gd_mono_class.cpp index 66339d7ae6..e2597a7d42 100644 --- a/modules/mono/mono_gd/gd_mono_class.cpp +++ b/modules/mono/mono_gd/gd_mono_class.cpp @@ -33,23 +33,37 @@ #include <mono/metadata/attrdefs.h> #include "gd_mono_assembly.h" +#include "gd_mono_marshal.h" -MonoType *GDMonoClass::get_raw_type(GDMonoClass *p_class) { +String GDMonoClass::get_full_name(MonoClass *p_mono_class) { + // mono_type_get_full_name is not exposed to embedders, but this seems to do the job + MonoReflectionType *type_obj = mono_type_get_object(mono_domain_get(), get_mono_type(p_mono_class)); - return mono_class_get_type(p_class->get_mono_ptr()); -} + MonoException *exc = NULL; + GD_MONO_BEGIN_RUNTIME_INVOKE; + MonoString *str = mono_object_to_string((MonoObject *)type_obj, (MonoObject **)&exc); + GD_MONO_END_RUNTIME_INVOKE; + UNLIKELY_UNHANDLED_EXCEPTION(exc); -bool GDMonoClass::is_assignable_from(GDMonoClass *p_from) const { + return GDMonoMarshal::mono_string_to_godot(str); +} - return mono_class_is_assignable_from(mono_class, p_from->mono_class); +MonoType *GDMonoClass::get_mono_type(MonoClass *p_mono_class) { + return mono_class_get_type(p_mono_class); } String GDMonoClass::get_full_name() const { + return get_full_name(mono_class); +} - String res = namespace_name; - if (res.length()) - res += "."; - return res + class_name; +MonoType *GDMonoClass::get_mono_type() { + // Care, you cannot compare MonoType pointers + return get_mono_type(mono_class); +} + +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() { diff --git a/modules/mono/mono_gd/gd_mono_class.h b/modules/mono/mono_gd/gd_mono_class.h index 417c138594..f81ab84cd0 100644 --- a/modules/mono/mono_gd/gd_mono_class.h +++ b/modules/mono/mono_gd/gd_mono_class.h @@ -98,7 +98,11 @@ class GDMonoClass { GDMonoClass(const StringName &p_namespace, const StringName &p_name, MonoClass *p_class, GDMonoAssembly *p_assembly); public: - static MonoType *get_raw_type(GDMonoClass *p_class); + static String get_full_name(MonoClass *p_mono_class); + static MonoType *get_mono_type(MonoClass *p_mono_class); + + String get_full_name() const; + MonoType *get_mono_type(); bool is_assignable_from(GDMonoClass *p_from) const; @@ -108,8 +112,6 @@ public: _FORCE_INLINE_ MonoClass *get_mono_ptr() const { return mono_class; } _FORCE_INLINE_ const GDMonoAssembly *get_assembly() const { return assembly; } - String get_full_name() const; - GDMonoClass *get_parent_class(); #ifdef TOOLS_ENABLED diff --git a/modules/mono/mono_gd/gd_mono_field.cpp b/modules/mono/mono_gd/gd_mono_field.cpp index 3b91777ed4..d3a673dc1b 100644 --- a/modules/mono/mono_gd/gd_mono_field.cpp +++ b/modules/mono/mono_gd/gd_mono_field.cpp @@ -148,7 +148,7 @@ void GDMonoField::set_value_from_variant(MonoObject *p_object, const Variant &p_ case MONO_TYPE_ARRAY: case MONO_TYPE_SZARRAY: { - MonoArrayType *array_type = mono_type_get_array_type(GDMonoClass::get_raw_type(type.type_class)); + MonoArrayType *array_type = mono_type_get_array_type(type.type_class->get_mono_type()); if (array_type->eklass == CACHED_CLASS_RAW(MonoObject)) SET_FROM_ARRAY_AND_BREAK(Array); @@ -200,6 +200,18 @@ void GDMonoField::set_value_from_variant(MonoObject *p_object, const Variant &p_ break; } + if (CACHED_CLASS(Dictionary) == type_class) { + MonoObject *managed = GDMonoUtils::create_managed_from(p_value.operator Dictionary(), CACHED_CLASS(Dictionary)); + mono_field_set_value(p_object, mono_field, managed); + break; + } + + if (CACHED_CLASS(Array) == type_class) { + MonoObject *managed = GDMonoUtils::create_managed_from(p_value.operator Array(), CACHED_CLASS(Array)); + 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; @@ -248,10 +260,13 @@ void GDMonoField::set_value_from_variant(MonoObject *p_object, const Variant &p_ break; } case Variant::DICTIONARY: { - MonoObject *managed = GDMonoMarshal::Dictionary_to_mono_object(p_value.operator Dictionary()); + MonoObject *managed = GDMonoUtils::create_managed_from(p_value.operator Dictionary(), CACHED_CLASS(Dictionary)); + mono_field_set_value(p_object, mono_field, managed); + } break; + case Variant::ARRAY: { + MonoObject *managed = GDMonoUtils::create_managed_from(p_value.operator Array(), CACHED_CLASS(Array)); 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); @@ -265,8 +280,28 @@ void GDMonoField::set_value_from_variant(MonoObject *p_object, const Variant &p_ } break; case MONO_TYPE_GENERICINST: { - if (CACHED_RAW_MONO_CLASS(Dictionary) == type.type_class->get_mono_ptr()) { - MonoObject *managed = GDMonoMarshal::Dictionary_to_mono_object(p_value.operator Dictionary()); + MonoReflectionType *reftype = mono_type_get_object(SCRIPTS_DOMAIN, type.type_class->get_mono_type()); + + MonoException *exc = NULL; + + GDMonoUtils::IsDictionaryGenericType type_is_dict = CACHED_METHOD_THUNK(MarshalUtils, IsDictionaryGenericType); + MonoBoolean is_dict = type_is_dict((MonoObject *)reftype, (MonoObject **)&exc); + UNLIKELY_UNHANDLED_EXCEPTION(exc); + + if (is_dict) { + MonoObject *managed = GDMonoUtils::create_managed_from(p_value.operator Dictionary(), type.type_class); + mono_field_set_value(p_object, mono_field, managed); + break; + } + + exc = NULL; + + GDMonoUtils::IsArrayGenericType type_is_array = CACHED_METHOD_THUNK(MarshalUtils, IsArrayGenericType); + MonoBoolean is_array = type_is_array((MonoObject *)reftype, (MonoObject **)&exc); + UNLIKELY_UNHANDLED_EXCEPTION(exc); + + if (is_array) { + MonoObject *managed = GDMonoUtils::create_managed_from(p_value.operator Array(), type.type_class); mono_field_set_value(p_object, mono_field, managed); break; } diff --git a/modules/mono/mono_gd/gd_mono_header.h b/modules/mono/mono_gd/gd_mono_header.h index 2b5110f0b9..72a5439044 100644 --- a/modules/mono/mono_gd/gd_mono_header.h +++ b/modules/mono/mono_gd/gd_mono_header.h @@ -45,7 +45,8 @@ struct ManagedType { GDMonoClass *type_class; ManagedType() { - type_class = 0; + type_encoding = 0; + type_class = NULL; } }; diff --git a/modules/mono/mono_gd/gd_mono_marshal.cpp b/modules/mono/mono_gd/gd_mono_marshal.cpp index 5cd77d63e2..de91e71bab 100644 --- a/modules/mono/mono_gd/gd_mono_marshal.cpp +++ b/modules/mono/mono_gd/gd_mono_marshal.cpp @@ -120,7 +120,7 @@ Variant::Type managed_to_variant_type(const ManagedType &p_type) { case MONO_TYPE_ARRAY: case MONO_TYPE_SZARRAY: { - MonoArrayType *array_type = mono_type_get_array_type(GDMonoClass::get_raw_type(p_type.type_class)); + MonoArrayType *array_type = mono_type_get_array_type(p_type.type_class->get_mono_type()); if (array_type->eklass == CACHED_CLASS_RAW(MonoObject)) return Variant::ARRAY; @@ -162,12 +162,36 @@ Variant::Type managed_to_variant_type(const ManagedType &p_type) { if (CACHED_CLASS(RID) == type_class) { return Variant::_RID; } + + if (CACHED_CLASS(Dictionary) == type_class) { + return Variant::DICTIONARY; + } + + if (CACHED_CLASS(Array) == type_class) { + return Variant::ARRAY; + } } break; case MONO_TYPE_GENERICINST: { - if (CACHED_RAW_MONO_CLASS(Dictionary) == p_type.type_class->get_mono_ptr()) { + MonoReflectionType *reftype = mono_type_get_object(SCRIPTS_DOMAIN, p_type.type_class->get_mono_type()); + + MonoException *exc = NULL; + GDMonoUtils::IsDictionaryGenericType type_is_dict = CACHED_METHOD_THUNK(MarshalUtils, IsDictionaryGenericType); + MonoBoolean is_dict = type_is_dict((MonoObject *)reftype, (MonoObject **)&exc); + UNLIKELY_UNHANDLED_EXCEPTION(exc); + + if (is_dict) { return Variant::DICTIONARY; } + + exc = NULL; + GDMonoUtils::IsArrayGenericType type_is_array = CACHED_METHOD_THUNK(MarshalUtils, IsArrayGenericType); + MonoBoolean is_array = type_is_array((MonoObject *)reftype, (MonoObject **)&exc); + UNLIKELY_UNHANDLED_EXCEPTION(exc); + + if (is_array) { + return Variant::ARRAY; + } } break; default: { @@ -216,6 +240,7 @@ MonoObject *variant_to_mono_object(const Variant *p_var) { ManagedType type; type.type_encoding = MONO_TYPE_OBJECT; + // type.type_class is not needed when we specify the MONO_TYPE_OBJECT encoding return variant_to_mono_object(p_var, type); } @@ -315,7 +340,7 @@ MonoObject *variant_to_mono_object(const Variant *p_var, const ManagedType &p_ty case MONO_TYPE_ARRAY: case MONO_TYPE_SZARRAY: { - MonoArrayType *array_type = mono_type_get_array_type(GDMonoClass::get_raw_type(p_type.type_class)); + MonoArrayType *array_type = mono_type_get_array_type(p_type.type_class->get_mono_type()); if (array_type->eklass == CACHED_CLASS_RAW(MonoObject)) return (MonoObject *)Array_to_mono_array(p_var->operator Array()); @@ -360,6 +385,14 @@ MonoObject *variant_to_mono_object(const Variant *p_var, const ManagedType &p_ty if (CACHED_CLASS(RID) == type_class) { return GDMonoUtils::create_managed_from(p_var->operator RID()); } + + if (CACHED_CLASS(Dictionary) == type_class) { + return GDMonoUtils::create_managed_from(p_var->operator Dictionary(), CACHED_CLASS(Dictionary)); + } + + if (CACHED_CLASS(Array) == type_class) { + return GDMonoUtils::create_managed_from(p_var->operator Array(), CACHED_CLASS(Array)); + } } break; case MONO_TYPE_OBJECT: { // Variant @@ -411,9 +444,9 @@ MonoObject *variant_to_mono_object(const Variant *p_var, const ManagedType &p_ty return GDMonoUtils::unmanaged_get_managed(p_var->operator Object *()); } case Variant::DICTIONARY: - return Dictionary_to_mono_object(p_var->operator Dictionary()); + return GDMonoUtils::create_managed_from(p_var->operator Dictionary(), CACHED_CLASS(Dictionary)); case Variant::ARRAY: - return (MonoObject *)Array_to_mono_array(p_var->operator Array()); + return GDMonoUtils::create_managed_from(p_var->operator Array(), CACHED_CLASS(Array)); case Variant::POOL_BYTE_ARRAY: return (MonoObject *)PoolByteArray_to_mono_array(p_var->operator PoolByteArray()); case Variant::POOL_INT_ARRAY: @@ -433,8 +466,24 @@ MonoObject *variant_to_mono_object(const Variant *p_var, const ManagedType &p_ty } break; case MONO_TYPE_GENERICINST: { - if (CACHED_RAW_MONO_CLASS(Dictionary) == p_type.type_class->get_mono_ptr()) { - return Dictionary_to_mono_object(p_var->operator Dictionary()); + MonoReflectionType *reftype = mono_type_get_object(SCRIPTS_DOMAIN, p_type.type_class->get_mono_type()); + + MonoException *exc = NULL; + GDMonoUtils::IsDictionaryGenericType type_is_dict = CACHED_METHOD_THUNK(MarshalUtils, IsDictionaryGenericType); + MonoBoolean is_dict = type_is_dict((MonoObject *)reftype, (MonoObject **)&exc); + UNLIKELY_UNHANDLED_EXCEPTION(exc); + + if (is_dict) { + return GDMonoUtils::create_managed_from(p_var->operator Dictionary(), p_type.type_class); + } + + exc = NULL; + GDMonoUtils::IsArrayGenericType type_is_array = CACHED_METHOD_THUNK(MarshalUtils, IsArrayGenericType); + MonoBoolean is_array = type_is_array((MonoObject *)reftype, (MonoObject **)&exc); + UNLIKELY_UNHANDLED_EXCEPTION(exc); + + if (is_array) { + return GDMonoUtils::create_managed_from(p_var->operator Array(), p_type.type_class); } } break; } break; @@ -452,7 +501,7 @@ Variant mono_object_to_variant(MonoObject *p_obj) { 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); + MonoType *raw_type = tclass->get_mono_type(); ManagedType type; @@ -531,7 +580,7 @@ Variant mono_object_to_variant(MonoObject *p_obj) { case MONO_TYPE_ARRAY: case MONO_TYPE_SZARRAY: { - MonoArrayType *array_type = mono_type_get_array_type(GDMonoClass::get_raw_type(type.type_class)); + MonoArrayType *array_type = mono_type_get_array_type(type.type_class->get_mono_type()); if (array_type->eklass == CACHED_CLASS_RAW(MonoObject)) return mono_array_to_Array((MonoArray *)p_obj); @@ -579,11 +628,51 @@ Variant mono_object_to_variant(MonoObject *p_obj) { RID *ptr = unbox<RID *>(CACHED_FIELD(RID, ptr)->get_value(p_obj)); return ptr ? Variant(*ptr) : Variant(); } + + if (CACHED_CLASS(Array) == type_class) { + MonoException *exc = NULL; + GDMonoUtils::Array_GetPtr get_ptr = CACHED_METHOD_THUNK(Array, GetPtr); + Array *ptr = get_ptr(p_obj, (MonoObject **)&exc); + UNLIKELY_UNHANDLED_EXCEPTION(exc); + return ptr ? Variant(*ptr) : Variant(); + } + + if (CACHED_CLASS(Dictionary) == type_class) { + MonoException *exc = NULL; + GDMonoUtils::Dictionary_GetPtr get_ptr = CACHED_METHOD_THUNK(Dictionary, GetPtr); + Dictionary *ptr = get_ptr(p_obj, (MonoObject **)&exc); + UNLIKELY_UNHANDLED_EXCEPTION(exc); + return ptr ? Variant(*ptr) : Variant(); + } } break; case MONO_TYPE_GENERICINST: { - if (CACHED_RAW_MONO_CLASS(Dictionary) == type.type_class->get_mono_ptr()) { - return mono_object_to_Dictionary(p_obj); + MonoReflectionType *reftype = mono_type_get_object(SCRIPTS_DOMAIN, type.type_class->get_mono_type()); + + MonoException *exc = NULL; + + GDMonoUtils::IsDictionaryGenericType type_is_dict = CACHED_METHOD_THUNK(MarshalUtils, IsDictionaryGenericType); + MonoBoolean is_dict = type_is_dict((MonoObject *)reftype, (MonoObject **)&exc); + UNLIKELY_UNHANDLED_EXCEPTION(exc); + + if (is_dict) { + MonoException *exc = NULL; + MonoObject *ret = type.type_class->get_method("GetPtr")->invoke(p_obj, &exc); + UNLIKELY_UNHANDLED_EXCEPTION(exc); + return *unbox<Dictionary *>(ret); + } + + exc = NULL; + + GDMonoUtils::IsArrayGenericType type_is_array = CACHED_METHOD_THUNK(MarshalUtils, IsArrayGenericType); + MonoBoolean is_array = type_is_array((MonoObject *)reftype, (MonoObject **)&exc); + UNLIKELY_UNHANDLED_EXCEPTION(exc); + + if (is_array) { + MonoException *exc = NULL; + MonoObject *ret = type.type_class->get_method("GetPtr")->invoke(p_obj, &exc); + UNLIKELY_UNHANDLED_EXCEPTION(exc); + return *unbox<Array *>(ret); } } break; } @@ -822,66 +911,4 @@ PoolVector3Array mono_array_to_PoolVector3Array(MonoArray *p_array) { 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); - - MonoException *exc = NULL; - GD_MONO_BEGIN_RUNTIME_INVOKE; - MonoObject *ret = arrays_to_dict(keys, values, (MonoObject **)&exc); - GD_MONO_END_RUNTIME_INVOKE; - - if (exc) { - GDMonoUtils::set_pending_exception(exc); - ERR_FAIL_V(NULL); - } - - return ret; -} - -Dictionary mono_object_to_Dictionary(MonoObject *p_dict) { - Dictionary ret; - - if (!p_dict) - return ret; - - GDMonoUtils::MarshalUtils_DictToArrays dict_to_arrays = CACHED_METHOD_THUNK(MarshalUtils, DictionaryToArrays); - - MonoArray *keys = NULL; - MonoArray *values = NULL; - MonoException *exc = NULL; - GD_MONO_BEGIN_RUNTIME_INVOKE; - dict_to_arrays(p_dict, &keys, &values, (MonoObject **)&exc); - GD_MONO_END_RUNTIME_INVOKE; - - if (exc) { - GDMonoUtils::set_pending_exception(exc); - 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; -} } // namespace GDMonoMarshal diff --git a/modules/mono/mono_gd/gd_mono_marshal.h b/modules/mono/mono_gd/gd_mono_marshal.h index 6572408ab5..464f584a0a 100644 --- a/modules/mono/mono_gd/gd_mono_marshal.h +++ b/modules/mono/mono_gd/gd_mono_marshal.h @@ -143,11 +143,6 @@ PoolVector2Array mono_array_to_PoolVector2Array(MonoArray *p_array); 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); diff --git a/modules/mono/mono_gd/gd_mono_property.cpp b/modules/mono/mono_gd/gd_mono_property.cpp index 1f837a2d78..a1c710c26c 100644 --- a/modules/mono/mono_gd/gd_mono_property.cpp +++ b/modules/mono/mono_gd/gd_mono_property.cpp @@ -139,23 +139,8 @@ bool GDMonoProperty::has_setter() { } void GDMonoProperty::set_value(MonoObject *p_object, MonoObject *p_value, MonoException **r_exc) { - MonoMethod *prop_method = mono_property_get_set_method(mono_property); - - MonoArray *params = mono_array_new(mono_domain_get(), CACHED_CLASS_RAW(MonoObject), 1); - mono_array_set(params, MonoObject *, 0, p_value); - - MonoException *exc = NULL; - GD_MONO_BEGIN_RUNTIME_INVOKE; - mono_runtime_invoke_array(prop_method, p_object, params, (MonoObject **)&exc); - GD_MONO_END_RUNTIME_INVOKE; - - if (exc) { - if (r_exc) { - *r_exc = exc; - } else { - GDMonoUtils::set_pending_exception(exc); - } - } + void *params[1] = { p_value }; + set_value(p_object, params, r_exc); } void GDMonoProperty::set_value(MonoObject *p_object, void **p_params, MonoException **r_exc) { diff --git a/modules/mono/mono_gd/gd_mono_utils.cpp b/modules/mono/mono_gd/gd_mono_utils.cpp index a229552b76..7cd922138f 100644 --- a/modules/mono/mono_gd/gd_mono_utils.cpp +++ b/modules/mono/mono_gd/gd_mono_utils.cpp @@ -87,6 +87,8 @@ void MonoCache::clear_members() { method_System_Diagnostics_StackTrace_ctor_Exception_bool = NULL; #endif + class_KeyNotFoundException = NULL; + rawclass_Dictionary = NULL; class_Vector2 = NULL; @@ -107,6 +109,8 @@ void MonoCache::clear_members() { class_Control = NULL; class_Spatial = NULL; class_WeakRef = NULL; + class_Array = NULL; + class_Dictionary = NULL; class_MarshalUtils = NULL; #ifdef DEBUG_ENABLED @@ -134,8 +138,10 @@ void MonoCache::clear_members() { field_Image_ptr = NULL; field_RID_ptr = NULL; - methodthunk_MarshalUtils_DictionaryToArrays = NULL; - methodthunk_MarshalUtils_ArraysToDictionary = NULL; + methodthunk_Array_GetPtr = NULL; + methodthunk_Dictionary_GetPtr = NULL; + methodthunk_MarshalUtils_IsArrayGenericType = NULL; + methodthunk_MarshalUtils_IsDictionaryGenericType = NULL; methodthunk_SignalAwaiter_SignalCallback = NULL; methodthunk_SignalAwaiter_FailureCallback = NULL; methodthunk_GodotTaskScheduler_Activate = NULL; @@ -175,6 +181,8 @@ void update_corlib_cache() { CACHE_METHOD_AND_CHECK(System_Diagnostics_StackTrace, ctor_Exception_bool, CACHED_CLASS(System_Diagnostics_StackTrace)->get_method_with_desc("System.Diagnostics.StackTrace:.ctor(System.Exception,bool)", true)); #endif + CACHE_CLASS_AND_CHECK(KeyNotFoundException, GDMono::get_singleton()->get_corlib_assembly()->get_class("System.Collections.Generic", "KeyNotFoundException")); + mono_cache.corlib_cache_updated = true; } @@ -198,6 +206,8 @@ void update_godot_api_cache() { 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(Array, GODOT_API_CLASS(Array)); + CACHE_CLASS_AND_CHECK(Dictionary, GODOT_API_CLASS(Dictionary)); CACHE_CLASS_AND_CHECK(MarshalUtils, GODOT_API_CLASS(MarshalUtils)); #ifdef DEBUG_ENABLED @@ -224,8 +234,10 @@ void update_godot_api_cache() { 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(Array, GetPtr, (Array_GetPtr)GODOT_API_CLASS(Array)->get_method("GetPtr", 0)->get_thunk()); + CACHE_METHOD_THUNK_AND_CHECK(Dictionary, GetPtr, (Dictionary_GetPtr)GODOT_API_CLASS(Dictionary)->get_method("GetPtr", 0)->get_thunk()); + CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, IsArrayGenericType, (IsArrayGenericType)GODOT_API_CLASS(MarshalUtils)->get_method("IsArrayGenericType", 1)->get_thunk()); + CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, IsDictionaryGenericType, (IsDictionaryGenericType)GODOT_API_CLASS(MarshalUtils)->get_method("IsDictionaryGenericType", 1)->get_thunk()); CACHE_METHOD_THUNK_AND_CHECK(SignalAwaiter, SignalCallback, (SignalAwaiter_SignalCallback)GODOT_API_CLASS(SignalAwaiter)->get_method("SignalCallback", 1)->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()); @@ -234,24 +246,9 @@ void update_godot_api_cache() { CACHE_METHOD_THUNK_AND_CHECK(DebuggingUtils, GetStackFrameInfo, (DebugUtils_StackFrameInfo)GODOT_API_CLASS(DebuggingUtils)->get_method("GetStackFrameInfo", 4)->get_thunk()); #endif - { - /* - * 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)); - } - + // TODO Move to CSharpLanguage::init() MonoObject *task_scheduler = mono_object_new(SCRIPTS_DOMAIN, GODOT_API_CLASS(GodotTaskScheduler)->get_mono_ptr()); - mono_runtime_object_init(task_scheduler); + GDMonoUtils::runtime_object_init(task_scheduler); mono_cache.task_scheduler_handle = MonoGCHandle::create_strong(task_scheduler); mono_cache.godot_api_cache_updated = true; @@ -304,6 +301,12 @@ MonoThread *get_current_thread() { return mono_thread_current(); } +void runtime_object_init(MonoObject *p_this_obj) { + GD_MONO_BEGIN_RUNTIME_INVOKE; + mono_runtime_object_init(p_this_obj); + GD_MONO_END_RUNTIME_INVOKE; +} + GDMonoClass *get_object_class(MonoObject *p_object) { return GDMono::get_singleton()->get_class(mono_object_get_class(p_object)); } @@ -358,7 +361,7 @@ MonoObject *create_managed_for_godot_object(GDMonoClass *p_class, const StringNa CACHED_FIELD(GodotObject, ptr)->set_value_raw(mono_object, p_object); // Construct - mono_runtime_object_init(mono_object); + GDMonoUtils::runtime_object_init(mono_object); return mono_object; } @@ -368,7 +371,7 @@ MonoObject *create_managed_from(const NodePath &p_from) { ERR_FAIL_NULL_V(mono_object, NULL); // Construct - mono_runtime_object_init(mono_object); + GDMonoUtils::runtime_object_init(mono_object); CACHED_FIELD(NodePath, ptr)->set_value_raw(mono_object, memnew(NodePath(p_from))); @@ -380,13 +383,73 @@ MonoObject *create_managed_from(const RID &p_from) { ERR_FAIL_NULL_V(mono_object, NULL); // Construct - mono_runtime_object_init(mono_object); + GDMonoUtils::runtime_object_init(mono_object); CACHED_FIELD(RID, ptr)->set_value_raw(mono_object, memnew(RID(p_from))); return mono_object; } +MonoObject *create_managed_from(const Array &p_from, GDMonoClass *p_class) { + MonoObject *mono_object = mono_object_new(SCRIPTS_DOMAIN, p_class->get_mono_ptr()); + ERR_FAIL_NULL_V(mono_object, NULL); + + // Search constructor that takes a pointer as parameter + MonoMethod *m; + void *iter = NULL; + while ((m = mono_class_get_methods(p_class->get_mono_ptr(), &iter))) { + if (strcmp(mono_method_get_name(m), ".ctor") == 0) { + MonoMethodSignature *sig = mono_method_signature(m); + void *front = NULL; + if (mono_signature_get_param_count(sig) == 1 && + mono_class_from_mono_type(mono_signature_get_params(sig, &front)) == CACHED_CLASS(IntPtr)->get_mono_ptr()) { + break; + } + } + } + + CRASH_COND(m == NULL); + + Array *new_array = memnew(Array(p_from)); + void *args[1] = { &new_array }; + + MonoException *exc = NULL; + mono_runtime_invoke(m, mono_object, args, (MonoObject **)&exc); + UNLIKELY_UNHANDLED_EXCEPTION(exc); + + return mono_object; +} + +MonoObject *create_managed_from(const Dictionary &p_from, GDMonoClass *p_class) { + MonoObject *mono_object = mono_object_new(SCRIPTS_DOMAIN, p_class->get_mono_ptr()); + ERR_FAIL_NULL_V(mono_object, NULL); + + // Search constructor that takes a pointer as parameter + MonoMethod *m; + void *iter = NULL; + while ((m = mono_class_get_methods(p_class->get_mono_ptr(), &iter))) { + if (strcmp(mono_method_get_name(m), ".ctor") == 0) { + MonoMethodSignature *sig = mono_method_signature(m); + void *front = NULL; + if (mono_signature_get_param_count(sig) == 1 && + mono_class_from_mono_type(mono_signature_get_params(sig, &front)) == CACHED_CLASS(IntPtr)->get_mono_ptr()) { + break; + } + } + } + + CRASH_COND(m == NULL); + + Dictionary *new_dict = memnew(Dictionary(p_from)); + void *args[1] = { &new_dict }; + + MonoException *exc = NULL; + mono_runtime_invoke(m, mono_object, args, (MonoObject **)&exc); + UNLIKELY_UNHANDLED_EXCEPTION(exc); + + 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); @@ -400,10 +463,10 @@ MonoDomain *create_domain(const String &p_friendly_name) { return domain; } -String get_exception_name_and_message(MonoException *p_ex) { +String get_exception_name_and_message(MonoException *p_exc) { String res; - MonoClass *klass = mono_object_get_class((MonoObject *)p_ex); + MonoClass *klass = mono_object_get_class((MonoObject *)p_exc); MonoType *type = mono_class_get_type(klass); char *full_name = mono_type_full_name(type); @@ -413,12 +476,24 @@ String get_exception_name_and_message(MonoException *p_ex) { res += ": "; MonoProperty *prop = mono_class_get_property_from_name(klass, "Message"); - MonoString *msg = (MonoString *)mono_property_get_value(prop, (MonoObject *)p_ex, NULL, NULL); + GD_MONO_BEGIN_RUNTIME_INVOKE; + MonoString *msg = (MonoString *)mono_property_get_value(prop, (MonoObject *)p_exc, NULL, NULL); + GD_MONO_END_RUNTIME_INVOKE; res += GDMonoMarshal::mono_string_to_godot(msg); return res; } +void set_exception_message(MonoException *p_exc, String message) { + MonoClass *klass = mono_object_get_class((MonoObject *)p_exc); + MonoProperty *prop = mono_class_get_property_from_name(klass, "Message"); + MonoString *msg = GDMonoMarshal::mono_string_from_godot(message); + void *params[1] = { msg }; + GD_MONO_BEGIN_RUNTIME_INVOKE; + mono_property_set_value(prop, (MonoObject *)p_exc, params, NULL); + GD_MONO_END_RUNTIME_INVOKE; +} + void debug_print_unhandled_exception(MonoException *p_exc) { print_unhandled_exception(p_exc); debug_send_unhandled_exception_error(p_exc); diff --git a/modules/mono/mono_gd/gd_mono_utils.h b/modules/mono/mono_gd/gd_mono_utils.h index 4f8e5932cd..d6774ed41d 100644 --- a/modules/mono/mono_gd/gd_mono_utils.h +++ b/modules/mono/mono_gd/gd_mono_utils.h @@ -41,14 +41,24 @@ #include "object.h" #include "reference.h" +#define UNLIKELY_UNHANDLED_EXCEPTION(m_exc) \ + if (unlikely(m_exc != NULL)) { \ + GDMonoUtils::debug_unhandled_exception(m_exc); \ + _UNREACHABLE_(); \ + } + namespace GDMonoUtils { -typedef MonoObject *(*MarshalUtils_DictToArrays)(MonoObject *, MonoArray **, MonoArray **, MonoObject **); -typedef MonoObject *(*MarshalUtils_ArraysToDict)(MonoArray *, MonoArray *, MonoObject **); +typedef Array *(*Array_GetPtr)(MonoObject *, MonoObject **); +typedef Dictionary *(*Dictionary_GetPtr)(MonoObject *, MonoObject **); typedef MonoObject *(*SignalAwaiter_SignalCallback)(MonoObject *, MonoArray *, MonoObject **); typedef MonoObject *(*SignalAwaiter_FailureCallback)(MonoObject *, MonoObject **); typedef MonoObject *(*GodotTaskScheduler_Activate)(MonoObject *, MonoObject **); typedef MonoArray *(*StackTrace_GetFrames)(MonoObject *, MonoObject **); +typedef MonoBoolean (*IsArrayGenericType)(MonoObject *, MonoObject **); +typedef MonoBoolean (*IsDictionaryGenericType)(MonoObject *, MonoObject **); +typedef MonoBoolean (*IsArrayGenericType)(MonoObject *, MonoObject **); +typedef MonoBoolean (*IsDictionaryGenericType)(MonoObject *, MonoObject **); typedef void (*DebugUtils_StackFrameInfo)(MonoObject *, MonoString **, int *, MonoString **, MonoObject **); struct MonoCache { @@ -79,6 +89,8 @@ struct MonoCache { GDMonoMethod *method_System_Diagnostics_StackTrace_ctor_Exception_bool; #endif + GDMonoClass *class_KeyNotFoundException; + MonoClass *rawclass_Dictionary; // ----------------------------------------------- @@ -100,6 +112,8 @@ struct MonoCache { GDMonoClass *class_Control; GDMonoClass *class_Spatial; GDMonoClass *class_WeakRef; + GDMonoClass *class_Array; + GDMonoClass *class_Dictionary; GDMonoClass *class_MarshalUtils; #ifdef DEBUG_ENABLED @@ -127,8 +141,10 @@ struct MonoCache { GDMonoField *field_Image_ptr; GDMonoField *field_RID_ptr; - MarshalUtils_DictToArrays methodthunk_MarshalUtils_DictionaryToArrays; - MarshalUtils_ArraysToDict methodthunk_MarshalUtils_ArraysToDictionary; + Array_GetPtr methodthunk_Array_GetPtr; + Dictionary_GetPtr methodthunk_Dictionary_GetPtr; + IsArrayGenericType methodthunk_MarshalUtils_IsArrayGenericType; + IsDictionaryGenericType methodthunk_MarshalUtils_IsDictionaryGenericType; SignalAwaiter_SignalCallback methodthunk_SignalAwaiter_SignalCallback; SignalAwaiter_FailureCallback methodthunk_SignalAwaiter_FailureCallback; GodotTaskScheduler_Activate methodthunk_GodotTaskScheduler_Activate; @@ -175,6 +191,8 @@ _FORCE_INLINE_ bool is_main_thread() { return mono_domain_get() != NULL && mono_thread_get_main() == mono_thread_current(); } +void runtime_object_init(MonoObject *p_this_obj); + GDMonoClass *get_object_class(MonoObject *p_object); GDMonoClass *type_get_proxy_class(const StringName &p_type); GDMonoClass *get_class_native_base(GDMonoClass *p_class); @@ -183,10 +201,13 @@ MonoObject *create_managed_for_godot_object(GDMonoClass *p_class, const StringNa MonoObject *create_managed_from(const NodePath &p_from); MonoObject *create_managed_from(const RID &p_from); +MonoObject *create_managed_from(const Array &p_from, GDMonoClass *p_class); +MonoObject *create_managed_from(const Dictionary &p_from, GDMonoClass *p_class); MonoDomain *create_domain(const String &p_friendly_name); -String get_exception_name_and_message(MonoException *p_ex); +String get_exception_name_and_message(MonoException *p_exc); +void set_exception_message(MonoException *p_exc, String message); void debug_print_unhandled_exception(MonoException *p_exc); void debug_send_unhandled_exception_error(MonoException *p_exc); |