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