Line data Source code
1 : /* valagerrormodule.vala
2 : *
3 : * Copyright (C) 2008-2010 Jürg Billeter
4 : *
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.
9 :
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.
14 :
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
18 : *
19 : * Author:
20 : * Jürg Billeter <j@bitron.ch>
21 : * Thijs Vermeir <thijsvermeir@gmail.com>
22 : */
23 :
24 : using GLib;
25 :
26 4371 : public class Vala.GErrorModule : CCodeDelegateModule {
27 1457 : private bool is_in_catch = false;
28 :
29 1459 : public override void generate_error_domain_declaration (ErrorDomain edomain, CCodeFile decl_space) {
30 1374 : if (add_symbol_declaration (decl_space, edomain, get_ccode_name (edomain))) {
31 : return;
32 : }
33 :
34 88 : generate_type_declaration (gquark_type, decl_space);
35 :
36 88 : var cenum = new CCodeEnum (get_ccode_name (edomain));
37 :
38 354 : foreach (ErrorCode ecode in edomain.get_codes ()) {
39 133 : if (ecode.value == null) {
40 123 : cenum.add_value (new CCodeEnumValue (get_ccode_name (ecode)));
41 : } else {
42 10 : ecode.value.emit (this);
43 10 : cenum.add_value (new CCodeEnumValue (get_ccode_name (ecode), get_cvalue (ecode.value)));
44 : }
45 : }
46 :
47 88 : decl_space.add_type_definition (cenum);
48 :
49 88 : string quark_fun_name = get_ccode_lower_case_prefix (edomain) + "quark";
50 :
51 88 : var error_domain_define = new CCodeMacroReplacement (get_ccode_upper_case_name (edomain), quark_fun_name + " ()");
52 88 : decl_space.add_type_definition (error_domain_define);
53 :
54 88 : var cquark_fun = new CCodeFunction (quark_fun_name, get_ccode_name (gquark_type.type_symbol));
55 88 : cquark_fun.modifiers |= CCodeModifiers.EXTERN;
56 88 : requires_vala_extern = true;
57 :
58 88 : decl_space.add_function_declaration (cquark_fun);
59 88 : decl_space.add_type_definition (new CCodeNewline ());
60 :
61 88 : if (!get_ccode_has_type_id (edomain)) {
62 3 : return;
63 : }
64 :
65 85 : decl_space.add_include ("glib-object.h");
66 85 : decl_space.add_type_declaration (new CCodeNewline ());
67 :
68 85 : var fun_name = get_ccode_type_function (edomain);
69 :
70 85 : var macro = "(%s ())".printf (fun_name);
71 85 : decl_space.add_type_declaration (new CCodeMacroReplacement (get_ccode_type_id (edomain), macro));
72 :
73 85 : var regfun = new CCodeFunction (fun_name, "GType");
74 85 : regfun.modifiers = CCodeModifiers.CONST;
75 :
76 85 : if (edomain.is_private_symbol ()) {
77 : // avoid C warning as this function is not always used
78 0 : regfun.modifiers |= CCodeModifiers.STATIC | CCodeModifiers.UNUSED;
79 85 : } else if (context.hide_internal && edomain.is_internal_symbol ()) {
80 2 : regfun.modifiers |= CCodeModifiers.INTERNAL;
81 : } else {
82 83 : regfun.modifiers |= CCodeModifiers.EXTERN;
83 83 : requires_vala_extern = true;
84 : }
85 :
86 85 : decl_space.add_function_declaration (regfun);
87 : }
88 :
89 78 : public override void visit_error_domain (ErrorDomain edomain) {
90 39 : if (edomain.comment != null) {
91 0 : cfile.add_type_definition (new CCodeComment (edomain.comment.content));
92 : }
93 :
94 39 : generate_error_domain_declaration (edomain, cfile);
95 :
96 39 : if (!edomain.is_internal_symbol ()) {
97 8 : generate_error_domain_declaration (edomain, header_file);
98 : }
99 39 : if (!edomain.is_private_symbol ()) {
100 39 : generate_error_domain_declaration (edomain, internal_header_file);
101 : }
102 :
103 39 : edomain.accept_children (this);
104 :
105 39 : string quark_fun_name = get_ccode_lower_case_prefix (edomain) + "quark";
106 :
107 39 : var cquark_fun = new CCodeFunction (quark_fun_name, get_ccode_name (gquark_type.type_symbol));
108 39 : push_function (cquark_fun);
109 :
110 39 : var cquark_call = new CCodeFunctionCall (new CCodeIdentifier ("g_quark_from_static_string"));
111 39 : cquark_call.add_argument (new CCodeConstant ("\"" + get_ccode_quark_name (edomain) + "\""));
112 :
113 39 : ccode.add_return (cquark_call);
114 :
115 39 : pop_function ();
116 39 : cfile.add_function (cquark_fun);
117 : }
118 :
119 62 : public override void visit_throw_statement (ThrowStatement stmt) {
120 : // method will fail
121 62 : current_method_inner_error = true;
122 62 : ccode.add_assignment (get_inner_error_cexpression (), get_cvalue (stmt.error_expression));
123 :
124 62 : add_simple_check (stmt, true);
125 : }
126 :
127 647 : public virtual void return_with_exception (CCodeExpression error_expr) {
128 210 : var cpropagate = new CCodeFunctionCall (new CCodeIdentifier ("g_propagate_error"));
129 210 : cpropagate.add_argument (new CCodeIdentifier ("error"));
130 210 : cpropagate.add_argument (error_expr);
131 :
132 210 : ccode.add_expression (cpropagate);
133 :
134 : // free local variables
135 210 : append_local_free (current_symbol);
136 :
137 : // free possibly already assigned out-parameter
138 210 : append_out_param_free (current_method);
139 :
140 216 : if (current_method is CreationMethod && current_method.parent_symbol is Class) {
141 1037 : var cl = (Class) current_method.parent_symbol;
142 6 : ccode.add_expression (destroy_value (new GLibValue (new ObjectType (cl), new CCodeIdentifier ("self"), true)));
143 6 : ccode.add_return (new CCodeConstant ("NULL"));
144 204 : } else if (is_in_coroutine ()) {
145 0 : ccode.add_return (new CCodeConstant ("FALSE"));
146 : } else {
147 204 : return_default_value (current_return_type, true);
148 : }
149 : }
150 :
151 1184 : void uncaught_error_statement (CCodeExpression inner_error, bool unexpected = false, CodeNode? start_at = null) {
152 : // free local variables
153 592 : if (start_at is TryStatement) {
154 104 : append_local_free (start_at.parent_node as Block);
155 : } else {
156 540 : append_local_free (current_symbol);
157 : }
158 :
159 : // free possibly already assigned out-parameter
160 592 : append_out_param_free (current_method);
161 :
162 592 : cfile.add_include ("glib.h");
163 :
164 592 : var ccritical = new CCodeFunctionCall (new CCodeIdentifier ("g_critical"));
165 592 : ccritical.add_argument (new CCodeConstant (unexpected ? "\"file %s: line %d: unexpected error: %s (%s, %d)\"" : "\"file %s: line %d: uncaught error: %s (%s, %d)\""));
166 592 : ccritical.add_argument (new CCodeConstant ("__FILE__"));
167 592 : ccritical.add_argument (new CCodeConstant ("__LINE__"));
168 592 : ccritical.add_argument (new CCodeMemberAccess.pointer (inner_error, "message"));
169 592 : var domain_name = new CCodeFunctionCall (new CCodeIdentifier ("g_quark_to_string"));
170 592 : domain_name.add_argument (new CCodeMemberAccess.pointer (inner_error, "domain"));
171 592 : ccritical.add_argument (domain_name);
172 592 : ccritical.add_argument (new CCodeMemberAccess.pointer (inner_error, "code"));
173 :
174 592 : var cclear = new CCodeFunctionCall (new CCodeIdentifier ("g_clear_error"));
175 592 : cclear.add_argument (new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, inner_error));
176 :
177 : // print critical message
178 592 : ccode.add_expression (ccritical);
179 592 : ccode.add_expression (cclear);
180 :
181 637 : if (is_in_coroutine ()) {
182 45 : var unref = new CCodeFunctionCall (new CCodeIdentifier ("g_object_unref"));
183 45 : unref.add_argument (get_variable_cexpression ("_async_result"));
184 45 : ccode.add_expression (unref);
185 45 : ccode.add_return (new CCodeConstant ("FALSE"));
186 547 : } else if (is_in_constructor () || is_in_destructor ()) {
187 : // just print critical, do not return prematurely
188 539 : } else if (current_method is CreationMethod) {
189 14 : if (current_method.parent_symbol is Struct) {
190 0 : ccode.add_return ();
191 : } else {
192 14 : ccode.add_return (new CCodeConstant ("NULL"));
193 : }
194 525 : } else if (current_return_type != null) {
195 525 : return_default_value (current_return_type, true);
196 : }
197 : }
198 :
199 59 : bool in_finally_block (CodeNode node) {
200 59 : var current_node = node;
201 500 : while (current_node != null) {
202 390 : var try_stmt = current_node.parent_node as TryStatement;
203 65 : if (try_stmt != null && try_stmt.finally_body == current_node) {
204 7 : return true;
205 : }
206 714 : current_node = current_node.parent_node;
207 : }
208 59 : return false;
209 : }
210 :
211 729 : public override void add_simple_check (CodeNode node, bool always_fails = false) {
212 729 : current_method_inner_error = true;
213 :
214 1386 : if (always_fails) {
215 : // inner_error is always set, avoid unnecessary if statement
216 : // eliminates C warnings
217 : } else {
218 657 : var ccond = new CCodeBinaryExpression (CCodeBinaryOperator.INEQUALITY, get_inner_error_cexpression (), new CCodeConstant ("NULL"));
219 657 : var unlikely = new CCodeFunctionCall (new CCodeIdentifier ("G_UNLIKELY"));
220 657 : unlikely.add_argument (ccond);
221 657 : ccode.open_if (unlikely);
222 : }
223 :
224 890 : if (current_try != null) {
225 : // surrounding try found
226 :
227 : // free local variables
228 161 : if (is_in_catch) {
229 4 : append_local_free (current_symbol, null, current_catch);
230 : } else {
231 157 : append_local_free (current_symbol, null, current_try);
232 : }
233 :
234 161 : var error_types = new ArrayList<DataType> ();
235 161 : node.get_error_types (error_types);
236 :
237 161 : bool has_general_catch_clause = false;
238 :
239 318 : if (!is_in_catch) {
240 157 : var handled_error_types = new ArrayList<DataType> ();
241 295 : foreach (CatchClause clause in current_try.get_catch_clauses ()) {
242 : // keep track of unhandled error types
243 484 : foreach (DataType node_error_type in error_types) {
244 162 : if (clause.error_type == null || node_error_type.compatible (clause.error_type)) {
245 153 : handled_error_types.add (node_error_type);
246 : }
247 : }
248 466 : foreach (DataType handled_error_type in handled_error_types) {
249 153 : error_types.remove (handled_error_type);
250 : }
251 160 : handled_error_types.clear ();
252 :
253 160 : if (clause.error_type.equals (gerror_type)) {
254 : // general catch clause, this should be the last one
255 91 : has_general_catch_clause = true;
256 91 : ccode.add_goto (clause.get_attribute_string ("CCode", "cname"));
257 91 : break;
258 : } else {
259 69 : unowned ErrorType catch_type = (ErrorType) clause.error_type;
260 :
261 138 : if (catch_type.error_code != null) {
262 : /* catch clause specifies a specific error code */
263 8 : var error_match = new CCodeFunctionCall (new CCodeIdentifier ("g_error_matches"));
264 8 : error_match.add_argument (get_inner_error_cexpression ());
265 8 : error_match.add_argument (new CCodeIdentifier (get_ccode_upper_case_name (catch_type.error_domain)));
266 8 : error_match.add_argument (new CCodeIdentifier (get_ccode_name (catch_type.error_code)));
267 :
268 8 : ccode.open_if (error_match);
269 : } else {
270 : /* catch clause specifies a full error domain */
271 61 : var ccond = new CCodeBinaryExpression (CCodeBinaryOperator.EQUALITY,
272 : new CCodeMemberAccess.pointer (get_inner_error_cexpression (), "domain"), new CCodeIdentifier
273 : (get_ccode_upper_case_name (catch_type.error_domain)));
274 :
275 61 : ccode.open_if (ccond);
276 : }
277 :
278 : // go to catch clause if error domain matches
279 69 : ccode.add_goto (clause.get_attribute_string ("CCode", "cname"));
280 69 : ccode.close ();
281 : }
282 : }
283 : }
284 :
285 157 : if (has_general_catch_clause) {
286 : // every possible error is already caught
287 : // as there is a general catch clause
288 : // no need to do anything else
289 70 : } else if (error_types.size > 0) {
290 : // go to finally clause if no catch clause matches
291 : // and there are still unhandled error types
292 11 : ccode.add_goto ("__finally%d".printf (current_try_id));
293 59 : } else if (in_finally_block (node)) {
294 : // do not check unexpected errors happening within finally blocks
295 : // as jump out of finally block is not supported
296 : } else {
297 : // should never happen with correct bindings
298 52 : uncaught_error_statement (get_inner_error_cexpression (), true, current_try);
299 : }
300 795 : } else if (current_method != null && current_method.tree_can_fail) {
301 : // current method can fail, propagate error
302 227 : CCodeBinaryExpression ccond = null;
303 :
304 227 : var error_types = new ArrayList<DataType> ();
305 227 : current_method.get_error_types (error_types);
306 627 : foreach (DataType error_type in error_types) {
307 : // If GLib.Error is allowed we propagate everything
308 228 : if (error_type.equals (gerror_type)) {
309 28 : ccond = null;
310 28 : break;
311 : }
312 :
313 : // Check the allowed error domains to propagate
314 200 : var domain_check = new CCodeBinaryExpression (CCodeBinaryOperator.EQUALITY, new CCodeMemberAccess.pointer
315 : (get_inner_error_cexpression (), "domain"), new CCodeIdentifier (get_ccode_upper_case_name (((ErrorType) error_type).error_domain)));
316 200 : if (ccond == null) {
317 200 : ccond = domain_check;
318 : } else {
319 0 : ccond = new CCodeBinaryExpression (CCodeBinaryOperator.OR, ccond, domain_check);
320 : }
321 : }
322 :
323 227 : if (ccond != null) {
324 199 : ccode.open_if (ccond);
325 199 : return_with_exception (get_inner_error_cexpression ());
326 :
327 199 : ccode.add_else ();
328 199 : uncaught_error_statement (get_inner_error_cexpression ());
329 199 : ccode.close ();
330 : } else {
331 28 : return_with_exception (get_inner_error_cexpression ());
332 : }
333 : } else {
334 341 : uncaught_error_statement (get_inner_error_cexpression ());
335 : }
336 :
337 729 : if (!always_fails) {
338 657 : ccode.close ();
339 : }
340 : }
341 :
342 328 : public override void visit_try_statement (TryStatement stmt) {
343 164 : int this_try_id = next_try_id++;
344 :
345 164 : var old_try = current_try;
346 164 : var old_try_id = current_try_id;
347 164 : var old_is_in_catch = is_in_catch;
348 164 : var old_catch = current_catch;
349 164 : current_try = stmt;
350 164 : current_try_id = this_try_id;
351 164 : is_in_catch = true;
352 :
353 440 : foreach (CatchClause clause in stmt.get_catch_clauses ()) {
354 138 : clause.set_attribute_string ("CCode", "cname", "__catch%d_%s".printf (this_try_id, get_ccode_lower_case_name (clause.error_type)));
355 : }
356 :
357 164 : is_in_catch = false;
358 164 : stmt.body.emit (this);
359 164 : is_in_catch = true;
360 :
361 440 : foreach (CatchClause clause in stmt.get_catch_clauses ()) {
362 138 : current_catch = clause;
363 138 : ccode.add_goto ("__finally%d".printf (this_try_id));
364 138 : clause.emit (this);
365 : }
366 :
367 164 : current_try = old_try;
368 164 : current_try_id = old_try_id;
369 164 : is_in_catch = old_is_in_catch;
370 164 : current_catch = old_catch;
371 :
372 164 : ccode.add_label ("__finally%d".printf (this_try_id));
373 164 : if (stmt.finally_body != null) {
374 : // use a dedicated inner_error variable, if there
375 : // is some error handling happening in finally-block
376 42 : current_inner_error_id++;
377 42 : stmt.finally_body.emit (this);
378 42 : current_inner_error_id--;
379 : }
380 :
381 : // check for errors not handled by this try statement
382 : // may be handled by outer try statements or propagated
383 164 : add_simple_check (stmt, !stmt.after_try_block_reachable);
384 : }
385 :
386 276 : public override void visit_catch_clause (CatchClause clause) {
387 138 : current_method_inner_error = true;
388 :
389 138 : var error_type = (ErrorType) clause.error_type;
390 138 : if (error_type.error_domain != null) {
391 58 : generate_error_domain_declaration (error_type.error_domain, cfile);
392 : }
393 :
394 138 : ccode.add_label (clause.get_attribute_string ("CCode", "cname"));
395 :
396 138 : ccode.open_block ();
397 :
398 233 : if (clause.error_variable != null && clause.error_variable.used) {
399 43 : visit_local_variable (clause.error_variable);
400 43 : ccode.add_assignment (get_variable_cexpression (get_local_cname (clause.error_variable)), get_inner_error_cexpression ());
401 43 : ccode.add_assignment (get_inner_error_cexpression (), new CCodeConstant ("NULL"));
402 : } else {
403 95 : if (clause.error_variable != null) {
404 44 : clause.error_variable.unreachable = true;
405 : }
406 : // error object is not used within catch statement, clear it
407 95 : cfile.add_include ("glib.h");
408 95 : var cclear = new CCodeFunctionCall (new CCodeIdentifier ("g_clear_error"));
409 95 : cclear.add_argument (new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, get_inner_error_cexpression ()));
410 95 : ccode.add_expression (cclear);
411 : }
412 :
413 138 : clause.body.emit (this);
414 :
415 138 : ccode.close ();
416 : }
417 :
418 8083 : protected override void append_scope_free (Symbol sym, CodeNode? stop_at = null) {
419 8083 : base.append_scope_free (sym, stop_at);
420 :
421 8100 : if (!(stop_at is TryStatement || stop_at is CatchClause)) {
422 7894 : var finally_block = (Block) null;
423 7894 : if (sym.parent_node is TryStatement) {
424 31 : finally_block = ((TryStatement) sym.parent_node).finally_body;
425 7863 : } else if (sym.parent_node is CatchClause) {
426 27 : finally_block = ((TryStatement) sym.parent_node.parent_node).finally_body;
427 : }
428 :
429 7911 : if (finally_block != null && finally_block != sym) {
430 4 : finally_block.emit (this);
431 : }
432 : }
433 : }
434 : }
435 :
436 : // vim:sw=8 noet
|