1 /* valagerrormodule.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>
26 public class Vala
.GErrorModule
: CCodeDelegateModule
{
27 private int current_try_id
= 0;
28 private int next_try_id
= 0;
29 private bool is_in_catch
= false;
31 public override void generate_error_domain_declaration (ErrorDomain edomain
, CCodeFile decl_space
) {
32 if (add_symbol_declaration (decl_space
, edomain
, edomain
.get_cname ())) {
36 var cenum
= new
CCodeEnum (edomain
.get_cname ());
38 foreach (ErrorCode ecode
in edomain
.get_codes ()) {
39 if (ecode
.value
== null) {
40 cenum
.add_value (new
CCodeEnumValue (ecode
.get_cname ()));
42 ecode
.value
.emit (this
);
43 cenum
.add_value (new
CCodeEnumValue (ecode
.get_cname (), get_cvalue (ecode
.value
)));
47 decl_space
.add_type_definition (cenum
);
49 string quark_fun_name
= edomain
.get_lower_case_cprefix () + "quark";
51 var error_domain_define
= new
CCodeMacroReplacement (edomain
.get_upper_case_cname (), quark_fun_name
+ " ()");
52 decl_space
.add_type_definition (error_domain_define
);
54 var cquark_fun
= new
CCodeFunction (quark_fun_name
, gquark_type
.data_type
.get_cname ());
56 decl_space
.add_function_declaration (cquark_fun
);
59 public override void visit_error_domain (ErrorDomain edomain
) {
60 if (edomain
.comment
!= null) {
61 cfile
.add_type_definition (new
CCodeComment (edomain
.comment
.content
));
64 generate_error_domain_declaration (edomain
, cfile
);
66 if (!edomain
.is_internal_symbol ()) {
67 generate_error_domain_declaration (edomain
, header_file
);
69 if (!edomain
.is_private_symbol ()) {
70 generate_error_domain_declaration (edomain
, internal_header_file
);
73 string quark_fun_name
= edomain
.get_lower_case_cprefix () + "quark";
75 var cquark_fun
= new
CCodeFunction (quark_fun_name
, gquark_type
.data_type
.get_cname ());
76 var cquark_block
= new
CCodeBlock ();
78 var cquark_call
= new
CCodeFunctionCall (new
CCodeIdentifier ("g_quark_from_static_string"));
79 cquark_call
.add_argument (new
CCodeConstant ("\"" + edomain
.get_lower_case_cname () + "-quark\""));
81 cquark_block
.add_statement (new
CCodeReturnStatement (cquark_call
));
83 cquark_fun
.block
= cquark_block
;
84 cfile
.add_function (cquark_fun
);
87 public override void visit_throw_statement (ThrowStatement stmt
) {
89 current_method_inner_error
= true;
90 ccode
.add_assignment (get_variable_cexpression ("_inner_error_"), get_cvalue (stmt
.error_expression
));
92 add_simple_check (stmt
, true);
95 public virtual void return_with_exception (CCodeExpression error_expr
) {
96 var cpropagate
= new
CCodeFunctionCall (new
CCodeIdentifier ("g_propagate_error"));
97 cpropagate
.add_argument (new
CCodeIdentifier ("error"));
98 cpropagate
.add_argument (error_expr
);
100 ccode
.add_expression (cpropagate
);
102 // free local variables
103 append_local_free (current_symbol
, false);
105 if (current_method is CreationMethod
&& current_method
.parent_symbol is Class
) {
106 var cl
= (Class
) current_method
.parent_symbol
;
107 var unref_call
= get_unref_expression (new
CCodeIdentifier ("self"), new
ObjectType (cl
), null);
108 ccode
.add_expression (unref_call
);
109 ccode
.add_return (new
CCodeConstant ("NULL"));
110 } else if (is_in_coroutine ()) {
111 ccode
.add_return (new
CCodeConstant ("FALSE"));
113 return_default_value (current_return_type
);
117 void uncaught_error_statement (CCodeExpression inner_error
, bool unexpected
= false) {
118 // free local variables
119 append_local_free (current_symbol
, false);
121 var ccritical
= new
CCodeFunctionCall (new
CCodeIdentifier ("g_critical"));
122 ccritical
.add_argument (new
CCodeConstant (unexpected ?
"\"file %s: line %d: unexpected error: %s (%s, %d)\"" : "\"file %s: line %d: uncaught error: %s (%s, %d)\""));
123 ccritical
.add_argument (new
CCodeConstant ("__FILE__"));
124 ccritical
.add_argument (new
CCodeConstant ("__LINE__"));
125 ccritical
.add_argument (new CCodeMemberAccess
.pointer (inner_error
, "message"));
126 var domain_name
= new
CCodeFunctionCall (new
CCodeIdentifier ("g_quark_to_string"));
127 domain_name
.add_argument (new CCodeMemberAccess
.pointer (inner_error
, "domain"));
128 ccritical
.add_argument (domain_name
);
129 ccritical
.add_argument (new CCodeMemberAccess
.pointer (inner_error
, "code"));
131 var cclear
= new
CCodeFunctionCall (new
CCodeIdentifier ("g_clear_error"));
132 cclear
.add_argument (new
CCodeUnaryExpression (CCodeUnaryOperator
.ADDRESS_OF
, inner_error
));
134 // print critical message
135 ccode
.add_expression (ccritical
);
136 ccode
.add_expression (cclear
);
138 if (is_in_constructor () || is_in_destructor ()) {
139 // just print critical, do not return prematurely
140 } else if (current_method is CreationMethod
) {
141 if (current_method
.parent_symbol is Struct
) {
144 ccode
.add_return (new
CCodeConstant ("NULL"));
146 } else if (is_in_coroutine ()) {
147 ccode
.add_return (new
CCodeConstant ("FALSE"));
148 } else if (current_return_type
!= null) {
149 return_default_value (current_return_type
);
153 bool in_finally_block (CodeNode node
) {
154 var current_node
= node
;
155 while (current_node
!= null) {
156 var try_stmt
= current_node
.parent_node as TryStatement
;
157 if (try_stmt
!= null && try_stmt
.finally_body
== current_node
) {
160 current_node
= current_node
.parent_node
;
165 public override void add_simple_check (CodeNode node
, bool always_fails
= false) {
166 current_method_inner_error
= true;
168 var inner_error
= get_variable_cexpression ("_inner_error_");
171 // inner_error is always set, avoid unnecessary if statement
172 // eliminates C warnings
174 var ccond
= new
CCodeBinaryExpression (CCodeBinaryOperator
.INEQUALITY
, inner_error
, new
CCodeConstant ("NULL"));
175 ccode
.open_if (ccond
);
178 if (current_try
!= null) {
179 // surrounding try found
181 // free local variables
183 append_local_free (current_symbol
, false, current_catch
);
185 append_local_free (current_symbol
, false, current_try
);
188 var error_types
= new ArrayList
<DataType
> ();
189 foreach (DataType node_error_type
in node
.get_error_types ()) {
190 error_types
.add (node_error_type
);
193 bool has_general_catch_clause
= false;
196 var handled_error_types
= new ArrayList
<DataType
> ();
197 foreach (CatchClause clause
in current_try
.get_catch_clauses ()) {
198 // keep track of unhandled error types
199 foreach (DataType node_error_type
in error_types
) {
200 if (clause
.error_type
== null || node_error_type
.compatible (clause
.error_type
)) {
201 handled_error_types
.add (node_error_type
);
204 foreach (DataType handled_error_type
in handled_error_types
) {
205 error_types
.remove (handled_error_type
);
207 handled_error_types
.clear ();
209 if (clause
.error_type
.equals (gerror_type
)) {
210 // general catch clause, this should be the last one
211 has_general_catch_clause
= true;
212 ccode
.add_goto (clause
.clabel_name
);
215 var catch_type
= clause
.error_type as ErrorType
;
217 if (catch_type
.error_code
!= null) {
218 /* catch clause specifies a specific error code */
219 var error_match
= new
CCodeFunctionCall (new
CCodeIdentifier ("g_error_matches"));
220 error_match
.add_argument (inner_error
);
221 error_match
.add_argument (new
CCodeIdentifier (catch_type
.data_type
.get_upper_case_cname ()));
222 error_match
.add_argument (new
CCodeIdentifier (catch_type
.error_code
.get_cname ()));
224 ccode
.open_if (error_match
);
226 /* catch clause specifies a full error domain */
227 var ccond
= new
CCodeBinaryExpression (CCodeBinaryOperator
.EQUALITY
,
228 new CCodeMemberAccess
.pointer (inner_error
, "domain"), new CCodeIdentifier
229 (clause
.error_type
.data_type
.get_upper_case_cname ()));
231 ccode
.open_if (ccond
);
234 // go to catch clause if error domain matches
235 ccode
.add_goto (clause
.clabel_name
);
241 if (has_general_catch_clause
) {
242 // every possible error is already caught
243 // as there is a general catch clause
244 // no need to do anything else
245 } else if (error_types
.size
> 0) {
246 // go to finally clause if no catch clause matches
247 // and there are still unhandled error types
248 ccode
.add_goto ("__finally%d".printf (current_try_id
));
249 } else if (in_finally_block (node
)) {
250 // do not check unexpected errors happening within finally blocks
251 // as jump out of finally block is not supported
253 // should never happen with correct bindings
254 uncaught_error_statement (inner_error
, true);
256 } else if (current_method
!= null && current_method
.get_error_types ().size
> 0) {
257 // current method can fail, propagate error
258 CCodeBinaryExpression ccond
= null;
260 foreach (DataType error_type
in current_method
.get_error_types ()) {
261 // If GLib.Error is allowed we propagate everything
262 if (error_type
.equals (gerror_type
)) {
267 // Check the allowed error domains to propagate
268 var domain_check
= new
CCodeBinaryExpression (CCodeBinaryOperator
.EQUALITY
, new CCodeMemberAccess
.pointer
269 (inner_error
, "domain"), new
CCodeIdentifier (error_type
.data_type
.get_upper_case_cname ()));
271 ccond
= domain_check
;
273 ccond
= new
CCodeBinaryExpression (CCodeBinaryOperator
.OR
, ccond
, domain_check
);
278 ccode
.open_if (ccond
);
279 return_with_exception (inner_error
);
282 uncaught_error_statement (inner_error
);
285 return_with_exception (inner_error
);
288 uncaught_error_statement (inner_error
);
296 public override void visit_try_statement (TryStatement stmt
) {
297 int this_try_id
= next_try_id
++;
299 var old_try
= current_try
;
300 var old_try_id
= current_try_id
;
301 var old_is_in_catch
= is_in_catch
;
302 var old_catch
= current_catch
;
304 current_try_id
= this_try_id
;
307 foreach (CatchClause clause
in stmt
.get_catch_clauses ()) {
308 clause
.clabel_name
= "__catch%d_%s".printf (this_try_id
, clause
.error_type
.get_lower_case_cname ());
312 stmt
.body
.emit (this
);
315 foreach (CatchClause clause
in stmt
.get_catch_clauses ()) {
316 current_catch
= clause
;
317 ccode
.add_goto ("__finally%d".printf (this_try_id
));
321 current_try
= old_try
;
322 current_try_id
= old_try_id
;
323 is_in_catch
= old_is_in_catch
;
324 current_catch
= old_catch
;
326 ccode
.add_label ("__finally%d".printf (this_try_id
));
327 if (stmt
.finally_body
!= null) {
328 stmt
.finally_body
.emit (this
);
331 // check for errors not handled by this try statement
332 // may be handled by outer try statements or propagated
333 add_simple_check (stmt
, !stmt
.after_try_block_reachable
);
336 public override void visit_catch_clause (CatchClause clause
) {
337 current_method_inner_error
= true;
339 var error_type
= (ErrorType
) clause
.error_type
;
340 if (error_type
.error_domain
!= null) {
341 generate_error_domain_declaration (error_type
.error_domain
, cfile
);
344 ccode
.add_label (clause
.clabel_name
);
348 string variable_name
;
349 if (clause
.variable_name
!= null) {
350 variable_name
= get_variable_cname (clause
.variable_name
);
352 variable_name
= "__err";
355 if (clause
.variable_name
!= null) {
356 if (is_in_coroutine ()) {
357 closure_struct
.add_field ("GError *", variable_name
);
359 ccode
.add_declaration ("GError *", new
CCodeVariableDeclarator (variable_name
));
361 ccode
.add_assignment (get_variable_cexpression (variable_name
), get_variable_cexpression ("_inner_error_"));
363 // error object is not used within catch statement, clear it
364 var cclear
= new
CCodeFunctionCall (new
CCodeIdentifier ("g_clear_error"));
365 cclear
.add_argument (new
CCodeUnaryExpression (CCodeUnaryOperator
.ADDRESS_OF
, get_variable_cexpression ("_inner_error_")));
366 ccode
.add_expression (cclear
);
368 ccode
.add_assignment (get_variable_cexpression ("_inner_error_"), new
CCodeConstant ("NULL"));
370 clause
.body
.emit (this
);
375 public override void append_local_free (Symbol sym
, bool stop_at_loop
= false, CodeNode? stop_at
= null) {
376 if (!(stop_at is TryStatement
|| stop_at is CatchClause
)) {
377 var finally_block
= (Block
) null;
378 if (sym
.parent_node is TryStatement
) {
379 finally_block
= (sym
.parent_node as TryStatement
).finally_body
;
380 } else if (sym
.parent_node is CatchClause
) {
381 finally_block
= (sym
.parent_node
.parent_node as TryStatement
).finally_body
;
384 if (finally_block
!= null && finally_block
!= sym
) {
385 finally_block
.emit (this
);
389 base.append_local_free (sym
, stop_at_loop
, stop_at
);