GError: Check the error code if available in the catch clause
[vala-lang.git] / gobject / valagerrormodule.vala
blob01b6e14f3eff029d34d6c492f7eb66e7bf9f01fb
1 /* valagerrormodule.vala
3 * Copyright (C) 2008 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;
25 using Gee;
27 internal class Vala.GErrorModule : CCodeDelegateModule {
28 private int current_try_id = 0;
29 private int next_try_id = 0;
31 public GErrorModule (CCodeGenerator codegen, CCodeModule? next) {
32 base (codegen, next);
35 public override void visit_error_domain (ErrorDomain edomain) {
36 cenum = new CCodeEnum (edomain.get_cname ());
38 if (edomain.source_reference.comment != null) {
39 header_declarations.add_type_definition (new CCodeComment (edomain.source_reference.comment));
41 header_declarations.add_type_definition (cenum);
43 edomain.accept_children (codegen);
45 string quark_fun_name = edomain.get_lower_case_cprefix () + "quark";
47 var error_domain_define = new CCodeMacroReplacement (edomain.get_upper_case_cname (), quark_fun_name + " ()");
48 header_declarations.add_type_definition (error_domain_define);
50 var cquark_fun = new CCodeFunction (quark_fun_name, gquark_type.data_type.get_cname ());
51 var cquark_block = new CCodeBlock ();
53 var cquark_call = new CCodeFunctionCall (new CCodeIdentifier ("g_quark_from_static_string"));
54 cquark_call.add_argument (new CCodeConstant ("\"" + edomain.get_lower_case_cname () + "-quark\""));
56 cquark_block.add_statement (new CCodeReturnStatement (cquark_call));
58 header_declarations.add_type_member_declaration (cquark_fun.copy ());
60 cquark_fun.block = cquark_block;
61 source_type_member_definition.append (cquark_fun);
64 public override void visit_error_code (ErrorCode ecode) {
65 if (ecode.value == null) {
66 cenum.add_value (new CCodeEnumValue (ecode.get_cname ()));
67 } else {
68 ecode.value.accept (codegen);
69 cenum.add_value (new CCodeEnumValue (ecode.get_cname (), (CCodeExpression) ecode.value.ccodenode));
73 public override void visit_throw_statement (ThrowStatement stmt) {
74 stmt.accept_children (codegen);
76 var cfrag = new CCodeFragment ();
78 // method will fail
79 current_method_inner_error = true;
80 var cassign = new CCodeAssignment (get_variable_cexpression ("inner_error"), (CCodeExpression) stmt.error_expression.ccodenode);
81 cfrag.append (new CCodeExpressionStatement (cassign));
83 head.add_simple_check (stmt, cfrag);
85 stmt.ccodenode = cfrag;
87 create_temp_decl (stmt, stmt.error_expression.temp_vars);
90 public virtual CCodeStatement return_with_exception (CCodeExpression error_expr)
92 var cpropagate = new CCodeFunctionCall (new CCodeIdentifier ("g_propagate_error"));
93 cpropagate.add_argument (get_variable_cexpression ("error"));
94 cpropagate.add_argument (error_expr);
96 var cerror_block = new CCodeBlock ();
97 cerror_block.add_statement (new CCodeExpressionStatement (cpropagate));
99 // free local variables
100 var free_frag = new CCodeFragment ();
101 append_local_free (current_symbol, free_frag, false);
102 cerror_block.add_statement (free_frag);
104 if (current_return_type is VoidType) {
105 cerror_block.add_statement (new CCodeReturnStatement ());
106 } else {
107 cerror_block.add_statement (new CCodeReturnStatement (default_value_for_type (current_return_type, false)));
110 return cerror_block;
113 CCodeStatement uncaught_error_statement (CCodeExpression inner_error) {
114 var cerror_block = new CCodeBlock ();
116 // free local variables
117 var free_frag = new CCodeFragment ();
118 append_local_free (current_symbol, free_frag, false);
119 cerror_block.add_statement (free_frag);
121 var ccritical = new CCodeFunctionCall (new CCodeIdentifier ("g_critical"));
122 ccritical.add_argument (new CCodeConstant ("\"file %s: line %d: uncaught error: %s\""));
123 ccritical.add_argument (new CCodeConstant ("__FILE__"));
124 ccritical.add_argument (new CCodeConstant ("__LINE__"));
125 ccritical.add_argument (new CCodeMemberAccess.pointer (inner_error, "message"));
127 var cclear = new CCodeFunctionCall (new CCodeIdentifier ("g_clear_error"));
128 cclear.add_argument (new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, inner_error));
130 var cprint_frag = new CCodeFragment ();
131 cprint_frag.append (new CCodeExpressionStatement (ccritical));
132 cprint_frag.append (new CCodeExpressionStatement (cclear));
134 // print critical message
135 cerror_block.add_statement (cprint_frag);
137 if (current_return_type is VoidType) {
138 cerror_block.add_statement (new CCodeReturnStatement ());
139 } else if (current_return_type != null) {
140 cerror_block.add_statement (new CCodeReturnStatement (default_value_for_type (current_return_type, false)));
143 return cerror_block;
146 public override void add_simple_check (CodeNode node, CCodeFragment cfrag) {
147 current_method_inner_error = true;
149 var inner_error = get_variable_cexpression ("inner_error");
151 CCodeStatement cerror_handler = null;
153 if (current_try != null) {
154 // surrounding try found
155 var cerror_block = new CCodeBlock ();
157 // free local variables
158 var free_frag = new CCodeFragment ();
159 append_error_free (current_symbol, free_frag, current_try);
160 cerror_block.add_statement (free_frag);
162 foreach (CatchClause clause in current_try.get_catch_clauses ()) {
163 // go to catch clause if error domain matches
164 var cgoto_stmt = new CCodeGotoStatement (clause.clabel_name);
166 if (clause.error_type.equals (gerror_type)) {
167 // general catch clause, this should be the last one
168 cerror_block.add_statement (cgoto_stmt);
169 break;
170 } else {
171 var catch_type = clause.error_type as ErrorType;
172 var cgoto_block = new CCodeBlock ();
173 cgoto_block.add_statement (cgoto_stmt);
175 if (catch_type.error_code != null) {
176 /* catch clause specifies a specific error code */
177 var error_match = new CCodeFunctionCall (new CCodeIdentifier ("g_error_matches"));
178 error_match.add_argument (inner_error);
179 error_match.add_argument (new CCodeIdentifier (catch_type.data_type.get_upper_case_cname ()));
180 error_match.add_argument (new CCodeIdentifier (catch_type.error_code.get_cname ()));
182 cerror_block.add_statement (new CCodeIfStatement (error_match, cgoto_block));
183 } else {
184 /* catch clause specifies a full error domain */
185 var ccond = new CCodeBinaryExpression (CCodeBinaryOperator.EQUALITY,
186 new CCodeMemberAccess.pointer (inner_error, "domain"), new CCodeIdentifier
187 (clause.error_type.data_type.get_upper_case_cname ()));
189 cerror_block.add_statement (new CCodeIfStatement (ccond, cgoto_block));
194 // go to finally clause if no catch clause matches
195 cerror_block.add_statement (new CCodeGotoStatement ("__finally%d".printf (current_try_id)));
197 cerror_handler = cerror_block;
198 } else if (current_method != null && current_method.get_error_types ().size > 0) {
199 // current method can fail, propagate error
200 CCodeBinaryExpression ccond = null;
202 foreach (DataType error_type in current_method.get_error_types ()) {
203 // If GLib.Error is allowed we propagate everything
204 if (error_type.equals (gerror_type)) {
205 ccond = null;
206 break;
209 // Check the allowed error domains to propagate
210 var domain_check = new CCodeBinaryExpression (CCodeBinaryOperator.EQUALITY, new CCodeMemberAccess.pointer
211 (inner_error, "domain"), new CCodeIdentifier (error_type.data_type.get_upper_case_cname ()));
212 if (ccond == null) {
213 ccond = domain_check;
214 } else {
215 ccond = new CCodeBinaryExpression (CCodeBinaryOperator.OR, ccond, domain_check);
219 if (ccond == null) {
220 cerror_handler = return_with_exception (inner_error);
221 } else {
222 var cerror_block = new CCodeBlock ();
223 cerror_block.add_statement (new CCodeIfStatement (ccond,
224 return_with_exception (inner_error),
225 uncaught_error_statement (inner_error)));
226 cerror_handler = cerror_block;
228 } else {
229 cerror_handler = uncaught_error_statement (inner_error);
232 var ccond = new CCodeBinaryExpression (CCodeBinaryOperator.INEQUALITY, inner_error, new CCodeConstant ("NULL"));
233 cfrag.append (new CCodeIfStatement (ccond, cerror_handler));
236 public override void visit_try_statement (TryStatement stmt) {
237 int this_try_id = next_try_id++;
239 var old_try = current_try;
240 var old_try_id = current_try_id;
241 current_try = stmt;
242 current_try_id = this_try_id;
244 foreach (CatchClause clause in stmt.get_catch_clauses ()) {
245 clause.clabel_name = "__catch%d_%s".printf (this_try_id, clause.error_type.get_lower_case_cname ());
248 if (stmt.finally_body != null) {
249 stmt.finally_body.accept (codegen);
252 stmt.body.accept (codegen);
254 current_try = old_try;
255 current_try_id = old_try_id;
257 foreach (CatchClause clause in stmt.get_catch_clauses ()) {
258 clause.accept (codegen);
261 if (stmt.finally_body != null) {
262 stmt.finally_body.accept (codegen);
265 var cfrag = new CCodeFragment ();
266 cfrag.append (stmt.body.ccodenode);
268 foreach (CatchClause clause in stmt.get_catch_clauses ()) {
269 cfrag.append (new CCodeGotoStatement ("__finally%d".printf (this_try_id)));
271 cfrag.append (clause.ccodenode);
274 cfrag.append (new CCodeLabel ("__finally%d".printf (this_try_id)));
275 if (stmt.finally_body != null) {
276 cfrag.append (stmt.finally_body.ccodenode);
279 // check for errors not handled by this try statement
280 // may be handled by outer try statements or propagated
281 add_simple_check (stmt, cfrag);
283 stmt.ccodenode = cfrag;
286 public override void visit_catch_clause (CatchClause clause) {
287 if (clause.error_variable != null) {
288 clause.error_variable.active = true;
291 current_method_inner_error = true;
293 clause.accept_children (codegen);
295 var cfrag = new CCodeFragment ();
296 cfrag.append (new CCodeLabel (clause.clabel_name));
298 var cblock = new CCodeBlock ();
300 string variable_name = clause.variable_name;
301 if (variable_name == null) {
302 variable_name = "__err";
305 if (current_method != null && current_method.coroutine) {
306 closure_struct.add_field ("GError *", variable_name);
307 cblock.add_statement (new CCodeExpressionStatement (new CCodeAssignment (get_variable_cexpression (variable_name), get_variable_cexpression ("inner_error"))));
308 } else {
309 var cdecl = new CCodeDeclaration ("GError *");
310 cdecl.add_declarator (new CCodeVariableDeclarator (variable_name, get_variable_cexpression ("inner_error")));
311 cblock.add_statement (cdecl);
313 cblock.add_statement (new CCodeExpressionStatement (new CCodeAssignment (get_variable_cexpression ("inner_error"), new CCodeConstant ("NULL"))));
315 cblock.add_statement (clause.body.ccodenode);
317 cfrag.append (cblock);
319 clause.ccodenode = cfrag;
323 // vim:sw=8 noet