Browse Source

C#: Add source generator for signals as events

Changed the signal declaration signal to:

```
// The following generates a MySignal event
[Signal] public delegate void MySignalEventHandler(int param);
```
Ignacio Roldán Etcheverry 3 years ago
parent
commit
97713ff77a
31 changed files with 996 additions and 522 deletions
  1. 71 39
      modules/mono/csharp_script.cpp
  2. 10 1
      modules/mono/csharp_script.h
  3. 7 0
      modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Sample/EventSignals.cs
  4. 49 0
      modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/Common.cs
  5. 39 25
      modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ExtensionMethods.cs
  6. 1 0
      modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/GodotClasses.cs
  7. 14 0
      modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/GodotEnums.cs
  8. 14 0
      modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/GodotMemberData.cs
  9. 24 0
      modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/MethodInfo.cs
  10. 23 0
      modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/PropertyInfo.cs
  11. 97 18
      modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptMemberInvokerGenerator.cs
  12. 5 26
      modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptPropertiesGenerator.cs
  13. 70 0
      modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptSerializationGenerator.cs
  14. 360 0
      modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptSignalsGenerator.cs
  15. 2 1
      modules/mono/editor/GodotTools/GodotTools/Build/BuildOutputView.cs
  16. 5 1
      modules/mono/glue/GodotSharp/GodotSharp/Core/Array.cs
  17. 25 13
      modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/GodotSerializationInfo.cs
  18. 1 5
      modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/ManagedCallbacks.cs
  19. 25 0
      modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/MethodInfo.cs
  20. 19 21
      modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/PropertyInfo.cs
  21. 103 186
      modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/ScriptManagerBridge.cs
  22. 6 2
      modules/mono/glue/GodotSharp/GodotSharp/Core/Dictionary.cs
  23. 0 4
      modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/NativeFuncs.cs
  24. 5 1
      modules/mono/glue/GodotSharp/GodotSharp/Core/NodePath.cs
  25. 3 152
      modules/mono/glue/GodotSharp/GodotSharp/Core/Object.base.cs
  26. 5 1
      modules/mono/glue/GodotSharp/GodotSharp/Core/StringName.cs
  27. 1 0
      modules/mono/glue/GodotSharp/GodotSharp/GodotSharp.csproj
  28. 1 9
      modules/mono/glue/runtime_interop.cpp
  29. 10 10
      modules/mono/mono_gd/gd_mono.cpp
  30. 0 2
      modules/mono/mono_gd/gd_mono_cache.cpp
  31. 1 5
      modules/mono/mono_gd/gd_mono_cache.h

+ 71 - 39
modules/mono/csharp_script.cpp

@@ -104,7 +104,7 @@ Error CSharpLanguage::execute_file(const String &p_path) {
 	return OK;
 	return OK;
 }
 }
 
 
-extern void *godotsharp_pinvoke_funcs[186];
+extern void *godotsharp_pinvoke_funcs[185];
 [[maybe_unused]] volatile void **do_not_strip_godotsharp_pinvoke_funcs;
 [[maybe_unused]] volatile void **do_not_strip_godotsharp_pinvoke_funcs;
 #ifdef TOOLS_ENABLED
 #ifdef TOOLS_ENABLED
 extern void *godotsharp_editor_pinvoke_funcs[30];
 extern void *godotsharp_editor_pinvoke_funcs[30];
@@ -1452,6 +1452,8 @@ void CSharpLanguage::tie_user_managed_to_unmanaged(GCHandleIntPtr p_gchandle_int
 	CSharpInstance *csharp_instance = CSharpInstance::create_for_managed_type(p_unmanaged, script.ptr(), gchandle);
 	CSharpInstance *csharp_instance = CSharpInstance::create_for_managed_type(p_unmanaged, script.ptr(), gchandle);
 
 
 	p_unmanaged->set_script_and_instance(script, csharp_instance);
 	p_unmanaged->set_script_and_instance(script, csharp_instance);
+
+	csharp_instance->connect_event_signals();
 }
 }
 
 
 void CSharpLanguage::tie_managed_to_unmanaged_with_pre_setup(GCHandleIntPtr p_gchandle_intptr, Object *p_unmanaged) {
 void CSharpLanguage::tie_managed_to_unmanaged_with_pre_setup(GCHandleIntPtr p_gchandle_intptr, Object *p_unmanaged) {
@@ -1480,6 +1482,8 @@ void CSharpLanguage::tie_managed_to_unmanaged_with_pre_setup(GCHandleIntPtr p_gc
 		// instances is a set, so it's safe to insert multiple times (e.g.: from _internal_new_managed)
 		// instances is a set, so it's safe to insert multiple times (e.g.: from _internal_new_managed)
 		instance->script->instances.insert(instance->owner);
 		instance->script->instances.insert(instance->owner);
 	}
 	}
+
+	instance->connect_event_signals();
 }
 }
 
 
 CSharpInstance *CSharpInstance::create_for_managed_type(Object *p_owner, CSharpScript *p_script, const MonoGCHandleData &p_gchandle) {
 CSharpInstance *CSharpInstance::create_for_managed_type(Object *p_owner, CSharpScript *p_script, const MonoGCHandleData &p_gchandle) {
@@ -1774,13 +1778,22 @@ void CSharpInstance::mono_object_disposed_baseref(GCHandleIntPtr p_gchandle_to_f
 	}
 	}
 }
 }
 
 
-void CSharpInstance::connect_event_signal(const StringName &p_event_signal) {
-	// TODO: Use pooling for ManagedCallable instances.
-	EventSignalCallable *event_signal_callable = memnew(EventSignalCallable(owner, p_event_signal));
+void CSharpInstance::connect_event_signals() {
+	CSharpScript *top = script.ptr();
+	while (top != nullptr) {
+		for (CSharpScript::EventSignalInfo &signal : top->get_script_event_signals()) {
+			String signal_name = signal.name;
 
 
-	Callable callable(event_signal_callable);
-	connected_event_signals.push_back(callable);
-	owner->connect(p_event_signal, callable);
+			// TODO: Use pooling for ManagedCallable instances.
+			EventSignalCallable *event_signal_callable = memnew(EventSignalCallable(owner, signal_name));
+
+			Callable callable(event_signal_callable);
+			connected_event_signals.push_back(callable);
+			owner->connect(signal_name, callable);
+		}
+
+		top = top->base_script.ptr();
+	}
 }
 }
 
 
 void CSharpInstance::disconnect_event_signals() {
 void CSharpInstance::disconnect_event_signals() {
@@ -2169,20 +2182,56 @@ void CSharpScript::reload_registered_script(Ref<CSharpScript> p_script) {
 // Extract information about the script using the mono class.
 // Extract information about the script using the mono class.
 void CSharpScript::update_script_class_info(Ref<CSharpScript> p_script) {
 void CSharpScript::update_script_class_info(Ref<CSharpScript> p_script) {
 	bool tool = false;
 	bool tool = false;
+
 	Dictionary rpc_functions_dict;
 	Dictionary rpc_functions_dict;
 	// Destructor won't be called from C#, and I don't want to include the GDNative header
 	// Destructor won't be called from C#, and I don't want to include the GDNative header
 	// only for this, so need to call the destructor manually before passing this to C#.
 	// only for this, so need to call the destructor manually before passing this to C#.
 	rpc_functions_dict.~Dictionary();
 	rpc_functions_dict.~Dictionary();
 
 
+	Dictionary signals_dict;
+	// Destructor won't be called from C#, and I don't want to include the GDNative header
+	// only for this, so need to call the destructor manually before passing this to C#.
+	signals_dict.~Dictionary();
+
 	Ref<CSharpScript> base_script;
 	Ref<CSharpScript> base_script;
 	GDMonoCache::managed_callbacks.ScriptManagerBridge_UpdateScriptClassInfo(
 	GDMonoCache::managed_callbacks.ScriptManagerBridge_UpdateScriptClassInfo(
-			p_script.ptr(), &tool, &rpc_functions_dict, &base_script);
+			p_script.ptr(), &tool, &rpc_functions_dict, &signals_dict, &base_script);
 
 
 	p_script->tool = tool;
 	p_script->tool = tool;
 
 
 	p_script->rpc_config.clear();
 	p_script->rpc_config.clear();
 	p_script->rpc_config = rpc_functions_dict;
 	p_script->rpc_config = rpc_functions_dict;
 
 
+	// Event signals
+
+	// Performance is not critical here as this will be replaced with source generators.
+
+	p_script->event_signals.clear();
+
+	// Sigh... can't we just have capacity?
+	p_script->event_signals.resize(signals_dict.size());
+	int push_index = 0;
+
+	for (const Variant *s = signals_dict.next(nullptr); s != nullptr; s = signals_dict.next(s)) {
+		StringName name = *s;
+
+		MethodInfo mi;
+		mi.name = name;
+
+		Array params = signals_dict[*s];
+
+		for (int i = 0; i < params.size(); i++) {
+			Dictionary param = params[i];
+
+			Variant::Type param_type = (Variant::Type)(int)param["type"];
+			PropertyInfo arg_info = PropertyInfo(param_type, (String)param["name"]);
+			arg_info.usage = (uint32_t)param["usage"];
+			mi.arguments.push_back(arg_info);
+		}
+
+		p_script->event_signals.set(push_index++, EventSignalInfo{ name, mi });
+	}
+
 	p_script->base_script = base_script;
 	p_script->base_script = base_script;
 }
 }
 
 
@@ -2467,9 +2516,13 @@ bool CSharpScript::has_script_signal(const StringName &p_signal) const {
 		return false;
 		return false;
 	}
 	}
 
 
-	String signal = p_signal;
+	for (const EventSignalInfo &signal : event_signals) {
+		if (signal.name == p_signal) {
+			return true;
+		}
+	}
 
 
-	return GDMonoCache::managed_callbacks.ScriptManagerBridge_HasScriptSignal(this, &signal);
+	return false;
 }
 }
 
 
 void CSharpScript::get_script_signal_list(List<MethodInfo> *r_signals) const {
 void CSharpScript::get_script_signal_list(List<MethodInfo> *r_signals) const {
@@ -2477,38 +2530,17 @@ void CSharpScript::get_script_signal_list(List<MethodInfo> *r_signals) const {
 		return;
 		return;
 	}
 	}
 
 
-	// Performance is not critical here as this will be replaced with source generators.
-
-	if (!GDMonoCache::godot_api_cache_updated) {
-		return;
+	for (const EventSignalInfo &signal : get_script_event_signals()) {
+		r_signals->push_back(signal.method_info);
 	}
 	}
+}
 
 
-	Dictionary signals_dict;
-	// Destructor won't be called from C#, and I don't want to include the GDNative header
-	// only for this, so need to call the destructor manually before passing this to C#.
-	signals_dict.~Dictionary();
-
-	GDMonoCache::managed_callbacks.ScriptManagerBridge_GetScriptSignalList(this, &signals_dict);
-
-	for (const Variant *s = signals_dict.next(nullptr); s != nullptr; s = signals_dict.next(s)) {
-		MethodInfo mi;
-		mi.name = *s;
-
-		Array params = signals_dict[*s];
-
-		for (int i = 0; i < params.size(); i++) {
-			Dictionary param = params[i];
-
-			Variant::Type param_type = (Variant::Type)(int)param["type"];
-			PropertyInfo arg_info = PropertyInfo(param_type, (String)param["name"]);
-			if (param_type == Variant::NIL && (bool)param["nil_is_variant"]) {
-				arg_info.usage |= PROPERTY_USAGE_NIL_IS_VARIANT;
-			}
-			mi.arguments.push_back(arg_info);
-		}
-
-		r_signals->push_back(mi);
+Vector<CSharpScript::EventSignalInfo> CSharpScript::get_script_event_signals() const {
+	if (!valid) {
+		return Vector<EventSignalInfo>();
 	}
 	}
+
+	return event_signals;
 }
 }
 
 
 bool CSharpScript::inherits_script(const Ref<Script> &p_script) const {
 bool CSharpScript::inherits_script(const Ref<Script> &p_script) const {

+ 10 - 1
modules/mono/csharp_script.h

@@ -98,6 +98,13 @@ class CSharpScript : public Script {
 
 
 	Dictionary rpc_config;
 	Dictionary rpc_config;
 
 
+	struct EventSignalInfo {
+		StringName name; // MethodInfo stores a string...
+		MethodInfo method_info;
+	};
+
+	Vector<EventSignalInfo> event_signals;
+
 #ifdef TOOLS_ENABLED
 #ifdef TOOLS_ENABLED
 	List<PropertyInfo> exported_members_cache; // members_cache
 	List<PropertyInfo> exported_members_cache; // members_cache
 	HashMap<StringName, Variant> exported_members_defval_cache; // member_default_values_cache
 	HashMap<StringName, Variant> exported_members_defval_cache; // member_default_values_cache
@@ -158,6 +165,8 @@ public:
 	bool has_script_signal(const StringName &p_signal) const override;
 	bool has_script_signal(const StringName &p_signal) const override;
 	void get_script_signal_list(List<MethodInfo> *r_signals) const override;
 	void get_script_signal_list(List<MethodInfo> *r_signals) const override;
 
 
+	Vector<EventSignalInfo> get_script_event_signals() const;
+
 	bool get_property_default_value(const StringName &p_property, Variant &r_value) const override;
 	bool get_property_default_value(const StringName &p_property, Variant &r_value) const override;
 	void get_script_property_list(List<PropertyInfo> *r_list) const override;
 	void get_script_property_list(List<PropertyInfo> *r_list) const override;
 	void update_exports() override;
 	void update_exports() override;
@@ -254,7 +263,7 @@ public:
 	 */
 	 */
 	void mono_object_disposed_baseref(GCHandleIntPtr p_gchandle_to_free, bool p_is_finalizer, bool &r_delete_owner, bool &r_remove_script_instance);
 	void mono_object_disposed_baseref(GCHandleIntPtr p_gchandle_to_free, bool p_is_finalizer, bool &r_delete_owner, bool &r_remove_script_instance);
 
 
-	void connect_event_signal(const StringName &p_event_signal);
+	void connect_event_signals();
 	void disconnect_event_signals();
 	void disconnect_event_signals();
 
 
 	void refcount_incremented() override;
 	void refcount_incremented() override;

+ 7 - 0
modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Sample/EventSignals.cs

@@ -0,0 +1,7 @@
+namespace Godot.SourceGenerators.Sample;
+
+public partial class EventSignals : Godot.Object
+{
+    [Signal]
+    public delegate void MySignalEventHandler(string str, int num);
+}

+ 49 - 0
modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/Common.cs

@@ -166,5 +166,54 @@ namespace Godot.SourceGenerators
                 location,
                 location,
                 location?.SourceTree?.FilePath));
                 location?.SourceTree?.FilePath));
         }
         }
+
+        public static void ReportSignalDelegateMissingSuffix(
+            GeneratorExecutionContext context,
+            INamedTypeSymbol delegateSymbol)
+        {
+            var locations = delegateSymbol.Locations;
+            var location = locations.FirstOrDefault(l => l.SourceTree != null) ?? locations.FirstOrDefault();
+
+            string message = "The name of the delegate must end with 'EventHandler': " +
+                             delegateSymbol.ToDisplayString() +
+                             $". Did you mean '{delegateSymbol.Name}EventHandler'?";
+
+            string description = $"{message}. Rename the delegate accordingly or remove the '[Signal]' attribute.";
+
+            context.ReportDiagnostic(Diagnostic.Create(
+                new DiagnosticDescriptor(id: "GODOT-G0201",
+                    title: message,
+                    messageFormat: message,
+                    category: "Usage",
+                    DiagnosticSeverity.Error,
+                    isEnabledByDefault: true,
+                    description),
+                location,
+                location?.SourceTree?.FilePath));
+        }
+
+        public static void ReportSignalDelegateSignatureNotSupported(
+            GeneratorExecutionContext context,
+            INamedTypeSymbol delegateSymbol)
+        {
+            var locations = delegateSymbol.Locations;
+            var location = locations.FirstOrDefault(l => l.SourceTree != null) ?? locations.FirstOrDefault();
+
+            string message = "The delegate signature of the signal " +
+                             $"is not supported: '{delegateSymbol.ToDisplayString()}'";
+
+            string description = $"{message}. Use supported types only or remove the '[Signal]' attribute.";
+
+            context.ReportDiagnostic(Diagnostic.Create(
+                new DiagnosticDescriptor(id: "GODOT-G0202",
+                    title: message,
+                    messageFormat: message,
+                    category: "Usage",
+                    DiagnosticSeverity.Error,
+                    isEnabledByDefault: true,
+                    description),
+                location,
+                location?.SourceTree?.FilePath));
+        }
     }
     }
 }
 }

+ 39 - 25
modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ExtensionMethods.cs

@@ -174,46 +174,60 @@ namespace Godot.SourceGenerators
         public static bool IsGodotExportAttribute(this INamedTypeSymbol symbol)
         public static bool IsGodotExportAttribute(this INamedTypeSymbol symbol)
             => symbol.ToString() == GodotClasses.ExportAttr;
             => symbol.ToString() == GodotClasses.ExportAttr;
 
 
+        public static bool IsGodotSignalAttribute(this INamedTypeSymbol symbol)
+            => symbol.ToString() == GodotClasses.SignalAttr;
+
         public static bool IsGodotClassNameAttribute(this INamedTypeSymbol symbol)
         public static bool IsGodotClassNameAttribute(this INamedTypeSymbol symbol)
             => symbol.ToString() == GodotClasses.GodotClassNameAttr;
             => symbol.ToString() == GodotClasses.GodotClassNameAttr;
 
 
         public static bool IsSystemFlagsAttribute(this INamedTypeSymbol symbol)
         public static bool IsSystemFlagsAttribute(this INamedTypeSymbol symbol)
             => symbol.ToString() == GodotClasses.SystemFlagsAttr;
             => symbol.ToString() == GodotClasses.SystemFlagsAttr;
 
 
-        public static IEnumerable<GodotMethodData> WhereHasGodotCompatibleSignature(
-            this IEnumerable<IMethodSymbol> methods,
+        public static GodotMethodData? HasGodotCompatibleSignature(
+            this IMethodSymbol method,
             MarshalUtils.TypeCache typeCache
             MarshalUtils.TypeCache typeCache
         )
         )
         {
         {
-            foreach (var method in methods)
-            {
-                if (method.IsGenericMethod)
-                    continue;
+            if (method.IsGenericMethod)
+                return null;
 
 
-                var retSymbol = method.ReturnType;
-                var retType = method.ReturnsVoid ?
-                    null :
-                    MarshalUtils.ConvertManagedTypeToMarshalType(method.ReturnType, typeCache);
+            var retSymbol = method.ReturnType;
+            var retType = method.ReturnsVoid ?
+                null :
+                MarshalUtils.ConvertManagedTypeToMarshalType(method.ReturnType, typeCache);
 
 
-                if (retType == null && !method.ReturnsVoid)
-                    continue;
+            if (retType == null && !method.ReturnsVoid)
+                return null;
 
 
-                var parameters = method.Parameters;
+            var parameters = method.Parameters;
 
 
-                var paramTypes = parameters
-                    // Currently we don't support `ref`, `out`, `in`, `ref readonly` parameters (and we never may)
-                    .Where(p => p.RefKind == RefKind.None)
-                    // Attempt to determine the variant type
-                    .Select(p => MarshalUtils.ConvertManagedTypeToMarshalType(p.Type, typeCache))
-                    // Discard parameter types that couldn't be determined (null entries)
-                    .Where(t => t != null).Cast<MarshalType>().ToImmutableArray();
+            var paramTypes = parameters
+                // Currently we don't support `ref`, `out`, `in`, `ref readonly` parameters (and we never may)
+                .Where(p => p.RefKind == RefKind.None)
+                // Attempt to determine the variant type
+                .Select(p => MarshalUtils.ConvertManagedTypeToMarshalType(p.Type, typeCache))
+                // Discard parameter types that couldn't be determined (null entries)
+                .Where(t => t != null).Cast<MarshalType>().ToImmutableArray();
 
 
-                // If any parameter type was incompatible, it was discarded so the length won't match
-                if (parameters.Length > paramTypes.Length)
-                    continue;
+            // If any parameter type was incompatible, it was discarded so the length won't match
+            if (parameters.Length > paramTypes.Length)
+                return null; // Ignore incompatible method
+
+            return new GodotMethodData(method, paramTypes, parameters
+                .Select(p => p.Type).ToImmutableArray(), retType, retSymbol);
+        }
+
+        public static IEnumerable<GodotMethodData> WhereHasGodotCompatibleSignature(
+            this IEnumerable<IMethodSymbol> methods,
+            MarshalUtils.TypeCache typeCache
+        )
+        {
+            foreach (var method in methods)
+            {
+                var methodData = HasGodotCompatibleSignature(method, typeCache);
 
 
-                yield return new GodotMethodData(method, paramTypes, parameters
-                    .Select(p => p.Type).ToImmutableArray(), retType, retSymbol);
+                if (methodData != null)
+                    yield return methodData.Value;
             }
             }
         }
         }
 
 

+ 1 - 0
modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/GodotClasses.cs

@@ -5,6 +5,7 @@ namespace Godot.SourceGenerators
         public const string Object = "Godot.Object";
         public const string Object = "Godot.Object";
         public const string AssemblyHasScriptsAttr = "Godot.AssemblyHasScriptsAttribute";
         public const string AssemblyHasScriptsAttr = "Godot.AssemblyHasScriptsAttribute";
         public const string ExportAttr = "Godot.ExportAttribute";
         public const string ExportAttr = "Godot.ExportAttribute";
+        public const string SignalAttr = "Godot.SignalAttribute";
         public const string GodotClassNameAttr = "Godot.GodotClassName";
         public const string GodotClassNameAttr = "Godot.GodotClassName";
         public const string SystemFlagsAttr = "System.FlagsAttribute";
         public const string SystemFlagsAttr = "System.FlagsAttribute";
     }
     }

+ 14 - 0
modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/GodotEnums.cs

@@ -2,6 +2,8 @@ using System;
 
 
 namespace Godot.SourceGenerators
 namespace Godot.SourceGenerators
 {
 {
+    // TODO: May need to think about compatibility here. Could Godot change these values between minor versions?
+
     internal enum VariantType
     internal enum VariantType
     {
     {
         Nil = 0,
         Nil = 0,
@@ -131,4 +133,16 @@ namespace Godot.SourceGenerators
         DefaultIntl = 38,
         DefaultIntl = 38,
         NoEditor = 2
         NoEditor = 2
     }
     }
+
+    public enum MethodFlags
+    {
+        Normal = 1,
+        Editor = 2,
+        Const = 4,
+        Virtual = 8,
+        Vararg = 16,
+        Static = 32,
+        ObjectCore = 64,
+        Default = 1
+    }
 }
 }

+ 14 - 0
modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/GodotMemberData.cs

@@ -22,6 +22,20 @@ namespace Godot.SourceGenerators
         public ITypeSymbol? RetSymbol { get; }
         public ITypeSymbol? RetSymbol { get; }
     }
     }
 
 
+    public struct GodotSignalDelegateData
+    {
+        public GodotSignalDelegateData(string name, INamedTypeSymbol delegateSymbol, GodotMethodData invokeMethodData)
+        {
+            Name = name;
+            DelegateSymbol = delegateSymbol;
+            InvokeMethodData = invokeMethodData;
+        }
+
+        public string Name { get; }
+        public INamedTypeSymbol DelegateSymbol { get; }
+        public GodotMethodData InvokeMethodData { get; }
+    }
+
     public struct GodotPropertyData
     public struct GodotPropertyData
     {
     {
         public GodotPropertyData(IPropertySymbol propertySymbol, MarshalType type)
         public GodotPropertyData(IPropertySymbol propertySymbol, MarshalType type)

+ 24 - 0
modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/MethodInfo.cs

@@ -0,0 +1,24 @@
+using System.Collections.Generic;
+
+namespace Godot.SourceGenerators
+{
+    internal struct MethodInfo
+    {
+        public MethodInfo(string name, PropertyInfo returnVal, MethodFlags flags,
+            List<PropertyInfo>? arguments,
+            List<string?>? defaultArguments)
+        {
+            Name = name;
+            ReturnVal = returnVal;
+            Flags = flags;
+            Arguments = arguments;
+            DefaultArguments = defaultArguments;
+        }
+
+        public string Name { get; }
+        public PropertyInfo ReturnVal { get; }
+        public MethodFlags Flags { get; }
+        public List<PropertyInfo>? Arguments { get; }
+        public List<string?>? DefaultArguments { get; }
+    }
+}

+ 23 - 0
modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/PropertyInfo.cs

@@ -0,0 +1,23 @@
+namespace Godot.SourceGenerators
+{
+    internal struct PropertyInfo
+    {
+        public PropertyInfo(VariantType type, string name, PropertyHint hint,
+            string? hintString, PropertyUsageFlags usage, bool exported)
+        {
+            Type = type;
+            Name = name;
+            Hint = hint;
+            HintString = hintString;
+            Usage = usage;
+            Exported = exported;
+        }
+
+        public VariantType Type { get; }
+        public string Name { get; }
+        public PropertyHint Hint { get; }
+        public string? HintString { get; }
+        public PropertyUsageFlags Usage { get; }
+        public bool Exported { get; }
+    }
+}

+ 97 - 18
modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptMemberInvokerGenerator.cs

@@ -1,3 +1,4 @@
+using System.Collections.Generic;
 using System.Linq;
 using System.Linq;
 using System.Text;
 using System.Text;
 using Microsoft.CodeAnalysis;
 using Microsoft.CodeAnalysis;
@@ -122,6 +123,33 @@ namespace Godot.SourceGenerators
             var godotClassProperties = propertySymbols.WhereIsGodotCompatibleType(typeCache).ToArray();
             var godotClassProperties = propertySymbols.WhereIsGodotCompatibleType(typeCache).ToArray();
             var godotClassFields = fieldSymbols.WhereIsGodotCompatibleType(typeCache).ToArray();
             var godotClassFields = fieldSymbols.WhereIsGodotCompatibleType(typeCache).ToArray();
 
 
+            var signalDelegateSymbols = members
+                .Where(s => s.Kind == SymbolKind.NamedType)
+                .Cast<INamedTypeSymbol>()
+                .Where(namedTypeSymbol => namedTypeSymbol.TypeKind == TypeKind.Delegate)
+                .Where(s => s.GetAttributes()
+                    .Any(a => a.AttributeClass?.IsGodotSignalAttribute() ?? false));
+
+            List<GodotSignalDelegateData> godotSignalDelegates = new();
+
+            foreach (var signalDelegateSymbol in signalDelegateSymbols)
+            {
+                if (!signalDelegateSymbol.Name.EndsWith(ScriptSignalsGenerator.SignalDelegateSuffix))
+                    continue;
+
+                string signalName = signalDelegateSymbol.Name;
+                signalName = signalName.Substring(0,
+                    signalName.Length - ScriptSignalsGenerator.SignalDelegateSuffix.Length);
+
+                var invokeMethodData = signalDelegateSymbol
+                    .DelegateInvokeMethod?.HasGodotCompatibleSignature(typeCache);
+
+                if (invokeMethodData == null)
+                    continue;
+
+                godotSignalDelegates.Add(new(signalName, signalDelegateSymbol, invokeMethodData.Value));
+            }
+
             source.Append("    private partial class GodotInternal {\n");
             source.Append("    private partial class GodotInternal {\n");
 
 
             // Generate cached StringNames for methods and properties, for fast lookup
             // Generate cached StringNames for methods and properties, for fast lookup
@@ -157,6 +185,42 @@ namespace Godot.SourceGenerators
                 source.Append("    }\n");
                 source.Append("    }\n");
             }
             }
 
 
+            // Generate HasGodotClassMethod
+
+            if (godotClassMethods.Length > 0)
+            {
+                source.Append("    protected override bool HasGodotClassMethod(in godot_string_name method)\n    {\n");
+
+                bool isFirstEntry = true;
+                foreach (var method in godotClassMethods)
+                {
+                    GenerateHasMethodEntry(method, source, isFirstEntry);
+                    isFirstEntry = false;
+                }
+
+                source.Append("        return base.HasGodotClassMethod(method);\n");
+
+                source.Append("    }\n");
+            }
+
+            // Generate RaiseGodotClassSignalCallbacks
+
+            if (godotSignalDelegates.Count > 0)
+            {
+                source.Append(
+                    "    protected override void RaiseGodotClassSignalCallbacks(in godot_string_name signal, ");
+                source.Append("NativeVariantPtrArgs args, int argCount)\n    {\n");
+
+                foreach (var signal in godotSignalDelegates)
+                {
+                    GenerateSignalEventInvoker(signal, source);
+                }
+
+                source.Append("        base.RaiseGodotClassSignalCallbacks(signal, args, argCount);\n");
+
+                source.Append("    }\n");
+            }
+
             // Generate Set/GetGodotClassPropertyValue
             // Generate Set/GetGodotClassPropertyValue
 
 
             if (godotClassProperties.Length > 0 || godotClassFields.Length > 0)
             if (godotClassProperties.Length > 0 || godotClassFields.Length > 0)
@@ -224,24 +288,6 @@ namespace Godot.SourceGenerators
                 source.Append("    }\n");
                 source.Append("    }\n");
             }
             }
 
 
-            // Generate HasGodotClassMethod
-
-            if (godotClassMethods.Length > 0)
-            {
-                source.Append("    protected override bool HasGodotClassMethod(in godot_string_name method)\n    {\n");
-
-                bool isFirstEntry = true;
-                foreach (var method in godotClassMethods)
-                {
-                    GenerateHasMethodEntry(method, source, isFirstEntry);
-                    isFirstEntry = false;
-                }
-
-                source.Append("        return base.HasGodotClassMethod(method);\n");
-
-                source.Append("    }\n");
-            }
-
             source.Append("}\n"); // partial class
             source.Append("}\n"); // partial class
 
 
             if (isInnerClass)
             if (isInnerClass)
@@ -314,6 +360,39 @@ namespace Godot.SourceGenerators
             source.Append("        }\n");
             source.Append("        }\n");
         }
         }
 
 
+        private static void GenerateSignalEventInvoker(
+            GodotSignalDelegateData signal,
+            StringBuilder source
+        )
+        {
+            string signalName = signal.Name;
+            var invokeMethodData = signal.InvokeMethodData;
+
+            source.Append("        if (signal == GodotInternal.SignalName_");
+            source.Append(signalName);
+            source.Append(" && argCount == ");
+            source.Append(invokeMethodData.ParamTypes.Length);
+            source.Append(") {\n");
+            source.Append("            backing_");
+            source.Append(signalName);
+            source.Append("?.Invoke(");
+
+            for (int i = 0; i < invokeMethodData.ParamTypes.Length; i++)
+            {
+                if (i != 0)
+                    source.Append(", ");
+
+                source.AppendVariantToManagedExpr(string.Concat("args[", i.ToString(), "]"),
+                    invokeMethodData.ParamTypeSymbols[i], invokeMethodData.ParamTypes[i]);
+            }
+
+            source.Append(");\n");
+
+            source.Append("            return;\n");
+
+            source.Append("        }\n");
+        }
+
         private static void GeneratePropertySetter(
         private static void GeneratePropertySetter(
             string propertyMemberName,
             string propertyMemberName,
             ITypeSymbol propertyTypeSymbol,
             ITypeSymbol propertyTypeSymbol,

+ 5 - 26
modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptPropertiesGenerator.cs

@@ -146,7 +146,7 @@ namespace Godot.SourceGenerators
 
 
             source.Append("    }\n"); // class GodotInternal
             source.Append("    }\n"); // class GodotInternal
 
 
-            // Generate GetGodotPropertiesMetadata
+            // Generate GetGodotPropertyList
 
 
             if (godotClassProperties.Length > 0 || godotClassFields.Length > 0)
             if (godotClassProperties.Length > 0 || godotClassFields.Length > 0)
             {
             {
@@ -156,7 +156,7 @@ namespace Godot.SourceGenerators
 
 
                 source.Append("    internal new static ")
                 source.Append("    internal new static ")
                     .Append(dictionaryType)
                     .Append(dictionaryType)
-                    .Append(" GetGodotPropertiesMetadata()\n    {\n");
+                    .Append(" GetGodotPropertyList()\n    {\n");
 
 
                 source.Append("        var properties = new ")
                 source.Append("        var properties = new ")
                     .Append(dictionaryType)
                     .Append(dictionaryType)
@@ -164,7 +164,7 @@ namespace Godot.SourceGenerators
 
 
                 foreach (var property in godotClassProperties)
                 foreach (var property in godotClassProperties)
                 {
                 {
-                    var propertyInfo = GetPropertyMetadata(context, typeCache,
+                    var propertyInfo = DeterminePropertyInfo(context, typeCache,
                         property.PropertySymbol, property.Type);
                         property.PropertySymbol, property.Type);
 
 
                     if (propertyInfo == null)
                     if (propertyInfo == null)
@@ -175,7 +175,7 @@ namespace Godot.SourceGenerators
 
 
                 foreach (var field in godotClassFields)
                 foreach (var field in godotClassFields)
                 {
                 {
-                    var propertyInfo = GetPropertyMetadata(context, typeCache,
+                    var propertyInfo = DeterminePropertyInfo(context, typeCache,
                         field.FieldSymbol, field.Type);
                         field.FieldSymbol, field.Type);
 
 
                     if (propertyInfo == null)
                     if (propertyInfo == null)
@@ -229,28 +229,7 @@ namespace Godot.SourceGenerators
                 .Append("));\n");
                 .Append("));\n");
         }
         }
 
 
-        private struct PropertyInfo
-        {
-            public PropertyInfo(VariantType type, string name, PropertyHint hint,
-                string? hintString, PropertyUsageFlags usage, bool exported)
-            {
-                Type = type;
-                Name = name;
-                Hint = hint;
-                HintString = hintString;
-                Usage = usage;
-                Exported = exported;
-            }
-
-            public VariantType Type { get; }
-            public string Name { get; }
-            public PropertyHint Hint { get; }
-            public string? HintString { get; }
-            public PropertyUsageFlags Usage { get; }
-            public bool Exported { get; }
-        }
-
-        private static PropertyInfo? GetPropertyMetadata(
+        private static PropertyInfo? DeterminePropertyInfo(
             GeneratorExecutionContext context,
             GeneratorExecutionContext context,
             MarshalUtils.TypeCache typeCache,
             MarshalUtils.TypeCache typeCache,
             ISymbol memberSymbol,
             ISymbol memberSymbol,

+ 70 - 0
modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptSerializationGenerator.cs

@@ -1,3 +1,4 @@
+using System.Collections.Generic;
 using System.Linq;
 using System.Linq;
 using System.Text;
 using System.Text;
 using Microsoft.CodeAnalysis;
 using Microsoft.CodeAnalysis;
@@ -120,10 +121,39 @@ namespace Godot.SourceGenerators
             var godotClassProperties = propertySymbols.WhereIsGodotCompatibleType(typeCache).ToArray();
             var godotClassProperties = propertySymbols.WhereIsGodotCompatibleType(typeCache).ToArray();
             var godotClassFields = fieldSymbols.WhereIsGodotCompatibleType(typeCache).ToArray();
             var godotClassFields = fieldSymbols.WhereIsGodotCompatibleType(typeCache).ToArray();
 
 
+            var signalDelegateSymbols = members
+                .Where(s => s.Kind == SymbolKind.NamedType)
+                .Cast<INamedTypeSymbol>()
+                .Where(namedTypeSymbol => namedTypeSymbol.TypeKind == TypeKind.Delegate)
+                .Where(s => s.GetAttributes()
+                    .Any(a => a.AttributeClass?.IsGodotSignalAttribute() ?? false));
+
+            List<GodotSignalDelegateData> godotSignalDelegates = new();
+
+            foreach (var signalDelegateSymbol in signalDelegateSymbols)
+            {
+                if (!signalDelegateSymbol.Name.EndsWith(ScriptSignalsGenerator.SignalDelegateSuffix))
+                    continue;
+
+                string signalName = signalDelegateSymbol.Name;
+                signalName = signalName.Substring(0,
+                    signalName.Length - ScriptSignalsGenerator.SignalDelegateSuffix.Length);
+
+                var invokeMethodData = signalDelegateSymbol
+                    .DelegateInvokeMethod?.HasGodotCompatibleSignature(typeCache);
+
+                if (invokeMethodData == null)
+                    continue;
+
+                godotSignalDelegates.Add(new(signalName, signalDelegateSymbol, invokeMethodData.Value));
+            }
+
             source.Append(
             source.Append(
                 "    protected override void SaveGodotObjectData(global::Godot.Bridge.GodotSerializationInfo info)\n    {\n");
                 "    protected override void SaveGodotObjectData(global::Godot.Bridge.GodotSerializationInfo info)\n    {\n");
             source.Append("        base.SaveGodotObjectData(info);\n");
             source.Append("        base.SaveGodotObjectData(info);\n");
 
 
+            // Save properties
+
             foreach (var property in godotClassProperties)
             foreach (var property in godotClassProperties)
             {
             {
                 string propertyName = property.PropertySymbol.Name;
                 string propertyName = property.PropertySymbol.Name;
@@ -135,6 +165,8 @@ namespace Godot.SourceGenerators
                     .Append(");\n");
                     .Append(");\n");
             }
             }
 
 
+            // Save fields
+
             foreach (var field in godotClassFields)
             foreach (var field in godotClassFields)
             {
             {
                 string fieldName = field.FieldSymbol.Name;
                 string fieldName = field.FieldSymbol.Name;
@@ -146,12 +178,27 @@ namespace Godot.SourceGenerators
                     .Append(");\n");
                     .Append(");\n");
             }
             }
 
 
+            // Save signal events
+
+            foreach (var signalDelegate in godotSignalDelegates)
+            {
+                string signalName = signalDelegate.Name;
+
+                source.Append("        info.AddSignalEventDelegate(GodotInternal.SignalName_")
+                    .Append(signalName)
+                    .Append(", this.backing_")
+                    .Append(signalName)
+                    .Append(");\n");
+            }
+
             source.Append("    }\n");
             source.Append("    }\n");
 
 
             source.Append(
             source.Append(
                 "    protected override void RestoreGodotObjectData(global::Godot.Bridge.GodotSerializationInfo info)\n    {\n");
                 "    protected override void RestoreGodotObjectData(global::Godot.Bridge.GodotSerializationInfo info)\n    {\n");
             source.Append("        base.RestoreGodotObjectData(info);\n");
             source.Append("        base.RestoreGodotObjectData(info);\n");
 
 
+            // Restore properties
+
             foreach (var property in godotClassProperties)
             foreach (var property in godotClassProperties)
             {
             {
                 string propertyName = property.PropertySymbol.Name;
                 string propertyName = property.PropertySymbol.Name;
@@ -171,6 +218,8 @@ namespace Godot.SourceGenerators
                     .Append(";\n");
                     .Append(";\n");
             }
             }
 
 
+            // Restore fields
+
             foreach (var field in godotClassFields)
             foreach (var field in godotClassFields)
             {
             {
                 string fieldName = field.FieldSymbol.Name;
                 string fieldName = field.FieldSymbol.Name;
@@ -190,6 +239,27 @@ namespace Godot.SourceGenerators
                     .Append(";\n");
                     .Append(";\n");
             }
             }
 
 
+            // Restore signal events
+
+            foreach (var signalDelegate in godotSignalDelegates)
+            {
+                string signalName = signalDelegate.Name;
+                string signalDelegateQualifiedName = signalDelegate.DelegateSymbol.FullQualifiedName();
+
+                source.Append("        if (info.TryGetSignalEventDelegate<")
+                    .Append(signalDelegateQualifiedName)
+                    .Append(">(GodotInternal.SignalName_")
+                    .Append(signalName)
+                    .Append(", out var _value_")
+                    .Append(signalName)
+                    .Append("))\n")
+                    .Append("            this.backing_")
+                    .Append(signalName)
+                    .Append(" = _value_")
+                    .Append(signalName)
+                    .Append(";\n");
+            }
+
             source.Append("    }\n");
             source.Append("    }\n");
 
 
             source.Append("}\n"); // partial class
             source.Append("}\n"); // partial class

+ 360 - 0
modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptSignalsGenerator.cs

@@ -0,0 +1,360 @@
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.CSharp.Syntax;
+using Microsoft.CodeAnalysis.Text;
+
+// TODO:
+//   Determine a proper way to emit the signal.
+//   'Emit(nameof(TheEvent))' creates a StringName everytime and has the overhead of string marshaling.
+//   I haven't decided on the best option yet. Some possibilities:
+//     - Expose the generated StringName fields to the user, for use with 'Emit(...)'.
+//     - Generate a 'EmitSignalName' method for each event signal.
+
+namespace Godot.SourceGenerators
+{
+    [Generator]
+    public class ScriptSignalsGenerator : ISourceGenerator
+    {
+        public void Initialize(GeneratorInitializationContext context)
+        {
+        }
+
+        public void Execute(GeneratorExecutionContext context)
+        {
+            if (context.AreGodotSourceGeneratorsDisabled())
+                return;
+
+            INamedTypeSymbol[] godotClasses = context
+                .Compilation.SyntaxTrees
+                .SelectMany(tree =>
+                    tree.GetRoot().DescendantNodes()
+                        .OfType<ClassDeclarationSyntax>()
+                        .SelectGodotScriptClasses(context.Compilation)
+                        // Report and skip non-partial classes
+                        .Where(x =>
+                        {
+                            if (x.cds.IsPartial())
+                            {
+                                if (x.cds.IsNested() && !x.cds.AreAllOuterTypesPartial(out var typeMissingPartial))
+                                {
+                                    Common.ReportNonPartialGodotScriptOuterClass(context, typeMissingPartial!);
+                                    return false;
+                                }
+
+                                return true;
+                            }
+
+                            Common.ReportNonPartialGodotScriptClass(context, x.cds, x.symbol);
+                            return false;
+                        })
+                        .Select(x => x.symbol)
+                )
+                .Distinct<INamedTypeSymbol>(SymbolEqualityComparer.Default)
+                .ToArray();
+
+            if (godotClasses.Length > 0)
+            {
+                var typeCache = new MarshalUtils.TypeCache(context);
+
+                foreach (var godotClass in godotClasses)
+                {
+                    VisitGodotScriptClass(context, typeCache, godotClass);
+                }
+            }
+        }
+
+        internal static string SignalDelegateSuffix = "EventHandler";
+
+        private static void VisitGodotScriptClass(
+            GeneratorExecutionContext context,
+            MarshalUtils.TypeCache typeCache,
+            INamedTypeSymbol symbol
+        )
+        {
+            INamespaceSymbol namespaceSymbol = symbol.ContainingNamespace;
+            string classNs = namespaceSymbol != null && !namespaceSymbol.IsGlobalNamespace ?
+                namespaceSymbol.FullQualifiedName() :
+                string.Empty;
+            bool hasNamespace = classNs.Length != 0;
+
+            bool isInnerClass = symbol.ContainingType != null;
+
+            string uniqueHint = symbol.FullQualifiedName().SanitizeQualifiedNameForUniqueHint()
+                                + "_ScriptSignals_Generated";
+
+            var source = new StringBuilder();
+
+            source.Append("using Godot;\n");
+            source.Append("using Godot.NativeInterop;\n");
+            source.Append("\n");
+
+            if (hasNamespace)
+            {
+                source.Append("namespace ");
+                source.Append(classNs);
+                source.Append(" {\n\n");
+            }
+
+            if (isInnerClass)
+            {
+                var containingType = symbol.ContainingType;
+
+                while (containingType != null)
+                {
+                    source.Append("partial ");
+                    source.Append(containingType.GetDeclarationKeyword());
+                    source.Append(" ");
+                    source.Append(containingType.NameWithTypeParameters());
+                    source.Append("\n{\n");
+
+                    containingType = containingType.ContainingType;
+                }
+            }
+
+            source.Append("partial class ");
+            source.Append(symbol.NameWithTypeParameters());
+            source.Append("\n{\n");
+
+            // TODO:
+            // The delegate name already needs to end with 'Signal' to avoid collision with the event name.
+            // Requiring SignalAttribute is redundant. Should we remove it to make declaration shorter?
+
+            var members = symbol.GetMembers();
+
+            var signalDelegateSymbols = members
+                .Where(s => s.Kind == SymbolKind.NamedType)
+                .Cast<INamedTypeSymbol>()
+                .Where(namedTypeSymbol => namedTypeSymbol.TypeKind == TypeKind.Delegate)
+                .Where(s => s.GetAttributes()
+                    .Any(a => a.AttributeClass?.IsGodotSignalAttribute() ?? false));
+
+            List<GodotSignalDelegateData> godotSignalDelegates = new();
+
+            foreach (var signalDelegateSymbol in signalDelegateSymbols)
+            {
+                if (!signalDelegateSymbol.Name.EndsWith(SignalDelegateSuffix))
+                {
+                    Common.ReportSignalDelegateMissingSuffix(context, signalDelegateSymbol);
+                    continue;
+                }
+
+                string signalName = signalDelegateSymbol.Name;
+                signalName = signalName.Substring(0, signalName.Length - SignalDelegateSuffix.Length);
+
+                var invokeMethodData = signalDelegateSymbol
+                    .DelegateInvokeMethod?.HasGodotCompatibleSignature(typeCache);
+
+                if (invokeMethodData == null)
+                {
+                    // TODO: Better error for incompatible signature. We should indicate incompatible argument types, as we do with exported properties.
+                    Common.ReportSignalDelegateSignatureNotSupported(context, signalDelegateSymbol);
+                    continue;
+                }
+
+                godotSignalDelegates.Add(new(signalName, signalDelegateSymbol, invokeMethodData.Value));
+            }
+
+            source.Append("    private partial class GodotInternal {\n");
+
+            // Generate cached StringNames for methods and properties, for fast lookup
+
+            foreach (var signalDelegate in godotSignalDelegates)
+            {
+                string signalName = signalDelegate.Name;
+                source.Append("        public static readonly StringName SignalName_");
+                source.Append(signalName);
+                source.Append(" = \"");
+                source.Append(signalName);
+                source.Append("\";\n");
+            }
+
+            source.Append("    }\n"); // class GodotInternal
+
+            // Generate GetGodotSignalList
+
+            if (godotSignalDelegates.Count > 0)
+            {
+                source.Append("#pragma warning disable CS0109 // Disable warning about redundant 'new' keyword\n");
+
+                const string listType = "System.Collections.Generic.List<global::Godot.Bridge.MethodInfo>";
+
+                source.Append("    internal new static ")
+                    .Append(listType)
+                    .Append(" GetGodotSignalList()\n    {\n");
+
+                source.Append("        var signals = new ")
+                    .Append(listType)
+                    .Append("(")
+                    .Append(godotSignalDelegates.Count)
+                    .Append(");\n");
+
+                foreach (var signalDelegateData in godotSignalDelegates)
+                {
+                    var methodInfo = DetermineMethodInfo(signalDelegateData);
+                    AppendMethodInfo(source, methodInfo);
+                }
+
+                source.Append("        return signals;\n");
+                source.Append("    }\n");
+
+                source.Append("#pragma warning restore CS0109\n");
+            }
+
+            // Generate signal event
+
+            foreach (var signalDelegate in godotSignalDelegates)
+            {
+                string signalName = signalDelegate.Name;
+
+                // TODO: Hide backing event from code-completion and debugger
+                // The reason we have a backing field is to hide the invoke method from the event,
+                // as it doesn't emit the signal, only the event delegates. This can confuse users.
+                // Maybe we should directly connect the delegates, as we do with native signals?
+                source.Append("    private ")
+                    .Append(signalDelegate.DelegateSymbol.FullQualifiedName())
+                    .Append(" backing_")
+                    .Append(signalName)
+                    .Append(";\n");
+
+                source.Append("    public event ")
+                    .Append(signalDelegate.DelegateSymbol.FullQualifiedName())
+                    .Append(" ")
+                    .Append(signalName)
+                    .Append(" {\n")
+                    .Append("        add => backing_")
+                    .Append(signalName)
+                    .Append(" += value;\n")
+                    .Append("        remove => backing_")
+                    .Append(signalName)
+                    .Append(" -= value;\n")
+                    .Append("}\n");
+            }
+
+            source.Append("}\n"); // partial class
+
+            if (isInnerClass)
+            {
+                var containingType = symbol.ContainingType;
+
+                while (containingType != null)
+                {
+                    source.Append("}\n"); // outer class
+
+                    containingType = containingType.ContainingType;
+                }
+            }
+
+            if (hasNamespace)
+            {
+                source.Append("\n}\n");
+            }
+
+            context.AddSource(uniqueHint, SourceText.From(source.ToString(), Encoding.UTF8));
+        }
+
+        private static void AppendMethodInfo(StringBuilder source, MethodInfo methodInfo)
+        {
+            source.Append("        signals.Add(new(name: GodotInternal.SignalName_")
+                .Append(methodInfo.Name)
+                .Append(", returnVal: ");
+
+            AppendPropertyInfo(source, methodInfo.ReturnVal);
+
+            source.Append(", flags: (Godot.MethodFlags)")
+                .Append((int)methodInfo.Flags)
+                .Append(", arguments: ");
+
+            if (methodInfo.Arguments is { Count: > 0 })
+            {
+                source.Append("new() { ");
+
+                foreach (var param in methodInfo.Arguments)
+                {
+                    AppendPropertyInfo(source, param);
+
+                    // C# allows colon after the last element
+                    source.Append(", ");
+                }
+
+                source.Append(" }");
+            }
+            else
+            {
+                source.Append("null");
+            }
+
+            source.Append(", defaultArguments: null));\n");
+        }
+
+        private static void AppendPropertyInfo(StringBuilder source, PropertyInfo propertyInfo)
+        {
+            source.Append("new(type: (Godot.Variant.Type)")
+                .Append((int)propertyInfo.Type)
+                .Append(", name: \"")
+                .Append(propertyInfo.Name)
+                .Append("\", hint: (Godot.PropertyHint)")
+                .Append((int)propertyInfo.Hint)
+                .Append(", hintString: \"")
+                .Append(propertyInfo.HintString)
+                .Append("\", usage: (Godot.PropertyUsageFlags)")
+                .Append((int)propertyInfo.Usage)
+                .Append(", exported: ")
+                .Append(propertyInfo.Exported ? "true" : "false")
+                .Append(")");
+        }
+
+        private static MethodInfo DetermineMethodInfo(GodotSignalDelegateData signalDelegateData)
+        {
+            var invokeMethodData = signalDelegateData.InvokeMethodData;
+
+            PropertyInfo returnVal;
+
+            if (invokeMethodData.RetType != null)
+            {
+                returnVal = DeterminePropertyInfo(invokeMethodData.RetType.Value, name: string.Empty);
+            }
+            else
+            {
+                returnVal = new PropertyInfo(VariantType.Nil, string.Empty, PropertyHint.None,
+                    hintString: null, PropertyUsageFlags.Default, exported: false);
+            }
+
+            int paramCount = invokeMethodData.ParamTypes.Length;
+
+            List<PropertyInfo>? arguments;
+
+            if (paramCount > 0)
+            {
+                arguments = new(capacity: paramCount);
+
+                for (int i = 0; i < paramCount; i++)
+                {
+                    arguments.Add(DeterminePropertyInfo(invokeMethodData.ParamTypes[i],
+                        name: invokeMethodData.Method.Parameters[i].Name));
+                }
+            }
+            else
+            {
+                arguments = null;
+            }
+
+            return new MethodInfo(signalDelegateData.Name, returnVal, MethodFlags.Default, arguments,
+                defaultArguments: null);
+        }
+
+        private static PropertyInfo DeterminePropertyInfo(MarshalType marshalType, string name)
+        {
+            var memberVariantType = MarshalUtils.ConvertMarshalTypeToVariantType(marshalType)!.Value;
+
+            var propUsage = PropertyUsageFlags.Default;
+
+            if (memberVariantType == VariantType.Nil)
+                propUsage |= PropertyUsageFlags.NilIsVariant;
+
+            return new PropertyInfo(memberVariantType, name,
+                PropertyHint.None, string.Empty, propUsage, exported: false);
+        }
+    }
+}

+ 2 - 1
modules/mono/editor/GodotTools/GodotTools/Build/BuildOutputView.cs

@@ -22,7 +22,8 @@ namespace GodotTools.Build
             public string ProjectFile { get; set; }
             public string ProjectFile { get; set; }
         }
         }
 
 
-        [Signal] public event Action BuildStateChanged;
+        [Signal]
+        public delegate void BuildStateChangedEventHandler();
 
 
         public bool HasBuildExited { get; private set; } = false;
         public bool HasBuildExited { get; private set; } = false;
 
 

+ 5 - 1
modules/mono/glue/GodotSharp/GodotSharp/Core/Array.cs

@@ -98,7 +98,11 @@ namespace Godot.Collections
         {
         {
             // Always dispose `NativeValue` even if disposing is true
             // Always dispose `NativeValue` even if disposing is true
             NativeValue.DangerousSelfRef.Dispose();
             NativeValue.DangerousSelfRef.Dispose();
-            DisposablesTracker.UnregisterDisposable(_weakReferenceToSelf);
+
+            if (_weakReferenceToSelf != null)
+            {
+                DisposablesTracker.UnregisterDisposable(_weakReferenceToSelf);
+            }
         }
         }
 
 
         /// <summary>
         /// <summary>

+ 25 - 13
modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/GodotSerializationInfo.cs

@@ -46,22 +46,34 @@ public class GodotSerializationInfo
         }
         }
     }
     }
 
 
-    public Delegate GetSignalEventDelegate(StringName name)
+    public bool TryGetSignalEventDelegate<T>(StringName name, [MaybeNullWhen(false)] out T value)
+        where T : Delegate
     {
     {
-        if (DelegateUtils.TryDeserializeDelegate(_signalEvents[name], out var eventDelegate))
+        if (_signalEvents.TryGetValue(name, out Collections.Array serializedData))
         {
         {
-            return eventDelegate;
-        }
-        else if (OS.IsStdoutVerbose())
-        {
-            Console.WriteLine($"Failed to deserialize event signal delegate: {name}");
-        }
+            if (DelegateUtils.TryDeserializeDelegate(serializedData, out var eventDelegate))
+            {
+                value = eventDelegate as T;
 
 
-        return null;
-    }
+                if (value == null)
+                {
+                    Console.WriteLine($"Cannot cast the deserialized event signal delegate: {name}. " +
+                                      $"Expected '{typeof(T).FullName}'; got '{eventDelegate.GetType().FullName}'.");
+                    return false;
+                }
 
 
-    public IEnumerable<StringName> GetSignalEventsList()
-    {
-        return _signalEvents.Keys;
+                return true;
+            }
+            else if (OS.IsStdoutVerbose())
+            {
+                Console.WriteLine($"Failed to deserialize event signal delegate: {name}");
+            }
+
+            value = null;
+            return false;
+        }
+
+        value = null;
+        return false;
     }
     }
 }
 }

+ 1 - 5
modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/ManagedCallbacks.cs

@@ -19,14 +19,12 @@ namespace Godot.Bridge
         public delegate* unmanaged<IntPtr, godot_string_name*, void> ScriptManagerBridge_GetScriptNativeName;
         public delegate* unmanaged<IntPtr, godot_string_name*, void> ScriptManagerBridge_GetScriptNativeName;
         public delegate* unmanaged<IntPtr, IntPtr, void> ScriptManagerBridge_SetGodotObjectPtr;
         public delegate* unmanaged<IntPtr, IntPtr, void> ScriptManagerBridge_SetGodotObjectPtr;
         public delegate* unmanaged<IntPtr, godot_string_name*, godot_variant**, int, godot_bool*, void> ScriptManagerBridge_RaiseEventSignal;
         public delegate* unmanaged<IntPtr, godot_string_name*, godot_variant**, int, godot_bool*, void> ScriptManagerBridge_RaiseEventSignal;
-        public delegate* unmanaged<IntPtr, godot_dictionary*, void> ScriptManagerBridge_GetScriptSignalList;
-        public delegate* unmanaged<IntPtr, godot_string*, godot_bool> ScriptManagerBridge_HasScriptSignal;
         public delegate* unmanaged<IntPtr, IntPtr, godot_bool> ScriptManagerBridge_ScriptIsOrInherits;
         public delegate* unmanaged<IntPtr, IntPtr, godot_bool> ScriptManagerBridge_ScriptIsOrInherits;
         public delegate* unmanaged<IntPtr, godot_string*, godot_bool> ScriptManagerBridge_AddScriptBridge;
         public delegate* unmanaged<IntPtr, godot_string*, godot_bool> ScriptManagerBridge_AddScriptBridge;
         public delegate* unmanaged<godot_string*, godot_ref*, void> ScriptManagerBridge_GetOrCreateScriptBridgeForPath;
         public delegate* unmanaged<godot_string*, godot_ref*, void> ScriptManagerBridge_GetOrCreateScriptBridgeForPath;
         public delegate* unmanaged<IntPtr, void> ScriptManagerBridge_RemoveScriptBridge;
         public delegate* unmanaged<IntPtr, void> ScriptManagerBridge_RemoveScriptBridge;
         public delegate* unmanaged<IntPtr, godot_bool> ScriptManagerBridge_TryReloadRegisteredScriptWithClass;
         public delegate* unmanaged<IntPtr, godot_bool> ScriptManagerBridge_TryReloadRegisteredScriptWithClass;
-        public delegate* unmanaged<IntPtr, godot_bool*, godot_dictionary*, godot_ref*, void> ScriptManagerBridge_UpdateScriptClassInfo;
+        public delegate* unmanaged<IntPtr, godot_bool*, godot_dictionary*, godot_dictionary*, godot_ref*, void> ScriptManagerBridge_UpdateScriptClassInfo;
         public delegate* unmanaged<IntPtr, IntPtr*, godot_bool, godot_bool> ScriptManagerBridge_SwapGCHandleForType;
         public delegate* unmanaged<IntPtr, IntPtr*, godot_bool, godot_bool> ScriptManagerBridge_SwapGCHandleForType;
         public delegate* unmanaged<IntPtr, delegate* unmanaged<IntPtr, godot_string*, void*, int, void>, void> ScriptManagerBridge_GetPropertyInfoList;
         public delegate* unmanaged<IntPtr, delegate* unmanaged<IntPtr, godot_string*, void*, int, void>, void> ScriptManagerBridge_GetPropertyInfoList;
         public delegate* unmanaged<IntPtr, delegate* unmanaged<IntPtr, void*, int, void>, void> ScriptManagerBridge_GetPropertyDefaultValues;
         public delegate* unmanaged<IntPtr, delegate* unmanaged<IntPtr, void*, int, void>, void> ScriptManagerBridge_GetPropertyDefaultValues;
@@ -60,8 +58,6 @@ namespace Godot.Bridge
                 ScriptManagerBridge_GetScriptNativeName = &ScriptManagerBridge.GetScriptNativeName,
                 ScriptManagerBridge_GetScriptNativeName = &ScriptManagerBridge.GetScriptNativeName,
                 ScriptManagerBridge_SetGodotObjectPtr = &ScriptManagerBridge.SetGodotObjectPtr,
                 ScriptManagerBridge_SetGodotObjectPtr = &ScriptManagerBridge.SetGodotObjectPtr,
                 ScriptManagerBridge_RaiseEventSignal = &ScriptManagerBridge.RaiseEventSignal,
                 ScriptManagerBridge_RaiseEventSignal = &ScriptManagerBridge.RaiseEventSignal,
-                ScriptManagerBridge_GetScriptSignalList = &ScriptManagerBridge.GetScriptSignalList,
-                ScriptManagerBridge_HasScriptSignal = &ScriptManagerBridge.HasScriptSignal,
                 ScriptManagerBridge_ScriptIsOrInherits = &ScriptManagerBridge.ScriptIsOrInherits,
                 ScriptManagerBridge_ScriptIsOrInherits = &ScriptManagerBridge.ScriptIsOrInherits,
                 ScriptManagerBridge_AddScriptBridge = &ScriptManagerBridge.AddScriptBridge,
                 ScriptManagerBridge_AddScriptBridge = &ScriptManagerBridge.AddScriptBridge,
                 ScriptManagerBridge_GetOrCreateScriptBridgeForPath = &ScriptManagerBridge.GetOrCreateScriptBridgeForPath,
                 ScriptManagerBridge_GetOrCreateScriptBridgeForPath = &ScriptManagerBridge.GetOrCreateScriptBridgeForPath,

+ 25 - 0
modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/MethodInfo.cs

@@ -0,0 +1,25 @@
+using System.Collections.Generic;
+
+namespace Godot.Bridge;
+
+#nullable enable
+
+public struct MethodInfo
+{
+    public StringName Name { get; init; }
+    public PropertyInfo ReturnVal { get; init; }
+    public MethodFlags Flags { get; init; }
+    public int Id { get; init; } = 0;
+    public List<PropertyInfo>? Arguments { get; init; }
+    public List<object>? DefaultArguments { get; init; }
+
+    public MethodInfo(StringName name, PropertyInfo returnVal, MethodFlags flags, List<PropertyInfo>? arguments,
+        List<object>? defaultArguments)
+    {
+        Name = name;
+        ReturnVal = returnVal;
+        Flags = flags;
+        Arguments = arguments;
+        DefaultArguments = defaultArguments;
+    }
+}

+ 19 - 21
modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/PropertyInfo.cs

@@ -1,26 +1,24 @@
-using System;
-using Godot.NativeInterop;
+namespace Godot.Bridge;
 
 
-namespace Godot.Bridge
+#nullable enable
+
+public struct PropertyInfo
 {
 {
-    public struct PropertyInfo
-    {
-        public Variant.Type Type { get; init; }
-        public StringName Name { get; init; }
-        public PropertyHint Hint { get; init; }
-        public string HintString { get; init; }
-        public PropertyUsageFlags Usage { get; init; }
-        public bool Exported { get; init; }
+    public Variant.Type Type { get; init; }
+    public StringName Name { get; init; }
+    public PropertyHint Hint { get; init; }
+    public string HintString { get; init; }
+    public PropertyUsageFlags Usage { get; init; }
+    public bool Exported { get; init; }
 
 
-        public PropertyInfo(Variant.Type type, StringName name, PropertyHint hint, string hintString,
-            PropertyUsageFlags usage, bool exported)
-        {
-            Type = type;
-            Name = name;
-            Hint = hint;
-            HintString = hintString;
-            Usage = usage;
-            Exported = exported;
-        }
+    public PropertyInfo(Variant.Type type, StringName name, PropertyHint hint, string hintString,
+        PropertyUsageFlags usage, bool exported)
+    {
+        Type = type;
+        Name = name;
+        Hint = hint;
+        HintString = hintString;
+        Usage = usage;
+        Exported = exported;
     }
     }
 }
 }

+ 103 - 186
modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/ScriptManagerBridge.cs

@@ -2,6 +2,7 @@
 
 
 using System;
 using System;
 using System.Collections.Concurrent;
 using System.Collections.Concurrent;
+using System.Collections.Generic;
 using System.Diagnostics.CodeAnalysis;
 using System.Diagnostics.CodeAnalysis;
 using System.Linq;
 using System.Linq;
 using System.Reflection;
 using System.Reflection;
@@ -9,7 +10,6 @@ using System.Runtime.CompilerServices;
 using System.Runtime.InteropServices;
 using System.Runtime.InteropServices;
 using System.Runtime.Loader;
 using System.Runtime.Loader;
 using System.Runtime.Serialization;
 using System.Runtime.Serialization;
-using Godot.Collections;
 using Godot.NativeInterop;
 using Godot.NativeInterop;
 
 
 namespace Godot.Bridge
 namespace Godot.Bridge
@@ -337,7 +337,7 @@ namespace Godot.Bridge
 
 
                 *outOwnerIsNull = godot_bool.False;
                 *outOwnerIsNull = godot_bool.False;
 
 
-                owner.InternalRaiseEventSignal(CustomUnsafe.AsRef(eventSignalName),
+                owner.RaiseGodotClassSignalCallbacks(CustomUnsafe.AsRef(eventSignalName),
                     new NativeVariantPtrArgs(args), argCount);
                     new NativeVariantPtrArgs(args), argCount);
             }
             }
             catch (Exception e)
             catch (Exception e)
@@ -347,151 +347,6 @@ namespace Godot.Bridge
             }
             }
         }
         }
 
 
-        [UnmanagedCallersOnly]
-        internal static unsafe void GetScriptSignalList(IntPtr scriptPtr, godot_dictionary* outRetSignals)
-        {
-            try
-            {
-                // Performance is not critical here as this will be replaced with source generators.
-                using var signals = new Dictionary();
-
-                Type? top = _scriptTypeBiMap.GetScriptType(scriptPtr);
-                Type native = Object.InternalGetClassNativeBase(top);
-
-                while (top != null && top != native)
-                {
-                    // Legacy signals
-
-                    foreach (var signalDelegate in top
-                                 .GetNestedTypes(BindingFlags.DeclaredOnly | BindingFlags.NonPublic |
-                                                 BindingFlags.Public)
-                                 .Where(nestedType => typeof(Delegate).IsAssignableFrom(nestedType))
-                                 .Where(@delegate => @delegate.GetCustomAttributes().OfType<SignalAttribute>().Any()))
-                    {
-                        var invokeMethod = signalDelegate.GetMethod("Invoke");
-
-                        if (invokeMethod == null)
-                            throw new MissingMethodException(signalDelegate.FullName, "Invoke");
-
-                        var signalParams = new Collections.Array();
-
-                        foreach (var parameters in invokeMethod.GetParameters())
-                        {
-                            var paramType = Marshaling.ConvertManagedTypeToVariantType(
-                                parameters.ParameterType, out bool nilIsVariant);
-                            signalParams.Add(new Dictionary()
-                            {
-                                { "name", parameters.Name },
-                                { "type", paramType },
-                                { "nil_is_variant", nilIsVariant }
-                            });
-                        }
-
-                        signals.Add(signalDelegate.Name, signalParams);
-                    }
-
-                    // Event signals
-
-                    var foundEventSignals = top.GetEvents(
-                            BindingFlags.DeclaredOnly | BindingFlags.Instance |
-                            BindingFlags.NonPublic | BindingFlags.Public)
-                        .Where(ev => ev.GetCustomAttributes().OfType<SignalAttribute>().Any())
-                        .Select(ev => ev.Name);
-
-                    var fields = top.GetFields(
-                        BindingFlags.DeclaredOnly | BindingFlags.Instance |
-                        BindingFlags.NonPublic | BindingFlags.Public);
-
-                    foreach (var eventSignalField in fields
-                                 .Where(f => typeof(Delegate).IsAssignableFrom(f.FieldType))
-                                 .Where(f => foundEventSignals.Contains(f.Name)))
-                    {
-                        var delegateType = eventSignalField.FieldType;
-                        var invokeMethod = delegateType.GetMethod("Invoke");
-
-                        if (invokeMethod == null)
-                            throw new MissingMethodException(delegateType.FullName, "Invoke");
-
-                        var signalParams = new Collections.Array();
-
-                        foreach (var parameters in invokeMethod.GetParameters())
-                        {
-                            var paramType = Marshaling.ConvertManagedTypeToVariantType(
-                                parameters.ParameterType, out bool nilIsVariant);
-                            signalParams.Add(new Dictionary()
-                            {
-                                { "name", parameters.Name },
-                                { "type", paramType },
-                                { "nil_is_variant", nilIsVariant }
-                            });
-                        }
-
-                        signals.Add(eventSignalField.Name, signalParams);
-                    }
-
-                    top = top.BaseType;
-                }
-
-                *outRetSignals = NativeFuncs.godotsharp_dictionary_new_copy((godot_dictionary)signals.NativeValue);
-            }
-            catch (Exception e)
-            {
-                ExceptionUtils.LogException(e);
-                *outRetSignals = NativeFuncs.godotsharp_dictionary_new();
-            }
-        }
-
-        [UnmanagedCallersOnly]
-        internal static unsafe godot_bool HasScriptSignal(IntPtr scriptPtr, godot_string* signalName)
-        {
-            try
-            {
-                // Performance is not critical here as this will be replaced with source generators.
-                using var signals = new Dictionary();
-
-                string signalNameStr = Marshaling.ConvertStringToManaged(*signalName);
-
-                Type? top = _scriptTypeBiMap.GetScriptType(scriptPtr);
-                Type native = Object.InternalGetClassNativeBase(top);
-
-                while (top != null && top != native)
-                {
-                    // Legacy signals
-
-                    if (top
-                        .GetNestedTypes(BindingFlags.DeclaredOnly | BindingFlags.NonPublic | BindingFlags.Public)
-                        .Where(nestedType => typeof(Delegate).IsAssignableFrom(nestedType))
-                        .Where(@delegate => @delegate.GetCustomAttributes().OfType<SignalAttribute>().Any())
-                        .Any(signalDelegate => signalDelegate.Name == signalNameStr)
-                       )
-                    {
-                        return godot_bool.True;
-                    }
-
-                    // Event signals
-
-                    if (top.GetEvents(
-                            BindingFlags.DeclaredOnly | BindingFlags.Instance |
-                            BindingFlags.NonPublic | BindingFlags.Public)
-                        .Where(ev => ev.GetCustomAttributes().OfType<SignalAttribute>().Any())
-                        .Any(eventSignal => eventSignal.Name == signalNameStr)
-                       )
-                    {
-                        return godot_bool.True;
-                    }
-
-                    top = top.BaseType;
-                }
-
-                return godot_bool.False;
-            }
-            catch (Exception e)
-            {
-                ExceptionUtils.LogException(e);
-                return godot_bool.False;
-            }
-        }
-
         [UnmanagedCallersOnly]
         [UnmanagedCallersOnly]
         internal static godot_bool ScriptIsOrInherits(IntPtr scriptPtr, IntPtr scriptPtrMaybeBase)
         internal static godot_bool ScriptIsOrInherits(IntPtr scriptPtr, IntPtr scriptPtrMaybeBase)
         {
         {
@@ -702,7 +557,7 @@ namespace Godot.Bridge
 
 
         [UnmanagedCallersOnly]
         [UnmanagedCallersOnly]
         internal static unsafe void UpdateScriptClassInfo(IntPtr scriptPtr, godot_bool* outTool,
         internal static unsafe void UpdateScriptClassInfo(IntPtr scriptPtr, godot_bool* outTool,
-            godot_dictionary* outRpcFunctionsDest, godot_ref* outBaseScript)
+            godot_dictionary* outRpcFunctionsDest, godot_dictionary* outEventSignalsDest, godot_ref* outBaseScript)
         {
         {
             try
             try
             {
             {
@@ -725,7 +580,7 @@ namespace Godot.Bridge
 
 
                 // RPC functions
                 // RPC functions
 
 
-                Dictionary<string, Dictionary> rpcFunctions = new();
+                Collections.Dictionary<string, Collections.Dictionary> rpcFunctions = new();
 
 
                 Type? top = scriptType;
                 Type? top = scriptType;
                 Type native = Object.InternalGetClassNativeBase(top);
                 Type native = Object.InternalGetClassNativeBase(top);
@@ -749,7 +604,7 @@ namespace Godot.Bridge
                         if (rpcAttr == null)
                         if (rpcAttr == null)
                             continue;
                             continue;
 
 
-                        var rpcConfig = new Dictionary();
+                        var rpcConfig = new Collections.Dictionary();
 
 
                         rpcConfig["rpc_mode"] = (long)rpcAttr.Mode;
                         rpcConfig["rpc_mode"] = (long)rpcAttr.Mode;
                         rpcConfig["call_local"] = rpcAttr.CallLocal;
                         rpcConfig["call_local"] = rpcAttr.CallLocal;
@@ -764,7 +619,54 @@ namespace Godot.Bridge
 
 
                 *outRpcFunctionsDest =
                 *outRpcFunctionsDest =
                     NativeFuncs.godotsharp_dictionary_new_copy(
                     NativeFuncs.godotsharp_dictionary_new_copy(
-                        (godot_dictionary)((Dictionary)rpcFunctions).NativeValue);
+                        (godot_dictionary)((Collections.Dictionary)rpcFunctions).NativeValue);
+
+                // Event signals
+
+                // Performance is not critical here as this will be replaced with source generators.
+                using var signals = new Collections.Dictionary();
+
+                top = scriptType;
+
+                while (top != null && top != native)
+                {
+                    var signalList = GetSignalListForType(top);
+
+                    if (signalList != null)
+                    {
+                        foreach (var signal in signalList)
+                        {
+                            string signalName = signal.Name;
+
+                            if (signals.ContainsKey(signalName))
+                                continue;
+
+                            var signalParams = new Collections.Array();
+
+                            if (signal.Arguments != null)
+                            {
+                                foreach (var param in signal.Arguments)
+                                {
+                                    signalParams.Add(new Collections.Dictionary()
+                                    {
+                                        { "name", param.Name },
+                                        { "type", param.Type },
+                                        { "usage", param.Usage }
+                                    });
+                                }
+                            }
+
+                            signals.Add(signalName, signalParams);
+                        }
+                    }
+
+                    top = top.BaseType;
+                }
+
+                *outEventSignalsDest =
+                    NativeFuncs.godotsharp_dictionary_new_copy((godot_dictionary)signals.NativeValue);
+
+                // Base script
 
 
                 var baseType = scriptType.BaseType;
                 var baseType = scriptType.BaseType;
                 if (baseType != null && baseType != native)
                 if (baseType != null && baseType != native)
@@ -781,41 +683,22 @@ namespace Godot.Bridge
                 ExceptionUtils.LogException(e);
                 ExceptionUtils.LogException(e);
                 *outTool = godot_bool.False;
                 *outTool = godot_bool.False;
                 *outRpcFunctionsDest = NativeFuncs.godotsharp_dictionary_new();
                 *outRpcFunctionsDest = NativeFuncs.godotsharp_dictionary_new();
+                *outEventSignalsDest = NativeFuncs.godotsharp_dictionary_new();
+                *outBaseScript = default;
             }
             }
         }
         }
 
 
-        [UnmanagedCallersOnly]
-        internal static unsafe godot_bool SwapGCHandleForType(IntPtr oldGCHandlePtr, IntPtr* outNewGCHandlePtr,
-            godot_bool createWeak)
+        private static List<MethodInfo>? GetSignalListForType(Type type)
         {
         {
-            try
-            {
-                var oldGCHandle = GCHandle.FromIntPtr(oldGCHandlePtr);
-
-                object? target = oldGCHandle.Target;
-
-                if (target == null)
-                {
-                    CustomGCHandle.Free(oldGCHandle);
-                    *outNewGCHandlePtr = IntPtr.Zero;
-                    return godot_bool.False; // Called after the managed side was collected, so nothing to do here
-                }
+            var getGodotSignalListMethod = type.GetMethod(
+                "GetGodotSignalList",
+                BindingFlags.DeclaredOnly | BindingFlags.Static |
+                BindingFlags.NonPublic | BindingFlags.Public);
 
 
-                // Release the current weak handle and replace it with a strong handle.
-                var newGCHandle = createWeak.ToBool() ?
-                    CustomGCHandle.AllocWeak(target) :
-                    CustomGCHandle.AllocStrong(target);
+            if (getGodotSignalListMethod == null)
+                return null;
 
 
-                CustomGCHandle.Free(oldGCHandle);
-                *outNewGCHandlePtr = GCHandle.ToIntPtr(newGCHandle);
-                return godot_bool.True;
-            }
-            catch (Exception e)
-            {
-                ExceptionUtils.LogException(e);
-                *outNewGCHandlePtr = IntPtr.Zero;
-                return godot_bool.False;
-            }
+            return (List<MethodInfo>?)getGodotSignalListMethod.Invoke(null, null);
         }
         }
 
 
         // ReSharper disable once InconsistentNaming
         // ReSharper disable once InconsistentNaming
@@ -857,16 +740,16 @@ namespace Godot.Bridge
         {
         {
             try
             try
             {
             {
-                var getGodotPropertiesMetadataMethod = type.GetMethod(
-                    "GetGodotPropertiesMetadata",
+                var getGodotPropertyListMethod = type.GetMethod(
+                    "GetGodotPropertyList",
                     BindingFlags.DeclaredOnly | BindingFlags.Static |
                     BindingFlags.DeclaredOnly | BindingFlags.Static |
                     BindingFlags.NonPublic | BindingFlags.Public);
                     BindingFlags.NonPublic | BindingFlags.Public);
 
 
-                if (getGodotPropertiesMetadataMethod == null)
+                if (getGodotPropertyListMethod == null)
                     return;
                     return;
 
 
-                var properties = (System.Collections.Generic.List<PropertyInfo>?)
-                    getGodotPropertiesMetadataMethod.Invoke(null, null);
+                var properties = (List<PropertyInfo>?)
+                    getGodotPropertyListMethod.Invoke(null, null);
 
 
                 if (properties == null || properties.Count <= 0)
                 if (properties == null || properties.Count <= 0)
                     return;
                     return;
@@ -985,7 +868,7 @@ namespace Godot.Bridge
                 if (getGodotPropertyDefaultValuesMethod == null)
                 if (getGodotPropertyDefaultValuesMethod == null)
                     return;
                     return;
 
 
-                var defaultValues = (System.Collections.Generic.Dictionary<StringName, object>?)
+                var defaultValues = (Dictionary<StringName, object>?)
                     getGodotPropertyDefaultValuesMethod.Invoke(null, null);
                     getGodotPropertyDefaultValuesMethod.Invoke(null, null);
 
 
                 if (defaultValues == null || defaultValues.Count <= 0)
                 if (defaultValues == null || defaultValues.Count <= 0)
@@ -1048,5 +931,39 @@ namespace Godot.Bridge
                 ExceptionUtils.LogException(e);
                 ExceptionUtils.LogException(e);
             }
             }
         }
         }
+
+        [UnmanagedCallersOnly]
+        internal static unsafe godot_bool SwapGCHandleForType(IntPtr oldGCHandlePtr, IntPtr* outNewGCHandlePtr,
+            godot_bool createWeak)
+        {
+            try
+            {
+                var oldGCHandle = GCHandle.FromIntPtr(oldGCHandlePtr);
+
+                object? target = oldGCHandle.Target;
+
+                if (target == null)
+                {
+                    CustomGCHandle.Free(oldGCHandle);
+                    *outNewGCHandlePtr = IntPtr.Zero;
+                    return godot_bool.False; // Called after the managed side was collected, so nothing to do here
+                }
+
+                // Release the current weak handle and replace it with a strong handle.
+                var newGCHandle = createWeak.ToBool() ?
+                    CustomGCHandle.AllocWeak(target) :
+                    CustomGCHandle.AllocStrong(target);
+
+                CustomGCHandle.Free(oldGCHandle);
+                *outNewGCHandlePtr = GCHandle.ToIntPtr(newGCHandle);
+                return godot_bool.True;
+            }
+            catch (Exception e)
+            {
+                ExceptionUtils.LogException(e);
+                *outNewGCHandlePtr = IntPtr.Zero;
+                return godot_bool.False;
+            }
+        }
     }
     }
 }
 }

+ 6 - 2
modules/mono/glue/GodotSharp/GodotSharp/Core/Dictionary.cs

@@ -76,7 +76,11 @@ namespace Godot.Collections
         {
         {
             // Always dispose `NativeValue` even if disposing is true
             // Always dispose `NativeValue` even if disposing is true
             NativeValue.DangerousSelfRef.Dispose();
             NativeValue.DangerousSelfRef.Dispose();
-            DisposablesTracker.UnregisterDisposable(_weakReferenceToSelf);
+
+            if (_weakReferenceToSelf != null)
+            {
+                DisposablesTracker.UnregisterDisposable(_weakReferenceToSelf);
+            }
         }
         }
 
 
         /// <summary>
         /// <summary>
@@ -704,7 +708,7 @@ namespace Godot.Collections
             return found;
             return found;
         }
         }
 
 
-        // TODO: This is temporary. It's needed for the serialization generator. It won't be needed once we replace Sysme.Object with a Variant type.
+        // TODO: This is temporary. It's needed for the serialization generator. It won't be needed once we replace System.Object with a Variant type.
         internal bool TryGetValueAsType<TValueCustom>(TKey key, [MaybeNullWhen(false)] out TValueCustom value)
         internal bool TryGetValueAsType<TValueCustom>(TKey key, [MaybeNullWhen(false)] out TValueCustom value)
         {
         {
             using godot_variant variantKey = Marshaling.ConvertManagedObjectToVariant(key);
             using godot_variant variantKey = Marshaling.ConvertManagedObjectToVariant(key);

+ 0 - 4
modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/NativeFuncs.cs

@@ -62,10 +62,6 @@ namespace Godot.NativeInterop
         internal static extern void godotsharp_internal_refcounted_disposed(IntPtr ptr, IntPtr gcHandleToFree,
         internal static extern void godotsharp_internal_refcounted_disposed(IntPtr ptr, IntPtr gcHandleToFree,
             godot_bool isFinalizer);
             godot_bool isFinalizer);
 
 
-        [DllImport(GodotDllName)]
-        internal static extern void godotsharp_internal_object_connect_event_signal(IntPtr obj,
-            in godot_string_name eventSignal);
-
         [DllImport(GodotDllName)]
         [DllImport(GodotDllName)]
         internal static extern Error godotsharp_internal_signal_awaiter_connect(IntPtr source,
         internal static extern Error godotsharp_internal_signal_awaiter_connect(IntPtr source,
             in godot_string_name signal,
             in godot_string_name signal,

+ 5 - 1
modules/mono/glue/GodotSharp/GodotSharp/Core/NodePath.cs

@@ -63,7 +63,11 @@ namespace Godot
         {
         {
             // Always dispose `NativeValue` even if disposing is true
             // Always dispose `NativeValue` even if disposing is true
             NativeValue.DangerousSelfRef.Dispose();
             NativeValue.DangerousSelfRef.Dispose();
-            DisposablesTracker.UnregisterDisposable(_weakReferenceToSelf);
+
+            if (_weakReferenceToSelf != null)
+            {
+                DisposablesTracker.UnregisterDisposable(_weakReferenceToSelf);
+            }
         }
         }
 
 
         private NodePath(godot_node_path nativeValueToOwn)
         private NodePath(godot_node_path nativeValueToOwn)

+ 3 - 152
modules/mono/glue/GodotSharp/GodotSharp/Core/Object.base.cs

@@ -49,30 +49,6 @@ namespace Godot
             }
             }
 
 
             _weakReferenceToSelf = DisposablesTracker.RegisterGodotObject(this);
             _weakReferenceToSelf = DisposablesTracker.RegisterGodotObject(this);
-
-            _InitializeGodotScriptInstanceInternals();
-        }
-
-        internal void _InitializeGodotScriptInstanceInternals()
-        {
-            // Performance is not critical here as this will be replaced with source generators.
-            Type top = GetType();
-            Type native = InternalGetClassNativeBase(top);
-
-            while (top != null && top != native)
-            {
-                foreach (var eventSignal in top.GetEvents(
-                                 BindingFlags.DeclaredOnly | BindingFlags.Instance |
-                                 BindingFlags.NonPublic | BindingFlags.Public)
-                             .Where(ev => ev.GetCustomAttributes().OfType<SignalAttribute>().Any()))
-                {
-                    using var eventSignalName = new StringName(eventSignal.Name);
-                    var eventSignalNameSelf = (godot_string_name)eventSignalName.NativeValue;
-                    NativeFuncs.godotsharp_internal_object_connect_event_signal(NativePtr, eventSignalNameSelf);
-                }
-
-                top = top.BaseType;
-            }
         }
         }
 
 
         internal Object(bool memoryOwn)
         internal Object(bool memoryOwn)
@@ -238,72 +214,10 @@ namespace Godot
             return false;
             return false;
         }
         }
 
 
-        internal void InternalRaiseEventSignal(in godot_string_name eventSignalName, NativeVariantPtrArgs args,
-            int argc)
+        // ReSharper disable once VirtualMemberNeverOverridden.Global
+        protected internal virtual void RaiseGodotClassSignalCallbacks(in godot_string_name signal,
+            NativeVariantPtrArgs args, int argCount)
         {
         {
-            // Performance is not critical here as this will be replaced with source generators.
-
-            using var stringName = StringName.CreateTakingOwnershipOfDisposableValue(
-                NativeFuncs.godotsharp_string_name_new_copy(eventSignalName));
-            string eventSignalNameStr = stringName.ToString();
-
-            Type top = GetType();
-            Type native = InternalGetClassNativeBase(top);
-
-            while (top != null && top != native)
-            {
-                var foundEventSignals = top.GetEvents(
-                        BindingFlags.DeclaredOnly | BindingFlags.Instance |
-                        BindingFlags.NonPublic | BindingFlags.Public)
-                    .Where(ev => ev.GetCustomAttributes().OfType<SignalAttribute>().Any())
-                    .Select(ev => ev.Name);
-
-                var fields = top.GetFields(
-                    BindingFlags.DeclaredOnly | BindingFlags.Instance |
-                    BindingFlags.NonPublic | BindingFlags.Public);
-
-                var eventSignalField = fields
-                    .Where(f => typeof(Delegate).IsAssignableFrom(f.FieldType))
-                    .Where(f => foundEventSignals.Contains(f.Name))
-                    .FirstOrDefault(f => f.Name == eventSignalNameStr);
-
-                if (eventSignalField != null)
-                {
-                    var @delegate = (Delegate)eventSignalField.GetValue(this);
-
-                    if (@delegate == null)
-                        continue;
-
-                    var delegateType = eventSignalField.FieldType;
-
-                    var invokeMethod = delegateType.GetMethod("Invoke");
-
-                    if (invokeMethod == null)
-                        throw new MissingMethodException(delegateType.FullName, "Invoke");
-
-                    var parameterInfos = invokeMethod.GetParameters();
-                    var paramsLength = parameterInfos.Length;
-
-                    if (argc != paramsLength)
-                    {
-                        throw new InvalidOperationException(
-                            $"The event delegate expects {paramsLength} arguments, but received {argc}.");
-                    }
-
-                    var managedArgs = new object[argc];
-
-                    for (int i = 0; i < argc; i++)
-                    {
-                        managedArgs[i] = Marshaling.ConvertVariantToManagedObjectOfType(
-                            args[i], parameterInfos[i].ParameterType);
-                    }
-
-                    invokeMethod.Invoke(@delegate, managedArgs);
-                    return;
-                }
-
-                top = top.BaseType;
-            }
         }
         }
 
 
         internal static IntPtr ClassDB_get_method(StringName type, StringName method)
         internal static IntPtr ClassDB_get_method(StringName type, StringName method)
@@ -332,74 +246,11 @@ namespace Godot
 
 
         protected internal virtual void SaveGodotObjectData(GodotSerializationInfo info)
         protected internal virtual void SaveGodotObjectData(GodotSerializationInfo info)
         {
         {
-            // Temporary solution via reflection until we add a signals events source generator
-
-            Type top = GetType();
-            Type native = InternalGetClassNativeBase(top);
-
-            while (top != null && top != native)
-            {
-                var foundEventSignals = top.GetEvents(
-                        BindingFlags.DeclaredOnly | BindingFlags.Instance |
-                        BindingFlags.NonPublic | BindingFlags.Public)
-                    .Where(ev => ev.GetCustomAttributes().OfType<SignalAttribute>().Any())
-                    .Select(ev => ev.Name);
-
-                var fields = top.GetFields(
-                    BindingFlags.DeclaredOnly | BindingFlags.Instance |
-                    BindingFlags.NonPublic | BindingFlags.Public);
-
-                foreach (var eventSignalField in fields
-                             .Where(f => typeof(Delegate).IsAssignableFrom(f.FieldType))
-                             .Where(f => foundEventSignals.Contains(f.Name)))
-                {
-                    var eventSignalDelegate = (Delegate)eventSignalField.GetValue(this);
-                    info.AddSignalEventDelegate(eventSignalField.Name, eventSignalDelegate);
-                }
-
-                top = top.BaseType;
-            }
         }
         }
 
 
         // TODO: Should this be a constructor overload?
         // TODO: Should this be a constructor overload?
         protected internal virtual void RestoreGodotObjectData(GodotSerializationInfo info)
         protected internal virtual void RestoreGodotObjectData(GodotSerializationInfo info)
         {
         {
-            // Temporary solution via reflection until we add a signals events source generator
-
-            void RestoreSignalEvent(StringName signalEventName)
-            {
-                Type top = GetType();
-                Type native = InternalGetClassNativeBase(top);
-
-                while (top != null && top != native)
-                {
-                    var foundEventSignal = top.GetEvent(signalEventName,
-                        BindingFlags.DeclaredOnly | BindingFlags.Instance |
-                        BindingFlags.NonPublic | BindingFlags.Public);
-
-                    if (foundEventSignal != null &&
-                        foundEventSignal.GetCustomAttributes().OfType<SignalAttribute>().Any())
-                    {
-                        var field = top.GetField(foundEventSignal.Name,
-                            BindingFlags.DeclaredOnly | BindingFlags.Instance |
-                            BindingFlags.NonPublic | BindingFlags.Public);
-
-                        if (field != null && typeof(Delegate).IsAssignableFrom(field.FieldType))
-                        {
-                            var eventSignalDelegate = info.GetSignalEventDelegate(signalEventName);
-                            field.SetValue(this, eventSignalDelegate);
-                            return;
-                        }
-                    }
-
-                    top = top.BaseType;
-                }
-            }
-
-            foreach (var signalEventName in info.GetSignalEventsList())
-            {
-                RestoreSignalEvent(signalEventName);
-            }
         }
         }
     }
     }
 }
 }

+ 5 - 1
modules/mono/glue/GodotSharp/GodotSharp/Core/StringName.cs

@@ -34,7 +34,11 @@ namespace Godot
         {
         {
             // Always dispose `NativeValue` even if disposing is true
             // Always dispose `NativeValue` even if disposing is true
             NativeValue.DangerousSelfRef.Dispose();
             NativeValue.DangerousSelfRef.Dispose();
-            DisposablesTracker.UnregisterDisposable(_weakReferenceToSelf);
+
+            if (_weakReferenceToSelf != null)
+            {
+                DisposablesTracker.UnregisterDisposable(_weakReferenceToSelf);
+            }
         }
         }
 
 
         private StringName(godot_string_name nativeValueToOwn)
         private StringName(godot_string_name nativeValueToOwn)

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

@@ -50,6 +50,7 @@
   <ItemGroup>
   <ItemGroup>
     <Compile Include="Core\AABB.cs" />
     <Compile Include="Core\AABB.cs" />
     <Compile Include="Core\Bridge\GodotSerializationInfo.cs" />
     <Compile Include="Core\Bridge\GodotSerializationInfo.cs" />
+    <Compile Include="Core\Bridge\MethodInfo.cs" />
     <Compile Include="Core\CustomGCHandle.cs" />
     <Compile Include="Core\CustomGCHandle.cs" />
     <Compile Include="Core\Array.cs" />
     <Compile Include="Core\Array.cs" />
     <Compile Include="Core\Attributes\AssemblyHasScriptsAttribute.cs" />
     <Compile Include="Core\Attributes\AssemblyHasScriptsAttribute.cs" />

+ 1 - 9
modules/mono/glue/runtime_interop.cpp

@@ -208,13 +208,6 @@ GD_PINVOKE_EXPORT void godotsharp_internal_refcounted_disposed(Object *p_ptr, GC
 	}
 	}
 }
 }
 
 
-GD_PINVOKE_EXPORT void godotsharp_internal_object_connect_event_signal(Object *p_ptr, const StringName *p_event_signal) {
-	CSharpInstance *csharp_instance = CAST_CSHARP_INSTANCE(p_ptr->get_script_instance());
-	if (csharp_instance) {
-		csharp_instance->connect_event_signal(*p_event_signal);
-	}
-}
-
 GD_PINVOKE_EXPORT int32_t godotsharp_internal_signal_awaiter_connect(Object *p_source, StringName *p_signal, Object *p_target, GCHandleIntPtr p_awaiter_handle_ptr) {
 GD_PINVOKE_EXPORT int32_t godotsharp_internal_signal_awaiter_connect(Object *p_source, StringName *p_signal, Object *p_target, GCHandleIntPtr p_awaiter_handle_ptr) {
 	StringName signal = p_signal ? *p_signal : StringName();
 	StringName signal = p_signal ? *p_signal : StringName();
 	return (int32_t)gd_mono_connect_signal_awaiter(p_source, signal, p_target, p_awaiter_handle_ptr);
 	return (int32_t)gd_mono_connect_signal_awaiter(p_source, signal, p_target, p_awaiter_handle_ptr);
@@ -1322,7 +1315,7 @@ GD_PINVOKE_EXPORT void godotsharp_object_to_string(Object *p_ptr, godot_string *
 #endif
 #endif
 
 
 // We need this to prevent the functions from being stripped.
 // We need this to prevent the functions from being stripped.
-void *godotsharp_pinvoke_funcs[186] = {
+void *godotsharp_pinvoke_funcs[185] = {
 	(void *)godotsharp_method_bind_get_method,
 	(void *)godotsharp_method_bind_get_method,
 	(void *)godotsharp_get_class_constructor,
 	(void *)godotsharp_get_class_constructor,
 	(void *)godotsharp_engine_get_singleton,
 	(void *)godotsharp_engine_get_singleton,
@@ -1333,7 +1326,6 @@ void *godotsharp_pinvoke_funcs[186] = {
 	(void *)godotsharp_internal_object_get_associated_gchandle,
 	(void *)godotsharp_internal_object_get_associated_gchandle,
 	(void *)godotsharp_internal_object_disposed,
 	(void *)godotsharp_internal_object_disposed,
 	(void *)godotsharp_internal_refcounted_disposed,
 	(void *)godotsharp_internal_refcounted_disposed,
-	(void *)godotsharp_internal_object_connect_event_signal,
 	(void *)godotsharp_internal_signal_awaiter_connect,
 	(void *)godotsharp_internal_signal_awaiter_connect,
 	(void *)godotsharp_internal_unmanaged_get_script_instance_managed,
 	(void *)godotsharp_internal_unmanaged_get_script_instance_managed,
 	(void *)godotsharp_internal_unmanaged_get_instance_binding_managed,
 	(void *)godotsharp_internal_unmanaged_get_instance_binding_managed,

+ 10 - 10
modules/mono/mono_gd/gd_mono.cpp

@@ -299,16 +299,6 @@ using godot_plugins_initialize_fn = bool (*)(void *, bool, gdmono::PluginCallbac
 using godot_plugins_initialize_fn = bool (*)(void *, GDMonoCache::ManagedCallbacks *);
 using godot_plugins_initialize_fn = bool (*)(void *, GDMonoCache::ManagedCallbacks *);
 #endif
 #endif
 
 
-static String get_assembly_name() {
-	String appname = ProjectSettings::get_singleton()->get("application/config/name");
-	String appname_safe = OS::get_singleton()->get_safe_dir_name(appname);
-	if (appname_safe.is_empty()) {
-		appname_safe = "UnnamedProject";
-	}
-
-	return appname_safe;
-}
-
 #ifdef TOOLS_ENABLED
 #ifdef TOOLS_ENABLED
 godot_plugins_initialize_fn initialize_hostfxr_and_godot_plugins(bool &r_runtime_initialized) {
 godot_plugins_initialize_fn initialize_hostfxr_and_godot_plugins(bool &r_runtime_initialized) {
 	godot_plugins_initialize_fn godot_plugins_initialize = nullptr;
 	godot_plugins_initialize_fn godot_plugins_initialize = nullptr;
@@ -338,6 +328,16 @@ godot_plugins_initialize_fn initialize_hostfxr_and_godot_plugins(bool &r_runtime
 	return godot_plugins_initialize;
 	return godot_plugins_initialize;
 }
 }
 #else
 #else
+static String get_assembly_name() {
+	String appname = ProjectSettings::get_singleton()->get("application/config/name");
+	String appname_safe = OS::get_singleton()->get_safe_dir_name(appname);
+	if (appname_safe.is_empty()) {
+		appname_safe = "UnnamedProject";
+	}
+
+	return appname_safe;
+}
+
 godot_plugins_initialize_fn initialize_hostfxr_and_godot_plugins(bool &r_runtime_initialized) {
 godot_plugins_initialize_fn initialize_hostfxr_and_godot_plugins(bool &r_runtime_initialized) {
 	godot_plugins_initialize_fn godot_plugins_initialize = nullptr;
 	godot_plugins_initialize_fn godot_plugins_initialize = nullptr;
 
 

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

@@ -60,8 +60,6 @@ void update_godot_api_cache(const ManagedCallbacks &p_managed_callbacks) {
 	CHECK_CALLBACK_NOT_NULL(ScriptManagerBridge, GetScriptNativeName);
 	CHECK_CALLBACK_NOT_NULL(ScriptManagerBridge, GetScriptNativeName);
 	CHECK_CALLBACK_NOT_NULL(ScriptManagerBridge, SetGodotObjectPtr);
 	CHECK_CALLBACK_NOT_NULL(ScriptManagerBridge, SetGodotObjectPtr);
 	CHECK_CALLBACK_NOT_NULL(ScriptManagerBridge, RaiseEventSignal);
 	CHECK_CALLBACK_NOT_NULL(ScriptManagerBridge, RaiseEventSignal);
-	CHECK_CALLBACK_NOT_NULL(ScriptManagerBridge, GetScriptSignalList);
-	CHECK_CALLBACK_NOT_NULL(ScriptManagerBridge, HasScriptSignal);
 	CHECK_CALLBACK_NOT_NULL(ScriptManagerBridge, ScriptIsOrInherits);
 	CHECK_CALLBACK_NOT_NULL(ScriptManagerBridge, ScriptIsOrInherits);
 	CHECK_CALLBACK_NOT_NULL(ScriptManagerBridge, AddScriptBridge);
 	CHECK_CALLBACK_NOT_NULL(ScriptManagerBridge, AddScriptBridge);
 	CHECK_CALLBACK_NOT_NULL(ScriptManagerBridge, GetOrCreateScriptBridgeForPath);
 	CHECK_CALLBACK_NOT_NULL(ScriptManagerBridge, GetOrCreateScriptBridgeForPath);

+ 1 - 5
modules/mono/mono_gd/gd_mono_cache.h

@@ -82,14 +82,12 @@ struct ManagedCallbacks {
 	using FuncScriptManagerBridge_GetScriptNativeName = void(GD_CLR_STDCALL *)(const CSharpScript *, StringName *);
 	using FuncScriptManagerBridge_GetScriptNativeName = void(GD_CLR_STDCALL *)(const CSharpScript *, StringName *);
 	using FuncScriptManagerBridge_SetGodotObjectPtr = void(GD_CLR_STDCALL *)(GCHandleIntPtr, Object *);
 	using FuncScriptManagerBridge_SetGodotObjectPtr = void(GD_CLR_STDCALL *)(GCHandleIntPtr, Object *);
 	using FuncScriptManagerBridge_RaiseEventSignal = void(GD_CLR_STDCALL *)(GCHandleIntPtr, const StringName *, const Variant **, int32_t, bool *);
 	using FuncScriptManagerBridge_RaiseEventSignal = void(GD_CLR_STDCALL *)(GCHandleIntPtr, const StringName *, const Variant **, int32_t, bool *);
-	using FuncScriptManagerBridge_GetScriptSignalList = void(GD_CLR_STDCALL *)(const CSharpScript *, Dictionary *);
-	using FuncScriptManagerBridge_HasScriptSignal = bool(GD_CLR_STDCALL *)(const CSharpScript *, const String *);
 	using FuncScriptManagerBridge_ScriptIsOrInherits = bool(GD_CLR_STDCALL *)(const CSharpScript *, const CSharpScript *);
 	using FuncScriptManagerBridge_ScriptIsOrInherits = bool(GD_CLR_STDCALL *)(const CSharpScript *, const CSharpScript *);
 	using FuncScriptManagerBridge_AddScriptBridge = bool(GD_CLR_STDCALL *)(const CSharpScript *, const String *);
 	using FuncScriptManagerBridge_AddScriptBridge = bool(GD_CLR_STDCALL *)(const CSharpScript *, const String *);
 	using FuncScriptManagerBridge_GetOrCreateScriptBridgeForPath = void(GD_CLR_STDCALL *)(const String *, Ref<CSharpScript> *);
 	using FuncScriptManagerBridge_GetOrCreateScriptBridgeForPath = void(GD_CLR_STDCALL *)(const String *, Ref<CSharpScript> *);
 	using FuncScriptManagerBridge_RemoveScriptBridge = void(GD_CLR_STDCALL *)(const CSharpScript *);
 	using FuncScriptManagerBridge_RemoveScriptBridge = void(GD_CLR_STDCALL *)(const CSharpScript *);
 	using FuncScriptManagerBridge_TryReloadRegisteredScriptWithClass = bool(GD_CLR_STDCALL *)(const CSharpScript *);
 	using FuncScriptManagerBridge_TryReloadRegisteredScriptWithClass = bool(GD_CLR_STDCALL *)(const CSharpScript *);
-	using FuncScriptManagerBridge_UpdateScriptClassInfo = void(GD_CLR_STDCALL *)(const CSharpScript *, bool *, Dictionary *, Ref<CSharpScript> *);
+	using FuncScriptManagerBridge_UpdateScriptClassInfo = void(GD_CLR_STDCALL *)(const CSharpScript *, bool *, Dictionary *, Dictionary *, Ref<CSharpScript> *);
 	using FuncScriptManagerBridge_SwapGCHandleForType = bool(GD_CLR_STDCALL *)(GCHandleIntPtr, GCHandleIntPtr *, bool);
 	using FuncScriptManagerBridge_SwapGCHandleForType = bool(GD_CLR_STDCALL *)(GCHandleIntPtr, GCHandleIntPtr *, bool);
 	using FuncScriptManagerBridge_GetPropertyInfoList = void(GD_CLR_STDCALL *)(CSharpScript *, Callback_ScriptManagerBridge_GetPropertyInfoList_Add);
 	using FuncScriptManagerBridge_GetPropertyInfoList = void(GD_CLR_STDCALL *)(CSharpScript *, Callback_ScriptManagerBridge_GetPropertyInfoList_Add);
 	using FuncScriptManagerBridge_GetPropertyDefaultValues = void(GD_CLR_STDCALL *)(CSharpScript *, Callback_ScriptManagerBridge_GetPropertyDefaultValues_Add);
 	using FuncScriptManagerBridge_GetPropertyDefaultValues = void(GD_CLR_STDCALL *)(CSharpScript *, Callback_ScriptManagerBridge_GetPropertyDefaultValues_Add);
@@ -117,8 +115,6 @@ struct ManagedCallbacks {
 	FuncScriptManagerBridge_GetScriptNativeName ScriptManagerBridge_GetScriptNativeName;
 	FuncScriptManagerBridge_GetScriptNativeName ScriptManagerBridge_GetScriptNativeName;
 	FuncScriptManagerBridge_SetGodotObjectPtr ScriptManagerBridge_SetGodotObjectPtr;
 	FuncScriptManagerBridge_SetGodotObjectPtr ScriptManagerBridge_SetGodotObjectPtr;
 	FuncScriptManagerBridge_RaiseEventSignal ScriptManagerBridge_RaiseEventSignal;
 	FuncScriptManagerBridge_RaiseEventSignal ScriptManagerBridge_RaiseEventSignal;
-	FuncScriptManagerBridge_GetScriptSignalList ScriptManagerBridge_GetScriptSignalList;
-	FuncScriptManagerBridge_HasScriptSignal ScriptManagerBridge_HasScriptSignal;
 	FuncScriptManagerBridge_ScriptIsOrInherits ScriptManagerBridge_ScriptIsOrInherits;
 	FuncScriptManagerBridge_ScriptIsOrInherits ScriptManagerBridge_ScriptIsOrInherits;
 	FuncScriptManagerBridge_AddScriptBridge ScriptManagerBridge_AddScriptBridge;
 	FuncScriptManagerBridge_AddScriptBridge ScriptManagerBridge_AddScriptBridge;
 	FuncScriptManagerBridge_GetOrCreateScriptBridgeForPath ScriptManagerBridge_GetOrCreateScriptBridgeForPath;
 	FuncScriptManagerBridge_GetOrCreateScriptBridgeForPath ScriptManagerBridge_GetOrCreateScriptBridgeForPath;