codegen: Fix use of property assignments as subexpressions
[vala-lang.git] / codegen / valaccodebasemodule.vala
blob764815cc60803e4fdee617fb32c5120ca2dd6170
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 while (true) {
181 unowned Method method = sym as Method;
182 if (method != null && !method.closure) {
183 // parent blocks are not captured by this method
184 break;
187 unowned Block block = sym as Block;
188 if (method == null && block == null) {
189 // no closure block
190 break;
193 if (block != null && block.captured) {
194 // closure block found
195 return block;
197 sym = sym.parent_symbol;
199 return null;
202 public CCodeFile header_file;
203 public CCodeFile internal_header_file;
204 public CCodeFile cfile;
206 public EmitContext class_init_context;
207 public EmitContext base_init_context;
208 public EmitContext class_finalize_context;
209 public EmitContext base_finalize_context;
210 public EmitContext instance_init_context;
211 public EmitContext instance_finalize_context;
213 public CCodeStruct param_spec_struct;
214 public CCodeStruct closure_struct;
215 public CCodeEnum prop_enum;
217 public CCodeFunction ccode { get { return emit_context.ccode; } }
219 /* temporary variables that own their content */
220 public ArrayList<LocalVariable> temp_ref_vars { get { return emit_context.temp_ref_vars; } }
221 /* cache to check whether a certain marshaller has been created yet */
222 public Set<string> user_marshal_set;
223 /* (constant) hash table with all predefined marshallers */
224 public Set<string> predefined_marshal_set;
225 /* (constant) hash table with all reserved identifiers in the generated code */
226 Set<string> reserved_identifiers;
228 public int next_temp_var_id {
229 get { return emit_context.next_temp_var_id; }
230 set { emit_context.next_temp_var_id = value; }
233 public int next_regex_id = 0;
234 public bool in_creation_method { get { return current_method is CreationMethod; } }
235 public bool in_constructor = false;
236 public bool in_static_or_class_context = false;
238 public bool current_method_inner_error {
239 get { return emit_context.current_method_inner_error; }
240 set { emit_context.current_method_inner_error = value; }
243 public bool current_method_return {
244 get { return emit_context.current_method_return; }
245 set { emit_context.current_method_return = value; }
248 public int next_coroutine_state = 1;
249 int next_block_id = 0;
250 Map<Block,int> block_map = new HashMap<Block,int> ();
252 public DataType void_type = new VoidType ();
253 public DataType bool_type;
254 public DataType char_type;
255 public DataType uchar_type;
256 public DataType? unichar_type;
257 public DataType short_type;
258 public DataType ushort_type;
259 public DataType int_type;
260 public DataType uint_type;
261 public DataType long_type;
262 public DataType ulong_type;
263 public DataType int8_type;
264 public DataType uint8_type;
265 public DataType int16_type;
266 public DataType uint16_type;
267 public DataType int32_type;
268 public DataType uint32_type;
269 public DataType int64_type;
270 public DataType uint64_type;
271 public DataType string_type;
272 public DataType regex_type;
273 public DataType float_type;
274 public DataType double_type;
275 public TypeSymbol gtype_type;
276 public TypeSymbol gobject_type;
277 public ErrorType gerror_type;
278 public Class glist_type;
279 public Class gslist_type;
280 public Class gnode_type;
281 public Class gvaluearray_type;
282 public TypeSymbol gstringbuilder_type;
283 public TypeSymbol garray_type;
284 public TypeSymbol gbytearray_type;
285 public TypeSymbol gptrarray_type;
286 public TypeSymbol gthreadpool_type;
287 public DataType gdestroynotify_type;
288 public DataType gquark_type;
289 public Struct gvalue_type;
290 public Class gvariant_type;
291 public Struct mutex_type;
292 public TypeSymbol type_module_type;
293 public TypeSymbol dbus_proxy_type;
294 public TypeSymbol dbus_object_type;
296 public bool in_plugin = false;
297 public string module_init_param_name;
299 public bool gvaluecollector_h_needed;
300 public bool requires_array_free;
301 public bool requires_array_move;
302 public bool requires_array_length;
304 public Set<string> wrappers;
305 Set<Symbol> generated_external_symbols;
307 public Map<string,string> variable_name_map { get { return emit_context.variable_name_map; } }
309 public CCodeBaseModule () {
310 predefined_marshal_set = new HashSet<string> (str_hash, str_equal);
311 predefined_marshal_set.add ("VOID:VOID");
312 predefined_marshal_set.add ("VOID:BOOLEAN");
313 predefined_marshal_set.add ("VOID:CHAR");
314 predefined_marshal_set.add ("VOID:UCHAR");
315 predefined_marshal_set.add ("VOID:INT");
316 predefined_marshal_set.add ("VOID:UINT");
317 predefined_marshal_set.add ("VOID:LONG");
318 predefined_marshal_set.add ("VOID:ULONG");
319 predefined_marshal_set.add ("VOID:ENUM");
320 predefined_marshal_set.add ("VOID:FLAGS");
321 predefined_marshal_set.add ("VOID:FLOAT");
322 predefined_marshal_set.add ("VOID:DOUBLE");
323 predefined_marshal_set.add ("VOID:STRING");
324 predefined_marshal_set.add ("VOID:POINTER");
325 predefined_marshal_set.add ("VOID:OBJECT");
326 predefined_marshal_set.add ("STRING:OBJECT,POINTER");
327 predefined_marshal_set.add ("VOID:UINT,POINTER");
328 predefined_marshal_set.add ("BOOLEAN:FLAGS");
330 reserved_identifiers = new HashSet<string> (str_hash, str_equal);
332 // C99 keywords
333 reserved_identifiers.add ("_Bool");
334 reserved_identifiers.add ("_Complex");
335 reserved_identifiers.add ("_Imaginary");
336 reserved_identifiers.add ("asm");
337 reserved_identifiers.add ("auto");
338 reserved_identifiers.add ("break");
339 reserved_identifiers.add ("case");
340 reserved_identifiers.add ("char");
341 reserved_identifiers.add ("const");
342 reserved_identifiers.add ("continue");
343 reserved_identifiers.add ("default");
344 reserved_identifiers.add ("do");
345 reserved_identifiers.add ("double");
346 reserved_identifiers.add ("else");
347 reserved_identifiers.add ("enum");
348 reserved_identifiers.add ("extern");
349 reserved_identifiers.add ("float");
350 reserved_identifiers.add ("for");
351 reserved_identifiers.add ("goto");
352 reserved_identifiers.add ("if");
353 reserved_identifiers.add ("inline");
354 reserved_identifiers.add ("int");
355 reserved_identifiers.add ("long");
356 reserved_identifiers.add ("register");
357 reserved_identifiers.add ("restrict");
358 reserved_identifiers.add ("return");
359 reserved_identifiers.add ("short");
360 reserved_identifiers.add ("signed");
361 reserved_identifiers.add ("sizeof");
362 reserved_identifiers.add ("static");
363 reserved_identifiers.add ("struct");
364 reserved_identifiers.add ("switch");
365 reserved_identifiers.add ("typedef");
366 reserved_identifiers.add ("union");
367 reserved_identifiers.add ("unsigned");
368 reserved_identifiers.add ("void");
369 reserved_identifiers.add ("volatile");
370 reserved_identifiers.add ("while");
372 // MSVC keywords
373 reserved_identifiers.add ("cdecl");
375 // reserved for Vala/GObject naming conventions
376 reserved_identifiers.add ("error");
377 reserved_identifiers.add ("result");
378 reserved_identifiers.add ("self");
381 public override void emit (CodeContext context) {
382 this.context = context;
384 root_symbol = context.root;
386 bool_type = new BooleanType ((Struct) root_symbol.scope.lookup ("bool"));
387 char_type = new IntegerType ((Struct) root_symbol.scope.lookup ("char"));
388 uchar_type = new IntegerType ((Struct) root_symbol.scope.lookup ("uchar"));
389 short_type = new IntegerType ((Struct) root_symbol.scope.lookup ("short"));
390 ushort_type = new IntegerType ((Struct) root_symbol.scope.lookup ("ushort"));
391 int_type = new IntegerType ((Struct) root_symbol.scope.lookup ("int"));
392 uint_type = new IntegerType ((Struct) root_symbol.scope.lookup ("uint"));
393 long_type = new IntegerType ((Struct) root_symbol.scope.lookup ("long"));
394 ulong_type = new IntegerType ((Struct) root_symbol.scope.lookup ("ulong"));
395 int8_type = new IntegerType ((Struct) root_symbol.scope.lookup ("int8"));
396 uint8_type = new IntegerType ((Struct) root_symbol.scope.lookup ("uint8"));
397 int16_type = new IntegerType ((Struct) root_symbol.scope.lookup ("int16"));
398 uint16_type = new IntegerType ((Struct) root_symbol.scope.lookup ("uint16"));
399 int32_type = new IntegerType ((Struct) root_symbol.scope.lookup ("int32"));
400 uint32_type = new IntegerType ((Struct) root_symbol.scope.lookup ("uint32"));
401 int64_type = new IntegerType ((Struct) root_symbol.scope.lookup ("int64"));
402 uint64_type = new IntegerType ((Struct) root_symbol.scope.lookup ("uint64"));
403 float_type = new FloatingType ((Struct) root_symbol.scope.lookup ("float"));
404 double_type = new FloatingType ((Struct) root_symbol.scope.lookup ("double"));
405 string_type = new ObjectType ((Class) root_symbol.scope.lookup ("string"));
406 var unichar_struct = (Struct) root_symbol.scope.lookup ("unichar");
407 if (unichar_struct != null) {
408 unichar_type = new IntegerType (unichar_struct);
411 if (context.profile == Profile.GOBJECT) {
412 var glib_ns = root_symbol.scope.lookup ("GLib");
414 gtype_type = (TypeSymbol) glib_ns.scope.lookup ("Type");
415 gobject_type = (TypeSymbol) glib_ns.scope.lookup ("Object");
416 gerror_type = new ErrorType (null, null);
417 glist_type = (Class) glib_ns.scope.lookup ("List");
418 gslist_type = (Class) glib_ns.scope.lookup ("SList");
419 gnode_type = (Class) glib_ns.scope.lookup ("Node");
420 gvaluearray_type = (Class) glib_ns.scope.lookup ("ValueArray");
421 gstringbuilder_type = (TypeSymbol) glib_ns.scope.lookup ("StringBuilder");
422 garray_type = (TypeSymbol) glib_ns.scope.lookup ("Array");
423 gbytearray_type = (TypeSymbol) glib_ns.scope.lookup ("ByteArray");
424 gptrarray_type = (TypeSymbol) glib_ns.scope.lookup ("PtrArray");
425 gthreadpool_type = (TypeSymbol) glib_ns.scope.lookup ("ThreadPool");
426 gdestroynotify_type = new DelegateType ((Delegate) glib_ns.scope.lookup ("DestroyNotify"));
428 gquark_type = new IntegerType ((Struct) glib_ns.scope.lookup ("Quark"));
429 gvalue_type = (Struct) glib_ns.scope.lookup ("Value");
430 gvariant_type = (Class) glib_ns.scope.lookup ("Variant");
431 mutex_type = (Struct) glib_ns.scope.lookup ("StaticRecMutex");
433 type_module_type = (TypeSymbol) glib_ns.scope.lookup ("TypeModule");
435 regex_type = new ObjectType ((Class) root_symbol.scope.lookup ("GLib").scope.lookup ("Regex"));
437 if (context.module_init_method != null) {
438 foreach (Parameter parameter in context.module_init_method.get_parameters ()) {
439 if (parameter.variable_type.data_type == type_module_type) {
440 in_plugin = true;
441 module_init_param_name = parameter.name;
442 break;
445 if (!in_plugin) {
446 Report.error (context.module_init_method.source_reference, "[ModuleInit] requires a parameter of type `GLib.TypeModule'");
450 dbus_proxy_type = (TypeSymbol) glib_ns.scope.lookup ("DBusProxy");
452 var dbus_ns = root_symbol.scope.lookup ("DBus");
453 if (dbus_ns != null) {
454 dbus_object_type = (TypeSymbol) dbus_ns.scope.lookup ("Object");
458 header_file = new CCodeFile ();
459 header_file.is_header = true;
460 internal_header_file = new CCodeFile ();
461 internal_header_file.is_header = true;
463 /* we're only interested in non-pkg source files */
464 var source_files = context.get_source_files ();
465 foreach (SourceFile file in source_files) {
466 if (file.file_type == SourceFileType.SOURCE ||
467 (context.header_filename != null && file.file_type == SourceFileType.FAST)) {
468 file.accept (this);
472 // generate symbols file for public API
473 if (context.symbols_filename != null) {
474 var stream = FileStream.open (context.symbols_filename, "w");
475 if (stream == null) {
476 Report.error (null, "unable to open `%s' for writing".printf (context.symbols_filename));
477 return;
480 foreach (string symbol in header_file.get_symbols ()) {
481 stream.puts (symbol);
482 stream.putc ('\n');
485 stream = null;
488 // generate C header file for public API
489 if (context.header_filename != null) {
490 bool ret;
491 if (context.profile == Profile.GOBJECT) {
492 ret = header_file.store (context.header_filename, null, context.version_header, false, "G_BEGIN_DECLS", "G_END_DECLS");
493 } else {
494 ret = header_file.store (context.header_filename, null, context.version_header, false);
496 if (!ret) {
497 Report.error (null, "unable to open `%s' for writing".printf (context.header_filename));
501 // generate C header file for internal API
502 if (context.internal_header_filename != null) {
503 bool ret;
504 if (context.profile == Profile.GOBJECT) {
505 ret = internal_header_file.store (context.internal_header_filename, null, context.version_header, false, "G_BEGIN_DECLS", "G_END_DECLS");
506 } else {
507 ret = internal_header_file.store (context.internal_header_filename, null, context.version_header, false);
509 if (!ret) {
510 Report.error (null, "unable to open `%s' for writing".printf (context.internal_header_filename));
515 public void push_context (EmitContext emit_context) {
516 if (this.emit_context != null) {
517 emit_context_stack.add (this.emit_context);
520 this.emit_context = emit_context;
523 public void pop_context () {
524 if (emit_context_stack.size > 0) {
525 this.emit_context = emit_context_stack[emit_context_stack.size - 1];
526 emit_context_stack.remove_at (emit_context_stack.size - 1);
527 } else {
528 this.emit_context = null;
532 public void push_function (CCodeFunction func) {
533 emit_context.ccode_stack.add (ccode);
534 emit_context.ccode = func;
537 public void pop_function () {
538 emit_context.ccode = emit_context.ccode_stack[emit_context.ccode_stack.size - 1];
539 emit_context.ccode_stack.remove_at (emit_context.ccode_stack.size - 1);
542 public bool add_symbol_declaration (CCodeFile decl_space, Symbol sym, string name) {
543 if (decl_space.add_declaration (name)) {
544 return true;
546 if (sym.source_reference != null) {
547 sym.source_reference.file.used = true;
549 if (sym.external_package || (!decl_space.is_header && CodeContext.get ().use_header && !sym.is_internal_symbol ())) {
550 // add appropriate include file
551 foreach (string header_filename in sym.get_cheader_filenames ()) {
552 decl_space.add_include (header_filename, !sym.external_package);
554 // declaration complete
555 return true;
556 } else {
557 // require declaration
558 return false;
562 public CCodeIdentifier get_value_setter_function (DataType type_reference) {
563 var array_type = type_reference as ArrayType;
564 if (type_reference.data_type != null) {
565 return new CCodeIdentifier (type_reference.data_type.get_set_value_function ());
566 } else if (array_type != null && array_type.element_type.data_type == string_type.data_type) {
567 // G_TYPE_STRV
568 return new CCodeIdentifier ("g_value_set_boxed");
569 } else {
570 return new CCodeIdentifier ("g_value_set_pointer");
574 public CCodeIdentifier get_value_taker_function (DataType type_reference) {
575 var array_type = type_reference as ArrayType;
576 if (type_reference.data_type != null) {
577 return new CCodeIdentifier (type_reference.data_type.get_take_value_function ());
578 } else if (array_type != null && array_type.element_type.data_type == string_type.data_type) {
579 // G_TYPE_STRV
580 return new CCodeIdentifier ("g_value_take_boxed");
581 } else {
582 return new CCodeIdentifier ("g_value_set_pointer");
586 CCodeIdentifier get_value_getter_function (DataType type_reference) {
587 var array_type = type_reference as ArrayType;
588 if (type_reference.data_type != null) {
589 return new CCodeIdentifier (type_reference.data_type.get_get_value_function ());
590 } else if (array_type != null && array_type.element_type.data_type == string_type.data_type) {
591 // G_TYPE_STRV
592 return new CCodeIdentifier ("g_value_get_boxed");
593 } else {
594 return new CCodeIdentifier ("g_value_get_pointer");
598 public virtual void append_vala_array_free () {
601 public virtual void append_vala_array_move () {
604 public virtual void append_vala_array_length () {
607 public override void visit_source_file (SourceFile source_file) {
608 cfile = new CCodeFile ();
610 user_marshal_set = new HashSet<string> (str_hash, str_equal);
612 next_regex_id = 0;
614 gvaluecollector_h_needed = false;
615 requires_array_free = false;
616 requires_array_move = false;
617 requires_array_length = false;
619 wrappers = new HashSet<string> (str_hash, str_equal);
620 generated_external_symbols = new HashSet<Symbol> ();
622 if (context.profile == Profile.GOBJECT) {
623 header_file.add_include ("glib.h");
624 internal_header_file.add_include ("glib.h");
625 cfile.add_include ("glib.h");
626 cfile.add_include ("glib-object.h");
629 source_file.accept_children (this);
631 if (context.report.get_errors () > 0) {
632 return;
635 /* For fast-vapi, we only wanted the header declarations
636 * to be emitted, so bail out here without writing the
637 * C code output.
639 if (source_file.file_type == SourceFileType.FAST) {
640 return;
643 if (requires_array_free) {
644 append_vala_array_free ();
646 if (requires_array_move) {
647 append_vala_array_move ();
649 if (requires_array_length) {
650 append_vala_array_length ();
653 if (gvaluecollector_h_needed) {
654 cfile.add_include ("gobject/gvaluecollector.h");
657 var comments = source_file.get_comments();
658 if (comments != null) {
659 foreach (Comment comment in comments) {
660 var ccomment = new CCodeComment (comment.content);
661 cfile.add_comment (ccomment);
665 if (!cfile.store (source_file.get_csource_filename (), source_file.filename, context.version_header, context.debug)) {
666 Report.error (null, "unable to open `%s' for writing".printf (source_file.get_csource_filename ()));
669 cfile = null;
672 public virtual bool generate_enum_declaration (Enum en, CCodeFile decl_space) {
673 if (add_symbol_declaration (decl_space, en, en.get_cname ())) {
674 return false;
677 var cenum = new CCodeEnum (en.get_cname ());
679 cenum.deprecated = en.deprecated;
681 int flag_shift = 0;
682 foreach (EnumValue ev in en.get_values ()) {
683 CCodeEnumValue c_ev;
684 if (ev.value == null) {
685 c_ev = new CCodeEnumValue (ev.get_cname ());
686 if (en.is_flags) {
687 c_ev.value = new CCodeConstant ("1 << %d".printf (flag_shift));
688 flag_shift += 1;
690 } else {
691 ev.value.emit (this);
692 c_ev = new CCodeEnumValue (ev.get_cname (), get_cvalue (ev.value));
694 c_ev.deprecated = ev.deprecated;
695 cenum.add_value (c_ev);
698 decl_space.add_type_definition (cenum);
699 decl_space.add_type_definition (new CCodeNewline ());
701 if (!en.has_type_id) {
702 return true;
705 decl_space.add_type_declaration (new CCodeNewline ());
707 var macro = "(%s_get_type ())".printf (en.get_lower_case_cname (null));
708 decl_space.add_type_declaration (new CCodeMacroReplacement (en.get_type_id (), macro));
710 var fun_name = "%s_get_type".printf (en.get_lower_case_cname (null));
711 var regfun = new CCodeFunction (fun_name, "GType");
712 regfun.attributes = "G_GNUC_CONST";
714 if (en.access == SymbolAccessibility.PRIVATE) {
715 regfun.modifiers = CCodeModifiers.STATIC;
716 // avoid C warning as this function is not always used
717 regfun.attributes = "G_GNUC_UNUSED";
720 decl_space.add_function_declaration (regfun);
722 return true;
725 public override void visit_enum (Enum en) {
726 en.accept_children (this);
728 if (en.comment != null) {
729 cfile.add_type_member_definition (new CCodeComment (en.comment.content));
732 generate_enum_declaration (en, cfile);
734 if (!en.is_internal_symbol ()) {
735 generate_enum_declaration (en, header_file);
737 if (!en.is_private_symbol ()) {
738 generate_enum_declaration (en, internal_header_file);
742 public void visit_member (Symbol m) {
743 /* stuff meant for all lockable members */
744 if (m is Lockable && ((Lockable) m).get_lock_used ()) {
745 CCodeExpression l = new CCodeIdentifier ("self");
746 var init_context = class_init_context;
747 var finalize_context = class_finalize_context;
749 if (m.is_instance_member ()) {
750 l = new CCodeMemberAccess.pointer (new CCodeMemberAccess.pointer (l, "priv"), get_symbol_lock_name (m.name));
751 init_context = instance_init_context;
752 finalize_context = instance_finalize_context;
753 } else if (m.is_class_member ()) {
754 TypeSymbol parent = (TypeSymbol)m.parent_symbol;
756 var get_class_private_call = new CCodeFunctionCall (new CCodeIdentifier ("%s_GET_CLASS_PRIVATE".printf(parent.get_upper_case_cname ())));
757 get_class_private_call.add_argument (new CCodeIdentifier ("klass"));
758 l = new CCodeMemberAccess.pointer (get_class_private_call, get_symbol_lock_name (m.name));
759 } else {
760 l = new CCodeIdentifier (get_symbol_lock_name ("%s_%s".printf(m.parent_symbol.get_lower_case_cname (), m.name)));
763 push_context (init_context);
764 var initf = new CCodeFunctionCall (new CCodeIdentifier (mutex_type.default_construction_method.get_cname ()));
765 initf.add_argument (new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, l));
766 ccode.add_expression (initf);
767 pop_context ();
769 if (finalize_context != null) {
770 push_context (finalize_context);
771 var fc = new CCodeFunctionCall (new CCodeIdentifier ("g_static_rec_mutex_free"));
772 fc.add_argument (new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, l));
773 ccode.add_expression (fc);
774 pop_context ();
779 public void generate_constant_declaration (Constant c, CCodeFile decl_space, bool definition = false) {
780 if (c.parent_symbol is Block) {
781 // local constant
782 return;
785 if (add_symbol_declaration (decl_space, c, c.get_cname ())) {
786 return;
789 if (!c.external) {
790 generate_type_declaration (c.type_reference, decl_space);
792 c.value.emit (this);
794 var initializer_list = c.value as InitializerList;
795 if (initializer_list != null) {
796 var cdecl = new CCodeDeclaration (c.type_reference.get_const_cname ());
797 var arr = "";
798 if (c.type_reference is ArrayType) {
799 arr = "[%d]".printf (initializer_list.size);
802 var cinitializer = get_cvalue (c.value);
803 if (!definition) {
804 // never output value in header
805 // special case needed as this method combines declaration and definition
806 cinitializer = null;
809 cdecl.add_declarator (new CCodeVariableDeclarator ("%s%s".printf (c.get_cname (), arr), cinitializer));
810 if (c.is_private_symbol ()) {
811 cdecl.modifiers = CCodeModifiers.STATIC;
812 } else {
813 cdecl.modifiers = CCodeModifiers.EXTERN;
816 decl_space.add_constant_declaration (cdecl);
817 } else {
818 var cdefine = new CCodeMacroReplacement.with_expression (c.get_cname (), get_cvalue (c.value));
819 decl_space.add_type_member_declaration (cdefine);
824 public override void visit_constant (Constant c) {
825 if (c.parent_symbol is Block) {
826 // local constant
828 generate_type_declaration (c.type_reference, cfile);
830 c.value.emit (this);
832 string type_name = c.type_reference.get_const_cname ();
833 string arr = "";
834 if (c.type_reference is ArrayType) {
835 arr = "[]";
838 if (c.type_reference.compatible (string_type)) {
839 type_name = "const char";
840 arr = "[]";
843 var cinitializer = get_cvalue (c.value);
845 ccode.add_declaration (type_name, new CCodeVariableDeclarator ("%s%s".printf (c.get_cname (), arr), cinitializer), CCodeModifiers.STATIC);
847 return;
850 generate_constant_declaration (c, cfile, true);
852 if (!c.is_internal_symbol ()) {
853 generate_constant_declaration (c, header_file);
855 if (!c.is_private_symbol ()) {
856 generate_constant_declaration (c, internal_header_file);
860 public void generate_field_declaration (Field f, CCodeFile decl_space) {
861 if (add_symbol_declaration (decl_space, f, f.get_cname ())) {
862 return;
865 generate_type_declaration (f.variable_type, decl_space);
867 string field_ctype = f.variable_type.get_cname ();
868 if (f.is_volatile) {
869 field_ctype = "volatile " + field_ctype;
872 var cdecl = new CCodeDeclaration (field_ctype);
873 cdecl.add_declarator (new CCodeVariableDeclarator (f.get_cname (), null, f.variable_type.get_cdeclarator_suffix ()));
874 if (f.is_private_symbol ()) {
875 cdecl.modifiers = CCodeModifiers.STATIC;
876 } else {
877 cdecl.modifiers = CCodeModifiers.EXTERN;
879 if (f.deprecated) {
880 cdecl.modifiers |= CCodeModifiers.DEPRECATED;
882 decl_space.add_type_member_declaration (cdecl);
884 if (f.get_lock_used ()) {
885 // Declare mutex for static member
886 var flock = new CCodeDeclaration (mutex_type.get_cname ());
887 var flock_decl = new CCodeVariableDeclarator (get_symbol_lock_name (f.get_cname ()), new CCodeConstant ("{0}"));
888 flock.add_declarator (flock_decl);
890 if (f.is_private_symbol ()) {
891 flock.modifiers = CCodeModifiers.STATIC;
892 } else {
893 flock.modifiers = CCodeModifiers.EXTERN;
895 decl_space.add_type_member_declaration (flock);
898 if (f.variable_type is ArrayType && !f.no_array_length) {
899 var array_type = (ArrayType) f.variable_type;
901 if (!array_type.fixed_length) {
902 for (int dim = 1; dim <= array_type.rank; dim++) {
903 var len_type = int_type.copy ();
905 cdecl = new CCodeDeclaration (len_type.get_cname ());
906 cdecl.add_declarator (new CCodeVariableDeclarator (get_array_length_cname (f.get_cname (), dim)));
907 if (f.is_private_symbol ()) {
908 cdecl.modifiers = CCodeModifiers.STATIC;
909 } else {
910 cdecl.modifiers = CCodeModifiers.EXTERN;
912 decl_space.add_type_member_declaration (cdecl);
915 } else if (f.variable_type is DelegateType) {
916 var delegate_type = (DelegateType) f.variable_type;
917 if (delegate_type.delegate_symbol.has_target) {
918 // create field to store delegate target
920 cdecl = new CCodeDeclaration ("gpointer");
921 cdecl.add_declarator (new CCodeVariableDeclarator (get_delegate_target_cname (f.get_cname ())));
922 if (f.is_private_symbol ()) {
923 cdecl.modifiers = CCodeModifiers.STATIC;
924 } else {
925 cdecl.modifiers = CCodeModifiers.EXTERN;
927 decl_space.add_type_member_declaration (cdecl);
929 if (delegate_type.value_owned) {
930 cdecl = new CCodeDeclaration ("GDestroyNotify");
931 cdecl.add_declarator (new CCodeVariableDeclarator (get_delegate_target_destroy_notify_cname (f.get_cname ())));
932 if (f.is_private_symbol ()) {
933 cdecl.modifiers = CCodeModifiers.STATIC;
934 } else {
935 cdecl.modifiers = CCodeModifiers.EXTERN;
937 decl_space.add_type_member_declaration (cdecl);
943 public override void visit_field (Field f) {
944 visit_member (f);
946 check_type (f.variable_type);
948 var cl = f.parent_symbol as Class;
949 bool is_gtypeinstance = (cl != null && !cl.is_compact);
951 CCodeExpression lhs = null;
953 string field_ctype = f.variable_type.get_cname ();
954 if (f.is_volatile) {
955 field_ctype = "volatile " + field_ctype;
958 if (f.binding == MemberBinding.INSTANCE) {
959 if (is_gtypeinstance && f.access == SymbolAccessibility.PRIVATE) {
960 lhs = new CCodeMemberAccess.pointer (new CCodeMemberAccess.pointer (new CCodeIdentifier ("self"), "priv"), f.get_cname ());
961 } else {
962 lhs = new CCodeMemberAccess.pointer (new CCodeIdentifier ("self"), f.get_cname ());
965 if (f.initializer != null) {
966 push_context (instance_init_context);
968 f.initializer.emit (this);
970 var rhs = get_cvalue (f.initializer);
972 ccode.add_assignment (lhs, rhs);
974 if (f.variable_type is ArrayType && !f.no_array_length &&
975 f.initializer is ArrayCreationExpression) {
976 var array_type = (ArrayType) f.variable_type;
977 var field_value = get_field_cvalue (f, load_this_parameter ((TypeSymbol) f.parent_symbol));
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_cvalue (field_value, 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 (field_value);
988 var rhs_array_len = get_array_length_cvalue (field_value, 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);
1004 ccode.add_expression (destroy_field (f, load_this_parameter ((TypeSymbol) f.parent_symbol)));
1005 pop_context ();
1007 } else if (f.binding == MemberBinding.CLASS) {
1008 if (!is_gtypeinstance) {
1009 Report.error (f.source_reference, "class fields are not supported in compact classes");
1010 f.error = true;
1011 return;
1014 if (f.access == SymbolAccessibility.PRIVATE) {
1015 var ccall = new CCodeFunctionCall (new CCodeIdentifier ("%s_GET_CLASS_PRIVATE".printf (cl.get_upper_case_cname ())));
1016 ccall.add_argument (new CCodeIdentifier ("klass"));
1017 lhs = new CCodeMemberAccess (ccall, f.get_cname (), true);
1018 } else {
1019 lhs = new CCodeMemberAccess (new CCodeIdentifier ("klass"), f.get_cname (), true);
1022 if (f.initializer != null) {
1023 push_context (class_init_context);
1025 f.initializer.emit (this);
1027 var rhs = get_cvalue (f.initializer);
1029 ccode.add_assignment (lhs, rhs);
1031 foreach (LocalVariable local in temp_ref_vars) {
1032 ccode.add_expression (destroy_local (local));
1035 temp_ref_vars.clear ();
1037 pop_context ();
1039 } else {
1040 generate_field_declaration (f, cfile);
1042 if (!f.is_internal_symbol ()) {
1043 generate_field_declaration (f, header_file);
1045 if (!f.is_private_symbol ()) {
1046 generate_field_declaration (f, internal_header_file);
1049 lhs = new CCodeIdentifier (f.get_cname ());
1051 var var_decl = new CCodeVariableDeclarator (f.get_cname (), null, f.variable_type.get_cdeclarator_suffix ());
1052 var_decl.initializer = default_value_for_type (f.variable_type, true);
1054 if (class_init_context != null) {
1055 push_context (class_init_context);
1056 } else {
1057 push_context (new EmitContext ());
1060 if (f.initializer != null) {
1061 f.initializer.emit (this);
1063 var init = get_cvalue (f.initializer);
1064 if (is_constant_ccode_expression (init)) {
1065 var_decl.initializer = init;
1069 var var_def = new CCodeDeclaration (field_ctype);
1070 var_def.add_declarator (var_decl);
1071 if (!f.is_private_symbol ()) {
1072 var_def.modifiers = CCodeModifiers.EXTERN;
1073 } else {
1074 var_def.modifiers = CCodeModifiers.STATIC;
1076 cfile.add_type_member_declaration (var_def);
1078 /* add array length fields where necessary */
1079 if (f.variable_type is ArrayType && !f.no_array_length) {
1080 var array_type = (ArrayType) f.variable_type;
1082 if (!array_type.fixed_length) {
1083 for (int dim = 1; dim <= array_type.rank; dim++) {
1084 var len_type = int_type.copy ();
1086 var len_def = new CCodeDeclaration (len_type.get_cname ());
1087 len_def.add_declarator (new CCodeVariableDeclarator (get_array_length_cname (f.get_cname (), dim), new CCodeConstant ("0")));
1088 if (!f.is_private_symbol ()) {
1089 len_def.modifiers = CCodeModifiers.EXTERN;
1090 } else {
1091 len_def.modifiers = CCodeModifiers.STATIC;
1093 cfile.add_type_member_declaration (len_def);
1096 if (array_type.rank == 1 && f.is_internal_symbol ()) {
1097 var len_type = int_type.copy ();
1099 var cdecl = new CCodeDeclaration (len_type.get_cname ());
1100 cdecl.add_declarator (new CCodeVariableDeclarator (get_array_size_cname (f.get_cname ()), new CCodeConstant ("0")));
1101 cdecl.modifiers = CCodeModifiers.STATIC;
1102 cfile.add_type_member_declaration (cdecl);
1105 } else if (f.variable_type is DelegateType) {
1106 var delegate_type = (DelegateType) f.variable_type;
1107 if (delegate_type.delegate_symbol.has_target) {
1108 // create field to store delegate target
1110 var target_def = new CCodeDeclaration ("gpointer");
1111 target_def.add_declarator (new CCodeVariableDeclarator (get_delegate_target_cname (f.get_cname ()), new CCodeConstant ("NULL")));
1112 if (!f.is_private_symbol ()) {
1113 target_def.modifiers = CCodeModifiers.EXTERN;
1114 } else {
1115 target_def.modifiers = CCodeModifiers.STATIC;
1117 cfile.add_type_member_declaration (target_def);
1119 if (delegate_type.value_owned) {
1120 var target_destroy_notify_def = new CCodeDeclaration ("GDestroyNotify");
1121 target_destroy_notify_def.add_declarator (new CCodeVariableDeclarator (get_delegate_target_destroy_notify_cname (f.get_cname ()), new CCodeConstant ("NULL")));
1122 if (!f.is_private_symbol ()) {
1123 target_destroy_notify_def.modifiers = CCodeModifiers.EXTERN;
1124 } else {
1125 target_destroy_notify_def.modifiers = CCodeModifiers.STATIC;
1127 cfile.add_type_member_declaration (target_destroy_notify_def);
1133 if (f.initializer != null) {
1134 var rhs = get_cvalue (f.initializer);
1135 if (!is_constant_ccode_expression (rhs)) {
1136 if (f.parent_symbol is Class) {
1137 if (f.initializer is InitializerList) {
1138 ccode.open_block ();
1140 var temp_decl = get_temp_variable (f.variable_type);
1141 var vardecl = new CCodeVariableDeclarator.zero (temp_decl.name, rhs);
1142 ccode.add_declaration (temp_decl.variable_type.get_cname (), vardecl);
1144 var tmp = get_variable_cexpression (get_variable_cname (temp_decl.name));
1145 ccode.add_assignment (lhs, tmp);
1147 ccode.close ();
1148 } else {
1149 ccode.add_assignment (lhs, rhs);
1152 if (f.variable_type is ArrayType && !f.no_array_length &&
1153 f.initializer is ArrayCreationExpression) {
1154 var array_type = (ArrayType) f.variable_type;
1155 var ma = new MemberAccess.simple (f.name);
1156 ma.symbol_reference = f;
1157 ma.value_type = f.variable_type.copy ();
1158 visit_member_access (ma);
1160 List<Expression> sizes = ((ArrayCreationExpression) f.initializer).get_sizes ();
1161 for (int dim = 1; dim <= array_type.rank; dim++) {
1162 var array_len_lhs = get_array_length_cexpression (ma, dim);
1163 var size = sizes[dim - 1];
1164 ccode.add_assignment (array_len_lhs, get_cvalue (size));
1167 } else {
1168 f.error = true;
1169 Report.error (f.source_reference, "Non-constant field initializers not supported in this context");
1170 return;
1175 pop_context ();
1179 public bool is_constant_ccode_expression (CCodeExpression cexpr) {
1180 if (cexpr is CCodeConstant) {
1181 return true;
1182 } else if (cexpr is CCodeCastExpression) {
1183 var ccast = (CCodeCastExpression) cexpr;
1184 return is_constant_ccode_expression (ccast.inner);
1185 } else if (cexpr is CCodeBinaryExpression) {
1186 var cbinary = (CCodeBinaryExpression) cexpr;
1187 return is_constant_ccode_expression (cbinary.left) && is_constant_ccode_expression (cbinary.right);
1190 var cparenthesized = (cexpr as CCodeParenthesizedExpression);
1191 return (null != cparenthesized && is_constant_ccode_expression (cparenthesized.inner));
1195 * Returns whether the passed cexpr is a pure expression, i.e. an
1196 * expression without side-effects.
1198 public bool is_pure_ccode_expression (CCodeExpression cexpr) {
1199 if (cexpr is CCodeConstant || cexpr is CCodeIdentifier) {
1200 return true;
1201 } else if (cexpr is CCodeBinaryExpression) {
1202 var cbinary = (CCodeBinaryExpression) cexpr;
1203 return is_pure_ccode_expression (cbinary.left) && is_constant_ccode_expression (cbinary.right);
1204 } else if (cexpr is CCodeUnaryExpression) {
1205 var cunary = (CCodeUnaryExpression) cexpr;
1206 switch (cunary.operator) {
1207 case CCodeUnaryOperator.PREFIX_INCREMENT:
1208 case CCodeUnaryOperator.PREFIX_DECREMENT:
1209 case CCodeUnaryOperator.POSTFIX_INCREMENT:
1210 case CCodeUnaryOperator.POSTFIX_DECREMENT:
1211 return false;
1212 default:
1213 return is_pure_ccode_expression (cunary.inner);
1215 } else if (cexpr is CCodeMemberAccess) {
1216 var cma = (CCodeMemberAccess) cexpr;
1217 return is_pure_ccode_expression (cma.inner);
1218 } else if (cexpr is CCodeElementAccess) {
1219 var cea = (CCodeElementAccess) cexpr;
1220 return is_pure_ccode_expression (cea.container) && is_pure_ccode_expression (cea.index);
1221 } else if (cexpr is CCodeCastExpression) {
1222 var ccast = (CCodeCastExpression) cexpr;
1223 return is_pure_ccode_expression (ccast.inner);
1224 } else if (cexpr is CCodeParenthesizedExpression) {
1225 var cparenthesized = (CCodeParenthesizedExpression) cexpr;
1226 return is_pure_ccode_expression (cparenthesized.inner);
1229 return false;
1232 public override void visit_formal_parameter (Parameter p) {
1233 if (!p.ellipsis) {
1234 check_type (p.variable_type);
1238 public override void visit_property (Property prop) {
1239 visit_member (prop);
1241 check_type (prop.property_type);
1243 if (prop.get_accessor != null) {
1244 prop.get_accessor.accept (this);
1246 if (prop.set_accessor != null) {
1247 prop.set_accessor.accept (this);
1251 public void generate_type_declaration (DataType type, CCodeFile decl_space) {
1252 if (type is ObjectType) {
1253 var object_type = (ObjectType) type;
1254 if (object_type.type_symbol is Class) {
1255 generate_class_declaration ((Class) object_type.type_symbol, decl_space);
1256 } else if (object_type.type_symbol is Interface) {
1257 generate_interface_declaration ((Interface) object_type.type_symbol, decl_space);
1259 } else if (type is DelegateType) {
1260 var deleg_type = (DelegateType) type;
1261 var d = deleg_type.delegate_symbol;
1262 generate_delegate_declaration (d, decl_space);
1263 } else if (type.data_type is Enum) {
1264 var en = (Enum) type.data_type;
1265 generate_enum_declaration (en, decl_space);
1266 } else if (type is ValueType) {
1267 var value_type = (ValueType) type;
1268 generate_struct_declaration ((Struct) value_type.type_symbol, decl_space);
1269 } else if (type is ArrayType) {
1270 var array_type = (ArrayType) type;
1271 generate_type_declaration (array_type.element_type, decl_space);
1272 } else if (type is ErrorType) {
1273 var error_type = (ErrorType) type;
1274 if (error_type.error_domain != null) {
1275 generate_error_domain_declaration (error_type.error_domain, decl_space);
1277 } else if (type is PointerType) {
1278 var pointer_type = (PointerType) type;
1279 generate_type_declaration (pointer_type.base_type, decl_space);
1282 foreach (DataType type_arg in type.get_type_arguments ()) {
1283 generate_type_declaration (type_arg, decl_space);
1287 public virtual void generate_class_struct_declaration (Class cl, CCodeFile decl_space) {
1290 public virtual void generate_struct_declaration (Struct st, CCodeFile decl_space) {
1293 public virtual void generate_delegate_declaration (Delegate d, CCodeFile decl_space) {
1296 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) {
1299 public void generate_property_accessor_declaration (PropertyAccessor acc, CCodeFile decl_space) {
1300 if (add_symbol_declaration (decl_space, acc, acc.get_cname ())) {
1301 return;
1304 var prop = (Property) acc.prop;
1306 bool returns_real_struct = acc.readable && prop.property_type.is_real_non_null_struct_type ();
1309 CCodeParameter cvalueparam;
1310 if (returns_real_struct) {
1311 cvalueparam = new CCodeParameter ("result", acc.value_type.get_cname () + "*");
1312 } else if (!acc.readable && prop.property_type.is_real_non_null_struct_type ()) {
1313 cvalueparam = new CCodeParameter ("value", acc.value_type.get_cname () + "*");
1314 } else {
1315 cvalueparam = new CCodeParameter ("value", acc.value_type.get_cname ());
1317 generate_type_declaration (acc.value_type, decl_space);
1319 CCodeFunction function;
1320 if (acc.readable && !returns_real_struct) {
1321 function = new CCodeFunction (acc.get_cname (), acc.value_type.get_cname ());
1322 } else {
1323 function = new CCodeFunction (acc.get_cname (), "void");
1326 if (prop.binding == MemberBinding.INSTANCE) {
1327 var t = (TypeSymbol) prop.parent_symbol;
1328 var this_type = get_data_type_for_symbol (t);
1329 generate_type_declaration (this_type, decl_space);
1330 var cselfparam = new CCodeParameter ("self", this_type.get_cname ());
1331 if (t is Struct) {
1332 cselfparam.type_name += "*";
1335 function.add_parameter (cselfparam);
1338 if (acc.writable || acc.construction || returns_real_struct) {
1339 function.add_parameter (cvalueparam);
1342 if (acc.value_type is ArrayType) {
1343 var array_type = (ArrayType) acc.value_type;
1345 var length_ctype = "int";
1346 if (acc.readable) {
1347 length_ctype = "int*";
1350 for (int dim = 1; dim <= array_type.rank; dim++) {
1351 function.add_parameter (new CCodeParameter (get_array_length_cname (acc.readable ? "result" : "value", dim), length_ctype));
1353 } else if ((acc.value_type is DelegateType) && ((DelegateType) acc.value_type).delegate_symbol.has_target) {
1354 function.add_parameter (new CCodeParameter (get_delegate_target_cname (acc.readable ? "result" : "value"), acc.readable ? "gpointer*" : "gpointer"));
1357 if (prop.is_private_symbol () || (!acc.readable && !acc.writable) || acc.access == SymbolAccessibility.PRIVATE) {
1358 function.modifiers |= CCodeModifiers.STATIC;
1360 decl_space.add_function_declaration (function);
1363 public override void visit_property_accessor (PropertyAccessor acc) {
1364 push_context (new EmitContext (acc));
1366 var prop = (Property) acc.prop;
1368 if (acc.comment != null) {
1369 cfile.add_type_member_definition (new CCodeComment (acc.comment.content));
1372 bool returns_real_struct = acc.readable && prop.property_type.is_real_non_null_struct_type ();
1374 if (acc.result_var != null) {
1375 acc.result_var.accept (this);
1378 var t = (TypeSymbol) prop.parent_symbol;
1380 if (acc.construction && !t.is_subtype_of (gobject_type)) {
1381 Report.error (acc.source_reference, "construct properties require GLib.Object");
1382 acc.error = true;
1383 return;
1384 } else if (acc.construction && !is_gobject_property (prop)) {
1385 Report.error (acc.source_reference, "construct properties not supported for specified property type");
1386 acc.error = true;
1387 return;
1390 // do not declare overriding properties and interface implementations
1391 if (prop.is_abstract || prop.is_virtual
1392 || (prop.base_property == null && prop.base_interface_property == null)) {
1393 generate_property_accessor_declaration (acc, cfile);
1395 // do not declare construct-only properties in header files
1396 if (acc.readable || acc.writable) {
1397 if (!prop.is_internal_symbol ()
1398 && (acc.access == SymbolAccessibility.PUBLIC
1399 || acc.access == SymbolAccessibility.PROTECTED)) {
1400 generate_property_accessor_declaration (acc, header_file);
1402 if (!prop.is_private_symbol () && acc.access != SymbolAccessibility.PRIVATE) {
1403 generate_property_accessor_declaration (acc, internal_header_file);
1408 if (acc.source_type == SourceFileType.FAST) {
1409 return;
1412 var this_type = get_data_type_for_symbol (t);
1413 var cselfparam = new CCodeParameter ("self", this_type.get_cname ());
1414 if (t is Struct) {
1415 cselfparam.type_name += "*";
1417 CCodeParameter cvalueparam;
1418 if (returns_real_struct) {
1419 cvalueparam = new CCodeParameter ("result", acc.value_type.get_cname () + "*");
1420 } else if (!acc.readable && prop.property_type.is_real_non_null_struct_type ()) {
1421 cvalueparam = new CCodeParameter ("value", acc.value_type.get_cname () + "*");
1422 } else {
1423 cvalueparam = new CCodeParameter ("value", acc.value_type.get_cname ());
1426 if (prop.is_abstract || prop.is_virtual) {
1427 CCodeFunction function;
1428 if (acc.readable && !returns_real_struct) {
1429 function = new CCodeFunction (acc.get_cname (), current_return_type.get_cname ());
1430 } else {
1431 function = new CCodeFunction (acc.get_cname (), "void");
1433 function.add_parameter (cselfparam);
1434 if (acc.writable || acc.construction || returns_real_struct) {
1435 function.add_parameter (cvalueparam);
1438 if (acc.value_type is ArrayType) {
1439 var array_type = (ArrayType) acc.value_type;
1441 var length_ctype = "int";
1442 if (acc.readable) {
1443 length_ctype = "int*";
1446 for (int dim = 1; dim <= array_type.rank; dim++) {
1447 function.add_parameter (new CCodeParameter (get_array_length_cname (acc.readable ? "result" : "value", dim), length_ctype));
1449 } else if ((acc.value_type is DelegateType) && ((DelegateType) acc.value_type).delegate_symbol.has_target) {
1450 function.add_parameter (new CCodeParameter (get_delegate_target_cname (acc.readable ? "result" : "value"), acc.readable ? "gpointer*" : "gpointer"));
1453 if (prop.is_private_symbol () || !(acc.readable || acc.writable) || acc.access == SymbolAccessibility.PRIVATE) {
1454 // accessor function should be private if the property is an internal symbol or it's a construct-only setter
1455 function.modifiers |= CCodeModifiers.STATIC;
1458 push_function (function);
1460 CCodeFunctionCall vcast = null;
1461 if (prop.parent_symbol is Interface) {
1462 var iface = (Interface) prop.parent_symbol;
1464 vcast = new CCodeFunctionCall (new CCodeIdentifier ("%s_GET_INTERFACE".printf (iface.get_upper_case_cname (null))));
1465 } else {
1466 var cl = (Class) prop.parent_symbol;
1468 vcast = new CCodeFunctionCall (new CCodeIdentifier ("%s_GET_CLASS".printf (cl.get_upper_case_cname (null))));
1470 vcast.add_argument (new CCodeIdentifier ("self"));
1472 if (acc.readable) {
1473 var vcall = new CCodeFunctionCall (new CCodeMemberAccess.pointer (vcast, "get_%s".printf (prop.name)));
1474 vcall.add_argument (new CCodeIdentifier ("self"));
1475 if (returns_real_struct) {
1476 vcall.add_argument (new CCodeIdentifier ("result"));
1477 ccode.add_expression (vcall);
1478 } else {
1479 if (acc.value_type is ArrayType) {
1480 var array_type = (ArrayType) acc.value_type;
1482 for (int dim = 1; dim <= array_type.rank; dim++) {
1483 var len_expr = new CCodeIdentifier (get_array_length_cname ("result", dim));
1484 vcall.add_argument (len_expr);
1486 } else if ((acc.value_type is DelegateType) && ((DelegateType) acc.value_type).delegate_symbol.has_target) {
1487 vcall.add_argument (new CCodeIdentifier (get_delegate_target_cname ("result")));
1490 ccode.add_return (vcall);
1492 } else {
1493 var vcall = new CCodeFunctionCall (new CCodeMemberAccess.pointer (vcast, "set_%s".printf (prop.name)));
1494 vcall.add_argument (new CCodeIdentifier ("self"));
1495 vcall.add_argument (new CCodeIdentifier ("value"));
1497 if (acc.value_type is ArrayType) {
1498 var array_type = (ArrayType) acc.value_type;
1500 for (int dim = 1; dim <= array_type.rank; dim++) {
1501 var len_expr = new CCodeIdentifier (get_array_length_cname ("value", dim));
1502 vcall.add_argument (len_expr);
1504 } else if ((acc.value_type is DelegateType) && ((DelegateType) acc.value_type).delegate_symbol.has_target) {
1505 vcall.add_argument (new CCodeIdentifier (get_delegate_target_cname ("value")));
1508 ccode.add_expression (vcall);
1511 pop_function ();
1513 cfile.add_function (function);
1516 if (!prop.is_abstract) {
1517 bool is_virtual = prop.base_property != null || prop.base_interface_property != null;
1519 string cname;
1520 if (is_virtual) {
1521 if (acc.readable) {
1522 cname = "%s_real_get_%s".printf (t.get_lower_case_cname (null), prop.name);
1523 } else {
1524 cname = "%s_real_set_%s".printf (t.get_lower_case_cname (null), prop.name);
1526 } else {
1527 cname = acc.get_cname ();
1530 CCodeFunction function;
1531 if (acc.writable || acc.construction || returns_real_struct) {
1532 function = new CCodeFunction (cname, "void");
1533 } else {
1534 function = new CCodeFunction (cname, acc.value_type.get_cname ());
1537 ObjectType base_type = null;
1538 if (prop.binding == MemberBinding.INSTANCE) {
1539 if (is_virtual) {
1540 if (prop.base_property != null) {
1541 base_type = new ObjectType ((ObjectTypeSymbol) prop.base_property.parent_symbol);
1542 } else if (prop.base_interface_property != null) {
1543 base_type = new ObjectType ((ObjectTypeSymbol) prop.base_interface_property.parent_symbol);
1545 function.modifiers |= CCodeModifiers.STATIC;
1546 function.add_parameter (new CCodeParameter ("base", base_type.get_cname ()));
1547 } else {
1548 function.add_parameter (cselfparam);
1551 if (acc.writable || acc.construction || returns_real_struct) {
1552 function.add_parameter (cvalueparam);
1555 if (acc.value_type is ArrayType) {
1556 var array_type = (ArrayType) acc.value_type;
1558 var length_ctype = "int";
1559 if (acc.readable) {
1560 length_ctype = "int*";
1563 for (int dim = 1; dim <= array_type.rank; dim++) {
1564 function.add_parameter (new CCodeParameter (get_array_length_cname (acc.readable ? "result" : "value", dim), length_ctype));
1566 } else if ((acc.value_type is DelegateType) && ((DelegateType) acc.value_type).delegate_symbol.has_target) {
1567 function.add_parameter (new CCodeParameter (get_delegate_target_cname (acc.readable ? "result" : "value"), acc.readable ? "gpointer*" : "gpointer"));
1570 if (!is_virtual) {
1571 if (prop.is_private_symbol () || !(acc.readable || acc.writable) || acc.access == SymbolAccessibility.PRIVATE) {
1572 // accessor function should be private if the property is an internal symbol or it's a construct-only setter
1573 function.modifiers |= CCodeModifiers.STATIC;
1577 push_function (function);
1579 if (prop.binding == MemberBinding.INSTANCE && !is_virtual) {
1580 if (!acc.readable || returns_real_struct) {
1581 create_property_type_check_statement (prop, false, t, true, "self");
1582 } else {
1583 create_property_type_check_statement (prop, true, t, true, "self");
1587 if (acc.readable && !returns_real_struct) {
1588 // do not declare result variable if exit block is known to be unreachable
1589 if (acc.return_block == null || acc.return_block.get_predecessors ().size > 0) {
1590 ccode.add_declaration (acc.value_type.get_cname (), new CCodeVariableDeclarator ("result"));
1594 if (is_virtual) {
1595 ccode.add_declaration (this_type.get_cname (), new CCodeVariableDeclarator ("self"));
1596 ccode.add_assignment (new CCodeIdentifier ("self"), transform_expression (new CCodeIdentifier ("base"), base_type, this_type));
1599 acc.body.emit (this);
1601 if (current_method_inner_error) {
1602 ccode.add_declaration ("GError *", new CCodeVariableDeclarator.zero ("_inner_error_", new CCodeConstant ("NULL")));
1605 // notify on property changes
1606 if (is_gobject_property (prop) &&
1607 prop.notify &&
1608 (acc.writable || acc.construction)) {
1609 var notify_call = new CCodeFunctionCall (new CCodeIdentifier ("g_object_notify"));
1610 notify_call.add_argument (new CCodeCastExpression (new CCodeIdentifier ("self"), "GObject *"));
1611 notify_call.add_argument (prop.get_canonical_cconstant ());
1612 ccode.add_expression (notify_call);
1615 cfile.add_function (function);
1618 pop_context ();
1621 public override void visit_destructor (Destructor d) {
1622 if (d.binding == MemberBinding.STATIC && !in_plugin) {
1623 Report.error (d.source_reference, "static destructors are only supported for dynamic types");
1624 d.error = true;
1625 return;
1629 public int get_block_id (Block b) {
1630 int result = block_map[b];
1631 if (result == 0) {
1632 result = ++next_block_id;
1633 block_map[b] = result;
1635 return result;
1638 void capture_parameter (Parameter param, CCodeStruct data, int block_id) {
1639 generate_type_declaration (param.variable_type, cfile);
1641 var param_type = param.variable_type.copy ();
1642 param_type.value_owned = true;
1643 data.add_field (param_type.get_cname (), get_variable_cname (param.name));
1645 bool is_unowned_delegate = param.variable_type is DelegateType && !param.variable_type.value_owned;
1647 // create copy if necessary as captured variables may need to be kept alive
1648 CCodeExpression cparam = get_variable_cexpression (param.name);
1649 if (param.variable_type.is_real_non_null_struct_type ()) {
1650 cparam = new CCodeUnaryExpression (CCodeUnaryOperator.POINTER_INDIRECTION, cparam);
1652 if (requires_copy (param_type) && !param.variable_type.value_owned && !is_unowned_delegate) {
1653 var ma = new MemberAccess.simple (param.name);
1654 ma.symbol_reference = param;
1655 ma.value_type = param.variable_type.copy ();
1656 // directly access parameters in ref expressions
1657 param.captured = false;
1658 visit_member_access (ma);
1659 cparam = get_ref_cexpression (param.variable_type, cparam, ma, param);
1660 param.captured = true;
1663 ccode.add_assignment (new CCodeMemberAccess.pointer (get_variable_cexpression ("_data%d_".printf (block_id)), get_variable_cname (param.name)), cparam);
1665 if (param.variable_type is ArrayType) {
1666 var array_type = (ArrayType) param.variable_type;
1667 for (int dim = 1; dim <= array_type.rank; dim++) {
1668 data.add_field ("gint", get_parameter_array_length_cname (param, dim));
1669 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)));
1671 } else if (param.variable_type is DelegateType) {
1672 CCodeExpression target_expr;
1673 CCodeExpression delegate_target_destroy_notify;
1674 if (is_in_coroutine ()) {
1675 target_expr = new CCodeMemberAccess.pointer (new CCodeIdentifier ("data"), get_delegate_target_cname (get_variable_cname (param.name)));
1676 delegate_target_destroy_notify = new CCodeMemberAccess.pointer (new CCodeIdentifier ("data"), get_delegate_target_destroy_notify_cname (get_variable_cname (param.name)));
1677 } else {
1678 target_expr = new CCodeIdentifier (get_delegate_target_cname (get_variable_cname (param.name)));
1679 delegate_target_destroy_notify = new CCodeIdentifier (get_delegate_target_destroy_notify_cname (get_variable_cname (param.name)));
1682 data.add_field ("gpointer", get_delegate_target_cname (get_variable_cname (param.name)));
1683 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);
1684 if (param.variable_type.value_owned) {
1685 data.add_field ("GDestroyNotify", get_delegate_target_destroy_notify_cname (get_variable_cname (param.name)));
1686 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);
1691 public override void visit_block (Block b) {
1692 emit_context.push_symbol (b);
1694 var local_vars = b.get_local_variables ();
1696 if (b.parent_node is Block || b.parent_node is SwitchStatement) {
1697 ccode.open_block ();
1700 if (b.captured) {
1701 var parent_block = next_closure_block (b.parent_symbol);
1703 int block_id = get_block_id (b);
1704 string struct_name = "Block%dData".printf (block_id);
1706 var data = new CCodeStruct ("_" + struct_name);
1707 data.add_field ("int", "_ref_count_");
1708 if (parent_block != null) {
1709 int parent_block_id = get_block_id (parent_block);
1711 data.add_field ("Block%dData *".printf (parent_block_id), "_data%d_".printf (parent_block_id));
1712 } else {
1713 if (in_constructor || (current_method != null && current_method.binding == MemberBinding.INSTANCE) ||
1714 (current_property_accessor != null && current_property_accessor.prop.binding == MemberBinding.INSTANCE)) {
1715 data.add_field ("%s *".printf (current_class.get_cname ()), "self");
1718 if (current_method != null) {
1719 // allow capturing generic type parameters
1720 foreach (var type_param in current_method.get_type_parameters ()) {
1721 string func_name;
1723 func_name = "%s_type".printf (type_param.name.down ());
1724 data.add_field ("GType", func_name);
1726 func_name = "%s_dup_func".printf (type_param.name.down ());
1727 data.add_field ("GBoxedCopyFunc", func_name);
1729 func_name = "%s_destroy_func".printf (type_param.name.down ());
1730 data.add_field ("GDestroyNotify", func_name);
1734 foreach (var local in local_vars) {
1735 if (local.captured) {
1736 generate_type_declaration (local.variable_type, cfile);
1738 data.add_field (local.variable_type.get_cname (), get_variable_cname (local.name) + local.variable_type.get_cdeclarator_suffix ());
1740 if (local.variable_type is ArrayType) {
1741 var array_type = (ArrayType) local.variable_type;
1742 for (int dim = 1; dim <= array_type.rank; dim++) {
1743 data.add_field ("gint", get_array_length_cname (get_variable_cname (local.name), dim));
1745 data.add_field ("gint", get_array_size_cname (get_variable_cname (local.name)));
1746 } else if (local.variable_type is DelegateType) {
1747 data.add_field ("gpointer", get_delegate_target_cname (get_variable_cname (local.name)));
1748 if (local.variable_type.value_owned) {
1749 data.add_field ("GDestroyNotify", get_delegate_target_destroy_notify_cname (get_variable_cname (local.name)));
1755 var data_alloc = new CCodeFunctionCall (new CCodeIdentifier ("g_slice_new0"));
1756 data_alloc.add_argument (new CCodeIdentifier (struct_name));
1758 if (is_in_coroutine ()) {
1759 closure_struct.add_field (struct_name + "*", "_data%d_".printf (block_id));
1760 } else {
1761 ccode.add_declaration (struct_name + "*", new CCodeVariableDeclarator ("_data%d_".printf (block_id)));
1763 ccode.add_assignment (get_variable_cexpression ("_data%d_".printf (block_id)), data_alloc);
1765 // initialize ref_count
1766 ccode.add_assignment (new CCodeMemberAccess.pointer (get_variable_cexpression ("_data%d_".printf (block_id)), "_ref_count_"), new CCodeIdentifier ("1"));
1768 if (parent_block != null) {
1769 int parent_block_id = get_block_id (parent_block);
1771 var ref_call = new CCodeFunctionCall (new CCodeIdentifier ("block%d_data_ref".printf (parent_block_id)));
1772 ref_call.add_argument (get_variable_cexpression ("_data%d_".printf (parent_block_id)));
1774 ccode.add_assignment (new CCodeMemberAccess.pointer (get_variable_cexpression ("_data%d_".printf (block_id)), "_data%d_".printf (parent_block_id)), ref_call);
1775 } else {
1776 if (in_constructor || (current_method != null && current_method.binding == MemberBinding.INSTANCE &&
1777 (!(current_method is CreationMethod) || current_method.body != b)) ||
1778 (current_property_accessor != null && current_property_accessor.prop.binding == MemberBinding.INSTANCE)) {
1779 var ref_call = new CCodeFunctionCall (get_dup_func_expression (new ObjectType (current_class), b.source_reference));
1780 ref_call.add_argument (get_result_cexpression ("self"));
1782 ccode.add_assignment (new CCodeMemberAccess.pointer (get_variable_cexpression ("_data%d_".printf (block_id)), "self"), ref_call);
1785 if (current_method != null) {
1786 // allow capturing generic type parameters
1787 foreach (var type_param in current_method.get_type_parameters ()) {
1788 string func_name;
1790 func_name = "%s_type".printf (type_param.name.down ());
1791 ccode.add_assignment (new CCodeMemberAccess.pointer (get_variable_cexpression ("_data%d_".printf (block_id)), func_name), new CCodeIdentifier (func_name));
1793 func_name = "%s_dup_func".printf (type_param.name.down ());
1794 ccode.add_assignment (new CCodeMemberAccess.pointer (get_variable_cexpression ("_data%d_".printf (block_id)), func_name), new CCodeIdentifier (func_name));
1796 func_name = "%s_destroy_func".printf (type_param.name.down ());
1797 ccode.add_assignment (new CCodeMemberAccess.pointer (get_variable_cexpression ("_data%d_".printf (block_id)), func_name), new CCodeIdentifier (func_name));
1802 if (b.parent_symbol is Method) {
1803 var m = (Method) b.parent_symbol;
1805 // parameters are captured with the top-level block of the method
1806 foreach (var param in m.get_parameters ()) {
1807 if (param.captured) {
1808 capture_parameter (param, data, block_id);
1812 if (m.coroutine) {
1813 // capture async data to allow invoking callback from inside closure
1814 data.add_field ("gpointer", "_async_data_");
1816 // async method is suspended while waiting for callback,
1817 // so we never need to care about memory management of async data
1818 ccode.add_assignment (new CCodeMemberAccess.pointer (get_variable_cexpression ("_data%d_".printf (block_id)), "_async_data_"), new CCodeIdentifier ("data"));
1820 } else if (b.parent_symbol is PropertyAccessor) {
1821 var acc = (PropertyAccessor) b.parent_symbol;
1823 if (!acc.readable && acc.value_parameter.captured) {
1824 capture_parameter (acc.value_parameter, data, block_id);
1828 var typedef = new CCodeTypeDefinition ("struct _" + struct_name, new CCodeVariableDeclarator (struct_name));
1829 cfile.add_type_declaration (typedef);
1830 cfile.add_type_definition (data);
1832 // create ref/unref functions
1833 var ref_fun = new CCodeFunction ("block%d_data_ref".printf (block_id), struct_name + "*");
1834 ref_fun.add_parameter (new CCodeParameter ("_data%d_".printf (block_id), struct_name + "*"));
1835 ref_fun.modifiers = CCodeModifiers.STATIC;
1836 cfile.add_function_declaration (ref_fun);
1837 ref_fun.block = new CCodeBlock ();
1839 var ccall = new CCodeFunctionCall (new CCodeIdentifier ("g_atomic_int_inc"));
1840 ccall.add_argument (new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, new CCodeMemberAccess.pointer (new CCodeIdentifier ("_data%d_".printf (block_id)), "_ref_count_")));
1841 ref_fun.block.add_statement (new CCodeExpressionStatement (ccall));
1842 ref_fun.block.add_statement (new CCodeReturnStatement (new CCodeIdentifier ("_data%d_".printf (block_id))));
1843 cfile.add_function (ref_fun);
1845 var unref_fun = new CCodeFunction ("block%d_data_unref".printf (block_id), "void");
1846 unref_fun.add_parameter (new CCodeParameter ("_data%d_".printf (block_id), struct_name + "*"));
1847 unref_fun.modifiers = CCodeModifiers.STATIC;
1848 cfile.add_function_declaration (unref_fun);
1850 push_function (unref_fun);
1852 ccall = new CCodeFunctionCall (new CCodeIdentifier ("g_atomic_int_dec_and_test"));
1853 ccall.add_argument (new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, new CCodeMemberAccess.pointer (new CCodeIdentifier ("_data%d_".printf (block_id)), "_ref_count_")));
1854 ccode.open_if (ccall);
1856 if (parent_block != null) {
1857 int parent_block_id = get_block_id (parent_block);
1859 var unref_call = new CCodeFunctionCall (new CCodeIdentifier ("block%d_data_unref".printf (parent_block_id)));
1860 unref_call.add_argument (new CCodeMemberAccess.pointer (new CCodeIdentifier ("_data%d_".printf (block_id)), "_data%d_".printf (parent_block_id)));
1861 ccode.add_expression (unref_call);
1862 ccode.add_assignment (new CCodeMemberAccess.pointer (new CCodeIdentifier ("_data%d_".printf (block_id)), "_data%d_".printf (parent_block_id)), new CCodeConstant ("NULL"));
1863 } else {
1864 if (in_constructor || (current_method != null && current_method.binding == MemberBinding.INSTANCE) ||
1865 (current_property_accessor != null && current_property_accessor.prop.binding == MemberBinding.INSTANCE)) {
1866 var ma = new MemberAccess.simple ("this");
1867 ma.symbol_reference = current_class;
1868 ccode.add_expression (get_unref_expression (new CCodeMemberAccess.pointer (new CCodeIdentifier ("_data%d_".printf (block_id)), "self"), new ObjectType (current_class), ma));
1872 // free in reverse order
1873 for (int i = local_vars.size - 1; i >= 0; i--) {
1874 var local = local_vars[i];
1875 if (local.captured) {
1876 if (requires_destroy (local.variable_type)) {
1877 bool old_coroutine = false;
1878 if (current_method != null) {
1879 old_coroutine = current_method.coroutine;
1880 current_method.coroutine = false;
1883 ccode.add_expression (destroy_local (local));
1885 if (old_coroutine) {
1886 current_method.coroutine = true;
1892 if (b.parent_symbol is Method) {
1893 var m = (Method) b.parent_symbol;
1895 // parameters are captured with the top-level block of the method
1896 foreach (var param in m.get_parameters ()) {
1897 if (param.captured) {
1898 var param_type = param.variable_type.copy ();
1899 param_type.value_owned = true;
1901 bool is_unowned_delegate = param.variable_type is DelegateType && !param.variable_type.value_owned;
1903 if (requires_destroy (param_type) && !is_unowned_delegate) {
1904 bool old_coroutine = false;
1905 if (m != null) {
1906 old_coroutine = m.coroutine;
1907 m.coroutine = false;
1910 ccode.add_expression (destroy_parameter (param));
1912 if (old_coroutine) {
1913 m.coroutine = true;
1918 } else if (b.parent_symbol is PropertyAccessor) {
1919 var acc = (PropertyAccessor) b.parent_symbol;
1921 if (!acc.readable && acc.value_parameter.captured) {
1922 var param_type = acc.value_parameter.variable_type.copy ();
1923 param_type.value_owned = true;
1925 bool is_unowned_delegate = acc.value_parameter.variable_type is DelegateType && !acc.value_parameter.variable_type.value_owned;
1927 if (requires_destroy (param_type) && !is_unowned_delegate) {
1928 ccode.add_expression (destroy_parameter (acc.value_parameter));
1933 var data_free = new CCodeFunctionCall (new CCodeIdentifier ("g_slice_free"));
1934 data_free.add_argument (new CCodeIdentifier (struct_name));
1935 data_free.add_argument (new CCodeIdentifier ("_data%d_".printf (block_id)));
1936 ccode.add_expression (data_free);
1938 ccode.close ();
1940 pop_function ();
1942 cfile.add_function (unref_fun);
1945 foreach (Statement stmt in b.get_statements ()) {
1946 stmt.emit (this);
1949 // free in reverse order
1950 for (int i = local_vars.size - 1; i >= 0; i--) {
1951 var local = local_vars[i];
1952 local.active = false;
1953 if (!local.unreachable && !local.floating && !local.captured && requires_destroy (local.variable_type)) {
1954 ccode.add_expression (destroy_local (local));
1958 if (b.parent_symbol is Method) {
1959 var m = (Method) b.parent_symbol;
1960 foreach (Parameter param in m.get_parameters ()) {
1961 if (!param.captured && !param.ellipsis && requires_destroy (param.variable_type) && param.direction == ParameterDirection.IN) {
1962 ccode.add_expression (destroy_parameter (param));
1963 } else if (param.direction == ParameterDirection.OUT && !m.coroutine) {
1964 return_out_parameter (param);
1969 if (b.captured) {
1970 int block_id = get_block_id (b);
1972 var data_unref = new CCodeFunctionCall (new CCodeIdentifier ("block%d_data_unref".printf (block_id)));
1973 data_unref.add_argument (get_variable_cexpression ("_data%d_".printf (block_id)));
1974 ccode.add_expression (data_unref);
1975 ccode.add_assignment (get_variable_cexpression ("_data%d_".printf (block_id)), new CCodeConstant ("NULL"));
1978 if (b.parent_node is Block || b.parent_node is SwitchStatement) {
1979 ccode.close ();
1982 emit_context.pop_symbol ();
1985 public override void visit_declaration_statement (DeclarationStatement stmt) {
1986 stmt.declaration.accept (this);
1989 public CCodeExpression get_variable_cexpression (string name) {
1990 if (is_in_coroutine ()) {
1991 return new CCodeMemberAccess.pointer (new CCodeIdentifier ("data"), get_variable_cname (name));
1992 } else {
1993 return new CCodeIdentifier (get_variable_cname (name));
1997 public string get_variable_cname (string name) {
1998 if (name[0] == '.') {
1999 if (name == ".result") {
2000 return "result";
2002 // compiler-internal variable
2003 if (!variable_name_map.contains (name)) {
2004 variable_name_map.set (name, "_tmp%d_".printf (next_temp_var_id));
2005 next_temp_var_id++;
2007 return variable_name_map.get (name);
2008 } else if (reserved_identifiers.contains (name)) {
2009 return "_%s_".printf (name);
2010 } else {
2011 return name;
2015 public CCodeExpression get_result_cexpression (string cname = "result") {
2016 if (is_in_coroutine ()) {
2017 return new CCodeMemberAccess.pointer (new CCodeIdentifier ("data"), cname);
2018 } else {
2019 return new CCodeIdentifier (cname);
2023 bool has_simple_struct_initializer (LocalVariable local) {
2024 var st = local.variable_type.data_type as Struct;
2025 var initializer = local.initializer as ObjectCreationExpression;
2026 if (st != null && (!st.is_simple_type () || st.get_cname () == "va_list") && !local.variable_type.nullable &&
2027 initializer != null && initializer.get_object_initializer ().size == 0) {
2028 return true;
2029 } else {
2030 return false;
2034 public override void visit_local_variable (LocalVariable local) {
2035 check_type (local.variable_type);
2037 if (local.initializer != null) {
2038 local.initializer.emit (this);
2040 visit_end_full_expression (local.initializer);
2043 generate_type_declaration (local.variable_type, cfile);
2045 CCodeExpression rhs = null;
2046 if (local.initializer != null && get_cvalue (local.initializer) != null) {
2047 rhs = get_cvalue (local.initializer);
2050 if (!local.captured) {
2051 if (current_method != null && current_method.coroutine) {
2052 closure_struct.add_field (local.variable_type.get_cname (), get_variable_cname (local.name) + local.variable_type.get_cdeclarator_suffix ());
2053 } else {
2054 var cvar = new CCodeVariableDeclarator (get_variable_cname (local.name), null, local.variable_type.get_cdeclarator_suffix ());
2056 // try to initialize uninitialized variables
2057 // initialization not necessary for variables stored in closure
2058 if (rhs == null || has_simple_struct_initializer (local)) {
2059 cvar.initializer = default_value_for_type (local.variable_type, true);
2060 cvar.init0 = true;
2063 ccode.add_declaration (local.variable_type.get_cname (), cvar);
2066 if (local.variable_type is ArrayType) {
2067 // create variables to store array dimensions
2068 var array_type = (ArrayType) local.variable_type;
2070 if (!array_type.fixed_length) {
2071 for (int dim = 1; dim <= array_type.rank; dim++) {
2072 var len_var = new LocalVariable (int_type.copy (), get_array_length_cname (get_variable_cname (local.name), dim));
2073 emit_temp_var (len_var, local.initializer == null);
2076 if (array_type.rank == 1) {
2077 var size_var = new LocalVariable (int_type.copy (), get_array_size_cname (get_variable_cname (local.name)));
2078 emit_temp_var (size_var, local.initializer == null);
2081 } else if (local.variable_type is DelegateType) {
2082 var deleg_type = (DelegateType) local.variable_type;
2083 var d = deleg_type.delegate_symbol;
2084 if (d.has_target) {
2085 // create variable to store delegate target
2086 var target_var = new LocalVariable (new PointerType (new VoidType ()), get_delegate_target_cname (get_variable_cname (local.name)));
2087 emit_temp_var (target_var, local.initializer == null);
2088 if (deleg_type.value_owned) {
2089 var target_destroy_notify_var = new LocalVariable (gdestroynotify_type, get_delegate_target_destroy_notify_cname (get_variable_cname (local.name)));
2090 emit_temp_var (target_destroy_notify_var, local.initializer == null);
2096 if (rhs != null) {
2097 if (!has_simple_struct_initializer (local)) {
2098 store_local (local, local.initializer.target_value, true);
2102 if (local.initializer != null && local.initializer.tree_can_fail) {
2103 add_simple_check (local.initializer);
2106 local.active = true;
2109 public override void visit_initializer_list (InitializerList list) {
2110 if (list.target_type.data_type is Struct) {
2111 /* initializer is used as struct initializer */
2112 var st = (Struct) list.target_type.data_type;
2114 if (list.parent_node is Constant || list.parent_node is Field || list.parent_node is InitializerList) {
2115 var clist = new CCodeInitializerList ();
2117 var field_it = st.get_fields ().iterator ();
2118 foreach (Expression expr in list.get_initializers ()) {
2119 Field field = null;
2120 while (field == null) {
2121 field_it.next ();
2122 field = field_it.get ();
2123 if (field.binding != MemberBinding.INSTANCE) {
2124 // we only initialize instance fields
2125 field = null;
2129 var cexpr = get_cvalue (expr);
2131 string ctype = field.get_ctype ();
2132 if (ctype != null) {
2133 cexpr = new CCodeCastExpression (cexpr, ctype);
2136 clist.append (cexpr);
2139 set_cvalue (list, clist);
2140 } else {
2141 // used as expression
2142 var temp_decl = get_temp_variable (list.target_type, false, list);
2143 emit_temp_var (temp_decl);
2145 var instance = get_variable_cexpression (get_variable_cname (temp_decl.name));
2147 var field_it = st.get_fields ().iterator ();
2148 foreach (Expression expr in list.get_initializers ()) {
2149 Field field = null;
2150 while (field == null) {
2151 field_it.next ();
2152 field = field_it.get ();
2153 if (field.binding != MemberBinding.INSTANCE) {
2154 // we only initialize instance fields
2155 field = null;
2159 var cexpr = get_cvalue (expr);
2161 string ctype = field.get_ctype ();
2162 if (ctype != null) {
2163 cexpr = new CCodeCastExpression (cexpr, ctype);
2166 var lhs = new CCodeMemberAccess (instance, field.get_cname ());;
2167 ccode.add_assignment (lhs, cexpr);
2170 set_cvalue (list, instance);
2172 } else {
2173 var clist = new CCodeInitializerList ();
2174 foreach (Expression expr in list.get_initializers ()) {
2175 clist.append (get_cvalue (expr));
2177 set_cvalue (list, clist);
2181 public override LocalVariable create_local (DataType type) {
2182 var result = get_temp_variable (type, type.value_owned);
2183 emit_temp_var (result);
2184 return result;
2187 public LocalVariable get_temp_variable (DataType type, bool value_owned = true, CodeNode? node_reference = null, bool init = true) {
2188 var var_type = type.copy ();
2189 var_type.value_owned = value_owned;
2190 var local = new LocalVariable (var_type, "_tmp%d_".printf (next_temp_var_id));
2191 local.no_init = !init;
2193 if (node_reference != null) {
2194 local.source_reference = node_reference.source_reference;
2197 next_temp_var_id++;
2199 return local;
2202 bool is_in_generic_type (DataType type) {
2203 if (current_symbol != null && type.type_parameter.parent_symbol is TypeSymbol
2204 && (current_method == null || current_method.binding == MemberBinding.INSTANCE)) {
2205 return true;
2206 } else {
2207 return false;
2211 public CCodeExpression get_type_id_expression (DataType type, bool is_chainup = false) {
2212 if (type is GenericType) {
2213 string var_name = "%s_type".printf (type.type_parameter.name.down ());
2214 if (is_in_generic_type (type) && !is_chainup && !in_creation_method) {
2215 return new CCodeMemberAccess.pointer (new CCodeMemberAccess.pointer (get_result_cexpression ("self"), "priv"), var_name);
2216 } else {
2217 return new CCodeIdentifier (var_name);
2219 } else {
2220 string type_id = type.get_type_id ();
2221 if (type_id == null) {
2222 type_id = "G_TYPE_INVALID";
2223 } else {
2224 generate_type_declaration (type, cfile);
2226 return new CCodeIdentifier (type_id);
2230 public virtual CCodeExpression? get_dup_func_expression (DataType type, SourceReference? source_reference, bool is_chainup = false) {
2231 if (type is ErrorType) {
2232 return new CCodeIdentifier ("g_error_copy");
2233 } else if (type.data_type != null) {
2234 string dup_function;
2235 var cl = type.data_type as Class;
2236 if (type.data_type.is_reference_counting ()) {
2237 dup_function = type.data_type.get_ref_function ();
2238 if (type.data_type is Interface && dup_function == null) {
2239 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 ()));
2240 return null;
2242 } else if (cl != null && cl.is_immutable) {
2243 // allow duplicates of immutable instances as for example strings
2244 dup_function = type.data_type.get_dup_function ();
2245 if (dup_function == null) {
2246 dup_function = "";
2248 } else if (cl != null && cl.is_gboxed) {
2249 // allow duplicates of gboxed instances
2250 dup_function = generate_dup_func_wrapper (type);
2251 if (dup_function == null) {
2252 dup_function = "";
2254 } else if (type is ValueType) {
2255 dup_function = type.data_type.get_dup_function ();
2256 if (dup_function == null && type.nullable) {
2257 dup_function = generate_struct_dup_wrapper ((ValueType) type);
2258 } else if (dup_function == null) {
2259 dup_function = "";
2261 } else {
2262 // duplicating non-reference counted objects may cause side-effects (and performance issues)
2263 Report.error (source_reference, "duplicating %s instance, use unowned variable or explicitly invoke copy method".printf (type.data_type.name));
2264 return null;
2267 return new CCodeIdentifier (dup_function);
2268 } else if (type.type_parameter != null) {
2269 string func_name = "%s_dup_func".printf (type.type_parameter.name.down ());
2270 if (is_in_generic_type (type) && !is_chainup && !in_creation_method) {
2271 return new CCodeMemberAccess.pointer (new CCodeMemberAccess.pointer (get_result_cexpression ("self"), "priv"), func_name);
2272 } else {
2273 return new CCodeIdentifier (func_name);
2275 } else if (type is PointerType) {
2276 var pointer_type = (PointerType) type;
2277 return get_dup_func_expression (pointer_type.base_type, source_reference);
2278 } else {
2279 return new CCodeConstant ("NULL");
2283 void make_comparable_cexpression (ref DataType left_type, ref CCodeExpression cleft, ref DataType right_type, ref CCodeExpression cright) {
2284 var left_type_as_struct = left_type.data_type as Struct;
2285 var right_type_as_struct = right_type.data_type as Struct;
2287 // GValue support
2288 var valuecast = try_cast_value_to_type (cleft, left_type, right_type);
2289 if (valuecast != null) {
2290 cleft = valuecast;
2291 left_type = right_type;
2292 make_comparable_cexpression (ref left_type, ref cleft, ref right_type, ref cright);
2293 return;
2296 valuecast = try_cast_value_to_type (cright, right_type, left_type);
2297 if (valuecast != null) {
2298 cright = valuecast;
2299 right_type = left_type;
2300 make_comparable_cexpression (ref left_type, ref cleft, ref right_type, ref cright);
2301 return;
2304 if (left_type.data_type is Class && !((Class) left_type.data_type).is_compact &&
2305 right_type.data_type is Class && !((Class) right_type.data_type).is_compact) {
2306 var left_cl = (Class) left_type.data_type;
2307 var right_cl = (Class) right_type.data_type;
2309 if (left_cl != right_cl) {
2310 if (left_cl.is_subtype_of (right_cl)) {
2311 cleft = generate_instance_cast (cleft, right_cl);
2312 } else if (right_cl.is_subtype_of (left_cl)) {
2313 cright = generate_instance_cast (cright, left_cl);
2316 } else if (left_type_as_struct != null && right_type_as_struct != null) {
2317 if (left_type is StructValueType) {
2318 // real structs (uses compare/equal function)
2319 if (!left_type.nullable) {
2320 cleft = new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, cleft);
2322 if (!right_type.nullable) {
2323 cright = new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, cright);
2325 } else {
2326 // integer or floating or boolean type
2327 if (left_type.nullable && right_type.nullable) {
2328 // FIXME also compare contents, not just address
2329 } else if (left_type.nullable) {
2330 // FIXME check left value is not null
2331 cleft = new CCodeUnaryExpression (CCodeUnaryOperator.POINTER_INDIRECTION, cleft);
2332 } else if (right_type.nullable) {
2333 // FIXME check right value is not null
2334 cright = new CCodeUnaryExpression (CCodeUnaryOperator.POINTER_INDIRECTION, cright);
2340 private string generate_struct_equal_function (Struct st) {
2341 string equal_func = "_%sequal".printf (st.get_lower_case_cprefix ());
2343 if (!add_wrapper (equal_func)) {
2344 // wrapper already defined
2345 return equal_func;
2348 var function = new CCodeFunction (equal_func, "gboolean");
2349 function.modifiers = CCodeModifiers.STATIC;
2351 function.add_parameter (new CCodeParameter ("s1", "const " + st.get_cname () + "*"));
2352 function.add_parameter (new CCodeParameter ("s2", "const " + st.get_cname () + "*"));
2354 push_function (function);
2356 // if (s1 == s2) return TRUE;
2358 var cexp = new CCodeBinaryExpression (CCodeBinaryOperator.EQUALITY, new CCodeIdentifier ("s1"), new CCodeIdentifier ("s2"));
2359 ccode.open_if (cexp);
2360 ccode.add_return (new CCodeConstant ("TRUE"));
2361 ccode.close ();
2363 // if (s1 == NULL || s2 == NULL) return FALSE;
2365 var cexp = new CCodeBinaryExpression (CCodeBinaryOperator.EQUALITY, new CCodeIdentifier ("s1"), new CCodeConstant ("NULL"));
2366 ccode.open_if (cexp);
2367 ccode.add_return (new CCodeConstant ("FALSE"));
2368 ccode.close ();
2370 cexp = new CCodeBinaryExpression (CCodeBinaryOperator.EQUALITY, new CCodeIdentifier ("s2"), new CCodeConstant ("NULL"));
2371 ccode.open_if (cexp);
2372 ccode.add_return (new CCodeConstant ("FALSE"));
2373 ccode.close ();
2376 foreach (Field f in st.get_fields ()) {
2377 if (f.binding != MemberBinding.INSTANCE) {
2378 // we only compare instance fields
2379 continue;
2382 CCodeExpression cexp; // if (cexp) return FALSE;
2383 var s1 = (CCodeExpression) new CCodeMemberAccess.pointer (new CCodeIdentifier ("s1"), f.name); // s1->f
2384 var s2 = (CCodeExpression) new CCodeMemberAccess.pointer (new CCodeIdentifier ("s2"), f.name); // s2->f
2386 var variable_type = f.variable_type.copy ();
2387 make_comparable_cexpression (ref variable_type, ref s1, ref variable_type, ref s2);
2389 if (!(f.variable_type is NullType) && f.variable_type.compatible (string_type)) {
2390 var ccall = new CCodeFunctionCall (new CCodeIdentifier ("g_strcmp0"));
2391 ccall.add_argument (s1);
2392 ccall.add_argument (s2);
2393 cexp = ccall;
2394 } else if (f.variable_type is StructValueType) {
2395 var equalfunc = generate_struct_equal_function (f.variable_type.data_type as Struct);
2396 var ccall = new CCodeFunctionCall (new CCodeIdentifier (equalfunc));
2397 ccall.add_argument (s1);
2398 ccall.add_argument (s2);
2399 cexp = new CCodeUnaryExpression (CCodeUnaryOperator.LOGICAL_NEGATION, ccall);
2400 } else {
2401 cexp = new CCodeBinaryExpression (CCodeBinaryOperator.INEQUALITY, s1, s2);
2404 ccode.open_if (cexp);
2405 ccode.add_return (new CCodeConstant ("FALSE"));
2406 ccode.close ();
2409 if (st.get_fields().size == 0) {
2410 // either opaque structure or simple type
2411 if (st.is_simple_type ()) {
2412 var cexp = new CCodeBinaryExpression (CCodeBinaryOperator.EQUALITY, new CCodeUnaryExpression (CCodeUnaryOperator.POINTER_INDIRECTION, new CCodeIdentifier ("s1")), new CCodeUnaryExpression (CCodeUnaryOperator.POINTER_INDIRECTION, new CCodeIdentifier ("s2")));
2413 ccode.add_return (cexp);
2414 } else {
2415 ccode.add_return (new CCodeConstant ("FALSE"));
2417 } else {
2418 ccode.add_return (new CCodeConstant ("TRUE"));
2421 pop_function ();
2423 cfile.add_function_declaration (function);
2424 cfile.add_function (function);
2426 return equal_func;
2429 private string generate_numeric_equal_function (Struct st) {
2430 string equal_func = "_%sequal".printf (st.get_lower_case_cprefix ());
2432 if (!add_wrapper (equal_func)) {
2433 // wrapper already defined
2434 return equal_func;
2437 var function = new CCodeFunction (equal_func, "gboolean");
2438 function.modifiers = CCodeModifiers.STATIC;
2440 function.add_parameter (new CCodeParameter ("s1", "const " + st.get_cname () + "*"));
2441 function.add_parameter (new CCodeParameter ("s2", "const " + st.get_cname () + "*"));
2443 push_function (function);
2445 // if (s1 == s2) return TRUE;
2447 var cexp = new CCodeBinaryExpression (CCodeBinaryOperator.EQUALITY, new CCodeIdentifier ("s1"), new CCodeIdentifier ("s2"));
2448 ccode.open_if (cexp);
2449 ccode.add_return (new CCodeConstant ("TRUE"));
2450 ccode.close ();
2452 // if (s1 == NULL || s2 == NULL) return FALSE;
2454 var cexp = new CCodeBinaryExpression (CCodeBinaryOperator.EQUALITY, new CCodeIdentifier ("s1"), new CCodeConstant ("NULL"));
2455 ccode.open_if (cexp);
2456 ccode.add_return (new CCodeConstant ("FALSE"));
2457 ccode.close ();
2459 cexp = new CCodeBinaryExpression (CCodeBinaryOperator.EQUALITY, new CCodeIdentifier ("s2"), new CCodeConstant ("NULL"));
2460 ccode.open_if (cexp);
2461 ccode.add_return (new CCodeConstant ("FALSE"));
2462 ccode.close ();
2464 // return (*s1 == *s2);
2466 var cexp = new CCodeBinaryExpression (CCodeBinaryOperator.EQUALITY, new CCodeUnaryExpression (CCodeUnaryOperator.POINTER_INDIRECTION, new CCodeIdentifier ("s1")), new CCodeUnaryExpression (CCodeUnaryOperator.POINTER_INDIRECTION, new CCodeIdentifier ("s2")));
2467 ccode.add_return (cexp);
2470 pop_function ();
2472 cfile.add_function_declaration (function);
2473 cfile.add_function (function);
2475 return equal_func;
2478 private string generate_struct_dup_wrapper (ValueType value_type) {
2479 string dup_func = "_%sdup".printf (value_type.type_symbol.get_lower_case_cprefix ());
2481 if (!add_wrapper (dup_func)) {
2482 // wrapper already defined
2483 return dup_func;
2486 var function = new CCodeFunction (dup_func, value_type.get_cname ());
2487 function.modifiers = CCodeModifiers.STATIC;
2489 function.add_parameter (new CCodeParameter ("self", value_type.get_cname ()));
2491 push_function (function);
2493 if (value_type.type_symbol == gvalue_type) {
2494 var dup_call = new CCodeFunctionCall (new CCodeIdentifier ("g_boxed_copy"));
2495 dup_call.add_argument (new CCodeIdentifier ("G_TYPE_VALUE"));
2496 dup_call.add_argument (new CCodeIdentifier ("self"));
2498 ccode.add_return (dup_call);
2499 } else {
2500 ccode.add_declaration (value_type.get_cname (), new CCodeVariableDeclarator ("dup"));
2502 var creation_call = new CCodeFunctionCall (new CCodeIdentifier ("g_new0"));
2503 creation_call.add_argument (new CCodeConstant (value_type.data_type.get_cname ()));
2504 creation_call.add_argument (new CCodeConstant ("1"));
2505 ccode.add_assignment (new CCodeIdentifier ("dup"), creation_call);
2507 var st = value_type.data_type as Struct;
2508 if (st != null && st.is_disposable ()) {
2509 if (!st.has_copy_function) {
2510 generate_struct_copy_function (st);
2513 var copy_call = new CCodeFunctionCall (new CCodeIdentifier (st.get_copy_function ()));
2514 copy_call.add_argument (new CCodeIdentifier ("self"));
2515 copy_call.add_argument (new CCodeIdentifier ("dup"));
2516 ccode.add_expression (copy_call);
2517 } else {
2518 cfile.add_include ("string.h");
2520 var sizeof_call = new CCodeFunctionCall (new CCodeIdentifier ("sizeof"));
2521 sizeof_call.add_argument (new CCodeConstant (value_type.data_type.get_cname ()));
2523 var copy_call = new CCodeFunctionCall (new CCodeIdentifier ("memcpy"));
2524 copy_call.add_argument (new CCodeIdentifier ("dup"));
2525 copy_call.add_argument (new CCodeIdentifier ("self"));
2526 copy_call.add_argument (sizeof_call);
2527 ccode.add_expression (copy_call);
2530 ccode.add_return (new CCodeIdentifier ("dup"));
2533 pop_function ();
2535 cfile.add_function_declaration (function);
2536 cfile.add_function (function);
2538 return dup_func;
2541 protected string generate_dup_func_wrapper (DataType type) {
2542 string destroy_func = "_vala_%s_copy".printf (type.data_type.get_cname ());
2544 if (!add_wrapper (destroy_func)) {
2545 // wrapper already defined
2546 return destroy_func;
2549 var function = new CCodeFunction (destroy_func, type.get_cname ());
2550 function.modifiers = CCodeModifiers.STATIC;
2551 function.add_parameter (new CCodeParameter ("self", type.get_cname ()));
2553 push_function (function);
2555 var cl = type.data_type as Class;
2556 assert (cl != null && cl.is_gboxed);
2558 var free_call = new CCodeFunctionCall (new CCodeIdentifier ("g_boxed_copy"));
2559 free_call.add_argument (new CCodeIdentifier (cl.get_type_id ()));
2560 free_call.add_argument (new CCodeIdentifier ("self"));
2562 ccode.add_return (free_call);
2564 pop_function ();
2566 cfile.add_function_declaration (function);
2567 cfile.add_function (function);
2569 return destroy_func;
2572 protected string generate_free_func_wrapper (DataType type) {
2573 string destroy_func = "_vala_%s_free".printf (type.data_type.get_cname ());
2575 if (!add_wrapper (destroy_func)) {
2576 // wrapper already defined
2577 return destroy_func;
2580 var function = new CCodeFunction (destroy_func, "void");
2581 function.modifiers = CCodeModifiers.STATIC;
2582 function.add_parameter (new CCodeParameter ("self", type.get_cname ()));
2584 push_function (function);
2586 var cl = type.data_type as Class;
2587 if (cl != null && cl.is_gboxed) {
2588 var free_call = new CCodeFunctionCall (new CCodeIdentifier ("g_boxed_free"));
2589 free_call.add_argument (new CCodeIdentifier (cl.get_type_id ()));
2590 free_call.add_argument (new CCodeIdentifier ("self"));
2592 ccode.add_expression (free_call);
2593 } else if (cl != null) {
2594 assert (cl.free_function_address_of);
2596 var free_call = new CCodeFunctionCall (new CCodeIdentifier (type.data_type.get_free_function ()));
2597 free_call.add_argument (new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, new CCodeIdentifier ("self")));
2599 ccode.add_expression (free_call);
2600 } else {
2601 var st = type.data_type as Struct;
2602 if (st != null && st.is_disposable ()) {
2603 if (!st.has_destroy_function) {
2604 generate_struct_destroy_function (st);
2607 var destroy_call = new CCodeFunctionCall (new CCodeIdentifier (st.get_destroy_function ()));
2608 destroy_call.add_argument (new CCodeIdentifier ("self"));
2609 ccode.add_expression (destroy_call);
2612 var free_call = new CCodeFunctionCall (new CCodeIdentifier ("g_free"));
2613 free_call.add_argument (new CCodeIdentifier ("self"));
2615 ccode.add_expression (free_call);
2618 pop_function ();
2620 cfile.add_function_declaration (function);
2621 cfile.add_function (function);
2623 return destroy_func;
2626 public CCodeExpression? get_destroy0_func_expression (DataType type, bool is_chainup = false) {
2627 var element_destroy_func_expression = get_destroy_func_expression (type, is_chainup);
2629 if (element_destroy_func_expression is CCodeIdentifier) {
2630 var freeid = (CCodeIdentifier) element_destroy_func_expression;
2631 string free0_func = "_%s0_".printf (freeid.name);
2633 if (add_wrapper (free0_func)) {
2634 var function = new CCodeFunction (free0_func, "void");
2635 function.modifiers = CCodeModifiers.STATIC;
2637 function.add_parameter (new CCodeParameter ("var", "gpointer"));
2639 push_function (function);
2641 ccode.add_expression (get_unref_expression (new CCodeIdentifier ("var"), type, null, true));
2643 pop_function ();
2645 cfile.add_function_declaration (function);
2646 cfile.add_function (function);
2649 element_destroy_func_expression = new CCodeIdentifier (free0_func);
2652 return element_destroy_func_expression;
2655 public CCodeExpression? get_destroy_func_expression (DataType type, bool is_chainup = false) {
2656 if (context.profile == Profile.GOBJECT && (type.data_type == glist_type || type.data_type == gslist_type || type.data_type == gnode_type)) {
2657 // create wrapper function to free list elements if necessary
2659 bool elements_require_free = false;
2660 CCodeExpression element_destroy_func_expression = null;
2662 foreach (DataType type_arg in type.get_type_arguments ()) {
2663 elements_require_free = requires_destroy (type_arg);
2664 if (elements_require_free) {
2665 element_destroy_func_expression = get_destroy0_func_expression (type_arg);
2669 if (elements_require_free && element_destroy_func_expression is CCodeIdentifier) {
2670 return new CCodeIdentifier (generate_collection_free_wrapper (type, (CCodeIdentifier) element_destroy_func_expression));
2671 } else {
2672 return new CCodeIdentifier (type.data_type.get_free_function ());
2674 } else if (type is ErrorType) {
2675 return new CCodeIdentifier ("g_error_free");
2676 } else if (type.data_type != null) {
2677 string unref_function;
2678 if (type is ReferenceType) {
2679 if (type.data_type.is_reference_counting ()) {
2680 unref_function = type.data_type.get_unref_function ();
2681 if (type.data_type is Interface && unref_function == null) {
2682 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 ()));
2683 return null;
2685 } else {
2686 var cl = type.data_type as Class;
2687 if (cl != null && (cl.free_function_address_of || cl.is_gboxed)) {
2688 unref_function = generate_free_func_wrapper (type);
2689 } else {
2690 unref_function = type.data_type.get_free_function ();
2693 } else {
2694 if (type.nullable) {
2695 unref_function = type.data_type.get_free_function ();
2696 if (unref_function == null) {
2697 if (type.data_type is Struct && ((Struct) type.data_type).is_disposable ()) {
2698 unref_function = generate_free_func_wrapper (type);
2699 } else {
2700 unref_function = "g_free";
2703 } else {
2704 var st = (Struct) type.data_type;
2705 if (!st.has_destroy_function) {
2706 generate_struct_destroy_function (st);
2708 unref_function = st.get_destroy_function ();
2711 if (unref_function == null) {
2712 return new CCodeConstant ("NULL");
2714 return new CCodeIdentifier (unref_function);
2715 } else if (type.type_parameter != null && current_type_symbol is Class) {
2716 string func_name = "%s_destroy_func".printf (type.type_parameter.name.down ());
2717 if (is_in_generic_type (type) && !is_chainup && !in_creation_method) {
2718 return new CCodeMemberAccess.pointer (new CCodeMemberAccess.pointer (get_result_cexpression ("self"), "priv"), func_name);
2719 } else {
2720 return new CCodeIdentifier (func_name);
2722 } else if (type is ArrayType) {
2723 if (context.profile == Profile.POSIX) {
2724 return new CCodeIdentifier ("free");
2725 } else {
2726 return new CCodeIdentifier ("g_free");
2728 } else if (type is PointerType) {
2729 if (context.profile == Profile.POSIX) {
2730 return new CCodeIdentifier ("free");
2731 } else {
2732 return new CCodeIdentifier ("g_free");
2734 } else {
2735 return new CCodeConstant ("NULL");
2739 private string generate_collection_free_wrapper (DataType collection_type, CCodeIdentifier element_destroy_func_expression) {
2740 string destroy_func = "_%s_%s".printf (collection_type.data_type.get_free_function (), element_destroy_func_expression.name);
2742 if (!add_wrapper (destroy_func)) {
2743 // wrapper already defined
2744 return destroy_func;
2747 var function = new CCodeFunction (destroy_func, "void");
2748 function.modifiers = CCodeModifiers.STATIC;
2750 function.add_parameter (new CCodeParameter ("self", collection_type.get_cname ()));
2752 push_function (function);
2754 CCodeFunctionCall element_free_call;
2755 if (collection_type.data_type == gnode_type) {
2756 /* A wrapper which converts GNodeTraverseFunc into GDestroyNotify */
2757 string destroy_node_func = "%s_node".printf (destroy_func);
2758 var wrapper = new CCodeFunction (destroy_node_func, "gboolean");
2759 wrapper.modifiers = CCodeModifiers.STATIC;
2760 wrapper.add_parameter (new CCodeParameter ("node", collection_type.get_cname ()));
2761 wrapper.add_parameter (new CCodeParameter ("unused", "gpointer"));
2762 var wrapper_block = new CCodeBlock ();
2763 var free_call = new CCodeFunctionCall (element_destroy_func_expression);
2764 free_call.add_argument (new CCodeMemberAccess.pointer(new CCodeIdentifier("node"), "data"));
2765 wrapper_block.add_statement (new CCodeExpressionStatement (free_call));
2766 wrapper_block.add_statement (new CCodeReturnStatement (new CCodeConstant ("FALSE")));
2767 cfile.add_function_declaration (function);
2768 wrapper.block = wrapper_block;
2769 cfile.add_function (wrapper);
2771 /* Now the code to call g_traverse with the above */
2772 element_free_call = new CCodeFunctionCall (new CCodeIdentifier ("g_node_traverse"));
2773 element_free_call.add_argument (new CCodeIdentifier("self"));
2774 element_free_call.add_argument (new CCodeConstant ("G_POST_ORDER"));
2775 element_free_call.add_argument (new CCodeConstant ("G_TRAVERSE_ALL"));
2776 element_free_call.add_argument (new CCodeConstant ("-1"));
2777 element_free_call.add_argument (new CCodeIdentifier (destroy_node_func));
2778 element_free_call.add_argument (new CCodeConstant ("NULL"));
2779 } else {
2780 if (collection_type.data_type == glist_type) {
2781 element_free_call = new CCodeFunctionCall (new CCodeIdentifier ("g_list_foreach"));
2782 } else {
2783 element_free_call = new CCodeFunctionCall (new CCodeIdentifier ("g_slist_foreach"));
2786 element_free_call.add_argument (new CCodeIdentifier ("self"));
2787 element_free_call.add_argument (new CCodeCastExpression (element_destroy_func_expression, "GFunc"));
2788 element_free_call.add_argument (new CCodeConstant ("NULL"));
2791 ccode.add_expression (element_free_call);
2793 var cfreecall = new CCodeFunctionCall (new CCodeIdentifier (collection_type.data_type.get_free_function ()));
2794 cfreecall.add_argument (new CCodeIdentifier ("self"));
2795 ccode.add_expression (cfreecall);
2797 pop_function ();
2799 cfile.add_function_declaration (function);
2800 cfile.add_function (function);
2802 return destroy_func;
2805 public virtual string? append_struct_array_free (Struct st) {
2806 return null;
2809 // logic in this method is temporarily duplicated in destroy_value
2810 // apply changes to both methods
2811 public virtual CCodeExpression destroy_variable (Variable variable, TargetValue target_lvalue) {
2812 var type = target_lvalue.value_type;
2813 var cvar = get_cvalue_ (target_lvalue);
2815 if (type is DelegateType) {
2816 var delegate_target = get_delegate_target_cvalue (target_lvalue);
2817 var delegate_target_destroy_notify = get_delegate_target_destroy_notify_cvalue (target_lvalue);
2819 var ccall = new CCodeFunctionCall (delegate_target_destroy_notify);
2820 ccall.add_argument (delegate_target);
2822 var destroy_call = new CCodeCommaExpression ();
2823 destroy_call.append_expression (ccall);
2824 destroy_call.append_expression (new CCodeConstant ("NULL"));
2826 var cisnull = new CCodeBinaryExpression (CCodeBinaryOperator.EQUALITY, delegate_target_destroy_notify, new CCodeConstant ("NULL"));
2828 var ccomma = new CCodeCommaExpression ();
2829 ccomma.append_expression (new CCodeConditionalExpression (cisnull, new CCodeConstant ("NULL"), destroy_call));
2830 ccomma.append_expression (new CCodeAssignment (cvar, new CCodeConstant ("NULL")));
2831 ccomma.append_expression (new CCodeAssignment (delegate_target, new CCodeConstant ("NULL")));
2832 ccomma.append_expression (new CCodeAssignment (delegate_target_destroy_notify, new CCodeConstant ("NULL")));
2834 return ccomma;
2837 var ccall = new CCodeFunctionCall (get_destroy_func_expression (type));
2839 if (type is ValueType && !type.nullable) {
2840 // normal value type, no null check
2841 var st = type.data_type as Struct;
2842 if (st != null && st.is_simple_type ()) {
2843 // used for va_list
2844 ccall.add_argument (cvar);
2845 } else {
2846 ccall.add_argument (new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, cvar));
2849 if (gvalue_type != null && type.data_type == gvalue_type) {
2850 // g_value_unset must not be called for already unset values
2851 var cisvalid = new CCodeFunctionCall (new CCodeIdentifier ("G_IS_VALUE"));
2852 cisvalid.add_argument (new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, cvar));
2854 var ccomma = new CCodeCommaExpression ();
2855 ccomma.append_expression (ccall);
2856 ccomma.append_expression (new CCodeConstant ("NULL"));
2858 return new CCodeConditionalExpression (cisvalid, ccomma, new CCodeConstant ("NULL"));
2859 } else {
2860 return ccall;
2864 if (ccall.call is CCodeIdentifier && !(type is ArrayType)) {
2865 // generate and use NULL-aware free macro to simplify code
2867 var freeid = (CCodeIdentifier) ccall.call;
2868 string free0_func = "_%s0".printf (freeid.name);
2870 if (add_wrapper (free0_func)) {
2871 var macro = destroy_value (new GLibValue (type, new CCodeIdentifier ("var")), true);
2872 cfile.add_type_declaration (new CCodeMacroReplacement.with_expression ("%s(var)".printf (free0_func), macro));
2875 ccall = new CCodeFunctionCall (new CCodeIdentifier (free0_func));
2876 ccall.add_argument (cvar);
2877 return ccall;
2880 /* (foo == NULL ? NULL : foo = (unref (foo), NULL)) */
2882 /* can be simplified to
2883 * foo = (unref (foo), NULL)
2884 * if foo is of static type non-null
2887 var cisnull = new CCodeBinaryExpression (CCodeBinaryOperator.EQUALITY, cvar, new CCodeConstant ("NULL"));
2888 if (type.type_parameter != null) {
2889 if (!(current_type_symbol is Class) || current_class.is_compact) {
2890 return new CCodeConstant ("NULL");
2893 // unref functions are optional for type parameters
2894 var cunrefisnull = new CCodeBinaryExpression (CCodeBinaryOperator.EQUALITY, get_destroy_func_expression (type), new CCodeConstant ("NULL"));
2895 cisnull = new CCodeBinaryExpression (CCodeBinaryOperator.OR, cisnull, cunrefisnull);
2898 ccall.add_argument (cvar);
2900 /* set freed references to NULL to prevent further use */
2901 var ccomma = new CCodeCommaExpression ();
2903 if (context.profile == Profile.GOBJECT) {
2904 if (type.data_type != null && !type.data_type.is_reference_counting () &&
2905 (type.data_type == gstringbuilder_type
2906 || type.data_type == garray_type
2907 || type.data_type == gbytearray_type
2908 || type.data_type == gptrarray_type)) {
2909 ccall.add_argument (new CCodeConstant ("TRUE"));
2910 } else if (type.data_type == gthreadpool_type) {
2911 ccall.add_argument (new CCodeConstant ("FALSE"));
2912 ccall.add_argument (new CCodeConstant ("TRUE"));
2913 } else if (type is ArrayType) {
2914 var array_type = (ArrayType) type;
2915 if (requires_destroy (array_type.element_type)) {
2916 CCodeExpression csizeexpr = null;
2917 if (variable.array_null_terminated) {
2918 var len_call = new CCodeFunctionCall (new CCodeIdentifier ("_vala_array_length"));
2919 len_call.add_argument (cvar);
2920 csizeexpr = len_call;
2921 } else if (variable.has_array_length_cexpr) {
2922 csizeexpr = new CCodeConstant (variable.get_array_length_cexpr ());
2923 } else if (!variable.no_array_length) {
2924 bool first = true;
2925 for (int dim = 1; dim <= array_type.rank; dim++) {
2926 if (first) {
2927 csizeexpr = get_array_length_cvalue (target_lvalue, dim);
2928 first = false;
2929 } else {
2930 csizeexpr = new CCodeBinaryExpression (CCodeBinaryOperator.MUL, csizeexpr, get_array_length_cvalue (target_lvalue, dim));
2935 if (csizeexpr != null) {
2936 var st = array_type.element_type.data_type as Struct;
2937 if (st != null && !array_type.element_type.nullable) {
2938 ccall.call = new CCodeIdentifier (append_struct_array_free (st));
2939 ccall.add_argument (csizeexpr);
2940 } else {
2941 requires_array_free = true;
2942 ccall.call = new CCodeIdentifier ("_vala_array_free");
2943 ccall.add_argument (csizeexpr);
2944 ccall.add_argument (new CCodeCastExpression (get_destroy_func_expression (array_type.element_type), "GDestroyNotify"));
2951 ccomma.append_expression (ccall);
2952 ccomma.append_expression (new CCodeConstant ("NULL"));
2954 var cassign = new CCodeAssignment (cvar, ccomma);
2956 // g_free (NULL) is allowed
2957 bool uses_gfree = (type.data_type != null && !type.data_type.is_reference_counting () && type.data_type.get_free_function () == "g_free");
2958 uses_gfree = uses_gfree || type is ArrayType;
2959 if (uses_gfree) {
2960 return cassign;
2963 return new CCodeConditionalExpression (cisnull, new CCodeConstant ("NULL"), cassign);
2966 public CCodeExpression destroy_local (LocalVariable local) {
2967 return destroy_variable (local, get_local_cvalue (local));
2970 public CCodeExpression destroy_parameter (Parameter param) {
2971 return destroy_variable (param, get_parameter_cvalue (param));
2974 public CCodeExpression destroy_field (Field field, TargetValue? instance) {
2975 return destroy_variable (field, get_field_cvalue (field, instance));
2978 public CCodeExpression get_unref_expression (CCodeExpression cvar, DataType type, Expression? expr, bool is_macro_definition = false) {
2979 if (expr != null) {
2980 if (expr.symbol_reference is LocalVariable) {
2981 return destroy_local ((LocalVariable) expr.symbol_reference);
2982 } else if (expr.symbol_reference is Parameter) {
2983 return destroy_parameter ((Parameter) expr.symbol_reference);
2986 var value = new GLibValue (type, cvar);
2987 if (expr != null && expr.target_value != null) {
2988 value.array_length_cvalues = ((GLibValue) expr.target_value).array_length_cvalues;
2989 value.delegate_target_cvalue = get_delegate_target_cvalue (expr.target_value);
2990 value.delegate_target_destroy_notify_cvalue = get_delegate_target_destroy_notify_cvalue (expr.target_value);
2992 return destroy_value (value, is_macro_definition);
2995 // logic in this method is temporarily duplicated in destroy_variable
2996 // apply changes to both methods
2997 public virtual CCodeExpression destroy_value (TargetValue value, bool is_macro_definition = false) {
2998 var type = value.value_type;
2999 var cvar = get_cvalue_ (value);
3001 if (type is DelegateType) {
3002 var delegate_target = get_delegate_target_cvalue (value);
3003 var delegate_target_destroy_notify = get_delegate_target_destroy_notify_cvalue (value);
3005 var ccall = new CCodeFunctionCall (delegate_target_destroy_notify);
3006 ccall.add_argument (delegate_target);
3008 var destroy_call = new CCodeCommaExpression ();
3009 destroy_call.append_expression (ccall);
3010 destroy_call.append_expression (new CCodeConstant ("NULL"));
3012 var cisnull = new CCodeBinaryExpression (CCodeBinaryOperator.EQUALITY, delegate_target_destroy_notify, new CCodeConstant ("NULL"));
3014 var ccomma = new CCodeCommaExpression ();
3015 ccomma.append_expression (new CCodeConditionalExpression (cisnull, new CCodeConstant ("NULL"), destroy_call));
3016 ccomma.append_expression (new CCodeAssignment (cvar, new CCodeConstant ("NULL")));
3017 ccomma.append_expression (new CCodeAssignment (delegate_target, new CCodeConstant ("NULL")));
3018 ccomma.append_expression (new CCodeAssignment (delegate_target_destroy_notify, new CCodeConstant ("NULL")));
3020 return ccomma;
3023 var ccall = new CCodeFunctionCall (get_destroy_func_expression (type));
3025 if (type is ValueType && !type.nullable) {
3026 // normal value type, no null check
3027 var st = type.data_type as Struct;
3028 if (st != null && st.is_simple_type ()) {
3029 // used for va_list
3030 ccall.add_argument (cvar);
3031 } else {
3032 ccall.add_argument (new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, cvar));
3035 if (gvalue_type != null && type.data_type == gvalue_type) {
3036 // g_value_unset must not be called for already unset values
3037 var cisvalid = new CCodeFunctionCall (new CCodeIdentifier ("G_IS_VALUE"));
3038 cisvalid.add_argument (new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, cvar));
3040 var ccomma = new CCodeCommaExpression ();
3041 ccomma.append_expression (ccall);
3042 ccomma.append_expression (new CCodeConstant ("NULL"));
3044 return new CCodeConditionalExpression (cisvalid, ccomma, new CCodeConstant ("NULL"));
3045 } else {
3046 return ccall;
3050 if (ccall.call is CCodeIdentifier && !(type is ArrayType) && !is_macro_definition) {
3051 // generate and use NULL-aware free macro to simplify code
3053 var freeid = (CCodeIdentifier) ccall.call;
3054 string free0_func = "_%s0".printf (freeid.name);
3056 if (add_wrapper (free0_func)) {
3057 var macro = destroy_value (new GLibValue (type, new CCodeIdentifier ("var")), true);
3058 cfile.add_type_declaration (new CCodeMacroReplacement.with_expression ("%s(var)".printf (free0_func), macro));
3061 ccall = new CCodeFunctionCall (new CCodeIdentifier (free0_func));
3062 ccall.add_argument (cvar);
3063 return ccall;
3066 /* (foo == NULL ? NULL : foo = (unref (foo), NULL)) */
3068 /* can be simplified to
3069 * foo = (unref (foo), NULL)
3070 * if foo is of static type non-null
3073 var cisnull = new CCodeBinaryExpression (CCodeBinaryOperator.EQUALITY, cvar, new CCodeConstant ("NULL"));
3074 if (type.type_parameter != null) {
3075 if (!(current_type_symbol is Class) || current_class.is_compact) {
3076 return new CCodeConstant ("NULL");
3079 // unref functions are optional for type parameters
3080 var cunrefisnull = new CCodeBinaryExpression (CCodeBinaryOperator.EQUALITY, get_destroy_func_expression (type), new CCodeConstant ("NULL"));
3081 cisnull = new CCodeBinaryExpression (CCodeBinaryOperator.OR, cisnull, cunrefisnull);
3084 ccall.add_argument (cvar);
3086 /* set freed references to NULL to prevent further use */
3087 var ccomma = new CCodeCommaExpression ();
3089 if (context.profile == Profile.GOBJECT) {
3090 if (type.data_type != null && !type.data_type.is_reference_counting () &&
3091 (type.data_type == gstringbuilder_type
3092 || type.data_type == garray_type
3093 || type.data_type == gbytearray_type
3094 || type.data_type == gptrarray_type)) {
3095 ccall.add_argument (new CCodeConstant ("TRUE"));
3096 } else if (type.data_type == gthreadpool_type) {
3097 ccall.add_argument (new CCodeConstant ("FALSE"));
3098 ccall.add_argument (new CCodeConstant ("TRUE"));
3099 } else if (type is ArrayType) {
3100 var array_type = (ArrayType) type;
3101 if (requires_destroy (array_type.element_type)) {
3102 CCodeExpression csizeexpr = null;
3103 bool first = true;
3104 for (int dim = 1; dim <= array_type.rank; dim++) {
3105 if (first) {
3106 csizeexpr = get_array_length_cvalue (value, dim);
3107 first = false;
3108 } else {
3109 csizeexpr = new CCodeBinaryExpression (CCodeBinaryOperator.MUL, csizeexpr, get_array_length_cvalue (value, dim));
3113 var st = array_type.element_type.data_type as Struct;
3114 if (st != null && !array_type.element_type.nullable) {
3115 ccall.call = new CCodeIdentifier (append_struct_array_free (st));
3116 ccall.add_argument (csizeexpr);
3117 } else {
3118 requires_array_free = true;
3119 ccall.call = new CCodeIdentifier ("_vala_array_free");
3120 ccall.add_argument (csizeexpr);
3121 ccall.add_argument (new CCodeCastExpression (get_destroy_func_expression (array_type.element_type), "GDestroyNotify"));
3127 ccomma.append_expression (ccall);
3128 ccomma.append_expression (new CCodeConstant ("NULL"));
3130 var cassign = new CCodeAssignment (cvar, ccomma);
3132 // g_free (NULL) is allowed
3133 bool uses_gfree = (type.data_type != null && !type.data_type.is_reference_counting () && type.data_type.get_free_function () == "g_free");
3134 uses_gfree = uses_gfree || type is ArrayType;
3135 if (uses_gfree) {
3136 return cassign;
3139 return new CCodeConditionalExpression (cisnull, new CCodeConstant ("NULL"), cassign);
3142 public override void visit_end_full_expression (Expression expr) {
3143 /* expr is a full expression, i.e. an initializer, the
3144 * expression in an expression statement, the controlling
3145 * expression in if, while, for, or foreach statements
3147 * we unref temporary variables at the end of a full
3148 * expression
3150 if (temp_ref_vars.size == 0) {
3151 /* nothing to do without temporary variables */
3152 return;
3155 LocalVariable full_expr_var = null;
3157 var local_decl = expr.parent_node as LocalVariable;
3158 if (!(local_decl != null && has_simple_struct_initializer (local_decl))) {
3159 var expr_type = expr.value_type;
3160 if (expr.target_type != null) {
3161 expr_type = expr.target_type;
3164 full_expr_var = get_temp_variable (expr_type, true, expr, false);
3165 emit_temp_var (full_expr_var);
3167 ccode.add_assignment (get_variable_cexpression (full_expr_var.name), get_cvalue (expr));
3170 foreach (LocalVariable local in temp_ref_vars) {
3171 ccode.add_expression (destroy_local (local));
3174 if (full_expr_var != null) {
3175 set_cvalue (expr, get_variable_cexpression (full_expr_var.name));
3178 temp_ref_vars.clear ();
3181 public void emit_temp_var (LocalVariable local, bool always_init = false) {
3182 var vardecl = new CCodeVariableDeclarator (local.name, null, local.variable_type.get_cdeclarator_suffix ());
3184 var st = local.variable_type.data_type as Struct;
3185 var array_type = local.variable_type as ArrayType;
3187 if (local.name.has_prefix ("*")) {
3188 // do not dereference unintialized variable
3189 // initialization is not needed for these special
3190 // pointer temp variables
3191 // used to avoid side-effects in assignments
3192 } else if (local.no_init) {
3193 // no initialization necessary for this temp var
3194 } else if (!local.variable_type.nullable &&
3195 (st != null && !st.is_simple_type ()) ||
3196 (array_type != null && array_type.fixed_length)) {
3197 // 0-initialize struct with struct initializer { 0 }
3198 // necessary as they will be passed by reference
3199 var clist = new CCodeInitializerList ();
3200 clist.append (new CCodeConstant ("0"));
3202 vardecl.initializer = clist;
3203 vardecl.init0 = true;
3204 } else if (local.variable_type.is_reference_type_or_type_parameter () ||
3205 local.variable_type.nullable ||
3206 local.variable_type is DelegateType) {
3207 vardecl.initializer = new CCodeConstant ("NULL");
3208 vardecl.init0 = true;
3209 } else if (always_init) {
3210 vardecl.initializer = default_value_for_type (local.variable_type, true);
3211 vardecl.init0 = true;
3214 if (is_in_coroutine ()) {
3215 closure_struct.add_field (local.variable_type.get_cname (), local.name);
3217 // even though closure struct is zerod, we need to initialize temporary variables
3218 // as they might be used multiple times when declared in a loop
3220 if (vardecl.initializer is CCodeInitializerList) {
3221 // C does not support initializer lists in assignments, use memset instead
3222 cfile.add_include ("string.h");
3223 var memset_call = new CCodeFunctionCall (new CCodeIdentifier ("memset"));
3224 memset_call.add_argument (new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, get_variable_cexpression (local.name)));
3225 memset_call.add_argument (new CCodeConstant ("0"));
3226 memset_call.add_argument (new CCodeIdentifier ("sizeof (%s)".printf (local.variable_type.get_cname ())));
3227 ccode.add_expression (memset_call);
3228 } else if (vardecl.initializer != null) {
3229 ccode.add_assignment (get_variable_cexpression (local.name), vardecl.initializer);
3231 } else {
3232 ccode.add_declaration (local.variable_type.get_cname (), vardecl);
3236 public override void visit_expression_statement (ExpressionStatement stmt) {
3237 if (stmt.expression.error) {
3238 stmt.error = true;
3239 return;
3242 /* free temporary objects and handle errors */
3244 foreach (LocalVariable local in temp_ref_vars) {
3245 ccode.add_expression (destroy_local (local));
3248 if (stmt.tree_can_fail && stmt.expression.tree_can_fail) {
3249 // simple case, no node breakdown necessary
3250 add_simple_check (stmt.expression);
3253 temp_ref_vars.clear ();
3256 public virtual void append_local_free (Symbol sym, bool stop_at_loop = false, CodeNode? stop_at = null) {
3257 var b = (Block) sym;
3259 var local_vars = b.get_local_variables ();
3260 // free in reverse order
3261 for (int i = local_vars.size - 1; i >= 0; i--) {
3262 var local = local_vars[i];
3263 if (!local.unreachable && local.active && !local.floating && !local.captured && requires_destroy (local.variable_type)) {
3264 ccode.add_expression (destroy_local (local));
3268 if (b.captured) {
3269 int block_id = get_block_id (b);
3271 var data_unref = new CCodeFunctionCall (new CCodeIdentifier ("block%d_data_unref".printf (block_id)));
3272 data_unref.add_argument (get_variable_cexpression ("_data%d_".printf (block_id)));
3273 ccode.add_expression (data_unref);
3274 ccode.add_assignment (get_variable_cexpression ("_data%d_".printf (block_id)), new CCodeConstant ("NULL"));
3277 if (stop_at_loop) {
3278 if (b.parent_node is Loop ||
3279 b.parent_node is ForeachStatement ||
3280 b.parent_node is SwitchStatement) {
3281 return;
3285 if (stop_at != null && b.parent_node == stop_at) {
3286 return;
3289 if (sym.parent_symbol is Block) {
3290 append_local_free (sym.parent_symbol, stop_at_loop, stop_at);
3291 } else if (sym.parent_symbol is Method) {
3292 append_param_free ((Method) sym.parent_symbol);
3296 private void append_param_free (Method m) {
3297 foreach (Parameter param in m.get_parameters ()) {
3298 if (!param.ellipsis && requires_destroy (param.variable_type) && param.direction == ParameterDirection.IN) {
3299 ccode.add_expression (destroy_parameter (param));
3304 public bool variable_accessible_in_finally (LocalVariable local) {
3305 if (current_try == null) {
3306 return false;
3309 var sym = current_symbol;
3311 while (!(sym is Method || sym is PropertyAccessor) && sym.scope.lookup (local.name) == null) {
3312 if ((sym.parent_node is TryStatement && ((TryStatement) sym.parent_node).finally_body != null) ||
3313 (sym.parent_node is CatchClause && ((TryStatement) sym.parent_node.parent_node).finally_body != null)) {
3315 return true;
3318 sym = sym.parent_symbol;
3321 return false;
3324 void return_out_parameter (Parameter param) {
3325 var delegate_type = param.variable_type as DelegateType;
3327 ccode.open_if (get_variable_cexpression (param.name));
3328 ccode.add_assignment (new CCodeUnaryExpression (CCodeUnaryOperator.POINTER_INDIRECTION, get_variable_cexpression (param.name)), get_variable_cexpression ("_" + param.name));
3330 if (delegate_type != null && delegate_type.delegate_symbol.has_target) {
3331 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))));
3332 if (delegate_type.value_owned) {
3333 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))));
3337 if (param.variable_type.is_disposable ()){
3338 ccode.add_else ();
3339 ccode.add_expression (destroy_parameter (param));
3341 ccode.close ();
3343 var array_type = param.variable_type as ArrayType;
3344 if (array_type != null && !array_type.fixed_length && !param.no_array_length) {
3345 for (int dim = 1; dim <= array_type.rank; dim++) {
3346 ccode.open_if (get_variable_cexpression (get_parameter_array_length_cname (param, dim)));
3347 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)));
3348 ccode.close ();
3353 public override void visit_return_statement (ReturnStatement stmt) {
3354 Symbol return_expression_symbol = null;
3356 if (stmt.return_expression != null) {
3357 // avoid unnecessary ref/unref pair
3358 var local = stmt.return_expression.symbol_reference as LocalVariable;
3359 if (current_return_type.value_owned
3360 && local != null && local.variable_type.value_owned
3361 && !local.captured
3362 && !variable_accessible_in_finally (local)) {
3363 /* return expression is local variable taking ownership and
3364 * current method is transferring ownership */
3366 return_expression_symbol = local;
3370 // return array length if appropriate
3371 if (((current_method != null && !current_method.no_array_length) || current_property_accessor != null) && current_return_type is ArrayType) {
3372 var return_expr_decl = get_temp_variable (stmt.return_expression.value_type, true, stmt, false);
3374 ccode.add_assignment (get_variable_cexpression (return_expr_decl.name), get_cvalue (stmt.return_expression));
3376 var array_type = (ArrayType) current_return_type;
3378 for (int dim = 1; dim <= array_type.rank; dim++) {
3379 var len_l = get_result_cexpression (get_array_length_cname ("result", dim));
3380 if (!is_in_coroutine ()) {
3381 len_l = new CCodeUnaryExpression (CCodeUnaryOperator.POINTER_INDIRECTION, len_l);
3383 var len_r = get_array_length_cexpression (stmt.return_expression, dim);
3384 ccode.add_assignment (len_l, len_r);
3387 set_cvalue (stmt.return_expression, get_variable_cexpression (return_expr_decl.name));
3389 emit_temp_var (return_expr_decl);
3390 } else if ((current_method != null || current_property_accessor != null) && current_return_type is DelegateType) {
3391 var delegate_type = (DelegateType) current_return_type;
3392 if (delegate_type.delegate_symbol.has_target) {
3393 var return_expr_decl = get_temp_variable (stmt.return_expression.value_type, true, stmt, false);
3395 ccode.add_assignment (get_variable_cexpression (return_expr_decl.name), get_cvalue (stmt.return_expression));
3397 var target_l = get_result_cexpression (get_delegate_target_cname ("result"));
3398 if (!is_in_coroutine ()) {
3399 target_l = new CCodeUnaryExpression (CCodeUnaryOperator.POINTER_INDIRECTION, target_l);
3401 CCodeExpression target_r_destroy_notify;
3402 var target_r = get_delegate_target_cexpression (stmt.return_expression, out target_r_destroy_notify);
3403 ccode.add_assignment (target_l, target_r);
3404 if (delegate_type.value_owned) {
3405 var target_l_destroy_notify = get_result_cexpression (get_delegate_target_destroy_notify_cname ("result"));
3406 if (!is_in_coroutine ()) {
3407 target_l_destroy_notify = new CCodeUnaryExpression (CCodeUnaryOperator.POINTER_INDIRECTION, target_l_destroy_notify);
3409 ccode.add_assignment (target_l_destroy_notify, target_r_destroy_notify);
3412 set_cvalue (stmt.return_expression, get_variable_cexpression (return_expr_decl.name));
3414 emit_temp_var (return_expr_decl);
3418 if (stmt.return_expression != null) {
3419 // assign method result to `result'
3420 CCodeExpression result_lhs = get_result_cexpression ();
3421 if (current_return_type.is_real_non_null_struct_type () && !is_in_coroutine ()) {
3422 result_lhs = new CCodeUnaryExpression (CCodeUnaryOperator.POINTER_INDIRECTION, result_lhs);
3424 ccode.add_assignment (result_lhs, get_cvalue (stmt.return_expression));
3427 // free local variables
3428 append_local_free (current_symbol);
3430 if (current_method != null) {
3431 // check postconditions
3432 foreach (Expression postcondition in current_method.get_postconditions ()) {
3433 create_postcondition_statement (postcondition);
3437 if (current_method != null && !current_method.coroutine) {
3438 // assign values to output parameters if they are not NULL
3439 // otherwise, free the value if necessary
3440 foreach (var param in current_method.get_parameters ()) {
3441 if (param.direction != ParameterDirection.OUT) {
3442 continue;
3445 return_out_parameter (param);
3449 if (is_in_constructor ()) {
3450 ccode.add_return (new CCodeIdentifier ("obj"));
3451 } else if (is_in_destructor ()) {
3452 // do not call return as member cleanup and chain up to base finalizer
3453 // stil need to be executed
3454 ccode.add_goto ("_return");
3455 } else if (current_method is CreationMethod) {
3456 ccode.add_return (new CCodeIdentifier ("self"));
3457 } else if (is_in_coroutine ()) {
3458 } else if (current_return_type is VoidType || current_return_type.is_real_non_null_struct_type ()) {
3459 // structs are returned via out parameter
3460 ccode.add_return ();
3461 } else {
3462 ccode.add_return (new CCodeIdentifier ("result"));
3465 if (return_expression_symbol != null) {
3466 return_expression_symbol.active = true;
3469 // required for destructors
3470 current_method_return = true;
3473 public string get_symbol_lock_name (string symname) {
3474 return "__lock_%s".printf (symname);
3477 private CCodeExpression get_lock_expression (Statement stmt, Expression resource) {
3478 CCodeExpression l = null;
3479 var inner_node = ((MemberAccess)resource).inner;
3480 var member = resource.symbol_reference;
3481 var parent = (TypeSymbol)resource.symbol_reference.parent_symbol;
3483 if (member.is_instance_member ()) {
3484 if (inner_node == null) {
3485 l = new CCodeIdentifier ("self");
3486 } else if (resource.symbol_reference.parent_symbol != current_type_symbol) {
3487 l = generate_instance_cast (get_cvalue (inner_node), parent);
3488 } else {
3489 l = get_cvalue (inner_node);
3492 l = new CCodeMemberAccess.pointer (new CCodeMemberAccess.pointer (l, "priv"), get_symbol_lock_name (resource.symbol_reference.name));
3493 } else if (member.is_class_member ()) {
3494 CCodeExpression klass;
3496 if (current_method != null && current_method.binding == MemberBinding.INSTANCE ||
3497 current_property_accessor != null && current_property_accessor.prop.binding == MemberBinding.INSTANCE ||
3498 (in_constructor && !in_static_or_class_context)) {
3499 var k = new CCodeFunctionCall (new CCodeIdentifier ("G_OBJECT_GET_CLASS"));
3500 k.add_argument (new CCodeIdentifier ("self"));
3501 klass = k;
3502 } else {
3503 klass = new CCodeIdentifier ("klass");
3506 var get_class_private_call = new CCodeFunctionCall (new CCodeIdentifier ("%s_GET_CLASS_PRIVATE".printf(parent.get_upper_case_cname ())));
3507 get_class_private_call.add_argument (klass);
3508 l = new CCodeMemberAccess.pointer (get_class_private_call, get_symbol_lock_name (resource.symbol_reference.name));
3509 } else {
3510 string lock_name = "%s_%s".printf(parent.get_lower_case_cname (), resource.symbol_reference.name);
3511 l = new CCodeIdentifier (get_symbol_lock_name (lock_name));
3513 return l;
3516 public override void visit_lock_statement (LockStatement stmt) {
3517 var l = get_lock_expression (stmt, stmt.resource);
3519 var fc = new CCodeFunctionCall (new CCodeIdentifier (((Method) mutex_type.scope.lookup ("lock")).get_cname ()));
3520 fc.add_argument (new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, l));
3522 ccode.add_expression (fc);
3525 public override void visit_unlock_statement (UnlockStatement stmt) {
3526 var l = get_lock_expression (stmt, stmt.resource);
3528 var fc = new CCodeFunctionCall (new CCodeIdentifier (((Method) mutex_type.scope.lookup ("unlock")).get_cname ()));
3529 fc.add_argument (new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, l));
3531 ccode.add_expression (fc);
3534 public override void visit_delete_statement (DeleteStatement stmt) {
3535 var pointer_type = (PointerType) stmt.expression.value_type;
3536 DataType type = pointer_type;
3537 if (pointer_type.base_type.data_type != null && pointer_type.base_type.data_type.is_reference_type ()) {
3538 type = pointer_type.base_type;
3541 var ccall = new CCodeFunctionCall (get_destroy_func_expression (type));
3542 ccall.add_argument (get_cvalue (stmt.expression));
3543 ccode.add_expression (ccall);
3546 public override void visit_expression (Expression expr) {
3547 if (get_cvalue (expr) != null && !expr.lvalue) {
3548 if (expr.formal_value_type is GenericType && !(expr.value_type is GenericType)) {
3549 var st = expr.formal_value_type.type_parameter.parent_symbol.parent_symbol as Struct;
3550 if (expr.formal_value_type.type_parameter.parent_symbol != garray_type &&
3551 (st == null || st.get_cname () != "va_list")) {
3552 // GArray and va_list don't use pointer-based generics
3553 set_cvalue (expr, convert_from_generic_pointer (get_cvalue (expr), expr.value_type));
3557 // memory management, implicit casts, and boxing/unboxing
3558 set_cvalue (expr, transform_expression (get_cvalue (expr), expr.value_type, expr.target_type, expr));
3560 if (expr.formal_target_type is GenericType && !(expr.target_type is GenericType)) {
3561 if (expr.formal_target_type.type_parameter.parent_symbol != garray_type) {
3562 // GArray doesn't use pointer-based generics
3563 set_cvalue (expr, convert_to_generic_pointer (get_cvalue (expr), expr.target_type));
3569 public override void visit_boolean_literal (BooleanLiteral expr) {
3570 if (context.profile == Profile.GOBJECT) {
3571 set_cvalue (expr, new CCodeConstant (expr.value ? "TRUE" : "FALSE"));
3572 } else {
3573 cfile.add_include ("stdbool.h");
3574 set_cvalue (expr, new CCodeConstant (expr.value ? "true" : "false"));
3578 public override void visit_character_literal (CharacterLiteral expr) {
3579 if (expr.get_char () >= 0x20 && expr.get_char () < 0x80) {
3580 set_cvalue (expr, new CCodeConstant (expr.value));
3581 } else {
3582 set_cvalue (expr, new CCodeConstant ("%uU".printf (expr.get_char ())));
3586 public override void visit_integer_literal (IntegerLiteral expr) {
3587 set_cvalue (expr, new CCodeConstant (expr.value + expr.type_suffix));
3590 public override void visit_real_literal (RealLiteral expr) {
3591 string c_literal = expr.value;
3592 if (c_literal.has_suffix ("d") || c_literal.has_suffix ("D")) {
3593 // there is no suffix for double in C
3594 c_literal = c_literal.substring (0, c_literal.length - 1);
3596 if (!("." in c_literal || "e" in c_literal || "E" in c_literal)) {
3597 // C requires period or exponent part for floating constants
3598 if ("f" in c_literal || "F" in c_literal) {
3599 c_literal = c_literal.substring (0, c_literal.length - 1) + ".f";
3600 } else {
3601 c_literal += ".";
3604 set_cvalue (expr, new CCodeConstant (c_literal));
3607 public override void visit_string_literal (StringLiteral expr) {
3608 set_cvalue (expr, new CCodeConstant.string (expr.value.replace ("\n", "\\n")));
3610 if (expr.translate) {
3611 // translated string constant
3613 var m = (Method) root_symbol.scope.lookup ("GLib").scope.lookup ("_");
3614 add_symbol_declaration (cfile, m, m.get_cname ());
3616 var translate = new CCodeFunctionCall (new CCodeIdentifier ("_"));
3617 translate.add_argument (get_cvalue (expr));
3618 set_cvalue (expr, translate);
3622 public override void visit_regex_literal (RegexLiteral expr) {
3623 string[] parts = expr.value.split ("/", 3);
3624 string re = parts[2].escape ("");
3625 string flags = "0";
3627 if (parts[1].contains ("i")) {
3628 flags += " | G_REGEX_CASELESS";
3630 if (parts[1].contains ("m")) {
3631 flags += " | G_REGEX_MULTILINE";
3633 if (parts[1].contains ("s")) {
3634 flags += " | G_REGEX_DOTALL";
3636 if (parts[1].contains ("x")) {
3637 flags += " | G_REGEX_EXTENDED";
3640 var regex_var = get_temp_variable (regex_type, true, expr, false);
3641 emit_temp_var (regex_var);
3643 var cdecl = new CCodeDeclaration ("GRegex*");
3645 var cname = regex_var.name + "regex_" + next_regex_id.to_string ();
3646 if (this.next_regex_id == 0) {
3647 var fun = new CCodeFunction ("_thread_safe_regex_init", "GRegex*");
3648 fun.modifiers = CCodeModifiers.STATIC | CCodeModifiers.INLINE;
3649 fun.add_parameter (new CCodeParameter ("re", "GRegex**"));
3650 fun.add_parameter (new CCodeParameter ("pattern", "const gchar *"));
3651 fun.add_parameter (new CCodeParameter ("match_options", "GRegexMatchFlags"));
3653 push_function (fun);
3655 var once_enter_call = new CCodeFunctionCall (new CCodeIdentifier ("g_once_init_enter"));
3656 once_enter_call.add_argument (new CCodeConstant ("(volatile gsize*) re"));
3657 ccode.open_if (once_enter_call);
3659 var regex_new_call = new CCodeFunctionCall (new CCodeIdentifier ("g_regex_new"));
3660 regex_new_call.add_argument (new CCodeConstant ("pattern"));
3661 regex_new_call.add_argument (new CCodeConstant ("match_options"));
3662 regex_new_call.add_argument (new CCodeConstant ("0"));
3663 regex_new_call.add_argument (new CCodeConstant ("NULL"));
3664 ccode.add_assignment (new CCodeIdentifier ("GRegex* val"), regex_new_call);
3666 var once_leave_call = new CCodeFunctionCall (new CCodeIdentifier ("g_once_init_leave"));
3667 once_leave_call.add_argument (new CCodeConstant ("(volatile gsize*) re"));
3668 once_leave_call.add_argument (new CCodeConstant ("(gsize) val"));
3669 ccode.add_expression (once_leave_call);
3671 ccode.close ();
3673 ccode.add_return (new CCodeIdentifier ("*re"));
3675 pop_function ();
3677 cfile.add_function (fun);
3679 this.next_regex_id++;
3681 cdecl.add_declarator (new CCodeVariableDeclarator (cname + " = NULL"));
3682 cdecl.modifiers = CCodeModifiers.STATIC;
3684 var regex_const = new CCodeConstant ("_thread_safe_regex_init (&%s, \"%s\", %s)".printf (cname, re, flags));
3686 cfile.add_constant_declaration (cdecl);
3687 set_cvalue (expr, regex_const);
3690 public override void visit_null_literal (NullLiteral expr) {
3691 if (context.profile != Profile.GOBJECT) {
3692 cfile.add_include ("stddef.h");
3694 set_cvalue (expr, new CCodeConstant ("NULL"));
3696 var array_type = expr.target_type as ArrayType;
3697 var delegate_type = expr.target_type as DelegateType;
3698 if (array_type != null) {
3699 for (int dim = 1; dim <= array_type.rank; dim++) {
3700 append_array_length (expr, new CCodeConstant ("0"));
3702 } else if (delegate_type != null && delegate_type.delegate_symbol.has_target) {
3703 set_delegate_target (expr, new CCodeConstant ("NULL"));
3704 set_delegate_target_destroy_notify (expr, new CCodeConstant ("NULL"));
3708 public abstract TargetValue get_local_cvalue (LocalVariable local);
3710 public abstract TargetValue get_parameter_cvalue (Parameter param);
3712 public abstract TargetValue get_field_cvalue (Field field, TargetValue? instance);
3714 public abstract TargetValue load_this_parameter (TypeSymbol sym);
3716 public virtual string get_delegate_target_cname (string delegate_cname) {
3717 assert_not_reached ();
3720 public virtual CCodeExpression get_delegate_target_cexpression (Expression delegate_expr, out CCodeExpression delegate_target_destroy_notify) {
3721 assert_not_reached ();
3724 public virtual CCodeExpression get_delegate_target_cvalue (TargetValue value) {
3725 return new CCodeInvalidExpression ();
3728 public virtual CCodeExpression get_delegate_target_destroy_notify_cvalue (TargetValue value) {
3729 return new CCodeInvalidExpression ();
3732 public virtual string get_delegate_target_destroy_notify_cname (string delegate_cname) {
3733 assert_not_reached ();
3736 public override void visit_base_access (BaseAccess expr) {
3737 CCodeExpression this_access;
3738 if (is_in_coroutine ()) {
3739 // use closure
3740 this_access = new CCodeMemberAccess.pointer (new CCodeIdentifier ("data"), "self");
3741 } else {
3742 this_access = new CCodeIdentifier ("self");
3745 set_cvalue (expr, generate_instance_cast (this_access, expr.value_type.data_type));
3748 public override void visit_postfix_expression (PostfixExpression expr) {
3749 MemberAccess ma = find_property_access (expr.inner);
3750 if (ma != null) {
3751 // property postfix expression
3752 var prop = (Property) ma.symbol_reference;
3754 // assign current value to temp variable
3755 var temp_decl = get_temp_variable (prop.property_type, true, expr, false);
3756 emit_temp_var (temp_decl);
3757 ccode.add_assignment (get_variable_cexpression (temp_decl.name), get_cvalue (expr.inner));
3759 // increment/decrement property
3760 var op = expr.increment ? CCodeBinaryOperator.PLUS : CCodeBinaryOperator.MINUS;
3761 var cexpr = new CCodeBinaryExpression (op, get_variable_cexpression (temp_decl.name), new CCodeConstant ("1"));
3762 store_property (prop, ma.inner, new GLibValue (expr.value_type, cexpr));
3764 // return previous value
3765 set_cvalue (expr, get_variable_cexpression (temp_decl.name));
3766 return;
3769 if (expr.parent_node is ExpressionStatement) {
3770 var op = expr.increment ? CCodeUnaryOperator.POSTFIX_INCREMENT : CCodeUnaryOperator.POSTFIX_DECREMENT;
3772 ccode.add_expression (new CCodeUnaryExpression (op, get_cvalue (expr.inner)));
3773 } else {
3774 // assign current value to temp variable
3775 var temp_decl = get_temp_variable (expr.inner.value_type, true, expr, false);
3776 emit_temp_var (temp_decl);
3777 ccode.add_assignment (get_variable_cexpression (temp_decl.name), get_cvalue (expr.inner));
3779 // increment/decrement variable
3780 var op = expr.increment ? CCodeBinaryOperator.PLUS : CCodeBinaryOperator.MINUS;
3781 var cexpr = new CCodeBinaryExpression (op, get_variable_cexpression (temp_decl.name), new CCodeConstant ("1"));
3782 ccode.add_assignment (get_cvalue (expr.inner), cexpr);
3784 // return previous value
3785 set_cvalue (expr, get_variable_cexpression (temp_decl.name));
3789 private MemberAccess? find_property_access (Expression expr) {
3790 if (!(expr is MemberAccess)) {
3791 return null;
3794 var ma = (MemberAccess) expr;
3795 if (ma.symbol_reference is Property) {
3796 return ma;
3799 return null;
3802 bool is_limited_generic_type (DataType type) {
3803 var cl = type.type_parameter.parent_symbol as Class;
3804 var st = type.type_parameter.parent_symbol as Struct;
3805 if ((cl != null && cl.is_compact) || st != null) {
3806 // compact classes and structs only
3807 // have very limited generics support
3808 return true;
3810 return false;
3813 public bool requires_copy (DataType type) {
3814 if (!type.is_disposable ()) {
3815 return false;
3818 var cl = type.data_type as Class;
3819 if (cl != null && cl.is_reference_counting ()
3820 && cl.get_ref_function () == "") {
3821 // empty ref_function => no ref necessary
3822 return false;
3825 if (type.type_parameter != null) {
3826 if (is_limited_generic_type (type)) {
3827 return false;
3831 return true;
3834 public bool requires_destroy (DataType type) {
3835 if (!type.is_disposable ()) {
3836 return false;
3839 var array_type = type as ArrayType;
3840 if (array_type != null && array_type.fixed_length) {
3841 return requires_destroy (array_type.element_type);
3844 var cl = type.data_type as Class;
3845 if (cl != null && cl.is_reference_counting ()
3846 && cl.get_unref_function () == "") {
3847 // empty unref_function => no unref necessary
3848 return false;
3851 if (type.type_parameter != null) {
3852 if (is_limited_generic_type (type)) {
3853 return false;
3857 return true;
3860 bool is_ref_function_void (DataType type) {
3861 var cl = type.data_type as Class;
3862 if (cl != null && cl.ref_function_void) {
3863 return true;
3864 } else {
3865 return false;
3869 public virtual CCodeExpression? get_ref_cexpression (DataType expression_type, CCodeExpression cexpr, Expression? expr, CodeNode node) {
3870 if (expression_type is DelegateType) {
3871 return cexpr;
3874 if (expression_type is ValueType && !expression_type.nullable) {
3875 // normal value type, no null check
3877 var decl = get_temp_variable (expression_type, false, node);
3878 emit_temp_var (decl);
3879 var ctemp = get_variable_cexpression (decl.name);
3881 var vt = (ValueType) expression_type;
3882 var st = (Struct) vt.type_symbol;
3883 var copy_call = new CCodeFunctionCall (new CCodeIdentifier (st.get_copy_function ()));
3884 copy_call.add_argument (new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, cexpr));
3885 copy_call.add_argument (new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, ctemp));
3887 if (!st.has_copy_function) {
3888 generate_struct_copy_function (st);
3891 if (gvalue_type != null && expression_type.data_type == gvalue_type) {
3892 var cisvalid = new CCodeFunctionCall (new CCodeIdentifier ("G_IS_VALUE"));
3893 cisvalid.add_argument (new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, cexpr));
3895 ccode.open_if (cisvalid);
3897 // GValue requires g_value_init in addition to g_value_copy
3898 var value_type_call = new CCodeFunctionCall (new CCodeIdentifier ("G_VALUE_TYPE"));
3899 value_type_call.add_argument (new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, cexpr));
3901 var init_call = new CCodeFunctionCall (new CCodeIdentifier ("g_value_init"));
3902 init_call.add_argument (new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, ctemp));
3903 init_call.add_argument (value_type_call);
3904 ccode.add_expression (init_call);
3905 ccode.add_expression (copy_call);
3907 ccode.add_else ();
3909 // g_value_init/copy must not be called for uninitialized values
3910 ccode.add_assignment (ctemp, cexpr);
3911 ccode.close ();
3912 } else {
3913 ccode.add_expression (copy_call);
3916 return ctemp;
3919 /* (temp = expr, temp == NULL ? NULL : ref (temp))
3921 * can be simplified to
3922 * ref (expr)
3923 * if static type of expr is non-null
3926 var dupexpr = get_dup_func_expression (expression_type, node.source_reference);
3928 if (dupexpr == null) {
3929 node.error = true;
3930 return null;
3933 if (dupexpr is CCodeIdentifier && !(expression_type is ArrayType) && !(expression_type is GenericType) && !is_ref_function_void (expression_type)) {
3934 // generate and call NULL-aware ref function to reduce number
3935 // of temporary variables and simplify code
3937 var dupid = (CCodeIdentifier) dupexpr;
3938 string dup0_func = "_%s0".printf (dupid.name);
3940 // g_strdup is already NULL-safe
3941 if (dupid.name == "g_strdup") {
3942 dup0_func = dupid.name;
3943 } else if (add_wrapper (dup0_func)) {
3944 string pointer_cname = "gpointer";
3945 if (context.profile == Profile.POSIX) {
3946 pointer_cname = "void*";
3948 var dup0_fun = new CCodeFunction (dup0_func, pointer_cname);
3949 dup0_fun.add_parameter (new CCodeParameter ("self", pointer_cname));
3950 dup0_fun.modifiers = CCodeModifiers.STATIC;
3952 push_function (dup0_fun);
3954 var dup_call = new CCodeFunctionCall (dupexpr);
3955 dup_call.add_argument (new CCodeIdentifier ("self"));
3957 ccode.add_return (new CCodeConditionalExpression (new CCodeIdentifier ("self"), dup_call, new CCodeConstant ("NULL")));
3959 pop_function ();
3961 cfile.add_function (dup0_fun);
3964 var ccall = new CCodeFunctionCall (new CCodeIdentifier (dup0_func));
3965 ccall.add_argument (cexpr);
3966 return ccall;
3969 var ccall = new CCodeFunctionCall (dupexpr);
3971 if (!(expression_type is ArrayType) && expr != null && expr.is_non_null ()
3972 && !is_ref_function_void (expression_type)) {
3973 // expression is non-null
3974 ccall.add_argument (get_cvalue (expr));
3976 return ccall;
3977 } else {
3978 var decl = get_temp_variable (expression_type, false, node, false);
3979 emit_temp_var (decl);
3981 var ctemp = get_variable_cexpression (decl.name);
3983 var cisnull = new CCodeBinaryExpression (CCodeBinaryOperator.EQUALITY, ctemp, new CCodeConstant ("NULL"));
3984 if (expression_type.type_parameter != null) {
3985 // dup functions are optional for type parameters
3986 var cdupisnull = new CCodeBinaryExpression (CCodeBinaryOperator.EQUALITY, get_dup_func_expression (expression_type, node.source_reference), new CCodeConstant ("NULL"));
3987 cisnull = new CCodeBinaryExpression (CCodeBinaryOperator.OR, cisnull, cdupisnull);
3990 if (expression_type.type_parameter != null) {
3991 // cast from gconstpointer to gpointer as GBoxedCopyFunc expects gpointer
3992 ccall.add_argument (new CCodeCastExpression (ctemp, "gpointer"));
3993 } else {
3994 ccall.add_argument (ctemp);
3997 if (expression_type is ArrayType) {
3998 var array_type = (ArrayType) expression_type;
3999 bool first = true;
4000 CCodeExpression csizeexpr = null;
4001 for (int dim = 1; dim <= array_type.rank; dim++) {
4002 if (first) {
4003 csizeexpr = get_array_length_cexpression (expr, dim);
4004 first = false;
4005 } else {
4006 csizeexpr = new CCodeBinaryExpression (CCodeBinaryOperator.MUL, csizeexpr, get_array_length_cexpression (expr, dim));
4010 ccall.add_argument (csizeexpr);
4012 if (array_type.element_type is GenericType) {
4013 var elem_dupexpr = get_dup_func_expression (array_type.element_type, node.source_reference);
4014 if (elem_dupexpr == null) {
4015 elem_dupexpr = new CCodeConstant ("NULL");
4017 ccall.add_argument (elem_dupexpr);
4021 var ccomma = new CCodeCommaExpression ();
4022 ccomma.append_expression (new CCodeAssignment (ctemp, cexpr));
4024 CCodeExpression cifnull;
4025 if (expression_type.data_type != null) {
4026 cifnull = new CCodeConstant ("NULL");
4027 } else {
4028 // the value might be non-null even when the dup function is null,
4029 // so we may not just use NULL for type parameters
4031 // cast from gconstpointer to gpointer as methods in
4032 // generic classes may not return gconstpointer
4033 cifnull = new CCodeCastExpression (ctemp, "gpointer");
4035 ccomma.append_expression (new CCodeConditionalExpression (cisnull, cifnull, ccall));
4037 // repeat temp variable at the end of the comma expression
4038 // if the ref function returns void
4039 if (is_ref_function_void (expression_type)) {
4040 ccomma.append_expression (ctemp);
4043 return ccomma;
4047 bool is_reference_type_argument (DataType type_arg) {
4048 if (type_arg is ErrorType || (type_arg.data_type != null && type_arg.data_type.is_reference_type ())) {
4049 return true;
4050 } else {
4051 return false;
4055 bool is_nullable_value_type_argument (DataType type_arg) {
4056 if (type_arg is ValueType && type_arg.nullable) {
4057 return true;
4058 } else {
4059 return false;
4063 bool is_signed_integer_type_argument (DataType type_arg) {
4064 var st = type_arg.data_type as Struct;
4065 if (type_arg.nullable) {
4066 return false;
4067 } else if (st == bool_type.data_type) {
4068 return true;
4069 } else if (st == char_type.data_type) {
4070 return true;
4071 } else if (unichar_type != null && st == unichar_type.data_type) {
4072 return true;
4073 } else if (st == short_type.data_type) {
4074 return true;
4075 } else if (st == int_type.data_type) {
4076 return true;
4077 } else if (st == long_type.data_type) {
4078 return true;
4079 } else if (st == int8_type.data_type) {
4080 return true;
4081 } else if (st == int16_type.data_type) {
4082 return true;
4083 } else if (st == int32_type.data_type) {
4084 return true;
4085 } else if (st == gtype_type) {
4086 return true;
4087 } else if (type_arg is EnumValueType) {
4088 return true;
4089 } else {
4090 return false;
4094 bool is_unsigned_integer_type_argument (DataType type_arg) {
4095 var st = type_arg.data_type as Struct;
4096 if (type_arg.nullable) {
4097 return false;
4098 } else if (st == uchar_type.data_type) {
4099 return true;
4100 } else if (st == ushort_type.data_type) {
4101 return true;
4102 } else if (st == uint_type.data_type) {
4103 return true;
4104 } else if (st == ulong_type.data_type) {
4105 return true;
4106 } else if (st == uint8_type.data_type) {
4107 return true;
4108 } else if (st == uint16_type.data_type) {
4109 return true;
4110 } else if (st == uint32_type.data_type) {
4111 return true;
4112 } else {
4113 return false;
4117 public void check_type (DataType type) {
4118 var array_type = type as ArrayType;
4119 if (array_type != null) {
4120 check_type (array_type.element_type);
4121 if (array_type.element_type is ArrayType) {
4122 Report.error (type.source_reference, "Stacked arrays are not supported");
4123 } else if (array_type.element_type is DelegateType) {
4124 var delegate_type = (DelegateType) array_type.element_type;
4125 if (delegate_type.delegate_symbol.has_target) {
4126 Report.error (type.source_reference, "Delegates with target are not supported as array element type");
4130 foreach (var type_arg in type.get_type_arguments ()) {
4131 check_type (type_arg);
4132 check_type_argument (type_arg);
4136 void check_type_argument (DataType type_arg) {
4137 if (type_arg is GenericType
4138 || type_arg is PointerType
4139 || is_reference_type_argument (type_arg)
4140 || is_nullable_value_type_argument (type_arg)
4141 || is_signed_integer_type_argument (type_arg)
4142 || is_unsigned_integer_type_argument (type_arg)) {
4143 // no error
4144 } else if (type_arg is DelegateType) {
4145 var delegate_type = (DelegateType) type_arg;
4146 if (delegate_type.delegate_symbol.has_target) {
4147 Report.error (type_arg.source_reference, "Delegates with target are not supported as generic type arguments");
4149 } else {
4150 Report.error (type_arg.source_reference, "`%s' is not a supported generic type argument, use `?' to box value types".printf (type_arg.to_string ()));
4154 public virtual void generate_class_declaration (Class cl, CCodeFile decl_space) {
4155 if (add_symbol_declaration (decl_space, cl, cl.get_cname ())) {
4156 return;
4160 public virtual void generate_interface_declaration (Interface iface, CCodeFile decl_space) {
4163 public virtual void generate_method_declaration (Method m, CCodeFile decl_space) {
4166 public virtual void generate_error_domain_declaration (ErrorDomain edomain, CCodeFile decl_space) {
4169 public void add_generic_type_arguments (Map<int,CCodeExpression> arg_map, List<DataType> type_args, CodeNode expr, bool is_chainup = false) {
4170 int type_param_index = 0;
4171 foreach (var type_arg in type_args) {
4172 arg_map.set (get_param_pos (0.1 * type_param_index + 0.01), get_type_id_expression (type_arg, is_chainup));
4173 if (requires_copy (type_arg)) {
4174 var dup_func = get_dup_func_expression (type_arg, type_arg.source_reference, is_chainup);
4175 if (dup_func == null) {
4176 // type doesn't contain a copy function
4177 expr.error = true;
4178 return;
4180 arg_map.set (get_param_pos (0.1 * type_param_index + 0.02), new CCodeCastExpression (dup_func, "GBoxedCopyFunc"));
4181 arg_map.set (get_param_pos (0.1 * type_param_index + 0.03), get_destroy_func_expression (type_arg, is_chainup));
4182 } else {
4183 arg_map.set (get_param_pos (0.1 * type_param_index + 0.02), new CCodeConstant ("NULL"));
4184 arg_map.set (get_param_pos (0.1 * type_param_index + 0.03), new CCodeConstant ("NULL"));
4186 type_param_index++;
4190 public override void visit_object_creation_expression (ObjectCreationExpression expr) {
4191 CCodeExpression instance = null;
4192 CCodeExpression creation_expr = null;
4194 check_type (expr.type_reference);
4196 var st = expr.type_reference.data_type as Struct;
4197 if ((st != null && (!st.is_simple_type () || st.get_cname () == "va_list")) || expr.get_object_initializer ().size > 0) {
4198 // value-type initialization or object creation expression with object initializer
4200 var local = expr.parent_node as LocalVariable;
4201 if (local != null && has_simple_struct_initializer (local)) {
4202 if (local.captured) {
4203 var block = (Block) local.parent_symbol;
4204 instance = new CCodeMemberAccess.pointer (get_variable_cexpression ("_data%d_".printf (get_block_id (block))), get_variable_cname (local.name));
4205 } else {
4206 instance = get_variable_cexpression (get_variable_cname (local.name));
4208 } else {
4209 var temp_decl = get_temp_variable (expr.type_reference, false, expr);
4210 emit_temp_var (temp_decl);
4212 instance = get_variable_cexpression (get_variable_cname (temp_decl.name));
4216 if (expr.symbol_reference == null) {
4217 // no creation method
4218 if (expr.type_reference.data_type is Struct) {
4219 // memset needs string.h
4220 cfile.add_include ("string.h");
4221 var creation_call = new CCodeFunctionCall (new CCodeIdentifier ("memset"));
4222 creation_call.add_argument (new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, instance));
4223 creation_call.add_argument (new CCodeConstant ("0"));
4224 creation_call.add_argument (new CCodeIdentifier ("sizeof (%s)".printf (expr.type_reference.get_cname ())));
4226 creation_expr = creation_call;
4228 } else if (expr.type_reference.data_type == glist_type ||
4229 expr.type_reference.data_type == gslist_type) {
4230 // NULL is an empty list
4231 set_cvalue (expr, new CCodeConstant ("NULL"));
4232 } else if (expr.symbol_reference is Method) {
4233 // use creation method
4234 var m = (Method) expr.symbol_reference;
4235 var params = m.get_parameters ();
4236 CCodeFunctionCall creation_call;
4238 generate_method_declaration (m, cfile);
4240 var cl = expr.type_reference.data_type as Class;
4242 if (!m.has_new_function) {
4243 // use construct function directly
4244 creation_call = new CCodeFunctionCall (new CCodeIdentifier (m.get_real_cname ()));
4245 creation_call.add_argument (new CCodeIdentifier (cl.get_type_id ()));
4246 } else {
4247 creation_call = new CCodeFunctionCall (new CCodeIdentifier (m.get_cname ()));
4250 if ((st != null && !st.is_simple_type ()) && !(m.cinstance_parameter_position < 0)) {
4251 creation_call.add_argument (new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, instance));
4252 } else if (st != null && st.get_cname () == "va_list") {
4253 creation_call.add_argument (instance);
4254 if (m.get_cname () == "va_start") {
4255 Parameter last_param = null;
4256 foreach (var param in current_method.get_parameters ()) {
4257 if (param.ellipsis) {
4258 break;
4260 last_param = param;
4262 creation_call.add_argument (new CCodeIdentifier (get_variable_cname (last_param.name)));
4266 generate_type_declaration (expr.type_reference, cfile);
4268 var carg_map = new HashMap<int,CCodeExpression> (direct_hash, direct_equal);
4270 if (cl != null && !cl.is_compact) {
4271 add_generic_type_arguments (carg_map, expr.type_reference.get_type_arguments (), expr);
4272 } else if (cl != null && m.simple_generics) {
4273 int type_param_index = 0;
4274 foreach (var type_arg in expr.type_reference.get_type_arguments ()) {
4275 if (requires_copy (type_arg)) {
4276 carg_map.set (get_param_pos (-1 + 0.1 * type_param_index + 0.03), get_destroy0_func_expression (type_arg));
4277 } else {
4278 carg_map.set (get_param_pos (-1 + 0.1 * type_param_index + 0.03), new CCodeConstant ("NULL"));
4280 type_param_index++;
4284 bool ellipsis = false;
4286 int i = 1;
4287 int arg_pos;
4288 Iterator<Parameter> params_it = params.iterator ();
4289 foreach (Expression arg in expr.get_argument_list ()) {
4290 CCodeExpression cexpr = get_cvalue (arg);
4291 Parameter param = null;
4292 if (params_it.next ()) {
4293 param = params_it.get ();
4294 ellipsis = param.ellipsis;
4295 if (!ellipsis) {
4296 if (!param.no_array_length && param.variable_type is ArrayType) {
4297 var array_type = (ArrayType) param.variable_type;
4298 for (int dim = 1; dim <= array_type.rank; dim++) {
4299 carg_map.set (get_param_pos (param.carray_length_parameter_position + 0.01 * dim), get_array_length_cexpression (arg, dim));
4301 } else if (param.variable_type is DelegateType) {
4302 var deleg_type = (DelegateType) param.variable_type;
4303 var d = deleg_type.delegate_symbol;
4304 if (d.has_target) {
4305 CCodeExpression delegate_target_destroy_notify;
4306 var delegate_target = get_delegate_target_cexpression (arg, out delegate_target_destroy_notify);
4307 carg_map.set (get_param_pos (param.cdelegate_target_parameter_position), delegate_target);
4308 if (deleg_type.value_owned) {
4309 carg_map.set (get_param_pos (param.cdelegate_target_parameter_position + 0.01), delegate_target_destroy_notify);
4314 cexpr = handle_struct_argument (param, arg, cexpr);
4316 if (param.ctype != null) {
4317 cexpr = new CCodeCastExpression (cexpr, param.ctype);
4319 } else {
4320 cexpr = handle_struct_argument (null, arg, cexpr);
4323 arg_pos = get_param_pos (param.cparameter_position, ellipsis);
4324 } else {
4325 // default argument position
4326 cexpr = handle_struct_argument (null, arg, cexpr);
4327 arg_pos = get_param_pos (i, ellipsis);
4330 carg_map.set (arg_pos, cexpr);
4332 i++;
4334 while (params_it.next ()) {
4335 var param = params_it.get ();
4337 if (param.ellipsis) {
4338 ellipsis = true;
4339 break;
4342 if (param.initializer == null) {
4343 Report.error (expr.source_reference, "no default expression for argument %d".printf (i));
4344 return;
4347 /* evaluate default expression here as the code
4348 * generator might not have visited the formal
4349 * parameter yet */
4350 param.initializer.emit (this);
4352 carg_map.set (get_param_pos (param.cparameter_position), get_cvalue (param.initializer));
4353 i++;
4356 // append C arguments in the right order
4357 int last_pos = -1;
4358 int min_pos;
4359 while (true) {
4360 min_pos = -1;
4361 foreach (int pos in carg_map.get_keys ()) {
4362 if (pos > last_pos && (min_pos == -1 || pos < min_pos)) {
4363 min_pos = pos;
4366 if (min_pos == -1) {
4367 break;
4369 creation_call.add_argument (carg_map.get (min_pos));
4370 last_pos = min_pos;
4373 if ((st != null && !st.is_simple_type ()) && m.cinstance_parameter_position < 0) {
4374 // instance parameter is at the end in a struct creation method
4375 creation_call.add_argument (new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, instance));
4378 if (expr.tree_can_fail) {
4379 // method can fail
4380 current_method_inner_error = true;
4381 creation_call.add_argument (new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, get_variable_cexpression ("_inner_error_")));
4384 if (ellipsis) {
4385 /* ensure variable argument list ends with NULL
4386 * except when using printf-style arguments */
4387 if (!m.printf_format && !m.scanf_format && m.sentinel != "") {
4388 creation_call.add_argument (new CCodeConstant (m.sentinel));
4392 creation_expr = creation_call;
4394 // cast the return value of the creation method back to the intended type if
4395 // it requested a special C return type
4396 if (get_custom_creturn_type (m) != null) {
4397 creation_expr = new CCodeCastExpression (creation_expr, expr.type_reference.get_cname ());
4399 } else if (expr.symbol_reference is ErrorCode) {
4400 var ecode = (ErrorCode) expr.symbol_reference;
4401 var edomain = (ErrorDomain) ecode.parent_symbol;
4402 CCodeFunctionCall creation_call;
4404 generate_error_domain_declaration (edomain, cfile);
4406 if (expr.get_argument_list ().size == 1) {
4407 // must not be a format argument
4408 creation_call = new CCodeFunctionCall (new CCodeIdentifier ("g_error_new_literal"));
4409 } else {
4410 creation_call = new CCodeFunctionCall (new CCodeIdentifier ("g_error_new"));
4412 creation_call.add_argument (new CCodeIdentifier (edomain.get_upper_case_cname ()));
4413 creation_call.add_argument (new CCodeIdentifier (ecode.get_cname ()));
4415 foreach (Expression arg in expr.get_argument_list ()) {
4416 creation_call.add_argument (get_cvalue (arg));
4419 creation_expr = creation_call;
4420 } else {
4421 assert (false);
4424 var local = expr.parent_node as LocalVariable;
4425 if (local != null && has_simple_struct_initializer (local)) {
4426 // no temporary variable necessary
4427 ccode.add_expression (creation_expr);
4428 set_cvalue (expr, instance);
4429 return;
4430 } else if (instance != null) {
4431 if (expr.type_reference.data_type is Struct) {
4432 ccode.add_expression (creation_expr);
4433 } else {
4434 ccode.add_assignment (instance, creation_expr);
4437 foreach (MemberInitializer init in expr.get_object_initializer ()) {
4438 if (init.symbol_reference is Field) {
4439 var f = (Field) init.symbol_reference;
4440 var instance_target_type = get_data_type_for_symbol ((TypeSymbol) f.parent_symbol);
4441 var typed_inst = transform_expression (instance, expr.type_reference, instance_target_type);
4442 CCodeExpression lhs;
4443 if (expr.type_reference.data_type is Struct) {
4444 lhs = new CCodeMemberAccess (typed_inst, f.get_cname ());
4445 } else {
4446 lhs = new CCodeMemberAccess.pointer (typed_inst, f.get_cname ());
4448 ccode.add_assignment (lhs, get_cvalue (init.initializer));
4450 if (f.variable_type is ArrayType && !f.no_array_length) {
4451 var array_type = (ArrayType) f.variable_type;
4452 for (int dim = 1; dim <= array_type.rank; dim++) {
4453 if (expr.type_reference.data_type is Struct) {
4454 lhs = new CCodeMemberAccess (typed_inst, get_array_length_cname (f.get_cname (), dim));
4455 } else {
4456 lhs = new CCodeMemberAccess.pointer (typed_inst, get_array_length_cname (f.get_cname (), dim));
4458 var rhs_array_len = get_array_length_cexpression (init.initializer, dim);
4459 ccode.add_assignment (lhs, rhs_array_len);
4461 } else if (f.variable_type is DelegateType && (f.variable_type as DelegateType).delegate_symbol.has_target && !f.no_delegate_target) {
4462 if (expr.type_reference.data_type is Struct) {
4463 lhs = new CCodeMemberAccess (typed_inst, get_delegate_target_cname (f.get_cname ()));
4464 } else {
4465 lhs = new CCodeMemberAccess.pointer (typed_inst, get_delegate_target_cname (f.get_cname ()));
4467 CCodeExpression rhs_delegate_target_destroy_notify;
4468 var rhs_delegate_target = get_delegate_target_cexpression (init.initializer, out rhs_delegate_target_destroy_notify);
4469 ccode.add_assignment (lhs, rhs_delegate_target);
4472 var cl = f.parent_symbol as Class;
4473 if (cl != null) {
4474 generate_class_struct_declaration (cl, cfile);
4476 } else if (init.symbol_reference is Property) {
4477 var inst_ma = new MemberAccess.simple ("new");
4478 inst_ma.value_type = expr.type_reference;
4479 set_cvalue (inst_ma, instance);
4480 store_property ((Property) init.symbol_reference, inst_ma, init.initializer.target_value);
4484 creation_expr = instance;
4487 if (creation_expr != null) {
4488 var temp_var = get_temp_variable (expr.value_type);
4489 var temp_ref = get_variable_cexpression (temp_var.name);
4491 emit_temp_var (temp_var);
4493 ccode.add_assignment (temp_ref, creation_expr);
4494 set_cvalue (expr, temp_ref);
4498 public CCodeExpression? handle_struct_argument (Parameter? param, Expression arg, CCodeExpression? cexpr) {
4499 DataType type;
4500 if (param != null) {
4501 type = param.variable_type;
4502 } else {
4503 // varargs
4504 type = arg.value_type;
4507 // pass non-simple struct instances always by reference
4508 if (!(arg.value_type is NullType) && type.is_real_struct_type ()) {
4509 // we already use a reference for arguments of ref, out, and nullable parameters
4510 if ((param == null || param.direction == ParameterDirection.IN) && !type.nullable) {
4511 var unary = cexpr as CCodeUnaryExpression;
4512 if (unary != null && unary.operator == CCodeUnaryOperator.POINTER_INDIRECTION) {
4513 // *expr => expr
4514 return unary.inner;
4515 } else if (cexpr is CCodeIdentifier || cexpr is CCodeMemberAccess) {
4516 return new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, cexpr);
4517 } else {
4518 // if cexpr is e.g. a function call, we can't take the address of the expression
4519 var temp_var = get_temp_variable (type, true, null, false);
4520 emit_temp_var (temp_var);
4522 ccode.add_assignment (get_variable_cexpression (temp_var.name), cexpr);
4523 return new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, get_variable_cexpression (temp_var.name));
4528 return cexpr;
4531 public override void visit_sizeof_expression (SizeofExpression expr) {
4532 generate_type_declaration (expr.type_reference, cfile);
4534 var csizeof = new CCodeFunctionCall (new CCodeIdentifier ("sizeof"));
4535 csizeof.add_argument (new CCodeIdentifier (expr.type_reference.get_cname ()));
4536 set_cvalue (expr, csizeof);
4539 public override void visit_typeof_expression (TypeofExpression expr) {
4540 set_cvalue (expr, get_type_id_expression (expr.type_reference));
4543 public override void visit_unary_expression (UnaryExpression expr) {
4544 if (expr.operator == UnaryOperator.REF || expr.operator == UnaryOperator.OUT) {
4545 var glib_value = (GLibValue) expr.inner.target_value;
4547 var ref_value = new GLibValue (glib_value.value_type);
4548 ref_value.cvalue = new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, glib_value.cvalue);
4550 if (glib_value.array_length_cvalues != null) {
4551 for (int i = 0; i < glib_value.array_length_cvalues.size; i++) {
4552 ref_value.append_array_length_cvalue (new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, glib_value.array_length_cvalues[i]));
4556 if (glib_value.delegate_target_cvalue != null) {
4557 ref_value.delegate_target_cvalue = new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, glib_value.delegate_target_cvalue);
4559 if (glib_value.delegate_target_destroy_notify_cvalue != null) {
4560 ref_value.delegate_target_destroy_notify_cvalue = new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, glib_value.delegate_target_destroy_notify_cvalue);
4563 expr.target_value = ref_value;
4564 return;
4567 CCodeUnaryOperator op;
4568 if (expr.operator == UnaryOperator.PLUS) {
4569 op = CCodeUnaryOperator.PLUS;
4570 } else if (expr.operator == UnaryOperator.MINUS) {
4571 op = CCodeUnaryOperator.MINUS;
4572 } else if (expr.operator == UnaryOperator.LOGICAL_NEGATION) {
4573 op = CCodeUnaryOperator.LOGICAL_NEGATION;
4574 } else if (expr.operator == UnaryOperator.BITWISE_COMPLEMENT) {
4575 op = CCodeUnaryOperator.BITWISE_COMPLEMENT;
4576 } else if (expr.operator == UnaryOperator.INCREMENT) {
4577 op = CCodeUnaryOperator.PREFIX_INCREMENT;
4578 } else if (expr.operator == UnaryOperator.DECREMENT) {
4579 op = CCodeUnaryOperator.PREFIX_DECREMENT;
4580 } else {
4581 assert_not_reached ();
4583 set_cvalue (expr, new CCodeUnaryExpression (op, get_cvalue (expr.inner)));
4586 public CCodeExpression? try_cast_value_to_type (CCodeExpression ccodeexpr, DataType from, DataType to, Expression? expr = null) {
4587 if (from == null || gvalue_type == null || from.data_type != gvalue_type || to.get_type_id () == null) {
4588 return null;
4591 // explicit conversion from GValue
4592 var ccall = new CCodeFunctionCall (get_value_getter_function (to));
4593 CCodeExpression gvalue;
4594 if (from.nullable) {
4595 gvalue = ccodeexpr;
4596 } else {
4597 gvalue = new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, ccodeexpr);
4599 ccall.add_argument (gvalue);
4601 CCodeExpression rv = ccall;
4603 if (expr != null && to is ArrayType) {
4604 // null-terminated string array
4605 var len_call = new CCodeFunctionCall (new CCodeIdentifier ("g_strv_length"));
4606 len_call.add_argument (rv);
4607 append_array_length (expr, len_call);
4608 } else if (to is StructValueType) {
4609 var temp_decl = get_temp_variable (to, true, null, true);
4610 emit_temp_var (temp_decl);
4611 var ctemp = get_variable_cexpression (temp_decl.name);
4613 rv = new CCodeUnaryExpression (CCodeUnaryOperator.POINTER_INDIRECTION, new CCodeCastExpression (rv, (new PointerType(to)).get_cname ()));
4614 var holds = new CCodeFunctionCall (new CCodeIdentifier ("G_VALUE_HOLDS"));
4615 holds.add_argument (gvalue);
4616 holds.add_argument (new CCodeIdentifier (to.get_type_id ()));
4617 var cond = new CCodeBinaryExpression (CCodeBinaryOperator.AND, holds, ccall);
4618 var warn = new CCodeFunctionCall (new CCodeIdentifier ("g_warning"));
4619 warn.add_argument (new CCodeConstant ("\"Invalid GValue unboxing (wrong type or NULL)\""));
4620 var fail = new CCodeCommaExpression ();
4621 fail.append_expression (warn);
4622 fail.append_expression (ctemp);
4623 rv = new CCodeConditionalExpression (cond, rv, fail);
4626 return rv;
4629 int next_variant_function_id = 0;
4631 public CCodeExpression? try_cast_variant_to_type (CCodeExpression ccodeexpr, DataType from, DataType to, Expression? expr = null) {
4632 if (from == null || gvariant_type == null || from.data_type != gvariant_type) {
4633 return null;
4636 string variant_func = "_variant_get%d".printf (++next_variant_function_id);
4638 var ccall = new CCodeFunctionCall (new CCodeIdentifier (variant_func));
4639 ccall.add_argument (ccodeexpr);
4641 var cfunc = new CCodeFunction (variant_func);
4642 cfunc.modifiers = CCodeModifiers.STATIC;
4643 cfunc.add_parameter (new CCodeParameter ("value", "GVariant*"));
4645 if (!to.is_real_non_null_struct_type ()) {
4646 cfunc.return_type = to.get_cname ();
4649 if (to.is_real_non_null_struct_type ()) {
4650 // structs are returned via out parameter
4651 cfunc.add_parameter (new CCodeParameter ("result", to.get_cname () + "*"));
4652 } else if (to is ArrayType) {
4653 // return array length if appropriate
4654 var array_type = (ArrayType) to;
4656 for (int dim = 1; dim <= array_type.rank; dim++) {
4657 var temp_decl = get_temp_variable (int_type, false, expr);
4658 emit_temp_var (temp_decl);
4660 ccall.add_argument (new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, get_variable_cexpression (temp_decl.name)));
4661 cfunc.add_parameter (new CCodeParameter (get_array_length_cname ("result", dim), "int*"));
4662 append_array_length (expr, get_variable_cexpression (temp_decl.name));
4666 push_function (cfunc);
4668 var result = deserialize_expression (to, new CCodeIdentifier ("value"), new CCodeIdentifier ("*result"));
4669 ccode.add_return (result);
4671 pop_function ();
4673 cfile.add_function_declaration (cfunc);
4674 cfile.add_function (cfunc);
4676 return ccall;
4679 public virtual CCodeExpression? deserialize_expression (DataType type, CCodeExpression variant_expr, CCodeExpression? expr, CCodeExpression? error_expr = null, out bool may_fail = null) {
4680 return null;
4683 public virtual CCodeExpression? serialize_expression (DataType type, CCodeExpression expr) {
4684 return null;
4687 public override void visit_cast_expression (CastExpression expr) {
4688 var valuecast = try_cast_value_to_type (get_cvalue (expr.inner), expr.inner.value_type, expr.type_reference, expr);
4689 if (valuecast != null) {
4690 set_cvalue (expr, valuecast);
4691 return;
4694 var variantcast = try_cast_variant_to_type (get_cvalue (expr.inner), expr.inner.value_type, expr.type_reference, expr);
4695 if (variantcast != null) {
4696 set_cvalue (expr, variantcast);
4697 return;
4700 generate_type_declaration (expr.type_reference, cfile);
4702 var cl = expr.type_reference.data_type as Class;
4703 var iface = expr.type_reference.data_type as Interface;
4704 if (context.profile == Profile.GOBJECT && (iface != null || (cl != null && !cl.is_compact))) {
4705 // checked cast for strict subtypes of GTypeInstance
4706 if (expr.is_silent_cast) {
4707 var temp_decl = get_temp_variable (expr.inner.value_type, expr.inner.value_type.value_owned, expr, false);
4708 emit_temp_var (temp_decl);
4709 var ctemp = get_variable_cexpression (temp_decl.name);
4711 ccode.add_assignment (ctemp, get_cvalue (expr.inner));
4712 var ccheck = create_type_check (ctemp, expr.type_reference);
4713 var ccast = new CCodeCastExpression (ctemp, expr.type_reference.get_cname ());
4714 var cnull = new CCodeConstant ("NULL");
4716 set_cvalue (expr, new CCodeConditionalExpression (ccheck, ccast, cnull));
4717 } else {
4718 set_cvalue (expr, generate_instance_cast (get_cvalue (expr.inner), expr.type_reference.data_type));
4720 } else {
4721 if (expr.is_silent_cast) {
4722 expr.error = true;
4723 Report.error (expr.source_reference, "Operation not supported for this type");
4724 return;
4727 // retain array length
4728 var array_type = expr.type_reference as ArrayType;
4729 if (array_type != null && expr.inner.value_type is ArrayType) {
4730 for (int dim = 1; dim <= array_type.rank; dim++) {
4731 append_array_length (expr, get_array_length_cexpression (expr.inner, dim));
4733 } else if (array_type != null) {
4734 // cast from non-array to array, set invalid length
4735 // required by string.data, e.g.
4736 for (int dim = 1; dim <= array_type.rank; dim++) {
4737 append_array_length (expr, new CCodeConstant ("-1"));
4741 var innercexpr = get_cvalue (expr.inner);
4742 if (expr.type_reference.data_type is Struct && !expr.type_reference.nullable &&
4743 expr.inner.value_type.data_type is Struct && expr.inner.value_type.nullable) {
4744 // nullable integer or float or boolean or struct cast to non-nullable
4745 innercexpr = new CCodeUnaryExpression (CCodeUnaryOperator.POINTER_INDIRECTION, innercexpr);
4747 set_cvalue (expr, new CCodeCastExpression (innercexpr, expr.type_reference.get_cname ()));
4749 if (expr.type_reference is DelegateType) {
4750 if (get_delegate_target (expr.inner) != null) {
4751 set_delegate_target (expr, get_delegate_target (expr.inner));
4752 } else {
4753 set_delegate_target (expr, new CCodeConstant ("NULL"));
4755 if (get_delegate_target_destroy_notify (expr.inner) != null) {
4756 set_delegate_target_destroy_notify (expr, get_delegate_target_destroy_notify (expr.inner));
4757 } else {
4758 set_delegate_target_destroy_notify (expr, new CCodeConstant ("NULL"));
4764 public override void visit_named_argument (NamedArgument expr) {
4765 set_cvalue (expr, get_cvalue (expr.inner));
4768 public override void visit_pointer_indirection (PointerIndirection expr) {
4769 set_cvalue (expr, new CCodeUnaryExpression (CCodeUnaryOperator.POINTER_INDIRECTION, get_cvalue (expr.inner)));
4772 public override void visit_addressof_expression (AddressofExpression expr) {
4773 set_cvalue (expr, new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, get_cvalue (expr.inner)));
4776 public override void visit_reference_transfer_expression (ReferenceTransferExpression expr) {
4777 /* (tmp = var, var = null, tmp) */
4778 var temp_decl = get_temp_variable (expr.value_type, true, expr, false);
4779 emit_temp_var (temp_decl);
4780 var cvar = get_variable_cexpression (temp_decl.name);
4782 ccode.add_assignment (cvar, get_cvalue (expr.inner));
4783 if (!(expr.value_type is DelegateType)) {
4784 ccode.add_assignment (get_cvalue (expr.inner), new CCodeConstant ("NULL"));
4787 set_cvalue (expr, cvar);
4789 var array_type = expr.value_type as ArrayType;
4790 if (array_type != null) {
4791 for (int dim = 1; dim <= array_type.rank; dim++) {
4792 append_array_length (expr, get_array_length_cexpression (expr.inner, dim));
4796 var delegate_type = expr.value_type as DelegateType;
4797 if (delegate_type != null && delegate_type.delegate_symbol.has_target) {
4798 var temp_target_decl = get_temp_variable (new PointerType (new VoidType ()), true, expr, false);
4799 emit_temp_var (temp_target_decl);
4800 var target_cvar = get_variable_cexpression (temp_target_decl.name);
4801 CCodeExpression target_destroy_notify;
4802 var target = get_delegate_target_cexpression (expr.inner, out target_destroy_notify);
4803 ccode.add_assignment (target_cvar, target);
4804 set_delegate_target (expr, target_cvar);
4805 if (target_destroy_notify != null) {
4806 var temp_target_destroy_notify_decl = get_temp_variable (gdestroynotify_type, true, expr, false);
4807 emit_temp_var (temp_target_destroy_notify_decl);
4808 var target_destroy_notify_cvar = get_variable_cexpression (temp_target_destroy_notify_decl.name);
4809 ccode.add_assignment (target_destroy_notify_cvar, target_destroy_notify);
4810 ccode.add_assignment (target_destroy_notify, new CCodeConstant ("NULL"));
4811 set_delegate_target_destroy_notify (expr, target_destroy_notify_cvar);
4816 public override void visit_binary_expression (BinaryExpression expr) {
4817 var cleft = get_cvalue (expr.left);
4818 var cright = get_cvalue (expr.right);
4820 CCodeExpression? left_chain = null;
4821 if (expr.chained) {
4822 var lbe = (BinaryExpression) expr.left;
4824 var temp_decl = get_temp_variable (lbe.right.value_type, true, null, false);
4825 emit_temp_var (temp_decl);
4826 var cvar = get_variable_cexpression (temp_decl.name);
4827 var clbe = (CCodeBinaryExpression) get_cvalue (lbe);
4828 if (lbe.chained) {
4829 clbe = (CCodeBinaryExpression) clbe.right;
4831 ccode.add_assignment (cvar, get_cvalue (lbe.right));
4832 clbe.right = get_variable_cexpression (temp_decl.name);
4833 left_chain = cleft;
4834 cleft = cvar;
4837 CCodeBinaryOperator op;
4838 if (expr.operator == BinaryOperator.PLUS) {
4839 op = CCodeBinaryOperator.PLUS;
4840 } else if (expr.operator == BinaryOperator.MINUS) {
4841 op = CCodeBinaryOperator.MINUS;
4842 } else if (expr.operator == BinaryOperator.MUL) {
4843 op = CCodeBinaryOperator.MUL;
4844 } else if (expr.operator == BinaryOperator.DIV) {
4845 op = CCodeBinaryOperator.DIV;
4846 } else if (expr.operator == BinaryOperator.MOD) {
4847 if (expr.value_type.equals (double_type)) {
4848 cfile.add_include ("math.h");
4849 var ccall = new CCodeFunctionCall (new CCodeIdentifier ("fmod"));
4850 ccall.add_argument (cleft);
4851 ccall.add_argument (cright);
4852 set_cvalue (expr, ccall);
4853 return;
4854 } else if (expr.value_type.equals (float_type)) {
4855 cfile.add_include ("math.h");
4856 var ccall = new CCodeFunctionCall (new CCodeIdentifier ("fmodf"));
4857 ccall.add_argument (cleft);
4858 ccall.add_argument (cright);
4859 set_cvalue (expr, ccall);
4860 return;
4861 } else {
4862 op = CCodeBinaryOperator.MOD;
4864 } else if (expr.operator == BinaryOperator.SHIFT_LEFT) {
4865 op = CCodeBinaryOperator.SHIFT_LEFT;
4866 } else if (expr.operator == BinaryOperator.SHIFT_RIGHT) {
4867 op = CCodeBinaryOperator.SHIFT_RIGHT;
4868 } else if (expr.operator == BinaryOperator.LESS_THAN) {
4869 op = CCodeBinaryOperator.LESS_THAN;
4870 } else if (expr.operator == BinaryOperator.GREATER_THAN) {
4871 op = CCodeBinaryOperator.GREATER_THAN;
4872 } else if (expr.operator == BinaryOperator.LESS_THAN_OR_EQUAL) {
4873 op = CCodeBinaryOperator.LESS_THAN_OR_EQUAL;
4874 } else if (expr.operator == BinaryOperator.GREATER_THAN_OR_EQUAL) {
4875 op = CCodeBinaryOperator.GREATER_THAN_OR_EQUAL;
4876 } else if (expr.operator == BinaryOperator.EQUALITY) {
4877 op = CCodeBinaryOperator.EQUALITY;
4878 } else if (expr.operator == BinaryOperator.INEQUALITY) {
4879 op = CCodeBinaryOperator.INEQUALITY;
4880 } else if (expr.operator == BinaryOperator.BITWISE_AND) {
4881 op = CCodeBinaryOperator.BITWISE_AND;
4882 } else if (expr.operator == BinaryOperator.BITWISE_OR) {
4883 op = CCodeBinaryOperator.BITWISE_OR;
4884 } else if (expr.operator == BinaryOperator.BITWISE_XOR) {
4885 op = CCodeBinaryOperator.BITWISE_XOR;
4886 } else if (expr.operator == BinaryOperator.AND) {
4887 op = CCodeBinaryOperator.AND;
4888 } else if (expr.operator == BinaryOperator.OR) {
4889 op = CCodeBinaryOperator.OR;
4890 } else if (expr.operator == BinaryOperator.IN) {
4891 if (expr.right.value_type is ArrayType) {
4892 var array_type = (ArrayType) expr.right.value_type;
4893 var node = new CCodeFunctionCall (new CCodeIdentifier (generate_array_contains_wrapper (array_type)));
4894 node.add_argument (cright);
4895 node.add_argument (get_array_length_cexpression (expr.right));
4896 if (array_type.element_type is StructValueType) {
4897 node.add_argument (new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, cleft));
4898 } else {
4899 node.add_argument (cleft);
4901 set_cvalue (expr, node);
4902 } else {
4903 set_cvalue (expr, new CCodeBinaryExpression (CCodeBinaryOperator.EQUALITY, new CCodeBinaryExpression (CCodeBinaryOperator.BITWISE_AND, cright, cleft), cleft));
4905 return;
4906 } else {
4907 assert_not_reached ();
4910 if (expr.operator == BinaryOperator.EQUALITY ||
4911 expr.operator == BinaryOperator.INEQUALITY) {
4912 var left_type = expr.left.target_type;
4913 var right_type = expr.right.target_type;
4914 make_comparable_cexpression (ref left_type, ref cleft, ref right_type, ref cright);
4916 if (left_type is StructValueType && right_type is StructValueType) {
4917 var equalfunc = generate_struct_equal_function ((Struct) left_type.data_type as Struct);
4918 var ccall = new CCodeFunctionCall (new CCodeIdentifier (equalfunc));
4919 ccall.add_argument (cleft);
4920 ccall.add_argument (cright);
4921 cleft = ccall;
4922 cright = new CCodeConstant ("TRUE");
4923 } else if ((left_type is IntegerType || left_type is FloatingType || left_type is BooleanType) && left_type.nullable &&
4924 (right_type is IntegerType || right_type is FloatingType || right_type is BooleanType) && right_type.nullable) {
4925 var equalfunc = generate_numeric_equal_function ((Struct) left_type.data_type as Struct);
4926 var ccall = new CCodeFunctionCall (new CCodeIdentifier (equalfunc));
4927 ccall.add_argument (cleft);
4928 ccall.add_argument (cright);
4929 cleft = ccall;
4930 cright = new CCodeConstant ("TRUE");
4934 if (!(expr.left.value_type is NullType)
4935 && expr.left.value_type.compatible (string_type)
4936 && !(expr.right.value_type is NullType)
4937 && expr.right.value_type.compatible (string_type)) {
4938 if (expr.operator == BinaryOperator.PLUS) {
4939 // string concatenation
4940 if (expr.left.is_constant () && expr.right.is_constant ()) {
4941 string left, right;
4943 if (cleft is CCodeIdentifier) {
4944 left = ((CCodeIdentifier) cleft).name;
4945 } else if (cleft is CCodeConstant) {
4946 left = ((CCodeConstant) cleft).name;
4947 } else {
4948 assert_not_reached ();
4950 if (cright is CCodeIdentifier) {
4951 right = ((CCodeIdentifier) cright).name;
4952 } else if (cright is CCodeConstant) {
4953 right = ((CCodeConstant) cright).name;
4954 } else {
4955 assert_not_reached ();
4958 set_cvalue (expr, new CCodeConstant ("%s %s".printf (left, right)));
4959 return;
4960 } else {
4961 if (context.profile == Profile.POSIX) {
4962 // convert to strcat(strcpy(malloc(1+strlen(a)+strlen(b)),a),b)
4963 var strcat = new CCodeFunctionCall (new CCodeIdentifier ("strcat"));
4964 var strcpy = new CCodeFunctionCall (new CCodeIdentifier ("strcpy"));
4965 var malloc = new CCodeFunctionCall (new CCodeIdentifier ("malloc"));
4967 var strlen_a = new CCodeFunctionCall (new CCodeIdentifier ("strlen"));
4968 strlen_a.add_argument(cleft);
4969 var strlen_b = new CCodeFunctionCall (new CCodeIdentifier ("strlen"));
4970 strlen_b.add_argument(cright);
4971 var newlength = new CCodeBinaryExpression (CCodeBinaryOperator.PLUS, new CCodeIdentifier("1"),
4972 new CCodeBinaryExpression (CCodeBinaryOperator.PLUS, strlen_a, strlen_b));
4973 malloc.add_argument(newlength);
4975 strcpy.add_argument(malloc);
4976 strcpy.add_argument(cleft);
4978 strcat.add_argument(strcpy);
4979 strcat.add_argument(cright);
4980 set_cvalue (expr, strcat);
4981 } else {
4982 // convert to g_strconcat (a, b, NULL)
4983 var temp_var = get_temp_variable (expr.value_type, true, null, false);
4984 var temp_ref = get_variable_cexpression (temp_var.name);
4985 emit_temp_var (temp_var);
4987 var ccall = new CCodeFunctionCall (new CCodeIdentifier ("g_strconcat"));
4988 ccall.add_argument (cleft);
4989 ccall.add_argument (cright);
4990 ccall.add_argument (new CCodeConstant("NULL"));
4992 ccode.add_assignment (temp_ref, ccall);
4993 set_cvalue (expr, temp_ref);
4995 return;
4997 } else if (expr.operator == BinaryOperator.EQUALITY
4998 || expr.operator == BinaryOperator.INEQUALITY
4999 || expr.operator == BinaryOperator.LESS_THAN
5000 || expr.operator == BinaryOperator.GREATER_THAN
5001 || expr.operator == BinaryOperator.LESS_THAN_OR_EQUAL
5002 || expr.operator == BinaryOperator.GREATER_THAN_OR_EQUAL) {
5003 var ccall = new CCodeFunctionCall (new CCodeIdentifier ("g_strcmp0"));
5004 ccall.add_argument (cleft);
5005 ccall.add_argument (cright);
5006 cleft = ccall;
5007 cright = new CCodeConstant ("0");
5011 set_cvalue (expr, new CCodeBinaryExpression (op, cleft, cright));
5012 if (left_chain != null) {
5013 set_cvalue (expr, new CCodeBinaryExpression (CCodeBinaryOperator.AND, left_chain, get_cvalue (expr)));
5017 public string? get_type_check_function (TypeSymbol type) {
5018 var cl = type as Class;
5019 if (cl != null && cl.type_check_function != null) {
5020 return cl.type_check_function;
5021 } else if ((cl != null && cl.is_compact) || type is Struct || type is Enum || type is Delegate) {
5022 return null;
5023 } else {
5024 return type.get_upper_case_cname ("IS_");
5028 CCodeExpression? create_type_check (CCodeNode ccodenode, DataType type) {
5029 var et = type as ErrorType;
5030 if (et != null && et.error_code != null) {
5031 var matches_call = new CCodeFunctionCall (new CCodeIdentifier ("g_error_matches"));
5032 matches_call.add_argument ((CCodeExpression) ccodenode);
5033 matches_call.add_argument (new CCodeIdentifier (et.error_domain.get_upper_case_cname ()));
5034 matches_call.add_argument (new CCodeIdentifier (et.error_code.get_cname ()));
5035 return matches_call;
5036 } else if (et != null && et.error_domain != null) {
5037 var instance_domain = new CCodeMemberAccess.pointer ((CCodeExpression) ccodenode, "domain");
5038 var type_domain = new CCodeIdentifier (et.error_domain.get_upper_case_cname ());
5039 return new CCodeBinaryExpression (CCodeBinaryOperator.EQUALITY, instance_domain, type_domain);
5040 } else {
5041 string type_check_func = get_type_check_function (type.data_type);
5042 if (type_check_func == null) {
5043 return new CCodeInvalidExpression ();
5045 var ccheck = new CCodeFunctionCall (new CCodeIdentifier (type_check_func));
5046 ccheck.add_argument ((CCodeExpression) ccodenode);
5047 return ccheck;
5051 string generate_array_contains_wrapper (ArrayType array_type) {
5052 string array_contains_func = "_vala_%s_array_contains".printf (array_type.element_type.get_lower_case_cname ());
5054 if (!add_wrapper (array_contains_func)) {
5055 return array_contains_func;
5058 var function = new CCodeFunction (array_contains_func, "gboolean");
5059 function.modifiers = CCodeModifiers.STATIC;
5061 function.add_parameter (new CCodeParameter ("stack", array_type.get_cname ()));
5062 function.add_parameter (new CCodeParameter ("stack_length", "int"));
5063 if (array_type.element_type is StructValueType) {
5064 function.add_parameter (new CCodeParameter ("needle", array_type.element_type.get_cname () + "*"));
5065 } else {
5066 function.add_parameter (new CCodeParameter ("needle", array_type.element_type.get_cname ()));
5069 push_function (function);
5071 ccode.add_declaration ("int", new CCodeVariableDeclarator ("i"));
5073 var cloop_initializer = new CCodeAssignment (new CCodeIdentifier ("i"), new CCodeConstant ("0"));
5074 var cloop_condition = new CCodeBinaryExpression (CCodeBinaryOperator.LESS_THAN, new CCodeIdentifier ("i"), new CCodeIdentifier ("stack_length"));
5075 var cloop_iterator = new CCodeUnaryExpression (CCodeUnaryOperator.POSTFIX_INCREMENT, new CCodeIdentifier ("i"));
5076 ccode.open_for (cloop_initializer, cloop_condition, cloop_iterator);
5078 var celement = new CCodeElementAccess (new CCodeIdentifier ("stack"), new CCodeIdentifier ("i"));
5079 var cneedle = new CCodeIdentifier ("needle");
5080 CCodeBinaryExpression cif_condition;
5081 if (array_type.element_type.compatible (string_type)) {
5082 var ccall = new CCodeFunctionCall (new CCodeIdentifier ("g_strcmp0"));
5083 ccall.add_argument (celement);
5084 ccall.add_argument (cneedle);
5085 cif_condition = new CCodeBinaryExpression (CCodeBinaryOperator.EQUALITY, ccall, new CCodeConstant ("0"));
5086 } else if (array_type.element_type is StructValueType) {
5087 var equalfunc = generate_struct_equal_function ((Struct) array_type.element_type.data_type as Struct);
5088 var ccall = new CCodeFunctionCall (new CCodeIdentifier (equalfunc));
5089 ccall.add_argument (new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, celement));
5090 ccall.add_argument (cneedle);
5091 cif_condition = new CCodeBinaryExpression (CCodeBinaryOperator.EQUALITY, ccall, new CCodeConstant ("TRUE"));
5092 } else {
5093 cif_condition = new CCodeBinaryExpression (CCodeBinaryOperator.EQUALITY, cneedle, celement);
5096 ccode.open_if (cif_condition);
5097 ccode.add_return (new CCodeConstant ("TRUE"));
5098 ccode.close ();
5100 ccode.close ();
5102 ccode.add_return (new CCodeConstant ("FALSE"));
5104 pop_function ();
5106 cfile.add_function_declaration (function);
5107 cfile.add_function (function);
5109 return array_contains_func;
5112 public override void visit_type_check (TypeCheck expr) {
5113 generate_type_declaration (expr.type_reference, cfile);
5115 set_cvalue (expr, create_type_check (get_cvalue (expr.expression), expr.type_reference));
5116 if (get_cvalue (expr) is CCodeInvalidExpression) {
5117 Report.error (expr.source_reference, "type check expressions not supported for compact classes, structs, and enums");
5121 public override void visit_lambda_expression (LambdaExpression lambda) {
5122 // use instance position from delegate
5123 var dt = (DelegateType) lambda.target_type;
5124 lambda.method.cinstance_parameter_position = dt.delegate_symbol.cinstance_parameter_position;
5126 lambda.accept_children (this);
5128 bool expr_owned = lambda.value_type.value_owned;
5130 set_cvalue (lambda, new CCodeIdentifier (lambda.method.get_cname ()));
5132 var delegate_type = (DelegateType) lambda.target_type;
5133 if (lambda.method.closure) {
5134 int block_id = get_block_id (current_closure_block);
5135 var delegate_target = get_variable_cexpression ("_data%d_".printf (block_id));
5136 if (expr_owned || delegate_type.is_called_once) {
5137 var ref_call = new CCodeFunctionCall (new CCodeIdentifier ("block%d_data_ref".printf (block_id)));
5138 ref_call.add_argument (delegate_target);
5139 delegate_target = ref_call;
5140 set_delegate_target_destroy_notify (lambda, new CCodeIdentifier ("block%d_data_unref".printf (block_id)));
5141 } else {
5142 set_delegate_target_destroy_notify (lambda, new CCodeConstant ("NULL"));
5144 set_delegate_target (lambda, delegate_target);
5145 } else if (get_this_type () != null || in_constructor) {
5146 CCodeExpression delegate_target = get_result_cexpression ("self");
5147 if (expr_owned || delegate_type.is_called_once) {
5148 if (get_this_type () != null) {
5149 var ref_call = new CCodeFunctionCall (get_dup_func_expression (get_this_type (), lambda.source_reference));
5150 ref_call.add_argument (delegate_target);
5151 delegate_target = ref_call;
5152 set_delegate_target_destroy_notify (lambda, get_destroy_func_expression (get_this_type ()));
5153 } else {
5154 // in constructor
5155 var ref_call = new CCodeFunctionCall (new CCodeIdentifier ("g_object_ref"));
5156 ref_call.add_argument (delegate_target);
5157 delegate_target = ref_call;
5158 set_delegate_target_destroy_notify (lambda, new CCodeIdentifier ("g_object_unref"));
5160 } else {
5161 set_delegate_target_destroy_notify (lambda, new CCodeConstant ("NULL"));
5163 set_delegate_target (lambda, delegate_target);
5164 } else {
5165 set_delegate_target (lambda, new CCodeConstant ("NULL"));
5166 set_delegate_target_destroy_notify (lambda, new CCodeConstant ("NULL"));
5170 public CCodeExpression convert_from_generic_pointer (CCodeExpression cexpr, DataType actual_type) {
5171 var result = cexpr;
5172 if (is_reference_type_argument (actual_type) || is_nullable_value_type_argument (actual_type)) {
5173 result = new CCodeCastExpression (cexpr, actual_type.get_cname ());
5174 } else if (is_signed_integer_type_argument (actual_type)) {
5175 var cconv = new CCodeFunctionCall (new CCodeIdentifier ("GPOINTER_TO_INT"));
5176 cconv.add_argument (cexpr);
5177 result = cconv;
5178 } else if (is_unsigned_integer_type_argument (actual_type)) {
5179 var cconv = new CCodeFunctionCall (new CCodeIdentifier ("GPOINTER_TO_UINT"));
5180 cconv.add_argument (cexpr);
5181 result = cconv;
5183 return result;
5186 public CCodeExpression convert_to_generic_pointer (CCodeExpression cexpr, DataType actual_type) {
5187 var result = cexpr;
5188 if (is_signed_integer_type_argument (actual_type)) {
5189 var cconv = new CCodeFunctionCall (new CCodeIdentifier ("GINT_TO_POINTER"));
5190 cconv.add_argument (cexpr);
5191 result = cconv;
5192 } else if (is_unsigned_integer_type_argument (actual_type)) {
5193 var cconv = new CCodeFunctionCall (new CCodeIdentifier ("GUINT_TO_POINTER"));
5194 cconv.add_argument (cexpr);
5195 result = cconv;
5197 return result;
5200 // manage memory and implicit casts
5201 public CCodeExpression transform_expression (CCodeExpression source_cexpr, DataType? expression_type, DataType? target_type, Expression? expr = null) {
5202 var cexpr = source_cexpr;
5203 if (expression_type == null) {
5204 return cexpr;
5208 if (expression_type.value_owned
5209 && expression_type.floating_reference) {
5210 /* floating reference, sink it.
5212 var cl = expression_type.data_type as ObjectTypeSymbol;
5213 var sink_func = (cl != null) ? cl.get_ref_sink_function () : null;
5215 if (sink_func != null) {
5216 var csink = new CCodeFunctionCall (new CCodeIdentifier (sink_func));
5217 csink.add_argument (cexpr);
5219 cexpr = csink;
5220 } else {
5221 Report.error (null, "type `%s' does not support floating references".printf (expression_type.data_type.name));
5225 bool boxing = (expression_type is ValueType && !expression_type.nullable
5226 && target_type is ValueType && target_type.nullable);
5227 bool unboxing = (expression_type is ValueType && expression_type.nullable
5228 && target_type is ValueType && !target_type.nullable);
5230 bool gvalue_boxing = (context.profile == Profile.GOBJECT
5231 && target_type != null
5232 && target_type.data_type == gvalue_type
5233 && !(expression_type is NullType)
5234 && expression_type.get_type_id () != "G_TYPE_VALUE");
5235 bool gvariant_boxing = (context.profile == Profile.GOBJECT
5236 && target_type != null
5237 && target_type.data_type == gvariant_type
5238 && !(expression_type is NullType)
5239 && expression_type.data_type != gvariant_type);
5241 if (expression_type.value_owned
5242 && (target_type == null || !target_type.value_owned || boxing || unboxing)
5243 && !gvalue_boxing /* gvalue can assume ownership of value, no need to free it */) {
5244 // value leaked, destroy it
5245 var pointer_type = target_type as PointerType;
5246 if (pointer_type != null && !(pointer_type.base_type is VoidType)) {
5247 // manual memory management for non-void pointers
5248 // treat void* special to not leak memory with void* method parameters
5249 } else if (requires_destroy (expression_type)) {
5250 var decl = get_temp_variable (expression_type, true, expression_type, false);
5251 emit_temp_var (decl);
5252 temp_ref_vars.insert (0, decl);
5253 ccode.add_assignment (get_variable_cexpression (decl.name), cexpr);
5254 cexpr = get_variable_cexpression (decl.name);
5256 if (expression_type is ArrayType && expr != null) {
5257 var array_type = (ArrayType) expression_type;
5258 for (int dim = 1; dim <= array_type.rank; dim++) {
5259 var len_decl = new LocalVariable (int_type.copy (), get_array_length_cname (decl.name, dim));
5260 emit_temp_var (len_decl);
5261 ccode.add_assignment (get_variable_cexpression (len_decl.name), get_array_length_cexpression (expr, dim));
5263 } else if (expression_type is DelegateType && expr != null) {
5264 var target_decl = new LocalVariable (new PointerType (new VoidType ()), get_delegate_target_cname (decl.name));
5265 emit_temp_var (target_decl);
5266 var target_destroy_notify_decl = new LocalVariable (gdestroynotify_type, get_delegate_target_destroy_notify_cname (decl.name));
5267 emit_temp_var (target_destroy_notify_decl);
5268 CCodeExpression target_destroy_notify;
5269 ccode.add_assignment (get_variable_cexpression (target_decl.name), get_delegate_target_cexpression (expr, out target_destroy_notify));
5270 ccode.add_assignment (get_variable_cexpression (target_destroy_notify_decl.name), target_destroy_notify);
5276 if (target_type == null) {
5277 // value will be destroyed, no need for implicit casts
5278 return cexpr;
5281 if (gvalue_boxing) {
5282 // implicit conversion to GValue
5283 var decl = get_temp_variable (target_type, true, target_type);
5284 emit_temp_var (decl);
5286 if (!target_type.value_owned) {
5287 // boxed GValue leaked, destroy it
5288 temp_ref_vars.insert (0, decl);
5291 if (target_type.nullable) {
5292 var newcall = new CCodeFunctionCall (new CCodeIdentifier ("g_new0"));
5293 newcall.add_argument (new CCodeConstant ("GValue"));
5294 newcall.add_argument (new CCodeConstant ("1"));
5295 var newassignment = new CCodeAssignment (get_variable_cexpression (decl.name), newcall);
5296 ccode.add_expression (newassignment);
5299 var ccall = new CCodeFunctionCall (new CCodeIdentifier ("g_value_init"));
5300 if (target_type.nullable) {
5301 ccall.add_argument (get_variable_cexpression (decl.name));
5302 } else {
5303 ccall.add_argument (new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, get_variable_cexpression (decl.name)));
5305 ccall.add_argument (new CCodeIdentifier (expression_type.get_type_id ()));
5306 ccode.add_expression (ccall);
5308 if (requires_destroy (expression_type)) {
5309 ccall = new CCodeFunctionCall (get_value_taker_function (expression_type));
5310 } else {
5311 ccall = new CCodeFunctionCall (get_value_setter_function (expression_type));
5313 if (target_type.nullable) {
5314 ccall.add_argument (get_variable_cexpression (decl.name));
5315 } else {
5316 ccall.add_argument (new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, get_variable_cexpression (decl.name)));
5318 if (expression_type.is_real_non_null_struct_type ()) {
5319 ccall.add_argument (new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, cexpr));
5320 } else {
5321 ccall.add_argument (cexpr);
5324 ccode.add_expression (ccall);
5326 cexpr = get_variable_cexpression (decl.name);
5328 return cexpr;
5329 } else if (gvariant_boxing) {
5330 // implicit conversion to GVariant
5331 string variant_func = "_variant_new%d".printf (++next_variant_function_id);
5333 var ccall = new CCodeFunctionCall (new CCodeIdentifier (variant_func));
5334 ccall.add_argument (cexpr);
5336 var cfunc = new CCodeFunction (variant_func, "GVariant*");
5337 cfunc.modifiers = CCodeModifiers.STATIC;
5338 cfunc.add_parameter (new CCodeParameter ("value", expression_type.get_cname ()));
5340 if (expression_type is ArrayType) {
5341 // return array length if appropriate
5342 var array_type = (ArrayType) expression_type;
5344 for (int dim = 1; dim <= array_type.rank; dim++) {
5345 ccall.add_argument (get_array_length_cexpression (expr, dim));
5346 cfunc.add_parameter (new CCodeParameter (get_array_length_cname ("value", dim), "gint"));
5350 push_function (cfunc);
5352 var result = serialize_expression (expression_type, new CCodeIdentifier ("value"));
5354 // sink floating reference
5355 var sink = new CCodeFunctionCall (new CCodeIdentifier ("g_variant_ref_sink"));
5356 sink.add_argument (result);
5357 ccode.add_return (sink);
5359 pop_function ();
5361 cfile.add_function_declaration (cfunc);
5362 cfile.add_function (cfunc);
5364 return ccall;
5365 } else if (boxing) {
5366 // value needs to be boxed
5368 var unary = cexpr as CCodeUnaryExpression;
5369 if (unary != null && unary.operator == CCodeUnaryOperator.POINTER_INDIRECTION) {
5370 // *expr => expr
5371 cexpr = unary.inner;
5372 } else if (cexpr is CCodeIdentifier || cexpr is CCodeMemberAccess) {
5373 cexpr = new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, cexpr);
5374 } else {
5375 var decl = get_temp_variable (expression_type, expression_type.value_owned, expression_type, false);
5376 emit_temp_var (decl);
5378 ccode.add_assignment (get_variable_cexpression (decl.name), cexpr);
5379 cexpr = new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, get_variable_cexpression (decl.name));
5381 } else if (unboxing) {
5382 // unbox value
5384 cexpr = new CCodeUnaryExpression (CCodeUnaryOperator.POINTER_INDIRECTION, cexpr);
5385 } else {
5386 cexpr = get_implicit_cast_expression (cexpr, expression_type, target_type, expr);
5389 if (target_type.value_owned && (!expression_type.value_owned || boxing || unboxing)) {
5390 // need to copy value
5391 if (requires_copy (target_type) && !(expression_type is NullType)) {
5392 CodeNode node = expr;
5393 if (node == null) {
5394 node = expression_type;
5397 var decl = get_temp_variable (target_type, true, node, false);
5398 emit_temp_var (decl);
5399 ccode.add_assignment (get_variable_cexpression (decl.name), get_ref_cexpression (target_type, cexpr, expr, node));
5400 cexpr = get_variable_cexpression (decl.name);
5404 return cexpr;
5407 public virtual CCodeExpression get_implicit_cast_expression (CCodeExpression source_cexpr, DataType? expression_type, DataType? target_type, Expression? expr = null) {
5408 var cexpr = source_cexpr;
5410 if (expression_type.data_type != null && expression_type.data_type == target_type.data_type) {
5411 // same type, no cast required
5412 return cexpr;
5415 if (expression_type is NullType) {
5416 // null literal, no cast required when not converting to generic type pointer
5417 return cexpr;
5420 generate_type_declaration (target_type, cfile);
5422 var cl = target_type.data_type as Class;
5423 var iface = target_type.data_type as Interface;
5424 if (context.checking && (iface != null || (cl != null && !cl.is_compact))) {
5425 // checked cast for strict subtypes of GTypeInstance
5426 return generate_instance_cast (cexpr, target_type.data_type);
5427 } else if (target_type.data_type != null && expression_type.get_cname () != target_type.get_cname ()) {
5428 var st = target_type.data_type as Struct;
5429 if (target_type.data_type.is_reference_type () || (st != null && st.is_simple_type ())) {
5430 // don't cast non-simple structs
5431 return new CCodeCastExpression (cexpr, target_type.get_cname ());
5432 } else {
5433 return cexpr;
5435 } else {
5436 return cexpr;
5440 public void store_property (Property prop, Expression? instance, TargetValue value) {
5441 if (instance is BaseAccess) {
5442 if (prop.base_property != null) {
5443 var base_class = (Class) prop.base_property.parent_symbol;
5444 var vcast = new CCodeFunctionCall (new CCodeIdentifier ("%s_CLASS".printf (base_class.get_upper_case_cname (null))));
5445 vcast.add_argument (new CCodeIdentifier ("%s_parent_class".printf (current_class.get_lower_case_cname (null))));
5447 var ccall = new CCodeFunctionCall (new CCodeMemberAccess.pointer (vcast, "set_%s".printf (prop.name)));
5448 ccall.add_argument ((CCodeExpression) get_ccodenode (instance));
5449 ccall.add_argument (get_cvalue_ (value));
5451 ccode.add_expression (ccall);
5452 } else if (prop.base_interface_property != null) {
5453 var base_iface = (Interface) prop.base_interface_property.parent_symbol;
5454 string parent_iface_var = "%s_%s_parent_iface".printf (current_class.get_lower_case_cname (null), base_iface.get_lower_case_cname (null));
5456 var ccall = new CCodeFunctionCall (new CCodeMemberAccess.pointer (new CCodeIdentifier (parent_iface_var), "set_%s".printf (prop.name)));
5457 ccall.add_argument ((CCodeExpression) get_ccodenode (instance));
5458 ccall.add_argument (get_cvalue_ (value));
5460 ccode.add_expression (ccall);
5462 return;
5465 var set_func = "g_object_set";
5467 var base_property = prop;
5468 if (!prop.no_accessor_method) {
5469 if (prop.base_property != null) {
5470 base_property = prop.base_property;
5471 } else if (prop.base_interface_property != null) {
5472 base_property = prop.base_interface_property;
5475 if (prop is DynamicProperty) {
5476 set_func = get_dynamic_property_setter_cname ((DynamicProperty) prop);
5477 } else {
5478 generate_property_accessor_declaration (base_property.set_accessor, cfile);
5479 set_func = base_property.set_accessor.get_cname ();
5481 if (!prop.external && prop.external_package) {
5482 // internal VAPI properties
5483 // only add them once per source file
5484 if (add_generated_external_symbol (prop)) {
5485 visit_property (prop);
5491 var ccall = new CCodeFunctionCall (new CCodeIdentifier (set_func));
5493 if (prop.binding == MemberBinding.INSTANCE) {
5494 /* target instance is first argument */
5495 var cinstance = (CCodeExpression) get_ccodenode (instance);
5497 if (prop.parent_symbol is Struct) {
5498 // we need to pass struct instance by reference
5499 var unary = cinstance as CCodeUnaryExpression;
5500 if (unary != null && unary.operator == CCodeUnaryOperator.POINTER_INDIRECTION) {
5501 // *expr => expr
5502 cinstance = unary.inner;
5503 } else if (cinstance is CCodeIdentifier || cinstance is CCodeMemberAccess) {
5504 cinstance = new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, cinstance);
5505 } else {
5506 // if instance is e.g. a function call, we can't take the address of the expression
5507 // (tmp = expr, &tmp)
5509 var temp_var = get_temp_variable (instance.target_type, true, null, false);
5510 emit_temp_var (temp_var);
5511 ccode.add_assignment (get_variable_cexpression (temp_var.name), cinstance);
5513 cinstance = new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, get_variable_cexpression (temp_var.name));
5517 ccall.add_argument (cinstance);
5520 if (prop.no_accessor_method) {
5521 /* property name is second argument of g_object_set */
5522 ccall.add_argument (prop.get_canonical_cconstant ());
5525 var cexpr = get_cvalue_ (value);
5527 if (prop.property_type.is_real_non_null_struct_type ()) {
5528 cexpr = new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, cexpr);
5531 var array_type = prop.property_type as ArrayType;
5533 if (array_type != null && !prop.no_array_length) {
5534 var temp_var = get_temp_variable (prop.property_type, true, null, false);
5535 emit_temp_var (temp_var);
5536 ccode.add_assignment (get_variable_cexpression (temp_var.name), cexpr);
5537 ccall.add_argument (get_variable_cexpression (temp_var.name));
5538 } else {
5539 ccall.add_argument (cexpr);
5542 if (array_type != null && !prop.no_array_length) {
5543 for (int dim = 1; dim <= array_type.rank; dim++) {
5544 ccall.add_argument (get_array_length_cvalue (value, dim));
5546 } else if (prop.property_type is DelegateType) {
5547 var delegate_type = (DelegateType) prop.property_type;
5548 if (delegate_type.delegate_symbol.has_target) {
5549 ccall.add_argument (get_delegate_target_cvalue (value));
5553 if (prop.no_accessor_method) {
5554 ccall.add_argument (new CCodeConstant ("NULL"));
5557 ccode.add_expression (ccall);
5560 public bool add_wrapper (string wrapper_name) {
5561 return wrappers.add (wrapper_name);
5564 public bool add_generated_external_symbol (Symbol external_symbol) {
5565 return generated_external_symbols.add (external_symbol);
5568 public static DataType get_data_type_for_symbol (TypeSymbol sym) {
5569 DataType type = null;
5571 if (sym is Class) {
5572 type = new ObjectType ((Class) sym);
5573 } else if (sym is Interface) {
5574 type = new ObjectType ((Interface) sym);
5575 } else if (sym is Struct) {
5576 var st = (Struct) sym;
5577 if (st.is_boolean_type ()) {
5578 type = new BooleanType (st);
5579 } else if (st.is_integer_type ()) {
5580 type = new IntegerType (st);
5581 } else if (st.is_floating_type ()) {
5582 type = new FloatingType (st);
5583 } else {
5584 type = new StructValueType (st);
5586 } else if (sym is Enum) {
5587 type = new EnumValueType ((Enum) sym);
5588 } else if (sym is ErrorDomain) {
5589 type = new ErrorType ((ErrorDomain) sym, null);
5590 } else if (sym is ErrorCode) {
5591 type = new ErrorType ((ErrorDomain) sym.parent_symbol, (ErrorCode) sym);
5592 } else {
5593 Report.error (null, "internal error: `%s' is not a supported type".printf (sym.get_full_name ()));
5594 return new InvalidType ();
5597 return type;
5600 public CCodeExpression? default_value_for_type (DataType type, bool initializer_expression) {
5601 var st = type.data_type as Struct;
5602 var array_type = type as ArrayType;
5603 if (initializer_expression && !type.nullable &&
5604 ((st != null && !st.is_simple_type ()) ||
5605 (array_type != null && array_type.fixed_length))) {
5606 // 0-initialize struct with struct initializer { 0 }
5607 // only allowed as initializer expression in C
5608 var clist = new CCodeInitializerList ();
5609 clist.append (new CCodeConstant ("0"));
5610 return clist;
5611 } else if ((type.data_type != null && type.data_type.is_reference_type ())
5612 || type.nullable
5613 || type is PointerType || type is DelegateType
5614 || (array_type != null && !array_type.fixed_length)) {
5615 return new CCodeConstant ("NULL");
5616 } else if (type.data_type != null && type.data_type.get_default_value () != null) {
5617 return new CCodeConstant (type.data_type.get_default_value ());
5618 } else if (type.type_parameter != null) {
5619 return new CCodeConstant ("NULL");
5620 } else if (type is ErrorType) {
5621 return new CCodeConstant ("NULL");
5623 return null;
5626 private void create_property_type_check_statement (Property prop, bool check_return_type, TypeSymbol t, bool non_null, string var_name) {
5627 if (check_return_type) {
5628 create_type_check_statement (prop, prop.property_type, t, non_null, var_name);
5629 } else {
5630 create_type_check_statement (prop, new VoidType (), t, non_null, var_name);
5634 public void create_type_check_statement (CodeNode method_node, DataType ret_type, TypeSymbol t, bool non_null, string var_name) {
5635 var ccheck = new CCodeFunctionCall ();
5637 if (!context.assert) {
5638 return;
5639 } else if (context.checking && ((t is Class && !((Class) t).is_compact) || t is Interface)) {
5640 var ctype_check = new CCodeFunctionCall (new CCodeIdentifier (get_type_check_function (t)));
5641 ctype_check.add_argument (new CCodeIdentifier (var_name));
5643 CCodeExpression cexpr = ctype_check;
5644 if (!non_null) {
5645 var cnull = new CCodeBinaryExpression (CCodeBinaryOperator.EQUALITY, new CCodeIdentifier (var_name), new CCodeConstant ("NULL"));
5647 cexpr = new CCodeBinaryExpression (CCodeBinaryOperator.OR, cnull, ctype_check);
5649 ccheck.add_argument (cexpr);
5650 } else if (!non_null) {
5651 return;
5652 } else if (t == glist_type || t == gslist_type) {
5653 // NULL is empty list
5654 return;
5655 } else {
5656 var cnonnull = new CCodeBinaryExpression (CCodeBinaryOperator.INEQUALITY, new CCodeIdentifier (var_name), new CCodeConstant ("NULL"));
5657 ccheck.add_argument (cnonnull);
5660 var cm = method_node as CreationMethod;
5661 if (cm != null && cm.parent_symbol is ObjectTypeSymbol) {
5662 ccheck.call = new CCodeIdentifier ("g_return_val_if_fail");
5663 ccheck.add_argument (new CCodeConstant ("NULL"));
5664 } else if (ret_type is VoidType) {
5665 /* void function */
5666 ccheck.call = new CCodeIdentifier ("g_return_if_fail");
5667 } else {
5668 ccheck.call = new CCodeIdentifier ("g_return_val_if_fail");
5670 var cdefault = default_value_for_type (ret_type, false);
5671 if (cdefault != null) {
5672 ccheck.add_argument (cdefault);
5673 } else {
5674 return;
5678 ccode.add_expression (ccheck);
5681 public int get_param_pos (double param_pos, bool ellipsis = false) {
5682 if (!ellipsis) {
5683 if (param_pos >= 0) {
5684 return (int) (param_pos * 1000);
5685 } else {
5686 return (int) ((100 + param_pos) * 1000);
5688 } else {
5689 if (param_pos >= 0) {
5690 return (int) ((100 + param_pos) * 1000);
5691 } else {
5692 return (int) ((200 + param_pos) * 1000);
5697 public CCodeExpression? get_ccodenode (Expression node) {
5698 if (get_cvalue (node) == null) {
5699 node.emit (this);
5701 return get_cvalue (node);
5704 public override void visit_class (Class cl) {
5707 public void create_postcondition_statement (Expression postcondition) {
5708 var cassert = new CCodeFunctionCall (new CCodeIdentifier ("g_warn_if_fail"));
5710 postcondition.emit (this);
5712 cassert.add_argument (get_cvalue (postcondition));
5714 ccode.add_expression (cassert);
5717 public virtual bool is_gobject_property (Property prop) {
5718 return false;
5721 public DataType? get_this_type () {
5722 if (current_method != null && current_method.binding == MemberBinding.INSTANCE) {
5723 return current_method.this_parameter.variable_type;
5724 } else if (current_property_accessor != null && current_property_accessor.prop.binding == MemberBinding.INSTANCE) {
5725 return current_property_accessor.prop.this_parameter.variable_type;
5727 return null;
5730 public CCodeFunctionCall generate_instance_cast (CCodeExpression expr, TypeSymbol type) {
5731 var result = new CCodeFunctionCall (new CCodeIdentifier (type.get_upper_case_cname (null)));
5732 result.add_argument (expr);
5733 return result;
5736 void generate_struct_destroy_function (Struct st) {
5737 if (cfile.add_declaration (st.get_destroy_function ())) {
5738 // only generate function once per source file
5739 return;
5742 var function = new CCodeFunction (st.get_destroy_function (), "void");
5743 function.modifiers = CCodeModifiers.STATIC;
5744 function.add_parameter (new CCodeParameter ("self", st.get_cname () + "*"));
5746 push_function (function);
5748 foreach (Field f in st.get_fields ()) {
5749 if (f.binding == MemberBinding.INSTANCE) {
5750 if (requires_destroy (f.variable_type)) {
5751 var this_access = new MemberAccess.simple ("this");
5752 this_access.value_type = get_data_type_for_symbol ((TypeSymbol) f.parent_symbol);
5753 set_cvalue (this_access, new CCodeIdentifier ("(*self)"));
5755 ccode.add_expression (destroy_field (f, this_access.target_value));
5760 pop_function ();
5762 cfile.add_function_declaration (function);
5763 cfile.add_function (function);
5766 void generate_struct_copy_function (Struct st) {
5767 if (cfile.add_declaration (st.get_copy_function ())) {
5768 // only generate function once per source file
5769 return;
5772 var function = new CCodeFunction (st.get_copy_function (), "void");
5773 function.modifiers = CCodeModifiers.STATIC;
5774 function.add_parameter (new CCodeParameter ("self", "const " + st.get_cname () + "*"));
5775 function.add_parameter (new CCodeParameter ("dest", st.get_cname () + "*"));
5777 push_context (new EmitContext ());
5778 push_function (function);
5780 foreach (Field f in st.get_fields ()) {
5781 if (f.binding == MemberBinding.INSTANCE) {
5782 CCodeExpression copy = new CCodeMemberAccess.pointer (new CCodeIdentifier ("self"), f.name);
5783 if (requires_copy (f.variable_type)) {
5784 var this_access = new MemberAccess.simple ("this");
5785 this_access.value_type = get_data_type_for_symbol ((TypeSymbol) f.parent_symbol);
5786 set_cvalue (this_access, new CCodeIdentifier ("(*self)"));
5787 var ma = new MemberAccess (this_access, f.name);
5788 ma.symbol_reference = f;
5789 ma.value_type = f.variable_type.copy ();
5790 visit_member_access (ma);
5791 copy = get_ref_cexpression (f.variable_type, copy, ma, f);
5793 var dest = new CCodeMemberAccess.pointer (new CCodeIdentifier ("dest"), f.name);
5795 var array_type = f.variable_type as ArrayType;
5796 if (array_type != null && array_type.fixed_length) {
5797 // fixed-length (stack-allocated) arrays
5798 cfile.add_include ("string.h");
5800 var sizeof_call = new CCodeFunctionCall (new CCodeIdentifier ("sizeof"));
5801 sizeof_call.add_argument (new CCodeIdentifier (array_type.element_type.get_cname ()));
5802 var size = new CCodeBinaryExpression (CCodeBinaryOperator.MUL, new CCodeConstant ("%d".printf (array_type.length)), sizeof_call);
5804 var array_copy_call = new CCodeFunctionCall (new CCodeIdentifier ("memcpy"));
5805 array_copy_call.add_argument (dest);
5806 array_copy_call.add_argument (copy);
5807 array_copy_call.add_argument (size);
5808 ccode.add_expression (array_copy_call);
5809 } else {
5810 ccode.add_assignment (dest, copy);
5812 if (array_type != null && !f.no_array_length) {
5813 for (int dim = 1; dim <= array_type.rank; dim++) {
5814 var len_src = new CCodeMemberAccess.pointer (new CCodeIdentifier ("self"), get_array_length_cname (f.name, dim));
5815 var len_dest = new CCodeMemberAccess.pointer (new CCodeIdentifier ("dest"), get_array_length_cname (f.name, dim));
5816 ccode.add_assignment (len_dest, len_src);
5823 pop_function ();
5824 pop_context ();
5826 cfile.add_function_declaration (function);
5827 cfile.add_function (function);
5830 public void return_default_value (DataType return_type) {
5831 ccode.add_return (default_value_for_type (return_type, false));
5834 public virtual string? get_custom_creturn_type (Method m) {
5835 return null;
5838 public virtual void generate_dynamic_method_wrapper (DynamicMethod method) {
5841 public virtual bool method_has_wrapper (Method method) {
5842 return false;
5845 public virtual CCodeFunctionCall get_param_spec (Property prop) {
5846 return new CCodeFunctionCall (new CCodeIdentifier (""));
5849 public virtual CCodeFunctionCall get_signal_creation (Signal sig, TypeSymbol type) {
5850 return new CCodeFunctionCall (new CCodeIdentifier (""));
5853 public virtual void register_dbus_info (CCodeBlock block, ObjectTypeSymbol bindable) {
5856 public virtual string get_dynamic_property_getter_cname (DynamicProperty node) {
5857 Report.error (node.source_reference, "dynamic properties are not supported for %s".printf (node.dynamic_type.to_string ()));
5858 return "";
5861 public virtual string get_dynamic_property_setter_cname (DynamicProperty node) {
5862 Report.error (node.source_reference, "dynamic properties are not supported for %s".printf (node.dynamic_type.to_string ()));
5863 return "";
5866 public virtual string get_dynamic_signal_cname (DynamicSignal node) {
5867 return "";
5870 public virtual string get_dynamic_signal_connect_wrapper_name (DynamicSignal node) {
5871 return "";
5874 public virtual string get_dynamic_signal_connect_after_wrapper_name (DynamicSignal node) {
5875 return "";
5878 public virtual string get_dynamic_signal_disconnect_wrapper_name (DynamicSignal node) {
5879 return "";
5882 public virtual void generate_marshaller (List<Parameter> params, DataType return_type, bool dbus = false) {
5885 public virtual string get_marshaller_function (List<Parameter> params, DataType return_type, string? prefix = null, bool dbus = false) {
5886 return "";
5889 public virtual string get_array_length_cname (string array_cname, int dim) {
5890 return "";
5893 public virtual string get_parameter_array_length_cname (Parameter param, int dim) {
5894 return "";
5897 public virtual CCodeExpression get_array_length_cexpression (Expression array_expr, int dim = -1) {
5898 return new CCodeConstant ("");
5901 public virtual CCodeExpression get_array_length_cvalue (TargetValue value, int dim = -1) {
5902 return new CCodeInvalidExpression ();
5905 public virtual string get_array_size_cname (string array_cname) {
5906 return "";
5909 public virtual void add_simple_check (CodeNode node, bool always_fails = false) {
5912 public virtual string generate_ready_function (Method m) {
5913 return "";
5916 public CCodeExpression? get_cvalue (Expression expr) {
5917 if (expr.target_value == null) {
5918 return null;
5920 var glib_value = (GLibValue) expr.target_value;
5921 return glib_value.cvalue;
5924 public CCodeExpression? get_cvalue_ (TargetValue value) {
5925 var glib_value = (GLibValue) value;
5926 return glib_value.cvalue;
5929 public void set_cvalue (Expression expr, CCodeExpression? cvalue) {
5930 var glib_value = (GLibValue) expr.target_value;
5931 if (glib_value == null) {
5932 glib_value = new GLibValue (expr.value_type);
5933 expr.target_value = glib_value;
5935 glib_value.cvalue = cvalue;
5938 public CCodeExpression? get_array_size_cvalue (TargetValue value) {
5939 var glib_value = (GLibValue) value;
5940 return glib_value.array_size_cvalue;
5943 public void set_array_size_cvalue (TargetValue value, CCodeExpression? cvalue) {
5944 var glib_value = (GLibValue) value;
5945 glib_value.array_size_cvalue = cvalue;
5948 public CCodeExpression? get_delegate_target (Expression expr) {
5949 if (expr.target_value == null) {
5950 return null;
5952 var glib_value = (GLibValue) expr.target_value;
5953 return glib_value.delegate_target_cvalue;
5956 public void set_delegate_target (Expression expr, CCodeExpression? delegate_target) {
5957 var glib_value = (GLibValue) expr.target_value;
5958 if (glib_value == null) {
5959 glib_value = new GLibValue (expr.value_type);
5960 expr.target_value = glib_value;
5962 glib_value.delegate_target_cvalue = delegate_target;
5965 public CCodeExpression? get_delegate_target_destroy_notify (Expression expr) {
5966 if (expr.target_value == null) {
5967 return null;
5969 var glib_value = (GLibValue) expr.target_value;
5970 return glib_value.delegate_target_destroy_notify_cvalue;
5973 public void set_delegate_target_destroy_notify (Expression expr, CCodeExpression? destroy_notify) {
5974 var glib_value = (GLibValue) expr.target_value;
5975 if (glib_value == null) {
5976 glib_value = new GLibValue (expr.value_type);
5977 expr.target_value = glib_value;
5979 glib_value.delegate_target_destroy_notify_cvalue = destroy_notify;
5982 public void append_array_length (Expression expr, CCodeExpression size) {
5983 var glib_value = (GLibValue) expr.target_value;
5984 if (glib_value == null) {
5985 glib_value = new GLibValue (expr.value_type);
5986 expr.target_value = glib_value;
5988 glib_value.append_array_length_cvalue (size);
5991 public List<CCodeExpression>? get_array_lengths (Expression expr) {
5992 var glib_value = (GLibValue) expr.target_value;
5993 if (glib_value == null) {
5994 glib_value = new GLibValue (expr.value_type);
5995 expr.target_value = glib_value;
5997 return glib_value.array_length_cvalues;
6001 public class Vala.GLibValue : TargetValue {
6002 public CCodeExpression cvalue;
6004 public List<CCodeExpression> array_length_cvalues;
6005 public CCodeExpression? array_size_cvalue;
6007 public CCodeExpression? delegate_target_cvalue;
6008 public CCodeExpression? delegate_target_destroy_notify_cvalue;
6010 public GLibValue (DataType? value_type = null, CCodeExpression? cvalue = null) {
6011 base (value_type);
6012 this.cvalue = cvalue;
6015 public void append_array_length_cvalue (CCodeExpression length_cvalue) {
6016 if (array_length_cvalues == null) {
6017 array_length_cvalues = new ArrayList<CCodeExpression> ();
6019 array_length_cvalues.add (length_cvalue);
6022 public GLibValue copy () {
6023 var result = new GLibValue (value_type.copy (), cvalue);
6025 if (array_length_cvalues != null) {
6026 foreach (var cexpr in array_length_cvalues) {
6027 result.append_array_length_cvalue (cexpr);
6030 result.array_size_cvalue = array_size_cvalue;
6032 result.delegate_target_cvalue = delegate_target_cvalue;
6033 result.delegate_target_destroy_notify_cvalue = delegate_target_destroy_notify_cvalue;
6035 return result;