codegen: Fix array size variable on assignment
[vala-lang.git] / codegen / valagerrormodule.vala
blob0918a20be6cea639e90c9ed50fd833b2b7b1f563
1 /* valagerrormodule.vala
3 * Copyright (C) 2008-2010 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
19 * Author:
20 * Jürg Billeter <j@bitron.ch>
21 * Thijs Vermeir <thijsvermeir@gmail.com>
24 using GLib;
26 public class Vala.GErrorModule : CCodeDelegateModule {
27 private int current_try_id = 0;
28 private int next_try_id = 0;
29 private bool is_in_catch = false;
31 public override void generate_error_domain_declaration (ErrorDomain edomain, CCodeFile decl_space) {
32 if (add_symbol_declaration (decl_space, edomain, edomain.get_cname ())) {
33 return;
36 var cenum = new CCodeEnum (edomain.get_cname ());
38 foreach (ErrorCode ecode in edomain.get_codes ()) {
39 if (ecode.value == null) {
40 cenum.add_value (new CCodeEnumValue (ecode.get_cname ()));
41 } else {
42 ecode.value.emit (this);
43 cenum.add_value (new CCodeEnumValue (ecode.get_cname (), get_cvalue (ecode.value)));
47 decl_space.add_type_definition (cenum);
49 string quark_fun_name = edomain.get_lower_case_cprefix () + "quark";
51 var error_domain_define = new CCodeMacroReplacement (edomain.get_upper_case_cname (), quark_fun_name + " ()");
52 decl_space.add_type_definition (error_domain_define);
54 var cquark_fun = new CCodeFunction (quark_fun_name, gquark_type.data_type.get_cname ());
56 decl_space.add_function_declaration (cquark_fun);
59 public override void visit_error_domain (ErrorDomain edomain) {
60 if (edomain.comment != null) {
61 cfile.add_type_definition (new CCodeComment (edomain.comment.content));
64 generate_error_domain_declaration (edomain, cfile);
66 if (!edomain.is_internal_symbol ()) {
67 generate_error_domain_declaration (edomain, header_file);
69 if (!edomain.is_private_symbol ()) {
70 generate_error_domain_declaration (edomain, internal_header_file);
73 string quark_fun_name = edomain.get_lower_case_cprefix () + "quark";
75 var cquark_fun = new CCodeFunction (quark_fun_name, gquark_type.data_type.get_cname ());
76 var cquark_block = new CCodeBlock ();
78 var cquark_call = new CCodeFunctionCall (new CCodeIdentifier ("g_quark_from_static_string"));
79 cquark_call.add_argument (new CCodeConstant ("\"" + edomain.get_lower_case_cname () + "-quark\""));
81 cquark_block.add_statement (new CCodeReturnStatement (cquark_call));
83 cquark_fun.block = cquark_block;
84 cfile.add_function (cquark_fun);
87 public override void visit_throw_statement (ThrowStatement stmt) {
88 // method will fail
89 current_method_inner_error = true;
90 ccode.add_assignment (get_variable_cexpression ("_inner_error_"), get_cvalue (stmt.error_expression));
92 add_simple_check (stmt, true);
95 public virtual void return_with_exception (CCodeExpression error_expr) {
96 var cpropagate = new CCodeFunctionCall (new CCodeIdentifier ("g_propagate_error"));
97 cpropagate.add_argument (new CCodeIdentifier ("error"));
98 cpropagate.add_argument (error_expr);
100 ccode.add_expression (cpropagate);
102 // free local variables
103 append_local_free (current_symbol, false);
105 if (current_method is CreationMethod && current_method.parent_symbol is Class) {
106 var cl = (Class) current_method.parent_symbol;
107 var unref_call = get_unref_expression (new CCodeIdentifier ("self"), new ObjectType (cl), null);
108 ccode.add_expression (unref_call);
109 ccode.add_return (new CCodeConstant ("NULL"));
110 } else if (is_in_coroutine ()) {
111 ccode.add_return (new CCodeConstant ("FALSE"));
112 } else {
113 return_default_value (current_return_type);
117 void uncaught_error_statement (CCodeExpression inner_error, bool unexpected = false) {
118 // free local variables
119 append_local_free (current_symbol, false);
121 var ccritical = new CCodeFunctionCall (new CCodeIdentifier ("g_critical"));
122 ccritical.add_argument (new CCodeConstant (unexpected ? "\"file %s: line %d: unexpected error: %s (%s, %d)\"" : "\"file %s: line %d: uncaught error: %s (%s, %d)\""));
123 ccritical.add_argument (new CCodeConstant ("__FILE__"));
124 ccritical.add_argument (new CCodeConstant ("__LINE__"));
125 ccritical.add_argument (new CCodeMemberAccess.pointer (inner_error, "message"));
126 var domain_name = new CCodeFunctionCall (new CCodeIdentifier ("g_quark_to_string"));
127 domain_name.add_argument (new CCodeMemberAccess.pointer (inner_error, "domain"));
128 ccritical.add_argument (domain_name);
129 ccritical.add_argument (new CCodeMemberAccess.pointer (inner_error, "code"));
131 var cclear = new CCodeFunctionCall (new CCodeIdentifier ("g_clear_error"));
132 cclear.add_argument (new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, inner_error));
134 // print critical message
135 ccode.add_expression (ccritical);
136 ccode.add_expression (cclear);
138 if (is_in_constructor () || is_in_destructor ()) {
139 // just print critical, do not return prematurely
140 } else if (current_method is CreationMethod) {
141 if (current_method.parent_symbol is Struct) {
142 ccode.add_return ();
143 } else {
144 ccode.add_return (new CCodeConstant ("NULL"));
146 } else if (is_in_coroutine ()) {
147 ccode.add_return (new CCodeConstant ("FALSE"));
148 } else if (current_return_type != null) {
149 return_default_value (current_return_type);
153 bool in_finally_block (CodeNode node) {
154 var current_node = node;
155 while (current_node != null) {
156 var try_stmt = current_node.parent_node as TryStatement;
157 if (try_stmt != null && try_stmt.finally_body == current_node) {
158 return true;
160 current_node = current_node.parent_node;
162 return false;
165 public override void add_simple_check (CodeNode node, bool always_fails = false) {
166 current_method_inner_error = true;
168 var inner_error = get_variable_cexpression ("_inner_error_");
170 if (always_fails) {
171 // inner_error is always set, avoid unnecessary if statement
172 // eliminates C warnings
173 } else {
174 var ccond = new CCodeBinaryExpression (CCodeBinaryOperator.INEQUALITY, inner_error, new CCodeConstant ("NULL"));
175 ccode.open_if (ccond);
178 if (current_try != null) {
179 // surrounding try found
181 // free local variables
182 if (is_in_catch) {
183 append_local_free (current_symbol, false, current_catch);
184 } else {
185 append_local_free (current_symbol, false, current_try);
188 var error_types = new ArrayList<DataType> ();
189 foreach (DataType node_error_type in node.get_error_types ()) {
190 error_types.add (node_error_type);
193 bool has_general_catch_clause = false;
195 if (!is_in_catch) {
196 var handled_error_types = new ArrayList<DataType> ();
197 foreach (CatchClause clause in current_try.get_catch_clauses ()) {
198 // keep track of unhandled error types
199 foreach (DataType node_error_type in error_types) {
200 if (clause.error_type == null || node_error_type.compatible (clause.error_type)) {
201 handled_error_types.add (node_error_type);
204 foreach (DataType handled_error_type in handled_error_types) {
205 error_types.remove (handled_error_type);
207 handled_error_types.clear ();
209 if (clause.error_type.equals (gerror_type)) {
210 // general catch clause, this should be the last one
211 has_general_catch_clause = true;
212 ccode.add_goto (clause.clabel_name);
213 break;
214 } else {
215 var catch_type = clause.error_type as ErrorType;
217 if (catch_type.error_code != null) {
218 /* catch clause specifies a specific error code */
219 var error_match = new CCodeFunctionCall (new CCodeIdentifier ("g_error_matches"));
220 error_match.add_argument (inner_error);
221 error_match.add_argument (new CCodeIdentifier (catch_type.data_type.get_upper_case_cname ()));
222 error_match.add_argument (new CCodeIdentifier (catch_type.error_code.get_cname ()));
224 ccode.open_if (error_match);
225 } else {
226 /* catch clause specifies a full error domain */
227 var ccond = new CCodeBinaryExpression (CCodeBinaryOperator.EQUALITY,
228 new CCodeMemberAccess.pointer (inner_error, "domain"), new CCodeIdentifier
229 (clause.error_type.data_type.get_upper_case_cname ()));
231 ccode.open_if (ccond);
234 // go to catch clause if error domain matches
235 ccode.add_goto (clause.clabel_name);
236 ccode.close ();
241 if (has_general_catch_clause) {
242 // every possible error is already caught
243 // as there is a general catch clause
244 // no need to do anything else
245 } else if (error_types.size > 0) {
246 // go to finally clause if no catch clause matches
247 // and there are still unhandled error types
248 ccode.add_goto ("__finally%d".printf (current_try_id));
249 } else if (in_finally_block (node)) {
250 // do not check unexpected errors happening within finally blocks
251 // as jump out of finally block is not supported
252 } else {
253 // should never happen with correct bindings
254 uncaught_error_statement (inner_error, true);
256 } else if (current_method != null && current_method.get_error_types ().size > 0) {
257 // current method can fail, propagate error
258 CCodeBinaryExpression ccond = null;
260 foreach (DataType error_type in current_method.get_error_types ()) {
261 // If GLib.Error is allowed we propagate everything
262 if (error_type.equals (gerror_type)) {
263 ccond = null;
264 break;
267 // Check the allowed error domains to propagate
268 var domain_check = new CCodeBinaryExpression (CCodeBinaryOperator.EQUALITY, new CCodeMemberAccess.pointer
269 (inner_error, "domain"), new CCodeIdentifier (error_type.data_type.get_upper_case_cname ()));
270 if (ccond == null) {
271 ccond = domain_check;
272 } else {
273 ccond = new CCodeBinaryExpression (CCodeBinaryOperator.OR, ccond, domain_check);
277 if (ccond != null) {
278 ccode.open_if (ccond);
279 return_with_exception (inner_error);
281 ccode.add_else ();
282 uncaught_error_statement (inner_error);
283 ccode.close ();
284 } else {
285 return_with_exception (inner_error);
287 } else {
288 uncaught_error_statement (inner_error);
291 if (!always_fails) {
292 ccode.close ();
296 public override void visit_try_statement (TryStatement stmt) {
297 int this_try_id = next_try_id++;
299 var old_try = current_try;
300 var old_try_id = current_try_id;
301 var old_is_in_catch = is_in_catch;
302 var old_catch = current_catch;
303 current_try = stmt;
304 current_try_id = this_try_id;
305 is_in_catch = true;
307 foreach (CatchClause clause in stmt.get_catch_clauses ()) {
308 clause.clabel_name = "__catch%d_%s".printf (this_try_id, clause.error_type.get_lower_case_cname ());
311 is_in_catch = false;
312 stmt.body.emit (this);
313 is_in_catch = true;
315 foreach (CatchClause clause in stmt.get_catch_clauses ()) {
316 current_catch = clause;
317 ccode.add_goto ("__finally%d".printf (this_try_id));
318 clause.emit (this);
321 current_try = old_try;
322 current_try_id = old_try_id;
323 is_in_catch = old_is_in_catch;
324 current_catch = old_catch;
326 ccode.add_label ("__finally%d".printf (this_try_id));
327 if (stmt.finally_body != null) {
328 stmt.finally_body.emit (this);
331 // check for errors not handled by this try statement
332 // may be handled by outer try statements or propagated
333 add_simple_check (stmt, !stmt.after_try_block_reachable);
336 public override void visit_catch_clause (CatchClause clause) {
337 current_method_inner_error = true;
339 var error_type = (ErrorType) clause.error_type;
340 if (error_type.error_domain != null) {
341 generate_error_domain_declaration (error_type.error_domain, cfile);
344 ccode.add_label (clause.clabel_name);
346 ccode.open_block ();
348 string variable_name;
349 if (clause.variable_name != null) {
350 variable_name = get_variable_cname (clause.variable_name);
351 } else {
352 variable_name = "__err";
355 if (clause.variable_name != null) {
356 if (is_in_coroutine ()) {
357 closure_struct.add_field ("GError *", variable_name);
358 } else {
359 ccode.add_declaration ("GError *", new CCodeVariableDeclarator (variable_name));
361 ccode.add_assignment (get_variable_cexpression (variable_name), get_variable_cexpression ("_inner_error_"));
362 } else {
363 // error object is not used within catch statement, clear it
364 var cclear = new CCodeFunctionCall (new CCodeIdentifier ("g_clear_error"));
365 cclear.add_argument (new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, get_variable_cexpression ("_inner_error_")));
366 ccode.add_expression (cclear);
368 ccode.add_assignment (get_variable_cexpression ("_inner_error_"), new CCodeConstant ("NULL"));
370 clause.body.emit (this);
372 ccode.close ();
375 public override void append_local_free (Symbol sym, bool stop_at_loop = false, CodeNode? stop_at = null) {
376 if (!(stop_at is TryStatement || stop_at is CatchClause)) {
377 var finally_block = (Block) null;
378 if (sym.parent_node is TryStatement) {
379 finally_block = (sym.parent_node as TryStatement).finally_body;
380 } else if (sym.parent_node is CatchClause) {
381 finally_block = (sym.parent_node.parent_node as TryStatement).finally_body;
384 if (finally_block != null && finally_block != sym) {
385 finally_block.emit (this);
389 base.append_local_free (sym, stop_at_loop, stop_at);
393 // vim:sw=8 noet