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
, get_ccode_name (edomain
))) {
36 var cenum
= new
CCodeEnum (get_ccode_name (edomain
));
38 foreach (ErrorCode ecode
in edomain
.get_codes ()) {
39 if (ecode
.value
== null) {
40 cenum
.add_value (new
CCodeEnumValue (get_ccode_name (ecode
)));
42 ecode
.value
.emit (this
);
43 cenum
.add_value (new
CCodeEnumValue (get_ccode_name (ecode
), get_cvalue (ecode
.value
)));
47 decl_space
.add_type_definition (cenum
);
49 string quark_fun_name
= get_ccode_lower_case_prefix (edomain
) + "quark";
51 var error_domain_define
= new
CCodeMacroReplacement (get_ccode_upper_case_name (edomain
), quark_fun_name
+ " ()");
52 decl_space
.add_type_definition (error_domain_define
);
54 var cquark_fun
= new
CCodeFunction (quark_fun_name
, get_ccode_name (gquark_type
.data_type
));
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
= get_ccode_lower_case_prefix (edomain
) + "quark";
75 var cquark_fun
= new
CCodeFunction (quark_fun_name
, get_ccode_name (gquark_type
.data_type
));
76 push_function (cquark_fun
);
78 var cquark_call
= new
CCodeFunctionCall (new
CCodeIdentifier ("g_quark_from_static_string"));
79 cquark_call
.add_argument (new
CCodeConstant ("\"" + get_ccode_quark_name (edomain
) + "\""));
81 ccode
.add_return (cquark_call
);
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 ccode
.add_expression (destroy_value (new
GLibValue (new
ObjectType (cl
), new
CCodeIdentifier ("self"), true)));
108 ccode
.add_return (new
CCodeConstant ("NULL"));
109 } else if (is_in_coroutine ()) {
110 ccode
.add_return (new
CCodeConstant ("FALSE"));
112 return_default_value (current_return_type
, true);
116 void uncaught_error_statement (CCodeExpression inner_error
, bool unexpected
= false) {
117 // free local variables
118 append_local_free (current_symbol
, false);
120 var ccritical
= new
CCodeFunctionCall (new
CCodeIdentifier ("g_critical"));
121 ccritical
.add_argument (new
CCodeConstant (unexpected ?
"\"file %s: line %d: unexpected error: %s (%s, %d)\"" : "\"file %s: line %d: uncaught error: %s (%s, %d)\""));
122 ccritical
.add_argument (new
CCodeConstant ("__FILE__"));
123 ccritical
.add_argument (new
CCodeConstant ("__LINE__"));
124 ccritical
.add_argument (new CCodeMemberAccess
.pointer (inner_error
, "message"));
125 var domain_name
= new
CCodeFunctionCall (new
CCodeIdentifier ("g_quark_to_string"));
126 domain_name
.add_argument (new CCodeMemberAccess
.pointer (inner_error
, "domain"));
127 ccritical
.add_argument (domain_name
);
128 ccritical
.add_argument (new CCodeMemberAccess
.pointer (inner_error
, "code"));
130 var cclear
= new
CCodeFunctionCall (new
CCodeIdentifier ("g_clear_error"));
131 cclear
.add_argument (new
CCodeUnaryExpression (CCodeUnaryOperator
.ADDRESS_OF
, inner_error
));
133 // print critical message
134 ccode
.add_expression (ccritical
);
135 ccode
.add_expression (cclear
);
137 if (is_in_constructor () || is_in_destructor ()) {
138 // just print critical, do not return prematurely
139 } else if (current_method is CreationMethod
) {
140 if (current_method
.parent_symbol is Struct
) {
143 ccode
.add_return (new
CCodeConstant ("NULL"));
145 } else if (is_in_coroutine ()) {
146 var async_result_expr
= new CCodeMemberAccess
.pointer (new
CCodeIdentifier ("_data_"), "_async_result");
147 var unref
= new
CCodeFunctionCall (new
CCodeIdentifier ("g_object_unref"));
148 unref
.add_argument (async_result_expr
);
149 ccode
.add_expression (unref
);
150 ccode
.add_return (new
CCodeConstant ("FALSE"));
151 } else if (current_return_type
!= null) {
152 return_default_value (current_return_type
, true);
156 bool in_finally_block (CodeNode node
) {
157 var current_node
= node
;
158 while (current_node
!= null) {
159 var try_stmt
= current_node
.parent_node as TryStatement
;
160 if (try_stmt
!= null && try_stmt
.finally_body
== current_node
) {
163 current_node
= current_node
.parent_node
;
168 public override void add_simple_check (CodeNode node
, bool always_fails
= false) {
169 current_method_inner_error
= true;
171 var inner_error
= get_variable_cexpression ("_inner_error_");
174 // inner_error is always set, avoid unnecessary if statement
175 // eliminates C warnings
177 var ccond
= new
CCodeBinaryExpression (CCodeBinaryOperator
.INEQUALITY
, inner_error
, new
CCodeConstant ("NULL"));
178 var unlikely
= new
CCodeFunctionCall (new
CCodeIdentifier ("G_UNLIKELY"));
179 unlikely
.add_argument (ccond
);
180 ccode
.open_if (unlikely
);
183 if (current_try
!= null) {
184 // surrounding try found
186 // free local variables
188 append_local_free (current_symbol
, false, current_catch
);
190 append_local_free (current_symbol
, false, current_try
);
193 var error_types
= new ArrayList
<DataType
> ();
194 foreach (DataType node_error_type
in node
.get_error_types ()) {
195 error_types
.add (node_error_type
);
198 bool has_general_catch_clause
= false;
201 var handled_error_types
= new ArrayList
<DataType
> ();
202 foreach (CatchClause clause
in current_try
.get_catch_clauses ()) {
203 // keep track of unhandled error types
204 foreach (DataType node_error_type
in error_types
) {
205 if (clause
.error_type
== null || node_error_type
.compatible (clause
.error_type
)) {
206 handled_error_types
.add (node_error_type
);
209 foreach (DataType handled_error_type
in handled_error_types
) {
210 error_types
.remove (handled_error_type
);
212 handled_error_types
.clear ();
214 if (clause
.error_type
.equals (gerror_type
)) {
215 // general catch clause, this should be the last one
216 has_general_catch_clause
= true;
217 ccode
.add_goto (clause
.clabel_name
);
220 var catch_type
= clause
.error_type as ErrorType
;
222 if (catch_type
.error_code
!= null) {
223 /* catch clause specifies a specific error code */
224 var error_match
= new
CCodeFunctionCall (new
CCodeIdentifier ("g_error_matches"));
225 error_match
.add_argument (inner_error
);
226 error_match
.add_argument (new
CCodeIdentifier (get_ccode_upper_case_name (catch_type
.data_type
)));
227 error_match
.add_argument (new
CCodeIdentifier (get_ccode_name (catch_type
.error_code
)));
229 ccode
.open_if (error_match
);
231 /* catch clause specifies a full error domain */
232 var ccond
= new
CCodeBinaryExpression (CCodeBinaryOperator
.EQUALITY
,
233 new CCodeMemberAccess
.pointer (inner_error
, "domain"), new CCodeIdentifier
234 (get_ccode_upper_case_name (clause
.error_type
.data_type
)));
236 ccode
.open_if (ccond
);
239 // go to catch clause if error domain matches
240 ccode
.add_goto (clause
.clabel_name
);
246 if (has_general_catch_clause
) {
247 // every possible error is already caught
248 // as there is a general catch clause
249 // no need to do anything else
250 } else if (error_types
.size
> 0) {
251 // go to finally clause if no catch clause matches
252 // and there are still unhandled error types
253 ccode
.add_goto ("__finally%d".printf (current_try_id
));
254 } else if (in_finally_block (node
)) {
255 // do not check unexpected errors happening within finally blocks
256 // as jump out of finally block is not supported
258 // should never happen with correct bindings
259 uncaught_error_statement (inner_error
, true);
261 } else if (current_method
!= null && current_method
.get_error_types ().size
> 0) {
262 // current method can fail, propagate error
263 CCodeBinaryExpression ccond
= null;
265 foreach (DataType error_type
in current_method
.get_error_types ()) {
266 // If GLib.Error is allowed we propagate everything
267 if (error_type
.equals (gerror_type
)) {
272 // Check the allowed error domains to propagate
273 var domain_check
= new
CCodeBinaryExpression (CCodeBinaryOperator
.EQUALITY
, new CCodeMemberAccess
.pointer
274 (inner_error
, "domain"), new
CCodeIdentifier (get_ccode_upper_case_name (error_type
.data_type
)));
276 ccond
= domain_check
;
278 ccond
= new
CCodeBinaryExpression (CCodeBinaryOperator
.OR
, ccond
, domain_check
);
283 ccode
.open_if (ccond
);
284 return_with_exception (inner_error
);
287 uncaught_error_statement (inner_error
);
290 return_with_exception (inner_error
);
293 uncaught_error_statement (inner_error
);
301 public override void visit_try_statement (TryStatement stmt
) {
302 int this_try_id
= next_try_id
++;
304 var old_try
= current_try
;
305 var old_try_id
= current_try_id
;
306 var old_is_in_catch
= is_in_catch
;
307 var old_catch
= current_catch
;
309 current_try_id
= this_try_id
;
312 foreach (CatchClause clause
in stmt
.get_catch_clauses ()) {
313 clause
.clabel_name
= "__catch%d_%s".printf (this_try_id
, get_ccode_lower_case_name (clause
.error_type
));
317 stmt
.body
.emit (this
);
320 foreach (CatchClause clause
in stmt
.get_catch_clauses ()) {
321 current_catch
= clause
;
322 ccode
.add_goto ("__finally%d".printf (this_try_id
));
326 current_try
= old_try
;
327 current_try_id
= old_try_id
;
328 is_in_catch
= old_is_in_catch
;
329 current_catch
= old_catch
;
331 ccode
.add_label ("__finally%d".printf (this_try_id
));
332 if (stmt
.finally_body
!= null) {
333 stmt
.finally_body
.emit (this
);
336 // check for errors not handled by this try statement
337 // may be handled by outer try statements or propagated
338 add_simple_check (stmt
, !stmt
.after_try_block_reachable
);
341 public override void visit_catch_clause (CatchClause clause
) {
342 current_method_inner_error
= true;
344 var error_type
= (ErrorType
) clause
.error_type
;
345 if (error_type
.error_domain
!= null) {
346 generate_error_domain_declaration (error_type
.error_domain
, cfile
);
349 ccode
.add_label (clause
.clabel_name
);
353 if (clause
.error_variable
!= null) {
354 visit_local_variable (clause
.error_variable
);
355 ccode
.add_assignment (get_variable_cexpression (get_local_cname (clause
.error_variable
)), get_variable_cexpression ("_inner_error_"));
357 // error object is not used within catch statement, clear it
358 var cclear
= new
CCodeFunctionCall (new
CCodeIdentifier ("g_clear_error"));
359 cclear
.add_argument (new
CCodeUnaryExpression (CCodeUnaryOperator
.ADDRESS_OF
, get_variable_cexpression ("_inner_error_")));
360 ccode
.add_expression (cclear
);
362 ccode
.add_assignment (get_variable_cexpression ("_inner_error_"), new
CCodeConstant ("NULL"));
364 clause
.body
.emit (this
);
369 protected override void append_scope_free (Symbol sym
, CodeNode? stop_at
= null) {
370 base.append_scope_free (sym
, stop_at
);
372 if (!(stop_at is TryStatement
|| stop_at is CatchClause
)) {
373 var finally_block
= (Block
) null;
374 if (sym
.parent_node is TryStatement
) {
375 finally_block
= ((TryStatement
) sym
.parent_node
).finally_body
;
376 } else if (sym
.parent_node is CatchClause
) {
377 finally_block
= ((TryStatement
) sym
.parent_node
.parent_node
).finally_body
;
380 if (finally_block
!= null && finally_block
!= sym
) {
381 finally_block
.emit (this
);