1 /* valadovaerrormodule.vala
3 * Copyright (C) 2008-2010 Jürg Billeter
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public
7 * License as published by the Free Software Foundation; either
8 * version 2.1 of the License, or (at your option) any later version.
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Lesser General Public License for more details.
15 * You should have received a copy of the GNU Lesser General Public
16 * License along with this library; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20 * Jürg Billeter <j@bitron.ch>
21 * Thijs Vermeir <thijsvermeir@gmail.com>
24 public class Vala
.DovaErrorModule
: DovaDelegateModule
{
25 private int current_try_id
= 0;
26 private int next_try_id
= 0;
27 private bool is_in_catch
= false;
29 public override void visit_throw_statement (ThrowStatement stmt
) {
30 var cfrag
= new
CCodeFragment ();
33 var cassign
= new
CCodeAssignment (new
CCodeIdentifier ("dova_error"), (CCodeExpression
) stmt
.error_expression
.ccodenode
);
34 cfrag
.append (new
CCodeExpressionStatement (cassign
));
36 add_simple_check (stmt
, cfrag
, true);
38 stmt
.ccodenode
= cfrag
;
40 create_temp_decl (stmt
, stmt
.error_expression
.temp_vars
);
43 public virtual CCodeStatement
return_with_exception () {
44 var cerror_block
= new
CCodeBlock ();
49 // free local variables
50 var free_frag
= new
CCodeFragment ();
51 append_local_free (current_symbol
, free_frag
, false);
52 cerror_block
.add_statement (free_frag
);
54 if (current_method is CreationMethod
&& current_method
.parent_symbol is Class
) {
55 var cl
= current_method
.parent_symbol as Class
;
56 var unref_call
= new
CCodeFunctionCall (new
CCodeIdentifier (cl
.get_unref_function ()));
57 unref_call
.add_argument (new
CCodeIdentifier ("this"));
58 cerror_block
.add_statement (new
CCodeExpressionStatement (unref_call
));
59 cerror_block
.add_statement (new
CCodeReturnStatement ());
60 } else if (current_return_type is VoidType
) {
61 cerror_block
.add_statement (new
CCodeReturnStatement ());
63 cerror_block
.add_statement (new
CCodeReturnStatement (default_value_for_type (current_return_type
, false)));
69 CCodeStatement
uncaught_error_statement (CCodeBlock? block
= null, bool unexpected
= false) {
70 var cerror_block
= block
;
71 if (cerror_block
== null) {
72 cerror_block
= new
CCodeBlock ();
75 // free local variables
76 var free_frag
= new
CCodeFragment ();
77 append_local_free (current_symbol
, free_frag
, false);
78 cerror_block
.add_statement (free_frag
);
80 // TODO log uncaught error as critical warning
82 if (current_method is CreationMethod
) {
83 cerror_block
.add_statement (new
CCodeReturnStatement ());
84 } else if (current_return_type is VoidType
) {
85 cerror_block
.add_statement (new
CCodeReturnStatement ());
86 } else if (current_return_type
!= null) {
87 cerror_block
.add_statement (new
CCodeReturnStatement (default_value_for_type (current_return_type
, false)));
93 bool in_finally_block (CodeNode node
) {
94 var current_node
= node
;
95 while (current_node
!= null) {
96 var try_stmt
= current_node
.parent_node as TryStatement
;
97 if (try_stmt
!= null && try_stmt
.finally_body
== current_node
) {
100 current_node
= current_node
.parent_node
;
105 public override void add_simple_check (CodeNode node
, CCodeFragment cfrag
, bool always_fails
= false) {
106 CCodeStatement cerror_handler
= null;
108 if (current_try
!= null) {
109 // surrounding try found
110 var cerror_block
= new
CCodeBlock ();
112 // free local variables
113 var free_frag
= new
CCodeFragment ();
114 append_error_free (current_symbol
, free_frag
, current_try
);
115 cerror_block
.add_statement (free_frag
);
117 var error_types
= new ArrayList
<DataType
> ();
118 foreach (DataType node_error_type
in node
.get_error_types ()) {
119 error_types
.add (node_error_type
);
122 bool has_general_catch_clause
= false;
125 var handled_error_types
= new ArrayList
<DataType
> ();
126 foreach (CatchClause clause
in current_try
.get_catch_clauses ()) {
127 // keep track of unhandled error types
128 foreach (DataType node_error_type
in error_types
) {
129 if (clause
.error_type
== null || node_error_type
.compatible (clause
.error_type
)) {
130 handled_error_types
.add (node_error_type
);
133 foreach (DataType handled_error_type
in handled_error_types
) {
134 error_types
.remove (handled_error_type
);
136 handled_error_types
.clear ();
138 // go to catch clause if error domain matches
139 var cgoto_stmt
= new
CCodeGotoStatement (clause
.clabel_name
);
141 if (clause
.error_type
.equals (new
ObjectType (error_class
))) {
142 // general catch clause, this should be the last one
143 has_general_catch_clause
= true;
144 cerror_block
.add_statement (cgoto_stmt
);
147 var catch_type
= clause
.error_type as ObjectType
;
148 var cgoto_block
= new
CCodeBlock ();
149 cgoto_block
.add_statement (cgoto_stmt
);
151 var type_check
= new
CCodeFunctionCall (new
CCodeIdentifier ("any_is_a"));
152 type_check
.add_argument (new
CCodeIdentifier ("dova_error"));
153 type_check
.add_argument (new
CCodeFunctionCall (new
CCodeIdentifier ("%s_type_get".printf (catch_type
.type_symbol
.get_lower_case_cname ()))));
155 cerror_block
.add_statement (new
CCodeIfStatement (type_check
, cgoto_block
));
160 if (has_general_catch_clause
) {
161 // every possible error is already caught
162 // as there is a general catch clause
163 // no need to do anything else
164 } else if (error_types
.size
> 0) {
165 // go to finally clause if no catch clause matches
166 // and there are still unhandled error types
167 cerror_block
.add_statement (new
CCodeGotoStatement ("__finally%d".printf (current_try_id
)));
168 } else if (in_finally_block (node
)) {
169 // do not check unexpected errors happening within finally blocks
170 // as jump out of finally block is not supported
172 // should never happen with correct bindings
173 uncaught_error_statement (cerror_block
, true);
176 cerror_handler
= cerror_block
;
177 } else if (current_method
!= null && current_method
.get_error_types ().size
> 0) {
178 // current method can fail, propagate error
179 CCodeExpression ccond
= null;
181 foreach (DataType error_type
in current_method
.get_error_types ()) {
182 // If Dova.Error is allowed we propagate everything
183 if (error_type
.equals (new
ObjectType (error_class
))) {
188 // Check the allowed error domains to propagate
189 var type_check
= new
CCodeFunctionCall (new
CCodeIdentifier ("any_is_a"));
190 type_check
.add_argument (new
CCodeIdentifier ("dova_error"));
191 type_check
.add_argument (new
CCodeFunctionCall (new
CCodeIdentifier ("%s_type_get".printf (error_class
.get_lower_case_cname ()))));
195 ccond
= new
CCodeBinaryExpression (CCodeBinaryOperator
.OR
, ccond
, type_check
);
200 cerror_handler
= return_with_exception ();
202 var cerror_block
= new
CCodeBlock ();
203 cerror_block
.add_statement (new
CCodeIfStatement (ccond
,
204 return_with_exception (),
205 uncaught_error_statement ()));
206 cerror_handler
= cerror_block
;
209 cerror_handler
= uncaught_error_statement ();
213 // inner_error is always set, avoid unnecessary if statement
214 // eliminates C warnings
215 cfrag
.append (cerror_handler
);
217 var ccond
= new
CCodeBinaryExpression (CCodeBinaryOperator
.INEQUALITY
, new
CCodeIdentifier ("dova_error"), new
CCodeConstant ("NULL"));
218 cfrag
.append (new
CCodeIfStatement (ccond
, cerror_handler
));
222 public override void visit_try_statement (TryStatement stmt
) {
223 int this_try_id
= next_try_id
++;
225 var old_try
= current_try
;
226 var old_try_id
= current_try_id
;
227 var old_is_in_catch
= is_in_catch
;
229 current_try_id
= this_try_id
;
232 foreach (CatchClause clause
in stmt
.get_catch_clauses ()) {
233 clause
.clabel_name
= "__catch%d_%s".printf (this_try_id
, clause
.error_type
.get_lower_case_cname ());
236 if (stmt
.finally_body
!= null) {
237 stmt
.finally_body
.emit (this
);
241 stmt
.body
.emit (this
);
244 foreach (CatchClause clause
in stmt
.get_catch_clauses ()) {
248 current_try
= old_try
;
249 current_try_id
= old_try_id
;
250 is_in_catch
= old_is_in_catch
;
252 var cfrag
= new
CCodeFragment ();
253 cfrag
.append (stmt
.body
.ccodenode
);
255 foreach (CatchClause clause
in stmt
.get_catch_clauses ()) {
256 cfrag
.append (new
CCodeGotoStatement ("__finally%d".printf (this_try_id
)));
257 cfrag
.append (clause
.ccodenode
);
260 cfrag
.append (new
CCodeLabel ("__finally%d".printf (this_try_id
)));
261 if (stmt
.finally_body
!= null) {
262 cfrag
.append (stmt
.finally_body
.ccodenode
);
265 // check for errors not handled by this try statement
266 // may be handled by outer try statements or propagated
267 add_simple_check (stmt
, cfrag
, !stmt
.after_try_block_reachable
);
269 stmt
.ccodenode
= cfrag
;
272 public override void visit_catch_clause (CatchClause clause
) {
273 generate_type_declaration (clause
.error_type
, source_declarations
);
275 clause
.body
.emit (this
);
277 var cfrag
= new
CCodeFragment ();
278 cfrag
.append (new
CCodeLabel (clause
.clabel_name
));
280 var cblock
= new
CCodeBlock ();
282 string variable_name
;
283 if (clause
.variable_name
!= null) {
284 variable_name
= get_variable_cname (clause
.variable_name
);
286 variable_name
= "__err";
289 if (clause
.variable_name
!= null) {
290 var cdecl
= new
CCodeDeclaration (clause
.error_type
.get_cname ());
291 cdecl
.add_declarator (new
CCodeVariableDeclarator (variable_name
, new
CCodeIdentifier ("dova_error")));
292 cblock
.add_statement (cdecl
);
294 // error object is not used within catch statement, clear it
295 var cclear
= new
CCodeFunctionCall (new
CCodeIdentifier ("dova_object_unref"));
296 cclear
.add_argument (new
CCodeIdentifier ("dova_error"));
297 cblock
.add_statement (new
CCodeExpressionStatement (cclear
));
299 cblock
.add_statement (new
CCodeExpressionStatement (new
CCodeAssignment (new
CCodeIdentifier ("dova_error"), new
CCodeConstant ("NULL"))));
301 cblock
.add_statement (clause
.body
.ccodenode
);
303 cfrag
.append (cblock
);
305 clause
.ccodenode
= cfrag
;
308 public override void append_local_free (Symbol sym
, CCodeFragment cfrag
, bool stop_at_loop
= false) {
309 var finally_block
= (Block
) null;
310 if (sym
.parent_node is TryStatement
) {
311 finally_block
= (sym
.parent_node as TryStatement
).finally_body
;
312 } else if (sym
.parent_node is CatchClause
) {
313 finally_block
= (sym
.parent_node
.parent_node as TryStatement
).finally_body
;
316 if (finally_block
!= null) {
317 cfrag
.append (finally_block
.ccodenode
);
320 base.append_local_free (sym
, cfrag
, stop_at_loop
);