From: Juerg Billeter Date: Tue, 15 Jan 2008 21:01:26 +0000 (+0000) Subject: add basic support for method pre- and postconditions X-Git-Tag: VALA_0_1_6~29 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=e53f3a38e2c030c828eccd47a48418dbe93743ee;p=thirdparty%2Fvala.git add basic support for method pre- and postconditions 2008-01-15 Juerg Billeter * 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 --- diff --git a/ChangeLog b/ChangeLog index 3c4d61360..b2bf6721c 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,11 @@ +2008-01-15 Jürg Billeter + + * 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 * vala/parser.y, vala/valainvocationexpression.vala, diff --git a/gobject/valaccodegeneratormethod.vala b/gobject/valaccodegeneratormethod.vala index b2b5df72c..7cab51bf2 100644 --- a/gobject/valaccodegeneratormethod.vala +++ b/gobject/valaccodegeneratormethod.vala @@ -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"); diff --git a/vala/parser.y b/vala/parser.y index 198033dbf..e4105bff5 100644 --- a/vala/parser.y +++ b/vala/parser.y @@ -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 fixed_parameter %type opt_throws_declaration %type throws_declaration +%type opt_requires_declarations +%type requires_declarations +%type requires_declaration +%type opt_ensures_declarations +%type ensures_declarations +%type ensures_declaration %type signal_declaration %type constructor_declaration %type 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 { diff --git a/vala/scanner.l b/vala/scanner.l index 7510875fc..3940e45b1 100644 --- a/vala/scanner.l +++ b/vala/scanner.l @@ -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; } diff --git a/vala/valaclass.vala b/vala/valaclass.vala index 1277053dc..ce3b0c4a4 100644 --- a/vala/valaclass.vala +++ b/vala/valaclass.vala @@ -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; diff --git a/vala/valainterface.vala b/vala/valainterface.vala index 31e6cb9c0..12ae31a31 100644 --- a/vala/valainterface.vala +++ b/vala/valainterface.vala @@ -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); diff --git a/vala/valamethod.vala b/vala/valamethod.vala index 831cc5552..59cb07016 100644 --- a/vala/valamethod.vala +++ b/vala/valamethod.vala @@ -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 error_domains = new ArrayList (); + private Gee.List preconditions = new ArrayList (); + private Gee.List postconditions = new ArrayList (); 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 (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 get_preconditions () { + return new ReadOnlyCollection (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 get_postconditions () { + return new ReadOnlyCollection (postconditions); + } + public override void replace_type (DataType! old_type, DataType! new_type) { if (return_type == old_type) { return_type = new_type; diff --git a/vala/valasemanticanalyzer.vala b/vala/valasemanticanalyzer.vala index 11e174aa8..9bcf65b7c 100644 --- a/vala/valasemanticanalyzer.vala +++ b/vala/valasemanticanalyzer.vala @@ -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) { diff --git a/vala/valastruct.vala b/vala/valastruct.vala index 036d18205..afbe6034c 100644 --- a/vala/valastruct.vala +++ b/vala/valastruct.vala @@ -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;