codegen: Fix array size variable on assignment
[vala-lang.git] / codegen / valadovabasemodule.vala
blob3ddf0e7195341f1c4873edbd1062957d4c2c2bb5
1 /* valadovabasemodule.vala
3 * Copyright (C) 2006-2010 Jürg Billeter
4 * Copyright (C) 2006-2008 Raffaele Sandrini
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.
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.
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
20 * Author:
21 * Jürg Billeter <j@bitron.ch>
22 * Raffaele Sandrini <raffaele@sandrini.ch>
25 /**
26 * Code visitor generating C Code.
28 public abstract class Vala.DovaBaseModule : CodeGenerator {
29 public class EmitContext {
30 public Symbol? current_symbol;
31 public ArrayList<Symbol> symbol_stack = new ArrayList<Symbol> ();
32 public TryStatement current_try;
33 public CCodeFunction ccode;
34 public ArrayList<CCodeFunction> ccode_stack = new ArrayList<CCodeFunction> ();
35 public ArrayList<LocalVariable> temp_ref_vars = new ArrayList<LocalVariable> ();
36 public int next_temp_var_id;
37 public Map<string,string> variable_name_map = new HashMap<string,string> (str_hash, str_equal);
39 public EmitContext (Symbol? symbol = null) {
40 current_symbol = symbol;
43 public void push_symbol (Symbol symbol) {
44 symbol_stack.add (current_symbol);
45 current_symbol = symbol;
48 public void pop_symbol () {
49 current_symbol = symbol_stack[symbol_stack.size - 1];
50 symbol_stack.remove_at (symbol_stack.size - 1);
54 public CodeContext context { get; set; }
56 public Symbol root_symbol;
58 public EmitContext emit_context = new EmitContext ();
60 List<EmitContext> emit_context_stack = new ArrayList<EmitContext> ();
62 public Symbol current_symbol { get { return emit_context.current_symbol; } }
64 public TryStatement current_try {
65 get { return emit_context.current_try; }
66 set { emit_context.current_try = value; }
69 public TypeSymbol? current_type_symbol {
70 get {
71 var sym = current_symbol;
72 while (sym != null) {
73 if (sym is TypeSymbol) {
74 return (TypeSymbol) sym;
76 sym = sym.parent_symbol;
78 return null;
82 public Class? current_class {
83 get { return current_type_symbol as Class; }
86 public Method? current_method {
87 get {
88 var sym = current_symbol;
89 while (sym is Block) {
90 sym = sym.parent_symbol;
92 return sym as Method;
96 public PropertyAccessor? current_property_accessor {
97 get {
98 var sym = current_symbol;
99 while (sym is Block) {
100 sym = sym.parent_symbol;
102 return sym as PropertyAccessor;
106 public DataType? current_return_type {
107 get {
108 var m = current_method;
109 if (m != null) {
110 return m.return_type;
113 var acc = current_property_accessor;
114 if (acc != null) {
115 if (acc.readable) {
116 return acc.value_type;
117 } else {
118 return void_type;
122 return null;
126 public Block? current_closure_block {
127 get {
128 return next_closure_block (current_symbol);
132 public unowned Block? next_closure_block (Symbol sym) {
133 unowned Block block = null;
134 while (true) {
135 block = sym as Block;
136 if (!(sym is Block || sym is Method)) {
137 // no closure block
138 break;
140 if (block != null && block.captured) {
141 // closure block found
142 break;
144 sym = sym.parent_symbol;
146 return block;
149 public CCodeFile header_file;
150 public CCodeFile cfile;
152 string? csource_filename;
154 public CCodeFunction ccode { get { return emit_context.ccode; } }
156 /* temporary variables that own their content */
157 public ArrayList<LocalVariable> temp_ref_vars { get { return emit_context.temp_ref_vars; } }
158 /* (constant) hash table with all reserved identifiers in the generated code */
159 Set<string> reserved_identifiers;
161 public List<Field> static_fields = new ArrayList<Field> ();
163 public int next_temp_var_id {
164 get { return emit_context.next_temp_var_id; }
165 set { emit_context.next_temp_var_id = value; }
168 public int next_wrapper_id = 0;
169 public bool in_creation_method { get { return current_method is CreationMethod; } }
170 int next_block_id = 0;
171 Map<Block,int> block_map = new HashMap<Block,int> ();
173 public DataType void_type = new VoidType ();
174 public DataType bool_type;
175 public DataType char_type;
176 public DataType int_type;
177 public DataType uint_type;
178 public DataType string_type;
179 public DataType float_type;
180 public DataType double_type;
181 public Class object_class;
182 public Class type_class;
183 public Class value_class;
184 public Class string_class;
185 public Class array_class;
186 public Class delegate_class;
187 public Class error_class;
189 Set<Symbol> generated_external_symbols;
191 public Map<string,string> variable_name_map { get { return emit_context.variable_name_map; } }
193 public DovaBaseModule () {
194 reserved_identifiers = new HashSet<string> (str_hash, str_equal);
196 // C99 keywords
197 reserved_identifiers.add ("_Bool");
198 reserved_identifiers.add ("_Complex");
199 reserved_identifiers.add ("_Imaginary");
200 reserved_identifiers.add ("auto");
201 reserved_identifiers.add ("break");
202 reserved_identifiers.add ("case");
203 reserved_identifiers.add ("char");
204 reserved_identifiers.add ("const");
205 reserved_identifiers.add ("continue");
206 reserved_identifiers.add ("default");
207 reserved_identifiers.add ("do");
208 reserved_identifiers.add ("double");
209 reserved_identifiers.add ("else");
210 reserved_identifiers.add ("enum");
211 reserved_identifiers.add ("extern");
212 reserved_identifiers.add ("float");
213 reserved_identifiers.add ("for");
214 reserved_identifiers.add ("goto");
215 reserved_identifiers.add ("if");
216 reserved_identifiers.add ("inline");
217 reserved_identifiers.add ("int");
218 reserved_identifiers.add ("long");
219 reserved_identifiers.add ("register");
220 reserved_identifiers.add ("restrict");
221 reserved_identifiers.add ("return");
222 reserved_identifiers.add ("short");
223 reserved_identifiers.add ("signed");
224 reserved_identifiers.add ("sizeof");
225 reserved_identifiers.add ("static");
226 reserved_identifiers.add ("struct");
227 reserved_identifiers.add ("switch");
228 reserved_identifiers.add ("typedef");
229 reserved_identifiers.add ("union");
230 reserved_identifiers.add ("unsigned");
231 reserved_identifiers.add ("void");
232 reserved_identifiers.add ("volatile");
233 reserved_identifiers.add ("while");
235 // reserved for Vala naming conventions
236 reserved_identifiers.add ("result");
237 reserved_identifiers.add ("this");
240 public override void emit (CodeContext context) {
241 this.context = context;
243 root_symbol = context.root;
245 bool_type = new BooleanType ((Struct) root_symbol.scope.lookup ("bool"));
246 char_type = new IntegerType ((Struct) root_symbol.scope.lookup ("char"));
247 int_type = new IntegerType ((Struct) root_symbol.scope.lookup ("int"));
248 uint_type = new IntegerType ((Struct) root_symbol.scope.lookup ("uint"));
249 float_type = new FloatingType ((Struct) root_symbol.scope.lookup ("float"));
250 double_type = new FloatingType ((Struct) root_symbol.scope.lookup ("double"));
251 string_type = new ObjectType ((Class) root_symbol.scope.lookup ("string"));
253 var dova_ns = (Namespace) root_symbol.scope.lookup ("Dova");
254 object_class = (Class) dova_ns.scope.lookup ("Object");
255 type_class = (Class) dova_ns.scope.lookup ("Type");
256 value_class = (Class) dova_ns.scope.lookup ("Value");
257 string_class = (Class) root_symbol.scope.lookup ("string");
258 array_class = (Class) dova_ns.scope.lookup ("Array");
259 delegate_class = (Class) dova_ns.scope.lookup ("Delegate");
260 error_class = (Class) dova_ns.scope.lookup ("Error");
262 header_file = new CCodeFile ();
263 header_file.is_header = true;
265 cfile = new CCodeFile ();
267 if (context.nostdpkg) {
268 header_file.add_include ("dova-types.h");
269 cfile.add_include ("dova-types.h");
270 } else {
271 header_file.add_include ("dova-base.h");
272 cfile.add_include ("dova-base.h");
275 generated_external_symbols = new HashSet<Symbol> ();
278 /* we're only interested in non-pkg source files */
279 var source_files = context.get_source_files ();
280 foreach (SourceFile file in source_files) {
281 if (file.file_type == SourceFileType.SOURCE) {
282 file.accept (this);
286 if (csource_filename != null) {
287 if (!cfile.store (csource_filename, null, context.version_header, context.debug)) {
288 Report.error (null, "unable to open `%s' for writing".printf (csource_filename));
292 cfile = null;
295 // generate C header file for public API
296 if (context.header_filename != null) {
297 if (!header_file.store (context.header_filename, null, context.version_header, false)) {
298 Report.error (null, "unable to open `%s' for writing".printf (context.header_filename));
303 public void push_context (EmitContext emit_context) {
304 if (this.emit_context != null) {
305 emit_context_stack.add (this.emit_context);
308 this.emit_context = emit_context;
311 public void pop_context () {
312 if (emit_context_stack.size > 0) {
313 this.emit_context = emit_context_stack[emit_context_stack.size - 1];
314 emit_context_stack.remove_at (emit_context_stack.size - 1);
315 } else {
316 this.emit_context = null;
320 public void push_function (CCodeFunction func) {
321 emit_context.ccode_stack.add (ccode);
322 emit_context.ccode = func;
325 public void pop_function () {
326 emit_context.ccode = emit_context.ccode_stack[emit_context.ccode_stack.size - 1];
327 emit_context.ccode_stack.remove_at (emit_context.ccode_stack.size - 1);
330 public bool add_symbol_declaration (CCodeFile decl_space, Symbol sym, string name) {
331 if (decl_space.add_declaration (name)) {
332 return true;
334 if (sym.external_package || (!decl_space.is_header && CodeContext.get ().use_header && !sym.is_internal_symbol ())) {
335 // add appropriate include file
336 foreach (string header_filename in sym.get_cheader_filenames ()) {
337 decl_space.add_include (header_filename, !sym.external_package);
339 // declaration complete
340 return true;
341 } else {
342 // require declaration
343 return false;
347 public override void visit_source_file (SourceFile source_file) {
348 if (csource_filename == null) {
349 csource_filename = source_file.get_csource_filename ();
350 } else {
351 var writer = new CCodeWriter (source_file.get_csource_filename ());
352 if (!writer.open (context.version_header)) {
353 Report.error (null, "unable to open `%s' for writing".printf (writer.filename));
354 return;
356 writer.close ();
359 source_file.accept_children (this);
361 if (context.report.get_errors () > 0) {
362 return;
366 public void generate_enum_declaration (Enum en, CCodeFile decl_space) {
367 if (add_symbol_declaration (decl_space, en, en.get_cname ())) {
368 return;
371 var cenum = new CCodeEnum (en.get_cname ());
373 foreach (EnumValue ev in en.get_values ()) {
374 if (ev.value == null) {
375 cenum.add_value (new CCodeEnumValue (ev.get_cname ()));
376 } else {
377 ev.value.emit (this);
378 cenum.add_value (new CCodeEnumValue (ev.get_cname (), get_cvalue (ev.value)));
382 decl_space.add_type_definition (cenum);
383 decl_space.add_type_definition (new CCodeNewline ());
386 public override void visit_enum (Enum en) {
387 en.accept_children (this);
389 generate_enum_declaration (en, cfile);
391 if (!en.is_internal_symbol ()) {
392 generate_enum_declaration (en, header_file);
396 public void generate_constant_declaration (Constant c, CCodeFile decl_space) {
397 if (add_symbol_declaration (decl_space, c, c.get_cname ())) {
398 return;
401 if (!c.external) {
402 c.value.emit (this);
404 if (c.value is InitializerList) {
405 var cdecl = new CCodeDeclaration (c.type_reference.get_const_cname ());
406 var arr = "";
407 if (c.type_reference is ArrayType) {
408 arr = "[]";
410 cdecl.add_declarator (new CCodeVariableDeclarator ("%s%s".printf (c.get_cname (), arr), get_cvalue (c.value)));
411 cdecl.modifiers = CCodeModifiers.STATIC;
413 decl_space.add_constant_declaration (cdecl);
414 } else {
415 var cdefine = new CCodeMacroReplacement.with_expression (c.get_cname (), get_cvalue (c.value));
416 decl_space.add_type_member_declaration (cdefine);
421 public override void visit_constant (Constant c) {
422 generate_constant_declaration (c, cfile);
424 if (!c.is_internal_symbol ()) {
425 generate_constant_declaration (c, header_file);
429 public void generate_field_declaration (Field f, CCodeFile decl_space) {
430 if (add_symbol_declaration (decl_space, f, f.get_cname ())) {
431 return;
434 generate_type_declaration (f.variable_type, decl_space);
436 string field_ctype = f.variable_type.get_cname ();
437 if (f.is_volatile) {
438 field_ctype = "volatile " + field_ctype;
441 var cdecl = new CCodeDeclaration (field_ctype);
442 cdecl.add_declarator (new CCodeVariableDeclarator (f.get_cname ()));
443 if (f.is_internal_symbol ()) {
444 cdecl.modifiers = CCodeModifiers.STATIC;
445 } else {
446 cdecl.modifiers = CCodeModifiers.EXTERN;
449 if (f.get_attribute ("ThreadLocal") != null) {
450 cdecl.modifiers |= CCodeModifiers.THREAD_LOCAL;
453 decl_space.add_type_member_declaration (cdecl);
456 public override void visit_field (Field f) {
457 if (f.binding == MemberBinding.STATIC) {
458 generate_field_declaration (f, cfile);
460 if (!f.is_internal_symbol ()) {
461 generate_field_declaration (f, header_file);
464 var var_decl = new CCodeVariableDeclarator (f.get_cname ());
465 var_decl.initializer = default_value_for_type (f.variable_type, true);
467 if (f.initializer != null) {
468 static_fields.add (f);
471 string field_ctype = f.variable_type.get_cname ();
472 if (f.is_volatile) {
473 field_ctype = "volatile " + field_ctype;
476 var var_def = new CCodeDeclaration (field_ctype);
477 var_def.add_declarator (var_decl);
478 if (!f.is_internal_symbol ()) {
479 var_def.modifiers = CCodeModifiers.EXTERN;
480 } else {
481 var_def.modifiers = CCodeModifiers.STATIC;
484 if (f.get_attribute ("ThreadLocal") != null) {
485 var_def.modifiers |= CCodeModifiers.THREAD_LOCAL;
488 cfile.add_type_member_declaration (var_def);
492 public bool is_constant_ccode_expression (CCodeExpression cexpr) {
493 if (cexpr is CCodeConstant) {
494 return true;
495 } else if (cexpr is CCodeCastExpression) {
496 var ccast = (CCodeCastExpression) cexpr;
497 return is_constant_ccode_expression (ccast.inner);
498 } else if (cexpr is CCodeBinaryExpression) {
499 var cbinary = (CCodeBinaryExpression) cexpr;
500 return is_constant_ccode_expression (cbinary.left) && is_constant_ccode_expression (cbinary.right);
503 var cparenthesized = (cexpr as CCodeParenthesizedExpression);
504 return (null != cparenthesized && is_constant_ccode_expression (cparenthesized.inner));
508 * Returns whether the passed cexpr is a pure expression, i.e. an
509 * expression without side-effects.
511 public bool is_pure_ccode_expression (CCodeExpression cexpr) {
512 if (cexpr is CCodeConstant || cexpr is CCodeIdentifier) {
513 return true;
514 } else if (cexpr is CCodeBinaryExpression) {
515 var cbinary = (CCodeBinaryExpression) cexpr;
516 return is_pure_ccode_expression (cbinary.left) && is_constant_ccode_expression (cbinary.right);
517 } else if (cexpr is CCodeUnaryExpression) {
518 var cunary = (CCodeUnaryExpression) cexpr;
519 switch (cunary.operator) {
520 case CCodeUnaryOperator.PREFIX_INCREMENT:
521 case CCodeUnaryOperator.PREFIX_DECREMENT:
522 case CCodeUnaryOperator.POSTFIX_INCREMENT:
523 case CCodeUnaryOperator.POSTFIX_DECREMENT:
524 return false;
525 default:
526 return is_pure_ccode_expression (cunary.inner);
528 } else if (cexpr is CCodeMemberAccess) {
529 var cma = (CCodeMemberAccess) cexpr;
530 return is_pure_ccode_expression (cma.inner);
531 } else if (cexpr is CCodeElementAccess) {
532 var cea = (CCodeElementAccess) cexpr;
533 return is_pure_ccode_expression (cea.container) && is_pure_ccode_expression (cea.index);
534 } else if (cexpr is CCodeCastExpression) {
535 var ccast = (CCodeCastExpression) cexpr;
536 return is_pure_ccode_expression (ccast.inner);
537 } else if (cexpr is CCodeParenthesizedExpression) {
538 var cparenthesized = (CCodeParenthesizedExpression) cexpr;
539 return is_pure_ccode_expression (cparenthesized.inner);
542 return false;
545 public override void visit_formal_parameter (Parameter p) {
548 public override void visit_property (Property prop) {
549 if (prop.get_accessor != null) {
550 prop.get_accessor.accept (this);
552 if (prop.set_accessor != null) {
553 prop.set_accessor.accept (this);
557 public void generate_type_declaration (DataType type, CCodeFile decl_space) {
558 if (type is ObjectType) {
559 var object_type = (ObjectType) type;
560 if (object_type.type_symbol is Class) {
561 generate_class_declaration ((Class) object_type.type_symbol, decl_space);
562 } else if (object_type.type_symbol is Interface) {
563 generate_interface_declaration ((Interface) object_type.type_symbol, decl_space);
565 } else if (type is DelegateType) {
566 var deleg_type = (DelegateType) type;
567 var d = deleg_type.delegate_symbol;
568 generate_delegate_declaration (d, decl_space);
569 } else if (type.data_type is Enum) {
570 var en = (Enum) type.data_type;
571 generate_enum_declaration (en, decl_space);
572 } else if (type is ValueType) {
573 var value_type = (ValueType) type;
574 generate_struct_declaration ((Struct) value_type.type_symbol, decl_space);
575 } else if (type is ArrayType) {
576 var array_type = (ArrayType) type;
577 generate_type_declaration (array_type.element_type, decl_space);
578 } else if (type is PointerType) {
579 var pointer_type = (PointerType) type;
580 generate_type_declaration (pointer_type.base_type, decl_space);
583 foreach (DataType type_arg in type.get_type_arguments ()) {
584 generate_type_declaration (type_arg, decl_space);
588 public virtual void generate_struct_declaration (Struct st, CCodeFile decl_space) {
591 public virtual void generate_delegate_declaration (Delegate d, CCodeFile decl_space) {
594 public virtual void generate_cparameters (Method m, CCodeFile decl_space, CCodeFunction func, CCodeFunctionDeclarator? vdeclarator = null, CCodeFunctionCall? vcall = null) {
597 public virtual void generate_property_accessor_declaration (PropertyAccessor acc, CCodeFile decl_space) {
600 public int get_block_id (Block b) {
601 int result = block_map[b];
602 if (result == 0) {
603 result = ++next_block_id;
604 block_map[b] = result;
606 return result;
609 void capture_parameter (Parameter param, CCodeStruct data, int block_id, CCodeBlock free_block) {
610 generate_type_declaration (param.variable_type, cfile);
612 var param_type = param.variable_type.copy ();
613 param_type.value_owned = true;
614 data.add_field (param_type.get_cname (), get_variable_cname (param.name));
616 // create copy if necessary as captured variables may need to be kept alive
617 CCodeExpression cparam = get_variable_cexpression (param.name);
618 if (requires_copy (param_type) && !param.variable_type.value_owned) {
619 var ma = new MemberAccess.simple (param.name);
620 ma.symbol_reference = param;
621 ma.value_type = param.variable_type.copy ();
622 // directly access parameters in ref expressions
623 param.captured = false;
624 cparam = get_ref_cexpression (param.variable_type, cparam, ma, param);
625 param.captured = true;
628 ccode.add_assignment (new CCodeMemberAccess.pointer (get_variable_cexpression ("_data%d_".printf (block_id)), get_variable_cname (param.name)), cparam);
630 if (requires_destroy (param_type)) {
631 var ma = new MemberAccess.simple (param.name);
632 ma.symbol_reference = param;
633 ma.value_type = param_type.copy ();
634 free_block.add_statement (new CCodeExpressionStatement (get_unref_expression (new CCodeMemberAccess.pointer (new CCodeIdentifier ("_data%d_".printf (block_id)), get_variable_cname (param.name)), param.variable_type, ma)));
638 public override void visit_block (Block b) {
639 emit_context.push_symbol (b);
641 var local_vars = b.get_local_variables ();
643 if (b.parent_node is Block || b.parent_node is SwitchStatement) {
644 ccode.open_block ();
647 if (b.captured) {
648 var parent_block = next_closure_block (b.parent_symbol);
650 int block_id = get_block_id (b);
651 string struct_name = "Block%dData".printf (block_id);
653 var free_block = new CCodeBlock ();
655 var data = new CCodeStruct ("_" + struct_name);
656 data.add_field ("DovaType*", "type");
657 data.add_field ("int32_t", "_ref_count_");
658 if (parent_block != null) {
659 int parent_block_id = get_block_id (parent_block);
661 data.add_field ("Block%dData *".printf (parent_block_id), "_data%d_".printf (parent_block_id));
663 var unref_call = new CCodeFunctionCall (new CCodeIdentifier ("dova_object_unref"));
664 unref_call.add_argument (new CCodeMemberAccess.pointer (new CCodeIdentifier ("_data%d_".printf (block_id)), "_data%d_".printf (parent_block_id)));
665 free_block.add_statement (new CCodeExpressionStatement (unref_call));
666 } else if ((current_method != null && current_method.binding == MemberBinding.INSTANCE) ||
667 (current_property_accessor != null && current_property_accessor.prop.binding == MemberBinding.INSTANCE)) {
668 data.add_field ("%s *".printf (current_class.get_cname ()), "this");
670 var ma = new MemberAccess.simple ("this");
671 ma.symbol_reference = current_class;
672 free_block.add_statement (new CCodeExpressionStatement (get_unref_expression (new CCodeMemberAccess.pointer (new CCodeIdentifier ("_data%d_".printf (block_id)), "this"), new ObjectType (current_class), ma)));
674 foreach (var local in local_vars) {
675 if (local.captured) {
676 generate_type_declaration (local.variable_type, cfile);
678 data.add_field (local.variable_type.get_cname (), get_variable_cname (local.name) + local.variable_type.get_cdeclarator_suffix ());
681 // free in reverse order
682 for (int i = local_vars.size - 1; i >= 0; i--) {
683 var local = local_vars[i];
684 if (local.captured) {
685 if (requires_destroy (local.variable_type)) {
686 var ma = new MemberAccess.simple (local.name);
687 ma.symbol_reference = local;
688 ma.value_type = local.variable_type.copy ();
689 free_block.add_statement (new CCodeExpressionStatement (get_unref_expression (new CCodeMemberAccess.pointer (new CCodeIdentifier ("_data%d_".printf (block_id)), get_variable_cname (local.name)), local.variable_type, ma)));
694 var data_alloc = new CCodeFunctionCall (new CCodeIdentifier ("dova_object_alloc"));
695 data_alloc.add_argument (new CCodeFunctionCall (new CCodeIdentifier ("block%d_data_type_get".printf (block_id))));
697 var data_decl = new CCodeDeclaration (struct_name + "*");
698 data_decl.add_declarator (new CCodeVariableDeclarator ("_data%d_".printf (block_id), data_alloc));
699 ccode.add_statement (data_decl);
701 if (parent_block != null) {
702 int parent_block_id = get_block_id (parent_block);
704 var ref_call = new CCodeFunctionCall (new CCodeIdentifier ("dova_object_ref"));
705 ref_call.add_argument (get_variable_cexpression ("_data%d_".printf (parent_block_id)));
707 ccode.add_statement (new CCodeExpressionStatement (new CCodeAssignment (new CCodeMemberAccess.pointer (get_variable_cexpression ("_data%d_".printf (block_id)), "_data%d_".printf (parent_block_id)), ref_call)));
708 } else if ((current_method != null && current_method.binding == MemberBinding.INSTANCE) ||
709 (current_property_accessor != null && current_property_accessor.prop.binding == MemberBinding.INSTANCE)) {
710 var ref_call = new CCodeFunctionCall (get_dup_func_expression (new ObjectType (current_class), b.source_reference));
711 ref_call.add_argument (new CCodeIdentifier ("this"));
713 ccode.add_statement (new CCodeExpressionStatement (new CCodeAssignment (new CCodeMemberAccess.pointer (get_variable_cexpression ("_data%d_".printf (block_id)), "this"), ref_call)));
716 if (b.parent_symbol is Method) {
717 var m = (Method) b.parent_symbol;
719 // parameters are captured with the top-level block of the method
720 foreach (var param in m.get_parameters ()) {
721 if (param.captured) {
722 capture_parameter (param, data, block_id, free_block);
725 } else if (b.parent_symbol is PropertyAccessor) {
726 var acc = (PropertyAccessor) b.parent_symbol;
728 if (!acc.readable && acc.value_parameter.captured) {
729 capture_parameter (acc.value_parameter, data, block_id, free_block);
733 var typedef = new CCodeTypeDefinition ("struct _" + struct_name, new CCodeVariableDeclarator (struct_name));
734 cfile.add_type_declaration (typedef);
735 cfile.add_type_definition (data);
737 var data_free = new CCodeFunctionCall (new CCodeIdentifier ("free"));
738 data_free.add_argument (new CCodeIdentifier ("_data%d_".printf (block_id)));
739 free_block.add_statement (new CCodeExpressionStatement (data_free));
741 // create type_get/finalize functions
742 var type_get_fun = new CCodeFunction ("block%d_data_type_get".printf (block_id), "DovaType*");
743 type_get_fun.modifiers = CCodeModifiers.STATIC;
744 cfile.add_function_declaration (type_get_fun);
745 type_get_fun.block = new CCodeBlock ();
747 var cdecl = new CCodeDeclaration ("intptr_t");
748 cdecl.add_declarator (new CCodeVariableDeclarator ("_block%d_data_object_offset".printf (block_id), new CCodeConstant ("0")));
749 cdecl.modifiers = CCodeModifiers.STATIC;
750 cfile.add_type_member_declaration (cdecl);
752 cdecl = new CCodeDeclaration ("intptr_t");
753 cdecl.add_declarator (new CCodeVariableDeclarator ("_block%d_data_type_offset".printf (block_id), new CCodeConstant ("0")));
754 cdecl.modifiers = CCodeModifiers.STATIC;
755 cfile.add_type_member_declaration (cdecl);
757 cdecl = new CCodeDeclaration ("DovaType *");
758 cdecl.add_declarator (new CCodeVariableDeclarator ("block%d_data_type".printf (block_id), new CCodeConstant ("NULL")));
759 cdecl.modifiers = CCodeModifiers.STATIC;
760 cfile.add_type_member_declaration (cdecl);
762 var type_init_block = new CCodeBlock ();
763 var alloc_call = new CCodeFunctionCall (new CCodeIdentifier ("dova_type_alloc"));
764 alloc_call.add_argument (new CCodeFunctionCall (new CCodeIdentifier ("dova_object_type_get")));
765 alloc_call.add_argument (new CCodeConstant ("sizeof (%s)".printf (struct_name)));
766 alloc_call.add_argument (new CCodeConstant ("0"));
767 alloc_call.add_argument (new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, new CCodeIdentifier ("block%d_data_type".printf (block_id))));
768 alloc_call.add_argument (new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, new CCodeIdentifier ("_block%d_data_object_offset".printf (block_id))));
769 alloc_call.add_argument (new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, new CCodeIdentifier ("_block%d_data_type_offset".printf (block_id))));
770 type_init_block.add_statement (new CCodeExpressionStatement (alloc_call));
771 var type_init_call = new CCodeFunctionCall (new CCodeIdentifier ("dova_object_type_init"));
772 type_init_call.add_argument (new CCodeIdentifier ("block%d_data_type".printf (block_id)));
773 type_init_block.add_statement (new CCodeExpressionStatement (type_init_call));
774 type_get_fun.block.add_statement (new CCodeIfStatement (new CCodeUnaryExpression (CCodeUnaryOperator.LOGICAL_NEGATION, new CCodeIdentifier ("block%d_data_type".printf (block_id))), type_init_block));
775 type_get_fun.block.add_statement (new CCodeReturnStatement (new CCodeIdentifier ("block%d_data_type".printf (block_id))));
777 cfile.add_function (type_get_fun);
779 var unref_fun = new CCodeFunction ("block%d_data_finalize".printf (block_id), "void");
780 unref_fun.add_parameter (new CCodeParameter ("_data%d_".printf (block_id), struct_name + "*"));
781 unref_fun.modifiers = CCodeModifiers.STATIC;
782 cfile.add_function_declaration (unref_fun);
783 unref_fun.block = free_block;
785 cfile.add_function (unref_fun);
788 foreach (Statement stmt in b.get_statements ()) {
789 stmt.emit (this);
792 // free in reverse order
793 for (int i = local_vars.size - 1; i >= 0; i--) {
794 var local = local_vars[i];
795 if (!local.floating && !local.captured && requires_destroy (local.variable_type)) {
796 var ma = new MemberAccess.simple (local.name);
797 ma.symbol_reference = local;
798 ccode.add_statement (new CCodeExpressionStatement (get_unref_expression (get_variable_cexpression (local.name), local.variable_type, ma)));
802 if (b.parent_symbol is Method) {
803 var m = (Method) b.parent_symbol;
804 foreach (Parameter param in m.get_parameters ()) {
805 if (!param.captured && requires_destroy (param.variable_type) && param.direction == ParameterDirection.IN) {
806 var ma = new MemberAccess.simple (param.name);
807 ma.symbol_reference = param;
808 ccode.add_statement (new CCodeExpressionStatement (get_unref_expression (get_variable_cexpression (param.name), param.variable_type, ma)));
813 if (b.captured) {
814 int block_id = get_block_id (b);
816 var data_unref = new CCodeFunctionCall (new CCodeIdentifier ("dova_object_unref"));
817 data_unref.add_argument (get_variable_cexpression ("_data%d_".printf (block_id)));
818 ccode.add_statement (new CCodeExpressionStatement (data_unref));
821 if (b.parent_node is Block || b.parent_node is SwitchStatement) {
822 ccode.close ();
825 emit_context.pop_symbol ();
828 public override void visit_declaration_statement (DeclarationStatement stmt) {
829 stmt.declaration.accept (this);
832 public CCodeExpression get_variable_cexpression (string name) {
833 return new CCodeIdentifier (get_variable_cname (name));
836 public string get_variable_cname (string name) {
837 if (name[0] == '.') {
838 // compiler-internal variable
839 if (!variable_name_map.contains (name)) {
840 variable_name_map.set (name, "_tmp%d_".printf (next_temp_var_id));
841 next_temp_var_id++;
843 return variable_name_map.get (name);
844 } else if (reserved_identifiers.contains (name)) {
845 return "_%s_".printf (name);
846 } else {
847 return name;
851 public override void visit_local_variable (LocalVariable local) {
852 if (local.initializer != null) {
853 local.initializer.emit (this);
855 visit_end_full_expression (local.initializer);
858 generate_type_declaration (local.variable_type, cfile);
860 CCodeExpression rhs = null;
861 if (local.initializer != null && get_cvalue (local.initializer) != null) {
862 rhs = get_cvalue (local.initializer);
865 if (local.captured) {
866 if (local.initializer != null) {
867 ccode.add_assignment (new CCodeMemberAccess.pointer (get_variable_cexpression ("_data%d_".printf (get_block_id ((Block) local.parent_symbol))), get_variable_cname (local.name)), rhs);
869 } else {
870 var cvar = new CCodeVariableDeclarator (get_variable_cname (local.name), rhs, local.variable_type.get_cdeclarator_suffix ());
872 var cdecl = new CCodeDeclaration (local.variable_type.get_cname ());
873 cdecl.add_declarator (cvar);
874 ccode.add_statement (cdecl);
876 // try to initialize uninitialized variables
877 // initialization not necessary for variables stored in closure
878 if (cvar.initializer == null) {
879 cvar.initializer = default_value_for_type (local.variable_type, true);
880 cvar.init0 = true;
884 if (local.initializer != null && local.initializer.tree_can_fail) {
885 add_simple_check (local.initializer);
888 local.active = true;
891 public override void visit_initializer_list (InitializerList list) {
892 if (list.target_type.data_type is Struct) {
893 /* initializer is used as struct initializer */
894 var st = (Struct) list.target_type.data_type;
896 var clist = new CCodeInitializerList ();
898 var field_it = st.get_fields ().iterator ();
899 foreach (Expression expr in list.get_initializers ()) {
900 Field field = null;
901 while (field == null) {
902 field_it.next ();
903 field = field_it.get ();
904 if (field.binding != MemberBinding.INSTANCE) {
905 // we only initialize instance fields
906 field = null;
910 var cexpr = get_cvalue (expr);
912 string ctype = field.get_ctype ();
913 if (ctype != null) {
914 cexpr = new CCodeCastExpression (cexpr, ctype);
917 clist.append (cexpr);
920 set_cvalue (list, clist);
921 } else {
922 var clist = new CCodeInitializerList ();
923 foreach (Expression expr in list.get_initializers ()) {
924 clist.append (get_cvalue (expr));
926 set_cvalue (list, clist);
930 public override LocalVariable create_local (DataType type) {
931 var result = get_temp_variable (type, type.value_owned);
932 emit_temp_var (result);
933 return result;
936 public LocalVariable get_temp_variable (DataType type, bool value_owned = true, CodeNode? node_reference = null) {
937 var var_type = type.copy ();
938 var_type.value_owned = value_owned;
939 var local = new LocalVariable (var_type, "_tmp%d_".printf (next_temp_var_id));
941 if (node_reference != null) {
942 local.source_reference = node_reference.source_reference;
945 next_temp_var_id++;
947 return local;
950 bool is_in_generic_type (DataType type) {
951 if (type.type_parameter.parent_symbol is TypeSymbol
952 && (current_method == null || current_method.binding == MemberBinding.INSTANCE)) {
953 return true;
954 } else {
955 return false;
959 public CCodeExpression get_type_private_from_type (ObjectTypeSymbol type_symbol, CCodeExpression type_expression) {
960 if (type_symbol is Class) {
961 // class
962 return new CCodeCastExpression (new CCodeBinaryExpression (CCodeBinaryOperator.PLUS, new CCodeCastExpression (type_expression, "char *"), new CCodeIdentifier ("_%s_type_offset".printf (((Class) type_symbol).get_lower_case_cname ()))), "%sTypePrivate *".printf (((Class) type_symbol).get_cname ()));
963 } else {
964 // interface
965 var get_interface = new CCodeFunctionCall (new CCodeIdentifier ("dova_type_get_interface"));
966 get_interface.add_argument (type_expression);
967 get_interface.add_argument (new CCodeIdentifier ("%s_type".printf (((Interface) type_symbol).get_lower_case_cname ())));
968 return new CCodeCastExpression (get_interface, "%sTypePrivate *".printf (((Interface) type_symbol).get_cname ()));
972 public CCodeExpression get_type_id_expression (DataType type, bool is_chainup = false) {
973 if (type is GenericType) {
974 string var_name = "%s_type".printf (type.type_parameter.name.down ());
975 if (is_in_generic_type (type) && !is_chainup) {
976 return new CCodeMemberAccess.pointer (get_type_private_from_type ((ObjectTypeSymbol) type.type_parameter.parent_symbol, new CCodeMemberAccess.pointer (new CCodeIdentifier ("this"), "type")), var_name);
977 } else {
978 return new CCodeIdentifier (var_name);
980 } else {
981 var ccall = new CCodeFunctionCall (new CCodeIdentifier ("%s_type_get".printf (type.data_type.get_lower_case_cname ())));
982 var object_type_symbol = type.data_type as ObjectTypeSymbol;
983 if (object_type_symbol != null) {
984 for (int i = 0; i < object_type_symbol.get_type_parameters ().size; i++) {
985 if (type.get_type_arguments ().size == 0) {
986 ccall.add_argument (new CCodeConstant ("NULL"));
987 } else {
988 ccall.add_argument (get_type_id_expression (type.get_type_arguments ().get (i)));
992 return ccall;
996 public virtual CCodeExpression? get_dup_func_expression (DataType type, SourceReference? source_reference, bool is_chainup = false) {
997 if (type.data_type != null) {
998 string dup_function = "";
999 if (type.data_type.is_reference_counting ()) {
1000 dup_function = type.data_type.get_ref_function ();
1001 } else if (type is ValueType) {
1002 dup_function = type.data_type.get_dup_function ();
1003 if (dup_function == null) {
1004 dup_function = "";
1008 return new CCodeIdentifier (dup_function);
1009 } else if (type.type_parameter != null) {
1010 return null;
1011 } else if (type is ArrayType) {
1012 return new CCodeIdentifier ("dova_object_ref");
1013 } else if (type is DelegateType) {
1014 return new CCodeIdentifier ("dova_object_ref");
1015 } else if (type is PointerType) {
1016 var pointer_type = (PointerType) type;
1017 return get_dup_func_expression (pointer_type.base_type, source_reference);
1018 } else {
1019 return new CCodeConstant ("NULL");
1023 public CCodeExpression? get_destroy_func_expression (DataType type, bool is_chainup = false) {
1024 if (type.data_type != null) {
1025 string unref_function;
1026 if (type is ReferenceType) {
1027 if (type.data_type.is_reference_counting ()) {
1028 unref_function = type.data_type.get_unref_function ();
1029 } else {
1030 unref_function = type.data_type.get_free_function ();
1032 } else {
1033 if (type.nullable) {
1034 unref_function = type.data_type.get_free_function ();
1035 if (unref_function == null) {
1036 unref_function = "free";
1038 } else {
1039 var st = (Struct) type.data_type;
1040 unref_function = st.get_copy_function ();
1043 if (unref_function == null) {
1044 return new CCodeConstant ("NULL");
1046 return new CCodeIdentifier (unref_function);
1047 } else if (type.type_parameter != null && current_type_symbol is Class) {
1048 // FIXME ask type for dup/ref function
1049 return new CCodeIdentifier ("dova_object_unref");
1050 } else if (type is ArrayType) {
1051 return new CCodeIdentifier ("dova_object_unref");
1052 } else if (type is DelegateType) {
1053 return new CCodeIdentifier ("dova_object_unref");
1054 } else if (type is PointerType) {
1055 return new CCodeIdentifier ("free");
1056 } else {
1057 return new CCodeConstant ("NULL");
1061 public CCodeExpression get_unref_expression (CCodeExpression cvar, DataType type, Expression? expr = null) {
1062 return destroy_value (new DovaValue (type, cvar));
1065 public CCodeExpression destroy_value (TargetValue value) {
1066 var type = value.value_type;
1067 var cvar = get_cvalue_ (value);
1069 var ccall = new CCodeFunctionCall (get_destroy_func_expression (type));
1071 if (type is ValueType && !type.nullable) {
1072 // normal value type, no null check
1073 ccall.add_argument (new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, cvar));
1074 ccall.add_argument (new CCodeConstant ("0"));
1075 ccall.add_argument (new CCodeConstant ("NULL"));
1076 ccall.add_argument (new CCodeConstant ("0"));
1078 return ccall;
1081 /* (foo == NULL ? NULL : foo = (unref (foo), NULL)) */
1083 /* can be simplified to
1084 * foo = (unref (foo), NULL)
1085 * if foo is of static type non-null
1088 var cisnull = new CCodeBinaryExpression (CCodeBinaryOperator.EQUALITY, cvar, new CCodeConstant ("NULL"));
1089 if (type.type_parameter != null) {
1090 if (!(current_type_symbol is Class) || current_class.is_compact) {
1091 return new CCodeConstant ("NULL");
1094 // unref functions are optional for type parameters
1095 var cunrefisnull = new CCodeBinaryExpression (CCodeBinaryOperator.EQUALITY, get_destroy_func_expression (type), new CCodeConstant ("NULL"));
1096 cisnull = new CCodeBinaryExpression (CCodeBinaryOperator.OR, cisnull, cunrefisnull);
1099 ccall.add_argument (cvar);
1101 /* set freed references to NULL to prevent further use */
1102 var ccomma = new CCodeCommaExpression ();
1104 ccomma.append_expression (ccall);
1105 ccomma.append_expression (new CCodeConstant ("NULL"));
1107 var cassign = new CCodeAssignment (cvar, ccomma);
1109 return new CCodeConditionalExpression (cisnull, new CCodeConstant ("NULL"), cassign);
1112 public override void visit_end_full_expression (Expression expr) {
1113 /* expr is a full expression, i.e. an initializer, the
1114 * expression in an expression statement, the controlling
1115 * expression in if, while, for, or foreach statements
1117 * we unref temporary variables at the end of a full
1118 * expression
1121 if (((List<LocalVariable>) temp_ref_vars).size == 0) {
1122 /* nothing to do without temporary variables */
1123 return;
1126 var expr_type = expr.value_type;
1127 if (expr.target_type != null) {
1128 expr_type = expr.target_type;
1131 var full_expr_var = get_temp_variable (expr_type, true, expr);
1132 emit_temp_var (full_expr_var);
1134 var expr_list = new CCodeCommaExpression ();
1135 expr_list.append_expression (new CCodeAssignment (get_variable_cexpression (full_expr_var.name), get_cvalue (expr)));
1137 foreach (LocalVariable local in temp_ref_vars) {
1138 var ma = new MemberAccess.simple (local.name);
1139 ma.symbol_reference = local;
1140 expr_list.append_expression (get_unref_expression (get_variable_cexpression (local.name), local.variable_type, ma));
1143 expr_list.append_expression (get_variable_cexpression (full_expr_var.name));
1145 set_cvalue (expr, expr_list);
1147 temp_ref_vars.clear ();
1150 public void emit_temp_var (LocalVariable local) {
1151 var cdecl = new CCodeDeclaration (local.variable_type.get_cname ());
1153 var vardecl = new CCodeVariableDeclarator (local.name, null, local.variable_type.get_cdeclarator_suffix ());
1154 cdecl.add_declarator (vardecl);
1156 var st = local.variable_type.data_type as Struct;
1157 var array_type = local.variable_type as ArrayType;
1159 if (local.name.has_prefix ("*")) {
1160 // do not dereference unintialized variable
1161 // initialization is not needed for these special
1162 // pointer temp variables
1163 // used to avoid side-effects in assignments
1164 } else if (local.variable_type is GenericType) {
1165 var value_size = new CCodeFunctionCall (new CCodeIdentifier ("dova_type_get_value_size"));
1166 value_size.add_argument (get_type_id_expression (local.variable_type));
1168 var alloca_call = new CCodeFunctionCall (new CCodeIdentifier ("alloca"));
1169 alloca_call.add_argument (value_size);
1171 var memset_call = new CCodeFunctionCall (new CCodeIdentifier ("memset"));
1172 memset_call.add_argument (alloca_call);
1173 memset_call.add_argument (new CCodeConstant ("0"));
1174 memset_call.add_argument (value_size);
1176 vardecl.initializer = memset_call;
1177 vardecl.init0 = true;
1178 } else if (!local.variable_type.nullable &&
1179 (st != null && st.get_fields ().size > 0) ||
1180 (array_type != null && array_type.fixed_length)) {
1181 // 0-initialize struct with struct initializer { 0 }
1182 // necessary as they will be passed by reference
1183 var clist = new CCodeInitializerList ();
1184 clist.append (new CCodeConstant ("0"));
1186 vardecl.initializer = clist;
1187 vardecl.init0 = true;
1188 } else if (local.variable_type.is_reference_type_or_type_parameter () ||
1189 local.variable_type.nullable) {
1190 vardecl.initializer = new CCodeConstant ("NULL");
1191 vardecl.init0 = true;
1194 ccode.add_statement (cdecl);
1197 public override void visit_expression_statement (ExpressionStatement stmt) {
1198 if (stmt.expression.error) {
1199 stmt.error = true;
1200 return;
1203 if (get_cvalue (stmt.expression) != null) {
1204 ccode.add_expression (get_cvalue (stmt.expression));
1206 /* free temporary objects and handle errors */
1208 foreach (LocalVariable local in temp_ref_vars) {
1209 var ma = new MemberAccess.simple (local.name);
1210 ma.symbol_reference = local;
1211 ma.value_type = local.variable_type.copy ();
1212 ccode.add_expression (get_unref_expression (get_variable_cexpression (local.name), local.variable_type, ma));
1215 if (stmt.tree_can_fail && stmt.expression.tree_can_fail) {
1216 // simple case, no node breakdown necessary
1217 add_simple_check (stmt.expression);
1220 temp_ref_vars.clear ();
1223 public virtual void append_local_free (Symbol sym, bool stop_at_loop = false, CodeNode? stop_at = null) {
1224 var b = (Block) sym;
1226 var local_vars = b.get_local_variables ();
1227 // free in reverse order
1228 for (int i = local_vars.size - 1; i >= 0; i--) {
1229 var local = local_vars[i];
1230 if (local.active && !local.floating && !local.captured && requires_destroy (local.variable_type)) {
1231 var ma = new MemberAccess.simple (local.name);
1232 ma.symbol_reference = local;
1233 ccode.add_expression (get_unref_expression (get_variable_cexpression (local.name), local.variable_type, ma));
1237 if (b.captured) {
1238 int block_id = get_block_id (b);
1240 var data_unref = new CCodeFunctionCall (new CCodeIdentifier ("dova_object_unref"));
1241 data_unref.add_argument (get_variable_cexpression ("_data%d_".printf (block_id)));
1242 ccode.add_expression (data_unref);
1245 if (stop_at_loop) {
1246 if (b.parent_node is Loop ||
1247 b.parent_node is ForeachStatement ||
1248 b.parent_node is SwitchStatement) {
1249 return;
1253 if (b.parent_node == stop_at) {
1254 return;
1257 if (sym.parent_symbol is Block) {
1258 append_local_free (sym.parent_symbol, stop_at_loop, stop_at);
1259 } else if (sym.parent_symbol is Method) {
1260 append_param_free ((Method) sym.parent_symbol);
1264 private void append_param_free (Method m) {
1265 foreach (Parameter param in m.get_parameters ()) {
1266 if (requires_destroy (param.variable_type) && param.direction == ParameterDirection.IN) {
1267 var ma = new MemberAccess.simple (param.name);
1268 ma.symbol_reference = param;
1269 ccode.add_expression (get_unref_expression (get_variable_cexpression (param.name), param.variable_type, ma));
1274 public override void visit_return_statement (ReturnStatement stmt) {
1275 // free local variables
1276 append_local_free (current_symbol);
1278 ccode.add_return ((current_return_type is VoidType) ? null : new CCodeIdentifier ("result"));
1281 public override void visit_delete_statement (DeleteStatement stmt) {
1282 var pointer_type = (PointerType) stmt.expression.value_type;
1283 DataType type = pointer_type;
1284 if (pointer_type.base_type.data_type != null && pointer_type.base_type.data_type.is_reference_type ()) {
1285 type = pointer_type.base_type;
1288 var ccall = new CCodeFunctionCall (get_destroy_func_expression (type));
1289 ccall.add_argument (get_cvalue (stmt.expression));
1290 ccode.add_expression (ccall);
1293 public override void visit_expression (Expression expr) {
1294 if (get_cvalue (expr) != null && !expr.lvalue) {
1295 // memory management, implicit casts, and boxing/unboxing
1296 set_cvalue (expr, transform_expression (get_cvalue (expr), expr.value_type, expr.target_type, expr));
1300 public override void visit_boolean_literal (BooleanLiteral expr) {
1301 set_cvalue (expr, new CCodeConstant (expr.value ? "true" : "false"));
1304 public override void visit_character_literal (CharacterLiteral expr) {
1305 if (expr.get_char () >= 0x20 && expr.get_char () < 0x80) {
1306 set_cvalue (expr, new CCodeConstant (expr.value));
1307 } else {
1308 set_cvalue (expr, new CCodeConstant ("%uU".printf (expr.get_char ())));
1312 public override void visit_integer_literal (IntegerLiteral expr) {
1313 set_cvalue (expr, new CCodeConstant (expr.value));
1316 public override void visit_real_literal (RealLiteral expr) {
1317 string c_literal = expr.value;
1318 if (c_literal.has_suffix ("d") || c_literal.has_suffix ("D")) {
1319 // there is no suffix for double in C
1320 c_literal = c_literal.substring (0, c_literal.length - 1);
1322 if (!("." in c_literal || "e" in c_literal || "E" in c_literal)) {
1323 // C requires period or exponent part for floating constants
1324 if ("f" in c_literal || "F" in c_literal) {
1325 c_literal = c_literal.substring (0, c_literal.length - 1) + ".f";
1326 } else {
1327 c_literal += ".";
1330 set_cvalue (expr, new CCodeConstant (c_literal));
1333 public override void visit_string_literal (StringLiteral expr) {
1334 // FIXME handle escaped characters in scanner/parser and escape them here again for C
1335 var cliteral = new CCodeConstant ("\"\\0\" " + expr.value);
1337 var cbinary = new CCodeBinaryExpression (CCodeBinaryOperator.PLUS, cliteral, new CCodeConstant ("1"));
1338 set_cvalue (expr, new CCodeCastExpression (cbinary, "string_t"));
1341 public override void visit_null_literal (NullLiteral expr) {
1342 set_cvalue (expr, new CCodeConstant ("NULL"));
1345 public override void visit_base_access (BaseAccess expr) {
1346 generate_type_declaration (expr.value_type, cfile);
1347 set_cvalue (expr, new CCodeCastExpression (new CCodeIdentifier ("this"), expr.value_type.get_cname ()));
1350 public override void visit_postfix_expression (PostfixExpression expr) {
1351 MemberAccess ma = find_property_access (expr.inner);
1352 if (ma != null) {
1353 // property postfix expression
1354 var prop = (Property) ma.symbol_reference;
1356 // assign current value to temp variable
1357 var temp_decl = get_temp_variable (prop.property_type, true, expr);
1358 emit_temp_var (temp_decl);
1359 ccode.add_assignment (get_variable_cexpression (temp_decl.name), get_cvalue (expr.inner));
1361 // increment/decrement property
1362 var op = expr.increment ? CCodeBinaryOperator.PLUS : CCodeBinaryOperator.MINUS;
1363 var cexpr = new CCodeBinaryExpression (op, get_variable_cexpression (temp_decl.name), new CCodeConstant ("1"));
1364 store_property (prop, ma.inner, new DovaValue (expr.value_type, cexpr));
1366 // return previous value
1367 set_cvalue (expr, new CCodeIdentifier (temp_decl.name));
1368 return;
1371 var op = expr.increment ? CCodeUnaryOperator.POSTFIX_INCREMENT : CCodeUnaryOperator.POSTFIX_DECREMENT;
1373 set_cvalue (expr, new CCodeUnaryExpression (op, get_cvalue (expr.inner)));
1376 private MemberAccess? find_property_access (Expression expr) {
1377 if (!(expr is MemberAccess)) {
1378 return null;
1381 var ma = (MemberAccess) expr;
1382 if (ma.symbol_reference is Property) {
1383 return ma;
1386 return null;
1389 public bool requires_copy (DataType type) {
1390 if (!type.is_disposable ()) {
1391 return false;
1394 var cl = type.data_type as Class;
1395 if (cl != null && cl.is_reference_counting ()
1396 && cl.get_ref_function () == "") {
1397 // empty ref_function => no ref necessary
1398 return false;
1401 if (type.type_parameter != null) {
1402 return false;
1405 return true;
1408 public bool requires_destroy (DataType type) {
1409 if (!type.is_disposable ()) {
1410 return false;
1413 var array_type = type as ArrayType;
1414 if (array_type != null && array_type.inline_allocated) {
1415 return requires_destroy (array_type.element_type);
1418 var cl = type.data_type as Class;
1419 if (cl != null && cl.is_reference_counting ()
1420 && cl.get_unref_function () == "") {
1421 // empty unref_function => no unref necessary
1422 return false;
1425 if (type.type_parameter != null) {
1426 return false;
1429 return true;
1432 bool is_ref_function_void (DataType type) {
1433 var cl = type.data_type as Class;
1434 if (cl != null && cl.ref_function_void) {
1435 return true;
1436 } else {
1437 return false;
1441 public virtual CCodeExpression? get_ref_cexpression (DataType expression_type, CCodeExpression cexpr, Expression? expr, CodeNode node) {
1442 if (expression_type is ValueType && !expression_type.nullable) {
1443 // normal value type, no null check
1444 // (copy (&temp, 0, &expr, 0), temp)
1446 var decl = get_temp_variable (expression_type, false, node);
1447 emit_temp_var (decl);
1449 var ctemp = get_variable_cexpression (decl.name);
1451 var vt = (ValueType) expression_type;
1452 var st = (Struct) vt.type_symbol;
1453 var copy_call = new CCodeFunctionCall (new CCodeIdentifier (st.get_copy_function ()));
1454 copy_call.add_argument (new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, ctemp));
1455 copy_call.add_argument (new CCodeConstant ("0"));
1456 copy_call.add_argument (new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, cexpr));
1457 copy_call.add_argument (new CCodeConstant ("0"));
1459 var ccomma = new CCodeCommaExpression ();
1461 ccomma.append_expression (copy_call);
1462 ccomma.append_expression (ctemp);
1464 return ccomma;
1467 /* (temp = expr, temp == NULL ? NULL : ref (temp))
1469 * can be simplified to
1470 * ref (expr)
1471 * if static type of expr is non-null
1474 var dupexpr = get_dup_func_expression (expression_type, node.source_reference);
1476 if (dupexpr == null) {
1477 node.error = true;
1478 return null;
1481 var ccall = new CCodeFunctionCall (dupexpr);
1483 if (expr != null && expr.is_non_null ()
1484 && !is_ref_function_void (expression_type)) {
1485 // expression is non-null
1486 ccall.add_argument (get_cvalue (expr));
1488 return ccall;
1489 } else {
1490 var decl = get_temp_variable (expression_type, false, node);
1491 emit_temp_var (decl);
1493 var ctemp = get_variable_cexpression (decl.name);
1495 var cisnull = new CCodeBinaryExpression (CCodeBinaryOperator.EQUALITY, ctemp, new CCodeConstant ("NULL"));
1496 if (expression_type.type_parameter != null) {
1497 // dup functions are optional for type parameters
1498 var cdupisnull = new CCodeBinaryExpression (CCodeBinaryOperator.EQUALITY, get_dup_func_expression (expression_type, node.source_reference), new CCodeConstant ("NULL"));
1499 cisnull = new CCodeBinaryExpression (CCodeBinaryOperator.OR, cisnull, cdupisnull);
1502 ccall.add_argument (ctemp);
1504 var ccomma = new CCodeCommaExpression ();
1505 ccomma.append_expression (new CCodeAssignment (ctemp, cexpr));
1507 var cifnull = new CCodeConstant ("NULL");
1508 ccomma.append_expression (new CCodeConditionalExpression (cisnull, cifnull, ccall));
1510 // repeat temp variable at the end of the comma expression
1511 // if the ref function returns void
1512 if (is_ref_function_void (expression_type)) {
1513 ccomma.append_expression (ctemp);
1516 return ccomma;
1520 public virtual void generate_class_declaration (Class cl, CCodeFile decl_space) {
1521 if (add_symbol_declaration (decl_space, cl, cl.get_cname ())) {
1522 return;
1526 public virtual void generate_interface_declaration (Interface iface, CCodeFile decl_space) {
1529 public virtual void generate_method_declaration (Method m, CCodeFile decl_space) {
1532 public void add_generic_type_arguments (CCodeFunctionCall ccall, List<DataType> type_args, CodeNode expr, bool is_chainup = false) {
1533 foreach (var type_arg in type_args) {
1534 ccall.add_argument (get_type_id_expression (type_arg, is_chainup));
1538 public override void visit_object_creation_expression (ObjectCreationExpression expr) {
1539 CCodeExpression instance = null;
1540 CCodeExpression creation_expr = null;
1542 var st = expr.type_reference.data_type as Struct;
1544 bool struct_by_ref = false;
1545 if (st != null && !st.is_boolean_type () && !st.is_integer_type () && !st.is_floating_type ()) {
1546 struct_by_ref = true;
1549 if (struct_by_ref || expr.get_object_initializer ().size > 0) {
1550 // value-type initialization or object creation expression with object initializer
1551 var temp_decl = get_temp_variable (expr.type_reference, false, expr);
1552 emit_temp_var (temp_decl);
1554 instance = get_variable_cexpression (get_variable_cname (temp_decl.name));
1557 if (expr.symbol_reference == null) {
1558 // no creation method
1559 if (expr.type_reference.data_type is Struct) {
1560 var creation_call = new CCodeFunctionCall (new CCodeIdentifier ("memset"));
1561 creation_call.add_argument (new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, instance));
1562 creation_call.add_argument (new CCodeConstant ("0"));
1563 creation_call.add_argument (new CCodeIdentifier ("sizeof (%s)".printf (expr.type_reference.get_cname ())));
1565 creation_expr = creation_call;
1567 } else if (expr.symbol_reference is Method) {
1568 // use creation method
1569 var m = (Method) expr.symbol_reference;
1570 var params = m.get_parameters ();
1571 CCodeFunctionCall creation_call;
1573 generate_method_declaration (m, cfile);
1575 var cl = expr.type_reference.data_type as Class;
1577 if (!m.has_new_function) {
1578 // use construct function directly
1579 creation_call = new CCodeFunctionCall (new CCodeIdentifier (m.get_real_cname ()));
1580 creation_call.add_argument (new CCodeIdentifier (cl.get_type_id ()));
1581 } else {
1582 creation_call = new CCodeFunctionCall (new CCodeIdentifier (m.get_cname ()));
1585 if (struct_by_ref && !(m.cinstance_parameter_position < 0)) {
1586 creation_call.add_argument (new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, instance));
1589 generate_type_declaration (expr.type_reference, cfile);
1591 if (cl != null && !cl.is_compact) {
1592 add_generic_type_arguments (creation_call, expr.type_reference.get_type_arguments (), expr);
1595 bool ellipsis = false;
1597 int i = 1;
1598 Iterator<Parameter> params_it = params.iterator ();
1599 foreach (Expression arg in expr.get_argument_list ()) {
1600 CCodeExpression cexpr = get_cvalue (arg);
1601 Parameter param = null;
1602 if (params_it.next ()) {
1603 param = params_it.get ();
1604 ellipsis = param.ellipsis;
1605 if (!ellipsis) {
1606 cexpr = handle_struct_argument (param, arg, cexpr);
1610 creation_call.add_argument (cexpr);
1612 i++;
1614 while (params_it.next ()) {
1615 var param = params_it.get ();
1617 if (param.ellipsis) {
1618 ellipsis = true;
1619 break;
1622 if (param.initializer == null) {
1623 Report.error (expr.source_reference, "no default expression for argument %d".printf (i));
1624 return;
1627 /* evaluate default expression here as the code
1628 * generator might not have visited the formal
1629 * parameter yet */
1630 param.initializer.emit (this);
1632 creation_call.add_argument (get_cvalue (param.initializer));
1633 i++;
1636 if (struct_by_ref && m.cinstance_parameter_position < 0) {
1637 // instance parameter is at the end in a struct creation method
1638 creation_call.add_argument (new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, instance));
1641 if (ellipsis) {
1642 /* ensure variable argument list ends with NULL
1643 * except when using printf-style arguments */
1644 if (!m.printf_format && !m.scanf_format && m.sentinel != "") {
1645 creation_call.add_argument (new CCodeConstant (m.sentinel));
1649 creation_expr = creation_call;
1651 // cast the return value of the creation method back to the intended type if
1652 // it requested a special C return type
1653 if (get_custom_creturn_type (m) != null) {
1654 creation_expr = new CCodeCastExpression (creation_expr, expr.type_reference.get_cname ());
1656 } else {
1657 assert (false);
1660 if (instance != null) {
1661 if (expr.type_reference.data_type is Struct) {
1662 ccode.add_expression (creation_expr);
1663 } else {
1664 ccode.add_assignment (instance, creation_expr);
1667 foreach (MemberInitializer init in expr.get_object_initializer ()) {
1668 if (init.symbol_reference is Field) {
1669 var f = (Field) init.symbol_reference;
1670 var instance_target_type = get_data_type_for_symbol ((TypeSymbol) f.parent_symbol);
1671 var typed_inst = transform_expression (instance, expr.type_reference, instance_target_type);
1672 CCodeExpression lhs;
1673 if (expr.type_reference.data_type is Struct) {
1674 lhs = new CCodeMemberAccess (typed_inst, f.get_cname ());
1675 } else {
1676 lhs = new CCodeMemberAccess.pointer (typed_inst, f.get_cname ());
1678 ccode.add_assignment (lhs, get_cvalue (init.initializer));
1679 } else if (init.symbol_reference is Property) {
1680 var inst_ma = new MemberAccess.simple ("new");
1681 inst_ma.value_type = expr.type_reference;
1682 set_cvalue (inst_ma, instance);
1683 store_property ((Property) init.symbol_reference, inst_ma, init.initializer.target_value);
1687 creation_expr = instance;
1690 if (creation_expr != null) {
1691 var temp_var = get_temp_variable (expr.value_type);
1692 var temp_ref = get_variable_cexpression (temp_var.name);
1694 emit_temp_var (temp_var);
1696 ccode.add_assignment (temp_ref, creation_expr);
1697 set_cvalue (expr, temp_ref);
1701 public CCodeExpression? handle_struct_argument (Parameter param, Expression arg, CCodeExpression? cexpr) {
1702 if (arg.formal_target_type is GenericType && !(arg.target_type is GenericType)) {
1703 // we already use a reference for arguments of ref and out parameters
1704 if (param.direction == ParameterDirection.IN) {
1705 var unary = cexpr as CCodeUnaryExpression;
1706 if (unary != null && unary.operator == CCodeUnaryOperator.POINTER_INDIRECTION) {
1707 // *expr => expr
1708 return unary.inner;
1709 } else if (cexpr is CCodeIdentifier || cexpr is CCodeMemberAccess) {
1710 return new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, cexpr);
1711 } else {
1712 // if cexpr is e.g. a function call, we can't take the address of the expression
1713 // (tmp = expr, &tmp)
1714 var ccomma = new CCodeCommaExpression ();
1716 var temp_var = get_temp_variable (arg.target_type);
1717 emit_temp_var (temp_var);
1718 ccomma.append_expression (new CCodeAssignment (get_variable_cexpression (temp_var.name), cexpr));
1719 ccomma.append_expression (new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, new CCodeIdentifier (temp_var.name)));
1721 return ccomma;
1726 return cexpr;
1729 public override void visit_sizeof_expression (SizeofExpression expr) {
1730 var csizeof = new CCodeFunctionCall (new CCodeIdentifier ("sizeof"));
1731 csizeof.add_argument (new CCodeIdentifier (expr.type_reference.get_cname ()));
1732 set_cvalue (expr, csizeof);
1735 public override void visit_typeof_expression (TypeofExpression expr) {
1736 set_cvalue (expr, get_type_id_expression (expr.type_reference));
1739 public override void visit_unary_expression (UnaryExpression expr) {
1740 CCodeUnaryOperator op;
1741 if (expr.operator == UnaryOperator.PLUS) {
1742 op = CCodeUnaryOperator.PLUS;
1743 } else if (expr.operator == UnaryOperator.MINUS) {
1744 op = CCodeUnaryOperator.MINUS;
1745 } else if (expr.operator == UnaryOperator.LOGICAL_NEGATION) {
1746 op = CCodeUnaryOperator.LOGICAL_NEGATION;
1747 } else if (expr.operator == UnaryOperator.BITWISE_COMPLEMENT) {
1748 op = CCodeUnaryOperator.BITWISE_COMPLEMENT;
1749 } else if (expr.operator == UnaryOperator.INCREMENT) {
1750 op = CCodeUnaryOperator.PREFIX_INCREMENT;
1751 } else if (expr.operator == UnaryOperator.DECREMENT) {
1752 op = CCodeUnaryOperator.PREFIX_DECREMENT;
1753 } else if (expr.operator == UnaryOperator.REF) {
1754 op = CCodeUnaryOperator.ADDRESS_OF;
1755 } else if (expr.operator == UnaryOperator.OUT) {
1756 op = CCodeUnaryOperator.ADDRESS_OF;
1757 } else {
1758 assert_not_reached ();
1760 set_cvalue (expr, new CCodeUnaryExpression (op, get_cvalue (expr.inner)));
1763 public override void visit_cast_expression (CastExpression expr) {
1764 if (expr.is_silent_cast) {
1765 if (expr.inner.value_type is ObjectType) {
1766 var ccomma = new CCodeCommaExpression ();
1767 var temp_decl = get_temp_variable (expr.inner.value_type, true, expr);
1769 emit_temp_var (temp_decl);
1771 var ctemp = get_variable_cexpression (temp_decl.name);
1772 var cinit = new CCodeAssignment (ctemp, get_cvalue (expr.inner));
1773 var ccheck = create_type_check (ctemp, expr.type_reference);
1774 var ccast = new CCodeCastExpression (ctemp, expr.type_reference.get_cname ());
1775 var cnull = new CCodeConstant ("NULL");
1777 ccomma.append_expression (cinit);
1778 ccomma.append_expression (new CCodeConditionalExpression (ccheck, ccast, cnull));
1780 set_cvalue (expr, ccomma);
1781 } else {
1782 expr.error = true;
1783 Report.error (expr.source_reference, "Operation not supported for this type");
1785 return;
1788 if (expr.type_reference.data_type != null && expr.type_reference.data_type.get_full_name () == "Dova.Value") {
1789 // box value
1790 var temp_decl = get_temp_variable (expr.inner.value_type, true, expr);
1791 emit_temp_var (temp_decl);
1792 var cvar = get_variable_cexpression (temp_decl.name);
1794 var ccomma = new CCodeCommaExpression ();
1795 ccomma.append_expression (new CCodeAssignment (cvar, get_cvalue (expr.inner)));
1797 var to_any = new CCodeFunctionCall (new CCodeIdentifier ("dova_type_value_to_any"));
1798 to_any.add_argument (get_type_id_expression (expr.inner.value_type));
1799 to_any.add_argument (new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, cvar));
1800 to_any.add_argument (new CCodeConstant ("0"));
1801 ccomma.append_expression (to_any);
1803 set_cvalue (expr, ccomma);
1804 return;
1805 } else if (expr.inner.value_type.data_type != null && expr.inner.value_type.data_type.get_full_name () == "Dova.Value") {
1806 // unbox value
1807 var temp_decl = get_temp_variable (expr.type_reference, true, expr);
1808 emit_temp_var (temp_decl);
1809 var cvar = get_variable_cexpression (temp_decl.name);
1811 var ccomma = new CCodeCommaExpression ();
1813 var sizeof_call = new CCodeFunctionCall (new CCodeIdentifier ("sizeof"));
1814 sizeof_call.add_argument (new CCodeIdentifier (expr.type_reference.get_cname ()));
1816 var to_any = new CCodeFunctionCall (new CCodeIdentifier ("dova_type_value_from_any"));
1817 to_any.add_argument (get_type_id_expression (expr.type_reference));
1818 to_any.add_argument (get_cvalue (expr.inner));
1819 to_any.add_argument (new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, cvar));
1820 to_any.add_argument (new CCodeConstant ("0"));
1821 ccomma.append_expression (to_any);
1823 ccomma.append_expression (cvar);
1825 set_cvalue (expr, ccomma);
1826 return;
1829 generate_type_declaration (expr.type_reference, cfile);
1831 if (expr.inner.value_type is GenericType && !(expr.type_reference is GenericType)) {
1832 // generic types use an extra pointer, dereference that pointer
1833 set_cvalue (expr, new CCodeUnaryExpression (CCodeUnaryOperator.POINTER_INDIRECTION, new CCodeCastExpression (get_cvalue (expr.inner), expr.type_reference.get_cname () + "*")));
1834 } else {
1835 set_cvalue (expr, new CCodeCastExpression (get_cvalue (expr.inner), expr.type_reference.get_cname ()));
1839 public override void visit_pointer_indirection (PointerIndirection expr) {
1840 set_cvalue (expr, new CCodeUnaryExpression (CCodeUnaryOperator.POINTER_INDIRECTION, get_cvalue (expr.inner)));
1843 public override void visit_addressof_expression (AddressofExpression expr) {
1844 set_cvalue (expr, new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, get_cvalue (expr.inner)));
1847 public override void visit_reference_transfer_expression (ReferenceTransferExpression expr) {
1848 /* (tmp = var, var = null, tmp) */
1849 var ccomma = new CCodeCommaExpression ();
1850 var temp_decl = get_temp_variable (expr.value_type, true, expr);
1851 emit_temp_var (temp_decl);
1852 var cvar = get_variable_cexpression (temp_decl.name);
1854 ccomma.append_expression (new CCodeAssignment (cvar, get_cvalue (expr.inner)));
1855 ccomma.append_expression (new CCodeAssignment (get_cvalue (expr.inner), new CCodeConstant ("NULL")));
1856 ccomma.append_expression (cvar);
1857 set_cvalue (expr, ccomma);
1860 public override void visit_binary_expression (BinaryExpression expr) {
1861 var cleft = get_cvalue (expr.left);
1862 var cright = get_cvalue (expr.right);
1864 CCodeBinaryOperator op;
1865 if (expr.operator == BinaryOperator.PLUS) {
1866 op = CCodeBinaryOperator.PLUS;
1867 } else if (expr.operator == BinaryOperator.MINUS) {
1868 op = CCodeBinaryOperator.MINUS;
1869 } else if (expr.operator == BinaryOperator.MUL) {
1870 op = CCodeBinaryOperator.MUL;
1871 } else if (expr.operator == BinaryOperator.DIV) {
1872 op = CCodeBinaryOperator.DIV;
1873 } else if (expr.operator == BinaryOperator.MOD) {
1874 op = CCodeBinaryOperator.MOD;
1875 } else if (expr.operator == BinaryOperator.SHIFT_LEFT) {
1876 op = CCodeBinaryOperator.SHIFT_LEFT;
1877 } else if (expr.operator == BinaryOperator.SHIFT_RIGHT) {
1878 op = CCodeBinaryOperator.SHIFT_RIGHT;
1879 } else if (expr.operator == BinaryOperator.LESS_THAN) {
1880 op = CCodeBinaryOperator.LESS_THAN;
1881 } else if (expr.operator == BinaryOperator.GREATER_THAN) {
1882 op = CCodeBinaryOperator.GREATER_THAN;
1883 } else if (expr.operator == BinaryOperator.LESS_THAN_OR_EQUAL) {
1884 op = CCodeBinaryOperator.LESS_THAN_OR_EQUAL;
1885 } else if (expr.operator == BinaryOperator.GREATER_THAN_OR_EQUAL) {
1886 op = CCodeBinaryOperator.GREATER_THAN_OR_EQUAL;
1887 } else if (expr.operator == BinaryOperator.EQUALITY) {
1888 op = CCodeBinaryOperator.EQUALITY;
1889 } else if (expr.operator == BinaryOperator.INEQUALITY) {
1890 op = CCodeBinaryOperator.INEQUALITY;
1891 } else if (expr.operator == BinaryOperator.BITWISE_AND) {
1892 op = CCodeBinaryOperator.BITWISE_AND;
1893 } else if (expr.operator == BinaryOperator.BITWISE_OR) {
1894 op = CCodeBinaryOperator.BITWISE_OR;
1895 } else if (expr.operator == BinaryOperator.BITWISE_XOR) {
1896 op = CCodeBinaryOperator.BITWISE_XOR;
1897 } else if (expr.operator == BinaryOperator.AND) {
1898 op = CCodeBinaryOperator.AND;
1899 } else if (expr.operator == BinaryOperator.OR) {
1900 op = CCodeBinaryOperator.OR;
1901 } else if (expr.operator == BinaryOperator.IN) {
1902 set_cvalue (expr, new CCodeBinaryExpression (CCodeBinaryOperator.EQUALITY, new CCodeBinaryExpression (CCodeBinaryOperator.BITWISE_AND, cright, cleft), cleft));
1903 return;
1904 } else {
1905 assert_not_reached ();
1908 if (expr.operator == BinaryOperator.EQUALITY ||
1909 expr.operator == BinaryOperator.INEQUALITY) {
1910 var left_type_as_struct = expr.left.value_type.data_type as Struct;
1911 var right_type_as_struct = expr.right.value_type.data_type as Struct;
1913 if (expr.left.value_type.data_type is Class && !((Class) expr.left.value_type.data_type).is_compact &&
1914 expr.right.value_type.data_type is Class && !((Class) expr.right.value_type.data_type).is_compact) {
1915 var left_cl = (Class) expr.left.value_type.data_type;
1916 var right_cl = (Class) expr.right.value_type.data_type;
1918 if (left_cl != right_cl) {
1919 if (left_cl.is_subtype_of (right_cl)) {
1920 cleft = generate_instance_cast (cleft, right_cl);
1921 } else if (right_cl.is_subtype_of (left_cl)) {
1922 cright = generate_instance_cast (cright, left_cl);
1925 } else if (left_type_as_struct != null && right_type_as_struct != null) {
1926 // FIXME generate and use compare/equal function for real structs
1927 if (expr.left.value_type.nullable && expr.right.value_type.nullable) {
1928 // FIXME also compare contents, not just address
1929 } else if (expr.left.value_type.nullable) {
1930 // FIXME check left value is not null
1931 cleft = new CCodeUnaryExpression (CCodeUnaryOperator.POINTER_INDIRECTION, cleft);
1932 } else if (expr.right.value_type.nullable) {
1933 // FIXME check right value is not null
1934 cright = new CCodeUnaryExpression (CCodeUnaryOperator.POINTER_INDIRECTION, cright);
1939 set_cvalue (expr, new CCodeBinaryExpression (op, cleft, cright));
1942 CCodeExpression? create_type_check (CCodeNode ccodenode, DataType type) {
1943 var ccheck = new CCodeFunctionCall (new CCodeIdentifier ("any_is_a"));
1944 ccheck.add_argument ((CCodeExpression) ccodenode);
1945 ccheck.add_argument (get_type_id_expression (type));
1946 return ccheck;
1949 public override void visit_type_check (TypeCheck expr) {
1950 generate_type_declaration (expr.type_reference, cfile);
1952 set_cvalue (expr, create_type_check (get_cvalue (expr.expression), expr.type_reference));
1953 if (get_cvalue (expr) is CCodeInvalidExpression) {
1954 Report.error (expr.source_reference, "type check expressions not supported for compact classes, structs, and enums");
1958 public override void visit_lambda_expression (LambdaExpression l) {
1959 // use instance position from delegate
1960 var dt = (DelegateType) l.target_type;
1961 l.method.cinstance_parameter_position = dt.delegate_symbol.cinstance_parameter_position;
1963 l.accept_children (this);
1965 set_cvalue (l, new CCodeIdentifier (l.method.get_cname ()));
1968 // manage memory and implicit casts
1969 public CCodeExpression transform_expression (CCodeExpression source_cexpr, DataType? expression_type, DataType? target_type, Expression? expr = null) {
1970 var cexpr = source_cexpr;
1971 if (expression_type == null) {
1972 return cexpr;
1976 if (expression_type.value_owned
1977 && (target_type == null || !target_type.value_owned)) {
1978 // value leaked, destroy it
1979 var pointer_type = target_type as PointerType;
1980 if (pointer_type != null && !(pointer_type.base_type is VoidType)) {
1981 // manual memory management for non-void pointers
1982 // treat void* special to not leak memory with void* method parameters
1983 } else if (requires_destroy (expression_type)) {
1984 var decl = get_temp_variable (expression_type, true, expression_type);
1985 emit_temp_var (decl);
1986 temp_ref_vars.insert (0, decl);
1987 cexpr = new CCodeAssignment (get_variable_cexpression (decl.name), cexpr);
1991 if (target_type == null) {
1992 // value will be destroyed, no need for implicit casts
1993 return cexpr;
1996 cexpr = get_implicit_cast_expression (cexpr, expression_type, target_type, expr);
1998 if (target_type.value_owned && !expression_type.value_owned) {
1999 // need to copy value
2000 if (requires_copy (target_type) && !(expression_type is NullType)) {
2001 CodeNode node = expr;
2002 if (node == null) {
2003 node = expression_type;
2005 cexpr = get_ref_cexpression (target_type, cexpr, expr, node);
2009 return cexpr;
2012 public virtual CCodeExpression get_implicit_cast_expression (CCodeExpression source_cexpr, DataType? expression_type, DataType? target_type, Expression? expr = null) {
2013 var cexpr = source_cexpr;
2015 if (expression_type.data_type != null && expression_type.data_type == target_type.data_type) {
2016 // same type, no cast required
2017 return cexpr;
2020 if (expression_type is NullType) {
2021 // null literal, no cast required when not converting to generic type pointer
2022 return cexpr;
2025 generate_type_declaration (target_type, cfile);
2027 if (target_type is DelegateType && expression_type is MethodType) {
2028 var deleg_type = (DelegateType) target_type;
2029 var method_type = (MethodType) expression_type;
2030 CCodeExpression delegate_target;
2031 if (expr is LambdaExpression) {
2032 var lambda = (LambdaExpression) expr;
2033 if (lambda.method.closure) {
2034 int block_id = get_block_id (current_closure_block);
2035 delegate_target = get_variable_cexpression ("_data%d_".printf (block_id));
2036 } else if (get_this_type () != null) {
2037 delegate_target = new CCodeIdentifier ("this");
2038 } else {
2039 delegate_target = new CCodeConstant ("NULL");
2041 } else {
2042 if (method_type.method_symbol.binding == MemberBinding.INSTANCE) {
2043 var ma = (MemberAccess) expr;
2044 delegate_target = (CCodeExpression) get_ccodenode (ma.inner);
2045 } else {
2046 delegate_target = new CCodeConstant ("NULL");
2050 var d = deleg_type.delegate_symbol;
2052 string wrapper_name = "_wrapper%d_".printf (next_wrapper_id++);
2053 var wrapper = new CCodeFunction (wrapper_name);
2054 wrapper.modifiers = CCodeModifiers.STATIC;
2055 var call = new CCodeFunctionCall (source_cexpr);
2057 if (method_type.method_symbol.binding == MemberBinding.INSTANCE) {
2058 wrapper.add_parameter (new CCodeParameter ("this", "void *"));
2059 call.add_argument (new CCodeIdentifier ("this"));
2062 var method_param_iter = method_type.method_symbol.get_parameters ().iterator ();
2063 foreach (Parameter param in d.get_parameters ()) {
2064 method_param_iter.next ();
2065 var method_param = method_param_iter.get ();
2066 string ctype = param.variable_type.get_cname ();
2067 if (param.variable_type is GenericType && !(method_param.variable_type is GenericType)) {
2068 ctype = method_param.variable_type.get_cname () + "*";
2069 call.add_argument (new CCodeUnaryExpression (CCodeUnaryOperator.POINTER_INDIRECTION, new CCodeIdentifier (param.name)));
2070 } else if (!(param.variable_type is GenericType) && method_param.variable_type is GenericType) {
2071 call.add_argument (new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, new CCodeIdentifier (param.name)));
2072 } else {
2073 call.add_argument (new CCodeIdentifier (param.name));
2076 wrapper.add_parameter (new CCodeParameter (param.name, ctype));
2079 wrapper.block = new CCodeBlock ();
2080 if (d.return_type is VoidType) {
2081 wrapper.block.add_statement (new CCodeExpressionStatement (call));
2082 } else {
2083 var method_return_type = method_type.method_symbol.return_type;
2084 if (d.return_type is GenericType && !(method_return_type is GenericType)) {
2085 wrapper.add_parameter (new CCodeParameter ("result", method_return_type.get_cname () + "*"));
2086 wrapper.block.add_statement (new CCodeExpressionStatement (new CCodeAssignment (new CCodeUnaryExpression (CCodeUnaryOperator.POINTER_INDIRECTION, new CCodeIdentifier ("result")), call)));
2087 } else if (!(d.return_type is GenericType) && method_return_type is GenericType) {
2088 wrapper.return_type = d.return_type.get_cname ();
2089 var cdecl = new CCodeDeclaration (d.return_type.get_cname ());
2090 cdecl.add_declarator (new CCodeVariableDeclarator ("result"));
2091 call.add_argument (new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, new CCodeIdentifier ("result")));
2092 wrapper.block.add_statement (new CCodeExpressionStatement (call));
2093 wrapper.block.add_statement (new CCodeReturnStatement (new CCodeIdentifier ("result")));
2094 } else if (d.return_type is GenericType) {
2095 wrapper.add_parameter (new CCodeParameter ("result", "void *"));
2096 wrapper.block.add_statement (new CCodeExpressionStatement (call));
2097 } else {
2098 wrapper.return_type = d.return_type.get_cname ();
2099 wrapper.block.add_statement (new CCodeReturnStatement (call));
2103 cfile.add_function (wrapper);
2105 var ccall = new CCodeFunctionCall (new CCodeIdentifier ("%s_new".printf (deleg_type.delegate_symbol.get_lower_case_cname ())));
2106 ccall.add_argument (delegate_target);
2107 ccall.add_argument (new CCodeIdentifier (wrapper_name));
2108 return ccall;
2111 var cl = target_type.data_type as Class;
2112 var iface = target_type.data_type as Interface;
2113 if (context.checking && (iface != null || (cl != null && !cl.is_compact))) {
2114 // checked cast for strict subtypes of GTypeInstance
2115 return generate_instance_cast (cexpr, target_type.data_type);
2116 } else if (target_type.data_type != null && expression_type.get_cname () != target_type.get_cname ()) {
2117 var st = target_type.data_type as Struct;
2118 if (target_type.data_type.is_reference_type () || (st != null && st.is_simple_type ())) {
2119 // don't cast non-simple structs
2120 return new CCodeCastExpression (cexpr, target_type.get_cname ());
2121 } else {
2122 return cexpr;
2124 } else {
2125 return cexpr;
2129 public void store_property (Property prop, Expression? instance, TargetValue value) {
2130 string set_func;
2132 var base_property = prop;
2133 if (prop.base_property != null) {
2134 base_property = prop.base_property;
2135 } else if (prop.base_interface_property != null) {
2136 base_property = prop.base_interface_property;
2139 generate_property_accessor_declaration (base_property.set_accessor, cfile);
2140 set_func = base_property.set_accessor.get_cname ();
2142 if (!prop.external && prop.external_package) {
2143 // internal VAPI properties
2144 // only add them once per source file
2145 if (add_generated_external_symbol (prop)) {
2146 visit_property (prop);
2150 var ccall = new CCodeFunctionCall (new CCodeIdentifier (set_func));
2152 if (prop.binding == MemberBinding.INSTANCE) {
2153 /* target instance is first argument */
2154 ccall.add_argument ((CCodeExpression) get_ccodenode (instance));
2157 ccall.add_argument (get_cvalue_ (value));
2159 ccode.add_expression (ccall);
2162 public bool add_generated_external_symbol (Symbol external_symbol) {
2163 return generated_external_symbols.add (external_symbol);
2166 public static DataType get_data_type_for_symbol (TypeSymbol sym) {
2167 DataType type = null;
2169 if (sym is Class) {
2170 type = new ObjectType ((Class) sym);
2171 } else if (sym is Interface) {
2172 type = new ObjectType ((Interface) sym);
2173 } else if (sym is Struct) {
2174 var st = (Struct) sym;
2175 if (st.is_boolean_type ()) {
2176 type = new BooleanType (st);
2177 } else if (st.is_integer_type ()) {
2178 type = new IntegerType (st);
2179 } else if (st.is_floating_type ()) {
2180 type = new FloatingType (st);
2181 } else {
2182 type = new StructValueType (st);
2184 } else if (sym is Enum) {
2185 type = new EnumValueType ((Enum) sym);
2186 } else {
2187 Report.error (null, "internal error: `%s' is not a supported type".printf (sym.get_full_name ()));
2188 return new InvalidType ();
2191 return type;
2194 public CCodeExpression? default_value_for_type (DataType type, bool initializer_expression) {
2195 var st = type.data_type as Struct;
2196 var array_type = type as ArrayType;
2197 if (type is GenericType) {
2198 var value_size = new CCodeFunctionCall (new CCodeIdentifier ("dova_type_get_value_size"));
2199 value_size.add_argument (get_type_id_expression (type));
2201 var alloca_call = new CCodeFunctionCall (new CCodeIdentifier ("alloca"));
2202 alloca_call.add_argument (value_size);
2204 var memset_call = new CCodeFunctionCall (new CCodeIdentifier ("memset"));
2205 memset_call.add_argument (alloca_call);
2206 memset_call.add_argument (new CCodeConstant ("0"));
2207 memset_call.add_argument (value_size);
2209 return memset_call;
2210 } else if (initializer_expression && !type.nullable &&
2211 ((st != null && st.get_fields ().size > 0) ||
2212 (array_type != null && array_type.fixed_length))) {
2213 // 0-initialize struct with struct initializer { 0 }
2214 // only allowed as initializer expression in C
2215 var clist = new CCodeInitializerList ();
2216 clist.append (new CCodeConstant ("0"));
2217 return clist;
2218 } else if ((type.data_type != null && type.data_type.is_reference_type ())
2219 || type.nullable
2220 || type is PointerType || type is DelegateType
2221 || (array_type != null && !array_type.fixed_length)) {
2222 return new CCodeConstant ("NULL");
2223 } else if (type.data_type != null && type.data_type.get_default_value () != null) {
2224 return new CCodeConstant (type.data_type.get_default_value ());
2226 return null;
2229 public CCodeExpression? get_ccodenode (Expression node) {
2230 if (get_cvalue (node) == null) {
2231 node.emit (this);
2233 return get_cvalue (node);
2236 public DataType? get_this_type () {
2237 if (current_method != null && current_method.binding == MemberBinding.INSTANCE) {
2238 return current_method.this_parameter.variable_type;
2239 } else if (current_property_accessor != null && current_property_accessor.prop.binding == MemberBinding.INSTANCE) {
2240 return current_property_accessor.prop.this_parameter.variable_type;
2242 return null;
2245 public CCodeExpression generate_instance_cast (CCodeExpression expr, TypeSymbol type) {
2246 return new CCodeCastExpression (expr, type.get_cname () + "*");
2249 public virtual string? get_custom_creturn_type (Method m) {
2250 return null;
2253 public virtual bool method_has_wrapper (Method method) {
2254 return false;
2257 public virtual void add_simple_check (CodeNode node, bool always_fails = false) {
2260 public CCodeExpression? get_cvalue (Expression expr) {
2261 if (expr.target_value == null) {
2262 return null;
2264 var dova_value = (DovaValue) expr.target_value;
2265 return dova_value.cvalue;
2268 public CCodeExpression? get_cvalue_ (TargetValue value) {
2269 var dova_value = (DovaValue) value;
2270 return dova_value.cvalue;
2273 public void set_cvalue (Expression expr, CCodeExpression? cvalue) {
2274 var dova_value = (DovaValue) expr.target_value;
2275 if (dova_value == null) {
2276 dova_value = new DovaValue (expr.value_type);
2277 expr.target_value = dova_value;
2279 dova_value.cvalue = cvalue;
2283 public class Vala.DovaValue : TargetValue {
2284 public CCodeExpression cvalue;
2286 public DovaValue (DataType? value_type = null, CCodeExpression? cvalue = null) {
2287 base (value_type);
2288 this.cvalue = cvalue;