Add support for dynamic properties
authorJuerg Billeter <j@bitron.ch>
Sun, 18 May 2008 16:57:27 +0000 (16:57 +0000)
committerJürg Billeter <juergbi@src.gnome.org>
Sun, 18 May 2008 16:57:27 +0000 (16:57 +0000)
2008-05-18  Juerg Billeter  <j@bitron.ch>

* vala/Makefile.am:
* vala/valacodegenerator.vala:
* vala/valadynamicproperty.vala:
* vala/valasemanticanalyzer.vala:
* gobject/Makefile.am:
* gobject/valaccodedynamicmethodbinding.vala:
* gobject/valaccodedynamicpropertybinding.vala:
* gobject/valaccodegenerator.vala:
* gobject/valaccodememberaccessbinding.vala:

Add support for dynamic properties

svn path=/trunk/; revision=1398

ChangeLog
gobject/Makefile.am
gobject/valaccodedynamicmethodbinding.vala
gobject/valaccodedynamicpropertybinding.vala [new file with mode: 0644]
gobject/valaccodegenerator.vala
gobject/valaccodememberaccessbinding.vala
vala/Makefile.am
vala/valacodegenerator.vala
vala/valadynamicproperty.vala [new file with mode: 0644]
vala/valasemanticanalyzer.vala

index d4e6190..bb95386 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,17 @@
+2008-05-18  Jürg Billeter  <j@bitron.ch>
+
+       * vala/Makefile.am:
+       * vala/valacodegenerator.vala:
+       * vala/valadynamicproperty.vala:
+       * vala/valasemanticanalyzer.vala:
+       * gobject/Makefile.am:
+       * gobject/valaccodedynamicmethodbinding.vala:
+       * gobject/valaccodedynamicpropertybinding.vala:
+       * gobject/valaccodegenerator.vala:
+       * gobject/valaccodememberaccessbinding.vala:
+
+       Add support for dynamic properties
+
 2008-05-17  Jürg Billeter  <j@bitron.ch>
 
        * vapi/glib-2.0.vapi: fix typo in GSpawnFlags binding,
index 6a0d9d1..8d5c11a 100644 (file)
@@ -19,6 +19,7 @@ libvala_la_VALASOURCES = \
        valaccodecompiler.vala \
        valaccodecreationmethodbinding.vala \
        valaccodedynamicmethodbinding.vala \
+       valaccodedynamicpropertybinding.vala \
        valaccodedynamicsignalbinding.vala \
        valaccodeelementaccessbinding.vala \
        valaccodeexpressionbinding.vala \
index aff92b1..17a2f24 100644 (file)
@@ -39,7 +39,7 @@ public class Vala.CCodeDynamicMethodBinding : CCodeMethodBinding {
 
                var cparam_map = new HashMap<int,CCodeFormalParameter> (direct_hash, direct_equal);
 
-               var instance_param = new CCodeFormalParameter ("obj", "gpointer");
+               var instance_param = new CCodeFormalParameter ("obj", dynamic_method.dynamic_type.get_cname ());
                cparam_map.set (codegen.get_param_pos (method.cinstance_parameter_position), instance_param);
 
                generate_cparameters (method, method.return_type, cparam_map, func);
diff --git a/gobject/valaccodedynamicpropertybinding.vala b/gobject/valaccodedynamicpropertybinding.vala
new file mode 100644 (file)
index 0000000..d0c37a4
--- /dev/null
@@ -0,0 +1,92 @@
+/* valaccodedynamicpropertybinding.vala
+ *
+ * Copyright (C) 2008  Jürg Billeter
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301  USA
+ *
+ * Author:
+ *     Jürg Billeter <j@bitron.ch>
+ */
+
+using GLib;
+using Gee;
+
+/**
+ * The link between a dynamic property and generated code.
+ */
+public class Vala.CCodeDynamicPropertyBinding : CCodeBinding {
+       public Property node { get; set; }
+
+       string? getter_cname;
+       string? setter_cname;
+
+       static int dynamic_property_id;
+
+       public CCodeDynamicPropertyBinding (CCodeGenerator codegen, DynamicProperty property) {
+               this.node = property;
+               this.codegen = codegen;
+       }
+
+       public string get_getter_cname () {
+               if (getter_cname != null) {
+                       return getter_cname;
+               }
+
+               getter_cname = "_dynamic_get_%s%d".printf (node.name, dynamic_property_id++);
+
+               var dynamic_property = (DynamicProperty) node;
+
+               var func = new CCodeFunction (getter_cname, node.property_type.get_cname ());
+
+               func.add_parameter (new CCodeFormalParameter ("obj", dynamic_property.dynamic_type.get_cname ()));
+
+               var block = new CCodeBlock ();
+               Report.error (node.source_reference, "dynamic properties are not supported for `%s'".printf (dynamic_property.dynamic_type.to_string ()));
+
+               // append to C source file
+               codegen.source_type_member_declaration.append (func.copy ());
+
+               func.block = block;
+               codegen.source_type_member_definition.append (func);
+
+               return getter_cname;
+       }
+
+       public string get_setter_cname () {
+               if (setter_cname != null) {
+                       return setter_cname;
+               }
+
+               getter_cname = "_dynamic_set_%s%d".printf (node.name, dynamic_property_id++);
+
+               var dynamic_property = (DynamicProperty) node;
+
+               var func = new CCodeFunction (getter_cname, "void");
+
+               func.add_parameter (new CCodeFormalParameter ("obj", dynamic_property.dynamic_type.get_cname ()));
+               func.add_parameter (new CCodeFormalParameter ("value", node.property_type.get_cname ()));
+
+               var block = new CCodeBlock ();
+               Report.error (node.source_reference, "dynamic properties are not supported for `%s'".printf (dynamic_property.dynamic_type.to_string ()));
+
+               // append to C source file
+               codegen.source_type_member_declaration.append (func.copy ());
+
+               func.block = block;
+               codegen.source_type_member_definition.append (func);
+
+               return getter_cname;
+       }
+}
index 5882af3..2eb4988 100644 (file)
@@ -3629,6 +3629,9 @@ public class Vala.CCodeGenerator : CodeGenerator {
                        }
                        var base_property_type = (Typesymbol) base_property.parent_symbol;
                        set_func = "%s_set_%s".printf (base_property_type.get_lower_case_cname (null), base_property.name);
+                       if (prop is DynamicProperty) {
+                               set_func = dynamic_property_binding ((DynamicProperty) prop).get_setter_cname ();
+                       }
                }
                
                var ccall = new CCodeFunctionCall (new CCodeIdentifier (set_func));
@@ -3875,6 +3878,10 @@ public class Vala.CCodeGenerator : CodeGenerator {
                return null;
        }
 
+       public override CodeBinding? create_dynamic_property_binding (DynamicProperty node) {
+               return new CCodeDynamicPropertyBinding (this, node);
+       }
+
        public override CodeBinding? create_property_accessor_binding (PropertyAccessor node) {
                return null;
        }
@@ -4103,6 +4110,10 @@ public class Vala.CCodeGenerator : CodeGenerator {
                return (CCodeDynamicMethodBinding) node.get_code_binding (this);
        }
 
+       public CCodeDynamicPropertyBinding dynamic_property_binding (DynamicProperty node) {
+               return (CCodeDynamicPropertyBinding) node.get_code_binding (this);
+       }
+
        public CCodeDynamicSignalBinding dynamic_signal_binding (DynamicSignal node) {
                return (CCodeDynamicSignalBinding) node.get_code_binding (this);
        }
index ef1408c..83126df 100644 (file)
@@ -131,7 +131,13 @@ public class Vala.CCodeMemberAccessBinding : CCodeExpressionBinding {
                                        base_property = prop.base_interface_property;
                                }
                                var base_property_type = (Typesymbol) base_property.parent_symbol;
-                               var ccall = new CCodeFunctionCall (new CCodeIdentifier ("%s_get_%s".printf (base_property_type.get_lower_case_cname (null), base_property.name)));
+                               string getter_cname;
+                               if (prop is DynamicProperty) {
+                                       getter_cname = codegen.dynamic_property_binding ((DynamicProperty) prop).get_getter_cname ();
+                               } else {
+                                       getter_cname = "%s_get_%s".printf (base_property_type.get_lower_case_cname (null), base_property.name);
+                               }
+                               var ccall = new CCodeFunctionCall (new CCodeIdentifier (getter_cname));
 
                                var instance_expression_type = base_type;
                                var instance_target_type = codegen.get_data_type_for_symbol (base_property_type);
index 9d4bf4b..89aa6e8 100644 (file)
@@ -54,6 +54,7 @@ libvalacore_la_VALASOURCES = \
        valadestructor.vala \
        valadostatement.vala \
        valadynamicmethod.vala \
+       valadynamicproperty.vala \
        valadynamicsignal.vala \
        valaelementaccess.vala \
        valaemptystatement.vala \
index 3367e83..2a35272 100644 (file)
@@ -98,6 +98,10 @@ public class Vala.CodeGenerator : CodeVisitor {
                return null;
        }
 
+       public virtual CodeBinding? create_dynamic_property_binding (DynamicProperty node) {
+               return null;
+       }
+
        public virtual CodeBinding? create_property_accessor_binding (PropertyAccessor node) {
                return null;
        }
diff --git a/vala/valadynamicproperty.vala b/vala/valadynamicproperty.vala
new file mode 100644 (file)
index 0000000..ea76ca5
--- /dev/null
@@ -0,0 +1,47 @@
+/* valadynamicproperty.vala
+ *
+ * Copyright (C) 2008  Jürg Billeter
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301  USA
+ *
+ * Author:
+ *     Jürg Billeter <j@bitron.ch>
+ */
+
+using GLib;
+using Gee;
+
+/**
+ * Represents a late bound property.
+ */
+public class Vala.DynamicProperty : Property {
+       public DataType dynamic_type { get; set; }
+
+       private string cname;
+
+       public DynamicProperty (DataType dynamic_type, string name, SourceReference? source_reference = null) {
+               this.dynamic_type = dynamic_type;
+               this.name = name;
+               this.source_reference = source_reference;
+       }
+
+       public override Collection<string> get_cheader_filenames () {
+               return new ReadOnlyCollection<string> ();
+       }
+
+       public override CodeBinding? create_code_binding (CodeGenerator codegen) {
+               return codegen.create_dynamic_property_binding (this);
+       }
+}
index d3a8e64..02c3989 100644 (file)
@@ -1380,9 +1380,11 @@ public class Vala.SemanticAnalyzer : CodeVisitor {
                        return c.type_reference;
                } else if (sym is Property) {
                        var prop = (Property) sym;
-                       var type = prop.property_type.copy ();
-                       type.value_owned = false;
-                       return type;
+                       if (prop.property_type != null) {
+                               var type = prop.property_type.copy ();
+                               type.value_owned = false;
+                               return type;
+                       }
                } else if (sym is FormalParameter) {
                        var p = (FormalParameter) sym;
                        var type = p.parameter_type.copy ();
@@ -1587,32 +1589,65 @@ public class Vala.SemanticAnalyzer : CodeVisitor {
                                // allow late bound members for dynamic types
                                if (expr.parent_node is InvocationExpression) {
                                        var invoc = (InvocationExpression) expr.parent_node;
-                                       DataType ret_type;
-                                       if (invoc.expected_type != null) {
-                                               ret_type = invoc.expected_type.copy ();
-                                               ret_type.value_owned = true;
-                                       } else {
-                                               ret_type = new VoidType ();
+                                       if (invoc.call == expr) {
+                                               // dynamic method
+                                               DataType ret_type;
+                                               if (invoc.expected_type != null) {
+                                                       ret_type = invoc.expected_type.copy ();
+                                                       ret_type.value_owned = true;
+                                               } else if (invoc.parent_node is ExpressionStatement) {
+                                                       ret_type = new VoidType ();
+                                               } else {
+                                                       // expect dynamic object of the same type
+                                                       ret_type = expr.inner.value_type.copy ();
+                                               }
+                                               var m = new DynamicMethod (expr.inner.value_type, expr.member_name, ret_type, expr.source_reference);
+                                               m.invocation = invoc;
+                                               m.add_error_domain (new ErrorType (null));
+                                               m.access = SymbolAccessibility.PUBLIC;
+                                               m.add_parameter (new FormalParameter.with_ellipsis ());
+                                               context.add_dynamic_member (m);
+                                               expr.symbol_reference = m;
                                        }
-                                       var m = new DynamicMethod (expr.inner.value_type, expr.member_name, ret_type, expr.source_reference);
-                                       m.invocation = invoc;
-                                       m.add_error_domain (new ErrorType (null));
-                                       m.access = SymbolAccessibility.PUBLIC;
-                                       m.add_parameter (new FormalParameter.with_ellipsis ());
-                                       context.add_dynamic_member (m);
-                                       expr.symbol_reference = m;
                                } else if (expr.parent_node is Assignment) {
                                        var a = (Assignment) expr.parent_node;
                                        if (a.left == expr
                                            && (a.operator == AssignmentOperator.ADD
                                                || a.operator == AssignmentOperator.SUB)) {
+                                               // dynamic signal
                                                var s = new DynamicSignal (expr.inner.value_type, expr.member_name, new VoidType (), expr.source_reference);
                                                s.handler = a.right;
                                                s.access = SymbolAccessibility.PUBLIC;
                                                context.add_dynamic_member (s);
                                                expr.symbol_reference = s;
+                                       } else if (a.left == expr) {
+                                               // dynamic property assignment
+                                               var prop = new DynamicProperty (expr.inner.value_type, expr.member_name, expr.source_reference);
+                                               prop.access = SymbolAccessibility.PUBLIC;
+                                               prop.set_accessor = new PropertyAccessor (false, true, false, null, null);
+                                               prop.set_accessor.access = SymbolAccessibility.PUBLIC;
+                                               prop.owner = expr.inner.value_type.data_type.scope;
+                                               context.add_dynamic_member (prop);
+                                               expr.symbol_reference = prop;
                                        }
                                }
+                               if (expr.symbol_reference == null) {
+                                       // dynamic property read access
+                                       var prop = new DynamicProperty (expr.inner.value_type, expr.member_name, expr.source_reference);
+                                       if (expr.expected_type != null) {
+                                               prop.property_type = expr.expected_type;
+                                       } else {
+                                               // expect dynamic object of the same type
+                                               prop.property_type = expr.inner.value_type.copy ();
+                                       }
+                                       prop.access = SymbolAccessibility.PUBLIC;
+                                       prop.get_accessor = new PropertyAccessor (true, false, false, null, null);
+                                       prop.get_accessor.access = SymbolAccessibility.PUBLIC;
+                                       prop.owner = expr.inner.value_type.data_type.scope;
+                                       // maybe better move add_dynamic_member to Symbol class
+                                       context.add_dynamic_member (prop);
+                                       expr.symbol_reference = prop;
+                               }
                                if (expr.symbol_reference != null) {
                                        may_access_instance_members = true;
                                }
@@ -2970,7 +3005,7 @@ public class Vala.SemanticAnalyzer : CodeVisitor {
                if (a.left is MemberAccess) {
                        var ma = (MemberAccess) a.left;
 
-                       if (!(ma.symbol_reference is Signal) && ma.value_type == null) {
+                       if (!(ma.symbol_reference is Signal || ma.symbol_reference is DynamicProperty) && ma.value_type == null) {
                                a.error = true;
                                Report.error (a.source_reference, "unsupported lvalue in assignment");
                                return;
@@ -3069,6 +3104,12 @@ public class Vala.SemanticAnalyzer : CodeVisitor {
                        } else if (ma.symbol_reference is Property) {
                                var prop = (Property) ma.symbol_reference;
 
+                               var dynamic_prop = prop as DynamicProperty;
+                               if (dynamic_prop != null) {
+                                       dynamic_prop.property_type = a.right.value_type.copy ();
+                                       a.left.value_type = dynamic_prop.property_type.copy ();
+                               }
+
                                if (prop.set_accessor == null
                                    || (!prop.set_accessor.writable && !(find_current_method () is CreationMethod || is_in_constructor ()))) {
                                        ma.error = true;