C#: Marshalling support for IEnumerable<> and IDictionary<,>

Also fixed the hint string of exported members.
This commit is contained in:
Ignacio Etcheverry 2019-05-18 04:14:21 +02:00
parent 7112a45d99
commit 5a4bf4f369
13 changed files with 591 additions and 192 deletions

View file

@ -2020,7 +2020,7 @@ bool CSharpScript::_update_exports() {
for (int i = fields.size() - 1; i >= 0; i--) {
GDMonoField *field = fields[i];
if (_get_member_export(top, field, prop_info, exported)) {
if (_get_member_export(field, prop_info, exported)) {
StringName name = field->get_name();
if (exported) {
@ -2041,7 +2041,7 @@ bool CSharpScript::_update_exports() {
for (int i = properties.size() - 1; i >= 0; i--) {
GDMonoProperty *property = properties[i];
if (_get_member_export(top, property, prop_info, exported)) {
if (_get_member_export(property, prop_info, exported)) {
StringName name = property->get_name();
if (exported) {
@ -2168,17 +2168,19 @@ bool CSharpScript::_get_signal(GDMonoClass *p_class, GDMonoClass *p_delegate, Ve
* Returns false if there was an error, otherwise true.
* If there was an error, r_prop_info and r_exported are not assigned any value.
*/
bool CSharpScript::_get_member_export(GDMonoClass *p_class, IMonoClassMember *p_member, PropertyInfo &r_prop_info, bool &r_exported) {
bool CSharpScript::_get_member_export(IMonoClassMember *p_member, PropertyInfo &r_prop_info, bool &r_exported) {
StringName name = p_member->get_name();
// Goddammit, C++. All I wanted was some nested functions.
#define MEMBER_FULL_QUALIFIED_NAME(m_member) \
(m_member->get_enclosing_class()->get_full_name() + "." + (String)m_member->get_name())
if (p_member->is_static()) {
if (p_member->has_attribute(CACHED_CLASS(ExportAttribute)))
ERR_PRINTS("Cannot export member because it is static: " + p_class->get_full_name() + "." + name.operator String());
ERR_PRINTS("Cannot export member because it is static: " + MEMBER_FULL_QUALIFIED_NAME(p_member));
return false;
}
if (member_info.has(name))
if (member_info.has(p_member->get_name()))
return false;
ManagedType type;
@ -2191,19 +2193,22 @@ bool CSharpScript::_get_member_export(GDMonoClass *p_class, IMonoClassMember *p_
CRASH_NOW();
}
GDMonoMarshal::ExportInfo export_info;
Variant::Type variant_type = GDMonoMarshal::managed_to_variant_type(type, &export_info);
Variant::Type variant_type = GDMonoMarshal::managed_to_variant_type(type);
if (!p_member->has_attribute(CACHED_CLASS(ExportAttribute))) {
r_prop_info = PropertyInfo(variant_type, name.operator String(), PROPERTY_HINT_NONE, "", PROPERTY_USAGE_SCRIPT_VARIABLE);
r_prop_info = PropertyInfo(variant_type, (String)p_member->get_name(), PROPERTY_HINT_NONE, "", PROPERTY_USAGE_SCRIPT_VARIABLE);
r_exported = false;
return true;
}
if (p_member->get_member_type() == IMonoClassMember::MEMBER_TYPE_PROPERTY) {
GDMonoProperty *property = static_cast<GDMonoProperty *>(p_member);
if (!property->has_getter() || !property->has_setter()) {
ERR_PRINTS("Cannot export property because it does not provide a getter or a setter: " + p_class->get_full_name() + "." + name.operator String());
if (!property->has_getter()) {
ERR_PRINTS("Read-only property cannot be exported: " + MEMBER_FULL_QUALIFIED_NAME(p_member));
return false;
}
if (!property->has_setter()) {
ERR_PRINTS("Set-only property (without getter) cannot be exported: " + MEMBER_FULL_QUALIFIED_NAME(p_member));
return false;
}
}
@ -2214,16 +2219,38 @@ bool CSharpScript::_get_member_export(GDMonoClass *p_class, IMonoClassMember *p_
String hint_string;
if (variant_type == Variant::NIL) {
ERR_PRINTS("Unknown type of exported member: " + p_class->get_full_name() + "." + name.operator String());
ERR_PRINTS("Unknown exported member type: " + MEMBER_FULL_QUALIFIED_NAME(p_member));
return false;
} else if (variant_type == Variant::INT && type.type_encoding == MONO_TYPE_VALUETYPE && mono_class_is_enum(type.type_class->get_mono_ptr())) {
// TODO: Move to ExportInfo?
variant_type = Variant::INT;
hint = PROPERTY_HINT_ENUM;
}
Vector<MonoClassField *> fields = type.type_class->get_enum_fields();
int hint_res = _try_get_member_export_hint(p_member, type, variant_type, /* allow_generics: */ true, hint, hint_string);
MonoType *enum_basetype = mono_class_enum_basetype(type.type_class->get_mono_ptr());
if (hint_res == -1) {
ERR_EXPLAIN("Error while trying to determine information about the exported member: " + MEMBER_FULL_QUALIFIED_NAME(p_member));
ERR_FAIL_V(false);
}
if (hint_res == 0) {
hint = PropertyHint(CACHED_FIELD(ExportAttribute, hint)->get_int_value(attr));
hint_string = CACHED_FIELD(ExportAttribute, hintString)->get_string_value(attr);
}
r_prop_info = PropertyInfo(variant_type, (String)p_member->get_name(), hint, hint_string, PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SCRIPT_VARIABLE);
r_exported = true;
return true;
#undef MEMBER_FULL_QUALIFIED_NAME
}
int CSharpScript::_try_get_member_export_hint(IMonoClassMember *p_member, ManagedType p_type, Variant::Type p_variant_type, bool p_allow_generics, PropertyHint &r_hint, String &r_hint_string) {
if (p_variant_type == Variant::INT && p_type.type_encoding == MONO_TYPE_VALUETYPE && mono_class_is_enum(p_type.type_class->get_mono_ptr())) {
r_hint = PROPERTY_HINT_ENUM;
Vector<MonoClassField *> fields = p_type.type_class->get_enum_fields();
MonoType *enum_basetype = mono_class_enum_basetype(p_type.type_class->get_mono_ptr());
String name_only_hint_string;
@ -2236,12 +2263,12 @@ bool CSharpScript::_get_member_export(GDMonoClass *p_class, IMonoClassMember *p_
MonoClassField *field = fields[i];
if (i > 0) {
hint_string += ",";
r_hint_string += ",";
name_only_hint_string += ",";
}
String enum_field_name = mono_field_get_name(field);
hint_string += enum_field_name;
r_hint_string += enum_field_name;
name_only_hint_string += enum_field_name;
// TODO:
@ -2251,54 +2278,73 @@ bool CSharpScript::_get_member_export(GDMonoClass *p_class, IMonoClassMember *p_
MonoObject *val_obj = mono_field_get_value_object(mono_domain_get(), field, NULL);
if (val_obj == NULL) {
ERR_PRINTS("Failed to get '" + enum_field_name + "' constant enum value of exported member: " +
p_class->get_full_name() + "." + name.operator String());
return false;
ERR_EXPLAIN("Failed to get '" + enum_field_name + "' constant enum value");
ERR_FAIL_V(-1);
}
bool r_error;
uint64_t val = GDMonoUtils::unbox_enum_value(val_obj, enum_basetype, r_error);
if (r_error) {
ERR_PRINTS("Failed to unbox '" + enum_field_name + "' constant enum value of exported member: " +
p_class->get_full_name() + "." + name.operator String());
return false;
ERR_EXPLAIN("Failed to unbox '" + enum_field_name + "' constant enum value");
ERR_FAIL_V(-1);
}
if (val != (unsigned int)i) {
uses_default_values = false;
}
hint_string += ":";
hint_string += String::num_uint64(val);
r_hint_string += ":";
r_hint_string += String::num_uint64(val);
}
if (uses_default_values) {
// If we use the format NAME:VAL, that's what the editor displays.
// That's annoying if the user is not using custom values for the enum constants.
// This may not be needed in the future if the editor is changed to not display values.
hint_string = name_only_hint_string;
r_hint_string = name_only_hint_string;
}
} else if (variant_type == Variant::OBJECT && CACHED_CLASS(GodotReference)->is_assignable_from(type.type_class)) {
GDMonoClass *field_native_class = GDMonoUtils::get_class_native_base(type.type_class);
} else if (p_variant_type == Variant::OBJECT && CACHED_CLASS(GodotResource)->is_assignable_from(p_type.type_class)) {
GDMonoClass *field_native_class = GDMonoUtils::get_class_native_base(p_type.type_class);
CRASH_COND(field_native_class == NULL);
hint = PROPERTY_HINT_RESOURCE_TYPE;
hint_string = NATIVE_GDMONOCLASS_NAME(field_native_class);
} else if (variant_type == Variant::ARRAY && export_info.array.element_type != Variant::NIL) {
String elem_type_str = itos(export_info.array.element_type);
hint = PROPERTY_HINT_TYPE_STRING;
hint_string = elem_type_str + "/" + elem_type_str + ":" + export_info.array.element_native_name;
} else if (variant_type == Variant::DICTIONARY && export_info.dictionary.key_type != Variant::NIL && export_info.dictionary.value_type != Variant::NIL) {
// TODO: There is no hint for this yet
r_hint = PROPERTY_HINT_RESOURCE_TYPE;
r_hint_string = NATIVE_GDMONOCLASS_NAME(field_native_class);
} else if (p_allow_generics && p_variant_type == Variant::ARRAY) {
// Nested arrays are not supported in the inspector
ManagedType elem_type;
if (!GDMonoMarshal::try_get_array_element_type(p_type, elem_type))
return 0;
Variant::Type elem_variant_type = GDMonoMarshal::managed_to_variant_type(elem_type);
PropertyHint elem_hint = PROPERTY_HINT_NONE;
String elem_hint_string;
if (elem_variant_type == Variant::NIL) {
ERR_EXPLAIN("Unknown array element type");
ERR_FAIL_V(-1);
}
int hint_res = _try_get_member_export_hint(p_member, elem_type, elem_variant_type, /* allow_generics: */ false, elem_hint, elem_hint_string);
if (hint_res == -1) {
ERR_EXPLAIN("Error while trying to determine information about the array element type");
ERR_FAIL_V(-1);
}
// Format: type/hint:hint_string
r_hint_string = itos(elem_variant_type) + "/" + itos(elem_hint) + ":" + elem_hint_string;
r_hint = PROPERTY_HINT_TYPE_STRING;
} else if (p_allow_generics && p_variant_type == Variant::DICTIONARY) {
// TODO: Dictionaries are not supported in the inspector
} else {
hint = PropertyHint(CACHED_FIELD(ExportAttribute, hint)->get_int_value(attr));
hint_string = CACHED_FIELD(ExportAttribute, hintString)->get_string_value(attr);
return 0;
}
r_prop_info = PropertyInfo(variant_type, name.operator String(), hint, hint_string, PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SCRIPT_VARIABLE);
r_exported = true;
return true;
return 1;
}
#endif

View file

@ -127,7 +127,8 @@ class CSharpScript : public Script {
bool _update_exports();
#ifdef TOOLS_ENABLED
bool _get_member_export(GDMonoClass *p_class, IMonoClassMember *p_member, PropertyInfo &r_prop_info, bool &r_exported);
bool _get_member_export(IMonoClassMember *p_member, PropertyInfo &r_prop_info, bool &r_exported);
static int _try_get_member_export_hint(IMonoClassMember *p_member, ManagedType p_type, Variant::Type p_variant_type, bool p_allow_generics, PropertyHint &r_hint, String &r_hint_string);
#endif
CSharpInstance *_create_instance(const Variant **p_args, int p_argcount, Object *p_owner, bool p_isref, Variant::CallError &r_error);

View file

@ -1,5 +1,6 @@
using System;
using System.Collections;
using System.Collections.Generic;
namespace Godot
{
@ -8,29 +9,113 @@ namespace Godot
static class MarshalUtils
{
/// <summary>
/// Returns <see langword="true"/> if the generic type definition of <paramref name="type"/>
/// is <see cref="Godot.Collections.Array{T}"/>; otherwise returns <see langword="false"/>.
/// </summary>
/// <exception cref="System.InvalidOperationException">
/// <paramref name="type"/> is not a generic type. That is, IsGenericType returns false.
/// </exception>
static bool TypeIsGenericArray(Type type)
{
return type.GetGenericTypeDefinition() == typeof(Godot.Collections.Array<>);
}
/// <summary>
/// Returns <see langword="true"/> if the generic type definition of <paramref name="type"/>
/// is <see cref="Godot.Collections.Dictionary{T}"/>; otherwise returns <see langword="false"/>.
/// </summary>
/// <exception cref="System.InvalidOperationException">
/// <paramref name="type"/> is not a generic type. That is, IsGenericType returns false.
/// </exception>
static bool TypeIsGenericDictionary(Type type)
{
return type.GetGenericTypeDefinition() == typeof(Godot.Collections.Dictionary<,>);
}
static void ArrayGetElementType(Type type, out Type elementType)
static void ArrayGetElementType(Type arrayType, out Type elementType)
{
elementType = type.GetGenericArguments()[0];
elementType = arrayType.GetGenericArguments()[0];
}
static void DictionaryGetKeyValueTypes(Type type, out Type keyType, out Type valueType)
static void DictionaryGetKeyValueTypes(Type dictionaryType, out Type keyType, out Type valueType)
{
var genericArgs = type.GetGenericArguments();
var genericArgs = dictionaryType.GetGenericArguments();
keyType = genericArgs[0];
valueType = genericArgs[1];
}
static bool GenericIEnumerableIsAssignableFromType(Type type, out Type elementType)
{
if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(IEnumerable<>))
{
elementType = type.GetGenericArguments()[0];
return true;
}
foreach (var interfaceType in type.GetInterfaces())
{
if (interfaceType.IsGenericType && interfaceType.GetGenericTypeDefinition() == typeof(IEnumerable<>))
{
elementType = interfaceType.GetGenericArguments()[0];
return true;
}
}
Type baseType = type.BaseType;
if (baseType == null)
{
elementType = null;
return false;
}
return GenericIEnumerableIsAssignableFromType(baseType, out elementType);
}
static bool GenericIDictionaryIsAssignableFromType(Type type, out Type keyType, out Type valueType)
{
if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(IDictionary<,>))
{
var genericArgs = type.GetGenericArguments();
keyType = genericArgs[0];
valueType = genericArgs[1];
return true;
}
foreach (var interfaceType in type.GetInterfaces())
{
if (interfaceType.IsGenericType && interfaceType.GetGenericTypeDefinition() == typeof(IDictionary<,>))
{
var genericArgs = interfaceType.GetGenericArguments();
keyType = genericArgs[0];
valueType = genericArgs[1];
return true;
}
}
Type baseType = type.BaseType;
if (baseType == null)
{
keyType = null;
valueType = null;
return false;
}
return GenericIDictionaryIsAssignableFromType(baseType, out keyType, out valueType);
}
static Type MakeGenericArrayType(Type elemType)
{
return typeof(Godot.Collections.Array<>).MakeGenericType(elemType);
}
static Type MakeGenericDictionaryType(Type keyType, Type valueType)
{
return typeof(Godot.Collections.Dictionary<,>).MakeGenericType(keyType, valueType);
}
// TODO Add support for IEnumerable<T> and IDictionary<TKey, TValue>
// TODO: EnumerableToArray and IDictionaryToDictionary can be optimized
@ -64,5 +149,26 @@ namespace Godot
Dictionary.godot_icall_Dictionary_Add(godotDictionaryPtr, entry.Key, entry.Value);
}
}
internal static void GenericIDictionaryToDictionary(object dictionary, IntPtr godotDictionaryPtr)
{
#if DEBUG
if (!GenericIDictionaryIsAssignableFromType(dictionary.GetType()))
throw new InvalidOperationException("The type does not implement IDictionary<,>");
#endif
// TODO: Can we optimize this?
var keys = ((IEnumerable)dictionary.GetType().GetProperty("Keys").GetValue(dictionary)).GetEnumerator();
var values = ((IEnumerable)dictionary.GetType().GetProperty("Values").GetValue(dictionary)).GetEnumerator();
while (keys.MoveNext() && values.MoveNext())
{
object key = keys.Current;
object value = values.Current;
Dictionary.godot_icall_Dictionary_Add(godotDictionaryPtr, key, value);
}
}
}
}

View file

@ -313,12 +313,32 @@ void GDMonoField::set_value_from_variant(MonoObject *p_object, const Variant &p_
break;
}
// The order in which we check the following interfaces is very important (dictionaries and generics first)
MonoReflectionType *reftype = mono_type_get_object(SCRIPTS_DOMAIN, type_class->get_mono_type());
MonoReflectionType *key_reftype, *value_reftype;
if (GDMonoUtils::Marshal::generic_idictionary_is_assignable_from(reftype, &key_reftype, &value_reftype)) {
MonoObject *managed = GDMonoUtils::create_managed_from(p_value.operator Dictionary(),
GDMonoUtils::Marshal::make_generic_dictionary_type(key_reftype, value_reftype));
mono_field_set_value(p_object, mono_field, managed);
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;
}
MonoReflectionType *elem_reftype;
if (GDMonoUtils::Marshal::generic_ienumerable_is_assignable_from(reftype, &elem_reftype)) {
MonoObject *managed = GDMonoUtils::create_managed_from(p_value.operator Array(),
GDMonoUtils::Marshal::make_generic_array_type(elem_reftype));
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);
@ -432,36 +452,42 @@ void GDMonoField::set_value_from_variant(MonoObject *p_object, const Variant &p_
case MONO_TYPE_GENERICINST: {
MonoReflectionType *reftype = mono_type_get_object(SCRIPTS_DOMAIN, type.type_class->get_mono_type());
MonoException *exc = NULL;
GDMonoUtils::TypeIsGenericDictionary type_is_dict = CACHED_METHOD_THUNK(MarshalUtils, TypeIsGenericDictionary);
MonoBoolean is_dict = invoke_method_thunk(type_is_dict, reftype, &exc);
UNLIKELY_UNHANDLED_EXCEPTION(exc);
if (is_dict) {
if (GDMonoUtils::Marshal::type_is_generic_dictionary(reftype)) {
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::TypeIsGenericArray type_is_array = CACHED_METHOD_THUNK(MarshalUtils, TypeIsGenericArray);
MonoBoolean is_array = invoke_method_thunk(type_is_array, reftype, &exc);
UNLIKELY_UNHANDLED_EXCEPTION(exc);
if (is_array) {
if (GDMonoUtils::Marshal::type_is_generic_array(reftype)) {
MonoObject *managed = GDMonoUtils::create_managed_from(p_value.operator Array(), type.type_class);
mono_field_set_value(p_object, mono_field, managed);
break;
}
// The order in which we check the following interfaces is very important (dictionaries and generics first)
MonoReflectionType *key_reftype, *value_reftype;
if (GDMonoUtils::Marshal::generic_idictionary_is_assignable_from(reftype, &key_reftype, &value_reftype)) {
MonoObject *managed = GDMonoUtils::create_managed_from(p_value.operator Dictionary(),
GDMonoUtils::Marshal::make_generic_dictionary_type(key_reftype, value_reftype));
mono_field_set_value(p_object, mono_field, managed);
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;
}
MonoReflectionType *elem_reftype;
if (GDMonoUtils::Marshal::generic_ienumerable_is_assignable_from(reftype, &elem_reftype)) {
MonoObject *managed = GDMonoUtils::create_managed_from(p_value.operator Array(),
GDMonoUtils::Marshal::make_generic_array_type(elem_reftype));
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);

View file

@ -47,9 +47,11 @@ class GDMonoField : public IMonoClassMember {
MonoCustomAttrInfo *attributes;
public:
virtual MemberType get_member_type() GD_FINAL { return MEMBER_TYPE_FIELD; }
virtual GDMonoClass *get_enclosing_class() const GD_FINAL { return owner; }
virtual StringName get_name() GD_FINAL { return name; }
virtual MemberType get_member_type() const GD_FINAL { return MEMBER_TYPE_FIELD; }
virtual StringName get_name() const GD_FINAL { return name; }
virtual bool is_static() GD_FINAL;
virtual Visibility get_visibility() GD_FINAL;

View file

@ -35,7 +35,7 @@
namespace GDMonoMarshal {
Variant::Type managed_to_variant_type(const ManagedType &p_type, ExportInfo *r_export_info) {
Variant::Type managed_to_variant_type(const ManagedType &p_type) {
switch (p_type.type_encoding) {
case MONO_TYPE_BOOLEAN:
return Variant::BOOL;
@ -157,10 +157,24 @@ Variant::Type managed_to_variant_type(const ManagedType &p_type, ExportInfo *r_e
return Variant::ARRAY;
}
// The order in which we check the following interfaces is very important (dictionaries and generics first)
MonoReflectionType *reftype = mono_type_get_object(SCRIPTS_DOMAIN, type_class->get_mono_type());
MonoReflectionType *key_reftype, *value_reftype;
if (GDMonoUtils::Marshal::generic_idictionary_is_assignable_from(reftype, &key_reftype, &value_reftype)) {
return Variant::DICTIONARY;
}
if (type_class->implements_interface(CACHED_CLASS(System_Collections_IDictionary))) {
return Variant::DICTIONARY;
}
MonoReflectionType *elem_reftype;
if (GDMonoUtils::Marshal::generic_ienumerable_is_assignable_from(reftype, &elem_reftype)) {
return Variant::ARRAY;
}
if (type_class->implements_interface(CACHED_CLASS(System_Collections_IEnumerable))) {
return Variant::ARRAY;
}
@ -169,60 +183,28 @@ Variant::Type managed_to_variant_type(const ManagedType &p_type, ExportInfo *r_e
case MONO_TYPE_GENERICINST: {
MonoReflectionType *reftype = mono_type_get_object(SCRIPTS_DOMAIN, p_type.type_class->get_mono_type());
MonoException *exc = NULL;
GDMonoUtils::TypeIsGenericDictionary type_is_dict = CACHED_METHOD_THUNK(MarshalUtils, TypeIsGenericDictionary);
MonoBoolean is_dict = invoke_method_thunk(type_is_dict, reftype, &exc);
UNLIKELY_UNHANDLED_EXCEPTION(exc);
if (is_dict) {
if (r_export_info) {
MonoReflectionType *key_reftype;
MonoReflectionType *value_reftype;
exc = NULL;
invoke_method_thunk(CACHED_METHOD_THUNK(MarshalUtils, DictionaryGetKeyValueTypes),
reftype, &key_reftype, &value_reftype, &exc);
UNLIKELY_UNHANDLED_EXCEPTION(exc);
ManagedType key_type = ManagedType::from_reftype(key_reftype);
ManagedType value_type = ManagedType::from_reftype(value_reftype);
r_export_info->dictionary.key_type = managed_to_variant_type(key_type);
r_export_info->dictionary.key_native_name = NATIVE_GDMONOCLASS_NAME(key_type.type_class);
r_export_info->dictionary.value_type = managed_to_variant_type(value_type);
r_export_info->dictionary.value_native_name = NATIVE_GDMONOCLASS_NAME(value_type.type_class);
}
if (GDMonoUtils::Marshal::type_is_generic_dictionary(reftype)) {
return Variant::DICTIONARY;
}
exc = NULL;
GDMonoUtils::TypeIsGenericArray type_is_array = CACHED_METHOD_THUNK(MarshalUtils, TypeIsGenericArray);
MonoBoolean is_array = invoke_method_thunk(type_is_array, reftype, &exc);
UNLIKELY_UNHANDLED_EXCEPTION(exc);
if (is_array) {
if (r_export_info) {
MonoReflectionType *elem_reftype;
exc = NULL;
invoke_method_thunk(CACHED_METHOD_THUNK(MarshalUtils, ArrayGetElementType),
reftype, &elem_reftype, &exc);
UNLIKELY_UNHANDLED_EXCEPTION(exc);
ManagedType elem_type = ManagedType::from_reftype(elem_reftype);
r_export_info->array.element_type = managed_to_variant_type(elem_type);
r_export_info->array.element_native_name = NATIVE_GDMONOCLASS_NAME(elem_type.type_class);
}
if (GDMonoUtils::Marshal::type_is_generic_array(reftype)) {
return Variant::ARRAY;
}
// The order in which we check the following interfaces is very important (dictionaries and generics first)
MonoReflectionType *key_reftype, *value_reftype;
if (GDMonoUtils::Marshal::generic_idictionary_is_assignable_from(reftype, &key_reftype, &value_reftype))
return Variant::DICTIONARY;
if (p_type.type_class->implements_interface(CACHED_CLASS(System_Collections_IDictionary))) {
return Variant::DICTIONARY;
}
MonoReflectionType *elem_reftype;
if (GDMonoUtils::Marshal::generic_ienumerable_is_assignable_from(reftype, &elem_reftype))
return Variant::ARRAY;
if (p_type.type_class->implements_interface(CACHED_CLASS(System_Collections_IEnumerable))) {
return Variant::ARRAY;
}
@ -236,6 +218,63 @@ Variant::Type managed_to_variant_type(const ManagedType &p_type, ExportInfo *r_e
return Variant::NIL;
}
bool try_get_array_element_type(const ManagedType &p_array_type, ManagedType &r_elem_type) {
switch (p_array_type.type_encoding) {
case MONO_TYPE_GENERICINST: {
MonoReflectionType *array_reftype = mono_type_get_object(SCRIPTS_DOMAIN, p_array_type.type_class->get_mono_type());
if (GDMonoUtils::Marshal::type_is_generic_array(array_reftype)) {
MonoReflectionType *elem_reftype;
GDMonoUtils::Marshal::array_get_element_type(array_reftype, &elem_reftype);
r_elem_type = ManagedType::from_reftype(elem_reftype);
return true;
}
MonoReflectionType *elem_reftype;
if (GDMonoUtils::Marshal::generic_ienumerable_is_assignable_from(array_reftype, &elem_reftype)) {
r_elem_type = ManagedType::from_reftype(elem_reftype);
return true;
}
} break;
default: {
} break;
}
return false;
}
bool try_get_dictionary_key_value_types(const ManagedType &p_dictionary_type, ManagedType &r_key_type, ManagedType &r_value_type) {
switch (p_dictionary_type.type_encoding) {
case MONO_TYPE_GENERICINST: {
MonoReflectionType *dict_reftype = mono_type_get_object(SCRIPTS_DOMAIN, p_dictionary_type.type_class->get_mono_type());
if (GDMonoUtils::Marshal::type_is_generic_dictionary(dict_reftype)) {
MonoReflectionType *key_reftype;
MonoReflectionType *value_reftype;
GDMonoUtils::Marshal::dictionary_get_key_value_types(dict_reftype, &key_reftype, &value_reftype);
r_key_type = ManagedType::from_reftype(key_reftype);
r_value_type = ManagedType::from_reftype(value_reftype);
return true;
}
MonoReflectionType *key_reftype, *value_reftype;
if (GDMonoUtils::Marshal::generic_idictionary_is_assignable_from(dict_reftype, &key_reftype, &value_reftype)) {
r_key_type = ManagedType::from_reftype(key_reftype);
r_value_type = ManagedType::from_reftype(value_reftype);
return true;
}
} break;
default: {
} break;
}
return false;
}
String mono_to_utf8_string(MonoString *p_mono_string) {
MonoError error;
char *utf8 = mono_string_to_utf8_checked(p_mono_string, &error);
@ -502,10 +541,26 @@ MonoObject *variant_to_mono_object(const Variant *p_var, const ManagedType &p_ty
return GDMonoUtils::create_managed_from(p_var->operator Array(), CACHED_CLASS(Array));
}
// The order in which we check the following interfaces is very important (dictionaries and generics first)
MonoReflectionType *reftype = mono_type_get_object(SCRIPTS_DOMAIN, type_class->get_mono_type());
MonoReflectionType *key_reftype, *value_reftype;
if (GDMonoUtils::Marshal::generic_idictionary_is_assignable_from(reftype, &key_reftype, &value_reftype)) {
return GDMonoUtils::create_managed_from(p_var->operator Dictionary(),
GDMonoUtils::Marshal::make_generic_dictionary_type(key_reftype, value_reftype));
}
if (type_class->implements_interface(CACHED_CLASS(System_Collections_IDictionary))) {
return GDMonoUtils::create_managed_from(p_var->operator Dictionary(), CACHED_CLASS(Dictionary));
}
MonoReflectionType *elem_reftype;
if (GDMonoUtils::Marshal::generic_ienumerable_is_assignable_from(reftype, &elem_reftype)) {
return GDMonoUtils::create_managed_from(p_var->operator Array(),
GDMonoUtils::Marshal::make_generic_array_type(elem_reftype));
}
if (type_class->implements_interface(CACHED_CLASS(System_Collections_IEnumerable))) {
return GDMonoUtils::create_managed_from(p_var->operator Array(), CACHED_CLASS(Array));
}
@ -603,28 +658,32 @@ MonoObject *variant_to_mono_object(const Variant *p_var, const ManagedType &p_ty
case MONO_TYPE_GENERICINST: {
MonoReflectionType *reftype = mono_type_get_object(SCRIPTS_DOMAIN, p_type.type_class->get_mono_type());
MonoException *exc = NULL;
GDMonoUtils::TypeIsGenericDictionary type_is_dict = CACHED_METHOD_THUNK(MarshalUtils, TypeIsGenericDictionary);
MonoBoolean is_dict = invoke_method_thunk(type_is_dict, reftype, &exc);
UNLIKELY_UNHANDLED_EXCEPTION(exc);
if (is_dict) {
if (GDMonoUtils::Marshal::type_is_generic_dictionary(reftype)) {
return GDMonoUtils::create_managed_from(p_var->operator Dictionary(), p_type.type_class);
}
exc = NULL;
GDMonoUtils::TypeIsGenericArray type_is_array = CACHED_METHOD_THUNK(MarshalUtils, TypeIsGenericArray);
MonoBoolean is_array = invoke_method_thunk(type_is_array, reftype, &exc);
UNLIKELY_UNHANDLED_EXCEPTION(exc);
if (is_array) {
if (GDMonoUtils::Marshal::type_is_generic_array(reftype)) {
return GDMonoUtils::create_managed_from(p_var->operator Array(), p_type.type_class);
}
// The order in which we check the following interfaces is very important (dictionaries and generics first)
MonoReflectionType *key_reftype, *value_reftype;
if (GDMonoUtils::Marshal::generic_idictionary_is_assignable_from(reftype, &key_reftype, &value_reftype)) {
return GDMonoUtils::create_managed_from(p_var->operator Dictionary(),
GDMonoUtils::Marshal::make_generic_dictionary_type(key_reftype, value_reftype));
}
if (p_type.type_class->implements_interface(CACHED_CLASS(System_Collections_IDictionary))) {
return GDMonoUtils::create_managed_from(p_var->operator Dictionary(), CACHED_CLASS(Dictionary));
}
MonoReflectionType *elem_reftype;
if (GDMonoUtils::Marshal::generic_ienumerable_is_assignable_from(reftype, &elem_reftype)) {
return GDMonoUtils::create_managed_from(p_var->operator Array(),
GDMonoUtils::Marshal::make_generic_array_type(elem_reftype));
}
if (p_type.type_class->implements_interface(CACHED_CLASS(System_Collections_IEnumerable))) {
return GDMonoUtils::create_managed_from(p_var->operator Array(), CACHED_CLASS(Array));
}
@ -787,66 +846,64 @@ Variant mono_object_to_variant(MonoObject *p_obj) {
return ptr ? Variant(*ptr) : Variant();
}
// The order in which we check the following interfaces is very important (dictionaries and generics first)
MonoReflectionType *reftype = mono_type_get_object(SCRIPTS_DOMAIN, type_class->get_mono_type());
MonoReflectionType *key_reftype, *value_reftype;
if (GDMonoUtils::Marshal::generic_idictionary_is_assignable_from(reftype, &key_reftype, &value_reftype)) {
return GDMonoUtils::Marshal::generic_idictionary_to_dictionary(p_obj);
}
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;
return GDMonoUtils::Marshal::idictionary_to_dictionary(p_obj);
}
MonoReflectionType *elem_reftype;
if (GDMonoUtils::Marshal::generic_ienumerable_is_assignable_from(reftype, &elem_reftype)) {
return GDMonoUtils::Marshal::enumerable_to_array(p_obj);
}
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;
return GDMonoUtils::Marshal::enumerable_to_array(p_obj);
}
} break;
case MONO_TYPE_GENERICINST: {
MonoReflectionType *reftype = mono_type_get_object(SCRIPTS_DOMAIN, type.type_class->get_mono_type());
MonoException *exc = NULL;
GDMonoUtils::TypeIsGenericDictionary type_is_dict = CACHED_METHOD_THUNK(MarshalUtils, TypeIsGenericDictionary);
MonoBoolean is_dict = invoke_method_thunk(type_is_dict, reftype, &exc);
UNLIKELY_UNHANDLED_EXCEPTION(exc);
if (is_dict) {
exc = NULL;
if (GDMonoUtils::Marshal::type_is_generic_dictionary(reftype)) {
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::TypeIsGenericArray type_is_array = CACHED_METHOD_THUNK(MarshalUtils, TypeIsGenericArray);
MonoBoolean is_array = invoke_method_thunk(type_is_array, reftype, &exc);
UNLIKELY_UNHANDLED_EXCEPTION(exc);
if (is_array) {
exc = NULL;
if (GDMonoUtils::Marshal::type_is_generic_array(reftype)) {
MonoException *exc = NULL;
MonoObject *ret = type.type_class->get_method("GetPtr")->invoke(p_obj, &exc);
UNLIKELY_UNHANDLED_EXCEPTION(exc);
return *unbox<Array *>(ret);
}
// The order in which we check the following interfaces is very important (dictionaries and generics first)
MonoReflectionType *key_reftype, *value_reftype;
if (GDMonoUtils::Marshal::generic_idictionary_is_assignable_from(reftype, &key_reftype, &value_reftype)) {
return GDMonoUtils::Marshal::generic_idictionary_to_dictionary(p_obj);
}
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;
return GDMonoUtils::Marshal::idictionary_to_dictionary(p_obj);
}
MonoReflectionType *elem_reftype;
if (GDMonoUtils::Marshal::generic_ienumerable_is_assignable_from(reftype, &elem_reftype)) {
return GDMonoUtils::Marshal::enumerable_to_array(p_obj);
}
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;
return GDMonoUtils::Marshal::enumerable_to_array(p_obj);
}
} break;
}
@ -1075,4 +1132,5 @@ PoolVector3Array mono_array_to_PoolVector3Array(MonoArray *p_array) {
return ret;
}
} // namespace GDMonoMarshal

View file

@ -57,28 +57,10 @@ T unbox(MonoObject *p_obj) {
#define BOX_PTR(x) mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(IntPtr), x)
#define BOX_ENUM(m_enum_class, x) mono_value_box(mono_domain_get(), m_enum_class, &x)
// FIXME: Made this struct in a hurry. It could be done differently.
struct ExportInfo {
struct ArrayInfo {
Variant::Type element_type;
String element_native_name;
Variant::Type managed_to_variant_type(const ManagedType &p_type);
ArrayInfo() :
element_type(Variant::NIL) {}
} array;
struct DictionaryInfo {
Variant::Type key_type;
String key_native_name;
Variant::Type value_type;
String value_native_name;
DictionaryInfo() :
key_type(Variant::NIL),
value_type(Variant::NIL) {}
} dictionary;
};
Variant::Type managed_to_variant_type(const ManagedType &p_type, ExportInfo *r_export_info = NULL);
bool try_get_array_element_type(const ManagedType &p_array_type, ManagedType &r_elem_type);
bool try_get_dictionary_key_value_types(const ManagedType &p_dictionary_type, ManagedType &r_key_type, ManagedType &r_value_type);
// String

View file

@ -74,6 +74,10 @@ void GDMonoMethod::_update_signature(MonoMethodSignature *p_method_sig) {
method_info = MethodInfo();
}
GDMonoClass *GDMonoMethod::get_enclosing_class() const {
return GDMono::get_singleton()->get_class(mono_method_get_class(mono_method));
}
bool GDMonoMethod::is_static() {
return mono_method_get_flags(mono_method, NULL) & MONO_METHOD_ATTR_STATIC;
}

View file

@ -57,9 +57,11 @@ class GDMonoMethod : public IMonoClassMember {
MonoMethod *mono_method;
public:
virtual MemberType get_member_type() GD_FINAL { return MEMBER_TYPE_METHOD; }
virtual GDMonoClass *get_enclosing_class() const GD_FINAL;
virtual StringName get_name() GD_FINAL { return name; }
virtual MemberType get_member_type() const GD_FINAL { return MEMBER_TYPE_METHOD; }
virtual StringName get_name() const GD_FINAL { return name; }
virtual bool is_static() GD_FINAL;

View file

@ -47,9 +47,11 @@ class GDMonoProperty : public IMonoClassMember {
MonoCustomAttrInfo *attributes;
public:
virtual MemberType get_member_type() GD_FINAL { return MEMBER_TYPE_PROPERTY; }
virtual GDMonoClass *get_enclosing_class() const GD_FINAL { return owner; }
virtual StringName get_name() GD_FINAL { return name; }
virtual MemberType get_member_type() const GD_FINAL { return MEMBER_TYPE_PROPERTY; }
virtual StringName get_name() const GD_FINAL { return name; }
virtual bool is_static() GD_FINAL;
virtual Visibility get_visibility() GD_FINAL;

View file

@ -109,7 +109,7 @@ void MonoCache::clear_members() {
class_NodePath = NULL;
class_RID = NULL;
class_GodotObject = NULL;
class_GodotReference = NULL;
class_GodotResource = NULL;
class_Node = NULL;
class_Control = NULL;
class_Spatial = NULL;
@ -151,12 +151,25 @@ void MonoCache::clear_members() {
methodthunk_SignalAwaiter_FailureCallback = NULL;
methodthunk_GodotTaskScheduler_Activate = NULL;
// Start of MarshalUtils methods
methodthunk_MarshalUtils_TypeIsGenericArray = NULL;
methodthunk_MarshalUtils_TypeIsGenericDictionary = NULL;
methodthunk_MarshalUtils_ArrayGetElementType = NULL;
methodthunk_MarshalUtils_DictionaryGetKeyValueTypes = NULL;
methodthunk_MarshalUtils_GenericIEnumerableIsAssignableFromType = NULL;
methodthunk_MarshalUtils_GenericIDictionaryIsAssignableFromType = NULL;
methodthunk_MarshalUtils_MakeGenericArrayType = NULL;
methodthunk_MarshalUtils_MakeGenericDictionaryType = NULL;
methodthunk_MarshalUtils_EnumerableToArray = NULL;
methodthunk_MarshalUtils_IDictionaryToDictionary = NULL;
methodthunk_MarshalUtils_GenericIDictionaryToDictionary = NULL;
// End of MarshalUtils methods
task_scheduler_handle = Ref<MonoGCHandle>();
}
@ -217,7 +230,7 @@ void update_godot_api_cache() {
CACHE_CLASS_AND_CHECK(NodePath, GODOT_API_CLASS(NodePath));
CACHE_CLASS_AND_CHECK(RID, GODOT_API_CLASS(RID));
CACHE_CLASS_AND_CHECK(GodotObject, GODOT_API_CLASS(Object));
CACHE_CLASS_AND_CHECK(GodotReference, GODOT_API_CLASS(Reference));
CACHE_CLASS_AND_CHECK(GodotResource, GODOT_API_CLASS(Resource));
CACHE_CLASS_AND_CHECK(Node, GODOT_API_CLASS(Node));
CACHE_CLASS_AND_CHECK(Control, GODOT_API_CLASS(Control));
CACHE_CLASS_AND_CHECK(Spatial, GODOT_API_CLASS(Spatial));
@ -258,12 +271,28 @@ void update_godot_api_cache() {
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));
// Start of MarshalUtils methods
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, ArrayGetElementType, (ArrayGetElementType)GODOT_API_CLASS(MarshalUtils)->get_method_thunk("ArrayGetElementType", 2));
CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, DictionaryGetKeyValueTypes, (DictionaryGetKeyValueTypes)GODOT_API_CLASS(MarshalUtils)->get_method_thunk("DictionaryGetKeyValueTypes", 3));
CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, ArrayGetElementType, (ArrayGetElementType)GODOT_API_CLASS(MarshalUtils)->get_method_thunk("ArrayGetElementType", 2));
CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, DictionaryGetKeyValueTypes, (DictionaryGetKeyValueTypes)GODOT_API_CLASS(MarshalUtils)->get_method_thunk("DictionaryGetKeyValueTypes", 3));
CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, GenericIEnumerableIsAssignableFromType, (GenericIEnumerableIsAssignableFromType)GODOT_API_CLASS(MarshalUtils)->get_method_thunk("GenericIEnumerableIsAssignableFromType", 2));
CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, GenericIDictionaryIsAssignableFromType, (GenericIDictionaryIsAssignableFromType)GODOT_API_CLASS(MarshalUtils)->get_method_thunk("GenericIDictionaryIsAssignableFromType", 3));
CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, MakeGenericArrayType, (MakeGenericArrayType)GODOT_API_CLASS(MarshalUtils)->get_method_thunk("MakeGenericArrayType", 1));
CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, MakeGenericDictionaryType, (MakeGenericDictionaryType)GODOT_API_CLASS(MarshalUtils)->get_method_thunk("MakeGenericDictionaryType", 2));
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));
CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, GenericIDictionaryToDictionary, (GenericIDictionaryToDictionary)GODOT_API_CLASS(MarshalUtils)->get_method_thunk("GenericIDictionaryToDictionary", 2));
// End of MarshalUtils methods
#ifdef DEBUG_ENABLED
CACHE_METHOD_THUNK_AND_CHECK(DebuggingUtils, GetStackFrameInfo, (DebugUtils_StackFrameInfo)GODOT_API_CLASS(DebuggingUtils)->get_method_thunk("GetStackFrameInfo", 4));
@ -727,4 +756,99 @@ void dispose(MonoObject *p_mono_object, MonoException **r_exc) {
invoke_method_thunk(CACHED_METHOD_THUNK(GodotObject, Dispose), p_mono_object, r_exc);
}
namespace Marshal {
MonoBoolean type_is_generic_array(MonoReflectionType *p_reftype) {
TypeIsGenericArray thunk = CACHED_METHOD_THUNK(MarshalUtils, TypeIsGenericArray);
MonoException *exc = NULL;
MonoBoolean res = invoke_method_thunk(thunk, p_reftype, &exc);
UNLIKELY_UNHANDLED_EXCEPTION(exc);
return res;
}
MonoBoolean type_is_generic_dictionary(MonoReflectionType *p_reftype) {
TypeIsGenericDictionary thunk = CACHED_METHOD_THUNK(MarshalUtils, TypeIsGenericDictionary);
MonoException *exc = NULL;
MonoBoolean res = invoke_method_thunk(thunk, p_reftype, &exc);
UNLIKELY_UNHANDLED_EXCEPTION(exc);
return res;
}
void array_get_element_type(MonoReflectionType *p_array_reftype, MonoReflectionType **r_elem_reftype) {
ArrayGetElementType thunk = CACHED_METHOD_THUNK(MarshalUtils, ArrayGetElementType);
MonoException *exc = NULL;
invoke_method_thunk(thunk, p_array_reftype, r_elem_reftype, &exc);
UNLIKELY_UNHANDLED_EXCEPTION(exc);
}
void dictionary_get_key_value_types(MonoReflectionType *p_dict_reftype, MonoReflectionType **r_key_reftype, MonoReflectionType **r_value_reftype) {
DictionaryGetKeyValueTypes thunk = CACHED_METHOD_THUNK(MarshalUtils, DictionaryGetKeyValueTypes);
MonoException *exc = NULL;
invoke_method_thunk(thunk, p_dict_reftype, r_key_reftype, r_value_reftype, &exc);
UNLIKELY_UNHANDLED_EXCEPTION(exc);
}
MonoBoolean generic_ienumerable_is_assignable_from(MonoReflectionType *p_reftype, MonoReflectionType **r_elem_reftype) {
GenericIEnumerableIsAssignableFromType thunk = CACHED_METHOD_THUNK(MarshalUtils, GenericIEnumerableIsAssignableFromType);
MonoException *exc = NULL;
MonoBoolean res = invoke_method_thunk(thunk, p_reftype, r_elem_reftype, &exc);
UNLIKELY_UNHANDLED_EXCEPTION(exc);
return res;
}
MonoBoolean generic_idictionary_is_assignable_from(MonoReflectionType *p_reftype, MonoReflectionType **r_key_reftype, MonoReflectionType **r_value_reftype) {
GenericIDictionaryIsAssignableFromType thunk = CACHED_METHOD_THUNK(MarshalUtils, GenericIDictionaryIsAssignableFromType);
MonoException *exc = NULL;
MonoBoolean res = invoke_method_thunk(thunk, p_reftype, r_key_reftype, r_value_reftype, &exc);
UNLIKELY_UNHANDLED_EXCEPTION(exc);
return res;
}
Array enumerable_to_array(MonoObject *p_enumerable) {
Array result;
EnumerableToArray thunk = CACHED_METHOD_THUNK(MarshalUtils, EnumerableToArray);
MonoException *exc = NULL;
invoke_method_thunk(thunk, p_enumerable, &result, &exc);
UNLIKELY_UNHANDLED_EXCEPTION(exc);
return result;
}
Dictionary idictionary_to_dictionary(MonoObject *p_idictionary) {
Dictionary result;
IDictionaryToDictionary thunk = CACHED_METHOD_THUNK(MarshalUtils, IDictionaryToDictionary);
MonoException *exc = NULL;
invoke_method_thunk(thunk, p_idictionary, &result, &exc);
UNLIKELY_UNHANDLED_EXCEPTION(exc);
return result;
}
Dictionary generic_idictionary_to_dictionary(MonoObject *p_generic_idictionary) {
Dictionary result;
GenericIDictionaryToDictionary thunk = CACHED_METHOD_THUNK(MarshalUtils, GenericIDictionaryToDictionary);
MonoException *exc = NULL;
invoke_method_thunk(thunk, p_generic_idictionary, &result, &exc);
UNLIKELY_UNHANDLED_EXCEPTION(exc);
return result;
}
GDMonoClass *make_generic_array_type(MonoReflectionType *p_elem_reftype) {
MakeGenericArrayType thunk = CACHED_METHOD_THUNK(MarshalUtils, MakeGenericArrayType);
MonoException *exc = NULL;
MonoReflectionType *reftype = invoke_method_thunk(thunk, p_elem_reftype, &exc);
UNLIKELY_UNHANDLED_EXCEPTION(exc);
return GDMono::get_singleton()->get_class(mono_class_from_mono_type(mono_reflection_type_get_type(reftype)));
}
GDMonoClass *make_generic_dictionary_type(MonoReflectionType *p_key_reftype, MonoReflectionType *p_value_reftype) {
MakeGenericDictionaryType thunk = CACHED_METHOD_THUNK(MarshalUtils, MakeGenericDictionaryType);
MonoException *exc = NULL;
MonoReflectionType *reftype = invoke_method_thunk(thunk, p_key_reftype, p_value_reftype, &exc);
UNLIKELY_UNHANDLED_EXCEPTION(exc);
return GDMono::get_singleton()->get_class(mono_class_from_mono_type(mono_reflection_type_get_type(reftype)));
}
} // namespace Marshal
// namespace Marshal
} // namespace GDMonoUtils

View file

@ -60,10 +60,41 @@ typedef void (*DebugUtils_StackFrameInfo)(MonoObject *, MonoString **, int *, Mo
typedef MonoBoolean (*TypeIsGenericArray)(MonoReflectionType *, MonoException **);
typedef MonoBoolean (*TypeIsGenericDictionary)(MonoReflectionType *, MonoException **);
typedef MonoBoolean (*ArrayGetElementType)(MonoReflectionType *, MonoReflectionType **, MonoException **);
typedef MonoBoolean (*DictionaryGetKeyValueTypes)(MonoReflectionType *, MonoReflectionType **, MonoReflectionType **, MonoException **);
typedef void (*ArrayGetElementType)(MonoReflectionType *, MonoReflectionType **, MonoException **);
typedef void (*DictionaryGetKeyValueTypes)(MonoReflectionType *, MonoReflectionType **, MonoReflectionType **, MonoException **);
typedef MonoBoolean (*GenericIEnumerableIsAssignableFromType)(MonoReflectionType *, MonoReflectionType **, MonoException **);
typedef MonoBoolean (*GenericIDictionaryIsAssignableFromType)(MonoReflectionType *, MonoReflectionType **, MonoReflectionType **, MonoException **);
typedef MonoReflectionType *(*MakeGenericArrayType)(MonoReflectionType *, MonoException **);
typedef MonoReflectionType *(*MakeGenericDictionaryType)(MonoReflectionType *, MonoReflectionType *, MonoException **);
typedef void (*EnumerableToArray)(MonoObject *, Array *, MonoException **);
typedef void (*IDictionaryToDictionary)(MonoObject *, Dictionary *, MonoException **);
typedef void (*GenericIDictionaryToDictionary)(MonoObject *, Dictionary *, MonoException **);
namespace Marshal {
MonoBoolean type_is_generic_array(MonoReflectionType *p_reftype);
MonoBoolean type_is_generic_dictionary(MonoReflectionType *p_reftype);
void array_get_element_type(MonoReflectionType *p_array_reftype, MonoReflectionType **r_elem_reftype);
void dictionary_get_key_value_types(MonoReflectionType *p_dict_reftype, MonoReflectionType **r_key_reftype, MonoReflectionType **r_value_reftype);
MonoBoolean generic_ienumerable_is_assignable_from(MonoReflectionType *p_reftype, MonoReflectionType **r_elem_reftype);
MonoBoolean generic_idictionary_is_assignable_from(MonoReflectionType *p_reftype, MonoReflectionType **r_key_reftype, MonoReflectionType **r_value_reftype);
GDMonoClass *make_generic_array_type(MonoReflectionType *p_elem_reftype);
GDMonoClass *make_generic_dictionary_type(MonoReflectionType *p_key_reftype, MonoReflectionType *p_value_reftype);
Array enumerable_to_array(MonoObject *p_enumerable);
Dictionary idictionary_to_dictionary(MonoObject *p_idictionary);
Dictionary generic_idictionary_to_dictionary(MonoObject *p_generic_idictionary);
} // namespace Marshal
// End of MarshalUtils methods
struct MonoCache {
@ -114,7 +145,7 @@ struct MonoCache {
GDMonoClass *class_NodePath;
GDMonoClass *class_RID;
GDMonoClass *class_GodotObject;
GDMonoClass *class_GodotReference;
GDMonoClass *class_GodotResource;
GDMonoClass *class_Node;
GDMonoClass *class_Control;
GDMonoClass *class_Spatial;
@ -156,12 +187,25 @@ struct MonoCache {
SignalAwaiter_FailureCallback methodthunk_SignalAwaiter_FailureCallback;
GodotTaskScheduler_Activate methodthunk_GodotTaskScheduler_Activate;
// Start of MarshalUtils methods
TypeIsGenericArray methodthunk_MarshalUtils_TypeIsGenericArray;
TypeIsGenericDictionary methodthunk_MarshalUtils_TypeIsGenericDictionary;
ArrayGetElementType methodthunk_MarshalUtils_ArrayGetElementType;
DictionaryGetKeyValueTypes methodthunk_MarshalUtils_DictionaryGetKeyValueTypes;
GenericIEnumerableIsAssignableFromType methodthunk_MarshalUtils_GenericIEnumerableIsAssignableFromType;
GenericIDictionaryIsAssignableFromType methodthunk_MarshalUtils_GenericIDictionaryIsAssignableFromType;
MakeGenericArrayType methodthunk_MarshalUtils_MakeGenericArrayType;
MakeGenericDictionaryType methodthunk_MarshalUtils_MakeGenericDictionaryType;
EnumerableToArray methodthunk_MarshalUtils_EnumerableToArray;
IDictionaryToDictionary methodthunk_MarshalUtils_IDictionaryToDictionary;
GenericIDictionaryToDictionary methodthunk_MarshalUtils_GenericIDictionaryToDictionary;
// End of MarshalUtils methods
Ref<MonoGCHandle> task_scheduler_handle;

View file

@ -53,9 +53,11 @@ public:
virtual ~IMonoClassMember() {}
virtual MemberType get_member_type() = 0;
virtual GDMonoClass *get_enclosing_class() const = 0;
virtual StringName get_name() = 0;
virtual MemberType get_member_type() const = 0;
virtual StringName get_name() const = 0;
virtual bool is_static() = 0;