]> git.ipfire.org Git - thirdparty/vala.git/commitdiff
codegen: Add support for abstract/virtual methods and properties in compact classes 28b4f45b709622e821e86655f245fdcb75d3afaf
authorDaniel Espinosa <esodan@gmail.com>
Mon, 15 Jan 2018 13:28:43 +0000 (14:28 +0100)
committerRico Tzschichholz <ricotz@ubuntu.com>
Mon, 15 Jan 2018 16:39:33 +0000 (17:39 +0100)
Reworked and extened by Rico Tzschichholz

https://bugzilla.gnome.org/show_bug.cgi?id=741465

codegen/valaccodebasemodule.vala
codegen/valaccodemethodcallmodule.vala
codegen/valaccodemethodmodule.vala
codegen/valagtypemodule.vala
tests/Makefile.am
tests/objects/bug741465.vala [new file with mode: 0644]
vala/valamethod.vala
vala/valaproperty.vala

index a65a3f4f51c4a4b5c34fb1b93df04b6aebe63b93..56e12c65d3a72e20ca09aa5340470a7660eb86d2 100644 (file)
@@ -1651,17 +1651,21 @@ public abstract class Vala.CCodeBaseModule : CodeGenerator {
                                }
                        }
 
-                       CCodeFunctionCall vcast = null;
+                       CCodeExpression vcast;
                        if (prop.parent_symbol is Interface) {
                                var iface = (Interface) prop.parent_symbol;
 
                                vcast = new CCodeFunctionCall (new CCodeIdentifier ("%s_GET_INTERFACE".printf (get_ccode_upper_case_name (iface, null))));
+                               ((CCodeFunctionCall) vcast).add_argument (new CCodeIdentifier ("self"));
                        } else {
                                var cl = (Class) prop.parent_symbol;
-
-                               vcast = new CCodeFunctionCall (new CCodeIdentifier ("%s_GET_CLASS".printf (get_ccode_upper_case_name (cl, null))));
+                               if (!cl.is_compact) {
+                                       vcast = new CCodeFunctionCall (new CCodeIdentifier ("%s_GET_CLASS".printf (get_ccode_upper_case_name (cl, null))));
+                                       ((CCodeFunctionCall) vcast).add_argument (new CCodeIdentifier ("self"));
+                               } else {
+                                       vcast = new CCodeIdentifier ("self");
+                               }
                        }
-                       vcast.add_argument (new CCodeIdentifier ("self"));
 
                        if (acc.readable) {
                                var vcall = new CCodeFunctionCall (new CCodeMemberAccess.pointer (vcast, "get_%s".printf (prop.name)));
index 007bcbdee7ff9c8a1d1bcfa400a3b6ae83fa20b1..bfcc21582514cff22a825d2cc3da8a8391d1108d 100644 (file)
@@ -871,7 +871,7 @@ public class Vala.CCodeMethodCallModule : CCodeAssignmentModule {
                        }
                }
 
-               if (m is CreationMethod && m.parent_symbol is Class && current_class.base_class == gsource_type) {
+               if (m is CreationMethod && m.parent_symbol is Class && ((current_class.is_compact && current_class.base_class != null) || current_class.base_class == gsource_type)) {
                        var cinitcall = new CCodeFunctionCall (new CCodeIdentifier ("%s_instance_init".printf (get_ccode_lower_case_name (current_class, null))));
                        cinitcall.add_argument (get_this_cexpression ());
                        ccode.add_expression (cinitcall);
index 4efd04b7225e757e464b4b22fe1e1d30824dced9..33c5ee485ab2469d4915bd2ee0078c410594febd 100644 (file)
@@ -655,7 +655,6 @@ public abstract class Vala.CCodeMethodModule : CCodeStructModule {
                                                }
 
                                                if (cl.base_class == null) {
-                                                       // derived compact classes do not have fields
                                                        var cinitcall = new CCodeFunctionCall (new CCodeIdentifier ("%s_instance_init".printf (get_ccode_lower_case_name (cl, null))));
                                                        cinitcall.add_argument (get_this_cexpression ());
                                                        ccode.add_expression (cinitcall);
@@ -815,17 +814,19 @@ public abstract class Vala.CCodeMethodModule : CCodeStructModule {
                        // complain during compile time of such en error.
 
                        // add critical warning that this method should not have been called
-                       var type_from_instance_call = new CCodeFunctionCall (new CCodeIdentifier ("G_TYPE_FROM_INSTANCE"));
-                       type_from_instance_call.add_argument (new CCodeIdentifier ("self"));
-
-                       var type_name_call = new CCodeFunctionCall (new CCodeIdentifier ("g_type_name"));
-                       type_name_call.add_argument (type_from_instance_call);
+                       var cerrorcall = new CCodeFunctionCall (new CCodeIdentifier ("g_critical"));
+                       if (!((Class) current_type_symbol).is_compact) {
+                               var type_from_instance_call = new CCodeFunctionCall (new CCodeIdentifier ("G_TYPE_FROM_INSTANCE"));
+                               type_from_instance_call.add_argument (new CCodeIdentifier ("self"));
 
-                       var error_string = "\"Type `%%s' does not implement abstract method `%s'\"".printf (get_ccode_name (m));
+                               var type_name_call = new CCodeFunctionCall (new CCodeIdentifier ("g_type_name"));
+                               type_name_call.add_argument (type_from_instance_call);
 
-                       var cerrorcall = new CCodeFunctionCall (new CCodeIdentifier ("g_critical"));
-                       cerrorcall.add_argument (new CCodeConstant (error_string));
-                       cerrorcall.add_argument (type_name_call);
+                               cerrorcall.add_argument (new CCodeConstant ("\"Type `%%s' does not implement abstract method `%s'\"".printf (get_ccode_name (m))));
+                               cerrorcall.add_argument (type_name_call);
+                       } else {
+                               cerrorcall.add_argument (new CCodeConstant ("\"Abstract method `%s' is not implemented\"".printf (get_ccode_name (m))));
+                       }
 
                        ccode.add_expression (cerrorcall);
 
@@ -1087,17 +1088,21 @@ public abstract class Vala.CCodeMethodModule : CCodeStructModule {
                }
                var vfunc = new CCodeFunction (cname + suffix);
 
-               CCodeFunctionCall vcast = null;
+               CCodeExpression vcast;
                if (m.parent_symbol is Interface) {
                        var iface = (Interface) m.parent_symbol;
 
                        vcast = new CCodeFunctionCall (new CCodeIdentifier ("%s_GET_INTERFACE".printf (get_ccode_upper_case_name (iface))));
+                       ((CCodeFunctionCall) vcast).add_argument (new CCodeIdentifier ("self"));
                } else {
                        var cl = (Class) m.parent_symbol;
-
-                       vcast = new CCodeFunctionCall (new CCodeIdentifier ("%s_GET_CLASS".printf (get_ccode_upper_case_name (cl))));
+                       if (!cl.is_compact) {
+                               vcast = new CCodeFunctionCall (new CCodeIdentifier ("%s_GET_CLASS".printf (get_ccode_upper_case_name (cl))));
+                               ((CCodeFunctionCall) vcast).add_argument (new CCodeIdentifier ("self"));
+                       } else {
+                               vcast = new CCodeIdentifier ("self");
+                       }
                }
-               vcast.add_argument (new CCodeIdentifier ("self"));
        
                cname = get_ccode_vfunc_name (m);
                if (suffix == "_finish" && cname.has_suffix ("_async")) {
index d34219826f5bee84a404e7f0ada99d768b9ba3e2..688523cf7167d763729c06e81632ba4f10f1e764 100644 (file)
@@ -225,11 +225,6 @@ public class Vala.GTypeModule : GErrorModule {
                        instance_struct.add_field ("volatile int", "ref_count");
                }
 
-               if (cl.is_compact && cl.base_class == null && cl.get_fields ().size == 0) {
-                       // add dummy member, C doesn't allow empty structs
-                       instance_struct.add_field ("int", "dummy");
-               }
-
                if (is_gtypeinstance) {
                        decl_space.add_type_declaration (new CCodeTypeDefinition ("struct %sPrivate".printf (instance_struct.name), new CCodeVariableDeclarator ("%sPrivate".printf (get_ccode_name (cl)))));
 
@@ -245,8 +240,14 @@ public class Vala.GTypeModule : GErrorModule {
                        }
                }
 
+               bool has_struct_member = false;
                foreach (Method m in cl.get_methods ()) {
-                       generate_virtual_method_declaration (m, decl_space, type_struct);
+                       if (!cl.is_compact) {
+                               generate_virtual_method_declaration (m, decl_space, type_struct);
+                       } else if (cl.is_compact && cl.base_class == null) {
+                               generate_virtual_method_declaration (m, decl_space, instance_struct);
+                               has_struct_member |= (m.is_abstract || m.is_virtual);
+                       }
                }
 
                foreach (Signal sig in cl.get_signals ()) {
@@ -290,6 +291,11 @@ public class Vala.GTypeModule : GErrorModule {
                                var vdecl = new CCodeDeclaration (creturn_type);
                                vdecl.add_declarator (vdeclarator);
                                type_struct.add_declaration (vdecl);
+
+                               if (cl.is_compact && cl.base_class == null) {
+                                       instance_struct.add_declaration (vdecl);
+                                       has_struct_member = true;
+                               }
                        }
                        if (prop.set_accessor != null) {
                                CCodeParameter cvalueparam;
@@ -315,6 +321,11 @@ public class Vala.GTypeModule : GErrorModule {
                                var vdecl = new CCodeDeclaration ("void");
                                vdecl.add_declarator (vdeclarator);
                                type_struct.add_declaration (vdecl);
+
+                               if (cl.is_compact && cl.base_class == null) {
+                                       instance_struct.add_declaration (vdecl);
+                                       has_struct_member = true;
+                               }
                        }
                }
 
@@ -325,6 +336,7 @@ public class Vala.GTypeModule : GErrorModule {
                                        generate_type_declaration (f.variable_type, decl_space);
 
                                        instance_struct.add_field (get_ccode_name (f.variable_type), get_ccode_name (f), modifiers, get_ccode_declarator_suffix (f.variable_type));
+                                       has_struct_member = true;
                                        if (f.variable_type is ArrayType && get_ccode_array_length (f)) {
                                                // create fields to store array dimensions
                                                var array_type = (ArrayType) f.variable_type;
@@ -362,6 +374,11 @@ public class Vala.GTypeModule : GErrorModule {
                        }
                }
 
+               if (cl.is_compact && cl.base_class == null && !has_struct_member) {
+                       // add dummy member, C doesn't allow empty structs
+                       instance_struct.add_field ("int", "dummy");
+               }
+
                if (!cl.is_compact || cl.base_class == null || is_gsource) {
                        // derived compact classes do not have a struct
                        decl_space.add_type_definition (instance_struct);
@@ -592,7 +609,7 @@ public class Vala.GTypeModule : GErrorModule {
                        begin_class_finalize_function (cl);
                        begin_finalize_function (cl);
                } else {
-                       if (cl.base_class == null || cl.base_class == gsource_type) {
+                       if (cl.is_compact || cl.base_class == null || cl.base_class == gsource_type) {
                                begin_instance_init_function (cl);
                                begin_finalize_function (cl);
                        }
@@ -735,8 +752,7 @@ public class Vala.GTypeModule : GErrorModule {
                                cfile.add_function (unref_fun);
                        }
                } else {
-                       if (cl.base_class == null || cl.base_class == gsource_type) {
-                               // derived compact classes do not have fields
+                       if (cl.is_compact || cl.base_class == null || cl.base_class == gsource_type) {
                                add_instance_init_function (cl);
                                add_finalize_function (cl);
                        }
@@ -1568,6 +1584,49 @@ public class Vala.GTypeModule : GErrorModule {
                        // Add declaration, since the instance_init function is explicitly called
                        // by the creation methods
                        cfile.add_function_declaration (func);
+
+                       // connect overridden methods
+                       foreach (Method m in cl.get_methods ()) {
+                               if (m.base_method == null) {
+                                       continue;
+                               }
+                               var base_type = (ObjectTypeSymbol) m.base_method.parent_symbol;
+
+                               // there is currently no default handler for abstract async methods
+                               if (!m.is_abstract || !m.coroutine) {
+                                       CCodeExpression cfunc = new CCodeIdentifier (get_ccode_real_name (m));
+                                       cfunc = cast_method_pointer (m.base_method, cfunc, base_type, (m.coroutine ? 1 : 3));
+                                       var ccast = new CCodeCastExpression (new CCodeIdentifier ("self"), "%s *".printf (get_ccode_name (base_type)));
+                                       func.add_assignment (new CCodeMemberAccess.pointer (ccast, get_ccode_vfunc_name (m.base_method)), cfunc);
+
+                                       if (m.coroutine) {
+                                               cfunc = new CCodeIdentifier (get_ccode_finish_real_name (m));
+                                               cfunc = cast_method_pointer (m.base_method, cfunc, base_type, 2);
+                                               ccode.add_assignment (new CCodeMemberAccess.pointer (ccast, get_ccode_finish_vfunc_name (m.base_method)), cfunc);
+                                       }
+                               }
+                       }
+
+                       // connect overridden properties
+                       foreach (Property prop in cl.get_properties ()) {
+                               if (prop.base_property == null) {
+                                       continue;
+                               }
+                               var base_type = prop.base_property.parent_symbol;
+
+                               var ccast = new CCodeCastExpression (new CCodeIdentifier ("self"), "%s *".printf (get_ccode_name (base_type)));
+
+                               if (!get_ccode_no_accessor_method (prop.base_property) && !get_ccode_concrete_accessor (prop.base_property)) {
+                                       if (prop.get_accessor != null) {
+                                               string cname = get_ccode_real_name (prop.get_accessor);
+                                               ccode.add_assignment (new CCodeMemberAccess.pointer (ccast, "get_%s".printf (prop.name)), new CCodeIdentifier (cname));
+                                       }
+                                       if (prop.set_accessor != null) {
+                                               string cname = get_ccode_real_name (prop.set_accessor);
+                                               ccode.add_assignment (new CCodeMemberAccess.pointer (ccast, "set_%s".printf (prop.name)), new CCodeIdentifier (cname));
+                                       }
+                               }
+                       }
                }
 
                if (!cl.is_compact && (cl.has_private_fields || cl.get_type_parameters ().size > 0)) {
@@ -2283,6 +2342,11 @@ 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 5f41f1cc5dfc59634f7e529f5ae7def5bbb7477a..e7057642a4d4b4db2c46ba880b27c1fd293bbd95 100644 (file)
@@ -243,6 +243,7 @@ TESTS = \
        objects/bug702736.vala \
        objects/bug702846.vala \
        objects/bug731547.vala \
+       objects/bug741465.vala \
        objects/bug751338.vala \
        objects/bug758816.vala \
        objects/bug760031.test \
diff --git a/tests/objects/bug741465.vala b/tests/objects/bug741465.vala
new file mode 100644 (file)
index 0000000..e420aec
--- /dev/null
@@ -0,0 +1,86 @@
+[Compact]
+abstract class AbstractFoo {
+       public int field = 23;
+       public abstract int prop { get; set; }
+       public abstract unowned string foo ();
+}
+
+[Compact]
+class Foo : AbstractFoo {
+       public override int prop {
+               get { return field + 1; }
+               set { field = value - 1; }
+       }
+
+       public Foo () {
+               assert (field == 23);
+               field = 37;
+               assert (prop == 38);
+       }
+
+       public override unowned string foo () {
+               return "Foo";
+       }
+}
+
+[Compact]
+class Bar : Foo {
+       public override int prop {
+               get { return field * 2; }
+               set { field = value / 2; }
+       }
+
+       public Bar () {
+               assert (field == 37);
+               field = 42;
+               assert (prop == 84);
+       }
+
+       public override unowned string foo () {
+               return "Bar";
+       }
+}
+
+[Compact]
+class Manam {
+       public int field = 23;
+
+       public virtual int prop {
+               get { return field + 1; }
+               set { field = value - 1; }
+       }
+
+       public virtual unowned string foo () {
+               return "Manam";
+       }
+}
+
+[Compact]
+class Baz : Manam {
+       public override unowned string foo () {
+               return "Baz";
+       }
+}
+
+void main () {
+       var foo = new Foo ();
+       assert (foo.foo () == "Foo");
+       assert (foo.prop == 38);
+       foo.prop = 4711;
+       assert (foo.field == 4710);
+
+       var bar = new Bar ();
+       assert (bar.foo () == "Bar");
+       assert (bar.prop == 84);
+       bar.prop = 32;
+       assert (bar.field == 16);
+
+       var manam = new Manam ();
+       assert (manam.foo () == "Manam");
+       assert (manam.prop == 24);
+
+       var baz = new Baz ();
+       assert (baz.foo () == "Baz");
+       baz.prop = 42;
+       assert (baz.prop == 42);
+}
index 945ec5dba7af5596b66478396062fa38fad9dee7..b0a742a3c25b24d08209f47e085a2d8e4a9a3cb1 100644 (file)
@@ -638,6 +638,15 @@ public class Vala.Method : Subroutine, Callable {
                        get_error_types ().clear ();
                }
 
+               if (parent_symbol is Class && (is_abstract || is_virtual)) {
+                       var cl = (Class) parent_symbol;
+                       if (cl.is_compact && cl.base_class != null) {
+                               error = true;
+                               Report.error (source_reference, "Abstract and virtual methods may not be declared in derived compact classes");
+                               return false;
+                       }
+               }
+
                if (is_abstract) {
                        if (parent_symbol is Class) {
                                var cl = (Class) parent_symbol;
@@ -657,14 +666,6 @@ public class Vala.Method : Subroutine, Callable {
                                Report.error (source_reference, "Virtual methods may not be declared outside of classes and interfaces");
                                return false;
                        }
-
-                       if (parent_symbol is Class) {
-                               var cl = (Class) parent_symbol;
-                               if (cl.is_compact && cl != context.analyzer.gsource_type) {
-                                       Report.error (source_reference, "Virtual methods may not be declared in compact classes");
-                                       return false;
-                               }
-                       }
                } else if (overrides) {
                        if (!(parent_symbol is Class)) {
                                error = true;
index 0968600dd56ce14eaba613332ed7fc7bb03c3f95..e0a8e4390cdb1887b92e8f82426251c7607f2b35 100644 (file)
@@ -382,6 +382,15 @@ public class Vala.Property : Symbol, Lockable {
 
                checked = true;
 
+               if (parent_symbol is Class && (is_abstract || is_virtual)) {
+                       var cl = (Class) parent_symbol;
+                       if (cl.is_compact && cl.base_class != null) {
+                               error = true;
+                               Report.error (source_reference, "Abstract and virtual properties may not be declared in derived compact classes");
+                               return false;
+                       }
+               }
+
                if (is_abstract) {
                        if (parent_symbol is Class) {
                                var cl = (Class) parent_symbol;
@@ -401,15 +410,6 @@ public class Vala.Property : Symbol, Lockable {
                                Report.error (source_reference, "Virtual properties may not be declared outside of classes and interfaces");
                                return false;
                        }
-
-                       if (parent_symbol is Class) {
-                               var cl = (Class) parent_symbol;
-                               if (cl.is_compact) {
-                                       error = true;
-                                       Report.error (source_reference, "Virtual properties may not be declared in compact classes");
-                                       return false;
-                               }
-                       }
                } else if (overrides) {
                        if (!(parent_symbol is Class)) {
                                error = true;