From: Simon Werbeck Date: Thu, 19 Mar 2020 14:20:39 +0000 (+0100) Subject: Add support for 'opaque' compact classes X-Git-Tag: 0.51.1~89 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=19ed5651a3f4a573934d58120be056b62102f554;p=thirdparty%2Fvala.git Add support for 'opaque' compact classes 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 --- diff --git a/codegen/valaccodebasemodule.vala b/codegen/valaccodebasemodule.vala index 0be29ed99..d27407667 100644 --- a/codegen/valaccodebasemodule.vala +++ b/codegen/valaccodebasemodule.vala @@ -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; } diff --git a/codegen/valagtypemodule.vala b/codegen/valagtypemodule.vala index d9fbaba65..ebf1ab6d6 100644 --- a/codegen/valagtypemodule.vala +++ b/codegen/valagtypemodule.vala @@ -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"); diff --git a/tests/Makefile.am b/tests/Makefile.am index 9ee73d0e7..6211ba77c 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -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 index 000000000..78048f05f --- /dev/null +++ b/tests/semantic/class-opaque-abstract-method.test @@ -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 index 000000000..7588b5da1 --- /dev/null +++ b/tests/semantic/class-opaque-abstract-property.test @@ -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 index 000000000..8400a78b5 --- /dev/null +++ b/tests/semantic/class-opaque-automatic-property.vala @@ -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 index 000000000..11bd88942 --- /dev/null +++ b/tests/semantic/class-opaque-public-field.test @@ -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 index 000000000..02f813822 --- /dev/null +++ b/tests/semantic/class-opaque.vala @@ -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); +} diff --git a/vala/valaclass.vala b/vala/valaclass.vala index 7d59cfaad..da1f81a33 100644 --- a/vala/valaclass.vala +++ b/vala/valaclass.vala @@ -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) { diff --git a/vala/valamethod.vala b/vala/valamethod.vala index 5f8dce29a..335bf4224 100644 --- a/vala/valamethod.vala +++ b/vala/valamethod.vala @@ -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)) { diff --git a/vala/valaproperty.vala b/vala/valaproperty.vala index 0a2e437d5..f07f137e0 100644 --- a/vala/valaproperty.vala +++ b/vala/valaproperty.vala @@ -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) { diff --git a/vala/valausedattr.vala b/vala/valausedattr.vala index ffb142eb0..c00d8e6a2 100644 --- a/vala/valausedattr.vala +++ b/vala/valausedattr.vala @@ -44,7 +44,7 @@ public class Vala.UsedAttr : CodeVisitor { "Immutable", "", "SingleInstance", "", - "Compact", "", + "Compact", "opaque", "", "NoWrapper", "", "NoThrow", "", "DestroysInstance", "",