9ecbced2f85826495994883a12a40b0786132a60
[roobuilder] / src / codegen / valaccodecontrolflowmodule.vala
1 /* valaccodecontrolflowmodule.vala
2  *
3  * Copyright (C) 2006-2011  Jürg Billeter
4  * Copyright (C) 2006-2008  Raffaele Sandrini
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301  USA
19  *
20  * Author:
21  *      Jürg Billeter <j@bitron.ch>
22  *      Raffaele Sandrini <raffaele@sandrini.ch>
23  */
24
25 using GLib;
26
27 public abstract class Vala.CCodeControlFlowModule : CCodeMethodModule {
28         public override void visit_if_statement (IfStatement stmt) {
29                 ccode.open_if (get_cvalue (stmt.condition));
30
31                 stmt.true_statement.emit (this);
32
33                 if (stmt.false_statement != null) {
34                         ccode.add_else ();
35                         stmt.false_statement.emit (this);
36                 }
37
38                 ccode.close ();
39         }
40
41         void visit_string_switch_statement (SwitchStatement stmt) {
42                 // we need a temporary variable to save the property value
43                 var temp_value = create_temp_value (stmt.expression.value_type, false, stmt);
44                 var ctemp = get_cvalue_ (temp_value);
45
46                 var cinit = new CCodeAssignment (ctemp, get_cvalue (stmt.expression));
47                 var czero = new CCodeConstant ("0");
48
49                 var free_call = new CCodeFunctionCall (new CCodeIdentifier ("g_free"));
50                 free_call.add_argument (ctemp);
51
52                 var cisnull = new CCodeBinaryExpression (CCodeBinaryOperator.EQUALITY, new CCodeConstant ("NULL"), ctemp);
53                 var cquark = new CCodeFunctionCall (new CCodeIdentifier ("g_quark_from_string"));
54                 cquark.add_argument (ctemp);
55
56                 var ccond = new CCodeConditionalExpression (cisnull, new CCodeConstant ("0"), cquark);
57
58                 int label_temp_id = next_temp_var_id++;
59
60                 temp_value = create_temp_value (gquark_type, true, stmt);
61
62                 int label_count = 0;
63
64                 foreach (SwitchSection section in stmt.get_sections ()) {
65                         if (section.has_default_label ()) {
66                                 continue;
67                         }
68
69                         foreach (SwitchLabel label in section.get_labels ()) {
70                                 label.expression.emit (this);
71                                 var cexpr = get_cvalue (label.expression);
72
73                                 if (is_constant_ccode_expression (cexpr)) {
74                                         var cname = "_tmp%d_label%d".printf (label_temp_id, label_count++);
75
76                                         ccode.add_declaration (get_ccode_name (gquark_type), new CCodeVariableDeclarator (cname, czero), CCodeModifiers.STATIC);
77                                 }
78                         }
79                 }
80
81                 ccode.add_expression (cinit);
82
83                 ctemp = get_cvalue_ (temp_value);
84                 cinit = new CCodeAssignment (ctemp, ccond);
85
86                 ccode.add_expression (cinit);
87
88                 if (stmt.expression.value_type.value_owned) {
89                         // free owned string
90                         ccode.add_expression (free_call);
91                 }
92
93                 SwitchSection default_section = null;
94                 label_count = 0;
95
96                 int n = 0;
97
98                 foreach (SwitchSection section in stmt.get_sections ()) {
99                         if (section.has_default_label ()) {
100                                 default_section = section;
101                                 continue;
102                         }
103
104                         CCodeBinaryExpression cor = null;
105                         foreach (SwitchLabel label in section.get_labels ()) {
106                                 label.expression.emit (this);
107                                 var cexpr = get_cvalue (label.expression);
108
109                                 if (is_constant_ccode_expression (cexpr)) {
110                                         var cname = new CCodeIdentifier ("_tmp%d_label%d".printf (label_temp_id, label_count++));
111                                         var ccondition = new CCodeBinaryExpression (CCodeBinaryOperator.INEQUALITY, czero, cname);
112                                         var ccall = new CCodeFunctionCall (new CCodeIdentifier ("g_quark_from_static_string"));
113                                         cinit = new CCodeAssignment (cname, ccall);
114
115                                         ccall.add_argument (cexpr);
116
117                                         cexpr = new CCodeConditionalExpression (ccondition, cname, cinit);
118                                 } else {
119                                         var ccall = new CCodeFunctionCall (new CCodeIdentifier ("g_quark_from_string"));
120                                         ccall.add_argument (cexpr);
121                                         cexpr = ccall;
122                                 }
123
124                                 var ccmp = new CCodeBinaryExpression (CCodeBinaryOperator.EQUALITY, ctemp, cexpr);
125
126                                 if (cor == null) {
127                                         cor = ccmp;
128                                 } else {
129                                         cor = new CCodeBinaryExpression (CCodeBinaryOperator.OR, cor, ccmp);
130                                 }
131                         }
132
133                         if (n > 0) {
134                                 ccode.else_if (cor);
135                         } else {
136                                 ccode.open_if (cor);
137                         }
138
139                         ccode.open_switch (new CCodeConstant ("0"));
140                         ccode.add_default ();
141
142                         section.emit (this);
143
144                         ccode.close ();
145
146                         n++;
147                 }
148
149                 if (default_section != null) {
150                         if (n > 0) {
151                                 ccode.add_else ();
152                         }
153
154                         ccode.open_switch (new CCodeConstant ("0"));
155                         ccode.add_default ();
156
157                         default_section.emit (this);
158
159                         ccode.close ();
160                 }
161
162                 if (n > 0) {
163                         ccode.close ();
164                 }
165         }
166
167         public override void visit_switch_statement (SwitchStatement stmt) {
168                 if (stmt.expression.value_type.compatible (string_type)) {
169                         visit_string_switch_statement (stmt);
170                         return;
171                 }
172
173                 ccode.open_switch (get_cvalue (stmt.expression));
174
175                 bool has_default = false;
176
177                 foreach (SwitchSection section in stmt.get_sections ()) {
178                         if (section.has_default_label ()) {
179                                 ccode.add_default ();
180                                 has_default = true;
181                         }
182                         section.emit (this);
183                 }
184
185                 if (!has_default) {
186                         // silence C compiler
187                         ccode.add_default ();
188                         ccode.add_break ();
189                 }
190
191                 ccode.close ();
192         }
193
194         public override void visit_switch_label (SwitchLabel label) {
195                 if (((SwitchStatement) label.section.parent_node).expression.value_type.compatible (string_type)) {
196                         return;
197                 }
198
199                 if (label.expression != null) {
200                         label.expression.emit (this);
201
202                         visit_end_full_expression (label.expression);
203
204                         ccode.add_case (get_cvalue (label.expression));
205                 }
206         }
207
208         public override void visit_loop_statement (LoopStatement stmt) {
209                 if (context.profile == Profile.GOBJECT) {
210                         ccode.open_while (new CCodeConstant ("TRUE"));
211                 } else {
212                         cfile.add_include ("stdbool.h");
213                         ccode.open_while (new CCodeConstant ("true"));
214                 }
215
216                 stmt.body.emit (this);
217
218                 ccode.close ();
219         }
220
221         public override void visit_foreach_statement (ForeachStatement stmt) {
222                 ccode.open_block ();
223
224                 var collection_backup = stmt.collection_variable;
225                 var collection_type = collection_backup.variable_type;
226
227                 var array_type = collection_type as ArrayType;
228                 if (array_type != null) {
229                         // avoid assignment issues
230                         array_type.inline_allocated = false;
231                         array_type.fixed_length = false;
232                 }
233
234                 visit_local_variable (collection_backup);
235                 ccode.add_assignment (get_variable_cexpression (get_local_cname (collection_backup)), get_cvalue (stmt.collection));
236
237                 if (stmt.tree_can_fail && stmt.collection.tree_can_fail) {
238                         // exception handling
239                         add_simple_check (stmt.collection);
240                 }
241
242                 if (stmt.collection.value_type is ArrayType) {
243                         array_type = (ArrayType) stmt.collection.value_type;
244
245                         var array_len = get_variable_cexpression (get_array_length_cname (get_local_cname (collection_backup), 1));
246
247                         // store array length for use by _vala_array_free
248                         ccode.add_assignment (array_len, get_array_length_cexpression (stmt.collection));
249
250                         var iterator_variable = new LocalVariable (array_type.length_type.copy (), stmt.variable_name + "_it");
251                         visit_local_variable (iterator_variable);
252                         var it_name = get_local_cname (iterator_variable);
253
254                         var ccond = new CCodeBinaryExpression (CCodeBinaryOperator.LESS_THAN, get_variable_cexpression (it_name), array_len);
255
256                         ccode.open_for (new CCodeAssignment (get_variable_cexpression (it_name), new CCodeConstant ("0")),
257                                            ccond,
258                                            new CCodeAssignment (get_variable_cexpression (it_name), new CCodeBinaryExpression (CCodeBinaryOperator.PLUS, get_variable_cexpression (it_name), new CCodeConstant ("1"))));
259
260                         CCodeExpression element_expr = new CCodeElementAccess (get_variable_cexpression (get_local_cname (collection_backup)), get_variable_cexpression (it_name));
261
262                         var element_type = array_type.element_type.copy ();
263                         element_type.value_owned = false;
264                         element_expr = get_cvalue_ (transform_value (new GLibValue (element_type, element_expr, true), stmt.type_reference, stmt));
265
266                         visit_local_variable (stmt.element_variable);
267                         ccode.add_assignment (get_variable_cexpression (get_local_cname (stmt.element_variable)), element_expr);
268
269                         // set array length for stacked arrays
270                         if (stmt.type_reference is ArrayType) {
271                                 var inner_array_type = (ArrayType) stmt.type_reference;
272                                 for (int dim = 1; dim <= inner_array_type.rank; dim++) {
273                                         ccode.add_assignment (get_variable_cexpression (get_array_length_cname (get_local_cname (stmt.element_variable), dim)), new CCodeConstant ("-1"));
274                                 }
275                         }
276
277                         stmt.body.emit (this);
278
279                         ccode.close ();
280                 } else if (stmt.collection.value_type.compatible (new ObjectType (garray_type))) {
281                         // iterating over a GArray
282
283                         var iterator_variable = new LocalVariable (uint_type.copy (), "%s_index".printf (stmt.variable_name));
284                         visit_local_variable (iterator_variable);
285                         var arr_index = get_variable_cname (get_local_cname (iterator_variable));
286
287                         var ccond = new CCodeBinaryExpression (CCodeBinaryOperator.LESS_THAN, get_variable_cexpression (arr_index), new CCodeMemberAccess.pointer (get_variable_cexpression (get_local_cname (collection_backup)), "len"));
288
289                         ccode.open_for (new CCodeAssignment (get_variable_cexpression (arr_index), new CCodeConstant ("0")),
290                                            ccond,
291                                            new CCodeAssignment (get_variable_cexpression (arr_index), new CCodeBinaryExpression (CCodeBinaryOperator.PLUS, get_variable_cexpression (arr_index), new CCodeConstant ("1"))));
292
293                         var get_item = new CCodeFunctionCall (new CCodeIdentifier ("g_array_index"));
294                         get_item.add_argument (get_variable_cexpression (get_local_cname (collection_backup)));
295                         get_item.add_argument (new CCodeIdentifier (get_ccode_name (stmt.type_reference)));
296                         get_item.add_argument (get_variable_cexpression (arr_index));
297
298                         if (collection_type.get_type_arguments ().size != 1) {
299                                 Report.error (stmt.source_reference, "internal error: missing generic type argument");
300                                 stmt.error = true;
301                                 return;
302                         }
303
304                         var element_type = collection_type.get_type_arguments ().get (0).copy ();
305                         element_type.value_owned = false;
306                         var element_expr = get_cvalue_ (transform_value (new GLibValue (element_type, get_item, true), stmt.type_reference, stmt));
307
308                         visit_local_variable (stmt.element_variable);
309                         ccode.add_assignment (get_variable_cexpression (get_local_cname (stmt.element_variable)), element_expr);
310
311                         stmt.body.emit (this);
312
313                         ccode.close ();
314                 } else if (stmt.collection.value_type.compatible (new ObjectType (glist_type)) || stmt.collection.value_type.compatible (new ObjectType (gslist_type))) {
315                         // iterating over a GList or GSList
316
317                         var iterator_variable = new LocalVariable (collection_type.copy (), stmt.variable_name + "_it");
318                         visit_local_variable (iterator_variable);
319                         var it_name = get_local_cname (iterator_variable);
320
321                         var ccond = new CCodeBinaryExpression (CCodeBinaryOperator.INEQUALITY, get_variable_cexpression (it_name), new CCodeConstant ("NULL"));
322
323                         ccode.open_for (new CCodeAssignment (get_variable_cexpression (it_name), get_variable_cexpression (get_local_cname (collection_backup))),
324                                                         ccond,
325                                                         new CCodeAssignment (get_variable_cexpression (it_name), new CCodeMemberAccess.pointer (get_variable_cexpression (it_name), "next")));
326
327                         CCodeExpression element_expr = new CCodeMemberAccess.pointer (get_variable_cexpression (it_name), "data");
328
329                         if (collection_type.get_type_arguments ().size != 1) {
330                                 Report.error (stmt.source_reference, "internal error: missing generic type argument");
331                                 stmt.error = true;
332                                 return;
333                         }
334
335                         var element_data_type = collection_type.get_type_arguments ().get (0).copy ();
336                         element_data_type.value_owned = false;
337                         element_expr = convert_from_generic_pointer (element_expr, element_data_type);
338                         element_expr = get_cvalue_ (transform_value (new GLibValue (element_data_type, element_expr), stmt.type_reference, stmt));
339
340                         visit_local_variable (stmt.element_variable);
341                         ccode.add_assignment (get_variable_cexpression (get_local_cname (stmt.element_variable)), element_expr);
342
343                         stmt.body.emit (this);
344
345                         ccode.close ();
346                 } else if (stmt.collection.value_type.compatible (new ObjectType ((Class) genericarray_type))) {
347                         // iterating over a GenericArray / GPtrArray
348
349                         var iterator_variable = new LocalVariable (uint_type.copy (), "%s_index".printf (stmt.variable_name));
350                         visit_local_variable (iterator_variable);
351                         var arr_index = get_variable_cname (get_local_cname (iterator_variable));
352
353                         var ccond = new CCodeBinaryExpression (CCodeBinaryOperator.LESS_THAN, get_variable_cexpression (arr_index), new CCodeMemberAccess.pointer (get_variable_cexpression (get_local_cname (collection_backup)), "len"));
354
355                         ccode.open_for (new CCodeAssignment (get_variable_cexpression (arr_index), new CCodeConstant ("0")),
356                                            ccond,
357                                            new CCodeAssignment (get_variable_cexpression (arr_index), new CCodeBinaryExpression (CCodeBinaryOperator.PLUS, get_variable_cexpression (arr_index), new CCodeConstant ("1"))));
358
359                         var get_item = new CCodeFunctionCall (new CCodeIdentifier ("g_ptr_array_index"));
360                         get_item.add_argument (get_variable_cexpression (get_local_cname (collection_backup)));
361                         get_item.add_argument (get_variable_cexpression (arr_index));
362
363                         CCodeExpression element_expr = get_item;
364
365                         if (stmt.type_reference.value_owned) {
366                                 element_expr = get_cvalue_ (copy_value (new GLibValue (stmt.type_reference, element_expr), stmt.element_variable));
367                         }
368
369                         visit_local_variable (stmt.element_variable);
370                         ccode.add_assignment (get_variable_cexpression (get_local_cname (stmt.element_variable)), element_expr);
371
372                         stmt.body.emit (this);
373
374                         ccode.close ();
375                 } else if (stmt.collection.value_type.compatible (new ObjectType (gvaluearray_type))) {
376                         // iterating over a GValueArray
377
378                         var iterator_variable = new LocalVariable (uint_type.copy (), "%s_index".printf (stmt.variable_name));
379                         visit_local_variable (iterator_variable);
380                         var arr_index = get_variable_cname (get_local_cname (iterator_variable));
381
382                         var ccond = new CCodeBinaryExpression (CCodeBinaryOperator.LESS_THAN, get_variable_cexpression (arr_index), new CCodeMemberAccess.pointer (get_variable_cexpression (get_local_cname (collection_backup)), "n_values"));
383
384                         ccode.open_for (new CCodeAssignment (get_variable_cexpression (arr_index), new CCodeConstant ("0")),
385                                            ccond,
386                                            new CCodeAssignment (get_variable_cexpression (arr_index), new CCodeBinaryExpression (CCodeBinaryOperator.PLUS, get_variable_cexpression (arr_index), new CCodeConstant ("1"))));
387
388                         var get_item = new CCodeFunctionCall (new CCodeIdentifier ("g_value_array_get_nth"));
389                         get_item.add_argument (get_variable_cexpression (get_local_cname (collection_backup)));
390                         get_item.add_argument (get_variable_cexpression (arr_index));
391
392                         CCodeExpression element_expr = new CCodeUnaryExpression (CCodeUnaryOperator.POINTER_INDIRECTION, get_item);
393
394                         if (stmt.type_reference.value_owned) {
395                                 element_expr = get_cvalue_ (copy_value (new GLibValue (stmt.type_reference, element_expr), new StructValueType (gvalue_type)));
396                         }
397
398                         visit_local_variable (stmt.element_variable);
399                         ccode.add_assignment (get_variable_cexpression (get_local_cname (stmt.element_variable)), element_expr);
400
401                         stmt.body.emit (this);
402
403                         ccode.close ();
404                 } else if (stmt.collection.value_type.compatible (new ObjectType (gsequence_type))) {
405                         // iterating over a GSequence
406
407                         var iterator_variable = new LocalVariable (new ObjectType (gsequence_iter_type), "%s_iter".printf (stmt.variable_name));
408                         visit_local_variable (iterator_variable);
409                         var sequence_iter = get_variable_cname (get_local_cname (iterator_variable));
410
411                         var ccond_is_end = new CCodeFunctionCall (new CCodeIdentifier ("g_sequence_iter_is_end"));
412                         ccond_is_end.add_argument (get_variable_cexpression (sequence_iter));
413                         var ccond = new CCodeUnaryExpression (CCodeUnaryOperator.LOGICAL_NEGATION, ccond_is_end);
414                         var cbegin = new CCodeFunctionCall (new CCodeIdentifier ("g_sequence_get_begin_iter"));
415                         cbegin.add_argument (get_variable_cexpression (get_local_cname (collection_backup)));
416                         var cnext = new CCodeFunctionCall (new CCodeIdentifier ("g_sequence_iter_next"));
417                         cnext.add_argument (get_variable_cexpression (sequence_iter));
418
419                         ccode.open_for (new CCodeAssignment (get_variable_cexpression (sequence_iter), cbegin),
420                                         ccond,
421                                         new CCodeAssignment (get_variable_cexpression (sequence_iter), cnext));
422
423                         var get_item = new CCodeFunctionCall (new CCodeIdentifier ("g_sequence_get"));
424                         get_item.add_argument (get_variable_cexpression (sequence_iter));
425
426                         CCodeExpression element_expr = get_item;
427
428                         if (collection_type.get_type_arguments ().size != 1) {
429                                 Report.error (stmt.source_reference, "internal error: missing generic type argument");
430                                 stmt.error = true;
431                                 return;
432                         }
433
434                         var element_type = collection_type.get_type_arguments ().get (0).copy ();
435                         element_type.value_owned = false;
436                         element_expr = convert_from_generic_pointer (element_expr, element_type);
437                         element_expr = get_cvalue_ (transform_value (new GLibValue (element_type, element_expr), stmt.type_reference, stmt));
438
439                         visit_local_variable (stmt.element_variable);
440                         ccode.add_assignment (get_variable_cexpression (get_local_cname (stmt.element_variable)), element_expr);
441
442                         stmt.body.emit (this);
443
444                         ccode.close ();
445                 } else {
446                         Report.error (stmt.source_reference, "internal error: unsupported collection type");
447                         stmt.error = true;
448                         return;
449                 }
450
451                 foreach (LocalVariable local in stmt.get_local_variables ()) {
452                         if (requires_destroy (local.variable_type)) {
453                                 ccode.add_expression (destroy_local (local));
454                         }
455                 }
456
457                 ccode.close ();
458         }
459
460         public override void visit_break_statement (BreakStatement stmt) {
461                 append_local_free (current_symbol, stmt);
462
463                 ccode.add_break ();
464         }
465
466         public override void visit_continue_statement (ContinueStatement stmt) {
467                 append_local_free (current_symbol, stmt);
468
469                 ccode.add_continue ();
470         }
471 }
472