Use accessor vfuncs to implement virtual and abstract properties, fixes
authorJürg Billeter <j@bitron.ch>
Sun, 10 Aug 2008 09:12:00 +0000 (09:12 +0000)
committerJürg Billeter <juergbi@src.gnome.org>
Sun, 10 Aug 2008 09:12:00 +0000 (09:12 +0000)
2008-08-10  Jürg Billeter  <j@bitron.ch>

* vala/valasemanticanalyzer.vala:
* gobject/valaccodeclassbinding.vala:
* gobject/valaccodegenerator.vala:

Use accessor vfuncs to implement virtual and abstract properties,
fixes bug 508472 and bug 505966

svn path=/trunk/; revision=1740

ChangeLog
gobject/valaccodeclassbinding.vala
gobject/valaccodegenerator.vala
vala/valasemanticanalyzer.vala

index 9b82016..ac7f041 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,14 @@
 2008-08-10  Jürg Billeter  <j@bitron.ch>
 
+       * vala/valasemanticanalyzer.vala:
+       * gobject/valaccodeclassbinding.vala:
+       * gobject/valaccodegenerator.vala:
+
+       Use accessor vfuncs to implement virtual and abstract properties,
+       fixes bug 508472 and bug 505966
+
+2008-08-10  Jürg Billeter  <j@bitron.ch>
+
        * vapi/packages/vte/:
 
        Fix vte_terminal_set_colors binding, fixes bug 547136
index 89d1dab..63ec3e8 100644 (file)
@@ -364,6 +364,26 @@ public class Vala.CCodeClassBinding : CCodeObjectTypeSymbolBinding {
                        init_block.add_statement (new CCodeExpressionStatement (new CCodeAssignment (new CCodeMemberAccess.pointer (ccast, m.base_method.vfunc_name), new CCodeIdentifier (m.get_real_cname ()))));
                }
 
+               /* 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 CCodeFunctionCall (new CCodeIdentifier ("%s_CLASS".printf (((Class) base_type).get_upper_case_cname (null))));
+                       ccast.add_argument (new CCodeIdentifier ("klass"));
+
+                       if (prop.get_accessor != null) {
+                               string cname = "%s_real_get_%s".printf (cl.get_lower_case_cname (null), prop.name);
+                               init_block.add_statement (new CCodeExpressionStatement (new CCodeAssignment (new CCodeMemberAccess.pointer (ccast, "get_%s".printf (prop.name)), new CCodeIdentifier (cname))));
+                       }
+                       if (prop.set_accessor != null) {
+                               string cname = "%s_real_set_%s".printf (cl.get_lower_case_cname (null), prop.name);
+                               init_block.add_statement (new CCodeExpressionStatement (new CCodeAssignment (new CCodeMemberAccess.pointer (ccast, "set_%s".printf (prop.name)), new CCodeIdentifier (cname))));
+                       }
+               }
+
                if (cl.is_subtype_of (codegen.gobject_type)) {
                        /* create type, dup_func, and destroy_func properties for generic types */
                        foreach (TypeParameter type_param in cl.get_type_parameters ()) {
@@ -528,7 +548,7 @@ public class Vala.CCodeClassBinding : CCodeObjectTypeSymbolBinding {
                        }
                        
                        var ciface = new CCodeIdentifier ("iface");
-                       var cname = m.get_real_cname ();
+                       string cname = m.get_real_cname ();
                        if (m.is_abstract || m.is_virtual) {
                                // FIXME results in C compiler warning
                                cname = m.get_cname ();
@@ -554,6 +574,61 @@ public class Vala.CCodeClassBinding : CCodeObjectTypeSymbolBinding {
                        }
                }
 
+               foreach (Property prop in cl.get_properties ()) {
+                       if (prop.base_interface_property == null) {
+                               continue;
+                       }
+
+                       var base_type = prop.base_interface_property.parent_symbol;
+                       if (base_type != iface) {
+                               continue;
+                       }
+                       
+                       var ciface = new CCodeIdentifier ("iface");
+
+                       if (prop.get_accessor != null) {
+                               string cname = "%s_real_get_%s".printf (cl.get_lower_case_cname (null), prop.name);
+                               if (prop.is_abstract || prop.is_virtual) {
+                                       cname = "%s_get_%s".printf (cl.get_lower_case_cname (null), prop.name);
+                               }
+                               init_block.add_statement (new CCodeExpressionStatement (new CCodeAssignment (new CCodeMemberAccess.pointer (ciface, "get_%s".printf (prop.name)), new CCodeIdentifier (cname))));
+                       }
+                       if (prop.set_accessor != null) {
+                               string cname = "%s_real_set_%s".printf (cl.get_lower_case_cname (null), prop.name);
+                               if (prop.is_abstract || prop.is_virtual) {
+                                       cname = "%s_set_%s".printf (cl.get_lower_case_cname (null), prop.name);
+                               }
+                               init_block.add_statement (new CCodeExpressionStatement (new CCodeAssignment (new CCodeMemberAccess.pointer (ciface, "set_%s".printf (prop.name)), new CCodeIdentifier (cname))));
+                       }
+               }
+
+               foreach (Property prop in iface.get_properties ()) {
+                       if (!prop.is_abstract) {
+                               continue;
+                       }
+
+                       Property cl_prop = null;
+                       var base_class = cl;
+                       while (base_class != null && cl_prop == null) {
+                               cl_prop = base_class.scope.lookup (prop.name) as Property;
+                               base_class = base_class.base_class;
+                       }
+                       if (base_class != null && cl_prop.parent_symbol != cl) {
+                               // property inherited from base class
+                               
+                               var ciface = new CCodeIdentifier ("iface");
+
+                               if (prop.get_accessor != null) {
+                                       string cname = "%s_real_get_%s".printf (cl.get_lower_case_cname (null), prop.name);
+                                       init_block.add_statement (new CCodeExpressionStatement (new CCodeAssignment (new CCodeMemberAccess.pointer (ciface, "get_%s".printf (prop.name)), new CCodeIdentifier (cname))));
+                               }
+                               if (prop.set_accessor != null) {
+                                       string cname = "%s_real_set_%s".printf (cl.get_lower_case_cname (null), prop.name);
+                                       init_block.add_statement (new CCodeExpressionStatement (new CCodeAssignment (new CCodeMemberAccess.pointer (ciface, "set_%s".printf (prop.name)), new CCodeIdentifier (cname))));
+                               }
+                       }
+               }
+
                codegen.source_type_member_definition.append (iface_init);
        }
        
@@ -664,16 +739,21 @@ public class Vala.CCodeClassBinding : CCodeObjectTypeSymbolBinding {
                                continue;
                        }
 
-                       bool is_virtual = prop.base_property != null || prop.base_interface_property != null;
-
                        string prefix = cl.get_lower_case_cname (null);
-                       if (is_virtual) {
-                               prefix += "_real";
+                       CCodeExpression cself = new CCodeIdentifier ("self");
+                       if (prop.base_property != null) {
+                               var base_type = (Class) prop.base_property.parent_symbol;
+                               prefix = base_type.get_lower_case_cname (null);
+                               cself = codegen.transform_expression (cself, new ObjectType (cl), new ObjectType (base_type));
+                       } else if (prop.base_interface_property != null) {
+                               var base_type = (Interface) prop.base_interface_property.parent_symbol;
+                               prefix = base_type.get_lower_case_cname (null);
+                               cself = codegen.transform_expression (cself, new ObjectType (cl), new ObjectType (base_type));
                        }
 
                        var ccase = new CCodeCaseStatement (new CCodeIdentifier (prop.get_upper_case_cname ()));
                        var ccall = new CCodeFunctionCall (new CCodeIdentifier ("%s_get_%s".printf (prefix, prop.name)));
-                       ccall.add_argument (new CCodeIdentifier ("self"));
+                       ccall.add_argument (cself);
                        var csetcall = new CCodeFunctionCall ();
                        csetcall.call = get_value_setter_function (prop.property_type);
                        csetcall.add_argument (new CCodeIdentifier ("value"));
@@ -719,16 +799,21 @@ public class Vala.CCodeClassBinding : CCodeObjectTypeSymbolBinding {
                                continue;
                        }
 
-                       bool is_virtual = prop.base_property != null || prop.base_interface_property != null;
-
                        string prefix = cl.get_lower_case_cname (null);
-                       if (is_virtual) {
-                               prefix += "_real";
+                       CCodeExpression cself = new CCodeIdentifier ("self");
+                       if (prop.base_property != null) {
+                               var base_type = (Class) prop.base_property.parent_symbol;
+                               prefix = base_type.get_lower_case_cname (null);
+                               cself = codegen.transform_expression (cself, new ObjectType (cl), new ObjectType (base_type));
+                       } else if (prop.base_interface_property != null) {
+                               var base_type = (Interface) prop.base_interface_property.parent_symbol;
+                               prefix = base_type.get_lower_case_cname (null);
+                               cself = codegen.transform_expression (cself, new ObjectType (cl), new ObjectType (base_type));
                        }
 
                        var ccase = new CCodeCaseStatement (new CCodeIdentifier (prop.get_upper_case_cname ()));
                        var ccall = new CCodeFunctionCall (new CCodeIdentifier ("%s_set_%s".printf (prefix, prop.name)));
-                       ccall.add_argument (new CCodeIdentifier ("self"));
+                       ccall.add_argument (cself);
                        var cgetcall = new CCodeFunctionCall ();
                        if (prop.property_type.data_type != null) {
                                cgetcall.call = new CCodeIdentifier (prop.property_type.data_type.get_get_value_function ());
index 40b7a5c..f7f6c61 100644 (file)
@@ -815,14 +815,28 @@ public class Vala.CCodeGenerator : CodeGenerator {
                var cvalueparam = new CCodeFormalParameter ("value", value_type.get_cname ());
 
                if (prop.is_abstract || prop.is_virtual) {
+                       CCodeFunctionDeclarator vdeclarator;
+
                        if (acc.readable) {
                                function = new CCodeFunction (acc.get_cname (), prop.property_type.get_cname ());
+
+                               var vdecl = new CCodeDeclaration (prop.property_type.get_cname ());
+                               vdeclarator = new CCodeFunctionDeclarator ("get_%s".printf (prop.name));
+                               vdecl.add_declarator (vdeclarator);
+                               type_struct.add_declaration (vdecl);
                        } else {
                                function = new CCodeFunction (acc.get_cname (), "void");
+
+                               var vdecl = new CCodeDeclaration ("void");
+                               vdeclarator = new CCodeFunctionDeclarator ("set_%s".printf (prop.name));
+                               vdecl.add_declarator (vdeclarator);
+                               type_struct.add_declaration (vdecl);
                        }
                        function.add_parameter (cselfparam);
+                       vdeclarator.add_parameter (cselfparam);
                        if (acc.writable || acc.construction) {
                                function.add_parameter (cvalueparam);
+                               vdeclarator.add_parameter (cvalueparam);
                        }
                        
                        if (!prop.is_internal_symbol () && (acc.readable || acc.writable) && acc.access != SymbolAccessibility.PRIVATE) {
@@ -836,53 +850,26 @@ public class Vala.CCodeGenerator : CodeGenerator {
                        var block = new CCodeBlock ();
                        function.block = block;
 
-                       if (acc.readable) {
-                               // declare temporary variable to save the property value
-                               var decl = new CCodeDeclaration (prop.property_type.get_cname ());
-                               decl.add_declarator (new CCodeVariableDeclarator ("value"));
-                               block.add_statement (decl);
-                       
-                               var ccall = new CCodeFunctionCall (new CCodeIdentifier ("g_object_get"));
-                       
-                               var ccast = new CCodeFunctionCall (new CCodeIdentifier ("G_OBJECT"));
-                               ccast.add_argument (new CCodeIdentifier ("self"));
-                               ccall.add_argument (ccast);
-                               
-                               // property name is second argument of g_object_get
-                               ccall.add_argument (prop.get_canonical_cconstant ());
-
-                               ccall.add_argument (new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, new CCodeIdentifier ("value")));
+                       CCodeFunctionCall vcast = null;
+                       if (prop.parent_symbol is Interface) {
+                               var iface = (Interface) prop.parent_symbol;
 
-                               ccall.add_argument (new CCodeConstant ("NULL"));
-                               
-                               block.add_statement (new CCodeExpressionStatement (ccall));
-
-                               // HACK: decrement the refcount before returning the value to simulate a weak reference getter function
-                               if (prop.property_type.data_type != null && prop.property_type.data_type.is_reference_counting ()) {
-                                       var unref_cond = new CCodeBinaryExpression (CCodeBinaryOperator.INEQUALITY, new CCodeIdentifier ("value"), new CCodeConstant ("NULL"));
-                                       var unref_function = new CCodeFunctionCall (get_destroy_func_expression (prop.property_type));
-                                       unref_function.add_argument (new CCodeIdentifier ("value"));
-                                       var unref_block = new CCodeBlock ();
-                                       unref_block.add_statement (new CCodeExpressionStatement (unref_function));
-                                       block.add_statement (new CCodeIfStatement (unref_cond, unref_block));
-                               }
-
-                               block.add_statement (new CCodeReturnStatement (new CCodeIdentifier ("value")));
+                               vcast = new CCodeFunctionCall (new CCodeIdentifier ("%s_GET_INTERFACE".printf (iface.get_upper_case_cname (null))));
                        } else {
-                               var ccall = new CCodeFunctionCall (new CCodeIdentifier ("g_object_set"));
-                       
-                               var ccast = new CCodeFunctionCall (new CCodeIdentifier ("G_OBJECT"));
-                               ccast.add_argument (new CCodeIdentifier ("self"));
-                               ccall.add_argument (ccast);
-                               
-                               // property name is second argument of g_object_set
-                               ccall.add_argument (prop.get_canonical_cconstant ());
+                               var cl = (Class) prop.parent_symbol;
 
-                               ccall.add_argument (new CCodeIdentifier ("value"));
+                               vcast = new CCodeFunctionCall (new CCodeIdentifier ("%s_GET_CLASS".printf (cl.get_upper_case_cname (null))));
+                       }
+                       vcast.add_argument (new CCodeIdentifier ("self"));
 
-                               ccall.add_argument (new CCodeConstant ("NULL"));
-                               
-                               block.add_statement (new CCodeExpressionStatement (ccall));
+                       if (acc.readable) {
+                               var vcall = new CCodeFunctionCall (new CCodeMemberAccess.pointer (vcast, "get_%s".printf (prop.name)));
+                               vcall.add_argument (new CCodeIdentifier ("self"));
+                               block.add_statement (new CCodeReturnStatement (vcall));
+                       } else {
+                               var vcall = new CCodeFunctionCall (new CCodeMemberAccess.pointer (vcast, "set_%s".printf (prop.name)));
+                               vcall.add_argument (new CCodeIdentifier ("self"));
+                               vcall.add_argument (new CCodeIdentifier ("value"));
                        }
 
                        source_type_member_definition.append (function);
@@ -913,10 +900,18 @@ public class Vala.CCodeGenerator : CodeGenerator {
                                function = new CCodeFunction (cname, "void");
                        }
 
+                       ObjectType base_type = null;
                        if (is_virtual) {
+                               if (prop.base_property != null) {
+                                       base_type = new ObjectType ((ObjectTypeSymbol) prop.base_property.parent_symbol);
+                               } else if (prop.base_interface_property != null) {
+                                       base_type = new ObjectType ((ObjectTypeSymbol) prop.base_interface_property.parent_symbol);
+                               }
                                function.modifiers |= CCodeModifiers.STATIC;
+                               function.add_parameter (new CCodeFormalParameter ("base", base_type.get_cname ()));
+                       } else {
+                               function.add_parameter (cselfparam);
                        }
-                       function.add_parameter (cselfparam);
                        if (returns_real_struct) {
                                // return non simple structs as out parameter
                                var coutparamname = "%s*".printf (prop.property_type.get_cname ());
@@ -940,16 +935,24 @@ public class Vala.CCodeGenerator : CodeGenerator {
 
                        function.block = (CCodeBlock) acc.body.ccodenode;
 
+                       if (is_virtual) {
+                               var cdecl = new CCodeDeclaration (this_type.get_cname ());
+                               cdecl.add_declarator (new CCodeVariableDeclarator.with_initializer ("self", transform_expression (new CCodeIdentifier ("base"), base_type, this_type)));
+                               function.block.prepend_statement (cdecl);
+                       }
+
                        if (current_method_inner_error) {
                                var cdecl = new CCodeDeclaration ("GError *");
                                cdecl.add_declarator (new CCodeVariableDeclarator.with_initializer ("inner_error", new CCodeConstant ("NULL")));
                                function.block.prepend_statement (cdecl);
                        }
 
-                       if (returns_real_struct) {
-                               function.block.prepend_statement (create_property_type_check_statement (prop, false, t, true, "self"));
-                       } else {
-                               function.block.prepend_statement (create_property_type_check_statement (prop, acc.readable, t, true, "self"));
+                       if (!is_virtual) {
+                               if (returns_real_struct) {
+                                       function.block.prepend_statement (create_property_type_check_statement (prop, false, t, true, "self"));
+                               } else {
+                                       function.block.prepend_statement (create_property_type_check_statement (prop, acc.readable, t, true, "self"));
+                               }
                        }
 
                        // notify on property changes
index 750133c..a7b6e6c 100644 (file)
@@ -654,22 +654,6 @@ public class Vala.SemanticAnalyzer : CodeVisitor {
                        return;
                }
 
-               /* abstract/virtual properties using reference types without
-                * reference counting need to transfer ownership of their
-                * return values because of limitations in the GObject property
-                * system (g_object_get always returns strong references).
-                * Reference counting types can simulate to return a weak
-                * reference */
-               if ((prop.is_abstract || prop.is_virtual) &&
-                   prop.property_type.data_type != null &&
-                   prop.property_type.data_type.is_reference_type () &&
-                   !prop.property_type.data_type.is_reference_counting () &&
-                   !prop.property_type.value_owned)
-               {
-                       Report.error (prop.source_reference, "%s: abstract or virtual properties using reference types not supporting reference counting, like `%s', have to mark their return value to transfer ownership.".printf (prop.get_full_name (), prop.property_type.data_type.get_full_name ()));
-                       prop.error = true;
-               }
-
                current_symbol = current_symbol.parent_symbol;
 
                if (!prop.is_internal_symbol ()) {