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_expression (new
CCodeAssignment (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 (current_method
!= null && current_method
.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 (current_method
!= null && current_method
.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
182 append_error_free (current_symbol
, current_try
);
184 var error_types
= new ArrayList
<DataType
> ();
185 foreach (DataType node_error_type
in node
.get_error_types ()) {
186 error_types
.add (node_error_type
);
189 bool has_general_catch_clause
= false;
192 var handled_error_types
= new ArrayList
<DataType
> ();
193 foreach (CatchClause clause
in current_try
.get_catch_clauses ()) {
194 // keep track of unhandled error types
195 foreach (DataType node_error_type
in error_types
) {
196 if (clause
.error_type
== null || node_error_type
.compatible (clause
.error_type
)) {
197 handled_error_types
.add (node_error_type
);
200 foreach (DataType handled_error_type
in handled_error_types
) {
201 error_types
.remove (handled_error_type
);
203 handled_error_types
.clear ();
205 if (clause
.error_type
.equals (gerror_type
)) {
206 // general catch clause, this should be the last one
207 has_general_catch_clause
= true;
208 ccode
.add_goto (clause
.clabel_name
);
211 var catch_type
= clause
.error_type as ErrorType
;
213 if (catch_type
.error_code
!= null) {
214 /* catch clause specifies a specific error code */
215 var error_match
= new
CCodeFunctionCall (new
CCodeIdentifier ("g_error_matches"));
216 error_match
.add_argument (inner_error
);
217 error_match
.add_argument (new
CCodeIdentifier (catch_type
.data_type
.get_upper_case_cname ()));
218 error_match
.add_argument (new
CCodeIdentifier (catch_type
.error_code
.get_cname ()));
220 ccode
.open_if (error_match
);
222 /* catch clause specifies a full error domain */
223 var ccond
= new
CCodeBinaryExpression (CCodeBinaryOperator
.EQUALITY
,
224 new CCodeMemberAccess
.pointer (inner_error
, "domain"), new CCodeIdentifier
225 (clause
.error_type
.data_type
.get_upper_case_cname ()));
227 ccode
.open_if (ccond
);
230 // go to catch clause if error domain matches
231 ccode
.add_goto (clause
.clabel_name
);
237 if (has_general_catch_clause
) {
238 // every possible error is already caught
239 // as there is a general catch clause
240 // no need to do anything else
241 } else if (error_types
.size
> 0) {
242 // go to finally clause if no catch clause matches
243 // and there are still unhandled error types
244 ccode
.add_goto ("__finally%d".printf (current_try_id
));
245 } else if (in_finally_block (node
)) {
246 // do not check unexpected errors happening within finally blocks
247 // as jump out of finally block is not supported
249 // should never happen with correct bindings
250 uncaught_error_statement (inner_error
, true);
252 } else if (current_method
!= null && current_method
.get_error_types ().size
> 0) {
253 // current method can fail, propagate error
254 CCodeBinaryExpression ccond
= null;
256 foreach (DataType error_type
in current_method
.get_error_types ()) {
257 // If GLib.Error is allowed we propagate everything
258 if (error_type
.equals (gerror_type
)) {
263 // Check the allowed error domains to propagate
264 var domain_check
= new
CCodeBinaryExpression (CCodeBinaryOperator
.EQUALITY
, new CCodeMemberAccess
.pointer
265 (inner_error
, "domain"), new
CCodeIdentifier (error_type
.data_type
.get_upper_case_cname ()));
267 ccond
= domain_check
;
269 ccond
= new
CCodeBinaryExpression (CCodeBinaryOperator
.OR
, ccond
, domain_check
);
274 ccode
.open_if (ccond
);
275 return_with_exception (inner_error
);
278 uncaught_error_statement (inner_error
);
281 return_with_exception (inner_error
);
284 uncaught_error_statement (inner_error
);
292 public override void visit_try_statement (TryStatement stmt
) {
293 int this_try_id
= next_try_id
++;
295 var old_try
= current_try
;
296 var old_try_id
= current_try_id
;
297 var old_is_in_catch
= is_in_catch
;
299 current_try_id
= this_try_id
;
302 foreach (CatchClause clause
in stmt
.get_catch_clauses ()) {
303 clause
.clabel_name
= "__catch%d_%s".printf (this_try_id
, clause
.error_type
.get_lower_case_cname ());
307 stmt
.body
.emit (this
);
310 foreach (CatchClause clause
in stmt
.get_catch_clauses ()) {
311 ccode
.add_goto ("__finally%d".printf (this_try_id
));
315 current_try
= old_try
;
316 current_try_id
= old_try_id
;
317 is_in_catch
= old_is_in_catch
;
319 ccode
.add_label ("__finally%d".printf (this_try_id
));
320 if (stmt
.finally_body
!= null) {
321 stmt
.finally_body
.emit (this
);
324 // check for errors not handled by this try statement
325 // may be handled by outer try statements or propagated
326 add_simple_check (stmt
, !stmt
.after_try_block_reachable
);
329 public override void visit_catch_clause (CatchClause clause
) {
330 current_method_inner_error
= true;
332 var error_type
= (ErrorType
) clause
.error_type
;
333 if (error_type
.error_domain
!= null) {
334 generate_error_domain_declaration (error_type
.error_domain
, cfile
);
337 ccode
.add_label (clause
.clabel_name
);
341 string variable_name
;
342 if (clause
.variable_name
!= null) {
343 variable_name
= get_variable_cname (clause
.variable_name
);
345 variable_name
= "__err";
348 if (clause
.variable_name
!= null) {
349 if (current_method
!= null && current_method
.coroutine
) {
350 closure_struct
.add_field ("GError *", variable_name
);
352 ccode
.add_declaration ("GError *", new
CCodeVariableDeclarator (variable_name
));
354 ccode
.add_expression (new
CCodeAssignment (get_variable_cexpression (variable_name
), get_variable_cexpression ("_inner_error_")));
356 // error object is not used within catch statement, clear it
357 var cclear
= new
CCodeFunctionCall (new
CCodeIdentifier ("g_clear_error"));
358 cclear
.add_argument (new
CCodeUnaryExpression (CCodeUnaryOperator
.ADDRESS_OF
, get_variable_cexpression ("_inner_error_")));
359 ccode
.add_expression (cclear
);
361 ccode
.add_expression (new
CCodeAssignment (get_variable_cexpression ("_inner_error_"), new
CCodeConstant ("NULL")));
363 clause
.body
.emit (this
);
368 public override void append_local_free (Symbol sym
, bool stop_at_loop
= false) {
369 var finally_block
= (Block
) null;
370 if (sym
.parent_node is TryStatement
) {
371 finally_block
= (sym
.parent_node as TryStatement
).finally_body
;
372 } else if (sym
.parent_node is CatchClause
) {
373 finally_block
= (sym
.parent_node
.parent_node as TryStatement
).finally_body
;
376 if (finally_block
!= null && finally_block
!= sym
) {
377 finally_block
.emit (this
);
380 base.append_local_free (sym
, stop_at_loop
);