Browse Source

Merge pull request #37050 from neikeq/fix-mono-after-vulkan-merge

Fix C# bindings after recent breaking changes
Ignacio Roldán Etcheverry 5 years ago
parent
commit
989a223c5a
51 changed files with 2422 additions and 830 deletions
  1. 6 0
      core/callable.cpp
  2. 1 0
      core/callable.h
  3. 1 1
      core/type_info.h
  4. 0 7
      modules/mono/SCsub
  5. 0 36
      modules/mono/build_scripts/tls_configure.py
  6. 328 94
      modules/mono/csharp_script.cpp
  7. 63 36
      modules/mono/csharp_script.h
  8. 6 6
      modules/mono/editor/GodotTools/GodotTools/BottomPanel.cs
  9. 1 1
      modules/mono/editor/GodotTools/GodotTools/BuildTab.cs
  10. 11 13
      modules/mono/editor/GodotTools/GodotTools/GodotSharpEditor.cs
  11. 1 1
      modules/mono/editor/GodotTools/GodotTools/HotReloadAssemblyWatcher.cs
  12. 296 34
      modules/mono/editor/bindings_generator.cpp
  13. 48 1
      modules/mono/editor/bindings_generator.h
  14. 1 1
      modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/SignalAttribute.cs
  15. 31 0
      modules/mono/glue/GodotSharp/GodotSharp/Core/Callable.cs
  16. 395 0
      modules/mono/glue/GodotSharp/GodotSharp/Core/DelegateUtils.cs
  17. 23 14
      modules/mono/glue/GodotSharp/GodotSharp/Core/GD.cs
  18. 24 35
      modules/mono/glue/GodotSharp/GodotSharp/Core/NodePath.cs
  19. 22 7
      modules/mono/glue/GodotSharp/GodotSharp/Core/Object.base.cs
  20. 3 9
      modules/mono/glue/GodotSharp/GodotSharp/Core/SignalAwaiter.cs
  21. 17 0
      modules/mono/glue/GodotSharp/GodotSharp/Core/SignalInfo.cs
  22. 82 0
      modules/mono/glue/GodotSharp/GodotSharp/Core/StringName.cs
  23. 4 0
      modules/mono/glue/GodotSharp/GodotSharp/GodotSharp.csproj
  24. 18 11
      modules/mono/glue/base_object_glue.cpp
  25. 5 3
      modules/mono/glue/base_object_glue.h
  26. 8 2
      modules/mono/glue/gd_glue.cpp
  27. 1 1
      modules/mono/glue/gd_glue.h
  28. 2 0
      modules/mono/glue/glue_header.h
  29. 21 67
      modules/mono/glue/string_name_glue.cpp
  30. 54 0
      modules/mono/glue/string_name_glue.h
  31. 143 0
      modules/mono/managed_callable.cpp
  32. 79 0
      modules/mono/managed_callable.h
  33. 2 0
      modules/mono/mono_gc_handle.h
  34. 1 1
      modules/mono/mono_gd/gd_mono.h
  35. 1 1
      modules/mono/mono_gd/gd_mono_assembly.cpp
  36. 18 2
      modules/mono/mono_gd/gd_mono_cache.cpp
  37. 9 7
      modules/mono/mono_gd/gd_mono_cache.h
  38. 52 3
      modules/mono/mono_gd/gd_mono_field.cpp
  39. 8 7
      modules/mono/mono_gd/gd_mono_field.h
  40. 4 3
      modules/mono/mono_gd/gd_mono_internals.cpp
  41. 297 71
      modules/mono/mono_gd/gd_mono_marshal.cpp
  42. 37 3
      modules/mono/mono_gd/gd_mono_marshal.h
  43. 32 32
      modules/mono/mono_gd/gd_mono_method.cpp
  44. 13 13
      modules/mono/mono_gd/gd_mono_method.h
  45. 7 7
      modules/mono/mono_gd/gd_mono_property.h
  46. 30 4
      modules/mono/mono_gd/gd_mono_utils.cpp
  47. 7 3
      modules/mono/mono_gd/gd_mono_utils.h
  48. 142 63
      modules/mono/signal_awaiter_utils.cpp
  49. 46 21
      modules/mono/signal_awaiter_utils.h
  50. 21 32
      modules/mono/utils/macros.h
  51. 0 177
      modules/mono/utils/thread_local.h

+ 6 - 0
core/callable.cpp

@@ -78,6 +78,12 @@ StringName Callable::get_method() const {
 	return method;
 }
 
+CallableCustom *Callable::get_custom() const {
+	ERR_FAIL_COND_V_MSG(!is_custom(), NULL,
+			vformat("Can't get custom on non-CallableCustom \"%s\".", operator String()));
+	return custom;
+}
+
 uint32_t Callable::hash() const {
 	if (is_custom()) {
 		return custom->hash();

+ 1 - 0
core/callable.h

@@ -84,6 +84,7 @@ public:
 	Object *get_object() const;
 	ObjectID get_object_id() const;
 	StringName get_method() const;
+	CallableCustom *get_custom() const;
 
 	uint32_t hash() const;
 

+ 1 - 1
core/type_info.h

@@ -174,7 +174,7 @@ MAKE_TYPE_INFO(IP_Address, Variant::STRING)
 template <>
 struct GetTypeInfo<ObjectID> {
 	static const Variant::Type VARIANT_TYPE = Variant::INT;
-	static const GodotTypeInfo::Metadata METADATA = GodotTypeInfo::METADATA_NONE;
+	static const GodotTypeInfo::Metadata METADATA = GodotTypeInfo::METADATA_INT_IS_UINT64;
 	static inline PropertyInfo get_class_info() {
 		return PropertyInfo(Variant::INT, String(), PROPERTY_HINT_INT_IS_OBJECTID);
 	}

+ 0 - 7
modules/mono/SCsub

@@ -1,6 +1,5 @@
 #!/usr/bin/env python
 
-import build_scripts.tls_configure as tls_configure
 import build_scripts.mono_configure as mono_configure
 
 Import('env')
@@ -24,12 +23,6 @@ if env_mono['mono_glue']:
 if env_mono['tools'] or env_mono['target'] != 'release':
     env_mono.Append(CPPDEFINES=['GD_MONO_HOT_RELOAD'])
 
-# Configure Thread Local Storage
-
-conf = Configure(env_mono)
-tls_configure.configure(conf)
-env_mono = conf.Finish()
-
 # Configure Mono
 
 mono_configure.configure(env, env_mono)

+ 0 - 36
modules/mono/build_scripts/tls_configure.py

@@ -1,36 +0,0 @@
-from __future__ import print_function
-
-def supported(result):
-    return 'supported' if result else 'not supported'
-
-
-def check_cxx11_thread_local(conf):
-    print('Checking for `thread_local` support...', end=" ")
-    result = conf.TryCompile('thread_local int foo = 0; int main() { return foo; }', '.cpp')
-    print(supported(result))
-    return bool(result)
-
-
-def check_declspec_thread(conf):
-    print('Checking for `__declspec(thread)` support...', end=" ")
-    result = conf.TryCompile('__declspec(thread) int foo = 0; int main() { return foo; }', '.cpp')
-    print(supported(result))
-    return bool(result)
-
-
-def check_gcc___thread(conf):
-    print('Checking for `__thread` support...', end=" ")
-    result = conf.TryCompile('__thread int foo = 0; int main() { return foo; }', '.cpp')
-    print(supported(result))
-    return bool(result)
-
-
-def configure(conf):
-    if check_cxx11_thread_local(conf):
-        conf.env.Append(CPPDEFINES=['HAVE_CXX11_THREAD_LOCAL'])
-    else:
-        if conf.env.msvc:
-            if check_declspec_thread(conf):
-                conf.env.Append(CPPDEFINES=['HAVE_DECLSPEC_THREAD'])
-        elif check_gcc___thread(conf):
-            conf.env.Append(CPPDEFINES=['HAVE_GCC___THREAD'])

+ 328 - 94
modules/mono/csharp_script.cpp

@@ -62,7 +62,6 @@
 #include "signal_awaiter_utils.h"
 #include "utils/macros.h"
 #include "utils/string_utils.h"
-#include "utils/thread_local.h"
 
 #define CACHED_STRING_NAME(m_var) (CSharpLanguage::get_singleton()->get_string_names().m_var)
 
@@ -75,7 +74,7 @@ static bool _create_project_solution_if_needed() {
 	if (!FileAccess::exists(sln_path) || !FileAccess::exists(csproj_path)) {
 		// A solution does not yet exist, create a new one
 
-		CRASH_COND(CSharpLanguage::get_singleton()->get_godotsharp_editor() == NULL);
+		CRASH_COND(CSharpLanguage::get_singleton()->get_godotsharp_editor() == nullptr);
 		return CSharpLanguage::get_singleton()->get_godotsharp_editor()->call("CreateProjectSolution");
 	}
 
@@ -83,7 +82,7 @@ static bool _create_project_solution_if_needed() {
 }
 #endif
 
-CSharpLanguage *CSharpLanguage::singleton = NULL;
+CSharpLanguage *CSharpLanguage::singleton = nullptr;
 
 String CSharpLanguage::get_name() const {
 
@@ -142,6 +141,9 @@ void CSharpLanguage::init() {
 
 void CSharpLanguage::finish() {
 
+	if (finalized)
+		return;
+
 	finalizing = true;
 
 	// Make sure all script binding gchandles are released before finalizing GDMono
@@ -175,7 +177,10 @@ void CSharpLanguage::finish() {
 	}
 #endif
 
+	memdelete(managed_callable_middleman);
+
 	finalizing = false;
+	finalized = true;
 }
 
 void CSharpLanguage::get_reserved_words(List<String> *p_words) const {
@@ -434,13 +439,12 @@ static String variant_type_to_managed_name(const String &p_var_type_name) {
 		return "byte[]";
 	if (p_var_type_name == Variant::get_type_name(Variant::PACKED_INT32_ARRAY))
 		return "int[]";
-	if (p_var_type_name == Variant::get_type_name(Variant::PACKED_FLOAT32_ARRAY)) {
-#ifdef REAL_T_IS_DOUBLE
-		return "double[]";
-#else
+	if (p_var_type_name == Variant::get_type_name(Variant::PACKED_INT64_ARRAY))
+		return "long[]";
+	if (p_var_type_name == Variant::get_type_name(Variant::PACKED_FLOAT32_ARRAY))
 		return "float[]";
-#endif
-	}
+	if (p_var_type_name == Variant::get_type_name(Variant::PACKED_FLOAT64_ARRAY))
+		return "double[]";
 	if (p_var_type_name == Variant::get_type_name(Variant::PACKED_STRING_ARRAY))
 		return "string[]";
 	if (p_var_type_name == Variant::get_type_name(Variant::PACKED_VECTOR2_ARRAY))
@@ -450,6 +454,9 @@ static String variant_type_to_managed_name(const String &p_var_type_name) {
 	if (p_var_type_name == Variant::get_type_name(Variant::PACKED_COLOR_ARRAY))
 		return "Color[]";
 
+	if (p_var_type_name == Variant::get_type_name(Variant::SIGNAL))
+		return "SignalInfo";
+
 	Variant::Type var_types[] = {
 		Variant::BOOL,
 		Variant::INT,
@@ -463,8 +470,10 @@ static String variant_type_to_managed_name(const String &p_var_type_name) {
 		Variant::BASIS,
 		Variant::TRANSFORM,
 		Variant::COLOR,
+		Variant::STRING_NAME,
 		Variant::NODE_PATH,
-		Variant::_RID
+		Variant::_RID,
+		Variant::CALLABLE
 	};
 
 	for (unsigned int i = 0; i < sizeof(var_types) / sizeof(Variant::Type); i++) {
@@ -561,7 +570,13 @@ String CSharpLanguage::debug_get_stack_level_source(int p_level) const {
 Vector<ScriptLanguage::StackInfo> CSharpLanguage::debug_get_current_stack_info() {
 
 #ifdef DEBUG_ENABLED
-	_TLS_RECURSION_GUARD_V_(Vector<StackInfo>());
+	// Printing an error here will result in endless recursion, so we must be careful
+	static thread_local bool _recursion_flag_ = false;
+	if (_recursion_flag_)
+		return Vector<StackInfo>();
+	_recursion_flag_ = true;
+	SCOPE_EXIT { _recursion_flag_ = false; };
+
 	GD_MONO_SCOPE_THREAD_ATTACH;
 
 	if (!gdmono->is_runtime_initialized() || !GDMono::get_singleton()->get_core_api_assembly() || !GDMonoCache::cached_data.corlib_cache_updated)
@@ -586,7 +601,13 @@ Vector<ScriptLanguage::StackInfo> CSharpLanguage::debug_get_current_stack_info()
 #ifdef DEBUG_ENABLED
 Vector<ScriptLanguage::StackInfo> CSharpLanguage::stack_trace_get_info(MonoObject *p_stack_trace) {
 
-	_TLS_RECURSION_GUARD_V_(Vector<StackInfo>());
+	// Printing an error here will result in endless recursion, so we must be careful
+	static thread_local bool _recursion_flag_ = false;
+	if (_recursion_flag_)
+		return Vector<StackInfo>();
+	_recursion_flag_ = true;
+	SCOPE_EXIT { _recursion_flag_ = false; };
+
 	GD_MONO_SCOPE_THREAD_ATTACH;
 
 	MonoException *exc = NULL;
@@ -774,6 +795,36 @@ void CSharpLanguage::reload_assemblies(bool p_soft_reload) {
 		}
 	}
 
+	scripts.sort_custom<CSharpScriptDepSort>(); // Update in inheritance dependency order
+
+	// Serialize managed callables
+	{
+		MutexLock lock(ManagedCallable::instances_mutex);
+
+		for (SelfList<ManagedCallable> *elem = ManagedCallable::instances.first(); elem; elem = elem->next()) {
+			ManagedCallable *managed_callable = elem->self();
+
+			MonoDelegate *delegate = (MonoDelegate *)managed_callable->delegate_handle->get_target();
+
+			Array serialized_data;
+			MonoObject *managed_serialized_data = GDMonoMarshal::variant_to_mono_object(serialized_data);
+
+			MonoException *exc = NULL;
+			bool success = (bool)CACHED_METHOD_THUNK(DelegateUtils, TrySerializeDelegate).invoke(delegate, managed_serialized_data, &exc);
+
+			if (exc) {
+				GDMonoUtils::debug_print_unhandled_exception(exc);
+				continue;
+			}
+
+			if (success) {
+				ManagedCallable::instances_pending_reload.insert(managed_callable, serialized_data);
+			} else if (OS::get_singleton()->is_stdout_verbose()) {
+				OS::get_singleton()->print("Failed to serialize delegate\n");
+			}
+		}
+	}
+
 	List<Ref<CSharpScript>> to_reload;
 
 	// We need to keep reference instances alive during reloading
@@ -789,8 +840,6 @@ void CSharpLanguage::reload_assemblies(bool p_soft_reload) {
 
 	// As scripts are going to be reloaded, must proceed without locking here
 
-	scripts.sort_custom<CSharpScriptDepSort>(); // Update in inheritance dependency order
-
 	for (List<Ref<CSharpScript>>::Element *E = scripts.front(); E; E = E->next()) {
 		Ref<CSharpScript> &script = E->get();
 
@@ -845,6 +894,7 @@ void CSharpLanguage::reload_assemblies(bool p_soft_reload) {
 
 			// TODO: Proper state backup (Not only variants, serialize managed state of scripts)
 			csi->get_properties_state_for_reloading(state.properties);
+			csi->get_event_signals_state_for_reloading(state.event_signals);
 
 			owners_map[obj->get_instance_id()] = state;
 		}
@@ -957,7 +1007,7 @@ void CSharpLanguage::reload_assemblies(bool p_soft_reload) {
 			CSharpScript::initialize_for_managed_type(script, script_class, native);
 		}
 
-		String native_name = NATIVE_GDMONOCLASS_NAME(script->native);
+		StringName native_name = NATIVE_GDMONOCLASS_NAME(script->native);
 
 		{
 			for (Set<ObjectID>::Element *F = script->pending_reload_instances.front(); F; F = F->next()) {
@@ -1034,15 +1084,80 @@ void CSharpLanguage::reload_assemblies(bool p_soft_reload) {
 				obj->get_script_instance()->set(G->get().first, G->get().second);
 			}
 
-			// Call OnAfterDeserialization
 			CSharpInstance *csi = CAST_CSHARP_INSTANCE(obj->get_script_instance());
-			if (csi && csi->script->script_class->implements_interface(CACHED_CLASS(ISerializationListener)))
-				obj->get_script_instance()->call_multilevel(string_names.on_after_deserialize);
+
+			if (csi) {
+				for (List<Pair<StringName, Array>>::Element *G = state_backup.event_signals.front(); G; G = G->next()) {
+					const StringName &name = G->get().first;
+					const Array &serialized_data = G->get().second;
+
+					Map<StringName, CSharpScript::EventSignal>::Element *match = script->event_signals.find(name);
+
+					if (!match) {
+						// The event or its signal attribute were removed
+						continue;
+					}
+
+					const CSharpScript::EventSignal &event_signal = match->value();
+
+					MonoObject *managed_serialized_data = GDMonoMarshal::variant_to_mono_object(serialized_data);
+					MonoDelegate *delegate = NULL;
+
+					MonoException *exc = NULL;
+					bool success = (bool)CACHED_METHOD_THUNK(DelegateUtils, TryDeserializeDelegate).invoke(managed_serialized_data, &delegate, &exc);
+
+					if (exc) {
+						GDMonoUtils::debug_print_unhandled_exception(exc);
+						continue;
+					}
+
+					if (success) {
+						ERR_CONTINUE(delegate == NULL);
+						event_signal.field->set_value(csi->get_mono_object(), (MonoObject *)delegate);
+					} else if (OS::get_singleton()->is_stdout_verbose()) {
+						OS::get_singleton()->print("Failed to deserialize event signal delegate\n");
+					}
+				}
+
+				// Call OnAfterDeserialization
+				if (csi->script->script_class->implements_interface(CACHED_CLASS(ISerializationListener)))
+					obj->get_script_instance()->call_multilevel(string_names.on_after_deserialize);
+			}
 		}
 
 		script->pending_reload_instances.clear();
 	}
 
+	// Deserialize managed callables
+	{
+		MutexLock lock(ManagedCallable::instances_mutex);
+
+		for (Map<ManagedCallable *, Array>::Element *elem = ManagedCallable::instances_pending_reload.front(); elem; elem = elem->next()) {
+			ManagedCallable *managed_callable = elem->key();
+			const Array &serialized_data = elem->value();
+
+			MonoObject *managed_serialized_data = GDMonoMarshal::variant_to_mono_object(serialized_data);
+			MonoDelegate *delegate = NULL;
+
+			MonoException *exc = NULL;
+			bool success = (bool)CACHED_METHOD_THUNK(DelegateUtils, TryDeserializeDelegate).invoke(managed_serialized_data, &delegate, &exc);
+
+			if (exc) {
+				GDMonoUtils::debug_print_unhandled_exception(exc);
+				continue;
+			}
+
+			if (success) {
+				ERR_CONTINUE(delegate == NULL);
+				managed_callable->set_delegate(delegate);
+			} else if (OS::get_singleton()->is_stdout_verbose()) {
+				OS::get_singleton()->print("Failed to deserialize delegate\n");
+			}
+		}
+
+		ManagedCallable::instances_pending_reload.clear();
+	}
+
 #ifdef TOOLS_ENABLED
 	// FIXME: Hack to refresh editor in order to display new properties and signals. See if there is a better alternative.
 	if (Engine::get_singleton()->is_editor_hint()) {
@@ -1166,6 +1281,16 @@ void CSharpLanguage::_on_scripts_domain_unloaded() {
 		script_binding.inited = false;
 	}
 
+	{
+		MutexLock lock(ManagedCallable::instances_mutex);
+
+		for (SelfList<ManagedCallable> *elem = ManagedCallable::instances.first(); elem; elem = elem->next()) {
+			ManagedCallable *managed_callable = elem->self();
+			managed_callable->delegate_handle.unref();
+			managed_callable->delegate_invoke = NULL;
+		}
+	}
+
 	scripts_metadata_invalidated = true;
 }
 
@@ -1236,24 +1361,12 @@ CSharpLanguage::CSharpLanguage() {
 
 	ERR_FAIL_COND_MSG(singleton, "C# singleton already exist.");
 	singleton = this;
-
-	finalizing = false;
-
-	gdmono = NULL;
-
-	lang_idx = -1;
-
-	scripts_metadata_invalidated = true;
-
-#ifdef TOOLS_ENABLED
-	godotsharp_editor = NULL;
-#endif
 }
 
 CSharpLanguage::~CSharpLanguage() {
 
 	finish();
-	singleton = NULL;
+	singleton = nullptr;
 }
 
 bool CSharpLanguage::setup_csharp_script_binding(CSharpScriptBinding &r_script_binding, Object *p_object) {
@@ -1440,12 +1553,11 @@ bool CSharpLanguage::refcount_decremented_instance_binding(Object *p_object) {
 
 CSharpInstance *CSharpInstance::create_for_managed_type(Object *p_owner, CSharpScript *p_script, const Ref<MonoGCHandle> &p_gchandle) {
 
-	CSharpInstance *instance = memnew(CSharpInstance);
+	CSharpInstance *instance = memnew(CSharpInstance(Ref<CSharpScript>(p_script)));
 
 	Reference *ref = Object::cast_to<Reference>(p_owner);
 
 	instance->base_ref = ref != NULL;
-	instance->script = Ref<CSharpScript>(p_script);
 	instance->owner = p_owner;
 	instance->gchandle = p_gchandle;
 
@@ -1610,6 +1722,37 @@ void CSharpInstance::get_properties_state_for_reloading(List<Pair<StringName, Va
 	}
 }
 
+void CSharpInstance::get_event_signals_state_for_reloading(List<Pair<StringName, Array>> &r_state) {
+
+	MonoObject *owner_managed = get_mono_object();
+	ERR_FAIL_NULL(owner_managed);
+
+	for (const Map<StringName, CSharpScript::EventSignal>::Element *E = script->event_signals.front(); E; E = E->next()) {
+		const CSharpScript::EventSignal &event_signal = E->value();
+
+		MonoDelegate *delegate_field_value = (MonoDelegate *)event_signal.field->get_value(owner_managed);
+		if (!delegate_field_value)
+			continue; // Empty
+
+		Array serialized_data;
+		MonoObject *managed_serialized_data = GDMonoMarshal::variant_to_mono_object(serialized_data);
+
+		MonoException *exc = NULL;
+		bool success = (bool)CACHED_METHOD_THUNK(DelegateUtils, TrySerializeDelegate).invoke(delegate_field_value, managed_serialized_data, &exc);
+
+		if (exc) {
+			GDMonoUtils::debug_print_unhandled_exception(exc);
+			continue;
+		}
+
+		if (success) {
+			r_state.push_back(Pair<StringName, Array>(event_signal.field->get_name(), serialized_data));
+		} else if (OS::get_singleton()->is_stdout_verbose()) {
+			OS::get_singleton()->print("Failed to serialize event signal delegate\n");
+		}
+	}
+}
+
 void CSharpInstance::get_property_list(List<PropertyInfo> *p_properties) const {
 
 	for (Map<StringName, PropertyInfo>::Element *E = script->member_info.front(); E; E = E->next()) {
@@ -1847,6 +1990,8 @@ MonoObject *CSharpInstance::_internal_new_managed() {
 
 void CSharpInstance::mono_object_disposed(MonoObject *p_obj) {
 
+	disconnect_event_signals();
+
 #ifdef DEBUG_ENABLED
 	CRASH_COND(base_ref);
 	CRASH_COND(gchandle.is_null());
@@ -1888,6 +2033,33 @@ void CSharpInstance::mono_object_disposed_baseref(MonoObject *p_obj, bool p_is_f
 	}
 }
 
+void CSharpInstance::connect_event_signals() {
+	for (const Map<StringName, CSharpScript::EventSignal>::Element *E = script->event_signals.front(); E; E = E->next()) {
+		const CSharpScript::EventSignal &event_signal = E->value();
+
+		StringName signal_name = event_signal.field->get_name();
+
+		// TODO: Use pooling for ManagedCallable instances.
+		auto event_signal_callable = memnew(EventSignalCallable(owner, &event_signal));
+
+		owner->connect(signal_name, Callable(event_signal_callable));
+	}
+}
+
+void CSharpInstance::disconnect_event_signals() {
+	for (const Map<StringName, CSharpScript::EventSignal>::Element *E = script->event_signals.front(); E; E = E->next()) {
+		const CSharpScript::EventSignal &event_signal = E->value();
+
+		StringName signal_name = event_signal.field->get_name();
+
+		// TODO: It would be great if we could store this EventSignalCallable on the stack.
+		// The problem is that Callable memdeletes it when it's destructed...
+		auto event_signal_callable = memnew(EventSignalCallable(owner, &event_signal));
+
+		owner->disconnect(signal_name, Callable(event_signal_callable));
+	}
+}
+
 void CSharpInstance::refcount_incremented() {
 
 #ifdef DEBUG_ENABLED
@@ -2087,13 +2259,8 @@ ScriptLanguage *CSharpInstance::get_language() {
 	return CSharpLanguage::get_singleton();
 }
 
-CSharpInstance::CSharpInstance() :
-		owner(NULL),
-		base_ref(false),
-		ref_dying(false),
-		unsafe_referenced(false),
-		predelete_notified(false),
-		destructing_script_instance(false) {
+CSharpInstance::CSharpInstance(const Ref<CSharpScript> &p_script) :
+		script(p_script) {
 }
 
 CSharpInstance::~CSharpInstance() {
@@ -2416,6 +2583,7 @@ void CSharpScript::load_script_signals(GDMonoClass *p_class, GDMonoClass *p_nati
 
 	// make sure this classes signals are empty when loading for the first time
 	_signals.clear();
+	event_signals.clear();
 
 	GD_MONO_SCOPE_THREAD_ATTACH;
 
@@ -2423,56 +2591,90 @@ void CSharpScript::load_script_signals(GDMonoClass *p_class, GDMonoClass *p_nati
 	while (top && top != p_native_class) {
 		const Vector<GDMonoClass *> &delegates = top->get_all_delegates();
 		for (int i = delegates.size() - 1; i >= 0; --i) {
-			Vector<Argument> parameters;
-
 			GDMonoClass *delegate = delegates[i];
 
-			if (_get_signal(top, delegate, parameters)) {
+			if (!delegate->has_attribute(CACHED_CLASS(SignalAttribute)))
+				continue;
+
+			// Arguments are accessibles as arguments of .Invoke method
+			GDMonoMethod *invoke_method = delegate->get_method(mono_get_delegate_invoke(delegate->get_mono_ptr()));
+
+			Vector<SignalParameter> parameters;
+			if (_get_signal(top, invoke_method, parameters)) {
 				_signals[delegate->get_name()] = parameters;
 			}
 		}
 
+		List<StringName> found_event_signals;
+
+		void *iter = NULL;
+		MonoEvent *raw_event = NULL;
+		while ((raw_event = mono_class_get_events(top->get_mono_ptr(), &iter)) != NULL) {
+			MonoCustomAttrInfo *event_attrs = mono_custom_attrs_from_event(top->get_mono_ptr(), raw_event);
+			if (event_attrs) {
+				if (mono_custom_attrs_has_attr(event_attrs, CACHED_CLASS(SignalAttribute)->get_mono_ptr())) {
+					const char *event_name = mono_event_get_name(raw_event);
+					found_event_signals.push_back(StringName(event_name));
+				}
+
+				mono_custom_attrs_free(event_attrs);
+			}
+		}
+
+		const Vector<GDMonoField *> &fields = top->get_all_fields();
+		for (int i = 0; i < fields.size(); i++) {
+			GDMonoField *field = fields[i];
+
+			GDMonoClass *field_class = field->get_type().type_class;
+
+			if (!mono_class_is_delegate(field_class->get_mono_ptr()))
+				continue;
+
+			if (!found_event_signals.find(field->get_name()))
+				continue;
+
+			GDMonoMethod *invoke_method = field_class->get_method(mono_get_delegate_invoke(field_class->get_mono_ptr()));
+
+			Vector<SignalParameter> parameters;
+			if (_get_signal(top, invoke_method, parameters)) {
+				event_signals[field->get_name()] = { field, invoke_method, parameters };
+			}
+		}
+
 		top = top->get_parent_class();
 	}
 
 	signals_invalidated = false;
 }
 
-bool CSharpScript::_get_signal(GDMonoClass *p_class, GDMonoClass *p_delegate, Vector<Argument> &params) {
+bool CSharpScript::_get_signal(GDMonoClass *p_class, GDMonoMethod *p_delegate_invoke, Vector<SignalParameter> &params) {
 	GD_MONO_ASSERT_THREAD_ATTACHED;
 
-	if (p_delegate->has_attribute(CACHED_CLASS(SignalAttribute))) {
-		MonoType *raw_type = p_delegate->get_mono_type();
+	Vector<StringName> names;
+	Vector<ManagedType> types;
+	p_delegate_invoke->get_parameter_names(names);
+	p_delegate_invoke->get_parameter_types(types);
 
-		if (mono_type_get_type(raw_type) == MONO_TYPE_CLASS) {
-			// Arguments are accessibles as arguments of .Invoke method
-			GDMonoMethod *invoke = p_delegate->get_method("Invoke", -1);
-
-			Vector<StringName> names;
-			Vector<ManagedType> types;
-			invoke->get_parameter_names(names);
-			invoke->get_parameter_types(types);
-
-			if (names.size() == types.size()) {
-				for (int i = 0; i < names.size(); ++i) {
-					Argument arg;
-					arg.name = names[i];
-					arg.type = GDMonoMarshal::managed_to_variant_type(types[i]);
-
-					if (arg.type == Variant::NIL) {
-						ERR_PRINT("Unknown type of signal parameter: '" + arg.name + "' in '" + p_class->get_full_name() + "'.");
-						return false;
-					}
+	for (int i = 0; i < names.size(); ++i) {
+		SignalParameter arg;
+		arg.name = names[i];
 
-					params.push_back(arg);
-				}
+		bool nil_is_variant = false;
+		arg.type = GDMonoMarshal::managed_to_variant_type(types[i], &nil_is_variant);
 
-				return true;
+		if (arg.type == Variant::NIL) {
+			if (nil_is_variant) {
+				arg.nil_is_variant = true;
+			} else {
+				ERR_PRINT("Unknown type of signal parameter: '" + arg.name + "' in '" + p_class->get_full_name() + "'.");
+				return false;
 			}
 		}
+
+		params.push_back(arg);
 	}
 
-	return false;
+	return true;
 }
 
 #ifdef TOOLS_ENABLED
@@ -2523,7 +2725,8 @@ bool CSharpScript::_get_member_export(IMonoClassMember *p_member, bool p_inspect
 		}
 	}
 
-	Variant::Type variant_type = GDMonoMarshal::managed_to_variant_type(type);
+	bool nil_is_variant = false;
+	Variant::Type variant_type = GDMonoMarshal::managed_to_variant_type(type, &nil_is_variant);
 
 	if (!p_inspect_export || !exported) {
 		r_prop_info = PropertyInfo(variant_type, (String)p_member->get_name(), PROPERTY_HINT_NONE, "", PROPERTY_USAGE_SCRIPT_VARIABLE);
@@ -2536,7 +2739,7 @@ bool CSharpScript::_get_member_export(IMonoClassMember *p_member, bool p_inspect
 	PropertyHint hint = PROPERTY_HINT_NONE;
 	String hint_string;
 
-	if (variant_type == Variant::NIL) {
+	if (variant_type == Variant::NIL && !nil_is_variant) {
 		ERR_PRINT("Unknown exported member type: '" + MEMBER_FULL_QUALIFIED_NAME(p_member) + "'.");
 		return false;
 	}
@@ -2552,7 +2755,14 @@ bool CSharpScript::_get_member_export(IMonoClassMember *p_member, bool p_inspect
 		hint_string = CACHED_FIELD(ExportAttribute, hintString)->get_string_value(attr);
 	}
 
-	r_prop_info = PropertyInfo(variant_type, (String)p_member->get_name(), hint, hint_string, PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SCRIPT_VARIABLE);
+	uint32_t prop_usage = PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SCRIPT_VARIABLE;
+
+	if (variant_type == Variant::NIL) {
+		// System.Object (Variant)
+		prop_usage |= PROPERTY_USAGE_NIL_IS_VARIANT;
+	}
+
+	r_prop_info = PropertyInfo(variant_type, (String)p_member->get_name(), hint, hint_string, prop_usage);
 	r_exported = true;
 
 	return true;
@@ -2562,6 +2772,11 @@ bool CSharpScript::_get_member_export(IMonoClassMember *p_member, bool p_inspect
 
 int CSharpScript::_try_get_member_export_hint(IMonoClassMember *p_member, ManagedType p_type, Variant::Type p_variant_type, bool p_allow_generics, PropertyHint &r_hint, String &r_hint_string) {
 
+	if (p_variant_type == Variant::NIL) {
+		// System.Object (Variant)
+		return 1;
+	}
+
 	GD_MONO_ASSERT_THREAD_ATTACHED;
 
 	if (p_variant_type == Variant::INT && p_type.type_encoding == MONO_TYPE_VALUETYPE && mono_class_is_enum(p_type.type_class->get_mono_ptr())) {
@@ -2621,7 +2836,7 @@ int CSharpScript::_try_get_member_export_hint(IMonoClassMember *p_member, Manage
 		CRASH_COND(field_native_class == NULL);
 
 		r_hint = PROPERTY_HINT_RESOURCE_TYPE;
-		r_hint_string = NATIVE_GDMONOCLASS_NAME(field_native_class);
+		r_hint_string = String(NATIVE_GDMONOCLASS_NAME(field_native_class));
 	} else if (p_allow_generics && p_variant_type == Variant::ARRAY) {
 		// Nested arrays are not supported in the inspector
 
@@ -2909,9 +3124,8 @@ CSharpInstance *CSharpScript::_create_instance(const Variant **p_args, int p_arg
 		}
 	}
 
-	CSharpInstance *instance = memnew(CSharpInstance);
+	CSharpInstance *instance = memnew(CSharpInstance(Ref<CSharpScript>(this)));
 	instance->base_ref = p_isref;
-	instance->script = Ref<CSharpScript>(this);
 	instance->owner = p_owner;
 	instance->owner->set_script_instance(instance);
 
@@ -2998,12 +3212,14 @@ ScriptInstance *CSharpScript::instance_create(Object *p_this) {
 #endif
 
 	if (native) {
-		String native_name = NATIVE_GDMONOCLASS_NAME(native);
+		StringName native_name = NATIVE_GDMONOCLASS_NAME(native);
 		if (!ClassDB::is_parent_class(p_this->get_class_name(), native_name)) {
 			if (EngineDebugger::is_active()) {
-				CSharpLanguage::get_singleton()->debug_break_parse(get_path(), 0, "Script inherits from native type '" + native_name + "', so it can't be instanced in object of type: '" + p_this->get_class() + "'");
+				CSharpLanguage::get_singleton()->debug_break_parse(get_path(), 0,
+						"Script inherits from native type '" + String(native_name) +
+								"', so it can't be instanced in object of type: '" + p_this->get_class() + "'");
 			}
-			ERR_FAIL_V_MSG(NULL, "Script inherits from native type '" + native_name +
+			ERR_FAIL_V_MSG(NULL, "Script inherits from native type '" + String(native_name) +
 										 "', so it can't be instanced in object of type: '" + p_this->get_class() + "'.");
 		}
 	}
@@ -3290,19 +3506,45 @@ void CSharpScript::update_exports() {
 }
 
 bool CSharpScript::has_script_signal(const StringName &p_signal) const {
-	return _signals.has(p_signal);
+	return event_signals.has(p_signal) || _signals.has(p_signal);
 }
 
 void CSharpScript::get_script_signal_list(List<MethodInfo> *r_signals) const {
-	for (const Map<StringName, Vector<Argument>>::Element *E = _signals.front(); E; E = E->next()) {
+
+	for (const Map<StringName, Vector<SignalParameter>>::Element *E = _signals.front(); E; E = E->next()) {
 		MethodInfo mi;
+		mi.name = E->key();
+
+		const Vector<SignalParameter> &params = E->value();
+		for (int i = 0; i < params.size(); i++) {
+			const SignalParameter &param = params[i];
+
+			PropertyInfo arg_info = PropertyInfo(param.type, param.name);
+			if (param.type == Variant::NIL && param.nil_is_variant)
+				arg_info.usage |= PROPERTY_USAGE_NIL_IS_VARIANT;
+
+			mi.arguments.push_back(arg_info);
+		}
+
+		r_signals->push_back(mi);
+	}
 
+	for (const Map<StringName, EventSignal>::Element *E = event_signals.front(); E; E = E->next()) {
+		MethodInfo mi;
 		mi.name = E->key();
-		for (int i = 0; i < E->get().size(); i++) {
-			PropertyInfo arg;
-			arg.name = E->get()[i].name;
-			mi.arguments.push_back(arg);
+
+		const EventSignal &event_signal = E->value();
+		const Vector<SignalParameter> &params = event_signal.parameters;
+		for (int i = 0; i < params.size(); i++) {
+			const SignalParameter &param = params[i];
+
+			PropertyInfo arg_info = PropertyInfo(param.type, param.name);
+			if (param.type == Variant::NIL && param.nil_is_variant)
+				arg_info.usage |= PROPERTY_USAGE_NIL_IS_VARIANT;
+
+			mi.arguments.push_back(arg_info);
 		}
+
 		r_signals->push_back(mi);
 	}
 }
@@ -3420,19 +3662,10 @@ StringName CSharpScript::get_script_name() const {
 	return name;
 }
 
-CSharpScript::CSharpScript() :
-		script_list(this) {
+CSharpScript::CSharpScript() {
 
 	_clear();
 
-#ifdef TOOLS_ENABLED
-	source_changed_cache = false;
-	placeholder_fallback_enabled = false;
-	exports_invalidated = true;
-#endif
-
-	signals_invalidated = true;
-
 	_resource_path_changed();
 
 #ifdef DEBUG_ENABLED
@@ -3561,4 +3794,5 @@ CSharpLanguage::StringNameCache::StringNameCache() {
 	on_before_serialize = StaticCString::create("OnBeforeSerialize");
 	on_after_deserialize = StaticCString::create("OnAfterDeserialize");
 	dotctor = StaticCString::create(".ctor");
+	delegate_invoke_method_name = StaticCString::create("Invoke");
 }

+ 63 - 36
modules/mono/csharp_script.h

@@ -53,8 +53,8 @@ class CSharpLanguage;
 template <typename TScriptInstance, typename TScriptLanguage>
 TScriptInstance *cast_script_instance(ScriptInstance *p_inst) {
 	if (!p_inst)
-		return NULL;
-	return p_inst->get_language() == TScriptLanguage::get_singleton() ? static_cast<TScriptInstance *>(p_inst) : NULL;
+		return nullptr;
+	return p_inst->get_language() == TScriptLanguage::get_singleton() ? static_cast<TScriptInstance *>(p_inst) : nullptr;
 }
 #else
 template <typename TScriptInstance, typename TScriptLanguage>
@@ -69,18 +69,32 @@ class CSharpScript : public Script {
 
 	GDCLASS(CSharpScript, Script);
 
+public:
+	struct SignalParameter {
+		String name;
+		Variant::Type type;
+		bool nil_is_variant = false;
+	};
+
+	struct EventSignal {
+		GDMonoField *field = NULL;
+		GDMonoMethod *invoke_method = NULL;
+		Vector<SignalParameter> parameters;
+	};
+
+private:
 	friend class CSharpInstance;
 	friend class CSharpLanguage;
 	friend struct CSharpScriptDepSort;
 
-	bool tool;
-	bool valid;
+	bool tool = false;
+	bool valid = false;
 
 	bool builtin;
 
-	GDMonoClass *base;
-	GDMonoClass *native;
-	GDMonoClass *script_class;
+	GDMonoClass *base = nullptr;
+	GDMonoClass *native = nullptr;
+	GDMonoClass *script_class = nullptr;
 
 	Ref<CSharpScript> base_cache; // TODO what's this for?
 
@@ -92,6 +106,7 @@ class CSharpScript : public Script {
 		// Replace with buffer containing the serialized state of managed scripts.
 		// Keep variant state backup to use only with script instance placeholders.
 		List<Pair<StringName, Variant>> properties;
+		List<Pair<StringName, Array>> event_signals;
 	};
 
 	Set<ObjectID> pending_reload_instances;
@@ -103,15 +118,11 @@ class CSharpScript : public Script {
 	String source;
 	StringName name;
 
-	SelfList<CSharpScript> script_list;
-
-	struct Argument {
-		String name;
-		Variant::Type type;
-	};
+	SelfList<CSharpScript> script_list = this;
 
-	Map<StringName, Vector<Argument>> _signals;
-	bool signals_invalidated;
+	Map<StringName, Vector<SignalParameter>> _signals;
+	Map<StringName, EventSignal> event_signals;
+	bool signals_invalidated = true;
 
 	Vector<ScriptNetData> rpc_functions;
 	Vector<ScriptNetData> rpc_variables;
@@ -120,9 +131,9 @@ class CSharpScript : public Script {
 	List<PropertyInfo> exported_members_cache; // members_cache
 	Map<StringName, Variant> exported_members_defval_cache; // member_default_values_cache
 	Set<PlaceHolderScriptInstance *> placeholders;
-	bool source_changed_cache;
-	bool placeholder_fallback_enabled;
-	bool exports_invalidated;
+	bool source_changed_cache = false;
+	bool placeholder_fallback_enabled = false;
+	bool exports_invalidated = true;
 	void _update_exports_values(Map<StringName, Variant> &values, List<PropertyInfo> &propnames);
 	void _update_member_info_no_exports();
 	virtual void _placeholder_erased(PlaceHolderScriptInstance *p_placeholder);
@@ -133,7 +144,7 @@ class CSharpScript : public Script {
 	void _clear();
 
 	void load_script_signals(GDMonoClass *p_class, GDMonoClass *p_native_class);
-	bool _get_signal(GDMonoClass *p_class, GDMonoClass *p_delegate, Vector<Argument> &params);
+	bool _get_signal(GDMonoClass *p_class, GDMonoMethod *p_delegate_invoke, Vector<SignalParameter> &params);
 
 	bool _update_exports();
 #ifdef TOOLS_ENABLED
@@ -221,16 +232,18 @@ class CSharpInstance : public ScriptInstance {
 	friend class CSharpScript;
 	friend class CSharpLanguage;
 
-	Object *owner;
-	bool base_ref;
-	bool ref_dying;
-	bool unsafe_referenced;
-	bool predelete_notified;
-	bool destructing_script_instance;
+	Object *owner = nullptr;
+	bool base_ref = false;
+	bool ref_dying = false;
+	bool unsafe_referenced = false;
+	bool predelete_notified = false;
+	bool destructing_script_instance = false;
 
 	Ref<CSharpScript> script;
 	Ref<MonoGCHandle> gchandle;
 
+	Vector<Callable> event_signal_callables;
+
 	bool _reference_owner_unsafe();
 
 	/*
@@ -239,7 +252,7 @@ class CSharpInstance : public ScriptInstance {
 	bool _unreference_owner_unsafe();
 
 	/*
-	 * If NULL is returned, the caller must destroy the script instance by removing it from its owner.
+	 * If nullptr is returned, the caller must destroy the script instance by removing it from its owner.
 	 */
 	MonoObject *_internal_new_managed();
 
@@ -250,6 +263,7 @@ class CSharpInstance : public ScriptInstance {
 	void _call_multilevel(MonoObject *p_mono_object, const StringName &p_method, const Variant **p_args, int p_argcount);
 
 	void get_properties_state_for_reloading(List<Pair<StringName, Variant>> &r_state);
+	void get_event_signals_state_for_reloading(List<Pair<StringName, Array>> &r_state);
 
 public:
 	MonoObject *get_mono_object() const;
@@ -272,11 +286,14 @@ public:
 	void mono_object_disposed(MonoObject *p_obj);
 
 	/*
-	 * If 'r_delete_owner' is set to true, the caller must memdelete the script instance's owner. Otherwise, if
+	 * If 'r_delete_owner' is set to true, the caller must memdelete the script instance's owner. Otherwise, ifevent_signal
 	 * 'r_remove_script_instance' is set to true, the caller must destroy the script instance by removing it from its owner.
 	 */
 	void mono_object_disposed_baseref(MonoObject *p_obj, bool p_is_finalizer, bool &r_delete_owner, bool &r_remove_script_instance);
 
+	void connect_event_signals();
+	void disconnect_event_signals();
+
 	virtual void refcount_incremented();
 	virtual bool refcount_decremented();
 
@@ -301,7 +318,7 @@ public:
 
 	virtual ScriptLanguage *get_language();
 
-	CSharpInstance();
+	CSharpInstance(const Ref<CSharpScript> &p_script);
 	~CSharpInstance();
 };
 
@@ -313,6 +330,10 @@ struct CSharpScriptBinding {
 	Object *owner;
 };
 
+class ManagedCallableMiddleman : public Object {
+	GDCLASS(ManagedCallableMiddleman, Object);
+};
+
 class CSharpLanguage : public ScriptLanguage {
 
 	friend class CSharpScript;
@@ -320,9 +341,10 @@ class CSharpLanguage : public ScriptLanguage {
 
 	static CSharpLanguage *singleton;
 
-	bool finalizing;
+	bool finalizing = false;
+	bool finalized = false;
 
-	GDMono *gdmono;
+	GDMono *gdmono = nullptr;
 	SelfList<CSharpScript>::List script_list;
 
 	Mutex script_instances_mutex;
@@ -337,6 +359,8 @@ class CSharpLanguage : public ScriptLanguage {
 	Mutex unsafe_object_references_lock;
 #endif
 
+	ManagedCallableMiddleman *managed_callable_middleman = memnew(ManagedCallableMiddleman);
+
 	struct StringNameCache {
 
 		StringName _signal_callback;
@@ -348,17 +372,18 @@ class CSharpLanguage : public ScriptLanguage {
 		StringName dotctor; // .ctor
 		StringName on_before_serialize; // OnBeforeSerialize
 		StringName on_after_deserialize; // OnAfterDeserialize
+		StringName delegate_invoke_method_name;
 
 		StringNameCache();
 	};
 
-	int lang_idx;
+	int lang_idx = -1;
 
 	Dictionary scripts_metadata;
-	bool scripts_metadata_invalidated;
+	bool scripts_metadata_invalidated = true;
 
 	// For debug_break and debug_break_parse
-	int _debug_parse_err_line;
+	int _debug_parse_err_line = -1;
 	String _debug_parse_err_file;
 	String _debug_error;
 
@@ -368,7 +393,7 @@ class CSharpLanguage : public ScriptLanguage {
 	void _on_scripts_domain_unloaded();
 
 #ifdef TOOLS_ENABLED
-	EditorPlugin *godotsharp_editor;
+	EditorPlugin *godotsharp_editor = nullptr;
 
 	static void _editor_init_callback();
 #endif
@@ -410,6 +435,8 @@ public:
 		return scripts_metadata;
 	}
 
+	_FORCE_INLINE_ ManagedCallableMiddleman *get_managed_callable_middleman() const { return managed_callable_middleman; }
+
 	virtual String get_name() const;
 
 	/* LANGUAGE FUNCTIONS */
@@ -426,7 +453,7 @@ public:
 	virtual Ref<Script> get_template(const String &p_class_name, const String &p_base_class_name) const;
 	virtual bool is_using_templates();
 	virtual void make_template(const String &p_class_name, const String &p_base_class_name, Ref<Script> &p_script);
-	/* TODO */ virtual bool validate(const String &p_script, int &r_line_error, int &r_col_error, String &r_test_error, const String &p_path, List<String> *r_functions, List<ScriptLanguage::Warning> *r_warnings = NULL, Set<int> *r_safe_lines = NULL) const { return true; }
+	/* TODO */ virtual bool validate(const String &p_script, int &r_line_error, int &r_col_error, String &r_test_error, const String &p_path, List<String> *r_functions, List<ScriptLanguage::Warning> *r_warnings = nullptr, Set<int> *r_safe_lines = nullptr) const { return true; }
 	virtual String validate_path(const String &p_path) const;
 	virtual Script *create_script() const;
 	virtual bool has_named_classes() const;
@@ -497,7 +524,7 @@ public:
 
 class ResourceFormatLoaderCSharpScript : public ResourceFormatLoader {
 public:
-	virtual RES load(const String &p_path, const String &p_original_path = "", Error *r_error = NULL, bool p_use_sub_threads = false, float *r_progress = nullptr);
+	virtual RES load(const String &p_path, const String &p_original_path = "", Error *r_error = nullptr, bool p_use_sub_threads = false, float *r_progress = nullptr);
 	virtual void get_recognized_extensions(List<String> *p_extensions) const;
 	virtual bool handles_type(const String &p_type) const;
 	virtual String get_resource_type(const String &p_path) const;

+ 6 - 6
modules/mono/editor/GodotTools/GodotTools/BottomPanel.cs

@@ -280,7 +280,7 @@ namespace GodotTools
                     Text = "Build Project".TTR(),
                     FocusMode = FocusModeEnum.None
                 };
-                buildProjectBtn.Connect("pressed", this, nameof(BuildProjectPressed));
+                buildProjectBtn.PressedSignal += BuildProjectPressed;
                 toolBarHBox.AddChild(buildProjectBtn);
 
                 toolBarHBox.AddSpacer(begin: false);
@@ -293,7 +293,7 @@ namespace GodotTools
                     Visible = false,
                     FocusMode = FocusModeEnum.None
                 };
-                warningsBtn.Connect("toggled", this, nameof(_WarningsToggled));
+                warningsBtn.Toggled += _WarningsToggled;
                 toolBarHBox.AddChild(warningsBtn);
 
                 errorsBtn = new ToolButton
@@ -304,7 +304,7 @@ namespace GodotTools
                     Visible = false,
                     FocusMode = FocusModeEnum.None
                 };
-                errorsBtn.Connect("toggled", this, nameof(_ErrorsToggled));
+                errorsBtn.Toggled += _ErrorsToggled;
                 toolBarHBox.AddChild(errorsBtn);
 
                 toolBarHBox.AddSpacer(begin: false);
@@ -315,7 +315,7 @@ namespace GodotTools
                     FocusMode = FocusModeEnum.None,
                     Visible = false
                 };
-                viewLogBtn.Connect("pressed", this, nameof(_ViewLogPressed));
+                viewLogBtn.PressedSignal += _ViewLogPressed;
                 toolBarHBox.AddChild(viewLogBtn);
 
                 var hsc = new HSplitContainer
@@ -326,8 +326,8 @@ namespace GodotTools
                 panelBuildsTab.AddChild(hsc);
 
                 buildTabsList = new ItemList { SizeFlagsHorizontal = (int)SizeFlags.ExpandFill };
-                buildTabsList.Connect("item_selected", this, nameof(_BuildTabsItemSelected));
-                buildTabsList.Connect("nothing_selected", this, nameof(_BuildTabsNothingSelected));
+                buildTabsList.ItemSelected += _BuildTabsItemSelected;
+                buildTabsList.NothingSelected += _BuildTabsNothingSelected;
                 hsc.AddChild(buildTabsList);
 
                 buildTabs = new TabContainer

+ 1 - 1
modules/mono/editor/GodotTools/GodotTools/BuildTab.cs

@@ -251,7 +251,7 @@ namespace GodotTools
             base._Ready();
 
             issuesList = new ItemList { SizeFlagsVertical = (int)SizeFlags.ExpandFill };
-            issuesList.Connect("item_activated", this, nameof(_IssueActivated));
+            issuesList.ItemActivated += _IssueActivated;
             AddChild(issuesList);
         }
 

+ 11 - 13
modules/mono/editor/GodotTools/GodotTools/GodotSharpEditor.cs

@@ -121,16 +121,9 @@ namespace GodotTools
             aboutDialog.PopupCenteredMinsize();
         }
 
-        private void _ToggleAboutDialogOnStart(bool enabled)
+        private void _MenuOptionPressed(int id)
         {
-            bool showOnStart = (bool)editorSettings.GetSetting("mono/editor/show_info_on_start");
-            if (showOnStart != enabled)
-                editorSettings.SetSetting("mono/editor/show_info_on_start", enabled);
-        }
-
-        private void _MenuOptionPressed(MenuOptions id)
-        {
-            switch (id)
+            switch ((MenuOptions)id)
             {
                 case MenuOptions.CreateSln:
                     CreateProjectSolution();
@@ -210,7 +203,7 @@ namespace GodotTools
                     string scriptPath = ProjectSettings.GlobalizePath(script.ResourcePath);
                     RiderPathManager.OpenFile(GodotSharpDirs.ProjectSlnPath, scriptPath, line);
                     return Error.Ok;
-                }        
+                }
                 case ExternalEditorId.MonoDevelop:
                 {
                     string scriptPath = ProjectSettings.GlobalizePath(script.ResourcePath);
@@ -395,7 +388,12 @@ namespace GodotTools
 
                 // CheckBox in main container
                 aboutDialogCheckBox = new CheckBox { Text = "Show this warning when starting the editor" };
-                aboutDialogCheckBox.Connect("toggled", this, nameof(_ToggleAboutDialogOnStart));
+                aboutDialogCheckBox.Toggled += enabled =>
+                {
+                    bool showOnStart = (bool)editorSettings.GetSetting("mono/editor/show_info_on_start");
+                    if (showOnStart != enabled)
+                        editorSettings.SetSetting("mono/editor/show_info_on_start", enabled);
+                };
                 aboutVBox.AddChild(aboutDialogCheckBox);
             }
 
@@ -424,7 +422,7 @@ namespace GodotTools
                 menuPopup.AddItem("Create C# solution".TTR(), (int)MenuOptions.CreateSln);
             }
 
-            menuPopup.Connect("id_pressed", this, nameof(_MenuOptionPressed));
+            menuPopup.IdPressed += _MenuOptionPressed;
 
             var buildButton = new ToolButton
             {
@@ -432,7 +430,7 @@ namespace GodotTools
                 HintTooltip = "Build solution",
                 FocusMode = Control.FocusModeEnum.None
             };
-            buildButton.Connect("pressed", this, nameof(_BuildSolutionPressed));
+            buildButton.PressedSignal += _BuildSolutionPressed;
             AddControlToContainer(CustomControlContainer.Toolbar, buildButton);
 
             // External editor settings

+ 1 - 1
modules/mono/editor/GodotTools/GodotTools/HotReloadAssemblyWatcher.cs

@@ -40,7 +40,7 @@ namespace GodotTools
                 OneShot = false,
                 WaitTime = (float)EditorDef("mono/assembly_watch_interval_sec", 0.5)
             };
-            watchTimer.Connect("timeout", this, nameof(TimerTimeout));
+            watchTimer.Timeout += TimerTimeout;
             AddChild(watchTimer);
             watchTimer.Start();
         }

+ 296 - 34
modules/mono/editor/bindings_generator.cpp

@@ -76,7 +76,7 @@
 #define GLUE_HEADER_FILE "glue_header.h"
 #define ICALL_PREFIX "godot_icall_"
 #define SINGLETON_ICALL_SUFFIX "_get_singleton"
-#define ICALL_GET_METHODBIND ICALL_PREFIX "Object_ClassDB_get_method"
+#define ICALL_GET_METHODBIND "__ClassDB_get_method"
 
 #define C_LOCAL_RET "ret"
 #define C_LOCAL_VARARG_RET "vararg_ret"
@@ -95,6 +95,10 @@
 #define C_METHOD_MONOSTR_FROM_GODOT C_NS_MONOMARSHAL "::mono_string_from_godot"
 #define C_METHOD_MONOARRAY_TO(m_type) C_NS_MONOMARSHAL "::mono_array_to_" #m_type
 #define C_METHOD_MONOARRAY_FROM(m_type) C_NS_MONOMARSHAL "::" #m_type "_to_mono_array"
+#define C_METHOD_MANAGED_TO_CALLABLE C_NS_MONOMARSHAL "::managed_to_callable"
+#define C_METHOD_MANAGED_FROM_CALLABLE C_NS_MONOMARSHAL "::callable_to_managed"
+#define C_METHOD_MANAGED_TO_SIGNAL C_NS_MONOMARSHAL "::signal_info_to_callable"
+#define C_METHOD_MANAGED_FROM_SIGNAL C_NS_MONOMARSHAL "::callable_to_signal_info"
 
 #define BINDINGS_GENERATOR_VERSION UINT32_C(11)
 
@@ -504,23 +508,23 @@ String BindingsGenerator::bbcode_to_xml(const String &p_bbcode, const TypeInterf
 				xml_output.append(tag);
 				xml_output.append("</c>");
 			} else if (tag == "PackedByteArray") {
-				xml_output.append("<see cref=\"byte\"/>");
+				xml_output.append("<see cref=\"T:byte[]\"/>");
 			} else if (tag == "PackedInt32Array") {
-				xml_output.append("<see cref=\"int\"/>");
+				xml_output.append("<see cref=\"T:int[]\"/>");
+			} else if (tag == "PackedInt64Array") {
+				xml_output.append("<see cref=\"T:long[]\"/>");
 			} else if (tag == "PackedFloat32Array") {
-#ifdef REAL_T_IS_DOUBLE
-				xml_output.append("<see cref=\"double\"/>");
-#else
-				xml_output.append("<see cref=\"float\"/>");
-#endif
+				xml_output.append("<see cref=\"T:float[]\"/>");
+			} else if (tag == "PackedFloat64Array") {
+				xml_output.append("<see cref=\"T:double[]\"/>");
 			} else if (tag == "PackedStringArray") {
-				xml_output.append("<see cref=\"string\"/>");
+				xml_output.append("<see cref=\"T:string[]\"/>");
 			} else if (tag == "PackedVector2Array") {
-				xml_output.append("<see cref=\"" BINDINGS_NAMESPACE ".Vector2\"/>");
+				xml_output.append("<see cref=\"T:" BINDINGS_NAMESPACE ".Vector2[]\"/>");
 			} else if (tag == "PackedVector3Array") {
-				xml_output.append("<see cref=\"" BINDINGS_NAMESPACE ".Vector3\"/>");
+				xml_output.append("<see cref=\"T:" BINDINGS_NAMESPACE ".Vector3[]\"/>");
 			} else if (tag == "PackedColorArray") {
-				xml_output.append("<see cref=\"" BINDINGS_NAMESPACE ".Color\"/>");
+				xml_output.append("<see cref=\"T:" BINDINGS_NAMESPACE ".Color[]\"/>");
 			} else {
 				const TypeInterface *target_itype = _get_type_or_null(TypeReference(tag));
 
@@ -932,7 +936,7 @@ Error BindingsGenerator::generate_cs_core_project(const String &p_proj_dir) {
 							 "using System.Runtime.CompilerServices;\n"
 							 "\n");
 	cs_icalls_content.append("namespace " BINDINGS_NAMESPACE "\n" OPEN_BLOCK);
-	cs_icalls_content.append(INDENT1 "internal static class " BINDINGS_CLASS_NATIVECALLS "\n" INDENT1 OPEN_BLOCK);
+	cs_icalls_content.append(INDENT1 "internal static class " BINDINGS_CLASS_NATIVECALLS "\n" INDENT1 "{");
 
 	cs_icalls_content.append(MEMBER_BEGIN "internal static ulong godot_api_hash = ");
 	cs_icalls_content.append(String::num_uint64(GDMono::get_singleton()->get_api_core_hash()) + ";\n");
@@ -944,7 +948,7 @@ Error BindingsGenerator::generate_cs_core_project(const String &p_proj_dir) {
 #define ADD_INTERNAL_CALL(m_icall)                                                               \
 	if (!m_icall.editor_only) {                                                                  \
 		cs_icalls_content.append(MEMBER_BEGIN "[MethodImpl(MethodImplOptions.InternalCall)]\n"); \
-		cs_icalls_content.append(INDENT2 "internal extern static ");                             \
+		cs_icalls_content.append(INDENT2 "internal static extern ");                             \
 		cs_icalls_content.append(m_icall.im_type_out + " ");                                     \
 		cs_icalls_content.append(m_icall.name + "(");                                            \
 		cs_icalls_content.append(m_icall.im_sig + ");\n");                                       \
@@ -1046,7 +1050,7 @@ Error BindingsGenerator::generate_cs_editor_project(const String &p_proj_dir) {
 #define ADD_INTERNAL_CALL(m_icall)                                                          \
 	if (m_icall.editor_only) {                                                              \
 		cs_icalls_content.append(INDENT2 "[MethodImpl(MethodImplOptions.InternalCall)]\n"); \
-		cs_icalls_content.append(INDENT2 "internal extern static ");                        \
+		cs_icalls_content.append(INDENT2 "internal static extern ");                        \
 		cs_icalls_content.append(m_icall.im_type_out + " ");                                \
 		cs_icalls_content.append(m_icall.name + "(");                                       \
 		cs_icalls_content.append(m_icall.im_sig + ");\n");                                  \
@@ -1312,7 +1316,7 @@ Error BindingsGenerator::_generate_cs_type(const TypeInterface &itype, const Str
 		output.append(itype.proxy_name);
 		output.append(").Name);\n" INDENT4 "return singleton;\n" INDENT3 "}\n" INDENT2 "}\n");
 
-		output.append(MEMBER_BEGIN "private const string " BINDINGS_NATIVE_NAME_FIELD " = \"");
+		output.append(MEMBER_BEGIN "private static StringName " BINDINGS_NATIVE_NAME_FIELD " = \"");
 		output.append(itype.name);
 		output.append("\";\n");
 
@@ -1324,7 +1328,7 @@ Error BindingsGenerator::_generate_cs_type(const TypeInterface &itype, const Str
 	} else if (is_derived_type) {
 		// Add member fields
 
-		output.append(MEMBER_BEGIN "private const string " BINDINGS_NATIVE_NAME_FIELD " = \"");
+		output.append(MEMBER_BEGIN "private static StringName " BINDINGS_NATIVE_NAME_FIELD " = \"");
 		output.append(itype.name);
 		output.append("\";\n");
 
@@ -1363,6 +1367,13 @@ Error BindingsGenerator::_generate_cs_type(const TypeInterface &itype, const Str
 				"Failed to generate method '" + imethod.name + "' for class '" + itype.name + "'.");
 	}
 
+	for (const List<SignalInterface>::Element *E = itype.signals_.front(); E; E = E->next()) {
+		const SignalInterface &isignal = E->get();
+		Error method_err = _generate_cs_signal(itype, isignal, output);
+		ERR_FAIL_COND_V_MSG(method_err != OK, method_err,
+				"Failed to generate signal '" + isignal.name + "' for class '" + itype.name + "'.");
+	}
+
 	if (itype.is_singleton) {
 		InternalCall singleton_icall = InternalCall(itype.api_type, ICALL_PREFIX + itype.name + SINGLETON_ICALL_SUFFIX, "IntPtr");
 
@@ -1424,7 +1435,16 @@ Error BindingsGenerator::_generate_cs_property(const BindingsGenerator::TypeInte
 	}
 
 	if (getter && setter) {
-		ERR_FAIL_COND_V(getter->return_type.cname != setter->arguments.back()->get().type.cname, ERR_BUG);
+		const ArgumentInterface &setter_first_arg = setter->arguments.back()->get();
+		if (getter->return_type.cname != setter_first_arg.type.cname) {
+			// Special case for Node::set_name
+			bool whitelisted = getter->return_type.cname == name_cache.type_StringName &&
+							   setter_first_arg.type.cname == name_cache.type_String;
+
+			ERR_FAIL_COND_V_MSG(!whitelisted, ERR_BUG,
+					"Return type from getter doesn't match first argument of setter for property: '" +
+							p_itype.name + "." + String(p_iprop.cname) + "'.");
+		}
 	}
 
 	const TypeReference &proptype_name = getter ? getter->return_type : setter->arguments.back()->get().type;
@@ -1525,7 +1545,7 @@ Error BindingsGenerator::_generate_cs_method(const BindingsGenerator::TypeInterf
 
 	const TypeInterface *return_type = _get_type_or_placeholder(p_imethod.return_type);
 
-	String method_bind_field = "method_bind_" + itos(p_method_bind_count);
+	String method_bind_field = "__method_bind_" + itos(p_method_bind_count);
 
 	String arguments_sig;
 	String cs_in_statements;
@@ -1612,7 +1632,8 @@ Error BindingsGenerator::_generate_cs_method(const BindingsGenerator::TypeInterf
 	{
 		if (!p_imethod.is_virtual && !p_imethod.requires_object_call) {
 			p_output.append(MEMBER_BEGIN "[DebuggerBrowsable(DebuggerBrowsableState.Never)]" MEMBER_BEGIN "private static IntPtr ");
-			p_output.append(method_bind_field + " = Object." ICALL_GET_METHODBIND "(" BINDINGS_NATIVE_NAME_FIELD ", \"");
+			p_output.append(method_bind_field);
+			p_output.append(" = Object." ICALL_GET_METHODBIND "(" BINDINGS_NATIVE_NAME_FIELD ", \"");
 			p_output.append(p_imethod.name);
 			p_output.append("\");\n");
 		}
@@ -1726,6 +1747,106 @@ Error BindingsGenerator::_generate_cs_method(const BindingsGenerator::TypeInterf
 	return OK;
 }
 
+Error BindingsGenerator::_generate_cs_signal(const BindingsGenerator::TypeInterface &p_itype, const BindingsGenerator::SignalInterface &p_isignal, StringBuilder &p_output) {
+	String arguments_sig;
+
+	// Retrieve information from the arguments
+	for (const List<ArgumentInterface>::Element *F = p_isignal.arguments.front(); F; F = F->next()) {
+		const ArgumentInterface &iarg = F->get();
+		const TypeInterface *arg_type = _get_type_or_placeholder(iarg.type);
+
+		// Add the current arguments to the signature
+
+		if (F != p_isignal.arguments.front())
+			arguments_sig += ", ";
+
+		arguments_sig += arg_type->cs_type;
+		arguments_sig += " ";
+		arguments_sig += iarg.name;
+	}
+
+	// Generate signal
+	{
+		if (p_isignal.method_doc && p_isignal.method_doc->description.size()) {
+			String xml_summary = bbcode_to_xml(fix_doc_description(p_isignal.method_doc->description), &p_itype);
+			Vector<String> summary_lines = xml_summary.length() ? xml_summary.split("\n") : Vector<String>();
+
+			if (summary_lines.size()) {
+				p_output.append(MEMBER_BEGIN "/// <summary>\n");
+
+				for (int i = 0; i < summary_lines.size(); i++) {
+					p_output.append(INDENT2 "/// ");
+					p_output.append(summary_lines[i]);
+					p_output.append("\n");
+				}
+
+				p_output.append(INDENT2 "/// </summary>");
+			}
+		}
+
+		if (p_isignal.is_deprecated) {
+			if (p_isignal.deprecation_message.empty())
+				WARN_PRINT("An empty deprecation message is discouraged. Signal: '" + p_isignal.proxy_name + "'.");
+
+			p_output.append(MEMBER_BEGIN "[Obsolete(\"");
+			p_output.append(p_isignal.deprecation_message);
+			p_output.append("\")]");
+		}
+
+		String delegate_name = p_isignal.proxy_name;
+		delegate_name += "Handler"; // Delegate name is [SignalName]Handler
+
+		// Generate delegate
+		p_output.append(MEMBER_BEGIN "public delegate void ");
+		p_output.append(delegate_name);
+		p_output.append("(");
+		p_output.append(arguments_sig);
+		p_output.append(");\n");
+
+		// TODO:
+		// Could we assume the StringName instance of signal name will never be freed (it's stored in ClassDB) before the managed world is unloaded?
+		// If so, we could store the pointer we get from `data_unique_pointer()` instead of allocating StringName here.
+
+		// Cached signal name (StringName)
+		p_output.append(MEMBER_BEGIN "[DebuggerBrowsable(DebuggerBrowsableState.Never)]" MEMBER_BEGIN "private static StringName __signal_name_");
+		p_output.append(p_isignal.name);
+		p_output.append(" = \"");
+		p_output.append(p_isignal.name);
+		p_output.append("\";\n");
+
+		// Generate event
+		p_output.append(MEMBER_BEGIN "[Signal]" MEMBER_BEGIN "public ");
+
+		if (p_itype.is_singleton)
+			p_output.append("static ");
+
+		p_output.append("event ");
+		p_output.append(delegate_name);
+		p_output.append(" ");
+		p_output.append(p_isignal.proxy_name);
+		p_output.append("\n" OPEN_BLOCK_L2);
+
+		if (p_itype.is_singleton)
+			p_output.append("add => Singleton.Connect(__signal_name_");
+		else
+			p_output.append("add => Connect(__signal_name_");
+
+		p_output.append(p_isignal.name);
+		p_output.append(", new Callable(value));\n");
+
+		if (p_itype.is_singleton)
+			p_output.append(INDENT3 "remove => Singleton.Disconnect(__signal_name_");
+		else
+			p_output.append(INDENT3 "remove => Disconnect(__signal_name_");
+
+		p_output.append(p_isignal.name);
+		p_output.append(", new Callable(value));\n");
+		p_output.append(CLOSE_BLOCK_L2);
+	}
+
+	return OK;
+}
+
 Error BindingsGenerator::generate_glue(const String &p_output_dir) {
 
 	ERR_FAIL_COND_V(!initialized, ERR_UNCONFIGURED);
@@ -2479,13 +2600,92 @@ bool BindingsGenerator::_populate_object_type_interfaces() {
 			}
 		}
 
+		// Populate signals
+
+		const HashMap<StringName, MethodInfo> &signal_map = class_info->signal_map;
+		const StringName *k = NULL;
+
+		while ((k = signal_map.next(k))) {
+			SignalInterface isignal;
+
+			const MethodInfo &method_info = signal_map.get(*k);
+
+			isignal.name = method_info.name;
+			isignal.cname = method_info.name;
+
+			int argc = method_info.arguments.size();
+
+			for (int i = 0; i < argc; i++) {
+				PropertyInfo arginfo = method_info.arguments[i];
+
+				String orig_arg_name = arginfo.name;
+
+				ArgumentInterface iarg;
+				iarg.name = orig_arg_name;
+
+				if (arginfo.type == Variant::INT && arginfo.usage & PROPERTY_USAGE_CLASS_IS_ENUM) {
+					iarg.type.cname = arginfo.class_name;
+					iarg.type.is_enum = true;
+				} else if (arginfo.class_name != StringName()) {
+					iarg.type.cname = arginfo.class_name;
+				} else if (arginfo.hint == PROPERTY_HINT_RESOURCE_TYPE) {
+					iarg.type.cname = arginfo.hint_string;
+				} else if (arginfo.type == Variant::NIL) {
+					iarg.type.cname = name_cache.type_Variant;
+				} else {
+					if (arginfo.type == Variant::INT) {
+						iarg.type.cname = _get_int_type_name_from_meta(GodotTypeInfo::METADATA_NONE);
+					} else if (arginfo.type == Variant::FLOAT) {
+						iarg.type.cname = _get_float_type_name_from_meta(GodotTypeInfo::METADATA_NONE);
+					} else {
+						iarg.type.cname = Variant::get_type_name(arginfo.type);
+					}
+				}
+
+				iarg.name = escape_csharp_keyword(snake_to_camel_case(iarg.name));
+
+				isignal.add_argument(iarg);
+			}
+
+			isignal.proxy_name = escape_csharp_keyword(snake_to_pascal_case(isignal.name));
+
+			// Prevent the signal and its enclosing type from sharing the same name
+			if (isignal.proxy_name == itype.proxy_name) {
+				_log("Name of signal '%s' is ambiguous with the name of its enclosing class '%s'. Renaming signal to '%s_'\n",
+						isignal.proxy_name.utf8().get_data(), itype.proxy_name.utf8().get_data(), isignal.proxy_name.utf8().get_data());
+
+				isignal.proxy_name += "_";
+			}
+
+			if (itype.find_property_by_proxy_name(isignal.proxy_name) || itype.find_method_by_proxy_name(isignal.proxy_name)) {
+				// ClassDB allows signal names that conflict with method or property names.
+				// While registering a signal with a conflicting name is considered wrong,
+				// it may still happen and it may take some time until someone fixes the name.
+				// We can't allow the bindings to be in a broken state while we wait for a fix;
+				// that's why we must handle this possibility by renaming the signal.
+				isignal.proxy_name += "Signal";
+			}
+
+			if (itype.class_doc) {
+				for (int i = 0; i < itype.class_doc->signals.size(); i++) {
+					const DocData::MethodDoc &signal_doc = itype.class_doc->signals[i];
+					if (signal_doc.name == isignal.name) {
+						isignal.method_doc = &signal_doc;
+						break;
+					}
+				}
+			}
+
+			itype.signals_.push_back(isignal);
+		}
+
 		// Populate enums and constants
 
 		List<String> constants;
 		ClassDB::get_integer_constant_list(type_cname, &constants, true);
 
 		const HashMap<StringName, List<StringName>> &enum_map = class_info->enum_map;
-		const StringName *k = NULL;
+		k = NULL;
 
 		while ((k = enum_map.next(k))) {
 			StringName enum_proxy_cname = *k;
@@ -2587,8 +2787,15 @@ bool BindingsGenerator::_arg_default_value_from_variant(const Variant &p_val, Ar
 #endif
 			break;
 		case Variant::STRING:
+		case Variant::STRING_NAME:
 		case Variant::NODE_PATH:
-			r_iarg.default_argument = "\"" + r_iarg.default_argument + "\"";
+			if (r_iarg.type.cname == name_cache.type_StringName || r_iarg.type.cname == name_cache.type_NodePath) {
+				r_iarg.default_argument = "(%s)\"" + r_iarg.default_argument + "\"";
+				r_iarg.def_param_mode = ArgumentInterface::NULLABLE_REF;
+			} else {
+				CRASH_COND(r_iarg.type.cname != name_cache.type_String);
+				r_iarg.default_argument = "\"" + r_iarg.default_argument + "\"";
+			}
 			break;
 		case Variant::TRANSFORM:
 			if (p_val.operator Transform() == Transform())
@@ -2630,8 +2837,8 @@ bool BindingsGenerator::_arg_default_value_from_variant(const Variant &p_val, Ar
 		case Variant::ARRAY:
 		case Variant::PACKED_BYTE_ARRAY:
 		case Variant::PACKED_INT32_ARRAY:
-		case Variant::PACKED_FLOAT32_ARRAY:
 		case Variant::PACKED_INT64_ARRAY:
+		case Variant::PACKED_FLOAT32_ARRAY:
 		case Variant::PACKED_FLOAT64_ARRAY:
 		case Variant::PACKED_STRING_ARRAY:
 		case Variant::PACKED_VECTOR2_ARRAY:
@@ -2646,8 +2853,13 @@ bool BindingsGenerator::_arg_default_value_from_variant(const Variant &p_val, Ar
 			r_iarg.default_argument = Variant::get_type_name(p_val.get_type()) + ".Identity";
 			r_iarg.def_param_mode = ArgumentInterface::NULLABLE_VAL;
 			break;
-		default: {
-		}
+		case Variant::CALLABLE:
+		case Variant::SIGNAL:
+			CRASH_NOW_MSG("Parameter of type '" + String(r_iarg.type.cname) + "' cannot have a default value.");
+			break;
+		default:
+			CRASH_NOW_MSG("Unexpected Variant type: " + itos(p_val.get_type()));
+			break;
 	}
 
 	if (r_iarg.def_param_mode == ArgumentInterface::CONSTANT && r_iarg.type.cname == name_cache.type_Variant && r_iarg.default_argument != "null")
@@ -2672,7 +2884,7 @@ void BindingsGenerator::_populate_builtin_type_interfaces() {
 		itype.c_type_out = "GDMonoMarshal::M_" #m_type;                \
 		itype.cs_in = "ref %s";                                        \
 		/* in cs_out, im_type_out (%3) includes the 'out ' part */     \
-		itype.cs_out = "%0(%1, %3 argRet); return (%2)argRet;";        \
+		itype.cs_out = "%0(%1, %3 argRet); return argRet;";            \
 		itype.im_type_out = "out " + itype.cs_type;                    \
 		itype.ret_as_byref_arg = true;                                 \
 		builtin_types.insert(itype.cname, itype);                      \
@@ -2749,7 +2961,7 @@ void BindingsGenerator::_populate_builtin_type_interfaces() {
 		itype.im_type_out = "out " + itype.name;
 		itype.cs_in = "ref %0";
 		/* in cs_out, im_type_out (%3) includes the 'out ' part */
-		itype.cs_out = "%0(%1, %3 argRet); return (%2)argRet;";
+		itype.cs_out = "%0(%1, %3 argRet); return argRet;";
 		itype.ret_as_byref_arg = true;
 		builtin_types.insert(itype.cname, itype);
 
@@ -2766,7 +2978,7 @@ void BindingsGenerator::_populate_builtin_type_interfaces() {
 		itype.im_type_out = "out " + itype.name;
 		itype.cs_in = "ref %0";
 		/* in cs_out, im_type_out (%3) includes the 'out ' part */
-		itype.cs_out = "%0(%1, %3 argRet); return (%2)argRet;";
+		itype.cs_out = "%0(%1, %3 argRet); return argRet;";
 		itype.ret_as_byref_arg = true;
 		builtin_types.insert(itype.cname, itype);
 	}
@@ -2792,7 +3004,7 @@ void BindingsGenerator::_populate_builtin_type_interfaces() {
 		itype.im_type_out = "out " + itype.proxy_name;
 		itype.cs_in = "ref %0";
 		/* in cs_out, im_type_out (%3) includes the 'out ' part */
-		itype.cs_out = "%0(%1, %3 argRet); return (%2)argRet;";
+		itype.cs_out = "%0(%1, %3 argRet); return argRet;";
 		itype.ret_as_byref_arg = true;
 		builtin_types.insert(itype.cname, itype);
 
@@ -2814,7 +3026,7 @@ void BindingsGenerator::_populate_builtin_type_interfaces() {
 		itype.im_type_out = "out " + itype.proxy_name;
 		itype.cs_in = "ref %0";
 		/* in cs_out, im_type_out (%3) includes the 'out ' part */
-		itype.cs_out = "%0(%1, %3 argRet); return (%2)argRet;";
+		itype.cs_out = "%0(%1, %3 argRet); return argRet;";
 		itype.ret_as_byref_arg = true;
 		builtin_types.insert(itype.cname, itype);
 	}
@@ -2835,6 +3047,24 @@ void BindingsGenerator::_populate_builtin_type_interfaces() {
 	itype.im_type_out = itype.proxy_name;
 	builtin_types.insert(itype.cname, itype);
 
+	// StringName
+	itype = TypeInterface();
+	itype.name = "StringName";
+	itype.cname = itype.name;
+	itype.proxy_name = "StringName";
+	itype.c_in = "\t%0 %1_in = %1 ? *%1 : StringName();\n";
+	itype.c_out = "\treturn memnew(StringName(%1));\n";
+	itype.c_arg_in = "&%s_in";
+	itype.c_type = itype.name;
+	itype.c_type_in = itype.c_type + "*";
+	itype.c_type_out = itype.c_type + "*";
+	itype.cs_type = itype.proxy_name;
+	itype.cs_in = "StringName." CS_SMETHOD_GETINSTANCE "(%0)";
+	itype.cs_out = "return new %2(%0(%1));";
+	itype.im_type_in = "IntPtr";
+	itype.im_type_out = "IntPtr";
+	builtin_types.insert(itype.cname, itype);
+
 	// NodePath
 	itype = TypeInterface();
 	itype.name = "NodePath";
@@ -2883,6 +3113,40 @@ void BindingsGenerator::_populate_builtin_type_interfaces() {
 	itype.im_type_out = itype.proxy_name;
 	builtin_types.insert(itype.cname, itype);
 
+	// Callable
+	itype = TypeInterface::create_value_type(String("Callable"));
+	itype.c_in = "\t%0 %1_in = " C_METHOD_MANAGED_TO_CALLABLE "(*%1);\n";
+	itype.c_out = "\t*%3 = " C_METHOD_MANAGED_FROM_CALLABLE "(%1);\n";
+	itype.c_arg_in = "&%s_in";
+	itype.c_type_in = "GDMonoMarshal::M_Callable*";
+	itype.c_type_out = "GDMonoMarshal::M_Callable";
+	itype.cs_in = "ref %s";
+	/* in cs_out, im_type_out (%3) includes the 'out ' part */
+	itype.cs_out = "%0(%1, %3 argRet); return argRet;";
+	itype.im_type_out = "out " + itype.cs_type;
+	itype.ret_as_byref_arg = true;
+	builtin_types.insert(itype.cname, itype);
+
+	// Signal
+	itype = TypeInterface();
+	itype.name = "Signal";
+	itype.cname = itype.name;
+	itype.proxy_name = "SignalInfo";
+	itype.c_in = "\t%0 %1_in = " C_METHOD_MANAGED_TO_SIGNAL "(*%1);\n";
+	itype.c_out = "\t*%3 = " C_METHOD_MANAGED_FROM_SIGNAL "(%1);\n";
+	itype.c_arg_in = "&%s_in";
+	itype.c_type = itype.name;
+	itype.c_type_in = "GDMonoMarshal::M_SignalInfo*";
+	itype.c_type_out = "GDMonoMarshal::M_SignalInfo";
+	itype.cs_in = "ref %s";
+	/* in cs_out, im_type_out (%3) includes the 'out ' part */
+	itype.cs_out = "%0(%1, %3 argRet); return argRet;";
+	itype.cs_type = itype.proxy_name;
+	itype.im_type_in = "ref " + itype.cs_type;
+	itype.im_type_out = "out " + itype.cs_type;
+	itype.ret_as_byref_arg = true;
+	builtin_types.insert(itype.cname, itype);
+
 	// VarArg (fictitious type to represent variable arguments)
 	itype = TypeInterface();
 	itype.name = "VarArg";
@@ -2917,13 +3181,11 @@ void BindingsGenerator::_populate_builtin_type_interfaces() {
 #define INSERT_ARRAY(m_type, m_proxy_t) INSERT_ARRAY_FULL(m_type, m_type, m_proxy_t)
 
 	INSERT_ARRAY(PackedInt32Array, int);
+	INSERT_ARRAY(PackedInt64Array, long);
 	INSERT_ARRAY_FULL(PackedByteArray, PackedByteArray, byte);
 
-#ifdef REAL_T_IS_DOUBLE
-	INSERT_ARRAY(PackedFloat32Array, double);
-#else
 	INSERT_ARRAY(PackedFloat32Array, float);
-#endif
+	INSERT_ARRAY(PackedFloat64Array, double);
 
 	INSERT_ARRAY(PackedStringArray, string);
 

+ 48 - 1
modules/mono/editor/bindings_generator.h

@@ -107,9 +107,15 @@ class BindingsGenerator {
 		TypeReference type;
 
 		String name;
-		String default_argument;
 		DefaultParamMode def_param_mode;
 
+		/**
+		 * Determines the expression for the parameter default value.
+		 * Formatting elements:
+		 * %0 or %s: [cs_type] of the argument type
+		 */
+		String default_argument;
+
 		ArgumentInterface() {
 			def_param_mode = CONSTANT;
 		}
@@ -175,6 +181,32 @@ class BindingsGenerator {
 		}
 	};
 
+	struct SignalInterface {
+		String name;
+		StringName cname;
+
+		/**
+		 * Name of the C# method
+		 */
+		String proxy_name;
+
+		List<ArgumentInterface> arguments;
+
+		const DocData::MethodDoc *method_doc;
+
+		bool is_deprecated;
+		String deprecation_message;
+
+		void add_argument(const ArgumentInterface &argument) {
+			arguments.push_back(argument);
+		}
+
+		SignalInterface() {
+			method_doc = NULL;
+			is_deprecated = false;
+		}
+	};
+
 	struct TypeInterface {
 		/**
 		 * Identifier name for this type.
@@ -336,6 +368,7 @@ class BindingsGenerator {
 		List<EnumInterface> enums;
 		List<PropertyInterface> properties;
 		List<MethodInterface> methods;
+		List<SignalInterface> signals_;
 
 		const MethodInterface *find_method_by_name(const StringName &p_cname) const {
 			for (const List<MethodInterface>::Element *E = methods.front(); E; E = E->next()) {
@@ -364,6 +397,15 @@ class BindingsGenerator {
 			return NULL;
 		}
 
+		const MethodInterface *find_method_by_proxy_name(const String &p_proxy_name) const {
+			for (const List<MethodInterface>::Element *E = methods.front(); E; E = E->next()) {
+				if (E->get().proxy_name == p_proxy_name)
+					return &E->get();
+			}
+
+			return NULL;
+		}
+
 	private:
 		static void _init_value_type(TypeInterface &itype) {
 			itype.proxy_name = itype.name;
@@ -524,6 +566,8 @@ class BindingsGenerator {
 		StringName type_Reference;
 		StringName type_RID;
 		StringName type_String;
+		StringName type_StringName;
+		StringName type_NodePath;
 		StringName type_at_GlobalScope;
 		StringName enum_Error;
 
@@ -548,6 +592,8 @@ class BindingsGenerator {
 			type_Reference = StaticCString::create("Reference");
 			type_RID = StaticCString::create("RID");
 			type_String = StaticCString::create("String");
+			type_StringName = StaticCString::create("StringName");
+			type_NodePath = StaticCString::create("NodePath");
 			type_at_GlobalScope = StaticCString::create("@GlobalScope");
 			enum_Error = StaticCString::create("Error");
 
@@ -623,6 +669,7 @@ class BindingsGenerator {
 
 	Error _generate_cs_property(const TypeInterface &p_itype, const PropertyInterface &p_iprop, StringBuilder &p_output);
 	Error _generate_cs_method(const TypeInterface &p_itype, const MethodInterface &p_imethod, int &p_method_bind_count, StringBuilder &p_output);
+	Error _generate_cs_signal(const BindingsGenerator::TypeInterface &p_itype, const BindingsGenerator::SignalInterface &p_isignal, StringBuilder &p_output);
 
 	void _generate_global_constants(StringBuilder &p_output);
 

+ 1 - 1
modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/SignalAttribute.cs

@@ -2,7 +2,7 @@ using System;
 
 namespace Godot
 {
-    [AttributeUsage(AttributeTargets.Delegate)]
+    [AttributeUsage(AttributeTargets.Delegate | AttributeTargets.Event)]
     public class SignalAttribute : Attribute
     {
     }

+ 31 - 0
modules/mono/glue/GodotSharp/GodotSharp/Core/Callable.cs

@@ -0,0 +1,31 @@
+using System;
+
+namespace Godot
+{
+    public struct Callable
+    {
+        private readonly Object _target;
+        private readonly StringName _method;
+        private readonly Delegate _delegate;
+
+        public Object Target => _target;
+        public StringName Method => _method;
+        public Delegate Delegate => _delegate;
+
+        public static implicit operator Callable(Delegate @delegate) => new Callable(@delegate);
+
+        public Callable(Object target, StringName method)
+        {
+            _target = target;
+            _method = method;
+            _delegate = null;
+        }
+
+        public Callable(Delegate @delegate)
+        {
+            _target = null;
+            _method = null;
+            _delegate = @delegate;
+        }
+    }
+}

+ 395 - 0
modules/mono/glue/GodotSharp/GodotSharp/Core/DelegateUtils.cs

@@ -0,0 +1,395 @@
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.IO;
+using System.Reflection;
+using System.Runtime.CompilerServices;
+
+namespace Godot
+{
+    internal static class DelegateUtils
+    {
+        private enum TargetKind : uint
+        {
+            Static,
+            GodotObject,
+            CompilerGenerated
+        }
+
+        internal static bool TrySerializeDelegate(Delegate @delegate, Collections.Array serializedData)
+        {
+            if (@delegate is MulticastDelegate multicastDelegate)
+            {
+                bool someDelegatesSerialized = false;
+
+                Delegate[] invocationList = multicastDelegate.GetInvocationList();
+
+                if (invocationList.Length > 1)
+                {
+                    var multiCastData = new Collections.Array();
+
+                    foreach (Delegate oneDelegate in invocationList)
+                        someDelegatesSerialized |= TrySerializeDelegate(oneDelegate, multiCastData);
+
+                    if (!someDelegatesSerialized)
+                        return false;
+
+                    serializedData.Add(multiCastData);
+                    return true;
+                }
+            }
+
+            if (TrySerializeSingleDelegate(@delegate, out byte[] buffer))
+            {
+                serializedData.Add(buffer);
+                return true;
+            }
+
+            return false;
+        }
+
+        private static bool TrySerializeSingleDelegate(Delegate @delegate, out byte[] buffer)
+        {
+            buffer = null;
+
+            object target = @delegate.Target;
+
+            switch (target)
+            {
+                case null:
+                {
+                    using (var stream = new MemoryStream())
+                    using (var writer = new BinaryWriter(stream))
+                    {
+                        writer.Write((ulong) TargetKind.Static);
+
+                        SerializeType(writer, @delegate.GetType());
+
+                        if (!TrySerializeMethodInfo(writer, @delegate.Method))
+                            return false;
+
+                        buffer = stream.ToArray();
+                        return true;
+                    }
+                }
+                case Godot.Object godotObject:
+                {
+                    using (var stream = new MemoryStream())
+                    using (var writer = new BinaryWriter(stream))
+                    {
+                        writer.Write((ulong) TargetKind.GodotObject);
+                        writer.Write((ulong) godotObject.GetInstanceId());
+
+                        SerializeType(writer, @delegate.GetType());
+
+                        if (!TrySerializeMethodInfo(writer, @delegate.Method))
+                            return false;
+
+                        buffer = stream.ToArray();
+                        return true;
+                    }
+                }
+                default:
+                {
+                    Type targetType = target.GetType();
+
+                    if (targetType.GetCustomAttribute(typeof(CompilerGeneratedAttribute), true) != null)
+                    {
+                        // Compiler generated. Probably a closure. Try to serialize it.
+
+                        using (var stream = new MemoryStream())
+                        using (var writer = new BinaryWriter(stream))
+                        {
+                            writer.Write((ulong) TargetKind.CompilerGenerated);
+                            SerializeType(writer, targetType);
+
+                            SerializeType(writer, @delegate.GetType());
+
+                            if (!TrySerializeMethodInfo(writer, @delegate.Method))
+                                return false;
+
+                            FieldInfo[] fields = targetType.GetFields(BindingFlags.Instance | BindingFlags.Public);
+
+                            writer.Write(fields.Length);
+
+                            foreach (FieldInfo field in fields)
+                            {
+                                Type fieldType = field.GetType();
+
+                                Variant.Type variantType = GD.TypeToVariantType(fieldType);
+
+                                if (variantType == Variant.Type.Nil)
+                                    return false;
+
+                                writer.Write(field.Name);
+                                byte[] valueBuffer = GD.Var2Bytes(field.GetValue(target));
+                                writer.Write(valueBuffer.Length);
+                                writer.Write(valueBuffer);
+                            }
+
+                            buffer = stream.ToArray();
+                            return true;
+                        }
+                    }
+
+                    return false;
+                }
+            }
+        }
+
+        private static bool TrySerializeMethodInfo(BinaryWriter writer, MethodInfo methodInfo)
+        {
+            if (methodInfo == null)
+                return false;
+
+            SerializeType(writer, methodInfo.DeclaringType);
+
+            writer.Write(methodInfo.Name);
+
+            int flags = 0;
+
+            if (methodInfo.IsPublic)
+                flags |= (int) BindingFlags.Public;
+            else
+                flags |= (int) BindingFlags.NonPublic;
+
+            if (methodInfo.IsStatic)
+                flags |= (int) BindingFlags.Static;
+            else
+                flags |= (int) BindingFlags.Instance;
+
+            writer.Write(flags);
+
+            Type returnType = methodInfo.ReturnType;
+            bool hasReturn = methodInfo.ReturnType != typeof(void);
+
+            writer.Write(hasReturn);
+            if (hasReturn)
+                SerializeType(writer, returnType);
+
+            ParameterInfo[] parameters = methodInfo.GetParameters();
+
+            writer.Write(parameters.Length);
+
+            if (parameters.Length > 0)
+            {
+                for (int i = 0; i < parameters.Length; i++)
+                    SerializeType(writer, parameters[i].ParameterType);
+            }
+
+            return true;
+        }
+
+        private static void SerializeType(BinaryWriter writer, Type type)
+        {
+            if (type == null)
+            {
+                int genericArgumentsCount = -1;
+                writer.Write(genericArgumentsCount);
+            }
+            else if (type.IsGenericType)
+            {
+                Type genericTypeDef = type.GetGenericTypeDefinition();
+                Type[] genericArgs = type.GetGenericArguments();
+
+                int genericArgumentsCount = genericArgs.Length;
+                writer.Write(genericArgumentsCount);
+
+                string assemblyQualifiedName = genericTypeDef.AssemblyQualifiedName;
+                Debug.Assert(assemblyQualifiedName != null);
+                writer.Write(assemblyQualifiedName);
+
+                for (int i = 0; i < genericArgs.Length; i++)
+                    SerializeType(writer, genericArgs[i]);
+            }
+            else
+            {
+                int genericArgumentsCount = 0;
+                writer.Write(genericArgumentsCount);
+
+                string assemblyQualifiedName = type.AssemblyQualifiedName;
+                Debug.Assert(assemblyQualifiedName != null);
+                writer.Write(assemblyQualifiedName);
+            }
+        }
+
+        private static bool TryDeserializeDelegate(Collections.Array serializedData, out Delegate @delegate)
+        {
+            if (serializedData.Count == 1)
+            {
+                object elem = serializedData[0];
+
+                if (elem is Collections.Array multiCastData)
+                    return TryDeserializeDelegate(multiCastData, out @delegate);
+
+                return TryDeserializeSingleDelegate((byte[])elem, out @delegate);
+            }
+
+            @delegate = null;
+
+            var delegates = new List<Delegate>(serializedData.Count);
+
+            foreach (object elem in serializedData)
+            {
+                if (elem is Collections.Array multiCastData)
+                {
+                    if (TryDeserializeDelegate(multiCastData, out Delegate oneDelegate))
+                        delegates.Add(oneDelegate);
+                }
+                else
+                {
+                    if (TryDeserializeSingleDelegate((byte[]) elem, out Delegate oneDelegate))
+                        delegates.Add(oneDelegate);
+                }
+            }
+
+            if (delegates.Count <= 0)
+                return false;
+
+            @delegate = delegates.Count == 1 ? delegates[0] : Delegate.Combine(delegates.ToArray());
+            return true;
+        }
+
+        private static bool TryDeserializeSingleDelegate(byte[] buffer, out Delegate @delegate)
+        {
+            @delegate = null;
+
+            using (var stream = new MemoryStream(buffer, writable: false))
+            using (var reader = new BinaryReader(stream))
+            {
+                var targetKind = (TargetKind) reader.ReadUInt64();
+
+                switch (targetKind)
+                {
+                    case TargetKind.Static:
+                    {
+                        Type delegateType = DeserializeType(reader);
+                        if (delegateType == null)
+                            return false;
+
+                        if (!TryDeserializeMethodInfo(reader, out MethodInfo methodInfo))
+                            return false;
+
+                        @delegate = Delegate.CreateDelegate(delegateType, null, methodInfo);
+                        return true;
+                    }
+                    case TargetKind.GodotObject:
+                    {
+                        ulong objectId = reader.ReadUInt64();
+                        Godot.Object godotObject = GD.InstanceFromId(objectId);
+                        if (godotObject == null)
+                            return false;
+
+                        Type delegateType = DeserializeType(reader);
+                        if (delegateType == null)
+                            return false;
+
+                        if (!TryDeserializeMethodInfo(reader, out MethodInfo methodInfo))
+                            return false;
+
+                        @delegate = Delegate.CreateDelegate(delegateType, godotObject, methodInfo);
+                        return true;
+                    }
+                    case TargetKind.CompilerGenerated:
+                    {
+                        Type targetType = DeserializeType(reader);
+                        if (targetType == null)
+                            return false;
+
+                        Type delegateType = DeserializeType(reader);
+                        if (delegateType == null)
+                            return false;
+
+                        if (!TryDeserializeMethodInfo(reader, out MethodInfo methodInfo))
+                            return false;
+
+                        int fieldCount = reader.ReadInt32();
+
+                        object recreatedTarget = Activator.CreateInstance(targetType);
+
+                        for (int i = 0; i < fieldCount; i++)
+                        {
+                            string name = reader.ReadString();
+                            int valueBufferLength = reader.ReadInt32();
+                            byte[] valueBuffer = reader.ReadBytes(valueBufferLength);
+
+                            FieldInfo fieldInfo = targetType.GetField(name, BindingFlags.Instance | BindingFlags.Public);
+                            fieldInfo?.SetValue(recreatedTarget, GD.Bytes2Var(valueBuffer));
+                        }
+
+                        @delegate = Delegate.CreateDelegate(delegateType, recreatedTarget, methodInfo);
+                        return true;
+                    }
+                    default:
+                        return false;
+                }
+            }
+        }
+
+        private static bool TryDeserializeMethodInfo(BinaryReader reader, out MethodInfo methodInfo)
+        {
+            methodInfo = null;
+
+            Type declaringType = DeserializeType(reader);
+
+            string methodName = reader.ReadString();
+
+            int flags = reader.ReadInt32();
+
+            bool hasReturn = reader.ReadBoolean();
+            Type returnType = hasReturn ? DeserializeType(reader) : typeof(void);
+
+            int parametersCount = reader.ReadInt32();
+
+            if (parametersCount > 0)
+            {
+                var parameterTypes = new Type[parametersCount];
+
+                for (int i = 0; i < parametersCount; i++)
+                {
+                    Type parameterType = DeserializeType(reader);
+                    if (parameterType == null)
+                        return false;
+                    parameterTypes[i] = parameterType;
+                }
+
+                methodInfo = declaringType.GetMethod(methodName, (BindingFlags) flags, null, parameterTypes, null);
+                return methodInfo != null && methodInfo.ReturnType == returnType;
+            }
+
+            methodInfo = declaringType.GetMethod(methodName, (BindingFlags) flags);
+            return methodInfo != null && methodInfo.ReturnType == returnType;
+        }
+
+        private static Type DeserializeType(BinaryReader reader)
+        {
+            int genericArgumentsCount = reader.ReadInt32();
+
+            if (genericArgumentsCount == -1)
+                return null;
+
+            string assemblyQualifiedName = reader.ReadString();
+            var type = Type.GetType(assemblyQualifiedName);
+
+            if (type == null)
+                return null; // Type not found
+
+            if (genericArgumentsCount != 0)
+            {
+                var genericArgumentTypes = new Type[genericArgumentsCount];
+
+                for (int i = 0; i < genericArgumentsCount; i++)
+                {
+                    Type genericArgumentType = DeserializeType(reader);
+                    if (genericArgumentType == null)
+                        return null;
+                    genericArgumentTypes[i] = genericArgumentType;
+                }
+
+                type = type.MakeGenericType(genericArgumentTypes);
+            }
+
+            return type;
+        }
+    }
+}

+ 23 - 14
modules/mono/glue/GodotSharp/GodotSharp/Core/GD.cs

@@ -5,6 +5,7 @@ using System.Runtime.CompilerServices;
 using real_t = System.Double;
 #else
 using real_t = System.Single;
+
 #endif
 
 // TODO: Add comments describing what this class does. It is not obvious.
@@ -13,9 +14,9 @@ namespace Godot
 {
     public static partial class GD
     {
-        public static object Bytes2Var(byte[] bytes, bool allow_objects = false)
+        public static object Bytes2Var(byte[] bytes, bool allowObjects = false)
         {
-            return godot_icall_GD_bytes2var(bytes, allow_objects);
+            return godot_icall_GD_bytes2var(bytes, allowObjects);
         }
 
         public static object Convert(object what, Variant.Type type)
@@ -25,7 +26,7 @@ namespace Godot
 
         public static real_t Db2Linear(real_t db)
         {
-            return (real_t)Math.Exp(db * 0.11512925464970228420089957273422);
+            return (real_t) Math.Exp(db * 0.11512925464970228420089957273422);
         }
 
         public static real_t DecTime(real_t value, real_t amount, real_t step)
@@ -38,11 +39,11 @@ namespace Godot
             return val * sgn;
         }
 
-        public static FuncRef FuncRef(Object instance, string funcname)
+        public static FuncRef FuncRef(Object instance, StringName funcName)
         {
             var ret = new FuncRef();
             ret.SetInstance(instance);
-            ret.SetFunction(funcname);
+            ret.SetFunction(funcName);
             return ret;
         }
 
@@ -58,7 +59,7 @@ namespace Godot
 
         public static real_t Linear2Db(real_t linear)
         {
-            return (real_t)(Math.Log(linear) * 8.6858896380650365530225783783321);
+            return (real_t) (Math.Log(linear) * 8.6858896380650365530225783783321);
         }
 
         public static Resource Load(string path)
@@ -181,14 +182,14 @@ namespace Godot
             return godot_icall_GD_str2var(str);
         }
 
-        public static bool TypeExists(string type)
+        public static bool TypeExists(StringName type)
         {
-            return godot_icall_GD_type_exists(type);
+            return godot_icall_GD_type_exists(StringName.GetPtr(type));
         }
 
-        public static byte[] Var2Bytes(object var, bool full_objects = false)
+        public static byte[] Var2Bytes(object var, bool fullObjects = false)
         {
-            return godot_icall_GD_var2bytes(var, full_objects);
+            return godot_icall_GD_var2bytes(var, fullObjects);
         }
 
         public static string Var2Str(object var)
@@ -196,8 +197,13 @@ namespace Godot
             return godot_icall_GD_var2str(var);
         }
 
+        public static Variant.Type TypeToVariantType(Type type)
+        {
+            return godot_icall_TypeToVariantType(type);
+        }
+
         [MethodImpl(MethodImplOptions.InternalCall)]
-        internal extern static object godot_icall_GD_bytes2var(byte[] bytes, bool allow_objects);
+        internal extern static object godot_icall_GD_bytes2var(byte[] bytes, bool allowObjects);
 
         [MethodImpl(MethodImplOptions.InternalCall)]
         internal extern static object godot_icall_GD_convert(object what, Variant.Type type);
@@ -206,7 +212,7 @@ namespace Godot
         internal extern static int godot_icall_GD_hash(object var);
 
         [MethodImpl(MethodImplOptions.InternalCall)]
-        internal extern static Object godot_icall_GD_instance_from_id(ulong instance_id);
+        internal extern static Object godot_icall_GD_instance_from_id(ulong instanceId);
 
         [MethodImpl(MethodImplOptions.InternalCall)]
         internal extern static void godot_icall_GD_print(object[] what);
@@ -249,10 +255,10 @@ namespace Godot
         internal extern static object godot_icall_GD_str2var(string str);
 
         [MethodImpl(MethodImplOptions.InternalCall)]
-        internal extern static bool godot_icall_GD_type_exists(string type);
+        internal extern static bool godot_icall_GD_type_exists(IntPtr type);
 
         [MethodImpl(MethodImplOptions.InternalCall)]
-        internal extern static byte[] godot_icall_GD_var2bytes(object what, bool full_objects);
+        internal extern static byte[] godot_icall_GD_var2bytes(object what, bool fullObjects);
 
         [MethodImpl(MethodImplOptions.InternalCall)]
         internal extern static string godot_icall_GD_var2str(object var);
@@ -262,5 +268,8 @@ namespace Godot
 
         [MethodImpl(MethodImplOptions.InternalCall)]
         internal extern static void godot_icall_GD_pushwarning(string type);
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern Variant.Type godot_icall_TypeToVariantType(Type type);
     }
 }

+ 24 - 35
modules/mono/glue/GodotSharp/GodotSharp/Core/NodePath.cs

@@ -7,7 +7,7 @@ namespace Godot
     {
         private bool disposed = false;
 
-        internal IntPtr ptr;
+        private IntPtr ptr;
 
         internal static IntPtr GetPtr(NodePath instance)
         {
@@ -50,104 +50,93 @@ namespace Godot
             this.ptr = ptr;
         }
 
-        public IntPtr NativeInstance
-        {
-            get { return ptr; }
-        }
-
         public NodePath() : this(string.Empty) {}
 
         public NodePath(string path)
         {
-            this.ptr = godot_icall_NodePath_Ctor(path);
+            ptr = godot_icall_NodePath_Ctor(path);
         }
 
-        public static implicit operator NodePath(string from)
-        {
-            return new NodePath(from);
-        }
+        public static implicit operator NodePath(string from) => new NodePath(from);
 
-        public static implicit operator string(NodePath from)
-        {
-            return godot_icall_NodePath_operator_String(NodePath.GetPtr(from));
-        }
+        public static implicit operator string(NodePath from) => from.ToString();
 
         public override string ToString()
         {
-            return (string)this;
+            return godot_icall_NodePath_operator_String(GetPtr(this));
         }
 
         public NodePath GetAsPropertyPath()
         {
-            return new NodePath(godot_icall_NodePath_get_as_property_path(NodePath.GetPtr(this)));
+            return new NodePath(godot_icall_NodePath_get_as_property_path(GetPtr(this)));
         }
 
         public string GetConcatenatedSubnames()
         {
-            return godot_icall_NodePath_get_concatenated_subnames(NodePath.GetPtr(this));
+            return godot_icall_NodePath_get_concatenated_subnames(GetPtr(this));
         }
 
         public string GetName(int idx)
         {
-            return godot_icall_NodePath_get_name(NodePath.GetPtr(this), idx);
+            return godot_icall_NodePath_get_name(GetPtr(this), idx);
         }
 
         public int GetNameCount()
         {
-            return godot_icall_NodePath_get_name_count(NodePath.GetPtr(this));
+            return godot_icall_NodePath_get_name_count(GetPtr(this));
         }
 
         public string GetSubname(int idx)
         {
-            return godot_icall_NodePath_get_subname(NodePath.GetPtr(this), idx);
+            return godot_icall_NodePath_get_subname(GetPtr(this), idx);
         }
 
         public int GetSubnameCount()
         {
-            return godot_icall_NodePath_get_subname_count(NodePath.GetPtr(this));
+            return godot_icall_NodePath_get_subname_count(GetPtr(this));
         }
 
         public bool IsAbsolute()
         {
-            return godot_icall_NodePath_is_absolute(NodePath.GetPtr(this));
+            return godot_icall_NodePath_is_absolute(GetPtr(this));
         }
 
         public bool IsEmpty()
         {
-            return godot_icall_NodePath_is_empty(NodePath.GetPtr(this));
+            return godot_icall_NodePath_is_empty(GetPtr(this));
         }
 
         [MethodImpl(MethodImplOptions.InternalCall)]
-        internal extern static IntPtr godot_icall_NodePath_Ctor(string path);
+        private static extern IntPtr godot_icall_NodePath_Ctor(string path);
 
         [MethodImpl(MethodImplOptions.InternalCall)]
-        internal extern static void godot_icall_NodePath_Dtor(IntPtr ptr);
+        private static extern void godot_icall_NodePath_Dtor(IntPtr ptr);
 
         [MethodImpl(MethodImplOptions.InternalCall)]
-        internal extern static string godot_icall_NodePath_operator_String(IntPtr ptr);
+        private static extern string godot_icall_NodePath_operator_String(IntPtr ptr);
 
         [MethodImpl(MethodImplOptions.InternalCall)]
-        internal extern static IntPtr godot_icall_NodePath_get_as_property_path(IntPtr ptr);
+        private static extern IntPtr godot_icall_NodePath_get_as_property_path(IntPtr ptr);
 
         [MethodImpl(MethodImplOptions.InternalCall)]
-        internal extern static string godot_icall_NodePath_get_concatenated_subnames(IntPtr ptr);
+        private static extern string godot_icall_NodePath_get_concatenated_subnames(IntPtr ptr);
 
         [MethodImpl(MethodImplOptions.InternalCall)]
-        internal extern static string godot_icall_NodePath_get_name(IntPtr ptr, int arg1);
+        private static extern string godot_icall_NodePath_get_name(IntPtr ptr, int arg1);
 
         [MethodImpl(MethodImplOptions.InternalCall)]
-        internal extern static int godot_icall_NodePath_get_name_count(IntPtr ptr);
+        private static extern int godot_icall_NodePath_get_name_count(IntPtr ptr);
 
         [MethodImpl(MethodImplOptions.InternalCall)]
-        internal extern static string godot_icall_NodePath_get_subname(IntPtr ptr, int arg1);
+        private static extern string godot_icall_NodePath_get_subname(IntPtr ptr, int arg1);
 
         [MethodImpl(MethodImplOptions.InternalCall)]
-        internal extern static int godot_icall_NodePath_get_subname_count(IntPtr ptr);
+        private static extern int godot_icall_NodePath_get_subname_count(IntPtr ptr);
 
         [MethodImpl(MethodImplOptions.InternalCall)]
-        internal extern static bool godot_icall_NodePath_is_absolute(IntPtr ptr);
+        private static extern bool godot_icall_NodePath_is_absolute(IntPtr ptr);
 
         [MethodImpl(MethodImplOptions.InternalCall)]
-        internal extern static bool godot_icall_NodePath_is_empty(IntPtr ptr);
+        private static extern bool godot_icall_NodePath_is_empty(IntPtr ptr);
     }
 }

+ 22 - 7
modules/mono/glue/GodotSharp/GodotSharp/Core/Object.base.cs

@@ -7,7 +7,7 @@ namespace Godot
     {
         private bool disposed = false;
 
-        private const string nativeName = "Object";
+        private static StringName nativeName = "Object";
 
         internal IntPtr ptr;
         internal bool memoryOwn;
@@ -15,7 +15,14 @@ namespace Godot
         public Object() : this(false)
         {
             if (ptr == IntPtr.Zero)
+            {
                 ptr = godot_icall_Object_Ctor(this);
+            }
+            else
+            {
+                // This is called inside godot_icall_Object_Ctor, so we must call it as well in this case.
+                godot_icall_Object_ConnectEventSignals(ptr);
+            }
         }
 
         internal Object(bool memoryOwn)
@@ -101,7 +108,7 @@ namespace Godot
         /// }
         /// </code>
         /// </example>
-        public SignalAwaiter ToSignal(Object source, string signal)
+        public SignalAwaiter ToSignal(Object source, StringName signal)
         {
             return new SignalAwaiter(source, signal, this);
         }
@@ -111,20 +118,28 @@ namespace Godot
         /// </summary>
         public dynamic DynamicObject => new DynamicGodotObject(this);
 
+        internal static IntPtr __ClassDB_get_method(StringName type, string method)
+        {
+            return godot_icall_Object_ClassDB_get_method(StringName.GetPtr(type), method);
+        }
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        internal static extern IntPtr godot_icall_Object_Ctor(Object obj);
+
         [MethodImpl(MethodImplOptions.InternalCall)]
-        internal extern static IntPtr godot_icall_Object_Ctor(Object obj);
+        internal static extern void godot_icall_Object_Disposed(Object obj, IntPtr ptr);
 
         [MethodImpl(MethodImplOptions.InternalCall)]
-        internal extern static void godot_icall_Object_Disposed(Object obj, IntPtr ptr);
+        internal static extern void godot_icall_Reference_Disposed(Object obj, IntPtr ptr, bool isFinalizer);
 
         [MethodImpl(MethodImplOptions.InternalCall)]
-        internal extern static void godot_icall_Reference_Disposed(Object obj, IntPtr ptr, bool isFinalizer);
+        internal static extern void godot_icall_Object_ConnectEventSignals(IntPtr obj);
 
         [MethodImpl(MethodImplOptions.InternalCall)]
-        internal extern static string godot_icall_Object_ToString(IntPtr ptr);
+        internal static extern string godot_icall_Object_ToString(IntPtr ptr);
 
         // Used by the generated API
         [MethodImpl(MethodImplOptions.InternalCall)]
-        internal extern static IntPtr godot_icall_Object_ClassDB_get_method(string type, string method);
+        internal static extern IntPtr godot_icall_Object_ClassDB_get_method(IntPtr type, string method);
     }
 }

+ 3 - 9
modules/mono/glue/GodotSharp/GodotSharp/Core/SignalAwaiter.cs

@@ -9,13 +9,13 @@ namespace Godot
         private object[] result;
         private Action action;
 
-        public SignalAwaiter(Object source, string signal, Object target)
+        public SignalAwaiter(Object source, StringName signal, Object target)
         {
-            godot_icall_SignalAwaiter_connect(Object.GetPtr(source), signal, Object.GetPtr(target), this);
+            godot_icall_SignalAwaiter_connect(Object.GetPtr(source), StringName.GetPtr(signal), Object.GetPtr(target), this);
         }
 
         [MethodImpl(MethodImplOptions.InternalCall)]
-        internal extern static Error godot_icall_SignalAwaiter_connect(IntPtr source, string signal, IntPtr target, SignalAwaiter awaiter);
+        internal extern static Error godot_icall_SignalAwaiter_connect(IntPtr source, IntPtr signal, IntPtr target, SignalAwaiter awaiter);
 
         public bool IsCompleted
         {
@@ -50,11 +50,5 @@ namespace Godot
                 action();
             }
         }
-
-        internal void FailureCallback()
-        {
-            action = null;
-            completed = true;
-        }
     }
 }

+ 17 - 0
modules/mono/glue/GodotSharp/GodotSharp/Core/SignalInfo.cs

@@ -0,0 +1,17 @@
+namespace Godot
+{
+    public struct SignalInfo
+    {
+        private readonly Object _owner;
+        private readonly StringName _signalName;
+
+        public Object Owner => _owner;
+        public StringName Name => _signalName;
+
+        public SignalInfo(Object owner, StringName name)
+        {
+            _owner = owner;
+            _signalName = name;
+        }
+    }
+}

+ 82 - 0
modules/mono/glue/GodotSharp/GodotSharp/Core/StringName.cs

@@ -0,0 +1,82 @@
+using System;
+using System.Runtime.CompilerServices;
+
+namespace Godot
+{
+    public sealed partial class StringName : IDisposable
+    {
+        private IntPtr ptr;
+
+        internal static IntPtr GetPtr(StringName instance)
+        {
+            if (instance == null)
+                throw new NullReferenceException($"The instance of type {nameof(StringName)} is null.");
+
+            if (instance.ptr == IntPtr.Zero)
+                throw new ObjectDisposedException(instance.GetType().FullName);
+
+            return instance.ptr;
+        }
+
+        ~StringName()
+        {
+            Dispose(false);
+        }
+
+        public void Dispose()
+        {
+            Dispose(true);
+            GC.SuppressFinalize(this);
+        }
+
+        private void Dispose(bool disposing)
+        {
+            if (ptr != IntPtr.Zero)
+            {
+                godot_icall_StringName_Dtor(ptr);
+                ptr = IntPtr.Zero;
+            }
+        }
+
+        internal StringName(IntPtr ptr)
+        {
+            this.ptr = ptr;
+        }
+
+        public StringName()
+        {
+            ptr = IntPtr.Zero;
+        }
+
+        public StringName(string path)
+        {
+            ptr = path == null ? IntPtr.Zero : godot_icall_StringName_Ctor(path);
+        }
+
+        public static implicit operator StringName(string from) => new StringName(from);
+
+        public static implicit operator string(StringName from) => from.ToString();
+
+        public override string ToString()
+        {
+            return ptr == IntPtr.Zero ? string.Empty : godot_icall_StringName_operator_String(GetPtr(this));
+        }
+
+        public bool IsEmpty()
+        {
+            return ptr == IntPtr.Zero || godot_icall_StringName_is_empty(GetPtr(this));
+        }
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern IntPtr godot_icall_StringName_Ctor(string path);
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern void godot_icall_StringName_Dtor(IntPtr ptr);
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern string godot_icall_StringName_operator_String(IntPtr ptr);
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern bool godot_icall_StringName_is_empty(IntPtr ptr);
+    }
+}

+ 4 - 0
modules/mono/glue/GodotSharp/GodotSharp/GodotSharp.csproj

@@ -41,9 +41,11 @@
     <Compile Include="Core\Attributes\SignalAttribute.cs" />
     <Compile Include="Core\Attributes\ToolAttribute.cs" />
     <Compile Include="Core\Basis.cs" />
+    <Compile Include="Core\Callable.cs" />
     <Compile Include="Core\Color.cs" />
     <Compile Include="Core\Colors.cs" />
     <Compile Include="Core\DebuggingUtils.cs" />
+    <Compile Include="Core\DelegateUtils.cs" />
     <Compile Include="Core\Dictionary.cs" />
     <Compile Include="Core\Dispatcher.cs" />
     <Compile Include="Core\DynamicObject.cs" />
@@ -66,8 +68,10 @@
     <Compile Include="Core\Quat.cs" />
     <Compile Include="Core\Rect2.cs" />
     <Compile Include="Core\RID.cs" />
+    <Compile Include="Core\SignalInfo.cs" />
     <Compile Include="Core\SignalAwaiter.cs" />
     <Compile Include="Core\StringExtensions.cs" />
+    <Compile Include="Core\StringName.cs" />
     <Compile Include="Core\Transform.cs" />
     <Compile Include="Core\Transform2D.cs" />
     <Compile Include="Core\Vector2.cs" />

+ 18 - 11
modules/mono/glue/base_object_glue.cpp

@@ -126,18 +126,25 @@ void godot_icall_Reference_Disposed(MonoObject *p_obj, Object *p_ptr, MonoBoolea
 	}
 }
 
-MethodBind *godot_icall_Object_ClassDB_get_method(MonoString *p_type, MonoString *p_method) {
-	StringName type(GDMonoMarshal::mono_string_to_godot(p_type));
+void godot_icall_Object_ConnectEventSignals(Object *p_ptr) {
+	CSharpInstance *csharp_instance = CAST_CSHARP_INSTANCE(p_ptr->get_script_instance());
+	if (csharp_instance) {
+		csharp_instance->connect_event_signals();
+	}
+}
+
+MethodBind *godot_icall_Object_ClassDB_get_method(StringName *p_type, MonoString *p_method) {
+	StringName type = p_type ? *p_type : StringName();
 	StringName method(GDMonoMarshal::mono_string_to_godot(p_method));
 	return ClassDB::get_method(type, method);
 }
 
-MonoObject *godot_icall_Object_weakref(Object *p_obj) {
-	if (!p_obj)
+MonoObject *godot_icall_Object_weakref(Object *p_ptr) {
+	if (!p_ptr)
 		return NULL;
 
 	Ref<WeakRef> wref;
-	Reference *ref = Object::cast_to<Reference>(p_obj);
+	Reference *ref = Object::cast_to<Reference>(p_ptr);
 
 	if (ref) {
 		REF r = ref;
@@ -148,15 +155,15 @@ MonoObject *godot_icall_Object_weakref(Object *p_obj) {
 		wref->set_ref(r);
 	} else {
 		wref.instance();
-		wref->set_obj(p_obj);
+		wref->set_obj(p_ptr);
 	}
 
 	return GDMonoUtils::unmanaged_get_managed(wref.ptr());
 }
 
-Error godot_icall_SignalAwaiter_connect(Object *p_source, MonoString *p_signal, Object *p_target, MonoObject *p_awaiter) {
-	String signal = GDMonoMarshal::mono_string_to_godot(p_signal);
-	return SignalAwaiterUtils::connect_signal_awaiter(p_source, signal, p_target, p_awaiter);
+Error godot_icall_SignalAwaiter_connect(Object *p_source, StringName *p_signal, Object *p_target, MonoObject *p_awaiter) {
+	StringName signal = p_signal ? *p_signal : StringName();
+	return gd_mono_connect_signal_awaiter(p_source, signal, p_target, p_awaiter);
 }
 
 MonoArray *godot_icall_DynamicGodotObject_SetMemberList(Object *p_ptr) {
@@ -225,8 +232,8 @@ MonoString *godot_icall_Object_ToString(Object *p_ptr) {
 	// Cannot happen in C#; would get an ObjectDisposedException instead.
 	CRASH_COND(p_ptr == NULL);
 #endif
-
-	String result = p_ptr->to_string();
+	// Can't call 'Object::to_string()' here, as that can end up calling 'ToString' again resulting in an endless circular loop.
+	String result = "[" + p_ptr->get_class() + ":" + itos(p_ptr->get_instance_id()) + "]";
 	return GDMonoMarshal::mono_string_from_godot(result);
 }
 

+ 5 - 3
modules/mono/glue/base_object_glue.h

@@ -44,11 +44,13 @@ void godot_icall_Object_Disposed(MonoObject *p_obj, Object *p_ptr);
 
 void godot_icall_Reference_Disposed(MonoObject *p_obj, Object *p_ptr, MonoBoolean p_is_finalizer);
 
-MethodBind *godot_icall_Object_ClassDB_get_method(MonoString *p_type, MonoString *p_method);
+void godot_icall_Object_ConnectEventSignals(Object *p_ptr);
 
-MonoObject *godot_icall_Object_weakref(Object *p_obj);
+MethodBind *godot_icall_Object_ClassDB_get_method(StringName *p_type, MonoString *p_method);
 
-Error godot_icall_SignalAwaiter_connect(Object *p_source, MonoString *p_signal, Object *p_target, MonoObject *p_awaiter);
+MonoObject *godot_icall_Object_weakref(Object *p_ptr);
+
+Error godot_icall_SignalAwaiter_connect(Object *p_source, StringName *p_signal, Object *p_target, MonoObject *p_awaiter);
 
 // DynamicGodotObject
 

+ 8 - 2
modules/mono/glue/gd_glue.cpp

@@ -241,8 +241,9 @@ MonoObject *godot_icall_GD_str2var(MonoString *p_str) {
 	return GDMonoMarshal::variant_to_mono_object(ret);
 }
 
-MonoBoolean godot_icall_GD_type_exists(MonoString *p_type) {
-	return ClassDB::class_exists(GDMonoMarshal::mono_string_to_godot(p_type));
+MonoBoolean godot_icall_GD_type_exists(StringName *p_type) {
+	StringName type = p_type ? *p_type : StringName();
+	return ClassDB::class_exists(type);
 }
 
 void godot_icall_GD_pusherror(MonoString *p_str) {
@@ -273,6 +274,10 @@ MonoString *godot_icall_GD_var2str(MonoObject *p_var) {
 	return GDMonoMarshal::mono_string_from_godot(vars);
 }
 
+uint32_t godot_icall_TypeToVariantType(MonoReflectionType *p_refl_type) {
+	return (uint32_t)GDMonoMarshal::managed_to_variant_type(ManagedType::from_reftype(p_refl_type));
+}
+
 MonoObject *godot_icall_DefaultGodotTaskScheduler() {
 	return GDMonoCache::cached_data.task_scheduler_handle->get_target();
 }
@@ -300,6 +305,7 @@ void godot_register_gd_icalls() {
 	mono_add_internal_call("Godot.GD::godot_icall_GD_type_exists", (void *)godot_icall_GD_type_exists);
 	mono_add_internal_call("Godot.GD::godot_icall_GD_var2bytes", (void *)godot_icall_GD_var2bytes);
 	mono_add_internal_call("Godot.GD::godot_icall_GD_var2str", (void *)godot_icall_GD_var2str);
+	mono_add_internal_call("Godot.GD::godot_icall_TypeToVariantType", (void *)godot_icall_TypeToVariantType);
 
 	// Dispatcher
 	mono_add_internal_call("Godot.Dispatcher::godot_icall_DefaultGodotTaskScheduler", (void *)godot_icall_DefaultGodotTaskScheduler);

+ 1 - 1
modules/mono/glue/gd_glue.h

@@ -69,7 +69,7 @@ MonoString *godot_icall_GD_str(MonoArray *p_what);
 
 MonoObject *godot_icall_GD_str2var(MonoString *p_str);
 
-MonoBoolean godot_icall_GD_type_exists(MonoString *p_type);
+MonoBoolean godot_icall_GD_type_exists(StringName *p_type);
 
 MonoArray *godot_icall_GD_var2bytes(MonoObject *p_var, MonoBoolean p_full_objects);
 

+ 2 - 0
modules/mono/glue/glue_header.h

@@ -36,6 +36,7 @@
 #include "nodepath_glue.h"
 #include "rid_glue.h"
 #include "string_glue.h"
+#include "string_name_glue.h"
 
 /**
  * Registers internal calls that were not generated. This function is called
@@ -44,6 +45,7 @@
 void godot_register_glue_header_icalls() {
 	godot_register_collections_icalls();
 	godot_register_gd_icalls();
+	godot_register_string_name_icalls();
 	godot_register_nodepath_icalls();
 	godot_register_object_icalls();
 	godot_register_rid_icalls();

+ 21 - 67
modules/mono/utils/thread_local.cpp → modules/mono/glue/string_name_glue.cpp

@@ -1,5 +1,5 @@
 /*************************************************************************/
-/*  thread_local.cpp                                                     */
+/*  string_name_glue.cpp                                                 */
 /*************************************************************************/
 /*                       This file is part of:                           */
 /*                           GODOT ENGINE                                */
@@ -28,80 +28,34 @@
 /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                */
 /*************************************************************************/
 
-#include "thread_local.h"
+#include "string_name_glue.h"
 
-#ifdef WINDOWS_ENABLED
-#include <windows.h>
-#else
-#include <pthread.h>
-#endif
+#ifdef MONO_GLUE_ENABLED
 
-#include "core/os/memory.h"
-#include "core/print_string.h"
+#include "core/ustring.h"
 
-struct ThreadLocalStorage::Impl {
-
-#ifdef WINDOWS_ENABLED
-	DWORD dwFlsIndex;
-#else
-	pthread_key_t key;
-#endif
-
-	void *get_value() const {
-#ifdef WINDOWS_ENABLED
-		return FlsGetValue(dwFlsIndex);
-#else
-		return pthread_getspecific(key);
-#endif
-	}
-
-	void set_value(void *p_value) const {
-#ifdef WINDOWS_ENABLED
-		FlsSetValue(dwFlsIndex, p_value);
-#else
-		pthread_setspecific(key, p_value);
-#endif
-	}
-
-#ifdef WINDOWS_ENABLED
-#define _CALLBACK_FUNC_ __stdcall
-#else
-#define _CALLBACK_FUNC_
-#endif
-
-	Impl(void(_CALLBACK_FUNC_ *p_destr_callback_func)(void *)) {
-#ifdef WINDOWS_ENABLED
-		dwFlsIndex = FlsAlloc(p_destr_callback_func);
-		ERR_FAIL_COND(dwFlsIndex == FLS_OUT_OF_INDEXES);
-#else
-		pthread_key_create(&key, p_destr_callback_func);
-#endif
-	}
-
-	~Impl() {
-#ifdef WINDOWS_ENABLED
-		FlsFree(dwFlsIndex);
-#else
-		pthread_key_delete(key);
-#endif
-	}
-};
-
-void *ThreadLocalStorage::get_value() const {
-	return pimpl->get_value();
+StringName *godot_icall_StringName_Ctor(MonoString *p_path) {
+	return memnew(StringName(GDMonoMarshal::mono_string_to_godot(p_path)));
 }
 
-void ThreadLocalStorage::set_value(void *p_value) const {
-	pimpl->set_value(p_value);
+void godot_icall_StringName_Dtor(StringName *p_ptr) {
+	ERR_FAIL_NULL(p_ptr);
+	memdelete(p_ptr);
 }
 
-void ThreadLocalStorage::alloc(void(_CALLBACK_FUNC_ *p_destr_callback)(void *)) {
-	pimpl = memnew(ThreadLocalStorage::Impl(p_destr_callback));
+MonoString *godot_icall_StringName_operator_String(StringName *p_np) {
+	return GDMonoMarshal::mono_string_from_godot(p_np->operator String());
 }
 
-#undef _CALLBACK_FUNC_
+MonoBoolean godot_icall_StringName_is_empty(StringName *p_ptr) {
+	return (MonoBoolean)(p_ptr == StringName());
+}
 
-void ThreadLocalStorage::free() {
-	memdelete(pimpl);
-	pimpl = NULL;
+void godot_register_string_name_icalls() {
+	mono_add_internal_call("Godot.StringName::godot_icall_StringName_Ctor", (void *)godot_icall_StringName_Ctor);
+	mono_add_internal_call("Godot.StringName::godot_icall_StringName_Dtor", (void *)godot_icall_StringName_Dtor);
+	mono_add_internal_call("Godot.StringName::godot_icall_StringName_operator_String", (void *)godot_icall_StringName_operator_String);
+	mono_add_internal_call("Godot.StringName::godot_icall_StringName_is_empty", (void *)godot_icall_StringName_is_empty);
 }
+
+#endif // MONO_GLUE_ENABLED

+ 54 - 0
modules/mono/glue/string_name_glue.h

@@ -0,0 +1,54 @@
+/*************************************************************************/
+/*  string_name_glue.h                                                   */
+/*************************************************************************/
+/*                       This file is part of:                           */
+/*                           GODOT ENGINE                                */
+/*                      https://godotengine.org                          */
+/*************************************************************************/
+/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur.                 */
+/* Copyright (c) 2014-2020 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 STRING_NAME_GLUE_H
+#define STRING_NAME_GLUE_H
+
+#ifdef MONO_GLUE_ENABLED
+
+#include "core/string_name.h"
+
+#include "../mono_gd/gd_mono_marshal.h"
+
+StringName *godot_icall_StringName_Ctor(MonoString *p_path);
+
+void godot_icall_StringName_Dtor(StringName *p_ptr);
+
+MonoString *godot_icall_StringName_operator_String(StringName *p_np);
+
+MonoBoolean godot_icall_StringName_is_empty(StringName *p_ptr);
+
+// Register internal calls
+
+void godot_register_string_name_icalls();
+
+#endif // MONO_GLUE_ENABLED
+
+#endif // STRING_NAME_GLUE_H

+ 143 - 0
modules/mono/managed_callable.cpp

@@ -0,0 +1,143 @@
+/*************************************************************************/
+/*  managed_callable.cpp                                                 */
+/*************************************************************************/
+/*                       This file is part of:                           */
+/*                           GODOT ENGINE                                */
+/*                      https://godotengine.org                          */
+/*************************************************************************/
+/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur.                 */
+/* Copyright (c) 2014-2020 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 "managed_callable.h"
+
+#include "csharp_script.h"
+#include "mono_gd/gd_mono_marshal.h"
+#include "mono_gd/gd_mono_utils.h"
+
+#ifdef GD_MONO_HOT_RELOAD
+SelfList<ManagedCallable>::List ManagedCallable::instances;
+Map<ManagedCallable *, Array> ManagedCallable::instances_pending_reload;
+Mutex ManagedCallable::instances_mutex;
+#endif
+
+bool ManagedCallable::compare_equal(const CallableCustom *p_a, const CallableCustom *p_b) {
+	const ManagedCallable *a = static_cast<const ManagedCallable *>(p_a);
+	const ManagedCallable *b = static_cast<const ManagedCallable *>(p_b);
+
+	MonoDelegate *delegate_a = (MonoDelegate *)a->delegate_handle->get_target();
+	MonoDelegate *delegate_b = (MonoDelegate *)b->delegate_handle->get_target();
+
+	if (!delegate_a || !delegate_b) {
+		if (!delegate_a && !delegate_b)
+			return true;
+		return false;
+	}
+
+	// Call Delegate's 'Equals'
+	return GDMonoUtils::mono_delegate_equal(delegate_a, delegate_b);
+}
+
+bool ManagedCallable::compare_less(const CallableCustom *p_a, const CallableCustom *p_b) {
+	if (compare_equal(p_a, p_b))
+		return false;
+	return p_a < p_b;
+}
+
+uint32_t ManagedCallable::hash() const {
+	// hmm
+	uint32_t hash = delegate_invoke->get_name().hash();
+	return hash_djb2_one_64(delegate_handle.ptr()->handle, hash);
+}
+
+String ManagedCallable::get_as_text() const {
+	return "Delegate::Invoke";
+}
+
+CallableCustom::CompareEqualFunc ManagedCallable::get_compare_equal_func() const {
+	return compare_equal_func_ptr;
+}
+
+CallableCustom::CompareLessFunc ManagedCallable::get_compare_less_func() const {
+	return compare_less_func_ptr;
+}
+
+ObjectID ManagedCallable::get_object() const {
+	return CSharpLanguage::get_singleton()->get_managed_callable_middleman()->get_instance_id();
+}
+
+void ManagedCallable::call(const Variant **p_arguments, int p_argcount, Variant &r_return_value, Callable::CallError &r_call_error) const {
+	r_call_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD; // Can't find anything better
+	r_return_value = Variant();
+
+#ifdef GD_MONO_HOT_RELOAD
+	// Lost during hot-reload
+	ERR_FAIL_NULL(delegate_invoke);
+	ERR_FAIL_COND(delegate_handle.is_null());
+#endif
+
+	ERR_FAIL_COND(delegate_invoke->get_parameters_count() < p_argcount);
+
+	MonoObject *delegate = delegate_handle->get_target();
+
+	MonoException *exc = NULL;
+	MonoObject *ret = delegate_invoke->invoke(delegate, p_arguments, &exc);
+
+	if (exc) {
+		GDMonoUtils::set_pending_exception(exc);
+	} else {
+		r_return_value = GDMonoMarshal::mono_object_to_variant(ret);
+		r_call_error.error = Callable::CallError::CALL_OK;
+	}
+}
+
+void ManagedCallable::set_delegate(MonoDelegate *p_delegate) {
+	delegate_handle = MonoGCHandle::create_strong((MonoObject *)p_delegate);
+	MonoMethod *delegate_invoke_raw = mono_get_delegate_invoke(mono_object_get_class((MonoObject *)p_delegate));
+	const StringName &delegate_invoke_name = CSharpLanguage::get_singleton()->get_string_names().delegate_invoke_method_name;
+	delegate_invoke = memnew(GDMonoMethod(delegate_invoke_name, delegate_invoke_raw)); // TODO: Use pooling for this GDMonoMethod instances
+}
+
+ManagedCallable::ManagedCallable(MonoDelegate *p_delegate) {
+#ifdef DEBUG_ENABLED
+	CRASH_COND(p_delegate == NULL);
+#endif
+
+	set_delegate(p_delegate);
+
+#ifdef GD_MONO_HOT_RELOAD
+	{
+		MutexLock lock(instances_mutex);
+		instances.add(&self_instance);
+	}
+#endif
+}
+
+#ifdef GD_MONO_HOT_RELOAD
+ManagedCallable::~ManagedCallable() {
+	{
+		MutexLock lock(instances_mutex);
+		instances.remove(&self_instance);
+		instances_pending_reload.erase(this);
+	}
+}
+#endif

+ 79 - 0
modules/mono/managed_callable.h

@@ -0,0 +1,79 @@
+/*************************************************************************/
+/*  managed_callable.h                                                   */
+/*************************************************************************/
+/*                       This file is part of:                           */
+/*                           GODOT ENGINE                                */
+/*                      https://godotengine.org                          */
+/*************************************************************************/
+/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur.                 */
+/* Copyright (c) 2014-2020 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 MANAGED_CALLABLE_H
+#define MANAGED_CALLABLE_H
+
+#include <mono/metadata/object.h>
+
+#include "core/callable.h"
+#include "core/os/mutex.h"
+#include "core/self_list.h"
+
+#include "mono_gc_handle.h"
+#include "mono_gd/gd_mono_method.h"
+
+class ManagedCallable : public CallableCustom {
+	friend class CSharpLanguage;
+	Ref<MonoGCHandle> delegate_handle;
+	GDMonoMethod *delegate_invoke;
+
+#ifdef GD_MONO_HOT_RELOAD
+	SelfList<ManagedCallable> self_instance = this;
+	static SelfList<ManagedCallable>::List instances;
+	static Map<ManagedCallable *, Array> instances_pending_reload;
+	static Mutex instances_mutex;
+#endif
+
+public:
+	uint32_t hash() const override;
+	String get_as_text() const override;
+	CompareEqualFunc get_compare_equal_func() const override;
+	CompareLessFunc get_compare_less_func() const override;
+	ObjectID get_object() const override;
+	void call(const Variant **p_arguments, int p_argcount, Variant &r_return_value, Callable::CallError &r_call_error) const override;
+
+	_FORCE_INLINE_ MonoDelegate *get_delegate() { return (MonoDelegate *)delegate_handle->get_target(); }
+
+	void set_delegate(MonoDelegate *p_delegate);
+
+	static bool compare_equal(const CallableCustom *p_a, const CallableCustom *p_b);
+	static bool compare_less(const CallableCustom *p_a, const CallableCustom *p_b);
+
+	static constexpr CompareEqualFunc compare_equal_func_ptr = &ManagedCallable::compare_equal;
+	static constexpr CompareEqualFunc compare_less_func_ptr = &ManagedCallable::compare_less;
+
+	ManagedCallable(MonoDelegate *p_delegate);
+#ifdef GD_MONO_HOT_RELOAD
+	~ManagedCallable();
+#endif
+};
+
+#endif // MANAGED_CALLABLE_H

+ 2 - 0
modules/mono/mono_gc_handle.h

@@ -41,6 +41,8 @@ class MonoGCHandle : public Reference {
 
 	bool released;
 	bool weak;
+
+	friend class ManagedCallable;
 	uint32_t handle;
 
 public:

+ 1 - 1
modules/mono/mono_gd/gd_mono.h

@@ -203,7 +203,7 @@ public:
 
 	static GDMono *get_singleton() { return singleton; }
 
-	GD_NORETURN static void unhandled_exception_hook(MonoObject *p_exc, void *p_user_data);
+	[[noreturn]] static void unhandled_exception_hook(MonoObject *p_exc, void *p_user_data);
 
 	UnhandledExceptionPolicy get_unhandled_exception_policy() const { return unhandled_exception_policy; }
 

+ 1 - 1
modules/mono/mono_gd/gd_mono_assembly.cpp

@@ -148,7 +148,7 @@ MonoAssembly *GDMonoAssembly::_search_hook(MonoAssemblyName *aname, void *user_d
 	return res ? res->get_assembly() : NULL;
 }
 
-static _THREAD_LOCAL_(MonoImage *) image_corlib_loading = NULL;
+static thread_local MonoImage *image_corlib_loading = NULL;
 
 MonoAssembly *GDMonoAssembly::_preload_hook(MonoAssemblyName *aname, char **, void *user_data, bool refonly) {
 

+ 18 - 2
modules/mono/mono_gd/gd_mono_cache.cpp

@@ -112,6 +112,7 @@ void CachedData::clear_godot_api_cache() {
 	class_AABB = NULL;
 	class_Color = NULL;
 	class_Plane = NULL;
+	class_StringName = NULL;
 	class_NodePath = NULL;
 	class_RID = NULL;
 	class_GodotObject = NULL;
@@ -120,6 +121,8 @@ void CachedData::clear_godot_api_cache() {
 	class_Control = NULL;
 	class_Spatial = NULL;
 	class_WeakRef = NULL;
+	class_Callable = NULL;
+	class_SignalInfo = NULL;
 	class_Array = NULL;
 	class_Dictionary = NULL;
 	class_MarshalUtils = NULL;
@@ -145,6 +148,7 @@ void CachedData::clear_godot_api_cache() {
 	field_GodotMethodAttribute_methodName = NULL;
 
 	field_GodotObject_ptr = NULL;
+	field_StringName_ptr = NULL;
 	field_NodePath_ptr = NULL;
 	field_Image_ptr = NULL;
 	field_RID_ptr = NULL;
@@ -153,9 +157,13 @@ void CachedData::clear_godot_api_cache() {
 	methodthunk_Array_GetPtr.nullify();
 	methodthunk_Dictionary_GetPtr.nullify();
 	methodthunk_SignalAwaiter_SignalCallback.nullify();
-	methodthunk_SignalAwaiter_FailureCallback.nullify();
 	methodthunk_GodotTaskScheduler_Activate.nullify();
 
+	methodthunk_Delegate_Equals.nullify();
+
+	methodthunk_DelegateUtils_TrySerializeDelegate.nullify();
+	methodthunk_DelegateUtils_TryDeserializeDelegate.nullify();
+
 	// Start of MarshalUtils methods
 
 	methodthunk_MarshalUtils_TypeIsGenericArray.nullify();
@@ -211,6 +219,8 @@ void update_corlib_cache() {
 	CACHE_METHOD_AND_CHECK(System_Diagnostics_StackTrace, ctor_Exception_bool, CACHED_CLASS(System_Diagnostics_StackTrace)->get_method_with_desc("System.Diagnostics.StackTrace:.ctor(System.Exception,bool)", true));
 #endif
 
+	CACHE_METHOD_THUNK_AND_CHECK(Delegate, Equals, GDMono::get_singleton()->get_corlib_assembly()->get_class("System", "Delegate")->get_method_with_desc("System.Delegate:Equals(object)", 1));
+
 	CACHE_CLASS_AND_CHECK(KeyNotFoundException, GDMono::get_singleton()->get_corlib_assembly()->get_class("System.Collections.Generic", "KeyNotFoundException"));
 
 	cached_data.corlib_cache_updated = true;
@@ -228,6 +238,7 @@ void update_godot_api_cache() {
 	CACHE_CLASS_AND_CHECK(AABB, GODOT_API_CLASS(AABB));
 	CACHE_CLASS_AND_CHECK(Color, GODOT_API_CLASS(Color));
 	CACHE_CLASS_AND_CHECK(Plane, GODOT_API_CLASS(Plane));
+	CACHE_CLASS_AND_CHECK(StringName, GODOT_API_CLASS(StringName));
 	CACHE_CLASS_AND_CHECK(NodePath, GODOT_API_CLASS(NodePath));
 	CACHE_CLASS_AND_CHECK(RID, GODOT_API_CLASS(RID));
 	CACHE_CLASS_AND_CHECK(GodotObject, GODOT_API_CLASS(Object));
@@ -236,6 +247,8 @@ void update_godot_api_cache() {
 	CACHE_CLASS_AND_CHECK(Control, GODOT_API_CLASS(Control));
 	CACHE_CLASS_AND_CHECK(Spatial, GODOT_API_CLASS(Spatial));
 	CACHE_CLASS_AND_CHECK(WeakRef, GODOT_API_CLASS(WeakRef));
+	CACHE_CLASS_AND_CHECK(Callable, GODOT_API_CLASS(Callable));
+	CACHE_CLASS_AND_CHECK(SignalInfo, GODOT_API_CLASS(SignalInfo));
 	CACHE_CLASS_AND_CHECK(Array, GODOT_API_NS_CLASS(BINDINGS_NAMESPACE_COLLECTIONS, Array));
 	CACHE_CLASS_AND_CHECK(Dictionary, GODOT_API_NS_CLASS(BINDINGS_NAMESPACE_COLLECTIONS, Dictionary));
 	CACHE_CLASS_AND_CHECK(MarshalUtils, GODOT_API_CLASS(MarshalUtils));
@@ -261,6 +274,7 @@ void update_godot_api_cache() {
 	CACHE_FIELD_AND_CHECK(GodotMethodAttribute, methodName, CACHED_CLASS(GodotMethodAttribute)->get_field("methodName"));
 
 	CACHE_FIELD_AND_CHECK(GodotObject, ptr, CACHED_CLASS(GodotObject)->get_field(BINDINGS_PTR_FIELD));
+	CACHE_FIELD_AND_CHECK(StringName, ptr, CACHED_CLASS(StringName)->get_field(BINDINGS_PTR_FIELD));
 	CACHE_FIELD_AND_CHECK(NodePath, ptr, CACHED_CLASS(NodePath)->get_field(BINDINGS_PTR_FIELD));
 	CACHE_FIELD_AND_CHECK(RID, ptr, CACHED_CLASS(RID)->get_field(BINDINGS_PTR_FIELD));
 
@@ -268,9 +282,11 @@ void update_godot_api_cache() {
 	CACHE_METHOD_THUNK_AND_CHECK(Array, GetPtr, GODOT_API_NS_CLASS(BINDINGS_NAMESPACE_COLLECTIONS, Array)->get_method("GetPtr", 0));
 	CACHE_METHOD_THUNK_AND_CHECK(Dictionary, GetPtr, GODOT_API_NS_CLASS(BINDINGS_NAMESPACE_COLLECTIONS, Dictionary)->get_method("GetPtr", 0));
 	CACHE_METHOD_THUNK_AND_CHECK(SignalAwaiter, SignalCallback, GODOT_API_CLASS(SignalAwaiter)->get_method("SignalCallback", 1));
-	CACHE_METHOD_THUNK_AND_CHECK(SignalAwaiter, FailureCallback, GODOT_API_CLASS(SignalAwaiter)->get_method("FailureCallback", 0));
 	CACHE_METHOD_THUNK_AND_CHECK(GodotTaskScheduler, Activate, GODOT_API_CLASS(GodotTaskScheduler)->get_method("Activate", 0));
 
+	CACHE_METHOD_THUNK_AND_CHECK(DelegateUtils, TrySerializeDelegate, GODOT_API_CLASS(DelegateUtils)->get_method("TrySerializeDelegate", 2));
+	CACHE_METHOD_THUNK_AND_CHECK(DelegateUtils, TryDeserializeDelegate, GODOT_API_CLASS(DelegateUtils)->get_method("TryDeserializeDelegate", 2));
+
 	// Start of MarshalUtils methods
 
 	CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, TypeIsGenericArray, GODOT_API_CLASS(MarshalUtils)->get_method("TypeIsGenericArray", 1));

+ 9 - 7
modules/mono/mono_gd/gd_mono_cache.h

@@ -82,6 +82,7 @@ struct CachedData {
 	GDMonoClass *class_AABB;
 	GDMonoClass *class_Color;
 	GDMonoClass *class_Plane;
+	GDMonoClass *class_StringName;
 	GDMonoClass *class_NodePath;
 	GDMonoClass *class_RID;
 	GDMonoClass *class_GodotObject;
@@ -90,6 +91,8 @@ struct CachedData {
 	GDMonoClass *class_Control;
 	GDMonoClass *class_Spatial;
 	GDMonoClass *class_WeakRef;
+	GDMonoClass *class_Callable;
+	GDMonoClass *class_SignalInfo;
 	GDMonoClass *class_Array;
 	GDMonoClass *class_Dictionary;
 	GDMonoClass *class_MarshalUtils;
@@ -115,6 +118,7 @@ struct CachedData {
 	GDMonoField *field_GodotMethodAttribute_methodName;
 
 	GDMonoField *field_GodotObject_ptr;
+	GDMonoField *field_StringName_ptr;
 	GDMonoField *field_NodePath_ptr;
 	GDMonoField *field_Image_ptr;
 	GDMonoField *field_RID_ptr;
@@ -123,9 +127,13 @@ struct CachedData {
 	GDMonoMethodThunkR<Array *, MonoObject *> methodthunk_Array_GetPtr;
 	GDMonoMethodThunkR<Dictionary *, MonoObject *> methodthunk_Dictionary_GetPtr;
 	GDMonoMethodThunk<MonoObject *, MonoArray *> methodthunk_SignalAwaiter_SignalCallback;
-	GDMonoMethodThunk<MonoObject *> methodthunk_SignalAwaiter_FailureCallback;
 	GDMonoMethodThunk<MonoObject *> methodthunk_GodotTaskScheduler_Activate;
 
+	GDMonoMethodThunkR<MonoBoolean, MonoObject *, MonoObject *> methodthunk_Delegate_Equals;
+
+	GDMonoMethodThunkR<MonoBoolean, MonoDelegate *, MonoObject *> methodthunk_DelegateUtils_TrySerializeDelegate;
+	GDMonoMethodThunkR<MonoBoolean, MonoObject *, MonoDelegate **> methodthunk_DelegateUtils_TryDeserializeDelegate;
+
 	// Start of MarshalUtils methods
 
 	GDMonoMethodThunkR<MonoBoolean, MonoReflectionType *> methodthunk_MarshalUtils_TypeIsGenericArray;
@@ -193,10 +201,4 @@ _FORCE_INLINE_ bool tools_godot_api_check() {
 #define CACHED_METHOD_THUNK(m_class, m_method) (GDMonoCache::cached_data.methodthunk_##m_class##_##m_method)
 #define CACHED_PROPERTY(m_class, m_property) (GDMonoCache::cached_data.property_##m_class##_##m_property)
 
-#ifdef REAL_T_IS_DOUBLE
-#define REAL_T_MONOCLASS CACHED_CLASS_RAW(double)
-#else
-#define REAL_T_MONOCLASS CACHED_CLASS_RAW(float)
-#endif
-
 #endif // GD_MONO_CACHE_H

+ 52 - 3
modules/mono/mono_gd/gd_mono_field.cpp

@@ -37,6 +37,10 @@
 #include "gd_mono_marshal.h"
 #include "gd_mono_utils.h"
 
+void GDMonoField::set_value(MonoObject *p_object, MonoObject *p_value) {
+	mono_field_set_value(p_object, mono_field, p_value);
+}
+
 void GDMonoField::set_value_raw(MonoObject *p_object, void *p_ptr) {
 	mono_field_set_value(p_object, mono_field, &p_ptr);
 }
@@ -173,6 +177,18 @@ void GDMonoField::set_value_from_variant(MonoObject *p_object, const Variant &p_
 				break;
 			}
 
+			if (tclass == CACHED_CLASS(Callable)) {
+				GDMonoMarshal::M_Callable val = GDMonoMarshal::callable_to_managed(p_value.operator Callable());
+				mono_field_set_value(p_object, mono_field, &val);
+				break;
+			}
+
+			if (tclass == CACHED_CLASS(SignalInfo)) {
+				GDMonoMarshal::M_SignalInfo val = GDMonoMarshal::signal_info_to_managed(p_value.operator Signal());
+				mono_field_set_value(p_object, mono_field, &val);
+				break;
+			}
+
 			if (mono_class_is_enum(tclass->get_mono_ptr())) {
 				MonoType *enum_basetype = mono_class_enum_basetype(tclass->get_mono_ptr());
 				switch (mono_type_get_type(enum_basetype)) {
@@ -256,11 +272,21 @@ void GDMonoField::set_value_from_variant(MonoObject *p_object, const Variant &p_
 				break;
 			}
 
-			if (array_type->eklass == REAL_T_MONOCLASS) {
+			if (array_type->eklass == CACHED_CLASS_RAW(int64_t)) {
+				SET_FROM_ARRAY(PackedInt64Array);
+				break;
+			}
+
+			if (array_type->eklass == CACHED_CLASS_RAW(float)) {
 				SET_FROM_ARRAY(PackedFloat32Array);
 				break;
 			}
 
+			if (array_type->eklass == CACHED_CLASS_RAW(double)) {
+				SET_FROM_ARRAY(PackedFloat64Array);
+				break;
+			}
+
 			if (array_type->eklass == CACHED_CLASS_RAW(String)) {
 				SET_FROM_ARRAY(PackedStringArray);
 				break;
@@ -294,6 +320,12 @@ void GDMonoField::set_value_from_variant(MonoObject *p_object, const Variant &p_
 				break;
 			}
 
+			if (CACHED_CLASS(StringName) == type_class) {
+				MonoObject *managed = GDMonoUtils::create_managed_from(p_value.operator StringName());
+				mono_field_set_value(p_object, mono_field, managed);
+				break;
+			}
+
 			if (CACHED_CLASS(NodePath) == type_class) {
 				MonoObject *managed = GDMonoUtils::create_managed_from(p_value.operator NodePath());
 				mono_field_set_value(p_object, mono_field, managed);
@@ -413,6 +445,10 @@ void GDMonoField::set_value_from_variant(MonoObject *p_object, const Variant &p_
 				case Variant::COLOR: {
 					SET_FROM_STRUCT(Color);
 				} break;
+				case Variant::STRING_NAME: {
+					MonoObject *managed = GDMonoUtils::create_managed_from(p_value.operator StringName());
+					mono_field_set_value(p_object, mono_field, managed);
+				} break;
 				case Variant::NODE_PATH: {
 					MonoObject *managed = GDMonoUtils::create_managed_from(p_value.operator NodePath());
 					mono_field_set_value(p_object, mono_field, managed);
@@ -424,8 +460,15 @@ void GDMonoField::set_value_from_variant(MonoObject *p_object, const Variant &p_
 				case Variant::OBJECT: {
 					MonoObject *managed = GDMonoUtils::unmanaged_get_managed(p_value.operator Object *());
 					mono_field_set_value(p_object, mono_field, managed);
-					break;
-				}
+				} break;
+				case Variant::CALLABLE: {
+					GDMonoMarshal::M_Callable val = GDMonoMarshal::callable_to_managed(p_value.operator Callable());
+					mono_field_set_value(p_object, mono_field, &val);
+				} break;
+				case Variant::SIGNAL: {
+					GDMonoMarshal::M_SignalInfo val = GDMonoMarshal::signal_info_to_managed(p_value.operator Signal());
+					mono_field_set_value(p_object, mono_field, &val);
+				} break;
 				case Variant::DICTIONARY: {
 					MonoObject *managed = GDMonoUtils::create_managed_from(p_value.operator Dictionary(), CACHED_CLASS(Dictionary));
 					mono_field_set_value(p_object, mono_field, managed);
@@ -440,9 +483,15 @@ void GDMonoField::set_value_from_variant(MonoObject *p_object, const Variant &p_
 				case Variant::PACKED_INT32_ARRAY: {
 					SET_FROM_ARRAY(PackedInt32Array);
 				} break;
+				case Variant::PACKED_INT64_ARRAY: {
+					SET_FROM_ARRAY(PackedInt64Array);
+				} break;
 				case Variant::PACKED_FLOAT32_ARRAY: {
 					SET_FROM_ARRAY(PackedFloat32Array);
 				} break;
+				case Variant::PACKED_FLOAT64_ARRAY: {
+					SET_FROM_ARRAY(PackedFloat64Array);
+				} break;
 				case Variant::PACKED_STRING_ARRAY: {
 					SET_FROM_ARRAY(PackedStringArray);
 				} break;

+ 8 - 7
modules/mono/mono_gd/gd_mono_field.h

@@ -47,21 +47,22 @@ class GDMonoField : public IMonoClassMember {
 	MonoCustomAttrInfo *attributes;
 
 public:
-	virtual GDMonoClass *get_enclosing_class() const GD_FINAL { return owner; }
+	virtual GDMonoClass *get_enclosing_class() const final { return owner; }
 
-	virtual MemberType get_member_type() const GD_FINAL { return MEMBER_TYPE_FIELD; }
+	virtual MemberType get_member_type() const final { return MEMBER_TYPE_FIELD; }
 
-	virtual StringName get_name() const GD_FINAL { return name; }
+	virtual StringName get_name() const final { return name; }
 
-	virtual bool is_static() GD_FINAL;
-	virtual Visibility get_visibility() GD_FINAL;
+	virtual bool is_static() final;
+	virtual Visibility get_visibility() final;
 
-	virtual bool has_attribute(GDMonoClass *p_attr_class) GD_FINAL;
-	virtual MonoObject *get_attribute(GDMonoClass *p_attr_class) GD_FINAL;
+	virtual bool has_attribute(GDMonoClass *p_attr_class) final;
+	virtual MonoObject *get_attribute(GDMonoClass *p_attr_class) final;
 	void fetch_attributes();
 
 	_FORCE_INLINE_ ManagedType get_type() const { return type; }
 
+	void set_value(MonoObject *p_object, MonoObject *p_value);
 	void set_value_raw(MonoObject *p_object, void *p_ptr);
 	void set_value_from_variant(MonoObject *p_object, const Variant &p_value);
 

+ 4 - 3
modules/mono/mono_gd/gd_mono_internals.cpp

@@ -33,7 +33,6 @@
 #include "../csharp_script.h"
 #include "../mono_gc_handle.h"
 #include "../utils/macros.h"
-#include "../utils/thread_local.h"
 #include "gd_mono_class.h"
 #include "gd_mono_marshal.h"
 #include "gd_mono_utils.h"
@@ -108,9 +107,11 @@ void tie_managed_to_unmanaged(MonoObject *managed, Object *unmanaged) {
 
 	CRASH_COND(script.is_null());
 
-	ScriptInstance *si = CSharpInstance::create_for_managed_type(unmanaged, script.ptr(), gchandle);
+	CSharpInstance *csharp_instance = CSharpInstance::create_for_managed_type(unmanaged, script.ptr(), gchandle);
 
-	unmanaged->set_script_and_instance(script, si);
+	unmanaged->set_script_and_instance(script, csharp_instance);
+
+	csharp_instance->connect_event_signals();
 }
 
 void unhandled_exception(MonoException *p_exc) {

+ 297 - 71
modules/mono/mono_gd/gd_mono_marshal.cpp

@@ -30,13 +30,14 @@
 
 #include "gd_mono_marshal.h"
 
+#include "../signal_awaiter_utils.h"
 #include "gd_mono.h"
 #include "gd_mono_cache.h"
 #include "gd_mono_class.h"
 
 namespace GDMonoMarshal {
 
-Variant::Type managed_to_variant_type(const ManagedType &p_type) {
+Variant::Type managed_to_variant_type(const ManagedType &p_type, bool *r_nil_is_variant) {
 	switch (p_type.type_encoding) {
 		case MONO_TYPE_BOOLEAN:
 			return Variant::BOOL;
@@ -101,6 +102,12 @@ Variant::Type managed_to_variant_type(const ManagedType &p_type) {
 			if (vtclass == CACHED_CLASS(Plane))
 				return Variant::PLANE;
 
+			if (vtclass == CACHED_CLASS(Callable))
+				return Variant::CALLABLE;
+
+			if (vtclass == CACHED_CLASS(SignalInfo))
+				return Variant::SIGNAL;
+
 			if (mono_class_is_enum(vtclass->get_mono_ptr()))
 				return Variant::INT;
 		} break;
@@ -118,9 +125,15 @@ Variant::Type managed_to_variant_type(const ManagedType &p_type) {
 			if (array_type->eklass == CACHED_CLASS_RAW(int32_t))
 				return Variant::PACKED_INT32_ARRAY;
 
-			if (array_type->eklass == REAL_T_MONOCLASS)
+			if (array_type->eklass == CACHED_CLASS_RAW(int64_t))
+				return Variant::PACKED_INT64_ARRAY;
+
+			if (array_type->eklass == CACHED_CLASS_RAW(float))
 				return Variant::PACKED_FLOAT32_ARRAY;
 
+			if (array_type->eklass == CACHED_CLASS_RAW(double))
+				return Variant::PACKED_FLOAT64_ARRAY;
+
 			if (array_type->eklass == CACHED_CLASS_RAW(String))
 				return Variant::PACKED_STRING_ARRAY;
 
@@ -142,6 +155,10 @@ Variant::Type managed_to_variant_type(const ManagedType &p_type) {
 				return Variant::OBJECT;
 			}
 
+			if (CACHED_CLASS(StringName) == type_class) {
+				return Variant::STRING_NAME;
+			}
+
 			if (CACHED_CLASS(NodePath) == type_class) {
 				return Variant::NODE_PATH;
 			}
@@ -179,6 +196,12 @@ Variant::Type managed_to_variant_type(const ManagedType &p_type) {
 			}
 		} break;
 
+		case MONO_TYPE_OBJECT: {
+			if (r_nil_is_variant)
+				*r_nil_is_variant = true;
+			return Variant::NIL;
+		} break;
+
 		case MONO_TYPE_GENERICINST: {
 			MonoReflectionType *reftype = mono_type_get_object(mono_domain_get(), p_type.type_class->get_mono_type());
 
@@ -211,6 +234,9 @@ Variant::Type managed_to_variant_type(const ManagedType &p_type) {
 		} break;
 	}
 
+	if (r_nil_is_variant)
+		*r_nil_is_variant = false;
+
 	// Unknown
 	return Variant::NIL;
 }
@@ -432,6 +458,16 @@ MonoObject *variant_to_mono_object(const Variant *p_var, const ManagedType &p_ty
 				return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(Plane), &from);
 			}
 
+			if (vtclass == CACHED_CLASS(Callable)) {
+				GDMonoMarshal::M_Callable from = GDMonoMarshal::callable_to_managed(p_var->operator Callable());
+				return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(Callable), &from);
+			}
+
+			if (vtclass == CACHED_CLASS(SignalInfo)) {
+				GDMonoMarshal::M_SignalInfo from = GDMonoMarshal::signal_info_to_managed(p_var->operator Signal());
+				return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(SignalInfo), &from);
+			}
+
 			if (mono_class_is_enum(vtclass->get_mono_ptr())) {
 				MonoType *enum_basetype = mono_class_enum_basetype(vtclass->get_mono_ptr());
 				MonoClass *enum_baseclass = mono_class_from_mono_type(enum_basetype);
@@ -496,9 +532,15 @@ MonoObject *variant_to_mono_object(const Variant *p_var, const ManagedType &p_ty
 			if (array_type->eklass == CACHED_CLASS_RAW(int32_t))
 				return (MonoObject *)PackedInt32Array_to_mono_array(p_var->operator PackedInt32Array());
 
-			if (array_type->eklass == REAL_T_MONOCLASS)
+			if (array_type->eklass == CACHED_CLASS_RAW(int64_t))
+				return (MonoObject *)PackedInt64Array_to_mono_array(p_var->operator PackedInt64Array());
+
+			if (array_type->eklass == CACHED_CLASS_RAW(float))
 				return (MonoObject *)PackedFloat32Array_to_mono_array(p_var->operator PackedFloat32Array());
 
+			if (array_type->eklass == CACHED_CLASS_RAW(double))
+				return (MonoObject *)PackedFloat64Array_to_mono_array(p_var->operator PackedFloat64Array());
+
 			if (array_type->eklass == CACHED_CLASS_RAW(String))
 				return (MonoObject *)PackedStringArray_to_mono_array(p_var->operator PackedStringArray());
 
@@ -522,6 +564,10 @@ MonoObject *variant_to_mono_object(const Variant *p_var, const ManagedType &p_ty
 				return GDMonoUtils::unmanaged_get_managed(p_var->operator Object *());
 			}
 
+			if (CACHED_CLASS(StringName) == type_class) {
+				return GDMonoUtils::create_managed_from(p_var->operator StringName());
+			}
+
 			if (CACHED_CLASS(NodePath) == type_class) {
 				return GDMonoUtils::create_managed_from(p_var->operator NodePath());
 			}
@@ -628,12 +674,22 @@ MonoObject *variant_to_mono_object(const Variant *p_var, const ManagedType &p_ty
 					GDMonoMarshal::M_Color from = MARSHALLED_OUT(Color, p_var->operator ::Color());
 					return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(Color), &from);
 				}
+				case Variant::STRING_NAME:
+					return GDMonoUtils::create_managed_from(p_var->operator StringName());
 				case Variant::NODE_PATH:
 					return GDMonoUtils::create_managed_from(p_var->operator NodePath());
 				case Variant::_RID:
 					return GDMonoUtils::create_managed_from(p_var->operator RID());
 				case Variant::OBJECT:
 					return GDMonoUtils::unmanaged_get_managed(p_var->operator Object *());
+				case Variant::CALLABLE: {
+					GDMonoMarshal::M_Callable from = GDMonoMarshal::callable_to_managed(p_var->operator Callable());
+					return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(Callable), &from);
+				}
+				case Variant::SIGNAL: {
+					GDMonoMarshal::M_SignalInfo from = GDMonoMarshal::signal_info_to_managed(p_var->operator Signal());
+					return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(SignalInfo), &from);
+				}
 				case Variant::DICTIONARY:
 					return GDMonoUtils::create_managed_from(p_var->operator Dictionary(), CACHED_CLASS(Dictionary));
 				case Variant::ARRAY:
@@ -642,8 +698,12 @@ MonoObject *variant_to_mono_object(const Variant *p_var, const ManagedType &p_ty
 					return (MonoObject *)PackedByteArray_to_mono_array(p_var->operator PackedByteArray());
 				case Variant::PACKED_INT32_ARRAY:
 					return (MonoObject *)PackedInt32Array_to_mono_array(p_var->operator PackedInt32Array());
+				case Variant::PACKED_INT64_ARRAY:
+					return (MonoObject *)PackedInt64Array_to_mono_array(p_var->operator PackedInt64Array());
 				case Variant::PACKED_FLOAT32_ARRAY:
 					return (MonoObject *)PackedFloat32Array_to_mono_array(p_var->operator PackedFloat32Array());
+				case Variant::PACKED_FLOAT64_ARRAY:
+					return (MonoObject *)PackedFloat64Array_to_mono_array(p_var->operator PackedFloat64Array());
 				case Variant::PACKED_STRING_ARRAY:
 					return (MonoObject *)PackedStringArray_to_mono_array(p_var->operator PackedStringArray());
 				case Variant::PACKED_VECTOR2_ARRAY:
@@ -744,34 +804,40 @@ Variant mono_object_to_variant_impl(MonoObject *p_obj, const ManagedType &p_type
 			GDMonoClass *vtclass = p_type.type_class;
 
 			if (vtclass == CACHED_CLASS(Vector2))
-				return MARSHALLED_IN(Vector2, (GDMonoMarshal::M_Vector2 *)mono_object_unbox(p_obj));
+				return MARSHALLED_IN(Vector2, unbox_addr<GDMonoMarshal::M_Vector2>(p_obj));
 
 			if (vtclass == CACHED_CLASS(Rect2))
-				return MARSHALLED_IN(Rect2, (GDMonoMarshal::M_Rect2 *)mono_object_unbox(p_obj));
+				return MARSHALLED_IN(Rect2, unbox_addr<GDMonoMarshal::M_Rect2>(p_obj));
 
 			if (vtclass == CACHED_CLASS(Transform2D))
-				return MARSHALLED_IN(Transform2D, (GDMonoMarshal::M_Transform2D *)mono_object_unbox(p_obj));
+				return MARSHALLED_IN(Transform2D, unbox_addr<GDMonoMarshal::M_Transform2D>(p_obj));
 
 			if (vtclass == CACHED_CLASS(Vector3))
-				return MARSHALLED_IN(Vector3, (GDMonoMarshal::M_Vector3 *)mono_object_unbox(p_obj));
+				return MARSHALLED_IN(Vector3, unbox_addr<GDMonoMarshal::M_Vector3>(p_obj));
 
 			if (vtclass == CACHED_CLASS(Basis))
-				return MARSHALLED_IN(Basis, (GDMonoMarshal::M_Basis *)mono_object_unbox(p_obj));
+				return MARSHALLED_IN(Basis, unbox_addr<GDMonoMarshal::M_Basis>(p_obj));
 
 			if (vtclass == CACHED_CLASS(Quat))
-				return MARSHALLED_IN(Quat, (GDMonoMarshal::M_Quat *)mono_object_unbox(p_obj));
+				return MARSHALLED_IN(Quat, unbox_addr<GDMonoMarshal::M_Quat>(p_obj));
 
 			if (vtclass == CACHED_CLASS(Transform))
-				return MARSHALLED_IN(Transform, (GDMonoMarshal::M_Transform *)mono_object_unbox(p_obj));
+				return MARSHALLED_IN(Transform, unbox_addr<GDMonoMarshal::M_Transform>(p_obj));
 
 			if (vtclass == CACHED_CLASS(AABB))
-				return MARSHALLED_IN(AABB, (GDMonoMarshal::M_AABB *)mono_object_unbox(p_obj));
+				return MARSHALLED_IN(AABB, unbox_addr<GDMonoMarshal::M_AABB>(p_obj));
 
 			if (vtclass == CACHED_CLASS(Color))
-				return MARSHALLED_IN(Color, (GDMonoMarshal::M_Color *)mono_object_unbox(p_obj));
+				return MARSHALLED_IN(Color, unbox_addr<GDMonoMarshal::M_Color>(p_obj));
 
 			if (vtclass == CACHED_CLASS(Plane))
-				return MARSHALLED_IN(Plane, (GDMonoMarshal::M_Plane *)mono_object_unbox(p_obj));
+				return MARSHALLED_IN(Plane, unbox_addr<GDMonoMarshal::M_Plane>(p_obj));
+
+			if (vtclass == CACHED_CLASS(Callable))
+				return managed_to_callable(unbox<GDMonoMarshal::M_Callable>(p_obj));
+
+			if (vtclass == CACHED_CLASS(SignalInfo))
+				return managed_to_signal_info(unbox<GDMonoMarshal::M_SignalInfo>(p_obj));
 
 			if (mono_class_is_enum(vtclass->get_mono_ptr()))
 				return unbox<int32_t>(p_obj);
@@ -790,9 +856,15 @@ Variant mono_object_to_variant_impl(MonoObject *p_obj, const ManagedType &p_type
 			if (array_type->eklass == CACHED_CLASS_RAW(int32_t))
 				return mono_array_to_PackedInt32Array((MonoArray *)p_obj);
 
-			if (array_type->eklass == REAL_T_MONOCLASS)
+			if (array_type->eklass == CACHED_CLASS_RAW(int64_t))
+				return mono_array_to_PackedInt64Array((MonoArray *)p_obj);
+
+			if (array_type->eklass == CACHED_CLASS_RAW(float))
 				return mono_array_to_PackedFloat32Array((MonoArray *)p_obj);
 
+			if (array_type->eklass == CACHED_CLASS_RAW(double))
+				return mono_array_to_PackedFloat64Array((MonoArray *)p_obj);
+
 			if (array_type->eklass == CACHED_CLASS_RAW(String))
 				return mono_array_to_PackedStringArray((MonoArray *)p_obj);
 
@@ -825,6 +897,11 @@ Variant mono_object_to_variant_impl(MonoObject *p_obj, const ManagedType &p_type
 				return Variant();
 			}
 
+			if (CACHED_CLASS(StringName) == type_class) {
+				StringName *ptr = unbox<StringName *>(CACHED_FIELD(StringName, ptr)->get_value(p_obj));
+				return ptr ? Variant(*ptr) : Variant();
+			}
+
 			if (CACHED_CLASS(NodePath) == type_class) {
 				NodePath *ptr = unbox<NodePath *>(CACHED_FIELD(NodePath, ptr)->get_value(p_obj));
 				return ptr ? Variant(*ptr) : Variant();
@@ -960,9 +1037,10 @@ String mono_object_to_variant_string(MonoObject *p_obj, MonoException **r_exc) {
 }
 
 MonoArray *Array_to_mono_array(const Array &p_array) {
-	MonoArray *ret = mono_array_new(mono_domain_get(), CACHED_CLASS_RAW(MonoObject), p_array.size());
+	int length = p_array.size();
+	MonoArray *ret = mono_array_new(mono_domain_get(), CACHED_CLASS_RAW(MonoObject), length);
 
-	for (int i = 0; i < p_array.size(); i++) {
+	for (int i = 0; i < length; i++) {
 		MonoObject *boxed = variant_to_mono_object(p_array[i]);
 		mono_array_setref(ret, i, boxed);
 	}
@@ -985,16 +1063,14 @@ Array mono_array_to_Array(MonoArray *p_array) {
 	return ret;
 }
 
-// TODO: Use memcpy where possible
-
 MonoArray *PackedInt32Array_to_mono_array(const PackedInt32Array &p_array) {
-	const int *r = p_array.ptr();
+	const int32_t *src = p_array.ptr();
+	int length = p_array.size();
 
-	MonoArray *ret = mono_array_new(mono_domain_get(), CACHED_CLASS_RAW(int32_t), p_array.size());
+	MonoArray *ret = mono_array_new(mono_domain_get(), CACHED_CLASS_RAW(int32_t), length);
 
-	for (int i = 0; i < p_array.size(); i++) {
-		mono_array_set(ret, int32_t, i, r[i]);
-	}
+	int32_t *dst = (int32_t *)mono_array_addr(ret, int32_t, 0);
+	memcpy(dst, src, length);
 
 	return ret;
 }
@@ -1005,23 +1081,48 @@ PackedInt32Array mono_array_to_PackedInt32Array(MonoArray *p_array) {
 		return ret;
 	int length = mono_array_length(p_array);
 	ret.resize(length);
-	int *w = ret.ptrw();
+	int32_t *dst = ret.ptrw();
 
-	for (int i = 0; i < length; i++) {
-		w[i] = mono_array_get(p_array, int32_t, i);
-	}
+	const int32_t *src = (const int32_t *)mono_array_addr(p_array, int32_t, 0);
+	memcpy(dst, src, length);
+
+	return ret;
+}
+
+MonoArray *PackedInt64Array_to_mono_array(const PackedInt64Array &p_array) {
+	const int64_t *src = p_array.ptr();
+	int length = p_array.size();
+
+	MonoArray *ret = mono_array_new(mono_domain_get(), CACHED_CLASS_RAW(int64_t), length);
+
+	int64_t *dst = (int64_t *)mono_array_addr(ret, int64_t, 0);
+	memcpy(dst, src, length);
+
+	return ret;
+}
+
+PackedInt64Array mono_array_to_PackedInt64Array(MonoArray *p_array) {
+	PackedInt64Array ret;
+	if (!p_array)
+		return ret;
+	int length = mono_array_length(p_array);
+	ret.resize(length);
+	int64_t *dst = ret.ptrw();
+
+	const int64_t *src = (const int64_t *)mono_array_addr(p_array, int64_t, 0);
+	memcpy(dst, src, length);
 
 	return ret;
 }
 
 MonoArray *PackedByteArray_to_mono_array(const PackedByteArray &p_array) {
-	const uint8_t *r = p_array.ptr();
+	const uint8_t *src = p_array.ptr();
+	int length = p_array.size();
 
-	MonoArray *ret = mono_array_new(mono_domain_get(), CACHED_CLASS_RAW(uint8_t), p_array.size());
+	MonoArray *ret = mono_array_new(mono_domain_get(), CACHED_CLASS_RAW(uint8_t), length);
 
-	for (int i = 0; i < p_array.size(); i++) {
-		mono_array_set(ret, uint8_t, i, r[i]);
-	}
+	uint8_t *dst = (uint8_t *)mono_array_addr(ret, uint8_t, 0);
+	memcpy(dst, src, length);
 
 	return ret;
 }
@@ -1032,23 +1133,22 @@ PackedByteArray mono_array_to_PackedByteArray(MonoArray *p_array) {
 		return ret;
 	int length = mono_array_length(p_array);
 	ret.resize(length);
-	uint8_t *w = ret.ptrw();
+	uint8_t *dst = ret.ptrw();
 
-	for (int i = 0; i < length; i++) {
-		w[i] = mono_array_get(p_array, uint8_t, i);
-	}
+	const uint8_t *src = (const uint8_t *)mono_array_addr(p_array, uint8_t, 0);
+	memcpy(dst, src, length);
 
 	return ret;
 }
 
 MonoArray *PackedFloat32Array_to_mono_array(const PackedFloat32Array &p_array) {
-	const real_t *r = p_array.ptr();
+	const float *src = p_array.ptr();
+	int length = p_array.size();
 
-	MonoArray *ret = mono_array_new(mono_domain_get(), REAL_T_MONOCLASS, p_array.size());
+	MonoArray *ret = mono_array_new(mono_domain_get(), CACHED_CLASS_RAW(float), length);
 
-	for (int i = 0; i < p_array.size(); i++) {
-		mono_array_set(ret, real_t, i, r[i]);
-	}
+	float *dst = (float *)mono_array_addr(ret, float, 0);
+	memcpy(dst, src, length);
 
 	return ret;
 }
@@ -1059,21 +1159,47 @@ PackedFloat32Array mono_array_to_PackedFloat32Array(MonoArray *p_array) {
 		return ret;
 	int length = mono_array_length(p_array);
 	ret.resize(length);
-	real_t *w = ret.ptrw();
+	float *dst = ret.ptrw();
 
-	for (int i = 0; i < length; i++) {
-		w[i] = mono_array_get(p_array, real_t, i);
-	}
+	const float *src = (const float *)mono_array_addr(p_array, float, 0);
+	memcpy(dst, src, length);
+
+	return ret;
+}
+
+MonoArray *PackedFloat64Array_to_mono_array(const PackedFloat64Array &p_array) {
+	const double *src = p_array.ptr();
+	int length = p_array.size();
+
+	MonoArray *ret = mono_array_new(mono_domain_get(), CACHED_CLASS_RAW(double), length);
+
+	double *dst = (double *)mono_array_addr(ret, double, 0);
+	memcpy(dst, src, length);
+
+	return ret;
+}
+
+PackedFloat64Array mono_array_to_PackedFloat64Array(MonoArray *p_array) {
+	PackedFloat64Array ret;
+	if (!p_array)
+		return ret;
+	int length = mono_array_length(p_array);
+	ret.resize(length);
+	double *dst = ret.ptrw();
+
+	const double *src = (const double *)mono_array_addr(p_array, double, 0);
+	memcpy(dst, src, length);
 
 	return ret;
 }
 
 MonoArray *PackedStringArray_to_mono_array(const PackedStringArray &p_array) {
 	const String *r = p_array.ptr();
+	int length = p_array.size();
 
-	MonoArray *ret = mono_array_new(mono_domain_get(), CACHED_CLASS_RAW(String), p_array.size());
+	MonoArray *ret = mono_array_new(mono_domain_get(), CACHED_CLASS_RAW(String), length);
 
-	for (int i = 0; i < p_array.size(); i++) {
+	for (int i = 0; i < length; i++) {
 		MonoString *boxed = mono_string_from_godot(r[i]);
 		mono_array_setref(ret, i, boxed);
 	}
@@ -1098,13 +1224,19 @@ PackedStringArray mono_array_to_PackedStringArray(MonoArray *p_array) {
 }
 
 MonoArray *PackedColorArray_to_mono_array(const PackedColorArray &p_array) {
-	const Color *r = p_array.ptr();
+	const Color *src = p_array.ptr();
+	int length = p_array.size();
 
-	MonoArray *ret = mono_array_new(mono_domain_get(), CACHED_CLASS_RAW(Color), p_array.size());
+	MonoArray *ret = mono_array_new(mono_domain_get(), CACHED_CLASS_RAW(Color), length);
 
-	for (int i = 0; i < p_array.size(); i++) {
-		M_Color *raw = (M_Color *)mono_array_addr_with_size(ret, sizeof(M_Color), i);
-		*raw = MARSHALLED_OUT(Color, r[i]);
+	if constexpr (InteropLayout::MATCHES_Color) {
+		Color *dst = (Color *)mono_array_addr(ret, Color, 0);
+		memcpy(dst, src, length);
+	} else {
+		for (int i = 0; i < length; i++) {
+			M_Color *raw = (M_Color *)mono_array_addr_with_size(ret, sizeof(M_Color), i);
+			*raw = MARSHALLED_OUT(Color, src[i]);
+		}
 	}
 
 	return ret;
@@ -1116,23 +1248,34 @@ PackedColorArray mono_array_to_PackedColorArray(MonoArray *p_array) {
 		return ret;
 	int length = mono_array_length(p_array);
 	ret.resize(length);
-	Color *w = ret.ptrw();
+	Color *dst = ret.ptrw();
 
-	for (int i = 0; i < length; i++) {
-		w[i] = MARSHALLED_IN(Color, (M_Color *)mono_array_addr_with_size(p_array, sizeof(M_Color), i));
+	if constexpr (InteropLayout::MATCHES_Color) {
+		const Color *src = (const Color *)mono_array_addr(p_array, Color, 0);
+		memcpy(dst, src, length);
+	} else {
+		for (int i = 0; i < length; i++) {
+			dst[i] = MARSHALLED_IN(Color, (M_Color *)mono_array_addr_with_size(p_array, sizeof(M_Color), i));
+		}
 	}
 
 	return ret;
 }
 
 MonoArray *PackedVector2Array_to_mono_array(const PackedVector2Array &p_array) {
-	const Vector2 *r = p_array.ptr();
+	const Vector2 *src = p_array.ptr();
+	int length = p_array.size();
 
-	MonoArray *ret = mono_array_new(mono_domain_get(), CACHED_CLASS_RAW(Vector2), p_array.size());
+	MonoArray *ret = mono_array_new(mono_domain_get(), CACHED_CLASS_RAW(Vector2), length);
 
-	for (int i = 0; i < p_array.size(); i++) {
-		M_Vector2 *raw = (M_Vector2 *)mono_array_addr_with_size(ret, sizeof(M_Vector2), i);
-		*raw = MARSHALLED_OUT(Vector2, r[i]);
+	if constexpr (InteropLayout::MATCHES_Vector2) {
+		Vector2 *dst = (Vector2 *)mono_array_addr(ret, Vector2, 0);
+		memcpy(dst, src, length);
+	} else {
+		for (int i = 0; i < length; i++) {
+			M_Vector2 *raw = (M_Vector2 *)mono_array_addr_with_size(ret, sizeof(M_Vector2), i);
+			*raw = MARSHALLED_OUT(Vector2, src[i]);
+		}
 	}
 
 	return ret;
@@ -1144,23 +1287,34 @@ PackedVector2Array mono_array_to_PackedVector2Array(MonoArray *p_array) {
 		return ret;
 	int length = mono_array_length(p_array);
 	ret.resize(length);
-	Vector2 *w = ret.ptrw();
+	Vector2 *dst = ret.ptrw();
 
-	for (int i = 0; i < length; i++) {
-		w[i] = MARSHALLED_IN(Vector2, (M_Vector2 *)mono_array_addr_with_size(p_array, sizeof(M_Vector2), i));
+	if constexpr (InteropLayout::MATCHES_Vector2) {
+		const Vector2 *src = (const Vector2 *)mono_array_addr(p_array, Vector2, 0);
+		memcpy(dst, src, length);
+	} else {
+		for (int i = 0; i < length; i++) {
+			dst[i] = MARSHALLED_IN(Vector2, (M_Vector2 *)mono_array_addr_with_size(p_array, sizeof(M_Vector2), i));
+		}
 	}
 
 	return ret;
 }
 
 MonoArray *PackedVector3Array_to_mono_array(const PackedVector3Array &p_array) {
-	const Vector3 *r = p_array.ptr();
+	const Vector3 *src = p_array.ptr();
+	int length = p_array.size();
 
-	MonoArray *ret = mono_array_new(mono_domain_get(), CACHED_CLASS_RAW(Vector3), p_array.size());
+	MonoArray *ret = mono_array_new(mono_domain_get(), CACHED_CLASS_RAW(Vector3), length);
 
-	for (int i = 0; i < p_array.size(); i++) {
-		M_Vector3 *raw = (M_Vector3 *)mono_array_addr_with_size(ret, sizeof(M_Vector3), i);
-		*raw = MARSHALLED_OUT(Vector3, r[i]);
+	if constexpr (InteropLayout::MATCHES_Vector3) {
+		Vector3 *dst = (Vector3 *)mono_array_addr(ret, Vector3, 0);
+		memcpy(dst, src, length);
+	} else {
+		for (int i = 0; i < length; i++) {
+			M_Vector3 *raw = (M_Vector3 *)mono_array_addr_with_size(ret, sizeof(M_Vector3), i);
+			*raw = MARSHALLED_OUT(Vector3, src[i]);
+		}
 	}
 
 	return ret;
@@ -1172,13 +1326,85 @@ PackedVector3Array mono_array_to_PackedVector3Array(MonoArray *p_array) {
 		return ret;
 	int length = mono_array_length(p_array);
 	ret.resize(length);
-	Vector3 *w = ret.ptrw();
+	Vector3 *dst = ret.ptrw();
 
-	for (int i = 0; i < length; i++) {
-		w[i] = MARSHALLED_IN(Vector3, (M_Vector3 *)mono_array_addr_with_size(p_array, sizeof(M_Vector3), i));
+	if constexpr (InteropLayout::MATCHES_Vector3) {
+		const Vector3 *src = (const Vector3 *)mono_array_addr(p_array, Vector3, 0);
+		memcpy(dst, src, length);
+	} else {
+		for (int i = 0; i < length; i++) {
+			dst[i] = MARSHALLED_IN(Vector3, (M_Vector3 *)mono_array_addr_with_size(p_array, sizeof(M_Vector3), i));
+		}
 	}
 
 	return ret;
 }
 
+Callable managed_to_callable(const M_Callable &p_managed_callable) {
+	if (p_managed_callable.delegate) {
+		// TODO: Use pooling for ManagedCallable instances.
+		CallableCustom *managed_callable = memnew(ManagedCallable(p_managed_callable.delegate));
+		return Callable(managed_callable);
+	} else {
+		Object *target = p_managed_callable.target ?
+								 unbox<Object *>(CACHED_FIELD(GodotObject, ptr)->get_value(p_managed_callable.target)) :
+								 NULL;
+		StringName *method_ptr = unbox<StringName *>(CACHED_FIELD(StringName, ptr)->get_value(p_managed_callable.method_string_name));
+		StringName method = method_ptr ? *method_ptr : StringName();
+		return Callable(target, method);
+	}
+}
+
+M_Callable callable_to_managed(const Callable &p_callable) {
+	if (p_callable.is_custom()) {
+		CallableCustom *custom = p_callable.get_custom();
+		CallableCustom::CompareEqualFunc compare_equal_func = custom->get_compare_equal_func();
+
+		if (compare_equal_func == ManagedCallable::compare_equal_func_ptr) {
+			ManagedCallable *managed_callable = static_cast<ManagedCallable *>(custom);
+			return {
+				NULL, NULL,
+				managed_callable->get_delegate()
+			};
+		} else if (compare_equal_func == SignalAwaiterCallable::compare_equal_func_ptr) {
+			SignalAwaiterCallable *signal_awaiter_callable = static_cast<SignalAwaiterCallable *>(custom);
+			return {
+				GDMonoUtils::unmanaged_get_managed(ObjectDB::get_instance(signal_awaiter_callable->get_object())),
+				GDMonoUtils::create_managed_from(signal_awaiter_callable->get_signal()),
+				NULL
+			};
+		} else if (compare_equal_func == EventSignalCallable::compare_equal_func_ptr) {
+			EventSignalCallable *event_signal_callable = static_cast<EventSignalCallable *>(custom);
+			return {
+				GDMonoUtils::unmanaged_get_managed(ObjectDB::get_instance(event_signal_callable->get_object())),
+				GDMonoUtils::create_managed_from(event_signal_callable->get_signal()),
+				NULL
+			};
+		}
+
+		// Some other CallableCustom. We only support ManagedCallable.
+		return { NULL, NULL, NULL };
+	} else {
+		MonoObject *target_managed = GDMonoUtils::unmanaged_get_managed(p_callable.get_object());
+		MonoObject *method_string_name_managed = GDMonoUtils::create_managed_from(p_callable.get_method());
+		return { target_managed, method_string_name_managed, NULL };
+	}
+}
+
+Signal managed_to_signal_info(const M_SignalInfo &p_managed_signal) {
+	Object *owner = p_managed_signal.owner ?
+							unbox<Object *>(CACHED_FIELD(GodotObject, ptr)->get_value(p_managed_signal.owner)) :
+							NULL;
+	StringName *name_ptr = unbox<StringName *>(CACHED_FIELD(StringName, ptr)->get_value(p_managed_signal.name_string_name));
+	StringName name = name_ptr ? *name_ptr : StringName();
+	return Signal(owner, name);
+}
+
+M_SignalInfo signal_info_to_managed(const Signal &p_signal) {
+	Object *owner = p_signal.get_object();
+	MonoObject *owner_managed = GDMonoUtils::unmanaged_get_managed(owner);
+	MonoObject *name_string_name_managed = GDMonoUtils::create_managed_from(p_signal.get_name());
+	return { owner_managed, name_string_name_managed };
+}
+
 } // namespace GDMonoMarshal

+ 37 - 3
modules/mono/mono_gd/gd_mono_marshal.h

@@ -33,6 +33,7 @@
 
 #include "core/variant.h"
 
+#include "../managed_callable.h"
 #include "gd_mono.h"
 #include "gd_mono_utils.h"
 
@@ -62,7 +63,7 @@ T *unbox_addr(MonoObject *p_obj) {
 #define BOX_PTR(x) mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(IntPtr), x)
 #define BOX_ENUM(m_enum_class, x) mono_value_box(mono_domain_get(), m_enum_class, &x)
 
-Variant::Type managed_to_variant_type(const ManagedType &p_type);
+Variant::Type managed_to_variant_type(const ManagedType &p_type, bool *r_nil_is_variant = NULL);
 
 bool try_get_array_element_type(const ManagedType &p_array_type, ManagedType &r_elem_type);
 bool try_get_dictionary_key_value_types(const ManagedType &p_dictionary_type, ManagedType &r_key_type, ManagedType &r_value_type);
@@ -132,6 +133,11 @@ Array mono_array_to_Array(MonoArray *p_array);
 MonoArray *PackedInt32Array_to_mono_array(const PackedInt32Array &p_array);
 PackedInt32Array mono_array_to_PackedInt32Array(MonoArray *p_array);
 
+// PackedInt64Array
+
+MonoArray *PackedInt64Array_to_mono_array(const PackedInt64Array &p_array);
+PackedInt64Array mono_array_to_PackedInt64Array(MonoArray *p_array);
+
 // PackedByteArray
 
 MonoArray *PackedByteArray_to_mono_array(const PackedByteArray &p_array);
@@ -142,6 +148,11 @@ PackedByteArray mono_array_to_PackedByteArray(MonoArray *p_array);
 MonoArray *PackedFloat32Array_to_mono_array(const PackedFloat32Array &p_array);
 PackedFloat32Array mono_array_to_PackedFloat32Array(MonoArray *p_array);
 
+// PackedFloat64Array
+
+MonoArray *PackedFloat64Array_to_mono_array(const PackedFloat64Array &p_array);
+PackedFloat64Array mono_array_to_PackedFloat64Array(MonoArray *p_array);
+
 // PackedStringArray
 
 MonoArray *PackedStringArray_to_mono_array(const PackedStringArray &p_array);
@@ -162,6 +173,29 @@ PackedVector2Array mono_array_to_PackedVector2Array(MonoArray *p_array);
 MonoArray *PackedVector3Array_to_mono_array(const PackedVector3Array &p_array);
 PackedVector3Array mono_array_to_PackedVector3Array(MonoArray *p_array);
 
+#pragma pack(push, 1)
+
+struct M_Callable {
+	MonoObject *target;
+	MonoObject *method_string_name;
+	MonoDelegate *delegate;
+};
+
+struct M_SignalInfo {
+	MonoObject *owner;
+	MonoObject *name_string_name;
+};
+
+#pragma pack(pop)
+
+// Callable
+Callable managed_to_callable(const M_Callable &p_managed_callable);
+M_Callable callable_to_managed(const Callable &p_callable);
+
+// SignalInfo
+Signal managed_to_signal_info(const M_SignalInfo &p_managed_signal);
+M_SignalInfo signal_info_to_managed(const Signal &p_signal);
+
 // Structures
 
 namespace InteropLayout {
@@ -222,8 +256,8 @@ enum {
 // In the future we may force this if we want to ref return these structs
 #ifdef GD_MONO_FORCE_INTEROP_STRUCT_COPY
 /* clang-format off */
-GD_STATIC_ASSERT(MATCHES_Vector2 && MATCHES_Rect2 && MATCHES_Transform2D && MATCHES_Vector3 &&
-				MATCHES_Basis && MATCHES_Quat && MATCHES_Transform && MATCHES_AABB && MATCHES_Color &&MATCHES_Plane);
+static_assert(MATCHES_Vector2 && MATCHES_Rect2 && MATCHES_Transform2D && MATCHES_Vector3 &&
+				MATCHES_Basis && MATCHES_Quat && MATCHES_Transform && MATCHES_AABB && MATCHES_Color && MATCHES_Plane);
 /* clang-format on */
 #endif
 

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

@@ -101,50 +101,41 @@ IMonoClassMember::Visibility GDMonoMethod::get_visibility() {
 	}
 }
 
-MonoObject *GDMonoMethod::invoke(MonoObject *p_object, const Variant **p_params, MonoException **r_exc) {
-	if (get_return_type().type_encoding != MONO_TYPE_VOID || get_parameters_count() > 0) {
-		MonoArray *params = mono_array_new(mono_domain_get(), CACHED_CLASS_RAW(MonoObject), get_parameters_count());
+MonoObject *GDMonoMethod::invoke(MonoObject *p_object, const Variant **p_params, MonoException **r_exc) const {
+	MonoException *exc = NULL;
+	MonoObject *ret;
+
+	if (params_count > 0) {
+		MonoArray *params = mono_array_new(mono_domain_get(), CACHED_CLASS_RAW(MonoObject), params_count);
 
 		for (int i = 0; i < params_count; i++) {
 			MonoObject *boxed_param = GDMonoMarshal::variant_to_mono_object(p_params[i], param_types[i]);
 			mono_array_setref(params, i, boxed_param);
 		}
 
-		MonoException *exc = NULL;
-		MonoObject *ret = GDMonoUtils::runtime_invoke_array(mono_method, p_object, params, &exc);
-
-		if (exc) {
-			ret = NULL;
-			if (r_exc) {
-				*r_exc = exc;
-			} else {
-				GDMonoUtils::set_pending_exception(exc);
-			}
-		}
-
-		return ret;
+		ret = GDMonoUtils::runtime_invoke_array(mono_method, p_object, params, &exc);
 	} else {
-		MonoException *exc = NULL;
-		GDMonoUtils::runtime_invoke(mono_method, p_object, NULL, &exc);
-
-		if (exc) {
-			if (r_exc) {
-				*r_exc = exc;
-			} else {
-				GDMonoUtils::set_pending_exception(exc);
-			}
-		}
+		ret = GDMonoUtils::runtime_invoke(mono_method, p_object, NULL, &exc);
+	}
 
-		return NULL;
+	if (exc) {
+		ret = NULL;
+		if (r_exc) {
+			*r_exc = exc;
+		} else {
+			GDMonoUtils::set_pending_exception(exc);
+		}
 	}
+
+	return ret;
 }
 
-MonoObject *GDMonoMethod::invoke(MonoObject *p_object, MonoException **r_exc) {
+MonoObject *GDMonoMethod::invoke(MonoObject *p_object, MonoException **r_exc) const {
 	ERR_FAIL_COND_V(get_parameters_count() > 0, NULL);
 	return invoke_raw(p_object, NULL, r_exc);
 }
 
-MonoObject *GDMonoMethod::invoke_raw(MonoObject *p_object, void **p_params, MonoException **r_exc) {
+MonoObject *GDMonoMethod::invoke_raw(MonoObject *p_object, void **p_params, MonoException **r_exc) const {
 	MonoException *exc = NULL;
 	MonoObject *ret = GDMonoUtils::runtime_invoke(mono_method, p_object, p_params, &exc);
 
@@ -247,7 +238,7 @@ void GDMonoMethod::get_parameter_names(Vector<StringName> &names) const {
 }
 
 void GDMonoMethod::get_parameter_types(Vector<ManagedType> &types) const {
-	for (int i = 0; i < param_types.size(); ++i) {
+	for (int i = 0; i < params_count; ++i) {
 		types.push_back(param_types[i]);
 	}
 }
@@ -256,13 +247,22 @@ const MethodInfo &GDMonoMethod::get_method_info() {
 
 	if (!method_info_fetched) {
 		method_info.name = name;
-		method_info.return_val = PropertyInfo(GDMonoMarshal::managed_to_variant_type(return_type), "");
+
+		bool nil_is_variant = false;
+		method_info.return_val = PropertyInfo(GDMonoMarshal::managed_to_variant_type(return_type, &nil_is_variant), "");
+		if (method_info.return_val.type == Variant::NIL && nil_is_variant)
+			method_info.return_val.usage |= PROPERTY_USAGE_NIL_IS_VARIANT;
 
 		Vector<StringName> names;
 		get_parameter_names(names);
 
 		for (int i = 0; i < params_count; ++i) {
-			method_info.arguments.push_back(PropertyInfo(GDMonoMarshal::managed_to_variant_type(param_types[i]), names[i]));
+			nil_is_variant = false;
+			PropertyInfo arg_info = PropertyInfo(GDMonoMarshal::managed_to_variant_type(param_types[i], &nil_is_variant), names[i]);
+			if (arg_info.type == Variant::NIL && nil_is_variant)
+				arg_info.usage |= PROPERTY_USAGE_NIL_IS_VARIANT;
+
+			method_info.arguments.push_back(arg_info);
 		}
 
 		// TODO: default arguments

+ 13 - 13
modules/mono/mono_gd/gd_mono_method.h

@@ -57,28 +57,28 @@ class GDMonoMethod : public IMonoClassMember {
 	MonoMethod *mono_method;
 
 public:
-	virtual GDMonoClass *get_enclosing_class() const GD_FINAL;
+	virtual GDMonoClass *get_enclosing_class() const final;
 
-	virtual MemberType get_member_type() const GD_FINAL { return MEMBER_TYPE_METHOD; }
+	virtual MemberType get_member_type() const final { return MEMBER_TYPE_METHOD; }
 
-	virtual StringName get_name() const GD_FINAL { return name; }
+	virtual StringName get_name() const final { return name; }
 
-	virtual bool is_static() GD_FINAL;
+	virtual bool is_static() final;
 
-	virtual Visibility get_visibility() GD_FINAL;
+	virtual Visibility get_visibility() final;
 
-	virtual bool has_attribute(GDMonoClass *p_attr_class) GD_FINAL;
-	virtual MonoObject *get_attribute(GDMonoClass *p_attr_class) GD_FINAL;
+	virtual bool has_attribute(GDMonoClass *p_attr_class) final;
+	virtual MonoObject *get_attribute(GDMonoClass *p_attr_class) final;
 	void fetch_attributes();
 
-	_FORCE_INLINE_ MonoMethod *get_mono_ptr() { return mono_method; }
+	_FORCE_INLINE_ MonoMethod *get_mono_ptr() const { return mono_method; }
 
-	_FORCE_INLINE_ int get_parameters_count() { return params_count; }
-	_FORCE_INLINE_ ManagedType get_return_type() { return return_type; }
+	_FORCE_INLINE_ int get_parameters_count() const { return params_count; }
+	_FORCE_INLINE_ ManagedType get_return_type() const { return return_type; }
 
-	MonoObject *invoke(MonoObject *p_object, const Variant **p_params, MonoException **r_exc = NULL);
-	MonoObject *invoke(MonoObject *p_object, MonoException **r_exc = NULL);
-	MonoObject *invoke_raw(MonoObject *p_object, void **p_params, MonoException **r_exc = NULL);
+	MonoObject *invoke(MonoObject *p_object, const Variant **p_params, MonoException **r_exc = NULL) const;
+	MonoObject *invoke(MonoObject *p_object, MonoException **r_exc = NULL) const;
+	MonoObject *invoke_raw(MonoObject *p_object, void **p_params, MonoException **r_exc = NULL) const;
 
 	String get_full_name(bool p_signature = false) const;
 	String get_full_name_no_class() const;

+ 7 - 7
modules/mono/mono_gd/gd_mono_property.h

@@ -47,17 +47,17 @@ class GDMonoProperty : public IMonoClassMember {
 	MonoCustomAttrInfo *attributes;
 
 public:
-	virtual GDMonoClass *get_enclosing_class() const GD_FINAL { return owner; }
+	virtual GDMonoClass *get_enclosing_class() const final { return owner; }
 
-	virtual MemberType get_member_type() const GD_FINAL { return MEMBER_TYPE_PROPERTY; }
+	virtual MemberType get_member_type() const final { return MEMBER_TYPE_PROPERTY; }
 
-	virtual StringName get_name() const GD_FINAL { return name; }
+	virtual StringName get_name() const final { return name; }
 
-	virtual bool is_static() GD_FINAL;
-	virtual Visibility get_visibility() GD_FINAL;
+	virtual bool is_static() final;
+	virtual Visibility get_visibility() final;
 
-	virtual bool has_attribute(GDMonoClass *p_attr_class) GD_FINAL;
-	virtual MonoObject *get_attribute(GDMonoClass *p_attr_class) GD_FINAL;
+	virtual bool has_attribute(GDMonoClass *p_attr_class) final;
+	virtual MonoObject *get_attribute(GDMonoClass *p_attr_class) final;
 	void fetch_attributes();
 
 	bool has_getter();

+ 30 - 4
modules/mono/mono_gd/gd_mono_utils.cpp

@@ -162,6 +162,13 @@ void runtime_object_init(MonoObject *p_this_obj, GDMonoClass *p_class, MonoExcep
 	ctor->invoke_raw(p_this_obj, NULL, r_exc);
 }
 
+bool mono_delegate_equal(MonoDelegate *p_a, MonoDelegate *p_b) {
+	MonoException *exc = NULL;
+	MonoBoolean res = CACHED_METHOD_THUNK(Delegate, Equals).invoke((MonoObject *)p_a, (MonoObject *)p_b, &exc);
+	UNHANDLED_EXCEPTION(exc);
+	return (bool)res;
+}
+
 GDMonoClass *get_object_class(MonoObject *p_object) {
 	return GDMono::get_singleton()->get_class(mono_object_get_class(p_object));
 }
@@ -220,6 +227,18 @@ MonoObject *create_managed_for_godot_object(GDMonoClass *p_class, const StringNa
 	return mono_object;
 }
 
+MonoObject *create_managed_from(const StringName &p_from) {
+	MonoObject *mono_object = mono_object_new(mono_domain_get(), CACHED_CLASS_RAW(StringName));
+	ERR_FAIL_NULL_V(mono_object, NULL);
+
+	// Construct
+	GDMonoUtils::runtime_object_init(mono_object, CACHED_CLASS(StringName));
+
+	CACHED_FIELD(StringName, ptr)->set_value_raw(mono_object, memnew(StringName(p_from)));
+
+	return mono_object;
+}
+
 MonoObject *create_managed_from(const NodePath &p_from) {
 	MonoObject *mono_object = mono_object_new(mono_domain_get(), CACHED_CLASS_RAW(NodePath));
 	ERR_FAIL_NULL_V(mono_object, NULL);
@@ -362,7 +381,11 @@ void debug_send_unhandled_exception_error(MonoException *p_exc) {
 		return;
 	}
 
-	_TLS_RECURSION_GUARD_;
+	static thread_local bool _recursion_flag_ = false;
+	if (_recursion_flag_)
+		return;
+	_recursion_flag_ = true;
+	SCOPE_EXIT { _recursion_flag_ = false; };
 
 	ScriptLanguage::StackInfo separator;
 	separator.file = String();
@@ -439,8 +462,7 @@ void set_pending_exception(MonoException *p_exc) {
 #endif
 }
 
-_THREAD_LOCAL_(int)
-current_invoke_count = 0;
+thread_local int current_invoke_count = 0;
 
 MonoObject *runtime_invoke(MonoMethod *p_method, void *p_obj, void **p_params, MonoException **r_exc) {
 	GD_MONO_BEGIN_RUNTIME_INVOKE;
@@ -644,6 +666,10 @@ ScopeThreadAttach::~ScopeThreadAttach() {
 	}
 }
 
-// namespace Marshal
+StringName get_native_godot_class_name(GDMonoClass *p_class) {
+	MonoObject *native_name_obj = p_class->get_field(BINDINGS_NATIVE_NAME_FIELD)->get_value(NULL);
+	StringName *ptr = GDMonoMarshal::unbox<StringName *>(CACHED_FIELD(StringName, ptr)->get_value(native_name_obj));
+	return ptr ? *ptr : StringName();
+}
 
 } // namespace GDMonoUtils

+ 7 - 3
modules/mono/mono_gd/gd_mono_utils.h

@@ -35,7 +35,6 @@
 
 #include "../mono_gc_handle.h"
 #include "../utils/macros.h"
-#include "../utils/thread_local.h"
 #include "gd_mono_header.h"
 
 #include "core/object.h"
@@ -95,12 +94,15 @@ _FORCE_INLINE_ bool is_main_thread() {
 
 void runtime_object_init(MonoObject *p_this_obj, GDMonoClass *p_class, MonoException **r_exc = NULL);
 
+bool mono_delegate_equal(MonoDelegate *p_a, MonoDelegate *p_b);
+
 GDMonoClass *get_object_class(MonoObject *p_object);
 GDMonoClass *type_get_proxy_class(const StringName &p_type);
 GDMonoClass *get_class_native_base(GDMonoClass *p_class);
 
 MonoObject *create_managed_for_godot_object(GDMonoClass *p_class, const StringName &p_native, Object *p_object);
 
+MonoObject *create_managed_from(const StringName &p_from);
 MonoObject *create_managed_from(const NodePath &p_from);
 MonoObject *create_managed_from(const RID &p_from);
 MonoObject *create_managed_from(const Array &p_from, GDMonoClass *p_class);
@@ -123,7 +125,7 @@ void print_unhandled_exception(MonoException *p_exc);
  */
 void set_pending_exception(MonoException *p_exc);
 
-extern _THREAD_LOCAL_(int) current_invoke_count;
+extern thread_local int current_invoke_count;
 
 _FORCE_INLINE_ int get_runtime_invoke_count() {
 	return current_invoke_count;
@@ -152,9 +154,11 @@ private:
 	MonoThread *mono_thread;
 };
 
+StringName get_native_godot_class_name(GDMonoClass *p_class);
+
 } // 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 NATIVE_GDMONOCLASS_NAME(m_class) (GDMonoUtils::get_native_godot_class_name(m_class))
 
 #define GD_MONO_BEGIN_RUNTIME_INVOKE                                              \
 	int &_runtime_invoke_count_ref = GDMonoUtils::get_runtime_invoke_count_ref(); \

+ 142 - 63
modules/mono/signal_awaiter_utils.cpp

@@ -36,103 +36,182 @@
 #include "mono_gd/gd_mono_marshal.h"
 #include "mono_gd/gd_mono_utils.h"
 
-namespace SignalAwaiterUtils {
-
-Error connect_signal_awaiter(Object *p_source, const String &p_signal, Object *p_target, MonoObject *p_awaiter) {
-
+Error gd_mono_connect_signal_awaiter(Object *p_source, const StringName &p_signal, Object *p_target, MonoObject *p_awaiter) {
 	ERR_FAIL_NULL_V(p_source, ERR_INVALID_DATA);
 	ERR_FAIL_NULL_V(p_target, ERR_INVALID_DATA);
 
-	Ref<SignalAwaiterHandle> sa_con = memnew(SignalAwaiterHandle(p_awaiter));
-#ifdef DEBUG_ENABLED
-	sa_con->set_connection_target(p_target);
-#endif
+	// TODO: Use pooling for ManagedCallable instances.
+	SignalAwaiterCallable *awaiter_callable = memnew(SignalAwaiterCallable(p_target, p_awaiter, p_signal));
+	Callable callable = Callable(awaiter_callable);
 
-	Vector<Variant> binds;
-	binds.push_back(sa_con);
+	return p_source->connect(p_signal, callable, Vector<Variant>(), Object::CONNECT_ONESHOT);
+}
 
-	Error err = p_source->connect_compat(p_signal, sa_con.ptr(),
-			CSharpLanguage::get_singleton()->get_string_names()._signal_callback,
-			binds, Object::CONNECT_ONESHOT);
+bool SignalAwaiterCallable::compare_equal(const CallableCustom *p_a, const CallableCustom *p_b) {
+	const SignalAwaiterCallable *a = static_cast<const SignalAwaiterCallable *>(p_a);
+	const SignalAwaiterCallable *b = static_cast<const SignalAwaiterCallable *>(p_b);
 
-	if (err != OK) {
-		// Set it as completed to prevent it from calling the failure callback when released.
-		// The awaiter will be aware of the failure by checking the returned error.
-		sa_con->set_completed(true);
-	}
+	if (a->target_id != b->target_id)
+		return false;
+
+	if (a->signal != b->signal)
+		return false;
 
-	return err;
+	return true;
 }
-} // namespace SignalAwaiterUtils
 
-Variant SignalAwaiterHandle::_signal_callback(const Variant **p_args, int p_argcount, Callable::CallError &r_error) {
+bool SignalAwaiterCallable::compare_less(const CallableCustom *p_a, const CallableCustom *p_b) {
+	if (compare_equal(p_a, p_b))
+		return false;
+	return p_a < p_b;
+}
 
-#ifdef DEBUG_ENABLED
-	ERR_FAIL_COND_V_MSG(conn_target_id.is_valid() && !ObjectDB::get_instance(conn_target_id), Variant(),
-			"Resumed after await, but class instance is gone.");
-#endif
+uint32_t SignalAwaiterCallable::hash() const {
+	uint32_t hash = signal.hash();
+	return hash_djb2_one_64(target_id, hash);
+}
+
+String SignalAwaiterCallable::get_as_text() const {
+	Object *base = ObjectDB::get_instance(target_id);
+	if (base) {
+		String class_name = base->get_class();
+		Ref<Script> script = base->get_script();
+		if (script.is_valid() && script->get_path().is_resource_file()) {
 
-	if (p_argcount < 1) {
-		r_error.error = Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS;
-		r_error.argument = 1;
-		return Variant();
+			class_name += "(" + script->get_path().get_file() + ")";
+		}
+		return class_name + "::SignalAwaiterMiddleman::" + String(signal);
+	} else {
+		return "null::SignalAwaiterMiddleman::" + String(signal);
 	}
+}
 
-	Ref<SignalAwaiterHandle> self = *p_args[p_argcount - 1];
+CallableCustom::CompareEqualFunc SignalAwaiterCallable::get_compare_equal_func() const {
+	return compare_equal_func_ptr;
+}
 
-	if (self.is_null()) {
-		r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
-		r_error.argument = p_argcount - 1;
-		r_error.expected = Variant::OBJECT;
-		return Variant();
-	}
+CallableCustom::CompareLessFunc SignalAwaiterCallable::get_compare_less_func() const {
+	return compare_less_func_ptr;
+}
 
-	set_completed(true);
+ObjectID SignalAwaiterCallable::get_object() const {
+	return target_id;
+}
 
-	int signal_argc = p_argcount - 1;
-	MonoArray *signal_args = mono_array_new(mono_domain_get(), CACHED_CLASS_RAW(MonoObject), signal_argc);
+void SignalAwaiterCallable::call(const Variant **p_arguments, int p_argcount, Variant &r_return_value, Callable::CallError &r_call_error) const {
+	r_call_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD; // Can't find anything better
+	r_return_value = Variant();
 
-	for (int i = 0; i < signal_argc; i++) {
-		MonoObject *boxed = GDMonoMarshal::variant_to_mono_object(*p_args[i]);
+#ifdef DEBUG_ENABLED
+	ERR_FAIL_COND_MSG(target_id.is_valid() && !ObjectDB::get_instance(target_id),
+			"Resumed after await, but class instance is gone.");
+#endif
+
+	MonoArray *signal_args = mono_array_new(mono_domain_get(), CACHED_CLASS_RAW(MonoObject), p_argcount);
+
+	for (int i = 0; i < p_argcount; i++) {
+		MonoObject *boxed = GDMonoMarshal::variant_to_mono_object(*p_arguments[i]);
 		mono_array_setref(signal_args, i, boxed);
 	}
 
 	MonoException *exc = NULL;
-	GD_MONO_BEGIN_RUNTIME_INVOKE;
-	CACHED_METHOD_THUNK(SignalAwaiter, SignalCallback).invoke(get_target(), signal_args, &exc);
-	GD_MONO_END_RUNTIME_INVOKE;
+	CACHED_METHOD_THUNK(SignalAwaiter, SignalCallback).invoke(awaiter_handle->get_target(), signal_args, &exc);
 
 	if (exc) {
 		GDMonoUtils::set_pending_exception(exc);
-		ERR_FAIL_V(Variant());
+		ERR_FAIL();
+	} else {
+		r_call_error.error = Callable::CallError::CALL_OK;
+	}
+}
+
+SignalAwaiterCallable::SignalAwaiterCallable(Object *p_target, MonoObject *p_awaiter, const StringName &p_signal) :
+		target_id(p_target->get_instance_id()),
+		awaiter_handle(MonoGCHandle::create_strong(p_awaiter)),
+		signal(p_signal) {
+}
+
+bool EventSignalCallable::compare_equal(const CallableCustom *p_a, const CallableCustom *p_b) {
+	const EventSignalCallable *a = static_cast<const EventSignalCallable *>(p_a);
+	const EventSignalCallable *b = static_cast<const EventSignalCallable *>(p_b);
+
+	if (a->owner != b->owner)
+		return false;
+
+	if (a->event_signal != b->event_signal)
+		return false;
+
+	return true;
+}
+
+bool EventSignalCallable::compare_less(const CallableCustom *p_a, const CallableCustom *p_b) {
+	if (compare_equal(p_a, p_b))
+		return false;
+	return p_a < p_b;
+}
+
+uint32_t EventSignalCallable::hash() const {
+	uint32_t hash = event_signal->field->get_name().hash();
+	return hash_djb2_one_64(owner->get_instance_id(), hash);
+}
+
+String EventSignalCallable::get_as_text() const {
+	String class_name = owner->get_class();
+	Ref<Script> script = owner->get_script();
+	if (script.is_valid() && script->get_path().is_resource_file()) {
+
+		class_name += "(" + script->get_path().get_file() + ")";
 	}
+	StringName signal = event_signal->field->get_name();
+	return class_name + "::EventSignalMiddleman::" + String(signal);
+}
 
-	return Variant();
+CallableCustom::CompareEqualFunc EventSignalCallable::get_compare_equal_func() const {
+	return compare_equal_func_ptr;
 }
 
-void SignalAwaiterHandle::_bind_methods() {
+CallableCustom::CompareLessFunc EventSignalCallable::get_compare_less_func() const {
+	return compare_less_func_ptr;
+}
 
-	ClassDB::bind_vararg_method(METHOD_FLAGS_DEFAULT, "_signal_callback", &SignalAwaiterHandle::_signal_callback, MethodInfo("_signal_callback"));
+ObjectID EventSignalCallable::get_object() const {
+	return owner->get_instance_id();
 }
 
-SignalAwaiterHandle::SignalAwaiterHandle(MonoObject *p_managed) :
-		MonoGCHandle(MonoGCHandle::new_strong_handle(p_managed), STRONG_HANDLE) {}
+StringName EventSignalCallable::get_signal() const {
+	return event_signal->field->get_name();
+}
 
-SignalAwaiterHandle::~SignalAwaiterHandle() {
+void EventSignalCallable::call(const Variant **p_arguments, int p_argcount, Variant &r_return_value, Callable::CallError &r_call_error) const {
+	r_call_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD; // Can't find anything better
+	r_return_value = Variant();
 
-	if (!completed) {
-		MonoObject *awaiter = get_target();
+	ERR_FAIL_COND(p_argcount < event_signal->invoke_method->get_parameters_count());
 
-		if (awaiter) {
-			MonoException *exc = NULL;
-			GD_MONO_BEGIN_RUNTIME_INVOKE;
-			CACHED_METHOD_THUNK(SignalAwaiter, FailureCallback).invoke(awaiter, &exc);
-			GD_MONO_END_RUNTIME_INVOKE;
+	CSharpInstance *csharp_instance = CAST_CSHARP_INSTANCE(owner->get_script_instance());
+	ERR_FAIL_NULL(csharp_instance);
 
-			if (exc) {
-				GDMonoUtils::set_pending_exception(exc);
-				ERR_FAIL();
-			}
-		}
+	MonoObject *owner_managed = csharp_instance->get_mono_object();
+	ERR_FAIL_NULL(owner_managed);
+
+	MonoObject *delegate_field_value = event_signal->field->get_value(owner_managed);
+	if (!delegate_field_value) {
+		r_call_error.error = Callable::CallError::CALL_OK;
+		return;
 	}
+
+	MonoException *exc = NULL;
+	event_signal->invoke_method->invoke(delegate_field_value, p_arguments, &exc);
+
+	if (exc) {
+		GDMonoUtils::set_pending_exception(exc);
+		ERR_FAIL();
+	} else {
+		r_call_error.error = Callable::CallError::CALL_OK;
+	}
+}
+
+EventSignalCallable::EventSignalCallable(Object *p_owner, const CSharpScript::EventSignal *p_event_signal) :
+		owner(p_owner),
+		event_signal(p_event_signal) {
 }

+ 46 - 21
modules/mono/signal_awaiter_utils.h

@@ -32,40 +32,65 @@
 #define SIGNAL_AWAITER_UTILS_H
 
 #include "core/reference.h"
+
+#include "csharp_script.h"
 #include "mono_gc_handle.h"
 
-namespace SignalAwaiterUtils {
+Error gd_mono_connect_signal_awaiter(Object *p_source, const StringName &p_signal, Object *p_target, MonoObject *p_awaiter);
+
+class SignalAwaiterCallable : public CallableCustom {
+	ObjectID target_id;
+	Ref<MonoGCHandle> awaiter_handle;
+	StringName signal;
+
+public:
+	static bool compare_equal(const CallableCustom *p_a, const CallableCustom *p_b);
+	static bool compare_less(const CallableCustom *p_a, const CallableCustom *p_b);
 
-Error connect_signal_awaiter(Object *p_source, const String &p_signal, Object *p_target, MonoObject *p_awaiter);
-}
+	static constexpr CompareEqualFunc compare_equal_func_ptr = &SignalAwaiterCallable::compare_equal;
+	static constexpr CompareEqualFunc compare_less_func_ptr = &SignalAwaiterCallable::compare_less;
 
-class SignalAwaiterHandle : public MonoGCHandle {
+	uint32_t hash() const override;
 
-	GDCLASS(SignalAwaiterHandle, MonoGCHandle);
+	String get_as_text() const override;
 
-	bool completed;
+	CompareEqualFunc get_compare_equal_func() const override;
+	CompareLessFunc get_compare_less_func() const override;
 
-#ifdef DEBUG_ENABLED
-	ObjectID conn_target_id;
-#endif
+	ObjectID get_object() const override;
 
-	Variant _signal_callback(const Variant **p_args, int p_argcount, Callable::CallError &r_error);
+	_FORCE_INLINE_ StringName get_signal() const { return signal; }
+
+	void call(const Variant **p_arguments, int p_argcount, Variant &r_return_value, Callable::CallError &r_call_error) const override;
+
+	SignalAwaiterCallable(Object *p_target, MonoObject *p_awaiter, const StringName &p_signal);
+};
 
-protected:
-	static void _bind_methods();
+class EventSignalCallable : public CallableCustom {
+	Object *owner;
+	const CSharpScript::EventSignal *event_signal;
 
 public:
-	_FORCE_INLINE_ bool is_completed() { return completed; }
-	_FORCE_INLINE_ void set_completed(bool p_completed) { completed = p_completed; }
+	static bool compare_equal(const CallableCustom *p_a, const CallableCustom *p_b);
+	static bool compare_less(const CallableCustom *p_a, const CallableCustom *p_b);
+
+	static constexpr CompareEqualFunc compare_equal_func_ptr = &EventSignalCallable::compare_equal;
+	static constexpr CompareEqualFunc compare_less_func_ptr = &EventSignalCallable::compare_less;
+
+	uint32_t hash() const override;
+
+	String get_as_text() const override;
+
+	CompareEqualFunc get_compare_equal_func() const override;
+	CompareLessFunc get_compare_less_func() const override;
+
+	ObjectID get_object() const override;
+
+	StringName get_signal() const;
 
-#ifdef DEBUG_ENABLED
-	_FORCE_INLINE_ void set_connection_target(Object *p_target) {
-		conn_target_id = p_target->get_instance_id();
-	}
-#endif
+	void call(const Variant **p_arguments, int p_argcount, Variant &r_return_value, Callable::CallError &r_call_error) const override;
 
-	SignalAwaiterHandle(MonoObject *p_managed);
-	~SignalAwaiterHandle();
+	EventSignalCallable(Object *p_owner, const CSharpScript::EventSignal *p_event_signal);
 };
 
 #endif // SIGNAL_AWAITER_UTILS_H

+ 21 - 32
modules/mono/utils/macros.h

@@ -36,38 +36,6 @@
 #define _GD_VARNAME_CONCAT_(m_a, m_b, m_c) _GD_VARNAME_CONCAT_A_(m_a, m_b, m_c)
 #define GD_UNIQUE_NAME(m_name) _GD_VARNAME_CONCAT_(m_name, _, __COUNTER__)
 
-// static assert
-// TODO: Get rid of this macro once we upgrade to C++11
-
-#ifdef __cpp_static_assert
-#define GD_STATIC_ASSERT(m_cond) static_assert((m_cond), "Condition '" #m_cond "' failed")
-#else
-#define GD_STATIC_ASSERT(m_cond) typedef int GD_UNIQUE_NAME(godot_static_assert)[((m_cond) ? 1 : -1)]
-#endif
-
-// final
-// TODO: Get rid of this macro once we upgrade to C++11
-
-#if (__cplusplus >= 201103L)
-#define GD_FINAL final
-#else
-#define GD_FINAL
-#endif
-
-// noreturn
-// TODO: Get rid of this macro once we upgrade to C++11
-
-#if (__cplusplus >= 201103L)
-#define GD_NORETURN [[noreturn]]
-#elif defined(__GNUC__)
-#define GD_NORETURN __attribute__((noreturn))
-#elif defined(_MSC_VER)
-#define GD_NORETURN __declspec(noreturn)
-#else
-#define GD_NORETURN
-#pragma message "Macro GD_NORETURN will have no effect"
-#endif
-
 // unreachable
 
 #if defined(_MSC_VER)
@@ -81,4 +49,25 @@
 	} while (true);
 #endif
 
+namespace gdmono {
+
+template <typename F>
+struct ScopeExit {
+	ScopeExit(F p_exit_func) :
+			exit_func(p_exit_func) {}
+	~ScopeExit() { exit_func(); }
+	F exit_func;
+};
+
+class ScopeExitAux {
+public:
+	template <typename F>
+	ScopeExit<F> operator+(F p_exit_func) { return ScopeExit<F>(p_exit_func); }
+};
+
+} // namespace gdmono
+
+#define SCOPE_EXIT \
+	auto GD_UNIQUE_NAME(gd_scope_exit) = gdmono::ScopeExitAux() + [=]()
+
 #endif // UTIL_MACROS_H

+ 0 - 177
modules/mono/utils/thread_local.h

@@ -1,177 +0,0 @@
-/*************************************************************************/
-/*  thread_local.h                                                       */
-/*************************************************************************/
-/*                       This file is part of:                           */
-/*                           GODOT ENGINE                                */
-/*                      https://godotengine.org                          */
-/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur.                 */
-/* Copyright (c) 2014-2020 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 THREAD_LOCAL_H
-#define THREAD_LOCAL_H
-
-#ifdef HAVE_CXX11_THREAD_LOCAL
-#define _THREAD_LOCAL_(m_t) thread_local m_t
-#else
-
-#if !defined(__GNUC__) && !defined(_MSC_VER)
-#error Platform or compiler not supported
-#endif
-
-#if defined(__GNUC__)
-
-#ifdef HAVE_GCC___THREAD
-#define _THREAD_LOCAL_(m_t) __thread m_t
-#else
-#define USE_CUSTOM_THREAD_LOCAL
-#endif
-
-#elif defined(_MSC_VER)
-
-#ifdef HAVE_DECLSPEC_THREAD
-#define _THREAD_LOCAL_(m_t) __declspec(thread) m_t
-#else
-#define USE_CUSTOM_THREAD_LOCAL
-#endif
-
-#endif // __GNUC__ _MSC_VER
-
-#endif // HAVE_CXX11_THREAD_LOCAL
-
-#ifdef USE_CUSTOM_THREAD_LOCAL
-#define _THREAD_LOCAL_(m_t) ThreadLocal<m_t>
-#endif
-
-#include "core/typedefs.h"
-
-#ifdef WINDOWS_ENABLED
-#define _CALLBACK_FUNC_ __stdcall
-#else
-#define _CALLBACK_FUNC_
-#endif
-
-struct ThreadLocalStorage {
-
-	void *get_value() const;
-	void set_value(void *p_value) const;
-
-	void alloc(void(_CALLBACK_FUNC_ *p_destr_callback)(void *));
-	void free();
-
-private:
-	struct Impl;
-	Impl *pimpl;
-};
-
-template <typename T>
-class ThreadLocal {
-
-	ThreadLocalStorage storage;
-
-	T init_val;
-
-	static void _CALLBACK_FUNC_ destr_callback(void *tls_data) {
-		memdelete(static_cast<T *>(tls_data));
-	}
-
-	T *_tls_get_value() const {
-		void *tls_data = storage.get_value();
-
-		if (tls_data)
-			return static_cast<T *>(tls_data);
-
-		T *data = memnew(T(init_val));
-
-		storage.set_value(data);
-
-		return data;
-	}
-
-	void _initialize(const T &p_init_val) {
-		init_val = p_init_val;
-		storage.alloc(&destr_callback);
-	}
-
-public:
-	ThreadLocal() {
-		_initialize(T());
-	}
-
-	ThreadLocal(const T &p_init_val) {
-		_initialize(p_init_val);
-	}
-
-	ThreadLocal(const ThreadLocal &other) {
-		_initialize(*other._tls_get_value());
-	}
-
-	~ThreadLocal() {
-		storage.free();
-	}
-
-	_FORCE_INLINE_ T *operator&() const {
-		return _tls_get_value();
-	}
-
-	_FORCE_INLINE_ operator T &() const {
-		return *_tls_get_value();
-	}
-
-	_FORCE_INLINE_ ThreadLocal &operator=(const T &val) {
-		T *ptr = _tls_get_value();
-		*ptr = val;
-		return *this;
-	}
-};
-
-struct FlagScopeGuard {
-
-	FlagScopeGuard(bool &p_flag) :
-			flag(p_flag) {
-		flag = !flag;
-	}
-
-	~FlagScopeGuard() {
-		flag = !flag;
-	}
-
-private:
-	bool &flag;
-};
-
-#undef _CALLBACK_FUNC_
-
-#define _TLS_RECURSION_GUARD_V_(m_ret)                    \
-	static _THREAD_LOCAL_(bool) _recursion_flag_ = false; \
-	if (_recursion_flag_)                                 \
-		return m_ret;                                     \
-	FlagScopeGuard _recursion_guard_(_recursion_flag_);
-
-#define _TLS_RECURSION_GUARD_                             \
-	static _THREAD_LOCAL_(bool) _recursion_flag_ = false; \
-	if (_recursion_flag_)                                 \
-		return;                                           \
-	FlagScopeGuard _recursion_guard_(_recursion_flag_);
-
-#endif // THREAD_LOCAL_H