valaccodecontrolflowmodule.vala 16.6 KB
Newer Older
Jürg Billeter's avatar
Jürg Billeter committed
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113
/* valaccodecontrolflowmodule.vala
 *
 * Copyright (C) 2006-2008  Jürg Billeter, Raffaele Sandrini
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.

 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.

 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301  USA
 *
 * Author:
 * 	Jürg Billeter <j@bitron.ch>
 *	Raffaele Sandrini <raffaele@sandrini.ch>
 */

using GLib;
using Gee;

public class Vala.CCodeControlFlowModule : CCodeMethodModule {
	public CCodeControlFlowModule (CCodeGenerator codegen, CCodeModule? next) {
		base (codegen, next);
	}

	public override void visit_if_statement (IfStatement stmt) {
		stmt.accept_children (codegen);

		if (stmt.false_statement != null) {
			stmt.ccodenode = new CCodeIfStatement ((CCodeExpression) stmt.condition.ccodenode, (CCodeStatement) stmt.true_statement.ccodenode, (CCodeStatement) stmt.false_statement.ccodenode);
		} else {
			stmt.ccodenode = new CCodeIfStatement ((CCodeExpression) stmt.condition.ccodenode, (CCodeStatement) stmt.true_statement.ccodenode);
		}
		
		create_temp_decl (stmt, stmt.condition.temp_vars);
	}

	void visit_string_switch_statement (SwitchStatement stmt) {
		// we need a temporary variable to save the property value
		var temp_var = get_temp_variable (stmt.expression.value_type, true, stmt);
		stmt.expression.temp_vars.insert (0, temp_var);

		var ctemp = new CCodeIdentifier (temp_var.name);
		var cinit = new CCodeAssignment (ctemp, (CCodeExpression) stmt.expression.ccodenode);
		var czero = new CCodeConstant ("0");

		var cswitchblock = new CCodeFragment ();
		stmt.ccodenode = cswitchblock;

		var cisnull = new CCodeBinaryExpression (CCodeBinaryOperator.EQUALITY, new CCodeConstant ("NULL"), ctemp);
		var cquark = new CCodeFunctionCall (new CCodeIdentifier ("g_quark_from_string"));
		cquark.add_argument (ctemp);

		var ccond = new CCodeConditionalExpression (cisnull, new CCodeConstant ("0"), cquark);

		temp_var = get_temp_variable (gquark_type);
		stmt.expression.temp_vars.insert (0, temp_var);

		int label_count = 0;

		foreach (SwitchSection section in stmt.get_sections ()) {
			if (section.has_default_label ()) {
				continue;
			}

			foreach (SwitchLabel label in section.get_labels ()) {
				var cexpr = (CCodeExpression) label.expression.ccodenode;

				if (is_constant_ccode_expression (cexpr)) {
					var cname = "%s_label%d".printf (temp_var.name, label_count++);
					var cdecl = new CCodeDeclaration (gquark_type.get_cname ());

					cdecl.modifiers = CCodeModifiers.STATIC;
					cdecl.add_declarator (new CCodeVariableDeclarator.with_initializer (cname, czero));

					cswitchblock.append (cdecl);
				}
			}
		}

		cswitchblock.append (new CCodeExpressionStatement (cinit));

		ctemp = new CCodeIdentifier (temp_var.name);
		cinit = new CCodeAssignment (ctemp, ccond);

		cswitchblock.append (new CCodeExpressionStatement (cinit));
		create_temp_decl (stmt, stmt.expression.temp_vars);

		Gee.List<Statement> default_statements = null;
		label_count = 0;

		// generate nested if statements		
		CCodeStatement ctopstmt = null;
		CCodeIfStatement coldif = null;

		foreach (SwitchSection section in stmt.get_sections ()) {
			if (section.has_default_label ()) {
				default_statements = section.get_statements ();
				continue;
			}

			CCodeBinaryExpression cor = null;
			foreach (SwitchLabel label in section.get_labels ()) {
				var cexpr = (CCodeExpression) label.expression.ccodenode;

				if (is_constant_ccode_expression (cexpr)) {
					var cname = new CCodeIdentifier ("%s_label%d".printf (temp_var.name, label_count++));
114
					var ccondition = new CCodeBinaryExpression (CCodeBinaryOperator.INEQUALITY, czero, cname);
Jürg Billeter's avatar
Jürg Billeter committed
115
					var ccall = new CCodeFunctionCall (new CCodeIdentifier ("g_quark_from_static_string"));
116
					cinit = new CCodeAssignment (cname, ccall);
Jürg Billeter's avatar
Jürg Billeter committed
117 118 119

					ccall.add_argument (cexpr);

120
					cexpr = new CCodeConditionalExpression (ccondition, cname, cinit);
Jürg Billeter's avatar
Jürg Billeter committed
121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179
				} else {
					var ccall = new CCodeFunctionCall (new CCodeIdentifier ("g_quark_from_string"));
					ccall.add_argument (cexpr);
					cexpr = ccall;
				}

				var ccmp = new CCodeBinaryExpression (CCodeBinaryOperator.EQUALITY, ctemp, cexpr);

				if (cor == null) {
					cor = ccmp;
				} else {
					cor = new CCodeBinaryExpression (CCodeBinaryOperator.OR, cor, ccmp);
				}
			}

			var cblock = new CCodeBlock ();
			foreach (CodeNode body_stmt in section.get_statements ()) {
				if (body_stmt.ccodenode is CCodeFragment) {
					foreach (CCodeNode cstmt in ((CCodeFragment) body_stmt.ccodenode).get_children ()) {
						cblock.add_statement (cstmt);
					}
				} else {
					cblock.add_statement (body_stmt.ccodenode);
				}
			}

			var cdo = new CCodeDoStatement (cblock, new CCodeConstant ("0"));
			var cif = new CCodeIfStatement (cor, cdo);

			if (coldif != null) {
				coldif.false_statement = cif;
			} else {
				ctopstmt = cif;
			}

			coldif = cif;
		}
	
		if (default_statements != null) {
			var cblock = new CCodeBlock ();
			foreach (CodeNode body_stmt in default_statements) {
				cblock.add_statement (body_stmt.ccodenode);
			}
		
			var cdo = new CCodeDoStatement (cblock, new CCodeConstant ("0"));

			if (coldif == null) {
				// there is only one section and that section
				// contains a default label
				ctopstmt = cdo;
			} else {
				coldif.false_statement = cdo;
			}
		}
	
		cswitchblock.append (ctopstmt);
	}

	public override void visit_switch_statement (SwitchStatement stmt) {
180 181
		stmt.accept_children (codegen);

Jürg Billeter's avatar
Jürg Billeter committed
182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210
		if (stmt.expression.value_type.compatible (string_type)) {
			visit_string_switch_statement (stmt);
			return;
		}

		var cswitch = new CCodeSwitchStatement ((CCodeExpression) stmt.expression.ccodenode);
		stmt.ccodenode = cswitch;

		foreach (SwitchSection section in stmt.get_sections ()) {
			if (section.has_default_label ()) {
				cswitch.add_statement (new CCodeLabel ("default"));
				var cdefaultblock = new CCodeBlock ();
				cswitch.add_statement (cdefaultblock);
				foreach (CodeNode default_stmt in section.get_statements ()) {
					cdefaultblock.add_statement (default_stmt.ccodenode);
				}
				continue;
			}

			foreach (SwitchLabel label in section.get_labels ()) {
				cswitch.add_statement (new CCodeCaseStatement ((CCodeExpression) label.expression.ccodenode));
			}

			var cblock = new CCodeBlock ();
			cswitch.add_statement (cblock);
			foreach (CodeNode body_stmt in section.get_statements ()) {
				cblock.add_statement (body_stmt.ccodenode);
			}
		}
211 212
		
		create_temp_decl (stmt, stmt.expression.temp_vars);
Jürg Billeter's avatar
Jürg Billeter committed
213 214 215 216 217 218
	}

	public override void visit_switch_section (SwitchSection section) {
		visit_block (section);
	}

219 220 221 222
	public override void visit_switch_label (SwitchLabel label) {
		label.accept_children (codegen);
	}

Jürg Billeter's avatar
Jürg Billeter committed
223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283
	public override void visit_while_statement (WhileStatement stmt) {
		stmt.accept_children (codegen);

		stmt.ccodenode = new CCodeWhileStatement ((CCodeExpression) stmt.condition.ccodenode, (CCodeStatement) stmt.body.ccodenode);
		
		create_temp_decl (stmt, stmt.condition.temp_vars);
	}

	public override void visit_do_statement (DoStatement stmt) {
		stmt.accept_children (codegen);

		stmt.ccodenode = new CCodeDoStatement ((CCodeStatement) stmt.body.ccodenode, (CCodeExpression) stmt.condition.ccodenode);
		
		create_temp_decl (stmt, stmt.condition.temp_vars);
	}

	public override void visit_for_statement (ForStatement stmt) {
		stmt.accept_children (codegen);

		CCodeExpression ccondition = null;
		if (stmt.condition != null) {
			ccondition = (CCodeExpression) stmt.condition.ccodenode;
		}

		var cfor = new CCodeForStatement (ccondition, (CCodeStatement) stmt.body.ccodenode);
		stmt.ccodenode = cfor;
		
		foreach (Expression init_expr in stmt.get_initializer ()) {
			cfor.add_initializer ((CCodeExpression) init_expr.ccodenode);
			create_temp_decl (stmt, init_expr.temp_vars);
		}
		
		foreach (Expression it_expr in stmt.get_iterator ()) {
			cfor.add_iterator ((CCodeExpression) it_expr.ccodenode);
			create_temp_decl (stmt, it_expr.temp_vars);
		}

		if (stmt.condition != null) {
			create_temp_decl (stmt, stmt.condition.temp_vars);
		}
	}

	public override void visit_foreach_statement (ForeachStatement stmt) {
		stmt.element_variable.active = true;
		stmt.collection_variable.active = true;
		if (stmt.iterator_variable != null) {
			stmt.iterator_variable.active = true;
		}

		visit_block (stmt);

		var cblock = new CCodeBlock ();
		// sets #line
		stmt.ccodenode = cblock;

		var cfrag = new CCodeFragment ();
		append_temp_decl (cfrag, stmt.collection.temp_vars);
		cblock.add_statement (cfrag);
		
		var collection_backup = stmt.collection_variable;
		var collection_type = collection_backup.variable_type.copy ();
Jürg Billeter's avatar
Jürg Billeter committed
284 285 286 287 288 289 290 291 292 293 294

		if (current_method != null && current_method.coroutine) {
			closure_struct.add_field (collection_type.get_cname (), collection_backup.name);
			cblock.add_statement (new CCodeExpressionStatement (new CCodeAssignment (get_variable_cexpression (collection_backup.name), (CCodeExpression) stmt.collection.ccodenode)));
		} else {
			var ccoldecl = new CCodeDeclaration (collection_type.get_cname ());
			var ccolvardecl = new CCodeVariableDeclarator.with_initializer (collection_backup.name, (CCodeExpression) stmt.collection.ccodenode);
			ccolvardecl.line = cblock.line;
			ccoldecl.add_declarator (ccolvardecl);
			cblock.add_statement (ccoldecl);
		}
Jürg Billeter's avatar
Jürg Billeter committed
295 296 297
		
		if (stmt.tree_can_fail && stmt.collection.tree_can_fail) {
			// exception handling
298
			cfrag = new CCodeFragment ();
Jürg Billeter's avatar
Jürg Billeter committed
299 300 301 302 303 304 305 306 307 308
			head.add_simple_check (stmt.collection, cfrag);
			cblock.add_statement (cfrag);
		}

		if (stmt.collection.value_type is ArrayType) {
			var array_type = (ArrayType) stmt.collection.value_type;
			
			var array_len = head.get_array_length_cexpression (stmt.collection);

			// store array length for use by _vala_array_free
Jürg Billeter's avatar
Jürg Billeter committed
309 310 311 312 313 314 315 316
			if (current_method != null && current_method.coroutine) {
				closure_struct.add_field ("int", head.get_array_length_cname (collection_backup.name, 1));
				cblock.add_statement (new CCodeExpressionStatement (new CCodeAssignment (get_variable_cexpression (head.get_array_length_cname (collection_backup.name, 1)), array_len)));
			} else {
				var clendecl = new CCodeDeclaration ("int");
				clendecl.add_declarator (new CCodeVariableDeclarator.with_initializer (head.get_array_length_cname (collection_backup.name, 1), array_len));
				cblock.add_statement (clendecl);
			}
Jürg Billeter's avatar
Jürg Billeter committed
317

318
			var it_name = (stmt.variable_name + "_it");
Jürg Billeter's avatar
Jürg Billeter committed
319
		
320 321
			if (current_method != null && current_method.coroutine) {
				closure_struct.add_field ("int", it_name);
Jürg Billeter's avatar
Jürg Billeter committed
322
			} else {
323 324 325 326
				var citdecl = new CCodeDeclaration ("int");
				citdecl.add_declarator (new CCodeVariableDeclarator (it_name));
				cblock.add_statement (citdecl);
			}
Jürg Billeter's avatar
Jürg Billeter committed
327
			
328
			var cbody = new CCodeBlock ();
Jürg Billeter's avatar
Jürg Billeter committed
329

330
			CCodeExpression element_expr = new CCodeElementAccess (get_variable_cexpression (collection_backup.name), get_variable_cexpression (it_name));
Jürg Billeter's avatar
Jürg Billeter committed
331

332 333 334
			var element_type = array_type.element_type.copy ();
			element_type.value_owned = false;
			element_expr = transform_expression (element_expr, element_type, stmt.type_reference);
Jürg Billeter's avatar
Jürg Billeter committed
335

336 337 338 339
			cfrag = new CCodeFragment ();
			append_temp_decl (cfrag, temp_vars);
			cbody.add_statement (cfrag);
			temp_vars.clear ();
Jürg Billeter's avatar
Jürg Billeter committed
340

341 342 343 344 345 346 347 348
			if (current_method != null && current_method.coroutine) {
				closure_struct.add_field (stmt.type_reference.get_cname (), stmt.variable_name);
				cbody.add_statement (new CCodeExpressionStatement (new CCodeAssignment (get_variable_cexpression (stmt.variable_name), element_expr)));
			} else {
				var cdecl = new CCodeDeclaration (stmt.type_reference.get_cname ());
				cdecl.add_declarator (new CCodeVariableDeclarator.with_initializer (stmt.variable_name, element_expr));
				cbody.add_statement (cdecl);
			}
Jürg Billeter's avatar
Jürg Billeter committed
349

350 351 352 353 354 355 356 357 358 359 360
			// add array length variable for stacked arrays
			if (stmt.type_reference is ArrayType) {
				var inner_array_type = (ArrayType) stmt.type_reference;
				for (int dim = 1; dim <= inner_array_type.rank; dim++) {
					if (current_method != null && current_method.coroutine) {
						closure_struct.add_field ("int", head.get_array_length_cname (stmt.variable_name, dim));
						cbody.add_statement (new CCodeExpressionStatement (new CCodeAssignment (get_variable_cexpression (head.get_array_length_cname (stmt.variable_name, dim)), new CCodeConstant ("-1"))));
					} else {
						var cdecl = new CCodeDeclaration ("int");
						cdecl.add_declarator (new CCodeVariableDeclarator.with_initializer (head.get_array_length_cname (stmt.variable_name, dim), new CCodeConstant ("-1")));
						cbody.add_statement (cdecl);
Jürg Billeter's avatar
Jürg Billeter committed
361 362
					}
				}
363
			}
Jürg Billeter's avatar
Jürg Billeter committed
364

365 366 367
			cbody.add_statement (stmt.body.ccodenode);
			
			var ccond = new CCodeBinaryExpression (CCodeBinaryOperator.LESS_THAN, get_variable_cexpression (it_name), array_len);
Jürg Billeter's avatar
Jürg Billeter committed
368

369 370 371 372
			var cfor = new CCodeForStatement (ccond, cbody);
			cfor.add_initializer (new CCodeAssignment (get_variable_cexpression (it_name), new CCodeConstant ("0")));
			cfor.add_iterator (new CCodeAssignment (get_variable_cexpression (it_name), new CCodeBinaryExpression (CCodeBinaryOperator.PLUS, get_variable_cexpression (it_name), new CCodeConstant ("1"))));
			cblock.add_statement (cfor);
Jürg Billeter's avatar
Jürg Billeter committed
373 374 375 376 377
		} else if (stmt.collection.value_type.compatible (new ObjectType (glist_type)) || stmt.collection.value_type.compatible (new ObjectType (gslist_type))) {
			// iterating over a GList or GSList

			var it_name = "%s_it".printf (stmt.variable_name);
		
Jürg Billeter's avatar
Jürg Billeter committed
378 379 380 381 382 383 384 385 386
			if (current_method != null && current_method.coroutine) {
				closure_struct.add_field (collection_type.get_cname (), it_name);
			} else {
				var citdecl = new CCodeDeclaration (collection_type.get_cname ());
				var citvardecl = new CCodeVariableDeclarator (it_name);
				citvardecl.line = cblock.line;
				citdecl.add_declarator (citvardecl);
				cblock.add_statement (citdecl);
			}
Jürg Billeter's avatar
Jürg Billeter committed
387 388 389
			
			var cbody = new CCodeBlock ();

Jürg Billeter's avatar
Jürg Billeter committed
390
			CCodeExpression element_expr = new CCodeMemberAccess.pointer (get_variable_cexpression (it_name), "data");
Jürg Billeter's avatar
Jürg Billeter committed
391 392 393 394 395 396 397 398 399

			if (collection_type.get_type_arguments ().size != 1) {
				Report.error (stmt.source_reference, "internal error: missing generic type argument");
				stmt.error = true;
				return;
			}

			var element_data_type = collection_type.get_type_arguments ().get (0).copy ();
			element_data_type.value_owned = false;
400
			element_expr = convert_from_generic_pointer (element_expr, element_data_type);
Jürg Billeter's avatar
Jürg Billeter committed
401 402
			element_expr = transform_expression (element_expr, element_data_type, stmt.type_reference);

403
			cfrag = new CCodeFragment ();
Jürg Billeter's avatar
Jürg Billeter committed
404 405 406 407
			append_temp_decl (cfrag, temp_vars);
			cbody.add_statement (cfrag);
			temp_vars.clear ();

Jürg Billeter's avatar
Jürg Billeter committed
408 409 410 411 412 413 414 415 416 417
			if (current_method != null && current_method.coroutine) {
				closure_struct.add_field (stmt.type_reference.get_cname (), stmt.variable_name);
				cbody.add_statement (new CCodeExpressionStatement (new CCodeAssignment (get_variable_cexpression (stmt.variable_name), element_expr)));
			} else {
				var cdecl = new CCodeDeclaration (stmt.type_reference.get_cname ());
				var cvardecl = new CCodeVariableDeclarator.with_initializer (stmt.variable_name, element_expr);
				cvardecl.line = cblock.line;
				cdecl.add_declarator (cvardecl);
				cbody.add_statement (cdecl);
			}
Jürg Billeter's avatar
Jürg Billeter committed
418 419 420
			
			cbody.add_statement (stmt.body.ccodenode);
			
Jürg Billeter's avatar
Jürg Billeter committed
421
			var ccond = new CCodeBinaryExpression (CCodeBinaryOperator.INEQUALITY, get_variable_cexpression (it_name), new CCodeConstant ("NULL"));
Jürg Billeter's avatar
Jürg Billeter committed
422 423 424
			
			var cfor = new CCodeForStatement (ccond, cbody);
			
Jürg Billeter's avatar
Jürg Billeter committed
425
			cfor.add_initializer (new CCodeAssignment (get_variable_cexpression (it_name), get_variable_cexpression (collection_backup.name)));
Jürg Billeter's avatar
Jürg Billeter committed
426

Jürg Billeter's avatar
Jürg Billeter committed
427
			cfor.add_iterator (new CCodeAssignment (get_variable_cexpression (it_name), new CCodeMemberAccess.pointer (get_variable_cexpression (it_name), "next")));
Jürg Billeter's avatar
Jürg Billeter committed
428 429 430 431 432 433 434
			cblock.add_statement (cfor);
		}

		foreach (LocalVariable local in stmt.get_local_variables ()) {
			if (requires_destroy (local.variable_type)) {
				var ma = new MemberAccess.simple (local.name);
				ma.symbol_reference = local;
Jürg Billeter's avatar
Jürg Billeter committed
435
				var cunref = new CCodeExpressionStatement (get_unref_expression (get_variable_cexpression (local.name), local.variable_type, ma));
Jürg Billeter's avatar
Jürg Billeter committed
436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454
				cunref.line = cblock.line;
				cblock.add_statement (cunref);
			}
		}
	}

	public override void visit_break_statement (BreakStatement stmt) {
		stmt.ccodenode = new CCodeBreakStatement ();

		create_local_free (stmt, true);
	}

	public override void visit_continue_statement (ContinueStatement stmt) {
		stmt.ccodenode = new CCodeContinueStatement ();

		create_local_free (stmt, true);
	}
}