diff options
Diffstat (limited to 'modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators')
-rw-r--r-- | modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/MarshalUtils.cs | 12 | ||||
-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.cs | 113 | ||||
-rw-r--r-- | modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptSignalsGenerator.cs | 51 |
4 files changed, 338 insertions, 221 deletions
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"); + } } } |