From 119a910bc6b7472f014773997bc036a461b44a38 Mon Sep 17 00:00:00 2001 From: Ignacio Etcheverry Date: Thu, 4 Jan 2018 21:05:46 +0100 Subject: [PATCH] Mono: Add properties support in scripts --- modules/mono/csharp_script.cpp | 253 ++++++++++++------- modules/mono/csharp_script.h | 4 + modules/mono/editor/godotsharp_builds.cpp | 2 +- modules/mono/editor/monodevelop_instance.cpp | 2 +- modules/mono/mono_gd/gd_mono.cpp | 3 +- modules/mono/mono_gd/gd_mono.h | 8 - modules/mono/mono_gd/gd_mono_assembly.cpp | 2 +- modules/mono/mono_gd/gd_mono_class.cpp | 67 ++++- modules/mono/mono_gd/gd_mono_class.h | 10 +- modules/mono/mono_gd/gd_mono_class_member.h | 67 +++++ modules/mono/mono_gd/gd_mono_field.cpp | 34 +-- modules/mono/mono_gd/gd_mono_field.h | 35 +-- modules/mono/mono_gd/gd_mono_header.h | 4 +- modules/mono/mono_gd/gd_mono_marshal.cpp | 14 +- modules/mono/mono_gd/gd_mono_method.cpp | 45 +++- modules/mono/mono_gd/gd_mono_method.h | 21 +- modules/mono/mono_gd/gd_mono_property.cpp | 199 +++++++++++++++ modules/mono/mono_gd/gd_mono_property.h | 77 ++++++ modules/mono/mono_gd/gd_mono_utils.cpp | 10 +- modules/mono/mono_gd/gd_mono_utils.h | 4 +- 20 files changed, 691 insertions(+), 170 deletions(-) create mode 100644 modules/mono/mono_gd/gd_mono_class_member.h create mode 100644 modules/mono/mono_gd/gd_mono_property.cpp create mode 100644 modules/mono/mono_gd/gd_mono_property.h diff --git a/modules/mono/csharp_script.cpp b/modules/mono/csharp_script.cpp index f95a3dab2d3..a621c5529d4 100644 --- a/modules/mono/csharp_script.cpp +++ b/modules/mono/csharp_script.cpp @@ -913,19 +913,23 @@ bool CSharpInstance::set(const StringName &p_name, const Variant &p_value) { ERR_FAIL_COND_V(!script.is_valid(), false); + MonoObject *mono_object = get_mono_object(); + ERR_FAIL_NULL_V(mono_object, false); + GDMonoClass *top = script->script_class; while (top && top != script->native) { GDMonoField *field = script->script_class->get_field(p_name); if (field) { - MonoObject *mono_object = get_mono_object(); + field->set_value_from_variant(mono_object, p_value); + return true; + } - ERR_EXPLAIN("Reference has been garbage collected?"); - ERR_FAIL_NULL_V(mono_object, false); - - field->set_value(mono_object, p_value); + GDMonoProperty *property = script->script_class->get_property(p_name); + if (property) { + property->set_value(mono_object, GDMonoMarshal::variant_to_mono_object(p_value)); return true; } @@ -934,16 +938,15 @@ bool CSharpInstance::set(const StringName &p_name, const Variant &p_value) { // Call _set - Variant name = p_name; - const Variant *args[2] = { &name, &p_value }; - - MonoObject *mono_object = get_mono_object(); top = script->script_class; while (top && top != script->native) { GDMonoMethod *method = top->get_method(CACHED_STRING_NAME(_set), 2); if (method) { + Variant name = p_name; + const Variant *args[2] = { &name, &p_value }; + MonoObject *ret = method->invoke(mono_object, args); if (ret && GDMonoMarshal::unbox(ret) == true) @@ -960,31 +963,49 @@ bool CSharpInstance::get(const StringName &p_name, Variant &r_ret) const { ERR_FAIL_COND_V(!script.is_valid(), false); + MonoObject *mono_object = get_mono_object(); + ERR_FAIL_NULL_V(mono_object, false); + GDMonoClass *top = script->script_class; while (top && top != script->native) { GDMonoField *field = top->get_field(p_name); if (field) { - MonoObject *mono_object = get_mono_object(); - - ERR_EXPLAIN("Reference has been garbage collected?"); - ERR_FAIL_NULL_V(mono_object, false); - MonoObject *value = field->get_value(mono_object); r_ret = GDMonoMarshal::mono_object_to_variant(value, field->get_type()); return true; } - // Call _get + GDMonoProperty *property = top->get_property(p_name); + if (property) { + MonoObject *exc = NULL; + MonoObject *value = property->get_value(mono_object, &exc); + if (exc) { + r_ret = Variant(); + GDMonoUtils::print_unhandled_exception(exc); + } else { + r_ret = GDMonoMarshal::mono_object_to_variant(value, property->get_type()); + } + return true; + } + + top = top->get_parent_class(); + } + + // Call _get + + top = script->script_class; + + while (top && top != script->native) { GDMonoMethod *method = top->get_method(CACHED_STRING_NAME(_get), 1); if (method) { Variant name = p_name; const Variant *args[1] = { &name }; - MonoObject *ret = method->invoke(get_mono_object(), args); + MonoObject *ret = method->invoke(mono_object, args); if (ret) { r_ret = GDMonoMarshal::mono_object_to_variant(ret); @@ -1186,6 +1207,20 @@ bool CSharpInstance::refcount_decremented() { return ref_dying; } +ScriptInstance::RPCMode CSharpInstance::_member_get_rpc_mode(GDMonoClassMember *p_member) const { + + if (p_member->has_attribute(CACHED_CLASS(RemoteAttribute))) + return RPC_MODE_REMOTE; + if (p_member->has_attribute(CACHED_CLASS(SyncAttribute))) + return RPC_MODE_SYNC; + if (p_member->has_attribute(CACHED_CLASS(MasterAttribute))) + return RPC_MODE_MASTER; + if (p_member->has_attribute(CACHED_CLASS(SlaveAttribute))) + return RPC_MODE_SLAVE; + + return RPC_MODE_DISABLED; +} + ScriptInstance::RPCMode CSharpInstance::get_rpc_mode(const StringName &p_method) const { GDMonoClass *top = script->script_class; @@ -1193,17 +1228,8 @@ ScriptInstance::RPCMode CSharpInstance::get_rpc_mode(const StringName &p_method) while (top && top != script->native) { GDMonoMethod *method = top->get_method(p_method); - if (method) { // TODO should we reject static methods? - // TODO cache result - if (method->has_attribute(CACHED_CLASS(RemoteAttribute))) - return RPC_MODE_REMOTE; - if (method->has_attribute(CACHED_CLASS(SyncAttribute))) - return RPC_MODE_SYNC; - if (method->has_attribute(CACHED_CLASS(MasterAttribute))) - return RPC_MODE_MASTER; - if (method->has_attribute(CACHED_CLASS(SlaveAttribute))) - return RPC_MODE_SLAVE; - } + if (method && !method->is_static()) + return _member_get_rpc_mode(method); top = top->get_parent_class(); } @@ -1218,17 +1244,13 @@ ScriptInstance::RPCMode CSharpInstance::get_rset_mode(const StringName &p_variab while (top && top != script->native) { GDMonoField *field = top->get_field(p_variable); - if (field) { // TODO should we reject static fields? - // TODO cache result - if (field->has_attribute(CACHED_CLASS(RemoteAttribute))) - return RPC_MODE_REMOTE; - if (field->has_attribute(CACHED_CLASS(SyncAttribute))) - return RPC_MODE_SYNC; - if (field->has_attribute(CACHED_CLASS(MasterAttribute))) - return RPC_MODE_MASTER; - if (field->has_attribute(CACHED_CLASS(SlaveAttribute))) - return RPC_MODE_SLAVE; - } + if (field && !field->is_static()) + return _member_get_rpc_mode(field); + + GDMonoProperty *property = top->get_property(p_variable); + + if (property && !property->is_static()) + return _member_get_rpc_mode(property); top = top->get_parent_class(); } @@ -1353,7 +1375,7 @@ bool CSharpScript::_update_exports() { // We are creating a temporary new instance of the class here to get the default value // TODO Workaround. Should be replaced with IL opcodes analysis - MonoObject *tmp_object = mono_object_new(SCRIPTS_DOMAIN, script_class->get_raw()); + MonoObject *tmp_object = mono_object_new(SCRIPTS_DOMAIN, script_class->get_mono_ptr()); if (tmp_object) { CACHED_FIELD(GodotObject, ptr)->set_value_raw(tmp_object, tmp_object); // FIXME WTF is this workaround @@ -1376,65 +1398,55 @@ bool CSharpScript::_update_exports() { GDMonoClass *top = script_class; while (top && top != native) { + PropertyInfo prop_info; + bool exported; + const Vector &fields = top->get_all_fields(); for (int i = fields.size() - 1; i >= 0; i--) { GDMonoField *field = fields[i]; - if (field->is_static()) { - if (field->has_attribute(CACHED_CLASS(ExportAttribute))) - ERR_PRINTS("Cannot export field because it is static: " + top->get_full_name() + "." + field->get_name()); - continue; - } + if (_get_member_export(top, field, prop_info, exported)) { + StringName name = field->get_name(); - String name = field->get_name(); - StringName cname = name; + if (exported) { + member_info[name] = prop_info; + exported_members_cache.push_front(prop_info); - if (member_info.has(cname)) - continue; - - ManagedType field_type = field->get_type(); - Variant::Type type = GDMonoMarshal::managed_to_variant_type(field_type); - - if (field->has_attribute(CACHED_CLASS(ExportAttribute))) { - // Field has Export attribute - MonoObject *attr = field->get_attribute(CACHED_CLASS(ExportAttribute)); - - PropertyHint hint; - String hint_string; - - if (type == Variant::NIL) { - ERR_PRINTS("Unknown type of exported field: " + top->get_full_name() + "." + field->get_name()); - continue; - } else if (type == Variant::INT && field_type.type_encoding == MONO_TYPE_VALUETYPE && mono_class_is_enum(field_type.type_class->get_raw())) { - type = Variant::INT; - hint = PROPERTY_HINT_ENUM; - - Vector fields = field_type.type_class->get_enum_fields(); - - for (int i = 0; i < fields.size(); i++) { - if (i > 0) - hint_string += ","; - hint_string += mono_field_get_name(fields[i]); + if (tmp_object) { + exported_members_defval_cache[name] = GDMonoMarshal::mono_object_to_variant(field->get_value(tmp_object)); } - } else if (type == Variant::OBJECT && CACHED_CLASS(GodotReference)->is_assignable_from(field_type.type_class)) { - hint = PROPERTY_HINT_RESOURCE_TYPE; - hint_string = NATIVE_GDMONOCLASS_NAME(field_type.type_class); } else { - hint = PropertyHint(CACHED_FIELD(ExportAttribute, hint)->get_int_value(attr)); - hint_string = CACHED_FIELD(ExportAttribute, hintString)->get_string_value(attr); + member_info[name] = prop_info; } + } + } - PropertyInfo prop_info = PropertyInfo(type, name, hint, hint_string, PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SCRIPT_VARIABLE); + const Vector &properties = top->get_all_properties(); - member_info[cname] = prop_info; - exported_members_cache.push_front(prop_info); + for (int i = properties.size() - 1; i >= 0; i--) { + GDMonoProperty *property = properties[i]; - if (tmp_object) { - exported_members_defval_cache[cname] = GDMonoMarshal::mono_object_to_variant(field->get_value(tmp_object)); + if (_get_member_export(top, property, prop_info, exported)) { + StringName name = property->get_name(); + + if (exported) { + member_info[name] = prop_info; + exported_members_cache.push_front(prop_info); + + if (tmp_object) { + MonoObject *exc = NULL; + MonoObject *ret = property->get_value(tmp_object, &exc); + if (exc) { + exported_members_defval_cache[name] = Variant(); + GDMonoUtils::print_unhandled_exception(exc); + } else { + exported_members_defval_cache[name] = GDMonoMarshal::mono_object_to_variant(ret); + } + } + } else { + member_info[name] = prop_info; } - } else { - member_info[cname] = PropertyInfo(type, name, PROPERTY_HINT_NONE, "", PROPERTY_USAGE_SCRIPT_VARIABLE); } } @@ -1458,6 +1470,77 @@ bool CSharpScript::_update_exports() { return false; } +bool CSharpScript::_get_member_export(GDMonoClass *p_class, GDMonoClassMember *p_member, PropertyInfo &r_prop_info, bool &r_exported) { + + StringName name = p_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()); + return false; + } + + if (member_info.has(name)) + return false; + + ManagedType type; + + if (p_member->get_member_type() == GDMonoClassMember::MEMBER_TYPE_FIELD) { + type = static_cast(p_member)->get_type(); + } else if (p_member->get_member_type() == GDMonoClassMember::MEMBER_TYPE_PROPERTY) { + type = static_cast(p_member)->get_type(); + } else { + CRASH_NOW(); + } + + Variant::Type variant_type = GDMonoMarshal::managed_to_variant_type(type); + + if (p_member->has_attribute(CACHED_CLASS(ExportAttribute))) { + if (p_member->get_member_type() == GDMonoClassMember::MEMBER_TYPE_PROPERTY) { + GDMonoProperty *property = static_cast(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()); + return false; + } + } + + MonoObject *attr = p_member->get_attribute(CACHED_CLASS(ExportAttribute)); + + PropertyHint hint; + String hint_string; + + if (variant_type == Variant::NIL) { + ERR_PRINTS("Unknown type of exported member: " + p_class->get_full_name() + "." + name.operator String()); + return false; + } else if (variant_type == Variant::INT && type.type_encoding == MONO_TYPE_VALUETYPE && mono_class_is_enum(type.type_class->get_mono_ptr())) { + variant_type = Variant::INT; + hint = PROPERTY_HINT_ENUM; + + Vector fields = type.type_class->get_enum_fields(); + + for (int i = 0; i < fields.size(); i++) { + if (i > 0) + hint_string += ","; + hint_string += mono_field_get_name(fields[i]); + } + } else if (variant_type == Variant::OBJECT && CACHED_CLASS(GodotReference)->is_assignable_from(type.type_class)) { + hint = PROPERTY_HINT_RESOURCE_TYPE; + hint_string = NATIVE_GDMONOCLASS_NAME(type.type_class); + } else { + 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, name.operator String(), hint, hint_string, PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SCRIPT_VARIABLE); + r_exported = true; + } else { + r_prop_info = PropertyInfo(variant_type, name.operator String(), PROPERTY_HINT_NONE, "", PROPERTY_USAGE_SCRIPT_VARIABLE); + r_exported = false; + } + + return true; +} + void CSharpScript::_clear() { tool = false; @@ -1626,7 +1709,7 @@ CSharpInstance *CSharpScript::_create_instance(const Variant **p_args, int p_arg /* STEP 2, INITIALIZE AND CONSTRUCT */ - MonoObject *mono_object = mono_object_new(SCRIPTS_DOMAIN, script_class->get_raw()); + MonoObject *mono_object = mono_object_new(SCRIPTS_DOMAIN, script_class->get_mono_ptr()); if (!mono_object) { instance->script = Ref(); diff --git a/modules/mono/csharp_script.h b/modules/mono/csharp_script.h index 27e68a7a567..f18daacf94c 100644 --- a/modules/mono/csharp_script.h +++ b/modules/mono/csharp_script.h @@ -104,6 +104,8 @@ class CSharpScript : public Script { void _clear(); bool _update_exports(); + bool _get_member_export(GDMonoClass *p_class, GDMonoClassMember *p_member, PropertyInfo &r_prop_info, bool &r_exported); + CSharpInstance *_create_instance(const Variant **p_args, int p_argcount, Object *p_owner, bool p_isref, Variant::CallError &r_error); Variant _new(const Variant **p_args, int p_argcount, Variant::CallError &r_error); @@ -178,6 +180,8 @@ class CSharpInstance : public ScriptInstance { void _call_multilevel(MonoObject *p_mono_object, const StringName &p_method, const Variant **p_args, int p_argcount); + RPCMode _member_get_rpc_mode(GDMonoClassMember *p_member) const; + public: MonoObject *get_mono_object() const; diff --git a/modules/mono/editor/godotsharp_builds.cpp b/modules/mono/editor/godotsharp_builds.cpp index 58d26a0f83e..993e8af581a 100644 --- a/modules/mono/editor/godotsharp_builds.cpp +++ b/modules/mono/editor/godotsharp_builds.cpp @@ -446,7 +446,7 @@ void GodotSharpBuilds::BuildProcess::start(bool p_blocking) { GDMonoClass *klass = GDMono::get_singleton()->get_editor_tools_assembly()->get_class("GodotSharpTools.Build", "BuildInstance"); - MonoObject *mono_object = mono_object_new(mono_domain_get(), klass->get_raw()); + MonoObject *mono_object = mono_object_new(mono_domain_get(), klass->get_mono_ptr()); // Construct diff --git a/modules/mono/editor/monodevelop_instance.cpp b/modules/mono/editor/monodevelop_instance.cpp index 57b3382bff0..8dc308b113c 100644 --- a/modules/mono/editor/monodevelop_instance.cpp +++ b/modules/mono/editor/monodevelop_instance.cpp @@ -62,7 +62,7 @@ MonoDevelopInstance::MonoDevelopInstance(const String &p_solution) { GDMonoClass *klass = GDMono::get_singleton()->get_editor_tools_assembly()->get_class("GodotSharpTools.Editor", "MonoDevelopInstance"); - MonoObject *obj = mono_object_new(TOOLS_DOMAIN, klass->get_raw()); + MonoObject *obj = mono_object_new(TOOLS_DOMAIN, klass->get_mono_ptr()); GDMonoMethod *ctor = klass->get_method(".ctor", 1); MonoObject *ex = NULL; diff --git a/modules/mono/mono_gd/gd_mono.cpp b/modules/mono/mono_gd/gd_mono.cpp index 5b65f020fcb..1152d70a042 100644 --- a/modules/mono/mono_gd/gd_mono.cpp +++ b/modules/mono/mono_gd/gd_mono.cpp @@ -52,8 +52,7 @@ void gdmono_unhandled_exception_hook(MonoObject *exc, void *user_data) { (void)user_data; // UNUSED - ERR_PRINT(GDMonoUtils::get_exception_name_and_message(exc).utf8()); - mono_print_unhandled_exception(exc); + GDMonoUtils::print_unhandled_exception(exc); abort(); } diff --git a/modules/mono/mono_gd/gd_mono.h b/modules/mono/mono_gd/gd_mono.h index c565e26de6a..8b753026d7a 100644 --- a/modules/mono/mono_gd/gd_mono.h +++ b/modules/mono/mono_gd/gd_mono.h @@ -112,14 +112,6 @@ public: #endif #endif - enum MemberVisibility { - PRIVATE, - PROTECTED_AND_INTERNAL, // FAM_AND_ASSEM - INTERNAL, // ASSEMBLY - PROTECTED, // FAMILY - PUBLIC - }; - static GDMono *get_singleton() { return singleton; } // Do not use these, unless you know what you're doing diff --git a/modules/mono/mono_gd/gd_mono_assembly.cpp b/modules/mono/mono_gd/gd_mono_assembly.cpp index 7a1bf99a361..085216a5b6d 100644 --- a/modules/mono/mono_gd/gd_mono_assembly.cpp +++ b/modules/mono/mono_gd/gd_mono_assembly.cpp @@ -318,7 +318,7 @@ GDMonoClass *GDMonoAssembly::get_object_derived_class(const StringName &p_class) void *iter = NULL; while (true) { - MonoClass *raw_nested = mono_class_get_nested_types(current_nested->get_raw(), &iter); + MonoClass *raw_nested = mono_class_get_nested_types(current_nested->get_mono_ptr(), &iter); if (!raw_nested) break; diff --git a/modules/mono/mono_gd/gd_mono_class.cpp b/modules/mono/mono_gd/gd_mono_class.cpp index 2bcce86d8c4..b94a626f4b0 100644 --- a/modules/mono/mono_gd/gd_mono_class.cpp +++ b/modules/mono/mono_gd/gd_mono_class.cpp @@ -35,7 +35,7 @@ MonoType *GDMonoClass::get_raw_type(GDMonoClass *p_class) { - return mono_class_get_type(p_class->get_raw()); + return mono_class_get_type(p_class->get_mono_ptr()); } bool GDMonoClass::is_assignable_from(GDMonoClass *p_from) const { @@ -74,7 +74,7 @@ Vector GDMonoClass::get_enum_fields() { void *iter = NULL; MonoClassField *raw_field = NULL; - while ((raw_field = mono_class_get_fields(get_raw(), &iter)) != NULL) { + while ((raw_field = mono_class_get_fields(get_mono_ptr(), &iter)) != NULL) { uint32_t field_flags = mono_field_get_flags(raw_field); // Enums have an instance field named value__ which holds the value of the enum. @@ -105,7 +105,7 @@ bool GDMonoClass::has_attribute(GDMonoClass *p_attr_class) { if (!attributes) return false; - return mono_custom_attrs_has_attr(attributes, p_attr_class->get_raw()); + return mono_custom_attrs_has_attr(attributes, p_attr_class->get_mono_ptr()); } MonoObject *GDMonoClass::get_attribute(GDMonoClass *p_attr_class) { @@ -120,14 +120,14 @@ MonoObject *GDMonoClass::get_attribute(GDMonoClass *p_attr_class) { if (!attributes) return NULL; - return mono_custom_attrs_get_attr(attributes, p_attr_class->get_raw()); + return mono_custom_attrs_get_attr(attributes, p_attr_class->get_mono_ptr()); } void GDMonoClass::fetch_attributes() { ERR_FAIL_COND(attributes != NULL); - attributes = mono_custom_attrs_from_class(get_raw()); + attributes = mono_custom_attrs_from_class(get_mono_ptr()); attrs_fetched = true; } @@ -140,7 +140,7 @@ void GDMonoClass::fetch_methods_with_godot_api_checks(GDMonoClass *p_native_base void *iter = NULL; MonoMethod *raw_method = NULL; - while ((raw_method = mono_class_get_methods(get_raw(), &iter)) != NULL) { + while ((raw_method = mono_class_get_methods(get_mono_ptr(), &iter)) != NULL) { StringName name = mono_method_get_name(raw_method); GDMonoMethod *method = get_method(raw_method, name); @@ -334,7 +334,7 @@ const Vector &GDMonoClass::get_all_fields() { void *iter = NULL; MonoClassField *raw_field = NULL; - while ((raw_field = mono_class_get_fields(get_raw(), &iter)) != NULL) { + while ((raw_field = mono_class_get_fields(mono_class, &iter)) != NULL) { StringName name = mono_field_get_name(raw_field); Map::Element *match = fields.find(name); @@ -353,6 +353,54 @@ const Vector &GDMonoClass::get_all_fields() { return fields_list; } +GDMonoProperty *GDMonoClass::get_property(const StringName &p_name) { + + Map::Element *result = properties.find(p_name); + + if (result) + return result->value(); + + if (properties_fetched) + return NULL; + + MonoProperty *raw_property = mono_class_get_property_from_name(mono_class, String(p_name).utf8().get_data()); + + if (raw_property) { + GDMonoProperty *property = memnew(GDMonoProperty(raw_property, this)); + properties.insert(p_name, property); + + return property; + } + + return NULL; +} + +const Vector &GDMonoClass::get_all_properties() { + + if (properties_fetched) + return properties_list; + + void *iter = NULL; + MonoProperty *raw_property = NULL; + while ((raw_property = mono_class_get_properties(mono_class, &iter)) != NULL) { + StringName name = mono_property_get_name(raw_property); + + Map::Element *match = properties.find(name); + + if (match) { + properties_list.push_back(match->get()); + } else { + GDMonoProperty *property = memnew(GDMonoProperty(raw_property, this)); + properties.insert(name, property); + properties_list.push_back(property); + } + } + + properties_fetched = true; + + return properties_list; +} + GDMonoClass::GDMonoClass(const StringName &p_namespace, const StringName &p_name, MonoClass *p_class, GDMonoAssembly *p_assembly) { namespace_name = p_namespace; @@ -365,6 +413,7 @@ GDMonoClass::GDMonoClass(const StringName &p_namespace, const StringName &p_name methods_fetched = false; fields_fetched = false; + properties_fetched = false; } GDMonoClass::~GDMonoClass() { @@ -377,6 +426,10 @@ GDMonoClass::~GDMonoClass() { memdelete(E->value()); } + for (Map::Element *E = properties.front(); E; E = E->next()) { + memdelete(E->value()); + } + { // Ugly workaround... // We may have duplicated values, because we redirect snake_case methods to PascalCasel (only Godot API methods). diff --git a/modules/mono/mono_gd/gd_mono_class.h b/modules/mono/mono_gd/gd_mono_class.h index b9d34eef3ab..32350d5a878 100644 --- a/modules/mono/mono_gd/gd_mono_class.h +++ b/modules/mono/mono_gd/gd_mono_class.h @@ -38,6 +38,7 @@ #include "gd_mono_field.h" #include "gd_mono_header.h" #include "gd_mono_method.h" +#include "gd_mono_property.h" #include "gd_mono_utils.h" class GDMonoClass { @@ -84,6 +85,10 @@ class GDMonoClass { Map fields; Vector fields_list; + bool properties_fetched; + Map properties; + Vector properties_list; + friend class GDMonoAssembly; GDMonoClass(const StringName &p_namespace, const StringName &p_name, MonoClass *p_class, GDMonoAssembly *p_assembly); @@ -95,7 +100,7 @@ public: _FORCE_INLINE_ StringName get_namespace() const { return namespace_name; } _FORCE_INLINE_ StringName get_name() const { return class_name; } - _FORCE_INLINE_ MonoClass *get_raw() const { return mono_class; } + _FORCE_INLINE_ MonoClass *get_mono_ptr() const { return mono_class; } _FORCE_INLINE_ const GDMonoAssembly *get_assembly() const { return assembly; } String get_full_name() const; @@ -124,6 +129,9 @@ public: GDMonoField *get_field(const StringName &p_name); const Vector &get_all_fields(); + GDMonoProperty *get_property(const StringName &p_name); + const Vector &get_all_properties(); + ~GDMonoClass(); }; diff --git a/modules/mono/mono_gd/gd_mono_class_member.h b/modules/mono/mono_gd/gd_mono_class_member.h new file mode 100644 index 00000000000..008ea0e416b --- /dev/null +++ b/modules/mono/mono_gd/gd_mono_class_member.h @@ -0,0 +1,67 @@ +/*************************************************************************/ +/* gd_mono_class_member.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ +#ifndef GD_MONO_CLASS_MEMBER_H +#define GD_MONO_CLASS_MEMBER_H + +#include "gd_mono_header.h" + +#include + +class GDMonoClassMember { +public: + enum Visibility { + PRIVATE, + PROTECTED_AND_INTERNAL, // FAM_AND_ASSEM + INTERNAL, // ASSEMBLY + PROTECTED, // FAMILY + PUBLIC + }; + + enum MemberType { + MEMBER_TYPE_FIELD, + MEMBER_TYPE_PROPERTY, + MEMBER_TYPE_METHOD + }; + + virtual ~GDMonoClassMember() {} + + virtual MemberType get_member_type() = 0; + + virtual StringName get_name() = 0; + + virtual bool is_static() = 0; + + virtual Visibility get_visibility() = 0; + + virtual bool has_attribute(GDMonoClass *p_attr_class) = 0; + virtual MonoObject *get_attribute(GDMonoClass *p_attr_class) = 0; +}; + +#endif // GD_MONO_CLASS_MEMBER_H diff --git a/modules/mono/mono_gd/gd_mono_field.cpp b/modules/mono/mono_gd/gd_mono_field.cpp index 133cfc9145a..f545dd28a6b 100644 --- a/modules/mono/mono_gd/gd_mono_field.cpp +++ b/modules/mono/mono_gd/gd_mono_field.cpp @@ -38,7 +38,7 @@ void GDMonoField::set_value_raw(MonoObject *p_object, void *p_ptr) { mono_field_set_value(p_object, mono_field, &p_ptr); } -void GDMonoField::set_value(MonoObject *p_object, const Variant &p_value) { +void GDMonoField::set_value_from_variant(MonoObject *p_object, const Variant &p_value) { #define SET_FROM_STRUCT_AND_BREAK(m_type) \ { \ const m_type &val = p_value.operator ::m_type(); \ @@ -138,7 +138,7 @@ void GDMonoField::set_value(MonoObject *p_object, const Variant &p_value) { if (tclass == CACHED_CLASS(Plane)) SET_FROM_STRUCT_AND_BREAK(Plane); - if (mono_class_is_enum(tclass->get_raw())) + if (mono_class_is_enum(tclass->get_mono_ptr())) SET_FROM_PRIMITIVE(signed int); ERR_EXPLAIN(String() + "Attempted to set the value of a field of unmarshallable type: " + tclass->get_name()); @@ -264,7 +264,7 @@ void GDMonoField::set_value(MonoObject *p_object, const Variant &p_value) { } break; case MONO_TYPE_GENERICINST: { - if (CACHED_RAW_MONO_CLASS(Dictionary) == type.type_class->get_raw()) { + if (CACHED_RAW_MONO_CLASS(Dictionary) == type.type_class->get_mono_ptr()) { MonoObject *managed = GDMonoMarshal::Dictionary_to_mono_object(p_value.operator Dictionary()); mono_field_set_value(p_object, mono_field, managed); break; @@ -280,6 +280,10 @@ void GDMonoField::set_value(MonoObject *p_object, const Variant &p_value) { #undef SET_FROM_PRIMITIVE } +MonoObject *GDMonoField::get_value(MonoObject *p_object) { + return mono_field_get_value_object(mono_domain_get(), mono_field, p_object); +} + bool GDMonoField::get_bool_value(MonoObject *p_object) { return (bool)GDMonoMarshal::unbox(get_value(p_object)); } @@ -302,7 +306,7 @@ bool GDMonoField::has_attribute(GDMonoClass *p_attr_class) { if (!attributes) return false; - return mono_custom_attrs_has_attr(attributes, p_attr_class->get_raw()); + return mono_custom_attrs_has_attr(attributes, p_attr_class->get_mono_ptr()); } MonoObject *GDMonoField::get_attribute(GDMonoClass *p_attr_class) { @@ -314,12 +318,12 @@ MonoObject *GDMonoField::get_attribute(GDMonoClass *p_attr_class) { if (!attributes) return NULL; - return mono_custom_attrs_get_attr(attributes, p_attr_class->get_raw()); + return mono_custom_attrs_get_attr(attributes, p_attr_class->get_mono_ptr()); } void GDMonoField::fetch_attributes() { ERR_FAIL_COND(attributes != NULL); - attributes = mono_custom_attrs_from_field(owner->get_raw(), get_raw()); + attributes = mono_custom_attrs_from_field(owner->get_mono_ptr(), mono_field); attrs_fetched = true; } @@ -327,26 +331,26 @@ bool GDMonoField::is_static() { return mono_field_get_flags(mono_field) & MONO_FIELD_ATTR_STATIC; } -GDMono::MemberVisibility GDMonoField::get_visibility() { +GDMonoClassMember::Visibility GDMonoField::get_visibility() { switch (mono_field_get_flags(mono_field) & MONO_FIELD_ATTR_FIELD_ACCESS_MASK) { case MONO_FIELD_ATTR_PRIVATE: - return GDMono::PRIVATE; + return GDMonoClassMember::PRIVATE; case MONO_FIELD_ATTR_FAM_AND_ASSEM: - return GDMono::PROTECTED_AND_INTERNAL; + return GDMonoClassMember::PROTECTED_AND_INTERNAL; case MONO_FIELD_ATTR_ASSEMBLY: - return GDMono::INTERNAL; + return GDMonoClassMember::INTERNAL; case MONO_FIELD_ATTR_FAMILY: - return GDMono::PROTECTED; + return GDMonoClassMember::PROTECTED; case MONO_FIELD_ATTR_PUBLIC: - return GDMono::PUBLIC; + return GDMonoClassMember::PUBLIC; default: - ERR_FAIL_V(GDMono::PRIVATE); + ERR_FAIL_V(GDMonoClassMember::PRIVATE); } } -GDMonoField::GDMonoField(MonoClassField *p_raw_field, GDMonoClass *p_owner) { +GDMonoField::GDMonoField(MonoClassField *p_mono_field, GDMonoClass *p_owner) { owner = p_owner; - mono_field = p_raw_field; + mono_field = p_mono_field; name = mono_field_get_name(mono_field); MonoType *field_type = mono_field_get_type(mono_field); type.type_encoding = mono_type_get_type(field_type); diff --git a/modules/mono/mono_gd/gd_mono_field.h b/modules/mono/mono_gd/gd_mono_field.h index a5559df0599..4458404a30b 100644 --- a/modules/mono/mono_gd/gd_mono_field.h +++ b/modules/mono/mono_gd/gd_mono_field.h @@ -31,43 +31,44 @@ #define GDMONOFIELD_H #include "gd_mono.h" +#include "gd_mono_class_member.h" #include "gd_mono_header.h" -class GDMonoField { +class GDMonoField : public GDMonoClassMember { + GDMonoClass *owner; MonoClassField *mono_field; - String name; + StringName name; ManagedType type; bool attrs_fetched; MonoCustomAttrInfo *attributes; public: - _FORCE_INLINE_ String get_name() const { return name; } + virtual MemberType get_member_type() { return MEMBER_TYPE_FIELD; } + + virtual StringName get_name() { return name; } + + virtual bool is_static(); + virtual Visibility get_visibility(); + + virtual bool has_attribute(GDMonoClass *p_attr_class); + virtual MonoObject *get_attribute(GDMonoClass *p_attr_class); + void fetch_attributes(); + _FORCE_INLINE_ ManagedType get_type() const { return type; } - _FORCE_INLINE_ MonoClassField *get_raw() const { return mono_field; } - void set_value_raw(MonoObject *p_object, void *p_ptr); - void set_value(MonoObject *p_object, const Variant &p_value); + void set_value_from_variant(MonoObject *p_object, const Variant &p_value); - _FORCE_INLINE_ MonoObject *get_value(MonoObject *p_object) { - return mono_field_get_value_object(mono_domain_get(), mono_field, p_object); - } + _FORCE_INLINE_ MonoObject *get_value(MonoObject *p_object); bool get_bool_value(MonoObject *p_object); int get_int_value(MonoObject *p_object); String get_string_value(MonoObject *p_object); - bool has_attribute(GDMonoClass *p_attr_class); - MonoObject *get_attribute(GDMonoClass *p_attr_class); - void fetch_attributes(); - - bool is_static(); - GDMono::MemberVisibility get_visibility(); - - GDMonoField(MonoClassField *p_raw_field, GDMonoClass *p_owner); + GDMonoField(MonoClassField *p_mono_field, GDMonoClass *p_owner); ~GDMonoField(); }; diff --git a/modules/mono/mono_gd/gd_mono_header.h b/modules/mono/mono_gd/gd_mono_header.h index a749aa8768c..ceacc80aba5 100644 --- a/modules/mono/mono_gd/gd_mono_header.h +++ b/modules/mono/mono_gd/gd_mono_header.h @@ -34,8 +34,10 @@ class GDMonoAssembly; class GDMonoClass; -class GDMonoMethod; +class GDMonoClassMember; class GDMonoField; +class GDMonoProperty; +class GDMonoMethod; struct ManagedType { int type_encoding; diff --git a/modules/mono/mono_gd/gd_mono_marshal.cpp b/modules/mono/mono_gd/gd_mono_marshal.cpp index 48d08b159c3..c2dbbc87fff 100644 --- a/modules/mono/mono_gd/gd_mono_marshal.cpp +++ b/modules/mono/mono_gd/gd_mono_marshal.cpp @@ -113,7 +113,7 @@ Variant::Type managed_to_variant_type(const ManagedType &p_type) { if (tclass == CACHED_CLASS(Plane)) return Variant::PLANE; - if (mono_class_is_enum(tclass->get_raw())) + if (mono_class_is_enum(tclass->get_mono_ptr())) return Variant::INT; } break; @@ -164,7 +164,7 @@ Variant::Type managed_to_variant_type(const ManagedType &p_type) { } break; case MONO_TYPE_GENERICINST: { - if (CACHED_RAW_MONO_CLASS(Dictionary) == p_type.type_class->get_raw()) { + if (CACHED_RAW_MONO_CLASS(Dictionary) == p_type.type_class->get_mono_ptr()) { return Variant::DICTIONARY; } } break; @@ -306,9 +306,9 @@ MonoObject *variant_to_mono_object(const Variant *p_var, const ManagedType &p_ty if (tclass == CACHED_CLASS(Plane)) RETURN_BOXED_STRUCT(Plane, p_var); - if (mono_class_is_enum(tclass->get_raw())) { + if (mono_class_is_enum(tclass->get_mono_ptr())) { int val = p_var->operator signed int(); - return BOX_ENUM(tclass->get_raw(), val); + return BOX_ENUM(tclass->get_mono_ptr(), val); } } break; @@ -432,7 +432,7 @@ MonoObject *variant_to_mono_object(const Variant *p_var, const ManagedType &p_ty } break; case MONO_TYPE_GENERICINST: { - if (CACHED_RAW_MONO_CLASS(Dictionary) == p_type.type_class->get_raw()) { + if (CACHED_RAW_MONO_CLASS(Dictionary) == p_type.type_class->get_mono_ptr()) { return Dictionary_to_mono_object(p_var->operator Dictionary()); } } break; @@ -528,7 +528,7 @@ Variant mono_object_to_variant(MonoObject *p_obj, const ManagedType &p_type) { if (tclass == CACHED_CLASS(Plane)) RETURN_UNBOXED_STRUCT(Plane, p_obj); - if (mono_class_is_enum(tclass->get_raw())) + if (mono_class_is_enum(tclass->get_mono_ptr())) return unbox(p_obj); } break; @@ -585,7 +585,7 @@ Variant mono_object_to_variant(MonoObject *p_obj, const ManagedType &p_type) { } break; case MONO_TYPE_GENERICINST: { - if (CACHED_RAW_MONO_CLASS(Dictionary) == p_type.type_class->get_raw()) { + if (CACHED_RAW_MONO_CLASS(Dictionary) == p_type.type_class->get_mono_ptr()) { return mono_object_to_Dictionary(p_obj); } } break; diff --git a/modules/mono/mono_gd/gd_mono_method.cpp b/modules/mono/mono_gd/gd_mono_method.cpp index 01afd1e51e6..467cdbcc205 100644 --- a/modules/mono/mono_gd/gd_mono_method.cpp +++ b/modules/mono/mono_gd/gd_mono_method.cpp @@ -32,6 +32,8 @@ #include "gd_mono_class.h" #include "gd_mono_marshal.h" +#include + void GDMonoMethod::_update_signature() { // Apparently MonoMethodSignature needs not to be freed. // mono_method_signature caches the result, we don't need to cache it ourselves. @@ -41,7 +43,6 @@ void GDMonoMethod::_update_signature() { } void GDMonoMethod::_update_signature(MonoMethodSignature *p_method_sig) { - is_instance = mono_signature_is_instance(p_method_sig); params_count = mono_signature_get_param_count(p_method_sig); MonoType *ret_type = mono_signature_get_return_type(p_method_sig); @@ -61,15 +62,34 @@ void GDMonoMethod::_update_signature(MonoMethodSignature *p_method_sig) { param_type.type_encoding = mono_type_get_type(param_raw_type); - if (param_type.type_encoding != MONO_TYPE_VOID) { - MonoClass *param_type_class = mono_class_from_mono_type(param_raw_type); - param_type.type_class = GDMono::get_singleton()->get_class(param_type_class); - } + MonoClass *param_type_class = mono_class_from_mono_type(param_raw_type); + param_type.type_class = GDMono::get_singleton()->get_class(param_type_class); param_types.push_back(param_type); } } +bool GDMonoMethod::is_static() { + return mono_method_get_flags(mono_method, NULL) & MONO_METHOD_ATTR_STATIC; +} + +GDMonoClassMember::Visibility GDMonoMethod::get_visibility() { + switch (mono_method_get_flags(mono_method, NULL) & MONO_METHOD_ATTR_ACCESS_MASK) { + case MONO_METHOD_ATTR_PRIVATE: + return GDMonoClassMember::PRIVATE; + case MONO_METHOD_ATTR_FAM_AND_ASSEM: + return GDMonoClassMember::PROTECTED_AND_INTERNAL; + case MONO_METHOD_ATTR_ASSEM: + return GDMonoClassMember::INTERNAL; + case MONO_METHOD_ATTR_FAMILY: + return GDMonoClassMember::PROTECTED; + case MONO_METHOD_ATTR_PUBLIC: + return GDMonoClassMember::PUBLIC; + default: + ERR_FAIL_V(GDMonoClassMember::PRIVATE); + } +} + void *GDMonoMethod::get_thunk() { return mono_method_get_unmanaged_thunk(mono_method); } @@ -87,11 +107,11 @@ MonoObject *GDMonoMethod::invoke(MonoObject *p_object, const Variant **p_params, MonoObject *ret = mono_runtime_invoke_array(mono_method, p_object, params, &exc); if (exc) { + ret = NULL; if (r_exc) { *r_exc = exc; } else { - ERR_PRINT(GDMonoUtils::get_exception_name_and_message(exc).utf8()); - mono_print_unhandled_exception(exc); + GDMonoUtils::print_unhandled_exception(exc); } } @@ -104,8 +124,7 @@ MonoObject *GDMonoMethod::invoke(MonoObject *p_object, const Variant **p_params, if (r_exc) { *r_exc = exc; } else { - ERR_PRINT(GDMonoUtils::get_exception_name_and_message(exc).utf8()); - mono_print_unhandled_exception(exc); + GDMonoUtils::print_unhandled_exception(exc); } } @@ -123,11 +142,11 @@ MonoObject *GDMonoMethod::invoke_raw(MonoObject *p_object, void **p_params, Mono MonoObject *ret = mono_runtime_invoke(mono_method, p_object, p_params, &exc); if (exc) { + ret = NULL; if (r_exc) { *r_exc = exc; } else { - ERR_PRINT(GDMonoUtils::get_exception_name_and_message(exc).utf8()); - mono_print_unhandled_exception(exc); + GDMonoUtils::print_unhandled_exception(exc); } } @@ -143,7 +162,7 @@ bool GDMonoMethod::has_attribute(GDMonoClass *p_attr_class) { if (!attributes) return false; - return mono_custom_attrs_has_attr(attributes, p_attr_class->get_raw()); + return mono_custom_attrs_has_attr(attributes, p_attr_class->get_mono_ptr()); } MonoObject *GDMonoMethod::get_attribute(GDMonoClass *p_attr_class) { @@ -155,7 +174,7 @@ MonoObject *GDMonoMethod::get_attribute(GDMonoClass *p_attr_class) { if (!attributes) return NULL; - return mono_custom_attrs_get_attr(attributes, p_attr_class->get_raw()); + return mono_custom_attrs_get_attr(attributes, p_attr_class->get_mono_ptr()); } void GDMonoMethod::fetch_attributes() { diff --git a/modules/mono/mono_gd/gd_mono_method.h b/modules/mono/mono_gd/gd_mono_method.h index f1f6e51d456..b1967a99f00 100644 --- a/modules/mono/mono_gd/gd_mono_method.h +++ b/modules/mono/mono_gd/gd_mono_method.h @@ -31,13 +31,13 @@ #define GD_MONO_METHOD_H #include "gd_mono.h" +#include "gd_mono_class_member.h" #include "gd_mono_header.h" -class GDMonoMethod { +class GDMonoMethod : public GDMonoClassMember { StringName name; - bool is_instance; int params_count; ManagedType return_type; Vector param_types; @@ -53,9 +53,18 @@ class GDMonoMethod { MonoMethod *mono_method; public: - _FORCE_INLINE_ StringName get_name() { return name; } + virtual MemberType get_member_type() { return MEMBER_TYPE_METHOD; } + + virtual StringName get_name() { return name; } + + virtual bool is_static(); + + virtual Visibility get_visibility(); + + virtual bool has_attribute(GDMonoClass *p_attr_class); + virtual MonoObject *get_attribute(GDMonoClass *p_attr_class); + virtual void fetch_attributes(); - _FORCE_INLINE_ bool is_static() { return !is_instance; } _FORCE_INLINE_ int get_parameters_count() { return params_count; } _FORCE_INLINE_ ManagedType get_return_type() { return return_type; } @@ -65,10 +74,6 @@ public: MonoObject *invoke(MonoObject *p_object, MonoObject **r_exc = NULL); MonoObject *invoke_raw(MonoObject *p_object, void **p_params, MonoObject **r_exc = NULL); - bool has_attribute(GDMonoClass *p_attr_class); - MonoObject *get_attribute(GDMonoClass *p_attr_class); - void fetch_attributes(); - String get_full_name(bool p_signature = false) const; String get_full_name_no_class() const; String get_ret_type_full_name() const; diff --git a/modules/mono/mono_gd/gd_mono_property.cpp b/modules/mono/mono_gd/gd_mono_property.cpp new file mode 100644 index 00000000000..bc5a1d3a390 --- /dev/null +++ b/modules/mono/mono_gd/gd_mono_property.cpp @@ -0,0 +1,199 @@ +/*************************************************************************/ +/* gd_mono_property.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ +#include "gd_mono_property.h" + +#include "gd_mono_class.h" +#include "gd_mono_marshal.h" + +#include + +GDMonoProperty::GDMonoProperty(MonoProperty *p_mono_property, GDMonoClass *p_owner) { + owner = p_owner; + mono_property = p_mono_property; + name = mono_property_get_name(mono_property); + + MonoMethod *prop_method = mono_property_get_get_method(mono_property); + + if (prop_method) { + MonoMethodSignature *getter_sig = mono_method_signature(prop_method); + + MonoType *ret_type = mono_signature_get_return_type(getter_sig); + + type.type_encoding = mono_type_get_type(ret_type); + MonoClass *ret_type_class = mono_class_from_mono_type(ret_type); + type.type_class = GDMono::get_singleton()->get_class(ret_type_class); + } else { + prop_method = mono_property_get_set_method(mono_property); + + MonoMethodSignature *setter_sig = mono_method_signature(prop_method); + + void *iter = NULL; + MonoType *param_raw_type = mono_signature_get_params(setter_sig, &iter); + + type.type_encoding = mono_type_get_type(param_raw_type); + MonoClass *param_type_class = mono_class_from_mono_type(param_raw_type); + type.type_class = GDMono::get_singleton()->get_class(param_type_class); + } + + attrs_fetched = false; + attributes = NULL; +} + +GDMonoProperty::~GDMonoProperty() { + if (attributes) { + mono_custom_attrs_free(attributes); + } +} + +bool GDMonoProperty::is_static() { + MonoMethod *prop_method = mono_property_get_get_method(mono_property); + if (prop_method == NULL) + prop_method = mono_property_get_set_method(mono_property); + return mono_method_get_flags(prop_method, NULL) & MONO_METHOD_ATTR_STATIC; +} + +GDMonoClassMember::Visibility GDMonoProperty::get_visibility() { + MonoMethod *prop_method = mono_property_get_get_method(mono_property); + if (prop_method == NULL) + prop_method = mono_property_get_set_method(mono_property); + + switch (mono_method_get_flags(prop_method, NULL) & MONO_METHOD_ATTR_ACCESS_MASK) { + case MONO_METHOD_ATTR_PRIVATE: + return GDMonoClassMember::PRIVATE; + case MONO_METHOD_ATTR_FAM_AND_ASSEM: + return GDMonoClassMember::PROTECTED_AND_INTERNAL; + case MONO_METHOD_ATTR_ASSEM: + return GDMonoClassMember::INTERNAL; + case MONO_METHOD_ATTR_FAMILY: + return GDMonoClassMember::PROTECTED; + case MONO_METHOD_ATTR_PUBLIC: + return GDMonoClassMember::PUBLIC; + default: + ERR_FAIL_V(GDMonoClassMember::PRIVATE); + } +} + +bool GDMonoProperty::has_attribute(GDMonoClass *p_attr_class) { + ERR_FAIL_NULL_V(p_attr_class, false); + + if (!attrs_fetched) + fetch_attributes(); + + if (!attributes) + return false; + + return mono_custom_attrs_has_attr(attributes, p_attr_class->get_mono_ptr()); +} + +MonoObject *GDMonoProperty::get_attribute(GDMonoClass *p_attr_class) { + ERR_FAIL_NULL_V(p_attr_class, NULL); + + if (!attrs_fetched) + fetch_attributes(); + + if (!attributes) + return NULL; + + return mono_custom_attrs_get_attr(attributes, p_attr_class->get_mono_ptr()); +} + +void GDMonoProperty::fetch_attributes() { + ERR_FAIL_COND(attributes != NULL); + attributes = mono_custom_attrs_from_property(owner->get_mono_ptr(), mono_property); + attrs_fetched = true; +} + +bool GDMonoProperty::has_getter() { + return mono_property_get_get_method(mono_property) != NULL; +} + +bool GDMonoProperty::has_setter() { + return mono_property_get_set_method(mono_property) != NULL; +} + +void GDMonoProperty::set_value(MonoObject *p_object, MonoObject *p_value, MonoObject **r_exc) { + MonoMethod *prop_method = mono_property_get_get_method(mono_property); + + MonoArray *params = mono_array_new(mono_domain_get(), CACHED_CLASS_RAW(MonoObject), 1); + mono_array_set(params, MonoObject *, 0, p_value); + + MonoObject *exc = NULL; + mono_runtime_invoke_array(prop_method, p_object, params, &exc); + + if (exc) { + if (r_exc) { + *r_exc = exc; + } else { + GDMonoUtils::print_unhandled_exception(exc); + } + } +} + +void GDMonoProperty::set_value(MonoObject *p_object, void **p_params, MonoObject **r_exc) { + MonoObject *exc = NULL; + mono_property_set_value(mono_property, p_object, p_params, &exc); + + if (exc) { + if (r_exc) { + *r_exc = exc; + } else { + GDMonoUtils::print_unhandled_exception(exc); + } + } +} + +MonoObject *GDMonoProperty::get_value(MonoObject *p_object, MonoObject **r_exc) { + MonoObject *exc = NULL; + MonoObject *ret = mono_property_get_value(mono_property, p_object, NULL, &exc); + + if (exc) { + ret = NULL; + if (r_exc) { + *r_exc = exc; + } else { + GDMonoUtils::print_unhandled_exception(exc); + } + } + + return ret; +} + +bool GDMonoProperty::get_bool_value(MonoObject *p_object) { + return (bool)GDMonoMarshal::unbox(get_value(p_object)); +} + +int GDMonoProperty::get_int_value(MonoObject *p_object) { + return GDMonoMarshal::unbox(get_value(p_object)); +} + +String GDMonoProperty::get_string_value(MonoObject *p_object) { + MonoObject *val = get_value(p_object); + return GDMonoMarshal::mono_string_to_godot((MonoString *)val); +} diff --git a/modules/mono/mono_gd/gd_mono_property.h b/modules/mono/mono_gd/gd_mono_property.h new file mode 100644 index 00000000000..2a0065e8502 --- /dev/null +++ b/modules/mono/mono_gd/gd_mono_property.h @@ -0,0 +1,77 @@ +/*************************************************************************/ +/* gd_mono_property.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ +#ifndef GD_MONO_PROPERTY_H +#define GD_MONO_PROPERTY_H + +#include "gd_mono.h" +#include "gd_mono_class_member.h" +#include "gd_mono_header.h" + +class GDMonoProperty : public GDMonoClassMember { + + GDMonoClass *owner; + MonoProperty *mono_property; + + StringName name; + ManagedType type; + + bool attrs_fetched; + MonoCustomAttrInfo *attributes; + +public: + virtual MemberType get_member_type() { return MEMBER_TYPE_PROPERTY; } + + virtual StringName get_name() { return name; } + + virtual bool is_static(); + virtual Visibility get_visibility(); + + virtual bool has_attribute(GDMonoClass *p_attr_class); + virtual MonoObject *get_attribute(GDMonoClass *p_attr_class); + void fetch_attributes(); + + bool has_getter(); + bool has_setter(); + + _FORCE_INLINE_ ManagedType get_type() const { return type; } + + void set_value(MonoObject *p_object, MonoObject *p_value, MonoObject **r_exc = NULL); + void set_value(MonoObject *p_object, void **p_params, MonoObject **r_exc = NULL); + MonoObject *get_value(MonoObject *p_object, MonoObject **r_exc = NULL); + + bool get_bool_value(MonoObject *p_object); + int get_int_value(MonoObject *p_object); + String get_string_value(MonoObject *p_object); + + GDMonoProperty(MonoProperty *p_mono_property, GDMonoClass *p_owner); + ~GDMonoProperty(); +}; + +#endif // GD_MONO_PROPERTY_H diff --git a/modules/mono/mono_gd/gd_mono_utils.cpp b/modules/mono/mono_gd/gd_mono_utils.cpp index 03f3053372f..276a50c3484 100644 --- a/modules/mono/mono_gd/gd_mono_utils.cpp +++ b/modules/mono/mono_gd/gd_mono_utils.cpp @@ -198,7 +198,7 @@ void update_godot_api_cache() { CACHE_RAW_MONO_CLASS_AND_CHECK(Dictionary, mono_class_from_mono_type(dict_type)); } - MonoObject *task_scheduler = mono_object_new(SCRIPTS_DOMAIN, GODOT_API_CLASS(GodotTaskScheduler)->get_raw()); + MonoObject *task_scheduler = mono_object_new(SCRIPTS_DOMAIN, GODOT_API_CLASS(GodotTaskScheduler)->get_mono_ptr()); mono_runtime_object_init(task_scheduler); mono_cache.task_scheduler_handle = MonoGCHandle::create_strong(task_scheduler); } @@ -298,7 +298,7 @@ MonoObject *create_managed_for_godot_object(GDMonoClass *p_class, const StringNa ERR_FAIL_V(NULL); } - MonoObject *mono_object = mono_object_new(SCRIPTS_DOMAIN, p_class->get_raw()); + MonoObject *mono_object = mono_object_new(SCRIPTS_DOMAIN, p_class->get_mono_ptr()); ERR_FAIL_NULL_V(mono_object, NULL); CACHED_FIELD(GodotObject, ptr)->set_value_raw(mono_object, p_object); @@ -364,4 +364,10 @@ String get_exception_name_and_message(MonoObject *p_ex) { return res; } + +void print_unhandled_exception(MonoObject *p_ex) { + ERR_PRINT(GDMonoUtils::get_exception_name_and_message(p_ex).utf8()); + mono_print_unhandled_exception(p_ex); +} + } // namespace GDMonoUtils diff --git a/modules/mono/mono_gd/gd_mono_utils.h b/modules/mono/mono_gd/gd_mono_utils.h index 62d31f78fb7..3135f728eea 100644 --- a/modules/mono/mono_gd/gd_mono_utils.h +++ b/modules/mono/mono_gd/gd_mono_utils.h @@ -166,12 +166,14 @@ MonoDomain *create_domain(const String &p_friendly_name); String get_exception_name_and_message(MonoObject *p_ex); +void print_unhandled_exception(MonoObject *p_ex); + } // namespace GDMonoUtils #define NATIVE_GDMONOCLASS_NAME(m_class) (GDMonoMarshal::mono_string_to_godot((MonoString *)m_class->get_field(BINDINGS_NATIVE_NAME_FIELD)->get_value(NULL))) #define CACHED_CLASS(m_class) (GDMonoUtils::mono_cache.class_##m_class) -#define CACHED_CLASS_RAW(m_class) (GDMonoUtils::mono_cache.class_##m_class->get_raw()) +#define CACHED_CLASS_RAW(m_class) (GDMonoUtils::mono_cache.class_##m_class->get_mono_ptr()) #define CACHED_NS_CLASS(m_ns, m_class) (GDMonoUtils::mono_cache.class_##m_ns##_##m_class) #define CACHED_RAW_MONO_CLASS(m_class) (GDMonoUtils::mono_cache.rawclass_##m_class) #define CACHED_FIELD(m_class, m_field) (GDMonoUtils::mono_cache.field_##m_class##_##m_field)