Fix ref_sink of Gtk.Window created with GLib.Object.new
[vala-lang.git] / codegen / valadovabasemodule.vala
blob2a45d2840f2fd3e714f8e88516e9e301838c322f
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 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 ArrayList<LocalVariable> temp_vars = new ArrayList<LocalVariable> ();
34 public ArrayList<LocalVariable> temp_ref_vars = new ArrayList<LocalVariable> ();
35 public int next_temp_var_id;
36 public Map<string,string> variable_name_map = new HashMap<string,string> (str_hash, str_equal);
38 public EmitContext (Symbol? symbol = null) {
39 current_symbol = symbol;
42 public void push_symbol (Symbol symbol) {
43 symbol_stack.add (current_symbol);
44 current_symbol = symbol;
47 public void pop_symbol () {
48 current_symbol = symbol_stack[symbol_stack.size - 1];
49 symbol_stack.remove_at (symbol_stack.size - 1);
53 public CodeContext context { get; set; }
55 public Symbol root_symbol;
57 public EmitContext emit_context = new EmitContext ();
59 List<EmitContext> emit_context_stack = new ArrayList<EmitContext> ();
61 public Symbol current_symbol { get { return emit_context.current_symbol; } }
63 public TryStatement current_try {
64 get { return emit_context.current_try; }
65 set { emit_context.current_try = value; }
68 public TypeSymbol? current_type_symbol {
69 get {
70 var sym = current_symbol;
71 while (sym != null) {
72 if (sym is TypeSymbol) {
73 return (TypeSymbol) sym;
75 sym = sym.parent_symbol;
77 return null;
81 public Class? current_class {
82 get { return current_type_symbol as Class; }
85 public Method? current_method {
86 get {
87 var sym = current_symbol;
88 while (sym is Block) {
89 sym = sym.parent_symbol;
91 return sym as Method;
95 public PropertyAccessor? current_property_accessor {
96 get {
97 var sym = current_symbol;
98 while (sym is Block) {
99 sym = sym.parent_symbol;
101 return sym as PropertyAccessor;
105 public DataType? current_return_type {
106 get {
107 var m = current_method;
108 if (m != null) {
109 return m.return_type;
112 var acc = current_property_accessor;
113 if (acc != null) {
114 if (acc.readable) {
115 return acc.value_type;
116 } else {
117 return void_type;
121 return null;
125 public Block? current_closure_block {
126 get {
127 return next_closure_block (current_symbol);
131 public unowned Block? next_closure_block (Symbol sym) {
132 unowned Block block = null;
133 while (true) {
134 block = sym as Block;
135 if (!(sym is Block || sym is Method)) {
136 // no closure block
137 break;
139 if (block != null && block.captured) {
140 // closure block found
141 break;
143 sym = sym.parent_symbol;
145 return block;
148 public CCodeDeclarationSpace header_declarations;
149 public CCodeDeclarationSpace source_declarations;
151 string? csource_filename;
153 public CCodeFragment source_type_member_definition;
154 public CCodeFragment module_init_fragment;
155 public CCodeFragment instance_init_fragment;
156 public CCodeFragment instance_finalize_fragment;
158 // code nodes to be inserted before the current statement
159 // used by async method calls in coroutines
160 public CCodeFragment pre_statement_fragment;
162 /* all temporary variables */
163 public ArrayList<LocalVariable> temp_vars { get { return emit_context.temp_vars; } }
164 /* temporary variables that own their content */
165 public ArrayList<LocalVariable> temp_ref_vars { get { return emit_context.temp_ref_vars; } }
166 /* (constant) hash table with all reserved identifiers in the generated code */
167 Set<string> reserved_identifiers;
169 public int next_temp_var_id {
170 get { return emit_context.next_temp_var_id; }
171 set { emit_context.next_temp_var_id = value; }
174 public int next_wrapper_id = 0;
175 public bool in_creation_method { get { return current_method is CreationMethod; } }
176 int next_block_id = 0;
177 Map<Block,int> block_map = new HashMap<Block,int> ();
179 public DataType void_type = new VoidType ();
180 public DataType bool_type;
181 public DataType char_type;
182 public DataType short_type;
183 public DataType ushort_type;
184 public DataType int_type;
185 public DataType uint_type;
186 public DataType long_type;
187 public DataType ulong_type;
188 public DataType string_type;
189 public DataType float_type;
190 public DataType double_type;
191 public Class object_class;
192 public Class type_class;
193 public Class value_class;
194 public Class string_class;
195 public Class array_class;
196 public Class delegate_class;
197 public Class error_class;
199 Set<Symbol> generated_external_symbols;
201 public Map<string,string> variable_name_map { get { return emit_context.variable_name_map; } }
203 public DovaBaseModule () {
204 reserved_identifiers = new HashSet<string> (str_hash, str_equal);
206 // C99 keywords
207 reserved_identifiers.add ("_Bool");
208 reserved_identifiers.add ("_Complex");
209 reserved_identifiers.add ("_Imaginary");
210 reserved_identifiers.add ("auto");
211 reserved_identifiers.add ("break");
212 reserved_identifiers.add ("case");
213 reserved_identifiers.add ("char");
214 reserved_identifiers.add ("const");
215 reserved_identifiers.add ("continue");
216 reserved_identifiers.add ("default");
217 reserved_identifiers.add ("do");
218 reserved_identifiers.add ("double");
219 reserved_identifiers.add ("else");
220 reserved_identifiers.add ("enum");
221 reserved_identifiers.add ("extern");
222 reserved_identifiers.add ("float");
223 reserved_identifiers.add ("for");
224 reserved_identifiers.add ("goto");
225 reserved_identifiers.add ("if");
226 reserved_identifiers.add ("inline");
227 reserved_identifiers.add ("int");
228 reserved_identifiers.add ("long");
229 reserved_identifiers.add ("register");
230 reserved_identifiers.add ("restrict");
231 reserved_identifiers.add ("return");
232 reserved_identifiers.add ("short");
233 reserved_identifiers.add ("signed");
234 reserved_identifiers.add ("sizeof");
235 reserved_identifiers.add ("static");
236 reserved_identifiers.add ("struct");
237 reserved_identifiers.add ("switch");
238 reserved_identifiers.add ("typedef");
239 reserved_identifiers.add ("union");
240 reserved_identifiers.add ("unsigned");
241 reserved_identifiers.add ("void");
242 reserved_identifiers.add ("volatile");
243 reserved_identifiers.add ("while");
245 // reserved for Vala naming conventions
246 reserved_identifiers.add ("result");
247 reserved_identifiers.add ("this");
250 public override void emit (CodeContext context) {
251 this.context = context;
253 root_symbol = context.root;
255 bool_type = new BooleanType ((Struct) root_symbol.scope.lookup ("bool"));
256 char_type = new IntegerType ((Struct) root_symbol.scope.lookup ("char"));
257 short_type = new IntegerType ((Struct) root_symbol.scope.lookup ("short"));
258 ushort_type = new IntegerType ((Struct) root_symbol.scope.lookup ("ushort"));
259 int_type = new IntegerType ((Struct) root_symbol.scope.lookup ("int"));
260 uint_type = new IntegerType ((Struct) root_symbol.scope.lookup ("uint"));
261 long_type = new IntegerType ((Struct) root_symbol.scope.lookup ("long"));
262 ulong_type = new IntegerType ((Struct) root_symbol.scope.lookup ("ulong"));
263 float_type = new FloatingType ((Struct) root_symbol.scope.lookup ("float"));
264 double_type = new FloatingType ((Struct) root_symbol.scope.lookup ("double"));
265 string_type = new ObjectType ((Class) root_symbol.scope.lookup ("string"));
267 var dova_ns = (Namespace) root_symbol.scope.lookup ("Dova");
268 object_class = (Class) dova_ns.scope.lookup ("Object");
269 type_class = (Class) dova_ns.scope.lookup ("Type");
270 value_class = (Class) dova_ns.scope.lookup ("Value");
271 string_class = (Class) root_symbol.scope.lookup ("string");
272 array_class = (Class) dova_ns.scope.lookup ("Array");
273 delegate_class = (Class) dova_ns.scope.lookup ("Delegate");
274 error_class = (Class) dova_ns.scope.lookup ("Error");
276 header_declarations = new CCodeDeclarationSpace ();
278 source_declarations = new CCodeDeclarationSpace ();
279 module_init_fragment = new CCodeFragment ();
280 source_type_member_definition = new CCodeFragment ();
282 if (context.nostdpkg) {
283 header_declarations.add_include ("dova-types.h");
284 source_declarations.add_include ("dova-types.h");
285 } else {
286 header_declarations.add_include ("dova-base.h");
287 source_declarations.add_include ("dova-base.h");
290 generated_external_symbols = new HashSet<Symbol> ();
293 /* we're only interested in non-pkg source files */
294 var source_files = context.get_source_files ();
295 foreach (SourceFile file in source_files) {
296 if (!file.external_package) {
297 file.accept (this);
301 if (csource_filename != null) {
302 var writer = new CCodeWriter (csource_filename);
303 if (!writer.open (context.version_header)) {
304 Report.error (null, "unable to open `%s' for writing".printf (writer.filename));
305 return;
307 writer.line_directives = context.debug;
309 writer.write_newline ();
310 source_declarations.include_directives.write (writer);
311 writer.write_newline ();
312 source_declarations.type_declaration.write_combined (writer);
313 writer.write_newline ();
314 source_declarations.type_definition.write_combined (writer);
315 writer.write_newline ();
316 source_declarations.type_member_declaration.write_declaration (writer);
317 writer.write_newline ();
318 source_declarations.type_member_declaration.write (writer);
319 writer.write_newline ();
320 source_declarations.constant_declaration.write_combined (writer);
321 writer.write_newline ();
322 source_type_member_definition.write (writer);
323 writer.write_newline ();
324 writer.close ();
327 source_declarations = null;
328 source_type_member_definition = null;
331 // generate C header file for public API
332 if (context.header_filename != null) {
333 var writer = new CCodeWriter (context.header_filename);
334 if (!writer.open (context.version_header)) {
335 Report.error (null, "unable to open `%s' for writing".printf (writer.filename));
336 return;
338 writer.write_newline ();
340 var once = new CCodeOnceSection (get_define_for_filename (writer.filename));
341 once.append (new CCodeNewline ());
342 once.append (header_declarations.include_directives);
343 once.append (new CCodeNewline ());
345 once.append (new CCodeNewline ());
346 once.append (header_declarations.type_declaration);
347 once.append (new CCodeNewline ());
348 once.append (header_declarations.type_definition);
349 once.append (new CCodeNewline ());
350 once.append (header_declarations.type_member_declaration);
351 once.append (new CCodeNewline ());
352 once.append (header_declarations.constant_declaration);
353 once.append (new CCodeNewline ());
355 once.append (new CCodeNewline ());
356 once.write (writer);
357 writer.close ();
361 public void push_context (EmitContext emit_context) {
362 if (this.emit_context != null) {
363 emit_context_stack.add (this.emit_context);
366 this.emit_context = emit_context;
369 public void pop_context () {
370 if (emit_context_stack.size > 0) {
371 this.emit_context = emit_context_stack[emit_context_stack.size - 1];
372 emit_context_stack.remove_at (emit_context_stack.size - 1);
373 } else {
374 this.emit_context = null;
378 public override void visit_source_file (SourceFile source_file) {
379 if (csource_filename == null) {
380 csource_filename = source_file.get_csource_filename ();
381 } else {
382 var writer = new CCodeWriter (source_file.get_csource_filename ());
383 if (!writer.open (context.version_header)) {
384 Report.error (null, "unable to open `%s' for writing".printf (writer.filename));
385 return;
387 writer.close ();
390 source_file.accept_children (this);
392 if (context.report.get_errors () > 0) {
393 return;
397 private static string get_define_for_filename (string filename) {
398 var define = new StringBuilder ("__");
400 var i = filename;
401 while (i.len () > 0) {
402 var c = i.get_char ();
403 if (c.isalnum () && c < 0x80) {
404 define.append_unichar (c.toupper ());
405 } else {
406 define.append_c ('_');
409 i = i.next_char ();
412 define.append ("__");
414 return define.str;
417 public void generate_enum_declaration (Enum en, CCodeDeclarationSpace decl_space) {
418 if (decl_space.add_symbol_declaration (en, en.get_cname ())) {
419 return;
422 var cenum = new CCodeEnum (en.get_cname ());
424 foreach (EnumValue ev in en.get_values ()) {
425 if (ev.value == null) {
426 cenum.add_value (new CCodeEnumValue (ev.get_cname ()));
427 } else {
428 ev.value.emit (this);
429 cenum.add_value (new CCodeEnumValue (ev.get_cname (), (CCodeExpression) ev.value.ccodenode));
433 decl_space.add_type_definition (cenum);
434 decl_space.add_type_definition (new CCodeNewline ());
437 public override void visit_enum (Enum en) {
438 en.accept_children (this);
440 generate_enum_declaration (en, source_declarations);
442 if (!en.is_internal_symbol ()) {
443 generate_enum_declaration (en, header_declarations);
447 public void generate_constant_declaration (Constant c, CCodeDeclarationSpace decl_space) {
448 if (decl_space.add_symbol_declaration (c, c.get_cname ())) {
449 return;
452 if (!c.external) {
453 c.value.emit (this);
455 if (c.value is InitializerList) {
456 var cdecl = new CCodeDeclaration (c.type_reference.get_const_cname ());
457 var arr = "";
458 if (c.type_reference is ArrayType) {
459 arr = "[]";
461 cdecl.add_declarator (new CCodeVariableDeclarator ("%s%s".printf (c.get_cname (), arr), (CCodeExpression) c.value.ccodenode));
462 cdecl.modifiers = CCodeModifiers.STATIC;
464 decl_space.add_constant_declaration (cdecl);
465 } else {
466 var cdefine = new CCodeMacroReplacement.with_expression (c.get_cname (), (CCodeExpression) c.value.ccodenode);
467 decl_space.add_type_member_declaration (cdefine);
472 public override void visit_constant (Constant c) {
473 generate_constant_declaration (c, source_declarations);
475 if (!c.is_internal_symbol ()) {
476 generate_constant_declaration (c, header_declarations);
480 public void generate_field_declaration (Field f, CCodeDeclarationSpace decl_space) {
481 if (decl_space.add_symbol_declaration (f, f.get_cname ())) {
482 return;
485 generate_type_declaration (f.variable_type, decl_space);
487 string field_ctype = f.variable_type.get_cname ();
488 if (f.is_volatile) {
489 field_ctype = "volatile " + field_ctype;
492 var cdecl = new CCodeDeclaration (field_ctype);
493 cdecl.add_declarator (new CCodeVariableDeclarator (f.get_cname ()));
494 if (f.is_internal_symbol ()) {
495 cdecl.modifiers = CCodeModifiers.STATIC;
496 } else {
497 cdecl.modifiers = CCodeModifiers.EXTERN;
500 if (f.get_attribute ("ThreadLocal") != null) {
501 cdecl.modifiers |= CCodeModifiers.THREAD_LOCAL;
504 decl_space.add_type_member_declaration (cdecl);
507 public override void visit_field (Field f) {
508 if (f.initializer != null) {
509 f.initializer.emit (this);
512 var cl = f.parent_symbol as Class;
514 CCodeExpression lhs = null;
516 string field_ctype = f.variable_type.get_cname ();
517 if (f.is_volatile) {
518 field_ctype = "volatile " + field_ctype;
521 if (f.binding == MemberBinding.INSTANCE) {
522 if (cl != null && f.is_internal_symbol ()) {
523 var priv_call = new CCodeFunctionCall (new CCodeIdentifier ("%s_GET_PRIVATE".printf (cl.get_upper_case_cname (null))));
524 priv_call.add_argument (new CCodeIdentifier ("this"));
525 lhs = new CCodeMemberAccess.pointer (priv_call, f.get_cname ());
526 } else {
527 lhs = new CCodeMemberAccess.pointer (new CCodeIdentifier ("this"), f.get_cname ());
530 if (f.initializer != null) {
531 var rhs = (CCodeExpression) f.initializer.ccodenode;
533 instance_init_fragment.append (new CCodeExpressionStatement (new CCodeAssignment (lhs, rhs)));
535 append_temp_decl (instance_init_fragment, temp_vars);
536 temp_vars.clear ();
539 if (requires_destroy (f.variable_type) && instance_finalize_fragment != null) {
540 var this_access = new MemberAccess.simple ("this");
541 this_access.value_type = get_data_type_for_symbol ((TypeSymbol) f.parent_symbol);
543 var field_st = f.parent_symbol as Struct;
544 if (field_st != null && !field_st.is_simple_type ()) {
545 this_access.ccodenode = new CCodeIdentifier ("(*this)");
546 } else {
547 this_access.ccodenode = new CCodeIdentifier ("this");
550 var ma = new MemberAccess (this_access, f.name);
551 ma.symbol_reference = f;
552 instance_finalize_fragment.append (new CCodeExpressionStatement (get_unref_expression (lhs, f.variable_type, ma)));
554 } else {
555 generate_field_declaration (f, source_declarations);
557 if (!f.is_internal_symbol ()) {
558 generate_field_declaration (f, header_declarations);
561 lhs = new CCodeIdentifier (f.get_cname ());
563 var var_decl = new CCodeVariableDeclarator (f.get_cname ());
564 var_decl.initializer = default_value_for_type (f.variable_type, true);
566 if (f.initializer != null) {
567 var rhs = (CCodeExpression) f.initializer.ccodenode;
569 module_init_fragment.append (new CCodeExpressionStatement (new CCodeAssignment (lhs, rhs)));
571 append_temp_decl (module_init_fragment, temp_vars);
572 temp_vars.clear ();
575 var var_def = new CCodeDeclaration (field_ctype);
576 var_def.add_declarator (var_decl);
577 if (!f.is_internal_symbol ()) {
578 var_def.modifiers = CCodeModifiers.EXTERN;
579 } else {
580 var_def.modifiers = CCodeModifiers.STATIC;
583 if (f.get_attribute ("ThreadLocal") != null) {
584 var_def.modifiers |= CCodeModifiers.THREAD_LOCAL;
587 source_declarations.add_type_member_declaration (var_def);
591 public bool is_constant_ccode_expression (CCodeExpression cexpr) {
592 if (cexpr is CCodeConstant) {
593 return true;
594 } else if (cexpr is CCodeCastExpression) {
595 var ccast = (CCodeCastExpression) cexpr;
596 return is_constant_ccode_expression (ccast.inner);
597 } else if (cexpr is CCodeBinaryExpression) {
598 var cbinary = (CCodeBinaryExpression) cexpr;
599 return is_constant_ccode_expression (cbinary.left) && is_constant_ccode_expression (cbinary.right);
602 var cparenthesized = (cexpr as CCodeParenthesizedExpression);
603 return (null != cparenthesized && is_constant_ccode_expression (cparenthesized.inner));
607 * Returns whether the passed cexpr is a pure expression, i.e. an
608 * expression without side-effects.
610 public bool is_pure_ccode_expression (CCodeExpression cexpr) {
611 if (cexpr is CCodeConstant || cexpr is CCodeIdentifier) {
612 return true;
613 } else if (cexpr is CCodeBinaryExpression) {
614 var cbinary = (CCodeBinaryExpression) cexpr;
615 return is_pure_ccode_expression (cbinary.left) && is_constant_ccode_expression (cbinary.right);
616 } else if (cexpr is CCodeUnaryExpression) {
617 var cunary = (CCodeUnaryExpression) cexpr;
618 switch (cunary.operator) {
619 case CCodeUnaryOperator.PREFIX_INCREMENT:
620 case CCodeUnaryOperator.PREFIX_DECREMENT:
621 case CCodeUnaryOperator.POSTFIX_INCREMENT:
622 case CCodeUnaryOperator.POSTFIX_DECREMENT:
623 return false;
624 default:
625 return is_pure_ccode_expression (cunary.inner);
627 } else if (cexpr is CCodeMemberAccess) {
628 var cma = (CCodeMemberAccess) cexpr;
629 return is_pure_ccode_expression (cma.inner);
630 } else if (cexpr is CCodeElementAccess) {
631 var cea = (CCodeElementAccess) cexpr;
632 return is_pure_ccode_expression (cea.container) && is_pure_ccode_expression (cea.index);
633 } else if (cexpr is CCodeCastExpression) {
634 var ccast = (CCodeCastExpression) cexpr;
635 return is_pure_ccode_expression (ccast.inner);
636 } else if (cexpr is CCodeParenthesizedExpression) {
637 var cparenthesized = (CCodeParenthesizedExpression) cexpr;
638 return is_pure_ccode_expression (cparenthesized.inner);
641 return false;
644 public override void visit_formal_parameter (FormalParameter p) {
647 public override void visit_property (Property prop) {
648 prop.accept_children (this);
651 public void generate_type_declaration (DataType type, CCodeDeclarationSpace decl_space) {
652 if (type is ObjectType) {
653 var object_type = (ObjectType) type;
654 if (object_type.type_symbol is Class) {
655 generate_class_declaration ((Class) object_type.type_symbol, decl_space);
656 } else if (object_type.type_symbol is Interface) {
657 generate_interface_declaration ((Interface) object_type.type_symbol, decl_space);
659 } else if (type is DelegateType) {
660 var deleg_type = (DelegateType) type;
661 var d = deleg_type.delegate_symbol;
662 generate_delegate_declaration (d, decl_space);
663 } else if (type.data_type is Enum) {
664 var en = (Enum) type.data_type;
665 generate_enum_declaration (en, decl_space);
666 } else if (type is ValueType) {
667 var value_type = (ValueType) type;
668 generate_struct_declaration ((Struct) value_type.type_symbol, decl_space);
669 } else if (type is ArrayType) {
670 var array_type = (ArrayType) type;
671 generate_type_declaration (array_type.element_type, decl_space);
672 } else if (type is PointerType) {
673 var pointer_type = (PointerType) type;
674 generate_type_declaration (pointer_type.base_type, decl_space);
677 foreach (DataType type_arg in type.get_type_arguments ()) {
678 generate_type_declaration (type_arg, decl_space);
682 public virtual void generate_struct_declaration (Struct st, CCodeDeclarationSpace decl_space) {
685 public virtual void generate_delegate_declaration (Delegate d, CCodeDeclarationSpace decl_space) {
688 public virtual void generate_cparameters (Method m, CCodeDeclarationSpace decl_space, CCodeFunction func, CCodeFunctionDeclarator? vdeclarator = null, CCodeFunctionCall? vcall = null) {
691 public virtual void generate_property_accessor_declaration (PropertyAccessor acc, CCodeDeclarationSpace decl_space) {
694 public override void visit_destructor (Destructor d) {
695 d.body.emit (this);
697 CCodeFragment cfrag = new CCodeFragment ();
699 cfrag.append (d.body.ccodenode);
701 d.ccodenode = cfrag;
704 public int get_block_id (Block b) {
705 int result = block_map[b];
706 if (result == 0) {
707 result = ++next_block_id;
708 block_map[b] = result;
710 return result;
713 void capture_parameter (FormalParameter param, CCodeStruct data, CCodeBlock cblock, int block_id, CCodeBlock free_block) {
714 generate_type_declaration (param.variable_type, source_declarations);
716 var param_type = param.variable_type.copy ();
717 param_type.value_owned = true;
718 data.add_field (param_type.get_cname (), get_variable_cname (param.name));
720 // create copy if necessary as captured variables may need to be kept alive
721 CCodeExpression cparam = get_variable_cexpression (param.name);
722 if (requires_copy (param_type) && !param.variable_type.value_owned) {
723 var ma = new MemberAccess.simple (param.name);
724 ma.symbol_reference = param;
725 ma.value_type = param.variable_type.copy ();
726 // directly access parameters in ref expressions
727 param.captured = false;
728 cparam = get_ref_cexpression (param.variable_type, cparam, ma, param);
729 param.captured = true;
732 cblock.add_statement (new CCodeExpressionStatement (new CCodeAssignment (new CCodeMemberAccess.pointer (get_variable_cexpression ("_data%d_".printf (block_id)), get_variable_cname (param.name)), cparam)));
734 if (requires_destroy (param_type)) {
735 var ma = new MemberAccess.simple (param.name);
736 ma.symbol_reference = param;
737 ma.value_type = param_type.copy ();
738 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)));
742 public override void visit_block (Block b) {
743 emit_context.push_symbol (b);
745 foreach (Statement stmt in b.get_statements ()) {
746 stmt.emit (this);
749 var local_vars = b.get_local_variables ();
750 foreach (LocalVariable local in local_vars) {
751 local.active = false;
754 var cblock = new CCodeBlock ();
757 if (b.captured) {
758 var parent_block = next_closure_block (b.parent_symbol);
760 int block_id = get_block_id (b);
761 string struct_name = "Block%dData".printf (block_id);
763 var free_block = new CCodeBlock ();
765 var data = new CCodeStruct ("_" + struct_name);
766 data.add_field ("DovaType*", "type");
767 data.add_field ("int", "_ref_count_");
768 if (parent_block != null) {
769 int parent_block_id = get_block_id (parent_block);
771 data.add_field ("Block%dData *".printf (parent_block_id), "_data%d_".printf (parent_block_id));
773 var unref_call = new CCodeFunctionCall (new CCodeIdentifier ("dova_object_unref"));
774 unref_call.add_argument (new CCodeMemberAccess.pointer (new CCodeIdentifier ("_data%d_".printf (block_id)), "_data%d_".printf (parent_block_id)));
775 free_block.add_statement (new CCodeExpressionStatement (unref_call));
776 } else if ((current_method != null && current_method.binding == MemberBinding.INSTANCE) ||
777 (current_property_accessor != null && current_property_accessor.prop.binding == MemberBinding.INSTANCE)) {
778 data.add_field ("%s *".printf (current_class.get_cname ()), "this");
780 var ma = new MemberAccess.simple ("this");
781 ma.symbol_reference = current_class;
782 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)));
784 foreach (var local in local_vars) {
785 if (local.captured) {
786 generate_type_declaration (local.variable_type, source_declarations);
788 data.add_field (local.variable_type.get_cname (), get_variable_cname (local.name) + local.variable_type.get_cdeclarator_suffix ());
791 // free in reverse order
792 for (int i = local_vars.size - 1; i >= 0; i--) {
793 var local = local_vars[i];
794 if (local.captured) {
795 if (requires_destroy (local.variable_type)) {
796 var ma = new MemberAccess.simple (local.name);
797 ma.symbol_reference = local;
798 ma.value_type = local.variable_type.copy ();
799 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)));
804 var data_alloc = new CCodeFunctionCall (new CCodeIdentifier ("dova_object_alloc"));
805 data_alloc.add_argument (new CCodeFunctionCall (new CCodeIdentifier ("block%d_data_type_get".printf (block_id))));
807 var data_decl = new CCodeDeclaration (struct_name + "*");
808 data_decl.add_declarator (new CCodeVariableDeclarator ("_data%d_".printf (block_id), data_alloc));
809 cblock.add_statement (data_decl);
811 if (parent_block != null) {
812 int parent_block_id = get_block_id (parent_block);
814 var ref_call = new CCodeFunctionCall (new CCodeIdentifier ("dova_object_ref"));
815 ref_call.add_argument (get_variable_cexpression ("_data%d_".printf (parent_block_id)));
817 cblock.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)));
818 } else if ((current_method != null && current_method.binding == MemberBinding.INSTANCE) ||
819 (current_property_accessor != null && current_property_accessor.prop.binding == MemberBinding.INSTANCE)) {
820 var ref_call = new CCodeFunctionCall (get_dup_func_expression (new ObjectType (current_class), b.source_reference));
821 ref_call.add_argument (new CCodeIdentifier ("this"));
823 cblock.add_statement (new CCodeExpressionStatement (new CCodeAssignment (new CCodeMemberAccess.pointer (get_variable_cexpression ("_data%d_".printf (block_id)), "this"), ref_call)));
826 if (b.parent_symbol is Method) {
827 var m = (Method) b.parent_symbol;
829 // parameters are captured with the top-level block of the method
830 foreach (var param in m.get_parameters ()) {
831 if (param.captured) {
832 capture_parameter (param, data, cblock, block_id, free_block);
836 var cfrag = new CCodeFragment ();
837 append_temp_decl (cfrag, temp_vars);
838 temp_vars.clear ();
839 cblock.add_statement (cfrag);
840 } else if (b.parent_symbol is PropertyAccessor) {
841 var acc = (PropertyAccessor) b.parent_symbol;
843 if (!acc.readable && acc.value_parameter.captured) {
844 capture_parameter (acc.value_parameter, data, cblock, block_id, free_block);
847 var cfrag = new CCodeFragment ();
848 append_temp_decl (cfrag, temp_vars);
849 temp_vars.clear ();
850 cblock.add_statement (cfrag);
853 var typedef = new CCodeTypeDefinition ("struct _" + struct_name, new CCodeVariableDeclarator (struct_name));
854 source_declarations.add_type_declaration (typedef);
855 source_declarations.add_type_definition (data);
857 var data_free = new CCodeFunctionCall (new CCodeIdentifier ("free"));
858 data_free.add_argument (new CCodeIdentifier ("_data%d_".printf (block_id)));
859 free_block.add_statement (new CCodeExpressionStatement (data_free));
861 // create type_get/finalize functions
862 var type_get_fun = new CCodeFunction ("block%d_data_type_get".printf (block_id), "DovaType*");
863 type_get_fun.modifiers = CCodeModifiers.STATIC;
864 source_declarations.add_type_member_declaration (type_get_fun.copy ());
865 type_get_fun.block = new CCodeBlock ();
867 var cdecl = new CCodeDeclaration ("int");
868 cdecl.add_declarator (new CCodeVariableDeclarator ("_block%d_data_object_offset".printf (block_id), new CCodeConstant ("0")));
869 cdecl.modifiers = CCodeModifiers.STATIC;
870 source_declarations.add_type_member_declaration (cdecl);
872 cdecl = new CCodeDeclaration ("int");
873 cdecl.add_declarator (new CCodeVariableDeclarator ("_block%d_data_type_offset".printf (block_id), new CCodeConstant ("0")));
874 cdecl.modifiers = CCodeModifiers.STATIC;
875 source_declarations.add_type_member_declaration (cdecl);
877 cdecl = new CCodeDeclaration ("DovaType *");
878 cdecl.add_declarator (new CCodeVariableDeclarator ("block%d_data_type".printf (block_id), new CCodeConstant ("NULL")));
879 cdecl.modifiers = CCodeModifiers.STATIC;
880 source_declarations.add_type_member_declaration (cdecl);
882 var type_init_block = new CCodeBlock ();
883 var alloc_call = new CCodeFunctionCall (new CCodeIdentifier ("dova_type_alloc"));
884 alloc_call.add_argument (new CCodeFunctionCall (new CCodeIdentifier ("dova_object_type_get")));
885 alloc_call.add_argument (new CCodeConstant ("sizeof (%s)".printf (struct_name)));
886 alloc_call.add_argument (new CCodeConstant ("0"));
887 alloc_call.add_argument (new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, new CCodeIdentifier ("block%d_data_type".printf (block_id))));
888 alloc_call.add_argument (new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, new CCodeIdentifier ("_block%d_data_object_offset".printf (block_id))));
889 alloc_call.add_argument (new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, new CCodeIdentifier ("_block%d_data_type_offset".printf (block_id))));
890 type_init_block.add_statement (new CCodeExpressionStatement (alloc_call));
891 var type_init_call = new CCodeFunctionCall (new CCodeIdentifier ("dova_object_type_init"));
892 type_init_call.add_argument (new CCodeIdentifier ("block%d_data_type".printf (block_id)));
893 type_init_block.add_statement (new CCodeExpressionStatement (type_init_call));
894 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));
895 type_get_fun.block.add_statement (new CCodeReturnStatement (new CCodeIdentifier ("block%d_data_type".printf (block_id))));
897 source_type_member_definition.append (type_get_fun);
899 var unref_fun = new CCodeFunction ("block%d_data_finalize".printf (block_id), "void");
900 unref_fun.add_parameter (new CCodeFormalParameter ("_data%d_".printf (block_id), struct_name + "*"));
901 unref_fun.modifiers = CCodeModifiers.STATIC;
902 source_declarations.add_type_member_declaration (unref_fun.copy ());
903 unref_fun.block = free_block;
905 source_type_member_definition.append (unref_fun);
908 foreach (CodeNode stmt in b.get_statements ()) {
909 if (stmt.error) {
910 continue;
913 if (stmt.ccodenode is CCodeFragment) {
914 foreach (CCodeNode cstmt in ((CCodeFragment) stmt.ccodenode).get_children ()) {
915 cblock.add_statement (cstmt);
917 } else {
918 cblock.add_statement (stmt.ccodenode);
922 // free in reverse order
923 for (int i = local_vars.size - 1; i >= 0; i--) {
924 var local = local_vars[i];
925 if (!local.floating && !local.captured && requires_destroy (local.variable_type)) {
926 var ma = new MemberAccess.simple (local.name);
927 ma.symbol_reference = local;
928 cblock.add_statement (new CCodeExpressionStatement (get_unref_expression (get_variable_cexpression (local.name), local.variable_type, ma)));
932 if (b.parent_symbol is Method) {
933 var m = (Method) b.parent_symbol;
934 foreach (FormalParameter param in m.get_parameters ()) {
935 if (!param.captured && requires_destroy (param.variable_type) && param.direction == ParameterDirection.IN) {
936 var ma = new MemberAccess.simple (param.name);
937 ma.symbol_reference = param;
938 cblock.add_statement (new CCodeExpressionStatement (get_unref_expression (get_variable_cexpression (param.name), param.variable_type, ma)));
943 if (b.captured) {
944 int block_id = get_block_id (b);
946 var data_unref = new CCodeFunctionCall (new CCodeIdentifier ("dova_object_unref"));
947 data_unref.add_argument (get_variable_cexpression ("_data%d_".printf (block_id)));
948 cblock.add_statement (new CCodeExpressionStatement (data_unref));
951 b.ccodenode = cblock;
953 emit_context.pop_symbol ();
956 public override void visit_empty_statement (EmptyStatement stmt) {
957 stmt.ccodenode = new CCodeEmptyStatement ();
960 public override void visit_declaration_statement (DeclarationStatement stmt) {
961 stmt.declaration.accept (this);
963 stmt.ccodenode = stmt.declaration.ccodenode;
965 var local = stmt.declaration as LocalVariable;
966 if (local != null && local.initializer != null) {
967 create_temp_decl (stmt, local.initializer.temp_vars);
970 create_temp_decl (stmt, temp_vars);
971 temp_vars.clear ();
974 public CCodeExpression get_variable_cexpression (string name) {
975 return new CCodeIdentifier (get_variable_cname (name));
978 public string get_variable_cname (string name) {
979 if (name[0] == '.') {
980 // compiler-internal variable
981 if (!variable_name_map.contains (name)) {
982 variable_name_map.set (name, "_tmp%d_".printf (next_temp_var_id));
983 next_temp_var_id++;
985 return variable_name_map.get (name);
986 } else if (reserved_identifiers.contains (name)) {
987 return "_%s_".printf (name);
988 } else {
989 return name;
993 public override void visit_local_variable (LocalVariable local) {
994 if (local.initializer != null) {
995 local.initializer.emit (this);
997 visit_end_full_expression (local.initializer);
1000 generate_type_declaration (local.variable_type, source_declarations);
1002 CCodeExpression rhs = null;
1003 if (local.initializer != null && local.initializer.ccodenode != null) {
1004 rhs = (CCodeExpression) local.initializer.ccodenode;
1007 var cfrag = new CCodeFragment ();
1009 if (pre_statement_fragment != null) {
1010 cfrag.append (pre_statement_fragment);
1011 pre_statement_fragment = null;
1014 if (local.captured) {
1015 if (local.initializer != null) {
1016 cfrag.append (new CCodeExpressionStatement (new CCodeAssignment (new CCodeMemberAccess.pointer (get_variable_cexpression ("_data%d_".printf (get_block_id ((Block) local.parent_symbol))), get_variable_cname (local.name)), rhs)));
1018 } else {
1019 var cvar = new CCodeVariableDeclarator (get_variable_cname (local.name), rhs, local.variable_type.get_cdeclarator_suffix ());
1021 var cdecl = new CCodeDeclaration (local.variable_type.get_cname ());
1022 cdecl.add_declarator (cvar);
1023 cfrag.append (cdecl);
1025 // try to initialize uninitialized variables
1026 // initialization not necessary for variables stored in closure
1027 if (cvar.initializer == null) {
1028 cvar.initializer = default_value_for_type (local.variable_type, true);
1029 cvar.init0 = true;
1033 if (local.initializer != null && local.initializer.tree_can_fail) {
1034 add_simple_check (local.initializer, cfrag);
1037 local.ccodenode = cfrag;
1039 local.active = true;
1042 public override void visit_initializer_list (InitializerList list) {
1043 if (list.target_type.data_type is Struct) {
1044 /* initializer is used as struct initializer */
1045 var st = (Struct) list.target_type.data_type;
1047 var clist = new CCodeInitializerList ();
1049 var field_it = st.get_fields ().iterator ();
1050 foreach (Expression expr in list.get_initializers ()) {
1051 Field field = null;
1052 while (field == null) {
1053 field_it.next ();
1054 field = field_it.get ();
1055 if (field.binding != MemberBinding.INSTANCE) {
1056 // we only initialize instance fields
1057 field = null;
1061 var cexpr = (CCodeExpression) expr.ccodenode;
1063 string ctype = field.get_ctype ();
1064 if (ctype != null) {
1065 cexpr = new CCodeCastExpression (cexpr, ctype);
1068 clist.append (cexpr);
1071 list.ccodenode = clist;
1072 } else {
1073 var clist = new CCodeInitializerList ();
1074 foreach (Expression expr in list.get_initializers ()) {
1075 clist.append ((CCodeExpression) expr.ccodenode);
1077 list.ccodenode = clist;
1081 public LocalVariable get_temp_variable (DataType type, bool value_owned = true, CodeNode? node_reference = null) {
1082 var var_type = type.copy ();
1083 var_type.value_owned = value_owned;
1084 var local = new LocalVariable (var_type, "_tmp%d_".printf (next_temp_var_id));
1086 if (node_reference != null) {
1087 local.source_reference = node_reference.source_reference;
1090 next_temp_var_id++;
1092 return local;
1095 bool is_in_generic_type (DataType type) {
1096 if (type.type_parameter.parent_symbol is TypeSymbol
1097 && (current_method == null || current_method.binding == MemberBinding.INSTANCE)) {
1098 return true;
1099 } else {
1100 return false;
1104 public CCodeExpression get_type_private_from_type (ObjectTypeSymbol type_symbol, CCodeExpression type_expression) {
1105 if (type_symbol is Class) {
1106 // class
1107 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 ()));
1108 } else {
1109 // interface
1110 var get_interface = new CCodeFunctionCall (new CCodeIdentifier ("dova_type_get_interface"));
1111 get_interface.add_argument (type_expression);
1112 get_interface.add_argument (new CCodeIdentifier ("%s_type".printf (((Interface) type_symbol).get_lower_case_cname ())));
1113 return new CCodeCastExpression (get_interface, "%sTypePrivate *".printf (((Interface) type_symbol).get_cname ()));
1117 public CCodeExpression get_type_id_expression (DataType type, bool is_chainup = false) {
1118 if (type is GenericType) {
1119 string var_name = "%s_type".printf (type.type_parameter.name.down ());
1120 if (is_in_generic_type (type) && !is_chainup) {
1121 return new CCodeMemberAccess.pointer (get_type_private_from_type ((ObjectTypeSymbol) type.type_parameter.parent_symbol, new CCodeMemberAccess.pointer (new CCodeIdentifier ("this"), "type")), var_name);
1122 } else {
1123 return new CCodeIdentifier (var_name);
1125 } else {
1126 var ccall = new CCodeFunctionCall (new CCodeIdentifier ("%s_type_get".printf (type.data_type.get_lower_case_cname ())));
1127 var object_type_symbol = type.data_type as ObjectTypeSymbol;
1128 if (object_type_symbol != null) {
1129 for (int i = 0; i < object_type_symbol.get_type_parameters ().size; i++) {
1130 if (type.get_type_arguments ().size == 0) {
1131 ccall.add_argument (new CCodeConstant ("NULL"));
1132 } else {
1133 ccall.add_argument (get_type_id_expression (type.get_type_arguments ().get (i)));
1137 return ccall;
1141 public virtual CCodeExpression? get_dup_func_expression (DataType type, SourceReference? source_reference, bool is_chainup = false) {
1142 if (type.data_type != null) {
1143 string dup_function = "";
1144 if (type.data_type.is_reference_counting ()) {
1145 dup_function = type.data_type.get_ref_function ();
1146 } else if (type is ValueType) {
1147 dup_function = type.data_type.get_dup_function ();
1148 if (dup_function == null) {
1149 dup_function = "";
1153 return new CCodeIdentifier (dup_function);
1154 } else if (type.type_parameter != null) {
1155 return null;
1156 } else if (type is ArrayType) {
1157 return new CCodeIdentifier ("dova_object_ref");
1158 } else if (type is DelegateType) {
1159 return new CCodeIdentifier ("dova_object_ref");
1160 } else if (type is PointerType) {
1161 var pointer_type = (PointerType) type;
1162 return get_dup_func_expression (pointer_type.base_type, source_reference);
1163 } else {
1164 return new CCodeConstant ("NULL");
1168 public CCodeExpression? get_destroy_func_expression (DataType type, bool is_chainup = false) {
1169 if (type.data_type != null) {
1170 string unref_function;
1171 if (type is ReferenceType) {
1172 if (type.data_type.is_reference_counting ()) {
1173 unref_function = type.data_type.get_unref_function ();
1174 } else {
1175 unref_function = type.data_type.get_free_function ();
1177 } else {
1178 if (type.nullable) {
1179 unref_function = type.data_type.get_free_function ();
1180 if (unref_function == null) {
1181 unref_function = "free";
1183 } else {
1184 var st = (Struct) type.data_type;
1185 unref_function = st.get_copy_function ();
1188 if (unref_function == null) {
1189 return new CCodeConstant ("NULL");
1191 return new CCodeIdentifier (unref_function);
1192 } else if (type.type_parameter != null && current_type_symbol is Class) {
1193 // FIXME ask type for dup/ref function
1194 return new CCodeIdentifier ("dova_object_unref");
1195 } else if (type is ArrayType) {
1196 return new CCodeIdentifier ("dova_object_unref");
1197 } else if (type is DelegateType) {
1198 return new CCodeIdentifier ("dova_object_unref");
1199 } else if (type is PointerType) {
1200 return new CCodeIdentifier ("free");
1201 } else {
1202 return new CCodeConstant ("NULL");
1206 public virtual CCodeExpression get_unref_expression (CCodeExpression cvar, DataType type, Expression? expr = null) {
1207 var ccall = new CCodeFunctionCall (get_destroy_func_expression (type));
1209 if (type is ValueType && !type.nullable) {
1210 // normal value type, no null check
1211 ccall.add_argument (new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, cvar));
1212 ccall.add_argument (new CCodeConstant ("0"));
1213 ccall.add_argument (new CCodeConstant ("NULL"));
1214 ccall.add_argument (new CCodeConstant ("0"));
1216 return ccall;
1219 /* (foo == NULL ? NULL : foo = (unref (foo), NULL)) */
1221 /* can be simplified to
1222 * foo = (unref (foo), NULL)
1223 * if foo is of static type non-null
1226 var cisnull = new CCodeBinaryExpression (CCodeBinaryOperator.EQUALITY, cvar, new CCodeConstant ("NULL"));
1227 if (type.type_parameter != null) {
1228 if (!(current_type_symbol is Class) || current_class.is_compact) {
1229 return new CCodeConstant ("NULL");
1232 // unref functions are optional for type parameters
1233 var cunrefisnull = new CCodeBinaryExpression (CCodeBinaryOperator.EQUALITY, get_destroy_func_expression (type), new CCodeConstant ("NULL"));
1234 cisnull = new CCodeBinaryExpression (CCodeBinaryOperator.OR, cisnull, cunrefisnull);
1237 ccall.add_argument (cvar);
1239 /* set freed references to NULL to prevent further use */
1240 var ccomma = new CCodeCommaExpression ();
1242 ccomma.append_expression (ccall);
1243 ccomma.append_expression (new CCodeConstant ("NULL"));
1245 var cassign = new CCodeAssignment (cvar, ccomma);
1247 return new CCodeConditionalExpression (cisnull, new CCodeConstant ("NULL"), cassign);
1250 public override void visit_end_full_expression (Expression expr) {
1251 /* expr is a full expression, i.e. an initializer, the
1252 * expression in an expression statement, the controlling
1253 * expression in if, while, for, or foreach statements
1255 * we unref temporary variables at the end of a full
1256 * expression
1259 /* can't automatically deep copy lists yet, so do it
1260 * manually for now
1261 * replace with
1262 * expr.temp_vars = temp_vars;
1263 * when deep list copying works
1265 if (temp_vars.size > 0) {
1266 if (expr.temp_vars == null) {
1267 expr.temp_vars = new ArrayList<LocalVariable> ();
1268 } else {
1269 expr.temp_vars.clear ();
1271 foreach (LocalVariable local in temp_vars) {
1272 expr.temp_vars.add (local);
1274 temp_vars.clear ();
1277 if (((List<LocalVariable>) temp_ref_vars).size == 0) {
1278 /* nothing to do without temporary variables */
1279 return;
1282 var expr_type = expr.value_type;
1283 if (expr.target_type != null) {
1284 expr_type = expr.target_type;
1287 var full_expr_var = get_temp_variable (expr_type, true, expr);
1288 expr.add_temp_var (full_expr_var);
1290 var expr_list = new CCodeCommaExpression ();
1291 expr_list.append_expression (new CCodeAssignment (get_variable_cexpression (full_expr_var.name), (CCodeExpression) expr.ccodenode));
1293 foreach (LocalVariable local in temp_ref_vars) {
1294 var ma = new MemberAccess.simple (local.name);
1295 ma.symbol_reference = local;
1296 expr_list.append_expression (get_unref_expression (get_variable_cexpression (local.name), local.variable_type, ma));
1299 expr_list.append_expression (get_variable_cexpression (full_expr_var.name));
1301 expr.ccodenode = expr_list;
1303 temp_ref_vars.clear ();
1306 public void append_temp_decl (CCodeFragment cfrag, List<LocalVariable>? temp_vars) {
1307 if (temp_vars == null) {
1308 return;
1310 foreach (LocalVariable local in temp_vars) {
1311 var cdecl = new CCodeDeclaration (local.variable_type.get_cname ());
1313 var vardecl = new CCodeVariableDeclarator (local.name, null, local.variable_type.get_cdeclarator_suffix ());
1314 // sets #line
1315 local.ccodenode = vardecl;
1316 cdecl.add_declarator (vardecl);
1318 var st = local.variable_type.data_type as Struct;
1319 var array_type = local.variable_type as ArrayType;
1321 if (local.name.has_prefix ("*")) {
1322 // do not dereference unintialized variable
1323 // initialization is not needed for these special
1324 // pointer temp variables
1325 // used to avoid side-effects in assignments
1326 } else if (local.variable_type is GenericType) {
1327 var value_size = new CCodeFunctionCall (new CCodeIdentifier ("dova_type_get_value_size"));
1328 value_size.add_argument (get_type_id_expression (local.variable_type));
1330 var alloca_call = new CCodeFunctionCall (new CCodeIdentifier ("alloca"));
1331 alloca_call.add_argument (value_size);
1333 var memset_call = new CCodeFunctionCall (new CCodeIdentifier ("memset"));
1334 memset_call.add_argument (alloca_call);
1335 memset_call.add_argument (new CCodeConstant ("0"));
1336 memset_call.add_argument (value_size);
1338 vardecl.initializer = memset_call;
1339 vardecl.init0 = true;
1340 } else if (!local.variable_type.nullable &&
1341 (st != null && st.get_fields ().size > 0) ||
1342 (array_type != null && array_type.fixed_length)) {
1343 // 0-initialize struct with struct initializer { 0 }
1344 // necessary as they will be passed by reference
1345 var clist = new CCodeInitializerList ();
1346 clist.append (new CCodeConstant ("0"));
1348 vardecl.initializer = clist;
1349 vardecl.init0 = true;
1350 } else if (local.variable_type.is_reference_type_or_type_parameter () ||
1351 local.variable_type.nullable) {
1352 vardecl.initializer = new CCodeConstant ("NULL");
1353 vardecl.init0 = true;
1356 cfrag.append (cdecl);
1360 public override void visit_expression_statement (ExpressionStatement stmt) {
1361 if (stmt.expression.error) {
1362 stmt.error = true;
1363 return;
1366 stmt.ccodenode = new CCodeExpressionStatement ((CCodeExpression) stmt.expression.ccodenode);
1368 if (stmt.tree_can_fail && stmt.expression.tree_can_fail) {
1369 // simple case, no node breakdown necessary
1371 var cfrag = new CCodeFragment ();
1373 cfrag.append (stmt.ccodenode);
1375 add_simple_check (stmt.expression, cfrag);
1377 stmt.ccodenode = cfrag;
1380 /* free temporary objects */
1382 if (((List<LocalVariable>) temp_vars).size == 0
1383 && pre_statement_fragment == null) {
1384 /* nothing to do without temporary variables */
1385 return;
1388 var cfrag = new CCodeFragment ();
1389 append_temp_decl (cfrag, temp_vars);
1391 if (pre_statement_fragment != null) {
1392 cfrag.append (pre_statement_fragment);
1393 pre_statement_fragment = null;
1396 cfrag.append (stmt.ccodenode);
1398 foreach (LocalVariable local in temp_ref_vars) {
1399 var ma = new MemberAccess.simple (local.name);
1400 ma.symbol_reference = local;
1401 cfrag.append (new CCodeExpressionStatement (get_unref_expression (new CCodeIdentifier (local.name), local.variable_type, ma)));
1404 stmt.ccodenode = cfrag;
1406 temp_vars.clear ();
1407 temp_ref_vars.clear ();
1410 public void create_temp_decl (Statement stmt, List<LocalVariable>? temp_vars) {
1411 /* declare temporary variables */
1413 if (temp_vars == null || temp_vars.size == 0) {
1414 /* nothing to do without temporary variables */
1415 return;
1418 var cfrag = new CCodeFragment ();
1419 append_temp_decl (cfrag, temp_vars);
1421 cfrag.append (stmt.ccodenode);
1423 stmt.ccodenode = cfrag;
1426 public virtual void append_local_free (Symbol sym, CCodeFragment cfrag, bool stop_at_loop = false) {
1427 var b = (Block) sym;
1429 var local_vars = b.get_local_variables ();
1430 // free in reverse order
1431 for (int i = local_vars.size - 1; i >= 0; i--) {
1432 var local = local_vars[i];
1433 if (local.active && !local.floating && !local.captured && requires_destroy (local.variable_type)) {
1434 var ma = new MemberAccess.simple (local.name);
1435 ma.symbol_reference = local;
1436 cfrag.append (new CCodeExpressionStatement (get_unref_expression (get_variable_cexpression (local.name), local.variable_type, ma)));
1440 if (b.captured) {
1441 int block_id = get_block_id (b);
1443 var data_unref = new CCodeFunctionCall (new CCodeIdentifier ("dova_object_unref"));
1444 data_unref.add_argument (get_variable_cexpression ("_data%d_".printf (block_id)));
1445 cfrag.append (new CCodeExpressionStatement (data_unref));
1448 if (stop_at_loop) {
1449 if (b.parent_node is Loop ||
1450 b.parent_node is ForeachStatement ||
1451 b.parent_node is SwitchStatement) {
1452 return;
1456 if (sym.parent_symbol is Block) {
1457 append_local_free (sym.parent_symbol, cfrag, stop_at_loop);
1458 } else if (sym.parent_symbol is Method) {
1459 append_param_free ((Method) sym.parent_symbol, cfrag);
1463 public void append_error_free (Symbol sym, CCodeFragment cfrag, TryStatement current_try) {
1464 var b = (Block) sym;
1466 var local_vars = b.get_local_variables ();
1467 // free in reverse order
1468 for (int i = local_vars.size - 1; i >= 0; i--) {
1469 var local = local_vars[i];
1470 if (local.active && !local.floating && !local.captured && requires_destroy (local.variable_type)) {
1471 var ma = new MemberAccess.simple (local.name);
1472 ma.symbol_reference = local;
1473 cfrag.append (new CCodeExpressionStatement (get_unref_expression (get_variable_cexpression (local.name), local.variable_type, ma)));
1477 if (b.captured) {
1478 int block_id = get_block_id (b);
1480 var data_unref = new CCodeFunctionCall (new CCodeIdentifier ("dova_object_unref"));
1481 data_unref.add_argument (get_variable_cexpression ("_data%d_".printf (block_id)));
1482 cfrag.append (new CCodeExpressionStatement (data_unref));
1485 if (sym == current_try.body) {
1486 return;
1489 if (sym.parent_symbol is Block) {
1490 append_error_free (sym.parent_symbol, cfrag, current_try);
1491 } else if (sym.parent_symbol is Method) {
1492 append_param_free ((Method) sym.parent_symbol, cfrag);
1496 private void append_param_free (Method m, CCodeFragment cfrag) {
1497 foreach (FormalParameter param in m.get_parameters ()) {
1498 if (requires_destroy (param.variable_type) && param.direction == ParameterDirection.IN) {
1499 var ma = new MemberAccess.simple (param.name);
1500 ma.symbol_reference = param;
1501 cfrag.append (new CCodeExpressionStatement (get_unref_expression (get_variable_cexpression (param.name), param.variable_type, ma)));
1506 public void create_local_free (CodeNode stmt, bool stop_at_loop = false) {
1507 var cfrag = new CCodeFragment ();
1509 append_local_free (current_symbol, cfrag, stop_at_loop);
1511 cfrag.append (stmt.ccodenode);
1512 stmt.ccodenode = cfrag;
1515 public override void visit_return_statement (ReturnStatement stmt) {
1516 var cfrag = new CCodeFragment ();
1518 // free local variables
1519 append_local_free (current_symbol, cfrag);
1521 cfrag.append (new CCodeReturnStatement ((current_return_type is VoidType) ? null : new CCodeIdentifier ("result")));
1523 stmt.ccodenode = cfrag;
1526 public override void visit_delete_statement (DeleteStatement stmt) {
1527 var pointer_type = (PointerType) stmt.expression.value_type;
1528 DataType type = pointer_type;
1529 if (pointer_type.base_type.data_type != null && pointer_type.base_type.data_type.is_reference_type ()) {
1530 type = pointer_type.base_type;
1533 var ccall = new CCodeFunctionCall (get_destroy_func_expression (type));
1534 ccall.add_argument ((CCodeExpression) stmt.expression.ccodenode);
1535 stmt.ccodenode = new CCodeExpressionStatement (ccall);
1538 public override void visit_expression (Expression expr) {
1539 if (expr.ccodenode != null && !expr.lvalue) {
1540 // memory management, implicit casts, and boxing/unboxing
1541 expr.ccodenode = transform_expression ((CCodeExpression) expr.ccodenode, expr.value_type, expr.target_type, expr);
1545 public override void visit_boolean_literal (BooleanLiteral expr) {
1546 expr.ccodenode = new CCodeConstant (expr.value ? "true" : "false");
1549 public override void visit_character_literal (CharacterLiteral expr) {
1550 if (expr.get_char () >= 0x20 && expr.get_char () < 0x80) {
1551 expr.ccodenode = new CCodeConstant (expr.value);
1552 } else {
1553 expr.ccodenode = new CCodeConstant ("%uU".printf (expr.get_char ()));
1557 public override void visit_integer_literal (IntegerLiteral expr) {
1558 expr.ccodenode = new CCodeConstant (expr.value);
1561 public override void visit_real_literal (RealLiteral expr) {
1562 string c_literal = expr.value;
1563 if (c_literal.has_suffix ("d") || c_literal.has_suffix ("D")) {
1564 // there is no suffix for double in C
1565 c_literal = c_literal.substring (0, c_literal.length - 1);
1567 if (!("." in c_literal || "e" in c_literal || "E" in c_literal)) {
1568 // C requires period or exponent part for floating constants
1569 if ("f" in c_literal || "F" in c_literal) {
1570 c_literal = c_literal.substring (0, c_literal.length - 1) + ".f";
1571 } else {
1572 c_literal += ".";
1575 expr.ccodenode = new CCodeConstant (c_literal);
1578 public override void visit_string_literal (StringLiteral expr) {
1579 // FIXME handle escaped characters in scanner/parser and escape them here again for C
1580 var cliteral = new CCodeConstant ("\"\\0\" " + expr.value);
1582 var cbinary = new CCodeBinaryExpression (CCodeBinaryOperator.PLUS, cliteral, new CCodeConstant ("1"));
1583 expr.ccodenode = new CCodeCastExpression (cbinary, "string_t");
1586 public override void visit_null_literal (NullLiteral expr) {
1587 expr.ccodenode = new CCodeConstant ("NULL");
1590 public override void visit_base_access (BaseAccess expr) {
1591 generate_type_declaration (expr.value_type, source_declarations);
1592 expr.ccodenode = new CCodeCastExpression (new CCodeIdentifier ("this"), expr.value_type.get_cname ());
1595 public override void visit_postfix_expression (PostfixExpression expr) {
1596 MemberAccess ma = find_property_access (expr.inner);
1597 if (ma != null) {
1598 // property postfix expression
1599 var prop = (Property) ma.symbol_reference;
1601 var ccomma = new CCodeCommaExpression ();
1603 // assign current value to temp variable
1604 var temp_decl = get_temp_variable (prop.property_type, true, expr);
1605 temp_vars.add (temp_decl);
1606 ccomma.append_expression (new CCodeAssignment (get_variable_cexpression (temp_decl.name), (CCodeExpression) expr.inner.ccodenode));
1608 // increment/decrement property
1609 var op = expr.increment ? CCodeBinaryOperator.PLUS : CCodeBinaryOperator.MINUS;
1610 var cexpr = new CCodeBinaryExpression (op, get_variable_cexpression (temp_decl.name), new CCodeConstant ("1"));
1611 var ccall = get_property_set_call (prop, ma, cexpr);
1612 ccomma.append_expression (ccall);
1614 // return previous value
1615 ccomma.append_expression (new CCodeIdentifier (temp_decl.name));
1617 expr.ccodenode = ccomma;
1618 return;
1621 var op = expr.increment ? CCodeUnaryOperator.POSTFIX_INCREMENT : CCodeUnaryOperator.POSTFIX_DECREMENT;
1623 expr.ccodenode = new CCodeUnaryExpression (op, (CCodeExpression) expr.inner.ccodenode);
1626 private MemberAccess? find_property_access (Expression expr) {
1627 if (!(expr is MemberAccess)) {
1628 return null;
1631 var ma = (MemberAccess) expr;
1632 if (ma.symbol_reference is Property) {
1633 return ma;
1636 return null;
1639 public bool requires_copy (DataType type) {
1640 if (!type.is_disposable ()) {
1641 return false;
1644 var cl = type.data_type as Class;
1645 if (cl != null && cl.is_reference_counting ()
1646 && cl.get_ref_function () == "") {
1647 // empty ref_function => no ref necessary
1648 return false;
1651 if (type.type_parameter != null) {
1652 return false;
1655 return true;
1658 public bool requires_destroy (DataType type) {
1659 if (!type.is_disposable ()) {
1660 return false;
1663 var array_type = type as ArrayType;
1664 if (array_type != null && array_type.inline_allocated) {
1665 return requires_destroy (array_type.element_type);
1668 var cl = type.data_type as Class;
1669 if (cl != null && cl.is_reference_counting ()
1670 && cl.get_unref_function () == "") {
1671 // empty unref_function => no unref necessary
1672 return false;
1675 if (type.type_parameter != null) {
1676 return false;
1679 return true;
1682 bool is_ref_function_void (DataType type) {
1683 var cl = type.data_type as Class;
1684 if (cl != null && cl.ref_function_void) {
1685 return true;
1686 } else {
1687 return false;
1691 public virtual CCodeExpression? get_ref_cexpression (DataType expression_type, CCodeExpression cexpr, Expression? expr, CodeNode node) {
1692 if (expression_type is ValueType && !expression_type.nullable) {
1693 // normal value type, no null check
1694 // (copy (&temp, 0, &expr, 0), temp)
1696 var decl = get_temp_variable (expression_type, false, node);
1697 temp_vars.add (decl);
1699 var ctemp = get_variable_cexpression (decl.name);
1701 var vt = (ValueType) expression_type;
1702 var st = (Struct) vt.type_symbol;
1703 var copy_call = new CCodeFunctionCall (new CCodeIdentifier (st.get_copy_function ()));
1704 copy_call.add_argument (new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, ctemp));
1705 copy_call.add_argument (new CCodeConstant ("0"));
1706 copy_call.add_argument (new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, cexpr));
1707 copy_call.add_argument (new CCodeConstant ("0"));
1709 var ccomma = new CCodeCommaExpression ();
1711 ccomma.append_expression (copy_call);
1712 ccomma.append_expression (ctemp);
1714 return ccomma;
1717 /* (temp = expr, temp == NULL ? NULL : ref (temp))
1719 * can be simplified to
1720 * ref (expr)
1721 * if static type of expr is non-null
1724 var dupexpr = get_dup_func_expression (expression_type, node.source_reference);
1726 if (dupexpr == null) {
1727 node.error = true;
1728 return null;
1731 var ccall = new CCodeFunctionCall (dupexpr);
1733 if (expr != null && expr.is_non_null ()
1734 && !is_ref_function_void (expression_type)) {
1735 // expression is non-null
1736 ccall.add_argument ((CCodeExpression) expr.ccodenode);
1738 return ccall;
1739 } else {
1740 var decl = get_temp_variable (expression_type, false, node);
1741 temp_vars.add (decl);
1743 var ctemp = get_variable_cexpression (decl.name);
1745 var cisnull = new CCodeBinaryExpression (CCodeBinaryOperator.EQUALITY, ctemp, new CCodeConstant ("NULL"));
1746 if (expression_type.type_parameter != null) {
1747 // dup functions are optional for type parameters
1748 var cdupisnull = new CCodeBinaryExpression (CCodeBinaryOperator.EQUALITY, get_dup_func_expression (expression_type, node.source_reference), new CCodeConstant ("NULL"));
1749 cisnull = new CCodeBinaryExpression (CCodeBinaryOperator.OR, cisnull, cdupisnull);
1752 ccall.add_argument (ctemp);
1754 var ccomma = new CCodeCommaExpression ();
1755 ccomma.append_expression (new CCodeAssignment (ctemp, cexpr));
1757 var cifnull = new CCodeConstant ("NULL");
1758 ccomma.append_expression (new CCodeConditionalExpression (cisnull, cifnull, ccall));
1760 // repeat temp variable at the end of the comma expression
1761 // if the ref function returns void
1762 if (is_ref_function_void (expression_type)) {
1763 ccomma.append_expression (ctemp);
1766 return ccomma;
1770 public virtual void generate_class_declaration (Class cl, CCodeDeclarationSpace decl_space) {
1771 if (decl_space.add_symbol_declaration (cl, cl.get_cname ())) {
1772 return;
1776 public virtual void generate_interface_declaration (Interface iface, CCodeDeclarationSpace decl_space) {
1779 public virtual void generate_method_declaration (Method m, CCodeDeclarationSpace decl_space) {
1782 public void add_generic_type_arguments (CCodeFunctionCall ccall, List<DataType> type_args, CodeNode expr, bool is_chainup = false) {
1783 foreach (var type_arg in type_args) {
1784 ccall.add_argument (get_type_id_expression (type_arg, is_chainup));
1788 public override void visit_object_creation_expression (ObjectCreationExpression expr) {
1789 CCodeExpression instance = null;
1790 CCodeExpression creation_expr = null;
1792 var st = expr.type_reference.data_type as Struct;
1794 bool struct_by_ref = false;
1795 if (st != null && !st.is_boolean_type () && !st.is_integer_type () && !st.is_floating_type ()) {
1796 struct_by_ref = true;
1799 if (struct_by_ref || expr.get_object_initializer ().size > 0) {
1800 // value-type initialization or object creation expression with object initializer
1801 var temp_decl = get_temp_variable (expr.type_reference, false, expr);
1802 temp_vars.add (temp_decl);
1804 instance = get_variable_cexpression (get_variable_cname (temp_decl.name));
1807 if (expr.symbol_reference == null) {
1808 // no creation method
1809 if (expr.type_reference.data_type is Struct) {
1810 var creation_call = new CCodeFunctionCall (new CCodeIdentifier ("memset"));
1811 creation_call.add_argument (new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, instance));
1812 creation_call.add_argument (new CCodeConstant ("0"));
1813 creation_call.add_argument (new CCodeIdentifier ("sizeof (%s)".printf (expr.type_reference.get_cname ())));
1815 creation_expr = creation_call;
1817 } else if (expr.symbol_reference is Method) {
1818 // use creation method
1819 var m = (Method) expr.symbol_reference;
1820 var params = m.get_parameters ();
1821 CCodeFunctionCall creation_call;
1823 generate_method_declaration (m, source_declarations);
1825 var cl = expr.type_reference.data_type as Class;
1827 if (!m.has_new_function) {
1828 // use construct function directly
1829 creation_call = new CCodeFunctionCall (new CCodeIdentifier (m.get_real_cname ()));
1830 creation_call.add_argument (new CCodeIdentifier (cl.get_type_id ()));
1831 } else {
1832 creation_call = new CCodeFunctionCall (new CCodeIdentifier (m.get_cname ()));
1835 if (struct_by_ref && !(m.cinstance_parameter_position < 0)) {
1836 creation_call.add_argument (new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, instance));
1839 generate_type_declaration (expr.type_reference, source_declarations);
1841 if (cl != null && !cl.is_compact) {
1842 add_generic_type_arguments (creation_call, expr.type_reference.get_type_arguments (), expr);
1845 bool ellipsis = false;
1847 int i = 1;
1848 Iterator<FormalParameter> params_it = params.iterator ();
1849 foreach (Expression arg in expr.get_argument_list ()) {
1850 CCodeExpression cexpr = (CCodeExpression) arg.ccodenode;
1851 FormalParameter param = null;
1852 if (params_it.next ()) {
1853 param = params_it.get ();
1854 ellipsis = param.ellipsis;
1855 if (!ellipsis) {
1856 cexpr = handle_struct_argument (param, arg, cexpr);
1860 creation_call.add_argument (cexpr);
1862 i++;
1864 while (params_it.next ()) {
1865 var param = params_it.get ();
1867 if (param.ellipsis) {
1868 ellipsis = true;
1869 break;
1872 if (param.initializer == null) {
1873 Report.error (expr.source_reference, "no default expression for argument %d".printf (i));
1874 return;
1877 /* evaluate default expression here as the code
1878 * generator might not have visited the formal
1879 * parameter yet */
1880 param.initializer.emit (this);
1882 creation_call.add_argument ((CCodeExpression) param.initializer.ccodenode);
1883 i++;
1886 if (struct_by_ref && m.cinstance_parameter_position < 0) {
1887 // instance parameter is at the end in a struct creation method
1888 creation_call.add_argument (new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, instance));
1891 if (ellipsis) {
1892 /* ensure variable argument list ends with NULL
1893 * except when using printf-style arguments */
1894 if (!m.printf_format && !m.scanf_format && m.sentinel != "") {
1895 creation_call.add_argument (new CCodeConstant (m.sentinel));
1899 creation_expr = creation_call;
1901 // cast the return value of the creation method back to the intended type if
1902 // it requested a special C return type
1903 if (get_custom_creturn_type (m) != null) {
1904 creation_expr = new CCodeCastExpression (creation_expr, expr.type_reference.get_cname ());
1906 } else {
1907 assert (false);
1910 if (instance != null) {
1911 var ccomma = new CCodeCommaExpression ();
1913 if (expr.type_reference.data_type is Struct) {
1914 ccomma.append_expression (creation_expr);
1915 } else {
1916 ccomma.append_expression (new CCodeAssignment (instance, creation_expr));
1919 foreach (MemberInitializer init in expr.get_object_initializer ()) {
1920 if (init.symbol_reference is Field) {
1921 var f = (Field) init.symbol_reference;
1922 var instance_target_type = get_data_type_for_symbol ((TypeSymbol) f.parent_symbol);
1923 var typed_inst = transform_expression (instance, expr.type_reference, instance_target_type);
1924 CCodeExpression lhs;
1925 if (expr.type_reference.data_type is Struct) {
1926 lhs = new CCodeMemberAccess (typed_inst, f.get_cname ());
1927 } else {
1928 lhs = new CCodeMemberAccess.pointer (typed_inst, f.get_cname ());
1930 ccomma.append_expression (new CCodeAssignment (lhs, (CCodeExpression) init.initializer.ccodenode));
1931 } else if (init.symbol_reference is Property) {
1932 var inst_ma = new MemberAccess.simple ("new");
1933 inst_ma.value_type = expr.type_reference;
1934 inst_ma.ccodenode = instance;
1935 var ma = new MemberAccess (inst_ma, init.name);
1936 ccomma.append_expression (get_property_set_call ((Property) init.symbol_reference, ma, (CCodeExpression) init.initializer.ccodenode));
1940 ccomma.append_expression (instance);
1942 expr.ccodenode = ccomma;
1943 } else if (creation_expr != null) {
1944 expr.ccodenode = creation_expr;
1948 public CCodeExpression? handle_struct_argument (FormalParameter param, Expression arg, CCodeExpression? cexpr) {
1949 if (arg.formal_target_type is GenericType && !(arg.target_type is GenericType)) {
1950 // we already use a reference for arguments of ref and out parameters
1951 if (param.direction == ParameterDirection.IN) {
1952 var unary = cexpr as CCodeUnaryExpression;
1953 if (unary != null && unary.operator == CCodeUnaryOperator.POINTER_INDIRECTION) {
1954 // *expr => expr
1955 return unary.inner;
1956 } else if (cexpr is CCodeIdentifier || cexpr is CCodeMemberAccess) {
1957 return new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, cexpr);
1958 } else {
1959 // if cexpr is e.g. a function call, we can't take the address of the expression
1960 // (tmp = expr, &tmp)
1961 var ccomma = new CCodeCommaExpression ();
1963 var temp_var = get_temp_variable (arg.target_type);
1964 temp_vars.add (temp_var);
1965 ccomma.append_expression (new CCodeAssignment (get_variable_cexpression (temp_var.name), cexpr));
1966 ccomma.append_expression (new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, new CCodeIdentifier (temp_var.name)));
1968 return ccomma;
1973 return cexpr;
1976 public override void visit_sizeof_expression (SizeofExpression expr) {
1977 var csizeof = new CCodeFunctionCall (new CCodeIdentifier ("sizeof"));
1978 csizeof.add_argument (new CCodeIdentifier (expr.type_reference.get_cname ()));
1979 expr.ccodenode = csizeof;
1982 public override void visit_typeof_expression (TypeofExpression expr) {
1983 expr.ccodenode = get_type_id_expression (expr.type_reference);
1986 public override void visit_unary_expression (UnaryExpression expr) {
1987 CCodeUnaryOperator op;
1988 if (expr.operator == UnaryOperator.PLUS) {
1989 op = CCodeUnaryOperator.PLUS;
1990 } else if (expr.operator == UnaryOperator.MINUS) {
1991 op = CCodeUnaryOperator.MINUS;
1992 } else if (expr.operator == UnaryOperator.LOGICAL_NEGATION) {
1993 op = CCodeUnaryOperator.LOGICAL_NEGATION;
1994 } else if (expr.operator == UnaryOperator.BITWISE_COMPLEMENT) {
1995 op = CCodeUnaryOperator.BITWISE_COMPLEMENT;
1996 } else if (expr.operator == UnaryOperator.INCREMENT) {
1997 op = CCodeUnaryOperator.PREFIX_INCREMENT;
1998 } else if (expr.operator == UnaryOperator.DECREMENT) {
1999 op = CCodeUnaryOperator.PREFIX_DECREMENT;
2000 } else if (expr.operator == UnaryOperator.REF) {
2001 op = CCodeUnaryOperator.ADDRESS_OF;
2002 } else if (expr.operator == UnaryOperator.OUT) {
2003 op = CCodeUnaryOperator.ADDRESS_OF;
2004 } else {
2005 assert_not_reached ();
2007 expr.ccodenode = new CCodeUnaryExpression (op, (CCodeExpression) expr.inner.ccodenode);
2010 public override void visit_cast_expression (CastExpression expr) {
2011 if (expr.is_silent_cast) {
2012 expr.error = true;
2013 Report.error (expr.source_reference, "Operation not supported for this type");
2014 return;
2017 if (expr.type_reference.data_type != null && expr.type_reference.data_type.get_full_name () == "Dova.Value") {
2018 // box value
2019 var temp_decl = get_temp_variable (expr.inner.value_type, true, expr);
2020 temp_vars.add (temp_decl);
2021 var cvar = get_variable_cexpression (temp_decl.name);
2023 var ccomma = new CCodeCommaExpression ();
2024 ccomma.append_expression (new CCodeAssignment (cvar, (CCodeExpression) expr.inner.ccodenode));
2026 var to_any = new CCodeFunctionCall (new CCodeIdentifier ("dova_type_value_to_any"));
2027 to_any.add_argument (get_type_id_expression (expr.inner.value_type));
2028 to_any.add_argument (new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, cvar));
2029 to_any.add_argument (new CCodeConstant ("0"));
2030 ccomma.append_expression (to_any);
2032 expr.ccodenode = ccomma;
2033 return;
2034 } else if (expr.inner.value_type.data_type != null && expr.inner.value_type.data_type.get_full_name () == "Dova.Value") {
2035 // unbox value
2036 var temp_decl = get_temp_variable (expr.type_reference, true, expr);
2037 temp_vars.add (temp_decl);
2038 var cvar = get_variable_cexpression (temp_decl.name);
2040 var ccomma = new CCodeCommaExpression ();
2042 var sizeof_call = new CCodeFunctionCall (new CCodeIdentifier ("sizeof"));
2043 sizeof_call.add_argument (new CCodeIdentifier (expr.type_reference.get_cname ()));
2045 var to_any = new CCodeFunctionCall (new CCodeIdentifier ("dova_type_value_from_any"));
2046 to_any.add_argument (get_type_id_expression (expr.type_reference));
2047 to_any.add_argument ((CCodeExpression) expr.inner.ccodenode);
2048 to_any.add_argument (new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, cvar));
2049 to_any.add_argument (new CCodeConstant ("0"));
2050 ccomma.append_expression (to_any);
2052 ccomma.append_expression (cvar);
2054 expr.ccodenode = ccomma;
2055 return;
2058 generate_type_declaration (expr.type_reference, source_declarations);
2060 if (expr.inner.value_type is GenericType && !(expr.type_reference is GenericType)) {
2061 // generic types use an extra pointer, dereference that pointer
2062 expr.ccodenode = new CCodeUnaryExpression (CCodeUnaryOperator.POINTER_INDIRECTION, new CCodeCastExpression ((CCodeExpression) expr.inner.ccodenode, expr.type_reference.get_cname () + "*"));
2063 } else {
2064 expr.ccodenode = new CCodeCastExpression ((CCodeExpression) expr.inner.ccodenode, expr.type_reference.get_cname ());
2068 public override void visit_pointer_indirection (PointerIndirection expr) {
2069 expr.ccodenode = new CCodeUnaryExpression (CCodeUnaryOperator.POINTER_INDIRECTION, (CCodeExpression) expr.inner.ccodenode);
2072 public override void visit_addressof_expression (AddressofExpression expr) {
2073 expr.ccodenode = new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, (CCodeExpression) expr.inner.ccodenode);
2076 public override void visit_reference_transfer_expression (ReferenceTransferExpression expr) {
2077 /* (tmp = var, var = null, tmp) */
2078 var ccomma = new CCodeCommaExpression ();
2079 var temp_decl = get_temp_variable (expr.value_type, true, expr);
2080 temp_vars.add (temp_decl);
2081 var cvar = get_variable_cexpression (temp_decl.name);
2083 ccomma.append_expression (new CCodeAssignment (cvar, (CCodeExpression) expr.inner.ccodenode));
2084 ccomma.append_expression (new CCodeAssignment ((CCodeExpression) expr.inner.ccodenode, new CCodeConstant ("NULL")));
2085 ccomma.append_expression (cvar);
2086 expr.ccodenode = ccomma;
2089 public override void visit_binary_expression (BinaryExpression expr) {
2090 var cleft = (CCodeExpression) expr.left.ccodenode;
2091 var cright = (CCodeExpression) expr.right.ccodenode;
2093 CCodeBinaryOperator op;
2094 if (expr.operator == BinaryOperator.PLUS) {
2095 op = CCodeBinaryOperator.PLUS;
2096 } else if (expr.operator == BinaryOperator.MINUS) {
2097 op = CCodeBinaryOperator.MINUS;
2098 } else if (expr.operator == BinaryOperator.MUL) {
2099 op = CCodeBinaryOperator.MUL;
2100 } else if (expr.operator == BinaryOperator.DIV) {
2101 op = CCodeBinaryOperator.DIV;
2102 } else if (expr.operator == BinaryOperator.MOD) {
2103 op = CCodeBinaryOperator.MOD;
2104 } else if (expr.operator == BinaryOperator.SHIFT_LEFT) {
2105 op = CCodeBinaryOperator.SHIFT_LEFT;
2106 } else if (expr.operator == BinaryOperator.SHIFT_RIGHT) {
2107 op = CCodeBinaryOperator.SHIFT_RIGHT;
2108 } else if (expr.operator == BinaryOperator.LESS_THAN) {
2109 op = CCodeBinaryOperator.LESS_THAN;
2110 } else if (expr.operator == BinaryOperator.GREATER_THAN) {
2111 op = CCodeBinaryOperator.GREATER_THAN;
2112 } else if (expr.operator == BinaryOperator.LESS_THAN_OR_EQUAL) {
2113 op = CCodeBinaryOperator.LESS_THAN_OR_EQUAL;
2114 } else if (expr.operator == BinaryOperator.GREATER_THAN_OR_EQUAL) {
2115 op = CCodeBinaryOperator.GREATER_THAN_OR_EQUAL;
2116 } else if (expr.operator == BinaryOperator.EQUALITY) {
2117 op = CCodeBinaryOperator.EQUALITY;
2118 } else if (expr.operator == BinaryOperator.INEQUALITY) {
2119 op = CCodeBinaryOperator.INEQUALITY;
2120 } else if (expr.operator == BinaryOperator.BITWISE_AND) {
2121 op = CCodeBinaryOperator.BITWISE_AND;
2122 } else if (expr.operator == BinaryOperator.BITWISE_OR) {
2123 op = CCodeBinaryOperator.BITWISE_OR;
2124 } else if (expr.operator == BinaryOperator.BITWISE_XOR) {
2125 op = CCodeBinaryOperator.BITWISE_XOR;
2126 } else if (expr.operator == BinaryOperator.AND) {
2127 op = CCodeBinaryOperator.AND;
2128 } else if (expr.operator == BinaryOperator.OR) {
2129 op = CCodeBinaryOperator.OR;
2130 } else if (expr.operator == BinaryOperator.IN) {
2131 expr.ccodenode = new CCodeBinaryExpression (CCodeBinaryOperator.EQUALITY, new CCodeBinaryExpression (CCodeBinaryOperator.BITWISE_AND, cright, cleft), cleft);
2132 return;
2133 } else {
2134 assert_not_reached ();
2137 if (expr.operator == BinaryOperator.EQUALITY ||
2138 expr.operator == BinaryOperator.INEQUALITY) {
2139 var left_type_as_struct = expr.left.value_type.data_type as Struct;
2140 var right_type_as_struct = expr.right.value_type.data_type as Struct;
2142 if (expr.left.value_type.data_type is Class && !((Class) expr.left.value_type.data_type).is_compact &&
2143 expr.right.value_type.data_type is Class && !((Class) expr.right.value_type.data_type).is_compact) {
2144 var left_cl = (Class) expr.left.value_type.data_type;
2145 var right_cl = (Class) expr.right.value_type.data_type;
2147 if (left_cl != right_cl) {
2148 if (left_cl.is_subtype_of (right_cl)) {
2149 cleft = generate_instance_cast (cleft, right_cl);
2150 } else if (right_cl.is_subtype_of (left_cl)) {
2151 cright = generate_instance_cast (cright, left_cl);
2154 } else if (left_type_as_struct != null && right_type_as_struct != null) {
2155 // FIXME generate and use compare/equal function for real structs
2156 if (expr.left.value_type.nullable && expr.right.value_type.nullable) {
2157 // FIXME also compare contents, not just address
2158 } else if (expr.left.value_type.nullable) {
2159 // FIXME check left value is not null
2160 cleft = new CCodeUnaryExpression (CCodeUnaryOperator.POINTER_INDIRECTION, cleft);
2161 } else if (expr.right.value_type.nullable) {
2162 // FIXME check right value is not null
2163 cright = new CCodeUnaryExpression (CCodeUnaryOperator.POINTER_INDIRECTION, cright);
2168 expr.ccodenode = new CCodeBinaryExpression (op, cleft, cright);
2171 public string? get_type_check_function (TypeSymbol type) {
2172 var cl = type as Class;
2173 if (cl != null && cl.type_check_function != null) {
2174 return cl.type_check_function;
2175 } else if ((cl != null && cl.is_compact) || type is Struct || type is Enum || type is Delegate) {
2176 return null;
2177 } else {
2178 return type.get_upper_case_cname ("IS_");
2182 CCodeExpression? create_type_check (CCodeNode ccodenode, DataType type) {
2183 string type_check_func = get_type_check_function (type.data_type);
2184 if (type_check_func == null) {
2185 return new CCodeInvalidExpression ();
2187 var ccheck = new CCodeFunctionCall (new CCodeIdentifier (type_check_func));
2188 ccheck.add_argument ((CCodeExpression) ccodenode);
2189 return ccheck;
2192 public override void visit_type_check (TypeCheck expr) {
2193 generate_type_declaration (expr.type_reference, source_declarations);
2195 expr.ccodenode = create_type_check (expr.expression.ccodenode, expr.type_reference);
2196 if (expr.ccodenode is CCodeInvalidExpression) {
2197 Report.error (expr.source_reference, "type check expressions not supported for compact classes, structs, and enums");
2201 public override void visit_lambda_expression (LambdaExpression l) {
2202 // use instance position from delegate
2203 var dt = (DelegateType) l.target_type;
2204 l.method.cinstance_parameter_position = dt.delegate_symbol.cinstance_parameter_position;
2206 l.accept_children (this);
2208 l.ccodenode = new CCodeIdentifier (l.method.get_cname ());
2211 // manage memory and implicit casts
2212 public CCodeExpression transform_expression (CCodeExpression source_cexpr, DataType? expression_type, DataType? target_type, Expression? expr = null) {
2213 var cexpr = source_cexpr;
2214 if (expression_type == null) {
2215 return cexpr;
2219 if (expression_type.value_owned
2220 && (target_type == null || !target_type.value_owned)) {
2221 // value leaked, destroy it
2222 var pointer_type = target_type as PointerType;
2223 if (pointer_type != null && !(pointer_type.base_type is VoidType)) {
2224 // manual memory management for non-void pointers
2225 // treat void* special to not leak memory with void* method parameters
2226 } else if (requires_destroy (expression_type)) {
2227 var decl = get_temp_variable (expression_type, true, expression_type);
2228 temp_vars.add (decl);
2229 temp_ref_vars.insert (0, decl);
2230 cexpr = new CCodeAssignment (get_variable_cexpression (decl.name), cexpr);
2234 if (target_type == null) {
2235 // value will be destroyed, no need for implicit casts
2236 return cexpr;
2239 cexpr = get_implicit_cast_expression (cexpr, expression_type, target_type, expr);
2241 if (target_type.value_owned && !expression_type.value_owned) {
2242 // need to copy value
2243 if (requires_copy (target_type) && !(expression_type is NullType)) {
2244 CodeNode node = expr;
2245 if (node == null) {
2246 node = expression_type;
2248 cexpr = get_ref_cexpression (target_type, cexpr, expr, node);
2252 return cexpr;
2255 public virtual CCodeExpression get_implicit_cast_expression (CCodeExpression source_cexpr, DataType? expression_type, DataType? target_type, Expression? expr = null) {
2256 var cexpr = source_cexpr;
2258 if (expression_type.data_type != null && expression_type.data_type == target_type.data_type) {
2259 // same type, no cast required
2260 return cexpr;
2263 if (expression_type is NullType) {
2264 // null literal, no cast required when not converting to generic type pointer
2265 return cexpr;
2268 generate_type_declaration (target_type, source_declarations);
2270 if (target_type is DelegateType && expression_type is MethodType) {
2271 var deleg_type = (DelegateType) target_type;
2272 var method_type = (MethodType) expression_type;
2273 CCodeExpression delegate_target;
2274 if (expr is LambdaExpression) {
2275 var lambda = (LambdaExpression) expr;
2276 if (lambda.method.closure) {
2277 int block_id = get_block_id (current_closure_block);
2278 delegate_target = get_variable_cexpression ("_data%d_".printf (block_id));
2279 } else if (get_this_type () != null) {
2280 delegate_target = new CCodeIdentifier ("this");
2281 } else {
2282 delegate_target = new CCodeConstant ("NULL");
2284 } else {
2285 if (method_type.method_symbol.binding == MemberBinding.INSTANCE) {
2286 var ma = (MemberAccess) expr;
2287 delegate_target = (CCodeExpression) get_ccodenode (ma.inner);
2288 } else {
2289 delegate_target = new CCodeConstant ("NULL");
2293 var d = deleg_type.delegate_symbol;
2295 string wrapper_name = "_wrapper%d_".printf (next_wrapper_id++);
2296 var wrapper = new CCodeFunction (wrapper_name);
2297 wrapper.modifiers = CCodeModifiers.STATIC;
2298 var call = new CCodeFunctionCall (source_cexpr);
2300 if (method_type.method_symbol.binding == MemberBinding.INSTANCE) {
2301 wrapper.add_parameter (new CCodeFormalParameter ("this", "void *"));
2302 call.add_argument (new CCodeIdentifier ("this"));
2305 var method_param_iter = method_type.method_symbol.get_parameters ().iterator ();
2306 foreach (FormalParameter param in d.get_parameters ()) {
2307 method_param_iter.next ();
2308 var method_param = method_param_iter.get ();
2309 string ctype = param.variable_type.get_cname ();
2310 if (param.variable_type is GenericType && !(method_param.variable_type is GenericType)) {
2311 ctype = method_param.variable_type.get_cname () + "*";
2312 call.add_argument (new CCodeUnaryExpression (CCodeUnaryOperator.POINTER_INDIRECTION, new CCodeIdentifier (param.name)));
2313 } else if (!(param.variable_type is GenericType) && method_param.variable_type is GenericType) {
2314 call.add_argument (new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, new CCodeIdentifier (param.name)));
2315 } else {
2316 call.add_argument (new CCodeIdentifier (param.name));
2319 wrapper.add_parameter (new CCodeFormalParameter (param.name, ctype));
2322 wrapper.block = new CCodeBlock ();
2323 if (d.return_type is VoidType) {
2324 wrapper.block.add_statement (new CCodeExpressionStatement (call));
2325 } else {
2326 var method_return_type = method_type.method_symbol.return_type;
2327 if (d.return_type is GenericType && !(method_return_type is GenericType)) {
2328 wrapper.add_parameter (new CCodeFormalParameter ("result", method_return_type.get_cname () + "*"));
2329 wrapper.block.add_statement (new CCodeExpressionStatement (new CCodeAssignment (new CCodeUnaryExpression (CCodeUnaryOperator.POINTER_INDIRECTION, new CCodeIdentifier ("result")), call)));
2330 } else if (!(d.return_type is GenericType) && method_return_type is GenericType) {
2331 wrapper.return_type = d.return_type.get_cname ();
2332 var cdecl = new CCodeDeclaration (d.return_type.get_cname ());
2333 cdecl.add_declarator (new CCodeVariableDeclarator ("result"));
2334 call.add_argument (new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, new CCodeIdentifier ("result")));
2335 wrapper.block.add_statement (new CCodeExpressionStatement (call));
2336 wrapper.block.add_statement (new CCodeReturnStatement (new CCodeIdentifier ("result")));
2337 } else if (d.return_type is GenericType) {
2338 wrapper.add_parameter (new CCodeFormalParameter ("result", "void *"));
2339 wrapper.block.add_statement (new CCodeExpressionStatement (call));
2340 } else {
2341 wrapper.return_type = d.return_type.get_cname ();
2342 wrapper.block.add_statement (new CCodeReturnStatement (call));
2346 source_type_member_definition.append (wrapper);
2348 var ccall = new CCodeFunctionCall (new CCodeIdentifier ("%s_new".printf (deleg_type.delegate_symbol.get_lower_case_cname ())));
2349 ccall.add_argument (delegate_target);
2350 ccall.add_argument (new CCodeIdentifier (wrapper_name));
2351 return ccall;
2354 var cl = target_type.data_type as Class;
2355 var iface = target_type.data_type as Interface;
2356 if (context.checking && (iface != null || (cl != null && !cl.is_compact))) {
2357 // checked cast for strict subtypes of GTypeInstance
2358 return generate_instance_cast (cexpr, target_type.data_type);
2359 } else if (target_type.data_type != null && expression_type.get_cname () != target_type.get_cname ()) {
2360 var st = target_type.data_type as Struct;
2361 if (target_type.data_type.is_reference_type () || (st != null && st.is_simple_type ())) {
2362 // don't cast non-simple structs
2363 return new CCodeCastExpression (cexpr, target_type.get_cname ());
2364 } else {
2365 return cexpr;
2367 } else {
2368 return cexpr;
2372 public CCodeFunctionCall get_property_set_call (Property prop, MemberAccess ma, CCodeExpression cexpr, Expression? rhs = null) {
2373 string set_func;
2375 var base_property = prop;
2376 if (prop.base_property != null) {
2377 base_property = prop.base_property;
2378 } else if (prop.base_interface_property != null) {
2379 base_property = prop.base_interface_property;
2382 generate_property_accessor_declaration (base_property.set_accessor, source_declarations);
2383 set_func = base_property.set_accessor.get_cname ();
2385 var ccall = new CCodeFunctionCall (new CCodeIdentifier (set_func));
2387 if (prop.binding == MemberBinding.INSTANCE) {
2388 /* target instance is first argument */
2389 ccall.add_argument ((CCodeExpression) get_ccodenode (ma.inner));
2392 ccall.add_argument (cexpr);
2394 return ccall;
2397 public bool add_generated_external_symbol (Symbol external_symbol) {
2398 return generated_external_symbols.add (external_symbol);
2401 public static DataType get_data_type_for_symbol (TypeSymbol sym) {
2402 DataType type = null;
2404 if (sym is Class) {
2405 type = new ObjectType ((Class) sym);
2406 } else if (sym is Interface) {
2407 type = new ObjectType ((Interface) sym);
2408 } else if (sym is Struct) {
2409 var st = (Struct) sym;
2410 if (st.is_boolean_type ()) {
2411 type = new BooleanType (st);
2412 } else if (st.is_integer_type ()) {
2413 type = new IntegerType (st);
2414 } else if (st.is_floating_type ()) {
2415 type = new FloatingType (st);
2416 } else {
2417 type = new StructValueType (st);
2419 } else if (sym is Enum) {
2420 type = new EnumValueType ((Enum) sym);
2421 } else {
2422 Report.error (null, "internal error: `%s' is not a supported type".printf (sym.get_full_name ()));
2423 return new InvalidType ();
2426 return type;
2429 public CCodeExpression? default_value_for_type (DataType type, bool initializer_expression) {
2430 var st = type.data_type as Struct;
2431 var array_type = type as ArrayType;
2432 if (type is GenericType) {
2433 var value_size = new CCodeFunctionCall (new CCodeIdentifier ("dova_type_get_value_size"));
2434 value_size.add_argument (get_type_id_expression (type));
2436 var alloca_call = new CCodeFunctionCall (new CCodeIdentifier ("alloca"));
2437 alloca_call.add_argument (value_size);
2439 var memset_call = new CCodeFunctionCall (new CCodeIdentifier ("memset"));
2440 memset_call.add_argument (alloca_call);
2441 memset_call.add_argument (new CCodeConstant ("0"));
2442 memset_call.add_argument (value_size);
2444 return memset_call;
2445 } else if (initializer_expression && !type.nullable &&
2446 ((st != null && st.get_fields ().size > 0) ||
2447 (array_type != null && array_type.fixed_length))) {
2448 // 0-initialize struct with struct initializer { 0 }
2449 // only allowed as initializer expression in C
2450 var clist = new CCodeInitializerList ();
2451 clist.append (new CCodeConstant ("0"));
2452 return clist;
2453 } else if ((type.data_type != null && type.data_type.is_reference_type ())
2454 || type.nullable
2455 || type is PointerType || type is DelegateType
2456 || (array_type != null && !array_type.fixed_length)) {
2457 return new CCodeConstant ("NULL");
2458 } else if (type.data_type != null && type.data_type.get_default_value () != null) {
2459 return new CCodeConstant (type.data_type.get_default_value ());
2461 return null;
2464 public CCodeNode? get_ccodenode (CodeNode node) {
2465 if (node.ccodenode == null) {
2466 node.emit (this);
2468 return node.ccodenode;
2471 public DataType? get_this_type () {
2472 if (current_method != null && current_method.binding == MemberBinding.INSTANCE) {
2473 return current_method.this_parameter.variable_type;
2474 } else if (current_property_accessor != null && current_property_accessor.prop.binding == MemberBinding.INSTANCE) {
2475 return current_property_accessor.prop.this_parameter.variable_type;
2477 return null;
2480 public CCodeExpression generate_instance_cast (CCodeExpression expr, TypeSymbol type) {
2481 return new CCodeCastExpression (expr, type.get_cname () + "*");
2484 public virtual string? get_custom_creturn_type (Method m) {
2485 return null;
2488 public virtual bool method_has_wrapper (Method method) {
2489 return false;
2492 public virtual void add_simple_check (CodeNode node, CCodeFragment cfrag, bool always_fails = false) {