C#: Add marshalling support for IEnumerable and IDictionary

Added constructor that takes IEnumerable for Array and IEnumerable<T> for Array<T>.
Added constructor that takes IDictionary for Dictionary and IDictionary<TKey, TValue> for Dictionary<TKey, TValue>.
This commit is contained in:
Ignacio Etcheverry 2019-03-05 18:52:19 +01:00
parent 92b02cb027
commit 187e6ae26d
11 changed files with 240 additions and 47 deletions

View file

@ -571,7 +571,7 @@ Vector<ScriptLanguage::StackInfo> CSharpLanguage::stack_trace_get_info(MonoObjec
MonoException *exc = NULL; MonoException *exc = NULL;
MonoArray *frames = invoke_method_thunk(CACHED_METHOD_THUNK(System_Diagnostics_StackTrace, GetFrames), p_stack_trace, (MonoObject **)&exc); MonoArray *frames = invoke_method_thunk(CACHED_METHOD_THUNK(System_Diagnostics_StackTrace, GetFrames), p_stack_trace, &exc);
if (exc) { if (exc) {
GDMonoUtils::debug_print_unhandled_exception(exc); GDMonoUtils::debug_print_unhandled_exception(exc);
@ -595,7 +595,7 @@ Vector<ScriptLanguage::StackInfo> CSharpLanguage::stack_trace_get_info(MonoObjec
MonoString *file_name; MonoString *file_name;
int file_line_num; int file_line_num;
MonoString *method_decl; MonoString *method_decl;
invoke_method_thunk(get_sf_info, frame, &file_name, &file_line_num, &method_decl, (MonoObject **)&exc); invoke_method_thunk(get_sf_info, frame, &file_name, &file_line_num, &method_decl, &exc);
if (exc) { if (exc) {
GDMonoUtils::debug_print_unhandled_exception(exc); GDMonoUtils::debug_print_unhandled_exception(exc);
@ -625,7 +625,7 @@ void CSharpLanguage::frame() {
if (task_scheduler) { if (task_scheduler) {
MonoException *exc = NULL; MonoException *exc = NULL;
invoke_method_thunk(CACHED_METHOD_THUNK(GodotTaskScheduler, Activate), task_scheduler, (MonoObject **)&exc); invoke_method_thunk(CACHED_METHOD_THUNK(GodotTaskScheduler, Activate), task_scheduler, &exc);
if (exc) { if (exc) {
GDMonoUtils::debug_unhandled_exception(exc); GDMonoUtils::debug_unhandled_exception(exc);

View file

@ -38,6 +38,14 @@ namespace Godot.Collections
safeHandle = new ArraySafeHandle(godot_icall_Array_Ctor()); safeHandle = new ArraySafeHandle(godot_icall_Array_Ctor());
} }
public Array(IEnumerable collection) : this()
{
if (collection == null)
throw new NullReferenceException($"Parameter '{nameof(collection)} cannot be null.'");
MarshalUtils.EnumerableToArray(collection, GetPtr());
}
internal Array(ArraySafeHandle handle) internal Array(ArraySafeHandle handle)
{ {
safeHandle = handle; safeHandle = handle;
@ -201,6 +209,14 @@ namespace Godot.Collections
objectArray = new Array(); objectArray = new Array();
} }
public Array(IEnumerable<T> collection)
{
if (collection == null)
throw new NullReferenceException($"Parameter '{nameof(collection)} cannot be null.'");
objectArray = new Array(collection);
}
public Array(Array array) public Array(Array array)
{ {
objectArray = array; objectArray = array;

View file

@ -40,6 +40,14 @@ namespace Godot.Collections
safeHandle = new DictionarySafeHandle(godot_icall_Dictionary_Ctor()); safeHandle = new DictionarySafeHandle(godot_icall_Dictionary_Ctor());
} }
public Dictionary(IDictionary dictionary) : this()
{
if (dictionary == null)
throw new NullReferenceException($"Parameter '{nameof(dictionary)} cannot be null.'");
MarshalUtils.IDictionaryToDictionary(dictionary, GetPtr());
}
internal Dictionary(DictionarySafeHandle handle) internal Dictionary(DictionarySafeHandle handle)
{ {
safeHandle = handle; safeHandle = handle;
@ -255,6 +263,23 @@ namespace Godot.Collections
objectDict = new Dictionary(); objectDict = new Dictionary();
} }
public Dictionary(IDictionary<TKey, TValue> dictionary)
{
objectDict = new Dictionary();
if (dictionary == null)
throw new NullReferenceException($"Parameter '{nameof(dictionary)} cannot be null.'");
// TODO: Can be optimized
IntPtr godotDictionaryPtr = GetPtr();
foreach (KeyValuePair<TKey, TValue> entry in dictionary)
{
Dictionary.godot_icall_Dictionary_Add(godotDictionaryPtr, entry.Key, entry.Value);
}
}
public Dictionary(Dictionary dictionary) public Dictionary(Dictionary dictionary)
{ {
objectDict = dictionary; objectDict = dictionary;

View file

@ -1,18 +1,54 @@
using System; using System;
using Godot.Collections; using System.Collections;
namespace Godot namespace Godot
{ {
using Array = Godot.Collections.Array;
using Dictionary = Godot.Collections.Dictionary;
static class MarshalUtils static class MarshalUtils
{ {
static bool IsArrayGenericType(Type type) static bool TypeIsGenericArray(Type type)
{ {
return type.GetGenericTypeDefinition() == typeof(Array<>); return type.GetGenericTypeDefinition() == typeof(Godot.Collections.Array<>);
} }
static bool IsDictionaryGenericType(Type type) static bool TypeIsGenericDictionary(Type type)
{ {
return type.GetGenericTypeDefinition() == typeof(Dictionary<, >); return type.GetGenericTypeDefinition() == typeof(Godot.Collections.Dictionary<,>);
}
// TODO: EnumerableToArray and IDictionaryToDictionary can be optimized
internal static void EnumerableToArray(IEnumerable enumerable, IntPtr godotArrayPtr)
{
if (enumerable is ICollection collection)
{
int count = collection.Count;
object[] tempArray = new object[count];
collection.CopyTo(tempArray, 0);
for (int i = 0; i < count; i++)
{
Array.godot_icall_Array_Add(godotArrayPtr, tempArray[i]);
}
}
else
{
foreach (object element in enumerable)
{
Array.godot_icall_Array_Add(godotArrayPtr, element);
}
}
}
internal static void IDictionaryToDictionary(IDictionary dictionary, IntPtr godotDictionaryPtr)
{
foreach (DictionaryEntry entry in dictionary)
{
Dictionary.godot_icall_Dictionary_Add(godotDictionaryPtr, entry.Key, entry.Value);
}
} }
} }
} }

View file

@ -260,6 +260,11 @@ bool GDMonoClass::has_fetched_method_unknown_params(const StringName &p_name) {
return get_fetched_method_unknown_params(p_name) != NULL; return get_fetched_method_unknown_params(p_name) != NULL;
} }
bool GDMonoClass::implements_interface(GDMonoClass *p_interface) {
return mono_class_implements_interface(mono_class, p_interface->get_mono_ptr());
}
GDMonoMethod *GDMonoClass::get_method(const StringName &p_name, int p_params_count) { GDMonoMethod *GDMonoClass::get_method(const StringName &p_name, int p_params_count) {
MethodKey key = MethodKey(p_name, p_params_count); MethodKey key = MethodKey(p_name, p_params_count);

View file

@ -135,6 +135,8 @@ public:
void fetch_attributes(); void fetch_attributes();
void fetch_methods_with_godot_api_checks(GDMonoClass *p_native_base); void fetch_methods_with_godot_api_checks(GDMonoClass *p_native_base);
bool implements_interface(GDMonoClass *p_interface);
GDMonoMethod *get_method(const StringName &p_name, int p_params_count = 0); GDMonoMethod *get_method(const StringName &p_name, int p_params_count = 0);
GDMonoMethod *get_method(MonoMethod *p_raw_method); 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);

View file

@ -313,6 +313,18 @@ void GDMonoField::set_value_from_variant(MonoObject *p_object, const Variant &p_
break; break;
} }
if (type_class->implements_interface(CACHED_CLASS(System_Collections_IDictionary))) {
MonoObject *managed = GDMonoUtils::create_managed_from(p_value.operator Dictionary(), CACHED_CLASS(Dictionary));
mono_field_set_value(p_object, mono_field, managed);
break;
}
if (type_class->implements_interface(CACHED_CLASS(System_Collections_IEnumerable))) {
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_EXPLAIN(String() + "Attempted to set the value of a field of unmarshallable type: " + type_class->get_name());
ERR_FAIL(); ERR_FAIL();
} break; } break;
@ -422,8 +434,8 @@ void GDMonoField::set_value_from_variant(MonoObject *p_object, const Variant &p_
MonoException *exc = NULL; MonoException *exc = NULL;
GDMonoUtils::IsDictionaryGenericType type_is_dict = CACHED_METHOD_THUNK(MarshalUtils, IsDictionaryGenericType); GDMonoUtils::TypeIsGenericDictionary type_is_dict = CACHED_METHOD_THUNK(MarshalUtils, TypeIsGenericDictionary);
MonoBoolean is_dict = invoke_method_thunk(type_is_dict, (MonoObject *)reftype, (MonoObject **)&exc); MonoBoolean is_dict = invoke_method_thunk(type_is_dict, (MonoObject *)reftype, &exc);
UNLIKELY_UNHANDLED_EXCEPTION(exc); UNLIKELY_UNHANDLED_EXCEPTION(exc);
if (is_dict) { if (is_dict) {
@ -434,8 +446,8 @@ void GDMonoField::set_value_from_variant(MonoObject *p_object, const Variant &p_
exc = NULL; exc = NULL;
GDMonoUtils::IsArrayGenericType type_is_array = CACHED_METHOD_THUNK(MarshalUtils, IsArrayGenericType); GDMonoUtils::TypeIsGenericArray type_is_array = CACHED_METHOD_THUNK(MarshalUtils, TypeIsGenericArray);
MonoBoolean is_array = invoke_method_thunk(type_is_array, (MonoObject *)reftype, (MonoObject **)&exc); MonoBoolean is_array = invoke_method_thunk(type_is_array, (MonoObject *)reftype, &exc);
UNLIKELY_UNHANDLED_EXCEPTION(exc); UNLIKELY_UNHANDLED_EXCEPTION(exc);
if (is_array) { if (is_array) {
@ -443,6 +455,18 @@ void GDMonoField::set_value_from_variant(MonoObject *p_object, const Variant &p_
mono_field_set_value(p_object, mono_field, managed); mono_field_set_value(p_object, mono_field, managed);
break; break;
} }
if (type.type_class->implements_interface(CACHED_CLASS(System_Collections_IDictionary))) {
MonoObject *managed = GDMonoUtils::create_managed_from(p_value.operator Dictionary(), CACHED_CLASS(Dictionary));
mono_field_set_value(p_object, mono_field, managed);
break;
}
if (type.type_class->implements_interface(CACHED_CLASS(System_Collections_IEnumerable))) {
MonoObject *managed = GDMonoUtils::create_managed_from(p_value.operator Array(), CACHED_CLASS(Array));
mono_field_set_value(p_object, mono_field, managed);
break;
}
} break; } break;
default: { default: {

View file

@ -156,14 +156,22 @@ Variant::Type managed_to_variant_type(const ManagedType &p_type) {
if (CACHED_CLASS(Array) == type_class) { if (CACHED_CLASS(Array) == type_class) {
return Variant::ARRAY; return Variant::ARRAY;
} }
if (type_class->implements_interface(CACHED_CLASS(System_Collections_IDictionary))) {
return Variant::DICTIONARY;
}
if (type_class->implements_interface(CACHED_CLASS(System_Collections_IEnumerable))) {
return Variant::ARRAY;
}
} break; } break;
case MONO_TYPE_GENERICINST: { case MONO_TYPE_GENERICINST: {
MonoReflectionType *reftype = mono_type_get_object(SCRIPTS_DOMAIN, p_type.type_class->get_mono_type()); MonoReflectionType *reftype = mono_type_get_object(SCRIPTS_DOMAIN, p_type.type_class->get_mono_type());
MonoException *exc = NULL; MonoException *exc = NULL;
GDMonoUtils::IsDictionaryGenericType type_is_dict = CACHED_METHOD_THUNK(MarshalUtils, IsDictionaryGenericType); GDMonoUtils::TypeIsGenericDictionary type_is_dict = CACHED_METHOD_THUNK(MarshalUtils, TypeIsGenericDictionary);
MonoBoolean is_dict = invoke_method_thunk(type_is_dict, (MonoObject *)reftype, (MonoObject **)&exc); MonoBoolean is_dict = invoke_method_thunk(type_is_dict, (MonoObject *)reftype, &exc);
UNLIKELY_UNHANDLED_EXCEPTION(exc); UNLIKELY_UNHANDLED_EXCEPTION(exc);
if (is_dict) { if (is_dict) {
@ -171,13 +179,21 @@ Variant::Type managed_to_variant_type(const ManagedType &p_type) {
} }
exc = NULL; exc = NULL;
GDMonoUtils::IsArrayGenericType type_is_array = CACHED_METHOD_THUNK(MarshalUtils, IsArrayGenericType); GDMonoUtils::TypeIsGenericArray type_is_array = CACHED_METHOD_THUNK(MarshalUtils, TypeIsGenericArray);
MonoBoolean is_array = invoke_method_thunk(type_is_array, (MonoObject *)reftype, (MonoObject **)&exc); MonoBoolean is_array = invoke_method_thunk(type_is_array, (MonoObject *)reftype, &exc);
UNLIKELY_UNHANDLED_EXCEPTION(exc); UNLIKELY_UNHANDLED_EXCEPTION(exc);
if (is_array) { if (is_array) {
return Variant::ARRAY; return Variant::ARRAY;
} }
if (p_type.type_class->implements_interface(CACHED_CLASS(System_Collections_IDictionary))) {
return Variant::DICTIONARY;
}
if (p_type.type_class->implements_interface(CACHED_CLASS(System_Collections_IEnumerable))) {
return Variant::ARRAY;
}
} break; } break;
default: { default: {
@ -453,6 +469,14 @@ MonoObject *variant_to_mono_object(const Variant *p_var, const ManagedType &p_ty
if (CACHED_CLASS(Array) == type_class) { if (CACHED_CLASS(Array) == type_class) {
return GDMonoUtils::create_managed_from(p_var->operator Array(), CACHED_CLASS(Array)); return GDMonoUtils::create_managed_from(p_var->operator Array(), CACHED_CLASS(Array));
} }
if (type_class->implements_interface(CACHED_CLASS(System_Collections_IDictionary))) {
return GDMonoUtils::create_managed_from(p_var->operator Dictionary(), CACHED_CLASS(Dictionary));
}
if (type_class->implements_interface(CACHED_CLASS(System_Collections_IEnumerable))) {
return GDMonoUtils::create_managed_from(p_var->operator Array(), CACHED_CLASS(Array));
}
} break; } break;
case MONO_TYPE_OBJECT: { case MONO_TYPE_OBJECT: {
// Variant // Variant
@ -548,8 +572,8 @@ MonoObject *variant_to_mono_object(const Variant *p_var, const ManagedType &p_ty
MonoReflectionType *reftype = mono_type_get_object(SCRIPTS_DOMAIN, p_type.type_class->get_mono_type()); MonoReflectionType *reftype = mono_type_get_object(SCRIPTS_DOMAIN, p_type.type_class->get_mono_type());
MonoException *exc = NULL; MonoException *exc = NULL;
GDMonoUtils::IsDictionaryGenericType type_is_dict = CACHED_METHOD_THUNK(MarshalUtils, IsDictionaryGenericType); GDMonoUtils::TypeIsGenericDictionary type_is_dict = CACHED_METHOD_THUNK(MarshalUtils, TypeIsGenericDictionary);
MonoBoolean is_dict = invoke_method_thunk(type_is_dict, (MonoObject *)reftype, (MonoObject **)&exc); MonoBoolean is_dict = invoke_method_thunk(type_is_dict, (MonoObject *)reftype, &exc);
UNLIKELY_UNHANDLED_EXCEPTION(exc); UNLIKELY_UNHANDLED_EXCEPTION(exc);
if (is_dict) { if (is_dict) {
@ -557,13 +581,21 @@ MonoObject *variant_to_mono_object(const Variant *p_var, const ManagedType &p_ty
} }
exc = NULL; exc = NULL;
GDMonoUtils::IsArrayGenericType type_is_array = CACHED_METHOD_THUNK(MarshalUtils, IsArrayGenericType); GDMonoUtils::TypeIsGenericArray type_is_array = CACHED_METHOD_THUNK(MarshalUtils, TypeIsGenericArray);
MonoBoolean is_array = invoke_method_thunk(type_is_array, (MonoObject *)reftype, (MonoObject **)&exc); MonoBoolean is_array = invoke_method_thunk(type_is_array, (MonoObject *)reftype, &exc);
UNLIKELY_UNHANDLED_EXCEPTION(exc); UNLIKELY_UNHANDLED_EXCEPTION(exc);
if (is_array) { if (is_array) {
return GDMonoUtils::create_managed_from(p_var->operator Array(), p_type.type_class); return GDMonoUtils::create_managed_from(p_var->operator Array(), p_type.type_class);
} }
if (p_type.type_class->implements_interface(CACHED_CLASS(System_Collections_IDictionary))) {
return GDMonoUtils::create_managed_from(p_var->operator Dictionary(), CACHED_CLASS(Dictionary));
}
if (p_type.type_class->implements_interface(CACHED_CLASS(System_Collections_IEnumerable))) {
return GDMonoUtils::create_managed_from(p_var->operator Array(), CACHED_CLASS(Array));
}
} break; } break;
} break; } break;
} }
@ -717,17 +749,33 @@ Variant mono_object_to_variant(MonoObject *p_obj) {
if (CACHED_CLASS(Array) == type_class) { if (CACHED_CLASS(Array) == type_class) {
MonoException *exc = NULL; MonoException *exc = NULL;
Array *ptr = invoke_method_thunk(CACHED_METHOD_THUNK(Array, GetPtr), p_obj, (MonoObject **)&exc); Array *ptr = invoke_method_thunk(CACHED_METHOD_THUNK(Array, GetPtr), p_obj, &exc);
UNLIKELY_UNHANDLED_EXCEPTION(exc); UNLIKELY_UNHANDLED_EXCEPTION(exc);
return ptr ? Variant(*ptr) : Variant(); return ptr ? Variant(*ptr) : Variant();
} }
if (CACHED_CLASS(Dictionary) == type_class) { if (CACHED_CLASS(Dictionary) == type_class) {
MonoException *exc = NULL; MonoException *exc = NULL;
Dictionary *ptr = invoke_method_thunk(CACHED_METHOD_THUNK(Dictionary, GetPtr), p_obj, (MonoObject **)&exc); Dictionary *ptr = invoke_method_thunk(CACHED_METHOD_THUNK(Dictionary, GetPtr), p_obj, &exc);
UNLIKELY_UNHANDLED_EXCEPTION(exc); UNLIKELY_UNHANDLED_EXCEPTION(exc);
return ptr ? Variant(*ptr) : Variant(); return ptr ? Variant(*ptr) : Variant();
} }
if (type_class->implements_interface(CACHED_CLASS(System_Collections_IDictionary))) {
Dictionary dict;
MonoException *exc = NULL;
invoke_method_thunk(CACHED_METHOD_THUNK(MarshalUtils, IDictionaryToDictionary), p_obj, &dict, &exc);
UNLIKELY_UNHANDLED_EXCEPTION(exc);
return dict;
}
if (type_class->implements_interface(CACHED_CLASS(System_Collections_IEnumerable))) {
Array array;
MonoException *exc = NULL;
invoke_method_thunk(CACHED_METHOD_THUNK(MarshalUtils, EnumerableToArray), p_obj, &array, &exc);
UNLIKELY_UNHANDLED_EXCEPTION(exc);
return array;
}
} break; } break;
case MONO_TYPE_GENERICINST: { case MONO_TYPE_GENERICINST: {
@ -735,8 +783,8 @@ Variant mono_object_to_variant(MonoObject *p_obj) {
MonoException *exc = NULL; MonoException *exc = NULL;
GDMonoUtils::IsDictionaryGenericType type_is_dict = CACHED_METHOD_THUNK(MarshalUtils, IsDictionaryGenericType); GDMonoUtils::TypeIsGenericDictionary type_is_dict = CACHED_METHOD_THUNK(MarshalUtils, TypeIsGenericDictionary);
MonoBoolean is_dict = invoke_method_thunk(type_is_dict, (MonoObject *)reftype, (MonoObject **)&exc); MonoBoolean is_dict = invoke_method_thunk(type_is_dict, (MonoObject *)reftype, &exc);
UNLIKELY_UNHANDLED_EXCEPTION(exc); UNLIKELY_UNHANDLED_EXCEPTION(exc);
if (is_dict) { if (is_dict) {
@ -748,8 +796,8 @@ Variant mono_object_to_variant(MonoObject *p_obj) {
exc = NULL; exc = NULL;
GDMonoUtils::IsArrayGenericType type_is_array = CACHED_METHOD_THUNK(MarshalUtils, IsArrayGenericType); GDMonoUtils::TypeIsGenericArray type_is_array = CACHED_METHOD_THUNK(MarshalUtils, TypeIsGenericArray);
MonoBoolean is_array = invoke_method_thunk(type_is_array, (MonoObject *)reftype, (MonoObject **)&exc); MonoBoolean is_array = invoke_method_thunk(type_is_array, (MonoObject *)reftype, &exc);
UNLIKELY_UNHANDLED_EXCEPTION(exc); UNLIKELY_UNHANDLED_EXCEPTION(exc);
if (is_array) { if (is_array) {
@ -758,6 +806,22 @@ Variant mono_object_to_variant(MonoObject *p_obj) {
UNLIKELY_UNHANDLED_EXCEPTION(exc); UNLIKELY_UNHANDLED_EXCEPTION(exc);
return *unbox<Array *>(ret); return *unbox<Array *>(ret);
} }
if (type.type_class->implements_interface(CACHED_CLASS(System_Collections_IDictionary))) {
Dictionary dict;
exc = NULL;
invoke_method_thunk(CACHED_METHOD_THUNK(MarshalUtils, IDictionaryToDictionary), p_obj, &dict, &exc);
UNLIKELY_UNHANDLED_EXCEPTION(exc);
return dict;
}
if (type.type_class->implements_interface(CACHED_CLASS(System_Collections_IEnumerable))) {
Array array;
exc = NULL;
invoke_method_thunk(CACHED_METHOD_THUNK(MarshalUtils, EnumerableToArray), p_obj, &array, &exc);
UNLIKELY_UNHANDLED_EXCEPTION(exc);
return array;
}
} break; } break;
} }

View file

@ -63,6 +63,7 @@ MonoCache mono_cache;
#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_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_AND_CHECK(m_class, m_method, m_val) CACHE_AND_CHECK(GDMonoUtils::mono_cache.method_##m_class##_##m_method, m_val) #define CACHE_METHOD_AND_CHECK(m_class, m_method, m_val) CACHE_AND_CHECK(GDMonoUtils::mono_cache.method_##m_class##_##m_method, 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) #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)
#define CACHE_PROPERTY_AND_CHECK(m_class, m_property, m_val) CACHE_AND_CHECK(GDMonoUtils::mono_cache.property_##m_class##_##m_property, m_val)
void MonoCache::clear_members() { void MonoCache::clear_members() {
@ -81,6 +82,9 @@ void MonoCache::clear_members() {
class_String = NULL; class_String = NULL;
class_IntPtr = NULL; class_IntPtr = NULL;
class_System_Collections_IEnumerable = NULL;
class_System_Collections_IDictionary = NULL;
#ifdef DEBUG_ENABLED #ifdef DEBUG_ENABLED
class_System_Diagnostics_StackTrace = NULL; class_System_Diagnostics_StackTrace = NULL;
methodthunk_System_Diagnostics_StackTrace_GetFrames = NULL; methodthunk_System_Diagnostics_StackTrace_GetFrames = NULL;
@ -143,12 +147,15 @@ void MonoCache::clear_members() {
methodthunk_GodotObject_Dispose = NULL; methodthunk_GodotObject_Dispose = NULL;
methodthunk_Array_GetPtr = NULL; methodthunk_Array_GetPtr = NULL;
methodthunk_Dictionary_GetPtr = NULL; methodthunk_Dictionary_GetPtr = NULL;
methodthunk_MarshalUtils_IsArrayGenericType = NULL;
methodthunk_MarshalUtils_IsDictionaryGenericType = NULL;
methodthunk_SignalAwaiter_SignalCallback = NULL; methodthunk_SignalAwaiter_SignalCallback = NULL;
methodthunk_SignalAwaiter_FailureCallback = NULL; methodthunk_SignalAwaiter_FailureCallback = NULL;
methodthunk_GodotTaskScheduler_Activate = NULL; methodthunk_GodotTaskScheduler_Activate = NULL;
methodthunk_MarshalUtils_TypeIsGenericArray = NULL;
methodthunk_MarshalUtils_TypeIsGenericDictionary = NULL;
methodthunk_MarshalUtils_EnumerableToArray = NULL;
methodthunk_MarshalUtils_IDictionaryToDictionary = NULL;
task_scheduler_handle = Ref<MonoGCHandle>(); task_scheduler_handle = Ref<MonoGCHandle>();
} }
@ -178,6 +185,9 @@ void update_corlib_cache() {
CACHE_CLASS_AND_CHECK(String, GDMono::get_singleton()->get_corlib_assembly()->get_class(mono_get_string_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())); CACHE_CLASS_AND_CHECK(IntPtr, GDMono::get_singleton()->get_corlib_assembly()->get_class(mono_get_intptr_class()));
CACHE_CLASS_AND_CHECK(System_Collections_IEnumerable, GDMono::get_singleton()->get_corlib_assembly()->get_class("System.Collections", "IEnumerable"));
CACHE_CLASS_AND_CHECK(System_Collections_IDictionary, GDMono::get_singleton()->get_corlib_assembly()->get_class("System.Collections", "IDictionary"));
#ifdef DEBUG_ENABLED #ifdef DEBUG_ENABLED
CACHE_CLASS_AND_CHECK(System_Diagnostics_StackTrace, GDMono::get_singleton()->get_corlib_assembly()->get_class("System.Diagnostics", "StackTrace")); CACHE_CLASS_AND_CHECK(System_Diagnostics_StackTrace, GDMono::get_singleton()->get_corlib_assembly()->get_class("System.Diagnostics", "StackTrace"));
CACHE_METHOD_THUNK_AND_CHECK(System_Diagnostics_StackTrace, GetFrames, (StackTrace_GetFrames)CACHED_CLASS(System_Diagnostics_StackTrace)->get_method_thunk("GetFrames")); CACHE_METHOD_THUNK_AND_CHECK(System_Diagnostics_StackTrace, GetFrames, (StackTrace_GetFrames)CACHED_CLASS(System_Diagnostics_StackTrace)->get_method_thunk("GetFrames"));
@ -242,12 +252,15 @@ void update_godot_api_cache() {
CACHE_METHOD_THUNK_AND_CHECK(GodotObject, Dispose, (GodotObject_Dispose)CACHED_CLASS(GodotObject)->get_method_thunk("Dispose", 0)); CACHE_METHOD_THUNK_AND_CHECK(GodotObject, Dispose, (GodotObject_Dispose)CACHED_CLASS(GodotObject)->get_method_thunk("Dispose", 0));
CACHE_METHOD_THUNK_AND_CHECK(Array, GetPtr, (Array_GetPtr)GODOT_API_NS_CLAS(BINDINGS_NAMESPACE_COLLECTIONS, Array)->get_method_thunk("GetPtr", 0)); CACHE_METHOD_THUNK_AND_CHECK(Array, GetPtr, (Array_GetPtr)GODOT_API_NS_CLAS(BINDINGS_NAMESPACE_COLLECTIONS, Array)->get_method_thunk("GetPtr", 0));
CACHE_METHOD_THUNK_AND_CHECK(Dictionary, GetPtr, (Dictionary_GetPtr)GODOT_API_NS_CLAS(BINDINGS_NAMESPACE_COLLECTIONS, Dictionary)->get_method_thunk("GetPtr", 0)); CACHE_METHOD_THUNK_AND_CHECK(Dictionary, GetPtr, (Dictionary_GetPtr)GODOT_API_NS_CLAS(BINDINGS_NAMESPACE_COLLECTIONS, Dictionary)->get_method_thunk("GetPtr", 0));
CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, IsArrayGenericType, (IsArrayGenericType)GODOT_API_CLASS(MarshalUtils)->get_method_thunk("IsArrayGenericType", 1));
CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, IsDictionaryGenericType, (IsDictionaryGenericType)GODOT_API_CLASS(MarshalUtils)->get_method_thunk("IsDictionaryGenericType", 1));
CACHE_METHOD_THUNK_AND_CHECK(SignalAwaiter, SignalCallback, (SignalAwaiter_SignalCallback)GODOT_API_CLASS(SignalAwaiter)->get_method_thunk("SignalCallback", 1)); CACHE_METHOD_THUNK_AND_CHECK(SignalAwaiter, SignalCallback, (SignalAwaiter_SignalCallback)GODOT_API_CLASS(SignalAwaiter)->get_method_thunk("SignalCallback", 1));
CACHE_METHOD_THUNK_AND_CHECK(SignalAwaiter, FailureCallback, (SignalAwaiter_FailureCallback)GODOT_API_CLASS(SignalAwaiter)->get_method_thunk("FailureCallback", 0)); CACHE_METHOD_THUNK_AND_CHECK(SignalAwaiter, FailureCallback, (SignalAwaiter_FailureCallback)GODOT_API_CLASS(SignalAwaiter)->get_method_thunk("FailureCallback", 0));
CACHE_METHOD_THUNK_AND_CHECK(GodotTaskScheduler, Activate, (GodotTaskScheduler_Activate)GODOT_API_CLASS(GodotTaskScheduler)->get_method_thunk("Activate", 0)); CACHE_METHOD_THUNK_AND_CHECK(GodotTaskScheduler, Activate, (GodotTaskScheduler_Activate)GODOT_API_CLASS(GodotTaskScheduler)->get_method_thunk("Activate", 0));
CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, TypeIsGenericArray, (TypeIsGenericArray)GODOT_API_CLASS(MarshalUtils)->get_method_thunk("TypeIsGenericArray", 1));
CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, TypeIsGenericDictionary, (TypeIsGenericDictionary)GODOT_API_CLASS(MarshalUtils)->get_method_thunk("TypeIsGenericDictionary", 1));
CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, EnumerableToArray, (EnumerableToArray)GODOT_API_CLASS(MarshalUtils)->get_method_thunk("EnumerableToArray", 2));
CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, IDictionaryToDictionary, (IDictionaryToDictionary)GODOT_API_CLASS(MarshalUtils)->get_method_thunk("IDictionaryToDictionary", 2));
#ifdef DEBUG_ENABLED #ifdef DEBUG_ENABLED
CACHE_METHOD_THUNK_AND_CHECK(DebuggingUtils, GetStackFrameInfo, (DebugUtils_StackFrameInfo)GODOT_API_CLASS(DebuggingUtils)->get_method_thunk("GetStackFrameInfo", 4)); CACHE_METHOD_THUNK_AND_CHECK(DebuggingUtils, GetStackFrameInfo, (DebugUtils_StackFrameInfo)GODOT_API_CLASS(DebuggingUtils)->get_method_thunk("GetStackFrameInfo", 4));
#endif #endif
@ -712,7 +725,7 @@ uint64_t unbox_enum_value(MonoObject *p_boxed, MonoType *p_enum_basetype, bool &
} }
void dispose(MonoObject *p_mono_object, MonoException **r_exc) { void dispose(MonoObject *p_mono_object, MonoException **r_exc) {
invoke_method_thunk(CACHED_METHOD_THUNK(GodotObject, Dispose), p_mono_object, (MonoObject **)r_exc); invoke_method_thunk(CACHED_METHOD_THUNK(GodotObject, Dispose), p_mono_object, r_exc);
} }
} // namespace GDMonoUtils } // namespace GDMonoUtils

View file

@ -49,18 +49,19 @@
namespace GDMonoUtils { namespace GDMonoUtils {
typedef void (*GodotObject_Dispose)(MonoObject *, MonoObject **); typedef void (*GodotObject_Dispose)(MonoObject *, MonoException **);
typedef Array *(*Array_GetPtr)(MonoObject *, MonoObject **); typedef Array *(*Array_GetPtr)(MonoObject *, MonoException **);
typedef Dictionary *(*Dictionary_GetPtr)(MonoObject *, MonoObject **); typedef Dictionary *(*Dictionary_GetPtr)(MonoObject *, MonoException **);
typedef MonoObject *(*SignalAwaiter_SignalCallback)(MonoObject *, MonoArray *, MonoObject **); typedef MonoObject *(*SignalAwaiter_SignalCallback)(MonoObject *, MonoArray *, MonoException **);
typedef MonoObject *(*SignalAwaiter_FailureCallback)(MonoObject *, MonoObject **); typedef MonoObject *(*SignalAwaiter_FailureCallback)(MonoObject *, MonoException **);
typedef MonoObject *(*GodotTaskScheduler_Activate)(MonoObject *, MonoObject **); typedef MonoObject *(*GodotTaskScheduler_Activate)(MonoObject *, MonoException **);
typedef MonoArray *(*StackTrace_GetFrames)(MonoObject *, MonoObject **); typedef MonoArray *(*StackTrace_GetFrames)(MonoObject *, MonoException **);
typedef MonoBoolean (*IsArrayGenericType)(MonoObject *, MonoObject **); typedef void (*DebugUtils_StackFrameInfo)(MonoObject *, MonoString **, int *, MonoString **, MonoException **);
typedef MonoBoolean (*IsDictionaryGenericType)(MonoObject *, MonoObject **);
typedef MonoBoolean (*IsArrayGenericType)(MonoObject *, MonoObject **); typedef MonoBoolean (*TypeIsGenericArray)(MonoObject *, MonoException **);
typedef MonoBoolean (*IsDictionaryGenericType)(MonoObject *, MonoObject **); typedef MonoBoolean (*TypeIsGenericDictionary)(MonoObject *, MonoException **);
typedef void (*DebugUtils_StackFrameInfo)(MonoObject *, MonoString **, int *, MonoString **, MonoObject **); typedef void (*EnumerableToArray)(MonoObject *, Array *, MonoException **);
typedef void (*IDictionaryToDictionary)(MonoObject *, Dictionary *, MonoException **);
struct MonoCache { struct MonoCache {
@ -83,6 +84,9 @@ struct MonoCache {
GDMonoClass *class_String; GDMonoClass *class_String;
GDMonoClass *class_IntPtr; GDMonoClass *class_IntPtr;
GDMonoClass *class_System_Collections_IEnumerable;
GDMonoClass *class_System_Collections_IDictionary;
#ifdef DEBUG_ENABLED #ifdef DEBUG_ENABLED
GDMonoClass *class_System_Diagnostics_StackTrace; GDMonoClass *class_System_Diagnostics_StackTrace;
StackTrace_GetFrames methodthunk_System_Diagnostics_StackTrace_GetFrames; StackTrace_GetFrames methodthunk_System_Diagnostics_StackTrace_GetFrames;
@ -146,12 +150,15 @@ struct MonoCache {
GodotObject_Dispose methodthunk_GodotObject_Dispose; GodotObject_Dispose methodthunk_GodotObject_Dispose;
Array_GetPtr methodthunk_Array_GetPtr; Array_GetPtr methodthunk_Array_GetPtr;
Dictionary_GetPtr methodthunk_Dictionary_GetPtr; Dictionary_GetPtr methodthunk_Dictionary_GetPtr;
IsArrayGenericType methodthunk_MarshalUtils_IsArrayGenericType;
IsDictionaryGenericType methodthunk_MarshalUtils_IsDictionaryGenericType;
SignalAwaiter_SignalCallback methodthunk_SignalAwaiter_SignalCallback; SignalAwaiter_SignalCallback methodthunk_SignalAwaiter_SignalCallback;
SignalAwaiter_FailureCallback methodthunk_SignalAwaiter_FailureCallback; SignalAwaiter_FailureCallback methodthunk_SignalAwaiter_FailureCallback;
GodotTaskScheduler_Activate methodthunk_GodotTaskScheduler_Activate; GodotTaskScheduler_Activate methodthunk_GodotTaskScheduler_Activate;
TypeIsGenericArray methodthunk_MarshalUtils_TypeIsGenericArray;
TypeIsGenericDictionary methodthunk_MarshalUtils_TypeIsGenericDictionary;
EnumerableToArray methodthunk_MarshalUtils_EnumerableToArray;
IDictionaryToDictionary methodthunk_MarshalUtils_IDictionaryToDictionary;
Ref<MonoGCHandle> task_scheduler_handle; Ref<MonoGCHandle> task_scheduler_handle;
bool corlib_cache_updated; bool corlib_cache_updated;
@ -255,6 +262,7 @@ void dispose(MonoObject *p_mono_object, MonoException **r_exc);
#define CACHED_FIELD(m_class, m_field) (GDMonoUtils::mono_cache.field_##m_class##_##m_field) #define CACHED_FIELD(m_class, m_field) (GDMonoUtils::mono_cache.field_##m_class##_##m_field)
#define CACHED_METHOD(m_class, m_method) (GDMonoUtils::mono_cache.method_##m_class##_##m_method) #define CACHED_METHOD(m_class, m_method) (GDMonoUtils::mono_cache.method_##m_class##_##m_method)
#define CACHED_METHOD_THUNK(m_class, m_method) (GDMonoUtils::mono_cache.methodthunk_##m_class##_##m_method) #define CACHED_METHOD_THUNK(m_class, m_method) (GDMonoUtils::mono_cache.methodthunk_##m_class##_##m_method)
#define CACHED_PROPERTY(m_class, m_property) (GDMonoUtils::mono_cache.property_##m_class##_##m_property)
#ifdef REAL_T_IS_DOUBLE #ifdef REAL_T_IS_DOUBLE
#define REAL_T_MONOCLASS CACHED_CLASS_RAW(double) #define REAL_T_MONOCLASS CACHED_CLASS_RAW(double)

View file

@ -100,7 +100,7 @@ Variant SignalAwaiterHandle::_signal_callback(const Variant **p_args, int p_argc
MonoException *exc = NULL; MonoException *exc = NULL;
GD_MONO_BEGIN_RUNTIME_INVOKE; GD_MONO_BEGIN_RUNTIME_INVOKE;
invoke_method_thunk(CACHED_METHOD_THUNK(SignalAwaiter, SignalCallback), get_target(), signal_args, (MonoObject **)&exc); invoke_method_thunk(CACHED_METHOD_THUNK(SignalAwaiter, SignalCallback), get_target(), signal_args, &exc);
GD_MONO_END_RUNTIME_INVOKE; GD_MONO_END_RUNTIME_INVOKE;
if (exc) { if (exc) {
@ -132,7 +132,7 @@ SignalAwaiterHandle::~SignalAwaiterHandle() {
if (awaiter) { if (awaiter) {
MonoException *exc = NULL; MonoException *exc = NULL;
GD_MONO_BEGIN_RUNTIME_INVOKE; GD_MONO_BEGIN_RUNTIME_INVOKE;
invoke_method_thunk(CACHED_METHOD_THUNK(SignalAwaiter, FailureCallback), awaiter, (MonoObject **)&exc); invoke_method_thunk(CACHED_METHOD_THUNK(SignalAwaiter, FailureCallback), awaiter, &exc);
GD_MONO_END_RUNTIME_INVOKE; GD_MONO_END_RUNTIME_INVOKE;
if (exc) { if (exc) {