LCOV - code coverage report
Current view: top level - codegen - valagerrormodule.vala (source / functions) Coverage Total Hit
Test: vala 0.57.0.298-a8cae1 Lines: 97.8 % 232 227
Test Date: 2024-04-25 11:34:36 Functions: - 0 0

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

Generated by: LCOV version 2.0-1