diff --git a/modules/mono/SdkPackageVersions.props b/modules/mono/SdkPackageVersions.props
index bdec051625d..2bd60bb7195 100644
--- a/modules/mono/SdkPackageVersions.props
+++ b/modules/mono/SdkPackageVersions.props
@@ -1,7 +1,7 @@
4.0.*-*
- 4.0.0-dev6
- 4.0.0-dev3
+ 4.0.0-dev7
+ 4.0.0-dev6
diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Sample/ScriptBoilerplate.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Sample/ScriptBoilerplate.cs
new file mode 100644
index 00000000000..05723c89400
--- /dev/null
+++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Sample/ScriptBoilerplate.cs
@@ -0,0 +1,21 @@
+namespace Godot.SourceGenerators.Sample
+{
+ public partial class ScriptBoilerplate : Godot.Node
+ {
+ private NodePath _nodePath;
+ private int _velocity;
+
+ public override void _Process(float delta)
+ {
+ _ = delta;
+
+ base._Process(delta);
+ }
+
+ public int Bazz(StringName name)
+ {
+ _ = name;
+ return 1;
+ }
+ }
+}
diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/Common.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/Common.cs
index 4867c986e6d..4dd67252edb 100644
--- a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/Common.cs
+++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/Common.cs
@@ -14,9 +14,8 @@ namespace Godot.SourceGenerators
"Missing partial modifier on declaration of type '" +
$"{symbol.FullQualifiedName()}' which is a subclass of '{GodotClasses.Object}'";
- string description = $"{message}. Subclasses of '{GodotClasses.Object}' must be " +
- "declared with the partial modifier or annotated with the " +
- $"attribute '{GodotClasses.DisableGodotGeneratorsAttr}'.";
+ string description = $"{message}. Subclasses of '{GodotClasses.Object}' " +
+ "must be declared with the partial modifier.";
context.ReportDiagnostic(Diagnostic.Create(
new DiagnosticDescriptor(id: "GODOT-G0001",
diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ExtensionMethods.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ExtensionMethods.cs
index e16f72f43af..f8c50e66c89 100644
--- a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ExtensionMethods.cs
+++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ExtensionMethods.cs
@@ -1,3 +1,4 @@
+using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.CodeAnalysis;
@@ -13,6 +14,11 @@ namespace Godot.SourceGenerators
) => context.AnalyzerConfigOptions.GlobalOptions
.TryGetValue("build_property." + property, out value);
+ public static bool AreGodotSourceGeneratorsDisabled(this GeneratorExecutionContext context)
+ => context.TryGetGlobalAnalyzerProperty("GodotSourceGenerators", out string? toggle) &&
+ toggle != null &&
+ toggle.Equals("disabled", StringComparison.OrdinalIgnoreCase);
+
private static bool InheritsFrom(this INamedTypeSymbol? symbol, string baseName)
{
if (symbol == null)
@@ -72,15 +78,11 @@ namespace Godot.SourceGenerators
public static bool IsPartial(this ClassDeclarationSyntax cds)
=> cds.Modifiers.Any(SyntaxKind.PartialKeyword);
- public static bool HasDisableGeneratorsAttribute(this INamedTypeSymbol symbol)
- => symbol.GetAttributes().Any(attr =>
- attr.AttributeClass?.ToString() == GodotClasses.DisableGodotGeneratorsAttr);
-
private static SymbolDisplayFormat FullyQualifiedFormatOmitGlobal { get; } =
SymbolDisplayFormat.FullyQualifiedFormat
.WithGlobalNamespaceStyle(SymbolDisplayGlobalNamespaceStyle.Omitted);
- public static string FullQualifiedName(this INamedTypeSymbol symbol)
+ public static string FullQualifiedName(this ITypeSymbol symbol)
=> symbol.ToDisplayString(NullableFlowState.NotNull, FullyQualifiedFormatOmitGlobal);
public static string FullQualifiedName(this INamespaceSymbol namespaceSymbol)
diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/Godot.SourceGenerators.props b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/Godot.SourceGenerators.props
index f9b47ad5b19..5025215d347 100644
--- a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/Godot.SourceGenerators.props
+++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/Godot.SourceGenerators.props
@@ -2,6 +2,6 @@
-
+
diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/GodotClasses.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/GodotClasses.cs
index 29e41d155a8..7cc8fa17fc6 100644
--- a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/GodotClasses.cs
+++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/GodotClasses.cs
@@ -3,7 +3,6 @@ namespace Godot.SourceGenerators
public static class GodotClasses
{
public const string Object = "Godot.Object";
- public const string DisableGodotGeneratorsAttr = "Godot.DisableGodotGeneratorsAttribute";
public const string AssemblyHasScriptsAttr = "Godot.AssemblyHasScriptsAttribute";
}
}
diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/MarshalType.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/MarshalType.cs
new file mode 100644
index 00000000000..7c8345d16ac
--- /dev/null
+++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/MarshalType.cs
@@ -0,0 +1,78 @@
+using System.Diagnostics.CodeAnalysis;
+
+namespace Godot.SourceGenerators
+{
+ [SuppressMessage("ReSharper", "InconsistentNaming")]
+ public enum MarshalType
+ {
+ Boolean,
+ Char,
+ SByte,
+ Int16,
+ Int32,
+ Int64,
+ Byte,
+ UInt16,
+ UInt32,
+ UInt64,
+ Single,
+ Double,
+ String,
+
+ // Godot structs
+ Vector2,
+ Vector2i,
+ Rect2,
+ Rect2i,
+ Transform2D,
+ Vector3,
+ Vector3i,
+ Basis,
+ Quaternion,
+ Transform3D,
+ AABB,
+ Color,
+ Plane,
+ Callable,
+ SignalInfo,
+
+ // Enums
+ Enum,
+
+ // Arrays
+ ByteArray,
+ Int32Array,
+ Int64Array,
+ SingleArray,
+ DoubleArray,
+ StringArray,
+ Vector2Array,
+ Vector3Array,
+ ColorArray,
+ GodotObjectOrDerivedArray,
+ SystemObjectArray,
+
+ // Generics
+ GodotGenericDictionary,
+ GodotGenericArray,
+ SystemGenericDictionary,
+ SystemGenericList,
+ GenericIDictionary,
+ GenericICollection,
+ GenericIEnumerable,
+
+ // Variant
+ SystemObject,
+
+ // Classes
+ GodotObjectOrDerived,
+ StringName,
+ NodePath,
+ RID,
+ GodotDictionary,
+ GodotArray,
+ IDictionary,
+ ICollection,
+ IEnumerable,
+ }
+}
diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/MarshalUtils.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/MarshalUtils.cs
new file mode 100644
index 00000000000..a77e1800fb5
--- /dev/null
+++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/MarshalUtils.cs
@@ -0,0 +1,224 @@
+using System;
+using Microsoft.CodeAnalysis;
+
+namespace Godot.SourceGenerators
+{
+ public static class MarshalUtils
+ {
+ public class TypeCache
+ {
+ public INamedTypeSymbol GodotObjectType { get; }
+ public INamedTypeSymbol GodotGenericDictionary { get; }
+ public INamedTypeSymbol GodotGenericArray { get; }
+ public INamedTypeSymbol IDictionary { get; }
+ public INamedTypeSymbol ICollection { get; }
+ public INamedTypeSymbol GenericIDictionary { get; }
+ public INamedTypeSymbol SystemGenericDictionary { get; }
+ public INamedTypeSymbol SystemGenericList { get; }
+
+ public TypeCache(GeneratorExecutionContext context)
+ {
+ INamedTypeSymbol GetTypeByMetadataNameOrThrow(string fullyQualifiedMetadataName)
+ {
+ return context.Compilation.GetTypeByMetadataName(fullyQualifiedMetadataName) ??
+ throw new InvalidOperationException("Type not found: " + fullyQualifiedMetadataName);
+ }
+
+ GodotObjectType = GetTypeByMetadataNameOrThrow("Godot.Object");
+ GodotGenericDictionary = GetTypeByMetadataNameOrThrow("Godot.Collections.Dictionary`2");
+ GodotGenericArray = GetTypeByMetadataNameOrThrow("Godot.Collections.Array`1");
+ IDictionary = GetTypeByMetadataNameOrThrow("System.Collections.IDictionary");
+ ICollection = GetTypeByMetadataNameOrThrow("System.Collections.ICollection");
+ GenericIDictionary = GetTypeByMetadataNameOrThrow("System.Collections.Generic.IDictionary`2");
+ SystemGenericDictionary = GetTypeByMetadataNameOrThrow("System.Collections.Generic.Dictionary`2");
+ SystemGenericList = GetTypeByMetadataNameOrThrow("System.Collections.Generic.List`1");
+ }
+ }
+
+ public static MarshalType? ConvertManagedTypeToVariantType(ITypeSymbol type, TypeCache typeCache)
+ {
+ var specialType = type.SpecialType;
+
+ switch (specialType)
+ {
+ case SpecialType.System_Boolean:
+ return MarshalType.Boolean;
+ case SpecialType.System_Char:
+ return MarshalType.Char;
+ case SpecialType.System_SByte:
+ return MarshalType.SByte;
+ case SpecialType.System_Int16:
+ return MarshalType.Int16;
+ case SpecialType.System_Int32:
+ return MarshalType.Int32;
+ case SpecialType.System_Int64:
+ return MarshalType.Int64;
+ case SpecialType.System_Byte:
+ return MarshalType.Byte;
+ case SpecialType.System_UInt16:
+ return MarshalType.UInt16;
+ case SpecialType.System_UInt32:
+ return MarshalType.UInt32;
+ case SpecialType.System_UInt64:
+ return MarshalType.UInt64;
+ case SpecialType.System_Single:
+ return MarshalType.Single;
+ case SpecialType.System_Double:
+ return MarshalType.Double;
+ case SpecialType.System_String:
+ return MarshalType.String;
+ case SpecialType.System_Object:
+ return MarshalType.SystemObject;
+ case SpecialType.System_ValueType:
+ {
+ if (type.ContainingAssembly.Name == "GodotSharp" &&
+ type.ContainingNamespace.Name == "Godot")
+ {
+ return type switch
+ {
+ { Name: "Vector2" } => MarshalType.Vector2,
+ { Name: "Vector2i" } => MarshalType.Vector2i,
+ { Name: "Rect2" } => MarshalType.Rect2,
+ { Name: "Rect2i" } => MarshalType.Rect2i,
+ { Name: "Transform2D" } => MarshalType.Transform2D,
+ { Name: "Vector3" } => MarshalType.Vector3,
+ { Name: "Vector3i" } => MarshalType.Vector3i,
+ { Name: "Basis" } => MarshalType.Basis,
+ { Name: "Quaternion" } => MarshalType.Quaternion,
+ { Name: "Transform3D" } => MarshalType.Transform3D,
+ { Name: "AABB" } => MarshalType.AABB,
+ { Name: "Color" } => MarshalType.Color,
+ { Name: "Plane" } => MarshalType.Plane,
+ { Name: "RID" } => MarshalType.RID,
+ { Name: "Callable" } => MarshalType.Callable,
+ { Name: "SignalInfo" } => MarshalType.SignalInfo,
+ { TypeKind: TypeKind.Enum } => MarshalType.Enum,
+ _ => null
+ };
+ }
+
+ return null;
+ }
+ default:
+ {
+ if (type.TypeKind == TypeKind.Array)
+ {
+ var arrayType = (IArrayTypeSymbol)type;
+ var elementType = arrayType.ElementType;
+
+ switch (elementType.SpecialType)
+ {
+ case SpecialType.System_Byte:
+ return MarshalType.ByteArray;
+ case SpecialType.System_Int32:
+ return MarshalType.Int32Array;
+ case SpecialType.System_Int64:
+ return MarshalType.Int64Array;
+ case SpecialType.System_Single:
+ return MarshalType.SingleArray;
+ case SpecialType.System_Double:
+ return MarshalType.DoubleArray;
+ case SpecialType.System_String:
+ return MarshalType.StringArray;
+ case SpecialType.System_Object:
+ return MarshalType.SystemObjectArray;
+ }
+
+ if (elementType.SimpleDerivesFrom(typeCache.GodotObjectType))
+ return MarshalType.GodotObjectOrDerivedArray;
+
+ if (type.ContainingAssembly.Name == "GodotSharp" &&
+ type.ContainingNamespace.Name == "Godot")
+ {
+ return elementType switch
+ {
+ { Name: "Vector2" } => MarshalType.Vector2Array,
+ { Name: "Vector3" } => MarshalType.Vector3Array,
+ { Name: "Color" } => MarshalType.ColorArray,
+ _ => null
+ };
+ }
+ }
+ else if (type is INamedTypeSymbol { IsGenericType: true } genericType)
+ {
+ var genericTypeDef = genericType.ConstructedFrom;
+
+ if (SymbolEqualityComparer.Default.Equals(genericTypeDef, typeCache.GodotGenericDictionary))
+ return MarshalType.GodotGenericDictionary;
+
+ if (SymbolEqualityComparer.Default.Equals(genericTypeDef, typeCache.GodotGenericArray))
+ return MarshalType.GodotGenericArray;
+
+ if (SymbolEqualityComparer.Default.Equals(genericTypeDef, typeCache.SystemGenericDictionary))
+ return MarshalType.SystemGenericDictionary;
+
+ if (SymbolEqualityComparer.Default.Equals(genericTypeDef, typeCache.SystemGenericList))
+ return MarshalType.SystemGenericList;
+
+ if (SymbolEqualityComparer.Default.Equals(genericTypeDef, typeCache.GenericIDictionary))
+ return MarshalType.GenericIDictionary;
+
+ return genericTypeDef.SpecialType switch
+ {
+ SpecialType.System_Collections_Generic_ICollection_T => MarshalType.GenericICollection,
+ SpecialType.System_Collections_Generic_IEnumerable_T => MarshalType.GenericIEnumerable,
+ _ => null
+ };
+ }
+ else
+ {
+ if (type.SimpleDerivesFrom(typeCache.GodotObjectType))
+ return MarshalType.GodotObjectOrDerived;
+
+ if (SymbolEqualityComparer.Default.Equals(type, typeCache.IDictionary))
+ return MarshalType.IDictionary;
+
+ if (SymbolEqualityComparer.Default.Equals(type, typeCache.ICollection))
+ return MarshalType.ICollection;
+
+ if (specialType == SpecialType.System_Collections_IEnumerable)
+ return MarshalType.IEnumerable;
+
+ if (type.ContainingAssembly.Name == "GodotSharp")
+ {
+ switch (type.ContainingNamespace.Name)
+ {
+ case "Godot":
+ return type switch
+ {
+ { Name: "StringName" } => MarshalType.StringName,
+ { Name: "NodePath" } => MarshalType.NodePath,
+ _ => null
+ };
+ case "Godot.Collections" when !(type is INamedTypeSymbol { IsGenericType: true }):
+ return type switch
+ {
+ { Name: "Dictionary" } => MarshalType.GodotDictionary,
+ { Name: "Array" } => MarshalType.GodotArray,
+ _ => null
+ };
+ }
+ }
+ }
+
+ break;
+ }
+ }
+
+ return null;
+ }
+
+ private static bool SimpleDerivesFrom(this ITypeSymbol? type, ITypeSymbol candidateBaseType)
+ {
+ while (type != null)
+ {
+ if (SymbolEqualityComparer.Default.Equals(type, candidateBaseType))
+ return true;
+
+ type = type.BaseType;
+ }
+
+ return false;
+ }
+ }
+}
diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptBoilerplateGenerator.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptBoilerplateGenerator.cs
new file mode 100644
index 00000000000..5ace809d959
--- /dev/null
+++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptBoilerplateGenerator.cs
@@ -0,0 +1,423 @@
+using System.Collections.Generic;
+using System.Collections.Immutable;
+using System.Linq;
+using System.Text;
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.CSharp.Syntax;
+using Microsoft.CodeAnalysis.Text;
+
+namespace Godot.SourceGenerators
+{
+ [Generator]
+ public class ScriptBoilerplateGenerator : ISourceGenerator
+ {
+ public void Execute(GeneratorExecutionContext context)
+ {
+ if (context.AreGodotSourceGeneratorsDisabled())
+ return;
+
+ // False positive for RS1024. We're already using `SymbolEqualityComparer.Default`...
+#pragma warning disable RS1024
+ INamedTypeSymbol[] godotClasses = context
+ .Compilation.SyntaxTrees
+ .SelectMany(tree =>
+ tree.GetRoot().DescendantNodes()
+ .OfType()
+ .SelectGodotScriptClasses(context.Compilation)
+ // Report and skip non-partial classes
+ .Where(x =>
+ {
+ if (x.cds.IsPartial())
+ return true;
+ Common.ReportNonPartialGodotScriptClass(context, x.cds, x.symbol);
+ return false;
+ })
+ .Select(x => x.symbol)
+ )
+ .Distinct(SymbolEqualityComparer.Default)
+ .ToArray();
+#pragma warning restore RS1024
+
+ if (godotClasses.Length > 0)
+ {
+ var typeCache = new MarshalUtils.TypeCache(context);
+
+ foreach (var godotClass in godotClasses)
+ {
+ VisitGodotScriptClass(context, typeCache, godotClass);
+ }
+ }
+ }
+
+ private static void VisitGodotScriptClass(
+ GeneratorExecutionContext context,
+ MarshalUtils.TypeCache typeCache,
+ INamedTypeSymbol symbol
+ )
+ {
+ string className = symbol.Name;
+
+ INamespaceSymbol namespaceSymbol = symbol.ContainingNamespace;
+ string classNs = namespaceSymbol != null && !namespaceSymbol.IsGlobalNamespace ?
+ namespaceSymbol.FullQualifiedName() :
+ string.Empty;
+ bool hasNamespace = classNs.Length != 0;
+
+ string uniqueName = hasNamespace ?
+ classNs + "." + className + "_ScriptBoilerplate_Generated" :
+ className + "_ScriptBoilerplate_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");
+ }
+
+ source.Append("partial class ");
+ source.Append(className);
+ source.Append("\n{\n");
+
+ var members = symbol.GetMembers();
+
+ // TODO: Static static marshaling (no reflection, no runtime type checks)
+
+ var methodSymbols = members
+ .Where(s => s.Kind == SymbolKind.Method)
+ .Cast()
+ .Where(m => m.MethodKind == MethodKind.Ordinary && !m.IsImplicitlyDeclared);
+
+ var propertySymbols = members
+ .Where(s => s.Kind == SymbolKind.Property)
+ .Cast();
+
+ var fieldSymbols = members
+ .Where(s => s.Kind == SymbolKind.Field)
+ .Cast()
+ .Where(p => !p.IsImplicitlyDeclared);
+
+ var methods = WhereHasCompatibleGodotType(methodSymbols, typeCache).ToArray();
+ var properties = WhereIsCompatibleGodotType(propertySymbols, typeCache).ToArray();
+ var fields = WhereIsCompatibleGodotType(fieldSymbols, typeCache).ToArray();
+
+ source.Append(" private class GodotInternal {\n");
+
+ // Generate cached StringNames for methods and properties, for fast lookup
+
+ foreach (var method in methods)
+ {
+ string methodName = method.Method.Name;
+ source.Append(" public static readonly StringName MethodName_");
+ source.Append(methodName);
+ source.Append(" = \"");
+ source.Append(methodName);
+ source.Append("\";\n");
+ }
+
+ foreach (var property in properties)
+ {
+ string propertyName = property.Property.Name;
+ source.Append(" public static readonly StringName PropName_");
+ source.Append(propertyName);
+ source.Append(" = \"");
+ source.Append(propertyName);
+ source.Append("\";\n");
+ }
+
+ foreach (var field in fields)
+ {
+ string fieldName = field.Field.Name;
+ source.Append(" public static readonly StringName PropName_");
+ source.Append(fieldName);
+ source.Append(" = \"");
+ source.Append(fieldName);
+ source.Append("\";\n");
+ }
+
+ source.Append(" }\n");
+
+ if (methods.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");
+
+ foreach (var method in methods)
+ {
+ GenerateMethodInvoker(method, source);
+ }
+
+ source.Append(" return base.InvokeGodotClassMethod(method, args, argCount, out ret);\n");
+
+ source.Append(" }\n");
+ }
+
+ if (properties.Length > 0 || fields.Length > 0)
+ {
+ // Setters
+
+ source.Append(" protected override bool SetGodotClassPropertyValue(in godot_string_name name, ");
+ source.Append("in godot_variant value)\n {\n");
+
+ foreach (var property in properties)
+ {
+ GeneratePropertySetter(property.Property.Name,
+ property.Property.Type.FullQualifiedName(), source);
+ }
+
+ foreach (var field in fields)
+ {
+ GeneratePropertySetter(field.Field.Name,
+ field.Field.Type.FullQualifiedName(), source);
+ }
+
+ source.Append(" return base.SetGodotClassPropertyValue(name, value);\n");
+
+ source.Append(" }\n");
+
+ // Getters
+
+ source.Append(" protected override bool GetGodotClassPropertyValue(in godot_string_name name, ");
+ source.Append("out godot_variant value)\n {\n");
+
+ foreach (var property in properties)
+ {
+ GeneratePropertyGetter(property.Property.Name, source);
+ }
+
+ foreach (var field in fields)
+ {
+ GeneratePropertyGetter(field.Field.Name, source);
+ }
+
+ source.Append(" return base.GetGodotClassPropertyValue(name, out value);\n");
+
+ source.Append(" }\n");
+ }
+
+ source.Append("}\n");
+
+ if (hasNamespace)
+ {
+ source.Append("\n}\n");
+ }
+
+ context.AddSource(uniqueName, SourceText.From(source.ToString(), Encoding.UTF8));
+ }
+
+ private static void GenerateMethodInvoker(
+ GodotMethodInfo method,
+ StringBuilder source
+ )
+ {
+ string methodName = method.Method.Name;
+
+ source.Append(" if (method == GodotInternal.MethodName_");
+ source.Append(methodName);
+ source.Append(" && argCount == ");
+ source.Append(method.ParamTypes.Length);
+ source.Append(") {\n");
+
+ if (method.RetType != null)
+ source.Append(" object retBoxed = ");
+ else
+ source.Append(" ");
+
+ source.Append(methodName);
+ source.Append("(");
+
+ for (int i = 0; i < method.ParamTypes.Length; i++)
+ {
+ if (i != 0)
+ source.Append(", ");
+
+ // TODO: static marshaling (no reflection, no runtime type checks)
+
+ string paramTypeQualifiedName = method.ParamTypeSymbols[i].FullQualifiedName();
+
+ source.Append("(");
+ source.Append(paramTypeQualifiedName);
+ source.Append(")Marshaling.ConvertVariantToManagedObjectOfType(args[");
+ source.Append(i);
+ source.Append("], typeof(");
+ source.Append(paramTypeQualifiedName);
+ source.Append("))");
+ }
+
+ source.Append(");\n");
+
+ if (method.RetType != null)
+ {
+ // TODO: static marshaling (no reflection, no runtime type checks)
+ source.Append(" ret = Marshaling.ConvertManagedObjectToVariant(retBoxed);\n");
+ source.Append(" return true;\n");
+ }
+ else
+ {
+ source.Append(" ret = default;\n");
+ source.Append(" return true;\n");
+ }
+
+ source.Append(" }\n");
+ }
+
+ private static void GeneratePropertySetter(
+ string propertyMemberName,
+ string propertyTypeQualifiedName,
+ StringBuilder source
+ )
+ {
+ source.Append(" if (name == GodotInternal.PropName_");
+ source.Append(propertyMemberName);
+ source.Append(") {\n");
+
+ source.Append(" ");
+ source.Append(propertyMemberName);
+ source.Append(" = ");
+
+ // TODO: static marshaling (no reflection, no runtime type checks)
+
+ source.Append("(");
+ source.Append(propertyTypeQualifiedName);
+ source.Append(")Marshaling.ConvertVariantToManagedObjectOfType(value, typeof(");
+ source.Append(propertyTypeQualifiedName);
+ source.Append("));\n");
+
+ source.Append(" return true;\n");
+
+ source.Append(" }\n");
+ }
+
+ private static void GeneratePropertyGetter(
+ string propertyMemberName,
+ StringBuilder source
+ )
+ {
+ source.Append(" if (name == GodotInternal.PropName_");
+ source.Append(propertyMemberName);
+ source.Append(") {\n");
+
+ // TODO: static marshaling (no reflection, no runtime type checks)
+
+ source.Append(" value = Marshaling.ConvertManagedObjectToVariant(");
+ source.Append(propertyMemberName);
+ source.Append(");\n");
+ source.Append(" return true;\n");
+
+ source.Append(" }\n");
+ }
+
+ public void Initialize(GeneratorInitializationContext context)
+ {
+ }
+
+ private struct GodotMethodInfo
+ {
+ public GodotMethodInfo(IMethodSymbol method, ImmutableArray paramTypes,
+ ImmutableArray paramTypeSymbols, MarshalType? retType)
+ {
+ Method = method;
+ ParamTypes = paramTypes;
+ ParamTypeSymbols = paramTypeSymbols;
+ RetType = retType;
+ }
+
+ public IMethodSymbol Method { get; }
+ public ImmutableArray ParamTypes { get; }
+ public ImmutableArray ParamTypeSymbols { get; }
+ public MarshalType? RetType { get; }
+ }
+
+ private struct GodotPropertyInfo
+ {
+ public GodotPropertyInfo(IPropertySymbol property, MarshalType type)
+ {
+ Property = property;
+ Type = type;
+ }
+
+ public IPropertySymbol Property { get; }
+ public MarshalType Type { get; }
+ }
+
+ private struct GodotFieldInfo
+ {
+ public GodotFieldInfo(IFieldSymbol field, MarshalType type)
+ {
+ Field = field;
+ Type = type;
+ }
+
+ public IFieldSymbol Field { get; }
+ public MarshalType Type { get; }
+ }
+
+ private static IEnumerable WhereHasCompatibleGodotType(
+ IEnumerable methods,
+ MarshalUtils.TypeCache typeCache
+ )
+ {
+ foreach (var method in methods)
+ {
+ if (method.IsGenericMethod)
+ continue;
+
+ var retType = method.ReturnsVoid ?
+ null :
+ MarshalUtils.ConvertManagedTypeToVariantType(method.ReturnType, typeCache);
+
+ if (retType == null && !method.ReturnsVoid)
+ continue;
+
+ var parameters = method.Parameters;
+
+ var paramTypes = parameters.Select(p =>
+ MarshalUtils.ConvertManagedTypeToVariantType(p.Type, typeCache))
+ .Where(t => t != null).Cast().ToImmutableArray();
+
+ if (parameters.Length > paramTypes.Length)
+ continue; // Some param types weren't compatible
+
+ yield return new GodotMethodInfo(method, paramTypes, parameters
+ .Select(p => p.Type).ToImmutableArray(), retType);
+ }
+ }
+
+ private static IEnumerable WhereIsCompatibleGodotType(
+ IEnumerable properties,
+ MarshalUtils.TypeCache typeCache
+ )
+ {
+ foreach (var property in properties)
+ {
+ var marshalType = MarshalUtils.ConvertManagedTypeToVariantType(property.Type, typeCache);
+
+ if (marshalType == null)
+ continue;
+
+ yield return new GodotPropertyInfo(property, marshalType.Value);
+ }
+ }
+
+ private static IEnumerable WhereIsCompatibleGodotType(
+ IEnumerable fields,
+ MarshalUtils.TypeCache typeCache
+ )
+ {
+ foreach (var field in fields)
+ {
+ var marshalType = MarshalUtils.ConvertManagedTypeToVariantType(field.Type, typeCache);
+
+ if (marshalType == null)
+ continue;
+
+ yield return new GodotFieldInfo(field, marshalType.Value);
+ }
+ }
+ }
+}
diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptPathAttributeGenerator.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptPathAttributeGenerator.cs
index fa65595290b..f0b27583424 100644
--- a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptPathAttributeGenerator.cs
+++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptPathAttributeGenerator.cs
@@ -14,13 +14,10 @@ namespace Godot.SourceGenerators
{
public void Execute(GeneratorExecutionContext context)
{
- if (context.TryGetGlobalAnalyzerProperty("GodotScriptPathAttributeGenerator", out string? toggle)
- && toggle == "disabled")
- {
+ if (context.AreGodotSourceGeneratorsDisabled())
return;
- }
- // NOTE: IsNullOrEmpty doesn't work well with nullable checks
+ // NOTE: NotNullWhen diagnostics don't work on projects targeting .NET Standard 2.0
// ReSharper disable once ReplaceWithStringIsNullOrEmpty
if (!context.TryGetGlobalAnalyzerProperty("GodotProjectDir", out string? godotProjectDir)
|| godotProjectDir!.Length == 0)
@@ -28,7 +25,8 @@ namespace Godot.SourceGenerators
throw new InvalidOperationException("Property 'GodotProjectDir' is null or empty.");
}
- var godotClasses = context.Compilation.SyntaxTrees
+ Dictionary> godotClasses = context
+ .Compilation.SyntaxTrees
.SelectMany(tree =>
tree.GetRoot().DescendantNodes()
.OfType()
@@ -38,7 +36,7 @@ namespace Godot.SourceGenerators
// Report and skip non-partial classes
.Where(x =>
{
- if (x.cds.IsPartial() || x.symbol.HasDisableGeneratorsAttribute())
+ if (x.cds.IsPartial())
return true;
Common.ReportNonPartialGodotScriptClass(context, x.cds, x.symbol);
return false;
diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptRegistrarGenerator.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptRegistrarGenerator.cs
new file mode 100644
index 00000000000..ec04a319e21
--- /dev/null
+++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptRegistrarGenerator.cs
@@ -0,0 +1,19 @@
+using Microsoft.CodeAnalysis;
+
+namespace Godot.SourceGenerators
+{
+ // Placeholder. Once we switch to native extensions this will act as the registrar for all
+ // user Godot classes in the assembly. Think of it as something similar to `register_types`.
+ public class ScriptRegistrarGenerator : ISourceGenerator
+ {
+ public void Initialize(GeneratorInitializationContext context)
+ {
+ throw new System.NotImplementedException();
+ }
+
+ public void Execute(GeneratorExecutionContext context)
+ {
+ throw new System.NotImplementedException();
+ }
+ }
+}
diff --git a/modules/mono/editor/bindings_generator.cpp b/modules/mono/editor/bindings_generator.cpp
index cce00a6ae13..89265d8f2b7 100644
--- a/modules/mono/editor/bindings_generator.cpp
+++ b/modules/mono/editor/bindings_generator.cpp
@@ -81,9 +81,12 @@ StringBuilder &operator<<(StringBuilder &r_sb, const char *p_cstring) {
#define CS_STATIC_METHOD_GETINSTANCE "GetPtr"
#define CS_METHOD_CALL "Call"
#define CS_PROPERTY_SINGLETON "Singleton"
+#define CS_METHOD_INVOKE_GODOT_CLASS_METHOD "InvokeGodotClassMethod"
#define CS_STATIC_FIELD_NATIVE_CTOR "NativeCtor"
#define CS_STATIC_FIELD_METHOD_BIND_PREFIX "MethodBind"
+#define CS_STATIC_FIELD_METHOD_NAME_PREFIX "MethodName_"
+#define CS_STATIC_FIELD_METHOD_PROXY_NAME_PREFIX "MethodProxyName_"
#define CS_STATIC_FIELD_SIGNAL_NAME_PREFIX "SignalName_"
#define ICALL_PREFIX "godot_icall_"
@@ -1604,11 +1607,27 @@ Error BindingsGenerator::_generate_cs_type(const TypeInterface &itype, const Str
// Script calls
if (!itype.is_singleton && (is_derived_type || itype.has_virtual_methods)) {
- // TODO: string is ok for now. But should be replaced with StringName in the future for performance.
+ for (const MethodInterface &imethod : itype.methods) {
+ if (!imethod.is_virtual) {
+ continue;
+ }
- output << MEMBER_BEGIN "internal " << (is_derived_type ? "override" : "virtual")
- << " bool InternalGodotScriptCall(string method, NativeVariantPtrArgs args, "
- << "int argCount, out godot_variant ret)\n"
+ output << MEMBER_BEGIN "// ReSharper disable once InconsistentNaming\n"
+ << INDENT2 "[DebuggerBrowsable(DebuggerBrowsableState.Never)]\n"
+ << INDENT2 "private static readonly StringName "
+ << CS_STATIC_FIELD_METHOD_NAME_PREFIX << imethod.name
+ << " = \"" << imethod.name << "\";\n";
+
+ output << MEMBER_BEGIN "// ReSharper disable once InconsistentNaming\n"
+ << INDENT2 "[DebuggerBrowsable(DebuggerBrowsableState.Never)]\n"
+ << INDENT2 "private static readonly StringName "
+ << CS_STATIC_FIELD_METHOD_PROXY_NAME_PREFIX << imethod.name
+ << " = \"" << imethod.proxy_name << "\";\n";
+ }
+
+ output << MEMBER_BEGIN "protected internal " << (is_derived_type ? "override" : "virtual")
+ << " bool " CS_METHOD_INVOKE_GODOT_CLASS_METHOD "(in godot_string_name method, "
+ << "NativeVariantPtrArgs args, int argCount, out godot_variant ret)\n"
<< INDENT2 "{\n";
for (const MethodInterface &imethod : itype.methods) {
@@ -1616,11 +1635,9 @@ Error BindingsGenerator::_generate_cs_type(const TypeInterface &itype, const Str
continue;
}
- // TODO:
- // Compare with cached StringName. We already have a cached StringName
- // field for the proxy name. We need one for the original snake_case name.
- output << INDENT3 "if ((method == nameof(" << imethod.proxy_name << ") || method == \"" << imethod.name
- << "\") && argCount == " << itos(imethod.arguments.size()) << ")\n"
+ output << INDENT3 "if ((method == " << CS_STATIC_FIELD_METHOD_PROXY_NAME_PREFIX << imethod.name
+ << " || method == " << CS_STATIC_FIELD_METHOD_NAME_PREFIX << imethod.name
+ << ") && argCount == " << itos(imethod.arguments.size()) << ")\n"
<< INDENT3 "{\n";
if (imethod.return_type.cname != name_cache.type_void) {
@@ -1668,9 +1685,10 @@ Error BindingsGenerator::_generate_cs_type(const TypeInterface &itype, const Str
}
if (is_derived_type) {
- output << INDENT3 "return base.InternalGodotScriptCall(method, args, argCount, out ret);\n";
+ output << INDENT3 "return base." CS_METHOD_INVOKE_GODOT_CLASS_METHOD "(method, args, argCount, out ret);\n";
} else {
- output << INDENT3 "return InternalGodotScriptCallViaReflection(method, args, argCount, out ret);\n";
+ output << INDENT3 "ret = default;\n"
+ << INDENT3 "return false;\n";
}
output << INDENT2 "}\n";
@@ -1989,8 +2007,8 @@ Error BindingsGenerator::_generate_cs_method(const BindingsGenerator::TypeInterf
// Generate method
{
if (!p_imethod.is_virtual && !p_imethod.requires_object_call) {
- p_output << MEMBER_BEGIN "[DebuggerBrowsable(DebuggerBrowsableState.Never)]" MEMBER_BEGIN "private static readonly IntPtr "
- << method_bind_field << " = ";
+ p_output << MEMBER_BEGIN "[DebuggerBrowsable(DebuggerBrowsableState.Never)]\n"
+ << INDENT2 "private static readonly IntPtr " << method_bind_field << " = ";
if (p_itype.is_singleton) {
// Singletons are static classes. They don't derive Godot.Object,
@@ -2024,16 +2042,6 @@ Error BindingsGenerator::_generate_cs_method(const BindingsGenerator::TypeInterf
p_output.append(default_args_doc.as_string());
}
- if (!p_imethod.is_internal) {
- // TODO: This alone adds ~0.2 MB of bloat to the core API assembly. It would be
- // better to generate a table in the C++ glue instead. That way the strings wouldn't
- // add that much extra bloat as they're already used in engine code. Also, it would
- // probably be much faster than looking up the attributes when fetching methods.
- p_output.append(MEMBER_BEGIN "[GodotMethod(\"");
- p_output.append(p_imethod.name);
- p_output.append("\")]");
- }
-
if (p_imethod.is_deprecated) {
if (p_imethod.deprecation_message.is_empty()) {
WARN_PRINT("An empty deprecation message is discouraged. Method: '" + p_imethod.proxy_name + "'.");
@@ -2196,8 +2204,9 @@ Error BindingsGenerator::_generate_cs_signal(const BindingsGenerator::TypeInterf
// If so, we could store the pointer we get from `data_unique_pointer()` instead of allocating StringName here.
// Cached signal name (StringName)
- p_output.append(MEMBER_BEGIN "[DebuggerBrowsable(DebuggerBrowsableState.Never)]" MEMBER_BEGIN
- "private static readonly StringName " CS_STATIC_FIELD_SIGNAL_NAME_PREFIX);
+ p_output.append(MEMBER_BEGIN "// ReSharper disable once InconsistentNaming\n");
+ p_output.append(INDENT2 "[DebuggerBrowsable(DebuggerBrowsableState.Never)]" MEMBER_BEGIN
+ "private static readonly StringName " CS_STATIC_FIELD_SIGNAL_NAME_PREFIX);
p_output.append(p_isignal.name);
p_output.append(" = \"");
p_output.append(p_isignal.name);
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/DisableGodotGeneratorsAttribute.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/DisableGodotGeneratorsAttribute.cs
deleted file mode 100644
index 0b00878e8cd..00000000000
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/DisableGodotGeneratorsAttribute.cs
+++ /dev/null
@@ -1,10 +0,0 @@
-using System;
-
-namespace Godot
-{
- ///
- /// An attribute that disables Godot Generators.
- ///
- [AttributeUsage(AttributeTargets.Class)]
- public class DisableGodotGeneratorsAttribute : Attribute { }
-}
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/CSharpInstanceBridge.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/CSharpInstanceBridge.cs
index 6bb1ba27e93..f28d7b1c51e 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/CSharpInstanceBridge.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/CSharpInstanceBridge.cs
@@ -12,7 +12,6 @@ namespace Godot.Bridge
{
try
{
- // Performance is not critical here as this will be replaced with source generators.
var godotObject = (Object)GCHandle.FromIntPtr(godotObjectGCHandle).Target;
if (godotObject == null)
@@ -27,7 +26,7 @@ namespace Godot.Bridge
using (dest)
methodStr = Marshaling.ConvertStringToManaged(dest);
- bool methodInvoked = godotObject.InternalGodotScriptCall(methodStr, new NativeVariantPtrArgs(args),
+ bool methodInvoked = godotObject.InvokeGodotClassMethod(CustomUnsafe.AsRef(method), new NativeVariantPtrArgs(args),
argCount, out godot_variant retValue);
if (!methodInvoked)
@@ -55,21 +54,19 @@ namespace Godot.Bridge
{
try
{
- // Performance is not critical here as this will be replaced with source generators.
var godotObject = (Object)GCHandle.FromIntPtr(godotObjectGCHandle).Target;
if (godotObject == null)
throw new InvalidOperationException();
- var nameManaged = StringName.CreateTakingOwnershipOfDisposableValue(
- NativeFuncs.godotsharp_string_name_new_copy(CustomUnsafe.AsRef(name)));
-
- if (godotObject.InternalGodotScriptSetFieldOrPropViaReflection(
- nameManaged.ToString(), CustomUnsafe.AsRef(value)))
+ if (godotObject.SetGodotClassPropertyValue(CustomUnsafe.AsRef(name), CustomUnsafe.AsRef(value)))
{
return true.ToGodotBool();
}
+ var nameManaged = StringName.CreateTakingOwnershipOfDisposableValue(
+ NativeFuncs.godotsharp_string_name_new_copy(CustomUnsafe.AsRef(name)));
+
object valueManaged = Marshaling.ConvertVariantToManagedObject(CustomUnsafe.AsRef(value));
return godotObject._Set(nameManaged, valueManaged).ToGodotBool();
@@ -87,22 +84,20 @@ namespace Godot.Bridge
{
try
{
- // Performance is not critical here as this will be replaced with source generators.
var godotObject = (Object)GCHandle.FromIntPtr(godotObjectGCHandle).Target;
if (godotObject == null)
throw new InvalidOperationException();
- var nameManaged = StringName.CreateTakingOwnershipOfDisposableValue(
- NativeFuncs.godotsharp_string_name_new_copy(CustomUnsafe.AsRef(name)));
-
- if (godotObject.InternalGodotScriptGetFieldOrPropViaReflection(nameManaged.ToString(),
- out godot_variant outRetValue))
+ if (godotObject.GetGodotClassPropertyValue(CustomUnsafe.AsRef(name), out godot_variant outRetValue))
{
*outRet = outRetValue;
return true.ToGodotBool();
}
+ var nameManaged = StringName.CreateTakingOwnershipOfDisposableValue(
+ NativeFuncs.godotsharp_string_name_new_copy(CustomUnsafe.AsRef(name)));
+
object ret = godotObject._Get(nameManaged);
if (ret == null)
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/InteropStructs.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/InteropStructs.cs
index ef20819d62f..f15dc941c96 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/InteropStructs.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/InteropStructs.cs
@@ -487,6 +487,31 @@ namespace Godot.NativeInterop
get => _data == IntPtr.Zero;
}
+ public static bool operator ==(godot_string_name left, godot_string_name right)
+ {
+ return left._data == right._data;
+ }
+
+ public static bool operator !=(godot_string_name left, godot_string_name right)
+ {
+ return !(left == right);
+ }
+
+ public bool Equals(godot_string_name other)
+ {
+ return _data == other._data;
+ }
+
+ public override bool Equals(object obj)
+ {
+ return obj is StringName s && s.Equals(this);
+ }
+
+ public override int GetHashCode()
+ {
+ return _data.GetHashCode();
+ }
+
[StructLayout(LayoutKind.Sequential)]
// ReSharper disable once InconsistentNaming
internal struct movable
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/NativeFuncs.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/NativeFuncs.cs
index f1cccfed0a7..c2812b89199 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/NativeFuncs.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/NativeFuncs.cs
@@ -26,7 +26,7 @@ namespace Godot.NativeInterop
[DllImport(GodotDllName)]
public static extern IntPtr godotsharp_method_bind_get_method(in godot_string_name p_classname,
- char* p_methodname);
+ in godot_string_name p_methodname);
[DllImport(GodotDllName)]
public static extern delegate* unmanaged godotsharp_get_class_constructor(
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Object.base.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Object.base.cs
index 5f4dc50c729..dbffd1d5d1f 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Object.base.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Object.base.cs
@@ -185,125 +185,20 @@ namespace Godot
return assemblyName.Name == "GodotSharp" || assemblyName.Name == "GodotSharpEditor";
}
- internal bool InternalGodotScriptCallViaReflection(string method, NativeVariantPtrArgs args, int argCount,
- out godot_variant ret)
+ // ReSharper disable once VirtualMemberNeverOverridden.Global
+ protected internal virtual bool SetGodotClassPropertyValue(in godot_string_name name, in godot_variant value)
{
- // 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)
- {
- var methodInfo = top.GetMethod(method,
- BindingFlags.DeclaredOnly | BindingFlags.Instance |
- BindingFlags.NonPublic | BindingFlags.Public);
-
- if (methodInfo != null)
- {
- var parameters = methodInfo.GetParameters();
- int paramCount = parameters.Length;
-
- if (argCount == paramCount)
- {
- object[] invokeParams = new object[paramCount];
-
- for (int i = 0; i < paramCount; i++)
- {
- invokeParams[i] = Marshaling.ConvertVariantToManagedObjectOfType(
- args[i], parameters[i].ParameterType);
- }
-
- object retObj = methodInfo.Invoke(this, invokeParams);
-
- ret = Marshaling.ConvertManagedObjectToVariant(retObj);
- return true;
- }
- }
-
- top = top.BaseType;
- }
-
- ret = default;
return false;
}
- internal bool InternalGodotScriptSetFieldOrPropViaReflection(string name, in godot_variant value)
+ // ReSharper disable once VirtualMemberNeverOverridden.Global
+ protected internal virtual bool GetGodotClassPropertyValue(in godot_string_name name, out godot_variant value)
{
- // 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)
- {
- var fieldInfo = top.GetField(name,
- BindingFlags.DeclaredOnly | BindingFlags.Instance |
- BindingFlags.NonPublic | BindingFlags.Public);
-
- if (fieldInfo != null)
- {
- object valueManaged = Marshaling.ConvertVariantToManagedObjectOfType(value, fieldInfo.FieldType);
- fieldInfo.SetValue(this, valueManaged);
-
- return true;
- }
-
- var propertyInfo = top.GetProperty(name,
- BindingFlags.DeclaredOnly | BindingFlags.Instance |
- BindingFlags.NonPublic | BindingFlags.Public);
-
- if (propertyInfo != null)
- {
- object valueManaged =
- Marshaling.ConvertVariantToManagedObjectOfType(value, propertyInfo.PropertyType);
- propertyInfo.SetValue(this, valueManaged);
-
- return true;
- }
-
- top = top.BaseType;
- }
-
- return false;
- }
-
- internal bool InternalGodotScriptGetFieldOrPropViaReflection(string name, out godot_variant value)
- {
- // 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)
- {
- var fieldInfo = top.GetField(name,
- BindingFlags.DeclaredOnly | BindingFlags.Instance |
- BindingFlags.NonPublic | BindingFlags.Public);
-
- if (fieldInfo != null)
- {
- object valueManaged = fieldInfo.GetValue(this);
- value = Marshaling.ConvertManagedObjectToVariant(valueManaged);
- return true;
- }
-
- var propertyInfo = top.GetProperty(name,
- BindingFlags.DeclaredOnly | BindingFlags.Instance |
- BindingFlags.NonPublic | BindingFlags.Public);
-
- if (propertyInfo != null)
- {
- object valueManaged = propertyInfo.GetValue(this);
- value = Marshaling.ConvertManagedObjectToVariant(valueManaged);
- return true;
- }
-
- top = top.BaseType;
- }
-
value = default;
return false;
}
- internal unsafe void InternalRaiseEventSignal(in godot_string_name eventSignalName, NativeVariantPtrArgs args,
+ internal void InternalRaiseEventSignal(in godot_string_name eventSignalName, NativeVariantPtrArgs args,
int argc)
{
// Performance is not critical here as this will be replaced with source generators.
@@ -371,14 +266,11 @@ namespace Godot
}
}
- internal static unsafe IntPtr ClassDB_get_method(StringName type, string method)
+ internal static IntPtr ClassDB_get_method(StringName type, StringName method)
{
- IntPtr methodBind;
- fixed (char* methodChars = method)
- {
- var typeSelf = (godot_string_name)type.NativeValue;
- methodBind = NativeFuncs.godotsharp_method_bind_get_method(typeSelf, methodChars);
- }
+ var typeSelf = (godot_string_name)type.NativeValue;
+ var methodSelf = (godot_string_name)method.NativeValue;
+ IntPtr methodBind = NativeFuncs.godotsharp_method_bind_get_method(typeSelf, methodSelf);
if (methodBind == IntPtr.Zero)
throw new NativeMethodBindNotFoundException(type + "." + method);
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/StringName.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/StringName.cs
index e8bda9b219e..3a415d3deb3 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/StringName.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/StringName.cs
@@ -92,5 +92,51 @@ namespace Godot
///
/// If the is empty.
public bool IsEmpty => NativeValue.DangerousSelfRef.IsEmpty;
+
+ public static bool operator ==(StringName left, StringName right)
+ {
+ if (left is null)
+ return right is null;
+ return left.Equals(right);
+ }
+
+ public static bool operator !=(StringName left, StringName right)
+ {
+ return !(left == right);
+ }
+
+ public bool Equals(StringName other)
+ {
+ if (other is null)
+ return false;
+ return NativeValue.DangerousSelfRef == other.NativeValue.DangerousSelfRef;
+ }
+
+ public static bool operator ==(StringName left, in godot_string_name right)
+ {
+ if (left is null)
+ return right.IsEmpty;
+ return left.Equals(right);
+ }
+
+ public static bool operator !=(StringName left, in godot_string_name right)
+ {
+ return !(left == right);
+ }
+
+ public static bool operator ==(in godot_string_name left, StringName right)
+ {
+ return right == left;
+ }
+
+ public static bool operator !=(in godot_string_name left, StringName right)
+ {
+ return !(right == left);
+ }
+
+ public bool Equals(in godot_string_name other)
+ {
+ return NativeValue.DangerousSelfRef == other;
+ }
}
}
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/GodotSharp.csproj b/modules/mono/glue/GodotSharp/GodotSharp/GodotSharp.csproj
index a06b448136e..d5bbbfb7ca7 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/GodotSharp.csproj
+++ b/modules/mono/glue/GodotSharp/GodotSharp/GodotSharp.csproj
@@ -32,7 +32,6 @@
-
diff --git a/modules/mono/glue/runtime_interop.cpp b/modules/mono/glue/runtime_interop.cpp
index c5db869a05f..c3d4f53048d 100644
--- a/modules/mono/glue/runtime_interop.cpp
+++ b/modules/mono/glue/runtime_interop.cpp
@@ -65,8 +65,8 @@ static_assert(sizeof(SafeRefCount) == sizeof(uint32_t));
typedef Object *(*godotsharp_class_creation_func)();
-GD_PINVOKE_EXPORT MethodBind *godotsharp_method_bind_get_method(const StringName *p_classname, const char16_t *p_methodname) {
- return ClassDB::get_method(*p_classname, StringName(String::utf16(p_methodname)));
+GD_PINVOKE_EXPORT MethodBind *godotsharp_method_bind_get_method(const StringName *p_classname, const StringName *p_methodname) {
+ return ClassDB::get_method(*p_classname, *p_methodname);
}
GD_PINVOKE_EXPORT godotsharp_class_creation_func godotsharp_get_class_constructor(const StringName *p_classname) {