Release 0.41.92
[vala-gnome.git] / codegen / valagerrormodule.vala
blobea8367fd361a924f6f006287f5bd8d610af3f7c6
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
19 * Author:
20 * Jürg Billeter <j@bitron.ch>
21 * Thijs Vermeir <thijsvermeir@gmail.com>
24 using GLib;
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))) {
33 return;
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)));
41 } else {
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);
83 pop_function ();
84 cfile.add_function (cquark_fun);
87 public override void visit_throw_statement (ThrowStatement stmt) {
88 // method will fail
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"));
111 } else {
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) {
141 ccode.add_return ();
142 } else {
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) {
161 return true;
163 current_node = current_node.parent_node;
165 return false;
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_");
173 if (always_fails) {
174 // inner_error is always set, avoid unnecessary if statement
175 // eliminates C warnings
176 } else {
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
187 if (is_in_catch) {
188 append_local_free (current_symbol, false, current_catch);
189 } else {
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;
200 if (!is_in_catch) {
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);
218 break;
219 } else {
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);
230 } else {
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);
241 ccode.close ();
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
257 } else {
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)) {
268 ccond = null;
269 break;
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)));
275 if (ccond == null) {
276 ccond = domain_check;
277 } else {
278 ccond = new CCodeBinaryExpression (CCodeBinaryOperator.OR, ccond, domain_check);
282 if (ccond != null) {
283 ccode.open_if (ccond);
284 return_with_exception (inner_error);
286 ccode.add_else ();
287 uncaught_error_statement (inner_error);
288 ccode.close ();
289 } else {
290 return_with_exception (inner_error);
292 } else {
293 uncaught_error_statement (inner_error);
296 if (!always_fails) {
297 ccode.close ();
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;
308 current_try = stmt;
309 current_try_id = this_try_id;
310 is_in_catch = true;
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));
316 is_in_catch = false;
317 stmt.body.emit (this);
318 is_in_catch = true;
320 foreach (CatchClause clause in stmt.get_catch_clauses ()) {
321 current_catch = clause;
322 ccode.add_goto ("__finally%d".printf (this_try_id));
323 clause.emit (this);
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);
351 ccode.open_block ();
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_"));
356 } else {
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);
366 ccode.close ();
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);
387 // vim:sw=8 noet