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
20 * Jürg Billeter <j@bitron.ch>
21 * Thijs Vermeir <thijsvermeir@gmail.com>
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
) {
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 ()));
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 ();
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 ());
107 cerror_block
.add_statement (new
CCodeReturnStatement (default_value_for_type (current_return_type
, false)));
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)));
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
);
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
));
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
)) {
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 ()));
213 ccond
= domain_check
;
215 ccond
= new
CCodeBinaryExpression (CCodeBinaryOperator
.OR
, ccond
, domain_check
);
220 cerror_handler
= return_with_exception (inner_error
);
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
;
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
;
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"))));
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
;