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 ccode
.add_assignment (new
CCodeIdentifier ("dova_error"), get_cvalue (stmt
.error_expression
));
32 add_simple_check (stmt
, true);
35 public void return_with_exception () {
39 // free local variables
40 append_local_free (current_symbol
, false);
42 if (current_method is CreationMethod
&& current_method
.parent_symbol is Class
) {
43 var cl
= current_method
.parent_symbol as Class
;
44 var unref_call
= new
CCodeFunctionCall (new
CCodeIdentifier (cl
.get_unref_function ()));
45 unref_call
.add_argument (new
CCodeIdentifier ("this"));
46 ccode
.add_expression (unref_call
);
48 } else if (current_return_type is VoidType
) {
51 ccode
.add_return (default_value_for_type (current_return_type
, false));
55 void uncaught_error_statement () {
56 // free local variables
57 append_local_free (current_symbol
, false);
59 // TODO log uncaught error as critical warning
61 if (current_method is CreationMethod
) {
63 } else if (current_return_type is VoidType
) {
65 } else if (current_return_type
!= null) {
66 ccode
.add_return (default_value_for_type (current_return_type
, false));
70 bool in_finally_block (CodeNode node
) {
71 var current_node
= node
;
72 while (current_node
!= null) {
73 var try_stmt
= current_node
.parent_node as TryStatement
;
74 if (try_stmt
!= null && try_stmt
.finally_body
== current_node
) {
77 current_node
= current_node
.parent_node
;
82 public override void add_simple_check (CodeNode node
, bool always_fails
= false) {
84 // inner_error is always set, avoid unnecessary if statement
85 // eliminates C warnings
87 var ccond
= new
CCodeBinaryExpression (CCodeBinaryOperator
.INEQUALITY
, new
CCodeIdentifier ("dova_error"), new
CCodeConstant ("NULL"));
88 ccode
.open_if (ccond
);
91 if (current_try
!= null) {
92 // surrounding try found
94 // free local variables
95 append_local_free (current_symbol
, false, current_try
);
97 var error_types
= new ArrayList
<DataType
> ();
98 foreach (DataType node_error_type
in node
.get_error_types ()) {
99 error_types
.add (node_error_type
);
102 bool has_general_catch_clause
= false;
105 var handled_error_types
= new ArrayList
<DataType
> ();
106 foreach (CatchClause clause
in current_try
.get_catch_clauses ()) {
107 // keep track of unhandled error types
108 foreach (DataType node_error_type
in error_types
) {
109 if (clause
.error_type
== null || node_error_type
.compatible (clause
.error_type
)) {
110 handled_error_types
.add (node_error_type
);
113 foreach (DataType handled_error_type
in handled_error_types
) {
114 error_types
.remove (handled_error_type
);
116 handled_error_types
.clear ();
118 if (clause
.error_type
.equals (new
ObjectType (error_class
))) {
119 // general catch clause, this should be the last one
120 has_general_catch_clause
= true;
121 ccode
.add_goto (clause
.clabel_name
);
124 var catch_type
= clause
.error_type as ObjectType
;
126 var type_check
= new
CCodeFunctionCall (new
CCodeIdentifier ("any_is_a"));
127 type_check
.add_argument (new
CCodeIdentifier ("dova_error"));
128 type_check
.add_argument (new
CCodeFunctionCall (new
CCodeIdentifier ("%s_type_get".printf (catch_type
.type_symbol
.get_lower_case_cname ()))));
130 ccode
.open_if (type_check
);
132 // go to catch clause if error domain matches
133 ccode
.add_goto (clause
.clabel_name
);
139 if (has_general_catch_clause
) {
140 // every possible error is already caught
141 // as there is a general catch clause
142 // no need to do anything else
143 } else if (error_types
.size
> 0) {
144 // go to finally clause if no catch clause matches
145 // and there are still unhandled error types
146 ccode
.add_goto ("__finally%d".printf (current_try_id
));
147 } else if (in_finally_block (node
)) {
148 // do not check unexpected errors happening within finally blocks
149 // as jump out of finally block is not supported
151 assert_not_reached ();
153 } else if (current_method
!= null && current_method
.get_error_types ().size
> 0) {
154 // current method can fail, propagate error
155 CCodeExpression ccond
= null;
157 foreach (DataType error_type
in current_method
.get_error_types ()) {
158 // If Dova.Error is allowed we propagate everything
159 if (error_type
.equals (new
ObjectType (error_class
))) {
164 // Check the allowed error domains to propagate
165 var type_check
= new
CCodeFunctionCall (new
CCodeIdentifier ("any_is_a"));
166 type_check
.add_argument (new
CCodeIdentifier ("dova_error"));
167 type_check
.add_argument (new
CCodeFunctionCall (new
CCodeIdentifier ("%s_type_get".printf (error_class
.get_lower_case_cname ()))));
171 ccond
= new
CCodeBinaryExpression (CCodeBinaryOperator
.OR
, ccond
, type_check
);
176 return_with_exception ();
178 ccode
.open_if (ccond
);
179 return_with_exception ();
181 uncaught_error_statement ();
185 uncaught_error_statement ();
193 public override void visit_try_statement (TryStatement stmt
) {
194 int this_try_id
= next_try_id
++;
196 var old_try
= current_try
;
197 var old_try_id
= current_try_id
;
198 var old_is_in_catch
= is_in_catch
;
200 current_try_id
= this_try_id
;
203 foreach (CatchClause clause
in stmt
.get_catch_clauses ()) {
204 clause
.clabel_name
= "__catch%d_%s".printf (this_try_id
, clause
.error_type
.get_lower_case_cname ());
208 stmt
.body
.emit (this
);
211 foreach (CatchClause clause
in stmt
.get_catch_clauses ()) {
212 ccode
.add_goto ("__finally%d".printf (this_try_id
));
216 current_try
= old_try
;
217 current_try_id
= old_try_id
;
218 is_in_catch
= old_is_in_catch
;
220 ccode
.add_label ("__finally%d".printf (this_try_id
));
221 if (stmt
.finally_body
!= null) {
222 stmt
.finally_body
.emit (this
);
225 // check for errors not handled by this try statement
226 // may be handled by outer try statements or propagated
227 add_simple_check (stmt
, !stmt
.after_try_block_reachable
);
230 public override void visit_catch_clause (CatchClause clause
) {
231 generate_type_declaration (clause
.error_type
, cfile
);
233 ccode
.add_label (clause
.clabel_name
);
237 string variable_name
;
238 if (clause
.variable_name
!= null) {
239 variable_name
= get_variable_cname (clause
.variable_name
);
241 variable_name
= "__err";
244 if (clause
.variable_name
!= null) {
245 var cdecl
= new
CCodeDeclaration (clause
.error_type
.get_cname ());
246 cdecl
.add_declarator (new
CCodeVariableDeclarator (variable_name
, new
CCodeIdentifier ("dova_error")));
247 ccode
.add_statement (cdecl
);
249 // error object is not used within catch statement, clear it
250 var cclear
= new
CCodeFunctionCall (new
CCodeIdentifier ("dova_object_unref"));
251 cclear
.add_argument (new
CCodeIdentifier ("dova_error"));
252 ccode
.add_statement (new
CCodeExpressionStatement (cclear
));
254 ccode
.add_statement (new
CCodeExpressionStatement (new
CCodeAssignment (new
CCodeIdentifier ("dova_error"), new
CCodeConstant ("NULL"))));
256 clause
.body
.emit (this
);
261 public override void append_local_free (Symbol sym
, bool stop_at_loop
= false, CodeNode? stop_at
= null) {
262 var finally_block
= (Block
) null;
263 if (sym
.parent_node is TryStatement
) {
264 finally_block
= (sym
.parent_node as TryStatement
).finally_body
;
265 } else if (sym
.parent_node is CatchClause
) {
266 finally_block
= (sym
.parent_node
.parent_node as TryStatement
).finally_body
;
269 if (finally_block
!= null) {
270 finally_block
.emit (this
);
273 base.append_local_free (sym
, stop_at_loop
, stop_at
);