1/* valaccodearraymodule.vala
2 *
3 * Copyright (C) 2006-2010  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
26public class Vala.CCodeArrayModule : CCodeMethodCallModule {
27	int next_array_dup_id = 0;
28	int next_array_add_id = 0;
29
30	void append_initializer_list (CCodeExpression name_cnode, InitializerList initializer_list, int rank, ref int i) {
31		foreach (Expression e in initializer_list.get_initializers ()) {
32			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				i++;
37			}
38		}
39	}
40
41	public override void visit_array_creation_expression (ArrayCreationExpression expr) {
42		var array_type = expr.target_type as ArrayType;
43		if (array_type != null && array_type.fixed_length) {
44			// no heap allocation for fixed-length arrays
45
46			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			int i = 0;
50
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			return;
58		}
59
60		CCodeFunctionCall gnew;
61		if (context.profile == Profile.POSIX) {
62			cfile.add_include ("stdlib.h");
63			gnew = new CCodeFunctionCall (new CCodeIdentifier ("calloc"));
64		} else {
65			gnew = new CCodeFunctionCall (new CCodeIdentifier ("g_new0"));
66			gnew.add_argument (new CCodeIdentifier (get_ccode_name (expr.element_type)));
67		}
68
69		bool first = true;
70		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			if (first) {
78				cexpr = csize;
79				first = false;
80			} else {
81				cexpr = new CCodeBinaryExpression (CCodeBinaryOperator.MUL, cexpr, csize);
82			}
83		}
84
85		// add extra item to have array NULL-terminated for all reference types
86		if (expr.element_type.type_symbol != null && expr.element_type.type_symbol.is_reference_type ()) {
87			cexpr = new CCodeBinaryExpression (CCodeBinaryOperator.PLUS, cexpr, new CCodeConstant ("1"));
88		}
89
90		gnew.add_argument (cexpr);
91
92		if (context.profile == Profile.POSIX) {
93			var csizeof = new CCodeFunctionCall (new CCodeIdentifier ("sizeof"));
94			csizeof.add_argument (new CCodeIdentifier (get_ccode_name (expr.element_type)));
95			gnew.add_argument (csizeof);
96		}
97
98		var temp_var = get_temp_variable (expr.value_type, true, expr);
99		var name_cnode = get_variable_cexpression (temp_var.name);
100		int i = 0;
101
102		emit_temp_var (temp_var);
103
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	public override string get_array_length_cname (string array_cname, int dim) {
114		return "%s_length%d".printf (array_cname, dim);
115	}
116
117	public override string get_variable_array_length_cname (Variable variable, int dim) {
118		string? length_cname = get_ccode_array_length_name (variable);
119		if (length_cname == null) {
120			length_cname = get_array_length_cname (get_ccode_name (variable), dim);
121		}
122		return (!) length_cname;
123	}
124
125	public override CCodeExpression get_array_length_cexpression (Expression array_expr, int dim = -1) {
126		return get_array_length_cvalue (array_expr.target_value, dim);
127	}
128
129	public override CCodeExpression get_array_length_cvalue (TargetValue value, int dim = -1) {
130		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		// dim == -1 => total size over all dimensions
137		if (dim == -1) {
138			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				return cexpr;
144			} else {
145				dim = 1;
146			}
147		}
148
149		List<CCodeExpression> size = ((GLibValue) value).array_length_cvalues;
150		if (size == null || size.size < dim) {
151			Report.error (array_type.source_reference, "internal: invalid array_length for given dimension");
152			return new CCodeInvalidExpression ();
153		}
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		int rank = indices.size;
164
165		var ccontainer = get_cvalue (expr.container);
166		var cindex = get_cvalue (indices[0]);
167		if (expr.container.symbol_reference is ArrayLengthField) {
168			/* Figure if cindex is a constant expression and calculate dim...*/
169			var lit = indices[0] as IntegerLiteral;
170			var memberaccess = expr.container as MemberAccess;
171			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		} else if (expr.container.symbol_reference is Constant && rank > 1) {
178			// access to element in a multi-dimensional array constant
179			var cindices = new ArrayList<CCodeExpression> ();
180			cindices.add (cindex);
181			for (int i = 1; i < rank; i++) {
182				cindices.add (get_cvalue (indices[i]));
183			}
184			set_cvalue (expr, new CCodeElementAccess.with_indices (ccontainer, cindices));
185		} else {
186			// access to element in an array
187			for (int i = 1; i < rank; i++) {
188				var cmul = new CCodeBinaryExpression (CCodeBinaryOperator.MUL, cindex, get_array_length_cexpression (expr.container, i + 1));
189				cindex = new CCodeBinaryExpression (CCodeBinaryOperator.PLUS, cmul, get_cvalue (indices[i]));
190				if (expr.container.is_constant ()) {
191					ccontainer = new CCodeUnaryExpression (CCodeUnaryOperator.POINTER_INDIRECTION, ccontainer);
192				}
193			}
194			set_cvalue (expr, new CCodeElementAccess (ccontainer, cindex));
195		}
196
197		expr.target_value.value_type = expr.value_type.copy ();
198		if (!expr.lvalue) {
199			expr.target_value = store_temp_value (expr.target_value, expr);
200		}
201		((GLibValue) expr.target_value).lvalue = true;
202	}
203
204	public override void visit_slice_expression (SliceExpression expr) {
205		var ccontainer = get_cvalue (expr.container);
206		var cstart = get_cvalue (expr.start);
207		var cstop = get_cvalue (expr.stop);
208
209		var cstartpointer = new CCodeBinaryExpression (CCodeBinaryOperator.PLUS, ccontainer, cstart);
210		var splicelen = new CCodeBinaryExpression (CCodeBinaryOperator.MINUS, cstop, cstart);
211
212		set_cvalue (expr, cstartpointer);
213		append_array_length (expr, splicelen);
214	}
215
216	void append_struct_array_free_loop (Struct st) {
217		var cforinit = new CCodeAssignment (new CCodeIdentifier ("i"), new CCodeConstant ("0"));
218		var cforcond = new CCodeBinaryExpression (CCodeBinaryOperator.LESS_THAN, new CCodeIdentifier ("i"), new CCodeIdentifier ("array_length"));
219		var cforiter = new CCodeAssignment (new CCodeIdentifier ("i"), new CCodeBinaryExpression (CCodeBinaryOperator.PLUS, new CCodeIdentifier ("i"), new CCodeConstant ("1")));
220		ccode.open_for (cforinit, cforcond, cforiter);
221
222		var cptrarray = new CCodeIdentifier ("array");
223		var cea = new CCodeElementAccess (cptrarray, new CCodeIdentifier ("i"));
224
225		var cfreecall = new CCodeFunctionCall (get_destroy_func_expression (new StructValueType (st)));
226		cfreecall.add_argument (new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, cea));
227		ccode.add_expression (cfreecall);
228
229		ccode.close ();
230	}
231
232	public override string? append_struct_array_free (Struct st) {
233		string cname = "_vala_%s_array_free".printf (get_ccode_name (st));
234
235		if (cfile.add_declaration (cname)) {
236			return cname;
237		}
238
239		var fun = new CCodeFunction (cname, "void");
240		fun.modifiers = CCodeModifiers.STATIC;
241		fun.add_parameter (new CCodeParameter ("array", "%s *".printf (get_ccode_name (st))));
242		fun.add_parameter (new CCodeParameter ("array_length", get_ccode_name (int_type)));
243
244		push_function (fun);
245
246		var ccondarr = new CCodeBinaryExpression (CCodeBinaryOperator.INEQUALITY, new CCodeIdentifier ("array"), new CCodeConstant ("NULL"));
247		ccode.open_if (ccondarr);
248
249		ccode.add_declaration (get_ccode_name (int_type), new CCodeVariableDeclarator ("i"));
250		append_struct_array_free_loop (st);
251
252		ccode.close ();
253
254		CCodeFunctionCall carrfree;
255		if (context.profile == Profile.POSIX) {
256			cfile.add_include ("stdlib.h");
257			carrfree = new CCodeFunctionCall (new CCodeIdentifier ("free"));
258		} else {
259			carrfree = new CCodeFunctionCall (new CCodeIdentifier ("g_free"));
260		}
261		carrfree.add_argument (new CCodeIdentifier ("array"));
262		ccode.add_expression (carrfree);
263
264		pop_function ();
265
266		cfile.add_function_declaration (fun);
267		cfile.add_function (fun);
268
269		return cname;
270	}
271
272	public override string? append_struct_array_destroy (Struct st) {
273		string cname = "_vala_%s_array_destroy".printf (get_ccode_name (st));
274
275		if (cfile.add_declaration (cname)) {
276			return cname;
277		}
278
279		var fun = new CCodeFunction (cname, "void");
280		fun.modifiers = CCodeModifiers.STATIC;
281		fun.add_parameter (new CCodeParameter ("array", "%s *".printf (get_ccode_name (st))));
282		fun.add_parameter (new CCodeParameter ("array_length", get_ccode_name (int_type)));
283
284		push_function (fun);
285
286		var ccondarr = new CCodeBinaryExpression (CCodeBinaryOperator.INEQUALITY, new CCodeIdentifier ("array"), new CCodeConstant ("NULL"));
287		ccode.open_if (ccondarr);
288
289		ccode.add_declaration (get_ccode_name (int_type), new CCodeVariableDeclarator ("i"));
290		append_struct_array_free_loop (st);
291
292		ccode.close ();
293
294		pop_function ();
295
296		cfile.add_function_declaration (fun);
297		cfile.add_function (fun);
298
299		return cname;
300	}
301
302	void append_vala_array_free_loop () {
303		var cforinit = new CCodeAssignment (new CCodeIdentifier ("i"), new CCodeConstant ("0"));
304		var cforcond = new CCodeBinaryExpression (CCodeBinaryOperator.LESS_THAN, new CCodeIdentifier ("i"), new CCodeIdentifier ("array_length"));
305		var cforiter = new CCodeAssignment (new CCodeIdentifier ("i"), new CCodeBinaryExpression (CCodeBinaryOperator.PLUS, new CCodeIdentifier ("i"), new CCodeConstant ("1")));
306		ccode.open_for (cforinit, cforcond, cforiter);
307
308		var cptrarray = new CCodeCastExpression (new CCodeIdentifier ("array"), "%s*".printf (get_ccode_name (pointer_type)));
309		var cea = new CCodeElementAccess (cptrarray, new CCodeIdentifier ("i"));
310
311		var cfreecond = new CCodeBinaryExpression (CCodeBinaryOperator.INEQUALITY, cea, new CCodeConstant ("NULL"));
312		ccode.open_if (cfreecond);
313
314		var cfreecall = new CCodeFunctionCall (new CCodeIdentifier ("destroy_func"));
315		cfreecall.add_argument (cea);
316		ccode.add_expression (cfreecall);
317
318		ccode.close ();
319	}
320
321	public override void append_vala_array_free () {
322		// _vala_array_destroy only frees elements but not the array itself
323		generate_type_declaration (delegate_target_destroy_type, cfile);
324
325		var fun = new CCodeFunction ("_vala_array_destroy", "void");
326		fun.modifiers = CCodeModifiers.STATIC;
327		fun.add_parameter (new CCodeParameter ("array", get_ccode_name (pointer_type)));
328		fun.add_parameter (new CCodeParameter ("array_length", get_ccode_name (int_type)));
329		fun.add_parameter (new CCodeParameter ("destroy_func", get_ccode_name (delegate_target_destroy_type)));
330
331		push_function (fun);
332
333		var ccondarr = new CCodeBinaryExpression (CCodeBinaryOperator.INEQUALITY, new CCodeIdentifier ("array"), new CCodeConstant ("NULL"));
334		var ccondfunc = new CCodeBinaryExpression (CCodeBinaryOperator.INEQUALITY, new CCodeIdentifier ("destroy_func"), new CCodeConstant ("NULL"));
335		ccode.open_if (new CCodeBinaryExpression (CCodeBinaryOperator.AND, ccondarr, ccondfunc));
336
337		ccode.add_declaration (get_ccode_name (int_type), new CCodeVariableDeclarator ("i"));
338		append_vala_array_free_loop ();
339
340		ccode.close ();
341
342		pop_function ();
343
344		cfile.add_function_declaration (fun);
345		cfile.add_function (fun);
346
347		// _vala_array_free frees elements and array
348
349		fun = new CCodeFunction ("_vala_array_free", "void");
350		fun.modifiers = CCodeModifiers.STATIC;
351		fun.add_parameter (new CCodeParameter ("array", get_ccode_name (pointer_type)));
352		fun.add_parameter (new CCodeParameter ("array_length", get_ccode_name (int_type)));
353		fun.add_parameter (new CCodeParameter ("destroy_func", get_ccode_name (delegate_target_destroy_type)));
354
355		push_function (fun);
356
357		// call _vala_array_destroy to free the array elements
358		var ccall = new CCodeFunctionCall (new CCodeIdentifier ("_vala_array_destroy"));
359		ccall.add_argument (new CCodeIdentifier ("array"));
360		ccall.add_argument (new CCodeIdentifier ("array_length"));
361		ccall.add_argument (new CCodeIdentifier ("destroy_func"));
362		ccode.add_expression (ccall);
363
364		CCodeFunctionCall carrfree;
365		if (context.profile == Profile.POSIX) {
366			cfile.add_include ("stdlib.h");
367			carrfree = new CCodeFunctionCall (new CCodeIdentifier ("free"));
368		} else {
369			carrfree = new CCodeFunctionCall (new CCodeIdentifier ("g_free"));
370		}
371		carrfree.add_argument (new CCodeIdentifier ("array"));
372		ccode.add_expression (carrfree);
373
374		pop_function ();
375
376		cfile.add_function_declaration (fun);
377		cfile.add_function (fun);
378	}
379
380	public override void append_vala_array_move () {
381		cfile.add_include ("string.h");
382
383		// assumes that overwritten array elements are null before invocation
384		// FIXME will leak memory if that's not the case
385		var fun = new CCodeFunction ("_vala_array_move", "void");
386		fun.modifiers = CCodeModifiers.STATIC;
387		fun.add_parameter (new CCodeParameter ("array", get_ccode_name (pointer_type)));
388		fun.add_parameter (new CCodeParameter ("element_size", get_ccode_name (size_t_type)));
389		fun.add_parameter (new CCodeParameter ("src", get_ccode_name (int_type)));
390		fun.add_parameter (new CCodeParameter ("dest", get_ccode_name (int_type)));
391		fun.add_parameter (new CCodeParameter ("length", get_ccode_name (int_type)));
392
393		push_function (fun);
394
395		var array = new CCodeCastExpression (new CCodeIdentifier ("array"), "char*");
396		var element_size = new CCodeIdentifier ("element_size");
397		var length = new CCodeIdentifier ("length");
398		var src = new CCodeIdentifier ("src");
399		var src_end = new CCodeBinaryExpression (CCodeBinaryOperator.PLUS, src, length);
400		var dest = new CCodeIdentifier ("dest");
401		var dest_end = new CCodeBinaryExpression (CCodeBinaryOperator.PLUS, dest, length);
402		var src_address = new CCodeBinaryExpression (CCodeBinaryOperator.PLUS, array, new CCodeBinaryExpression (CCodeBinaryOperator.MUL, src, element_size));
403		var dest_address = new CCodeBinaryExpression (CCodeBinaryOperator.PLUS, array, new CCodeBinaryExpression (CCodeBinaryOperator.MUL, dest, element_size));
404		var dest_end_address = new CCodeBinaryExpression (CCodeBinaryOperator.PLUS, array, new CCodeBinaryExpression (CCodeBinaryOperator.MUL, dest_end, element_size));
405
406		var ccall = new CCodeFunctionCall (new CCodeIdentifier ("memmove"));
407		ccall.add_argument (dest_address);
408		ccall.add_argument (src_address);
409		ccall.add_argument (new CCodeBinaryExpression (CCodeBinaryOperator.MUL, length, element_size));
410		ccode.add_expression (ccall);
411
412		ccode.open_if (new CCodeBinaryExpression (CCodeBinaryOperator.AND, new CCodeBinaryExpression (CCodeBinaryOperator.LESS_THAN, src, dest), new CCodeBinaryExpression (CCodeBinaryOperator.GREATER_THAN, src_end, dest)));
413
414		var czero1 = new CCodeFunctionCall (new CCodeIdentifier ("memset"));
415		czero1.add_argument (src_address);
416		czero1.add_argument (new CCodeConstant ("0"));
417		czero1.add_argument (new CCodeBinaryExpression (CCodeBinaryOperator.MUL, new CCodeBinaryExpression (CCodeBinaryOperator.MINUS, dest, src), element_size));
418		ccode.add_expression (czero1);
419
420		ccode.else_if (new CCodeBinaryExpression (CCodeBinaryOperator.AND, new CCodeBinaryExpression (CCodeBinaryOperator.GREATER_THAN, src, dest), new CCodeBinaryExpression (CCodeBinaryOperator.LESS_THAN, src, dest_end)));
421
422		var czero2 = new CCodeFunctionCall (new CCodeIdentifier ("memset"));
423		czero2.add_argument (dest_end_address);
424		czero2.add_argument (new CCodeConstant ("0"));
425		czero2.add_argument (new CCodeBinaryExpression (CCodeBinaryOperator.MUL, new CCodeBinaryExpression (CCodeBinaryOperator.MINUS, src, dest), element_size));
426		ccode.add_expression (czero2);
427
428		ccode.else_if (new CCodeBinaryExpression (CCodeBinaryOperator.INEQUALITY, src, dest));
429
430		var czero3 = new CCodeFunctionCall (new CCodeIdentifier ("memset"));
431		czero3.add_argument (src_address);
432		czero3.add_argument (new CCodeConstant ("0"));
433		czero3.add_argument (new CCodeBinaryExpression (CCodeBinaryOperator.MUL, length, element_size));
434		ccode.add_expression (czero3);
435
436		ccode.close ();
437
438		pop_function ();
439
440		cfile.add_function_declaration (fun);
441		cfile.add_function (fun);
442	}
443
444	public override void append_vala_array_length () {
445		var fun = new CCodeFunction ("_vala_array_length", get_ccode_name (int_type));
446		fun.modifiers = CCodeModifiers.STATIC;
447		fun.add_parameter (new CCodeParameter ("array", get_ccode_name (pointer_type)));
448
449		push_function (fun);
450
451		ccode.add_declaration (get_ccode_name (int_type), new CCodeVariableDeclarator ("length", new CCodeConstant ("0")));
452
453		// return 0 if the array is NULL
454		// avoids an extra NULL check on the caller side
455		var array_check = new CCodeIdentifier ("array");
456		ccode.open_if (array_check);
457
458		var array_element_check = new CCodeElementAccess (new CCodeCastExpression (new CCodeIdentifier ("array"), "%s*".printf (get_ccode_name (pointer_type))), new CCodeConstant ("length"));
459		ccode.open_while (array_element_check);
460		ccode.add_expression (new CCodeUnaryExpression (CCodeUnaryOperator.POSTFIX_INCREMENT, new CCodeIdentifier ("length")));
461		ccode.close ();
462
463		ccode.close ();
464
465		ccode.add_return (new CCodeIdentifier ("length"));
466
467		pop_function ();
468
469		cfile.add_function_declaration (fun);
470		cfile.add_function (fun);
471	}
472
473	public override TargetValue? copy_value (TargetValue value, CodeNode node) {
474		var type = value.value_type;
475		var cexpr = get_cvalue_ (value);
476
477		if (type is ArrayType) {
478			var array_type = (ArrayType) type;
479
480			if (!array_type.fixed_length) {
481				return base.copy_value (value, node);
482			}
483
484			var temp_value = create_temp_value (type, false, node);
485
486			var copy_call = new CCodeFunctionCall (new CCodeIdentifier (generate_array_copy_wrapper (array_type)));
487			copy_call.add_argument (cexpr);
488			copy_call.add_argument (get_cvalue_ (temp_value));
489			ccode.add_expression (copy_call);
490
491			return temp_value;
492		} else {
493			return base.copy_value (value, node);
494		}
495	}
496
497	public override CCodeExpression? get_dup_func_expression (DataType type, SourceReference? source_reference, bool is_chainup) {
498		if (type is ArrayType) {
499			var array_type = (ArrayType) type;
500			// fixed length arrays use different code
501			// generated by overridden get_ref_cexpression method
502			assert (!array_type.fixed_length);
503			return new CCodeIdentifier (generate_array_dup_wrapper (array_type));
504		} else {
505			return base.get_dup_func_expression (type, source_reference, is_chainup);
506		}
507	}
508
509	public override CCodeExpression destroy_value (TargetValue value, bool is_macro_definition = false) {
510		unowned ArrayType? array_type = value.value_type as ArrayType;
511
512		if (array_type != null && array_type.fixed_length) {
513			unowned Struct? st = array_type.element_type.type_symbol as Struct;
514			if (st != null && !array_type.element_type.nullable) {
515				var ccall = new CCodeFunctionCall (new CCodeIdentifier (append_struct_array_destroy (st)));
516				ccall.add_argument (get_cvalue_ (value));
517				ccall.add_argument (get_ccodenode (array_type.length));
518				return ccall;
519			}
520
521			requires_array_free = true;
522			generate_type_declaration (delegate_target_destroy_type, cfile);
523
524			var ccall = new CCodeFunctionCall (new CCodeIdentifier ("_vala_array_destroy"));
525			ccall.add_argument (get_cvalue_ (value));
526			ccall.add_argument (get_ccodenode (array_type.length));
527			ccall.add_argument (new CCodeCastExpression (get_destroy_func_expression (array_type.element_type), get_ccode_name (delegate_target_destroy_type)));
528
529			return ccall;
530		} else {
531			return base.destroy_value (value, is_macro_definition);
532		}
533	}
534
535	string generate_array_dup_wrapper (ArrayType array_type) {
536		string dup_func = "_vala_array_dup%d".printf (++next_array_dup_id);
537
538		if (!add_wrapper (dup_func)) {
539			// wrapper already defined
540			return dup_func;
541		}
542
543		// declaration
544
545		var function = new CCodeFunction (dup_func, get_ccode_name (array_type));
546		function.modifiers = CCodeModifiers.STATIC;
547
548		function.add_parameter (new CCodeParameter ("self", get_ccode_name (array_type)));
549		// total length over all dimensions
550		function.add_parameter (new CCodeParameter ("length", get_ccode_name (int_type)));
551		if (array_type.element_type is GenericType) {
552			// dup function array elements
553			string func_name = "%s_dup_func".printf (((GenericType) array_type.element_type).type_parameter.name.ascii_down ());
554			function.add_parameter (new CCodeParameter (func_name, "GBoxedCopyFunc"));
555		}
556
557		// definition
558
559		push_context (new EmitContext ());
560		push_function (function);
561
562		if (requires_copy (array_type.element_type)) {
563			var cvardecl = new CCodeVariableDeclarator ("result");
564			CCodeFunctionCall gnew;
565			if (context.profile == Profile.POSIX) {
566				cfile.add_include ("stdlib.h");
567				gnew = new CCodeFunctionCall (new CCodeIdentifier ("calloc"));
568			} else {
569				gnew = new CCodeFunctionCall (new CCodeIdentifier ("g_new0"));
570				gnew.add_argument (new CCodeIdentifier (get_ccode_name (array_type.element_type)));
571			}
572
573			CCodeExpression length_expr = new CCodeIdentifier ("length");
574			CCodeBinaryOperator length_check_op;
575			// add extra item to have array NULL-terminated for all reference types
576			if (array_type.element_type.type_symbol != null && array_type.element_type.type_symbol.is_reference_type ()) {
577				length_expr = new CCodeBinaryExpression (CCodeBinaryOperator.PLUS, length_expr, new CCodeConstant ("1"));
578				length_check_op = CCodeBinaryOperator.GREATER_THAN_OR_EQUAL;
579			} else {
580				length_check_op = CCodeBinaryOperator.GREATER_THAN;
581			}
582			gnew.add_argument (length_expr);
583
584			if (context.profile == Profile.POSIX) {
585				var csizeof = new CCodeFunctionCall (new CCodeIdentifier ("sizeof"));
586				csizeof.add_argument (new CCodeIdentifier (get_ccode_name (array_type.element_type)));
587				gnew.add_argument (csizeof);
588			}
589
590			// only attempt to dup if length >=/> 0, this deals with negative lengths and returns NULL
591			var length_check = new CCodeBinaryExpression (length_check_op, new CCodeIdentifier ("length"), new CCodeConstant ("0"));
592			ccode.open_if (length_check);
593
594			ccode.add_declaration (get_ccode_name (array_type), cvardecl);
595			ccode.add_assignment (new CCodeIdentifier ("result"), gnew);
596
597			ccode.add_declaration (get_ccode_name (int_type), new CCodeVariableDeclarator ("i"));
598
599			ccode.open_for (new CCodeAssignment (new CCodeIdentifier ("i"), new CCodeConstant ("0")),
600			                   new CCodeBinaryExpression (CCodeBinaryOperator.LESS_THAN, new CCodeIdentifier ("i"), new CCodeIdentifier ("length")),
601			                   new CCodeUnaryExpression (CCodeUnaryOperator.POSTFIX_INCREMENT, new CCodeIdentifier ("i")));
602
603			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)));
604			ccode.close ();
605
606			ccode.add_return (new CCodeIdentifier ("result"));
607
608			ccode.close ();
609			ccode.add_return (new CCodeConstant ("NULL"));
610		} else {
611			// only dup if length > 0, this deals with negative lengths and returns NULL
612			var length_check = new CCodeBinaryExpression (CCodeBinaryOperator.GREATER_THAN, new CCodeIdentifier ("length"), new CCodeConstant ("0"));
613			ccode.open_if (length_check);
614
615			var sizeof_call = new CCodeFunctionCall (new CCodeIdentifier ("sizeof"));
616			sizeof_call.add_argument (new CCodeIdentifier (get_ccode_name (array_type.element_type)));
617			var length_expr = new CCodeIdentifier ("length");
618
619			if (context.profile == Profile.POSIX) {
620				cfile.add_include ("stdlib.h");
621				cfile.add_include ("string.h");
622
623				var alloc = new CCodeFunctionCall (new CCodeIdentifier ("calloc"));
624				alloc.add_argument (length_expr);
625				alloc.add_argument (sizeof_call);
626
627				var cvardecl = new CCodeVariableDeclarator ("result");
628				ccode.add_declaration (get_ccode_name (array_type), cvardecl);
629				ccode.add_assignment (new CCodeIdentifier ("result"), alloc);
630
631				var dup_call = new CCodeFunctionCall (new CCodeIdentifier ("memcpy"));
632				dup_call.add_argument (new CCodeIdentifier ("result"));
633				dup_call.add_argument (new CCodeIdentifier ("self"));
634				dup_call.add_argument (new CCodeBinaryExpression (CCodeBinaryOperator.MUL, length_expr, sizeof_call));
635				ccode.add_expression (dup_call);
636
637				ccode.add_return (new CCodeIdentifier ("result"));
638			} else {
639				var dup_call = new CCodeFunctionCall (new CCodeIdentifier ("g_memdup"));
640				dup_call.add_argument (new CCodeIdentifier ("self"));
641				dup_call.add_argument (new CCodeBinaryExpression (CCodeBinaryOperator.MUL, length_expr, sizeof_call));
642
643				ccode.add_return (dup_call);
644			}
645
646			ccode.close ();
647			ccode.add_return (new CCodeConstant ("NULL"));
648		}
649
650		// append to file
651
652		cfile.add_function_declaration (function);
653		cfile.add_function (function);
654
655		pop_context ();
656
657		return dup_func;
658	}
659
660	string generate_array_copy_wrapper (ArrayType array_type) {
661		string dup_func = "_vala_array_copy%d".printf (++next_array_dup_id);
662
663		if (!add_wrapper (dup_func)) {
664			// wrapper already defined
665			return dup_func;
666		}
667
668		// declaration
669
670		var function = new CCodeFunction (dup_func, "void");
671		function.modifiers = CCodeModifiers.STATIC;
672
673		function.add_parameter (new CCodeParameter ("self", "%s *".printf (get_ccode_name (array_type))));
674		function.add_parameter (new CCodeParameter ("dest", "%s *".printf (get_ccode_name (array_type))));
675
676		// definition
677
678		push_context (new EmitContext ());
679		push_function (function);
680
681		if (requires_copy (array_type.element_type)) {
682			ccode.add_declaration (get_ccode_name (int_type), new CCodeVariableDeclarator ("i"));
683
684			ccode.open_for (new CCodeAssignment (new CCodeIdentifier ("i"), new CCodeConstant ("0")),
685			                   new CCodeBinaryExpression (CCodeBinaryOperator.LESS_THAN, new CCodeIdentifier ("i"), get_ccodenode (array_type.length)),
686			                   new CCodeUnaryExpression (CCodeUnaryOperator.POSTFIX_INCREMENT, new CCodeIdentifier ("i")));
687
688
689			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)));
690		} else {
691			cfile.add_include ("string.h");
692
693			var dup_call = new CCodeFunctionCall (new CCodeIdentifier ("memcpy"));
694			dup_call.add_argument (new CCodeIdentifier ("dest"));
695			dup_call.add_argument (new CCodeIdentifier ("self"));
696
697			var sizeof_call = new CCodeFunctionCall (new CCodeIdentifier ("sizeof"));
698			sizeof_call.add_argument (new CCodeIdentifier (get_ccode_name (array_type.element_type)));
699			dup_call.add_argument (new CCodeBinaryExpression (CCodeBinaryOperator.MUL, get_ccodenode (array_type.length), sizeof_call));
700
701			ccode.add_expression (dup_call);
702		}
703
704		// append to file
705
706		cfile.add_function_declaration (function);
707		cfile.add_function (function);
708
709		pop_context ();
710
711		return dup_func;
712	}
713
714	string generate_array_add_wrapper (ArrayType array_type) {
715		string add_func = "_vala_array_add%d".printf (++next_array_add_id);
716
717		if (!add_wrapper (add_func)) {
718			// wrapper already defined
719			return add_func;
720		}
721
722		var function = new CCodeFunction (add_func, "void");
723		function.modifiers = CCodeModifiers.STATIC;
724
725		function.add_parameter (new CCodeParameter ("array", "%s *".printf (get_ccode_name (array_type))));
726		function.add_parameter (new CCodeParameter ("length", "%s*".printf (get_ccode_name (int_type))));
727		function.add_parameter (new CCodeParameter ("size", "%s*".printf (get_ccode_name (int_type))));
728
729		push_function (function);
730
731		string typename = get_ccode_name (array_type.element_type);
732		CCodeExpression value = new CCodeIdentifier ("value");
733		if (array_type.element_type.is_real_struct_type ()) {
734			if (!array_type.element_type.nullable || !array_type.element_type.value_owned) {
735				typename = "const " + typename;
736			}
737			if (!array_type.element_type.nullable) {
738				typename += "*";
739				value = new CCodeUnaryExpression (CCodeUnaryOperator.POINTER_INDIRECTION, value);
740			}
741		}
742		function.add_parameter (new CCodeParameter ("value", typename));
743
744		var array = new CCodeUnaryExpression (CCodeUnaryOperator.POINTER_INDIRECTION, new CCodeIdentifier ("array"));
745		var length = new CCodeUnaryExpression (CCodeUnaryOperator.POINTER_INDIRECTION, new CCodeIdentifier ("length"));
746		var size = new CCodeUnaryExpression (CCodeUnaryOperator.POINTER_INDIRECTION, new CCodeIdentifier ("size"));
747
748		CCodeFunctionCall renew_call;
749		if (context.profile == Profile.POSIX) {
750			cfile.add_include ("stdlib.h");
751			renew_call = new CCodeFunctionCall (new CCodeIdentifier ("realloc"));
752			renew_call.add_argument (array);
753		} else {
754			renew_call = new CCodeFunctionCall (new CCodeIdentifier ("g_renew"));
755			renew_call.add_argument (new CCodeIdentifier (get_ccode_name (array_type.element_type)));
756			renew_call.add_argument (array);
757		}
758		CCodeExpression renew_call_size;
759		if (array_type.element_type.is_reference_type_or_type_parameter ()) {
760			// NULL terminate array
761			renew_call_size = new CCodeBinaryExpression (CCodeBinaryOperator.PLUS, size, new CCodeConstant ("1"));
762		} else {
763			renew_call_size = size;
764		}
765		if (context.profile == Profile.POSIX) {
766			var csizeof = new CCodeFunctionCall (new CCodeIdentifier ("sizeof"));
767			csizeof.add_argument (new CCodeIdentifier (get_ccode_name (array_type.element_type)));
768			renew_call_size = new CCodeBinaryExpression (CCodeBinaryOperator.MUL, size, csizeof);
769		}
770		renew_call.add_argument (renew_call_size);
771
772		var csizecheck = new CCodeBinaryExpression (CCodeBinaryOperator.EQUALITY, length, size);
773		ccode.open_if (csizecheck);
774		ccode.add_assignment (size, new CCodeConditionalExpression (size, new CCodeBinaryExpression (CCodeBinaryOperator.MUL, new CCodeConstant ("2"), size), new CCodeConstant ("4")));
775		ccode.add_assignment (array, renew_call);
776		ccode.close ();
777
778		ccode.add_assignment (new CCodeElementAccess (array, new CCodeUnaryExpression (CCodeUnaryOperator.POSTFIX_INCREMENT, length)), value);
779
780		if (array_type.element_type.is_reference_type_or_type_parameter ()) {
781			// NULL terminate array
782			ccode.add_assignment (new CCodeElementAccess (array, length), new CCodeConstant ("NULL"));
783		}
784
785		pop_function ();
786
787		cfile.add_function_declaration (function);
788		cfile.add_function (function);
789
790		return add_func;
791	}
792
793	bool is_array_add (Assignment assignment) {
794		var binary = assignment.right as BinaryExpression;
795		if (binary != null && binary.left.value_type is ArrayType) {
796			if (binary.operator == BinaryOperator.PLUS) {
797				if (assignment.left.symbol_reference == binary.left.symbol_reference) {
798					return true;
799				}
800			}
801		}
802
803		return false;
804	}
805
806	public override void visit_assignment (Assignment assignment) {
807		if (!is_array_add (assignment)) {
808			base.visit_assignment (assignment);
809			return;
810		}
811
812		var binary = (BinaryExpression) assignment.right;
813
814		var array = assignment.left;
815		var array_type = (ArrayType) array.value_type;
816		var element = binary.right;
817
818		var array_var = array.symbol_reference;
819		if (array_type.rank == 1 && array_var != null && array_var.is_internal_symbol ()
820		    && (array_var is LocalVariable || array_var is Field)) {
821			// valid array add
822		} else {
823			Report.error (assignment.source_reference, "Array concatenation not supported for public array variables and parameters");
824			return;
825		}
826
827		var value_param = new Parameter ("value", element.target_type);
828
829		var ccall = new CCodeFunctionCall (new CCodeIdentifier (generate_array_add_wrapper (array_type)));
830		ccall.add_argument (new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, get_cvalue (array)));
831		ccall.add_argument (new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, get_array_length_cexpression (array)));
832		ccall.add_argument (new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, get_array_size_cvalue (array.target_value)));
833		ccall.add_argument (handle_struct_argument (value_param, element, get_cvalue (element)));
834
835		ccode.add_expression (ccall);
836	}
837
838	public override CCodeParameter generate_parameter (Parameter param, CCodeFile decl_space, Map<int,CCodeParameter> cparam_map, Map<int,CCodeExpression>? carg_map) {
839		if (param.params_array || !(param.variable_type is ArrayType)) {
840			return base.generate_parameter (param, decl_space, cparam_map, carg_map);
841		}
842
843		string ctypename = get_ccode_name (param.variable_type);
844		string name = get_ccode_name (param);
845		var array_type = (ArrayType) param.variable_type;
846
847		if (array_type.fixed_length) {
848			ctypename += "*";
849		}
850
851		if (param.direction != ParameterDirection.IN) {
852			ctypename += "*";
853		}
854
855		var main_cparam = new CCodeParameter (name, ctypename);
856
857		generate_type_declaration (array_type.element_type, decl_space);
858
859		cparam_map.set (get_param_pos (get_ccode_pos (param)), main_cparam);
860		if (carg_map != null) {
861			carg_map.set (get_param_pos (get_ccode_pos (param)), get_parameter_cexpression (param));
862		}
863
864		if (!array_type.fixed_length && get_ccode_array_length (param)) {
865			var length_ctype = get_ccode_array_length_type (param);
866			if (param.direction != ParameterDirection.IN) {
867				length_ctype = "%s*".printf (length_ctype);
868			}
869
870			for (int dim = 1; dim <= array_type.rank; dim++) {
871				var cparam = new CCodeParameter (get_variable_array_length_cname (param, dim), length_ctype);
872				cparam_map.set (get_param_pos (get_ccode_array_length_pos (param) + 0.01 * dim), cparam);
873				if (carg_map != null) {
874					carg_map.set (get_param_pos (get_ccode_array_length_pos (param) + 0.01 * dim), get_cexpression (cparam.name));
875				}
876			}
877		}
878
879		return main_cparam;
880	}
881
882	public override void append_params_array (Method m) {
883		var local = m.params_array_var;
884		var array_type = (ArrayType) local.variable_type;
885
886		var local_length = new LocalVariable (array_type.length_type.copy (), get_array_length_cname (local.name, 1), null, local.source_reference);
887		var local_size = new LocalVariable (array_type.length_type.copy (), get_array_size_cname (get_local_cname (local)));
888
889		CCodeFunctionCall gnew;
890		if (context.profile == Profile.POSIX) {
891			cfile.add_include ("stdlib.h");
892			gnew = new CCodeFunctionCall (new CCodeIdentifier ("calloc"));
893		} else {
894			gnew = new CCodeFunctionCall (new CCodeIdentifier ("g_new0"));
895			gnew.add_argument (new CCodeIdentifier (get_ccode_name (array_type.element_type)));
896		}
897
898		CCodeExpression length_expr = get_local_cexpression (local_length);
899		// add extra item to have array NULL-terminated for all reference types
900		if (array_type.element_type.type_symbol != null && array_type.element_type.type_symbol.is_reference_type ()) {
901			length_expr = new CCodeBinaryExpression (CCodeBinaryOperator.PLUS, length_expr, new CCodeConstant ("1"));
902		}
903		gnew.add_argument (length_expr);
904		if (context.profile == Profile.POSIX) {
905			var csizeof = new CCodeFunctionCall (new CCodeIdentifier ("sizeof"));
906			csizeof.add_argument (new CCodeIdentifier (get_ccode_name (array_type.element_type)));
907			gnew.add_argument (csizeof);
908		}
909		ccode.add_assignment (get_local_cexpression (local), gnew);
910
911		var element = new LocalVariable (array_type.element_type.copy (), "_%s_element".printf (get_ccode_name (local)), null, local.source_reference);
912		emit_temp_var (element);
913
914		if (context.profile == Profile.POSIX) {
915			cfile.add_include ("stdarg.h");
916		}
917
918		if (!(m is CreationMethod)) {
919			ccode.add_declaration ("va_list", new CCodeVariableDeclarator ("_va_list_%s".printf (get_ccode_name (local))));
920			var vastart = new CCodeFunctionCall (new CCodeIdentifier ("va_start"));
921			vastart.add_argument (new CCodeIdentifier ("_va_list_%s".printf (get_ccode_name (local))));
922			vastart.add_argument (new CCodeIdentifier ("_first_%s".printf (get_ccode_name (local))));
923			ccode.add_expression (vastart);
924		}
925
926		ccode.add_assignment (get_local_cexpression (element), new CCodeIdentifier ("_first_%s".printf (get_ccode_name (local))));
927		ccode.open_while (new CCodeBinaryExpression (CCodeBinaryOperator.INEQUALITY, get_local_cexpression (element), new CCodeConstant ("NULL")));
928
929		var vaarg = new CCodeFunctionCall (new CCodeIdentifier ("va_arg"));
930		vaarg.add_argument (new CCodeIdentifier ("_va_list_%s".printf (get_ccode_name (local))));
931		vaarg.add_argument (new CCodeIdentifier (get_ccode_name (array_type.element_type)));
932
933		var ccall = new CCodeFunctionCall (new CCodeIdentifier (generate_array_add_wrapper (array_type)));
934		ccall.add_argument (new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, get_local_cexpression (local)));
935		ccall.add_argument (new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, get_local_cexpression (local_length)));
936		ccall.add_argument (new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, get_local_cexpression (local_size)));
937		ccall.add_argument (get_local_cexpression (element));
938
939		ccode.add_expression (ccall);
940		ccode.add_assignment (get_local_cexpression (element), vaarg);
941
942		ccode.close ();
943
944		var vaend = new CCodeFunctionCall (new CCodeIdentifier ("va_end"));
945		vaend.add_argument (new CCodeIdentifier ("_va_list_%s".printf (get_ccode_name (local))));
946		ccode.add_expression (vaend);
947	}
948}
949