Browse Source

Mono: Add properties support in scripts

Ignacio Etcheverry 7 years ago
parent
commit
119a910bc6

+ 167 - 84
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();
-
-			ERR_EXPLAIN("Reference has been garbage collected?");
-			ERR_FAIL_NULL_V(mono_object, false);
+			field->set_value_from_variant(mono_object, p_value);
+			return true;
+		}
 
-			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<MonoBoolean>(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<GDMonoField *> &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;
-				}
-
-				String name = field->get_name();
-				StringName cname = name;
+				if (_get_member_export(top, field, prop_info, exported)) {
+					StringName name = field->get_name();
 
-				if (member_info.has(cname))
-					continue;
+					if (exported) {
+						member_info[name] = prop_info;
+						exported_members_cache.push_front(prop_info);
 
-				ManagedType field_type = field->get_type();
-				Variant::Type type = GDMonoMarshal::managed_to_variant_type(field_type);
+						if (tmp_object) {
+							exported_members_defval_cache[name] = GDMonoMarshal::mono_object_to_variant(field->get_value(tmp_object));
+						}
+					} else {
+						member_info[name] = prop_info;
+					}
+				}
+			}
 
-				if (field->has_attribute(CACHED_CLASS(ExportAttribute))) {
-					// Field has Export attribute
-					MonoObject *attr = field->get_attribute(CACHED_CLASS(ExportAttribute));
+			const Vector<GDMonoProperty *> &properties = top->get_all_properties();
 
-					PropertyHint hint;
-					String hint_string;
+			for (int i = properties.size() - 1; i >= 0; i--) {
+				GDMonoProperty *property = properties[i];
 
-					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;
+				if (_get_member_export(top, property, prop_info, exported)) {
+					StringName name = property->get_name();
 
-						Vector<MonoClassField *> fields = field_type.type_class->get_enum_fields();
+					if (exported) {
+						member_info[name] = prop_info;
+						exported_members_cache.push_front(prop_info);
 
-						for (int i = 0; i < fields.size(); i++) {
-							if (i > 0)
-								hint_string += ",";
-							hint_string += mono_field_get_name(fields[i]);
+						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 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);
-					}
-
-					PropertyInfo prop_info = PropertyInfo(type, name, hint, hint_string, PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SCRIPT_VARIABLE);
-
-					member_info[cname] = prop_info;
-					exported_members_cache.push_front(prop_info);
-
-					if (tmp_object) {
-						exported_members_defval_cache[cname] = GDMonoMarshal::mono_object_to_variant(field->get_value(tmp_object));
+						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<GDMonoField *>(p_member)->get_type();
+	} else if (p_member->get_member_type() == GDMonoClassMember::MEMBER_TYPE_PROPERTY) {
+		type = static_cast<GDMonoProperty *>(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<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());
+				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<MonoClassField *> 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<CSharpScript>();

+ 4 - 0
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;
 

+ 1 - 1
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
 

+ 1 - 1
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;

+ 1 - 2
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();
 }
 

+ 0 - 8
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

+ 1 - 1
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;

+ 60 - 7
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<MonoClassField *> 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<GDMonoField *> &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<StringName, GDMonoField *>::Element *match = fields.find(name);
@@ -353,6 +353,54 @@ const Vector<GDMonoField *> &GDMonoClass::get_all_fields() {
 	return fields_list;
 }
 
+GDMonoProperty *GDMonoClass::get_property(const StringName &p_name) {
+
+	Map<StringName, GDMonoProperty *>::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<GDMonoProperty *> &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<StringName, GDMonoProperty *>::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<StringName, GDMonoProperty *>::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).

+ 9 - 1
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<StringName, GDMonoField *> fields;
 	Vector<GDMonoField *> fields_list;
 
+	bool properties_fetched;
+	Map<StringName, GDMonoProperty *> properties;
+	Vector<GDMonoProperty *> 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<GDMonoField *> &get_all_fields();
 
+	GDMonoProperty *get_property(const StringName &p_name);
+	const Vector<GDMonoProperty *> &get_all_properties();
+
 	~GDMonoClass();
 };
 

+ 67 - 0
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 <mono/metadata/object.h>
+
+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

+ 19 - 15
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<MonoBoolean>(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);

+ 18 - 17
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; }
-	_FORCE_INLINE_ ManagedType get_type() const { return type; }
+	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_ MonoClassField *get_raw() const { return mono_field; }
+	_FORCE_INLINE_ ManagedType get_type() const { return type; }
 
 	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();
 };
 

+ 3 - 1
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;

+ 7 - 7
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<int32_t>(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;

+ 32 - 13
modules/mono/mono_gd/gd_mono_method.cpp

@@ -32,6 +32,8 @@
 #include "gd_mono_class.h"
 #include "gd_mono_marshal.h"
 
+#include <mono/metadata/attrdefs.h>
+
 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() {

+ 13 - 8
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<ManagedType> 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;

+ 199 - 0
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 <mono/metadata/attrdefs.h>
+
+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<MonoBoolean>(get_value(p_object));
+}
+
+int GDMonoProperty::get_int_value(MonoObject *p_object) {
+	return GDMonoMarshal::unbox<int32_t>(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);
+}

+ 77 - 0
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

+ 8 - 2
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

+ 3 - 1
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)