codegen: Add destroy_local, destroy_parameter, and destroy_field
[vala-lang.git] / codegen / valaccodebasemodule.vala
blob4bba12caf960ccf88a352630888f141dca127b05
1 /* valaccodebasemodule.vala
3 * Copyright (C) 2006-2011 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>
26 /**
27 * Code visitor generating C Code.
29 public abstract class Vala.CCodeBaseModule : CodeGenerator {
30 public class EmitContext {
31 public Symbol? current_symbol;
32 public ArrayList<Symbol> symbol_stack = new ArrayList<Symbol> ();
33 public TryStatement current_try;
34 public CatchClause current_catch;
35 public CCodeFunction ccode;
36 public ArrayList<CCodeFunction> ccode_stack = new ArrayList<CCodeFunction> ();
37 public ArrayList<LocalVariable> temp_ref_vars = new ArrayList<LocalVariable> ();
38 public int next_temp_var_id;
39 public bool current_method_inner_error;
40 public bool current_method_return;
41 public Map<string,string> variable_name_map = new HashMap<string,string> (str_hash, str_equal);
43 public EmitContext (Symbol? symbol = null) {
44 current_symbol = symbol;
47 public void push_symbol (Symbol symbol) {
48 symbol_stack.add (current_symbol);
49 current_symbol = symbol;
52 public void pop_symbol () {
53 current_symbol = symbol_stack[symbol_stack.size - 1];
54 symbol_stack.remove_at (symbol_stack.size - 1);
58 public CodeContext context { get; set; }
60 public Symbol root_symbol;
62 public EmitContext emit_context = new EmitContext ();
64 List<EmitContext> emit_context_stack = new ArrayList<EmitContext> ();
66 public Symbol current_symbol { get { return emit_context.current_symbol; } }
68 public TryStatement current_try {
69 get { return emit_context.current_try; }
70 set { emit_context.current_try = value; }
73 public CatchClause current_catch {
74 get { return emit_context.current_catch; }
75 set { emit_context.current_catch = value; }
78 public TypeSymbol? current_type_symbol {
79 get {
80 var sym = current_symbol;
81 while (sym != null) {
82 if (sym is TypeSymbol) {
83 return (TypeSymbol) sym;
85 sym = sym.parent_symbol;
87 return null;
91 public Class? current_class {
92 get { return current_type_symbol as Class; }
95 public Method? current_method {
96 get {
97 var sym = current_symbol;
98 while (sym is Block) {
99 sym = sym.parent_symbol;
101 return sym as Method;
105 public PropertyAccessor? current_property_accessor {
106 get {
107 var sym = current_symbol;
108 while (sym is Block) {
109 sym = sym.parent_symbol;
111 return sym as PropertyAccessor;
115 public DataType? current_return_type {
116 get {
117 var m = current_method;
118 if (m != null) {
119 return m.return_type;
122 var acc = current_property_accessor;
123 if (acc != null) {
124 if (acc.readable) {
125 return acc.value_type;
126 } else {
127 return void_type;
131 if (is_in_constructor () || is_in_destructor ()) {
132 return void_type;
135 return null;
139 public bool is_in_coroutine () {
140 return current_method != null && current_method.coroutine;
143 public bool is_in_constructor () {
144 if (current_method != null) {
145 // make sure to not return true in lambda expression inside constructor
146 return false;
148 var sym = current_symbol;
149 while (sym != null) {
150 if (sym is Constructor) {
151 return true;
153 sym = sym.parent_symbol;
155 return false;
158 public bool is_in_destructor () {
159 if (current_method != null) {
160 // make sure to not return true in lambda expression inside constructor
161 return false;
163 var sym = current_symbol;
164 while (sym != null) {
165 if (sym is Destructor) {
166 return true;
168 sym = sym.parent_symbol;
170 return false;
173 public Block? current_closure_block {
174 get {
175 return next_closure_block (current_symbol);
179 public unowned Block? next_closure_block (Symbol sym) {
180 unowned Block block = null;
181 while (true) {
182 block = sym as Block;
183 if (!(sym is Block || sym is Method)) {
184 // no closure block
185 break;
187 if (block != null && block.captured) {
188 // closure block found
189 break;
191 sym = sym.parent_symbol;
193 return block;
196 public CCodeFile header_file;
197 public CCodeFile internal_header_file;
198 public CCodeFile cfile;
200 public EmitContext class_init_context;
201 public EmitContext base_init_context;
202 public EmitContext class_finalize_context;
203 public EmitContext base_finalize_context;
204 public EmitContext instance_init_context;
205 public EmitContext instance_finalize_context;
207 public CCodeStruct param_spec_struct;
208 public CCodeStruct closure_struct;
209 public CCodeEnum prop_enum;
211 public CCodeFunction ccode { get { return emit_context.ccode; } }
213 /* temporary variables that own their content */
214 public ArrayList<LocalVariable> temp_ref_vars { get { return emit_context.temp_ref_vars; } }
215 /* cache to check whether a certain marshaller has been created yet */
216 public Set<string> user_marshal_set;
217 /* (constant) hash table with all predefined marshallers */
218 public Set<string> predefined_marshal_set;
219 /* (constant) hash table with all reserved identifiers in the generated code */
220 Set<string> reserved_identifiers;
222 public int next_temp_var_id {
223 get { return emit_context.next_temp_var_id; }
224 set { emit_context.next_temp_var_id = value; }
227 public int next_regex_id = 0;
228 public bool in_creation_method { get { return current_method is CreationMethod; } }
229 public bool in_constructor = false;
230 public bool in_static_or_class_context = false;
232 public bool current_method_inner_error {
233 get { return emit_context.current_method_inner_error; }
234 set { emit_context.current_method_inner_error = value; }
237 public bool current_method_return {
238 get { return emit_context.current_method_return; }
239 set { emit_context.current_method_return = value; }
242 public int next_coroutine_state = 1;
243 int next_block_id = 0;
244 Map<Block,int> block_map = new HashMap<Block,int> ();
246 public DataType void_type = new VoidType ();
247 public DataType bool_type;
248 public DataType char_type;
249 public DataType uchar_type;
250 public DataType? unichar_type;
251 public DataType short_type;
252 public DataType ushort_type;
253 public DataType int_type;
254 public DataType uint_type;
255 public DataType long_type;
256 public DataType ulong_type;
257 public DataType int8_type;
258 public DataType uint8_type;
259 public DataType int16_type;
260 public DataType uint16_type;
261 public DataType int32_type;
262 public DataType uint32_type;
263 public DataType int64_type;
264 public DataType uint64_type;
265 public DataType string_type;
266 public DataType regex_type;
267 public DataType float_type;
268 public DataType double_type;
269 public TypeSymbol gtype_type;
270 public TypeSymbol gobject_type;
271 public ErrorType gerror_type;
272 public Class glist_type;
273 public Class gslist_type;
274 public Class gnode_type;
275 public Class gvaluearray_type;
276 public TypeSymbol gstringbuilder_type;
277 public TypeSymbol garray_type;
278 public TypeSymbol gbytearray_type;
279 public TypeSymbol gptrarray_type;
280 public TypeSymbol gthreadpool_type;
281 public DataType gdestroynotify_type;
282 public DataType gquark_type;
283 public Struct gvalue_type;
284 public Class gvariant_type;
285 public Struct mutex_type;
286 public TypeSymbol type_module_type;
287 public TypeSymbol dbus_proxy_type;
288 public TypeSymbol dbus_object_type;
290 public bool in_plugin = false;
291 public string module_init_param_name;
293 public bool gvaluecollector_h_needed;
294 public bool requires_array_free;
295 public bool requires_array_move;
296 public bool requires_array_length;
298 public Set<string> wrappers;
299 Set<Symbol> generated_external_symbols;
301 public Map<string,string> variable_name_map { get { return emit_context.variable_name_map; } }
303 public CCodeBaseModule () {
304 predefined_marshal_set = new HashSet<string> (str_hash, str_equal);
305 predefined_marshal_set.add ("VOID:VOID");
306 predefined_marshal_set.add ("VOID:BOOLEAN");
307 predefined_marshal_set.add ("VOID:CHAR");
308 predefined_marshal_set.add ("VOID:UCHAR");
309 predefined_marshal_set.add ("VOID:INT");
310 predefined_marshal_set.add ("VOID:UINT");
311 predefined_marshal_set.add ("VOID:LONG");
312 predefined_marshal_set.add ("VOID:ULONG");
313 predefined_marshal_set.add ("VOID:ENUM");
314 predefined_marshal_set.add ("VOID:FLAGS");
315 predefined_marshal_set.add ("VOID:FLOAT");
316 predefined_marshal_set.add ("VOID:DOUBLE");
317 predefined_marshal_set.add ("VOID:STRING");
318 predefined_marshal_set.add ("VOID:POINTER");
319 predefined_marshal_set.add ("VOID:OBJECT");
320 predefined_marshal_set.add ("STRING:OBJECT,POINTER");
321 predefined_marshal_set.add ("VOID:UINT,POINTER");
322 predefined_marshal_set.add ("BOOLEAN:FLAGS");
324 reserved_identifiers = new HashSet<string> (str_hash, str_equal);
326 // C99 keywords
327 reserved_identifiers.add ("_Bool");
328 reserved_identifiers.add ("_Complex");
329 reserved_identifiers.add ("_Imaginary");
330 reserved_identifiers.add ("asm");
331 reserved_identifiers.add ("auto");
332 reserved_identifiers.add ("break");
333 reserved_identifiers.add ("case");
334 reserved_identifiers.add ("char");
335 reserved_identifiers.add ("const");
336 reserved_identifiers.add ("continue");
337 reserved_identifiers.add ("default");
338 reserved_identifiers.add ("do");
339 reserved_identifiers.add ("double");
340 reserved_identifiers.add ("else");
341 reserved_identifiers.add ("enum");
342 reserved_identifiers.add ("extern");
343 reserved_identifiers.add ("float");
344 reserved_identifiers.add ("for");
345 reserved_identifiers.add ("goto");
346 reserved_identifiers.add ("if");
347 reserved_identifiers.add ("inline");
348 reserved_identifiers.add ("int");
349 reserved_identifiers.add ("long");
350 reserved_identifiers.add ("register");
351 reserved_identifiers.add ("restrict");
352 reserved_identifiers.add ("return");
353 reserved_identifiers.add ("short");
354 reserved_identifiers.add ("signed");
355 reserved_identifiers.add ("sizeof");
356 reserved_identifiers.add ("static");
357 reserved_identifiers.add ("struct");
358 reserved_identifiers.add ("switch");
359 reserved_identifiers.add ("typedef");
360 reserved_identifiers.add ("union");
361 reserved_identifiers.add ("unsigned");
362 reserved_identifiers.add ("void");
363 reserved_identifiers.add ("volatile");
364 reserved_identifiers.add ("while");
366 // MSVC keywords
367 reserved_identifiers.add ("cdecl");
369 // reserved for Vala/GObject naming conventions
370 reserved_identifiers.add ("error");
371 reserved_identifiers.add ("result");
372 reserved_identifiers.add ("self");
375 public override void emit (CodeContext context) {
376 this.context = context;
378 root_symbol = context.root;
380 bool_type = new BooleanType ((Struct) root_symbol.scope.lookup ("bool"));
381 char_type = new IntegerType ((Struct) root_symbol.scope.lookup ("char"));
382 uchar_type = new IntegerType ((Struct) root_symbol.scope.lookup ("uchar"));
383 short_type = new IntegerType ((Struct) root_symbol.scope.lookup ("short"));
384 ushort_type = new IntegerType ((Struct) root_symbol.scope.lookup ("ushort"));
385 int_type = new IntegerType ((Struct) root_symbol.scope.lookup ("int"));
386 uint_type = new IntegerType ((Struct) root_symbol.scope.lookup ("uint"));
387 long_type = new IntegerType ((Struct) root_symbol.scope.lookup ("long"));
388 ulong_type = new IntegerType ((Struct) root_symbol.scope.lookup ("ulong"));
389 int8_type = new IntegerType ((Struct) root_symbol.scope.lookup ("int8"));
390 uint8_type = new IntegerType ((Struct) root_symbol.scope.lookup ("uint8"));
391 int16_type = new IntegerType ((Struct) root_symbol.scope.lookup ("int16"));
392 uint16_type = new IntegerType ((Struct) root_symbol.scope.lookup ("uint16"));
393 int32_type = new IntegerType ((Struct) root_symbol.scope.lookup ("int32"));
394 uint32_type = new IntegerType ((Struct) root_symbol.scope.lookup ("uint32"));
395 int64_type = new IntegerType ((Struct) root_symbol.scope.lookup ("int64"));
396 uint64_type = new IntegerType ((Struct) root_symbol.scope.lookup ("uint64"));
397 float_type = new FloatingType ((Struct) root_symbol.scope.lookup ("float"));
398 double_type = new FloatingType ((Struct) root_symbol.scope.lookup ("double"));
399 string_type = new ObjectType ((Class) root_symbol.scope.lookup ("string"));
400 var unichar_struct = (Struct) root_symbol.scope.lookup ("unichar");
401 if (unichar_struct != null) {
402 unichar_type = new IntegerType (unichar_struct);
405 if (context.profile == Profile.GOBJECT) {
406 var glib_ns = root_symbol.scope.lookup ("GLib");
408 gtype_type = (TypeSymbol) glib_ns.scope.lookup ("Type");
409 gobject_type = (TypeSymbol) glib_ns.scope.lookup ("Object");
410 gerror_type = new ErrorType (null, null);
411 glist_type = (Class) glib_ns.scope.lookup ("List");
412 gslist_type = (Class) glib_ns.scope.lookup ("SList");
413 gnode_type = (Class) glib_ns.scope.lookup ("Node");
414 gvaluearray_type = (Class) glib_ns.scope.lookup ("ValueArray");
415 gstringbuilder_type = (TypeSymbol) glib_ns.scope.lookup ("StringBuilder");
416 garray_type = (TypeSymbol) glib_ns.scope.lookup ("Array");
417 gbytearray_type = (TypeSymbol) glib_ns.scope.lookup ("ByteArray");
418 gptrarray_type = (TypeSymbol) glib_ns.scope.lookup ("PtrArray");
419 gthreadpool_type = (TypeSymbol) glib_ns.scope.lookup ("ThreadPool");
420 gdestroynotify_type = new DelegateType ((Delegate) glib_ns.scope.lookup ("DestroyNotify"));
422 gquark_type = new IntegerType ((Struct) glib_ns.scope.lookup ("Quark"));
423 gvalue_type = (Struct) glib_ns.scope.lookup ("Value");
424 gvariant_type = (Class) glib_ns.scope.lookup ("Variant");
425 mutex_type = (Struct) glib_ns.scope.lookup ("StaticRecMutex");
427 type_module_type = (TypeSymbol) glib_ns.scope.lookup ("TypeModule");
429 regex_type = new ObjectType ((Class) root_symbol.scope.lookup ("GLib").scope.lookup ("Regex"));
431 if (context.module_init_method != null) {
432 foreach (Parameter parameter in context.module_init_method.get_parameters ()) {
433 if (parameter.variable_type.data_type == type_module_type) {
434 in_plugin = true;
435 module_init_param_name = parameter.name;
436 break;
439 if (!in_plugin) {
440 Report.error (context.module_init_method.source_reference, "[ModuleInit] requires a parameter of type `GLib.TypeModule'");
444 dbus_proxy_type = (TypeSymbol) glib_ns.scope.lookup ("DBusProxy");
446 var dbus_ns = root_symbol.scope.lookup ("DBus");
447 if (dbus_ns != null) {
448 dbus_object_type = (TypeSymbol) dbus_ns.scope.lookup ("Object");
452 header_file = new CCodeFile ();
453 header_file.is_header = true;
454 internal_header_file = new CCodeFile ();
455 internal_header_file.is_header = true;
457 /* we're only interested in non-pkg source files */
458 var source_files = context.get_source_files ();
459 foreach (SourceFile file in source_files) {
460 if (file.file_type == SourceFileType.SOURCE ||
461 (context.header_filename != null && file.file_type == SourceFileType.FAST)) {
462 file.accept (this);
466 // generate symbols file for public API
467 if (context.symbols_filename != null) {
468 var stream = FileStream.open (context.symbols_filename, "w");
469 if (stream == null) {
470 Report.error (null, "unable to open `%s' for writing".printf (context.symbols_filename));
471 return;
474 foreach (string symbol in header_file.get_symbols ()) {
475 stream.puts (symbol);
476 stream.putc ('\n');
479 stream = null;
482 // generate C header file for public API
483 if (context.header_filename != null) {
484 bool ret;
485 if (context.profile == Profile.GOBJECT) {
486 ret = header_file.store (context.header_filename, null, context.version_header, false, "G_BEGIN_DECLS", "G_END_DECLS");
487 } else {
488 ret = header_file.store (context.header_filename, null, context.version_header, false);
490 if (!ret) {
491 Report.error (null, "unable to open `%s' for writing".printf (context.header_filename));
495 // generate C header file for internal API
496 if (context.internal_header_filename != null) {
497 bool ret;
498 if (context.profile == Profile.GOBJECT) {
499 ret = internal_header_file.store (context.internal_header_filename, null, context.version_header, false, "G_BEGIN_DECLS", "G_END_DECLS");
500 } else {
501 ret = internal_header_file.store (context.internal_header_filename, null, context.version_header, false);
503 if (!ret) {
504 Report.error (null, "unable to open `%s' for writing".printf (context.internal_header_filename));
509 public void push_context (EmitContext emit_context) {
510 if (this.emit_context != null) {
511 emit_context_stack.add (this.emit_context);
514 this.emit_context = emit_context;
517 public void pop_context () {
518 if (emit_context_stack.size > 0) {
519 this.emit_context = emit_context_stack[emit_context_stack.size - 1];
520 emit_context_stack.remove_at (emit_context_stack.size - 1);
521 } else {
522 this.emit_context = null;
526 public void push_function (CCodeFunction func) {
527 emit_context.ccode_stack.add (ccode);
528 emit_context.ccode = func;
531 public void pop_function () {
532 emit_context.ccode = emit_context.ccode_stack[emit_context.ccode_stack.size - 1];
533 emit_context.ccode_stack.remove_at (emit_context.ccode_stack.size - 1);
536 public bool add_symbol_declaration (CCodeFile decl_space, Symbol sym, string name) {
537 if (decl_space.add_declaration (name)) {
538 return true;
540 if (sym.source_reference != null) {
541 sym.source_reference.file.used = true;
543 if (sym.external_package || (!decl_space.is_header && CodeContext.get ().use_header && !sym.is_internal_symbol ())) {
544 // add appropriate include file
545 foreach (string header_filename in sym.get_cheader_filenames ()) {
546 decl_space.add_include (header_filename, !sym.external_package);
548 // declaration complete
549 return true;
550 } else {
551 // require declaration
552 return false;
556 public CCodeIdentifier get_value_setter_function (DataType type_reference) {
557 var array_type = type_reference as ArrayType;
558 if (type_reference.data_type != null) {
559 return new CCodeIdentifier (type_reference.data_type.get_set_value_function ());
560 } else if (array_type != null && array_type.element_type.data_type == string_type.data_type) {
561 // G_TYPE_STRV
562 return new CCodeIdentifier ("g_value_set_boxed");
563 } else {
564 return new CCodeIdentifier ("g_value_set_pointer");
568 public CCodeIdentifier get_value_taker_function (DataType type_reference) {
569 var array_type = type_reference as ArrayType;
570 if (type_reference.data_type != null) {
571 return new CCodeIdentifier (type_reference.data_type.get_take_value_function ());
572 } else if (array_type != null && array_type.element_type.data_type == string_type.data_type) {
573 // G_TYPE_STRV
574 return new CCodeIdentifier ("g_value_take_boxed");
575 } else {
576 return new CCodeIdentifier ("g_value_set_pointer");
580 CCodeIdentifier get_value_getter_function (DataType type_reference) {
581 var array_type = type_reference as ArrayType;
582 if (type_reference.data_type != null) {
583 return new CCodeIdentifier (type_reference.data_type.get_get_value_function ());
584 } else if (array_type != null && array_type.element_type.data_type == string_type.data_type) {
585 // G_TYPE_STRV
586 return new CCodeIdentifier ("g_value_get_boxed");
587 } else {
588 return new CCodeIdentifier ("g_value_get_pointer");
592 public virtual void append_vala_array_free () {
595 public virtual void append_vala_array_move () {
598 public virtual void append_vala_array_length () {
601 public override void visit_source_file (SourceFile source_file) {
602 cfile = new CCodeFile ();
604 user_marshal_set = new HashSet<string> (str_hash, str_equal);
606 next_regex_id = 0;
608 gvaluecollector_h_needed = false;
609 requires_array_free = false;
610 requires_array_move = false;
611 requires_array_length = false;
613 wrappers = new HashSet<string> (str_hash, str_equal);
614 generated_external_symbols = new HashSet<Symbol> ();
616 if (context.profile == Profile.GOBJECT) {
617 header_file.add_include ("glib.h");
618 internal_header_file.add_include ("glib.h");
619 cfile.add_include ("glib.h");
620 cfile.add_include ("glib-object.h");
623 source_file.accept_children (this);
625 if (context.report.get_errors () > 0) {
626 return;
629 /* For fast-vapi, we only wanted the header declarations
630 * to be emitted, so bail out here without writing the
631 * C code output.
633 if (source_file.file_type == SourceFileType.FAST) {
634 return;
637 if (requires_array_free) {
638 append_vala_array_free ();
640 if (requires_array_move) {
641 append_vala_array_move ();
643 if (requires_array_length) {
644 append_vala_array_length ();
647 if (gvaluecollector_h_needed) {
648 cfile.add_include ("gobject/gvaluecollector.h");
651 var comments = source_file.get_comments();
652 if (comments != null) {
653 foreach (Comment comment in comments) {
654 var ccomment = new CCodeComment (comment.content);
655 cfile.add_comment (ccomment);
659 if (!cfile.store (source_file.get_csource_filename (), source_file.filename, context.version_header, context.debug)) {
660 Report.error (null, "unable to open `%s' for writing".printf (source_file.get_csource_filename ()));
663 cfile = null;
666 public virtual bool generate_enum_declaration (Enum en, CCodeFile decl_space) {
667 if (add_symbol_declaration (decl_space, en, en.get_cname ())) {
668 return false;
671 var cenum = new CCodeEnum (en.get_cname ());
673 cenum.deprecated = en.deprecated;
675 int flag_shift = 0;
676 foreach (EnumValue ev in en.get_values ()) {
677 CCodeEnumValue c_ev;
678 if (ev.value == null) {
679 c_ev = new CCodeEnumValue (ev.get_cname ());
680 if (en.is_flags) {
681 c_ev.value = new CCodeConstant ("1 << %d".printf (flag_shift));
682 flag_shift += 1;
684 } else {
685 ev.value.emit (this);
686 c_ev = new CCodeEnumValue (ev.get_cname (), get_cvalue (ev.value));
688 c_ev.deprecated = ev.deprecated;
689 cenum.add_value (c_ev);
692 decl_space.add_type_definition (cenum);
693 decl_space.add_type_definition (new CCodeNewline ());
695 if (!en.has_type_id) {
696 return true;
699 decl_space.add_type_declaration (new CCodeNewline ());
701 var macro = "(%s_get_type ())".printf (en.get_lower_case_cname (null));
702 decl_space.add_type_declaration (new CCodeMacroReplacement (en.get_type_id (), macro));
704 var fun_name = "%s_get_type".printf (en.get_lower_case_cname (null));
705 var regfun = new CCodeFunction (fun_name, "GType");
706 regfun.attributes = "G_GNUC_CONST";
708 if (en.access == SymbolAccessibility.PRIVATE) {
709 regfun.modifiers = CCodeModifiers.STATIC;
710 // avoid C warning as this function is not always used
711 regfun.attributes = "G_GNUC_UNUSED";
714 decl_space.add_function_declaration (regfun);
716 return true;
719 public override void visit_enum (Enum en) {
720 en.accept_children (this);
722 if (en.comment != null) {
723 cfile.add_type_member_definition (new CCodeComment (en.comment.content));
726 generate_enum_declaration (en, cfile);
728 if (!en.is_internal_symbol ()) {
729 generate_enum_declaration (en, header_file);
731 if (!en.is_private_symbol ()) {
732 generate_enum_declaration (en, internal_header_file);
736 public void visit_member (Symbol m) {
737 /* stuff meant for all lockable members */
738 if (m is Lockable && ((Lockable) m).get_lock_used ()) {
739 CCodeExpression l = new CCodeIdentifier ("self");
740 var init_context = class_init_context;
741 var finalize_context = class_finalize_context;
743 if (m.is_instance_member ()) {
744 l = new CCodeMemberAccess.pointer (new CCodeMemberAccess.pointer (l, "priv"), get_symbol_lock_name (m.name));
745 init_context = instance_init_context;
746 finalize_context = instance_finalize_context;
747 } else if (m.is_class_member ()) {
748 TypeSymbol parent = (TypeSymbol)m.parent_symbol;
750 var get_class_private_call = new CCodeFunctionCall (new CCodeIdentifier ("%s_GET_CLASS_PRIVATE".printf(parent.get_upper_case_cname ())));
751 get_class_private_call.add_argument (new CCodeIdentifier ("klass"));
752 l = new CCodeMemberAccess.pointer (get_class_private_call, get_symbol_lock_name (m.name));
753 } else {
754 l = new CCodeIdentifier (get_symbol_lock_name ("%s_%s".printf(m.parent_symbol.get_lower_case_cname (), m.name)));
757 push_context (init_context);
758 var initf = new CCodeFunctionCall (new CCodeIdentifier (mutex_type.default_construction_method.get_cname ()));
759 initf.add_argument (new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, l));
760 ccode.add_expression (initf);
761 pop_context ();
763 if (finalize_context != null) {
764 push_context (finalize_context);
765 var fc = new CCodeFunctionCall (new CCodeIdentifier ("g_static_rec_mutex_free"));
766 fc.add_argument (new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, l));
767 ccode.add_expression (fc);
768 pop_context ();
773 public void generate_constant_declaration (Constant c, CCodeFile decl_space, bool definition = false) {
774 if (c.parent_symbol is Block) {
775 // local constant
776 return;
779 if (add_symbol_declaration (decl_space, c, c.get_cname ())) {
780 return;
783 if (!c.external) {
784 generate_type_declaration (c.type_reference, decl_space);
786 c.value.emit (this);
788 var initializer_list = c.value as InitializerList;
789 if (initializer_list != null) {
790 var cdecl = new CCodeDeclaration (c.type_reference.get_const_cname ());
791 var arr = "";
792 if (c.type_reference is ArrayType) {
793 arr = "[%d]".printf (initializer_list.size);
796 var cinitializer = get_cvalue (c.value);
797 if (!definition) {
798 // never output value in header
799 // special case needed as this method combines declaration and definition
800 cinitializer = null;
803 cdecl.add_declarator (new CCodeVariableDeclarator ("%s%s".printf (c.get_cname (), arr), cinitializer));
804 if (c.is_private_symbol ()) {
805 cdecl.modifiers = CCodeModifiers.STATIC;
806 } else {
807 cdecl.modifiers = CCodeModifiers.EXTERN;
810 decl_space.add_constant_declaration (cdecl);
811 } else {
812 var cdefine = new CCodeMacroReplacement.with_expression (c.get_cname (), get_cvalue (c.value));
813 decl_space.add_type_member_declaration (cdefine);
818 public override void visit_constant (Constant c) {
819 if (c.parent_symbol is Block) {
820 // local constant
822 generate_type_declaration (c.type_reference, cfile);
824 c.value.emit (this);
826 string type_name = c.type_reference.get_const_cname ();
827 string arr = "";
828 if (c.type_reference is ArrayType) {
829 arr = "[]";
832 if (c.type_reference.compatible (string_type)) {
833 type_name = "const char";
834 arr = "[]";
837 var cinitializer = get_cvalue (c.value);
839 ccode.add_declaration (type_name, new CCodeVariableDeclarator ("%s%s".printf (c.get_cname (), arr), cinitializer), CCodeModifiers.STATIC);
841 return;
844 generate_constant_declaration (c, cfile, true);
846 if (!c.is_internal_symbol ()) {
847 generate_constant_declaration (c, header_file);
849 if (!c.is_private_symbol ()) {
850 generate_constant_declaration (c, internal_header_file);
854 public void generate_field_declaration (Field f, CCodeFile decl_space) {
855 if (add_symbol_declaration (decl_space, f, f.get_cname ())) {
856 return;
859 generate_type_declaration (f.variable_type, decl_space);
861 string field_ctype = f.variable_type.get_cname ();
862 if (f.is_volatile) {
863 field_ctype = "volatile " + field_ctype;
866 var cdecl = new CCodeDeclaration (field_ctype);
867 cdecl.add_declarator (new CCodeVariableDeclarator (f.get_cname (), null, f.variable_type.get_cdeclarator_suffix ()));
868 if (f.is_private_symbol ()) {
869 cdecl.modifiers = CCodeModifiers.STATIC;
870 } else {
871 cdecl.modifiers = CCodeModifiers.EXTERN;
873 if (f.deprecated) {
874 cdecl.modifiers |= CCodeModifiers.DEPRECATED;
876 decl_space.add_type_member_declaration (cdecl);
878 if (f.get_lock_used ()) {
879 // Declare mutex for static member
880 var flock = new CCodeDeclaration (mutex_type.get_cname ());
881 var flock_decl = new CCodeVariableDeclarator (get_symbol_lock_name (f.get_cname ()), new CCodeConstant ("{0}"));
882 flock.add_declarator (flock_decl);
884 if (f.is_private_symbol ()) {
885 flock.modifiers = CCodeModifiers.STATIC;
886 } else {
887 flock.modifiers = CCodeModifiers.EXTERN;
889 decl_space.add_type_member_declaration (flock);
892 if (f.variable_type is ArrayType && !f.no_array_length) {
893 var array_type = (ArrayType) f.variable_type;
895 if (!array_type.fixed_length) {
896 for (int dim = 1; dim <= array_type.rank; dim++) {
897 var len_type = int_type.copy ();
899 cdecl = new CCodeDeclaration (len_type.get_cname ());
900 cdecl.add_declarator (new CCodeVariableDeclarator (get_array_length_cname (f.get_cname (), dim)));
901 if (f.is_private_symbol ()) {
902 cdecl.modifiers = CCodeModifiers.STATIC;
903 } else {
904 cdecl.modifiers = CCodeModifiers.EXTERN;
906 decl_space.add_type_member_declaration (cdecl);
909 } else if (f.variable_type is DelegateType) {
910 var delegate_type = (DelegateType) f.variable_type;
911 if (delegate_type.delegate_symbol.has_target) {
912 // create field to store delegate target
914 cdecl = new CCodeDeclaration ("gpointer");
915 cdecl.add_declarator (new CCodeVariableDeclarator (get_delegate_target_cname (f.get_cname ())));
916 if (f.is_private_symbol ()) {
917 cdecl.modifiers = CCodeModifiers.STATIC;
918 } else {
919 cdecl.modifiers = CCodeModifiers.EXTERN;
921 decl_space.add_type_member_declaration (cdecl);
923 if (delegate_type.value_owned) {
924 cdecl = new CCodeDeclaration ("GDestroyNotify");
925 cdecl.add_declarator (new CCodeVariableDeclarator (get_delegate_target_destroy_notify_cname (f.get_cname ())));
926 if (f.is_private_symbol ()) {
927 cdecl.modifiers = CCodeModifiers.STATIC;
928 } else {
929 cdecl.modifiers = CCodeModifiers.EXTERN;
931 decl_space.add_type_member_declaration (cdecl);
937 public override void visit_field (Field f) {
938 visit_member (f);
940 check_type (f.variable_type);
942 var cl = f.parent_symbol as Class;
943 bool is_gtypeinstance = (cl != null && !cl.is_compact);
945 CCodeExpression lhs = null;
947 string field_ctype = f.variable_type.get_cname ();
948 if (f.is_volatile) {
949 field_ctype = "volatile " + field_ctype;
952 if (f.binding == MemberBinding.INSTANCE) {
953 if (is_gtypeinstance && f.access == SymbolAccessibility.PRIVATE) {
954 lhs = new CCodeMemberAccess.pointer (new CCodeMemberAccess.pointer (new CCodeIdentifier ("self"), "priv"), f.get_cname ());
955 } else {
956 lhs = new CCodeMemberAccess.pointer (new CCodeIdentifier ("self"), f.get_cname ());
959 if (f.initializer != null) {
960 push_context (instance_init_context);
962 f.initializer.emit (this);
964 var rhs = get_cvalue (f.initializer);
966 ccode.add_assignment (lhs, rhs);
968 if (f.variable_type is ArrayType && !f.no_array_length &&
969 f.initializer is ArrayCreationExpression) {
970 var array_type = (ArrayType) f.variable_type;
971 var this_access = new MemberAccess.simple ("this");
972 this_access.value_type = get_data_type_for_symbol ((TypeSymbol) f.parent_symbol);
973 set_cvalue (this_access, new CCodeIdentifier ("self"));
974 var ma = new MemberAccess (this_access, f.name);
975 ma.symbol_reference = f;
976 ma.value_type = f.variable_type.copy ();
977 visit_member_access (ma);
979 List<Expression> sizes = ((ArrayCreationExpression) f.initializer).get_sizes ();
980 for (int dim = 1; dim <= array_type.rank; dim++) {
981 var array_len_lhs = get_array_length_cexpression (ma, dim);
982 var size = sizes[dim - 1];
983 ccode.add_assignment (array_len_lhs, get_cvalue (size));
986 if (array_type.rank == 1 && f.is_internal_symbol ()) {
987 var lhs_array_size = get_array_size_cvalue (ma.target_value);
988 var rhs_array_len = get_array_length_cexpression (ma, 1);
989 ccode.add_assignment (lhs_array_size, rhs_array_len);
993 foreach (LocalVariable local in temp_ref_vars) {
994 ccode.add_expression (destroy_local (local));
997 temp_ref_vars.clear ();
999 pop_context ();
1002 if (requires_destroy (f.variable_type) && instance_finalize_context != null) {
1003 push_context (instance_finalize_context);
1005 var this_access = new MemberAccess.simple ("this");
1006 this_access.value_type = get_data_type_for_symbol ((TypeSymbol) f.parent_symbol);
1008 var field_st = f.parent_symbol as Struct;
1009 if (field_st != null && !field_st.is_simple_type ()) {
1010 set_cvalue (this_access, new CCodeIdentifier ("(*self)"));
1011 } else {
1012 set_cvalue (this_access, new CCodeIdentifier ("self"));
1015 var ma = new MemberAccess (this_access, f.name);
1016 ma.symbol_reference = f;
1017 ma.value_type = f.variable_type.copy ();
1018 visit_member_access (ma);
1019 ccode.add_expression (get_unref_expression (lhs, f.variable_type, ma));
1021 pop_context ();
1023 } else if (f.binding == MemberBinding.CLASS) {
1024 if (!is_gtypeinstance) {
1025 Report.error (f.source_reference, "class fields are not supported in compact classes");
1026 f.error = true;
1027 return;
1030 if (f.access == SymbolAccessibility.PRIVATE) {
1031 var ccall = new CCodeFunctionCall (new CCodeIdentifier ("%s_GET_CLASS_PRIVATE".printf (cl.get_upper_case_cname ())));
1032 ccall.add_argument (new CCodeIdentifier ("klass"));
1033 lhs = new CCodeMemberAccess (ccall, f.get_cname (), true);
1034 } else {
1035 lhs = new CCodeMemberAccess (new CCodeIdentifier ("klass"), f.get_cname (), true);
1038 if (f.initializer != null) {
1039 push_context (class_init_context);
1041 f.initializer.emit (this);
1043 var rhs = get_cvalue (f.initializer);
1045 ccode.add_assignment (lhs, rhs);
1047 foreach (LocalVariable local in temp_ref_vars) {
1048 ccode.add_expression (destroy_local (local));
1051 temp_ref_vars.clear ();
1053 pop_context ();
1055 } else {
1056 generate_field_declaration (f, cfile);
1058 if (!f.is_internal_symbol ()) {
1059 generate_field_declaration (f, header_file);
1061 if (!f.is_private_symbol ()) {
1062 generate_field_declaration (f, internal_header_file);
1065 lhs = new CCodeIdentifier (f.get_cname ());
1067 var var_decl = new CCodeVariableDeclarator (f.get_cname (), null, f.variable_type.get_cdeclarator_suffix ());
1068 var_decl.initializer = default_value_for_type (f.variable_type, true);
1070 if (class_init_context != null) {
1071 push_context (class_init_context);
1072 } else {
1073 push_context (new EmitContext ());
1076 if (f.initializer != null) {
1077 f.initializer.emit (this);
1079 var init = get_cvalue (f.initializer);
1080 if (is_constant_ccode_expression (init)) {
1081 var_decl.initializer = init;
1085 var var_def = new CCodeDeclaration (field_ctype);
1086 var_def.add_declarator (var_decl);
1087 if (!f.is_private_symbol ()) {
1088 var_def.modifiers = CCodeModifiers.EXTERN;
1089 } else {
1090 var_def.modifiers = CCodeModifiers.STATIC;
1092 cfile.add_type_member_declaration (var_def);
1094 /* add array length fields where necessary */
1095 if (f.variable_type is ArrayType && !f.no_array_length) {
1096 var array_type = (ArrayType) f.variable_type;
1098 if (!array_type.fixed_length) {
1099 for (int dim = 1; dim <= array_type.rank; dim++) {
1100 var len_type = int_type.copy ();
1102 var len_def = new CCodeDeclaration (len_type.get_cname ());
1103 len_def.add_declarator (new CCodeVariableDeclarator (get_array_length_cname (f.get_cname (), dim), new CCodeConstant ("0")));
1104 if (!f.is_private_symbol ()) {
1105 len_def.modifiers = CCodeModifiers.EXTERN;
1106 } else {
1107 len_def.modifiers = CCodeModifiers.STATIC;
1109 cfile.add_type_member_declaration (len_def);
1112 if (array_type.rank == 1 && f.is_internal_symbol ()) {
1113 var len_type = int_type.copy ();
1115 var cdecl = new CCodeDeclaration (len_type.get_cname ());
1116 cdecl.add_declarator (new CCodeVariableDeclarator (get_array_size_cname (f.get_cname ()), new CCodeConstant ("0")));
1117 cdecl.modifiers = CCodeModifiers.STATIC;
1118 cfile.add_type_member_declaration (cdecl);
1121 } else if (f.variable_type is DelegateType) {
1122 var delegate_type = (DelegateType) f.variable_type;
1123 if (delegate_type.delegate_symbol.has_target) {
1124 // create field to store delegate target
1126 var target_def = new CCodeDeclaration ("gpointer");
1127 target_def.add_declarator (new CCodeVariableDeclarator (get_delegate_target_cname (f.get_cname ()), new CCodeConstant ("NULL")));
1128 if (!f.is_private_symbol ()) {
1129 target_def.modifiers = CCodeModifiers.EXTERN;
1130 } else {
1131 target_def.modifiers = CCodeModifiers.STATIC;
1133 cfile.add_type_member_declaration (target_def);
1135 if (delegate_type.value_owned) {
1136 var target_destroy_notify_def = new CCodeDeclaration ("GDestroyNotify");
1137 target_destroy_notify_def.add_declarator (new CCodeVariableDeclarator (get_delegate_target_destroy_notify_cname (f.get_cname ()), new CCodeConstant ("NULL")));
1138 if (!f.is_private_symbol ()) {
1139 target_destroy_notify_def.modifiers = CCodeModifiers.EXTERN;
1140 } else {
1141 target_destroy_notify_def.modifiers = CCodeModifiers.STATIC;
1143 cfile.add_type_member_declaration (target_destroy_notify_def);
1149 if (f.initializer != null) {
1150 var rhs = get_cvalue (f.initializer);
1151 if (!is_constant_ccode_expression (rhs)) {
1152 if (f.parent_symbol is Class) {
1153 if (f.initializer is InitializerList) {
1154 ccode.open_block ();
1156 var temp_decl = get_temp_variable (f.variable_type);
1157 var vardecl = new CCodeVariableDeclarator.zero (temp_decl.name, rhs);
1158 ccode.add_declaration (temp_decl.variable_type.get_cname (), vardecl);
1160 var tmp = get_variable_cexpression (get_variable_cname (temp_decl.name));
1161 ccode.add_assignment (lhs, tmp);
1163 ccode.close ();
1164 } else {
1165 ccode.add_assignment (lhs, rhs);
1168 if (f.variable_type is ArrayType && !f.no_array_length &&
1169 f.initializer is ArrayCreationExpression) {
1170 var array_type = (ArrayType) f.variable_type;
1171 var ma = new MemberAccess.simple (f.name);
1172 ma.symbol_reference = f;
1173 ma.value_type = f.variable_type.copy ();
1174 visit_member_access (ma);
1176 List<Expression> sizes = ((ArrayCreationExpression) f.initializer).get_sizes ();
1177 for (int dim = 1; dim <= array_type.rank; dim++) {
1178 var array_len_lhs = get_array_length_cexpression (ma, dim);
1179 var size = sizes[dim - 1];
1180 ccode.add_assignment (array_len_lhs, get_cvalue (size));
1183 } else {
1184 f.error = true;
1185 Report.error (f.source_reference, "Non-constant field initializers not supported in this context");
1186 return;
1191 pop_context ();
1195 public bool is_constant_ccode_expression (CCodeExpression cexpr) {
1196 if (cexpr is CCodeConstant) {
1197 return true;
1198 } else if (cexpr is CCodeCastExpression) {
1199 var ccast = (CCodeCastExpression) cexpr;
1200 return is_constant_ccode_expression (ccast.inner);
1201 } else if (cexpr is CCodeBinaryExpression) {
1202 var cbinary = (CCodeBinaryExpression) cexpr;
1203 return is_constant_ccode_expression (cbinary.left) && is_constant_ccode_expression (cbinary.right);
1206 var cparenthesized = (cexpr as CCodeParenthesizedExpression);
1207 return (null != cparenthesized && is_constant_ccode_expression (cparenthesized.inner));
1211 * Returns whether the passed cexpr is a pure expression, i.e. an
1212 * expression without side-effects.
1214 public bool is_pure_ccode_expression (CCodeExpression cexpr) {
1215 if (cexpr is CCodeConstant || cexpr is CCodeIdentifier) {
1216 return true;
1217 } else if (cexpr is CCodeBinaryExpression) {
1218 var cbinary = (CCodeBinaryExpression) cexpr;
1219 return is_pure_ccode_expression (cbinary.left) && is_constant_ccode_expression (cbinary.right);
1220 } else if (cexpr is CCodeUnaryExpression) {
1221 var cunary = (CCodeUnaryExpression) cexpr;
1222 switch (cunary.operator) {
1223 case CCodeUnaryOperator.PREFIX_INCREMENT:
1224 case CCodeUnaryOperator.PREFIX_DECREMENT:
1225 case CCodeUnaryOperator.POSTFIX_INCREMENT:
1226 case CCodeUnaryOperator.POSTFIX_DECREMENT:
1227 return false;
1228 default:
1229 return is_pure_ccode_expression (cunary.inner);
1231 } else if (cexpr is CCodeMemberAccess) {
1232 var cma = (CCodeMemberAccess) cexpr;
1233 return is_pure_ccode_expression (cma.inner);
1234 } else if (cexpr is CCodeElementAccess) {
1235 var cea = (CCodeElementAccess) cexpr;
1236 return is_pure_ccode_expression (cea.container) && is_pure_ccode_expression (cea.index);
1237 } else if (cexpr is CCodeCastExpression) {
1238 var ccast = (CCodeCastExpression) cexpr;
1239 return is_pure_ccode_expression (ccast.inner);
1240 } else if (cexpr is CCodeParenthesizedExpression) {
1241 var cparenthesized = (CCodeParenthesizedExpression) cexpr;
1242 return is_pure_ccode_expression (cparenthesized.inner);
1245 return false;
1248 public override void visit_formal_parameter (Parameter p) {
1249 if (!p.ellipsis) {
1250 check_type (p.variable_type);
1254 public override void visit_property (Property prop) {
1255 visit_member (prop);
1257 check_type (prop.property_type);
1259 if (prop.get_accessor != null) {
1260 prop.get_accessor.accept (this);
1262 if (prop.set_accessor != null) {
1263 prop.set_accessor.accept (this);
1267 public void generate_type_declaration (DataType type, CCodeFile decl_space) {
1268 if (type is ObjectType) {
1269 var object_type = (ObjectType) type;
1270 if (object_type.type_symbol is Class) {
1271 generate_class_declaration ((Class) object_type.type_symbol, decl_space);
1272 } else if (object_type.type_symbol is Interface) {
1273 generate_interface_declaration ((Interface) object_type.type_symbol, decl_space);
1275 } else if (type is DelegateType) {
1276 var deleg_type = (DelegateType) type;
1277 var d = deleg_type.delegate_symbol;
1278 generate_delegate_declaration (d, decl_space);
1279 } else if (type.data_type is Enum) {
1280 var en = (Enum) type.data_type;
1281 generate_enum_declaration (en, decl_space);
1282 } else if (type is ValueType) {
1283 var value_type = (ValueType) type;
1284 generate_struct_declaration ((Struct) value_type.type_symbol, decl_space);
1285 } else if (type is ArrayType) {
1286 var array_type = (ArrayType) type;
1287 generate_type_declaration (array_type.element_type, decl_space);
1288 } else if (type is ErrorType) {
1289 var error_type = (ErrorType) type;
1290 if (error_type.error_domain != null) {
1291 generate_error_domain_declaration (error_type.error_domain, decl_space);
1293 } else if (type is PointerType) {
1294 var pointer_type = (PointerType) type;
1295 generate_type_declaration (pointer_type.base_type, decl_space);
1298 foreach (DataType type_arg in type.get_type_arguments ()) {
1299 generate_type_declaration (type_arg, decl_space);
1303 public virtual void generate_class_struct_declaration (Class cl, CCodeFile decl_space) {
1306 public virtual void generate_struct_declaration (Struct st, CCodeFile decl_space) {
1309 public virtual void generate_delegate_declaration (Delegate d, CCodeFile decl_space) {
1312 public virtual void generate_cparameters (Method m, CCodeFile decl_space, Map<int,CCodeParameter> cparam_map, CCodeFunction func, CCodeFunctionDeclarator? vdeclarator = null, Map<int,CCodeExpression>? carg_map = null, CCodeFunctionCall? vcall = null, int direction = 3) {
1315 public void generate_property_accessor_declaration (PropertyAccessor acc, CCodeFile decl_space) {
1316 if (add_symbol_declaration (decl_space, acc, acc.get_cname ())) {
1317 return;
1320 var prop = (Property) acc.prop;
1322 bool returns_real_struct = acc.readable && prop.property_type.is_real_non_null_struct_type ();
1325 CCodeParameter cvalueparam;
1326 if (returns_real_struct) {
1327 cvalueparam = new CCodeParameter ("result", acc.value_type.get_cname () + "*");
1328 } else if (!acc.readable && prop.property_type.is_real_non_null_struct_type ()) {
1329 cvalueparam = new CCodeParameter ("value", acc.value_type.get_cname () + "*");
1330 } else {
1331 cvalueparam = new CCodeParameter ("value", acc.value_type.get_cname ());
1333 generate_type_declaration (acc.value_type, decl_space);
1335 CCodeFunction function;
1336 if (acc.readable && !returns_real_struct) {
1337 function = new CCodeFunction (acc.get_cname (), acc.value_type.get_cname ());
1338 } else {
1339 function = new CCodeFunction (acc.get_cname (), "void");
1342 if (prop.binding == MemberBinding.INSTANCE) {
1343 var t = (TypeSymbol) prop.parent_symbol;
1344 var this_type = get_data_type_for_symbol (t);
1345 generate_type_declaration (this_type, decl_space);
1346 var cselfparam = new CCodeParameter ("self", this_type.get_cname ());
1347 if (t is Struct) {
1348 cselfparam.type_name += "*";
1351 function.add_parameter (cselfparam);
1354 if (acc.writable || acc.construction || returns_real_struct) {
1355 function.add_parameter (cvalueparam);
1358 if (acc.value_type is ArrayType) {
1359 var array_type = (ArrayType) acc.value_type;
1361 var length_ctype = "int";
1362 if (acc.readable) {
1363 length_ctype = "int*";
1366 for (int dim = 1; dim <= array_type.rank; dim++) {
1367 function.add_parameter (new CCodeParameter (get_array_length_cname (acc.readable ? "result" : "value", dim), length_ctype));
1369 } else if ((acc.value_type is DelegateType) && ((DelegateType) acc.value_type).delegate_symbol.has_target) {
1370 function.add_parameter (new CCodeParameter (get_delegate_target_cname (acc.readable ? "result" : "value"), acc.readable ? "gpointer*" : "gpointer"));
1373 if (prop.is_private_symbol () || (!acc.readable && !acc.writable) || acc.access == SymbolAccessibility.PRIVATE) {
1374 function.modifiers |= CCodeModifiers.STATIC;
1376 decl_space.add_function_declaration (function);
1379 public override void visit_property_accessor (PropertyAccessor acc) {
1380 push_context (new EmitContext (acc));
1382 var prop = (Property) acc.prop;
1384 if (acc.comment != null) {
1385 cfile.add_type_member_definition (new CCodeComment (acc.comment.content));
1388 bool returns_real_struct = acc.readable && prop.property_type.is_real_non_null_struct_type ();
1390 if (acc.result_var != null) {
1391 acc.result_var.accept (this);
1394 var t = (TypeSymbol) prop.parent_symbol;
1396 if (acc.construction && !t.is_subtype_of (gobject_type)) {
1397 Report.error (acc.source_reference, "construct properties require GLib.Object");
1398 acc.error = true;
1399 return;
1400 } else if (acc.construction && !is_gobject_property (prop)) {
1401 Report.error (acc.source_reference, "construct properties not supported for specified property type");
1402 acc.error = true;
1403 return;
1406 // do not declare overriding properties and interface implementations
1407 if (prop.is_abstract || prop.is_virtual
1408 || (prop.base_property == null && prop.base_interface_property == null)) {
1409 generate_property_accessor_declaration (acc, cfile);
1411 // do not declare construct-only properties in header files
1412 if (acc.readable || acc.writable) {
1413 if (!prop.is_internal_symbol ()
1414 && (acc.access == SymbolAccessibility.PUBLIC
1415 || acc.access == SymbolAccessibility.PROTECTED)) {
1416 generate_property_accessor_declaration (acc, header_file);
1418 if (!prop.is_private_symbol () && acc.access != SymbolAccessibility.PRIVATE) {
1419 generate_property_accessor_declaration (acc, internal_header_file);
1424 if (acc.source_type == SourceFileType.FAST) {
1425 return;
1428 var this_type = get_data_type_for_symbol (t);
1429 var cselfparam = new CCodeParameter ("self", this_type.get_cname ());
1430 if (t is Struct) {
1431 cselfparam.type_name += "*";
1433 CCodeParameter cvalueparam;
1434 if (returns_real_struct) {
1435 cvalueparam = new CCodeParameter ("result", acc.value_type.get_cname () + "*");
1436 } else if (!acc.readable && prop.property_type.is_real_non_null_struct_type ()) {
1437 cvalueparam = new CCodeParameter ("value", acc.value_type.get_cname () + "*");
1438 } else {
1439 cvalueparam = new CCodeParameter ("value", acc.value_type.get_cname ());
1442 if (prop.is_abstract || prop.is_virtual) {
1443 CCodeFunction function;
1444 if (acc.readable && !returns_real_struct) {
1445 function = new CCodeFunction (acc.get_cname (), current_return_type.get_cname ());
1446 } else {
1447 function = new CCodeFunction (acc.get_cname (), "void");
1449 function.add_parameter (cselfparam);
1450 if (acc.writable || acc.construction || returns_real_struct) {
1451 function.add_parameter (cvalueparam);
1454 if (acc.value_type is ArrayType) {
1455 var array_type = (ArrayType) acc.value_type;
1457 var length_ctype = "int";
1458 if (acc.readable) {
1459 length_ctype = "int*";
1462 for (int dim = 1; dim <= array_type.rank; dim++) {
1463 function.add_parameter (new CCodeParameter (get_array_length_cname (acc.readable ? "result" : "value", dim), length_ctype));
1465 } else if ((acc.value_type is DelegateType) && ((DelegateType) acc.value_type).delegate_symbol.has_target) {
1466 function.add_parameter (new CCodeParameter (get_delegate_target_cname (acc.readable ? "result" : "value"), acc.readable ? "gpointer*" : "gpointer"));
1469 if (prop.is_private_symbol () || !(acc.readable || acc.writable) || acc.access == SymbolAccessibility.PRIVATE) {
1470 // accessor function should be private if the property is an internal symbol or it's a construct-only setter
1471 function.modifiers |= CCodeModifiers.STATIC;
1474 push_function (function);
1476 CCodeFunctionCall vcast = null;
1477 if (prop.parent_symbol is Interface) {
1478 var iface = (Interface) prop.parent_symbol;
1480 vcast = new CCodeFunctionCall (new CCodeIdentifier ("%s_GET_INTERFACE".printf (iface.get_upper_case_cname (null))));
1481 } else {
1482 var cl = (Class) prop.parent_symbol;
1484 vcast = new CCodeFunctionCall (new CCodeIdentifier ("%s_GET_CLASS".printf (cl.get_upper_case_cname (null))));
1486 vcast.add_argument (new CCodeIdentifier ("self"));
1488 if (acc.readable) {
1489 var vcall = new CCodeFunctionCall (new CCodeMemberAccess.pointer (vcast, "get_%s".printf (prop.name)));
1490 vcall.add_argument (new CCodeIdentifier ("self"));
1491 if (returns_real_struct) {
1492 vcall.add_argument (new CCodeIdentifier ("result"));
1493 ccode.add_expression (vcall);
1494 } else {
1495 if (acc.value_type is ArrayType) {
1496 var array_type = (ArrayType) acc.value_type;
1498 for (int dim = 1; dim <= array_type.rank; dim++) {
1499 var len_expr = new CCodeIdentifier (get_array_length_cname ("result", dim));
1500 vcall.add_argument (len_expr);
1502 } else if ((acc.value_type is DelegateType) && ((DelegateType) acc.value_type).delegate_symbol.has_target) {
1503 vcall.add_argument (new CCodeIdentifier (get_delegate_target_cname ("result")));
1506 ccode.add_return (vcall);
1508 } else {
1509 var vcall = new CCodeFunctionCall (new CCodeMemberAccess.pointer (vcast, "set_%s".printf (prop.name)));
1510 vcall.add_argument (new CCodeIdentifier ("self"));
1511 vcall.add_argument (new CCodeIdentifier ("value"));
1513 if (acc.value_type is ArrayType) {
1514 var array_type = (ArrayType) acc.value_type;
1516 for (int dim = 1; dim <= array_type.rank; dim++) {
1517 var len_expr = new CCodeIdentifier (get_array_length_cname ("value", dim));
1518 vcall.add_argument (len_expr);
1520 } else if ((acc.value_type is DelegateType) && ((DelegateType) acc.value_type).delegate_symbol.has_target) {
1521 vcall.add_argument (new CCodeIdentifier (get_delegate_target_cname ("value")));
1524 ccode.add_expression (vcall);
1527 pop_function ();
1529 cfile.add_function (function);
1532 if (!prop.is_abstract) {
1533 bool is_virtual = prop.base_property != null || prop.base_interface_property != null;
1535 string cname;
1536 if (is_virtual) {
1537 if (acc.readable) {
1538 cname = "%s_real_get_%s".printf (t.get_lower_case_cname (null), prop.name);
1539 } else {
1540 cname = "%s_real_set_%s".printf (t.get_lower_case_cname (null), prop.name);
1542 } else {
1543 cname = acc.get_cname ();
1546 CCodeFunction function;
1547 if (acc.writable || acc.construction || returns_real_struct) {
1548 function = new CCodeFunction (cname, "void");
1549 } else {
1550 function = new CCodeFunction (cname, acc.value_type.get_cname ());
1553 ObjectType base_type = null;
1554 if (prop.binding == MemberBinding.INSTANCE) {
1555 if (is_virtual) {
1556 if (prop.base_property != null) {
1557 base_type = new ObjectType ((ObjectTypeSymbol) prop.base_property.parent_symbol);
1558 } else if (prop.base_interface_property != null) {
1559 base_type = new ObjectType ((ObjectTypeSymbol) prop.base_interface_property.parent_symbol);
1561 function.modifiers |= CCodeModifiers.STATIC;
1562 function.add_parameter (new CCodeParameter ("base", base_type.get_cname ()));
1563 } else {
1564 function.add_parameter (cselfparam);
1567 if (acc.writable || acc.construction || returns_real_struct) {
1568 function.add_parameter (cvalueparam);
1571 if (acc.value_type is ArrayType) {
1572 var array_type = (ArrayType) acc.value_type;
1574 var length_ctype = "int";
1575 if (acc.readable) {
1576 length_ctype = "int*";
1579 for (int dim = 1; dim <= array_type.rank; dim++) {
1580 function.add_parameter (new CCodeParameter (get_array_length_cname (acc.readable ? "result" : "value", dim), length_ctype));
1582 } else if ((acc.value_type is DelegateType) && ((DelegateType) acc.value_type).delegate_symbol.has_target) {
1583 function.add_parameter (new CCodeParameter (get_delegate_target_cname (acc.readable ? "result" : "value"), acc.readable ? "gpointer*" : "gpointer"));
1586 if (!is_virtual) {
1587 if (prop.is_private_symbol () || !(acc.readable || acc.writable) || acc.access == SymbolAccessibility.PRIVATE) {
1588 // accessor function should be private if the property is an internal symbol or it's a construct-only setter
1589 function.modifiers |= CCodeModifiers.STATIC;
1593 push_function (function);
1595 if (prop.binding == MemberBinding.INSTANCE && !is_virtual) {
1596 if (!acc.readable || returns_real_struct) {
1597 create_property_type_check_statement (prop, false, t, true, "self");
1598 } else {
1599 create_property_type_check_statement (prop, true, t, true, "self");
1603 if (acc.readable && !returns_real_struct) {
1604 // do not declare result variable if exit block is known to be unreachable
1605 if (acc.return_block == null || acc.return_block.get_predecessors ().size > 0) {
1606 ccode.add_declaration (acc.value_type.get_cname (), new CCodeVariableDeclarator ("result"));
1610 if (is_virtual) {
1611 ccode.add_declaration (this_type.get_cname (), new CCodeVariableDeclarator ("self"));
1612 ccode.add_assignment (new CCodeIdentifier ("self"), transform_expression (new CCodeIdentifier ("base"), base_type, this_type));
1615 acc.body.emit (this);
1617 if (current_method_inner_error) {
1618 ccode.add_declaration ("GError *", new CCodeVariableDeclarator.zero ("_inner_error_", new CCodeConstant ("NULL")));
1621 // notify on property changes
1622 if (is_gobject_property (prop) &&
1623 prop.notify &&
1624 (acc.writable || acc.construction)) {
1625 var notify_call = new CCodeFunctionCall (new CCodeIdentifier ("g_object_notify"));
1626 notify_call.add_argument (new CCodeCastExpression (new CCodeIdentifier ("self"), "GObject *"));
1627 notify_call.add_argument (prop.get_canonical_cconstant ());
1628 ccode.add_expression (notify_call);
1631 cfile.add_function (function);
1634 pop_context ();
1637 public override void visit_destructor (Destructor d) {
1638 if (d.binding == MemberBinding.STATIC && !in_plugin) {
1639 Report.error (d.source_reference, "static destructors are only supported for dynamic types");
1640 d.error = true;
1641 return;
1645 public int get_block_id (Block b) {
1646 int result = block_map[b];
1647 if (result == 0) {
1648 result = ++next_block_id;
1649 block_map[b] = result;
1651 return result;
1654 void capture_parameter (Parameter param, CCodeStruct data, int block_id) {
1655 generate_type_declaration (param.variable_type, cfile);
1657 var param_type = param.variable_type.copy ();
1658 param_type.value_owned = true;
1659 data.add_field (param_type.get_cname (), get_variable_cname (param.name));
1661 bool is_unowned_delegate = param.variable_type is DelegateType && !param.variable_type.value_owned;
1663 // create copy if necessary as captured variables may need to be kept alive
1664 CCodeExpression cparam = get_variable_cexpression (param.name);
1665 if (param.variable_type.is_real_non_null_struct_type ()) {
1666 cparam = new CCodeUnaryExpression (CCodeUnaryOperator.POINTER_INDIRECTION, cparam);
1668 if (requires_copy (param_type) && !param.variable_type.value_owned && !is_unowned_delegate) {
1669 var ma = new MemberAccess.simple (param.name);
1670 ma.symbol_reference = param;
1671 ma.value_type = param.variable_type.copy ();
1672 // directly access parameters in ref expressions
1673 param.captured = false;
1674 visit_member_access (ma);
1675 cparam = get_ref_cexpression (param.variable_type, cparam, ma, param);
1676 param.captured = true;
1679 ccode.add_assignment (new CCodeMemberAccess.pointer (get_variable_cexpression ("_data%d_".printf (block_id)), get_variable_cname (param.name)), cparam);
1681 if (param.variable_type is ArrayType) {
1682 var array_type = (ArrayType) param.variable_type;
1683 for (int dim = 1; dim <= array_type.rank; dim++) {
1684 data.add_field ("gint", get_parameter_array_length_cname (param, dim));
1685 ccode.add_assignment (new CCodeMemberAccess.pointer (get_variable_cexpression ("_data%d_".printf (block_id)), get_array_length_cname (get_variable_cname (param.name), dim)), new CCodeIdentifier (get_array_length_cname (get_variable_cname (param.name), dim)));
1687 } else if (param.variable_type is DelegateType) {
1688 CCodeExpression target_expr;
1689 CCodeExpression delegate_target_destroy_notify;
1690 if (is_in_coroutine ()) {
1691 target_expr = new CCodeMemberAccess.pointer (new CCodeIdentifier ("data"), get_delegate_target_cname (get_variable_cname (param.name)));
1692 delegate_target_destroy_notify = new CCodeMemberAccess.pointer (new CCodeIdentifier ("data"), get_delegate_target_destroy_notify_cname (get_variable_cname (param.name)));
1693 } else {
1694 target_expr = new CCodeIdentifier (get_delegate_target_cname (get_variable_cname (param.name)));
1695 delegate_target_destroy_notify = new CCodeIdentifier (get_delegate_target_destroy_notify_cname (get_variable_cname (param.name)));
1698 data.add_field ("gpointer", get_delegate_target_cname (get_variable_cname (param.name)));
1699 ccode.add_assignment (new CCodeMemberAccess.pointer (get_variable_cexpression ("_data%d_".printf (block_id)), get_delegate_target_cname (get_variable_cname (param.name))), target_expr);
1700 if (param.variable_type.value_owned) {
1701 data.add_field ("GDestroyNotify", get_delegate_target_destroy_notify_cname (get_variable_cname (param.name)));
1702 ccode.add_assignment (new CCodeMemberAccess.pointer (get_variable_cexpression ("_data%d_".printf (block_id)), get_delegate_target_destroy_notify_cname (get_variable_cname (param.name))), delegate_target_destroy_notify);
1707 public override void visit_block (Block b) {
1708 emit_context.push_symbol (b);
1710 var local_vars = b.get_local_variables ();
1712 if (b.parent_node is Block || b.parent_node is SwitchStatement) {
1713 ccode.open_block ();
1716 if (b.captured) {
1717 var parent_block = next_closure_block (b.parent_symbol);
1719 int block_id = get_block_id (b);
1720 string struct_name = "Block%dData".printf (block_id);
1722 var data = new CCodeStruct ("_" + struct_name);
1723 data.add_field ("int", "_ref_count_");
1724 if (parent_block != null) {
1725 int parent_block_id = get_block_id (parent_block);
1727 data.add_field ("Block%dData *".printf (parent_block_id), "_data%d_".printf (parent_block_id));
1728 } else {
1729 if (in_constructor || (current_method != null && current_method.binding == MemberBinding.INSTANCE) ||
1730 (current_property_accessor != null && current_property_accessor.prop.binding == MemberBinding.INSTANCE)) {
1731 data.add_field ("%s *".printf (current_class.get_cname ()), "self");
1734 if (current_method != null) {
1735 // allow capturing generic type parameters
1736 foreach (var type_param in current_method.get_type_parameters ()) {
1737 string func_name;
1739 func_name = "%s_type".printf (type_param.name.down ());
1740 data.add_field ("GType", func_name);
1742 func_name = "%s_dup_func".printf (type_param.name.down ());
1743 data.add_field ("GBoxedCopyFunc", func_name);
1745 func_name = "%s_destroy_func".printf (type_param.name.down ());
1746 data.add_field ("GDestroyNotify", func_name);
1750 foreach (var local in local_vars) {
1751 if (local.captured) {
1752 generate_type_declaration (local.variable_type, cfile);
1754 data.add_field (local.variable_type.get_cname (), get_variable_cname (local.name) + local.variable_type.get_cdeclarator_suffix ());
1756 if (local.variable_type is ArrayType) {
1757 var array_type = (ArrayType) local.variable_type;
1758 for (int dim = 1; dim <= array_type.rank; dim++) {
1759 data.add_field ("gint", get_array_length_cname (get_variable_cname (local.name), dim));
1761 data.add_field ("gint", get_array_size_cname (get_variable_cname (local.name)));
1762 } else if (local.variable_type is DelegateType) {
1763 data.add_field ("gpointer", get_delegate_target_cname (get_variable_cname (local.name)));
1764 if (local.variable_type.value_owned) {
1765 data.add_field ("GDestroyNotify", get_delegate_target_destroy_notify_cname (get_variable_cname (local.name)));
1771 var data_alloc = new CCodeFunctionCall (new CCodeIdentifier ("g_slice_new0"));
1772 data_alloc.add_argument (new CCodeIdentifier (struct_name));
1774 if (is_in_coroutine ()) {
1775 closure_struct.add_field (struct_name + "*", "_data%d_".printf (block_id));
1776 } else {
1777 ccode.add_declaration (struct_name + "*", new CCodeVariableDeclarator ("_data%d_".printf (block_id)));
1779 ccode.add_assignment (get_variable_cexpression ("_data%d_".printf (block_id)), data_alloc);
1781 // initialize ref_count
1782 ccode.add_assignment (new CCodeMemberAccess.pointer (get_variable_cexpression ("_data%d_".printf (block_id)), "_ref_count_"), new CCodeIdentifier ("1"));
1784 if (parent_block != null) {
1785 int parent_block_id = get_block_id (parent_block);
1787 var ref_call = new CCodeFunctionCall (new CCodeIdentifier ("block%d_data_ref".printf (parent_block_id)));
1788 ref_call.add_argument (get_variable_cexpression ("_data%d_".printf (parent_block_id)));
1790 ccode.add_assignment (new CCodeMemberAccess.pointer (get_variable_cexpression ("_data%d_".printf (block_id)), "_data%d_".printf (parent_block_id)), ref_call);
1791 } else {
1792 if (in_constructor || (current_method != null && current_method.binding == MemberBinding.INSTANCE &&
1793 (!(current_method is CreationMethod) || current_method.body != b)) ||
1794 (current_property_accessor != null && current_property_accessor.prop.binding == MemberBinding.INSTANCE)) {
1795 var ref_call = new CCodeFunctionCall (get_dup_func_expression (new ObjectType (current_class), b.source_reference));
1796 ref_call.add_argument (get_result_cexpression ("self"));
1798 ccode.add_assignment (new CCodeMemberAccess.pointer (get_variable_cexpression ("_data%d_".printf (block_id)), "self"), ref_call);
1801 if (current_method != null) {
1802 // allow capturing generic type parameters
1803 foreach (var type_param in current_method.get_type_parameters ()) {
1804 string func_name;
1806 func_name = "%s_type".printf (type_param.name.down ());
1807 ccode.add_assignment (new CCodeMemberAccess.pointer (get_variable_cexpression ("_data%d_".printf (block_id)), func_name), new CCodeIdentifier (func_name));
1809 func_name = "%s_dup_func".printf (type_param.name.down ());
1810 ccode.add_assignment (new CCodeMemberAccess.pointer (get_variable_cexpression ("_data%d_".printf (block_id)), func_name), new CCodeIdentifier (func_name));
1812 func_name = "%s_destroy_func".printf (type_param.name.down ());
1813 ccode.add_assignment (new CCodeMemberAccess.pointer (get_variable_cexpression ("_data%d_".printf (block_id)), func_name), new CCodeIdentifier (func_name));
1818 if (b.parent_symbol is Method) {
1819 var m = (Method) b.parent_symbol;
1821 // parameters are captured with the top-level block of the method
1822 foreach (var param in m.get_parameters ()) {
1823 if (param.captured) {
1824 capture_parameter (param, data, block_id);
1828 if (m.coroutine) {
1829 // capture async data to allow invoking callback from inside closure
1830 data.add_field ("gpointer", "_async_data_");
1832 // async method is suspended while waiting for callback,
1833 // so we never need to care about memory management of async data
1834 ccode.add_assignment (new CCodeMemberAccess.pointer (get_variable_cexpression ("_data%d_".printf (block_id)), "_async_data_"), new CCodeIdentifier ("data"));
1836 } else if (b.parent_symbol is PropertyAccessor) {
1837 var acc = (PropertyAccessor) b.parent_symbol;
1839 if (!acc.readable && acc.value_parameter.captured) {
1840 capture_parameter (acc.value_parameter, data, block_id);
1844 var typedef = new CCodeTypeDefinition ("struct _" + struct_name, new CCodeVariableDeclarator (struct_name));
1845 cfile.add_type_declaration (typedef);
1846 cfile.add_type_definition (data);
1848 // create ref/unref functions
1849 var ref_fun = new CCodeFunction ("block%d_data_ref".printf (block_id), struct_name + "*");
1850 ref_fun.add_parameter (new CCodeParameter ("_data%d_".printf (block_id), struct_name + "*"));
1851 ref_fun.modifiers = CCodeModifiers.STATIC;
1852 cfile.add_function_declaration (ref_fun);
1853 ref_fun.block = new CCodeBlock ();
1855 var ccall = new CCodeFunctionCall (new CCodeIdentifier ("g_atomic_int_inc"));
1856 ccall.add_argument (new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, new CCodeMemberAccess.pointer (new CCodeIdentifier ("_data%d_".printf (block_id)), "_ref_count_")));
1857 ref_fun.block.add_statement (new CCodeExpressionStatement (ccall));
1858 ref_fun.block.add_statement (new CCodeReturnStatement (new CCodeIdentifier ("_data%d_".printf (block_id))));
1859 cfile.add_function (ref_fun);
1861 var unref_fun = new CCodeFunction ("block%d_data_unref".printf (block_id), "void");
1862 unref_fun.add_parameter (new CCodeParameter ("_data%d_".printf (block_id), struct_name + "*"));
1863 unref_fun.modifiers = CCodeModifiers.STATIC;
1864 cfile.add_function_declaration (unref_fun);
1866 push_function (unref_fun);
1868 ccall = new CCodeFunctionCall (new CCodeIdentifier ("g_atomic_int_dec_and_test"));
1869 ccall.add_argument (new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, new CCodeMemberAccess.pointer (new CCodeIdentifier ("_data%d_".printf (block_id)), "_ref_count_")));
1870 ccode.open_if (ccall);
1872 if (parent_block != null) {
1873 int parent_block_id = get_block_id (parent_block);
1875 var unref_call = new CCodeFunctionCall (new CCodeIdentifier ("block%d_data_unref".printf (parent_block_id)));
1876 unref_call.add_argument (new CCodeMemberAccess.pointer (new CCodeIdentifier ("_data%d_".printf (block_id)), "_data%d_".printf (parent_block_id)));
1877 ccode.add_expression (unref_call);
1878 ccode.add_assignment (new CCodeMemberAccess.pointer (new CCodeIdentifier ("_data%d_".printf (block_id)), "_data%d_".printf (parent_block_id)), new CCodeConstant ("NULL"));
1879 } else {
1880 if (in_constructor || (current_method != null && current_method.binding == MemberBinding.INSTANCE) ||
1881 (current_property_accessor != null && current_property_accessor.prop.binding == MemberBinding.INSTANCE)) {
1882 var ma = new MemberAccess.simple ("this");
1883 ma.symbol_reference = current_class;
1884 ccode.add_expression (get_unref_expression (new CCodeMemberAccess.pointer (new CCodeIdentifier ("_data%d_".printf (block_id)), "self"), new ObjectType (current_class), ma));
1888 // free in reverse order
1889 for (int i = local_vars.size - 1; i >= 0; i--) {
1890 var local = local_vars[i];
1891 if (local.captured) {
1892 if (requires_destroy (local.variable_type)) {
1893 bool old_coroutine = false;
1894 if (current_method != null) {
1895 old_coroutine = current_method.coroutine;
1896 current_method.coroutine = false;
1899 ccode.add_expression (destroy_local (local));
1901 if (old_coroutine) {
1902 current_method.coroutine = true;
1908 if (b.parent_symbol is Method) {
1909 var m = (Method) b.parent_symbol;
1911 // parameters are captured with the top-level block of the method
1912 foreach (var param in m.get_parameters ()) {
1913 if (param.captured) {
1914 var param_type = param.variable_type.copy ();
1915 param_type.value_owned = true;
1917 bool is_unowned_delegate = param.variable_type is DelegateType && !param.variable_type.value_owned;
1919 if (requires_destroy (param_type) && !is_unowned_delegate) {
1920 bool old_coroutine = false;
1921 if (m != null) {
1922 old_coroutine = m.coroutine;
1923 m.coroutine = false;
1926 ccode.add_expression (destroy_parameter (param));
1928 if (old_coroutine) {
1929 m.coroutine = true;
1934 } else if (b.parent_symbol is PropertyAccessor) {
1935 var acc = (PropertyAccessor) b.parent_symbol;
1937 if (!acc.readable && acc.value_parameter.captured) {
1938 var param_type = acc.value_parameter.variable_type.copy ();
1939 param_type.value_owned = true;
1941 bool is_unowned_delegate = acc.value_parameter.variable_type is DelegateType && !acc.value_parameter.variable_type.value_owned;
1943 if (requires_destroy (param_type) && !is_unowned_delegate) {
1944 ccode.add_expression (destroy_parameter (acc.value_parameter));
1949 var data_free = new CCodeFunctionCall (new CCodeIdentifier ("g_slice_free"));
1950 data_free.add_argument (new CCodeIdentifier (struct_name));
1951 data_free.add_argument (new CCodeIdentifier ("_data%d_".printf (block_id)));
1952 ccode.add_expression (data_free);
1954 ccode.close ();
1956 pop_function ();
1958 cfile.add_function (unref_fun);
1961 foreach (Statement stmt in b.get_statements ()) {
1962 stmt.emit (this);
1965 // free in reverse order
1966 for (int i = local_vars.size - 1; i >= 0; i--) {
1967 var local = local_vars[i];
1968 local.active = false;
1969 if (!local.unreachable && !local.floating && !local.captured && requires_destroy (local.variable_type)) {
1970 ccode.add_expression (destroy_local (local));
1974 if (b.parent_symbol is Method) {
1975 var m = (Method) b.parent_symbol;
1976 foreach (Parameter param in m.get_parameters ()) {
1977 if (!param.captured && !param.ellipsis && requires_destroy (param.variable_type) && param.direction == ParameterDirection.IN) {
1978 ccode.add_expression (destroy_parameter (param));
1979 } else if (param.direction == ParameterDirection.OUT && !m.coroutine) {
1980 return_out_parameter (param);
1985 if (b.captured) {
1986 int block_id = get_block_id (b);
1988 var data_unref = new CCodeFunctionCall (new CCodeIdentifier ("block%d_data_unref".printf (block_id)));
1989 data_unref.add_argument (get_variable_cexpression ("_data%d_".printf (block_id)));
1990 ccode.add_expression (data_unref);
1991 ccode.add_assignment (get_variable_cexpression ("_data%d_".printf (block_id)), new CCodeConstant ("NULL"));
1994 if (b.parent_node is Block || b.parent_node is SwitchStatement) {
1995 ccode.close ();
1998 emit_context.pop_symbol ();
2001 public override void visit_declaration_statement (DeclarationStatement stmt) {
2002 stmt.declaration.accept (this);
2005 public CCodeExpression get_variable_cexpression (string name) {
2006 if (is_in_coroutine ()) {
2007 return new CCodeMemberAccess.pointer (new CCodeIdentifier ("data"), get_variable_cname (name));
2008 } else {
2009 return new CCodeIdentifier (get_variable_cname (name));
2013 public string get_variable_cname (string name) {
2014 if (name[0] == '.') {
2015 if (name == ".result") {
2016 return "result";
2018 // compiler-internal variable
2019 if (!variable_name_map.contains (name)) {
2020 variable_name_map.set (name, "_tmp%d_".printf (next_temp_var_id));
2021 next_temp_var_id++;
2023 return variable_name_map.get (name);
2024 } else if (reserved_identifiers.contains (name)) {
2025 return "_%s_".printf (name);
2026 } else {
2027 return name;
2031 public CCodeExpression get_result_cexpression (string cname = "result") {
2032 if (is_in_coroutine ()) {
2033 return new CCodeMemberAccess.pointer (new CCodeIdentifier ("data"), cname);
2034 } else {
2035 return new CCodeIdentifier (cname);
2039 bool has_simple_struct_initializer (LocalVariable local) {
2040 var st = local.variable_type.data_type as Struct;
2041 var initializer = local.initializer as ObjectCreationExpression;
2042 if (st != null && (!st.is_simple_type () || st.get_cname () == "va_list") && !local.variable_type.nullable &&
2043 initializer != null && initializer.get_object_initializer ().size == 0) {
2044 return true;
2045 } else {
2046 return false;
2050 public override void visit_local_variable (LocalVariable local) {
2051 check_type (local.variable_type);
2053 if (local.initializer != null) {
2054 local.initializer.emit (this);
2056 visit_end_full_expression (local.initializer);
2059 generate_type_declaration (local.variable_type, cfile);
2061 CCodeExpression rhs = null;
2062 if (local.initializer != null && get_cvalue (local.initializer) != null) {
2063 rhs = get_cvalue (local.initializer);
2066 if (!local.captured) {
2067 if (current_method != null && current_method.coroutine) {
2068 closure_struct.add_field (local.variable_type.get_cname (), get_variable_cname (local.name) + local.variable_type.get_cdeclarator_suffix ());
2069 } else {
2070 var cvar = new CCodeVariableDeclarator (get_variable_cname (local.name), null, local.variable_type.get_cdeclarator_suffix ());
2072 // try to initialize uninitialized variables
2073 // initialization not necessary for variables stored in closure
2074 if (rhs == null || has_simple_struct_initializer (local)) {
2075 cvar.initializer = default_value_for_type (local.variable_type, true);
2076 cvar.init0 = true;
2079 ccode.add_declaration (local.variable_type.get_cname (), cvar);
2082 if (local.variable_type is ArrayType) {
2083 // create variables to store array dimensions
2084 var array_type = (ArrayType) local.variable_type;
2086 if (!array_type.fixed_length) {
2087 for (int dim = 1; dim <= array_type.rank; dim++) {
2088 var len_var = new LocalVariable (int_type.copy (), get_array_length_cname (get_variable_cname (local.name), dim));
2089 emit_temp_var (len_var, local.initializer == null);
2092 if (array_type.rank == 1) {
2093 var size_var = new LocalVariable (int_type.copy (), get_array_size_cname (get_variable_cname (local.name)));
2094 emit_temp_var (size_var, local.initializer == null);
2097 } else if (local.variable_type is DelegateType) {
2098 var deleg_type = (DelegateType) local.variable_type;
2099 var d = deleg_type.delegate_symbol;
2100 if (d.has_target) {
2101 // create variable to store delegate target
2102 var target_var = new LocalVariable (new PointerType (new VoidType ()), get_delegate_target_cname (get_variable_cname (local.name)));
2103 emit_temp_var (target_var, local.initializer == null);
2104 if (deleg_type.value_owned) {
2105 var target_destroy_notify_var = new LocalVariable (gdestroynotify_type, get_delegate_target_destroy_notify_cname (get_variable_cname (local.name)));
2106 emit_temp_var (target_destroy_notify_var, local.initializer == null);
2112 if (rhs != null) {
2113 if (!has_simple_struct_initializer (local)) {
2114 store_local (local, local.initializer.target_value, true);
2118 if (local.initializer != null && local.initializer.tree_can_fail) {
2119 add_simple_check (local.initializer);
2122 local.active = true;
2125 public override void visit_initializer_list (InitializerList list) {
2126 if (list.target_type.data_type is Struct) {
2127 /* initializer is used as struct initializer */
2128 var st = (Struct) list.target_type.data_type;
2130 if (list.parent_node is Constant || list.parent_node is Field || list.parent_node is InitializerList) {
2131 var clist = new CCodeInitializerList ();
2133 var field_it = st.get_fields ().iterator ();
2134 foreach (Expression expr in list.get_initializers ()) {
2135 Field field = null;
2136 while (field == null) {
2137 field_it.next ();
2138 field = field_it.get ();
2139 if (field.binding != MemberBinding.INSTANCE) {
2140 // we only initialize instance fields
2141 field = null;
2145 var cexpr = get_cvalue (expr);
2147 string ctype = field.get_ctype ();
2148 if (ctype != null) {
2149 cexpr = new CCodeCastExpression (cexpr, ctype);
2152 clist.append (cexpr);
2155 set_cvalue (list, clist);
2156 } else {
2157 // used as expression
2158 var temp_decl = get_temp_variable (list.target_type, false, list);
2159 emit_temp_var (temp_decl);
2161 var instance = get_variable_cexpression (get_variable_cname (temp_decl.name));
2163 var field_it = st.get_fields ().iterator ();
2164 foreach (Expression expr in list.get_initializers ()) {
2165 Field field = null;
2166 while (field == null) {
2167 field_it.next ();
2168 field = field_it.get ();
2169 if (field.binding != MemberBinding.INSTANCE) {
2170 // we only initialize instance fields
2171 field = null;
2175 var cexpr = get_cvalue (expr);
2177 string ctype = field.get_ctype ();
2178 if (ctype != null) {
2179 cexpr = new CCodeCastExpression (cexpr, ctype);
2182 var lhs = new CCodeMemberAccess (instance, field.get_cname ());;
2183 ccode.add_assignment (lhs, cexpr);
2186 set_cvalue (list, instance);
2188 } else {
2189 var clist = new CCodeInitializerList ();
2190 foreach (Expression expr in list.get_initializers ()) {
2191 clist.append (get_cvalue (expr));
2193 set_cvalue (list, clist);
2197 public override LocalVariable create_local (DataType type) {
2198 var result = get_temp_variable (type, type.value_owned);
2199 emit_temp_var (result);
2200 return result;
2203 public LocalVariable get_temp_variable (DataType type, bool value_owned = true, CodeNode? node_reference = null, bool init = true) {
2204 var var_type = type.copy ();
2205 var_type.value_owned = value_owned;
2206 var local = new LocalVariable (var_type, "_tmp%d_".printf (next_temp_var_id));
2207 local.no_init = !init;
2209 if (node_reference != null) {
2210 local.source_reference = node_reference.source_reference;
2213 next_temp_var_id++;
2215 return local;
2218 bool is_in_generic_type (DataType type) {
2219 if (current_symbol != null && type.type_parameter.parent_symbol is TypeSymbol
2220 && (current_method == null || current_method.binding == MemberBinding.INSTANCE)) {
2221 return true;
2222 } else {
2223 return false;
2227 public CCodeExpression get_type_id_expression (DataType type, bool is_chainup = false) {
2228 if (type is GenericType) {
2229 string var_name = "%s_type".printf (type.type_parameter.name.down ());
2230 if (is_in_generic_type (type) && !is_chainup && !in_creation_method) {
2231 return new CCodeMemberAccess.pointer (new CCodeMemberAccess.pointer (get_result_cexpression ("self"), "priv"), var_name);
2232 } else {
2233 return new CCodeIdentifier (var_name);
2235 } else {
2236 string type_id = type.get_type_id ();
2237 if (type_id == null) {
2238 type_id = "G_TYPE_INVALID";
2239 } else {
2240 generate_type_declaration (type, cfile);
2242 return new CCodeIdentifier (type_id);
2246 public virtual CCodeExpression? get_dup_func_expression (DataType type, SourceReference? source_reference, bool is_chainup = false) {
2247 if (type is ErrorType) {
2248 return new CCodeIdentifier ("g_error_copy");
2249 } else if (type.data_type != null) {
2250 string dup_function;
2251 var cl = type.data_type as Class;
2252 if (type.data_type.is_reference_counting ()) {
2253 dup_function = type.data_type.get_ref_function ();
2254 if (type.data_type is Interface && dup_function == null) {
2255 Report.error (source_reference, "missing class prerequisite for interface `%s', add GLib.Object to interface declaration if unsure".printf (type.data_type.get_full_name ()));
2256 return null;
2258 } else if (cl != null && cl.is_immutable) {
2259 // allow duplicates of immutable instances as for example strings
2260 dup_function = type.data_type.get_dup_function ();
2261 if (dup_function == null) {
2262 dup_function = "";
2264 } else if (cl != null && cl.is_gboxed) {
2265 // allow duplicates of gboxed instances
2266 dup_function = generate_dup_func_wrapper (type);
2267 if (dup_function == null) {
2268 dup_function = "";
2270 } else if (type is ValueType) {
2271 dup_function = type.data_type.get_dup_function ();
2272 if (dup_function == null && type.nullable) {
2273 dup_function = generate_struct_dup_wrapper ((ValueType) type);
2274 } else if (dup_function == null) {
2275 dup_function = "";
2277 } else {
2278 // duplicating non-reference counted objects may cause side-effects (and performance issues)
2279 Report.error (source_reference, "duplicating %s instance, use unowned variable or explicitly invoke copy method".printf (type.data_type.name));
2280 return null;
2283 return new CCodeIdentifier (dup_function);
2284 } else if (type.type_parameter != null) {
2285 string func_name = "%s_dup_func".printf (type.type_parameter.name.down ());
2286 if (is_in_generic_type (type) && !is_chainup && !in_creation_method) {
2287 return new CCodeMemberAccess.pointer (new CCodeMemberAccess.pointer (get_result_cexpression ("self"), "priv"), func_name);
2288 } else {
2289 return new CCodeIdentifier (func_name);
2291 } else if (type is PointerType) {
2292 var pointer_type = (PointerType) type;
2293 return get_dup_func_expression (pointer_type.base_type, source_reference);
2294 } else {
2295 return new CCodeConstant ("NULL");
2299 void make_comparable_cexpression (ref DataType left_type, ref CCodeExpression cleft, ref DataType right_type, ref CCodeExpression cright) {
2300 var left_type_as_struct = left_type.data_type as Struct;
2301 var right_type_as_struct = right_type.data_type as Struct;
2303 // GValue support
2304 var valuecast = try_cast_value_to_type (cleft, left_type, right_type);
2305 if (valuecast != null) {
2306 cleft = valuecast;
2307 left_type = right_type;
2308 make_comparable_cexpression (ref left_type, ref cleft, ref right_type, ref cright);
2309 return;
2312 valuecast = try_cast_value_to_type (cright, right_type, left_type);
2313 if (valuecast != null) {
2314 cright = valuecast;
2315 right_type = left_type;
2316 make_comparable_cexpression (ref left_type, ref cleft, ref right_type, ref cright);
2317 return;
2320 if (left_type.data_type is Class && !((Class) left_type.data_type).is_compact &&
2321 right_type.data_type is Class && !((Class) right_type.data_type).is_compact) {
2322 var left_cl = (Class) left_type.data_type;
2323 var right_cl = (Class) right_type.data_type;
2325 if (left_cl != right_cl) {
2326 if (left_cl.is_subtype_of (right_cl)) {
2327 cleft = generate_instance_cast (cleft, right_cl);
2328 } else if (right_cl.is_subtype_of (left_cl)) {
2329 cright = generate_instance_cast (cright, left_cl);
2332 } else if (left_type_as_struct != null && right_type_as_struct != null) {
2333 if (left_type is StructValueType) {
2334 // real structs (uses compare/equal function)
2335 if (!left_type.nullable) {
2336 cleft = new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, cleft);
2338 if (!right_type.nullable) {
2339 cright = new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, cright);
2341 } else {
2342 // integer or floating or boolean type
2343 if (left_type.nullable && right_type.nullable) {
2344 // FIXME also compare contents, not just address
2345 } else if (left_type.nullable) {
2346 // FIXME check left value is not null
2347 cleft = new CCodeUnaryExpression (CCodeUnaryOperator.POINTER_INDIRECTION, cleft);
2348 } else if (right_type.nullable) {
2349 // FIXME check right value is not null
2350 cright = new CCodeUnaryExpression (CCodeUnaryOperator.POINTER_INDIRECTION, cright);
2356 private string generate_struct_equal_function (Struct st) {
2357 string equal_func = "_%sequal".printf (st.get_lower_case_cprefix ());
2359 if (!add_wrapper (equal_func)) {
2360 // wrapper already defined
2361 return equal_func;
2364 var function = new CCodeFunction (equal_func, "gboolean");
2365 function.modifiers = CCodeModifiers.STATIC;
2367 function.add_parameter (new CCodeParameter ("s1", "const " + st.get_cname () + "*"));
2368 function.add_parameter (new CCodeParameter ("s2", "const " + st.get_cname () + "*"));
2370 push_function (function);
2372 // if (s1 == s2) return TRUE;
2374 var cexp = new CCodeBinaryExpression (CCodeBinaryOperator.EQUALITY, new CCodeIdentifier ("s1"), new CCodeIdentifier ("s2"));
2375 ccode.open_if (cexp);
2376 ccode.add_return (new CCodeConstant ("TRUE"));
2377 ccode.close ();
2379 // if (s1 == NULL || s2 == NULL) return FALSE;
2381 var cexp = new CCodeBinaryExpression (CCodeBinaryOperator.EQUALITY, new CCodeIdentifier ("s1"), new CCodeConstant ("NULL"));
2382 ccode.open_if (cexp);
2383 ccode.add_return (new CCodeConstant ("FALSE"));
2384 ccode.close ();
2386 cexp = new CCodeBinaryExpression (CCodeBinaryOperator.EQUALITY, new CCodeIdentifier ("s2"), new CCodeConstant ("NULL"));
2387 ccode.open_if (cexp);
2388 ccode.add_return (new CCodeConstant ("FALSE"));
2389 ccode.close ();
2392 foreach (Field f in st.get_fields ()) {
2393 if (f.binding != MemberBinding.INSTANCE) {
2394 // we only compare instance fields
2395 continue;
2398 CCodeExpression cexp; // if (cexp) return FALSE;
2399 var s1 = (CCodeExpression) new CCodeMemberAccess.pointer (new CCodeIdentifier ("s1"), f.name); // s1->f
2400 var s2 = (CCodeExpression) new CCodeMemberAccess.pointer (new CCodeIdentifier ("s2"), f.name); // s2->f
2402 var variable_type = f.variable_type.copy ();
2403 make_comparable_cexpression (ref variable_type, ref s1, ref variable_type, ref s2);
2405 if (!(f.variable_type is NullType) && f.variable_type.compatible (string_type)) {
2406 var ccall = new CCodeFunctionCall (new CCodeIdentifier ("g_strcmp0"));
2407 ccall.add_argument (s1);
2408 ccall.add_argument (s2);
2409 cexp = ccall;
2410 } else if (f.variable_type is StructValueType) {
2411 var equalfunc = generate_struct_equal_function (f.variable_type.data_type as Struct);
2412 var ccall = new CCodeFunctionCall (new CCodeIdentifier (equalfunc));
2413 ccall.add_argument (s1);
2414 ccall.add_argument (s2);
2415 cexp = new CCodeUnaryExpression (CCodeUnaryOperator.LOGICAL_NEGATION, ccall);
2416 } else {
2417 cexp = new CCodeBinaryExpression (CCodeBinaryOperator.INEQUALITY, s1, s2);
2420 ccode.open_if (cexp);
2421 ccode.add_return (new CCodeConstant ("FALSE"));
2422 ccode.close ();
2425 if (st.get_fields().size == 0) {
2426 // either opaque structure or simple type
2427 if (st.is_simple_type ()) {
2428 var cexp = new CCodeBinaryExpression (CCodeBinaryOperator.EQUALITY, new CCodeUnaryExpression (CCodeUnaryOperator.POINTER_INDIRECTION, new CCodeIdentifier ("s1")), new CCodeUnaryExpression (CCodeUnaryOperator.POINTER_INDIRECTION, new CCodeIdentifier ("s2")));
2429 ccode.add_return (cexp);
2430 } else {
2431 ccode.add_return (new CCodeConstant ("FALSE"));
2433 } else {
2434 ccode.add_return (new CCodeConstant ("TRUE"));
2437 pop_function ();
2439 cfile.add_function_declaration (function);
2440 cfile.add_function (function);
2442 return equal_func;
2445 private string generate_numeric_equal_function (Struct st) {
2446 string equal_func = "_%sequal".printf (st.get_lower_case_cprefix ());
2448 if (!add_wrapper (equal_func)) {
2449 // wrapper already defined
2450 return equal_func;
2453 var function = new CCodeFunction (equal_func, "gboolean");
2454 function.modifiers = CCodeModifiers.STATIC;
2456 function.add_parameter (new CCodeParameter ("s1", "const " + st.get_cname () + "*"));
2457 function.add_parameter (new CCodeParameter ("s2", "const " + st.get_cname () + "*"));
2459 push_function (function);
2461 // if (s1 == s2) return TRUE;
2463 var cexp = new CCodeBinaryExpression (CCodeBinaryOperator.EQUALITY, new CCodeIdentifier ("s1"), new CCodeIdentifier ("s2"));
2464 ccode.open_if (cexp);
2465 ccode.add_return (new CCodeConstant ("TRUE"));
2466 ccode.close ();
2468 // if (s1 == NULL || s2 == NULL) return FALSE;
2470 var cexp = new CCodeBinaryExpression (CCodeBinaryOperator.EQUALITY, new CCodeIdentifier ("s1"), new CCodeConstant ("NULL"));
2471 ccode.open_if (cexp);
2472 ccode.add_return (new CCodeConstant ("FALSE"));
2473 ccode.close ();
2475 cexp = new CCodeBinaryExpression (CCodeBinaryOperator.EQUALITY, new CCodeIdentifier ("s2"), new CCodeConstant ("NULL"));
2476 ccode.open_if (cexp);
2477 ccode.add_return (new CCodeConstant ("FALSE"));
2478 ccode.close ();
2480 // return (*s1 == *s2);
2482 var cexp = new CCodeBinaryExpression (CCodeBinaryOperator.EQUALITY, new CCodeUnaryExpression (CCodeUnaryOperator.POINTER_INDIRECTION, new CCodeIdentifier ("s1")), new CCodeUnaryExpression (CCodeUnaryOperator.POINTER_INDIRECTION, new CCodeIdentifier ("s2")));
2483 ccode.add_return (cexp);
2486 pop_function ();
2488 cfile.add_function_declaration (function);
2489 cfile.add_function (function);
2491 return equal_func;
2494 private string generate_struct_dup_wrapper (ValueType value_type) {
2495 string dup_func = "_%sdup".printf (value_type.type_symbol.get_lower_case_cprefix ());
2497 if (!add_wrapper (dup_func)) {
2498 // wrapper already defined
2499 return dup_func;
2502 var function = new CCodeFunction (dup_func, value_type.get_cname ());
2503 function.modifiers = CCodeModifiers.STATIC;
2505 function.add_parameter (new CCodeParameter ("self", value_type.get_cname ()));
2507 push_function (function);
2509 if (value_type.type_symbol == gvalue_type) {
2510 var dup_call = new CCodeFunctionCall (new CCodeIdentifier ("g_boxed_copy"));
2511 dup_call.add_argument (new CCodeIdentifier ("G_TYPE_VALUE"));
2512 dup_call.add_argument (new CCodeIdentifier ("self"));
2514 ccode.add_return (dup_call);
2515 } else {
2516 ccode.add_declaration (value_type.get_cname (), new CCodeVariableDeclarator ("dup"));
2518 var creation_call = new CCodeFunctionCall (new CCodeIdentifier ("g_new0"));
2519 creation_call.add_argument (new CCodeConstant (value_type.data_type.get_cname ()));
2520 creation_call.add_argument (new CCodeConstant ("1"));
2521 ccode.add_assignment (new CCodeIdentifier ("dup"), creation_call);
2523 var st = value_type.data_type as Struct;
2524 if (st != null && st.is_disposable ()) {
2525 if (!st.has_copy_function) {
2526 generate_struct_copy_function (st);
2529 var copy_call = new CCodeFunctionCall (new CCodeIdentifier (st.get_copy_function ()));
2530 copy_call.add_argument (new CCodeIdentifier ("self"));
2531 copy_call.add_argument (new CCodeIdentifier ("dup"));
2532 ccode.add_expression (copy_call);
2533 } else {
2534 cfile.add_include ("string.h");
2536 var sizeof_call = new CCodeFunctionCall (new CCodeIdentifier ("sizeof"));
2537 sizeof_call.add_argument (new CCodeConstant (value_type.data_type.get_cname ()));
2539 var copy_call = new CCodeFunctionCall (new CCodeIdentifier ("memcpy"));
2540 copy_call.add_argument (new CCodeIdentifier ("dup"));
2541 copy_call.add_argument (new CCodeIdentifier ("self"));
2542 copy_call.add_argument (sizeof_call);
2543 ccode.add_expression (copy_call);
2546 ccode.add_return (new CCodeIdentifier ("dup"));
2549 pop_function ();
2551 cfile.add_function_declaration (function);
2552 cfile.add_function (function);
2554 return dup_func;
2557 protected string generate_dup_func_wrapper (DataType type) {
2558 string destroy_func = "_vala_%s_copy".printf (type.data_type.get_cname ());
2560 if (!add_wrapper (destroy_func)) {
2561 // wrapper already defined
2562 return destroy_func;
2565 var function = new CCodeFunction (destroy_func, type.get_cname ());
2566 function.modifiers = CCodeModifiers.STATIC;
2567 function.add_parameter (new CCodeParameter ("self", type.get_cname ()));
2569 push_function (function);
2571 var cl = type.data_type as Class;
2572 assert (cl != null && cl.is_gboxed);
2574 var free_call = new CCodeFunctionCall (new CCodeIdentifier ("g_boxed_copy"));
2575 free_call.add_argument (new CCodeIdentifier (cl.get_type_id ()));
2576 free_call.add_argument (new CCodeIdentifier ("self"));
2578 ccode.add_return (free_call);
2580 pop_function ();
2582 cfile.add_function_declaration (function);
2583 cfile.add_function (function);
2585 return destroy_func;
2588 protected string generate_free_func_wrapper (DataType type) {
2589 string destroy_func = "_vala_%s_free".printf (type.data_type.get_cname ());
2591 if (!add_wrapper (destroy_func)) {
2592 // wrapper already defined
2593 return destroy_func;
2596 var function = new CCodeFunction (destroy_func, "void");
2597 function.modifiers = CCodeModifiers.STATIC;
2598 function.add_parameter (new CCodeParameter ("self", type.get_cname ()));
2600 push_function (function);
2602 var cl = type.data_type as Class;
2603 if (cl != null && cl.is_gboxed) {
2604 var free_call = new CCodeFunctionCall (new CCodeIdentifier ("g_boxed_free"));
2605 free_call.add_argument (new CCodeIdentifier (cl.get_type_id ()));
2606 free_call.add_argument (new CCodeIdentifier ("self"));
2608 ccode.add_expression (free_call);
2609 } else if (cl != null) {
2610 assert (cl.free_function_address_of);
2612 var free_call = new CCodeFunctionCall (new CCodeIdentifier (type.data_type.get_free_function ()));
2613 free_call.add_argument (new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, new CCodeIdentifier ("self")));
2615 ccode.add_expression (free_call);
2616 } else {
2617 var st = type.data_type as Struct;
2618 if (st != null && st.is_disposable ()) {
2619 if (!st.has_destroy_function) {
2620 generate_struct_destroy_function (st);
2623 var destroy_call = new CCodeFunctionCall (new CCodeIdentifier (st.get_destroy_function ()));
2624 destroy_call.add_argument (new CCodeIdentifier ("self"));
2625 ccode.add_expression (destroy_call);
2628 var free_call = new CCodeFunctionCall (new CCodeIdentifier ("g_free"));
2629 free_call.add_argument (new CCodeIdentifier ("self"));
2631 ccode.add_expression (free_call);
2634 pop_function ();
2636 cfile.add_function_declaration (function);
2637 cfile.add_function (function);
2639 return destroy_func;
2642 public CCodeExpression? get_destroy0_func_expression (DataType type, bool is_chainup = false) {
2643 var element_destroy_func_expression = get_destroy_func_expression (type, is_chainup);
2645 if (element_destroy_func_expression is CCodeIdentifier) {
2646 var freeid = (CCodeIdentifier) element_destroy_func_expression;
2647 string free0_func = "_%s0_".printf (freeid.name);
2649 if (add_wrapper (free0_func)) {
2650 var function = new CCodeFunction (free0_func, "void");
2651 function.modifiers = CCodeModifiers.STATIC;
2653 function.add_parameter (new CCodeParameter ("var", "gpointer"));
2655 push_function (function);
2657 ccode.add_expression (get_unref_expression (new CCodeIdentifier ("var"), type, null, true));
2659 pop_function ();
2661 cfile.add_function_declaration (function);
2662 cfile.add_function (function);
2665 element_destroy_func_expression = new CCodeIdentifier (free0_func);
2668 return element_destroy_func_expression;
2671 public CCodeExpression? get_destroy_func_expression (DataType type, bool is_chainup = false) {
2672 if (context.profile == Profile.GOBJECT && (type.data_type == glist_type || type.data_type == gslist_type || type.data_type == gnode_type)) {
2673 // create wrapper function to free list elements if necessary
2675 bool elements_require_free = false;
2676 CCodeExpression element_destroy_func_expression = null;
2678 foreach (DataType type_arg in type.get_type_arguments ()) {
2679 elements_require_free = requires_destroy (type_arg);
2680 if (elements_require_free) {
2681 element_destroy_func_expression = get_destroy0_func_expression (type_arg);
2685 if (elements_require_free && element_destroy_func_expression is CCodeIdentifier) {
2686 return new CCodeIdentifier (generate_collection_free_wrapper (type, (CCodeIdentifier) element_destroy_func_expression));
2687 } else {
2688 return new CCodeIdentifier (type.data_type.get_free_function ());
2690 } else if (type is ErrorType) {
2691 return new CCodeIdentifier ("g_error_free");
2692 } else if (type.data_type != null) {
2693 string unref_function;
2694 if (type is ReferenceType) {
2695 if (type.data_type.is_reference_counting ()) {
2696 unref_function = type.data_type.get_unref_function ();
2697 if (type.data_type is Interface && unref_function == null) {
2698 Report.error (type.source_reference, "missing class prerequisite for interface `%s', add GLib.Object to interface declaration if unsure".printf (type.data_type.get_full_name ()));
2699 return null;
2701 } else {
2702 var cl = type.data_type as Class;
2703 if (cl != null && (cl.free_function_address_of || cl.is_gboxed)) {
2704 unref_function = generate_free_func_wrapper (type);
2705 } else {
2706 unref_function = type.data_type.get_free_function ();
2709 } else {
2710 if (type.nullable) {
2711 unref_function = type.data_type.get_free_function ();
2712 if (unref_function == null) {
2713 if (type.data_type is Struct && ((Struct) type.data_type).is_disposable ()) {
2714 unref_function = generate_free_func_wrapper (type);
2715 } else {
2716 unref_function = "g_free";
2719 } else {
2720 var st = (Struct) type.data_type;
2721 if (!st.has_destroy_function) {
2722 generate_struct_destroy_function (st);
2724 unref_function = st.get_destroy_function ();
2727 if (unref_function == null) {
2728 return new CCodeConstant ("NULL");
2730 return new CCodeIdentifier (unref_function);
2731 } else if (type.type_parameter != null && current_type_symbol is Class) {
2732 string func_name = "%s_destroy_func".printf (type.type_parameter.name.down ());
2733 if (is_in_generic_type (type) && !is_chainup && !in_creation_method) {
2734 return new CCodeMemberAccess.pointer (new CCodeMemberAccess.pointer (get_result_cexpression ("self"), "priv"), func_name);
2735 } else {
2736 return new CCodeIdentifier (func_name);
2738 } else if (type is ArrayType) {
2739 if (context.profile == Profile.POSIX) {
2740 return new CCodeIdentifier ("free");
2741 } else {
2742 return new CCodeIdentifier ("g_free");
2744 } else if (type is PointerType) {
2745 if (context.profile == Profile.POSIX) {
2746 return new CCodeIdentifier ("free");
2747 } else {
2748 return new CCodeIdentifier ("g_free");
2750 } else {
2751 return new CCodeConstant ("NULL");
2755 private string generate_collection_free_wrapper (DataType collection_type, CCodeIdentifier element_destroy_func_expression) {
2756 string destroy_func = "_%s_%s".printf (collection_type.data_type.get_free_function (), element_destroy_func_expression.name);
2758 if (!add_wrapper (destroy_func)) {
2759 // wrapper already defined
2760 return destroy_func;
2763 var function = new CCodeFunction (destroy_func, "void");
2764 function.modifiers = CCodeModifiers.STATIC;
2766 function.add_parameter (new CCodeParameter ("self", collection_type.get_cname ()));
2768 push_function (function);
2770 CCodeFunctionCall element_free_call;
2771 if (collection_type.data_type == gnode_type) {
2772 /* A wrapper which converts GNodeTraverseFunc into GDestroyNotify */
2773 string destroy_node_func = "%s_node".printf (destroy_func);
2774 var wrapper = new CCodeFunction (destroy_node_func, "gboolean");
2775 wrapper.modifiers = CCodeModifiers.STATIC;
2776 wrapper.add_parameter (new CCodeParameter ("node", collection_type.get_cname ()));
2777 wrapper.add_parameter (new CCodeParameter ("unused", "gpointer"));
2778 var wrapper_block = new CCodeBlock ();
2779 var free_call = new CCodeFunctionCall (element_destroy_func_expression);
2780 free_call.add_argument (new CCodeMemberAccess.pointer(new CCodeIdentifier("node"), "data"));
2781 wrapper_block.add_statement (new CCodeExpressionStatement (free_call));
2782 wrapper_block.add_statement (new CCodeReturnStatement (new CCodeConstant ("FALSE")));
2783 cfile.add_function_declaration (function);
2784 wrapper.block = wrapper_block;
2785 cfile.add_function (wrapper);
2787 /* Now the code to call g_traverse with the above */
2788 element_free_call = new CCodeFunctionCall (new CCodeIdentifier ("g_node_traverse"));
2789 element_free_call.add_argument (new CCodeIdentifier("self"));
2790 element_free_call.add_argument (new CCodeConstant ("G_POST_ORDER"));
2791 element_free_call.add_argument (new CCodeConstant ("G_TRAVERSE_ALL"));
2792 element_free_call.add_argument (new CCodeConstant ("-1"));
2793 element_free_call.add_argument (new CCodeIdentifier (destroy_node_func));
2794 element_free_call.add_argument (new CCodeConstant ("NULL"));
2795 } else {
2796 if (collection_type.data_type == glist_type) {
2797 element_free_call = new CCodeFunctionCall (new CCodeIdentifier ("g_list_foreach"));
2798 } else {
2799 element_free_call = new CCodeFunctionCall (new CCodeIdentifier ("g_slist_foreach"));
2802 element_free_call.add_argument (new CCodeIdentifier ("self"));
2803 element_free_call.add_argument (new CCodeCastExpression (element_destroy_func_expression, "GFunc"));
2804 element_free_call.add_argument (new CCodeConstant ("NULL"));
2807 ccode.add_expression (element_free_call);
2809 var cfreecall = new CCodeFunctionCall (new CCodeIdentifier (collection_type.data_type.get_free_function ()));
2810 cfreecall.add_argument (new CCodeIdentifier ("self"));
2811 ccode.add_expression (cfreecall);
2813 pop_function ();
2815 cfile.add_function_declaration (function);
2816 cfile.add_function (function);
2818 return destroy_func;
2821 public virtual string? append_struct_array_free (Struct st) {
2822 return null;
2825 // logic in this method is temporarily duplicated in destroy_value
2826 // apply changes to both methods
2827 public virtual CCodeExpression destroy_variable (Variable variable, TargetValue target_lvalue) {
2828 var type = variable.variable_type;
2829 var cvar = get_cvalue_ (target_lvalue);
2831 if (type is DelegateType) {
2832 var delegate_target = get_delegate_target_cvalue (target_lvalue);
2833 var delegate_target_destroy_notify = get_delegate_target_destroy_notify_cvalue (target_lvalue);
2835 var ccall = new CCodeFunctionCall (delegate_target_destroy_notify);
2836 ccall.add_argument (delegate_target);
2838 var destroy_call = new CCodeCommaExpression ();
2839 destroy_call.append_expression (ccall);
2840 destroy_call.append_expression (new CCodeConstant ("NULL"));
2842 var cisnull = new CCodeBinaryExpression (CCodeBinaryOperator.EQUALITY, delegate_target_destroy_notify, new CCodeConstant ("NULL"));
2844 var ccomma = new CCodeCommaExpression ();
2845 ccomma.append_expression (new CCodeConditionalExpression (cisnull, new CCodeConstant ("NULL"), destroy_call));
2846 ccomma.append_expression (new CCodeAssignment (cvar, new CCodeConstant ("NULL")));
2847 ccomma.append_expression (new CCodeAssignment (delegate_target, new CCodeConstant ("NULL")));
2848 ccomma.append_expression (new CCodeAssignment (delegate_target_destroy_notify, new CCodeConstant ("NULL")));
2850 return ccomma;
2853 var ccall = new CCodeFunctionCall (get_destroy_func_expression (type));
2855 if (type is ValueType && !type.nullable) {
2856 // normal value type, no null check
2857 var st = type.data_type as Struct;
2858 if (st != null && st.is_simple_type ()) {
2859 // used for va_list
2860 ccall.add_argument (cvar);
2861 } else {
2862 ccall.add_argument (new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, cvar));
2865 if (gvalue_type != null && type.data_type == gvalue_type) {
2866 // g_value_unset must not be called for already unset values
2867 var cisvalid = new CCodeFunctionCall (new CCodeIdentifier ("G_IS_VALUE"));
2868 cisvalid.add_argument (new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, cvar));
2870 var ccomma = new CCodeCommaExpression ();
2871 ccomma.append_expression (ccall);
2872 ccomma.append_expression (new CCodeConstant ("NULL"));
2874 return new CCodeConditionalExpression (cisvalid, ccomma, new CCodeConstant ("NULL"));
2875 } else {
2876 return ccall;
2880 if (ccall.call is CCodeIdentifier && !(type is ArrayType)) {
2881 // generate and use NULL-aware free macro to simplify code
2883 var freeid = (CCodeIdentifier) ccall.call;
2884 string free0_func = "_%s0".printf (freeid.name);
2886 if (add_wrapper (free0_func)) {
2887 var macro = destroy_value (new GLibValue (type, new CCodeIdentifier ("var")), true);
2888 cfile.add_type_declaration (new CCodeMacroReplacement.with_expression ("%s(var)".printf (free0_func), macro));
2891 ccall = new CCodeFunctionCall (new CCodeIdentifier (free0_func));
2892 ccall.add_argument (cvar);
2893 return ccall;
2896 /* (foo == NULL ? NULL : foo = (unref (foo), NULL)) */
2898 /* can be simplified to
2899 * foo = (unref (foo), NULL)
2900 * if foo is of static type non-null
2903 var cisnull = new CCodeBinaryExpression (CCodeBinaryOperator.EQUALITY, cvar, new CCodeConstant ("NULL"));
2904 if (type.type_parameter != null) {
2905 if (!(current_type_symbol is Class) || current_class.is_compact) {
2906 return new CCodeConstant ("NULL");
2909 // unref functions are optional for type parameters
2910 var cunrefisnull = new CCodeBinaryExpression (CCodeBinaryOperator.EQUALITY, get_destroy_func_expression (type), new CCodeConstant ("NULL"));
2911 cisnull = new CCodeBinaryExpression (CCodeBinaryOperator.OR, cisnull, cunrefisnull);
2914 ccall.add_argument (cvar);
2916 /* set freed references to NULL to prevent further use */
2917 var ccomma = new CCodeCommaExpression ();
2919 if (context.profile == Profile.GOBJECT) {
2920 if (type.data_type != null && !type.data_type.is_reference_counting () &&
2921 (type.data_type == gstringbuilder_type
2922 || type.data_type == garray_type
2923 || type.data_type == gbytearray_type
2924 || type.data_type == gptrarray_type)) {
2925 ccall.add_argument (new CCodeConstant ("TRUE"));
2926 } else if (type.data_type == gthreadpool_type) {
2927 ccall.add_argument (new CCodeConstant ("FALSE"));
2928 ccall.add_argument (new CCodeConstant ("TRUE"));
2929 } else if (type is ArrayType) {
2930 var array_type = (ArrayType) type;
2931 if (requires_destroy (array_type.element_type) && !variable.no_array_length) {
2932 CCodeExpression csizeexpr = null;
2933 if (variable.array_null_terminated) {
2934 var len_call = new CCodeFunctionCall (new CCodeIdentifier ("_vala_array_length"));
2935 len_call.add_argument (cvar);
2936 csizeexpr = len_call;
2937 } else if (variable.has_array_length_cexpr) {
2938 csizeexpr = new CCodeConstant (variable.get_array_length_cexpr ());
2939 } else {
2940 bool first = true;
2941 for (int dim = 1; dim <= array_type.rank; dim++) {
2942 if (first) {
2943 csizeexpr = get_array_length_cvalue (target_lvalue, dim);
2944 first = false;
2945 } else {
2946 csizeexpr = new CCodeBinaryExpression (CCodeBinaryOperator.MUL, csizeexpr, get_array_length_cvalue (target_lvalue, dim));
2951 var st = array_type.element_type.data_type as Struct;
2952 if (st != null && !array_type.element_type.nullable) {
2953 ccall.call = new CCodeIdentifier (append_struct_array_free (st));
2954 ccall.add_argument (csizeexpr);
2955 } else {
2956 requires_array_free = true;
2957 ccall.call = new CCodeIdentifier ("_vala_array_free");
2958 ccall.add_argument (csizeexpr);
2959 ccall.add_argument (new CCodeCastExpression (get_destroy_func_expression (array_type.element_type), "GDestroyNotify"));
2965 ccomma.append_expression (ccall);
2966 ccomma.append_expression (new CCodeConstant ("NULL"));
2968 var cassign = new CCodeAssignment (cvar, ccomma);
2970 // g_free (NULL) is allowed
2971 bool uses_gfree = (type.data_type != null && !type.data_type.is_reference_counting () && type.data_type.get_free_function () == "g_free");
2972 uses_gfree = uses_gfree || type is ArrayType;
2973 if (uses_gfree) {
2974 return cassign;
2977 return new CCodeConditionalExpression (cisnull, new CCodeConstant ("NULL"), cassign);
2980 public CCodeExpression destroy_local (LocalVariable local) {
2981 return destroy_variable (local, get_local_cvalue (local));
2984 public CCodeExpression destroy_parameter (Parameter param) {
2985 return destroy_variable (param, get_parameter_cvalue (param));
2988 public CCodeExpression destroy_field (Field field, Expression? instance) {
2989 return destroy_variable (field, get_field_cvalue (field, instance));
2992 public CCodeExpression get_unref_expression (CCodeExpression cvar, DataType type, Expression? expr, bool is_macro_definition = false) {
2993 if (expr != null) {
2994 if (expr.symbol_reference is LocalVariable) {
2995 return destroy_local ((LocalVariable) expr.symbol_reference);
2996 } else if (expr.symbol_reference is Parameter) {
2997 return destroy_parameter ((Parameter) expr.symbol_reference);
3000 var value = new GLibValue (type, cvar);
3001 if (expr != null && expr.target_value != null) {
3002 value.array_length_cvalues = ((GLibValue) expr.target_value).array_length_cvalues;
3003 value.delegate_target_cvalue = get_delegate_target_cvalue (expr.target_value);
3004 value.delegate_target_destroy_notify_cvalue = get_delegate_target_destroy_notify_cvalue (expr.target_value);
3006 return destroy_value (value, is_macro_definition);
3009 // logic in this method is temporarily duplicated in destroy_variable
3010 // apply changes to both methods
3011 public virtual CCodeExpression destroy_value (TargetValue value, bool is_macro_definition = false) {
3012 var type = value.value_type;
3013 var cvar = get_cvalue_ (value);
3015 if (type is DelegateType) {
3016 var delegate_target = get_delegate_target_cvalue (value);
3017 var delegate_target_destroy_notify = get_delegate_target_destroy_notify_cvalue (value);
3019 var ccall = new CCodeFunctionCall (delegate_target_destroy_notify);
3020 ccall.add_argument (delegate_target);
3022 var destroy_call = new CCodeCommaExpression ();
3023 destroy_call.append_expression (ccall);
3024 destroy_call.append_expression (new CCodeConstant ("NULL"));
3026 var cisnull = new CCodeBinaryExpression (CCodeBinaryOperator.EQUALITY, delegate_target_destroy_notify, new CCodeConstant ("NULL"));
3028 var ccomma = new CCodeCommaExpression ();
3029 ccomma.append_expression (new CCodeConditionalExpression (cisnull, new CCodeConstant ("NULL"), destroy_call));
3030 ccomma.append_expression (new CCodeAssignment (cvar, new CCodeConstant ("NULL")));
3031 ccomma.append_expression (new CCodeAssignment (delegate_target, new CCodeConstant ("NULL")));
3032 ccomma.append_expression (new CCodeAssignment (delegate_target_destroy_notify, new CCodeConstant ("NULL")));
3034 return ccomma;
3037 var ccall = new CCodeFunctionCall (get_destroy_func_expression (type));
3039 if (type is ValueType && !type.nullable) {
3040 // normal value type, no null check
3041 var st = type.data_type as Struct;
3042 if (st != null && st.is_simple_type ()) {
3043 // used for va_list
3044 ccall.add_argument (cvar);
3045 } else {
3046 ccall.add_argument (new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, cvar));
3049 if (gvalue_type != null && type.data_type == gvalue_type) {
3050 // g_value_unset must not be called for already unset values
3051 var cisvalid = new CCodeFunctionCall (new CCodeIdentifier ("G_IS_VALUE"));
3052 cisvalid.add_argument (new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, cvar));
3054 var ccomma = new CCodeCommaExpression ();
3055 ccomma.append_expression (ccall);
3056 ccomma.append_expression (new CCodeConstant ("NULL"));
3058 return new CCodeConditionalExpression (cisvalid, ccomma, new CCodeConstant ("NULL"));
3059 } else {
3060 return ccall;
3064 if (ccall.call is CCodeIdentifier && !(type is ArrayType) && !is_macro_definition) {
3065 // generate and use NULL-aware free macro to simplify code
3067 var freeid = (CCodeIdentifier) ccall.call;
3068 string free0_func = "_%s0".printf (freeid.name);
3070 if (add_wrapper (free0_func)) {
3071 var macro = destroy_value (new GLibValue (type, new CCodeIdentifier ("var")), true);
3072 cfile.add_type_declaration (new CCodeMacroReplacement.with_expression ("%s(var)".printf (free0_func), macro));
3075 ccall = new CCodeFunctionCall (new CCodeIdentifier (free0_func));
3076 ccall.add_argument (cvar);
3077 return ccall;
3080 /* (foo == NULL ? NULL : foo = (unref (foo), NULL)) */
3082 /* can be simplified to
3083 * foo = (unref (foo), NULL)
3084 * if foo is of static type non-null
3087 var cisnull = new CCodeBinaryExpression (CCodeBinaryOperator.EQUALITY, cvar, new CCodeConstant ("NULL"));
3088 if (type.type_parameter != null) {
3089 if (!(current_type_symbol is Class) || current_class.is_compact) {
3090 return new CCodeConstant ("NULL");
3093 // unref functions are optional for type parameters
3094 var cunrefisnull = new CCodeBinaryExpression (CCodeBinaryOperator.EQUALITY, get_destroy_func_expression (type), new CCodeConstant ("NULL"));
3095 cisnull = new CCodeBinaryExpression (CCodeBinaryOperator.OR, cisnull, cunrefisnull);
3098 ccall.add_argument (cvar);
3100 /* set freed references to NULL to prevent further use */
3101 var ccomma = new CCodeCommaExpression ();
3103 if (context.profile == Profile.GOBJECT) {
3104 if (type.data_type != null && !type.data_type.is_reference_counting () &&
3105 (type.data_type == gstringbuilder_type
3106 || type.data_type == garray_type
3107 || type.data_type == gbytearray_type
3108 || type.data_type == gptrarray_type)) {
3109 ccall.add_argument (new CCodeConstant ("TRUE"));
3110 } else if (type.data_type == gthreadpool_type) {
3111 ccall.add_argument (new CCodeConstant ("FALSE"));
3112 ccall.add_argument (new CCodeConstant ("TRUE"));
3113 } else if (type is ArrayType) {
3114 var array_type = (ArrayType) type;
3115 if (requires_destroy (array_type.element_type)) {
3116 CCodeExpression csizeexpr = null;
3117 bool first = true;
3118 for (int dim = 1; dim <= array_type.rank; dim++) {
3119 if (first) {
3120 csizeexpr = get_array_length_cvalue (value, dim);
3121 first = false;
3122 } else {
3123 csizeexpr = new CCodeBinaryExpression (CCodeBinaryOperator.MUL, csizeexpr, get_array_length_cvalue (value, dim));
3127 var st = array_type.element_type.data_type as Struct;
3128 if (st != null && !array_type.element_type.nullable) {
3129 ccall.call = new CCodeIdentifier (append_struct_array_free (st));
3130 ccall.add_argument (csizeexpr);
3131 } else {
3132 requires_array_free = true;
3133 ccall.call = new CCodeIdentifier ("_vala_array_free");
3134 ccall.add_argument (csizeexpr);
3135 ccall.add_argument (new CCodeCastExpression (get_destroy_func_expression (array_type.element_type), "GDestroyNotify"));
3141 ccomma.append_expression (ccall);
3142 ccomma.append_expression (new CCodeConstant ("NULL"));
3144 var cassign = new CCodeAssignment (cvar, ccomma);
3146 // g_free (NULL) is allowed
3147 bool uses_gfree = (type.data_type != null && !type.data_type.is_reference_counting () && type.data_type.get_free_function () == "g_free");
3148 uses_gfree = uses_gfree || type is ArrayType;
3149 if (uses_gfree) {
3150 return cassign;
3153 return new CCodeConditionalExpression (cisnull, new CCodeConstant ("NULL"), cassign);
3156 public override void visit_end_full_expression (Expression expr) {
3157 /* expr is a full expression, i.e. an initializer, the
3158 * expression in an expression statement, the controlling
3159 * expression in if, while, for, or foreach statements
3161 * we unref temporary variables at the end of a full
3162 * expression
3164 if (temp_ref_vars.size == 0) {
3165 /* nothing to do without temporary variables */
3166 return;
3169 LocalVariable full_expr_var = null;
3171 var local_decl = expr.parent_node as LocalVariable;
3172 if (!(local_decl != null && has_simple_struct_initializer (local_decl))) {
3173 var expr_type = expr.value_type;
3174 if (expr.target_type != null) {
3175 expr_type = expr.target_type;
3178 full_expr_var = get_temp_variable (expr_type, true, expr, false);
3179 emit_temp_var (full_expr_var);
3181 ccode.add_assignment (get_variable_cexpression (full_expr_var.name), get_cvalue (expr));
3184 foreach (LocalVariable local in temp_ref_vars) {
3185 ccode.add_expression (destroy_local (local));
3188 if (full_expr_var != null) {
3189 set_cvalue (expr, get_variable_cexpression (full_expr_var.name));
3192 temp_ref_vars.clear ();
3195 public void emit_temp_var (LocalVariable local, bool always_init = false) {
3196 var vardecl = new CCodeVariableDeclarator (local.name, null, local.variable_type.get_cdeclarator_suffix ());
3198 var st = local.variable_type.data_type as Struct;
3199 var array_type = local.variable_type as ArrayType;
3201 if (local.name.has_prefix ("*")) {
3202 // do not dereference unintialized variable
3203 // initialization is not needed for these special
3204 // pointer temp variables
3205 // used to avoid side-effects in assignments
3206 } else if (local.no_init) {
3207 // no initialization necessary for this temp var
3208 } else if (!local.variable_type.nullable &&
3209 (st != null && !st.is_simple_type ()) ||
3210 (array_type != null && array_type.fixed_length)) {
3211 // 0-initialize struct with struct initializer { 0 }
3212 // necessary as they will be passed by reference
3213 var clist = new CCodeInitializerList ();
3214 clist.append (new CCodeConstant ("0"));
3216 vardecl.initializer = clist;
3217 vardecl.init0 = true;
3218 } else if (local.variable_type.is_reference_type_or_type_parameter () ||
3219 local.variable_type.nullable ||
3220 local.variable_type is DelegateType) {
3221 vardecl.initializer = new CCodeConstant ("NULL");
3222 vardecl.init0 = true;
3223 } else if (always_init) {
3224 vardecl.initializer = default_value_for_type (local.variable_type, true);
3225 vardecl.init0 = true;
3228 if (is_in_coroutine ()) {
3229 closure_struct.add_field (local.variable_type.get_cname (), local.name);
3231 // even though closure struct is zerod, we need to initialize temporary variables
3232 // as they might be used multiple times when declared in a loop
3234 if (vardecl.initializer is CCodeInitializerList) {
3235 // C does not support initializer lists in assignments, use memset instead
3236 cfile.add_include ("string.h");
3237 var memset_call = new CCodeFunctionCall (new CCodeIdentifier ("memset"));
3238 memset_call.add_argument (new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, get_variable_cexpression (local.name)));
3239 memset_call.add_argument (new CCodeConstant ("0"));
3240 memset_call.add_argument (new CCodeIdentifier ("sizeof (%s)".printf (local.variable_type.get_cname ())));
3241 ccode.add_expression (memset_call);
3242 } else if (vardecl.initializer != null) {
3243 ccode.add_assignment (get_variable_cexpression (local.name), vardecl.initializer);
3245 } else {
3246 ccode.add_declaration (local.variable_type.get_cname (), vardecl);
3250 public override void visit_expression_statement (ExpressionStatement stmt) {
3251 if (stmt.expression.error) {
3252 stmt.error = true;
3253 return;
3256 /* free temporary objects and handle errors */
3258 foreach (LocalVariable local in temp_ref_vars) {
3259 ccode.add_expression (destroy_local (local));
3262 if (stmt.tree_can_fail && stmt.expression.tree_can_fail) {
3263 // simple case, no node breakdown necessary
3264 add_simple_check (stmt.expression);
3267 temp_ref_vars.clear ();
3270 public virtual void append_local_free (Symbol sym, bool stop_at_loop = false, CodeNode? stop_at = null) {
3271 var b = (Block) sym;
3273 var local_vars = b.get_local_variables ();
3274 // free in reverse order
3275 for (int i = local_vars.size - 1; i >= 0; i--) {
3276 var local = local_vars[i];
3277 if (!local.unreachable && local.active && !local.floating && !local.captured && requires_destroy (local.variable_type)) {
3278 ccode.add_expression (destroy_local (local));
3282 if (b.captured) {
3283 int block_id = get_block_id (b);
3285 var data_unref = new CCodeFunctionCall (new CCodeIdentifier ("block%d_data_unref".printf (block_id)));
3286 data_unref.add_argument (get_variable_cexpression ("_data%d_".printf (block_id)));
3287 ccode.add_expression (data_unref);
3288 ccode.add_assignment (get_variable_cexpression ("_data%d_".printf (block_id)), new CCodeConstant ("NULL"));
3291 if (stop_at_loop) {
3292 if (b.parent_node is Loop ||
3293 b.parent_node is ForeachStatement ||
3294 b.parent_node is SwitchStatement) {
3295 return;
3299 if (b.parent_node == stop_at) {
3300 return;
3303 if (sym.parent_symbol is Block) {
3304 append_local_free (sym.parent_symbol, stop_at_loop, stop_at);
3305 } else if (sym.parent_symbol is Method) {
3306 append_param_free ((Method) sym.parent_symbol);
3310 private void append_param_free (Method m) {
3311 foreach (Parameter param in m.get_parameters ()) {
3312 if (!param.ellipsis && requires_destroy (param.variable_type) && param.direction == ParameterDirection.IN) {
3313 ccode.add_expression (destroy_parameter (param));
3318 public bool variable_accessible_in_finally (LocalVariable local) {
3319 if (current_try == null) {
3320 return false;
3323 var sym = current_symbol;
3325 while (!(sym is Method || sym is PropertyAccessor) && sym.scope.lookup (local.name) == null) {
3326 if ((sym.parent_node is TryStatement && ((TryStatement) sym.parent_node).finally_body != null) ||
3327 (sym.parent_node is CatchClause && ((TryStatement) sym.parent_node.parent_node).finally_body != null)) {
3329 return true;
3332 sym = sym.parent_symbol;
3335 return false;
3338 void return_out_parameter (Parameter param) {
3339 var delegate_type = param.variable_type as DelegateType;
3341 ccode.open_if (get_variable_cexpression (param.name));
3342 ccode.add_assignment (new CCodeUnaryExpression (CCodeUnaryOperator.POINTER_INDIRECTION, get_variable_cexpression (param.name)), get_variable_cexpression ("_" + param.name));
3344 if (delegate_type != null && delegate_type.delegate_symbol.has_target) {
3345 ccode.add_assignment (new CCodeUnaryExpression (CCodeUnaryOperator.POINTER_INDIRECTION, get_variable_cexpression (get_delegate_target_cname (param.name))), new CCodeIdentifier (get_delegate_target_cname (get_variable_cname ("_" + param.name))));
3346 if (delegate_type.value_owned) {
3347 ccode.add_assignment (new CCodeUnaryExpression (CCodeUnaryOperator.POINTER_INDIRECTION, get_variable_cexpression (get_delegate_target_destroy_notify_cname (param.name))), new CCodeIdentifier (get_delegate_target_destroy_notify_cname (get_variable_cname ("_" + param.name))));
3351 if (param.variable_type.is_disposable ()){
3352 ccode.add_else ();
3353 ccode.add_expression (destroy_parameter (param));
3355 ccode.close ();
3357 var array_type = param.variable_type as ArrayType;
3358 if (array_type != null && !array_type.fixed_length && !param.no_array_length) {
3359 for (int dim = 1; dim <= array_type.rank; dim++) {
3360 ccode.open_if (get_variable_cexpression (get_parameter_array_length_cname (param, dim)));
3361 ccode.add_assignment (new CCodeUnaryExpression (CCodeUnaryOperator.POINTER_INDIRECTION, get_variable_cexpression (get_parameter_array_length_cname (param, dim))), new CCodeIdentifier (get_array_length_cname (get_variable_cname ("_" + param.name), dim)));
3362 ccode.close ();
3367 public override void visit_return_statement (ReturnStatement stmt) {
3368 Symbol return_expression_symbol = null;
3370 if (stmt.return_expression != null) {
3371 // avoid unnecessary ref/unref pair
3372 var local = stmt.return_expression.symbol_reference as LocalVariable;
3373 if (current_return_type.value_owned
3374 && local != null && local.variable_type.value_owned
3375 && !local.captured
3376 && !variable_accessible_in_finally (local)) {
3377 /* return expression is local variable taking ownership and
3378 * current method is transferring ownership */
3380 return_expression_symbol = local;
3384 // return array length if appropriate
3385 if (((current_method != null && !current_method.no_array_length) || current_property_accessor != null) && current_return_type is ArrayType) {
3386 var return_expr_decl = get_temp_variable (stmt.return_expression.value_type, true, stmt, false);
3388 ccode.add_assignment (get_variable_cexpression (return_expr_decl.name), get_cvalue (stmt.return_expression));
3390 var array_type = (ArrayType) current_return_type;
3392 for (int dim = 1; dim <= array_type.rank; dim++) {
3393 var len_l = get_result_cexpression (get_array_length_cname ("result", dim));
3394 if (!is_in_coroutine ()) {
3395 len_l = new CCodeUnaryExpression (CCodeUnaryOperator.POINTER_INDIRECTION, len_l);
3397 var len_r = get_array_length_cexpression (stmt.return_expression, dim);
3398 ccode.add_assignment (len_l, len_r);
3401 set_cvalue (stmt.return_expression, get_variable_cexpression (return_expr_decl.name));
3403 emit_temp_var (return_expr_decl);
3404 } else if ((current_method != null || current_property_accessor != null) && current_return_type is DelegateType) {
3405 var delegate_type = (DelegateType) current_return_type;
3406 if (delegate_type.delegate_symbol.has_target) {
3407 var return_expr_decl = get_temp_variable (stmt.return_expression.value_type, true, stmt, false);
3409 ccode.add_assignment (get_variable_cexpression (return_expr_decl.name), get_cvalue (stmt.return_expression));
3411 var target_l = get_result_cexpression (get_delegate_target_cname ("result"));
3412 if (!is_in_coroutine ()) {
3413 target_l = new CCodeUnaryExpression (CCodeUnaryOperator.POINTER_INDIRECTION, target_l);
3415 CCodeExpression target_r_destroy_notify;
3416 var target_r = get_delegate_target_cexpression (stmt.return_expression, out target_r_destroy_notify);
3417 ccode.add_assignment (target_l, target_r);
3418 if (delegate_type.value_owned) {
3419 var target_l_destroy_notify = get_result_cexpression (get_delegate_target_destroy_notify_cname ("result"));
3420 if (!is_in_coroutine ()) {
3421 target_l_destroy_notify = new CCodeUnaryExpression (CCodeUnaryOperator.POINTER_INDIRECTION, target_l_destroy_notify);
3423 ccode.add_assignment (target_l_destroy_notify, target_r_destroy_notify);
3426 set_cvalue (stmt.return_expression, get_variable_cexpression (return_expr_decl.name));
3428 emit_temp_var (return_expr_decl);
3432 if (stmt.return_expression != null) {
3433 // assign method result to `result'
3434 CCodeExpression result_lhs = get_result_cexpression ();
3435 if (current_return_type.is_real_non_null_struct_type () && !is_in_coroutine ()) {
3436 result_lhs = new CCodeUnaryExpression (CCodeUnaryOperator.POINTER_INDIRECTION, result_lhs);
3438 ccode.add_assignment (result_lhs, get_cvalue (stmt.return_expression));
3441 // free local variables
3442 append_local_free (current_symbol);
3444 if (current_method != null) {
3445 // check postconditions
3446 foreach (Expression postcondition in current_method.get_postconditions ()) {
3447 create_postcondition_statement (postcondition);
3451 if (current_method != null && !current_method.coroutine) {
3452 // assign values to output parameters if they are not NULL
3453 // otherwise, free the value if necessary
3454 foreach (var param in current_method.get_parameters ()) {
3455 if (param.direction != ParameterDirection.OUT) {
3456 continue;
3459 return_out_parameter (param);
3463 if (is_in_constructor ()) {
3464 ccode.add_return (new CCodeIdentifier ("obj"));
3465 } else if (is_in_destructor ()) {
3466 // do not call return as member cleanup and chain up to base finalizer
3467 // stil need to be executed
3468 ccode.add_goto ("_return");
3469 } else if (current_method is CreationMethod) {
3470 ccode.add_return (new CCodeIdentifier ("self"));
3471 } else if (is_in_coroutine ()) {
3472 } else if (current_return_type is VoidType || current_return_type.is_real_non_null_struct_type ()) {
3473 // structs are returned via out parameter
3474 ccode.add_return ();
3475 } else {
3476 ccode.add_return (new CCodeIdentifier ("result"));
3479 if (return_expression_symbol != null) {
3480 return_expression_symbol.active = true;
3483 // required for destructors
3484 current_method_return = true;
3487 public string get_symbol_lock_name (string symname) {
3488 return "__lock_%s".printf (symname);
3491 private CCodeExpression get_lock_expression (Statement stmt, Expression resource) {
3492 CCodeExpression l = null;
3493 var inner_node = ((MemberAccess)resource).inner;
3494 var member = resource.symbol_reference;
3495 var parent = (TypeSymbol)resource.symbol_reference.parent_symbol;
3497 if (member.is_instance_member ()) {
3498 if (inner_node == null) {
3499 l = new CCodeIdentifier ("self");
3500 } else if (resource.symbol_reference.parent_symbol != current_type_symbol) {
3501 l = generate_instance_cast (get_cvalue (inner_node), parent);
3502 } else {
3503 l = get_cvalue (inner_node);
3506 l = new CCodeMemberAccess.pointer (new CCodeMemberAccess.pointer (l, "priv"), get_symbol_lock_name (resource.symbol_reference.name));
3507 } else if (member.is_class_member ()) {
3508 CCodeExpression klass;
3510 if (current_method != null && current_method.binding == MemberBinding.INSTANCE ||
3511 current_property_accessor != null && current_property_accessor.prop.binding == MemberBinding.INSTANCE ||
3512 (in_constructor && !in_static_or_class_context)) {
3513 var k = new CCodeFunctionCall (new CCodeIdentifier ("G_OBJECT_GET_CLASS"));
3514 k.add_argument (new CCodeIdentifier ("self"));
3515 klass = k;
3516 } else {
3517 klass = new CCodeIdentifier ("klass");
3520 var get_class_private_call = new CCodeFunctionCall (new CCodeIdentifier ("%s_GET_CLASS_PRIVATE".printf(parent.get_upper_case_cname ())));
3521 get_class_private_call.add_argument (klass);
3522 l = new CCodeMemberAccess.pointer (get_class_private_call, get_symbol_lock_name (resource.symbol_reference.name));
3523 } else {
3524 string lock_name = "%s_%s".printf(parent.get_lower_case_cname (), resource.symbol_reference.name);
3525 l = new CCodeIdentifier (get_symbol_lock_name (lock_name));
3527 return l;
3530 public override void visit_lock_statement (LockStatement stmt) {
3531 var l = get_lock_expression (stmt, stmt.resource);
3533 var fc = new CCodeFunctionCall (new CCodeIdentifier (((Method) mutex_type.scope.lookup ("lock")).get_cname ()));
3534 fc.add_argument (new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, l));
3536 ccode.add_expression (fc);
3539 public override void visit_unlock_statement (UnlockStatement stmt) {
3540 var l = get_lock_expression (stmt, stmt.resource);
3542 var fc = new CCodeFunctionCall (new CCodeIdentifier (((Method) mutex_type.scope.lookup ("unlock")).get_cname ()));
3543 fc.add_argument (new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, l));
3545 ccode.add_expression (fc);
3548 public override void visit_delete_statement (DeleteStatement stmt) {
3549 var pointer_type = (PointerType) stmt.expression.value_type;
3550 DataType type = pointer_type;
3551 if (pointer_type.base_type.data_type != null && pointer_type.base_type.data_type.is_reference_type ()) {
3552 type = pointer_type.base_type;
3555 var ccall = new CCodeFunctionCall (get_destroy_func_expression (type));
3556 ccall.add_argument (get_cvalue (stmt.expression));
3557 ccode.add_expression (ccall);
3560 public override void visit_expression (Expression expr) {
3561 if (get_cvalue (expr) != null && !expr.lvalue) {
3562 if (expr.formal_value_type is GenericType && !(expr.value_type is GenericType)) {
3563 var st = expr.formal_value_type.type_parameter.parent_symbol.parent_symbol as Struct;
3564 if (expr.formal_value_type.type_parameter.parent_symbol != garray_type &&
3565 (st == null || st.get_cname () != "va_list")) {
3566 // GArray and va_list don't use pointer-based generics
3567 set_cvalue (expr, convert_from_generic_pointer (get_cvalue (expr), expr.value_type));
3571 // memory management, implicit casts, and boxing/unboxing
3572 set_cvalue (expr, transform_expression (get_cvalue (expr), expr.value_type, expr.target_type, expr));
3574 if (expr.formal_target_type is GenericType && !(expr.target_type is GenericType)) {
3575 if (expr.formal_target_type.type_parameter.parent_symbol != garray_type) {
3576 // GArray doesn't use pointer-based generics
3577 set_cvalue (expr, convert_to_generic_pointer (get_cvalue (expr), expr.target_type));
3583 public override void visit_boolean_literal (BooleanLiteral expr) {
3584 if (context.profile == Profile.GOBJECT) {
3585 set_cvalue (expr, new CCodeConstant (expr.value ? "TRUE" : "FALSE"));
3586 } else {
3587 cfile.add_include ("stdbool.h");
3588 set_cvalue (expr, new CCodeConstant (expr.value ? "true" : "false"));
3592 public override void visit_character_literal (CharacterLiteral expr) {
3593 if (expr.get_char () >= 0x20 && expr.get_char () < 0x80) {
3594 set_cvalue (expr, new CCodeConstant (expr.value));
3595 } else {
3596 set_cvalue (expr, new CCodeConstant ("%uU".printf (expr.get_char ())));
3600 public override void visit_integer_literal (IntegerLiteral expr) {
3601 set_cvalue (expr, new CCodeConstant (expr.value + expr.type_suffix));
3604 public override void visit_real_literal (RealLiteral expr) {
3605 string c_literal = expr.value;
3606 if (c_literal.has_suffix ("d") || c_literal.has_suffix ("D")) {
3607 // there is no suffix for double in C
3608 c_literal = c_literal.substring (0, c_literal.length - 1);
3610 if (!("." in c_literal || "e" in c_literal || "E" in c_literal)) {
3611 // C requires period or exponent part for floating constants
3612 if ("f" in c_literal || "F" in c_literal) {
3613 c_literal = c_literal.substring (0, c_literal.length - 1) + ".f";
3614 } else {
3615 c_literal += ".";
3618 set_cvalue (expr, new CCodeConstant (c_literal));
3621 public override void visit_string_literal (StringLiteral expr) {
3622 set_cvalue (expr, new CCodeConstant.string (expr.value.replace ("\n", "\\n")));
3624 if (expr.translate) {
3625 // translated string constant
3627 var m = (Method) root_symbol.scope.lookup ("GLib").scope.lookup ("_");
3628 add_symbol_declaration (cfile, m, m.get_cname ());
3630 var translate = new CCodeFunctionCall (new CCodeIdentifier ("_"));
3631 translate.add_argument (get_cvalue (expr));
3632 set_cvalue (expr, translate);
3636 public override void visit_regex_literal (RegexLiteral expr) {
3637 string[] parts = expr.value.split ("/", 3);
3638 string re = parts[2].escape ("");
3639 string flags = "0";
3641 if (parts[1].contains ("i")) {
3642 flags += " | G_REGEX_CASELESS";
3644 if (parts[1].contains ("m")) {
3645 flags += " | G_REGEX_MULTILINE";
3647 if (parts[1].contains ("s")) {
3648 flags += " | G_REGEX_DOTALL";
3650 if (parts[1].contains ("x")) {
3651 flags += " | G_REGEX_EXTENDED";
3654 var regex_var = get_temp_variable (regex_type, true, expr, false);
3655 emit_temp_var (regex_var);
3657 var cdecl = new CCodeDeclaration ("GRegex*");
3659 var cname = regex_var.name + "regex_" + next_regex_id.to_string ();
3660 if (this.next_regex_id == 0) {
3661 var fun = new CCodeFunction ("_thread_safe_regex_init", "GRegex*");
3662 fun.modifiers = CCodeModifiers.STATIC | CCodeModifiers.INLINE;
3663 fun.add_parameter (new CCodeParameter ("re", "GRegex**"));
3664 fun.add_parameter (new CCodeParameter ("pattern", "const gchar *"));
3665 fun.add_parameter (new CCodeParameter ("match_options", "GRegexMatchFlags"));
3667 push_function (fun);
3669 var once_enter_call = new CCodeFunctionCall (new CCodeIdentifier ("g_once_init_enter"));
3670 once_enter_call.add_argument (new CCodeConstant ("(volatile gsize*) re"));
3671 ccode.open_if (once_enter_call);
3673 var regex_new_call = new CCodeFunctionCall (new CCodeIdentifier ("g_regex_new"));
3674 regex_new_call.add_argument (new CCodeConstant ("pattern"));
3675 regex_new_call.add_argument (new CCodeConstant ("match_options"));
3676 regex_new_call.add_argument (new CCodeConstant ("0"));
3677 regex_new_call.add_argument (new CCodeConstant ("NULL"));
3678 ccode.add_assignment (new CCodeIdentifier ("GRegex* val"), regex_new_call);
3680 var once_leave_call = new CCodeFunctionCall (new CCodeIdentifier ("g_once_init_leave"));
3681 once_leave_call.add_argument (new CCodeConstant ("(volatile gsize*) re"));
3682 once_leave_call.add_argument (new CCodeConstant ("(gsize) val"));
3683 ccode.add_expression (once_leave_call);
3685 ccode.close ();
3687 ccode.add_return (new CCodeIdentifier ("*re"));
3689 pop_function ();
3691 cfile.add_function (fun);
3693 this.next_regex_id++;
3695 cdecl.add_declarator (new CCodeVariableDeclarator (cname + " = NULL"));
3696 cdecl.modifiers = CCodeModifiers.STATIC;
3698 var regex_const = new CCodeConstant ("_thread_safe_regex_init (&%s, \"%s\", %s)".printf (cname, re, flags));
3700 cfile.add_constant_declaration (cdecl);
3701 set_cvalue (expr, regex_const);
3704 public override void visit_null_literal (NullLiteral expr) {
3705 if (context.profile != Profile.GOBJECT) {
3706 cfile.add_include ("stddef.h");
3708 set_cvalue (expr, new CCodeConstant ("NULL"));
3710 var array_type = expr.target_type as ArrayType;
3711 var delegate_type = expr.target_type as DelegateType;
3712 if (array_type != null) {
3713 for (int dim = 1; dim <= array_type.rank; dim++) {
3714 append_array_length (expr, new CCodeConstant ("0"));
3716 } else if (delegate_type != null && delegate_type.delegate_symbol.has_target) {
3717 set_delegate_target (expr, new CCodeConstant ("NULL"));
3718 set_delegate_target_destroy_notify (expr, new CCodeConstant ("NULL"));
3722 public abstract TargetValue get_local_cvalue (LocalVariable local);
3724 public abstract TargetValue get_parameter_cvalue (Parameter param);
3726 public abstract TargetValue get_field_cvalue (Field field, Expression? instance);
3728 public virtual string get_delegate_target_cname (string delegate_cname) {
3729 assert_not_reached ();
3732 public virtual CCodeExpression get_delegate_target_cexpression (Expression delegate_expr, out CCodeExpression delegate_target_destroy_notify) {
3733 assert_not_reached ();
3736 public virtual CCodeExpression get_delegate_target_cvalue (TargetValue value) {
3737 return new CCodeInvalidExpression ();
3740 public virtual CCodeExpression get_delegate_target_destroy_notify_cvalue (TargetValue value) {
3741 return new CCodeInvalidExpression ();
3744 public virtual string get_delegate_target_destroy_notify_cname (string delegate_cname) {
3745 assert_not_reached ();
3748 public override void visit_base_access (BaseAccess expr) {
3749 CCodeExpression this_access;
3750 if (is_in_coroutine ()) {
3751 // use closure
3752 this_access = new CCodeMemberAccess.pointer (new CCodeIdentifier ("data"), "self");
3753 } else {
3754 this_access = new CCodeIdentifier ("self");
3757 set_cvalue (expr, generate_instance_cast (this_access, expr.value_type.data_type));
3760 public override void visit_postfix_expression (PostfixExpression expr) {
3761 MemberAccess ma = find_property_access (expr.inner);
3762 if (ma != null) {
3763 // property postfix expression
3764 var prop = (Property) ma.symbol_reference;
3766 // assign current value to temp variable
3767 var temp_decl = get_temp_variable (prop.property_type, true, expr, false);
3768 emit_temp_var (temp_decl);
3769 ccode.add_assignment (get_variable_cexpression (temp_decl.name), get_cvalue (expr.inner));
3771 // increment/decrement property
3772 var op = expr.increment ? CCodeBinaryOperator.PLUS : CCodeBinaryOperator.MINUS;
3773 var cexpr = new CCodeBinaryExpression (op, get_variable_cexpression (temp_decl.name), new CCodeConstant ("1"));
3774 store_property (prop, ma.inner, new GLibValue (expr.value_type, cexpr));
3776 // return previous value
3777 set_cvalue (expr, get_variable_cexpression (temp_decl.name));
3778 return;
3781 if (expr.parent_node is ExpressionStatement) {
3782 var op = expr.increment ? CCodeUnaryOperator.POSTFIX_INCREMENT : CCodeUnaryOperator.POSTFIX_DECREMENT;
3784 ccode.add_expression (new CCodeUnaryExpression (op, get_cvalue (expr.inner)));
3785 } else {
3786 // assign current value to temp variable
3787 var temp_decl = get_temp_variable (expr.inner.value_type, true, expr, false);
3788 emit_temp_var (temp_decl);
3789 ccode.add_assignment (get_variable_cexpression (temp_decl.name), get_cvalue (expr.inner));
3791 // increment/decrement variable
3792 var op = expr.increment ? CCodeBinaryOperator.PLUS : CCodeBinaryOperator.MINUS;
3793 var cexpr = new CCodeBinaryExpression (op, get_variable_cexpression (temp_decl.name), new CCodeConstant ("1"));
3794 ccode.add_assignment (get_cvalue (expr.inner), cexpr);
3796 // return previous value
3797 set_cvalue (expr, get_variable_cexpression (temp_decl.name));
3801 private MemberAccess? find_property_access (Expression expr) {
3802 if (!(expr is MemberAccess)) {
3803 return null;
3806 var ma = (MemberAccess) expr;
3807 if (ma.symbol_reference is Property) {
3808 return ma;
3811 return null;
3814 bool is_limited_generic_type (DataType type) {
3815 var cl = type.type_parameter.parent_symbol as Class;
3816 var st = type.type_parameter.parent_symbol as Struct;
3817 if ((cl != null && cl.is_compact) || st != null) {
3818 // compact classes and structs only
3819 // have very limited generics support
3820 return true;
3822 return false;
3825 public bool requires_copy (DataType type) {
3826 if (!type.is_disposable ()) {
3827 return false;
3830 var cl = type.data_type as Class;
3831 if (cl != null && cl.is_reference_counting ()
3832 && cl.get_ref_function () == "") {
3833 // empty ref_function => no ref necessary
3834 return false;
3837 if (type.type_parameter != null) {
3838 if (is_limited_generic_type (type)) {
3839 return false;
3843 return true;
3846 public bool requires_destroy (DataType type) {
3847 if (!type.is_disposable ()) {
3848 return false;
3851 var array_type = type as ArrayType;
3852 if (array_type != null && array_type.fixed_length) {
3853 return requires_destroy (array_type.element_type);
3856 var cl = type.data_type as Class;
3857 if (cl != null && cl.is_reference_counting ()
3858 && cl.get_unref_function () == "") {
3859 // empty unref_function => no unref necessary
3860 return false;
3863 if (type.type_parameter != null) {
3864 if (is_limited_generic_type (type)) {
3865 return false;
3869 return true;
3872 bool is_ref_function_void (DataType type) {
3873 var cl = type.data_type as Class;
3874 if (cl != null && cl.ref_function_void) {
3875 return true;
3876 } else {
3877 return false;
3881 public virtual CCodeExpression? get_ref_cexpression (DataType expression_type, CCodeExpression cexpr, Expression? expr, CodeNode node) {
3882 if (expression_type is DelegateType) {
3883 return cexpr;
3886 if (expression_type is ValueType && !expression_type.nullable) {
3887 // normal value type, no null check
3888 // (copy (&expr, &temp), temp)
3890 var decl = get_temp_variable (expression_type, false, node);
3891 emit_temp_var (decl);
3893 var ctemp = get_variable_cexpression (decl.name);
3895 var vt = (ValueType) expression_type;
3896 var st = (Struct) vt.type_symbol;
3897 var copy_call = new CCodeFunctionCall (new CCodeIdentifier (st.get_copy_function ()));
3898 copy_call.add_argument (new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, cexpr));
3899 copy_call.add_argument (new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, ctemp));
3901 if (!st.has_copy_function) {
3902 generate_struct_copy_function (st);
3905 var ccomma = new CCodeCommaExpression ();
3907 if (st.get_copy_function () == "g_value_copy") {
3908 // GValue requires g_value_init in addition to g_value_copy
3910 var value_type_call = new CCodeFunctionCall (new CCodeIdentifier ("G_VALUE_TYPE"));
3911 value_type_call.add_argument (new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, cexpr));
3913 var init_call = new CCodeFunctionCall (new CCodeIdentifier ("g_value_init"));
3914 init_call.add_argument (new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, ctemp));
3915 init_call.add_argument (value_type_call);
3917 ccomma.append_expression (init_call);
3920 ccomma.append_expression (copy_call);
3921 ccomma.append_expression (ctemp);
3923 if (gvalue_type != null && expression_type.data_type == gvalue_type) {
3924 // g_value_init/copy must not be called for uninitialized values
3925 var cisvalid = new CCodeFunctionCall (new CCodeIdentifier ("G_IS_VALUE"));
3926 cisvalid.add_argument (new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, cexpr));
3928 return new CCodeConditionalExpression (cisvalid, ccomma, cexpr);
3929 } else {
3930 return ccomma;
3934 /* (temp = expr, temp == NULL ? NULL : ref (temp))
3936 * can be simplified to
3937 * ref (expr)
3938 * if static type of expr is non-null
3941 var dupexpr = get_dup_func_expression (expression_type, node.source_reference);
3943 if (dupexpr == null) {
3944 node.error = true;
3945 return null;
3948 if (dupexpr is CCodeIdentifier && !(expression_type is ArrayType) && !(expression_type is GenericType) && !is_ref_function_void (expression_type)) {
3949 // generate and call NULL-aware ref function to reduce number
3950 // of temporary variables and simplify code
3952 var dupid = (CCodeIdentifier) dupexpr;
3953 string dup0_func = "_%s0".printf (dupid.name);
3955 // g_strdup is already NULL-safe
3956 if (dupid.name == "g_strdup") {
3957 dup0_func = dupid.name;
3958 } else if (add_wrapper (dup0_func)) {
3959 string pointer_cname = "gpointer";
3960 if (context.profile == Profile.POSIX) {
3961 pointer_cname = "void*";
3963 var dup0_fun = new CCodeFunction (dup0_func, pointer_cname);
3964 dup0_fun.add_parameter (new CCodeParameter ("self", pointer_cname));
3965 dup0_fun.modifiers = CCodeModifiers.STATIC;
3967 push_function (dup0_fun);
3969 var dup_call = new CCodeFunctionCall (dupexpr);
3970 dup_call.add_argument (new CCodeIdentifier ("self"));
3972 ccode.add_return (new CCodeConditionalExpression (new CCodeIdentifier ("self"), dup_call, new CCodeConstant ("NULL")));
3974 pop_function ();
3976 cfile.add_function (dup0_fun);
3979 var ccall = new CCodeFunctionCall (new CCodeIdentifier (dup0_func));
3980 ccall.add_argument (cexpr);
3981 return ccall;
3984 var ccall = new CCodeFunctionCall (dupexpr);
3986 if (!(expression_type is ArrayType) && expr != null && expr.is_non_null ()
3987 && !is_ref_function_void (expression_type)) {
3988 // expression is non-null
3989 ccall.add_argument (get_cvalue (expr));
3991 return ccall;
3992 } else {
3993 var decl = get_temp_variable (expression_type, false, node, false);
3994 emit_temp_var (decl);
3996 var ctemp = get_variable_cexpression (decl.name);
3998 var cisnull = new CCodeBinaryExpression (CCodeBinaryOperator.EQUALITY, ctemp, new CCodeConstant ("NULL"));
3999 if (expression_type.type_parameter != null) {
4000 // dup functions are optional for type parameters
4001 var cdupisnull = new CCodeBinaryExpression (CCodeBinaryOperator.EQUALITY, get_dup_func_expression (expression_type, node.source_reference), new CCodeConstant ("NULL"));
4002 cisnull = new CCodeBinaryExpression (CCodeBinaryOperator.OR, cisnull, cdupisnull);
4005 if (expression_type.type_parameter != null) {
4006 // cast from gconstpointer to gpointer as GBoxedCopyFunc expects gpointer
4007 ccall.add_argument (new CCodeCastExpression (ctemp, "gpointer"));
4008 } else {
4009 ccall.add_argument (ctemp);
4012 if (expression_type is ArrayType) {
4013 var array_type = (ArrayType) expression_type;
4014 bool first = true;
4015 CCodeExpression csizeexpr = null;
4016 for (int dim = 1; dim <= array_type.rank; dim++) {
4017 if (first) {
4018 csizeexpr = get_array_length_cexpression (expr, dim);
4019 first = false;
4020 } else {
4021 csizeexpr = new CCodeBinaryExpression (CCodeBinaryOperator.MUL, csizeexpr, get_array_length_cexpression (expr, dim));
4025 ccall.add_argument (csizeexpr);
4027 if (array_type.element_type is GenericType) {
4028 var elem_dupexpr = get_dup_func_expression (array_type.element_type, node.source_reference);
4029 if (elem_dupexpr == null) {
4030 elem_dupexpr = new CCodeConstant ("NULL");
4032 ccall.add_argument (elem_dupexpr);
4036 var ccomma = new CCodeCommaExpression ();
4037 ccomma.append_expression (new CCodeAssignment (ctemp, cexpr));
4039 CCodeExpression cifnull;
4040 if (expression_type.data_type != null) {
4041 cifnull = new CCodeConstant ("NULL");
4042 } else {
4043 // the value might be non-null even when the dup function is null,
4044 // so we may not just use NULL for type parameters
4046 // cast from gconstpointer to gpointer as methods in
4047 // generic classes may not return gconstpointer
4048 cifnull = new CCodeCastExpression (ctemp, "gpointer");
4050 ccomma.append_expression (new CCodeConditionalExpression (cisnull, cifnull, ccall));
4052 // repeat temp variable at the end of the comma expression
4053 // if the ref function returns void
4054 if (is_ref_function_void (expression_type)) {
4055 ccomma.append_expression (ctemp);
4058 return ccomma;
4062 bool is_reference_type_argument (DataType type_arg) {
4063 if (type_arg is ErrorType || (type_arg.data_type != null && type_arg.data_type.is_reference_type ())) {
4064 return true;
4065 } else {
4066 return false;
4070 bool is_nullable_value_type_argument (DataType type_arg) {
4071 if (type_arg is ValueType && type_arg.nullable) {
4072 return true;
4073 } else {
4074 return false;
4078 bool is_signed_integer_type_argument (DataType type_arg) {
4079 var st = type_arg.data_type as Struct;
4080 if (type_arg.nullable) {
4081 return false;
4082 } else if (st == bool_type.data_type) {
4083 return true;
4084 } else if (st == char_type.data_type) {
4085 return true;
4086 } else if (unichar_type != null && st == unichar_type.data_type) {
4087 return true;
4088 } else if (st == short_type.data_type) {
4089 return true;
4090 } else if (st == int_type.data_type) {
4091 return true;
4092 } else if (st == long_type.data_type) {
4093 return true;
4094 } else if (st == int8_type.data_type) {
4095 return true;
4096 } else if (st == int16_type.data_type) {
4097 return true;
4098 } else if (st == int32_type.data_type) {
4099 return true;
4100 } else if (st == gtype_type) {
4101 return true;
4102 } else if (type_arg is EnumValueType) {
4103 return true;
4104 } else {
4105 return false;
4109 bool is_unsigned_integer_type_argument (DataType type_arg) {
4110 var st = type_arg.data_type as Struct;
4111 if (type_arg.nullable) {
4112 return false;
4113 } else if (st == uchar_type.data_type) {
4114 return true;
4115 } else if (st == ushort_type.data_type) {
4116 return true;
4117 } else if (st == uint_type.data_type) {
4118 return true;
4119 } else if (st == ulong_type.data_type) {
4120 return true;
4121 } else if (st == uint8_type.data_type) {
4122 return true;
4123 } else if (st == uint16_type.data_type) {
4124 return true;
4125 } else if (st == uint32_type.data_type) {
4126 return true;
4127 } else {
4128 return false;
4132 public void check_type (DataType type) {
4133 var array_type = type as ArrayType;
4134 if (array_type != null) {
4135 check_type (array_type.element_type);
4136 if (array_type.element_type is ArrayType) {
4137 Report.error (type.source_reference, "Stacked arrays are not supported");
4138 } else if (array_type.element_type is DelegateType) {
4139 var delegate_type = (DelegateType) array_type.element_type;
4140 if (delegate_type.delegate_symbol.has_target) {
4141 Report.error (type.source_reference, "Delegates with target are not supported as array element type");
4145 foreach (var type_arg in type.get_type_arguments ()) {
4146 check_type (type_arg);
4147 check_type_argument (type_arg);
4151 void check_type_argument (DataType type_arg) {
4152 if (type_arg is GenericType
4153 || type_arg is PointerType
4154 || is_reference_type_argument (type_arg)
4155 || is_nullable_value_type_argument (type_arg)
4156 || is_signed_integer_type_argument (type_arg)
4157 || is_unsigned_integer_type_argument (type_arg)) {
4158 // no error
4159 } else if (type_arg is DelegateType) {
4160 var delegate_type = (DelegateType) type_arg;
4161 if (delegate_type.delegate_symbol.has_target) {
4162 Report.error (type_arg.source_reference, "Delegates with target are not supported as generic type arguments");
4164 } else {
4165 Report.error (type_arg.source_reference, "`%s' is not a supported generic type argument, use `?' to box value types".printf (type_arg.to_string ()));
4169 public virtual void generate_class_declaration (Class cl, CCodeFile decl_space) {
4170 if (add_symbol_declaration (decl_space, cl, cl.get_cname ())) {
4171 return;
4175 public virtual void generate_interface_declaration (Interface iface, CCodeFile decl_space) {
4178 public virtual void generate_method_declaration (Method m, CCodeFile decl_space) {
4181 public virtual void generate_error_domain_declaration (ErrorDomain edomain, CCodeFile decl_space) {
4184 public void add_generic_type_arguments (Map<int,CCodeExpression> arg_map, List<DataType> type_args, CodeNode expr, bool is_chainup = false) {
4185 int type_param_index = 0;
4186 foreach (var type_arg in type_args) {
4187 arg_map.set (get_param_pos (0.1 * type_param_index + 0.01), get_type_id_expression (type_arg, is_chainup));
4188 if (requires_copy (type_arg)) {
4189 var dup_func = get_dup_func_expression (type_arg, type_arg.source_reference, is_chainup);
4190 if (dup_func == null) {
4191 // type doesn't contain a copy function
4192 expr.error = true;
4193 return;
4195 arg_map.set (get_param_pos (0.1 * type_param_index + 0.02), new CCodeCastExpression (dup_func, "GBoxedCopyFunc"));
4196 arg_map.set (get_param_pos (0.1 * type_param_index + 0.03), get_destroy_func_expression (type_arg, is_chainup));
4197 } else {
4198 arg_map.set (get_param_pos (0.1 * type_param_index + 0.02), new CCodeConstant ("NULL"));
4199 arg_map.set (get_param_pos (0.1 * type_param_index + 0.03), new CCodeConstant ("NULL"));
4201 type_param_index++;
4205 public override void visit_object_creation_expression (ObjectCreationExpression expr) {
4206 CCodeExpression instance = null;
4207 CCodeExpression creation_expr = null;
4209 check_type (expr.type_reference);
4211 var st = expr.type_reference.data_type as Struct;
4212 if ((st != null && (!st.is_simple_type () || st.get_cname () == "va_list")) || expr.get_object_initializer ().size > 0) {
4213 // value-type initialization or object creation expression with object initializer
4215 var local = expr.parent_node as LocalVariable;
4216 if (local != null && has_simple_struct_initializer (local)) {
4217 if (local.captured) {
4218 var block = (Block) local.parent_symbol;
4219 instance = new CCodeMemberAccess.pointer (get_variable_cexpression ("_data%d_".printf (get_block_id (block))), get_variable_cname (local.name));
4220 } else {
4221 instance = get_variable_cexpression (get_variable_cname (local.name));
4223 } else {
4224 var temp_decl = get_temp_variable (expr.type_reference, false, expr);
4225 emit_temp_var (temp_decl);
4227 instance = get_variable_cexpression (get_variable_cname (temp_decl.name));
4231 if (expr.symbol_reference == null) {
4232 // no creation method
4233 if (expr.type_reference.data_type is Struct) {
4234 // memset needs string.h
4235 cfile.add_include ("string.h");
4236 var creation_call = new CCodeFunctionCall (new CCodeIdentifier ("memset"));
4237 creation_call.add_argument (new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, instance));
4238 creation_call.add_argument (new CCodeConstant ("0"));
4239 creation_call.add_argument (new CCodeIdentifier ("sizeof (%s)".printf (expr.type_reference.get_cname ())));
4241 creation_expr = creation_call;
4243 } else if (expr.type_reference.data_type == glist_type ||
4244 expr.type_reference.data_type == gslist_type) {
4245 // NULL is an empty list
4246 set_cvalue (expr, new CCodeConstant ("NULL"));
4247 } else if (expr.symbol_reference is Method) {
4248 // use creation method
4249 var m = (Method) expr.symbol_reference;
4250 var params = m.get_parameters ();
4251 CCodeFunctionCall creation_call;
4253 generate_method_declaration (m, cfile);
4255 var cl = expr.type_reference.data_type as Class;
4257 if (!m.has_new_function) {
4258 // use construct function directly
4259 creation_call = new CCodeFunctionCall (new CCodeIdentifier (m.get_real_cname ()));
4260 creation_call.add_argument (new CCodeIdentifier (cl.get_type_id ()));
4261 } else {
4262 creation_call = new CCodeFunctionCall (new CCodeIdentifier (m.get_cname ()));
4265 if ((st != null && !st.is_simple_type ()) && !(m.cinstance_parameter_position < 0)) {
4266 creation_call.add_argument (new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, instance));
4267 } else if (st != null && st.get_cname () == "va_list") {
4268 creation_call.add_argument (instance);
4269 if (m.get_cname () == "va_start") {
4270 Parameter last_param = null;
4271 foreach (var param in current_method.get_parameters ()) {
4272 if (param.ellipsis) {
4273 break;
4275 last_param = param;
4277 creation_call.add_argument (new CCodeIdentifier (get_variable_cname (last_param.name)));
4281 generate_type_declaration (expr.type_reference, cfile);
4283 var carg_map = new HashMap<int,CCodeExpression> (direct_hash, direct_equal);
4285 if (cl != null && !cl.is_compact) {
4286 add_generic_type_arguments (carg_map, expr.type_reference.get_type_arguments (), expr);
4287 } else if (cl != null && m.simple_generics) {
4288 int type_param_index = 0;
4289 foreach (var type_arg in expr.type_reference.get_type_arguments ()) {
4290 if (requires_copy (type_arg)) {
4291 carg_map.set (get_param_pos (-1 + 0.1 * type_param_index + 0.03), get_destroy0_func_expression (type_arg));
4292 } else {
4293 carg_map.set (get_param_pos (-1 + 0.1 * type_param_index + 0.03), new CCodeConstant ("NULL"));
4295 type_param_index++;
4299 bool ellipsis = false;
4301 int i = 1;
4302 int arg_pos;
4303 Iterator<Parameter> params_it = params.iterator ();
4304 foreach (Expression arg in expr.get_argument_list ()) {
4305 CCodeExpression cexpr = get_cvalue (arg);
4306 Parameter param = null;
4307 if (params_it.next ()) {
4308 param = params_it.get ();
4309 ellipsis = param.ellipsis;
4310 if (!ellipsis) {
4311 if (!param.no_array_length && param.variable_type is ArrayType) {
4312 var array_type = (ArrayType) param.variable_type;
4313 for (int dim = 1; dim <= array_type.rank; dim++) {
4314 carg_map.set (get_param_pos (param.carray_length_parameter_position + 0.01 * dim), get_array_length_cexpression (arg, dim));
4316 } else if (param.variable_type is DelegateType) {
4317 var deleg_type = (DelegateType) param.variable_type;
4318 var d = deleg_type.delegate_symbol;
4319 if (d.has_target) {
4320 CCodeExpression delegate_target_destroy_notify;
4321 var delegate_target = get_delegate_target_cexpression (arg, out delegate_target_destroy_notify);
4322 carg_map.set (get_param_pos (param.cdelegate_target_parameter_position), delegate_target);
4323 if (deleg_type.value_owned) {
4324 carg_map.set (get_param_pos (param.cdelegate_target_parameter_position + 0.01), delegate_target_destroy_notify);
4329 cexpr = handle_struct_argument (param, arg, cexpr);
4331 if (param.ctype != null) {
4332 cexpr = new CCodeCastExpression (cexpr, param.ctype);
4334 } else {
4335 cexpr = handle_struct_argument (null, arg, cexpr);
4338 arg_pos = get_param_pos (param.cparameter_position, ellipsis);
4339 } else {
4340 // default argument position
4341 cexpr = handle_struct_argument (null, arg, cexpr);
4342 arg_pos = get_param_pos (i, ellipsis);
4345 carg_map.set (arg_pos, cexpr);
4347 i++;
4349 while (params_it.next ()) {
4350 var param = params_it.get ();
4352 if (param.ellipsis) {
4353 ellipsis = true;
4354 break;
4357 if (param.initializer == null) {
4358 Report.error (expr.source_reference, "no default expression for argument %d".printf (i));
4359 return;
4362 /* evaluate default expression here as the code
4363 * generator might not have visited the formal
4364 * parameter yet */
4365 param.initializer.emit (this);
4367 carg_map.set (get_param_pos (param.cparameter_position), get_cvalue (param.initializer));
4368 i++;
4371 // append C arguments in the right order
4372 int last_pos = -1;
4373 int min_pos;
4374 while (true) {
4375 min_pos = -1;
4376 foreach (int pos in carg_map.get_keys ()) {
4377 if (pos > last_pos && (min_pos == -1 || pos < min_pos)) {
4378 min_pos = pos;
4381 if (min_pos == -1) {
4382 break;
4384 creation_call.add_argument (carg_map.get (min_pos));
4385 last_pos = min_pos;
4388 if ((st != null && !st.is_simple_type ()) && m.cinstance_parameter_position < 0) {
4389 // instance parameter is at the end in a struct creation method
4390 creation_call.add_argument (new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, instance));
4393 if (expr.tree_can_fail) {
4394 // method can fail
4395 current_method_inner_error = true;
4396 creation_call.add_argument (new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, get_variable_cexpression ("_inner_error_")));
4399 if (ellipsis) {
4400 /* ensure variable argument list ends with NULL
4401 * except when using printf-style arguments */
4402 if (!m.printf_format && !m.scanf_format && m.sentinel != "") {
4403 creation_call.add_argument (new CCodeConstant (m.sentinel));
4407 creation_expr = creation_call;
4409 // cast the return value of the creation method back to the intended type if
4410 // it requested a special C return type
4411 if (get_custom_creturn_type (m) != null) {
4412 creation_expr = new CCodeCastExpression (creation_expr, expr.type_reference.get_cname ());
4414 } else if (expr.symbol_reference is ErrorCode) {
4415 var ecode = (ErrorCode) expr.symbol_reference;
4416 var edomain = (ErrorDomain) ecode.parent_symbol;
4417 CCodeFunctionCall creation_call;
4419 generate_error_domain_declaration (edomain, cfile);
4421 if (expr.get_argument_list ().size == 1) {
4422 // must not be a format argument
4423 creation_call = new CCodeFunctionCall (new CCodeIdentifier ("g_error_new_literal"));
4424 } else {
4425 creation_call = new CCodeFunctionCall (new CCodeIdentifier ("g_error_new"));
4427 creation_call.add_argument (new CCodeIdentifier (edomain.get_upper_case_cname ()));
4428 creation_call.add_argument (new CCodeIdentifier (ecode.get_cname ()));
4430 foreach (Expression arg in expr.get_argument_list ()) {
4431 creation_call.add_argument (get_cvalue (arg));
4434 creation_expr = creation_call;
4435 } else {
4436 assert (false);
4439 var local = expr.parent_node as LocalVariable;
4440 if (local != null && has_simple_struct_initializer (local)) {
4441 // no temporary variable necessary
4442 ccode.add_expression (creation_expr);
4443 set_cvalue (expr, instance);
4444 return;
4445 } else if (instance != null) {
4446 if (expr.type_reference.data_type is Struct) {
4447 ccode.add_expression (creation_expr);
4448 } else {
4449 ccode.add_assignment (instance, creation_expr);
4452 foreach (MemberInitializer init in expr.get_object_initializer ()) {
4453 if (init.symbol_reference is Field) {
4454 var f = (Field) init.symbol_reference;
4455 var instance_target_type = get_data_type_for_symbol ((TypeSymbol) f.parent_symbol);
4456 var typed_inst = transform_expression (instance, expr.type_reference, instance_target_type);
4457 CCodeExpression lhs;
4458 if (expr.type_reference.data_type is Struct) {
4459 lhs = new CCodeMemberAccess (typed_inst, f.get_cname ());
4460 } else {
4461 lhs = new CCodeMemberAccess.pointer (typed_inst, f.get_cname ());
4463 ccode.add_assignment (lhs, get_cvalue (init.initializer));
4465 if (f.variable_type is ArrayType && !f.no_array_length) {
4466 var array_type = (ArrayType) f.variable_type;
4467 for (int dim = 1; dim <= array_type.rank; dim++) {
4468 if (expr.type_reference.data_type is Struct) {
4469 lhs = new CCodeMemberAccess (typed_inst, get_array_length_cname (f.get_cname (), dim));
4470 } else {
4471 lhs = new CCodeMemberAccess.pointer (typed_inst, get_array_length_cname (f.get_cname (), dim));
4473 var rhs_array_len = get_array_length_cexpression (init.initializer, dim);
4474 ccode.add_assignment (lhs, rhs_array_len);
4476 } else if (f.variable_type is DelegateType && (f.variable_type as DelegateType).delegate_symbol.has_target && !f.no_delegate_target) {
4477 if (expr.type_reference.data_type is Struct) {
4478 lhs = new CCodeMemberAccess (typed_inst, get_delegate_target_cname (f.get_cname ()));
4479 } else {
4480 lhs = new CCodeMemberAccess.pointer (typed_inst, get_delegate_target_cname (f.get_cname ()));
4482 CCodeExpression rhs_delegate_target_destroy_notify;
4483 var rhs_delegate_target = get_delegate_target_cexpression (init.initializer, out rhs_delegate_target_destroy_notify);
4484 ccode.add_assignment (lhs, rhs_delegate_target);
4487 var cl = f.parent_symbol as Class;
4488 if (cl != null) {
4489 generate_class_struct_declaration (cl, cfile);
4491 } else if (init.symbol_reference is Property) {
4492 var inst_ma = new MemberAccess.simple ("new");
4493 inst_ma.value_type = expr.type_reference;
4494 set_cvalue (inst_ma, instance);
4495 store_property ((Property) init.symbol_reference, inst_ma, init.initializer.target_value);
4499 creation_expr = instance;
4502 if (creation_expr != null) {
4503 var temp_var = get_temp_variable (expr.value_type);
4504 var temp_ref = get_variable_cexpression (temp_var.name);
4506 emit_temp_var (temp_var);
4508 ccode.add_assignment (temp_ref, creation_expr);
4509 set_cvalue (expr, temp_ref);
4513 public CCodeExpression? handle_struct_argument (Parameter? param, Expression arg, CCodeExpression? cexpr) {
4514 DataType type;
4515 if (param != null) {
4516 type = param.variable_type;
4517 } else {
4518 // varargs
4519 type = arg.value_type;
4522 // pass non-simple struct instances always by reference
4523 if (!(arg.value_type is NullType) && type.is_real_struct_type ()) {
4524 // we already use a reference for arguments of ref, out, and nullable parameters
4525 if ((param == null || param.direction == ParameterDirection.IN) && !type.nullable) {
4526 var unary = cexpr as CCodeUnaryExpression;
4527 if (unary != null && unary.operator == CCodeUnaryOperator.POINTER_INDIRECTION) {
4528 // *expr => expr
4529 return unary.inner;
4530 } else if (cexpr is CCodeIdentifier || cexpr is CCodeMemberAccess) {
4531 return new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, cexpr);
4532 } else {
4533 // if cexpr is e.g. a function call, we can't take the address of the expression
4534 // (tmp = expr, &tmp)
4535 var ccomma = new CCodeCommaExpression ();
4537 var temp_var = get_temp_variable (type, true, null, false);
4538 emit_temp_var (temp_var);
4539 ccomma.append_expression (new CCodeAssignment (get_variable_cexpression (temp_var.name), cexpr));
4540 ccomma.append_expression (new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, get_variable_cexpression (temp_var.name)));
4542 return ccomma;
4547 return cexpr;
4550 public override void visit_sizeof_expression (SizeofExpression expr) {
4551 generate_type_declaration (expr.type_reference, cfile);
4553 var csizeof = new CCodeFunctionCall (new CCodeIdentifier ("sizeof"));
4554 csizeof.add_argument (new CCodeIdentifier (expr.type_reference.get_cname ()));
4555 set_cvalue (expr, csizeof);
4558 public override void visit_typeof_expression (TypeofExpression expr) {
4559 set_cvalue (expr, get_type_id_expression (expr.type_reference));
4562 public override void visit_unary_expression (UnaryExpression expr) {
4563 if (expr.operator == UnaryOperator.REF || expr.operator == UnaryOperator.OUT) {
4564 var glib_value = (GLibValue) expr.inner.target_value;
4566 var ref_value = new GLibValue (glib_value.value_type);
4567 ref_value.cvalue = new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, glib_value.cvalue);
4569 if (glib_value.array_length_cvalues != null) {
4570 for (int i = 0; i < glib_value.array_length_cvalues.size; i++) {
4571 ref_value.append_array_length_cvalue (new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, glib_value.array_length_cvalues[i]));
4575 if (glib_value.delegate_target_cvalue != null) {
4576 ref_value.delegate_target_cvalue = new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, glib_value.delegate_target_cvalue);
4578 if (glib_value.delegate_target_destroy_notify_cvalue != null) {
4579 ref_value.delegate_target_destroy_notify_cvalue = new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, glib_value.delegate_target_destroy_notify_cvalue);
4582 expr.target_value = ref_value;
4583 return;
4586 CCodeUnaryOperator op;
4587 if (expr.operator == UnaryOperator.PLUS) {
4588 op = CCodeUnaryOperator.PLUS;
4589 } else if (expr.operator == UnaryOperator.MINUS) {
4590 op = CCodeUnaryOperator.MINUS;
4591 } else if (expr.operator == UnaryOperator.LOGICAL_NEGATION) {
4592 op = CCodeUnaryOperator.LOGICAL_NEGATION;
4593 } else if (expr.operator == UnaryOperator.BITWISE_COMPLEMENT) {
4594 op = CCodeUnaryOperator.BITWISE_COMPLEMENT;
4595 } else if (expr.operator == UnaryOperator.INCREMENT) {
4596 op = CCodeUnaryOperator.PREFIX_INCREMENT;
4597 } else if (expr.operator == UnaryOperator.DECREMENT) {
4598 op = CCodeUnaryOperator.PREFIX_DECREMENT;
4599 } else {
4600 assert_not_reached ();
4602 set_cvalue (expr, new CCodeUnaryExpression (op, get_cvalue (expr.inner)));
4605 public CCodeExpression? try_cast_value_to_type (CCodeExpression ccodeexpr, DataType from, DataType to, Expression? expr = null) {
4606 if (from == null || gvalue_type == null || from.data_type != gvalue_type || to.get_type_id () == null) {
4607 return null;
4610 // explicit conversion from GValue
4611 var ccall = new CCodeFunctionCall (get_value_getter_function (to));
4612 CCodeExpression gvalue;
4613 if (from.nullable) {
4614 gvalue = ccodeexpr;
4615 } else {
4616 gvalue = new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, ccodeexpr);
4618 ccall.add_argument (gvalue);
4620 CCodeExpression rv = ccall;
4622 if (expr != null && to is ArrayType) {
4623 // null-terminated string array
4624 var len_call = new CCodeFunctionCall (new CCodeIdentifier ("g_strv_length"));
4625 len_call.add_argument (rv);
4626 append_array_length (expr, len_call);
4627 } else if (to is StructValueType) {
4628 var temp_decl = get_temp_variable (to, true, null, true);
4629 emit_temp_var (temp_decl);
4630 var ctemp = get_variable_cexpression (temp_decl.name);
4632 rv = new CCodeUnaryExpression (CCodeUnaryOperator.POINTER_INDIRECTION, new CCodeCastExpression (rv, (new PointerType(to)).get_cname ()));
4633 var holds = new CCodeFunctionCall (new CCodeIdentifier ("G_VALUE_HOLDS"));
4634 holds.add_argument (gvalue);
4635 holds.add_argument (new CCodeIdentifier (to.get_type_id ()));
4636 var cond = new CCodeBinaryExpression (CCodeBinaryOperator.AND, holds, ccall);
4637 var warn = new CCodeFunctionCall (new CCodeIdentifier ("g_warning"));
4638 warn.add_argument (new CCodeConstant ("\"Invalid GValue unboxing (wrong type or NULL)\""));
4639 var fail = new CCodeCommaExpression ();
4640 fail.append_expression (warn);
4641 fail.append_expression (ctemp);
4642 rv = new CCodeConditionalExpression (cond, rv, fail);
4645 return rv;
4648 int next_variant_function_id = 0;
4650 public CCodeExpression? try_cast_variant_to_type (CCodeExpression ccodeexpr, DataType from, DataType to, Expression? expr = null) {
4651 if (from == null || gvariant_type == null || from.data_type != gvariant_type) {
4652 return null;
4655 string variant_func = "_variant_get%d".printf (++next_variant_function_id);
4657 var ccall = new CCodeFunctionCall (new CCodeIdentifier (variant_func));
4658 ccall.add_argument (ccodeexpr);
4660 var cfunc = new CCodeFunction (variant_func);
4661 cfunc.modifiers = CCodeModifiers.STATIC;
4662 cfunc.add_parameter (new CCodeParameter ("value", "GVariant*"));
4664 if (!to.is_real_non_null_struct_type ()) {
4665 cfunc.return_type = to.get_cname ();
4668 if (to.is_real_non_null_struct_type ()) {
4669 // structs are returned via out parameter
4670 cfunc.add_parameter (new CCodeParameter ("result", to.get_cname () + "*"));
4671 } else if (to is ArrayType) {
4672 // return array length if appropriate
4673 var array_type = (ArrayType) to;
4675 for (int dim = 1; dim <= array_type.rank; dim++) {
4676 var temp_decl = get_temp_variable (int_type, false, expr);
4677 emit_temp_var (temp_decl);
4679 ccall.add_argument (new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, get_variable_cexpression (temp_decl.name)));
4680 cfunc.add_parameter (new CCodeParameter (get_array_length_cname ("result", dim), "int*"));
4681 append_array_length (expr, get_variable_cexpression (temp_decl.name));
4685 push_function (cfunc);
4687 var result = deserialize_expression (to, new CCodeIdentifier ("value"), new CCodeIdentifier ("*result"));
4688 ccode.add_return (result);
4690 pop_function ();
4692 cfile.add_function_declaration (cfunc);
4693 cfile.add_function (cfunc);
4695 return ccall;
4698 public virtual CCodeExpression? deserialize_expression (DataType type, CCodeExpression variant_expr, CCodeExpression? expr, CCodeExpression? error_expr = null, out bool may_fail = null) {
4699 return null;
4702 public virtual CCodeExpression? serialize_expression (DataType type, CCodeExpression expr) {
4703 return null;
4706 public override void visit_cast_expression (CastExpression expr) {
4707 var valuecast = try_cast_value_to_type (get_cvalue (expr.inner), expr.inner.value_type, expr.type_reference, expr);
4708 if (valuecast != null) {
4709 set_cvalue (expr, valuecast);
4710 return;
4713 var variantcast = try_cast_variant_to_type (get_cvalue (expr.inner), expr.inner.value_type, expr.type_reference, expr);
4714 if (variantcast != null) {
4715 set_cvalue (expr, variantcast);
4716 return;
4719 generate_type_declaration (expr.type_reference, cfile);
4721 var cl = expr.type_reference.data_type as Class;
4722 var iface = expr.type_reference.data_type as Interface;
4723 if (context.profile == Profile.GOBJECT && (iface != null || (cl != null && !cl.is_compact))) {
4724 // checked cast for strict subtypes of GTypeInstance
4725 if (expr.is_silent_cast) {
4726 var temp_decl = get_temp_variable (expr.inner.value_type, expr.inner.value_type.value_owned, expr, false);
4727 emit_temp_var (temp_decl);
4728 var ctemp = get_variable_cexpression (temp_decl.name);
4730 ccode.add_assignment (ctemp, get_cvalue (expr.inner));
4731 var ccheck = create_type_check (ctemp, expr.type_reference);
4732 var ccast = new CCodeCastExpression (ctemp, expr.type_reference.get_cname ());
4733 var cnull = new CCodeConstant ("NULL");
4735 set_cvalue (expr, new CCodeConditionalExpression (ccheck, ccast, cnull));
4736 } else {
4737 set_cvalue (expr, generate_instance_cast (get_cvalue (expr.inner), expr.type_reference.data_type));
4739 } else {
4740 if (expr.is_silent_cast) {
4741 expr.error = true;
4742 Report.error (expr.source_reference, "Operation not supported for this type");
4743 return;
4746 // retain array length
4747 var array_type = expr.type_reference as ArrayType;
4748 if (array_type != null && expr.inner.value_type is ArrayType) {
4749 for (int dim = 1; dim <= array_type.rank; dim++) {
4750 append_array_length (expr, get_array_length_cexpression (expr.inner, dim));
4752 } else if (array_type != null) {
4753 // cast from non-array to array, set invalid length
4754 // required by string.data, e.g.
4755 for (int dim = 1; dim <= array_type.rank; dim++) {
4756 append_array_length (expr, new CCodeConstant ("-1"));
4760 var innercexpr = get_cvalue (expr.inner);
4761 if (expr.type_reference.data_type is Struct && !expr.type_reference.nullable &&
4762 expr.inner.value_type.data_type is Struct && expr.inner.value_type.nullable) {
4763 // nullable integer or float or boolean or struct cast to non-nullable
4764 innercexpr = new CCodeUnaryExpression (CCodeUnaryOperator.POINTER_INDIRECTION, innercexpr);
4766 set_cvalue (expr, new CCodeCastExpression (innercexpr, expr.type_reference.get_cname ()));
4768 if (expr.type_reference is DelegateType) {
4769 if (get_delegate_target (expr.inner) != null) {
4770 set_delegate_target (expr, get_delegate_target (expr.inner));
4771 } else {
4772 set_delegate_target (expr, new CCodeConstant ("NULL"));
4774 if (get_delegate_target_destroy_notify (expr.inner) != null) {
4775 set_delegate_target_destroy_notify (expr, get_delegate_target_destroy_notify (expr.inner));
4776 } else {
4777 set_delegate_target_destroy_notify (expr, new CCodeConstant ("NULL"));
4783 public override void visit_named_argument (NamedArgument expr) {
4784 set_cvalue (expr, get_cvalue (expr.inner));
4787 public override void visit_pointer_indirection (PointerIndirection expr) {
4788 set_cvalue (expr, new CCodeUnaryExpression (CCodeUnaryOperator.POINTER_INDIRECTION, get_cvalue (expr.inner)));
4791 public override void visit_addressof_expression (AddressofExpression expr) {
4792 if (get_cvalue (expr.inner) is CCodeCommaExpression) {
4793 var ccomma = get_cvalue (expr.inner) as CCodeCommaExpression;
4794 var inner = ccomma.get_inner ();
4795 var last = inner.get (inner.size - 1);
4796 ccomma.set_expression (inner.size - 1, new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, (CCodeExpression) last));
4797 set_cvalue (expr, ccomma);
4798 } else {
4799 set_cvalue (expr, new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, get_cvalue (expr.inner)));
4803 public override void visit_reference_transfer_expression (ReferenceTransferExpression expr) {
4804 /* (tmp = var, var = null, tmp) */
4805 var temp_decl = get_temp_variable (expr.value_type, true, expr, false);
4806 emit_temp_var (temp_decl);
4807 var cvar = get_variable_cexpression (temp_decl.name);
4809 ccode.add_assignment (cvar, get_cvalue (expr.inner));
4810 if (!(expr.value_type is DelegateType)) {
4811 ccode.add_assignment (get_cvalue (expr.inner), new CCodeConstant ("NULL"));
4814 set_cvalue (expr, cvar);
4816 var array_type = expr.value_type as ArrayType;
4817 if (array_type != null) {
4818 for (int dim = 1; dim <= array_type.rank; dim++) {
4819 append_array_length (expr, get_array_length_cexpression (expr.inner, dim));
4823 var delegate_type = expr.value_type as DelegateType;
4824 if (delegate_type != null && delegate_type.delegate_symbol.has_target) {
4825 var temp_target_decl = get_temp_variable (new PointerType (new VoidType ()), true, expr, false);
4826 emit_temp_var (temp_target_decl);
4827 var target_cvar = get_variable_cexpression (temp_target_decl.name);
4828 CCodeExpression target_destroy_notify;
4829 var target = get_delegate_target_cexpression (expr.inner, out target_destroy_notify);
4830 ccode.add_assignment (target_cvar, target);
4831 set_delegate_target (expr, target_cvar);
4832 if (target_destroy_notify != null) {
4833 var temp_target_destroy_notify_decl = get_temp_variable (gdestroynotify_type, true, expr, false);
4834 emit_temp_var (temp_target_destroy_notify_decl);
4835 var target_destroy_notify_cvar = get_variable_cexpression (temp_target_destroy_notify_decl.name);
4836 ccode.add_assignment (target_destroy_notify_cvar, target_destroy_notify);
4837 ccode.add_assignment (target_destroy_notify, new CCodeConstant ("NULL"));
4838 set_delegate_target_destroy_notify (expr, target_destroy_notify_cvar);
4843 public override void visit_binary_expression (BinaryExpression expr) {
4844 var cleft = get_cvalue (expr.left);
4845 var cright = get_cvalue (expr.right);
4847 CCodeExpression? left_chain = null;
4848 if (expr.chained) {
4849 var lbe = (BinaryExpression) expr.left;
4851 var temp_decl = get_temp_variable (lbe.right.value_type, true, null, false);
4852 emit_temp_var (temp_decl);
4853 var cvar = get_variable_cexpression (temp_decl.name);
4854 var ccomma = new CCodeCommaExpression ();
4855 var clbe = (CCodeBinaryExpression) get_cvalue (lbe);
4856 if (lbe.chained) {
4857 clbe = (CCodeBinaryExpression) clbe.right;
4859 ccomma.append_expression (new CCodeAssignment (cvar, get_cvalue (lbe.right)));
4860 clbe.right = get_variable_cexpression (temp_decl.name);
4861 ccomma.append_expression (cleft);
4862 cleft = cvar;
4863 left_chain = ccomma;
4866 CCodeBinaryOperator op;
4867 if (expr.operator == BinaryOperator.PLUS) {
4868 op = CCodeBinaryOperator.PLUS;
4869 } else if (expr.operator == BinaryOperator.MINUS) {
4870 op = CCodeBinaryOperator.MINUS;
4871 } else if (expr.operator == BinaryOperator.MUL) {
4872 op = CCodeBinaryOperator.MUL;
4873 } else if (expr.operator == BinaryOperator.DIV) {
4874 op = CCodeBinaryOperator.DIV;
4875 } else if (expr.operator == BinaryOperator.MOD) {
4876 if (expr.value_type.equals (double_type)) {
4877 cfile.add_include ("math.h");
4878 var ccall = new CCodeFunctionCall (new CCodeIdentifier ("fmod"));
4879 ccall.add_argument (cleft);
4880 ccall.add_argument (cright);
4881 set_cvalue (expr, ccall);
4882 return;
4883 } else if (expr.value_type.equals (float_type)) {
4884 cfile.add_include ("math.h");
4885 var ccall = new CCodeFunctionCall (new CCodeIdentifier ("fmodf"));
4886 ccall.add_argument (cleft);
4887 ccall.add_argument (cright);
4888 set_cvalue (expr, ccall);
4889 return;
4890 } else {
4891 op = CCodeBinaryOperator.MOD;
4893 } else if (expr.operator == BinaryOperator.SHIFT_LEFT) {
4894 op = CCodeBinaryOperator.SHIFT_LEFT;
4895 } else if (expr.operator == BinaryOperator.SHIFT_RIGHT) {
4896 op = CCodeBinaryOperator.SHIFT_RIGHT;
4897 } else if (expr.operator == BinaryOperator.LESS_THAN) {
4898 op = CCodeBinaryOperator.LESS_THAN;
4899 } else if (expr.operator == BinaryOperator.GREATER_THAN) {
4900 op = CCodeBinaryOperator.GREATER_THAN;
4901 } else if (expr.operator == BinaryOperator.LESS_THAN_OR_EQUAL) {
4902 op = CCodeBinaryOperator.LESS_THAN_OR_EQUAL;
4903 } else if (expr.operator == BinaryOperator.GREATER_THAN_OR_EQUAL) {
4904 op = CCodeBinaryOperator.GREATER_THAN_OR_EQUAL;
4905 } else if (expr.operator == BinaryOperator.EQUALITY) {
4906 op = CCodeBinaryOperator.EQUALITY;
4907 } else if (expr.operator == BinaryOperator.INEQUALITY) {
4908 op = CCodeBinaryOperator.INEQUALITY;
4909 } else if (expr.operator == BinaryOperator.BITWISE_AND) {
4910 op = CCodeBinaryOperator.BITWISE_AND;
4911 } else if (expr.operator == BinaryOperator.BITWISE_OR) {
4912 op = CCodeBinaryOperator.BITWISE_OR;
4913 } else if (expr.operator == BinaryOperator.BITWISE_XOR) {
4914 op = CCodeBinaryOperator.BITWISE_XOR;
4915 } else if (expr.operator == BinaryOperator.AND) {
4916 op = CCodeBinaryOperator.AND;
4917 } else if (expr.operator == BinaryOperator.OR) {
4918 op = CCodeBinaryOperator.OR;
4919 } else if (expr.operator == BinaryOperator.IN) {
4920 if (expr.right.value_type is ArrayType) {
4921 var array_type = (ArrayType) expr.right.value_type;
4922 var node = new CCodeFunctionCall (new CCodeIdentifier (generate_array_contains_wrapper (array_type)));
4923 node.add_argument (cright);
4924 node.add_argument (get_array_length_cexpression (expr.right));
4925 if (array_type.element_type is StructValueType) {
4926 node.add_argument (new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, cleft));
4927 } else {
4928 node.add_argument (cleft);
4930 set_cvalue (expr, node);
4931 } else {
4932 set_cvalue (expr, new CCodeBinaryExpression (CCodeBinaryOperator.EQUALITY, new CCodeBinaryExpression (CCodeBinaryOperator.BITWISE_AND, cright, cleft), cleft));
4934 return;
4935 } else {
4936 assert_not_reached ();
4939 if (expr.operator == BinaryOperator.EQUALITY ||
4940 expr.operator == BinaryOperator.INEQUALITY) {
4941 var left_type = expr.left.target_type;
4942 var right_type = expr.right.target_type;
4943 make_comparable_cexpression (ref left_type, ref cleft, ref right_type, ref cright);
4945 if (left_type is StructValueType && right_type is StructValueType) {
4946 var equalfunc = generate_struct_equal_function ((Struct) left_type.data_type as Struct);
4947 var ccall = new CCodeFunctionCall (new CCodeIdentifier (equalfunc));
4948 ccall.add_argument (cleft);
4949 ccall.add_argument (cright);
4950 cleft = ccall;
4951 cright = new CCodeConstant ("TRUE");
4952 } else if ((left_type is IntegerType || left_type is FloatingType || left_type is BooleanType) && left_type.nullable &&
4953 (right_type is IntegerType || right_type is FloatingType || right_type is BooleanType) && right_type.nullable) {
4954 var equalfunc = generate_numeric_equal_function ((Struct) left_type.data_type as Struct);
4955 var ccall = new CCodeFunctionCall (new CCodeIdentifier (equalfunc));
4956 ccall.add_argument (cleft);
4957 ccall.add_argument (cright);
4958 cleft = ccall;
4959 cright = new CCodeConstant ("TRUE");
4963 if (!(expr.left.value_type is NullType)
4964 && expr.left.value_type.compatible (string_type)
4965 && !(expr.right.value_type is NullType)
4966 && expr.right.value_type.compatible (string_type)) {
4967 if (expr.operator == BinaryOperator.PLUS) {
4968 // string concatenation
4969 if (expr.left.is_constant () && expr.right.is_constant ()) {
4970 string left, right;
4972 if (cleft is CCodeIdentifier) {
4973 left = ((CCodeIdentifier) cleft).name;
4974 } else if (cleft is CCodeConstant) {
4975 left = ((CCodeConstant) cleft).name;
4976 } else {
4977 assert_not_reached ();
4979 if (cright is CCodeIdentifier) {
4980 right = ((CCodeIdentifier) cright).name;
4981 } else if (cright is CCodeConstant) {
4982 right = ((CCodeConstant) cright).name;
4983 } else {
4984 assert_not_reached ();
4987 set_cvalue (expr, new CCodeConstant ("%s %s".printf (left, right)));
4988 return;
4989 } else {
4990 if (context.profile == Profile.POSIX) {
4991 // convert to strcat(strcpy(malloc(1+strlen(a)+strlen(b)),a),b)
4992 var strcat = new CCodeFunctionCall (new CCodeIdentifier ("strcat"));
4993 var strcpy = new CCodeFunctionCall (new CCodeIdentifier ("strcpy"));
4994 var malloc = new CCodeFunctionCall (new CCodeIdentifier ("malloc"));
4996 var strlen_a = new CCodeFunctionCall (new CCodeIdentifier ("strlen"));
4997 strlen_a.add_argument(cleft);
4998 var strlen_b = new CCodeFunctionCall (new CCodeIdentifier ("strlen"));
4999 strlen_b.add_argument(cright);
5000 var newlength = new CCodeBinaryExpression (CCodeBinaryOperator.PLUS, new CCodeIdentifier("1"),
5001 new CCodeBinaryExpression (CCodeBinaryOperator.PLUS, strlen_a, strlen_b));
5002 malloc.add_argument(newlength);
5004 strcpy.add_argument(malloc);
5005 strcpy.add_argument(cleft);
5007 strcat.add_argument(strcpy);
5008 strcat.add_argument(cright);
5009 set_cvalue (expr, strcat);
5010 } else {
5011 // convert to g_strconcat (a, b, NULL)
5012 var temp_var = get_temp_variable (expr.value_type, true, null, false);
5013 var temp_ref = get_variable_cexpression (temp_var.name);
5014 emit_temp_var (temp_var);
5016 var ccall = new CCodeFunctionCall (new CCodeIdentifier ("g_strconcat"));
5017 ccall.add_argument (cleft);
5018 ccall.add_argument (cright);
5019 ccall.add_argument (new CCodeConstant("NULL"));
5021 ccode.add_assignment (temp_ref, ccall);
5022 set_cvalue (expr, temp_ref);
5024 return;
5026 } else if (expr.operator == BinaryOperator.EQUALITY
5027 || expr.operator == BinaryOperator.INEQUALITY
5028 || expr.operator == BinaryOperator.LESS_THAN
5029 || expr.operator == BinaryOperator.GREATER_THAN
5030 || expr.operator == BinaryOperator.LESS_THAN_OR_EQUAL
5031 || expr.operator == BinaryOperator.GREATER_THAN_OR_EQUAL) {
5032 var ccall = new CCodeFunctionCall (new CCodeIdentifier ("g_strcmp0"));
5033 ccall.add_argument (cleft);
5034 ccall.add_argument (cright);
5035 cleft = ccall;
5036 cright = new CCodeConstant ("0");
5040 set_cvalue (expr, new CCodeBinaryExpression (op, cleft, cright));
5041 if (left_chain != null) {
5042 set_cvalue (expr, new CCodeBinaryExpression (CCodeBinaryOperator.AND, left_chain, get_cvalue (expr)));
5046 public string? get_type_check_function (TypeSymbol type) {
5047 var cl = type as Class;
5048 if (cl != null && cl.type_check_function != null) {
5049 return cl.type_check_function;
5050 } else if ((cl != null && cl.is_compact) || type is Struct || type is Enum || type is Delegate) {
5051 return null;
5052 } else {
5053 return type.get_upper_case_cname ("IS_");
5057 CCodeExpression? create_type_check (CCodeNode ccodenode, DataType type) {
5058 var et = type as ErrorType;
5059 if (et != null && et.error_code != null) {
5060 var matches_call = new CCodeFunctionCall (new CCodeIdentifier ("g_error_matches"));
5061 matches_call.add_argument ((CCodeExpression) ccodenode);
5062 matches_call.add_argument (new CCodeIdentifier (et.error_domain.get_upper_case_cname ()));
5063 matches_call.add_argument (new CCodeIdentifier (et.error_code.get_cname ()));
5064 return matches_call;
5065 } else if (et != null && et.error_domain != null) {
5066 var instance_domain = new CCodeMemberAccess.pointer ((CCodeExpression) ccodenode, "domain");
5067 var type_domain = new CCodeIdentifier (et.error_domain.get_upper_case_cname ());
5068 return new CCodeBinaryExpression (CCodeBinaryOperator.EQUALITY, instance_domain, type_domain);
5069 } else {
5070 string type_check_func = get_type_check_function (type.data_type);
5071 if (type_check_func == null) {
5072 return new CCodeInvalidExpression ();
5074 var ccheck = new CCodeFunctionCall (new CCodeIdentifier (type_check_func));
5075 ccheck.add_argument ((CCodeExpression) ccodenode);
5076 return ccheck;
5080 string generate_array_contains_wrapper (ArrayType array_type) {
5081 string array_contains_func = "_vala_%s_array_contains".printf (array_type.element_type.get_lower_case_cname ());
5083 if (!add_wrapper (array_contains_func)) {
5084 return array_contains_func;
5087 var function = new CCodeFunction (array_contains_func, "gboolean");
5088 function.modifiers = CCodeModifiers.STATIC;
5090 function.add_parameter (new CCodeParameter ("stack", array_type.get_cname ()));
5091 function.add_parameter (new CCodeParameter ("stack_length", "int"));
5092 if (array_type.element_type is StructValueType) {
5093 function.add_parameter (new CCodeParameter ("needle", array_type.element_type.get_cname () + "*"));
5094 } else {
5095 function.add_parameter (new CCodeParameter ("needle", array_type.element_type.get_cname ()));
5098 push_function (function);
5100 ccode.add_declaration ("int", new CCodeVariableDeclarator ("i"));
5102 var cloop_initializer = new CCodeAssignment (new CCodeIdentifier ("i"), new CCodeConstant ("0"));
5103 var cloop_condition = new CCodeBinaryExpression (CCodeBinaryOperator.LESS_THAN, new CCodeIdentifier ("i"), new CCodeIdentifier ("stack_length"));
5104 var cloop_iterator = new CCodeUnaryExpression (CCodeUnaryOperator.POSTFIX_INCREMENT, new CCodeIdentifier ("i"));
5105 ccode.open_for (cloop_initializer, cloop_condition, cloop_iterator);
5107 var celement = new CCodeElementAccess (new CCodeIdentifier ("stack"), new CCodeIdentifier ("i"));
5108 var cneedle = new CCodeIdentifier ("needle");
5109 CCodeBinaryExpression cif_condition;
5110 if (array_type.element_type.compatible (string_type)) {
5111 var ccall = new CCodeFunctionCall (new CCodeIdentifier ("g_strcmp0"));
5112 ccall.add_argument (celement);
5113 ccall.add_argument (cneedle);
5114 cif_condition = new CCodeBinaryExpression (CCodeBinaryOperator.EQUALITY, ccall, new CCodeConstant ("0"));
5115 } else if (array_type.element_type is StructValueType) {
5116 var equalfunc = generate_struct_equal_function ((Struct) array_type.element_type.data_type as Struct);
5117 var ccall = new CCodeFunctionCall (new CCodeIdentifier (equalfunc));
5118 ccall.add_argument (new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, celement));
5119 ccall.add_argument (cneedle);
5120 cif_condition = new CCodeBinaryExpression (CCodeBinaryOperator.EQUALITY, ccall, new CCodeConstant ("TRUE"));
5121 } else {
5122 cif_condition = new CCodeBinaryExpression (CCodeBinaryOperator.EQUALITY, cneedle, celement);
5125 ccode.open_if (cif_condition);
5126 ccode.add_return (new CCodeConstant ("TRUE"));
5127 ccode.close ();
5129 ccode.close ();
5131 ccode.add_return (new CCodeConstant ("FALSE"));
5133 pop_function ();
5135 cfile.add_function_declaration (function);
5136 cfile.add_function (function);
5138 return array_contains_func;
5141 public override void visit_type_check (TypeCheck expr) {
5142 generate_type_declaration (expr.type_reference, cfile);
5144 set_cvalue (expr, create_type_check (get_cvalue (expr.expression), expr.type_reference));
5145 if (get_cvalue (expr) is CCodeInvalidExpression) {
5146 Report.error (expr.source_reference, "type check expressions not supported for compact classes, structs, and enums");
5150 public override void visit_lambda_expression (LambdaExpression lambda) {
5151 // use instance position from delegate
5152 var dt = (DelegateType) lambda.target_type;
5153 lambda.method.cinstance_parameter_position = dt.delegate_symbol.cinstance_parameter_position;
5155 lambda.accept_children (this);
5157 bool expr_owned = lambda.value_type.value_owned;
5159 set_cvalue (lambda, new CCodeIdentifier (lambda.method.get_cname ()));
5161 var delegate_type = (DelegateType) lambda.target_type;
5162 if (lambda.method.closure) {
5163 int block_id = get_block_id (current_closure_block);
5164 var delegate_target = get_variable_cexpression ("_data%d_".printf (block_id));
5165 if (expr_owned || delegate_type.is_called_once) {
5166 var ref_call = new CCodeFunctionCall (new CCodeIdentifier ("block%d_data_ref".printf (block_id)));
5167 ref_call.add_argument (delegate_target);
5168 delegate_target = ref_call;
5169 set_delegate_target_destroy_notify (lambda, new CCodeIdentifier ("block%d_data_unref".printf (block_id)));
5170 } else {
5171 set_delegate_target_destroy_notify (lambda, new CCodeConstant ("NULL"));
5173 set_delegate_target (lambda, delegate_target);
5174 } else if (get_this_type () != null || in_constructor) {
5175 CCodeExpression delegate_target = get_result_cexpression ("self");
5176 if (expr_owned || delegate_type.is_called_once) {
5177 if (get_this_type () != null) {
5178 var ref_call = new CCodeFunctionCall (get_dup_func_expression (get_this_type (), lambda.source_reference));
5179 ref_call.add_argument (delegate_target);
5180 delegate_target = ref_call;
5181 set_delegate_target_destroy_notify (lambda, get_destroy_func_expression (get_this_type ()));
5182 } else {
5183 // in constructor
5184 var ref_call = new CCodeFunctionCall (new CCodeIdentifier ("g_object_ref"));
5185 ref_call.add_argument (delegate_target);
5186 delegate_target = ref_call;
5187 set_delegate_target_destroy_notify (lambda, new CCodeIdentifier ("g_object_unref"));
5189 } else {
5190 set_delegate_target_destroy_notify (lambda, new CCodeConstant ("NULL"));
5192 set_delegate_target (lambda, delegate_target);
5193 } else {
5194 set_delegate_target (lambda, new CCodeConstant ("NULL"));
5195 set_delegate_target_destroy_notify (lambda, new CCodeConstant ("NULL"));
5199 public CCodeExpression convert_from_generic_pointer (CCodeExpression cexpr, DataType actual_type) {
5200 var result = cexpr;
5201 if (is_reference_type_argument (actual_type) || is_nullable_value_type_argument (actual_type)) {
5202 result = new CCodeCastExpression (cexpr, actual_type.get_cname ());
5203 } else if (is_signed_integer_type_argument (actual_type)) {
5204 var cconv = new CCodeFunctionCall (new CCodeIdentifier ("GPOINTER_TO_INT"));
5205 cconv.add_argument (cexpr);
5206 result = cconv;
5207 } else if (is_unsigned_integer_type_argument (actual_type)) {
5208 var cconv = new CCodeFunctionCall (new CCodeIdentifier ("GPOINTER_TO_UINT"));
5209 cconv.add_argument (cexpr);
5210 result = cconv;
5212 return result;
5215 public CCodeExpression convert_to_generic_pointer (CCodeExpression cexpr, DataType actual_type) {
5216 var result = cexpr;
5217 if (is_signed_integer_type_argument (actual_type)) {
5218 var cconv = new CCodeFunctionCall (new CCodeIdentifier ("GINT_TO_POINTER"));
5219 cconv.add_argument (cexpr);
5220 result = cconv;
5221 } else if (is_unsigned_integer_type_argument (actual_type)) {
5222 var cconv = new CCodeFunctionCall (new CCodeIdentifier ("GUINT_TO_POINTER"));
5223 cconv.add_argument (cexpr);
5224 result = cconv;
5226 return result;
5229 // manage memory and implicit casts
5230 public CCodeExpression transform_expression (CCodeExpression source_cexpr, DataType? expression_type, DataType? target_type, Expression? expr = null) {
5231 var cexpr = source_cexpr;
5232 if (expression_type == null) {
5233 return cexpr;
5237 if (expression_type.value_owned
5238 && expression_type.floating_reference) {
5239 /* floating reference, sink it.
5241 var cl = expression_type.data_type as ObjectTypeSymbol;
5242 var sink_func = (cl != null) ? cl.get_ref_sink_function () : null;
5244 if (sink_func != null) {
5245 var csink = new CCodeFunctionCall (new CCodeIdentifier (sink_func));
5246 csink.add_argument (cexpr);
5248 cexpr = csink;
5249 } else {
5250 Report.error (null, "type `%s' does not support floating references".printf (expression_type.data_type.name));
5254 bool boxing = (expression_type is ValueType && !expression_type.nullable
5255 && target_type is ValueType && target_type.nullable);
5256 bool unboxing = (expression_type is ValueType && expression_type.nullable
5257 && target_type is ValueType && !target_type.nullable);
5259 bool gvalue_boxing = (context.profile == Profile.GOBJECT
5260 && target_type != null
5261 && target_type.data_type == gvalue_type
5262 && !(expression_type is NullType)
5263 && expression_type.get_type_id () != "G_TYPE_VALUE");
5264 bool gvariant_boxing = (context.profile == Profile.GOBJECT
5265 && target_type != null
5266 && target_type.data_type == gvariant_type
5267 && !(expression_type is NullType)
5268 && expression_type.data_type != gvariant_type);
5270 if (expression_type.value_owned
5271 && (target_type == null || !target_type.value_owned || boxing || unboxing)
5272 && !gvalue_boxing /* gvalue can assume ownership of value, no need to free it */) {
5273 // value leaked, destroy it
5274 var pointer_type = target_type as PointerType;
5275 if (pointer_type != null && !(pointer_type.base_type is VoidType)) {
5276 // manual memory management for non-void pointers
5277 // treat void* special to not leak memory with void* method parameters
5278 } else if (requires_destroy (expression_type)) {
5279 var decl = get_temp_variable (expression_type, true, expression_type, false);
5280 emit_temp_var (decl);
5281 temp_ref_vars.insert (0, decl);
5282 ccode.add_assignment (get_variable_cexpression (decl.name), cexpr);
5283 cexpr = get_variable_cexpression (decl.name);
5285 if (expression_type is ArrayType && expr != null) {
5286 var array_type = (ArrayType) expression_type;
5287 for (int dim = 1; dim <= array_type.rank; dim++) {
5288 var len_decl = new LocalVariable (int_type.copy (), get_array_length_cname (decl.name, dim));
5289 emit_temp_var (len_decl);
5290 ccode.add_assignment (get_variable_cexpression (len_decl.name), get_array_length_cexpression (expr, dim));
5292 } else if (expression_type is DelegateType && expr != null) {
5293 var target_decl = new LocalVariable (new PointerType (new VoidType ()), get_delegate_target_cname (decl.name));
5294 emit_temp_var (target_decl);
5295 var target_destroy_notify_decl = new LocalVariable (gdestroynotify_type, get_delegate_target_destroy_notify_cname (decl.name));
5296 emit_temp_var (target_destroy_notify_decl);
5297 CCodeExpression target_destroy_notify;
5298 ccode.add_assignment (get_variable_cexpression (target_decl.name), get_delegate_target_cexpression (expr, out target_destroy_notify));
5299 ccode.add_assignment (get_variable_cexpression (target_destroy_notify_decl.name), target_destroy_notify);
5305 if (target_type == null) {
5306 // value will be destroyed, no need for implicit casts
5307 return cexpr;
5310 if (gvalue_boxing) {
5311 // implicit conversion to GValue
5312 var decl = get_temp_variable (target_type, true, target_type);
5313 emit_temp_var (decl);
5315 if (!target_type.value_owned) {
5316 // boxed GValue leaked, destroy it
5317 temp_ref_vars.insert (0, decl);
5320 if (target_type.nullable) {
5321 var newcall = new CCodeFunctionCall (new CCodeIdentifier ("g_new0"));
5322 newcall.add_argument (new CCodeConstant ("GValue"));
5323 newcall.add_argument (new CCodeConstant ("1"));
5324 var newassignment = new CCodeAssignment (get_variable_cexpression (decl.name), newcall);
5325 ccode.add_expression (newassignment);
5328 var ccall = new CCodeFunctionCall (new CCodeIdentifier ("g_value_init"));
5329 if (target_type.nullable) {
5330 ccall.add_argument (get_variable_cexpression (decl.name));
5331 } else {
5332 ccall.add_argument (new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, get_variable_cexpression (decl.name)));
5334 ccall.add_argument (new CCodeIdentifier (expression_type.get_type_id ()));
5335 ccode.add_expression (ccall);
5337 if (requires_destroy (expression_type)) {
5338 ccall = new CCodeFunctionCall (get_value_taker_function (expression_type));
5339 } else {
5340 ccall = new CCodeFunctionCall (get_value_setter_function (expression_type));
5342 if (target_type.nullable) {
5343 ccall.add_argument (get_variable_cexpression (decl.name));
5344 } else {
5345 ccall.add_argument (new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, get_variable_cexpression (decl.name)));
5347 if (expression_type.is_real_non_null_struct_type ()) {
5348 ccall.add_argument (new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, cexpr));
5349 } else {
5350 ccall.add_argument (cexpr);
5353 ccode.add_expression (ccall);
5355 cexpr = get_variable_cexpression (decl.name);
5357 return cexpr;
5358 } else if (gvariant_boxing) {
5359 // implicit conversion to GVariant
5360 string variant_func = "_variant_new%d".printf (++next_variant_function_id);
5362 var ccall = new CCodeFunctionCall (new CCodeIdentifier (variant_func));
5363 ccall.add_argument (cexpr);
5365 var cfunc = new CCodeFunction (variant_func, "GVariant*");
5366 cfunc.modifiers = CCodeModifiers.STATIC;
5367 cfunc.add_parameter (new CCodeParameter ("value", expression_type.get_cname ()));
5369 if (expression_type is ArrayType) {
5370 // return array length if appropriate
5371 var array_type = (ArrayType) expression_type;
5373 for (int dim = 1; dim <= array_type.rank; dim++) {
5374 ccall.add_argument (get_array_length_cexpression (expr, dim));
5375 cfunc.add_parameter (new CCodeParameter (get_array_length_cname ("value", dim), "gint"));
5379 push_function (cfunc);
5381 var result = serialize_expression (expression_type, new CCodeIdentifier ("value"));
5383 // sink floating reference
5384 var sink = new CCodeFunctionCall (new CCodeIdentifier ("g_variant_ref_sink"));
5385 sink.add_argument (result);
5386 ccode.add_return (sink);
5388 pop_function ();
5390 cfile.add_function_declaration (cfunc);
5391 cfile.add_function (cfunc);
5393 return ccall;
5394 } else if (boxing) {
5395 // value needs to be boxed
5397 var unary = cexpr as CCodeUnaryExpression;
5398 if (unary != null && unary.operator == CCodeUnaryOperator.POINTER_INDIRECTION) {
5399 // *expr => expr
5400 cexpr = unary.inner;
5401 } else if (cexpr is CCodeIdentifier || cexpr is CCodeMemberAccess) {
5402 cexpr = new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, cexpr);
5403 } else {
5404 var decl = get_temp_variable (expression_type, expression_type.value_owned, expression_type, false);
5405 emit_temp_var (decl);
5407 ccode.add_assignment (get_variable_cexpression (decl.name), cexpr);
5408 cexpr = new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, get_variable_cexpression (decl.name));
5410 } else if (unboxing) {
5411 // unbox value
5413 cexpr = new CCodeUnaryExpression (CCodeUnaryOperator.POINTER_INDIRECTION, cexpr);
5414 } else {
5415 cexpr = get_implicit_cast_expression (cexpr, expression_type, target_type, expr);
5418 if (target_type.value_owned && (!expression_type.value_owned || boxing || unboxing)) {
5419 // need to copy value
5420 if (requires_copy (target_type) && !(expression_type is NullType)) {
5421 CodeNode node = expr;
5422 if (node == null) {
5423 node = expression_type;
5426 var decl = get_temp_variable (target_type, true, node, false);
5427 emit_temp_var (decl);
5428 ccode.add_assignment (get_variable_cexpression (decl.name), get_ref_cexpression (target_type, cexpr, expr, node));
5429 cexpr = get_variable_cexpression (decl.name);
5433 return cexpr;
5436 public virtual CCodeExpression get_implicit_cast_expression (CCodeExpression source_cexpr, DataType? expression_type, DataType? target_type, Expression? expr = null) {
5437 var cexpr = source_cexpr;
5439 if (expression_type.data_type != null && expression_type.data_type == target_type.data_type) {
5440 // same type, no cast required
5441 return cexpr;
5444 if (expression_type is NullType) {
5445 // null literal, no cast required when not converting to generic type pointer
5446 return cexpr;
5449 generate_type_declaration (target_type, cfile);
5451 var cl = target_type.data_type as Class;
5452 var iface = target_type.data_type as Interface;
5453 if (context.checking && (iface != null || (cl != null && !cl.is_compact))) {
5454 // checked cast for strict subtypes of GTypeInstance
5455 return generate_instance_cast (cexpr, target_type.data_type);
5456 } else if (target_type.data_type != null && expression_type.get_cname () != target_type.get_cname ()) {
5457 var st = target_type.data_type as Struct;
5458 if (target_type.data_type.is_reference_type () || (st != null && st.is_simple_type ())) {
5459 // don't cast non-simple structs
5460 return new CCodeCastExpression (cexpr, target_type.get_cname ());
5461 } else {
5462 return cexpr;
5464 } else {
5465 return cexpr;
5469 public void store_property (Property prop, Expression? instance, TargetValue value) {
5470 if (instance is BaseAccess) {
5471 if (prop.base_property != null) {
5472 var base_class = (Class) prop.base_property.parent_symbol;
5473 var vcast = new CCodeFunctionCall (new CCodeIdentifier ("%s_CLASS".printf (base_class.get_upper_case_cname (null))));
5474 vcast.add_argument (new CCodeIdentifier ("%s_parent_class".printf (current_class.get_lower_case_cname (null))));
5476 var ccall = new CCodeFunctionCall (new CCodeMemberAccess.pointer (vcast, "set_%s".printf (prop.name)));
5477 ccall.add_argument ((CCodeExpression) get_ccodenode (instance));
5478 ccall.add_argument (get_cvalue_ (value));
5480 ccode.add_expression (ccall);
5481 } else if (prop.base_interface_property != null) {
5482 var base_iface = (Interface) prop.base_interface_property.parent_symbol;
5483 string parent_iface_var = "%s_%s_parent_iface".printf (current_class.get_lower_case_cname (null), base_iface.get_lower_case_cname (null));
5485 var ccall = new CCodeFunctionCall (new CCodeMemberAccess.pointer (new CCodeIdentifier (parent_iface_var), "set_%s".printf (prop.name)));
5486 ccall.add_argument ((CCodeExpression) get_ccodenode (instance));
5487 ccall.add_argument (get_cvalue_ (value));
5489 ccode.add_expression (ccall);
5491 return;
5494 var set_func = "g_object_set";
5496 var base_property = prop;
5497 if (!prop.no_accessor_method) {
5498 if (prop.base_property != null) {
5499 base_property = prop.base_property;
5500 } else if (prop.base_interface_property != null) {
5501 base_property = prop.base_interface_property;
5504 if (prop is DynamicProperty) {
5505 set_func = get_dynamic_property_setter_cname ((DynamicProperty) prop);
5506 } else {
5507 generate_property_accessor_declaration (base_property.set_accessor, cfile);
5508 set_func = base_property.set_accessor.get_cname ();
5510 if (!prop.external && prop.external_package) {
5511 // internal VAPI properties
5512 // only add them once per source file
5513 if (add_generated_external_symbol (prop)) {
5514 visit_property (prop);
5520 var ccall = new CCodeFunctionCall (new CCodeIdentifier (set_func));
5522 if (prop.binding == MemberBinding.INSTANCE) {
5523 /* target instance is first argument */
5524 var cinstance = (CCodeExpression) get_ccodenode (instance);
5526 if (prop.parent_symbol is Struct) {
5527 // we need to pass struct instance by reference
5528 var unary = cinstance as CCodeUnaryExpression;
5529 if (unary != null && unary.operator == CCodeUnaryOperator.POINTER_INDIRECTION) {
5530 // *expr => expr
5531 cinstance = unary.inner;
5532 } else if (cinstance is CCodeIdentifier || cinstance is CCodeMemberAccess) {
5533 cinstance = new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, cinstance);
5534 } else {
5535 // if instance is e.g. a function call, we can't take the address of the expression
5536 // (tmp = expr, &tmp)
5538 var temp_var = get_temp_variable (instance.target_type, true, null, false);
5539 emit_temp_var (temp_var);
5540 ccode.add_assignment (get_variable_cexpression (temp_var.name), cinstance);
5542 cinstance = new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, get_variable_cexpression (temp_var.name));
5546 ccall.add_argument (cinstance);
5549 if (prop.no_accessor_method) {
5550 /* property name is second argument of g_object_set */
5551 ccall.add_argument (prop.get_canonical_cconstant ());
5554 var cexpr = get_cvalue_ (value);
5556 if (prop.property_type.is_real_non_null_struct_type ()) {
5557 cexpr = new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, cexpr);
5560 var array_type = prop.property_type as ArrayType;
5562 if (array_type != null && !prop.no_array_length) {
5563 var temp_var = get_temp_variable (prop.property_type, true, null, false);
5564 emit_temp_var (temp_var);
5565 ccode.add_assignment (get_variable_cexpression (temp_var.name), cexpr);
5566 ccall.add_argument (get_variable_cexpression (temp_var.name));
5567 } else {
5568 ccall.add_argument (cexpr);
5571 if (array_type != null && !prop.no_array_length) {
5572 for (int dim = 1; dim <= array_type.rank; dim++) {
5573 ccall.add_argument (get_array_length_cvalue (value, dim));
5575 } else if (prop.property_type is DelegateType) {
5576 var delegate_type = (DelegateType) prop.property_type;
5577 if (delegate_type.delegate_symbol.has_target) {
5578 ccall.add_argument (get_delegate_target_cvalue (value));
5582 if (prop.no_accessor_method) {
5583 ccall.add_argument (new CCodeConstant ("NULL"));
5586 ccode.add_expression (ccall);
5589 /* indicates whether a given Expression eligable for an ADDRESS_OF operator
5590 * from a vala to C point of view all expressions denoting locals, fields and
5591 * parameters are eligable to an ADDRESS_OF operator */
5592 public bool is_address_of_possible (Expression e) {
5593 if (gvalue_type != null && e.target_type.data_type == gvalue_type && e.value_type.data_type != gvalue_type) {
5594 // implicit conversion to GValue is not addressable
5595 return false;
5598 var ma = e as MemberAccess;
5600 if (ma == null) {
5601 return false;
5604 return (ma.symbol_reference is Variable);
5607 /* retrieve the correct address_of expression for a give expression, creates temporary variables
5608 * where necessary, ce is the corresponding ccode expression for e */
5609 public CCodeExpression get_address_of_expression (Expression e, CCodeExpression ce) {
5610 // is address of trivially possible?
5611 if (is_address_of_possible (e)) {
5612 return new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, ce);
5615 var ccomma = new CCodeCommaExpression ();
5616 DataType address_of_type;
5617 if (gvalue_type != null && e.target_type != null && e.target_type.data_type == gvalue_type) {
5618 // implicit conversion to GValue
5619 address_of_type = e.target_type;
5620 } else {
5621 address_of_type = e.value_type;
5623 var temp_decl = get_temp_variable (address_of_type, true, null, false);
5624 var ctemp = get_variable_cexpression (temp_decl.name);
5625 emit_temp_var (temp_decl);
5626 ccomma.append_expression (new CCodeAssignment (ctemp, ce));
5627 ccomma.append_expression (new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, ctemp));
5628 return ccomma;
5631 public bool add_wrapper (string wrapper_name) {
5632 return wrappers.add (wrapper_name);
5635 public bool add_generated_external_symbol (Symbol external_symbol) {
5636 return generated_external_symbols.add (external_symbol);
5639 public static DataType get_data_type_for_symbol (TypeSymbol sym) {
5640 DataType type = null;
5642 if (sym is Class) {
5643 type = new ObjectType ((Class) sym);
5644 } else if (sym is Interface) {
5645 type = new ObjectType ((Interface) sym);
5646 } else if (sym is Struct) {
5647 var st = (Struct) sym;
5648 if (st.is_boolean_type ()) {
5649 type = new BooleanType (st);
5650 } else if (st.is_integer_type ()) {
5651 type = new IntegerType (st);
5652 } else if (st.is_floating_type ()) {
5653 type = new FloatingType (st);
5654 } else {
5655 type = new StructValueType (st);
5657 } else if (sym is Enum) {
5658 type = new EnumValueType ((Enum) sym);
5659 } else if (sym is ErrorDomain) {
5660 type = new ErrorType ((ErrorDomain) sym, null);
5661 } else if (sym is ErrorCode) {
5662 type = new ErrorType ((ErrorDomain) sym.parent_symbol, (ErrorCode) sym);
5663 } else {
5664 Report.error (null, "internal error: `%s' is not a supported type".printf (sym.get_full_name ()));
5665 return new InvalidType ();
5668 return type;
5671 public CCodeExpression? default_value_for_type (DataType type, bool initializer_expression) {
5672 var st = type.data_type as Struct;
5673 var array_type = type as ArrayType;
5674 if (initializer_expression && !type.nullable &&
5675 ((st != null && !st.is_simple_type ()) ||
5676 (array_type != null && array_type.fixed_length))) {
5677 // 0-initialize struct with struct initializer { 0 }
5678 // only allowed as initializer expression in C
5679 var clist = new CCodeInitializerList ();
5680 clist.append (new CCodeConstant ("0"));
5681 return clist;
5682 } else if ((type.data_type != null && type.data_type.is_reference_type ())
5683 || type.nullable
5684 || type is PointerType || type is DelegateType
5685 || (array_type != null && !array_type.fixed_length)) {
5686 return new CCodeConstant ("NULL");
5687 } else if (type.data_type != null && type.data_type.get_default_value () != null) {
5688 return new CCodeConstant (type.data_type.get_default_value ());
5689 } else if (type.type_parameter != null) {
5690 return new CCodeConstant ("NULL");
5691 } else if (type is ErrorType) {
5692 return new CCodeConstant ("NULL");
5694 return null;
5697 private void create_property_type_check_statement (Property prop, bool check_return_type, TypeSymbol t, bool non_null, string var_name) {
5698 if (check_return_type) {
5699 create_type_check_statement (prop, prop.property_type, t, non_null, var_name);
5700 } else {
5701 create_type_check_statement (prop, new VoidType (), t, non_null, var_name);
5705 public void create_type_check_statement (CodeNode method_node, DataType ret_type, TypeSymbol t, bool non_null, string var_name) {
5706 var ccheck = new CCodeFunctionCall ();
5708 if (!context.assert) {
5709 return;
5710 } else if (context.checking && ((t is Class && !((Class) t).is_compact) || t is Interface)) {
5711 var ctype_check = new CCodeFunctionCall (new CCodeIdentifier (get_type_check_function (t)));
5712 ctype_check.add_argument (new CCodeIdentifier (var_name));
5714 CCodeExpression cexpr = ctype_check;
5715 if (!non_null) {
5716 var cnull = new CCodeBinaryExpression (CCodeBinaryOperator.EQUALITY, new CCodeIdentifier (var_name), new CCodeConstant ("NULL"));
5718 cexpr = new CCodeBinaryExpression (CCodeBinaryOperator.OR, cnull, ctype_check);
5720 ccheck.add_argument (cexpr);
5721 } else if (!non_null) {
5722 return;
5723 } else if (t == glist_type || t == gslist_type) {
5724 // NULL is empty list
5725 return;
5726 } else {
5727 var cnonnull = new CCodeBinaryExpression (CCodeBinaryOperator.INEQUALITY, new CCodeIdentifier (var_name), new CCodeConstant ("NULL"));
5728 ccheck.add_argument (cnonnull);
5731 var cm = method_node as CreationMethod;
5732 if (cm != null && cm.parent_symbol is ObjectTypeSymbol) {
5733 ccheck.call = new CCodeIdentifier ("g_return_val_if_fail");
5734 ccheck.add_argument (new CCodeConstant ("NULL"));
5735 } else if (ret_type is VoidType) {
5736 /* void function */
5737 ccheck.call = new CCodeIdentifier ("g_return_if_fail");
5738 } else {
5739 ccheck.call = new CCodeIdentifier ("g_return_val_if_fail");
5741 var cdefault = default_value_for_type (ret_type, false);
5742 if (cdefault != null) {
5743 ccheck.add_argument (cdefault);
5744 } else {
5745 return;
5749 ccode.add_expression (ccheck);
5752 public int get_param_pos (double param_pos, bool ellipsis = false) {
5753 if (!ellipsis) {
5754 if (param_pos >= 0) {
5755 return (int) (param_pos * 1000);
5756 } else {
5757 return (int) ((100 + param_pos) * 1000);
5759 } else {
5760 if (param_pos >= 0) {
5761 return (int) ((100 + param_pos) * 1000);
5762 } else {
5763 return (int) ((200 + param_pos) * 1000);
5768 public CCodeExpression? get_ccodenode (Expression node) {
5769 if (get_cvalue (node) == null) {
5770 node.emit (this);
5772 return get_cvalue (node);
5775 public override void visit_class (Class cl) {
5778 public void create_postcondition_statement (Expression postcondition) {
5779 var cassert = new CCodeFunctionCall (new CCodeIdentifier ("g_warn_if_fail"));
5781 postcondition.emit (this);
5783 cassert.add_argument (get_cvalue (postcondition));
5785 ccode.add_expression (cassert);
5788 public virtual bool is_gobject_property (Property prop) {
5789 return false;
5792 public DataType? get_this_type () {
5793 if (current_method != null && current_method.binding == MemberBinding.INSTANCE) {
5794 return current_method.this_parameter.variable_type;
5795 } else if (current_property_accessor != null && current_property_accessor.prop.binding == MemberBinding.INSTANCE) {
5796 return current_property_accessor.prop.this_parameter.variable_type;
5798 return null;
5801 public CCodeFunctionCall generate_instance_cast (CCodeExpression expr, TypeSymbol type) {
5802 var result = new CCodeFunctionCall (new CCodeIdentifier (type.get_upper_case_cname (null)));
5803 result.add_argument (expr);
5804 return result;
5807 void generate_struct_destroy_function (Struct st) {
5808 if (cfile.add_declaration (st.get_destroy_function ())) {
5809 // only generate function once per source file
5810 return;
5813 var function = new CCodeFunction (st.get_destroy_function (), "void");
5814 function.modifiers = CCodeModifiers.STATIC;
5815 function.add_parameter (new CCodeParameter ("self", st.get_cname () + "*"));
5817 push_function (function);
5819 foreach (Field f in st.get_fields ()) {
5820 if (f.binding == MemberBinding.INSTANCE) {
5821 if (requires_destroy (f.variable_type)) {
5822 var lhs = new CCodeMemberAccess.pointer (new CCodeIdentifier ("self"), f.get_cname ());
5824 var this_access = new MemberAccess.simple ("this");
5825 this_access.value_type = get_data_type_for_symbol ((TypeSymbol) f.parent_symbol);
5826 set_cvalue (this_access, new CCodeIdentifier ("(*self)"));
5828 var ma = new MemberAccess (this_access, f.name);
5829 ma.symbol_reference = f;
5830 ma.value_type = f.variable_type.copy ();
5831 visit_member_access (ma);
5832 ccode.add_expression (get_unref_expression (lhs, f.variable_type, ma));
5837 pop_function ();
5839 cfile.add_function_declaration (function);
5840 cfile.add_function (function);
5843 void generate_struct_copy_function (Struct st) {
5844 if (cfile.add_declaration (st.get_copy_function ())) {
5845 // only generate function once per source file
5846 return;
5849 var function = new CCodeFunction (st.get_copy_function (), "void");
5850 function.modifiers = CCodeModifiers.STATIC;
5851 function.add_parameter (new CCodeParameter ("self", "const " + st.get_cname () + "*"));
5852 function.add_parameter (new CCodeParameter ("dest", st.get_cname () + "*"));
5854 push_context (new EmitContext ());
5855 push_function (function);
5857 foreach (Field f in st.get_fields ()) {
5858 if (f.binding == MemberBinding.INSTANCE) {
5859 CCodeExpression copy = new CCodeMemberAccess.pointer (new CCodeIdentifier ("self"), f.name);
5860 if (requires_copy (f.variable_type)) {
5861 var this_access = new MemberAccess.simple ("this");
5862 this_access.value_type = get_data_type_for_symbol ((TypeSymbol) f.parent_symbol);
5863 set_cvalue (this_access, new CCodeIdentifier ("(*self)"));
5864 var ma = new MemberAccess (this_access, f.name);
5865 ma.symbol_reference = f;
5866 ma.value_type = f.variable_type.copy ();
5867 visit_member_access (ma);
5868 copy = get_ref_cexpression (f.variable_type, copy, ma, f);
5870 var dest = new CCodeMemberAccess.pointer (new CCodeIdentifier ("dest"), f.name);
5872 var array_type = f.variable_type as ArrayType;
5873 if (array_type != null && array_type.fixed_length) {
5874 // fixed-length (stack-allocated) arrays
5875 cfile.add_include ("string.h");
5877 var sizeof_call = new CCodeFunctionCall (new CCodeIdentifier ("sizeof"));
5878 sizeof_call.add_argument (new CCodeIdentifier (array_type.element_type.get_cname ()));
5879 var size = new CCodeBinaryExpression (CCodeBinaryOperator.MUL, new CCodeConstant ("%d".printf (array_type.length)), sizeof_call);
5881 var array_copy_call = new CCodeFunctionCall (new CCodeIdentifier ("memcpy"));
5882 array_copy_call.add_argument (dest);
5883 array_copy_call.add_argument (copy);
5884 array_copy_call.add_argument (size);
5885 ccode.add_expression (array_copy_call);
5886 } else {
5887 ccode.add_assignment (dest, copy);
5889 if (array_type != null && !f.no_array_length) {
5890 for (int dim = 1; dim <= array_type.rank; dim++) {
5891 var len_src = new CCodeMemberAccess.pointer (new CCodeIdentifier ("self"), get_array_length_cname (f.name, dim));
5892 var len_dest = new CCodeMemberAccess.pointer (new CCodeIdentifier ("dest"), get_array_length_cname (f.name, dim));
5893 ccode.add_assignment (len_dest, len_src);
5900 pop_function ();
5901 pop_context ();
5903 cfile.add_function_declaration (function);
5904 cfile.add_function (function);
5907 public void return_default_value (DataType return_type) {
5908 ccode.add_return (default_value_for_type (return_type, false));
5911 public virtual string? get_custom_creturn_type (Method m) {
5912 return null;
5915 public virtual void generate_dynamic_method_wrapper (DynamicMethod method) {
5918 public virtual bool method_has_wrapper (Method method) {
5919 return false;
5922 public virtual CCodeFunctionCall get_param_spec (Property prop) {
5923 return new CCodeFunctionCall (new CCodeIdentifier (""));
5926 public virtual CCodeFunctionCall get_signal_creation (Signal sig, TypeSymbol type) {
5927 return new CCodeFunctionCall (new CCodeIdentifier (""));
5930 public virtual void register_dbus_info (CCodeBlock block, ObjectTypeSymbol bindable) {
5933 public virtual string get_dynamic_property_getter_cname (DynamicProperty node) {
5934 Report.error (node.source_reference, "dynamic properties are not supported for %s".printf (node.dynamic_type.to_string ()));
5935 return "";
5938 public virtual string get_dynamic_property_setter_cname (DynamicProperty node) {
5939 Report.error (node.source_reference, "dynamic properties are not supported for %s".printf (node.dynamic_type.to_string ()));
5940 return "";
5943 public virtual string get_dynamic_signal_cname (DynamicSignal node) {
5944 return "";
5947 public virtual string get_dynamic_signal_connect_wrapper_name (DynamicSignal node) {
5948 return "";
5951 public virtual string get_dynamic_signal_connect_after_wrapper_name (DynamicSignal node) {
5952 return "";
5955 public virtual string get_dynamic_signal_disconnect_wrapper_name (DynamicSignal node) {
5956 return "";
5959 public virtual void generate_marshaller (List<Parameter> params, DataType return_type, bool dbus = false) {
5962 public virtual string get_marshaller_function (List<Parameter> params, DataType return_type, string? prefix = null, bool dbus = false) {
5963 return "";
5966 public virtual string get_array_length_cname (string array_cname, int dim) {
5967 return "";
5970 public virtual string get_parameter_array_length_cname (Parameter param, int dim) {
5971 return "";
5974 public virtual CCodeExpression get_array_length_cexpression (Expression array_expr, int dim = -1) {
5975 return new CCodeConstant ("");
5978 public virtual CCodeExpression get_array_length_cvalue (TargetValue value, int dim = -1) {
5979 return new CCodeInvalidExpression ();
5982 public virtual string get_array_size_cname (string array_cname) {
5983 return "";
5986 public virtual void add_simple_check (CodeNode node, bool always_fails = false) {
5989 public virtual string generate_ready_function (Method m) {
5990 return "";
5993 public CCodeExpression? get_cvalue (Expression expr) {
5994 if (expr.target_value == null) {
5995 return null;
5997 var glib_value = (GLibValue) expr.target_value;
5998 return glib_value.cvalue;
6001 public CCodeExpression? get_cvalue_ (TargetValue value) {
6002 var glib_value = (GLibValue) value;
6003 return glib_value.cvalue;
6006 public void set_cvalue (Expression expr, CCodeExpression? cvalue) {
6007 var glib_value = (GLibValue) expr.target_value;
6008 if (glib_value == null) {
6009 glib_value = new GLibValue (expr.value_type);
6010 expr.target_value = glib_value;
6012 glib_value.cvalue = cvalue;
6015 public CCodeExpression? get_array_size_cvalue (TargetValue value) {
6016 var glib_value = (GLibValue) value;
6017 return glib_value.array_size_cvalue;
6020 public void set_array_size_cvalue (TargetValue value, CCodeExpression? cvalue) {
6021 var glib_value = (GLibValue) value;
6022 glib_value.array_size_cvalue = cvalue;
6025 public CCodeExpression? get_delegate_target (Expression expr) {
6026 if (expr.target_value == null) {
6027 return null;
6029 var glib_value = (GLibValue) expr.target_value;
6030 return glib_value.delegate_target_cvalue;
6033 public void set_delegate_target (Expression expr, CCodeExpression? delegate_target) {
6034 var glib_value = (GLibValue) expr.target_value;
6035 if (glib_value == null) {
6036 glib_value = new GLibValue (expr.value_type);
6037 expr.target_value = glib_value;
6039 glib_value.delegate_target_cvalue = delegate_target;
6042 public CCodeExpression? get_delegate_target_destroy_notify (Expression expr) {
6043 if (expr.target_value == null) {
6044 return null;
6046 var glib_value = (GLibValue) expr.target_value;
6047 return glib_value.delegate_target_destroy_notify_cvalue;
6050 public void set_delegate_target_destroy_notify (Expression expr, CCodeExpression? destroy_notify) {
6051 var glib_value = (GLibValue) expr.target_value;
6052 if (glib_value == null) {
6053 glib_value = new GLibValue (expr.value_type);
6054 expr.target_value = glib_value;
6056 glib_value.delegate_target_destroy_notify_cvalue = destroy_notify;
6059 public void append_array_length (Expression expr, CCodeExpression size) {
6060 var glib_value = (GLibValue) expr.target_value;
6061 if (glib_value == null) {
6062 glib_value = new GLibValue (expr.value_type);
6063 expr.target_value = glib_value;
6065 glib_value.append_array_length_cvalue (size);
6068 public List<CCodeExpression>? get_array_lengths (Expression expr) {
6069 var glib_value = (GLibValue) expr.target_value;
6070 if (glib_value == null) {
6071 glib_value = new GLibValue (expr.value_type);
6072 expr.target_value = glib_value;
6074 return glib_value.array_length_cvalues;
6078 public class Vala.GLibValue : TargetValue {
6079 public CCodeExpression cvalue;
6081 public List<CCodeExpression> array_length_cvalues;
6082 public CCodeExpression? array_size_cvalue;
6084 public CCodeExpression? delegate_target_cvalue;
6085 public CCodeExpression? delegate_target_destroy_notify_cvalue;
6087 public GLibValue (DataType? value_type = null, CCodeExpression? cvalue = null) {
6088 base (value_type);
6089 this.cvalue = cvalue;
6092 public void append_array_length_cvalue (CCodeExpression length_cvalue) {
6093 if (array_length_cvalues == null) {
6094 array_length_cvalues = new ArrayList<CCodeExpression> ();
6096 array_length_cvalues.add (length_cvalue);