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 bool is_in_catch = false;
29 public override void generate_error_domain_declaration (ErrorDomain edomain, CCodeFile decl_space) {
30 if (add_symbol_declaration (decl_space, edomain, get_ccode_name (edomain))) {
34 generate_type_declaration (gquark_type, decl_space);
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.type_symbol));
55 cquark_fun.modifiers |= CCodeModifiers.EXTERN;
56 requires_vala_extern = true;
58 decl_space.add_function_declaration (cquark_fun);
61 public override void visit_error_domain (ErrorDomain edomain) {
62 if (edomain.comment != null) {
63 cfile.add_type_definition (new CCodeComment (edomain.comment.content));
66 generate_error_domain_declaration (edomain, cfile);
68 if (!edomain.is_internal_symbol ()) {
69 generate_error_domain_declaration (edomain, header_file);
71 if (!edomain.is_private_symbol ()) {
72 generate_error_domain_declaration (edomain, internal_header_file);
75 edomain.accept_children (this);
77 string quark_fun_name = get_ccode_lower_case_prefix (edomain) + "quark";
79 var cquark_fun = new CCodeFunction (quark_fun_name, get_ccode_name (gquark_type.type_symbol));
80 push_function (cquark_fun);
82 var cquark_call = new CCodeFunctionCall (new CCodeIdentifier ("g_quark_from_static_string"));
83 cquark_call.add_argument (new CCodeConstant ("\"" + get_ccode_quark_name (edomain) + "\""));
85 ccode.add_return (cquark_call);
88 cfile.add_function (cquark_fun);
91 public override void visit_throw_statement (ThrowStatement stmt) {
93 current_method_inner_error = true;
94 ccode.add_assignment (get_inner_error_cexpression (), get_cvalue (stmt.error_expression));
96 add_simple_check (stmt, true);
99 public virtual void return_with_exception (CCodeExpression error_expr) {
100 var cpropagate = new CCodeFunctionCall (new CCodeIdentifier ("g_propagate_error"));
101 cpropagate.add_argument (new CCodeIdentifier ("error"));
102 cpropagate.add_argument (error_expr);
104 ccode.add_expression (cpropagate);
106 // free local variables
107 append_local_free (current_symbol);
109 // free possibly already assigned out-parameter
110 append_out_param_free (current_method);
112 if (current_method is CreationMethod && current_method.parent_symbol is Class) {
113 var cl = (Class) current_method.parent_symbol;
114 ccode.add_expression (destroy_value (new GLibValue (new ObjectType (cl), new CCodeIdentifier ("self"), true)));
115 ccode.add_return (new CCodeConstant ("NULL"));
116 } else if (is_in_coroutine ()) {
117 ccode.add_return (new CCodeConstant ("FALSE"));
119 return_default_value (current_return_type, true);
123 void uncaught_error_statement (CCodeExpression inner_error, bool unexpected = false, CodeNode? start_at = null) {
124 // free local variables
125 if (start_at is TryStatement) {
126 append_local_free (start_at.parent_node as Block);
128 append_local_free (current_symbol);
131 // free possibly already assigned out-parameter
132 append_out_param_free (current_method);
134 cfile.add_include ("glib.h");
136 var ccritical = new CCodeFunctionCall (new CCodeIdentifier ("g_critical"));
137 ccritical.add_argument (new CCodeConstant (unexpected ? "\"file %s: line %d: unexpected error: %s (%s, %d)\"" : "\"file %s: line %d: uncaught error: %s (%s, %d)\""));
138 ccritical.add_argument (new CCodeConstant ("__FILE__"));
139 ccritical.add_argument (new CCodeConstant ("__LINE__"));
140 ccritical.add_argument (new CCodeMemberAccess.pointer (inner_error, "message"));
141 var domain_name = new CCodeFunctionCall (new CCodeIdentifier ("g_quark_to_string"));
142 domain_name.add_argument (new CCodeMemberAccess.pointer (inner_error, "domain"));
143 ccritical.add_argument (domain_name);
144 ccritical.add_argument (new CCodeMemberAccess.pointer (inner_error, "code"));
146 var cclear = new CCodeFunctionCall (new CCodeIdentifier ("g_clear_error"));
147 cclear.add_argument (new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, inner_error));
149 // print critical message
150 ccode.add_expression (ccritical);
151 ccode.add_expression (cclear);
153 if (is_in_constructor () || is_in_destructor ()) {
154 // just print critical, do not return prematurely
155 } else if (current_method is CreationMethod) {
156 if (current_method.parent_symbol is Struct) {
159 ccode.add_return (new CCodeConstant ("NULL"));
161 } else if (is_in_coroutine ()) {
162 var unref = new CCodeFunctionCall (new CCodeIdentifier ("g_object_unref"));
163 unref.add_argument (get_variable_cexpression ("_async_result"));
164 ccode.add_expression (unref);
165 ccode.add_return (new CCodeConstant ("FALSE"));
166 } else if (current_return_type != null) {
167 return_default_value (current_return_type, true);
171 bool in_finally_block (CodeNode node) {
172 var current_node = node;
173 while (current_node != null) {
174 var try_stmt = current_node.parent_node as TryStatement;
175 if (try_stmt != null && try_stmt.finally_body == current_node) {
178 current_node = current_node.parent_node;
183 public override void add_simple_check (CodeNode node, bool always_fails = false) {
184 current_method_inner_error = true;
187 // inner_error is always set, avoid unnecessary if statement
188 // eliminates C warnings
190 var ccond = new CCodeBinaryExpression (CCodeBinaryOperator.INEQUALITY, get_inner_error_cexpression (), new CCodeConstant ("NULL"));
191 var unlikely = new CCodeFunctionCall (new CCodeIdentifier ("G_UNLIKELY"));
192 unlikely.add_argument (ccond);
193 ccode.open_if (unlikely);
196 if (current_try != null) {
197 // surrounding try found
199 // free local variables
201 append_local_free (current_symbol, null, current_catch);
203 append_local_free (current_symbol, null, current_try);
206 var error_types = new ArrayList<DataType> ();
207 node.get_error_types (error_types);
209 bool has_general_catch_clause = false;
212 var handled_error_types = new ArrayList<DataType> ();
213 foreach (CatchClause clause in current_try.get_catch_clauses ()) {
214 // keep track of unhandled error types
215 foreach (DataType node_error_type in error_types) {
216 if (clause.error_type == null || node_error_type.compatible (clause.error_type)) {
217 handled_error_types.add (node_error_type);
220 foreach (DataType handled_error_type in handled_error_types) {
221 error_types.remove (handled_error_type);
223 handled_error_types.clear ();
225 if (clause.error_type.equals (gerror_type)) {
226 // general catch clause, this should be the last one
227 has_general_catch_clause = true;
228 ccode.add_goto (clause.get_attribute_string ("CCode", "cname"));
231 unowned ErrorType catch_type = (ErrorType) clause.error_type;
233 if (catch_type.error_code != null) {
234 /* catch clause specifies a specific error code */
235 var error_match = new CCodeFunctionCall (new CCodeIdentifier ("g_error_matches"));
236 error_match.add_argument (get_inner_error_cexpression ());
237 error_match.add_argument (new CCodeIdentifier (get_ccode_upper_case_name (catch_type.error_domain)));
238 error_match.add_argument (new CCodeIdentifier (get_ccode_name (catch_type.error_code)));
240 ccode.open_if (error_match);
242 /* catch clause specifies a full error domain */
243 var ccond = new CCodeBinaryExpression (CCodeBinaryOperator.EQUALITY,
244 new CCodeMemberAccess.pointer (get_inner_error_cexpression (), "domain"), new CCodeIdentifier
245 (get_ccode_upper_case_name (catch_type.error_domain)));
247 ccode.open_if (ccond);
250 // go to catch clause if error domain matches
251 ccode.add_goto (clause.get_attribute_string ("CCode", "cname"));
257 if (has_general_catch_clause) {
258 // every possible error is already caught
259 // as there is a general catch clause
260 // no need to do anything else
261 } else if (error_types.size > 0) {
262 // go to finally clause if no catch clause matches
263 // and there are still unhandled error types
264 ccode.add_goto ("__finally%d".printf (current_try_id));
265 } else if (in_finally_block (node)) {
266 // do not check unexpected errors happening within finally blocks
267 // as jump out of finally block is not supported
269 // should never happen with correct bindings
270 uncaught_error_statement (get_inner_error_cexpression (), true, current_try);
272 } else if (current_method != null && current_method.tree_can_fail) {
273 // current method can fail, propagate error
274 CCodeBinaryExpression ccond = null;
276 var error_types = new ArrayList<DataType> ();
277 current_method.get_error_types (error_types);
278 foreach (DataType error_type in error_types) {
279 // If GLib.Error is allowed we propagate everything
280 if (error_type.equals (gerror_type)) {
285 // Check the allowed error domains to propagate
286 var domain_check = new CCodeBinaryExpression (CCodeBinaryOperator.EQUALITY, new CCodeMemberAccess.pointer
287 (get_inner_error_cexpression (), "domain"), new CCodeIdentifier (get_ccode_upper_case_name (((ErrorType) error_type).error_domain)));
289 ccond = domain_check;
291 ccond = new CCodeBinaryExpression (CCodeBinaryOperator.OR, ccond, domain_check);
296 ccode.open_if (ccond);
297 return_with_exception (get_inner_error_cexpression ());
300 uncaught_error_statement (get_inner_error_cexpression ());
303 return_with_exception (get_inner_error_cexpression ());
306 uncaught_error_statement (get_inner_error_cexpression ());
314 public override void visit_try_statement (TryStatement stmt) {
315 int this_try_id = next_try_id++;
317 var old_try = current_try;
318 var old_try_id = current_try_id;
319 var old_is_in_catch = is_in_catch;
320 var old_catch = current_catch;
322 current_try_id = this_try_id;
325 foreach (CatchClause clause in stmt.get_catch_clauses ()) {
326 clause.set_attribute_string ("CCode", "cname", "__catch%d_%s".printf (this_try_id, get_ccode_lower_case_name (clause.error_type)));
330 stmt.body.emit (this);
333 foreach (CatchClause clause in stmt.get_catch_clauses ()) {
334 current_catch = clause;
335 ccode.add_goto ("__finally%d".printf (this_try_id));
339 current_try = old_try;
340 current_try_id = old_try_id;
341 is_in_catch = old_is_in_catch;
342 current_catch = old_catch;
344 ccode.add_label ("__finally%d".printf (this_try_id));
345 if (stmt.finally_body != null) {
346 // use a dedicated inner_error variable, if there
347 // is some error handling happening in finally-block
348 current_inner_error_id++;
349 stmt.finally_body.emit (this);
350 current_inner_error_id--;
353 // check for errors not handled by this try statement
354 // may be handled by outer try statements or propagated
355 add_simple_check (stmt, !stmt.after_try_block_reachable);
358 public override void visit_catch_clause (CatchClause clause) {
359 current_method_inner_error = true;
361 var error_type = (ErrorType) clause.error_type;
362 if (error_type.error_domain != null) {
363 generate_error_domain_declaration (error_type.error_domain, cfile);
366 ccode.add_label (clause.get_attribute_string ("CCode", "cname"));
370 if (clause.error_variable != null && clause.error_variable.used) {
371 visit_local_variable (clause.error_variable);
372 ccode.add_assignment (get_variable_cexpression (get_local_cname (clause.error_variable)), get_inner_error_cexpression ());
373 ccode.add_assignment (get_inner_error_cexpression (), new CCodeConstant ("NULL"));
375 if (clause.error_variable != null) {
376 clause.error_variable.unreachable = true;
378 // error object is not used within catch statement, clear it
379 cfile.add_include ("glib.h");
380 var cclear = new CCodeFunctionCall (new CCodeIdentifier ("g_clear_error"));
381 cclear.add_argument (new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, get_inner_error_cexpression ()));
382 ccode.add_expression (cclear);
385 clause.body.emit (this);
390 protected override void append_scope_free (Symbol sym, CodeNode? stop_at = null) {
391 base.append_scope_free (sym, stop_at);
393 if (!(stop_at is TryStatement || stop_at is CatchClause)) {
394 var finally_block = (Block) null;
395 if (sym.parent_node is TryStatement) {
396 finally_block = ((TryStatement) sym.parent_node).finally_body;
397 } else if (sym.parent_node is CatchClause) {
398 finally_block = ((TryStatement) sym.parent_node.parent_node).finally_body;
401 if (finally_block != null && finally_block != sym) {
402 finally_block.emit (this);