Fix ref_sink of Gtk.Window created with GLib.Object.new
[vala-lang.git] / codegen / valadovaerrormodule.vala
blob8813760048f4d9c86f80cde70ec104c13b8bda5f
1 /* valadovaerrormodule.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 public class Vala.DovaErrorModule : DovaDelegateModule {
25 private int current_try_id = 0;
26 private int next_try_id = 0;
27 private bool is_in_catch = false;
29 public override void visit_throw_statement (ThrowStatement stmt) {
30 var cfrag = new CCodeFragment ();
32 // method will fail
33 var cassign = new CCodeAssignment (new CCodeIdentifier ("dova_error"), (CCodeExpression) stmt.error_expression.ccodenode);
34 cfrag.append (new CCodeExpressionStatement (cassign));
36 add_simple_check (stmt, cfrag, true);
38 stmt.ccodenode = cfrag;
40 create_temp_decl (stmt, stmt.error_expression.temp_vars);
43 public virtual CCodeStatement return_with_exception () {
44 var cerror_block = new CCodeBlock ();
46 // propagate error
47 // nothing to do
49 // free local variables
50 var free_frag = new CCodeFragment ();
51 append_local_free (current_symbol, free_frag, false);
52 cerror_block.add_statement (free_frag);
54 if (current_method is CreationMethod && current_method.parent_symbol is Class) {
55 var cl = current_method.parent_symbol as Class;
56 var unref_call = new CCodeFunctionCall (new CCodeIdentifier (cl.get_unref_function ()));
57 unref_call.add_argument (new CCodeIdentifier ("this"));
58 cerror_block.add_statement (new CCodeExpressionStatement (unref_call));
59 cerror_block.add_statement (new CCodeReturnStatement ());
60 } else if (current_return_type is VoidType) {
61 cerror_block.add_statement (new CCodeReturnStatement ());
62 } else {
63 cerror_block.add_statement (new CCodeReturnStatement (default_value_for_type (current_return_type, false)));
66 return cerror_block;
69 CCodeStatement uncaught_error_statement (CCodeBlock? block = null, bool unexpected = false) {
70 var cerror_block = block;
71 if (cerror_block == null) {
72 cerror_block = new CCodeBlock ();
75 // free local variables
76 var free_frag = new CCodeFragment ();
77 append_local_free (current_symbol, free_frag, false);
78 cerror_block.add_statement (free_frag);
80 // TODO log uncaught error as critical warning
82 if (current_method is CreationMethod) {
83 cerror_block.add_statement (new CCodeReturnStatement ());
84 } else if (current_return_type is VoidType) {
85 cerror_block.add_statement (new CCodeReturnStatement ());
86 } else if (current_return_type != null) {
87 cerror_block.add_statement (new CCodeReturnStatement (default_value_for_type (current_return_type, false)));
90 return cerror_block;
93 bool in_finally_block (CodeNode node) {
94 var current_node = node;
95 while (current_node != null) {
96 var try_stmt = current_node.parent_node as TryStatement;
97 if (try_stmt != null && try_stmt.finally_body == current_node) {
98 return true;
100 current_node = current_node.parent_node;
102 return false;
105 public override void add_simple_check (CodeNode node, CCodeFragment cfrag, bool always_fails = false) {
106 CCodeStatement cerror_handler = null;
108 if (current_try != null) {
109 // surrounding try found
110 var cerror_block = new CCodeBlock ();
112 // free local variables
113 var free_frag = new CCodeFragment ();
114 append_error_free (current_symbol, free_frag, current_try);
115 cerror_block.add_statement (free_frag);
117 var error_types = new ArrayList<DataType> ();
118 foreach (DataType node_error_type in node.get_error_types ()) {
119 error_types.add (node_error_type);
122 bool has_general_catch_clause = false;
124 if (!is_in_catch) {
125 var handled_error_types = new ArrayList<DataType> ();
126 foreach (CatchClause clause in current_try.get_catch_clauses ()) {
127 // keep track of unhandled error types
128 foreach (DataType node_error_type in error_types) {
129 if (clause.error_type == null || node_error_type.compatible (clause.error_type)) {
130 handled_error_types.add (node_error_type);
133 foreach (DataType handled_error_type in handled_error_types) {
134 error_types.remove (handled_error_type);
136 handled_error_types.clear ();
138 // go to catch clause if error domain matches
139 var cgoto_stmt = new CCodeGotoStatement (clause.clabel_name);
141 if (clause.error_type.equals (new ObjectType (error_class))) {
142 // general catch clause, this should be the last one
143 has_general_catch_clause = true;
144 cerror_block.add_statement (cgoto_stmt);
145 break;
146 } else {
147 var catch_type = clause.error_type as ObjectType;
148 var cgoto_block = new CCodeBlock ();
149 cgoto_block.add_statement (cgoto_stmt);
151 var type_check = new CCodeFunctionCall (new CCodeIdentifier ("any_is_a"));
152 type_check.add_argument (new CCodeIdentifier ("dova_error"));
153 type_check.add_argument (new CCodeFunctionCall (new CCodeIdentifier ("%s_type_get".printf (catch_type.type_symbol.get_lower_case_cname ()))));
155 cerror_block.add_statement (new CCodeIfStatement (type_check, cgoto_block));
160 if (has_general_catch_clause) {
161 // every possible error is already caught
162 // as there is a general catch clause
163 // no need to do anything else
164 } else if (error_types.size > 0) {
165 // go to finally clause if no catch clause matches
166 // and there are still unhandled error types
167 cerror_block.add_statement (new CCodeGotoStatement ("__finally%d".printf (current_try_id)));
168 } else if (in_finally_block (node)) {
169 // do not check unexpected errors happening within finally blocks
170 // as jump out of finally block is not supported
171 } else {
172 // should never happen with correct bindings
173 uncaught_error_statement (cerror_block, true);
176 cerror_handler = cerror_block;
177 } else if (current_method != null && current_method.get_error_types ().size > 0) {
178 // current method can fail, propagate error
179 CCodeExpression ccond = null;
181 foreach (DataType error_type in current_method.get_error_types ()) {
182 // If Dova.Error is allowed we propagate everything
183 if (error_type.equals (new ObjectType (error_class))) {
184 ccond = null;
185 break;
188 // Check the allowed error domains to propagate
189 var type_check = new CCodeFunctionCall (new CCodeIdentifier ("any_is_a"));
190 type_check.add_argument (new CCodeIdentifier ("dova_error"));
191 type_check.add_argument (new CCodeFunctionCall (new CCodeIdentifier ("%s_type_get".printf (error_class.get_lower_case_cname ()))));
192 if (ccond == null) {
193 ccond = type_check;
194 } else {
195 ccond = new CCodeBinaryExpression (CCodeBinaryOperator.OR, ccond, type_check);
199 if (ccond == null) {
200 cerror_handler = return_with_exception ();
201 } else {
202 var cerror_block = new CCodeBlock ();
203 cerror_block.add_statement (new CCodeIfStatement (ccond,
204 return_with_exception (),
205 uncaught_error_statement ()));
206 cerror_handler = cerror_block;
208 } else {
209 cerror_handler = uncaught_error_statement ();
212 if (always_fails) {
213 // inner_error is always set, avoid unnecessary if statement
214 // eliminates C warnings
215 cfrag.append (cerror_handler);
216 } else {
217 var ccond = new CCodeBinaryExpression (CCodeBinaryOperator.INEQUALITY, new CCodeIdentifier ("dova_error"), new CCodeConstant ("NULL"));
218 cfrag.append (new CCodeIfStatement (ccond, cerror_handler));
222 public override void visit_try_statement (TryStatement stmt) {
223 int this_try_id = next_try_id++;
225 var old_try = current_try;
226 var old_try_id = current_try_id;
227 var old_is_in_catch = is_in_catch;
228 current_try = stmt;
229 current_try_id = this_try_id;
230 is_in_catch = true;
232 foreach (CatchClause clause in stmt.get_catch_clauses ()) {
233 clause.clabel_name = "__catch%d_%s".printf (this_try_id, clause.error_type.get_lower_case_cname ());
236 if (stmt.finally_body != null) {
237 stmt.finally_body.emit (this);
240 is_in_catch = false;
241 stmt.body.emit (this);
242 is_in_catch = true;
244 foreach (CatchClause clause in stmt.get_catch_clauses ()) {
245 clause.emit (this);
248 current_try = old_try;
249 current_try_id = old_try_id;
250 is_in_catch = old_is_in_catch;
252 var cfrag = new CCodeFragment ();
253 cfrag.append (stmt.body.ccodenode);
255 foreach (CatchClause clause in stmt.get_catch_clauses ()) {
256 cfrag.append (new CCodeGotoStatement ("__finally%d".printf (this_try_id)));
257 cfrag.append (clause.ccodenode);
260 cfrag.append (new CCodeLabel ("__finally%d".printf (this_try_id)));
261 if (stmt.finally_body != null) {
262 cfrag.append (stmt.finally_body.ccodenode);
265 // check for errors not handled by this try statement
266 // may be handled by outer try statements or propagated
267 add_simple_check (stmt, cfrag, !stmt.after_try_block_reachable);
269 stmt.ccodenode = cfrag;
272 public override void visit_catch_clause (CatchClause clause) {
273 generate_type_declaration (clause.error_type, source_declarations);
275 clause.body.emit (this);
277 var cfrag = new CCodeFragment ();
278 cfrag.append (new CCodeLabel (clause.clabel_name));
280 var cblock = new CCodeBlock ();
282 string variable_name;
283 if (clause.variable_name != null) {
284 variable_name = get_variable_cname (clause.variable_name);
285 } else {
286 variable_name = "__err";
289 if (clause.variable_name != null) {
290 var cdecl = new CCodeDeclaration (clause.error_type.get_cname ());
291 cdecl.add_declarator (new CCodeVariableDeclarator (variable_name, new CCodeIdentifier ("dova_error")));
292 cblock.add_statement (cdecl);
293 } else {
294 // error object is not used within catch statement, clear it
295 var cclear = new CCodeFunctionCall (new CCodeIdentifier ("dova_object_unref"));
296 cclear.add_argument (new CCodeIdentifier ("dova_error"));
297 cblock.add_statement (new CCodeExpressionStatement (cclear));
299 cblock.add_statement (new CCodeExpressionStatement (new CCodeAssignment (new CCodeIdentifier ("dova_error"), new CCodeConstant ("NULL"))));
301 cblock.add_statement (clause.body.ccodenode);
303 cfrag.append (cblock);
305 clause.ccodenode = cfrag;
308 public override void append_local_free (Symbol sym, CCodeFragment cfrag, bool stop_at_loop = false) {
309 var finally_block = (Block) null;
310 if (sym.parent_node is TryStatement) {
311 finally_block = (sym.parent_node as TryStatement).finally_body;
312 } else if (sym.parent_node is CatchClause) {
313 finally_block = (sym.parent_node.parent_node as TryStatement).finally_body;
316 if (finally_block != null) {
317 cfrag.append (finally_block.ccodenode);
320 base.append_local_free (sym, cfrag, stop_at_loop);
324 // vim:sw=8 noet