C#: Marshalling support for IEnumerable<> and IDictionary<,>
Also fixed the hint string of exported members.
This commit is contained in:
parent
7112a45d99
commit
5a4bf4f369
13 changed files with 591 additions and 192 deletions
|
@ -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
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
Loading…
Reference in a new issue