valaccodearraymodule.vala 29.2 KB
Newer Older
Jürg Billeter's avatar
Jürg Billeter committed
1
/* valaccodearraymodule.vala
2
 *
3
 * Copyright (C) 2006-2010  Jürg Billeter
4
 * Copyright (C) 2006-2008  Raffaele Sandrini
5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
 *
 * 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>
 */


26
public class Vala.CCodeArrayModule : CCodeMethodCallModule {
27 28 29
	int next_array_dup_id = 0;
	int next_array_add_id = 0;

30
	void append_initializer_list (CCodeExpression name_cnode, InitializerList initializer_list, int rank, ref int i) {
31 32
		foreach (Expression e in initializer_list.get_initializers ()) {
			if (rank > 1) {
33
				append_initializer_list (name_cnode, (InitializerList) e, rank - 1, ref i);
34
			} else {
35
				ccode.add_assignment (new CCodeElementAccess (name_cnode, new CCodeConstant (i.to_string ())), get_cvalue (e));
36 37 38 39 40
				i++;
			}
		}
	}

Jürg Billeter's avatar
Jürg Billeter committed
41
	public override void visit_array_creation_expression (ArrayCreationExpression expr) {
42 43 44 45 46
		var array_type = expr.target_type as ArrayType;
		if (array_type != null && array_type.fixed_length) {
			// no heap allocation for fixed-length arrays

			var temp_var = get_temp_variable (array_type, true, expr);
47
			temp_var.init = true;
48
			var name_cnode = get_variable_cexpression (temp_var.name);
49 50
			int i = 0;

51
			emit_temp_var (temp_var);
52

53
			append_initializer_list (name_cnode, expr.initializer_list, expr.rank, ref i);
54

55
			set_cvalue (expr, name_cnode);
56 57 58 59

			return;
		}

60 61 62 63 64 65 66 67
		CCodeFunctionCall gnew;
		if (context.profile == Profile.POSIX) {
			cfile.add_include ("stdlib.h");
			gnew = new CCodeFunctionCall (new CCodeIdentifier ("calloc"));
		} else {
			gnew = new CCodeFunctionCall (new CCodeIdentifier ("g_new0"));
			gnew.add_argument (new CCodeIdentifier (get_ccode_name (expr.element_type)));
		}
68

69 70
		bool first = true;
		CCodeExpression cexpr = null;
71 72

		// iterate over each dimension
73
		foreach (Expression size in expr.get_sizes ()) {
74
			CCodeExpression csize = get_cvalue (size);
75
			append_array_length (expr, csize);
76

77 78 79 80 81 82 83
			if (first) {
				cexpr = csize;
				first = false;
			} else {
				cexpr = new CCodeBinaryExpression (CCodeBinaryOperator.MUL, cexpr, csize);
			}
		}
84

85 86 87 88
		// add extra item to have array NULL-terminated for all reference types
		if (expr.element_type.data_type != null && expr.element_type.data_type.is_reference_type ()) {
			cexpr = new CCodeBinaryExpression (CCodeBinaryOperator.PLUS, cexpr, new CCodeConstant ("1"));
		}
89

90 91
		gnew.add_argument (cexpr);

92 93 94 95 96 97
		if (context.profile == Profile.POSIX) {
			var csizeof = new CCodeFunctionCall (new CCodeIdentifier ("sizeof"));
			csizeof.add_argument (new CCodeIdentifier (get_ccode_name (expr.element_type)));
			gnew.add_argument (csizeof);
		}

98 99 100 101 102 103
		var temp_var = get_temp_variable (expr.value_type, true, expr);
		var name_cnode = get_variable_cexpression (temp_var.name);
		int i = 0;

		emit_temp_var (temp_var);

104
		ccode.add_assignment (name_cnode, gnew);
105

106
		if (expr.initializer_list != null) {
107
			append_initializer_list (name_cnode, expr.initializer_list, expr.rank, ref i);
108
		}
109 110

		set_cvalue (expr, name_cnode);
111
	}
112 113 114 115 116

	public override string get_array_length_cname (string array_cname, int dim) {
		return "%s_length%d".printf (array_cname, dim);
	}

117
	public override string get_parameter_array_length_cname (Parameter param, int dim) {
118 119
		if (get_ccode_array_length_name (param) != null) {
			return get_ccode_array_length_name (param);
120 121 122 123 124
		} else {
			return get_array_length_cname (get_variable_cname (param.name), dim);
		}
	}

125
	public override CCodeExpression get_array_length_cexpression (Expression array_expr, int dim = -1) {
126 127 128 129 130
		return get_array_length_cvalue (array_expr.target_value, dim);
	}

	public override CCodeExpression get_array_length_cvalue (TargetValue value, int dim = -1) {
		var array_type = value.value_type as ArrayType;
131 132

		if (array_type != null && array_type.fixed_length) {
133
			return get_ccodenode (array_type.length);
134 135
		}

136 137 138
		// dim == -1 => total size over all dimensions
		if (dim == -1) {
			if (array_type != null && array_type.rank > 1) {
139
				CCodeExpression cexpr = get_array_length_cvalue (value, 1);
140
				for (dim = 2; dim <= array_type.rank; dim++) {
141
					cexpr = new CCodeBinaryExpression (CCodeBinaryOperator.MUL, cexpr, get_array_length_cvalue (value, dim));
142 143 144 145 146 147 148
				}
				return cexpr;
			} else {
				dim = 1;
			}
		}

149
		List<CCodeExpression> size = ((GLibValue) value).array_length_cvalues;
150 151 152 153
		if (size == null || size.size < dim) {
			Report.error (null, "internal error: invalid array_length for given dimension");
			return new CCodeInvalidExpression ();
		}
154
		return size[dim - 1];
155
	}
156

157
	public override string get_array_size_cname (string array_cname) {
158
		return "_%s_size_".printf (array_cname);
159 160
	}

161
	public override void visit_element_access (ElementAccess expr) {
162
		List<Expression> indices = expr.get_indices ();
163 164
		int rank = indices.size;

165 166
		var ccontainer = get_cvalue (expr.container);
		var cindex = get_cvalue (indices[0]);
167 168 169 170 171
		if (expr.container.symbol_reference is ArrayLengthField) {
			/* Figure if cindex is a constant expression and calculate dim...*/
			var lit = indices[0] as IntegerLiteral;
			var memberaccess = expr.container as MemberAccess;
			if (lit != null && memberaccess != null) {
172
				int dim = int.parse (lit.value);
173
				set_cvalue (expr, get_array_length_cexpression (memberaccess.inner, dim + 1));
174
			} else {
175
				Report.error (expr.source_reference, "internal error: only integer literals supported as index");
176 177 178 179
			}
		} else {
			// access to element in an array
			for (int i = 1; i < rank; i++) {
180
				var cmul = new CCodeBinaryExpression (CCodeBinaryOperator.MUL, cindex, get_array_length_cexpression (expr.container, i + 1));
181
				cindex = new CCodeBinaryExpression (CCodeBinaryOperator.PLUS, cmul, get_cvalue (indices[i]));
182 183 184
				if (expr.container.is_constant ()) {
					ccontainer = new CCodeUnaryExpression (CCodeUnaryOperator.POINTER_INDIRECTION, ccontainer);
				}
185
			}
186
			set_cvalue (expr, new CCodeElementAccess (ccontainer, cindex));
187
		}
188 189 190 191 192 193

		expr.target_value.value_type = expr.value_type.copy ();
		if (!expr.lvalue) {
			expr.target_value = store_temp_value (expr.target_value, expr);
		}
		((GLibValue) expr.target_value).lvalue = true;
194
	}
195

196
	public override void visit_slice_expression (SliceExpression expr) {
197 198 199
		var ccontainer = get_cvalue (expr.container);
		var cstart = get_cvalue (expr.start);
		var cstop = get_cvalue (expr.stop);
200 201 202 203

		var cstartpointer = new CCodeBinaryExpression (CCodeBinaryOperator.PLUS, ccontainer, cstart);
		var splicelen = new CCodeBinaryExpression (CCodeBinaryOperator.MINUS, cstop, cstart);

204
		set_cvalue (expr, cstartpointer);
205
		append_array_length (expr, splicelen);
206 207
	}

208 209 210 211 212 213
	void append_struct_array_free_loop (Struct st) {
		var cforinit = new CCodeAssignment (new CCodeIdentifier ("i"), new CCodeConstant ("0"));
		var cforcond = new CCodeBinaryExpression (CCodeBinaryOperator.LESS_THAN, new CCodeIdentifier ("i"), new CCodeIdentifier ("array_length"));
		var cforiter = new CCodeAssignment (new CCodeIdentifier ("i"), new CCodeBinaryExpression (CCodeBinaryOperator.PLUS, new CCodeIdentifier ("i"), new CCodeConstant ("1")));
		ccode.open_for (cforinit, cforcond, cforiter);

214 215 216 217 218
		var cptrarray = new CCodeIdentifier ("array");
		var cea = new CCodeElementAccess (cptrarray, new CCodeIdentifier ("i"));

		var cfreecall = new CCodeFunctionCall (get_destroy_func_expression (new StructValueType (st)));
		cfreecall.add_argument (new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, cea));
219
		ccode.add_expression (cfreecall);
220

221
		ccode.close ();
222 223 224
	}

	public override string? append_struct_array_free (Struct st) {
225
		string cname = "_vala_%s_array_free".printf (get_ccode_name (st));
226

227
		if (cfile.add_declaration (cname)) {
228 229 230 231 232
			return cname;
		}

		var fun = new CCodeFunction (cname, "void");
		fun.modifiers = CCodeModifiers.STATIC;
233
		fun.add_parameter (new CCodeParameter ("array", "%s *".printf (get_ccode_name (st))));
234
		fun.add_parameter (new CCodeParameter ("array_length", "gint"));
235

236
		push_function (fun);
237

238 239
		var ccondarr = new CCodeBinaryExpression (CCodeBinaryOperator.INEQUALITY, new CCodeIdentifier ("array"), new CCodeConstant ("NULL"));
		ccode.open_if (ccondarr);
240

241 242
		ccode.add_declaration ("int", new CCodeVariableDeclarator ("i"));
		append_struct_array_free_loop (st);
243

244
		ccode.close ();
245 246 247

		var carrfree = new CCodeFunctionCall (new CCodeIdentifier ("g_free"));
		carrfree.add_argument (new CCodeIdentifier ("array"));
248
		ccode.add_expression (carrfree);
249

250 251 252
		pop_function ();

		cfile.add_function_declaration (fun);
253
		cfile.add_function (fun);
254 255 256 257

		return cname;
	}

258 259 260 261 262 263
	void append_vala_array_free_loop () {
		var cforinit = new CCodeAssignment (new CCodeIdentifier ("i"), new CCodeConstant ("0"));
		var cforcond = new CCodeBinaryExpression (CCodeBinaryOperator.LESS_THAN, new CCodeIdentifier ("i"), new CCodeIdentifier ("array_length"));
		var cforiter = new CCodeAssignment (new CCodeIdentifier ("i"), new CCodeBinaryExpression (CCodeBinaryOperator.PLUS, new CCodeIdentifier ("i"), new CCodeConstant ("1")));
		ccode.open_for (cforinit, cforcond, cforiter);

264 265 266
		var cptrarray = new CCodeCastExpression (new CCodeIdentifier ("array"), "gpointer*");
		var cea = new CCodeElementAccess (cptrarray, new CCodeIdentifier ("i"));

267
		var cfreecond = new CCodeBinaryExpression (CCodeBinaryOperator.INEQUALITY, cea, new CCodeConstant ("NULL"));
268
		ccode.open_if (cfreecond);
269

270 271 272
		var cfreecall = new CCodeFunctionCall (new CCodeIdentifier ("destroy_func"));
		cfreecall.add_argument (cea);
		ccode.add_expression (cfreecall);
273

274
		ccode.close ();
275 276 277
	}

	public override void append_vala_array_free () {
278 279 280
		// _vala_array_destroy only frees elements but not the array itself

		var fun = new CCodeFunction ("_vala_array_destroy", "void");
281
		fun.modifiers = CCodeModifiers.STATIC;
282 283 284
		fun.add_parameter (new CCodeParameter ("array", "gpointer"));
		fun.add_parameter (new CCodeParameter ("array_length", "gint"));
		fun.add_parameter (new CCodeParameter ("destroy_func", "GDestroyNotify"));
285

286
		push_function (fun);
287 288 289

		var ccondarr = new CCodeBinaryExpression (CCodeBinaryOperator.INEQUALITY, new CCodeIdentifier ("array"), new CCodeConstant ("NULL"));
		var ccondfunc = new CCodeBinaryExpression (CCodeBinaryOperator.INEQUALITY, new CCodeIdentifier ("destroy_func"), new CCodeConstant ("NULL"));
290 291 292 293 294 295 296 297
		ccode.open_if (new CCodeBinaryExpression (CCodeBinaryOperator.AND, ccondarr, ccondfunc));

		ccode.add_declaration ("int", new CCodeVariableDeclarator ("i"));
		append_vala_array_free_loop ();

		ccode.close ();

		pop_function ();
298

299
		cfile.add_function_declaration (fun);
300
		cfile.add_function (fun);
301 302 303 304 305

		// _vala_array_free frees elements and array

		fun = new CCodeFunction ("_vala_array_free", "void");
		fun.modifiers = CCodeModifiers.STATIC;
306 307 308
		fun.add_parameter (new CCodeParameter ("array", "gpointer"));
		fun.add_parameter (new CCodeParameter ("array_length", "gint"));
		fun.add_parameter (new CCodeParameter ("destroy_func", "GDestroyNotify"));
309 310

		push_function (fun);
311 312 313 314 315 316

		// call _vala_array_destroy to free the array elements
		var ccall = new CCodeFunctionCall (new CCodeIdentifier ("_vala_array_destroy"));
		ccall.add_argument (new CCodeIdentifier ("array"));
		ccall.add_argument (new CCodeIdentifier ("array_length"));
		ccall.add_argument (new CCodeIdentifier ("destroy_func"));
317
		ccode.add_expression (ccall);
318

319 320
		var carrfree = new CCodeFunctionCall (new CCodeIdentifier ("g_free"));
		carrfree.add_argument (new CCodeIdentifier ("array"));
321 322 323
		ccode.add_expression (carrfree);

		pop_function ();
324

325
		cfile.add_function_declaration (fun);
326
		cfile.add_function (fun);
327 328 329
	}

	public override void append_vala_array_move () {
330
		cfile.add_include ("string.h");
331 332 333 334 335

		// assumes that overwritten array elements are null before invocation
		// FIXME will leak memory if that's not the case
		var fun = new CCodeFunction ("_vala_array_move", "void");
		fun.modifiers = CCodeModifiers.STATIC;
336 337 338 339 340
		fun.add_parameter (new CCodeParameter ("array", "gpointer"));
		fun.add_parameter (new CCodeParameter ("element_size", "gsize"));
		fun.add_parameter (new CCodeParameter ("src", "gint"));
		fun.add_parameter (new CCodeParameter ("dest", "gint"));
		fun.add_parameter (new CCodeParameter ("length", "gint"));
341 342

		push_function (fun);
343 344 345 346 347

		var array = new CCodeCastExpression (new CCodeIdentifier ("array"), "char*");
		var element_size = new CCodeIdentifier ("element_size");
		var length = new CCodeIdentifier ("length");
		var src = new CCodeIdentifier ("src");
348
		var src_end = new CCodeBinaryExpression (CCodeBinaryOperator.PLUS, src, length);
349
		var dest = new CCodeIdentifier ("dest");
350
		var dest_end = new CCodeBinaryExpression (CCodeBinaryOperator.PLUS, dest, length);
351 352
		var src_address = new CCodeBinaryExpression (CCodeBinaryOperator.PLUS, array, new CCodeBinaryExpression (CCodeBinaryOperator.MUL, src, element_size));
		var dest_address = new CCodeBinaryExpression (CCodeBinaryOperator.PLUS, array, new CCodeBinaryExpression (CCodeBinaryOperator.MUL, dest, element_size));
353
		var dest_end_address = new CCodeBinaryExpression (CCodeBinaryOperator.PLUS, array, new CCodeBinaryExpression (CCodeBinaryOperator.MUL, dest_end, element_size));
354 355 356 357 358

		var ccall = new CCodeFunctionCall (new CCodeIdentifier ("g_memmove"));
		ccall.add_argument (dest_address);
		ccall.add_argument (src_address);
		ccall.add_argument (new CCodeBinaryExpression (CCodeBinaryOperator.MUL, length, element_size));
359 360
		ccode.add_expression (ccall);

361
		ccode.open_if (new CCodeBinaryExpression (CCodeBinaryOperator.AND, new CCodeBinaryExpression (CCodeBinaryOperator.LESS_THAN, src, dest), new CCodeBinaryExpression (CCodeBinaryOperator.GREATER_THAN, src_end, dest)));
362 363 364 365

		var czero1 = new CCodeFunctionCall (new CCodeIdentifier ("memset"));
		czero1.add_argument (src_address);
		czero1.add_argument (new CCodeConstant ("0"));
366
		czero1.add_argument (new CCodeBinaryExpression (CCodeBinaryOperator.MUL, new CCodeBinaryExpression (CCodeBinaryOperator.MINUS, dest, src), element_size));
367 368
		ccode.add_expression (czero1);

369
		ccode.else_if (new CCodeBinaryExpression (CCodeBinaryOperator.AND, new CCodeBinaryExpression (CCodeBinaryOperator.GREATER_THAN, src, dest), new CCodeBinaryExpression (CCodeBinaryOperator.LESS_THAN, src, dest_end)));
370 371 372 373

		var czero2 = new CCodeFunctionCall (new CCodeIdentifier ("memset"));
		czero2.add_argument (dest_end_address);
		czero2.add_argument (new CCodeConstant ("0"));
374
		czero2.add_argument (new CCodeBinaryExpression (CCodeBinaryOperator.MUL, new CCodeBinaryExpression (CCodeBinaryOperator.MINUS, src, dest), element_size));
375
		ccode.add_expression (czero2);
376

377 378 379 380 381 382 383 384
		ccode.else_if (new CCodeBinaryExpression (CCodeBinaryOperator.INEQUALITY, src, dest));

		var czero3 = new CCodeFunctionCall (new CCodeIdentifier ("memset"));
		czero3.add_argument (src_address);
		czero3.add_argument (new CCodeConstant ("0"));
		czero3.add_argument (new CCodeBinaryExpression (CCodeBinaryOperator.MUL, length, element_size));
		ccode.add_expression (czero3);

385
		ccode.close ();
386

387 388 389
		pop_function ();

		cfile.add_function_declaration (fun);
390
		cfile.add_function (fun);
391
	}
392

393 394 395
	public override void append_vala_array_length () {
		var fun = new CCodeFunction ("_vala_array_length", "gint");
		fun.modifiers = CCodeModifiers.STATIC;
396
		fun.add_parameter (new CCodeParameter ("array", "gpointer"));
397

398
		push_function (fun);
399

400
		ccode.add_declaration ("int", new CCodeVariableDeclarator ("length", new CCodeConstant ("0")));
401 402 403 404

		// return 0 if the array is NULL
		// avoids an extra NULL check on the caller side
		var array_check = new CCodeIdentifier ("array");
405
		ccode.open_if (array_check);
406

407 408 409 410 411 412
		var array_element_check = new CCodeElementAccess (new CCodeCastExpression (new CCodeIdentifier ("array"), "gpointer*"), new CCodeConstant ("length"));
		ccode.open_while (array_element_check);
		ccode.add_expression (new CCodeUnaryExpression (CCodeUnaryOperator.POSTFIX_INCREMENT, new CCodeIdentifier ("length")));
		ccode.close ();

		ccode.close ();
413

414
		ccode.add_return (new CCodeIdentifier ("length"));
415

416 417 418
		pop_function ();

		cfile.add_function_declaration (fun);
419
		cfile.add_function (fun);
420 421
	}

422
	public override TargetValue? copy_value (TargetValue value, CodeNode node) {
423 424 425 426 427
		var type = value.value_type;
		var cexpr = get_cvalue_ (value);

		if (type is ArrayType) {
			var array_type = (ArrayType) type;
428 429

			if (!array_type.fixed_length) {
430
				return base.copy_value (value, node);
431 432
			}

433
			var temp_value = create_temp_value (type, false, node);
434 435 436

			var copy_call = new CCodeFunctionCall (new CCodeIdentifier (generate_array_copy_wrapper (array_type)));
			copy_call.add_argument (cexpr);
437
			copy_call.add_argument (get_cvalue_ (temp_value));
438
			ccode.add_expression (copy_call);
439

440
			return temp_value;
441
		} else {
442
			return base.copy_value (value, node);
443 444 445
		}
	}

446
	public override CCodeExpression? get_dup_func_expression (DataType type, SourceReference? source_reference, bool is_chainup) {
447
		if (type is ArrayType) {
448 449 450 451 452
			var array_type = (ArrayType) type;
			// fixed length arrays use different code
			// generated by overridden get_ref_cexpression method
			assert (!array_type.fixed_length);
			return new CCodeIdentifier (generate_array_dup_wrapper (array_type));
453
		} else {
454
			return base.get_dup_func_expression (type, source_reference, is_chainup);
455 456 457
		}
	}

458 459 460
	public override CCodeExpression destroy_value (TargetValue value, bool is_macro_definition = false) {
		var type = value.value_type;

461 462 463 464
		if (type is ArrayType) {
			var array_type = (ArrayType) type;

			if (!array_type.fixed_length) {
465
				return base.destroy_value (value, is_macro_definition);
466 467 468 469 470 471 472
			}

			requires_array_free = true;

			var ccall = new CCodeFunctionCall (get_destroy_func_expression (type));

			ccall = new CCodeFunctionCall (new CCodeIdentifier ("_vala_array_destroy"));
473
			ccall.add_argument (get_cvalue_ (value));
474
			ccall.add_argument (get_ccodenode (array_type.length));
475 476 477 478
			ccall.add_argument (new CCodeCastExpression (get_destroy_func_expression (array_type.element_type), "GDestroyNotify"));

			return ccall;
		} else {
479
			return base.destroy_value (value, is_macro_definition);
480 481 482
		}
	}

483
	string generate_array_dup_wrapper (ArrayType array_type) {
484
		string dup_func = "_vala_array_dup%d".printf (++next_array_dup_id);
485 486 487 488 489 490 491 492

		if (!add_wrapper (dup_func)) {
			// wrapper already defined
			return dup_func;
		}

		// declaration

493
		var function = new CCodeFunction (dup_func, get_ccode_name (array_type));
494 495
		function.modifiers = CCodeModifiers.STATIC;

496
		function.add_parameter (new CCodeParameter ("self", get_ccode_name (array_type)));
497
		// total length over all dimensions
498
		function.add_parameter (new CCodeParameter ("length", "int"));
499 500
		if (array_type.element_type is GenericType) {
			// dup function array elements
501
			string func_name = "%s_dup_func".printf (((GenericType) array_type.element_type).type_parameter.name.down ());
502
			function.add_parameter (new CCodeParameter (func_name, "GBoxedCopyFunc"));
503
		}
504 505 506

		// definition

507 508
		push_context (new EmitContext ());
		push_function (function);
509 510 511 512

		if (requires_copy (array_type.element_type)) {
			var cvardecl = new CCodeVariableDeclarator ("result");
			var gnew = new CCodeFunctionCall (new CCodeIdentifier ("g_new0"));
513
			gnew.add_argument (new CCodeIdentifier (get_ccode_name (array_type.element_type)));
514 515 516 517 518 519 520 521

			CCodeExpression length_expr = new CCodeIdentifier ("length");
			// add extra item to have array NULL-terminated for all reference types
			if (array_type.element_type.data_type != null && array_type.element_type.data_type.is_reference_type ()) {
				length_expr = new CCodeBinaryExpression (CCodeBinaryOperator.PLUS, length_expr, new CCodeConstant ("1"));
			}
			gnew.add_argument (length_expr);

522
			ccode.add_declaration (get_ccode_name (array_type), cvardecl);
523
			ccode.add_assignment (new CCodeIdentifier ("result"), gnew);
524

525
			ccode.add_declaration ("int", new CCodeVariableDeclarator ("i"));
526

527 528 529
			ccode.open_for (new CCodeAssignment (new CCodeIdentifier ("i"), new CCodeConstant ("0")),
			                   new CCodeBinaryExpression (CCodeBinaryOperator.LESS_THAN, new CCodeIdentifier ("i"), new CCodeIdentifier ("length")),
			                   new CCodeUnaryExpression (CCodeUnaryOperator.POSTFIX_INCREMENT, new CCodeIdentifier ("i")));
530

531
			ccode.add_assignment (new CCodeElementAccess (new CCodeIdentifier ("result"), new CCodeIdentifier ("i")), get_cvalue_ (copy_value (new GLibValue (array_type.element_type, new CCodeElementAccess (new CCodeIdentifier ("self"), new CCodeIdentifier ("i")), true), array_type)));
532
			ccode.close ();
533

534
			ccode.add_return (new CCodeIdentifier ("result"));
535 536 537 538 539
		} else {
			var dup_call = new CCodeFunctionCall (new CCodeIdentifier ("g_memdup"));
			dup_call.add_argument (new CCodeIdentifier ("self"));

			var sizeof_call = new CCodeFunctionCall (new CCodeIdentifier ("sizeof"));
540
			sizeof_call.add_argument (new CCodeIdentifier (get_ccode_name (array_type.element_type)));
541 542
			dup_call.add_argument (new CCodeBinaryExpression (CCodeBinaryOperator.MUL, new CCodeIdentifier ("length"), sizeof_call));

543
			ccode.add_return (dup_call);
544 545 546 547
		}

		// append to file

548
		cfile.add_function_declaration (function);
549
		cfile.add_function (function);
550

551 552
		pop_context ();

553 554
		return dup_func;
	}
555

556
	string generate_array_copy_wrapper (ArrayType array_type) {
557
		string dup_func = "_vala_array_copy%d".printf (++next_array_dup_id);
558 559 560 561 562 563 564 565 566 567 568

		if (!add_wrapper (dup_func)) {
			// wrapper already defined
			return dup_func;
		}

		// declaration

		var function = new CCodeFunction (dup_func, "void");
		function.modifiers = CCodeModifiers.STATIC;

569 570
		function.add_parameter (new CCodeParameter ("self", "%s *".printf (get_ccode_name (array_type))));
		function.add_parameter (new CCodeParameter ("dest", "%s *".printf (get_ccode_name (array_type))));
571 572 573

		// definition

574 575
		push_context (new EmitContext ());
		push_function (function);
576 577

		if (requires_copy (array_type.element_type)) {
578
			ccode.add_declaration ("int", new CCodeVariableDeclarator ("i"));
579

580
			ccode.open_for (new CCodeAssignment (new CCodeIdentifier ("i"), new CCodeConstant ("0")),
581
			                   new CCodeBinaryExpression (CCodeBinaryOperator.LESS_THAN, new CCodeIdentifier ("i"), get_ccodenode (array_type.length)),
582
			                   new CCodeUnaryExpression (CCodeUnaryOperator.POSTFIX_INCREMENT, new CCodeIdentifier ("i")));
583

584

585
			ccode.add_assignment (new CCodeElementAccess (new CCodeIdentifier ("dest"), new CCodeIdentifier ("i")), get_cvalue_ (copy_value (new GLibValue (array_type.element_type, new CCodeElementAccess (new CCodeIdentifier ("self"), new CCodeIdentifier ("i")), true), array_type)));
586
		} else {
587
			cfile.add_include ("string.h");
588

589 590 591 592 593
			var dup_call = new CCodeFunctionCall (new CCodeIdentifier ("memcpy"));
			dup_call.add_argument (new CCodeIdentifier ("dest"));
			dup_call.add_argument (new CCodeIdentifier ("self"));

			var sizeof_call = new CCodeFunctionCall (new CCodeIdentifier ("sizeof"));
594
			sizeof_call.add_argument (new CCodeIdentifier (get_ccode_name (array_type.element_type)));
595
			dup_call.add_argument (new CCodeBinaryExpression (CCodeBinaryOperator.MUL, get_ccodenode (array_type.length), sizeof_call));
596

597
			ccode.add_expression (dup_call);
598 599 600 601
		}

		// append to file

602
		cfile.add_function_declaration (function);
603
		cfile.add_function (function);
604

605 606
		pop_context ();

607 608 609
		return dup_func;
	}

610
	string generate_array_add_wrapper (ArrayType array_type) {
611
		string add_func = "_vala_array_add%d".printf (++next_array_add_id);
612 613 614 615 616 617 618 619 620

		if (!add_wrapper (add_func)) {
			// wrapper already defined
			return add_func;
		}

		var function = new CCodeFunction (add_func, "void");
		function.modifiers = CCodeModifiers.STATIC;

621
		function.add_parameter (new CCodeParameter ("array", "%s *".printf (get_ccode_name (array_type))));
622 623
		function.add_parameter (new CCodeParameter ("length", "int*"));
		function.add_parameter (new CCodeParameter ("size", "int*"));
624

625 626
		push_function (function);

627
		string typename = get_ccode_name (array_type.element_type);
628 629 630 631 632 633 634 635 636 637
		CCodeExpression value = new CCodeIdentifier ("value");
		if (array_type.element_type.is_real_struct_type ()) {
			if (!array_type.element_type.nullable || !array_type.element_type.value_owned) {
				typename = "const " + typename;
			}
			if (!array_type.element_type.nullable) {
				typename += "*";
				value = new CCodeUnaryExpression (CCodeUnaryOperator.POINTER_INDIRECTION, value);
			}
		}
638
		function.add_parameter (new CCodeParameter ("value", typename));
639 640 641 642 643 644

		var array = new CCodeUnaryExpression (CCodeUnaryOperator.POINTER_INDIRECTION, new CCodeIdentifier ("array"));
		var length = new CCodeUnaryExpression (CCodeUnaryOperator.POINTER_INDIRECTION, new CCodeIdentifier ("length"));
		var size = new CCodeUnaryExpression (CCodeUnaryOperator.POINTER_INDIRECTION, new CCodeIdentifier ("size"));

		var renew_call = new CCodeFunctionCall (new CCodeIdentifier ("g_renew"));
645
		renew_call.add_argument (new CCodeIdentifier (get_ccode_name (array_type.element_type)));
646
		renew_call.add_argument (array);
647 648 649 650 651 652
		if (array_type.element_type.is_reference_type_or_type_parameter ()) {
			// NULL terminate array
			renew_call.add_argument (new CCodeBinaryExpression (CCodeBinaryOperator.PLUS, size, new CCodeConstant ("1")));
		} else {
			renew_call.add_argument (size);
		}
653 654

		var csizecheck = new CCodeBinaryExpression (CCodeBinaryOperator.EQUALITY, length, size);
655
		ccode.open_if (csizecheck);
656 657
		ccode.add_assignment (size, new CCodeConditionalExpression (size, new CCodeBinaryExpression (CCodeBinaryOperator.MUL, new CCodeConstant ("2"), size), new CCodeConstant ("4")));
		ccode.add_assignment (array, renew_call);
658
		ccode.close ();
659

660
		ccode.add_assignment (new CCodeElementAccess (array, new CCodeUnaryExpression (CCodeUnaryOperator.POSTFIX_INCREMENT, length)), value);
661

662 663
		if (array_type.element_type.is_reference_type_or_type_parameter ()) {
			// NULL terminate array
664
			ccode.add_assignment (new CCodeElementAccess (array, length), new CCodeConstant ("NULL"));
665 666
		}

667
		pop_function ();
668

669
		cfile.add_function_declaration (function);
670
		cfile.add_function (function);
671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693

		return add_func;
	}

	bool is_array_add (Assignment assignment) {
		var binary = assignment.right as BinaryExpression;
		if (binary != null && binary.left.value_type is ArrayType) {
			if (binary.operator == BinaryOperator.PLUS) {
				if (assignment.left.symbol_reference == binary.left.symbol_reference) {
					return true;
				}
			}
		}

		return false;
	}

	public override void visit_assignment (Assignment assignment) {
		if (!is_array_add (assignment)) {
			base.visit_assignment (assignment);
			return;
		}

694
		var binary = (BinaryExpression) assignment.right;
695

696
		var array = assignment.left;
697 698 699
		var array_type = (ArrayType) array.value_type;
		var element = binary.right;

700
		var array_var = array.symbol_reference;
701
		if (array_type.rank == 1 && array_var != null && array_var.is_internal_symbol ()
702
		    && (array_var is LocalVariable || array_var is Field)) {
703 704 705 706 707 708
			// valid array add
		} else {
			Report.error (assignment.source_reference, "Array concatenation not supported for public array variables and parameters");
			return;
		}

709
		var value_param = new Parameter ("value", element.target_type);
710 711

		var ccall = new CCodeFunctionCall (new CCodeIdentifier (generate_array_add_wrapper (array_type)));
712
		ccall.add_argument (new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, get_cvalue (array)));
713
		ccall.add_argument (new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, get_array_length_cexpression (array)));
714
		ccall.add_argument (new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, get_array_size_cvalue (array.target_value)));
715
		ccall.add_argument (handle_struct_argument (value_param, element, get_cvalue (element)));
716

717
		ccode.add_expression (ccall);
718
	}
719

720
	public override CCodeParameter generate_parameter (Parameter param, CCodeFile decl_space, Map<int,CCodeParameter> cparam_map, Map<int,CCodeExpression>? carg_map) {
Jürg Billeter's avatar
Jürg Billeter committed
721
		if (!(param.variable_type is ArrayType)) {
Jürg Billeter's avatar
Jürg Billeter committed
722
			return base.generate_parameter (param, decl_space, cparam_map, carg_map);
723 724
		}

725
		string ctypename = get_ccode_name (param.variable_type);
726 727
		string name = get_variable_cname (param.name);
		var array_type = (ArrayType) param.variable_type;
728

729
		if (array_type.fixed_length) {
730 731 732
			ctypename += "*";
		}

733 734 735
		if (param.direction != ParameterDirection.IN) {
			ctypename += "*";
		}
736

737
		var main_cparam = new CCodeParameter (name, ctypename);
738

739 740
		generate_type_declaration (array_type.element_type, decl_space);

741
		cparam_map.set (get_param_pos (get_ccode_pos (param)), main_cparam);
742
		if (carg_map != null) {
743
			carg_map.set (get_param_pos (get_ccode_pos (param)), get_variable_cexpression (param.name));
744 745
		}

746
		if (!array_type.fixed_length && get_ccode_array_length (param)) {
747
			var length_ctype = get_ccode_array_length_type (param) ?? get_ccode_array_length_type (array_type);
748
			if (param.direction != ParameterDirection.IN) {
749
				length_ctype = "%s*".printf (length_ctype);
750
			}
751

752
			for (int dim = 1; dim <= array_type.rank; dim++) {
753
				var cparam = new CCodeParameter (get_parameter_array_length_cname (param, dim), length_ctype);
754
				cparam_map.set (get_param_pos (get_ccode_array_length_pos (param) + 0.01 * dim), cparam);
755
				if (carg_map != null) {
756
					carg_map.set (get_param_pos (get_ccode_array_length_pos (param) + 0.01 * dim), get_variable_cexpression (cparam.name));
757 758 759
				}
			}
		}
Jürg Billeter's avatar
Jürg Billeter committed
760 761

		return main_cparam;
762
	}
763
}