summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorIgnacio Roldán Etcheverry <ignalfonsore@gmail.com>2022-07-28 17:41:48 +0200
committerIgnacio Roldán Etcheverry <ignalfonsore@gmail.com>2022-08-22 03:36:52 +0200
commita9892f257153a2d760a5d221dc16e484e1428c71 (patch)
treeb7b463a133f67d3e859f0b0444910d20f9b1c47c
parent97713ff77a339faa72d54bd596e3d8c2b8520ce0 (diff)
C#: Add source generator for method list
-rw-r--r--modules/mono/csharp_script.cpp109
-rw-r--r--modules/mono/csharp_script.h6
-rw-r--r--modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Sample/Methods.cs31
-rw-r--r--modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/MarshalUtils.cs12
-rw-r--r--modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptMethodsGenerator.cs (renamed from modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptMemberInvokerGenerator.cs)383
-rw-r--r--modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptPropertiesGenerator.cs113
-rw-r--r--modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptSignalsGenerator.cs51
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/ManagedCallbacks.cs2
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/ScriptManagerBridge.cs75
-rw-r--r--modules/mono/mono_gd/gd_mono_cache.h2
10 files changed, 502 insertions, 282 deletions
diff --git a/modules/mono/csharp_script.cpp b/modules/mono/csharp_script.cpp
index 5235c6d176..c69a149d9c 100644
--- a/modules/mono/csharp_script.cpp
+++ b/modules/mono/csharp_script.cpp
@@ -1618,29 +1618,18 @@ bool CSharpInstance::property_get_revert(const StringName &p_name, Variant &r_re
}
void CSharpInstance::get_method_list(List<MethodInfo> *p_list) const {
-#warning TODO
-#if 0
- if (!script->is_valid() || !script->script_class) {
+ if (!script->is_valid() || !script->valid) {
return;
}
- GD_MONO_SCOPE_THREAD_ATTACH;
-
- // TODO: We're filtering out constructors but there may be other methods unsuitable for explicit calls.
- GDMonoClass *top = script->script_class;
-
- while (top && top != script->native) {
- const Vector<GDMonoMethod *> &methods = top->get_all_methods();
- for (int i = 0; i < methods.size(); ++i) {
- MethodInfo minfo = methods[i]->get_method_info();
- if (minfo.name != CACHED_STRING_NAME(dotctor)) {
- p_list->push_back(minfo);
- }
+ const CSharpScript *top = script.ptr();
+ while (top != nullptr) {
+ for (const CSharpScript::CSharpMethodInfo &E : top->methods) {
+ p_list->push_back(E.method_info);
}
- top = top->get_parent_class();
+ top = top->base_script.ptr();
}
-#endif
}
bool CSharpInstance::has_method(const StringName &p_method) const {
@@ -2183,25 +2172,52 @@ void CSharpScript::reload_registered_script(Ref<CSharpScript> p_script) {
void CSharpScript::update_script_class_info(Ref<CSharpScript> p_script) {
bool tool = false;
+ // TODO: Use GDNative godot_dictionary
+ Array methods_array;
+ methods_array.~Array();
Dictionary rpc_functions_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#.
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;
GDMonoCache::managed_callbacks.ScriptManagerBridge_UpdateScriptClassInfo(
- p_script.ptr(), &tool, &rpc_functions_dict, &signals_dict, &base_script);
+ p_script.ptr(), &tool, &methods_array, &rpc_functions_dict, &signals_dict, &base_script);
p_script->tool = tool;
p_script->rpc_config.clear();
p_script->rpc_config = rpc_functions_dict;
+ // Methods
+
+ p_script->methods.clear();
+
+ p_script->methods.resize(methods_array.size());
+ int push_index = 0;
+
+ for (int i = 0; i < methods_array.size(); i++) {
+ Dictionary method_info_dict = methods_array[i];
+
+ StringName name = method_info_dict["name"];
+
+ MethodInfo mi;
+ mi.name = name;
+
+ Array params = method_info_dict["params"];
+
+ for (int j = 0; j < params.size(); j++) {
+ Dictionary param = params[j];
+
+ 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->methods.set(push_index++, CSharpMethodInfo{ name, mi });
+ }
+
// Event signals
// Performance is not critical here as this will be replaced with source generators.
@@ -2210,7 +2226,7 @@ void CSharpScript::update_script_class_info(Ref<CSharpScript> p_script) {
// Sigh... can't we just have capacity?
p_script->event_signals.resize(signals_dict.size());
- int push_index = 0;
+ push_index = 0;
for (const Variant *s = signals_dict.next(nullptr); s != nullptr; s = signals_dict.next(s)) {
StringName name = *s;
@@ -2407,28 +2423,27 @@ void CSharpScript::get_script_method_list(List<MethodInfo> *p_list) const {
return;
}
-#warning TODO
-#if 0
- // TODO: We're filtering out constructors but there may be other methods unsuitable for explicit calls.
- GDMonoClass *top = script_class;
-
- while (top && top != native) {
- const Vector<GDMonoMethod *> &methods = top->get_all_methods();
- for (int i = 0; i < methods.size(); ++i) {
- MethodInfo minfo = methods[i]->get_method_info();
- if (minfo.name != CACHED_STRING_NAME(dotctor)) {
- p_list->push_back(methods[i]->get_method_info());
- }
+ const CSharpScript *top = this;
+ while (top != nullptr) {
+ for (const CSharpMethodInfo &E : top->methods) {
+ p_list->push_back(E.method_info);
}
- top = top->get_parent_class();
+ top = top->base_script.ptr();
}
-#endif
}
bool CSharpScript::has_method(const StringName &p_method) const {
- // The equivalent of this will be implemented once we switch to the GDExtension system
- ERR_PRINT_ONCE("CSharpScript::has_method is not implemented");
+ if (!valid) {
+ return false;
+ }
+
+ for (const CSharpMethodInfo &E : methods) {
+ if (E.name == p_method) {
+ return true;
+ }
+ }
+
return false;
}
@@ -2437,19 +2452,11 @@ MethodInfo CSharpScript::get_method_info(const StringName &p_method) const {
return MethodInfo();
}
-#warning TODO
-#if 0
- GDMonoClass *top = script_class;
-
- while (top && top != native) {
- GDMonoMethod *params = top->get_method_unknown_params(p_method);
- if (params) {
- return params->get_method_info();
+ for (const CSharpMethodInfo &E : methods) {
+ if (E.name == p_method) {
+ return E.method_info;
}
-
- top = top->get_parent_class();
}
-#endif
return MethodInfo();
}
diff --git a/modules/mono/csharp_script.h b/modules/mono/csharp_script.h
index f3a8ead319..3509a5c87d 100644
--- a/modules/mono/csharp_script.h
+++ b/modules/mono/csharp_script.h
@@ -103,7 +103,13 @@ class CSharpScript : public Script {
MethodInfo method_info;
};
+ struct CSharpMethodInfo {
+ StringName name; // MethodInfo stores a string...
+ MethodInfo method_info;
+ };
+
Vector<EventSignalInfo> event_signals;
+ Vector<CSharpMethodInfo> methods;
#ifdef TOOLS_ENABLED
List<PropertyInfo> exported_members_cache; // members_cache
diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Sample/Methods.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Sample/Methods.cs
new file mode 100644
index 0000000000..618ba24abc
--- /dev/null
+++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Sample/Methods.cs
@@ -0,0 +1,31 @@
+using System.Diagnostics.CodeAnalysis;
+
+namespace Godot.SourceGenerators.Sample;
+
+[SuppressMessage("ReSharper", "RedundantNameQualifier")]
+public partial class Methods : Godot.Object
+{
+ private void MethodWithOverload()
+ {
+ }
+
+ private void MethodWithOverload(int a)
+ {
+ }
+
+ private void MethodWithOverload(int a, int b)
+ {
+ }
+
+ // Should be ignored. The previous one is picked.
+ // ReSharper disable once UnusedMember.Local
+ private void MethodWithOverload(float a, float b)
+ {
+ }
+
+ // Generic methods should be ignored.
+ // ReSharper disable once UnusedMember.Local
+ private void GenericMethod<T>(T t)
+ {
+ }
+}
diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/MarshalUtils.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/MarshalUtils.cs
index 9dff404ede..cd4c8ff828 100644
--- a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/MarshalUtils.cs
+++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/MarshalUtils.cs
@@ -400,6 +400,12 @@ namespace Godot.SourceGenerators
source.Append(VariantUtils, ".ConvertToQuaternion(", inputExpr, ")"),
MarshalType.Transform3D =>
source.Append(VariantUtils, ".ConvertToTransform3D(", inputExpr, ")"),
+ MarshalType.Vector4 =>
+ source.Append(VariantUtils, ".ConvertToVector4(", inputExpr, ")"),
+ MarshalType.Vector4i =>
+ source.Append(VariantUtils, ".ConvertToVector4i(", inputExpr, ")"),
+ MarshalType.Projection =>
+ source.Append(VariantUtils, ".ConvertToProjection(", inputExpr, ")"),
MarshalType.AABB =>
source.Append(VariantUtils, ".ConvertToAABB(", inputExpr, ")"),
MarshalType.Color =>
@@ -535,6 +541,12 @@ namespace Godot.SourceGenerators
source.Append(VariantUtils, ".CreateFromQuaternion(", inputExpr, ")"),
MarshalType.Transform3D =>
source.Append(VariantUtils, ".CreateFromTransform3D(", inputExpr, ")"),
+ MarshalType.Vector4 =>
+ source.Append(VariantUtils, ".CreateFromVector4(", inputExpr, ")"),
+ MarshalType.Vector4i =>
+ source.Append(VariantUtils, ".CreateFromVector4i(", inputExpr, ")"),
+ MarshalType.Projection =>
+ source.Append(VariantUtils, ".CreateFromProjection(", inputExpr, ")"),
MarshalType.AABB =>
source.Append(VariantUtils, ".CreateFromAABB(", inputExpr, ")"),
MarshalType.Color =>
diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptMemberInvokerGenerator.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptMethodsGenerator.cs
index f10942fc24..8ee9489fe2 100644
--- a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptMemberInvokerGenerator.cs
+++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptMethodsGenerator.cs
@@ -8,8 +8,12 @@ using Microsoft.CodeAnalysis.Text;
namespace Godot.SourceGenerators
{
[Generator]
- public class ScriptMemberInvokerGenerator : ISourceGenerator
+ public class ScriptMethodsGenerator : ISourceGenerator
{
+ public void Initialize(GeneratorInitializationContext context)
+ {
+ }
+
public void Execute(GeneratorExecutionContext context)
{
if (context.AreGodotSourceGeneratorsDisabled())
@@ -54,6 +58,20 @@ namespace Godot.SourceGenerators
}
}
+ private class MethodOverloadEqualityComparer : IEqualityComparer<GodotMethodData>
+ {
+ public bool Equals(GodotMethodData x, GodotMethodData y)
+ => x.ParamTypes.Length == y.ParamTypes.Length && x.Method.Name == y.Method.Name;
+
+ public int GetHashCode(GodotMethodData obj)
+ {
+ unchecked
+ {
+ return (obj.ParamTypes.Length.GetHashCode() * 397) ^ obj.Method.Name.GetHashCode();
+ }
+ }
+ }
+
private static void VisitGodotScriptClass(
GeneratorExecutionContext context,
MarshalUtils.TypeCache typeCache,
@@ -69,7 +87,7 @@ namespace Godot.SourceGenerators
bool isInnerClass = symbol.ContainingType != null;
string uniqueHint = symbol.FullQualifiedName().SanitizeQualifiedNameForUniqueHint()
- + "_ScriptMemberInvoker_Generated";
+ + "_ScriptMethods_Generated";
var source = new StringBuilder();
@@ -111,54 +129,21 @@ namespace Godot.SourceGenerators
.Cast<IMethodSymbol>()
.Where(m => m.MethodKind == MethodKind.Ordinary);
- var propertySymbols = members
- .Where(s => !s.IsStatic && s.Kind == SymbolKind.Property)
- .Cast<IPropertySymbol>();
-
- var fieldSymbols = members
- .Where(s => !s.IsStatic && s.Kind == SymbolKind.Field && !s.IsImplicitlyDeclared)
- .Cast<IFieldSymbol>();
-
- var godotClassMethods = methodSymbols.WhereHasGodotCompatibleSignature(typeCache).ToArray();
- var godotClassProperties = propertySymbols.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));
- }
+ var godotClassMethods = methodSymbols.WhereHasGodotCompatibleSignature(typeCache)
+ .Distinct(new MethodOverloadEqualityComparer())
+ .ToArray();
source.Append(" private partial class GodotInternal {\n");
// Generate cached StringNames for methods and properties, for fast lookup
- // TODO: Move the generation of these cached StringNames to its own generator
+ var distinctMethodNames = godotClassMethods
+ .Select(m => m.Method.Name)
+ .Distinct()
+ .ToArray();
- foreach (var method in godotClassMethods)
+ foreach (string methodName in distinctMethodNames)
{
- string methodName = method.Method.Name;
source.Append(" public static readonly StringName MethodName_");
source.Append(methodName);
source.Append(" = \"");
@@ -168,6 +153,36 @@ namespace Godot.SourceGenerators
source.Append(" }\n"); // class GodotInternal
+ // Generate GetGodotMethodList
+
+ if (godotClassMethods.Length > 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(" GetGodotMethodList()\n {\n");
+
+ source.Append(" var methods = new ")
+ .Append(listType)
+ .Append("(")
+ .Append(godotClassMethods.Length)
+ .Append(");\n");
+
+ foreach (var method in godotClassMethods)
+ {
+ var methodInfo = DetermineMethodInfo(method);
+ AppendMethodInfo(source, methodInfo);
+ }
+
+ source.Append(" return methods;\n");
+ source.Append(" }\n");
+
+ source.Append("#pragma warning restore CS0109\n");
+ }
+
// Generate InvokeGodotClassMethod
if (godotClassMethods.Length > 0)
@@ -187,14 +202,14 @@ namespace Godot.SourceGenerators
// Generate HasGodotClassMethod
- if (godotClassMethods.Length > 0)
+ if (distinctMethodNames.Length > 0)
{
source.Append(" protected override bool HasGodotClassMethod(in godot_string_name method)\n {\n");
bool isFirstEntry = true;
- foreach (var method in godotClassMethods)
+ foreach (string methodName in distinctMethodNames)
{
- GenerateHasMethodEntry(method, source, isFirstEntry);
+ GenerateHasMethodEntry(methodName, source, isFirstEntry);
isFirstEntry = false;
}
@@ -203,111 +218,141 @@ namespace Godot.SourceGenerators
source.Append(" }\n");
}
- // Generate RaiseGodotClassSignalCallbacks
+ source.Append("}\n"); // partial class
- if (godotSignalDelegates.Count > 0)
+ if (isInnerClass)
{
- source.Append(
- " protected override void RaiseGodotClassSignalCallbacks(in godot_string_name signal, ");
- source.Append("NativeVariantPtrArgs args, int argCount)\n {\n");
+ var containingType = symbol.ContainingType;
- foreach (var signal in godotSignalDelegates)
+ while (containingType != null)
{
- GenerateSignalEventInvoker(signal, source);
- }
-
- source.Append(" base.RaiseGodotClassSignalCallbacks(signal, args, argCount);\n");
+ source.Append("}\n"); // outer class
- source.Append(" }\n");
+ containingType = containingType.ContainingType;
+ }
}
- // Generate Set/GetGodotClassPropertyValue
-
- if (godotClassProperties.Length > 0 || godotClassFields.Length > 0)
+ if (hasNamespace)
{
- bool isFirstEntry;
-
- // Setters
-
- bool allPropertiesAreReadOnly = godotClassFields.All(fi => fi.FieldSymbol.IsReadOnly) &&
- godotClassProperties.All(pi => pi.PropertySymbol.IsReadOnly);
+ source.Append("\n}\n");
+ }
- if (!allPropertiesAreReadOnly)
- {
- source.Append(" protected override bool SetGodotClassPropertyValue(in godot_string_name name, ");
- source.Append("in godot_variant value)\n {\n");
+ context.AddSource(uniqueHint, SourceText.From(source.ToString(), Encoding.UTF8));
+ }
- isFirstEntry = true;
- foreach (var property in godotClassProperties)
- {
- if (property.PropertySymbol.IsReadOnly)
- continue;
+ private static void AppendMethodInfo(StringBuilder source, MethodInfo methodInfo)
+ {
+ source.Append(" methods.Add(new(name: GodotInternal.MethodName_")
+ .Append(methodInfo.Name)
+ .Append(", returnVal: ");
- GeneratePropertySetter(property.PropertySymbol.Name,
- property.PropertySymbol.Type, property.Type, source, isFirstEntry);
- isFirstEntry = false;
- }
+ AppendPropertyInfo(source, methodInfo.ReturnVal);
- foreach (var field in godotClassFields)
- {
- if (field.FieldSymbol.IsReadOnly)
- continue;
+ source.Append(", flags: (Godot.MethodFlags)")
+ .Append((int)methodInfo.Flags)
+ .Append(", arguments: ");
- GeneratePropertySetter(field.FieldSymbol.Name,
- field.FieldSymbol.Type, field.Type, source, isFirstEntry);
- isFirstEntry = false;
- }
+ if (methodInfo.Arguments is { Count: > 0 })
+ {
+ source.Append("new() { ");
- source.Append(" return base.SetGodotClassPropertyValue(name, value);\n");
+ foreach (var param in methodInfo.Arguments)
+ {
+ AppendPropertyInfo(source, param);
- source.Append(" }\n");
+ // C# allows colon after the last element
+ source.Append(", ");
}
- // Getters
-
- source.Append(" protected override bool GetGodotClassPropertyValue(in godot_string_name name, ");
- source.Append("out godot_variant value)\n {\n");
+ source.Append(" }");
+ }
+ else
+ {
+ source.Append("null");
+ }
- isFirstEntry = true;
- foreach (var property in godotClassProperties)
- {
- GeneratePropertyGetter(property.PropertySymbol.Name,
- property.Type, source, isFirstEntry);
- isFirstEntry = false;
- }
+ source.Append(", defaultArguments: null));\n");
+ }
- foreach (var field in godotClassFields)
- {
- GeneratePropertyGetter(field.FieldSymbol.Name,
- field.Type, source, isFirstEntry);
- isFirstEntry = false;
- }
+ 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(")");
+ }
- source.Append(" return base.GetGodotClassPropertyValue(name, out value);\n");
+ private static MethodInfo DetermineMethodInfo(GodotMethodData method)
+ {
+ PropertyInfo returnVal;
- source.Append(" }\n");
+ if (method.RetType != null)
+ {
+ returnVal = DeterminePropertyInfo(method.RetType.Value, name: string.Empty);
+ }
+ else
+ {
+ returnVal = new PropertyInfo(VariantType.Nil, string.Empty, PropertyHint.None,
+ hintString: null, PropertyUsageFlags.Default, exported: false);
}
- source.Append("}\n"); // partial class
+ int paramCount = method.ParamTypes.Length;
- if (isInnerClass)
+ List<PropertyInfo>? arguments;
+
+ if (paramCount > 0)
{
- var containingType = symbol.ContainingType;
+ arguments = new(capacity: paramCount);
- while (containingType != null)
+ for (int i = 0; i < paramCount; i++)
{
- source.Append("}\n"); // outer class
-
- containingType = containingType.ContainingType;
+ arguments.Add(DeterminePropertyInfo(method.ParamTypes[i],
+ name: method.Method.Parameters[i].Name));
}
}
-
- if (hasNamespace)
+ else
{
- source.Append("\n}\n");
+ arguments = null;
}
- context.AddSource(uniqueHint, SourceText.From(source.ToString(), Encoding.UTF8));
+ return new MethodInfo(method.Method.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);
+ }
+
+ private static void GenerateHasMethodEntry(
+ string methodName,
+ StringBuilder source,
+ bool isFirstEntry
+ )
+ {
+ source.Append(" ");
+ if (!isFirstEntry)
+ source.Append("else ");
+ source.Append("if (method == GodotInternal.MethodName_");
+ source.Append(methodName);
+ source.Append(") {\n return true;\n }\n");
}
private static void GenerateMethodInvoker(
@@ -359,105 +404,5 @@ namespace Godot.SourceGenerators
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(
- string propertyMemberName,
- ITypeSymbol propertyTypeSymbol,
- MarshalType propertyMarshalType,
- StringBuilder source,
- bool isFirstEntry
- )
- {
- source.Append(" ");
-
- if (!isFirstEntry)
- source.Append("else ");
-
- source.Append("if (name == GodotInternal.PropName_")
- .Append(propertyMemberName)
- .Append(") {\n")
- .Append(" ")
- .Append(propertyMemberName)
- .Append(" = ")
- .AppendVariantToManagedExpr("value", propertyTypeSymbol, propertyMarshalType)
- .Append(";\n")
- .Append(" return true;\n")
- .Append(" }\n");
- }
-
- private static void GeneratePropertyGetter(
- string propertyMemberName,
- MarshalType propertyMarshalType,
- StringBuilder source,
- bool isFirstEntry
- )
- {
- source.Append(" ");
-
- if (!isFirstEntry)
- source.Append("else ");
-
- source.Append("if (name == GodotInternal.PropName_")
- .Append(propertyMemberName)
- .Append(") {\n")
- .Append(" value = ")
- .AppendManagedToVariantExpr(propertyMemberName, propertyMarshalType)
- .Append(";\n")
- .Append(" return true;\n")
- .Append(" }\n");
- }
-
- private static void GenerateHasMethodEntry(
- GodotMethodData method,
- StringBuilder source,
- bool isFirstEntry
- )
- {
- string methodName = method.Method.Name;
-
- source.Append(" ");
- if (!isFirstEntry)
- source.Append("else ");
- source.Append("if (method == GodotInternal.MethodName_");
- source.Append(methodName);
- source.Append(") {\n return true;\n }\n");
- }
-
- public void Initialize(GeneratorInitializationContext context)
- {
- }
}
}
diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptPropertiesGenerator.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptPropertiesGenerator.cs
index c527f738b3..29a15e2d16 100644
--- a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptPropertiesGenerator.cs
+++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptPropertiesGenerator.cs
@@ -146,10 +146,72 @@ namespace Godot.SourceGenerators
source.Append(" }\n"); // class GodotInternal
- // Generate GetGodotPropertyList
-
if (godotClassProperties.Length > 0 || godotClassFields.Length > 0)
{
+ bool isFirstEntry;
+
+ // Generate SetGodotClassPropertyValue
+
+ bool allPropertiesAreReadOnly = godotClassFields.All(fi => fi.FieldSymbol.IsReadOnly) &&
+ godotClassProperties.All(pi => pi.PropertySymbol.IsReadOnly);
+
+ if (!allPropertiesAreReadOnly)
+ {
+ source.Append(" protected override bool SetGodotClassPropertyValue(in godot_string_name name, ");
+ source.Append("in godot_variant value)\n {\n");
+
+ isFirstEntry = true;
+ foreach (var property in godotClassProperties)
+ {
+ if (property.PropertySymbol.IsReadOnly)
+ continue;
+
+ GeneratePropertySetter(property.PropertySymbol.Name,
+ property.PropertySymbol.Type, property.Type, source, isFirstEntry);
+ isFirstEntry = false;
+ }
+
+ foreach (var field in godotClassFields)
+ {
+ if (field.FieldSymbol.IsReadOnly)
+ continue;
+
+ GeneratePropertySetter(field.FieldSymbol.Name,
+ field.FieldSymbol.Type, field.Type, source, isFirstEntry);
+ isFirstEntry = false;
+ }
+
+ source.Append(" return base.SetGodotClassPropertyValue(name, value);\n");
+
+ source.Append(" }\n");
+ }
+
+ // Generate GetGodotClassPropertyValue
+
+ source.Append(" protected override bool GetGodotClassPropertyValue(in godot_string_name name, ");
+ source.Append("out godot_variant value)\n {\n");
+
+ isFirstEntry = true;
+ foreach (var property in godotClassProperties)
+ {
+ GeneratePropertyGetter(property.PropertySymbol.Name,
+ property.Type, source, isFirstEntry);
+ isFirstEntry = false;
+ }
+
+ foreach (var field in godotClassFields)
+ {
+ GeneratePropertyGetter(field.FieldSymbol.Name,
+ field.Type, source, isFirstEntry);
+ isFirstEntry = false;
+ }
+
+ source.Append(" return base.GetGodotClassPropertyValue(name, out value);\n");
+
+ source.Append(" }\n");
+
+ // Generate GetGodotPropertyList
+
source.Append("#pragma warning disable CS0109 // Disable warning about redundant 'new' keyword\n");
string dictionaryType = "System.Collections.Generic.List<global::Godot.Bridge.PropertyInfo>";
@@ -212,6 +274,53 @@ namespace Godot.SourceGenerators
context.AddSource(uniqueHint, SourceText.From(source.ToString(), Encoding.UTF8));
}
+ private static void GeneratePropertySetter(
+ string propertyMemberName,
+ ITypeSymbol propertyTypeSymbol,
+ MarshalType propertyMarshalType,
+ StringBuilder source,
+ bool isFirstEntry
+ )
+ {
+ source.Append(" ");
+
+ if (!isFirstEntry)
+ source.Append("else ");
+
+ source.Append("if (name == GodotInternal.PropName_")
+ .Append(propertyMemberName)
+ .Append(") {\n")
+ .Append(" ")
+ .Append(propertyMemberName)
+ .Append(" = ")
+ .AppendVariantToManagedExpr("value", propertyTypeSymbol, propertyMarshalType)
+ .Append(";\n")
+ .Append(" return true;\n")
+ .Append(" }\n");
+ }
+
+ private static void GeneratePropertyGetter(
+ string propertyMemberName,
+ MarshalType propertyMarshalType,
+ StringBuilder source,
+ bool isFirstEntry
+ )
+ {
+ source.Append(" ");
+
+ if (!isFirstEntry)
+ source.Append("else ");
+
+ source.Append("if (name == GodotInternal.PropName_")
+ .Append(propertyMemberName)
+ .Append(") {\n")
+ .Append(" value = ")
+ .AppendManagedToVariantExpr(propertyMemberName, propertyMarshalType)
+ .Append(";\n")
+ .Append(" return true;\n")
+ .Append(" }\n");
+ }
+
private static void AppendPropertyInfo(StringBuilder source, PropertyInfo propertyInfo)
{
source.Append(" properties.Add(new(type: (Godot.Variant.Type)")
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 31cc8e220b..10f4ddd149 100644
--- a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptSignalsGenerator.cs
+++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptSignalsGenerator.cs
@@ -232,6 +232,24 @@ namespace Godot.SourceGenerators
.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");
+ }
+
source.Append("}\n"); // partial class
if (isInnerClass)
@@ -356,5 +374,38 @@ namespace Godot.SourceGenerators
return new PropertyInfo(memberVariantType, name,
PropertyHint.None, string.Empty, propUsage, exported: false);
}
+
+ 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");
+ }
}
}
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/ManagedCallbacks.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/ManagedCallbacks.cs
index 5d3f140cba..57240624bc 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/ManagedCallbacks.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/ManagedCallbacks.cs
@@ -24,7 +24,7 @@ namespace Godot.Bridge
public delegate* unmanaged<godot_string*, godot_ref*, void> ScriptManagerBridge_GetOrCreateScriptBridgeForPath;
public delegate* unmanaged<IntPtr, void> ScriptManagerBridge_RemoveScriptBridge;
public delegate* unmanaged<IntPtr, godot_bool> ScriptManagerBridge_TryReloadRegisteredScriptWithClass;
- public delegate* unmanaged<IntPtr, godot_bool*, godot_dictionary*, godot_dictionary*, godot_ref*, void> ScriptManagerBridge_UpdateScriptClassInfo;
+ public delegate* unmanaged<IntPtr, godot_bool*, godot_array*, godot_dictionary*, godot_dictionary*, godot_ref*, void> ScriptManagerBridge_UpdateScriptClassInfo;
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, void*, int, void>, void> ScriptManagerBridge_GetPropertyDefaultValues;
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/ScriptManagerBridge.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/ScriptManagerBridge.cs
index 4b9f851925..03094cbe81 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/ScriptManagerBridge.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/ScriptManagerBridge.cs
@@ -557,7 +557,8 @@ namespace Godot.Bridge
[UnmanagedCallersOnly]
internal static unsafe void UpdateScriptClassInfo(IntPtr scriptPtr, godot_bool* outTool,
- godot_dictionary* outRpcFunctionsDest, godot_dictionary* outEventSignalsDest, godot_ref* outBaseScript)
+ godot_array* outMethodsDest, godot_dictionary* outRpcFunctionsDest,
+ godot_dictionary* outEventSignalsDest, godot_ref* outBaseScript)
{
try
{
@@ -578,15 +579,61 @@ namespace Godot.Bridge
if (!(*outTool).ToBool() && scriptType.Assembly.GetName().Name == "GodotTools")
*outTool = godot_bool.True;
- // RPC functions
+ // Methods
- Collections.Dictionary<string, Collections.Dictionary> rpcFunctions = new();
+ // Performance is not critical here as this will be replaced with source generators.
+ using var methods = new Collections.Array();
Type? top = scriptType;
Type native = Object.InternalGetClassNativeBase(top);
while (top != null && top != native)
{
+ var methodList = GetMethodListForType(top);
+
+ if (methodList != null)
+ {
+ foreach (var method in methodList)
+ {
+ var methodInfo = new Collections.Dictionary();
+
+ methodInfo.Add("name", method.Name);
+
+ var methodParams = new Collections.Array();
+
+ if (method.Arguments != null)
+ {
+ foreach (var param in method.Arguments)
+ {
+ methodParams.Add(new Collections.Dictionary()
+ {
+ { "name", param.Name },
+ { "type", param.Type },
+ { "usage", param.Usage }
+ });
+ }
+ }
+
+ methodInfo.Add("params", methodParams);
+
+ methods.Add(methodInfo);
+ }
+ }
+
+ top = top.BaseType;
+ }
+
+ *outMethodsDest = NativeFuncs.godotsharp_array_new_copy(
+ (godot_array)methods.NativeValue);
+
+ // RPC functions
+
+ Collections.Dictionary<string, Collections.Dictionary> rpcFunctions = new();
+
+ top = scriptType;
+
+ while (top != null && top != native)
+ {
foreach (var method in top.GetMethods(BindingFlags.DeclaredOnly | BindingFlags.Instance |
BindingFlags.NonPublic | BindingFlags.Public))
{
@@ -617,9 +664,8 @@ namespace Godot.Bridge
top = top.BaseType;
}
- *outRpcFunctionsDest =
- NativeFuncs.godotsharp_dictionary_new_copy(
- (godot_dictionary)((Collections.Dictionary)rpcFunctions).NativeValue);
+ *outRpcFunctionsDest = NativeFuncs.godotsharp_dictionary_new_copy(
+ (godot_dictionary)((Collections.Dictionary)rpcFunctions).NativeValue);
// Event signals
@@ -663,8 +709,8 @@ namespace Godot.Bridge
top = top.BaseType;
}
- *outEventSignalsDest =
- NativeFuncs.godotsharp_dictionary_new_copy((godot_dictionary)signals.NativeValue);
+ *outEventSignalsDest = NativeFuncs.godotsharp_dictionary_new_copy(
+ (godot_dictionary)signals.NativeValue);
// Base script
@@ -701,6 +747,19 @@ namespace Godot.Bridge
return (List<MethodInfo>?)getGodotSignalListMethod.Invoke(null, null);
}
+ private static List<MethodInfo>? GetMethodListForType(Type type)
+ {
+ var getGodotMethodListMethod = type.GetMethod(
+ "GetGodotMethodList",
+ BindingFlags.DeclaredOnly | BindingFlags.Static |
+ BindingFlags.NonPublic | BindingFlags.Public);
+
+ if (getGodotMethodListMethod == null)
+ return null;
+
+ return (List<MethodInfo>?)getGodotMethodListMethod.Invoke(null, null);
+ }
+
// ReSharper disable once InconsistentNaming
[SuppressMessage("ReSharper", "NotAccessedField.Local")]
[StructLayout(LayoutKind.Sequential)]
diff --git a/modules/mono/mono_gd/gd_mono_cache.h b/modules/mono/mono_gd/gd_mono_cache.h
index 3c58c251b7..ca3a6c95a7 100644
--- a/modules/mono/mono_gd/gd_mono_cache.h
+++ b/modules/mono/mono_gd/gd_mono_cache.h
@@ -87,7 +87,7 @@ struct ManagedCallbacks {
using FuncScriptManagerBridge_GetOrCreateScriptBridgeForPath = void(GD_CLR_STDCALL *)(const String *, Ref<CSharpScript> *);
using FuncScriptManagerBridge_RemoveScriptBridge = void(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 *, Dictionary *, Ref<CSharpScript> *);
+ using FuncScriptManagerBridge_UpdateScriptClassInfo = void(GD_CLR_STDCALL *)(const CSharpScript *, bool *, Array *, Dictionary *, Dictionary *, Ref<CSharpScript> *);
using FuncScriptManagerBridge_SwapGCHandleForType = bool(GD_CLR_STDCALL *)(GCHandleIntPtr, GCHandleIntPtr *, bool);
using FuncScriptManagerBridge_GetPropertyInfoList = void(GD_CLR_STDCALL *)(CSharpScript *, Callback_ScriptManagerBridge_GetPropertyInfoList_Add);
using FuncScriptManagerBridge_GetPropertyDefaultValues = void(GD_CLR_STDCALL *)(CSharpScript *, Callback_ScriptManagerBridge_GetPropertyDefaultValues_Add);