]> git.ipfire.org Git - thirdparty/vala.git/commitdiff
Add support for 'opaque' compact classes
authorSimon Werbeck <simon.werbeck@gmail.com>
Thu, 19 Mar 2020 14:20:39 +0000 (15:20 +0100)
committerRico Tzschichholz <ricotz@ubuntu.com>
Tue, 19 Jan 2021 14:27:57 +0000 (15:27 +0100)
This change intruduces a new attribute switch [Compact (opaque = true)]
which allows to completely hide the implementation of a compact class.
This is especially useful for libraries when maintaining a stable abi.

An 'opaque' compact class exposes no struct definition in the generated
c header, only a typedef is provided. As such, certain requirements
apply for members of such classes:
- Access to instance fields must be either private or internal.
- No abstract/virtual methods or properties are allowed.

Fixes https://gitlab.gnome.org/GNOME/vala/issues/1129

12 files changed:
codegen/valaccodebasemodule.vala
codegen/valagtypemodule.vala
tests/Makefile.am
tests/semantic/class-opaque-abstract-method.test [new file with mode: 0644]
tests/semantic/class-opaque-abstract-property.test [new file with mode: 0644]
tests/semantic/class-opaque-automatic-property.vala [new file with mode: 0644]
tests/semantic/class-opaque-public-field.test [new file with mode: 0644]
tests/semantic/class-opaque.vala [new file with mode: 0644]
vala/valaclass.vala
vala/valamethod.vala
vala/valaproperty.vala
vala/valausedattr.vala

index 0be29ed997f27a83b4a0a794aa83eabb361c60e1..d2740766719f88df6c8bf4ba0f7db5b20f93e68b 100644 (file)
@@ -675,7 +675,7 @@ public abstract class Vala.CCodeBaseModule : CodeGenerator {
 
        public bool add_symbol_declaration (CCodeFile decl_space, Symbol sym, string name) {
                bool in_generated_header = context.header_filename != null
-                                          && (decl_space.file_type != CCodeFileType.PUBLIC_HEADER && !sym.is_internal_symbol ());
+                                          && (decl_space.file_type != CCodeFileType.PUBLIC_HEADER && !sym.is_internal_symbol () && !(sym is Class && ((Class) sym).is_opaque));
                if (decl_space.add_declaration (name)) {
                        return true;
                }
index d9fbaba65e2fbbb1b7ea73fa5362a5e658e3a2fe..ebf1ab6d6588ddeb95e8cdda29622cb0840f42e8 100644 (file)
@@ -281,7 +281,7 @@ public class Vala.GTypeModule : GErrorModule {
                                        var prop = (Property) s;
                                        generate_struct_property_declaration (cl, prop, instance_struct, type_struct, decl_space, ref has_struct_member);
                                } else if (s is Field) {
-                                       if (s.access != SymbolAccessibility.PRIVATE) {
+                                       if (s.access != SymbolAccessibility.PRIVATE || cl.is_opaque) {
                                                generate_struct_field_declaration ((Field) s, instance_struct, type_struct, decl_space, ref has_struct_member);
                                        }
                                } else {
@@ -308,7 +308,7 @@ public class Vala.GTypeModule : GErrorModule {
                        }
 
                        foreach (Field f in cl.get_fields ()) {
-                               if (f.access != SymbolAccessibility.PRIVATE) {
+                               if (f.access != SymbolAccessibility.PRIVATE || cl.is_opaque) {
                                        generate_struct_field_declaration (f, instance_struct, type_struct, decl_space, ref has_struct_member);
                                }
                        }
@@ -475,7 +475,7 @@ public class Vala.GTypeModule : GErrorModule {
        }
 
        void generate_class_private_declaration (Class cl, CCodeFile decl_space) {
-               if (decl_space.add_declaration ("%sPrivate".printf (get_ccode_name (cl)))) {
+               if (cl.is_opaque || decl_space.add_declaration ("%sPrivate".printf (get_ccode_name (cl)))) {
                        return;
                }
 
@@ -623,7 +623,11 @@ public class Vala.GTypeModule : GErrorModule {
                }
 
                if (!cl.is_internal_symbol ()) {
-                       generate_class_struct_declaration (cl, header_file);
+                       if (!cl.is_opaque) {
+                               generate_class_struct_declaration (cl, header_file);
+                       } else {
+                               generate_class_declaration (cl, header_file);
+                       }
                }
                if (!cl.is_private_symbol ()) {
                        generate_class_struct_declaration (cl, internal_header_file);
@@ -2356,11 +2360,6 @@ public class Vala.GTypeModule : GErrorModule {
                        base_prop = prop.base_interface_property;
                }
 
-               if (cl != null && cl.is_compact && (prop.get_accessor == null || prop.get_accessor.automatic_body)) {
-                       Report.error (prop.source_reference, "Properties without accessor bodies are not supported in compact classes");
-                       return;
-               }
-
                if (base_prop.get_attribute ("NoAccessorMethod") == null &&
                        prop.name == "type" && ((cl != null && !cl.is_compact) || (st != null && get_ccode_has_type_id (st)))) {
                        Report.error (prop.source_reference, "Property 'type' not allowed");
index 9ee73d0e7f4cf7276a6b5013807bbf11ef71c7b2..6211ba77c1e5559d88cfc54acc7918fc3750e931 100644 (file)
@@ -871,6 +871,11 @@ TESTS = \
        semantic/class-compact-interface.test \
        semantic/class-compact-method-baseaccess.test \
        semantic/class-compact-property-baseaccess.test \
+       semantic/class-opaque.vala \
+       semantic/class-opaque-public-field.test \
+       semantic/class-opaque-abstract-method.test \
+       semantic/class-opaque-abstract-property.test \
+       semantic/class-opaque-automatic-property.vala \
        semantic/class-missing-implement-interface-method.test \
        semantic/class-missing-implement-interface-property.test \
        semantic/class-missing-implement-interfaces-methods.test \
diff --git a/tests/semantic/class-opaque-abstract-method.test b/tests/semantic/class-opaque-abstract-method.test
new file mode 100644 (file)
index 0000000..78048f0
--- /dev/null
@@ -0,0 +1,9 @@
+Invalid Code
+
+[Compact (opaque = true)]
+class Foo {
+       public abstract void bar ();
+}
+
+void main () {
+}
diff --git a/tests/semantic/class-opaque-abstract-property.test b/tests/semantic/class-opaque-abstract-property.test
new file mode 100644 (file)
index 0000000..7588b5d
--- /dev/null
@@ -0,0 +1,9 @@
+Invalid Code
+
+[Compact (opaque = true)]
+class Foo {
+       public abstract int bar { get; set; }
+}
+
+void main () {
+}
diff --git a/tests/semantic/class-opaque-automatic-property.vala b/tests/semantic/class-opaque-automatic-property.vala
new file mode 100644 (file)
index 0000000..8400a78
--- /dev/null
@@ -0,0 +1,10 @@
+[Compact (opaque = true)]
+public class Foo {
+       public int bar { get; set; }
+}
+
+void main () {
+       var foo = new Foo ();
+       foo.bar = 42;
+       assert (foo.bar == 42);
+}
diff --git a/tests/semantic/class-opaque-public-field.test b/tests/semantic/class-opaque-public-field.test
new file mode 100644 (file)
index 0000000..11bd889
--- /dev/null
@@ -0,0 +1,9 @@
+Invalid Code
+
+[Compact (opaque = true)]
+class Foo {
+       public int i;
+}
+
+void main () {
+}
diff --git a/tests/semantic/class-opaque.vala b/tests/semantic/class-opaque.vala
new file mode 100644 (file)
index 0000000..02f8138
--- /dev/null
@@ -0,0 +1,20 @@
+[Compact (opaque = true)]
+public class Foo {
+       private int i;
+       internal int j;
+
+       public Foo () {
+               i = 42;
+       }
+
+       public int get_i () {
+               return i;
+       }
+}
+
+void main () {
+       var foo = new Foo ();
+       foo.j = 23;
+       assert (foo.j == 23);
+       assert (foo.get_i () == 42);
+}
index 7d59cfaad4d9b8aee759465e48d9911a4ef20a24..da1f81a33cf65a206fdf390416e256d2d99d2560 100644 (file)
@@ -65,6 +65,19 @@ public class Vala.Class : ObjectTypeSymbol {
                }
        }
 
+       /**
+        * Opaque compact classes hide their memory layout, only allowing private or
+        * internal instance members.
+        */
+       public bool is_opaque {
+               get {
+                       if (_is_opaque == null) {
+                               _is_opaque = get_attribute_bool ("Compact", "opaque");
+                       }
+                       return _is_opaque;
+               }
+       }
+
        /**
         * Instances of immutable classes are immutable after construction.
         */
@@ -112,6 +125,7 @@ public class Vala.Class : ObjectTypeSymbol {
        public bool has_class_private_fields { get; private set; }
 
        private bool? _is_compact;
+       private bool? _is_opaque;
        private bool? _is_immutable;
        private bool? _is_singleton;
 
@@ -624,8 +638,12 @@ public class Vala.Class : ObjectTypeSymbol {
                foreach (Field f in get_fields ()) {
                        if (is_compact && f.binding != MemberBinding.STATIC) {
                                //FIXME Should external bindings follow this too?
-                               if (!external_package && f.access == SymbolAccessibility.PRIVATE) {
-                                       Report.error (source_reference, "private fields are not supported in compact classes");
+                               if (!external_package && !is_opaque && f.access == SymbolAccessibility.PRIVATE) {
+                                       Report.error (f.source_reference, "private fields are only supported in opaque compact classes, use [Compact (opaque = true)]");
+                                       error = true;
+                               }
+                               if (!external_package && is_opaque && (f.access == SymbolAccessibility.PUBLIC || f.access == SymbolAccessibility.PROTECTED)) {
+                                       Report.error (f.source_reference, "fields in opaque compact classes must be private or internal");
                                        error = true;
                                }
                                if (f.binding == MemberBinding.CLASS) {
index 5f8dce29a3946af2e6f4dc4e0b3c3422ba54bfac..335bf4224bfce0247ae2da1ddeec2a9dbd62fa28 100644 (file)
@@ -735,6 +735,11 @@ public class Vala.Method : Subroutine, Callable {
                                Report.error (source_reference, "Abstract and virtual methods may not be declared in derived compact classes");
                                return false;
                        }
+                       if (cl.is_opaque) {
+                               error = true;
+                               Report.error (source_reference, "Abstract and virtual methods may not be declared in opaque compact classes");
+                               return false;
+                       }
                }
 
                if (is_variadic () && (is_abstract || is_virtual)) {
index 0a2e437d596cec0984a3688b7cb023c465814e62..f07f137e0a078c94f7bd7e4ce196fc6061b4e11e 100644 (file)
@@ -424,6 +424,11 @@ public class Vala.Property : Symbol, Lockable {
                                Report.error (source_reference, "Abstract and virtual properties may not be declared in derived compact classes");
                                return false;
                        }
+                       if (cl.is_opaque) {
+                               error = true;
+                               Report.error (source_reference, "Abstract and virtual properties may not be declared in opaque compact classes");
+                               return false;
+                       }
                }
 
                if (is_abstract) {
index ffb142eb05b8ceb75cb2dceec53f55e2cce93f87..c00d8e6a2f284b91a1a2beb05d56724cb2503397 100644 (file)
@@ -44,7 +44,7 @@ public class Vala.UsedAttr : CodeVisitor {
 
                "Immutable", "",
                "SingleInstance", "",
-               "Compact", "",
+               "Compact", "opaque", "",
                "NoWrapper", "",
                "NoThrow", "",
                "DestroysInstance", "",