1/* valagerrormodule.vala 2 * 3 * Copyright (C) 2008-2010 Jürg Billeter 4 * 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. 9 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. 14 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 18 * 19 * Author: 20 * Jürg Billeter <j@bitron.ch> 21 * Thijs Vermeir <thijsvermeir@gmail.com> 22 */ 23 24using GLib; 25 26public class Vala.GErrorModule : CCodeDelegateModule { 27 private bool is_in_catch = false; 28 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))) { 31 return; 32 } 33 34 generate_type_declaration (gquark_type, decl_space); 35 36 var cenum = new CCodeEnum (get_ccode_name (edomain)); 37 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))); 44 } 45 } 46 47 decl_space.add_type_definition (cenum); 48 49 string quark_fun_name = get_ccode_lower_case_prefix (edomain) + "quark"; 50 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); 53 54 var cquark_fun = new CCodeFunction (quark_fun_name, get_ccode_name (gquark_type.type_symbol)); 55 56 decl_space.add_function_declaration (cquark_fun); 57 } 58 59 public override void visit_error_domain (ErrorDomain edomain) { 60 if (edomain.comment != null) { 61 cfile.add_type_definition (new CCodeComment (edomain.comment.content)); 62 } 63 64 generate_error_domain_declaration (edomain, cfile); 65 66 if (!edomain.is_internal_symbol ()) { 67 generate_error_domain_declaration (edomain, header_file); 68 } 69 if (!edomain.is_private_symbol ()) { 70 generate_error_domain_declaration (edomain, internal_header_file); 71 } 72 73 edomain.accept_children (this); 74 75 string quark_fun_name = get_ccode_lower_case_prefix (edomain) + "quark"; 76 77 var cquark_fun = new CCodeFunction (quark_fun_name, get_ccode_name (gquark_type.type_symbol)); 78 push_function (cquark_fun); 79 80 var cquark_call = new CCodeFunctionCall (new CCodeIdentifier ("g_quark_from_static_string")); 81 cquark_call.add_argument (new CCodeConstant ("\"" + get_ccode_quark_name (edomain) + "\"")); 82 83 ccode.add_return (cquark_call); 84 85 pop_function (); 86 cfile.add_function (cquark_fun); 87 } 88 89 public override void visit_throw_statement (ThrowStatement stmt) { 90 // method will fail 91 current_method_inner_error = true; 92 ccode.add_assignment (get_inner_error_cexpression (), get_cvalue (stmt.error_expression)); 93 94 add_simple_check (stmt, true); 95 } 96 97 public virtual void return_with_exception (CCodeExpression error_expr) { 98 var cpropagate = new CCodeFunctionCall (new CCodeIdentifier ("g_propagate_error")); 99 cpropagate.add_argument (new CCodeIdentifier ("error")); 100 cpropagate.add_argument (error_expr); 101 102 ccode.add_expression (cpropagate); 103 104 // free local variables 105 append_local_free (current_symbol); 106 107 // free possibly already assigned out-parameter 108 append_out_param_free (current_method); 109 110 if (current_method is CreationMethod && current_method.parent_symbol is Class) { 111 var cl = (Class) current_method.parent_symbol; 112 ccode.add_expression (destroy_value (new GLibValue (new ObjectType (cl), new CCodeIdentifier ("self"), true))); 113 ccode.add_return (new CCodeConstant ("NULL")); 114 } else if (is_in_coroutine ()) { 115 ccode.add_return (new CCodeConstant ("FALSE")); 116 } else { 117 return_default_value (current_return_type, true); 118 } 119 } 120 121 void uncaught_error_statement (CCodeExpression inner_error, bool unexpected = false, CodeNode? start_at = null) { 122 // free local variables 123 if (start_at is TryStatement) { 124 append_local_free (start_at.parent_node as Block); 125 } else { 126 append_local_free (current_symbol); 127 } 128 129 // free possibly already assigned out-parameter 130 append_out_param_free (current_method); 131 132 cfile.add_include ("glib.h"); 133 134 var ccritical = new CCodeFunctionCall (new CCodeIdentifier ("g_critical")); 135 ccritical.add_argument (new CCodeConstant (unexpected ? "\"file %s: line %d: unexpected error: %s (%s, %d)\"" : "\"file %s: line %d: uncaught error: %s (%s, %d)\"")); 136 ccritical.add_argument (new CCodeConstant ("__FILE__")); 137 ccritical.add_argument (new CCodeConstant ("__LINE__")); 138 ccritical.add_argument (new CCodeMemberAccess.pointer (inner_error, "message")); 139 var domain_name = new CCodeFunctionCall (new CCodeIdentifier ("g_quark_to_string")); 140 domain_name.add_argument (new CCodeMemberAccess.pointer (inner_error, "domain")); 141 ccritical.add_argument (domain_name); 142 ccritical.add_argument (new CCodeMemberAccess.pointer (inner_error, "code")); 143 144 var cclear = new CCodeFunctionCall (new CCodeIdentifier ("g_clear_error")); 145 cclear.add_argument (new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, inner_error)); 146 147 // print critical message 148 ccode.add_expression (ccritical); 149 ccode.add_expression (cclear); 150 151 if (is_in_constructor () || is_in_destructor ()) { 152 // just print critical, do not return prematurely 153 } else if (current_method is CreationMethod) { 154 if (current_method.parent_symbol is Struct) { 155 ccode.add_return (); 156 } else { 157 ccode.add_return (new CCodeConstant ("NULL")); 158 } 159 } else if (is_in_coroutine ()) { 160 var async_result_expr = new CCodeMemberAccess.pointer (new CCodeIdentifier ("_data_"), "_async_result"); 161 var unref = new CCodeFunctionCall (new CCodeIdentifier ("g_object_unref")); 162 unref.add_argument (async_result_expr); 163 ccode.add_expression (unref); 164 ccode.add_return (new CCodeConstant ("FALSE")); 165 } else if (current_return_type != null) { 166 return_default_value (current_return_type, true); 167 } 168 } 169 170 bool in_finally_block (CodeNode node) { 171 var current_node = node; 172 while (current_node != null) { 173 var try_stmt = current_node.parent_node as TryStatement; 174 if (try_stmt != null && try_stmt.finally_body == current_node) { 175 return true; 176 } 177 current_node = current_node.parent_node; 178 } 179 return false; 180 } 181 182 public override void add_simple_check (CodeNode node, bool always_fails = false) { 183 current_method_inner_error = true; 184 185 if (always_fails) { 186 // inner_error is always set, avoid unnecessary if statement 187 // eliminates C warnings 188 } else { 189 var ccond = new CCodeBinaryExpression (CCodeBinaryOperator.INEQUALITY, get_inner_error_cexpression (), new CCodeConstant ("NULL")); 190 var unlikely = new CCodeFunctionCall (new CCodeIdentifier ("G_UNLIKELY")); 191 unlikely.add_argument (ccond); 192 ccode.open_if (unlikely); 193 } 194 195 if (current_try != null) { 196 // surrounding try found 197 198 // free local variables 199 if (is_in_catch) { 200 append_local_free (current_symbol, null, current_catch); 201 } else { 202 append_local_free (current_symbol, null, current_try); 203 } 204 205 var error_types = new ArrayList<DataType> (); 206 node.get_error_types (error_types); 207 208 bool has_general_catch_clause = false; 209 210 if (!is_in_catch) { 211 var handled_error_types = new ArrayList<DataType> (); 212 foreach (CatchClause clause in current_try.get_catch_clauses ()) { 213 // keep track of unhandled error types 214 foreach (DataType node_error_type in error_types) { 215 if (clause.error_type == null || node_error_type.compatible (clause.error_type)) { 216 handled_error_types.add (node_error_type); 217 } 218 } 219 foreach (DataType handled_error_type in handled_error_types) { 220 error_types.remove (handled_error_type); 221 } 222 handled_error_types.clear (); 223 224 if (clause.error_type.equals (gerror_type)) { 225 // general catch clause, this should be the last one 226 has_general_catch_clause = true; 227 ccode.add_goto (clause.clabel_name); 228 break; 229 } else { 230 var catch_type = clause.error_type as ErrorType; 231 232 if (catch_type.error_code != null) { 233 /* catch clause specifies a specific error code */ 234 var error_match = new CCodeFunctionCall (new CCodeIdentifier ("g_error_matches")); 235 error_match.add_argument (get_inner_error_cexpression ()); 236 error_match.add_argument (new CCodeIdentifier (get_ccode_upper_case_name (catch_type.type_symbol))); 237 error_match.add_argument (new CCodeIdentifier (get_ccode_name (catch_type.error_code))); 238 239 ccode.open_if (error_match); 240 } else { 241 /* catch clause specifies a full error domain */ 242 var ccond = new CCodeBinaryExpression (CCodeBinaryOperator.EQUALITY, 243 new CCodeMemberAccess.pointer (get_inner_error_cexpression (), "domain"), new CCodeIdentifier 244 (get_ccode_upper_case_name (clause.error_type.type_symbol))); 245 246 ccode.open_if (ccond); 247 } 248 249 // go to catch clause if error domain matches 250 ccode.add_goto (clause.clabel_name); 251 ccode.close (); 252 } 253 } 254 } 255 256 if (has_general_catch_clause) { 257 // every possible error is already caught 258 // as there is a general catch clause 259 // no need to do anything else 260 } else if (error_types.size > 0) { 261 // go to finally clause if no catch clause matches 262 // and there are still unhandled error types 263 ccode.add_goto ("__finally%d".printf (current_try_id)); 264 } else if (in_finally_block (node)) { 265 // do not check unexpected errors happening within finally blocks 266 // as jump out of finally block is not supported 267 } else { 268 // should never happen with correct bindings 269 uncaught_error_statement (get_inner_error_cexpression (), true, current_try); 270 } 271 } else if (current_method != null && current_method.tree_can_fail) { 272 // current method can fail, propagate error 273 CCodeBinaryExpression ccond = null; 274 275 var error_types = new ArrayList<DataType> (); 276 current_method.get_error_types (error_types); 277 foreach (DataType error_type in error_types) { 278 // If GLib.Error is allowed we propagate everything 279 if (error_type.equals (gerror_type)) { 280 ccond = null; 281 break; 282 } 283 284 // Check the allowed error domains to propagate 285 var domain_check = new CCodeBinaryExpression (CCodeBinaryOperator.EQUALITY, new CCodeMemberAccess.pointer 286 (get_inner_error_cexpression (), "domain"), new CCodeIdentifier (get_ccode_upper_case_name (error_type.type_symbol))); 287 if (ccond == null) { 288 ccond = domain_check; 289 } else { 290 ccond = new CCodeBinaryExpression (CCodeBinaryOperator.OR, ccond, domain_check); 291 } 292 } 293 294 if (ccond != null) { 295 ccode.open_if (ccond); 296 return_with_exception (get_inner_error_cexpression ()); 297 298 ccode.add_else (); 299 uncaught_error_statement (get_inner_error_cexpression ()); 300 ccode.close (); 301 } else { 302 return_with_exception (get_inner_error_cexpression ()); 303 } 304 } else { 305 uncaught_error_statement (get_inner_error_cexpression ()); 306 } 307 308 if (!always_fails) { 309 ccode.close (); 310 } 311 } 312 313 public override void visit_try_statement (TryStatement stmt) { 314 int this_try_id = next_try_id++; 315 316 var old_try = current_try; 317 var old_try_id = current_try_id; 318 var old_is_in_catch = is_in_catch; 319 var old_catch = current_catch; 320 current_try = stmt; 321 current_try_id = this_try_id; 322 is_in_catch = true; 323 324 foreach (CatchClause clause in stmt.get_catch_clauses ()) { 325 clause.clabel_name = "__catch%d_%s".printf (this_try_id, get_ccode_lower_case_name (clause.error_type)); 326 } 327 328 is_in_catch = false; 329 stmt.body.emit (this); 330 is_in_catch = true; 331 332 foreach (CatchClause clause in stmt.get_catch_clauses ()) { 333 current_catch = clause; 334 ccode.add_goto ("__finally%d".printf (this_try_id)); 335 clause.emit (this); 336 } 337 338 current_try = old_try; 339 current_try_id = old_try_id; 340 is_in_catch = old_is_in_catch; 341 current_catch = old_catch; 342 343 ccode.add_label ("__finally%d".printf (this_try_id)); 344 if (stmt.finally_body != null) { 345 // use a dedicated inner_error variable, if there 346 // is some error handling happening in finally-block 347 current_inner_error_id++; 348 stmt.finally_body.emit (this); 349 current_inner_error_id--; 350 } 351 352 // check for errors not handled by this try statement 353 // may be handled by outer try statements or propagated 354 add_simple_check (stmt, !stmt.after_try_block_reachable); 355 } 356 357 public override void visit_catch_clause (CatchClause clause) { 358 current_method_inner_error = true; 359 360 var error_type = (ErrorType) clause.error_type; 361 if (error_type.error_domain != null) { 362 generate_error_domain_declaration (error_type.error_domain, cfile); 363 } 364 365 ccode.add_label (clause.clabel_name); 366 367 ccode.open_block (); 368 369 if (clause.error_variable != null && clause.error_variable.used) { 370 visit_local_variable (clause.error_variable); 371 ccode.add_assignment (get_variable_cexpression (get_local_cname (clause.error_variable)), get_inner_error_cexpression ()); 372 ccode.add_assignment (get_inner_error_cexpression (), new CCodeConstant ("NULL")); 373 } else { 374 if (clause.error_variable != null) { 375 clause.error_variable.unreachable = true; 376 } 377 // error object is not used within catch statement, clear it 378 cfile.add_include ("glib.h"); 379 var cclear = new CCodeFunctionCall (new CCodeIdentifier ("g_clear_error")); 380 cclear.add_argument (new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, get_inner_error_cexpression ())); 381 ccode.add_expression (cclear); 382 } 383 384 clause.body.emit (this); 385 386 ccode.close (); 387 } 388 389 protected override void append_scope_free (Symbol sym, CodeNode? stop_at = null) { 390 base.append_scope_free (sym, stop_at); 391 392 if (!(stop_at is TryStatement || stop_at is CatchClause)) { 393 var finally_block = (Block) null; 394 if (sym.parent_node is TryStatement) { 395 finally_block = ((TryStatement) sym.parent_node).finally_body; 396 } else if (sym.parent_node is CatchClause) { 397 finally_block = ((TryStatement) sym.parent_node.parent_node).finally_body; 398 } 399 400 if (finally_block != null && finally_block != sym) { 401 finally_block.emit (this); 402 } 403 } 404 } 405} 406 407// vim:sw=8 noet 408