1 /* valagerrormodule.vala
3 * Copyright (C) 2008-2009 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;
30 private bool is_in_catch
= false;
32 public GErrorModule (CCodeGenerator codegen
, CCodeModule? next
) {
36 public override void generate_error_domain_declaration (ErrorDomain edomain
, CCodeDeclarationSpace decl_space
) {
37 if (decl_space
.add_symbol_declaration (edomain
, edomain
.get_cname ())) {
41 var cenum
= new
CCodeEnum (edomain
.get_cname ());
43 foreach (ErrorCode ecode
in edomain
.get_codes ()) {
44 if (ecode
.value
== null) {
45 cenum
.add_value (new
CCodeEnumValue (ecode
.get_cname ()));
47 ecode
.value
.accept (codegen
);
48 cenum
.add_value (new
CCodeEnumValue (ecode
.get_cname (), (CCodeExpression
) ecode
.value
.ccodenode
));
52 decl_space
.add_type_definition (cenum
);
54 string quark_fun_name
= edomain
.get_lower_case_cprefix () + "quark";
56 var error_domain_define
= new
CCodeMacroReplacement (edomain
.get_upper_case_cname (), quark_fun_name
+ " ()");
57 decl_space
.add_type_definition (error_domain_define
);
59 var cquark_fun
= new
CCodeFunction (quark_fun_name
, gquark_type
.data_type
.get_cname ());
61 decl_space
.add_type_member_declaration (cquark_fun
);
64 public override void visit_error_domain (ErrorDomain edomain
) {
65 generate_error_domain_declaration (edomain
, source_declarations
);
67 if (!edomain
.is_internal_symbol ()) {
68 generate_error_domain_declaration (edomain
, header_declarations
);
70 if (!edomain
.is_private_symbol ()) {
71 generate_error_domain_declaration (edomain
, internal_header_declarations
);
74 string quark_fun_name
= edomain
.get_lower_case_cprefix () + "quark";
76 var cquark_fun
= new
CCodeFunction (quark_fun_name
, gquark_type
.data_type
.get_cname ());
77 var cquark_block
= new
CCodeBlock ();
79 var cquark_call
= new
CCodeFunctionCall (new
CCodeIdentifier ("g_quark_from_static_string"));
80 cquark_call
.add_argument (new
CCodeConstant ("\"" + edomain
.get_lower_case_cname () + "-quark\""));
82 cquark_block
.add_statement (new
CCodeReturnStatement (cquark_call
));
84 cquark_fun
.block
= cquark_block
;
85 source_type_member_definition
.append (cquark_fun
);
88 public override void visit_throw_statement (ThrowStatement stmt
) {
89 stmt
.accept_children (codegen
);
91 var cfrag
= new
CCodeFragment ();
94 current_method_inner_error
= true;
95 var cassign
= new
CCodeAssignment (get_variable_cexpression ("_inner_error_"), (CCodeExpression
) stmt
.error_expression
.ccodenode
);
96 cfrag
.append (new
CCodeExpressionStatement (cassign
));
98 head
.add_simple_check (stmt
, cfrag
);
100 stmt
.ccodenode
= cfrag
;
102 create_temp_decl (stmt
, stmt
.error_expression
.temp_vars
);
105 public virtual CCodeStatement
return_with_exception (CCodeExpression error_expr
)
107 var cpropagate
= new
CCodeFunctionCall (new
CCodeIdentifier ("g_propagate_error"));
108 cpropagate
.add_argument (new
CCodeIdentifier ("error"));
109 cpropagate
.add_argument (error_expr
);
111 var cerror_block
= new
CCodeBlock ();
112 cerror_block
.add_statement (new
CCodeExpressionStatement (cpropagate
));
114 // free local variables
115 var free_frag
= new
CCodeFragment ();
116 append_local_free (current_symbol
, free_frag
, false);
117 cerror_block
.add_statement (free_frag
);
119 if (current_return_type is VoidType
) {
120 cerror_block
.add_statement (new
CCodeReturnStatement ());
122 cerror_block
.add_statement (new
CCodeReturnStatement (default_value_for_type (current_return_type
, false)));
128 CCodeStatement
uncaught_error_statement (CCodeExpression inner_error
) {
129 var cerror_block
= new
CCodeBlock ();
131 // free local variables
132 var free_frag
= new
CCodeFragment ();
133 append_local_free (current_symbol
, free_frag
, false);
134 cerror_block
.add_statement (free_frag
);
136 var ccritical
= new
CCodeFunctionCall (new
CCodeIdentifier ("g_critical"));
137 ccritical
.add_argument (new
CCodeConstant ("\"file %s: line %d: uncaught error: %s\""));
138 ccritical
.add_argument (new
CCodeConstant ("__FILE__"));
139 ccritical
.add_argument (new
CCodeConstant ("__LINE__"));
140 ccritical
.add_argument (new CCodeMemberAccess
.pointer (inner_error
, "message"));
142 var cclear
= new
CCodeFunctionCall (new
CCodeIdentifier ("g_clear_error"));
143 cclear
.add_argument (new
CCodeUnaryExpression (CCodeUnaryOperator
.ADDRESS_OF
, inner_error
));
145 var cprint_frag
= new
CCodeFragment ();
146 cprint_frag
.append (new
CCodeExpressionStatement (ccritical
));
147 cprint_frag
.append (new
CCodeExpressionStatement (cclear
));
149 // print critical message
150 cerror_block
.add_statement (cprint_frag
);
152 if (current_method is CreationMethod
) {
153 cerror_block
.add_statement (new
CCodeReturnStatement (new
CCodeConstant ("NULL")));
154 } else if (current_method
!= null && current_method
.coroutine
) {
155 cerror_block
.add_statement (new
CCodeReturnStatement (new
CCodeConstant ("FALSE")));
156 } else if (current_return_type is VoidType
) {
157 cerror_block
.add_statement (new
CCodeReturnStatement ());
158 } else if (current_return_type
!= null) {
159 cerror_block
.add_statement (new
CCodeReturnStatement (default_value_for_type (current_return_type
, false)));
165 public override void add_simple_check (CodeNode node
, CCodeFragment cfrag
) {
166 current_method_inner_error
= true;
168 var inner_error
= get_variable_cexpression ("_inner_error_");
170 CCodeStatement cerror_handler
= null;
172 if (current_try
!= null) {
173 // surrounding try found
174 var cerror_block
= new
CCodeBlock ();
176 // free local variables
177 var free_frag
= new
CCodeFragment ();
178 append_error_free (current_symbol
, free_frag
, current_try
);
179 cerror_block
.add_statement (free_frag
);
182 foreach (CatchClause clause
in current_try
.get_catch_clauses ()) {
183 // go to catch clause if error domain matches
184 var cgoto_stmt
= new
CCodeGotoStatement (clause
.clabel_name
);
186 if (clause
.error_type
.equals (gerror_type
)) {
187 // general catch clause, this should be the last one
188 cerror_block
.add_statement (cgoto_stmt
);
191 var catch_type
= clause
.error_type as ErrorType
;
192 var cgoto_block
= new
CCodeBlock ();
193 cgoto_block
.add_statement (cgoto_stmt
);
195 if (catch_type
.error_code
!= null) {
196 /* catch clause specifies a specific error code */
197 var error_match
= new
CCodeFunctionCall (new
CCodeIdentifier ("g_error_matches"));
198 error_match
.add_argument (inner_error
);
199 error_match
.add_argument (new
CCodeIdentifier (catch_type
.data_type
.get_upper_case_cname ()));
200 error_match
.add_argument (new
CCodeIdentifier (catch_type
.error_code
.get_cname ()));
202 cerror_block
.add_statement (new
CCodeIfStatement (error_match
, cgoto_block
));
204 /* catch clause specifies a full error domain */
205 var ccond
= new
CCodeBinaryExpression (CCodeBinaryOperator
.EQUALITY
,
206 new CCodeMemberAccess
.pointer (inner_error
, "domain"), new CCodeIdentifier
207 (clause
.error_type
.data_type
.get_upper_case_cname ()));
209 cerror_block
.add_statement (new
CCodeIfStatement (ccond
, cgoto_block
));
215 // go to finally clause if no catch clause matches
216 cerror_block
.add_statement (new
CCodeGotoStatement ("__finally%d".printf (current_try_id
)));
218 cerror_handler
= cerror_block
;
219 } else if (current_method
!= null && current_method
.get_error_types ().size
> 0) {
220 // current method can fail, propagate error
221 CCodeBinaryExpression ccond
= null;
223 foreach (DataType error_type
in current_method
.get_error_types ()) {
224 // If GLib.Error is allowed we propagate everything
225 if (error_type
.equals (gerror_type
)) {
230 // Check the allowed error domains to propagate
231 var domain_check
= new
CCodeBinaryExpression (CCodeBinaryOperator
.EQUALITY
, new CCodeMemberAccess
.pointer
232 (inner_error
, "domain"), new
CCodeIdentifier (error_type
.data_type
.get_upper_case_cname ()));
234 ccond
= domain_check
;
236 ccond
= new
CCodeBinaryExpression (CCodeBinaryOperator
.OR
, ccond
, domain_check
);
241 cerror_handler
= return_with_exception (inner_error
);
243 var cerror_block
= new
CCodeBlock ();
244 cerror_block
.add_statement (new
CCodeIfStatement (ccond
,
245 return_with_exception (inner_error
),
246 uncaught_error_statement (inner_error
)));
247 cerror_handler
= cerror_block
;
250 cerror_handler
= uncaught_error_statement (inner_error
);
253 var ccond
= new
CCodeBinaryExpression (CCodeBinaryOperator
.INEQUALITY
, inner_error
, new
CCodeConstant ("NULL"));
254 cfrag
.append (new
CCodeIfStatement (ccond
, cerror_handler
));
257 public override void visit_try_statement (TryStatement stmt
) {
258 int this_try_id
= next_try_id
++;
260 var old_try
= current_try
;
261 var old_try_id
= current_try_id
;
262 var old_is_in_catch
= is_in_catch
;
264 current_try_id
= this_try_id
;
267 foreach (CatchClause clause
in stmt
.get_catch_clauses ()) {
268 clause
.clabel_name
= "__catch%d_%s".printf (this_try_id
, clause
.error_type
.get_lower_case_cname ());
271 if (stmt
.finally_body
!= null) {
272 stmt
.finally_body
.accept (codegen
);
276 stmt
.body
.accept (codegen
);
279 foreach (CatchClause clause
in stmt
.get_catch_clauses ()) {
280 clause
.accept (codegen
);
283 current_try
= old_try
;
284 current_try_id
= old_try_id
;
285 is_in_catch
= old_is_in_catch
;
287 var cfrag
= new
CCodeFragment ();
288 cfrag
.append (stmt
.body
.ccodenode
);
290 foreach (CatchClause clause
in stmt
.get_catch_clauses ()) {
291 cfrag
.append (new
CCodeGotoStatement ("__finally%d".printf (this_try_id
)));
292 cfrag
.append (clause
.ccodenode
);
295 cfrag
.append (new
CCodeLabel ("__finally%d".printf (this_try_id
)));
296 if (stmt
.finally_body
!= null) {
297 cfrag
.append (stmt
.finally_body
.ccodenode
);
300 // check for errors not handled by this try statement
301 // may be handled by outer try statements or propagated
302 add_simple_check (stmt
, cfrag
);
304 stmt
.ccodenode
= cfrag
;
307 public override void visit_catch_clause (CatchClause clause
) {
308 if (clause
.error_variable
!= null) {
309 clause
.error_variable
.active
= true;
312 current_method_inner_error
= true;
314 var error_type
= (ErrorType
) clause
.error_type
;
315 if (error_type
.error_domain
!= null) {
316 generate_error_domain_declaration (error_type
.error_domain
, source_declarations
);
319 clause
.accept_children (codegen
);
321 var cfrag
= new
CCodeFragment ();
322 cfrag
.append (new
CCodeLabel (clause
.clabel_name
));
324 var cblock
= new
CCodeBlock ();
326 string variable_name
;
327 if (clause
.variable_name
!= null) {
328 variable_name
= get_variable_cname (clause
.variable_name
);
330 variable_name
= "__err";
333 if (current_method
!= null && current_method
.coroutine
) {
334 closure_struct
.add_field ("GError *", variable_name
);
335 cblock
.add_statement (new
CCodeExpressionStatement (new
CCodeAssignment (get_variable_cexpression (variable_name
), get_variable_cexpression ("_inner_error_"))));
337 var cdecl
= new
CCodeDeclaration ("GError *");
338 cdecl
.add_declarator (new
CCodeVariableDeclarator (variable_name
, get_variable_cexpression ("_inner_error_")));
339 cblock
.add_statement (cdecl
);
341 cblock
.add_statement (new
CCodeExpressionStatement (new
CCodeAssignment (get_variable_cexpression ("_inner_error_"), new
CCodeConstant ("NULL"))));
343 cblock
.add_statement (clause
.body
.ccodenode
);
345 cfrag
.append (cblock
);
347 clause
.ccodenode
= cfrag
;
350 public override void append_local_free (Symbol sym
, CCodeFragment cfrag
, bool stop_at_loop
= false) {
351 var finally_block
= (Block
) null;
352 if (sym
.parent_node is TryStatement
) {
353 finally_block
= (sym
.parent_node as TryStatement
).finally_body
;
354 } else if (sym
.parent_node is CatchClause
) {
355 finally_block
= (sym
.parent_node
.parent_node as TryStatement
).finally_body
;
358 if (finally_block
!= null) {
359 cfrag
.append (finally_block
.ccodenode
);
362 base.append_local_free (sym
, cfrag
, stop_at_loop
);