summaryrefslogtreecommitdiff
path: root/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators
diff options
context:
space:
mode:
authorIgnacio Roldán Etcheverry <ignalfonsore@gmail.com>2022-10-28 22:59:13 +0200
committerIgnacio Roldán Etcheverry <ignalfonsore@gmail.com>2022-10-30 01:24:15 +0200
commit282bd37e5c9dbc1bba4fe349fc7868643a54164b (patch)
tree6e3d5f7499ceb4b0d928764fd9b8da16548911ce /modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators
parentf013315980ca13f4b66df29b5e32d3ecc310cb84 (diff)
C#: Remove need for reflection to invoking callable delegates
We aim to make the C# API reflection-free, mainly for concerns about performance, and to be able to target NativeAOT in refletion-free mode, which reduces the binary size. One of the main usages of reflection still left was the dynamic invokation of callable delegates, and for some time I wasn't sure I would find an alternative solution that I'd be happy with. The new solution uses trampoline functions to invoke the delegates: ``` static void Trampoline(object delegateObj, NativeVariantPtrArgs args, out godot_variant ret) { if (args.Count != 1) throw new ArgumentException($"Callable expected 1 arguments but received {args.Count}."); string res = ((Func<int, string>)delegateObj)( VariantConversionCallbacks.GetToManagedCallback<int>()(args[0]) ); ret = VariantConversionCallbacks.GetToVariantCallback<string>()(res); } Callable.CreateWithUnsafeTrampoline((int num) => "Foo" + num, &Trampoline); ``` Of course, this is too much boilerplate for user code. To improve this, the `Callable.From` methods were added. These are overloads that take `Action` and `Func` delegates, which covers the most common use cases: lambdas and method groups: ``` // Lambda Callable.From((int num) => "Foo" + num); // Method group string AppendNum(int num) => "Foo" + num; Callable.From(AppendNum); ``` Unfortunately, due to limitations in the C# language, implicit conversions from delegates to `Callable` are not supported. `Callable.From` does not support custom delegates. These should be uncommon, but the Godot C# API actually uses them for event signals. As such, the bindings generator was updated to generate trampoline functions for event signals. It was also optimized to use `Action` instead of a custom delegate for parameterless signals, which removes the need for the trampoline functions for those signals. The change to reflection-free invokation removes one of the last needs for `ConvertVariantToManagedObjectOfType`. The only remaining usage is from calling script constructors with parameters from the engine (`CreateManagedForGodotObjectScriptInstance`). Once that one is made reflection-free, `ConvertVariantToManagedObjectOfType` can be removed.
Diffstat (limited to 'modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators')
-rw-r--r--modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptMethodsGenerator.cs6
-rw-r--r--modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptSignalsGenerator.cs7
2 files changed, 7 insertions, 6 deletions
diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptMethodsGenerator.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptMethodsGenerator.cs
index d915eeac0b..d5d80df643 100644
--- a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptMethodsGenerator.cs
+++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptMethodsGenerator.cs
@@ -188,14 +188,14 @@ namespace Godot.SourceGenerators
if (godotClassMethods.Length > 0)
{
source.Append(" protected override bool InvokeGodotClassMethod(in godot_string_name method, ");
- source.Append("NativeVariantPtrArgs args, int argCount, out godot_variant ret)\n {\n");
+ source.Append("NativeVariantPtrArgs args, out godot_variant ret)\n {\n");
foreach (var method in godotClassMethods)
{
GenerateMethodInvoker(method, source);
}
- source.Append(" return base.InvokeGodotClassMethod(method, args, argCount, out ret);\n");
+ source.Append(" return base.InvokeGodotClassMethod(method, args, out ret);\n");
source.Append(" }\n");
}
@@ -364,7 +364,7 @@ namespace Godot.SourceGenerators
source.Append(" if (method == MethodName.");
source.Append(methodName);
- source.Append(" && argCount == ");
+ source.Append(" && args.Count == ");
source.Append(method.ParamTypes.Length);
source.Append(") {\n");
diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptSignalsGenerator.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptSignalsGenerator.cs
index 4e443ce26e..50196b84f0 100644
--- a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptSignalsGenerator.cs
+++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptSignalsGenerator.cs
@@ -167,6 +167,7 @@ namespace Godot.SourceGenerators
Common.ReportSignalDelegateSignatureMustReturnVoid(context, signalDelegateSymbol);
}
}
+
continue;
}
@@ -257,14 +258,14 @@ namespace Godot.SourceGenerators
{
source.Append(
" protected override void RaiseGodotClassSignalCallbacks(in godot_string_name signal, ");
- source.Append("NativeVariantPtrArgs args, int argCount)\n {\n");
+ source.Append("NativeVariantPtrArgs args)\n {\n");
foreach (var signal in godotSignalDelegates)
{
GenerateSignalEventInvoker(signal, source);
}
- source.Append(" base.RaiseGodotClassSignalCallbacks(signal, args, argCount);\n");
+ source.Append(" base.RaiseGodotClassSignalCallbacks(signal, args);\n");
source.Append(" }\n");
}
@@ -404,7 +405,7 @@ namespace Godot.SourceGenerators
source.Append(" if (signal == SignalName.");
source.Append(signalName);
- source.Append(" && argCount == ");
+ source.Append(" && args.Count == ");
source.Append(invokeMethodData.ParamTypes.Length);
source.Append(") {\n");
source.Append(" backing_");