Browse Source

Allow readonly and writeonly C# properties to be accessed from GDScript

William Scalf 2 years ago
parent
commit
41cf94e8b6

+ 10 - 0
modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Sample/OneWayProperties/AllReadOnly.cs

@@ -0,0 +1,10 @@
+namespace Godot.SourceGenerators.Sample
+{
+    public partial class AllReadOnly : GodotObject
+    {
+        public readonly string readonly_field = "foo";
+        public string readonly_auto_property { get; } = "foo";
+        public string readonly_property { get => "foo"; }
+        public string initonly_auto_property { get; init; }
+    }
+}

+ 10 - 0
modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Sample/OneWayProperties/AllWriteOnly.cs

@@ -0,0 +1,10 @@
+using System;
+
+namespace Godot.SourceGenerators.Sample
+{
+    public partial class AllWriteOnly : GodotObject
+    {
+        bool writeonly_backing_field = false;
+        public bool writeonly_property { set => writeonly_backing_field = value; }
+    }
+}

+ 13 - 0
modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Sample/OneWayProperties/MixedReadOnlyWriteOnly.cs

@@ -0,0 +1,13 @@
+namespace Godot.SourceGenerators.Sample
+{
+    public partial class MixedReadonlyWriteOnly : GodotObject
+    {
+        public readonly string readonly_field = "foo";
+        public string readonly_auto_property { get; } = "foo";
+        public string readonly_property { get => "foo"; }
+        public string initonly_auto_property { get; init; }
+
+        bool writeonly_backing_field = false;
+        public bool writeonly_property { set => writeonly_backing_field = value; }
+    }
+}

+ 0 - 9
modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ExtensionMethods.cs

@@ -303,11 +303,6 @@ namespace Godot.SourceGenerators
         {
             foreach (var property in properties)
             {
-                // TODO: We should still restore read-only properties after reloading assembly. Two possible ways: reflection or turn RestoreGodotObjectData into a constructor overload.
-                // Ignore properties without a getter, without a setter or with an init-only setter. Godot properties must be both readable and writable.
-                if (property.IsWriteOnly || property.IsReadOnly || property.SetMethod!.IsInitOnly)
-                    continue;
-
                 var marshalType = MarshalUtils.ConvertManagedTypeToMarshalType(property.Type, typeCache);
 
                 if (marshalType == null)
@@ -325,10 +320,6 @@ namespace Godot.SourceGenerators
             foreach (var field in fields)
             {
                 // TODO: We should still restore read-only fields after reloading assembly. Two possible ways: reflection or turn RestoreGodotObjectData into a constructor overload.
-                // Ignore properties without a getter or without a setter. Godot properties must be both readable and writable.
-                if (field.IsReadOnly)
-                    continue;
-
                 var marshalType = MarshalUtils.ConvertManagedTypeToMarshalType(field.Type, typeCache);
 
                 if (marshalType == null)

+ 25 - 19
modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptPropertiesGenerator.cs

@@ -212,31 +212,37 @@ namespace Godot.SourceGenerators
                 }
 
                 // Generate GetGodotClassPropertyValue
+                bool allPropertiesAreWriteOnly = godotClassFields.Length == 0 && godotClassProperties.All(pi => pi.PropertySymbol.IsWriteOnly);
 
-                source.Append("    /// <inheritdoc/>\n");
-                source.Append("    [global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)]\n");
-                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)
+                if (!allPropertiesAreWriteOnly)
                 {
-                    GeneratePropertyGetter(property.PropertySymbol.Name,
-                        property.PropertySymbol.Type, property.Type, source, isFirstEntry);
-                    isFirstEntry = false;
-                }
+                    source.Append("    /// <inheritdoc/>\n");
+                    source.Append("    [global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)]\n");
+                    source.Append("    protected override bool GetGodotClassPropertyValue(in godot_string_name name, ");
+                    source.Append("out godot_variant value)\n    {\n");
 
-                foreach (var field in godotClassFields)
-                {
-                    GeneratePropertyGetter(field.FieldSymbol.Name,
-                        field.FieldSymbol.Type, field.Type, source, isFirstEntry);
-                    isFirstEntry = false;
-                }
+                    isFirstEntry = true;
+                    foreach (var property in godotClassProperties)
+                    {
+                        if (property.PropertySymbol.IsWriteOnly)
+                            continue;
 
-                source.Append("        return base.GetGodotClassPropertyValue(name, out value);\n");
+                        GeneratePropertyGetter(property.PropertySymbol.Name,
+                            property.PropertySymbol.Type, property.Type, source, isFirstEntry);
+                        isFirstEntry = false;
+                    }
 
-                source.Append("    }\n");
+                    foreach (var field in godotClassFields)
+                    {
+                        GeneratePropertyGetter(field.FieldSymbol.Name,
+                            field.FieldSymbol.Type, field.Type, source, isFirstEntry);
+                        isFirstEntry = false;
+                    }
+
+                    source.Append("        return base.GetGodotClassPropertyValue(name, out value);\n");
 
+                    source.Append("    }\n");
+                }
                 // Generate GetGodotPropertyList
 
                 const string dictionaryType = "global::System.Collections.Generic.List<global::Godot.Bridge.PropertyInfo>";

+ 8 - 2
modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptSerializationGenerator.cs

@@ -119,8 +119,14 @@ namespace Godot.SourceGenerators
                 .Where(s => !s.IsStatic && s.Kind == SymbolKind.Field && !s.IsImplicitlyDeclared)
                 .Cast<IFieldSymbol>();
 
-            var godotClassProperties = propertySymbols.WhereIsGodotCompatibleType(typeCache).ToArray();
-            var godotClassFields = fieldSymbols.WhereIsGodotCompatibleType(typeCache).ToArray();
+            // TODO: We should still restore read-only properties after reloading assembly. Two possible ways: reflection or turn RestoreGodotObjectData into a constructor overload.
+            // Ignore properties without a getter, without a setter or with an init-only setter. Godot properties must be both readable and writable.
+            var godotClassProperties = propertySymbols.Where(property => !(property.IsReadOnly || property.IsWriteOnly || property.SetMethod!.IsInitOnly))
+                .WhereIsGodotCompatibleType(typeCache)
+                .ToArray();
+            var godotClassFields = fieldSymbols.Where(property => !property.IsReadOnly)
+                .WhereIsGodotCompatibleType(typeCache)
+                .ToArray();
 
             var signalDelegateSymbols = members
                 .Where(s => s.Kind == SymbolKind.NamedType)