public override void add_simple_check(CodeNode node, bool always_fails = false) { current_method_inner_error = true; var inner_error = get_variable_cexpression("_inner_error_"); if (always_fails) { // inner_error is always set, avoid unnecessary if statement // eliminates C warnings } else { var ccond = new CCodeBinaryExpression(CCodeBinaryOperator.INEQUALITY, inner_error, new CCodeConstant("NULL")); var unlikely = new CCodeFunctionCall(new CCodeIdentifier("G_UNLIKELY")); unlikely.add_argument(ccond); ccode.open_if(unlikely); } if (current_try != null) { // surrounding try found // free local variables if (is_in_catch) { append_local_free(current_symbol, false, current_catch); } else { append_local_free(current_symbol, false, current_try); } var error_types = new List <DataType>(); foreach (DataType node_error_type in node.get_error_types()) { error_types.Add(node_error_type); } bool has_general_catch_clause = false; if (!is_in_catch) { var handled_error_types = new List <DataType>(); foreach (CatchClause clause in current_try.get_catch_clauses()) { // keep track of unhandled error types foreach (DataType node_error_type in error_types) { if (clause.error_type == null || node_error_type.compatible(clause.error_type)) { handled_error_types.Add(node_error_type); } } foreach (DataType handled_error_type in handled_error_types) { error_types.Remove(handled_error_type); } handled_error_types.Clear(); if (clause.error_type.equals(gerror_type)) { // general catch clause, this should be the last one has_general_catch_clause = true; ccode.add_goto(clause.clabel_name); break; } else { var catch_type = clause.error_type as ErrorType; if (catch_type.error_code != null) { /* catch clause specifies a specific error code */ var error_match = new CCodeFunctionCall(new CCodeIdentifier("g_error_matches")); error_match.add_argument(inner_error); error_match.add_argument(new CCodeIdentifier(get_ccode_upper_case_name(catch_type.data_type))); error_match.add_argument(new CCodeIdentifier(get_ccode_name(catch_type.error_code))); ccode.open_if(error_match); } else { /* catch clause specifies a full error domain */ var ccond = new CCodeBinaryExpression(CCodeBinaryOperator.EQUALITY, CCodeMemberAccess.pointer(inner_error, "domain"), new CCodeIdentifier (get_ccode_upper_case_name(clause.error_type.data_type))); ccode.open_if(ccond); } // go to catch clause if error domain matches ccode.add_goto(clause.clabel_name); ccode.close(); } } } if (has_general_catch_clause) { // every possible error is already caught // as there is a general catch clause // no need to do anything else } else if (error_types.Count > 0) { // go to finally clause if no catch clause matches // and there are still unhandled error types ccode.add_goto("__finally%d".printf(current_try_id)); } else if (in_finally_block(node)) { // do not check unexpected errors happening within finally blocks // as jump out of finally block is not supported } else { // should never happen with correct bindings uncaught_error_statement(inner_error, true); } } else if (current_method != null && current_method.get_error_types().Count > 0) { // current method can fail, propagate error CCodeBinaryExpression ccond = null; foreach (DataType error_type in current_method.get_error_types()) { // If GLib.Error is allowed we propagate everything if (error_type.equals(gerror_type)) { ccond = null; break; } // Check the allowed error domains to propagate var domain_check = new CCodeBinaryExpression(CCodeBinaryOperator.EQUALITY, CCodeMemberAccess.pointer (inner_error, "domain"), new CCodeIdentifier(get_ccode_upper_case_name(error_type.data_type))); if (ccond == null) { ccond = domain_check; } else { ccond = new CCodeBinaryExpression(CCodeBinaryOperator.OR, ccond, domain_check); } } if (ccond != null) { ccode.open_if(ccond); return_with_exception(inner_error); ccode.add_else(); uncaught_error_statement(inner_error); ccode.close(); } else { return_with_exception(inner_error); } } else { uncaught_error_statement(inner_error); } if (!always_fails) { ccode.close(); } }
private void handle_errors(CodeNode node, bool always_fail = false) { if (node.tree_can_fail) { var last_block = current_block; // exceptional control flow foreach (DataType error_data_type in node.get_error_types()) { var error_type = error_data_type as ErrorType; current_block = last_block; unreachable_reported = true; for (int i = jump_stack.Count - 1; i >= 0; i--) { var jump_target = jump_stack[i]; if (jump_target.is_exit_target) { current_block.connect(jump_target.basic_block); mark_unreachable(); break; } else if (jump_target.is_error_target) { if (jump_target.error_domain == null || (jump_target.error_domain == error_type.error_domain && (jump_target.error_code == null || jump_target.error_code == error_type.error_code))) { // error can always be caught by this catch clause // following catch clauses cannot be reached by this error current_block.connect(jump_target.basic_block); mark_unreachable(); break; } else if (error_type.error_domain == null || (error_type.error_domain == jump_target.error_domain && (error_type.error_code == null || error_type.error_code == jump_target.error_code))) { // error might be caught by this catch clause // unknown at compile time // following catch clauses might still be reached by this error current_block.connect(jump_target.basic_block); } } else if (jump_target.is_finally_clause) { current_block.connect(jump_target.basic_block); current_block = jump_target.last_block; } } } // normal control flow if (!always_fail) { current_block = new BasicBlock(); last_block.connect(current_block); } } }