vapigen: Remove misleading --metadata, each .gir must have its own .metadata.
[vala-lang.git] / codegen / valagerrormodule.vala
blob3ccc68f3425be8551ca9644aace3df5455bef06a
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, edomain.get_cname ())) {
33 return;
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 ()));
41 } else {
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) {
88 // method will fail
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"));
112 } else {
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) {
142 ccode.add_return ();
143 } else {
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) {
158 return true;
160 current_node = current_node.parent_node;
162 return false;
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_");
170 if (always_fails) {
171 // inner_error is always set, avoid unnecessary if statement
172 // eliminates C warnings
173 } else {
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;
191 if (!is_in_catch) {
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);
209 break;
210 } else {
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);
221 } else {
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);
232 ccode.close ();
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
248 } else {
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)) {
259 ccond = null;
260 break;
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 ()));
266 if (ccond == null) {
267 ccond = domain_check;
268 } else {
269 ccond = new CCodeBinaryExpression (CCodeBinaryOperator.OR, ccond, domain_check);
273 if (ccond != null) {
274 ccode.open_if (ccond);
275 return_with_exception (inner_error);
277 ccode.add_else ();
278 uncaught_error_statement (inner_error);
279 ccode.close ();
280 } else {
281 return_with_exception (inner_error);
283 } else {
284 uncaught_error_statement (inner_error);
287 if (!always_fails) {
288 ccode.close ();
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;
298 current_try = stmt;
299 current_try_id = this_try_id;
300 is_in_catch = true;
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 ());
306 is_in_catch = false;
307 stmt.body.emit (this);
308 is_in_catch = true;
310 foreach (CatchClause clause in stmt.get_catch_clauses ()) {
311 ccode.add_goto ("__finally%d".printf (this_try_id));
312 clause.emit (this);
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);
339 ccode.open_block ();
341 string variable_name;
342 if (clause.variable_name != null) {
343 variable_name = get_variable_cname (clause.variable_name);
344 } else {
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);
351 } else {
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_")));
355 } else {
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);
365 ccode.close ();
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);
384 // vim:sw=8 noet