codegen: Add get_field_cvalue and load_field
[vala-lang.git] / codegen / valadovaerrormodule.vala
blobc0df425131edaf271f584f9dbee67cc25be18ead
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 ccode.add_assignment (new CCodeIdentifier ("dova_error"), get_cvalue (stmt.error_expression));
32 add_simple_check (stmt, true);
35 public void return_with_exception () {
36 // propagate error
37 // nothing to do
39 // free local variables
40 append_local_free (current_symbol, false);
42 if (current_method is CreationMethod && current_method.parent_symbol is Class) {
43 var cl = current_method.parent_symbol as Class;
44 var unref_call = new CCodeFunctionCall (new CCodeIdentifier (cl.get_unref_function ()));
45 unref_call.add_argument (new CCodeIdentifier ("this"));
46 ccode.add_expression (unref_call);
47 ccode.add_return ();
48 } else if (current_return_type is VoidType) {
49 ccode.add_return ();
50 } else {
51 ccode.add_return (default_value_for_type (current_return_type, false));
55 void uncaught_error_statement () {
56 // free local variables
57 append_local_free (current_symbol, false);
59 // TODO log uncaught error as critical warning
61 if (current_method is CreationMethod) {
62 ccode.add_return ();
63 } else if (current_return_type is VoidType) {
64 ccode.add_return ();
65 } else if (current_return_type != null) {
66 ccode.add_return (default_value_for_type (current_return_type, false));
70 bool in_finally_block (CodeNode node) {
71 var current_node = node;
72 while (current_node != null) {
73 var try_stmt = current_node.parent_node as TryStatement;
74 if (try_stmt != null && try_stmt.finally_body == current_node) {
75 return true;
77 current_node = current_node.parent_node;
79 return false;
82 public override void add_simple_check (CodeNode node, bool always_fails = false) {
83 if (always_fails) {
84 // inner_error is always set, avoid unnecessary if statement
85 // eliminates C warnings
86 } else {
87 var ccond = new CCodeBinaryExpression (CCodeBinaryOperator.INEQUALITY, new CCodeIdentifier ("dova_error"), new CCodeConstant ("NULL"));
88 ccode.open_if (ccond);
91 if (current_try != null) {
92 // surrounding try found
94 // free local variables
95 append_local_free (current_symbol, false, current_try);
97 var error_types = new ArrayList<DataType> ();
98 foreach (DataType node_error_type in node.get_error_types ()) {
99 error_types.add (node_error_type);
102 bool has_general_catch_clause = false;
104 if (!is_in_catch) {
105 var handled_error_types = new ArrayList<DataType> ();
106 foreach (CatchClause clause in current_try.get_catch_clauses ()) {
107 // keep track of unhandled error types
108 foreach (DataType node_error_type in error_types) {
109 if (clause.error_type == null || node_error_type.compatible (clause.error_type)) {
110 handled_error_types.add (node_error_type);
113 foreach (DataType handled_error_type in handled_error_types) {
114 error_types.remove (handled_error_type);
116 handled_error_types.clear ();
118 if (clause.error_type.equals (new ObjectType (error_class))) {
119 // general catch clause, this should be the last one
120 has_general_catch_clause = true;
121 ccode.add_goto (clause.clabel_name);
122 break;
123 } else {
124 var catch_type = clause.error_type as ObjectType;
126 var type_check = new CCodeFunctionCall (new CCodeIdentifier ("any_is_a"));
127 type_check.add_argument (new CCodeIdentifier ("dova_error"));
128 type_check.add_argument (new CCodeFunctionCall (new CCodeIdentifier ("%s_type_get".printf (catch_type.type_symbol.get_lower_case_cname ()))));
130 ccode.open_if (type_check);
132 // go to catch clause if error domain matches
133 ccode.add_goto (clause.clabel_name);
134 ccode.close ();
139 if (has_general_catch_clause) {
140 // every possible error is already caught
141 // as there is a general catch clause
142 // no need to do anything else
143 } else if (error_types.size > 0) {
144 // go to finally clause if no catch clause matches
145 // and there are still unhandled error types
146 ccode.add_goto ("__finally%d".printf (current_try_id));
147 } else if (in_finally_block (node)) {
148 // do not check unexpected errors happening within finally blocks
149 // as jump out of finally block is not supported
150 } else {
151 assert_not_reached ();
153 } else if (current_method != null && current_method.get_error_types ().size > 0) {
154 // current method can fail, propagate error
155 CCodeExpression ccond = null;
157 foreach (DataType error_type in current_method.get_error_types ()) {
158 // If Dova.Error is allowed we propagate everything
159 if (error_type.equals (new ObjectType (error_class))) {
160 ccond = null;
161 break;
164 // Check the allowed error domains to propagate
165 var type_check = new CCodeFunctionCall (new CCodeIdentifier ("any_is_a"));
166 type_check.add_argument (new CCodeIdentifier ("dova_error"));
167 type_check.add_argument (new CCodeFunctionCall (new CCodeIdentifier ("%s_type_get".printf (error_class.get_lower_case_cname ()))));
168 if (ccond == null) {
169 ccond = type_check;
170 } else {
171 ccond = new CCodeBinaryExpression (CCodeBinaryOperator.OR, ccond, type_check);
175 if (ccond == null) {
176 return_with_exception ();
177 } else {
178 ccode.open_if (ccond);
179 return_with_exception ();
180 ccode.add_else ();
181 uncaught_error_statement ();
182 ccode.close ();
184 } else {
185 uncaught_error_statement ();
188 if (!always_fails) {
189 ccode.close ();
193 public override void visit_try_statement (TryStatement stmt) {
194 int this_try_id = next_try_id++;
196 var old_try = current_try;
197 var old_try_id = current_try_id;
198 var old_is_in_catch = is_in_catch;
199 current_try = stmt;
200 current_try_id = this_try_id;
201 is_in_catch = true;
203 foreach (CatchClause clause in stmt.get_catch_clauses ()) {
204 clause.clabel_name = "__catch%d_%s".printf (this_try_id, clause.error_type.get_lower_case_cname ());
207 is_in_catch = false;
208 stmt.body.emit (this);
209 is_in_catch = true;
211 foreach (CatchClause clause in stmt.get_catch_clauses ()) {
212 ccode.add_goto ("__finally%d".printf (this_try_id));
213 clause.emit (this);
216 current_try = old_try;
217 current_try_id = old_try_id;
218 is_in_catch = old_is_in_catch;
220 ccode.add_label ("__finally%d".printf (this_try_id));
221 if (stmt.finally_body != null) {
222 stmt.finally_body.emit (this);
225 // check for errors not handled by this try statement
226 // may be handled by outer try statements or propagated
227 add_simple_check (stmt, !stmt.after_try_block_reachable);
230 public override void visit_catch_clause (CatchClause clause) {
231 generate_type_declaration (clause.error_type, cfile);
233 ccode.add_label (clause.clabel_name);
235 ccode.open_block ();
237 string variable_name;
238 if (clause.variable_name != null) {
239 variable_name = get_variable_cname (clause.variable_name);
240 } else {
241 variable_name = "__err";
244 if (clause.variable_name != null) {
245 var cdecl = new CCodeDeclaration (clause.error_type.get_cname ());
246 cdecl.add_declarator (new CCodeVariableDeclarator (variable_name, new CCodeIdentifier ("dova_error")));
247 ccode.add_statement (cdecl);
248 } else {
249 // error object is not used within catch statement, clear it
250 var cclear = new CCodeFunctionCall (new CCodeIdentifier ("dova_object_unref"));
251 cclear.add_argument (new CCodeIdentifier ("dova_error"));
252 ccode.add_statement (new CCodeExpressionStatement (cclear));
254 ccode.add_statement (new CCodeExpressionStatement (new CCodeAssignment (new CCodeIdentifier ("dova_error"), new CCodeConstant ("NULL"))));
256 clause.body.emit (this);
258 ccode.close ();
261 public override void append_local_free (Symbol sym, bool stop_at_loop = false, CodeNode? stop_at = null) {
262 var finally_block = (Block) null;
263 if (sym.parent_node is TryStatement) {
264 finally_block = (sym.parent_node as TryStatement).finally_body;
265 } else if (sym.parent_node is CatchClause) {
266 finally_block = (sym.parent_node.parent_node as TryStatement).finally_body;
269 if (finally_block != null) {
270 finally_block.emit (this);
273 base.append_local_free (sym, stop_at_loop, stop_at);
277 // vim:sw=8 noet