add basic support for method pre- and postconditions
authorJuerg Billeter <j@bitron.ch>
Tue, 15 Jan 2008 21:01:26 +0000 (21:01 +0000)
committerJürg Billeter <juergbi@src.gnome.org>
Tue, 15 Jan 2008 21:01:26 +0000 (21:01 +0000)
2008-01-15  Juerg Billeter  <j@bitron.ch>

* vala/parser.y, vala/scanner.l, vala/valaclass.vala,
  vala/valainterface.vala, vala/valamethod.vala,
  vala/valasemanticanalyzer.vala, vala/valastruct.vala,
  gobject/valaccodegeneratormethod.vala: add basic support for
  method pre- and postconditions

svn path=/trunk/; revision=838

ChangeLog
gobject/valaccodegeneratormethod.vala
vala/parser.y
vala/scanner.l
vala/valaclass.vala
vala/valainterface.vala
vala/valamethod.vala
vala/valasemanticanalyzer.vala
vala/valastruct.vala

index 3c4d613..b2bf672 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,11 @@
+2008-01-15  Jürg Billeter  <j@bitron.ch>
+
+       * vala/parser.y, vala/scanner.l, vala/valaclass.vala,
+         vala/valainterface.vala, vala/valamethod.vala,
+         vala/valasemanticanalyzer.vala, vala/valastruct.vala,
+         gobject/valaccodegeneratormethod.vala: add basic support for
+         method pre- and postconditions
+
 2008-01-14  Jürg Billeter  <j@bitron.ch>
 
        * vala/parser.y, vala/valainvocationexpression.vala,
index b2b5df7..7cab51b 100644 (file)
@@ -418,6 +418,10 @@ public class Vala.CCodeGenerator {
                                        // GTypeModule-based plug-in, register types
                                        cinit.append (module_init_fragment);
                                }
+
+                               foreach (Expression precondition in m.get_preconditions ()) {
+                                       cinit.append (create_precondition_statement (m, creturn_type, precondition));
+                               }
                        }
                }
                
@@ -436,7 +440,11 @@ public class Vala.CCodeGenerator {
                        vfunc.add_parameter (cparam);
                        
                        var vblock = new CCodeBlock ();
-                       
+
+                       foreach (Expression precondition in m.get_preconditions ()) {
+                               vblock.add_statement (create_precondition_statement (m, creturn_type, precondition));
+                       }
+
                        CCodeFunctionCall vcast = null;
                        if (m.parent_symbol is Interface) {
                                var iface = (Interface) m.parent_symbol;
@@ -491,15 +499,27 @@ public class Vala.CCodeGenerator {
                        }
 
                        CCodeStatement cstmt;
-                       if (creturn_type.data_type == null && creturn_type.type_parameter == null) {
+                       if (creturn_type is VoidType) {
                                cstmt = new CCodeExpressionStatement (vcall);
                        } else {
-                               /* pass method return value */
-                               cstmt = new CCodeReturnStatement (vcall);
+                               /* store method return value */
+                               var cdecl = new CCodeDeclaration (creturn_type.get_cname ());
+                               cdecl.add_declarator (new CCodeVariableDeclarator.with_initializer ("result", vcall));
+                               cstmt = cdecl;
                        }
                        cstmt.line = vfunc.line;
                        vblock.add_statement (cstmt);
 
+                       foreach (Expression postcondition in m.get_postconditions ()) {
+                               vblock.add_statement (create_postcondition_statement (postcondition));
+                       }
+
+                       if (!(creturn_type is VoidType)) {
+                               var cret_stmt = new CCodeReturnStatement (new CCodeIdentifier ("result"));
+                               cret_stmt.line = vfunc.line;
+                               vblock.add_statement (cret_stmt);
+                       }
+
                        if (visible) {
                                header_type_member_declaration.append (vfunc.copy ());
                        } else {
@@ -592,7 +612,7 @@ public class Vala.CCodeGenerator {
                        return create_type_check_statement (prop, new VoidType (), t, non_null, var_name);
                }
        }
-       
+
        private CCodeStatement create_type_check_statement (CodeNode! method_node, DataType ret_type, Typesymbol! t, bool non_null, string! var_name) {
                var ccheck = new CCodeFunctionCall ();
                
@@ -632,6 +652,37 @@ public class Vala.CCodeGenerator {
                return new CCodeExpressionStatement (ccheck);
        }
 
+       private CCodeStatement create_precondition_statement (CodeNode! method_node, DataType ret_type, Expression precondition) {
+               var ccheck = new CCodeFunctionCall ();
+
+               ccheck.add_argument ((CCodeExpression) precondition.ccodenode);
+
+               if (ret_type is VoidType) {
+                       /* void function */
+                       ccheck.call = new CCodeIdentifier ("g_return_if_fail");
+               } else {
+                       ccheck.call = new CCodeIdentifier ("g_return_val_if_fail");
+
+                       var cdefault = default_value_for_type (ret_type);
+                       if (cdefault != null) {
+                               ccheck.add_argument (cdefault);
+                       } else {
+                               Report.warning (method_node.source_reference, "not supported return type for runtime type checks");
+                               return new CCodeExpressionStatement (new CCodeConstant ("0"));
+                       }
+               }
+               
+               return new CCodeExpressionStatement (ccheck);
+       }
+
+       private CCodeStatement create_postcondition_statement (Expression postcondition) {
+               var cassert = new CCodeFunctionCall (new CCodeIdentifier ("g_assert"));
+
+               cassert.add_argument ((CCodeExpression) postcondition.ccodenode);
+
+               return new CCodeExpressionStatement (cassert);
+       }
+
        private CCodeExpression default_value_for_type (DataType! type) {
                if ((type.data_type != null && type.data_type.is_reference_type ()) || type is PointerType) {
                        return new CCodeConstant ("NULL");
index 198033d..e4105bf 100644 (file)
@@ -169,6 +169,7 @@ static gboolean check_is_struct (ValaSymbol *symbol, ValaSourceReference *src);
 %token DELEGATE "delegate"
 %token DO "do"
 %token ELSE "else"
+%token ENSURES "ensures"
 %token ENUM "enum"
 %token VALA_FALSE "false"
 %token FINALLY "finally"
@@ -190,6 +191,7 @@ static gboolean check_is_struct (ValaSymbol *symbol, ValaSourceReference *src);
 %token PROTECTED "protected"
 %token PUBLIC "public"
 %token REF "ref"
+%token REQUIRES "requires"
 %token RETURN "return"
 %token SET "set"
 %token SIGNAL "signal"
@@ -358,6 +360,12 @@ static gboolean check_is_struct (ValaSymbol *symbol, ValaSourceReference *src);
 %type <formal_parameter> fixed_parameter
 %type <list> opt_throws_declaration
 %type <list> throws_declaration
+%type <list> opt_requires_declarations
+%type <list> requires_declarations
+%type <expression> requires_declaration
+%type <list> opt_ensures_declarations
+%type <list> ensures_declarations
+%type <expression> ensures_declaration
 %type <signal> signal_declaration
 %type <constructor> constructor_declaration
 %type <destructor> destructor_declaration
@@ -393,7 +401,7 @@ opt_comma
        | COMMA
        ;
 
-/* identifiers never conflict with context-specific keywords get or set */
+/* identifiers never conflict with context-specific keywords get, set, requires, or ensures */
 identifier
        : IDENTIFIER
        | GET
@@ -404,6 +412,14 @@ identifier
          {
                $$ = g_strdup ("set");
          }
+       | REQUIRES
+         {
+               $$ = g_strdup ("requires");
+         }
+       | ENSURES
+         {
+               $$ = g_strdup ("ensures");
+         }
        ;
 
 literal
@@ -3001,7 +3017,7 @@ method_declaration
        ;
 
 method_header
-       : comment opt_attributes opt_access_modifier opt_modifiers type identifier OPEN_PARENS opt_formal_parameter_list CLOSE_PARENS opt_throws_declaration
+       : comment opt_attributes opt_access_modifier opt_modifiers type identifier OPEN_PARENS opt_formal_parameter_list CLOSE_PARENS opt_throws_declaration opt_requires_declarations opt_ensures_declarations
          {
                GList *l;
                ValaSourceReference *src;
@@ -3059,6 +3075,22 @@ method_header
                        g_list_free ($10);
                }
 
+               if ($11 != NULL) {
+                       for (l = $11; l != NULL; l = l->next) {
+                               vala_method_add_precondition ($$, l->data);
+                               g_object_unref (l->data);
+                       }
+                       g_list_free ($11);
+               }
+
+               if ($12 != NULL) {
+                       for (l = $12; l != NULL; l = l->next) {
+                               vala_method_add_postcondition ($$, l->data);
+                               g_object_unref (l->data);
+                       }
+                       g_list_free ($12);
+               }
+
                g_object_unref ($5);
                g_free ($6);
          }
@@ -3219,6 +3251,58 @@ throws_declaration
          }
        ;
 
+opt_requires_declarations
+       : /* empty */
+         {
+               $$ = NULL;
+         }
+       | requires_declarations
+       ;
+
+requires_declarations
+       : requires_declaration
+         {
+               $$ = g_list_append (NULL, $1);
+         }
+       | requires_declarations requires_declaration
+         {
+               $$ = g_list_append ($1, $2);
+         }
+       ;
+
+requires_declaration
+       : REQUIRES open_parens expression CLOSE_PARENS
+         {
+               $$ = $3;
+         }
+       ;
+
+opt_ensures_declarations
+       : /* empty */
+         {
+               $$ = NULL;
+         }
+       | ensures_declarations
+       ;
+
+ensures_declarations
+       : ensures_declaration
+         {
+               $$ = g_list_append (NULL, $1);
+         }
+       | ensures_declarations ensures_declaration
+         {
+               $$ = g_list_append ($1, $2);
+         }
+       ;
+
+ensures_declaration
+       : ENSURES open_parens expression CLOSE_PARENS
+         {
+               $$ = $3;
+         }
+       ;
+
 property_declaration
        : comment opt_attributes opt_access_modifier opt_modifiers type identifier OPEN_BRACE get_accessor_declaration opt_set_accessor_declaration CLOSE_BRACE
          {
index 7510875..3940e45 100644 (file)
@@ -146,6 +146,7 @@ literal                             ({integer_literal}|{real_literal}|{character_literal}|{string_literal
 "delegate"     { uploc; return DELEGATE; }
 "do"           { uploc; return DO; }
 "else"         { uploc; return ELSE; }
+"ensures"      { uploc; return ENSURES; }
 "enum"         { uploc; return ENUM; }
 "false"                { uploc; return VALA_FALSE; }
 "finally"      { uploc; return FINALLY; }
@@ -167,6 +168,7 @@ literal                             ({integer_literal}|{real_literal}|{character_literal}|{string_literal
 "protected"    { uploc; return PROTECTED; }
 "public"       { uploc; return PUBLIC; }
 "ref"          { uploc; return REF; }
+"requires"     { uploc; return REQUIRES; }
 "set"          { uploc; return SET; }
 "signal"       { uploc; return SIGNAL; }
 "sizeof"       { uploc; return SIZEOF; }
index 1277053..ce3b0c4 100644 (file)
@@ -190,6 +190,11 @@ public class Vala.Class : Typesymbol {
                        m.this_parameter = new FormalParameter ("this", new ClassType (this));
                        m.scope.add (m.this_parameter.name, m.this_parameter);
                }
+               if (!(m.return_type is VoidType)) {
+                       m.result_var = new VariableDeclarator ("result");
+                       m.result_var.type_reference = m.return_type.copy ();
+                       m.scope.add (m.result_var.name, m.result_var);
+               }
                if (m is CreationMethod) {
                        if (m.name == null) {
                                default_construction_method = m;
index 31e6cb9..12ae31a 100644 (file)
@@ -125,6 +125,11 @@ public class Vala.Interface : Typesymbol {
                        m.this_parameter = new FormalParameter ("this", new InterfaceType (this));
                        m.scope.add (m.this_parameter.name, m.this_parameter);
                }
+               if (!(m.return_type is VoidType)) {
+                       m.result_var = new VariableDeclarator ("result");
+                       m.result_var.type_reference = m.return_type.copy ();
+                       m.scope.add (m.result_var.name, m.result_var);
+               }
 
                methods.add (m);
                scope.add (m.name, m);
index 831cc55..59cb070 100644 (file)
@@ -1,6 +1,6 @@
 /* valamethod.vala
  *
- * Copyright (C) 2006-2007  Jürg Billeter, Raffaele Sandrini
+ * Copyright (C) 2006-2008  Jürg Billeter, Raffaele Sandrini
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
@@ -141,7 +141,12 @@ public class Vala.Method : Member {
         * Specifies the generated `this' parameter for instance methods.
         */
        public FormalParameter this_parameter { get; set; }
-       
+
+       /**
+        * Specifies the generated `result' variable for postconditions.
+        */
+       public VariableDeclarator result_var { get; set; }
+
        /**
         * Specifies whether the array length should implicitly be passed
         * if the parameter type is an array.
@@ -170,6 +175,8 @@ public class Vala.Method : Member {
        private string _sentinel;
        private bool _no_array_length;
        private Gee.List<DataType> error_domains = new ArrayList<DataType> ();
+       private Gee.List<Expression> preconditions = new ArrayList<Expression> ();
+       private Gee.List<Expression> postconditions = new ArrayList<Expression> ();
        private DataType _return_type;
 
        /**
@@ -220,6 +227,18 @@ public class Vala.Method : Member {
                        error_domain.accept (visitor);
                }
 
+               if (result_var != null) {
+                       result_var.type_reference.accept (visitor);
+               }
+
+               foreach (Expression precondition in preconditions) {
+                       precondition.accept (visitor);
+               }
+
+               foreach (Expression postcondition in postconditions) {
+                       postcondition.accept (visitor);
+               }
+
                if (body != null) {
                        body.accept (visitor);
                }
@@ -378,6 +397,44 @@ public class Vala.Method : Member {
                return new ReadOnlyCollection<DataType> (error_domains);
        }
 
+       /**
+        * Adds a precondition to this method.
+        *
+        * @param precondition a boolean precondition expression
+        */
+       public void add_precondition (Expression! precondition) {
+               preconditions.add (precondition);
+               precondition.parent_node = this;
+       }
+
+       /**
+        * Returns a copy of the list of preconditions of this method.
+        *
+        * @return list of preconditions
+        */
+       public Collection<Expression> get_preconditions () {
+               return new ReadOnlyCollection<Expression> (preconditions);
+       }
+
+       /**
+        * Adds a postcondition to this method.
+        *
+        * @param postcondition a boolean postcondition expression
+        */
+       public void add_postcondition (Expression! postcondition) {
+               postconditions.add (postcondition);
+               postcondition.parent_node = this;
+       }
+
+       /**
+        * Returns a copy of the list of postconditions of this method.
+        *
+        * @return list of postconditions
+        */
+       public Collection<Expression> get_postconditions () {
+               return new ReadOnlyCollection<Expression> (postconditions);
+       }
+
        public override void replace_type (DataType! old_type, DataType! new_type) {
                if (return_type == old_type) {
                        return_type = new_type;
index 11e174a..9bcf65b 100644 (file)
@@ -391,6 +391,34 @@ public class Vala.SemanticAnalyzer : CodeVisitor {
                                return;
                        }
                }
+
+               /*foreach (Expression precondition in m.get_preconditions ()) {
+                       if (precondition.error) {
+                               // if there was an error in the precondition, skip this check
+                               m.error = true;
+                               return;
+                       }
+
+                       if (!precondition.static_type.compatible (bool_type)) {
+                               m.error = true;
+                               Report.error (precondition.source_reference, "Precondition must be boolean");
+                               return;
+                       }
+               }
+
+               foreach (Expression postcondition in m.get_postconditions ()) {
+                       if (postcondition.error) {
+                               // if there was an error in the postcondition, skip this check
+                               m.error = true;
+                               return;
+                       }
+
+                       if (!postcondition.static_type.compatible (bool_type)) {
+                               m.error = true;
+                               Report.error (postcondition.source_reference, "Postcondition must be boolean");
+                               return;
+                       }
+               }*/
        }
 
        private void find_base_class_method (Method! m, Class! cl) {
index 036d182..afbe603 100644 (file)
@@ -117,6 +117,11 @@ public class Vala.Struct : Typesymbol {
                        m.this_parameter = new FormalParameter ("this", new ValueType (this));
                        m.scope.add (m.this_parameter.name, m.this_parameter);
                }
+               if (!(m.return_type is VoidType)) {
+                       m.result_var = new VariableDeclarator ("result");
+                       m.result_var.type_reference = m.return_type.copy ();
+                       m.scope.add (m.result_var.name, m.result_var);
+               }
                if (m is CreationMethod) {
                        if (m.name == null) {
                                default_construction_method = m;