+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,
// 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));
+ }
}
}
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;
}
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 {
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 ();
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");
%token DELEGATE "delegate"
%token DO "do"
%token ELSE "else"
+%token ENSURES "ensures"
%token ENUM "enum"
%token VALA_FALSE "false"
%token FINALLY "finally"
%token PROTECTED "protected"
%token PUBLIC "public"
%token REF "ref"
+%token REQUIRES "requires"
%token RETURN "return"
%token SET "set"
%token SIGNAL "signal"
%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
| 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
{
$$ = g_strdup ("set");
}
+ | REQUIRES
+ {
+ $$ = g_strdup ("requires");
+ }
+ | ENSURES
+ {
+ $$ = g_strdup ("ensures");
+ }
;
literal
;
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;
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);
}
}
;
+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
{
"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; }
"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; }
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;
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);
/* 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
* 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.
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;
/**
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);
}
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;
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) {
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;