vapigen: Remove misleading --metadata, each .gir must have its own .metadata.
[vala-lang.git] / codegen / valaccodebasemodule.vala
blob26437d2b962cf778fdc308ccdf2fd7fa2e8bcb36
1 /* valaccodebasemodule.vala
3 * Copyright (C) 2006-2010 Jürg Billeter
4 * Copyright (C) 2006-2008 Raffaele Sandrini
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20 * Author:
21 * Jürg Billeter <j@bitron.ch>
22 * Raffaele Sandrini <raffaele@sandrini.ch>
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 CCodeFunction ccode;
35 public ArrayList<CCodeFunction> ccode_stack = new ArrayList<CCodeFunction> ();
36 public ArrayList<LocalVariable> temp_ref_vars = new ArrayList<LocalVariable> ();
37 public int next_temp_var_id;
38 public bool current_method_inner_error;
39 public bool current_method_return;
40 public Map<string,string> variable_name_map = new HashMap<string,string> (str_hash, str_equal);
42 public EmitContext (Symbol? symbol = null) {
43 current_symbol = symbol;
46 public void push_symbol (Symbol symbol) {
47 symbol_stack.add (current_symbol);
48 current_symbol = symbol;
51 public void pop_symbol () {
52 current_symbol = symbol_stack[symbol_stack.size - 1];
53 symbol_stack.remove_at (symbol_stack.size - 1);
57 public CodeContext context { get; set; }
59 public Symbol root_symbol;
61 public EmitContext emit_context = new EmitContext ();
63 List<EmitContext> emit_context_stack = new ArrayList<EmitContext> ();
65 public Symbol current_symbol { get { return emit_context.current_symbol; } }
67 public TryStatement current_try {
68 get { return emit_context.current_try; }
69 set { emit_context.current_try = value; }
72 public TypeSymbol? current_type_symbol {
73 get {
74 var sym = current_symbol;
75 while (sym != null) {
76 if (sym is TypeSymbol) {
77 return (TypeSymbol) sym;
79 sym = sym.parent_symbol;
81 return null;
85 public Class? current_class {
86 get { return current_type_symbol as Class; }
89 public Method? current_method {
90 get {
91 var sym = current_symbol;
92 while (sym is Block) {
93 sym = sym.parent_symbol;
95 return sym as Method;
99 public PropertyAccessor? current_property_accessor {
100 get {
101 var sym = current_symbol;
102 while (sym is Block) {
103 sym = sym.parent_symbol;
105 return sym as PropertyAccessor;
109 public DataType? current_return_type {
110 get {
111 var m = current_method;
112 if (m != null) {
113 return m.return_type;
116 var acc = current_property_accessor;
117 if (acc != null) {
118 if (acc.readable) {
119 return acc.value_type;
120 } else {
121 return void_type;
125 if (is_in_constructor () || is_in_destructor ()) {
126 return void_type;
129 return null;
133 public bool is_in_constructor () {
134 var sym = current_symbol;
135 while (sym != null) {
136 if (sym is Constructor) {
137 return true;
139 sym = sym.parent_symbol;
141 return false;
144 public bool is_in_destructor () {
145 var sym = current_symbol;
146 while (sym != null) {
147 if (sym is Destructor) {
148 return true;
150 sym = sym.parent_symbol;
152 return false;
155 public Block? current_closure_block {
156 get {
157 return next_closure_block (current_symbol);
161 public unowned Block? next_closure_block (Symbol sym) {
162 unowned Block block = null;
163 while (true) {
164 block = sym as Block;
165 if (!(sym is Block || sym is Method)) {
166 // no closure block
167 break;
169 if (block != null && block.captured) {
170 // closure block found
171 break;
173 sym = sym.parent_symbol;
175 return block;
178 public CCodeFile header_file;
179 public CCodeFile internal_header_file;
180 public CCodeFile cfile;
182 public EmitContext class_init_context;
183 public EmitContext base_init_context;
184 public EmitContext class_finalize_context;
185 public EmitContext base_finalize_context;
186 public EmitContext instance_init_context;
187 public EmitContext instance_finalize_context;
189 public CCodeStruct param_spec_struct;
190 public CCodeStruct closure_struct;
191 public CCodeEnum prop_enum;
193 public CCodeFunction ccode { get { return emit_context.ccode; } }
195 /* temporary variables that own their content */
196 public ArrayList<LocalVariable> temp_ref_vars { get { return emit_context.temp_ref_vars; } }
197 /* cache to check whether a certain marshaller has been created yet */
198 public Set<string> user_marshal_set;
199 /* (constant) hash table with all predefined marshallers */
200 public Set<string> predefined_marshal_set;
201 /* (constant) hash table with all reserved identifiers in the generated code */
202 Set<string> reserved_identifiers;
204 public int next_temp_var_id {
205 get { return emit_context.next_temp_var_id; }
206 set { emit_context.next_temp_var_id = value; }
209 public int next_regex_id = 0;
210 public bool in_creation_method { get { return current_method is CreationMethod; } }
211 public bool in_constructor = false;
212 public bool in_static_or_class_context = false;
214 public bool current_method_inner_error {
215 get { return emit_context.current_method_inner_error; }
216 set { emit_context.current_method_inner_error = value; }
219 public bool current_method_return {
220 get { return emit_context.current_method_return; }
221 set { emit_context.current_method_return = value; }
224 public int next_coroutine_state = 1;
225 int next_block_id = 0;
226 Map<Block,int> block_map = new HashMap<Block,int> ();
228 public DataType void_type = new VoidType ();
229 public DataType bool_type;
230 public DataType char_type;
231 public DataType uchar_type;
232 public DataType? unichar_type;
233 public DataType short_type;
234 public DataType ushort_type;
235 public DataType int_type;
236 public DataType uint_type;
237 public DataType long_type;
238 public DataType ulong_type;
239 public DataType int8_type;
240 public DataType uint8_type;
241 public DataType int16_type;
242 public DataType uint16_type;
243 public DataType int32_type;
244 public DataType uint32_type;
245 public DataType int64_type;
246 public DataType uint64_type;
247 public DataType string_type;
248 public DataType regex_type;
249 public DataType float_type;
250 public DataType double_type;
251 public TypeSymbol gtype_type;
252 public TypeSymbol gobject_type;
253 public ErrorType gerror_type;
254 public Class glist_type;
255 public Class gslist_type;
256 public Class gnode_type;
257 public Class gvaluearray_type;
258 public TypeSymbol gstringbuilder_type;
259 public TypeSymbol garray_type;
260 public TypeSymbol gbytearray_type;
261 public TypeSymbol gptrarray_type;
262 public TypeSymbol gthreadpool_type;
263 public DataType gquark_type;
264 public Struct gvalue_type;
265 public Class gvariant_type;
266 public Struct mutex_type;
267 public TypeSymbol type_module_type;
268 public TypeSymbol dbus_proxy_type;
269 public TypeSymbol dbus_object_type;
271 public bool in_plugin = false;
272 public string module_init_param_name;
274 public bool gvaluecollector_h_needed;
275 public bool requires_array_free;
276 public bool requires_array_move;
277 public bool requires_array_length;
279 public Set<string> wrappers;
280 Set<Symbol> generated_external_symbols;
282 public Map<string,string> variable_name_map { get { return emit_context.variable_name_map; } }
284 public CCodeBaseModule () {
285 predefined_marshal_set = new HashSet<string> (str_hash, str_equal);
286 predefined_marshal_set.add ("VOID:VOID");
287 predefined_marshal_set.add ("VOID:BOOLEAN");
288 predefined_marshal_set.add ("VOID:CHAR");
289 predefined_marshal_set.add ("VOID:UCHAR");
290 predefined_marshal_set.add ("VOID:INT");
291 predefined_marshal_set.add ("VOID:UINT");
292 predefined_marshal_set.add ("VOID:LONG");
293 predefined_marshal_set.add ("VOID:ULONG");
294 predefined_marshal_set.add ("VOID:ENUM");
295 predefined_marshal_set.add ("VOID:FLAGS");
296 predefined_marshal_set.add ("VOID:FLOAT");
297 predefined_marshal_set.add ("VOID:DOUBLE");
298 predefined_marshal_set.add ("VOID:STRING");
299 predefined_marshal_set.add ("VOID:POINTER");
300 predefined_marshal_set.add ("VOID:OBJECT");
301 predefined_marshal_set.add ("STRING:OBJECT,POINTER");
302 predefined_marshal_set.add ("VOID:UINT,POINTER");
303 predefined_marshal_set.add ("BOOLEAN:FLAGS");
305 reserved_identifiers = new HashSet<string> (str_hash, str_equal);
307 // C99 keywords
308 reserved_identifiers.add ("_Bool");
309 reserved_identifiers.add ("_Complex");
310 reserved_identifiers.add ("_Imaginary");
311 reserved_identifiers.add ("asm");
312 reserved_identifiers.add ("auto");
313 reserved_identifiers.add ("break");
314 reserved_identifiers.add ("case");
315 reserved_identifiers.add ("char");
316 reserved_identifiers.add ("const");
317 reserved_identifiers.add ("continue");
318 reserved_identifiers.add ("default");
319 reserved_identifiers.add ("do");
320 reserved_identifiers.add ("double");
321 reserved_identifiers.add ("else");
322 reserved_identifiers.add ("enum");
323 reserved_identifiers.add ("extern");
324 reserved_identifiers.add ("float");
325 reserved_identifiers.add ("for");
326 reserved_identifiers.add ("goto");
327 reserved_identifiers.add ("if");
328 reserved_identifiers.add ("inline");
329 reserved_identifiers.add ("int");
330 reserved_identifiers.add ("long");
331 reserved_identifiers.add ("register");
332 reserved_identifiers.add ("restrict");
333 reserved_identifiers.add ("return");
334 reserved_identifiers.add ("short");
335 reserved_identifiers.add ("signed");
336 reserved_identifiers.add ("sizeof");
337 reserved_identifiers.add ("static");
338 reserved_identifiers.add ("struct");
339 reserved_identifiers.add ("switch");
340 reserved_identifiers.add ("typedef");
341 reserved_identifiers.add ("union");
342 reserved_identifiers.add ("unsigned");
343 reserved_identifiers.add ("void");
344 reserved_identifiers.add ("volatile");
345 reserved_identifiers.add ("while");
347 // MSVC keywords
348 reserved_identifiers.add ("cdecl");
350 // reserved for Vala/GObject naming conventions
351 reserved_identifiers.add ("error");
352 reserved_identifiers.add ("result");
353 reserved_identifiers.add ("self");
356 public override void emit (CodeContext context) {
357 this.context = context;
359 root_symbol = context.root;
361 bool_type = new BooleanType ((Struct) root_symbol.scope.lookup ("bool"));
362 char_type = new IntegerType ((Struct) root_symbol.scope.lookup ("char"));
363 uchar_type = new IntegerType ((Struct) root_symbol.scope.lookup ("uchar"));
364 short_type = new IntegerType ((Struct) root_symbol.scope.lookup ("short"));
365 ushort_type = new IntegerType ((Struct) root_symbol.scope.lookup ("ushort"));
366 int_type = new IntegerType ((Struct) root_symbol.scope.lookup ("int"));
367 uint_type = new IntegerType ((Struct) root_symbol.scope.lookup ("uint"));
368 long_type = new IntegerType ((Struct) root_symbol.scope.lookup ("long"));
369 ulong_type = new IntegerType ((Struct) root_symbol.scope.lookup ("ulong"));
370 int8_type = new IntegerType ((Struct) root_symbol.scope.lookup ("int8"));
371 uint8_type = new IntegerType ((Struct) root_symbol.scope.lookup ("uint8"));
372 int16_type = new IntegerType ((Struct) root_symbol.scope.lookup ("int16"));
373 uint16_type = new IntegerType ((Struct) root_symbol.scope.lookup ("uint16"));
374 int32_type = new IntegerType ((Struct) root_symbol.scope.lookup ("int32"));
375 uint32_type = new IntegerType ((Struct) root_symbol.scope.lookup ("uint32"));
376 int64_type = new IntegerType ((Struct) root_symbol.scope.lookup ("int64"));
377 uint64_type = new IntegerType ((Struct) root_symbol.scope.lookup ("uint64"));
378 float_type = new FloatingType ((Struct) root_symbol.scope.lookup ("float"));
379 double_type = new FloatingType ((Struct) root_symbol.scope.lookup ("double"));
380 string_type = new ObjectType ((Class) root_symbol.scope.lookup ("string"));
381 var unichar_struct = (Struct) root_symbol.scope.lookup ("unichar");
382 if (unichar_struct != null) {
383 unichar_type = new IntegerType (unichar_struct);
386 if (context.profile == Profile.GOBJECT) {
387 var glib_ns = root_symbol.scope.lookup ("GLib");
389 gtype_type = (TypeSymbol) glib_ns.scope.lookup ("Type");
390 gobject_type = (TypeSymbol) glib_ns.scope.lookup ("Object");
391 gerror_type = new ErrorType (null, null);
392 glist_type = (Class) glib_ns.scope.lookup ("List");
393 gslist_type = (Class) glib_ns.scope.lookup ("SList");
394 gnode_type = (Class) glib_ns.scope.lookup ("Node");
395 gvaluearray_type = (Class) glib_ns.scope.lookup ("ValueArray");
396 gstringbuilder_type = (TypeSymbol) glib_ns.scope.lookup ("StringBuilder");
397 garray_type = (TypeSymbol) glib_ns.scope.lookup ("Array");
398 gbytearray_type = (TypeSymbol) glib_ns.scope.lookup ("ByteArray");
399 gptrarray_type = (TypeSymbol) glib_ns.scope.lookup ("PtrArray");
400 gthreadpool_type = (TypeSymbol) glib_ns.scope.lookup ("ThreadPool");
402 gquark_type = new IntegerType ((Struct) glib_ns.scope.lookup ("Quark"));
403 gvalue_type = (Struct) glib_ns.scope.lookup ("Value");
404 gvariant_type = (Class) glib_ns.scope.lookup ("Variant");
405 mutex_type = (Struct) glib_ns.scope.lookup ("StaticRecMutex");
407 type_module_type = (TypeSymbol) glib_ns.scope.lookup ("TypeModule");
409 regex_type = new ObjectType ((Class) root_symbol.scope.lookup ("GLib").scope.lookup ("Regex"));
411 if (context.module_init_method != null) {
412 foreach (Parameter parameter in context.module_init_method.get_parameters ()) {
413 if (parameter.variable_type.data_type == type_module_type) {
414 in_plugin = true;
415 module_init_param_name = parameter.name;
416 break;
419 if (!in_plugin) {
420 Report.error (context.module_init_method.source_reference, "[ModuleInit] requires a parameter of type `GLib.TypeModule'");
424 dbus_proxy_type = (TypeSymbol) glib_ns.scope.lookup ("DBusProxy");
426 var dbus_ns = root_symbol.scope.lookup ("DBus");
427 if (dbus_ns != null) {
428 dbus_object_type = (TypeSymbol) dbus_ns.scope.lookup ("Object");
432 header_file = new CCodeFile ();
433 header_file.is_header = true;
434 internal_header_file = new CCodeFile ();
435 internal_header_file.is_header = true;
437 /* we're only interested in non-pkg source files */
438 var source_files = context.get_source_files ();
439 foreach (SourceFile file in source_files) {
440 if (file.file_type == SourceFileType.SOURCE ||
441 (context.header_filename != null && file.file_type == SourceFileType.FAST)) {
442 file.accept (this);
446 // generate symbols file for public API
447 if (context.symbols_filename != null) {
448 var stream = FileStream.open (context.symbols_filename, "w");
449 if (stream == null) {
450 Report.error (null, "unable to open `%s' for writing".printf (context.symbols_filename));
451 return;
454 foreach (string symbol in header_file.get_symbols ()) {
455 stream.puts (symbol);
456 stream.putc ('\n');
459 stream = null;
462 // generate C header file for public API
463 if (context.header_filename != null) {
464 bool ret;
465 if (context.profile == Profile.GOBJECT) {
466 ret = header_file.store (context.header_filename, null, context.version_header, false, "G_BEGIN_DECLS", "G_END_DECLS");
467 } else {
468 ret = header_file.store (context.header_filename, null, context.version_header, false);
470 if (!ret) {
471 Report.error (null, "unable to open `%s' for writing".printf (context.header_filename));
475 // generate C header file for internal API
476 if (context.internal_header_filename != null) {
477 bool ret;
478 if (context.profile == Profile.GOBJECT) {
479 ret = internal_header_file.store (context.internal_header_filename, null, context.version_header, false, "G_BEGIN_DECLS", "G_END_DECLS");
480 } else {
481 ret = internal_header_file.store (context.internal_header_filename, null, context.version_header, false);
483 if (!ret) {
484 Report.error (null, "unable to open `%s' for writing".printf (context.internal_header_filename));
489 public void push_context (EmitContext emit_context) {
490 if (this.emit_context != null) {
491 emit_context_stack.add (this.emit_context);
494 this.emit_context = emit_context;
497 public void pop_context () {
498 if (emit_context_stack.size > 0) {
499 this.emit_context = emit_context_stack[emit_context_stack.size - 1];
500 emit_context_stack.remove_at (emit_context_stack.size - 1);
501 } else {
502 this.emit_context = null;
506 public void push_function (CCodeFunction func) {
507 emit_context.ccode_stack.add (ccode);
508 emit_context.ccode = func;
511 public void pop_function () {
512 emit_context.ccode = emit_context.ccode_stack[emit_context.ccode_stack.size - 1];
513 emit_context.ccode_stack.remove_at (emit_context.ccode_stack.size - 1);
516 public bool add_symbol_declaration (CCodeFile decl_space, Symbol sym, string name) {
517 if (decl_space.add_declaration (name)) {
518 return true;
520 if (sym.source_reference != null) {
521 sym.source_reference.file.used = true;
523 if (sym.external_package || (!decl_space.is_header && CodeContext.get ().use_header && !sym.is_internal_symbol ())) {
524 // add appropriate include file
525 foreach (string header_filename in sym.get_cheader_filenames ()) {
526 decl_space.add_include (header_filename, !sym.external_package);
528 // declaration complete
529 return true;
530 } else {
531 // require declaration
532 return false;
536 public CCodeIdentifier get_value_setter_function (DataType type_reference) {
537 var array_type = type_reference as ArrayType;
538 if (type_reference.data_type != null) {
539 return new CCodeIdentifier (type_reference.data_type.get_set_value_function ());
540 } else if (array_type != null && array_type.element_type.data_type == string_type.data_type) {
541 // G_TYPE_STRV
542 return new CCodeIdentifier ("g_value_set_boxed");
543 } else {
544 return new CCodeIdentifier ("g_value_set_pointer");
548 public CCodeIdentifier get_value_taker_function (DataType type_reference) {
549 var array_type = type_reference as ArrayType;
550 if (type_reference.data_type != null) {
551 return new CCodeIdentifier (type_reference.data_type.get_take_value_function ());
552 } else if (array_type != null && array_type.element_type.data_type == string_type.data_type) {
553 // G_TYPE_STRV
554 return new CCodeIdentifier ("g_value_take_boxed");
555 } else {
556 return new CCodeIdentifier ("g_value_set_pointer");
560 CCodeIdentifier get_value_getter_function (DataType type_reference) {
561 var array_type = type_reference as ArrayType;
562 if (type_reference.data_type != null) {
563 return new CCodeIdentifier (type_reference.data_type.get_get_value_function ());
564 } else if (array_type != null && array_type.element_type.data_type == string_type.data_type) {
565 // G_TYPE_STRV
566 return new CCodeIdentifier ("g_value_get_boxed");
567 } else {
568 return new CCodeIdentifier ("g_value_get_pointer");
572 public virtual void append_vala_array_free () {
575 public virtual void append_vala_array_move () {
578 public virtual void append_vala_array_length () {
581 public override void visit_source_file (SourceFile source_file) {
582 cfile = new CCodeFile ();
584 user_marshal_set = new HashSet<string> (str_hash, str_equal);
586 next_regex_id = 0;
588 gvaluecollector_h_needed = false;
589 requires_array_free = false;
590 requires_array_move = false;
591 requires_array_length = false;
593 wrappers = new HashSet<string> (str_hash, str_equal);
594 generated_external_symbols = new HashSet<Symbol> ();
596 if (context.profile == Profile.GOBJECT) {
597 header_file.add_include ("glib.h");
598 internal_header_file.add_include ("glib.h");
599 cfile.add_include ("glib.h");
600 cfile.add_include ("glib-object.h");
603 source_file.accept_children (this);
605 if (context.report.get_errors () > 0) {
606 return;
609 /* For fast-vapi, we only wanted the header declarations
610 * to be emitted, so bail out here without writing the
611 * C code output.
613 if (source_file.file_type == SourceFileType.FAST) {
614 return;
617 if (requires_array_free) {
618 append_vala_array_free ();
620 if (requires_array_move) {
621 append_vala_array_move ();
623 if (requires_array_length) {
624 append_vala_array_length ();
627 if (gvaluecollector_h_needed) {
628 cfile.add_include ("gobject/gvaluecollector.h");
631 var comments = source_file.get_comments();
632 if (comments != null) {
633 foreach (Comment comment in comments) {
634 var ccomment = new CCodeComment (comment.content);
635 cfile.add_comment (ccomment);
639 if (!cfile.store (source_file.get_csource_filename (), source_file.filename, context.version_header, context.debug)) {
640 Report.error (null, "unable to open `%s' for writing".printf (source_file.get_csource_filename ()));
643 cfile = null;
646 public virtual bool generate_enum_declaration (Enum en, CCodeFile decl_space) {
647 if (add_symbol_declaration (decl_space, en, en.get_cname ())) {
648 return false;
651 var cenum = new CCodeEnum (en.get_cname ());
653 cenum.deprecated = en.deprecated;
655 int flag_shift = 0;
656 foreach (EnumValue ev in en.get_values ()) {
657 CCodeEnumValue c_ev;
658 if (ev.value == null) {
659 c_ev = new CCodeEnumValue (ev.get_cname ());
660 if (en.is_flags) {
661 c_ev.value = new CCodeConstant ("1 << %d".printf (flag_shift));
662 flag_shift += 1;
664 } else {
665 ev.value.emit (this);
666 c_ev = new CCodeEnumValue (ev.get_cname (), get_cvalue (ev.value));
668 c_ev.deprecated = ev.deprecated;
669 cenum.add_value (c_ev);
672 decl_space.add_type_definition (cenum);
673 decl_space.add_type_definition (new CCodeNewline ());
675 if (!en.has_type_id) {
676 return true;
679 decl_space.add_type_declaration (new CCodeNewline ());
681 var macro = "(%s_get_type ())".printf (en.get_lower_case_cname (null));
682 decl_space.add_type_declaration (new CCodeMacroReplacement (en.get_type_id (), macro));
684 var fun_name = "%s_get_type".printf (en.get_lower_case_cname (null));
685 var regfun = new CCodeFunction (fun_name, "GType");
686 regfun.attributes = "G_GNUC_CONST";
688 if (en.access == SymbolAccessibility.PRIVATE) {
689 regfun.modifiers = CCodeModifiers.STATIC;
690 // avoid C warning as this function is not always used
691 regfun.attributes = "G_GNUC_UNUSED";
694 decl_space.add_function_declaration (regfun);
696 return true;
699 public override void visit_enum (Enum en) {
700 en.accept_children (this);
702 if (en.comment != null) {
703 cfile.add_type_member_definition (new CCodeComment (en.comment.content));
706 generate_enum_declaration (en, cfile);
708 if (!en.is_internal_symbol ()) {
709 generate_enum_declaration (en, header_file);
711 if (!en.is_private_symbol ()) {
712 generate_enum_declaration (en, internal_header_file);
716 public void visit_member (Symbol m) {
717 /* stuff meant for all lockable members */
718 if (m is Lockable && ((Lockable) m).get_lock_used ()) {
719 CCodeExpression l = new CCodeIdentifier ("self");
720 var init_context = class_init_context;
721 var finalize_context = class_finalize_context;
723 if (m.is_instance_member ()) {
724 l = new CCodeMemberAccess.pointer (new CCodeMemberAccess.pointer (l, "priv"), get_symbol_lock_name (m.name));
725 init_context = instance_init_context;
726 finalize_context = instance_finalize_context;
727 } else if (m.is_class_member ()) {
728 TypeSymbol parent = (TypeSymbol)m.parent_symbol;
730 var get_class_private_call = new CCodeFunctionCall (new CCodeIdentifier ("%s_GET_CLASS_PRIVATE".printf(parent.get_upper_case_cname ())));
731 get_class_private_call.add_argument (new CCodeIdentifier ("klass"));
732 l = new CCodeMemberAccess.pointer (get_class_private_call, get_symbol_lock_name (m.name));
733 } else {
734 l = new CCodeIdentifier (get_symbol_lock_name ("%s_%s".printf(m.parent_symbol.get_lower_case_cname (), m.name)));
737 push_context (init_context);
738 var initf = new CCodeFunctionCall (new CCodeIdentifier (mutex_type.default_construction_method.get_cname ()));
739 initf.add_argument (new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, l));
740 ccode.add_expression (initf);
741 pop_context ();
743 if (finalize_context != null) {
744 push_context (finalize_context);
745 var fc = new CCodeFunctionCall (new CCodeIdentifier ("g_static_rec_mutex_free"));
746 fc.add_argument (new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, l));
747 ccode.add_expression (fc);
748 pop_context ();
753 public void generate_constant_declaration (Constant c, CCodeFile decl_space, bool definition = false) {
754 if (c.parent_symbol is Block) {
755 // local constant
756 return;
759 if (add_symbol_declaration (decl_space, c, c.get_cname ())) {
760 return;
763 if (!c.external) {
764 generate_type_declaration (c.type_reference, decl_space);
766 c.value.emit (this);
768 var initializer_list = c.value as InitializerList;
769 if (initializer_list != null) {
770 var cdecl = new CCodeDeclaration (c.type_reference.get_const_cname ());
771 var arr = "";
772 if (c.type_reference is ArrayType) {
773 arr = "[%d]".printf (initializer_list.size);
776 var cinitializer = get_cvalue (c.value);
777 if (!definition) {
778 // never output value in header
779 // special case needed as this method combines declaration and definition
780 cinitializer = null;
783 cdecl.add_declarator (new CCodeVariableDeclarator ("%s%s".printf (c.get_cname (), arr), cinitializer));
784 if (c.is_private_symbol ()) {
785 cdecl.modifiers = CCodeModifiers.STATIC;
786 } else {
787 cdecl.modifiers = CCodeModifiers.EXTERN;
790 decl_space.add_constant_declaration (cdecl);
791 } else {
792 var cdefine = new CCodeMacroReplacement.with_expression (c.get_cname (), get_cvalue (c.value));
793 decl_space.add_type_member_declaration (cdefine);
798 public override void visit_constant (Constant c) {
799 if (c.parent_symbol is Block) {
800 // local constant
802 generate_type_declaration (c.type_reference, cfile);
804 c.value.emit (this);
806 string type_name = c.type_reference.get_const_cname ();
807 string arr = "";
808 if (c.type_reference is ArrayType) {
809 arr = "[]";
812 if (c.type_reference.compatible (string_type)) {
813 type_name = "const char";
814 arr = "[]";
817 var cinitializer = get_cvalue (c.value);
819 ccode.add_declaration (type_name, new CCodeVariableDeclarator ("%s%s".printf (c.get_cname (), arr), cinitializer), CCodeModifiers.STATIC);
821 return;
824 generate_constant_declaration (c, cfile, true);
826 if (!c.is_internal_symbol ()) {
827 generate_constant_declaration (c, header_file);
829 if (!c.is_private_symbol ()) {
830 generate_constant_declaration (c, internal_header_file);
834 public void generate_field_declaration (Field f, CCodeFile decl_space) {
835 if (add_symbol_declaration (decl_space, f, f.get_cname ())) {
836 return;
839 generate_type_declaration (f.variable_type, decl_space);
841 string field_ctype = f.variable_type.get_cname ();
842 if (f.is_volatile) {
843 field_ctype = "volatile " + field_ctype;
846 var cdecl = new CCodeDeclaration (field_ctype);
847 cdecl.add_declarator (new CCodeVariableDeclarator (f.get_cname (), null, f.variable_type.get_cdeclarator_suffix ()));
848 if (f.is_private_symbol ()) {
849 cdecl.modifiers = CCodeModifiers.STATIC;
850 } else {
851 cdecl.modifiers = CCodeModifiers.EXTERN;
853 if (f.deprecated) {
854 cdecl.modifiers |= CCodeModifiers.DEPRECATED;
856 decl_space.add_type_member_declaration (cdecl);
858 if (f.get_lock_used ()) {
859 // Declare mutex for static member
860 var flock = new CCodeDeclaration (mutex_type.get_cname ());
861 var flock_decl = new CCodeVariableDeclarator (get_symbol_lock_name (f.get_cname ()), new CCodeConstant ("{0}"));
862 flock.add_declarator (flock_decl);
864 if (f.is_private_symbol ()) {
865 flock.modifiers = CCodeModifiers.STATIC;
866 } else {
867 flock.modifiers = CCodeModifiers.EXTERN;
869 decl_space.add_type_member_declaration (flock);
872 if (f.variable_type is ArrayType && !f.no_array_length) {
873 var array_type = (ArrayType) f.variable_type;
875 if (!array_type.fixed_length) {
876 for (int dim = 1; dim <= array_type.rank; dim++) {
877 var len_type = int_type.copy ();
879 cdecl = new CCodeDeclaration (len_type.get_cname ());
880 cdecl.add_declarator (new CCodeVariableDeclarator (get_array_length_cname (f.get_cname (), dim)));
881 if (f.is_private_symbol ()) {
882 cdecl.modifiers = CCodeModifiers.STATIC;
883 } else {
884 cdecl.modifiers = CCodeModifiers.EXTERN;
886 decl_space.add_type_member_declaration (cdecl);
889 } else if (f.variable_type is DelegateType) {
890 var delegate_type = (DelegateType) f.variable_type;
891 if (delegate_type.delegate_symbol.has_target) {
892 // create field to store delegate target
894 cdecl = new CCodeDeclaration ("gpointer");
895 cdecl.add_declarator (new CCodeVariableDeclarator (get_delegate_target_cname (f.get_cname ())));
896 if (f.is_private_symbol ()) {
897 cdecl.modifiers = CCodeModifiers.STATIC;
898 } else {
899 cdecl.modifiers = CCodeModifiers.EXTERN;
901 decl_space.add_type_member_declaration (cdecl);
903 if (delegate_type.value_owned) {
904 cdecl = new CCodeDeclaration ("GDestroyNotify");
905 cdecl.add_declarator (new CCodeVariableDeclarator (get_delegate_target_destroy_notify_cname (f.get_cname ())));
906 if (f.is_private_symbol ()) {
907 cdecl.modifiers = CCodeModifiers.STATIC;
908 } else {
909 cdecl.modifiers = CCodeModifiers.EXTERN;
911 decl_space.add_type_member_declaration (cdecl);
917 public override void visit_field (Field f) {
918 visit_member (f);
920 check_type (f.variable_type);
922 var cl = f.parent_symbol as Class;
923 bool is_gtypeinstance = (cl != null && !cl.is_compact);
925 CCodeExpression lhs = null;
927 string field_ctype = f.variable_type.get_cname ();
928 if (f.is_volatile) {
929 field_ctype = "volatile " + field_ctype;
932 if (f.binding == MemberBinding.INSTANCE) {
933 if (is_gtypeinstance && f.access == SymbolAccessibility.PRIVATE) {
934 lhs = new CCodeMemberAccess.pointer (new CCodeMemberAccess.pointer (new CCodeIdentifier ("self"), "priv"), f.get_cname ());
935 } else {
936 lhs = new CCodeMemberAccess.pointer (new CCodeIdentifier ("self"), f.get_cname ());
939 if (f.initializer != null) {
940 push_context (instance_init_context);
942 f.initializer.emit (this);
944 var rhs = get_cvalue (f.initializer);
946 ccode.add_expression (new CCodeAssignment (lhs, rhs));
948 if (f.variable_type is ArrayType && !f.no_array_length &&
949 f.initializer is ArrayCreationExpression) {
950 var array_type = (ArrayType) f.variable_type;
951 var this_access = new MemberAccess.simple ("this");
952 this_access.value_type = get_data_type_for_symbol ((TypeSymbol) f.parent_symbol);
953 set_cvalue (this_access, new CCodeIdentifier ("self"));
954 var ma = new MemberAccess (this_access, f.name);
955 ma.symbol_reference = f;
956 ma.value_type = f.variable_type.copy ();
957 visit_member_access (ma);
959 List<Expression> sizes = ((ArrayCreationExpression) f.initializer).get_sizes ();
960 for (int dim = 1; dim <= array_type.rank; dim++) {
961 var array_len_lhs = get_array_length_cexpression (ma, dim);
962 var size = sizes[dim - 1];
963 ccode.add_expression (new CCodeAssignment (array_len_lhs, get_cvalue (size)));
966 if (array_type.rank == 1 && f.is_internal_symbol ()) {
967 var lhs_array_size = get_array_size_cvalue (ma.target_value);
968 var rhs_array_len = get_array_length_cexpression (ma, 1);
969 ccode.add_expression (new CCodeAssignment (lhs_array_size, rhs_array_len));
973 foreach (LocalVariable local in temp_ref_vars) {
974 var ma = new MemberAccess.simple (local.name);
975 ma.symbol_reference = local;
976 ma.value_type = local.variable_type.copy ();
977 visit_member_access (ma);
978 ccode.add_expression (get_unref_expression (get_variable_cexpression (local.name), local.variable_type, ma));
981 temp_ref_vars.clear ();
983 pop_context ();
986 if (requires_destroy (f.variable_type) && instance_finalize_context != null) {
987 push_context (instance_finalize_context);
989 var this_access = new MemberAccess.simple ("this");
990 this_access.value_type = get_data_type_for_symbol ((TypeSymbol) f.parent_symbol);
992 var field_st = f.parent_symbol as Struct;
993 if (field_st != null && !field_st.is_simple_type ()) {
994 set_cvalue (this_access, new CCodeIdentifier ("(*self)"));
995 } else {
996 set_cvalue (this_access, new CCodeIdentifier ("self"));
999 var ma = new MemberAccess (this_access, f.name);
1000 ma.symbol_reference = f;
1001 ma.value_type = f.variable_type.copy ();
1002 visit_member_access (ma);
1003 ccode.add_expression (get_unref_expression (lhs, f.variable_type, ma));
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_expression (new CCodeAssignment (lhs, rhs));
1031 foreach (LocalVariable local in temp_ref_vars) {
1032 var ma = new MemberAccess.simple (local.name);
1033 ma.symbol_reference = local;
1034 ma.value_type = local.variable_type.copy ();
1035 visit_member_access (ma);
1036 ccode.add_expression (get_unref_expression (get_variable_cexpression (local.name), local.variable_type, ma));
1039 temp_ref_vars.clear ();
1041 pop_context ();
1043 } else {
1044 generate_field_declaration (f, cfile);
1046 if (!f.is_internal_symbol ()) {
1047 generate_field_declaration (f, header_file);
1049 if (!f.is_private_symbol ()) {
1050 generate_field_declaration (f, internal_header_file);
1053 lhs = new CCodeIdentifier (f.get_cname ());
1055 var var_decl = new CCodeVariableDeclarator (f.get_cname (), null, f.variable_type.get_cdeclarator_suffix ());
1056 var_decl.initializer = default_value_for_type (f.variable_type, true);
1058 if (class_init_context != null) {
1059 push_context (class_init_context);
1060 } else {
1061 push_context (new EmitContext ());
1064 if (f.initializer != null) {
1065 f.initializer.emit (this);
1067 var init = get_cvalue (f.initializer);
1068 if (is_constant_ccode_expression (init)) {
1069 var_decl.initializer = init;
1073 var var_def = new CCodeDeclaration (field_ctype);
1074 var_def.add_declarator (var_decl);
1075 if (!f.is_private_symbol ()) {
1076 var_def.modifiers = CCodeModifiers.EXTERN;
1077 } else {
1078 var_def.modifiers = CCodeModifiers.STATIC;
1080 cfile.add_type_member_declaration (var_def);
1082 /* add array length fields where necessary */
1083 if (f.variable_type is ArrayType && !f.no_array_length) {
1084 var array_type = (ArrayType) f.variable_type;
1086 if (!array_type.fixed_length) {
1087 for (int dim = 1; dim <= array_type.rank; dim++) {
1088 var len_type = int_type.copy ();
1090 var len_def = new CCodeDeclaration (len_type.get_cname ());
1091 len_def.add_declarator (new CCodeVariableDeclarator (get_array_length_cname (f.get_cname (), dim), new CCodeConstant ("0")));
1092 if (!f.is_private_symbol ()) {
1093 len_def.modifiers = CCodeModifiers.EXTERN;
1094 } else {
1095 len_def.modifiers = CCodeModifiers.STATIC;
1097 cfile.add_type_member_declaration (len_def);
1100 if (array_type.rank == 1 && f.is_internal_symbol ()) {
1101 var len_type = int_type.copy ();
1103 var cdecl = new CCodeDeclaration (len_type.get_cname ());
1104 cdecl.add_declarator (new CCodeVariableDeclarator (get_array_size_cname (f.get_cname ()), new CCodeConstant ("0")));
1105 cdecl.modifiers = CCodeModifiers.STATIC;
1106 cfile.add_type_member_declaration (cdecl);
1109 } else if (f.variable_type is DelegateType) {
1110 var delegate_type = (DelegateType) f.variable_type;
1111 if (delegate_type.delegate_symbol.has_target) {
1112 // create field to store delegate target
1114 var target_def = new CCodeDeclaration ("gpointer");
1115 target_def.add_declarator (new CCodeVariableDeclarator (get_delegate_target_cname (f.get_cname ()), new CCodeConstant ("NULL")));
1116 if (!f.is_private_symbol ()) {
1117 target_def.modifiers = CCodeModifiers.EXTERN;
1118 } else {
1119 target_def.modifiers = CCodeModifiers.STATIC;
1121 cfile.add_type_member_declaration (target_def);
1123 if (delegate_type.value_owned) {
1124 var target_destroy_notify_def = new CCodeDeclaration ("GDestroyNotify");
1125 target_destroy_notify_def.add_declarator (new CCodeVariableDeclarator (get_delegate_target_destroy_notify_cname (f.get_cname ()), new CCodeConstant ("NULL")));
1126 if (!f.is_private_symbol ()) {
1127 target_destroy_notify_def.modifiers = CCodeModifiers.EXTERN;
1128 } else {
1129 target_destroy_notify_def.modifiers = CCodeModifiers.STATIC;
1131 cfile.add_type_member_declaration (target_destroy_notify_def);
1137 if (f.initializer != null) {
1138 var rhs = get_cvalue (f.initializer);
1139 if (!is_constant_ccode_expression (rhs)) {
1140 if (f.parent_symbol is Class) {
1141 if (f.initializer is InitializerList) {
1142 ccode.open_block ();
1144 var temp_decl = get_temp_variable (f.variable_type);
1145 var vardecl = new CCodeVariableDeclarator.zero (temp_decl.name, rhs);
1146 ccode.add_declaration (temp_decl.variable_type.get_cname (), vardecl);
1148 var tmp = get_variable_cexpression (get_variable_cname (temp_decl.name));
1149 ccode.add_expression (new CCodeAssignment (lhs, tmp));
1151 ccode.close ();
1152 } else {
1153 ccode.add_expression (new CCodeAssignment (lhs, rhs));
1156 if (f.variable_type is ArrayType && !f.no_array_length &&
1157 f.initializer is ArrayCreationExpression) {
1158 var array_type = (ArrayType) f.variable_type;
1159 var ma = new MemberAccess.simple (f.name);
1160 ma.symbol_reference = f;
1161 ma.value_type = f.variable_type.copy ();
1162 visit_member_access (ma);
1164 List<Expression> sizes = ((ArrayCreationExpression) f.initializer).get_sizes ();
1165 for (int dim = 1; dim <= array_type.rank; dim++) {
1166 var array_len_lhs = get_array_length_cexpression (ma, dim);
1167 var size = sizes[dim - 1];
1168 ccode.add_expression (new CCodeAssignment (array_len_lhs, get_cvalue (size)));
1171 } else {
1172 f.error = true;
1173 Report.error (f.source_reference, "Non-constant field initializers not supported in this context");
1174 return;
1179 pop_context ();
1183 public bool is_constant_ccode_expression (CCodeExpression cexpr) {
1184 if (cexpr is CCodeConstant) {
1185 return true;
1186 } else if (cexpr is CCodeCastExpression) {
1187 var ccast = (CCodeCastExpression) cexpr;
1188 return is_constant_ccode_expression (ccast.inner);
1189 } else if (cexpr is CCodeBinaryExpression) {
1190 var cbinary = (CCodeBinaryExpression) cexpr;
1191 return is_constant_ccode_expression (cbinary.left) && is_constant_ccode_expression (cbinary.right);
1194 var cparenthesized = (cexpr as CCodeParenthesizedExpression);
1195 return (null != cparenthesized && is_constant_ccode_expression (cparenthesized.inner));
1199 * Returns whether the passed cexpr is a pure expression, i.e. an
1200 * expression without side-effects.
1202 public bool is_pure_ccode_expression (CCodeExpression cexpr) {
1203 if (cexpr is CCodeConstant || cexpr is CCodeIdentifier) {
1204 return true;
1205 } else if (cexpr is CCodeBinaryExpression) {
1206 var cbinary = (CCodeBinaryExpression) cexpr;
1207 return is_pure_ccode_expression (cbinary.left) && is_constant_ccode_expression (cbinary.right);
1208 } else if (cexpr is CCodeUnaryExpression) {
1209 var cunary = (CCodeUnaryExpression) cexpr;
1210 switch (cunary.operator) {
1211 case CCodeUnaryOperator.PREFIX_INCREMENT:
1212 case CCodeUnaryOperator.PREFIX_DECREMENT:
1213 case CCodeUnaryOperator.POSTFIX_INCREMENT:
1214 case CCodeUnaryOperator.POSTFIX_DECREMENT:
1215 return false;
1216 default:
1217 return is_pure_ccode_expression (cunary.inner);
1219 } else if (cexpr is CCodeMemberAccess) {
1220 var cma = (CCodeMemberAccess) cexpr;
1221 return is_pure_ccode_expression (cma.inner);
1222 } else if (cexpr is CCodeElementAccess) {
1223 var cea = (CCodeElementAccess) cexpr;
1224 return is_pure_ccode_expression (cea.container) && is_pure_ccode_expression (cea.index);
1225 } else if (cexpr is CCodeCastExpression) {
1226 var ccast = (CCodeCastExpression) cexpr;
1227 return is_pure_ccode_expression (ccast.inner);
1228 } else if (cexpr is CCodeParenthesizedExpression) {
1229 var cparenthesized = (CCodeParenthesizedExpression) cexpr;
1230 return is_pure_ccode_expression (cparenthesized.inner);
1233 return false;
1236 public override void visit_formal_parameter (Parameter p) {
1237 if (!p.ellipsis) {
1238 check_type (p.variable_type);
1242 public override void visit_property (Property prop) {
1243 visit_member (prop);
1245 check_type (prop.property_type);
1247 if (prop.get_accessor != null) {
1248 prop.get_accessor.accept (this);
1250 if (prop.set_accessor != null) {
1251 prop.set_accessor.accept (this);
1255 public void generate_type_declaration (DataType type, CCodeFile decl_space) {
1256 if (type is ObjectType) {
1257 var object_type = (ObjectType) type;
1258 if (object_type.type_symbol is Class) {
1259 generate_class_declaration ((Class) object_type.type_symbol, decl_space);
1260 } else if (object_type.type_symbol is Interface) {
1261 generate_interface_declaration ((Interface) object_type.type_symbol, decl_space);
1263 } else if (type is DelegateType) {
1264 var deleg_type = (DelegateType) type;
1265 var d = deleg_type.delegate_symbol;
1266 generate_delegate_declaration (d, decl_space);
1267 } else if (type.data_type is Enum) {
1268 var en = (Enum) type.data_type;
1269 generate_enum_declaration (en, decl_space);
1270 } else if (type is ValueType) {
1271 var value_type = (ValueType) type;
1272 generate_struct_declaration ((Struct) value_type.type_symbol, decl_space);
1273 } else if (type is ArrayType) {
1274 var array_type = (ArrayType) type;
1275 generate_type_declaration (array_type.element_type, decl_space);
1276 } else if (type is ErrorType) {
1277 var error_type = (ErrorType) type;
1278 if (error_type.error_domain != null) {
1279 generate_error_domain_declaration (error_type.error_domain, decl_space);
1281 } else if (type is PointerType) {
1282 var pointer_type = (PointerType) type;
1283 generate_type_declaration (pointer_type.base_type, decl_space);
1286 foreach (DataType type_arg in type.get_type_arguments ()) {
1287 generate_type_declaration (type_arg, decl_space);
1291 public virtual void generate_class_struct_declaration (Class cl, CCodeFile decl_space) {
1294 public virtual void generate_struct_declaration (Struct st, CCodeFile decl_space) {
1297 public virtual void generate_delegate_declaration (Delegate d, CCodeFile decl_space) {
1300 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) {
1303 public void generate_property_accessor_declaration (PropertyAccessor acc, CCodeFile decl_space) {
1304 if (add_symbol_declaration (decl_space, acc, acc.get_cname ())) {
1305 return;
1308 var prop = (Property) acc.prop;
1310 bool returns_real_struct = acc.readable && prop.property_type.is_real_non_null_struct_type ();
1313 CCodeParameter cvalueparam;
1314 if (returns_real_struct) {
1315 cvalueparam = new CCodeParameter ("result", acc.value_type.get_cname () + "*");
1316 } else if (!acc.readable && prop.property_type.is_real_non_null_struct_type ()) {
1317 cvalueparam = new CCodeParameter ("value", acc.value_type.get_cname () + "*");
1318 } else {
1319 cvalueparam = new CCodeParameter ("value", acc.value_type.get_cname ());
1321 generate_type_declaration (acc.value_type, decl_space);
1323 CCodeFunction function;
1324 if (acc.readable && !returns_real_struct) {
1325 function = new CCodeFunction (acc.get_cname (), acc.value_type.get_cname ());
1326 } else {
1327 function = new CCodeFunction (acc.get_cname (), "void");
1330 if (prop.binding == MemberBinding.INSTANCE) {
1331 var t = (TypeSymbol) prop.parent_symbol;
1332 var this_type = get_data_type_for_symbol (t);
1333 generate_type_declaration (this_type, decl_space);
1334 var cselfparam = new CCodeParameter ("self", this_type.get_cname ());
1335 if (t is Struct) {
1336 cselfparam.type_name += "*";
1339 function.add_parameter (cselfparam);
1342 if (acc.writable || acc.construction || returns_real_struct) {
1343 function.add_parameter (cvalueparam);
1346 if (acc.value_type is ArrayType) {
1347 var array_type = (ArrayType) acc.value_type;
1349 var length_ctype = "int";
1350 if (acc.readable) {
1351 length_ctype = "int*";
1354 for (int dim = 1; dim <= array_type.rank; dim++) {
1355 function.add_parameter (new CCodeParameter (get_array_length_cname (acc.readable ? "result" : "value", dim), length_ctype));
1357 } else if ((acc.value_type is DelegateType) && ((DelegateType) acc.value_type).delegate_symbol.has_target) {
1358 function.add_parameter (new CCodeParameter (get_delegate_target_cname (acc.readable ? "result" : "value"), acc.readable ? "gpointer*" : "gpointer"));
1361 if (prop.is_private_symbol () || (!acc.readable && !acc.writable) || acc.access == SymbolAccessibility.PRIVATE) {
1362 function.modifiers |= CCodeModifiers.STATIC;
1364 decl_space.add_function_declaration (function);
1367 public override void visit_property_accessor (PropertyAccessor acc) {
1368 push_context (new EmitContext (acc));
1370 var prop = (Property) acc.prop;
1372 if (acc.comment != null) {
1373 cfile.add_type_member_definition (new CCodeComment (acc.comment.content));
1376 bool returns_real_struct = acc.readable && prop.property_type.is_real_non_null_struct_type ();
1378 if (acc.result_var != null) {
1379 acc.result_var.accept (this);
1382 var t = (TypeSymbol) prop.parent_symbol;
1384 if (acc.construction && !t.is_subtype_of (gobject_type)) {
1385 Report.error (acc.source_reference, "construct properties require GLib.Object");
1386 acc.error = true;
1387 return;
1388 } else if (acc.construction && !is_gobject_property (prop)) {
1389 Report.error (acc.source_reference, "construct properties not supported for specified property type");
1390 acc.error = true;
1391 return;
1394 // do not declare overriding properties and interface implementations
1395 if (prop.is_abstract || prop.is_virtual
1396 || (prop.base_property == null && prop.base_interface_property == null)) {
1397 generate_property_accessor_declaration (acc, cfile);
1399 // do not declare construct-only properties in header files
1400 if (acc.readable || acc.writable) {
1401 if (!prop.is_internal_symbol ()
1402 && (acc.access == SymbolAccessibility.PUBLIC
1403 || acc.access == SymbolAccessibility.PROTECTED)) {
1404 generate_property_accessor_declaration (acc, header_file);
1406 if (!prop.is_private_symbol () && acc.access != SymbolAccessibility.PRIVATE) {
1407 generate_property_accessor_declaration (acc, internal_header_file);
1412 if (acc.source_type == SourceFileType.FAST) {
1413 return;
1416 var this_type = get_data_type_for_symbol (t);
1417 var cselfparam = new CCodeParameter ("self", this_type.get_cname ());
1418 if (t is Struct) {
1419 cselfparam.type_name += "*";
1421 CCodeParameter cvalueparam;
1422 if (returns_real_struct) {
1423 cvalueparam = new CCodeParameter ("result", acc.value_type.get_cname () + "*");
1424 } else if (!acc.readable && prop.property_type.is_real_non_null_struct_type ()) {
1425 cvalueparam = new CCodeParameter ("value", acc.value_type.get_cname () + "*");
1426 } else {
1427 cvalueparam = new CCodeParameter ("value", acc.value_type.get_cname ());
1430 if (prop.is_abstract || prop.is_virtual) {
1431 CCodeFunction function;
1432 if (acc.readable && !returns_real_struct) {
1433 function = new CCodeFunction (acc.get_cname (), current_return_type.get_cname ());
1434 } else {
1435 function = new CCodeFunction (acc.get_cname (), "void");
1437 function.add_parameter (cselfparam);
1438 if (acc.writable || acc.construction || returns_real_struct) {
1439 function.add_parameter (cvalueparam);
1442 if (acc.value_type is ArrayType) {
1443 var array_type = (ArrayType) acc.value_type;
1445 var length_ctype = "int";
1446 if (acc.readable) {
1447 length_ctype = "int*";
1450 for (int dim = 1; dim <= array_type.rank; dim++) {
1451 function.add_parameter (new CCodeParameter (get_array_length_cname (acc.readable ? "result" : "value", dim), length_ctype));
1453 } else if ((acc.value_type is DelegateType) && ((DelegateType) acc.value_type).delegate_symbol.has_target) {
1454 function.add_parameter (new CCodeParameter (get_delegate_target_cname (acc.readable ? "result" : "value"), acc.readable ? "gpointer*" : "gpointer"));
1457 if (prop.is_private_symbol () || !(acc.readable || acc.writable) || acc.access == SymbolAccessibility.PRIVATE) {
1458 // accessor function should be private if the property is an internal symbol or it's a construct-only setter
1459 function.modifiers |= CCodeModifiers.STATIC;
1462 push_function (function);
1464 CCodeFunctionCall vcast = null;
1465 if (prop.parent_symbol is Interface) {
1466 var iface = (Interface) prop.parent_symbol;
1468 vcast = new CCodeFunctionCall (new CCodeIdentifier ("%s_GET_INTERFACE".printf (iface.get_upper_case_cname (null))));
1469 } else {
1470 var cl = (Class) prop.parent_symbol;
1472 vcast = new CCodeFunctionCall (new CCodeIdentifier ("%s_GET_CLASS".printf (cl.get_upper_case_cname (null))));
1474 vcast.add_argument (new CCodeIdentifier ("self"));
1476 if (acc.readable) {
1477 var vcall = new CCodeFunctionCall (new CCodeMemberAccess.pointer (vcast, "get_%s".printf (prop.name)));
1478 vcall.add_argument (new CCodeIdentifier ("self"));
1479 if (returns_real_struct) {
1480 vcall.add_argument (new CCodeIdentifier ("result"));
1481 ccode.add_expression (vcall);
1482 } else {
1483 if (acc.value_type is ArrayType) {
1484 var array_type = (ArrayType) acc.value_type;
1486 for (int dim = 1; dim <= array_type.rank; dim++) {
1487 var len_expr = new CCodeIdentifier (get_array_length_cname ("result", dim));
1488 vcall.add_argument (len_expr);
1490 } else if ((acc.value_type is DelegateType) && ((DelegateType) acc.value_type).delegate_symbol.has_target) {
1491 vcall.add_argument (new CCodeIdentifier (get_delegate_target_cname ("result")));
1494 ccode.add_return (vcall);
1496 } else {
1497 var vcall = new CCodeFunctionCall (new CCodeMemberAccess.pointer (vcast, "set_%s".printf (prop.name)));
1498 vcall.add_argument (new CCodeIdentifier ("self"));
1499 vcall.add_argument (new CCodeIdentifier ("value"));
1501 if (acc.value_type is ArrayType) {
1502 var array_type = (ArrayType) acc.value_type;
1504 for (int dim = 1; dim <= array_type.rank; dim++) {
1505 var len_expr = new CCodeIdentifier (get_array_length_cname ("value", dim));
1506 vcall.add_argument (len_expr);
1508 } else if ((acc.value_type is DelegateType) && ((DelegateType) acc.value_type).delegate_symbol.has_target) {
1509 vcall.add_argument (new CCodeIdentifier (get_delegate_target_cname ("value")));
1512 ccode.add_expression (vcall);
1515 pop_function ();
1517 cfile.add_function (function);
1520 if (!prop.is_abstract) {
1521 bool is_virtual = prop.base_property != null || prop.base_interface_property != null;
1523 string cname;
1524 if (is_virtual) {
1525 if (acc.readable) {
1526 cname = "%s_real_get_%s".printf (t.get_lower_case_cname (null), prop.name);
1527 } else {
1528 cname = "%s_real_set_%s".printf (t.get_lower_case_cname (null), prop.name);
1530 } else {
1531 cname = acc.get_cname ();
1534 CCodeFunction function;
1535 if (acc.writable || acc.construction || returns_real_struct) {
1536 function = new CCodeFunction (cname, "void");
1537 } else {
1538 function = new CCodeFunction (cname, acc.value_type.get_cname ());
1541 ObjectType base_type = null;
1542 if (prop.binding == MemberBinding.INSTANCE) {
1543 if (is_virtual) {
1544 if (prop.base_property != null) {
1545 base_type = new ObjectType ((ObjectTypeSymbol) prop.base_property.parent_symbol);
1546 } else if (prop.base_interface_property != null) {
1547 base_type = new ObjectType ((ObjectTypeSymbol) prop.base_interface_property.parent_symbol);
1549 function.modifiers |= CCodeModifiers.STATIC;
1550 function.add_parameter (new CCodeParameter ("base", base_type.get_cname ()));
1551 } else {
1552 function.add_parameter (cselfparam);
1555 if (acc.writable || acc.construction || returns_real_struct) {
1556 function.add_parameter (cvalueparam);
1559 if (acc.value_type is ArrayType) {
1560 var array_type = (ArrayType) acc.value_type;
1562 var length_ctype = "int";
1563 if (acc.readable) {
1564 length_ctype = "int*";
1567 for (int dim = 1; dim <= array_type.rank; dim++) {
1568 function.add_parameter (new CCodeParameter (get_array_length_cname (acc.readable ? "result" : "value", dim), length_ctype));
1570 } else if ((acc.value_type is DelegateType) && ((DelegateType) acc.value_type).delegate_symbol.has_target) {
1571 function.add_parameter (new CCodeParameter (get_delegate_target_cname (acc.readable ? "result" : "value"), acc.readable ? "gpointer*" : "gpointer"));
1574 if (!is_virtual) {
1575 if (prop.is_private_symbol () || !(acc.readable || acc.writable) || acc.access == SymbolAccessibility.PRIVATE) {
1576 // accessor function should be private if the property is an internal symbol or it's a construct-only setter
1577 function.modifiers |= CCodeModifiers.STATIC;
1581 push_function (function);
1583 if (prop.binding == MemberBinding.INSTANCE && !is_virtual) {
1584 if (!acc.readable || returns_real_struct) {
1585 create_property_type_check_statement (prop, false, t, true, "self");
1586 } else {
1587 create_property_type_check_statement (prop, true, t, true, "self");
1591 if (acc.readable && !returns_real_struct) {
1592 // do not declare result variable if exit block is known to be unreachable
1593 if (acc.return_block == null || acc.return_block.get_predecessors ().size > 0) {
1594 ccode.add_declaration (acc.value_type.get_cname (), new CCodeVariableDeclarator ("result"));
1598 if (is_virtual) {
1599 ccode.add_declaration (this_type.get_cname (), new CCodeVariableDeclarator ("self"));
1600 ccode.add_expression (new CCodeAssignment (new CCodeIdentifier ("self"), transform_expression (new CCodeIdentifier ("base"), base_type, this_type)));
1603 acc.body.emit (this);
1605 if (current_method_inner_error) {
1606 ccode.add_declaration ("GError *", new CCodeVariableDeclarator.zero ("_inner_error_", new CCodeConstant ("NULL")));
1609 // notify on property changes
1610 if (is_gobject_property (prop) &&
1611 prop.notify &&
1612 (acc.writable || acc.construction)) {
1613 var notify_call = new CCodeFunctionCall (new CCodeIdentifier ("g_object_notify"));
1614 notify_call.add_argument (new CCodeCastExpression (new CCodeIdentifier ("self"), "GObject *"));
1615 notify_call.add_argument (prop.get_canonical_cconstant ());
1616 ccode.add_expression (notify_call);
1619 cfile.add_function (function);
1622 pop_context ();
1625 public override void visit_destructor (Destructor d) {
1626 if (d.binding == MemberBinding.STATIC && !in_plugin) {
1627 Report.error (d.source_reference, "static destructors are only supported for dynamic types");
1628 d.error = true;
1629 return;
1633 public int get_block_id (Block b) {
1634 int result = block_map[b];
1635 if (result == 0) {
1636 result = ++next_block_id;
1637 block_map[b] = result;
1639 return result;
1642 void capture_parameter (Parameter param, CCodeStruct data, int block_id, CCodeBlock free_block) {
1643 generate_type_declaration (param.variable_type, cfile);
1645 var param_type = param.variable_type.copy ();
1646 param_type.value_owned = true;
1647 data.add_field (param_type.get_cname (), get_variable_cname (param.name));
1649 bool is_unowned_delegate = param.variable_type is DelegateType && !param.variable_type.value_owned;
1651 // create copy if necessary as captured variables may need to be kept alive
1652 CCodeExpression cparam = get_variable_cexpression (param.name);
1653 if (param.variable_type.is_real_non_null_struct_type ()) {
1654 cparam = new CCodeUnaryExpression (CCodeUnaryOperator.POINTER_INDIRECTION, cparam);
1656 if (requires_copy (param_type) && !param.variable_type.value_owned && !is_unowned_delegate) {
1657 var ma = new MemberAccess.simple (param.name);
1658 ma.symbol_reference = param;
1659 ma.value_type = param.variable_type.copy ();
1660 visit_member_access (ma);
1661 // directly access parameters in ref expressions
1662 param.captured = false;
1663 cparam = get_ref_cexpression (param.variable_type, cparam, ma, param);
1664 param.captured = true;
1667 ccode.add_expression (new CCodeAssignment (new CCodeMemberAccess.pointer (get_variable_cexpression ("_data%d_".printf (block_id)), get_variable_cname (param.name)), cparam));
1669 if (param.variable_type is ArrayType) {
1670 var array_type = (ArrayType) param.variable_type;
1671 for (int dim = 1; dim <= array_type.rank; dim++) {
1672 data.add_field ("gint", get_parameter_array_length_cname (param, dim));
1673 ccode.add_expression (new CCodeAssignment (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))));
1675 } else if (param.variable_type is DelegateType) {
1676 data.add_field ("gpointer", get_delegate_target_cname (get_variable_cname (param.name)));
1677 ccode.add_expression (new CCodeAssignment (new CCodeMemberAccess.pointer (get_variable_cexpression ("_data%d_".printf (block_id)), get_delegate_target_cname (get_variable_cname (param.name))), new CCodeIdentifier (get_delegate_target_cname (get_variable_cname (param.name)))));
1678 if (param.variable_type.value_owned) {
1679 data.add_field ("GDestroyNotify", get_delegate_target_destroy_notify_cname (get_variable_cname (param.name)));
1680 ccode.add_expression (new CCodeAssignment (new CCodeMemberAccess.pointer (get_variable_cexpression ("_data%d_".printf (block_id)), get_delegate_target_destroy_notify_cname (get_variable_cname (param.name))), new CCodeIdentifier (get_delegate_target_destroy_notify_cname (get_variable_cname (param.name)))));
1684 if (requires_destroy (param_type) && !is_unowned_delegate) {
1685 bool old_coroutine = false;
1686 if (current_method != null) {
1687 old_coroutine = current_method.coroutine;
1688 current_method.coroutine = false;
1691 var ma = new MemberAccess.simple (param.name);
1692 ma.symbol_reference = param;
1693 ma.value_type = param_type.copy ();
1694 visit_member_access (ma);
1695 free_block.add_statement (new CCodeExpressionStatement (get_unref_expression (new CCodeMemberAccess.pointer (new CCodeIdentifier ("_data%d_".printf (block_id)), get_variable_cname (param.name)), param.variable_type, ma)));
1697 if (old_coroutine) {
1698 current_method.coroutine = true;
1703 public override void visit_block (Block b) {
1704 emit_context.push_symbol (b);
1706 var local_vars = b.get_local_variables ();
1708 if (b.parent_node is Block || b.parent_node is SwitchStatement) {
1709 ccode.open_block ();
1712 if (b.captured) {
1713 var parent_block = next_closure_block (b.parent_symbol);
1715 int block_id = get_block_id (b);
1716 string struct_name = "Block%dData".printf (block_id);
1718 var free_block = new CCodeBlock ();
1720 var data = new CCodeStruct ("_" + struct_name);
1721 data.add_field ("int", "_ref_count_");
1722 if (parent_block != null) {
1723 int parent_block_id = get_block_id (parent_block);
1725 data.add_field ("Block%dData *".printf (parent_block_id), "_data%d_".printf (parent_block_id));
1727 var unref_call = new CCodeFunctionCall (new CCodeIdentifier ("block%d_data_unref".printf (parent_block_id)));
1728 unref_call.add_argument (new CCodeMemberAccess.pointer (new CCodeIdentifier ("_data%d_".printf (block_id)), "_data%d_".printf (parent_block_id)));
1729 free_block.add_statement (new CCodeExpressionStatement (unref_call));
1730 } else {
1731 if (in_constructor || (current_method != null && current_method.binding == MemberBinding.INSTANCE) ||
1732 (current_property_accessor != null && current_property_accessor.prop.binding == MemberBinding.INSTANCE)) {
1733 data.add_field ("%s *".printf (current_class.get_cname ()), "self");
1735 var ma = new MemberAccess.simple ("this");
1736 ma.symbol_reference = current_class;
1737 free_block.add_statement (new CCodeExpressionStatement (get_unref_expression (new CCodeMemberAccess.pointer (new CCodeIdentifier ("_data%d_".printf (block_id)), "self"), new ObjectType (current_class), ma)));
1740 if (current_method != null) {
1741 // allow capturing generic type parameters
1742 foreach (var type_param in current_method.get_type_parameters ()) {
1743 string func_name;
1745 func_name = "%s_type".printf (type_param.name.down ());
1746 data.add_field ("GType", func_name);
1748 func_name = "%s_dup_func".printf (type_param.name.down ());
1749 data.add_field ("GBoxedCopyFunc", func_name);
1751 func_name = "%s_destroy_func".printf (type_param.name.down ());
1752 data.add_field ("GDestroyNotify", func_name);
1756 foreach (var local in local_vars) {
1757 if (local.captured) {
1758 generate_type_declaration (local.variable_type, cfile);
1760 data.add_field (local.variable_type.get_cname (), get_variable_cname (local.name) + local.variable_type.get_cdeclarator_suffix ());
1762 if (local.variable_type is ArrayType) {
1763 var array_type = (ArrayType) local.variable_type;
1764 for (int dim = 1; dim <= array_type.rank; dim++) {
1765 data.add_field ("gint", get_array_length_cname (get_variable_cname (local.name), dim));
1767 data.add_field ("gint", get_array_size_cname (get_variable_cname (local.name)));
1768 } else if (local.variable_type is DelegateType) {
1769 data.add_field ("gpointer", get_delegate_target_cname (get_variable_cname (local.name)));
1770 if (local.variable_type.value_owned) {
1771 data.add_field ("GDestroyNotify", get_delegate_target_destroy_notify_cname (get_variable_cname (local.name)));
1776 // free in reverse order
1777 for (int i = local_vars.size - 1; i >= 0; i--) {
1778 var local = local_vars[i];
1779 if (local.captured) {
1780 if (requires_destroy (local.variable_type)) {
1781 bool old_coroutine = false;
1782 if (current_method != null) {
1783 old_coroutine = current_method.coroutine;
1784 current_method.coroutine = false;
1787 var ma = new MemberAccess.simple (local.name);
1788 ma.symbol_reference = local;
1789 ma.value_type = local.variable_type.copy ();
1790 visit_member_access (ma);
1791 free_block.add_statement (new CCodeExpressionStatement (get_unref_expression (new CCodeMemberAccess.pointer (new CCodeIdentifier ("_data%d_".printf (block_id)), get_variable_cname (local.name)), local.variable_type, ma)));
1793 if (old_coroutine) {
1794 current_method.coroutine = true;
1800 var data_alloc = new CCodeFunctionCall (new CCodeIdentifier ("g_slice_new0"));
1801 data_alloc.add_argument (new CCodeIdentifier (struct_name));
1803 if (current_method != null && current_method.coroutine) {
1804 closure_struct.add_field (struct_name + "*", "_data%d_".printf (block_id));
1805 } else {
1806 ccode.add_declaration (struct_name + "*", new CCodeVariableDeclarator ("_data%d_".printf (block_id)));
1808 ccode.add_expression (new CCodeAssignment (get_variable_cexpression ("_data%d_".printf (block_id)), data_alloc));
1810 // initialize ref_count
1811 ccode.add_expression (new CCodeAssignment (new CCodeMemberAccess.pointer (get_variable_cexpression ("_data%d_".printf (block_id)), "_ref_count_"), new CCodeIdentifier ("1")));
1813 if (parent_block != null) {
1814 int parent_block_id = get_block_id (parent_block);
1816 var ref_call = new CCodeFunctionCall (new CCodeIdentifier ("block%d_data_ref".printf (parent_block_id)));
1817 ref_call.add_argument (get_variable_cexpression ("_data%d_".printf (parent_block_id)));
1819 ccode.add_expression (new CCodeAssignment (new CCodeMemberAccess.pointer (get_variable_cexpression ("_data%d_".printf (block_id)), "_data%d_".printf (parent_block_id)), ref_call));
1820 } else {
1821 if (in_constructor || (current_method != null && current_method.binding == MemberBinding.INSTANCE &&
1822 (!(current_method is CreationMethod) || current_method.body != b)) ||
1823 (current_property_accessor != null && current_property_accessor.prop.binding == MemberBinding.INSTANCE)) {
1824 var ref_call = new CCodeFunctionCall (get_dup_func_expression (new ObjectType (current_class), b.source_reference));
1825 ref_call.add_argument (get_result_cexpression ("self"));
1827 ccode.add_expression (new CCodeAssignment (new CCodeMemberAccess.pointer (get_variable_cexpression ("_data%d_".printf (block_id)), "self"), ref_call));
1830 if (current_method != null) {
1831 // allow capturing generic type parameters
1832 foreach (var type_param in current_method.get_type_parameters ()) {
1833 string func_name;
1835 func_name = "%s_type".printf (type_param.name.down ());
1836 ccode.add_expression (new CCodeAssignment (new CCodeMemberAccess.pointer (get_variable_cexpression ("_data%d_".printf (block_id)), func_name), new CCodeIdentifier (func_name)));
1838 func_name = "%s_dup_func".printf (type_param.name.down ());
1839 ccode.add_expression (new CCodeAssignment (new CCodeMemberAccess.pointer (get_variable_cexpression ("_data%d_".printf (block_id)), func_name), new CCodeIdentifier (func_name)));
1841 func_name = "%s_destroy_func".printf (type_param.name.down ());
1842 ccode.add_expression (new CCodeAssignment (new CCodeMemberAccess.pointer (get_variable_cexpression ("_data%d_".printf (block_id)), func_name), new CCodeIdentifier (func_name)));
1847 if (b.parent_symbol is Method) {
1848 var m = (Method) b.parent_symbol;
1850 // parameters are captured with the top-level block of the method
1851 foreach (var param in m.get_parameters ()) {
1852 if (param.captured) {
1853 capture_parameter (param, data, block_id, free_block);
1857 if (m.coroutine) {
1858 // capture async data to allow invoking callback from inside closure
1859 data.add_field ("gpointer", "_async_data_");
1861 // async method is suspended while waiting for callback,
1862 // so we never need to care about memory management of async data
1863 ccode.add_expression (new CCodeAssignment (new CCodeMemberAccess.pointer (get_variable_cexpression ("_data%d_".printf (block_id)), "_async_data_"), new CCodeIdentifier ("data")));
1865 } else if (b.parent_symbol is PropertyAccessor) {
1866 var acc = (PropertyAccessor) b.parent_symbol;
1868 if (!acc.readable && acc.value_parameter.captured) {
1869 capture_parameter (acc.value_parameter, data, block_id, free_block);
1873 var typedef = new CCodeTypeDefinition ("struct _" + struct_name, new CCodeVariableDeclarator (struct_name));
1874 cfile.add_type_declaration (typedef);
1875 cfile.add_type_definition (data);
1877 var data_free = new CCodeFunctionCall (new CCodeIdentifier ("g_slice_free"));
1878 data_free.add_argument (new CCodeIdentifier (struct_name));
1879 data_free.add_argument (new CCodeIdentifier ("_data%d_".printf (block_id)));
1880 free_block.add_statement (new CCodeExpressionStatement (data_free));
1882 // create ref/unref functions
1883 var ref_fun = new CCodeFunction ("block%d_data_ref".printf (block_id), struct_name + "*");
1884 ref_fun.add_parameter (new CCodeParameter ("_data%d_".printf (block_id), struct_name + "*"));
1885 ref_fun.modifiers = CCodeModifiers.STATIC;
1886 cfile.add_function_declaration (ref_fun);
1887 ref_fun.block = new CCodeBlock ();
1889 var ccall = new CCodeFunctionCall (new CCodeIdentifier ("g_atomic_int_inc"));
1890 ccall.add_argument (new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, new CCodeMemberAccess.pointer (new CCodeIdentifier ("_data%d_".printf (block_id)), "_ref_count_")));
1891 ref_fun.block.add_statement (new CCodeExpressionStatement (ccall));
1892 ref_fun.block.add_statement (new CCodeReturnStatement (new CCodeIdentifier ("_data%d_".printf (block_id))));
1893 cfile.add_function (ref_fun);
1895 var unref_fun = new CCodeFunction ("block%d_data_unref".printf (block_id), "void");
1896 unref_fun.add_parameter (new CCodeParameter ("_data%d_".printf (block_id), struct_name + "*"));
1897 unref_fun.modifiers = CCodeModifiers.STATIC;
1898 cfile.add_function_declaration (unref_fun);
1899 unref_fun.block = new CCodeBlock ();
1901 ccall = new CCodeFunctionCall (new CCodeIdentifier ("g_atomic_int_dec_and_test"));
1902 ccall.add_argument (new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, new CCodeMemberAccess.pointer (new CCodeIdentifier ("_data%d_".printf (block_id)), "_ref_count_")));
1903 unref_fun.block.add_statement (new CCodeIfStatement (ccall, free_block));
1905 cfile.add_function (unref_fun);
1908 foreach (Statement stmt in b.get_statements ()) {
1909 stmt.emit (this);
1912 // free in reverse order
1913 for (int i = local_vars.size - 1; i >= 0; i--) {
1914 var local = local_vars[i];
1915 local.active = false;
1916 if (!local.unreachable && !local.floating && !local.captured && requires_destroy (local.variable_type)) {
1917 var ma = new MemberAccess.simple (local.name);
1918 ma.symbol_reference = local;
1919 ma.value_type = local.variable_type.copy ();
1920 visit_member_access (ma);
1921 ccode.add_expression (get_unref_expression (get_variable_cexpression (local.name), local.variable_type, ma));
1925 if (b.parent_symbol is Method) {
1926 var m = (Method) b.parent_symbol;
1927 foreach (Parameter param in m.get_parameters ()) {
1928 if (!param.captured && !param.ellipsis && requires_destroy (param.variable_type) && param.direction == ParameterDirection.IN) {
1929 var ma = new MemberAccess.simple (param.name);
1930 ma.symbol_reference = param;
1931 ma.value_type = param.variable_type.copy ();
1932 visit_member_access (ma);
1933 ccode.add_expression (get_unref_expression (get_variable_cexpression (param.name), param.variable_type, ma));
1934 } else if (param.direction == ParameterDirection.OUT && !m.coroutine) {
1935 return_out_parameter (param);
1940 if (b.captured) {
1941 int block_id = get_block_id (b);
1943 var data_unref = new CCodeFunctionCall (new CCodeIdentifier ("block%d_data_unref".printf (block_id)));
1944 data_unref.add_argument (get_variable_cexpression ("_data%d_".printf (block_id)));
1945 ccode.add_expression (data_unref);
1948 if (b.parent_node is Block || b.parent_node is SwitchStatement) {
1949 ccode.close ();
1952 emit_context.pop_symbol ();
1955 public override void visit_declaration_statement (DeclarationStatement stmt) {
1956 stmt.declaration.accept (this);
1959 public CCodeExpression get_variable_cexpression (string name) {
1960 if (current_method != null && current_method.coroutine) {
1961 return new CCodeMemberAccess.pointer (new CCodeIdentifier ("data"), get_variable_cname (name));
1962 } else {
1963 return new CCodeIdentifier (get_variable_cname (name));
1967 public string get_variable_cname (string name) {
1968 if (name[0] == '.') {
1969 if (name == ".result") {
1970 return "result";
1972 // compiler-internal variable
1973 if (!variable_name_map.contains (name)) {
1974 variable_name_map.set (name, "_tmp%d_".printf (next_temp_var_id));
1975 next_temp_var_id++;
1977 return variable_name_map.get (name);
1978 } else if (reserved_identifiers.contains (name)) {
1979 return "_%s_".printf (name);
1980 } else {
1981 return name;
1985 public CCodeExpression get_result_cexpression (string cname = "result") {
1986 if (current_method != null && current_method.coroutine) {
1987 return new CCodeMemberAccess.pointer (new CCodeIdentifier ("data"), cname);
1988 } else {
1989 return new CCodeIdentifier (cname);
1993 bool has_simple_struct_initializer (LocalVariable local) {
1994 var st = local.variable_type.data_type as Struct;
1995 var initializer = local.initializer as ObjectCreationExpression;
1996 if (st != null && (!st.is_simple_type () || st.get_cname () == "va_list") && !local.variable_type.nullable &&
1997 initializer != null && initializer.get_object_initializer ().size == 0) {
1998 return true;
1999 } else {
2000 return false;
2004 public override void visit_local_variable (LocalVariable local) {
2005 check_type (local.variable_type);
2007 if (local.initializer != null) {
2008 local.initializer.emit (this);
2010 visit_end_full_expression (local.initializer);
2013 generate_type_declaration (local.variable_type, cfile);
2015 if (!local.captured) {
2016 if (local.variable_type is ArrayType) {
2017 // create variables to store array dimensions
2018 var array_type = (ArrayType) local.variable_type;
2020 if (!array_type.fixed_length) {
2021 for (int dim = 1; dim <= array_type.rank; dim++) {
2022 var len_var = new LocalVariable (int_type.copy (), get_array_length_cname (get_variable_cname (local.name), dim));
2023 emit_temp_var (len_var);
2026 if (array_type.rank == 1) {
2027 var size_var = new LocalVariable (int_type.copy (), get_array_size_cname (get_variable_cname (local.name)));
2028 emit_temp_var (size_var);
2031 } else if (local.variable_type is DelegateType) {
2032 var deleg_type = (DelegateType) local.variable_type;
2033 var d = deleg_type.delegate_symbol;
2034 if (d.has_target) {
2035 // create variable to store delegate target
2036 var target_var = new LocalVariable (new PointerType (new VoidType ()), get_delegate_target_cname (get_variable_cname (local.name)));
2037 emit_temp_var (target_var);
2038 if (deleg_type.value_owned) {
2039 var target_destroy_notify_var = new LocalVariable (new DelegateType ((Delegate) context.root.scope.lookup ("GLib").scope.lookup ("DestroyNotify")), get_delegate_target_destroy_notify_cname (get_variable_cname (local.name)));
2040 emit_temp_var (target_destroy_notify_var);
2046 CCodeExpression rhs = null;
2047 if (local.initializer != null && get_cvalue (local.initializer) != null) {
2048 var ma = new MemberAccess.simple (local.name);
2049 ma.symbol_reference = local;
2050 ma.value_type = local.variable_type.copy ();
2051 visit_member_access (ma);
2053 rhs = get_cvalue (local.initializer);
2055 if (local.variable_type is ArrayType) {
2056 var array_type = (ArrayType) local.variable_type;
2058 if (array_type.fixed_length) {
2059 rhs = null;
2060 } else {
2061 var temp_var = get_temp_variable (local.variable_type, true, local, false);
2062 emit_temp_var (temp_var);
2063 ccode.add_expression (new CCodeAssignment (get_variable_cexpression (temp_var.name), rhs));
2065 for (int dim = 1; dim <= array_type.rank; dim++) {
2066 var lhs_array_len = get_array_length_cexpression (ma, dim);
2067 var rhs_array_len = get_array_length_cexpression (local.initializer, dim);
2068 ccode.add_expression (new CCodeAssignment (lhs_array_len, rhs_array_len));
2070 if (array_type.rank == 1 && !local.captured) {
2071 var lhs_array_size = get_array_size_cvalue (ma.target_value);
2072 var rhs_array_len = get_array_length_cexpression (ma, 1);
2073 ccode.add_expression (new CCodeAssignment (lhs_array_size, rhs_array_len));
2076 rhs = get_variable_cexpression (temp_var.name);
2078 } else if (local.variable_type is DelegateType) {
2079 var deleg_type = (DelegateType) local.variable_type;
2080 var d = deleg_type.delegate_symbol;
2081 if (d.has_target) {
2082 var temp_var = get_temp_variable (local.variable_type, true, local, false);
2083 emit_temp_var (temp_var);
2084 ccode.add_expression (new CCodeAssignment (get_variable_cexpression (temp_var.name), rhs));
2086 CCodeExpression lhs_delegate_target_destroy_notify;
2087 var lhs_delegate_target = get_delegate_target_cexpression (ma, out lhs_delegate_target_destroy_notify);
2088 if (local.captured) {
2089 var block = (Block) local.parent_symbol;
2090 lhs_delegate_target = new CCodeMemberAccess.pointer (get_variable_cexpression ("_data%d_".printf (get_block_id (block))), get_delegate_target_cname (local.name));
2092 CCodeExpression rhs_delegate_target_destroy_notify;
2093 var rhs_delegate_target = get_delegate_target_cexpression (local.initializer, out rhs_delegate_target_destroy_notify);
2094 ccode.add_expression (new CCodeAssignment (lhs_delegate_target, rhs_delegate_target));
2096 if (deleg_type.value_owned) {
2097 if (local.captured) {
2098 var block = (Block) local.parent_symbol;
2099 lhs_delegate_target = new CCodeMemberAccess.pointer (get_variable_cexpression ("_data%d_".printf (get_block_id (block))), get_delegate_target_destroy_notify_cname (local.name));
2101 ccode.add_expression (new CCodeAssignment (lhs_delegate_target_destroy_notify, rhs_delegate_target_destroy_notify));
2104 rhs = get_variable_cexpression (temp_var.name);
2107 } else if (local.variable_type.is_reference_type_or_type_parameter ()) {
2108 rhs = new CCodeConstant ("NULL");
2110 if (local.variable_type is ArrayType) {
2111 // initialize array length variables
2112 var array_type = (ArrayType) local.variable_type;
2114 if (array_type.fixed_length) {
2115 rhs = null;
2116 } else {
2117 for (int dim = 1; dim <= array_type.rank; dim++) {
2118 ccode.add_expression (new CCodeAssignment (get_variable_cexpression (get_array_length_cname (get_variable_cname (local.name), dim)), new CCodeConstant ("0")));
2124 if (local.captured) {
2125 if (local.initializer != null) {
2126 if (has_simple_struct_initializer (local)) {
2127 ccode.add_expression (rhs);
2128 } else {
2129 ccode.add_expression (new CCodeAssignment (new CCodeMemberAccess.pointer (get_variable_cexpression ("_data%d_".printf (get_block_id ((Block) local.parent_symbol))), get_variable_cname (local.name)), rhs));
2132 } else if (current_method != null && current_method.coroutine) {
2133 closure_struct.add_field (local.variable_type.get_cname (), get_variable_cname (local.name) + local.variable_type.get_cdeclarator_suffix ());
2135 if (local.initializer != null) {
2136 if (has_simple_struct_initializer (local)) {
2137 ccode.add_expression (rhs);
2138 } else {
2139 ccode.add_expression (new CCodeAssignment (new CCodeMemberAccess.pointer (new CCodeIdentifier ("data"), get_variable_cname (local.name)), rhs));
2142 } else {
2143 CCodeExpression post_rhs = null;
2144 if (has_simple_struct_initializer (local)) {
2145 post_rhs = rhs;
2146 rhs = null;
2149 var cvar = new CCodeVariableDeclarator (get_variable_cname (local.name), rhs, local.variable_type.get_cdeclarator_suffix ());
2150 if (rhs != null) {
2151 cvar.line = rhs.line;
2154 // try to initialize uninitialized variables
2155 // initialization not necessary for variables stored in closure
2156 if (cvar.initializer == null) {
2157 cvar.initializer = default_value_for_type (local.variable_type, true);
2158 cvar.init0 = true;
2161 ccode.add_declaration (local.variable_type.get_cname (), cvar);
2163 if (cvar.initializer != null && !cvar.init0) {
2164 cvar.initializer = null;
2165 ccode.add_expression (new CCodeAssignment (get_variable_cexpression (local.name), rhs));
2168 if (post_rhs != null) {
2169 ccode.add_expression (post_rhs);
2173 if (local.initializer != null && local.variable_type is ArrayType) {
2174 var array_type = (ArrayType) local.variable_type;
2176 if (array_type.fixed_length) {
2177 cfile.add_include ("string.h");
2179 // it is necessary to use memcpy for fixed-length (stack-allocated) arrays
2180 // simple assignments do not work in C
2181 var sizeof_call = new CCodeFunctionCall (new CCodeIdentifier ("sizeof"));
2182 sizeof_call.add_argument (new CCodeIdentifier (array_type.element_type.get_cname ()));
2183 var size = new CCodeBinaryExpression (CCodeBinaryOperator.MUL, new CCodeConstant ("%d".printf (array_type.length)), sizeof_call);
2185 var ccopy = new CCodeFunctionCall (new CCodeIdentifier ("memcpy"));
2186 ccopy.add_argument (get_variable_cexpression (local.name));
2187 ccopy.add_argument (get_cvalue (local.initializer));
2188 ccopy.add_argument (size);
2189 ccode.add_expression (ccopy);
2193 if (local.initializer != null && local.initializer.tree_can_fail) {
2194 add_simple_check (local.initializer);
2197 local.active = true;
2200 public override void visit_initializer_list (InitializerList list) {
2201 if (list.target_type.data_type is Struct) {
2202 /* initializer is used as struct initializer */
2203 var st = (Struct) list.target_type.data_type;
2205 if (list.parent_node is Constant || list.parent_node is Field || list.parent_node is InitializerList) {
2206 var clist = new CCodeInitializerList ();
2208 var field_it = st.get_fields ().iterator ();
2209 foreach (Expression expr in list.get_initializers ()) {
2210 Field field = null;
2211 while (field == null) {
2212 field_it.next ();
2213 field = field_it.get ();
2214 if (field.binding != MemberBinding.INSTANCE) {
2215 // we only initialize instance fields
2216 field = null;
2220 var cexpr = get_cvalue (expr);
2222 string ctype = field.get_ctype ();
2223 if (ctype != null) {
2224 cexpr = new CCodeCastExpression (cexpr, ctype);
2227 clist.append (cexpr);
2230 set_cvalue (list, clist);
2231 } else {
2232 // used as expression
2233 var temp_decl = get_temp_variable (list.target_type, false, list);
2234 emit_temp_var (temp_decl);
2236 var instance = get_variable_cexpression (get_variable_cname (temp_decl.name));
2238 var ccomma = new CCodeCommaExpression ();
2240 var field_it = st.get_fields ().iterator ();
2241 foreach (Expression expr in list.get_initializers ()) {
2242 Field field = null;
2243 while (field == null) {
2244 field_it.next ();
2245 field = field_it.get ();
2246 if (field.binding != MemberBinding.INSTANCE) {
2247 // we only initialize instance fields
2248 field = null;
2252 var cexpr = get_cvalue (expr);
2254 string ctype = field.get_ctype ();
2255 if (ctype != null) {
2256 cexpr = new CCodeCastExpression (cexpr, ctype);
2259 var lhs = new CCodeMemberAccess (instance, field.get_cname ());;
2260 ccomma.append_expression (new CCodeAssignment (lhs, cexpr));
2263 ccomma.append_expression (instance);
2264 set_cvalue (list, ccomma);
2266 } else {
2267 var clist = new CCodeInitializerList ();
2268 foreach (Expression expr in list.get_initializers ()) {
2269 clist.append (get_cvalue (expr));
2271 set_cvalue (list, clist);
2275 public override LocalVariable create_local (DataType type) {
2276 var result = get_temp_variable (type, type.value_owned);
2277 emit_temp_var (result);
2278 return result;
2281 public LocalVariable get_temp_variable (DataType type, bool value_owned = true, CodeNode? node_reference = null, bool init = true) {
2282 var var_type = type.copy ();
2283 var_type.value_owned = value_owned;
2284 var local = new LocalVariable (var_type, "_tmp%d_".printf (next_temp_var_id));
2285 local.no_init = !init;
2287 if (node_reference != null) {
2288 local.source_reference = node_reference.source_reference;
2291 next_temp_var_id++;
2293 return local;
2296 bool is_in_generic_type (DataType type) {
2297 if (current_symbol != null && type.type_parameter.parent_symbol is TypeSymbol
2298 && (current_method == null || current_method.binding == MemberBinding.INSTANCE)) {
2299 return true;
2300 } else {
2301 return false;
2305 public CCodeExpression get_type_id_expression (DataType type, bool is_chainup = false) {
2306 if (type is GenericType) {
2307 string var_name = "%s_type".printf (type.type_parameter.name.down ());
2308 if (is_in_generic_type (type) && !is_chainup && !in_creation_method) {
2309 return new CCodeMemberAccess.pointer (new CCodeMemberAccess.pointer (get_result_cexpression ("self"), "priv"), var_name);
2310 } else {
2311 return new CCodeIdentifier (var_name);
2313 } else {
2314 string type_id = type.get_type_id ();
2315 if (type_id == null) {
2316 type_id = "G_TYPE_INVALID";
2317 } else {
2318 generate_type_declaration (type, cfile);
2320 return new CCodeIdentifier (type_id);
2324 public virtual CCodeExpression? get_dup_func_expression (DataType type, SourceReference? source_reference, bool is_chainup = false) {
2325 if (type is ErrorType) {
2326 return new CCodeIdentifier ("g_error_copy");
2327 } else if (type.data_type != null) {
2328 string dup_function;
2329 var cl = type.data_type as Class;
2330 if (type.data_type.is_reference_counting ()) {
2331 dup_function = type.data_type.get_ref_function ();
2332 if (type.data_type is Interface && dup_function == null) {
2333 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 ()));
2334 return null;
2336 } else if (cl != null && cl.is_immutable) {
2337 // allow duplicates of immutable instances as for example strings
2338 dup_function = type.data_type.get_dup_function ();
2339 if (dup_function == null) {
2340 dup_function = "";
2342 } else if (cl != null && cl.is_gboxed) {
2343 // allow duplicates of gboxed instances
2344 dup_function = generate_dup_func_wrapper (type);
2345 if (dup_function == null) {
2346 dup_function = "";
2348 } else if (type is ValueType) {
2349 dup_function = type.data_type.get_dup_function ();
2350 if (dup_function == null && type.nullable) {
2351 dup_function = generate_struct_dup_wrapper ((ValueType) type);
2352 } else if (dup_function == null) {
2353 dup_function = "";
2355 } else {
2356 // duplicating non-reference counted objects may cause side-effects (and performance issues)
2357 Report.error (source_reference, "duplicating %s instance, use unowned variable or explicitly invoke copy method".printf (type.data_type.name));
2358 return null;
2361 return new CCodeIdentifier (dup_function);
2362 } else if (type.type_parameter != null) {
2363 string func_name = "%s_dup_func".printf (type.type_parameter.name.down ());
2364 if (is_in_generic_type (type) && !is_chainup && !in_creation_method) {
2365 return new CCodeMemberAccess.pointer (new CCodeMemberAccess.pointer (get_result_cexpression ("self"), "priv"), func_name);
2366 } else {
2367 return new CCodeIdentifier (func_name);
2369 } else if (type is PointerType) {
2370 var pointer_type = (PointerType) type;
2371 return get_dup_func_expression (pointer_type.base_type, source_reference);
2372 } else {
2373 return new CCodeConstant ("NULL");
2377 void make_comparable_cexpression (ref DataType left_type, ref CCodeExpression cleft, ref DataType right_type, ref CCodeExpression cright) {
2378 var left_type_as_struct = left_type.data_type as Struct;
2379 var right_type_as_struct = right_type.data_type as Struct;
2381 // GValue support
2382 var valuecast = try_cast_value_to_type (cleft, left_type, right_type);
2383 if (valuecast != null) {
2384 cleft = valuecast;
2385 left_type = right_type;
2386 make_comparable_cexpression (ref left_type, ref cleft, ref right_type, ref cright);
2387 return;
2390 valuecast = try_cast_value_to_type (cright, right_type, left_type);
2391 if (valuecast != null) {
2392 cright = valuecast;
2393 right_type = left_type;
2394 make_comparable_cexpression (ref left_type, ref cleft, ref right_type, ref cright);
2395 return;
2398 if (left_type.data_type is Class && !((Class) left_type.data_type).is_compact &&
2399 right_type.data_type is Class && !((Class) right_type.data_type).is_compact) {
2400 var left_cl = (Class) left_type.data_type;
2401 var right_cl = (Class) right_type.data_type;
2403 if (left_cl != right_cl) {
2404 if (left_cl.is_subtype_of (right_cl)) {
2405 cleft = generate_instance_cast (cleft, right_cl);
2406 } else if (right_cl.is_subtype_of (left_cl)) {
2407 cright = generate_instance_cast (cright, left_cl);
2410 } else if (left_type_as_struct != null && right_type_as_struct != null) {
2411 if (left_type is StructValueType) {
2412 // real structs (uses compare/equal function)
2413 if (!left_type.nullable) {
2414 cleft = new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, cleft);
2416 if (!right_type.nullable) {
2417 cright = new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, cright);
2419 } else {
2420 // integer or floating or boolean type
2421 if (left_type.nullable && right_type.nullable) {
2422 // FIXME also compare contents, not just address
2423 } else if (left_type.nullable) {
2424 // FIXME check left value is not null
2425 cleft = new CCodeUnaryExpression (CCodeUnaryOperator.POINTER_INDIRECTION, cleft);
2426 } else if (right_type.nullable) {
2427 // FIXME check right value is not null
2428 cright = new CCodeUnaryExpression (CCodeUnaryOperator.POINTER_INDIRECTION, cright);
2434 private string generate_struct_equal_function (Struct st) {
2435 string equal_func = "_%sequal".printf (st.get_lower_case_cprefix ());
2437 if (!add_wrapper (equal_func)) {
2438 // wrapper already defined
2439 return equal_func;
2442 var function = new CCodeFunction (equal_func, "gboolean");
2443 function.modifiers = CCodeModifiers.STATIC;
2445 function.add_parameter (new CCodeParameter ("s1", "const " + st.get_cname () + "*"));
2446 function.add_parameter (new CCodeParameter ("s2", "const " + st.get_cname () + "*"));
2448 push_function (function);
2450 // if (s1 == s2) return TRUE;
2452 var cexp = new CCodeBinaryExpression (CCodeBinaryOperator.EQUALITY, new CCodeIdentifier ("s1"), new CCodeIdentifier ("s2"));
2453 ccode.open_if (cexp);
2454 ccode.add_return (new CCodeConstant ("TRUE"));
2455 ccode.close ();
2457 // if (s1 == NULL || s2 == NULL) return FALSE;
2459 var cexp = new CCodeBinaryExpression (CCodeBinaryOperator.EQUALITY, new CCodeIdentifier ("s1"), new CCodeConstant ("NULL"));
2460 ccode.open_if (cexp);
2461 ccode.add_return (new CCodeConstant ("FALSE"));
2462 ccode.close ();
2464 cexp = new CCodeBinaryExpression (CCodeBinaryOperator.EQUALITY, new CCodeIdentifier ("s2"), new CCodeConstant ("NULL"));
2465 ccode.open_if (cexp);
2466 ccode.add_return (new CCodeConstant ("FALSE"));
2467 ccode.close ();
2470 foreach (Field f in st.get_fields ()) {
2471 if (f.binding != MemberBinding.INSTANCE) {
2472 // we only compare instance fields
2473 continue;
2476 CCodeExpression cexp; // if (cexp) return FALSE;
2477 var s1 = (CCodeExpression) new CCodeMemberAccess.pointer (new CCodeIdentifier ("s1"), f.name); // s1->f
2478 var s2 = (CCodeExpression) new CCodeMemberAccess.pointer (new CCodeIdentifier ("s2"), f.name); // s2->f
2480 var variable_type = f.variable_type.copy ();
2481 make_comparable_cexpression (ref variable_type, ref s1, ref variable_type, ref s2);
2483 if (!(f.variable_type is NullType) && f.variable_type.compatible (string_type)) {
2484 var ccall = new CCodeFunctionCall (new CCodeIdentifier ("g_strcmp0"));
2485 ccall.add_argument (s1);
2486 ccall.add_argument (s2);
2487 cexp = ccall;
2488 } else if (f.variable_type is StructValueType) {
2489 var equalfunc = generate_struct_equal_function (f.variable_type.data_type as Struct);
2490 var ccall = new CCodeFunctionCall (new CCodeIdentifier (equalfunc));
2491 ccall.add_argument (s1);
2492 ccall.add_argument (s2);
2493 cexp = new CCodeUnaryExpression (CCodeUnaryOperator.LOGICAL_NEGATION, ccall);
2494 } else {
2495 cexp = new CCodeBinaryExpression (CCodeBinaryOperator.INEQUALITY, s1, s2);
2498 ccode.open_if (cexp);
2499 ccode.add_return (new CCodeConstant ("FALSE"));
2500 ccode.close ();
2503 if (st.get_fields().size == 0) {
2504 // either opaque structure or simple type
2505 if (st.is_simple_type ()) {
2506 var cexp = new CCodeBinaryExpression (CCodeBinaryOperator.EQUALITY, new CCodeUnaryExpression (CCodeUnaryOperator.POINTER_INDIRECTION, new CCodeIdentifier ("s1")), new CCodeUnaryExpression (CCodeUnaryOperator.POINTER_INDIRECTION, new CCodeIdentifier ("s2")));
2507 ccode.add_return (cexp);
2508 } else {
2509 ccode.add_return (new CCodeConstant ("FALSE"));
2511 } else {
2512 ccode.add_return (new CCodeConstant ("TRUE"));
2515 pop_function ();
2517 cfile.add_function_declaration (function);
2518 cfile.add_function (function);
2520 return equal_func;
2523 private string generate_numeric_equal_function (Struct st) {
2524 string equal_func = "_%sequal".printf (st.get_lower_case_cprefix ());
2526 if (!add_wrapper (equal_func)) {
2527 // wrapper already defined
2528 return equal_func;
2531 var function = new CCodeFunction (equal_func, "gboolean");
2532 function.modifiers = CCodeModifiers.STATIC;
2534 function.add_parameter (new CCodeParameter ("s1", "const " + st.get_cname () + "*"));
2535 function.add_parameter (new CCodeParameter ("s2", "const " + st.get_cname () + "*"));
2537 push_function (function);
2539 // if (s1 == s2) return TRUE;
2541 var cexp = new CCodeBinaryExpression (CCodeBinaryOperator.EQUALITY, new CCodeIdentifier ("s1"), new CCodeIdentifier ("s2"));
2542 ccode.open_if (cexp);
2543 ccode.add_return (new CCodeConstant ("TRUE"));
2544 ccode.close ();
2546 // if (s1 == NULL || s2 == NULL) return FALSE;
2548 var cexp = new CCodeBinaryExpression (CCodeBinaryOperator.EQUALITY, new CCodeIdentifier ("s1"), new CCodeConstant ("NULL"));
2549 ccode.open_if (cexp);
2550 ccode.add_return (new CCodeConstant ("FALSE"));
2551 ccode.close ();
2553 cexp = new CCodeBinaryExpression (CCodeBinaryOperator.EQUALITY, new CCodeIdentifier ("s2"), new CCodeConstant ("NULL"));
2554 ccode.open_if (cexp);
2555 ccode.add_return (new CCodeConstant ("FALSE"));
2556 ccode.close ();
2558 // return (*s1 == *s2);
2560 var cexp = new CCodeBinaryExpression (CCodeBinaryOperator.EQUALITY, new CCodeUnaryExpression (CCodeUnaryOperator.POINTER_INDIRECTION, new CCodeIdentifier ("s1")), new CCodeUnaryExpression (CCodeUnaryOperator.POINTER_INDIRECTION, new CCodeIdentifier ("s2")));
2561 ccode.add_return (cexp);
2564 pop_function ();
2566 cfile.add_function_declaration (function);
2567 cfile.add_function (function);
2569 return equal_func;
2572 private string generate_struct_dup_wrapper (ValueType value_type) {
2573 string dup_func = "_%sdup".printf (value_type.type_symbol.get_lower_case_cprefix ());
2575 if (!add_wrapper (dup_func)) {
2576 // wrapper already defined
2577 return dup_func;
2580 var function = new CCodeFunction (dup_func, value_type.get_cname ());
2581 function.modifiers = CCodeModifiers.STATIC;
2583 function.add_parameter (new CCodeParameter ("self", value_type.get_cname ()));
2585 push_function (function);
2587 if (value_type.type_symbol == gvalue_type) {
2588 var dup_call = new CCodeFunctionCall (new CCodeIdentifier ("g_boxed_copy"));
2589 dup_call.add_argument (new CCodeIdentifier ("G_TYPE_VALUE"));
2590 dup_call.add_argument (new CCodeIdentifier ("self"));
2592 ccode.add_return (dup_call);
2593 } else {
2594 ccode.add_declaration (value_type.get_cname (), new CCodeVariableDeclarator ("dup"));
2596 var creation_call = new CCodeFunctionCall (new CCodeIdentifier ("g_new0"));
2597 creation_call.add_argument (new CCodeConstant (value_type.data_type.get_cname ()));
2598 creation_call.add_argument (new CCodeConstant ("1"));
2599 ccode.add_expression (new CCodeAssignment (new CCodeIdentifier ("dup"), creation_call));
2601 var st = value_type.data_type as Struct;
2602 if (st != null && st.is_disposable ()) {
2603 if (!st.has_copy_function) {
2604 generate_struct_copy_function (st);
2607 var copy_call = new CCodeFunctionCall (new CCodeIdentifier (st.get_copy_function ()));
2608 copy_call.add_argument (new CCodeIdentifier ("self"));
2609 copy_call.add_argument (new CCodeIdentifier ("dup"));
2610 ccode.add_expression (copy_call);
2611 } else {
2612 cfile.add_include ("string.h");
2614 var sizeof_call = new CCodeFunctionCall (new CCodeIdentifier ("sizeof"));
2615 sizeof_call.add_argument (new CCodeConstant (value_type.data_type.get_cname ()));
2617 var copy_call = new CCodeFunctionCall (new CCodeIdentifier ("memcpy"));
2618 copy_call.add_argument (new CCodeIdentifier ("dup"));
2619 copy_call.add_argument (new CCodeIdentifier ("self"));
2620 copy_call.add_argument (sizeof_call);
2621 ccode.add_expression (copy_call);
2624 ccode.add_return (new CCodeIdentifier ("dup"));
2627 pop_function ();
2629 cfile.add_function_declaration (function);
2630 cfile.add_function (function);
2632 return dup_func;
2635 protected string generate_dup_func_wrapper (DataType type) {
2636 string destroy_func = "_vala_%s_copy".printf (type.data_type.get_cname ());
2638 if (!add_wrapper (destroy_func)) {
2639 // wrapper already defined
2640 return destroy_func;
2643 var function = new CCodeFunction (destroy_func, type.get_cname ());
2644 function.modifiers = CCodeModifiers.STATIC;
2645 function.add_parameter (new CCodeParameter ("self", type.get_cname ()));
2647 push_function (function);
2649 var cl = type.data_type as Class;
2650 assert (cl != null && cl.is_gboxed);
2652 var free_call = new CCodeFunctionCall (new CCodeIdentifier ("g_boxed_copy"));
2653 free_call.add_argument (new CCodeIdentifier (cl.get_type_id ()));
2654 free_call.add_argument (new CCodeIdentifier ("self"));
2656 ccode.add_return (free_call);
2658 pop_function ();
2660 cfile.add_function_declaration (function);
2661 cfile.add_function (function);
2663 return destroy_func;
2666 protected string generate_free_func_wrapper (DataType type) {
2667 string destroy_func = "_vala_%s_free".printf (type.data_type.get_cname ());
2669 if (!add_wrapper (destroy_func)) {
2670 // wrapper already defined
2671 return destroy_func;
2674 var function = new CCodeFunction (destroy_func, "void");
2675 function.modifiers = CCodeModifiers.STATIC;
2676 function.add_parameter (new CCodeParameter ("self", type.get_cname ()));
2678 push_function (function);
2680 var cl = type.data_type as Class;
2681 if (cl != null && cl.is_gboxed) {
2682 var free_call = new CCodeFunctionCall (new CCodeIdentifier ("g_boxed_free"));
2683 free_call.add_argument (new CCodeIdentifier (cl.get_type_id ()));
2684 free_call.add_argument (new CCodeIdentifier ("self"));
2686 ccode.add_expression (free_call);
2687 } else if (cl != null) {
2688 assert (cl.free_function_address_of);
2690 var free_call = new CCodeFunctionCall (new CCodeIdentifier (type.data_type.get_free_function ()));
2691 free_call.add_argument (new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, new CCodeIdentifier ("self")));
2693 ccode.add_expression (free_call);
2694 } else {
2695 var st = type.data_type as Struct;
2696 if (st != null && st.is_disposable ()) {
2697 if (!st.has_destroy_function) {
2698 generate_struct_destroy_function (st);
2701 var destroy_call = new CCodeFunctionCall (new CCodeIdentifier (st.get_destroy_function ()));
2702 destroy_call.add_argument (new CCodeIdentifier ("self"));
2703 ccode.add_expression (destroy_call);
2706 var free_call = new CCodeFunctionCall (new CCodeIdentifier ("g_free"));
2707 free_call.add_argument (new CCodeIdentifier ("self"));
2709 ccode.add_expression (free_call);
2712 pop_function ();
2714 cfile.add_function_declaration (function);
2715 cfile.add_function (function);
2717 return destroy_func;
2720 public CCodeExpression? get_destroy0_func_expression (DataType type, bool is_chainup = false) {
2721 var element_destroy_func_expression = get_destroy_func_expression (type, is_chainup);
2723 if (element_destroy_func_expression is CCodeIdentifier) {
2724 var freeid = (CCodeIdentifier) element_destroy_func_expression;
2725 string free0_func = "_%s0_".printf (freeid.name);
2727 if (add_wrapper (free0_func)) {
2728 var function = new CCodeFunction (free0_func, "void");
2729 function.modifiers = CCodeModifiers.STATIC;
2731 function.add_parameter (new CCodeParameter ("var", "gpointer"));
2733 push_function (function);
2735 ccode.add_expression (get_unref_expression (new CCodeIdentifier ("var"), type, null, true));
2737 pop_function ();
2739 cfile.add_function_declaration (function);
2740 cfile.add_function (function);
2743 element_destroy_func_expression = new CCodeIdentifier (free0_func);
2746 return element_destroy_func_expression;
2749 public CCodeExpression? get_destroy_func_expression (DataType type, bool is_chainup = false) {
2750 if (context.profile == Profile.GOBJECT && (type.data_type == glist_type || type.data_type == gslist_type || type.data_type == gnode_type)) {
2751 // create wrapper function to free list elements if necessary
2753 bool elements_require_free = false;
2754 CCodeExpression element_destroy_func_expression = null;
2756 foreach (DataType type_arg in type.get_type_arguments ()) {
2757 elements_require_free = requires_destroy (type_arg);
2758 if (elements_require_free) {
2759 element_destroy_func_expression = get_destroy0_func_expression (type_arg);
2763 if (elements_require_free && element_destroy_func_expression is CCodeIdentifier) {
2764 return new CCodeIdentifier (generate_collection_free_wrapper (type, (CCodeIdentifier) element_destroy_func_expression));
2765 } else {
2766 return new CCodeIdentifier (type.data_type.get_free_function ());
2768 } else if (type is ErrorType) {
2769 return new CCodeIdentifier ("g_error_free");
2770 } else if (type.data_type != null) {
2771 string unref_function;
2772 if (type is ReferenceType) {
2773 if (type.data_type.is_reference_counting ()) {
2774 unref_function = type.data_type.get_unref_function ();
2775 if (type.data_type is Interface && unref_function == null) {
2776 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 ()));
2777 return null;
2779 } else {
2780 var cl = type.data_type as Class;
2781 if (cl != null && (cl.free_function_address_of || cl.is_gboxed)) {
2782 unref_function = generate_free_func_wrapper (type);
2783 } else {
2784 unref_function = type.data_type.get_free_function ();
2787 } else {
2788 if (type.nullable) {
2789 unref_function = type.data_type.get_free_function ();
2790 if (unref_function == null) {
2791 if (type.data_type is Struct && ((Struct) type.data_type).is_disposable ()) {
2792 unref_function = generate_free_func_wrapper (type);
2793 } else {
2794 unref_function = "g_free";
2797 } else {
2798 var st = (Struct) type.data_type;
2799 if (!st.has_destroy_function) {
2800 generate_struct_destroy_function (st);
2802 unref_function = st.get_destroy_function ();
2805 if (unref_function == null) {
2806 return new CCodeConstant ("NULL");
2808 return new CCodeIdentifier (unref_function);
2809 } else if (type.type_parameter != null && current_type_symbol is Class) {
2810 string func_name = "%s_destroy_func".printf (type.type_parameter.name.down ());
2811 if (is_in_generic_type (type) && !is_chainup && !in_creation_method) {
2812 return new CCodeMemberAccess.pointer (new CCodeMemberAccess.pointer (get_result_cexpression ("self"), "priv"), func_name);
2813 } else {
2814 return new CCodeIdentifier (func_name);
2816 } else if (type is ArrayType) {
2817 if (context.profile == Profile.POSIX) {
2818 return new CCodeIdentifier ("free");
2819 } else {
2820 return new CCodeIdentifier ("g_free");
2822 } else if (type is PointerType) {
2823 if (context.profile == Profile.POSIX) {
2824 return new CCodeIdentifier ("free");
2825 } else {
2826 return new CCodeIdentifier ("g_free");
2828 } else {
2829 return new CCodeConstant ("NULL");
2833 private string generate_collection_free_wrapper (DataType collection_type, CCodeIdentifier element_destroy_func_expression) {
2834 string destroy_func = "_%s_%s".printf (collection_type.data_type.get_free_function (), element_destroy_func_expression.name);
2836 if (!add_wrapper (destroy_func)) {
2837 // wrapper already defined
2838 return destroy_func;
2841 var function = new CCodeFunction (destroy_func, "void");
2842 function.modifiers = CCodeModifiers.STATIC;
2844 function.add_parameter (new CCodeParameter ("self", collection_type.get_cname ()));
2846 push_function (function);
2848 CCodeFunctionCall element_free_call;
2849 if (collection_type.data_type == gnode_type) {
2850 /* A wrapper which converts GNodeTraverseFunc into GDestroyNotify */
2851 string destroy_node_func = "%s_node".printf (destroy_func);
2852 var wrapper = new CCodeFunction (destroy_node_func, "gboolean");
2853 wrapper.modifiers = CCodeModifiers.STATIC;
2854 wrapper.add_parameter (new CCodeParameter ("node", collection_type.get_cname ()));
2855 wrapper.add_parameter (new CCodeParameter ("unused", "gpointer"));
2856 var wrapper_block = new CCodeBlock ();
2857 var free_call = new CCodeFunctionCall (element_destroy_func_expression);
2858 free_call.add_argument (new CCodeMemberAccess.pointer(new CCodeIdentifier("node"), "data"));
2859 wrapper_block.add_statement (new CCodeExpressionStatement (free_call));
2860 wrapper_block.add_statement (new CCodeReturnStatement (new CCodeConstant ("FALSE")));
2861 cfile.add_function_declaration (function);
2862 wrapper.block = wrapper_block;
2863 cfile.add_function (wrapper);
2865 /* Now the code to call g_traverse with the above */
2866 element_free_call = new CCodeFunctionCall (new CCodeIdentifier ("g_node_traverse"));
2867 element_free_call.add_argument (new CCodeIdentifier("self"));
2868 element_free_call.add_argument (new CCodeConstant ("G_POST_ORDER"));
2869 element_free_call.add_argument (new CCodeConstant ("G_TRAVERSE_ALL"));
2870 element_free_call.add_argument (new CCodeConstant ("-1"));
2871 element_free_call.add_argument (new CCodeIdentifier (destroy_node_func));
2872 element_free_call.add_argument (new CCodeConstant ("NULL"));
2873 } else {
2874 if (collection_type.data_type == glist_type) {
2875 element_free_call = new CCodeFunctionCall (new CCodeIdentifier ("g_list_foreach"));
2876 } else {
2877 element_free_call = new CCodeFunctionCall (new CCodeIdentifier ("g_slist_foreach"));
2880 element_free_call.add_argument (new CCodeIdentifier ("self"));
2881 element_free_call.add_argument (new CCodeCastExpression (element_destroy_func_expression, "GFunc"));
2882 element_free_call.add_argument (new CCodeConstant ("NULL"));
2885 ccode.add_expression (element_free_call);
2887 var cfreecall = new CCodeFunctionCall (new CCodeIdentifier (collection_type.data_type.get_free_function ()));
2888 cfreecall.add_argument (new CCodeIdentifier ("self"));
2889 ccode.add_expression (cfreecall);
2891 pop_function ();
2893 cfile.add_function_declaration (function);
2894 cfile.add_function (function);
2896 return destroy_func;
2899 public virtual string? append_struct_array_free (Struct st) {
2900 return null;
2903 public CCodeExpression get_unref_expression (CCodeExpression cvar, DataType type, Expression? expr, bool is_macro_definition = false) {
2904 var value = new GLibValue (type, cvar);
2905 if (expr != null && expr.target_value != null) {
2906 value.array_length_cvalues = ((GLibValue) expr.target_value).array_length_cvalues;
2907 value.delegate_target_cvalue = get_delegate_target_cvalue (expr.target_value);
2908 value.delegate_target_destroy_notify_cvalue = get_delegate_target_destroy_notify_cvalue (expr.target_value);
2910 return destroy_value (value, is_macro_definition);
2913 public virtual CCodeExpression destroy_value (TargetValue value, bool is_macro_definition = false) {
2914 var type = value.value_type;
2915 var cvar = get_cvalue_ (value);
2917 if (type is DelegateType) {
2918 var delegate_target = get_delegate_target_cvalue (value);
2919 var delegate_target_destroy_notify = get_delegate_target_destroy_notify_cvalue (value);
2921 var ccall = new CCodeFunctionCall (delegate_target_destroy_notify);
2922 ccall.add_argument (delegate_target);
2924 var destroy_call = new CCodeCommaExpression ();
2925 destroy_call.append_expression (ccall);
2926 destroy_call.append_expression (new CCodeConstant ("NULL"));
2928 var cisnull = new CCodeBinaryExpression (CCodeBinaryOperator.EQUALITY, delegate_target_destroy_notify, new CCodeConstant ("NULL"));
2930 var ccomma = new CCodeCommaExpression ();
2931 ccomma.append_expression (new CCodeConditionalExpression (cisnull, new CCodeConstant ("NULL"), destroy_call));
2932 ccomma.append_expression (new CCodeAssignment (cvar, new CCodeConstant ("NULL")));
2933 ccomma.append_expression (new CCodeAssignment (delegate_target, new CCodeConstant ("NULL")));
2934 ccomma.append_expression (new CCodeAssignment (delegate_target_destroy_notify, new CCodeConstant ("NULL")));
2936 return ccomma;
2939 var ccall = new CCodeFunctionCall (get_destroy_func_expression (type));
2941 if (type is ValueType && !type.nullable) {
2942 // normal value type, no null check
2943 var st = type.data_type as Struct;
2944 if (st != null && st.is_simple_type ()) {
2945 // used for va_list
2946 ccall.add_argument (cvar);
2947 } else {
2948 ccall.add_argument (new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, cvar));
2951 if (gvalue_type != null && type.data_type == gvalue_type) {
2952 // g_value_unset must not be called for already unset values
2953 var cisvalid = new CCodeFunctionCall (new CCodeIdentifier ("G_IS_VALUE"));
2954 cisvalid.add_argument (new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, cvar));
2956 var ccomma = new CCodeCommaExpression ();
2957 ccomma.append_expression (ccall);
2958 ccomma.append_expression (new CCodeConstant ("NULL"));
2960 return new CCodeConditionalExpression (cisvalid, ccomma, new CCodeConstant ("NULL"));
2961 } else {
2962 return ccall;
2966 if (ccall.call is CCodeIdentifier && !(type is ArrayType) && !is_macro_definition) {
2967 // generate and use NULL-aware free macro to simplify code
2969 var freeid = (CCodeIdentifier) ccall.call;
2970 string free0_func = "_%s0".printf (freeid.name);
2972 if (add_wrapper (free0_func)) {
2973 var macro = destroy_value (new GLibValue (type, new CCodeIdentifier ("var")), true);
2974 cfile.add_type_declaration (new CCodeMacroReplacement.with_expression ("%s(var)".printf (free0_func), macro));
2977 ccall = new CCodeFunctionCall (new CCodeIdentifier (free0_func));
2978 ccall.add_argument (cvar);
2979 return ccall;
2982 /* (foo == NULL ? NULL : foo = (unref (foo), NULL)) */
2984 /* can be simplified to
2985 * foo = (unref (foo), NULL)
2986 * if foo is of static type non-null
2989 var cisnull = new CCodeBinaryExpression (CCodeBinaryOperator.EQUALITY, cvar, new CCodeConstant ("NULL"));
2990 if (type.type_parameter != null) {
2991 if (!(current_type_symbol is Class) || current_class.is_compact) {
2992 return new CCodeConstant ("NULL");
2995 // unref functions are optional for type parameters
2996 var cunrefisnull = new CCodeBinaryExpression (CCodeBinaryOperator.EQUALITY, get_destroy_func_expression (type), new CCodeConstant ("NULL"));
2997 cisnull = new CCodeBinaryExpression (CCodeBinaryOperator.OR, cisnull, cunrefisnull);
3000 ccall.add_argument (cvar);
3002 /* set freed references to NULL to prevent further use */
3003 var ccomma = new CCodeCommaExpression ();
3005 if (context.profile == Profile.GOBJECT) {
3006 if (type.data_type != null && !type.data_type.is_reference_counting () &&
3007 (type.data_type == gstringbuilder_type
3008 || type.data_type == garray_type
3009 || type.data_type == gbytearray_type
3010 || type.data_type == gptrarray_type)) {
3011 ccall.add_argument (new CCodeConstant ("TRUE"));
3012 } else if (type.data_type == gthreadpool_type) {
3013 ccall.add_argument (new CCodeConstant ("FALSE"));
3014 ccall.add_argument (new CCodeConstant ("TRUE"));
3015 } else if (type is ArrayType) {
3016 var array_type = (ArrayType) type;
3017 if (requires_destroy (array_type.element_type)) {
3018 CCodeExpression csizeexpr = null;
3019 bool first = true;
3020 for (int dim = 1; dim <= array_type.rank; dim++) {
3021 if (first) {
3022 csizeexpr = get_array_length_cvalue (value, dim);
3023 first = false;
3024 } else {
3025 csizeexpr = new CCodeBinaryExpression (CCodeBinaryOperator.MUL, csizeexpr, get_array_length_cvalue (value, dim));
3029 var st = array_type.element_type.data_type as Struct;
3030 if (st != null && !array_type.element_type.nullable) {
3031 ccall.call = new CCodeIdentifier (append_struct_array_free (st));
3032 ccall.add_argument (csizeexpr);
3033 } else {
3034 requires_array_free = true;
3035 ccall.call = new CCodeIdentifier ("_vala_array_free");
3036 ccall.add_argument (csizeexpr);
3037 ccall.add_argument (new CCodeCastExpression (get_destroy_func_expression (array_type.element_type), "GDestroyNotify"));
3043 ccomma.append_expression (ccall);
3044 ccomma.append_expression (new CCodeConstant ("NULL"));
3046 var cassign = new CCodeAssignment (cvar, ccomma);
3048 // g_free (NULL) is allowed
3049 bool uses_gfree = (type.data_type != null && !type.data_type.is_reference_counting () && type.data_type.get_free_function () == "g_free");
3050 uses_gfree = uses_gfree || type is ArrayType;
3051 if (uses_gfree) {
3052 return cassign;
3055 return new CCodeConditionalExpression (cisnull, new CCodeConstant ("NULL"), cassign);
3058 public override void visit_end_full_expression (Expression expr) {
3059 /* expr is a full expression, i.e. an initializer, the
3060 * expression in an expression statement, the controlling
3061 * expression in if, while, for, or foreach statements
3063 * we unref temporary variables at the end of a full
3064 * expression
3066 if (((List<LocalVariable>) temp_ref_vars).size == 0) {
3067 /* nothing to do without temporary variables */
3068 return;
3071 var expr_list = new CCodeCommaExpression ();
3073 LocalVariable full_expr_var = null;
3075 var local_decl = expr.parent_node as LocalVariable;
3076 if (local_decl != null && has_simple_struct_initializer (local_decl)) {
3077 expr_list.append_expression (get_cvalue (expr));
3078 } else {
3079 var expr_type = expr.value_type;
3080 if (expr.target_type != null) {
3081 expr_type = expr.target_type;
3084 full_expr_var = get_temp_variable (expr_type, true, expr, false);
3085 emit_temp_var (full_expr_var);
3087 expr_list.append_expression (new CCodeAssignment (get_variable_cexpression (full_expr_var.name), get_cvalue (expr)));
3090 foreach (LocalVariable local in temp_ref_vars) {
3091 var ma = new MemberAccess.simple (local.name);
3092 ma.symbol_reference = local;
3093 ma.value_type = local.variable_type.copy ();
3094 visit_member_access (ma);
3095 expr_list.append_expression (get_unref_expression (get_variable_cexpression (local.name), local.variable_type, ma));
3098 if (full_expr_var != null) {
3099 expr_list.append_expression (get_variable_cexpression (full_expr_var.name));
3102 set_cvalue (expr, expr_list);
3104 temp_ref_vars.clear ();
3107 public void emit_temp_var (LocalVariable local) {
3108 var vardecl = new CCodeVariableDeclarator (local.name, null, local.variable_type.get_cdeclarator_suffix ());
3110 var st = local.variable_type.data_type as Struct;
3111 var array_type = local.variable_type as ArrayType;
3113 if (local.name.has_prefix ("*")) {
3114 // do not dereference unintialized variable
3115 // initialization is not needed for these special
3116 // pointer temp variables
3117 // used to avoid side-effects in assignments
3118 } else if (local.no_init) {
3119 // no initialization necessary for this temp var
3120 } else if (!local.variable_type.nullable &&
3121 (st != null && !st.is_simple_type ()) ||
3122 (array_type != null && array_type.fixed_length)) {
3123 // 0-initialize struct with struct initializer { 0 }
3124 // necessary as they will be passed by reference
3125 var clist = new CCodeInitializerList ();
3126 clist.append (new CCodeConstant ("0"));
3128 vardecl.initializer = clist;
3129 vardecl.init0 = true;
3130 } else if (local.variable_type.is_reference_type_or_type_parameter () ||
3131 local.variable_type.nullable ||
3132 local.variable_type is DelegateType) {
3133 vardecl.initializer = new CCodeConstant ("NULL");
3134 vardecl.init0 = true;
3137 if (current_method != null && current_method.coroutine) {
3138 closure_struct.add_field (local.variable_type.get_cname (), local.name);
3140 // even though closure struct is zerod, we need to initialize temporary variables
3141 // as they might be used multiple times when declared in a loop
3143 if (vardecl.initializer is CCodeInitializerList) {
3144 // C does not support initializer lists in assignments, use memset instead
3145 cfile.add_include ("string.h");
3146 var memset_call = new CCodeFunctionCall (new CCodeIdentifier ("memset"));
3147 memset_call.add_argument (new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, get_variable_cexpression (local.name)));
3148 memset_call.add_argument (new CCodeConstant ("0"));
3149 memset_call.add_argument (new CCodeIdentifier ("sizeof (%s)".printf (local.variable_type.get_cname ())));
3150 ccode.add_expression (memset_call);
3151 } else if (vardecl.initializer != null) {
3152 ccode.add_expression (new CCodeAssignment (get_variable_cexpression (local.name), vardecl.initializer));
3154 } else {
3155 ccode.add_declaration (local.variable_type.get_cname (), vardecl);
3159 public override void visit_expression_statement (ExpressionStatement stmt) {
3160 if (stmt.expression.error) {
3161 stmt.error = true;
3162 return;
3165 if (get_cvalue (stmt.expression) != null) {
3166 ccode.add_expression (get_cvalue (stmt.expression));
3169 /* free temporary objects and handle errors */
3171 foreach (LocalVariable local in temp_ref_vars) {
3172 var ma = new MemberAccess.simple (local.name);
3173 ma.symbol_reference = local;
3174 ma.value_type = local.variable_type.copy ();
3175 visit_member_access (ma);
3176 ccode.add_expression (get_unref_expression (get_variable_cexpression (local.name), local.variable_type, ma));
3179 if (stmt.tree_can_fail && stmt.expression.tree_can_fail) {
3180 // simple case, no node breakdown necessary
3181 add_simple_check (stmt.expression);
3184 temp_ref_vars.clear ();
3187 public virtual void append_local_free (Symbol sym, bool stop_at_loop = false) {
3188 var b = (Block) sym;
3190 var local_vars = b.get_local_variables ();
3191 // free in reverse order
3192 for (int i = local_vars.size - 1; i >= 0; i--) {
3193 var local = local_vars[i];
3194 if (!local.unreachable && local.active && !local.floating && !local.captured && requires_destroy (local.variable_type)) {
3195 var ma = new MemberAccess.simple (local.name);
3196 ma.symbol_reference = local;
3197 ma.value_type = local.variable_type.copy ();
3198 visit_member_access (ma);
3199 ccode.add_expression (get_unref_expression (get_variable_cexpression (local.name), local.variable_type, ma));
3203 if (b.captured) {
3204 int block_id = get_block_id (b);
3206 var data_unref = new CCodeFunctionCall (new CCodeIdentifier ("block%d_data_unref".printf (block_id)));
3207 data_unref.add_argument (get_variable_cexpression ("_data%d_".printf (block_id)));
3208 ccode.add_expression (data_unref);
3211 if (stop_at_loop) {
3212 if (b.parent_node is Loop ||
3213 b.parent_node is ForeachStatement ||
3214 b.parent_node is SwitchStatement) {
3215 return;
3219 if (sym.parent_symbol is Block) {
3220 append_local_free (sym.parent_symbol, stop_at_loop);
3221 } else if (sym.parent_symbol is Method) {
3222 append_param_free ((Method) sym.parent_symbol);
3226 public void append_error_free (Symbol sym, TryStatement current_try) {
3227 var b = (Block) sym;
3229 var local_vars = b.get_local_variables ();
3230 // free in reverse order
3231 for (int i = local_vars.size - 1; i >= 0; i--) {
3232 var local = local_vars[i];
3233 if (!local.unreachable && local.active && !local.floating && !local.captured && requires_destroy (local.variable_type)) {
3234 var ma = new MemberAccess.simple (local.name);
3235 ma.symbol_reference = local;
3236 ma.value_type = local.variable_type.copy ();
3237 visit_member_access (ma);
3238 ccode.add_expression (get_unref_expression (get_variable_cexpression (local.name), local.variable_type, ma));
3242 if (b.captured) {
3243 int block_id = get_block_id (b);
3245 var data_unref = new CCodeFunctionCall (new CCodeIdentifier ("block%d_data_unref".printf (block_id)));
3246 data_unref.add_argument (get_variable_cexpression ("_data%d_".printf (block_id)));
3247 ccode.add_expression (data_unref);
3250 if (sym == current_try.body) {
3251 return;
3254 if (sym.parent_symbol is Block) {
3255 append_error_free (sym.parent_symbol, current_try);
3256 } else if (sym.parent_symbol is Method) {
3257 append_param_free ((Method) sym.parent_symbol);
3261 private void append_param_free (Method m) {
3262 foreach (Parameter param in m.get_parameters ()) {
3263 if (!param.ellipsis && requires_destroy (param.variable_type) && param.direction == ParameterDirection.IN) {
3264 var ma = new MemberAccess.simple (param.name);
3265 ma.symbol_reference = param;
3266 ma.value_type = param.variable_type.copy ();
3267 visit_member_access (ma);
3268 ccode.add_expression (get_unref_expression (get_variable_cexpression (param.name), param.variable_type, ma));
3273 public bool variable_accessible_in_finally (LocalVariable local) {
3274 if (current_try == null) {
3275 return false;
3278 var sym = current_symbol;
3280 while (!(sym is Method || sym is PropertyAccessor) && sym.scope.lookup (local.name) == null) {
3281 if ((sym.parent_node is TryStatement && ((TryStatement) sym.parent_node).finally_body != null) ||
3282 (sym.parent_node is CatchClause && ((TryStatement) sym.parent_node.parent_node).finally_body != null)) {
3284 return true;
3287 sym = sym.parent_symbol;
3290 return false;
3293 void return_out_parameter (Parameter param) {
3294 var delegate_type = param.variable_type as DelegateType;
3296 ccode.open_if (get_variable_cexpression (param.name));
3297 ccode.add_expression (new CCodeAssignment (new CCodeUnaryExpression (CCodeUnaryOperator.POINTER_INDIRECTION, get_variable_cexpression (param.name)), get_variable_cexpression ("_" + param.name)));
3299 if (delegate_type != null && delegate_type.delegate_symbol.has_target) {
3300 ccode.add_expression (new CCodeAssignment (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)))));
3301 if (delegate_type.value_owned) {
3302 ccode.add_expression (new CCodeAssignment (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)))));
3306 if (param.variable_type.is_disposable ()){
3307 ccode.add_else ();
3308 var ma = new MemberAccess (null, param.name);
3309 ma.symbol_reference = param;
3310 ma.value_type = param.variable_type.copy ();
3311 visit_member_access (ma);
3312 ccode.add_expression (get_unref_expression (get_variable_cexpression ("_" + param.name), param.variable_type, ma));
3314 ccode.close ();
3316 var array_type = param.variable_type as ArrayType;
3317 if (array_type != null && !array_type.fixed_length && !param.no_array_length) {
3318 for (int dim = 1; dim <= array_type.rank; dim++) {
3319 ccode.open_if (get_variable_cexpression (get_parameter_array_length_cname (param, dim)));
3320 ccode.add_expression (new CCodeAssignment (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))));
3321 ccode.close ();
3326 public override void visit_return_statement (ReturnStatement stmt) {
3327 Symbol return_expression_symbol = null;
3329 if (stmt.return_expression != null) {
3330 // avoid unnecessary ref/unref pair
3331 var local = stmt.return_expression.symbol_reference as LocalVariable;
3332 if (current_return_type.value_owned
3333 && local != null && local.variable_type.value_owned
3334 && !local.captured
3335 && !variable_accessible_in_finally (local)) {
3336 /* return expression is local variable taking ownership and
3337 * current method is transferring ownership */
3339 return_expression_symbol = local;
3343 // return array length if appropriate
3344 if (((current_method != null && !current_method.no_array_length) || current_property_accessor != null) && current_return_type is ArrayType) {
3345 var return_expr_decl = get_temp_variable (stmt.return_expression.value_type, true, stmt, false);
3347 ccode.add_expression (new CCodeAssignment (get_variable_cexpression (return_expr_decl.name), get_cvalue (stmt.return_expression)));
3349 var array_type = (ArrayType) current_return_type;
3351 for (int dim = 1; dim <= array_type.rank; dim++) {
3352 var len_l = get_result_cexpression (get_array_length_cname ("result", dim));
3353 if (current_method == null || !current_method.coroutine) {
3354 len_l = new CCodeUnaryExpression (CCodeUnaryOperator.POINTER_INDIRECTION, len_l);
3356 var len_r = get_array_length_cexpression (stmt.return_expression, dim);
3357 ccode.add_expression (new CCodeAssignment (len_l, len_r));
3360 set_cvalue (stmt.return_expression, get_variable_cexpression (return_expr_decl.name));
3362 emit_temp_var (return_expr_decl);
3363 } else if ((current_method != null || current_property_accessor != null) && current_return_type is DelegateType) {
3364 var delegate_type = (DelegateType) current_return_type;
3365 if (delegate_type.delegate_symbol.has_target) {
3366 var return_expr_decl = get_temp_variable (stmt.return_expression.value_type, true, stmt, false);
3368 ccode.add_expression (new CCodeAssignment (get_variable_cexpression (return_expr_decl.name), get_cvalue (stmt.return_expression)));
3370 var target_l = get_result_cexpression (get_delegate_target_cname ("result"));
3371 if (current_method == null || !current_method.coroutine) {
3372 target_l = new CCodeUnaryExpression (CCodeUnaryOperator.POINTER_INDIRECTION, target_l);
3374 CCodeExpression target_r_destroy_notify;
3375 var target_r = get_delegate_target_cexpression (stmt.return_expression, out target_r_destroy_notify);
3376 ccode.add_expression (new CCodeAssignment (target_l, target_r));
3377 if (delegate_type.value_owned) {
3378 var target_l_destroy_notify = get_result_cexpression (get_delegate_target_destroy_notify_cname ("result"));
3379 if (current_method == null || !current_method.coroutine) {
3380 target_l_destroy_notify = new CCodeUnaryExpression (CCodeUnaryOperator.POINTER_INDIRECTION, target_l_destroy_notify);
3382 ccode.add_expression (new CCodeAssignment (target_l_destroy_notify, target_r_destroy_notify));
3385 set_cvalue (stmt.return_expression, get_variable_cexpression (return_expr_decl.name));
3387 emit_temp_var (return_expr_decl);
3391 if (stmt.return_expression != null) {
3392 // assign method result to `result'
3393 CCodeExpression result_lhs = get_result_cexpression ();
3394 if (current_return_type.is_real_non_null_struct_type () && (current_method == null || !current_method.coroutine)) {
3395 result_lhs = new CCodeUnaryExpression (CCodeUnaryOperator.POINTER_INDIRECTION, result_lhs);
3397 ccode.add_expression (new CCodeAssignment (result_lhs, get_cvalue (stmt.return_expression)));
3400 // free local variables
3401 append_local_free (current_symbol);
3403 if (current_method != null) {
3404 // check postconditions
3405 foreach (Expression postcondition in current_method.get_postconditions ()) {
3406 create_postcondition_statement (postcondition);
3410 if (current_method != null && !current_method.coroutine) {
3411 // assign values to output parameters if they are not NULL
3412 // otherwise, free the value if necessary
3413 foreach (var param in current_method.get_parameters ()) {
3414 if (param.direction != ParameterDirection.OUT) {
3415 continue;
3418 return_out_parameter (param);
3422 if (is_in_constructor ()) {
3423 ccode.add_return (new CCodeIdentifier ("obj"));
3424 } else if (is_in_destructor ()) {
3425 // do not call return as member cleanup and chain up to base finalizer
3426 // stil need to be executed
3427 ccode.add_goto ("_return");
3428 } else if (current_method is CreationMethod) {
3429 ccode.add_return (new CCodeIdentifier ("self"));
3430 } else if (current_method != null && current_method.coroutine) {
3431 } else if (current_return_type is VoidType || current_return_type.is_real_non_null_struct_type ()) {
3432 // structs are returned via out parameter
3433 ccode.add_return ();
3434 } else {
3435 ccode.add_return (new CCodeIdentifier ("result"));
3438 if (return_expression_symbol != null) {
3439 return_expression_symbol.active = true;
3442 // required for destructors
3443 current_method_return = true;
3446 public string get_symbol_lock_name (string symname) {
3447 return "__lock_%s".printf (symname);
3450 private CCodeExpression get_lock_expression (Statement stmt, Expression resource) {
3451 CCodeExpression l = null;
3452 var inner_node = ((MemberAccess)resource).inner;
3453 var member = resource.symbol_reference;
3454 var parent = (TypeSymbol)resource.symbol_reference.parent_symbol;
3456 if (member.is_instance_member ()) {
3457 if (inner_node == null) {
3458 l = new CCodeIdentifier ("self");
3459 } else if (resource.symbol_reference.parent_symbol != current_type_symbol) {
3460 l = generate_instance_cast (get_cvalue (inner_node), parent);
3461 } else {
3462 l = get_cvalue (inner_node);
3465 l = new CCodeMemberAccess.pointer (new CCodeMemberAccess.pointer (l, "priv"), get_symbol_lock_name (resource.symbol_reference.name));
3466 } else if (member.is_class_member ()) {
3467 CCodeExpression klass;
3469 if (current_method != null && current_method.binding == MemberBinding.INSTANCE ||
3470 current_property_accessor != null && current_property_accessor.prop.binding == MemberBinding.INSTANCE ||
3471 (in_constructor && !in_static_or_class_context)) {
3472 var k = new CCodeFunctionCall (new CCodeIdentifier ("G_OBJECT_GET_CLASS"));
3473 k.add_argument (new CCodeIdentifier ("self"));
3474 klass = k;
3475 } else {
3476 klass = new CCodeIdentifier ("klass");
3479 var get_class_private_call = new CCodeFunctionCall (new CCodeIdentifier ("%s_GET_CLASS_PRIVATE".printf(parent.get_upper_case_cname ())));
3480 get_class_private_call.add_argument (klass);
3481 l = new CCodeMemberAccess.pointer (get_class_private_call, get_symbol_lock_name (resource.symbol_reference.name));
3482 } else {
3483 string lock_name = "%s_%s".printf(parent.get_lower_case_cname (), resource.symbol_reference.name);
3484 l = new CCodeIdentifier (get_symbol_lock_name (lock_name));
3486 return l;
3489 public override void visit_lock_statement (LockStatement stmt) {
3490 var l = get_lock_expression (stmt, stmt.resource);
3492 var fc = new CCodeFunctionCall (new CCodeIdentifier (((Method) mutex_type.scope.lookup ("lock")).get_cname ()));
3493 fc.add_argument (new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, l));
3495 ccode.add_expression (fc);
3498 public override void visit_unlock_statement (UnlockStatement stmt) {
3499 var l = get_lock_expression (stmt, stmt.resource);
3501 var fc = new CCodeFunctionCall (new CCodeIdentifier (((Method) mutex_type.scope.lookup ("unlock")).get_cname ()));
3502 fc.add_argument (new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, l));
3504 ccode.add_expression (fc);
3507 public override void visit_delete_statement (DeleteStatement stmt) {
3508 var pointer_type = (PointerType) stmt.expression.value_type;
3509 DataType type = pointer_type;
3510 if (pointer_type.base_type.data_type != null && pointer_type.base_type.data_type.is_reference_type ()) {
3511 type = pointer_type.base_type;
3514 var ccall = new CCodeFunctionCall (get_destroy_func_expression (type));
3515 ccall.add_argument (get_cvalue (stmt.expression));
3516 ccode.add_expression (ccall);
3519 public override void visit_expression (Expression expr) {
3520 if (get_cvalue (expr) != null && !expr.lvalue) {
3521 if (expr.formal_value_type is GenericType && !(expr.value_type is GenericType)) {
3522 var st = expr.formal_value_type.type_parameter.parent_symbol.parent_symbol as Struct;
3523 if (expr.formal_value_type.type_parameter.parent_symbol != garray_type &&
3524 (st == null || st.get_cname () != "va_list")) {
3525 // GArray and va_list don't use pointer-based generics
3526 set_cvalue (expr, convert_from_generic_pointer (get_cvalue (expr), expr.value_type));
3530 // memory management, implicit casts, and boxing/unboxing
3531 set_cvalue (expr, transform_expression (get_cvalue (expr), expr.value_type, expr.target_type, expr));
3533 if (expr.formal_target_type is GenericType && !(expr.target_type is GenericType)) {
3534 if (expr.formal_target_type.type_parameter.parent_symbol != garray_type) {
3535 // GArray doesn't use pointer-based generics
3536 set_cvalue (expr, convert_to_generic_pointer (get_cvalue (expr), expr.target_type));
3542 public override void visit_boolean_literal (BooleanLiteral expr) {
3543 if (context.profile == Profile.GOBJECT) {
3544 set_cvalue (expr, new CCodeConstant (expr.value ? "TRUE" : "FALSE"));
3545 } else {
3546 cfile.add_include ("stdbool.h");
3547 set_cvalue (expr, new CCodeConstant (expr.value ? "true" : "false"));
3551 public override void visit_character_literal (CharacterLiteral expr) {
3552 if (expr.get_char () >= 0x20 && expr.get_char () < 0x80) {
3553 set_cvalue (expr, new CCodeConstant (expr.value));
3554 } else {
3555 set_cvalue (expr, new CCodeConstant ("%uU".printf (expr.get_char ())));
3559 public override void visit_integer_literal (IntegerLiteral expr) {
3560 set_cvalue (expr, new CCodeConstant (expr.value + expr.type_suffix));
3563 public override void visit_real_literal (RealLiteral expr) {
3564 string c_literal = expr.value;
3565 if (c_literal.has_suffix ("d") || c_literal.has_suffix ("D")) {
3566 // there is no suffix for double in C
3567 c_literal = c_literal.substring (0, c_literal.length - 1);
3569 if (!("." in c_literal || "e" in c_literal || "E" in c_literal)) {
3570 // C requires period or exponent part for floating constants
3571 if ("f" in c_literal || "F" in c_literal) {
3572 c_literal = c_literal.substring (0, c_literal.length - 1) + ".f";
3573 } else {
3574 c_literal += ".";
3577 set_cvalue (expr, new CCodeConstant (c_literal));
3580 public override void visit_string_literal (StringLiteral expr) {
3581 set_cvalue (expr, new CCodeConstant.string (expr.value.replace ("\n", "\\n")));
3584 public override void visit_regex_literal (RegexLiteral expr) {
3585 string[] parts = expr.value.split ("/", 3);
3586 string re = parts[2].escape ("");
3587 string flags = "0";
3589 if (parts[1].contains ("i")) {
3590 flags += " | G_REGEX_CASELESS";
3592 if (parts[1].contains ("m")) {
3593 flags += " | G_REGEX_MULTILINE";
3595 if (parts[1].contains ("s")) {
3596 flags += " | G_REGEX_DOTALL";
3598 if (parts[1].contains ("x")) {
3599 flags += " | G_REGEX_EXTENDED";
3602 var regex_var = get_temp_variable (regex_type, true, expr, false);
3603 emit_temp_var (regex_var);
3605 var cdecl = new CCodeDeclaration ("GRegex*");
3607 var cname = regex_var.name + "regex_" + next_regex_id.to_string ();
3608 if (this.next_regex_id == 0) {
3609 var fun = new CCodeFunction ("_thread_safe_regex_init", "GRegex*");
3610 fun.modifiers = CCodeModifiers.STATIC | CCodeModifiers.INLINE;
3611 fun.add_parameter (new CCodeParameter ("re", "GRegex**"));
3612 fun.add_parameter (new CCodeParameter ("pattern", "const gchar *"));
3613 fun.add_parameter (new CCodeParameter ("match_options", "GRegexMatchFlags"));
3615 push_function (fun);
3617 var once_enter_call = new CCodeFunctionCall (new CCodeIdentifier ("g_once_init_enter"));
3618 once_enter_call.add_argument (new CCodeConstant ("(volatile gsize*) re"));
3619 ccode.open_if (once_enter_call);
3621 var regex_new_call = new CCodeFunctionCall (new CCodeIdentifier ("g_regex_new"));
3622 regex_new_call.add_argument (new CCodeConstant ("pattern"));
3623 regex_new_call.add_argument (new CCodeConstant ("match_options"));
3624 regex_new_call.add_argument (new CCodeConstant ("0"));
3625 regex_new_call.add_argument (new CCodeConstant ("NULL"));
3626 ccode.add_expression (new CCodeAssignment (new CCodeIdentifier ("GRegex* val"), regex_new_call));
3628 var once_leave_call = new CCodeFunctionCall (new CCodeIdentifier ("g_once_init_leave"));
3629 once_leave_call.add_argument (new CCodeConstant ("(volatile gsize*) re"));
3630 once_leave_call.add_argument (new CCodeConstant ("(gsize) val"));
3631 ccode.add_expression (once_leave_call);
3633 ccode.close ();
3635 ccode.add_return (new CCodeIdentifier ("*re"));
3637 pop_function ();
3639 cfile.add_function (fun);
3641 this.next_regex_id++;
3643 cdecl.add_declarator (new CCodeVariableDeclarator (cname + " = NULL"));
3644 cdecl.modifiers = CCodeModifiers.STATIC;
3646 var regex_const = new CCodeConstant ("_thread_safe_regex_init (&%s, \"%s\", %s)".printf (cname, re, flags));
3648 cfile.add_constant_declaration (cdecl);
3649 set_cvalue (expr, regex_const);
3652 public override void visit_null_literal (NullLiteral expr) {
3653 if (context.profile != Profile.GOBJECT) {
3654 cfile.add_include ("stddef.h");
3656 set_cvalue (expr, new CCodeConstant ("NULL"));
3658 var array_type = expr.target_type as ArrayType;
3659 var delegate_type = expr.target_type as DelegateType;
3660 if (array_type != null) {
3661 for (int dim = 1; dim <= array_type.rank; dim++) {
3662 append_array_size (expr, new CCodeConstant ("0"));
3664 } else if (delegate_type != null && delegate_type.delegate_symbol.has_target) {
3665 set_delegate_target (expr, new CCodeConstant ("NULL"));
3666 set_delegate_target_destroy_notify (expr, new CCodeConstant ("NULL"));
3670 public virtual string get_delegate_target_cname (string delegate_cname) {
3671 assert_not_reached ();
3674 public virtual CCodeExpression get_delegate_target_cexpression (Expression delegate_expr, out CCodeExpression delegate_target_destroy_notify) {
3675 assert_not_reached ();
3678 public virtual CCodeExpression get_delegate_target_cvalue (TargetValue value) {
3679 return new CCodeInvalidExpression ();
3682 public virtual CCodeExpression get_delegate_target_destroy_notify_cvalue (TargetValue value) {
3683 return new CCodeInvalidExpression ();
3686 public virtual string get_delegate_target_destroy_notify_cname (string delegate_cname) {
3687 assert_not_reached ();
3690 public override void visit_base_access (BaseAccess expr) {
3691 CCodeExpression this_access;
3692 if (current_method != null && current_method.coroutine) {
3693 // use closure
3694 this_access = new CCodeMemberAccess.pointer (new CCodeIdentifier ("data"), "self");
3695 } else {
3696 this_access = new CCodeIdentifier ("self");
3699 set_cvalue (expr, generate_instance_cast (this_access, expr.value_type.data_type));
3702 public override void visit_postfix_expression (PostfixExpression expr) {
3703 MemberAccess ma = find_property_access (expr.inner);
3704 if (ma != null) {
3705 // property postfix expression
3706 var prop = (Property) ma.symbol_reference;
3708 // assign current value to temp variable
3709 var temp_decl = get_temp_variable (prop.property_type, true, expr, false);
3710 emit_temp_var (temp_decl);
3711 ccode.add_expression (new CCodeAssignment (get_variable_cexpression (temp_decl.name), get_cvalue (expr.inner)));
3713 // increment/decrement property
3714 var op = expr.increment ? CCodeBinaryOperator.PLUS : CCodeBinaryOperator.MINUS;
3715 var cexpr = new CCodeBinaryExpression (op, get_variable_cexpression (temp_decl.name), new CCodeConstant ("1"));
3716 store_property (prop, ma, cexpr);
3718 // return previous value
3719 set_cvalue (expr, get_variable_cexpression (temp_decl.name));
3720 return;
3723 var op = expr.increment ? CCodeUnaryOperator.POSTFIX_INCREMENT : CCodeUnaryOperator.POSTFIX_DECREMENT;
3725 set_cvalue (expr, new CCodeUnaryExpression (op, get_cvalue (expr.inner)));
3728 private MemberAccess? find_property_access (Expression expr) {
3729 if (!(expr is MemberAccess)) {
3730 return null;
3733 var ma = (MemberAccess) expr;
3734 if (ma.symbol_reference is Property) {
3735 return ma;
3738 return null;
3741 bool is_limited_generic_type (DataType type) {
3742 var cl = type.type_parameter.parent_symbol as Class;
3743 var st = type.type_parameter.parent_symbol as Struct;
3744 if ((cl != null && cl.is_compact) || st != null) {
3745 // compact classes and structs only
3746 // have very limited generics support
3747 return true;
3749 return false;
3752 public bool requires_copy (DataType type) {
3753 if (!type.is_disposable ()) {
3754 return false;
3757 var cl = type.data_type as Class;
3758 if (cl != null && cl.is_reference_counting ()
3759 && cl.get_ref_function () == "") {
3760 // empty ref_function => no ref necessary
3761 return false;
3764 if (type.type_parameter != null) {
3765 if (is_limited_generic_type (type)) {
3766 return false;
3770 return true;
3773 public bool requires_destroy (DataType type) {
3774 if (!type.is_disposable ()) {
3775 return false;
3778 var array_type = type as ArrayType;
3779 if (array_type != null && array_type.fixed_length) {
3780 return requires_destroy (array_type.element_type);
3783 var cl = type.data_type as Class;
3784 if (cl != null && cl.is_reference_counting ()
3785 && cl.get_unref_function () == "") {
3786 // empty unref_function => no unref necessary
3787 return false;
3790 if (type.type_parameter != null) {
3791 if (is_limited_generic_type (type)) {
3792 return false;
3796 return true;
3799 bool is_ref_function_void (DataType type) {
3800 var cl = type.data_type as Class;
3801 if (cl != null && cl.ref_function_void) {
3802 return true;
3803 } else {
3804 return false;
3808 public virtual CCodeExpression? get_ref_cexpression (DataType expression_type, CCodeExpression cexpr, Expression? expr, CodeNode node) {
3809 if (expression_type is DelegateType) {
3810 return cexpr;
3813 if (expression_type is ValueType && !expression_type.nullable) {
3814 // normal value type, no null check
3815 // (copy (&expr, &temp), temp)
3817 var decl = get_temp_variable (expression_type, false, node);
3818 emit_temp_var (decl);
3820 var ctemp = get_variable_cexpression (decl.name);
3822 var vt = (ValueType) expression_type;
3823 var st = (Struct) vt.type_symbol;
3824 var copy_call = new CCodeFunctionCall (new CCodeIdentifier (st.get_copy_function ()));
3825 copy_call.add_argument (new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, cexpr));
3826 copy_call.add_argument (new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, ctemp));
3828 if (!st.has_copy_function) {
3829 generate_struct_copy_function (st);
3832 var ccomma = new CCodeCommaExpression ();
3834 if (st.get_copy_function () == "g_value_copy") {
3835 // GValue requires g_value_init in addition to g_value_copy
3837 var value_type_call = new CCodeFunctionCall (new CCodeIdentifier ("G_VALUE_TYPE"));
3838 value_type_call.add_argument (new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, cexpr));
3840 var init_call = new CCodeFunctionCall (new CCodeIdentifier ("g_value_init"));
3841 init_call.add_argument (new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, ctemp));
3842 init_call.add_argument (value_type_call);
3844 ccomma.append_expression (init_call);
3847 ccomma.append_expression (copy_call);
3848 ccomma.append_expression (ctemp);
3850 if (gvalue_type != null && expression_type.data_type == gvalue_type) {
3851 // g_value_init/copy must not be called for uninitialized values
3852 var cisvalid = new CCodeFunctionCall (new CCodeIdentifier ("G_IS_VALUE"));
3853 cisvalid.add_argument (new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, cexpr));
3855 return new CCodeConditionalExpression (cisvalid, ccomma, cexpr);
3856 } else {
3857 return ccomma;
3861 /* (temp = expr, temp == NULL ? NULL : ref (temp))
3863 * can be simplified to
3864 * ref (expr)
3865 * if static type of expr is non-null
3868 var dupexpr = get_dup_func_expression (expression_type, node.source_reference);
3870 if (dupexpr == null) {
3871 node.error = true;
3872 return null;
3875 if (dupexpr is CCodeIdentifier && !(expression_type is ArrayType) && !(expression_type is GenericType) && !is_ref_function_void (expression_type)) {
3876 // generate and call NULL-aware ref function to reduce number
3877 // of temporary variables and simplify code
3879 var dupid = (CCodeIdentifier) dupexpr;
3880 string dup0_func = "_%s0".printf (dupid.name);
3882 // g_strdup is already NULL-safe
3883 if (dupid.name == "g_strdup") {
3884 dup0_func = dupid.name;
3885 } else if (add_wrapper (dup0_func)) {
3886 string pointer_cname = "gpointer";
3887 if (context.profile == Profile.POSIX) {
3888 pointer_cname = "void*";
3890 var dup0_fun = new CCodeFunction (dup0_func, pointer_cname);
3891 dup0_fun.add_parameter (new CCodeParameter ("self", pointer_cname));
3892 dup0_fun.modifiers = CCodeModifiers.STATIC;
3894 push_function (dup0_fun);
3896 var dup_call = new CCodeFunctionCall (dupexpr);
3897 dup_call.add_argument (new CCodeIdentifier ("self"));
3899 ccode.add_return (new CCodeConditionalExpression (new CCodeIdentifier ("self"), dup_call, new CCodeConstant ("NULL")));
3901 pop_function ();
3903 cfile.add_function (dup0_fun);
3906 var ccall = new CCodeFunctionCall (new CCodeIdentifier (dup0_func));
3907 ccall.add_argument (cexpr);
3908 return ccall;
3911 var ccall = new CCodeFunctionCall (dupexpr);
3913 if (!(expression_type is ArrayType) && expr != null && expr.is_non_null ()
3914 && !is_ref_function_void (expression_type)) {
3915 // expression is non-null
3916 ccall.add_argument (get_cvalue (expr));
3918 return ccall;
3919 } else {
3920 var decl = get_temp_variable (expression_type, false, node, false);
3921 emit_temp_var (decl);
3923 var ctemp = get_variable_cexpression (decl.name);
3925 var cisnull = new CCodeBinaryExpression (CCodeBinaryOperator.EQUALITY, ctemp, new CCodeConstant ("NULL"));
3926 if (expression_type.type_parameter != null) {
3927 // dup functions are optional for type parameters
3928 var cdupisnull = new CCodeBinaryExpression (CCodeBinaryOperator.EQUALITY, get_dup_func_expression (expression_type, node.source_reference), new CCodeConstant ("NULL"));
3929 cisnull = new CCodeBinaryExpression (CCodeBinaryOperator.OR, cisnull, cdupisnull);
3932 if (expression_type.type_parameter != null) {
3933 // cast from gconstpointer to gpointer as GBoxedCopyFunc expects gpointer
3934 ccall.add_argument (new CCodeCastExpression (ctemp, "gpointer"));
3935 } else {
3936 ccall.add_argument (ctemp);
3939 if (expression_type is ArrayType) {
3940 var array_type = (ArrayType) expression_type;
3941 bool first = true;
3942 CCodeExpression csizeexpr = null;
3943 for (int dim = 1; dim <= array_type.rank; dim++) {
3944 if (first) {
3945 csizeexpr = get_array_length_cexpression (expr, dim);
3946 first = false;
3947 } else {
3948 csizeexpr = new CCodeBinaryExpression (CCodeBinaryOperator.MUL, csizeexpr, get_array_length_cexpression (expr, dim));
3952 ccall.add_argument (csizeexpr);
3954 if (array_type.element_type is GenericType) {
3955 var elem_dupexpr = get_dup_func_expression (array_type.element_type, node.source_reference);
3956 if (elem_dupexpr == null) {
3957 elem_dupexpr = new CCodeConstant ("NULL");
3959 ccall.add_argument (elem_dupexpr);
3963 var ccomma = new CCodeCommaExpression ();
3964 ccomma.append_expression (new CCodeAssignment (ctemp, cexpr));
3966 CCodeExpression cifnull;
3967 if (expression_type.data_type != null) {
3968 cifnull = new CCodeConstant ("NULL");
3969 } else {
3970 // the value might be non-null even when the dup function is null,
3971 // so we may not just use NULL for type parameters
3973 // cast from gconstpointer to gpointer as methods in
3974 // generic classes may not return gconstpointer
3975 cifnull = new CCodeCastExpression (ctemp, "gpointer");
3977 ccomma.append_expression (new CCodeConditionalExpression (cisnull, cifnull, ccall));
3979 // repeat temp variable at the end of the comma expression
3980 // if the ref function returns void
3981 if (is_ref_function_void (expression_type)) {
3982 ccomma.append_expression (ctemp);
3985 return ccomma;
3989 bool is_reference_type_argument (DataType type_arg) {
3990 if (type_arg is ErrorType || (type_arg.data_type != null && type_arg.data_type.is_reference_type ())) {
3991 return true;
3992 } else {
3993 return false;
3997 bool is_nullable_value_type_argument (DataType type_arg) {
3998 if (type_arg is ValueType && type_arg.nullable) {
3999 return true;
4000 } else {
4001 return false;
4005 bool is_signed_integer_type_argument (DataType type_arg) {
4006 var st = type_arg.data_type as Struct;
4007 if (type_arg.nullable) {
4008 return false;
4009 } else if (st == bool_type.data_type) {
4010 return true;
4011 } else if (st == char_type.data_type) {
4012 return true;
4013 } else if (unichar_type != null && st == unichar_type.data_type) {
4014 return true;
4015 } else if (st == short_type.data_type) {
4016 return true;
4017 } else if (st == int_type.data_type) {
4018 return true;
4019 } else if (st == long_type.data_type) {
4020 return true;
4021 } else if (st == int8_type.data_type) {
4022 return true;
4023 } else if (st == int16_type.data_type) {
4024 return true;
4025 } else if (st == int32_type.data_type) {
4026 return true;
4027 } else if (st == gtype_type) {
4028 return true;
4029 } else if (type_arg is EnumValueType) {
4030 return true;
4031 } else {
4032 return false;
4036 bool is_unsigned_integer_type_argument (DataType type_arg) {
4037 var st = type_arg.data_type as Struct;
4038 if (type_arg.nullable) {
4039 return false;
4040 } else if (st == uchar_type.data_type) {
4041 return true;
4042 } else if (st == ushort_type.data_type) {
4043 return true;
4044 } else if (st == uint_type.data_type) {
4045 return true;
4046 } else if (st == ulong_type.data_type) {
4047 return true;
4048 } else if (st == uint8_type.data_type) {
4049 return true;
4050 } else if (st == uint16_type.data_type) {
4051 return true;
4052 } else if (st == uint32_type.data_type) {
4053 return true;
4054 } else {
4055 return false;
4059 public void check_type (DataType type) {
4060 var array_type = type as ArrayType;
4061 if (array_type != null) {
4062 check_type (array_type.element_type);
4063 if (array_type.element_type is ArrayType) {
4064 Report.error (type.source_reference, "Stacked arrays are not supported");
4065 } else if (array_type.element_type is DelegateType) {
4066 var delegate_type = (DelegateType) array_type.element_type;
4067 if (delegate_type.delegate_symbol.has_target) {
4068 Report.error (type.source_reference, "Delegates with target are not supported as array element type");
4072 foreach (var type_arg in type.get_type_arguments ()) {
4073 check_type (type_arg);
4074 check_type_argument (type_arg);
4078 void check_type_argument (DataType type_arg) {
4079 if (type_arg is GenericType
4080 || type_arg is PointerType
4081 || is_reference_type_argument (type_arg)
4082 || is_nullable_value_type_argument (type_arg)
4083 || is_signed_integer_type_argument (type_arg)
4084 || is_unsigned_integer_type_argument (type_arg)) {
4085 // no error
4086 } else if (type_arg is DelegateType) {
4087 var delegate_type = (DelegateType) type_arg;
4088 if (delegate_type.delegate_symbol.has_target) {
4089 Report.error (type_arg.source_reference, "Delegates with target are not supported as generic type arguments");
4091 } else {
4092 Report.error (type_arg.source_reference, "`%s' is not a supported generic type argument, use `?' to box value types".printf (type_arg.to_string ()));
4096 public virtual void generate_class_declaration (Class cl, CCodeFile decl_space) {
4097 if (add_symbol_declaration (decl_space, cl, cl.get_cname ())) {
4098 return;
4102 public virtual void generate_interface_declaration (Interface iface, CCodeFile decl_space) {
4105 public virtual void generate_method_declaration (Method m, CCodeFile decl_space) {
4108 public virtual void generate_error_domain_declaration (ErrorDomain edomain, CCodeFile decl_space) {
4111 public void add_generic_type_arguments (Map<int,CCodeExpression> arg_map, List<DataType> type_args, CodeNode expr, bool is_chainup = false) {
4112 int type_param_index = 0;
4113 foreach (var type_arg in type_args) {
4114 arg_map.set (get_param_pos (0.1 * type_param_index + 0.01), get_type_id_expression (type_arg, is_chainup));
4115 if (requires_copy (type_arg)) {
4116 var dup_func = get_dup_func_expression (type_arg, type_arg.source_reference, is_chainup);
4117 if (dup_func == null) {
4118 // type doesn't contain a copy function
4119 expr.error = true;
4120 return;
4122 arg_map.set (get_param_pos (0.1 * type_param_index + 0.02), new CCodeCastExpression (dup_func, "GBoxedCopyFunc"));
4123 arg_map.set (get_param_pos (0.1 * type_param_index + 0.03), get_destroy_func_expression (type_arg, is_chainup));
4124 } else {
4125 arg_map.set (get_param_pos (0.1 * type_param_index + 0.02), new CCodeConstant ("NULL"));
4126 arg_map.set (get_param_pos (0.1 * type_param_index + 0.03), new CCodeConstant ("NULL"));
4128 type_param_index++;
4132 public override void visit_object_creation_expression (ObjectCreationExpression expr) {
4133 CCodeExpression instance = null;
4134 CCodeExpression creation_expr = null;
4136 check_type (expr.type_reference);
4138 var st = expr.type_reference.data_type as Struct;
4139 if ((st != null && (!st.is_simple_type () || st.get_cname () == "va_list")) || expr.get_object_initializer ().size > 0) {
4140 // value-type initialization or object creation expression with object initializer
4142 var local = expr.parent_node as LocalVariable;
4143 if (local != null && has_simple_struct_initializer (local)) {
4144 if (local.captured) {
4145 var block = (Block) local.parent_symbol;
4146 instance = new CCodeMemberAccess.pointer (get_variable_cexpression ("_data%d_".printf (get_block_id (block))), get_variable_cname (local.name));
4147 } else {
4148 instance = get_variable_cexpression (get_variable_cname (local.name));
4150 } else {
4151 var temp_decl = get_temp_variable (expr.type_reference, false, expr);
4152 emit_temp_var (temp_decl);
4154 instance = get_variable_cexpression (get_variable_cname (temp_decl.name));
4158 if (expr.symbol_reference == null) {
4159 // no creation method
4160 if (expr.type_reference.data_type is Struct) {
4161 // memset needs string.h
4162 cfile.add_include ("string.h");
4163 var creation_call = new CCodeFunctionCall (new CCodeIdentifier ("memset"));
4164 creation_call.add_argument (new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, instance));
4165 creation_call.add_argument (new CCodeConstant ("0"));
4166 creation_call.add_argument (new CCodeIdentifier ("sizeof (%s)".printf (expr.type_reference.get_cname ())));
4168 creation_expr = creation_call;
4170 } else if (expr.type_reference.data_type == glist_type ||
4171 expr.type_reference.data_type == gslist_type) {
4172 // NULL is an empty list
4173 set_cvalue (expr, new CCodeConstant ("NULL"));
4174 } else if (expr.symbol_reference is Method) {
4175 // use creation method
4176 var m = (Method) expr.symbol_reference;
4177 var params = m.get_parameters ();
4178 CCodeFunctionCall creation_call;
4180 generate_method_declaration (m, cfile);
4182 var cl = expr.type_reference.data_type as Class;
4184 if (!m.has_new_function) {
4185 // use construct function directly
4186 creation_call = new CCodeFunctionCall (new CCodeIdentifier (m.get_real_cname ()));
4187 creation_call.add_argument (new CCodeIdentifier (cl.get_type_id ()));
4188 } else {
4189 creation_call = new CCodeFunctionCall (new CCodeIdentifier (m.get_cname ()));
4192 if ((st != null && !st.is_simple_type ()) && !(m.cinstance_parameter_position < 0)) {
4193 creation_call.add_argument (new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, instance));
4194 } else if (st != null && st.get_cname () == "va_list") {
4195 creation_call.add_argument (instance);
4196 if (m.get_cname () == "va_start") {
4197 Parameter last_param = null;
4198 foreach (var param in current_method.get_parameters ()) {
4199 if (param.ellipsis) {
4200 break;
4202 last_param = param;
4204 creation_call.add_argument (new CCodeIdentifier (get_variable_cname (last_param.name)));
4208 generate_type_declaration (expr.type_reference, cfile);
4210 var carg_map = new HashMap<int,CCodeExpression> (direct_hash, direct_equal);
4212 if (cl != null && !cl.is_compact) {
4213 add_generic_type_arguments (carg_map, expr.type_reference.get_type_arguments (), expr);
4214 } else if (cl != null && m.simple_generics) {
4215 int type_param_index = 0;
4216 foreach (var type_arg in expr.type_reference.get_type_arguments ()) {
4217 if (requires_copy (type_arg)) {
4218 carg_map.set (get_param_pos (-1 + 0.1 * type_param_index + 0.03), get_destroy0_func_expression (type_arg));
4219 } else {
4220 carg_map.set (get_param_pos (-1 + 0.1 * type_param_index + 0.03), new CCodeConstant ("NULL"));
4222 type_param_index++;
4226 bool ellipsis = false;
4228 int i = 1;
4229 int arg_pos;
4230 Iterator<Parameter> params_it = params.iterator ();
4231 foreach (Expression arg in expr.get_argument_list ()) {
4232 CCodeExpression cexpr = get_cvalue (arg);
4233 Parameter param = null;
4234 if (params_it.next ()) {
4235 param = params_it.get ();
4236 ellipsis = param.ellipsis;
4237 if (!ellipsis) {
4238 if (!param.no_array_length && param.variable_type is ArrayType) {
4239 var array_type = (ArrayType) param.variable_type;
4240 for (int dim = 1; dim <= array_type.rank; dim++) {
4241 carg_map.set (get_param_pos (param.carray_length_parameter_position + 0.01 * dim), get_array_length_cexpression (arg, dim));
4243 } else if (param.variable_type is DelegateType) {
4244 var deleg_type = (DelegateType) param.variable_type;
4245 var d = deleg_type.delegate_symbol;
4246 if (d.has_target) {
4247 CCodeExpression delegate_target_destroy_notify;
4248 var delegate_target = get_delegate_target_cexpression (arg, out delegate_target_destroy_notify);
4249 carg_map.set (get_param_pos (param.cdelegate_target_parameter_position), delegate_target);
4250 if (deleg_type.value_owned) {
4251 carg_map.set (get_param_pos (param.cdelegate_target_parameter_position + 0.01), delegate_target_destroy_notify);
4256 cexpr = handle_struct_argument (param, arg, cexpr);
4258 if (param.ctype != null) {
4259 cexpr = new CCodeCastExpression (cexpr, param.ctype);
4261 } else {
4262 cexpr = handle_struct_argument (null, arg, cexpr);
4265 arg_pos = get_param_pos (param.cparameter_position, ellipsis);
4266 } else {
4267 // default argument position
4268 cexpr = handle_struct_argument (null, arg, cexpr);
4269 arg_pos = get_param_pos (i, ellipsis);
4272 carg_map.set (arg_pos, cexpr);
4274 i++;
4276 while (params_it.next ()) {
4277 var param = params_it.get ();
4279 if (param.ellipsis) {
4280 ellipsis = true;
4281 break;
4284 if (param.initializer == null) {
4285 Report.error (expr.source_reference, "no default expression for argument %d".printf (i));
4286 return;
4289 /* evaluate default expression here as the code
4290 * generator might not have visited the formal
4291 * parameter yet */
4292 param.initializer.emit (this);
4294 carg_map.set (get_param_pos (param.cparameter_position), get_cvalue (param.initializer));
4295 i++;
4298 // append C arguments in the right order
4299 int last_pos = -1;
4300 int min_pos;
4301 while (true) {
4302 min_pos = -1;
4303 foreach (int pos in carg_map.get_keys ()) {
4304 if (pos > last_pos && (min_pos == -1 || pos < min_pos)) {
4305 min_pos = pos;
4308 if (min_pos == -1) {
4309 break;
4311 creation_call.add_argument (carg_map.get (min_pos));
4312 last_pos = min_pos;
4315 if ((st != null && !st.is_simple_type ()) && m.cinstance_parameter_position < 0) {
4316 // instance parameter is at the end in a struct creation method
4317 creation_call.add_argument (new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, instance));
4320 if (expr.tree_can_fail) {
4321 // method can fail
4322 current_method_inner_error = true;
4323 creation_call.add_argument (new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, get_variable_cexpression ("_inner_error_")));
4326 if (ellipsis) {
4327 /* ensure variable argument list ends with NULL
4328 * except when using printf-style arguments */
4329 if (!m.printf_format && !m.scanf_format && m.sentinel != "") {
4330 creation_call.add_argument (new CCodeConstant (m.sentinel));
4334 creation_expr = creation_call;
4336 // cast the return value of the creation method back to the intended type if
4337 // it requested a special C return type
4338 if (get_custom_creturn_type (m) != null) {
4339 creation_expr = new CCodeCastExpression (creation_expr, expr.type_reference.get_cname ());
4341 } else if (expr.symbol_reference is ErrorCode) {
4342 var ecode = (ErrorCode) expr.symbol_reference;
4343 var edomain = (ErrorDomain) ecode.parent_symbol;
4344 CCodeFunctionCall creation_call;
4346 generate_error_domain_declaration (edomain, cfile);
4348 if (expr.get_argument_list ().size == 1) {
4349 // must not be a format argument
4350 creation_call = new CCodeFunctionCall (new CCodeIdentifier ("g_error_new_literal"));
4351 } else {
4352 creation_call = new CCodeFunctionCall (new CCodeIdentifier ("g_error_new"));
4354 creation_call.add_argument (new CCodeIdentifier (edomain.get_upper_case_cname ()));
4355 creation_call.add_argument (new CCodeIdentifier (ecode.get_cname ()));
4357 foreach (Expression arg in expr.get_argument_list ()) {
4358 creation_call.add_argument (get_cvalue (arg));
4361 creation_expr = creation_call;
4362 } else {
4363 assert (false);
4366 var local = expr.parent_node as LocalVariable;
4367 if (local != null && has_simple_struct_initializer (local)) {
4368 // no temporary variable necessary
4369 set_cvalue (expr, creation_expr);
4370 return;
4371 } else if (instance != null) {
4372 if (expr.type_reference.data_type is Struct) {
4373 ccode.add_expression (creation_expr);
4374 } else {
4375 ccode.add_expression (new CCodeAssignment (instance, creation_expr));
4378 foreach (MemberInitializer init in expr.get_object_initializer ()) {
4379 if (init.symbol_reference is Field) {
4380 var f = (Field) init.symbol_reference;
4381 var instance_target_type = get_data_type_for_symbol ((TypeSymbol) f.parent_symbol);
4382 var typed_inst = transform_expression (instance, expr.type_reference, instance_target_type);
4383 CCodeExpression lhs;
4384 if (expr.type_reference.data_type is Struct) {
4385 lhs = new CCodeMemberAccess (typed_inst, f.get_cname ());
4386 } else {
4387 lhs = new CCodeMemberAccess.pointer (typed_inst, f.get_cname ());
4389 ccode.add_expression (new CCodeAssignment (lhs, get_cvalue (init.initializer)));
4391 if (f.variable_type is ArrayType && !f.no_array_length) {
4392 var array_type = (ArrayType) f.variable_type;
4393 for (int dim = 1; dim <= array_type.rank; dim++) {
4394 if (expr.type_reference.data_type is Struct) {
4395 lhs = new CCodeMemberAccess (typed_inst, get_array_length_cname (f.get_cname (), dim));
4396 } else {
4397 lhs = new CCodeMemberAccess.pointer (typed_inst, get_array_length_cname (f.get_cname (), dim));
4399 var rhs_array_len = get_array_length_cexpression (init.initializer, dim);
4400 ccode.add_expression (new CCodeAssignment (lhs, rhs_array_len));
4402 } else if (f.variable_type is DelegateType && (f.variable_type as DelegateType).delegate_symbol.has_target && !f.no_delegate_target) {
4403 if (expr.type_reference.data_type is Struct) {
4404 lhs = new CCodeMemberAccess (typed_inst, get_delegate_target_cname (f.get_cname ()));
4405 } else {
4406 lhs = new CCodeMemberAccess.pointer (typed_inst, get_delegate_target_cname (f.get_cname ()));
4408 CCodeExpression rhs_delegate_target_destroy_notify;
4409 var rhs_delegate_target = get_delegate_target_cexpression (init.initializer, out rhs_delegate_target_destroy_notify);
4410 ccode.add_expression (new CCodeAssignment (lhs, rhs_delegate_target));
4413 var cl = f.parent_symbol as Class;
4414 if (cl != null) {
4415 generate_class_struct_declaration (cl, cfile);
4417 } else if (init.symbol_reference is Property) {
4418 var inst_ma = new MemberAccess.simple ("new");
4419 inst_ma.value_type = expr.type_reference;
4420 set_cvalue (inst_ma, instance);
4421 var ma = new MemberAccess (inst_ma, init.name);
4422 store_property ((Property) init.symbol_reference, ma, get_cvalue (init.initializer));
4426 creation_expr = instance;
4429 if (creation_expr != null) {
4430 var temp_var = get_temp_variable (expr.value_type);
4431 var temp_ref = get_variable_cexpression (temp_var.name);
4433 emit_temp_var (temp_var);
4435 ccode.add_expression (new CCodeAssignment (temp_ref, creation_expr));
4436 set_cvalue (expr, temp_ref);
4440 public CCodeExpression? handle_struct_argument (Parameter? param, Expression arg, CCodeExpression? cexpr) {
4441 DataType type;
4442 if (param != null) {
4443 type = param.variable_type;
4444 } else {
4445 // varargs
4446 type = arg.value_type;
4449 // pass non-simple struct instances always by reference
4450 if (!(arg.value_type is NullType) && type.is_real_struct_type ()) {
4451 // we already use a reference for arguments of ref, out, and nullable parameters
4452 if ((param == null || param.direction == ParameterDirection.IN) && !type.nullable) {
4453 var unary = cexpr as CCodeUnaryExpression;
4454 if (unary != null && unary.operator == CCodeUnaryOperator.POINTER_INDIRECTION) {
4455 // *expr => expr
4456 return unary.inner;
4457 } else if (cexpr is CCodeIdentifier || cexpr is CCodeMemberAccess) {
4458 return new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, cexpr);
4459 } else {
4460 // if cexpr is e.g. a function call, we can't take the address of the expression
4461 // (tmp = expr, &tmp)
4462 var ccomma = new CCodeCommaExpression ();
4464 var temp_var = get_temp_variable (type, true, null, false);
4465 emit_temp_var (temp_var);
4466 ccomma.append_expression (new CCodeAssignment (get_variable_cexpression (temp_var.name), cexpr));
4467 ccomma.append_expression (new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, get_variable_cexpression (temp_var.name)));
4469 return ccomma;
4474 return cexpr;
4477 public override void visit_sizeof_expression (SizeofExpression expr) {
4478 generate_type_declaration (expr.type_reference, cfile);
4480 var csizeof = new CCodeFunctionCall (new CCodeIdentifier ("sizeof"));
4481 csizeof.add_argument (new CCodeIdentifier (expr.type_reference.get_cname ()));
4482 set_cvalue (expr, csizeof);
4485 public override void visit_typeof_expression (TypeofExpression expr) {
4486 set_cvalue (expr, get_type_id_expression (expr.type_reference));
4489 public override void visit_unary_expression (UnaryExpression expr) {
4490 if (expr.operator == UnaryOperator.REF || expr.operator == UnaryOperator.OUT) {
4491 set_cvalue (expr, new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, get_cvalue (expr.inner)));
4493 var array_type = expr.value_type as ArrayType;
4494 if (array_type != null) {
4495 for (int dim = 1; dim <= array_type.rank; dim++) {
4496 append_array_size (expr, new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, get_array_length_cexpression (expr.inner, dim)));
4500 var delegate_type = expr.value_type as DelegateType;
4501 if (delegate_type != null && delegate_type.delegate_symbol.has_target) {
4502 CCodeExpression target_destroy_notify;
4503 set_delegate_target (expr, new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, get_delegate_target_cexpression (expr.inner, out target_destroy_notify)));
4504 if (target_destroy_notify != null) {
4505 set_delegate_target_destroy_notify (expr, new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, target_destroy_notify));
4509 return;
4512 CCodeUnaryOperator op;
4513 if (expr.operator == UnaryOperator.PLUS) {
4514 op = CCodeUnaryOperator.PLUS;
4515 } else if (expr.operator == UnaryOperator.MINUS) {
4516 op = CCodeUnaryOperator.MINUS;
4517 } else if (expr.operator == UnaryOperator.LOGICAL_NEGATION) {
4518 op = CCodeUnaryOperator.LOGICAL_NEGATION;
4519 } else if (expr.operator == UnaryOperator.BITWISE_COMPLEMENT) {
4520 op = CCodeUnaryOperator.BITWISE_COMPLEMENT;
4521 } else if (expr.operator == UnaryOperator.INCREMENT) {
4522 op = CCodeUnaryOperator.PREFIX_INCREMENT;
4523 } else if (expr.operator == UnaryOperator.DECREMENT) {
4524 op = CCodeUnaryOperator.PREFIX_DECREMENT;
4525 } else {
4526 assert_not_reached ();
4528 set_cvalue (expr, new CCodeUnaryExpression (op, get_cvalue (expr.inner)));
4531 public CCodeExpression? try_cast_value_to_type (CCodeExpression ccodeexpr, DataType from, DataType to, Expression? expr = null) {
4532 if (from == null || gvalue_type == null || from.data_type != gvalue_type || to.get_type_id () == null) {
4533 return null;
4536 // explicit conversion from GValue
4537 var ccall = new CCodeFunctionCall (get_value_getter_function (to));
4538 CCodeExpression gvalue;
4539 if (from.nullable) {
4540 gvalue = ccodeexpr;
4541 } else {
4542 gvalue = new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, ccodeexpr);
4544 ccall.add_argument (gvalue);
4546 CCodeExpression rv = ccall;
4548 if (expr != null && to is ArrayType) {
4549 // null-terminated string array
4550 var len_call = new CCodeFunctionCall (new CCodeIdentifier ("g_strv_length"));
4551 len_call.add_argument (rv);
4552 append_array_size (expr, len_call);
4553 } else if (to is StructValueType) {
4554 var temp_decl = get_temp_variable (to, true, null, true);
4555 emit_temp_var (temp_decl);
4556 var ctemp = get_variable_cexpression (temp_decl.name);
4558 rv = new CCodeUnaryExpression (CCodeUnaryOperator.POINTER_INDIRECTION, new CCodeCastExpression (rv, (new PointerType(to)).get_cname ()));
4559 var holds = new CCodeFunctionCall (new CCodeIdentifier ("G_VALUE_HOLDS"));
4560 holds.add_argument (gvalue);
4561 holds.add_argument (new CCodeIdentifier (to.get_type_id ()));
4562 var cond = new CCodeBinaryExpression (CCodeBinaryOperator.AND, holds, ccall);
4563 var warn = new CCodeFunctionCall (new CCodeIdentifier ("g_warning"));
4564 warn.add_argument (new CCodeConstant ("\"Invalid GValue unboxing (wrong type or NULL)\""));
4565 var fail = new CCodeCommaExpression ();
4566 fail.append_expression (warn);
4567 fail.append_expression (ctemp);
4568 rv = new CCodeConditionalExpression (cond, rv, fail);
4571 return rv;
4574 int next_variant_function_id = 0;
4576 public CCodeExpression? try_cast_variant_to_type (CCodeExpression ccodeexpr, DataType from, DataType to, Expression? expr = null) {
4577 if (from == null || gvariant_type == null || from.data_type != gvariant_type) {
4578 return null;
4581 string variant_func = "_variant_get%d".printf (++next_variant_function_id);
4583 var ccall = new CCodeFunctionCall (new CCodeIdentifier (variant_func));
4584 ccall.add_argument (ccodeexpr);
4586 var cfunc = new CCodeFunction (variant_func);
4587 cfunc.modifiers = CCodeModifiers.STATIC;
4588 cfunc.add_parameter (new CCodeParameter ("value", "GVariant*"));
4590 if (!to.is_real_non_null_struct_type ()) {
4591 cfunc.return_type = to.get_cname ();
4594 if (to.is_real_non_null_struct_type ()) {
4595 // structs are returned via out parameter
4596 cfunc.add_parameter (new CCodeParameter ("result", to.get_cname () + "*"));
4597 } else if (to is ArrayType) {
4598 // return array length if appropriate
4599 var array_type = (ArrayType) to;
4601 for (int dim = 1; dim <= array_type.rank; dim++) {
4602 var temp_decl = get_temp_variable (int_type, false, expr);
4603 emit_temp_var (temp_decl);
4605 ccall.add_argument (new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, get_variable_cexpression (temp_decl.name)));
4606 cfunc.add_parameter (new CCodeParameter (get_array_length_cname ("result", dim), "int*"));
4607 append_array_size (expr, get_variable_cexpression (temp_decl.name));
4611 push_function (cfunc);
4613 var result = deserialize_expression (to, new CCodeIdentifier ("value"), new CCodeIdentifier ("*result"));
4614 ccode.add_return (result);
4616 pop_function ();
4618 cfile.add_function_declaration (cfunc);
4619 cfile.add_function (cfunc);
4621 return ccall;
4624 public virtual CCodeExpression? deserialize_expression (DataType type, CCodeExpression variant_expr, CCodeExpression? expr) {
4625 return null;
4628 public virtual CCodeExpression? serialize_expression (DataType type, CCodeExpression expr) {
4629 return null;
4632 public override void visit_cast_expression (CastExpression expr) {
4633 var valuecast = try_cast_value_to_type (get_cvalue (expr.inner), expr.inner.value_type, expr.type_reference, expr);
4634 if (valuecast != null) {
4635 set_cvalue (expr, valuecast);
4636 return;
4639 var variantcast = try_cast_variant_to_type (get_cvalue (expr.inner), expr.inner.value_type, expr.type_reference, expr);
4640 if (variantcast != null) {
4641 set_cvalue (expr, variantcast);
4642 return;
4645 generate_type_declaration (expr.type_reference, cfile);
4647 var cl = expr.type_reference.data_type as Class;
4648 var iface = expr.type_reference.data_type as Interface;
4649 if (context.profile == Profile.GOBJECT && (iface != null || (cl != null && !cl.is_compact))) {
4650 // checked cast for strict subtypes of GTypeInstance
4651 if (expr.is_silent_cast) {
4652 var ccomma = new CCodeCommaExpression ();
4653 var temp_decl = get_temp_variable (expr.inner.value_type, true, expr, false);
4655 emit_temp_var (temp_decl);
4657 var ctemp = get_variable_cexpression (temp_decl.name);
4658 var cinit = new CCodeAssignment (ctemp, get_cvalue (expr.inner));
4659 var ccheck = create_type_check (ctemp, expr.type_reference);
4660 var ccast = new CCodeCastExpression (ctemp, expr.type_reference.get_cname ());
4661 var cnull = new CCodeConstant ("NULL");
4663 ccomma.append_expression (cinit);
4664 ccomma.append_expression (new CCodeConditionalExpression (ccheck, ccast, cnull));
4666 set_cvalue (expr, ccomma);
4667 } else {
4668 set_cvalue (expr, generate_instance_cast (get_cvalue (expr.inner), expr.type_reference.data_type));
4670 } else {
4671 if (expr.is_silent_cast) {
4672 expr.error = true;
4673 Report.error (expr.source_reference, "Operation not supported for this type");
4674 return;
4677 // retain array length
4678 var array_type = expr.type_reference as ArrayType;
4679 if (array_type != null && expr.inner.value_type is ArrayType) {
4680 for (int dim = 1; dim <= array_type.rank; dim++) {
4681 append_array_size (expr, get_array_length_cexpression (expr.inner, dim));
4683 } else if (array_type != null) {
4684 // cast from non-array to array, set invalid length
4685 // required by string.data, e.g.
4686 for (int dim = 1; dim <= array_type.rank; dim++) {
4687 append_array_size (expr, new CCodeConstant ("-1"));
4691 var innercexpr = get_cvalue (expr.inner);
4692 if (expr.type_reference.data_type is Struct && !expr.type_reference.nullable &&
4693 expr.inner.value_type.data_type is Struct && expr.inner.value_type.nullable) {
4694 // nullable integer or float or boolean or struct cast to non-nullable
4695 innercexpr = new CCodeUnaryExpression (CCodeUnaryOperator.POINTER_INDIRECTION, innercexpr);
4697 set_cvalue (expr, new CCodeCastExpression (innercexpr, expr.type_reference.get_cname ()));
4699 if (expr.type_reference is DelegateType) {
4700 if (get_delegate_target (expr.inner) != null) {
4701 set_delegate_target (expr, get_delegate_target (expr.inner));
4702 } else {
4703 set_delegate_target (expr, new CCodeConstant ("NULL"));
4705 if (get_delegate_target_destroy_notify (expr.inner) != null) {
4706 set_delegate_target_destroy_notify (expr, get_delegate_target_destroy_notify (expr.inner));
4707 } else {
4708 set_delegate_target_destroy_notify (expr, new CCodeConstant ("NULL"));
4714 public override void visit_named_argument (NamedArgument expr) {
4715 set_cvalue (expr, get_cvalue (expr.inner));
4718 public override void visit_pointer_indirection (PointerIndirection expr) {
4719 set_cvalue (expr, new CCodeUnaryExpression (CCodeUnaryOperator.POINTER_INDIRECTION, get_cvalue (expr.inner)));
4722 public override void visit_addressof_expression (AddressofExpression expr) {
4723 if (get_cvalue (expr.inner) is CCodeCommaExpression) {
4724 var ccomma = get_cvalue (expr.inner) as CCodeCommaExpression;
4725 var inner = ccomma.get_inner ();
4726 var last = inner.get (inner.size - 1);
4727 ccomma.set_expression (inner.size - 1, new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, (CCodeExpression) last));
4728 set_cvalue (expr, ccomma);
4729 } else {
4730 set_cvalue (expr, new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, get_cvalue (expr.inner)));
4734 public override void visit_reference_transfer_expression (ReferenceTransferExpression expr) {
4735 /* (tmp = var, var = null, tmp) */
4736 var temp_decl = get_temp_variable (expr.value_type, true, expr, false);
4737 emit_temp_var (temp_decl);
4738 var cvar = get_variable_cexpression (temp_decl.name);
4740 ccode.add_expression (new CCodeAssignment (cvar, get_cvalue (expr.inner)));
4741 ccode.add_expression (new CCodeAssignment (get_cvalue (expr.inner), new CCodeConstant ("NULL")));
4743 set_cvalue (expr, cvar);
4745 var array_type = expr.value_type as ArrayType;
4746 if (array_type != null) {
4747 for (int dim = 1; dim <= array_type.rank; dim++) {
4748 append_array_size (expr, get_array_length_cexpression (expr.inner, dim));
4752 var delegate_type = expr.value_type as DelegateType;
4753 if (delegate_type != null && delegate_type.delegate_symbol.has_target) {
4754 CCodeExpression target_destroy_notify;
4755 set_delegate_target (expr, get_delegate_target_cexpression (expr.inner, out target_destroy_notify));
4756 if (target_destroy_notify != null) {
4757 set_delegate_target_destroy_notify (expr, target_destroy_notify);
4762 public override void visit_binary_expression (BinaryExpression expr) {
4763 var cleft = get_cvalue (expr.left);
4764 var cright = get_cvalue (expr.right);
4766 CCodeExpression? left_chain = null;
4767 if (expr.chained) {
4768 var lbe = (BinaryExpression) expr.left;
4770 var temp_decl = get_temp_variable (lbe.right.value_type, true, null, false);
4771 emit_temp_var (temp_decl);
4772 var cvar = get_variable_cexpression (temp_decl.name);
4773 var ccomma = new CCodeCommaExpression ();
4774 var clbe = (CCodeBinaryExpression) get_cvalue (lbe);
4775 if (lbe.chained) {
4776 clbe = (CCodeBinaryExpression) clbe.right;
4778 ccomma.append_expression (new CCodeAssignment (cvar, get_cvalue (lbe.right)));
4779 clbe.right = get_variable_cexpression (temp_decl.name);
4780 ccomma.append_expression (cleft);
4781 cleft = cvar;
4782 left_chain = ccomma;
4785 CCodeBinaryOperator op;
4786 if (expr.operator == BinaryOperator.PLUS) {
4787 op = CCodeBinaryOperator.PLUS;
4788 } else if (expr.operator == BinaryOperator.MINUS) {
4789 op = CCodeBinaryOperator.MINUS;
4790 } else if (expr.operator == BinaryOperator.MUL) {
4791 op = CCodeBinaryOperator.MUL;
4792 } else if (expr.operator == BinaryOperator.DIV) {
4793 op = CCodeBinaryOperator.DIV;
4794 } else if (expr.operator == BinaryOperator.MOD) {
4795 if (expr.value_type.equals (double_type)) {
4796 cfile.add_include ("math.h");
4797 var ccall = new CCodeFunctionCall (new CCodeIdentifier ("fmod"));
4798 ccall.add_argument (cleft);
4799 ccall.add_argument (cright);
4800 set_cvalue (expr, ccall);
4801 return;
4802 } else if (expr.value_type.equals (float_type)) {
4803 cfile.add_include ("math.h");
4804 var ccall = new CCodeFunctionCall (new CCodeIdentifier ("fmodf"));
4805 ccall.add_argument (cleft);
4806 ccall.add_argument (cright);
4807 set_cvalue (expr, ccall);
4808 return;
4809 } else {
4810 op = CCodeBinaryOperator.MOD;
4812 } else if (expr.operator == BinaryOperator.SHIFT_LEFT) {
4813 op = CCodeBinaryOperator.SHIFT_LEFT;
4814 } else if (expr.operator == BinaryOperator.SHIFT_RIGHT) {
4815 op = CCodeBinaryOperator.SHIFT_RIGHT;
4816 } else if (expr.operator == BinaryOperator.LESS_THAN) {
4817 op = CCodeBinaryOperator.LESS_THAN;
4818 } else if (expr.operator == BinaryOperator.GREATER_THAN) {
4819 op = CCodeBinaryOperator.GREATER_THAN;
4820 } else if (expr.operator == BinaryOperator.LESS_THAN_OR_EQUAL) {
4821 op = CCodeBinaryOperator.LESS_THAN_OR_EQUAL;
4822 } else if (expr.operator == BinaryOperator.GREATER_THAN_OR_EQUAL) {
4823 op = CCodeBinaryOperator.GREATER_THAN_OR_EQUAL;
4824 } else if (expr.operator == BinaryOperator.EQUALITY) {
4825 op = CCodeBinaryOperator.EQUALITY;
4826 } else if (expr.operator == BinaryOperator.INEQUALITY) {
4827 op = CCodeBinaryOperator.INEQUALITY;
4828 } else if (expr.operator == BinaryOperator.BITWISE_AND) {
4829 op = CCodeBinaryOperator.BITWISE_AND;
4830 } else if (expr.operator == BinaryOperator.BITWISE_OR) {
4831 op = CCodeBinaryOperator.BITWISE_OR;
4832 } else if (expr.operator == BinaryOperator.BITWISE_XOR) {
4833 op = CCodeBinaryOperator.BITWISE_XOR;
4834 } else if (expr.operator == BinaryOperator.AND) {
4835 op = CCodeBinaryOperator.AND;
4836 } else if (expr.operator == BinaryOperator.OR) {
4837 op = CCodeBinaryOperator.OR;
4838 } else if (expr.operator == BinaryOperator.IN) {
4839 if (expr.right.value_type is ArrayType) {
4840 var array_type = (ArrayType) expr.right.value_type;
4841 var node = new CCodeFunctionCall (new CCodeIdentifier (generate_array_contains_wrapper (array_type)));
4842 node.add_argument (cright);
4843 node.add_argument (get_array_length_cexpression (expr.right));
4844 if (array_type.element_type is StructValueType) {
4845 node.add_argument (new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, cleft));
4846 } else {
4847 node.add_argument (cleft);
4849 set_cvalue (expr, node);
4850 } else {
4851 set_cvalue (expr, new CCodeBinaryExpression (CCodeBinaryOperator.EQUALITY, new CCodeBinaryExpression (CCodeBinaryOperator.BITWISE_AND, cright, cleft), cleft));
4853 return;
4854 } else {
4855 assert_not_reached ();
4858 if (expr.operator == BinaryOperator.EQUALITY ||
4859 expr.operator == BinaryOperator.INEQUALITY) {
4860 var left_type = expr.left.target_type;
4861 var right_type = expr.right.target_type;
4862 make_comparable_cexpression (ref left_type, ref cleft, ref right_type, ref cright);
4864 if (left_type is StructValueType && right_type is StructValueType) {
4865 var equalfunc = generate_struct_equal_function ((Struct) left_type.data_type as Struct);
4866 var ccall = new CCodeFunctionCall (new CCodeIdentifier (equalfunc));
4867 ccall.add_argument (cleft);
4868 ccall.add_argument (cright);
4869 cleft = ccall;
4870 cright = new CCodeConstant ("TRUE");
4871 } else if ((left_type is IntegerType || left_type is FloatingType || left_type is BooleanType) && left_type.nullable &&
4872 (right_type is IntegerType || right_type is FloatingType || right_type is BooleanType) && right_type.nullable) {
4873 var equalfunc = generate_numeric_equal_function ((Struct) left_type.data_type as Struct);
4874 var ccall = new CCodeFunctionCall (new CCodeIdentifier (equalfunc));
4875 ccall.add_argument (cleft);
4876 ccall.add_argument (cright);
4877 cleft = ccall;
4878 cright = new CCodeConstant ("TRUE");
4882 if (!(expr.left.value_type is NullType)
4883 && expr.left.value_type.compatible (string_type)
4884 && !(expr.right.value_type is NullType)
4885 && expr.right.value_type.compatible (string_type)) {
4886 if (expr.operator == BinaryOperator.PLUS) {
4887 // string concatenation
4888 if (expr.left.is_constant () && expr.right.is_constant ()) {
4889 string left, right;
4891 if (cleft is CCodeIdentifier) {
4892 left = ((CCodeIdentifier) cleft).name;
4893 } else if (cleft is CCodeConstant) {
4894 left = ((CCodeConstant) cleft).name;
4895 } else {
4896 assert_not_reached ();
4898 if (cright is CCodeIdentifier) {
4899 right = ((CCodeIdentifier) cright).name;
4900 } else if (cright is CCodeConstant) {
4901 right = ((CCodeConstant) cright).name;
4902 } else {
4903 assert_not_reached ();
4906 set_cvalue (expr, new CCodeConstant ("%s %s".printf (left, right)));
4907 return;
4908 } else {
4909 if (context.profile == Profile.POSIX) {
4910 // convert to strcat(strcpy(malloc(1+strlen(a)+strlen(b)),a),b)
4911 var strcat = new CCodeFunctionCall (new CCodeIdentifier ("strcat"));
4912 var strcpy = new CCodeFunctionCall (new CCodeIdentifier ("strcpy"));
4913 var malloc = new CCodeFunctionCall (new CCodeIdentifier ("malloc"));
4915 var strlen_a = new CCodeFunctionCall (new CCodeIdentifier ("strlen"));
4916 strlen_a.add_argument(cleft);
4917 var strlen_b = new CCodeFunctionCall (new CCodeIdentifier ("strlen"));
4918 strlen_b.add_argument(cright);
4919 var newlength = new CCodeBinaryExpression (CCodeBinaryOperator.PLUS, new CCodeIdentifier("1"),
4920 new CCodeBinaryExpression (CCodeBinaryOperator.PLUS, strlen_a, strlen_b));
4921 malloc.add_argument(newlength);
4923 strcpy.add_argument(malloc);
4924 strcpy.add_argument(cleft);
4926 strcat.add_argument(strcpy);
4927 strcat.add_argument(cright);
4928 set_cvalue (expr, strcat);
4929 } else {
4930 // convert to g_strconcat (a, b, NULL)
4931 var ccall = new CCodeFunctionCall (new CCodeIdentifier ("g_strconcat"));
4932 ccall.add_argument (cleft);
4933 ccall.add_argument (cright);
4934 ccall.add_argument (new CCodeConstant("NULL"));
4935 set_cvalue (expr, ccall);
4937 return;
4939 } else if (expr.operator == BinaryOperator.EQUALITY
4940 || expr.operator == BinaryOperator.INEQUALITY
4941 || expr.operator == BinaryOperator.LESS_THAN
4942 || expr.operator == BinaryOperator.GREATER_THAN
4943 || expr.operator == BinaryOperator.LESS_THAN_OR_EQUAL
4944 || expr.operator == BinaryOperator.GREATER_THAN_OR_EQUAL) {
4945 var ccall = new CCodeFunctionCall (new CCodeIdentifier ("g_strcmp0"));
4946 ccall.add_argument (cleft);
4947 ccall.add_argument (cright);
4948 cleft = ccall;
4949 cright = new CCodeConstant ("0");
4953 set_cvalue (expr, new CCodeBinaryExpression (op, cleft, cright));
4954 if (left_chain != null) {
4955 set_cvalue (expr, new CCodeBinaryExpression (CCodeBinaryOperator.AND, left_chain, get_cvalue (expr)));
4959 public string? get_type_check_function (TypeSymbol type) {
4960 var cl = type as Class;
4961 if (cl != null && cl.type_check_function != null) {
4962 return cl.type_check_function;
4963 } else if ((cl != null && cl.is_compact) || type is Struct || type is Enum || type is Delegate) {
4964 return null;
4965 } else {
4966 return type.get_upper_case_cname ("IS_");
4970 CCodeExpression? create_type_check (CCodeNode ccodenode, DataType type) {
4971 var et = type as ErrorType;
4972 if (et != null && et.error_code != null) {
4973 var matches_call = new CCodeFunctionCall (new CCodeIdentifier ("g_error_matches"));
4974 matches_call.add_argument ((CCodeExpression) ccodenode);
4975 matches_call.add_argument (new CCodeIdentifier (et.error_domain.get_upper_case_cname ()));
4976 matches_call.add_argument (new CCodeIdentifier (et.error_code.get_cname ()));
4977 return matches_call;
4978 } else if (et != null && et.error_domain != null) {
4979 var instance_domain = new CCodeMemberAccess.pointer ((CCodeExpression) ccodenode, "domain");
4980 var type_domain = new CCodeIdentifier (et.error_domain.get_upper_case_cname ());
4981 return new CCodeBinaryExpression (CCodeBinaryOperator.EQUALITY, instance_domain, type_domain);
4982 } else {
4983 string type_check_func = get_type_check_function (type.data_type);
4984 if (type_check_func == null) {
4985 return new CCodeInvalidExpression ();
4987 var ccheck = new CCodeFunctionCall (new CCodeIdentifier (type_check_func));
4988 ccheck.add_argument ((CCodeExpression) ccodenode);
4989 return ccheck;
4993 string generate_array_contains_wrapper (ArrayType array_type) {
4994 string array_contains_func = "_vala_%s_array_contains".printf (array_type.element_type.get_lower_case_cname ());
4996 if (!add_wrapper (array_contains_func)) {
4997 return array_contains_func;
5000 var function = new CCodeFunction (array_contains_func, "gboolean");
5001 function.modifiers = CCodeModifiers.STATIC;
5003 function.add_parameter (new CCodeParameter ("stack", array_type.get_cname ()));
5004 function.add_parameter (new CCodeParameter ("stack_length", "int"));
5005 if (array_type.element_type is StructValueType) {
5006 function.add_parameter (new CCodeParameter ("needle", array_type.element_type.get_cname () + "*"));
5007 } else {
5008 function.add_parameter (new CCodeParameter ("needle", array_type.element_type.get_cname ()));
5011 push_function (function);
5013 ccode.add_declaration ("int", new CCodeVariableDeclarator ("i"));
5015 var cloop_initializer = new CCodeAssignment (new CCodeIdentifier ("i"), new CCodeConstant ("0"));
5016 var cloop_condition = new CCodeBinaryExpression (CCodeBinaryOperator.LESS_THAN, new CCodeIdentifier ("i"), new CCodeIdentifier ("stack_length"));
5017 var cloop_iterator = new CCodeUnaryExpression (CCodeUnaryOperator.POSTFIX_INCREMENT, new CCodeIdentifier ("i"));
5018 ccode.open_for (cloop_initializer, cloop_condition, cloop_iterator);
5020 var celement = new CCodeElementAccess (new CCodeIdentifier ("stack"), new CCodeIdentifier ("i"));
5021 var cneedle = new CCodeIdentifier ("needle");
5022 CCodeBinaryExpression cif_condition;
5023 if (array_type.element_type.compatible (string_type)) {
5024 var ccall = new CCodeFunctionCall (new CCodeIdentifier ("g_strcmp0"));
5025 ccall.add_argument (celement);
5026 ccall.add_argument (cneedle);
5027 cif_condition = new CCodeBinaryExpression (CCodeBinaryOperator.EQUALITY, ccall, new CCodeConstant ("0"));
5028 } else if (array_type.element_type is StructValueType) {
5029 var equalfunc = generate_struct_equal_function ((Struct) array_type.element_type.data_type as Struct);
5030 var ccall = new CCodeFunctionCall (new CCodeIdentifier (equalfunc));
5031 ccall.add_argument (new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, celement));
5032 ccall.add_argument (cneedle);
5033 cif_condition = new CCodeBinaryExpression (CCodeBinaryOperator.EQUALITY, ccall, new CCodeConstant ("TRUE"));
5034 } else {
5035 cif_condition = new CCodeBinaryExpression (CCodeBinaryOperator.EQUALITY, cneedle, celement);
5038 ccode.open_if (cif_condition);
5039 ccode.add_return (new CCodeConstant ("TRUE"));
5040 ccode.close ();
5042 ccode.close ();
5044 ccode.add_return (new CCodeConstant ("FALSE"));
5046 pop_function ();
5048 cfile.add_function_declaration (function);
5049 cfile.add_function (function);
5051 return array_contains_func;
5054 public override void visit_type_check (TypeCheck expr) {
5055 generate_type_declaration (expr.type_reference, cfile);
5057 set_cvalue (expr, create_type_check (get_cvalue (expr.expression), expr.type_reference));
5058 if (get_cvalue (expr) is CCodeInvalidExpression) {
5059 Report.error (expr.source_reference, "type check expressions not supported for compact classes, structs, and enums");
5063 public override void visit_lambda_expression (LambdaExpression lambda) {
5064 // use instance position from delegate
5065 var dt = (DelegateType) lambda.target_type;
5066 lambda.method.cinstance_parameter_position = dt.delegate_symbol.cinstance_parameter_position;
5068 lambda.accept_children (this);
5070 bool expr_owned = lambda.value_type.value_owned;
5072 set_cvalue (lambda, new CCodeIdentifier (lambda.method.get_cname ()));
5074 var delegate_type = (DelegateType) lambda.target_type;
5075 if (lambda.method.closure) {
5076 int block_id = get_block_id (current_closure_block);
5077 var delegate_target = get_variable_cexpression ("_data%d_".printf (block_id));
5078 if (expr_owned || delegate_type.is_called_once) {
5079 var ref_call = new CCodeFunctionCall (new CCodeIdentifier ("block%d_data_ref".printf (block_id)));
5080 ref_call.add_argument (delegate_target);
5081 delegate_target = ref_call;
5082 set_delegate_target_destroy_notify (lambda, new CCodeIdentifier ("block%d_data_unref".printf (block_id)));
5083 } else {
5084 set_delegate_target_destroy_notify (lambda, new CCodeConstant ("NULL"));
5086 set_delegate_target (lambda, delegate_target);
5087 } else if (get_this_type () != null || in_constructor) {
5088 CCodeExpression delegate_target = get_result_cexpression ("self");
5089 if (expr_owned || delegate_type.is_called_once) {
5090 if (get_this_type () != null) {
5091 var ref_call = new CCodeFunctionCall (get_dup_func_expression (get_this_type (), lambda.source_reference));
5092 ref_call.add_argument (delegate_target);
5093 delegate_target = ref_call;
5094 set_delegate_target_destroy_notify (lambda, get_destroy_func_expression (get_this_type ()));
5095 } else {
5096 // in constructor
5097 var ref_call = new CCodeFunctionCall (new CCodeIdentifier ("g_object_ref"));
5098 ref_call.add_argument (delegate_target);
5099 delegate_target = ref_call;
5100 set_delegate_target_destroy_notify (lambda, new CCodeIdentifier ("g_object_unref"));
5102 } else {
5103 set_delegate_target_destroy_notify (lambda, new CCodeConstant ("NULL"));
5105 set_delegate_target (lambda, delegate_target);
5106 } else {
5107 set_delegate_target (lambda, new CCodeConstant ("NULL"));
5108 set_delegate_target_destroy_notify (lambda, new CCodeConstant ("NULL"));
5112 public CCodeExpression convert_from_generic_pointer (CCodeExpression cexpr, DataType actual_type) {
5113 var result = cexpr;
5114 if (is_reference_type_argument (actual_type) || is_nullable_value_type_argument (actual_type)) {
5115 result = new CCodeCastExpression (cexpr, actual_type.get_cname ());
5116 } else if (is_signed_integer_type_argument (actual_type)) {
5117 var cconv = new CCodeFunctionCall (new CCodeIdentifier ("GPOINTER_TO_INT"));
5118 cconv.add_argument (cexpr);
5119 result = cconv;
5120 } else if (is_unsigned_integer_type_argument (actual_type)) {
5121 var cconv = new CCodeFunctionCall (new CCodeIdentifier ("GPOINTER_TO_UINT"));
5122 cconv.add_argument (cexpr);
5123 result = cconv;
5125 return result;
5128 public CCodeExpression convert_to_generic_pointer (CCodeExpression cexpr, DataType actual_type) {
5129 var result = cexpr;
5130 if (is_signed_integer_type_argument (actual_type)) {
5131 var cconv = new CCodeFunctionCall (new CCodeIdentifier ("GINT_TO_POINTER"));
5132 cconv.add_argument (cexpr);
5133 result = cconv;
5134 } else if (is_unsigned_integer_type_argument (actual_type)) {
5135 var cconv = new CCodeFunctionCall (new CCodeIdentifier ("GUINT_TO_POINTER"));
5136 cconv.add_argument (cexpr);
5137 result = cconv;
5139 return result;
5142 // manage memory and implicit casts
5143 public CCodeExpression transform_expression (CCodeExpression source_cexpr, DataType? expression_type, DataType? target_type, Expression? expr = null) {
5144 var cexpr = source_cexpr;
5145 if (expression_type == null) {
5146 return cexpr;
5150 if (expression_type.value_owned
5151 && expression_type.floating_reference) {
5152 /* floating reference, sink it.
5154 var cl = expression_type.data_type as ObjectTypeSymbol;
5155 var sink_func = (cl != null) ? cl.get_ref_sink_function () : null;
5157 if (sink_func != null) {
5158 var csink = new CCodeFunctionCall (new CCodeIdentifier (sink_func));
5159 csink.add_argument (cexpr);
5161 cexpr = csink;
5162 } else {
5163 Report.error (null, "type `%s' does not support floating references".printf (expression_type.data_type.name));
5167 bool boxing = (expression_type is ValueType && !expression_type.nullable
5168 && target_type is ValueType && target_type.nullable);
5169 bool unboxing = (expression_type is ValueType && expression_type.nullable
5170 && target_type is ValueType && !target_type.nullable);
5172 bool gvalue_boxing = (context.profile == Profile.GOBJECT
5173 && target_type != null
5174 && target_type.data_type == gvalue_type
5175 && !(expression_type is NullType)
5176 && expression_type.get_type_id () != "G_TYPE_VALUE");
5177 bool gvariant_boxing = (context.profile == Profile.GOBJECT
5178 && target_type != null
5179 && target_type.data_type == gvariant_type
5180 && !(expression_type is NullType)
5181 && expression_type.data_type != gvariant_type);
5183 if (expression_type.value_owned
5184 && (target_type == null || !target_type.value_owned || boxing || unboxing)
5185 && !gvalue_boxing /* gvalue can assume ownership of value, no need to free it */) {
5186 // value leaked, destroy it
5187 var pointer_type = target_type as PointerType;
5188 if (pointer_type != null && !(pointer_type.base_type is VoidType)) {
5189 // manual memory management for non-void pointers
5190 // treat void* special to not leak memory with void* method parameters
5191 } else if (requires_destroy (expression_type)) {
5192 var decl = get_temp_variable (expression_type, true, expression_type, false);
5193 emit_temp_var (decl);
5194 temp_ref_vars.insert (0, decl);
5195 ccode.add_expression (new CCodeAssignment (get_variable_cexpression (decl.name), cexpr));
5196 cexpr = get_variable_cexpression (decl.name);
5198 if (expression_type is ArrayType && expr != null) {
5199 var array_type = (ArrayType) expression_type;
5200 for (int dim = 1; dim <= array_type.rank; dim++) {
5201 var len_decl = new LocalVariable (int_type.copy (), get_array_length_cname (decl.name, dim));
5202 emit_temp_var (len_decl);
5203 ccode.add_expression (new CCodeAssignment (get_variable_cexpression (len_decl.name), get_array_length_cexpression (expr, dim)));
5205 } else if (expression_type is DelegateType && expr != null) {
5206 var target_decl = new LocalVariable (new PointerType (new VoidType ()), get_delegate_target_cname (decl.name));
5207 emit_temp_var (target_decl);
5208 var target_destroy_notify_decl = new LocalVariable (new DelegateType ((Delegate) context.root.scope.lookup ("GLib").scope.lookup ("DestroyNotify")), get_delegate_target_destroy_notify_cname (decl.name));
5209 emit_temp_var (target_destroy_notify_decl);
5210 CCodeExpression target_destroy_notify;
5211 ccode.add_expression (new CCodeAssignment (get_variable_cexpression (target_decl.name), get_delegate_target_cexpression (expr, out target_destroy_notify)));
5212 ccode.add_expression (new CCodeAssignment (get_variable_cexpression (target_destroy_notify_decl.name), target_destroy_notify));
5218 if (target_type == null) {
5219 // value will be destroyed, no need for implicit casts
5220 return cexpr;
5223 if (gvalue_boxing) {
5224 // implicit conversion to GValue
5225 var decl = get_temp_variable (target_type, true, target_type);
5226 emit_temp_var (decl);
5228 if (!target_type.value_owned) {
5229 // boxed GValue leaked, destroy it
5230 temp_ref_vars.insert (0, decl);
5233 var ccomma = new CCodeCommaExpression ();
5235 if (target_type.nullable) {
5236 var newcall = new CCodeFunctionCall (new CCodeIdentifier ("g_new0"));
5237 newcall.add_argument (new CCodeConstant ("GValue"));
5238 newcall.add_argument (new CCodeConstant ("1"));
5239 var newassignment = new CCodeAssignment (get_variable_cexpression (decl.name), newcall);
5240 ccomma.append_expression (newassignment);
5243 var ccall = new CCodeFunctionCall (new CCodeIdentifier ("g_value_init"));
5244 if (target_type.nullable) {
5245 ccall.add_argument (get_variable_cexpression (decl.name));
5246 } else {
5247 ccall.add_argument (new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, get_variable_cexpression (decl.name)));
5249 ccall.add_argument (new CCodeIdentifier (expression_type.get_type_id ()));
5250 ccomma.append_expression (ccall);
5252 if (requires_destroy (expression_type)) {
5253 ccall = new CCodeFunctionCall (get_value_taker_function (expression_type));
5254 } else {
5255 ccall = new CCodeFunctionCall (get_value_setter_function (expression_type));
5257 if (target_type.nullable) {
5258 ccall.add_argument (get_variable_cexpression (decl.name));
5259 } else {
5260 ccall.add_argument (new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, get_variable_cexpression (decl.name)));
5262 if (expression_type.is_real_non_null_struct_type ()) {
5263 ccall.add_argument (new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, cexpr));
5264 } else {
5265 ccall.add_argument (cexpr);
5268 ccomma.append_expression (ccall);
5270 ccomma.append_expression (get_variable_cexpression (decl.name));
5271 cexpr = ccomma;
5273 return cexpr;
5274 } else if (gvariant_boxing) {
5275 // implicit conversion to GVariant
5276 string variant_func = "_variant_new%d".printf (++next_variant_function_id);
5278 var ccall = new CCodeFunctionCall (new CCodeIdentifier (variant_func));
5279 ccall.add_argument (cexpr);
5281 var cfunc = new CCodeFunction (variant_func, "GVariant*");
5282 cfunc.modifiers = CCodeModifiers.STATIC;
5283 cfunc.add_parameter (new CCodeParameter ("value", expression_type.get_cname ()));
5285 if (expression_type is ArrayType) {
5286 // return array length if appropriate
5287 var array_type = (ArrayType) expression_type;
5289 for (int dim = 1; dim <= array_type.rank; dim++) {
5290 ccall.add_argument (get_array_length_cexpression (expr, dim));
5291 cfunc.add_parameter (new CCodeParameter (get_array_length_cname ("value", dim), "gint"));
5295 push_function (cfunc);
5297 var result = serialize_expression (expression_type, new CCodeIdentifier ("value"));
5299 // sink floating reference
5300 var sink = new CCodeFunctionCall (new CCodeIdentifier ("g_variant_ref_sink"));
5301 sink.add_argument (result);
5302 ccode.add_return (sink);
5304 pop_function ();
5306 cfile.add_function_declaration (cfunc);
5307 cfile.add_function (cfunc);
5309 return ccall;
5310 } else if (boxing) {
5311 // value needs to be boxed
5313 var unary = cexpr as CCodeUnaryExpression;
5314 if (unary != null && unary.operator == CCodeUnaryOperator.POINTER_INDIRECTION) {
5315 // *expr => expr
5316 cexpr = unary.inner;
5317 } else if (cexpr is CCodeIdentifier || cexpr is CCodeMemberAccess) {
5318 cexpr = new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, cexpr);
5319 } else {
5320 var decl = get_temp_variable (expression_type, expression_type.value_owned, expression_type, false);
5321 emit_temp_var (decl);
5323 var ccomma = new CCodeCommaExpression ();
5324 ccomma.append_expression (new CCodeAssignment (get_variable_cexpression (decl.name), cexpr));
5325 ccomma.append_expression (new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, get_variable_cexpression (decl.name)));
5326 cexpr = ccomma;
5328 } else if (unboxing) {
5329 // unbox value
5331 cexpr = new CCodeUnaryExpression (CCodeUnaryOperator.POINTER_INDIRECTION, cexpr);
5332 } else {
5333 cexpr = get_implicit_cast_expression (cexpr, expression_type, target_type, expr);
5336 if (target_type.value_owned && (!expression_type.value_owned || boxing || unboxing)) {
5337 // need to copy value
5338 if (requires_copy (target_type) && !(expression_type is NullType)) {
5339 CodeNode node = expr;
5340 if (node == null) {
5341 node = expression_type;
5344 var decl = get_temp_variable (target_type, true, node, false);
5345 emit_temp_var (decl);
5346 ccode.add_expression (new CCodeAssignment (get_variable_cexpression (decl.name), get_ref_cexpression (target_type, cexpr, expr, node)));
5347 cexpr = get_variable_cexpression (decl.name);
5351 return cexpr;
5354 public virtual CCodeExpression get_implicit_cast_expression (CCodeExpression source_cexpr, DataType? expression_type, DataType? target_type, Expression? expr = null) {
5355 var cexpr = source_cexpr;
5357 if (expression_type.data_type != null && expression_type.data_type == target_type.data_type) {
5358 // same type, no cast required
5359 return cexpr;
5362 if (expression_type is NullType) {
5363 // null literal, no cast required when not converting to generic type pointer
5364 return cexpr;
5367 generate_type_declaration (target_type, cfile);
5369 var cl = target_type.data_type as Class;
5370 var iface = target_type.data_type as Interface;
5371 if (context.checking && (iface != null || (cl != null && !cl.is_compact))) {
5372 // checked cast for strict subtypes of GTypeInstance
5373 return generate_instance_cast (cexpr, target_type.data_type);
5374 } else if (target_type.data_type != null && expression_type.get_cname () != target_type.get_cname ()) {
5375 var st = target_type.data_type as Struct;
5376 if (target_type.data_type.is_reference_type () || (st != null && st.is_simple_type ())) {
5377 // don't cast non-simple structs
5378 return new CCodeCastExpression (cexpr, target_type.get_cname ());
5379 } else {
5380 return cexpr;
5382 } else {
5383 return cexpr;
5387 public void store_property (Property prop, MemberAccess ma, CCodeExpression cexpr, Expression? rhs = null) {
5388 if (ma.inner is BaseAccess) {
5389 if (prop.base_property != null) {
5390 var base_class = (Class) prop.base_property.parent_symbol;
5391 var vcast = new CCodeFunctionCall (new CCodeIdentifier ("%s_CLASS".printf (base_class.get_upper_case_cname (null))));
5392 vcast.add_argument (new CCodeIdentifier ("%s_parent_class".printf (current_class.get_lower_case_cname (null))));
5394 var ccall = new CCodeFunctionCall (new CCodeMemberAccess.pointer (vcast, "set_%s".printf (prop.name)));
5395 ccall.add_argument ((CCodeExpression) get_ccodenode (ma.inner));
5396 ccall.add_argument (cexpr);
5398 ccode.add_expression (ccall);
5399 } else if (prop.base_interface_property != null) {
5400 var base_iface = (Interface) prop.base_interface_property.parent_symbol;
5401 string parent_iface_var = "%s_%s_parent_iface".printf (current_class.get_lower_case_cname (null), base_iface.get_lower_case_cname (null));
5403 var ccall = new CCodeFunctionCall (new CCodeMemberAccess.pointer (new CCodeIdentifier (parent_iface_var), "set_%s".printf (prop.name)));
5404 ccall.add_argument ((CCodeExpression) get_ccodenode (ma.inner));
5405 ccall.add_argument (cexpr);
5407 ccode.add_expression (ccall);
5409 return;
5412 var set_func = "g_object_set";
5414 var base_property = prop;
5415 if (!prop.no_accessor_method) {
5416 if (prop.base_property != null) {
5417 base_property = prop.base_property;
5418 } else if (prop.base_interface_property != null) {
5419 base_property = prop.base_interface_property;
5422 if (prop is DynamicProperty) {
5423 set_func = get_dynamic_property_setter_cname ((DynamicProperty) prop);
5424 } else {
5425 generate_property_accessor_declaration (base_property.set_accessor, cfile);
5426 set_func = base_property.set_accessor.get_cname ();
5430 var ccall = new CCodeFunctionCall (new CCodeIdentifier (set_func));
5432 if (prop.binding == MemberBinding.INSTANCE) {
5433 /* target instance is first argument */
5434 var instance = (CCodeExpression) get_ccodenode (ma.inner);
5436 if (prop.parent_symbol is Struct) {
5437 // we need to pass struct instance by reference
5438 var unary = instance as CCodeUnaryExpression;
5439 if (unary != null && unary.operator == CCodeUnaryOperator.POINTER_INDIRECTION) {
5440 // *expr => expr
5441 instance = unary.inner;
5442 } else if (instance is CCodeIdentifier || instance is CCodeMemberAccess) {
5443 instance = new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, instance);
5444 } else {
5445 // if instance is e.g. a function call, we can't take the address of the expression
5446 // (tmp = expr, &tmp)
5448 var temp_var = get_temp_variable (ma.inner.target_type, true, null, false);
5449 emit_temp_var (temp_var);
5450 ccode.add_expression (new CCodeAssignment (get_variable_cexpression (temp_var.name), instance));
5452 instance = new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, get_variable_cexpression (temp_var.name));
5456 ccall.add_argument (instance);
5459 if (prop.no_accessor_method) {
5460 /* property name is second argument of g_object_set */
5461 ccall.add_argument (prop.get_canonical_cconstant ());
5464 var array_type = prop.property_type as ArrayType;
5466 if (array_type != null && !prop.no_array_length) {
5467 var temp_var = get_temp_variable (prop.property_type, true, null, false);
5468 emit_temp_var (temp_var);
5469 ccode.add_expression (new CCodeAssignment (get_variable_cexpression (temp_var.name), cexpr));
5470 ccall.add_argument (get_variable_cexpression (temp_var.name));
5471 } else {
5472 ccall.add_argument (cexpr);
5475 if (array_type != null && !prop.no_array_length && rhs != null) {
5476 for (int dim = 1; dim <= array_type.rank; dim++) {
5477 ccall.add_argument (get_array_length_cexpression (rhs, dim));
5479 } else if (prop.property_type is DelegateType && rhs != null) {
5480 var delegate_type = (DelegateType) prop.property_type;
5481 if (delegate_type.delegate_symbol.has_target) {
5482 CCodeExpression delegate_target_destroy_notify;
5483 ccall.add_argument (get_delegate_target_cexpression (rhs, out delegate_target_destroy_notify));
5487 if (prop.no_accessor_method) {
5488 ccall.add_argument (new CCodeConstant ("NULL"));
5491 ccode.add_expression (ccall);
5494 /* indicates whether a given Expression eligable for an ADDRESS_OF operator
5495 * from a vala to C point of view all expressions denoting locals, fields and
5496 * parameters are eligable to an ADDRESS_OF operator */
5497 public bool is_address_of_possible (Expression e) {
5498 if (gvalue_type != null && e.target_type.data_type == gvalue_type && e.value_type.data_type != gvalue_type) {
5499 // implicit conversion to GValue is not addressable
5500 return false;
5503 var ma = e as MemberAccess;
5505 if (ma == null) {
5506 return false;
5509 return (ma.symbol_reference is Variable);
5512 /* retrieve the correct address_of expression for a give expression, creates temporary variables
5513 * where necessary, ce is the corresponding ccode expression for e */
5514 public CCodeExpression get_address_of_expression (Expression e, CCodeExpression ce) {
5515 // is address of trivially possible?
5516 if (is_address_of_possible (e)) {
5517 return new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, ce);
5520 var ccomma = new CCodeCommaExpression ();
5521 DataType address_of_type;
5522 if (gvalue_type != null && e.target_type != null && e.target_type.data_type == gvalue_type) {
5523 // implicit conversion to GValue
5524 address_of_type = e.target_type;
5525 } else {
5526 address_of_type = e.value_type;
5528 var temp_decl = get_temp_variable (address_of_type, true, null, false);
5529 var ctemp = get_variable_cexpression (temp_decl.name);
5530 emit_temp_var (temp_decl);
5531 ccomma.append_expression (new CCodeAssignment (ctemp, ce));
5532 ccomma.append_expression (new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, ctemp));
5533 return ccomma;
5536 public bool add_wrapper (string wrapper_name) {
5537 return wrappers.add (wrapper_name);
5540 public bool add_generated_external_symbol (Symbol external_symbol) {
5541 return generated_external_symbols.add (external_symbol);
5544 public static DataType get_data_type_for_symbol (TypeSymbol sym) {
5545 DataType type = null;
5547 if (sym is Class) {
5548 type = new ObjectType ((Class) sym);
5549 } else if (sym is Interface) {
5550 type = new ObjectType ((Interface) sym);
5551 } else if (sym is Struct) {
5552 var st = (Struct) sym;
5553 if (st.is_boolean_type ()) {
5554 type = new BooleanType (st);
5555 } else if (st.is_integer_type ()) {
5556 type = new IntegerType (st);
5557 } else if (st.is_floating_type ()) {
5558 type = new FloatingType (st);
5559 } else {
5560 type = new StructValueType (st);
5562 } else if (sym is Enum) {
5563 type = new EnumValueType ((Enum) sym);
5564 } else if (sym is ErrorDomain) {
5565 type = new ErrorType ((ErrorDomain) sym, null);
5566 } else if (sym is ErrorCode) {
5567 type = new ErrorType ((ErrorDomain) sym.parent_symbol, (ErrorCode) sym);
5568 } else {
5569 Report.error (null, "internal error: `%s' is not a supported type".printf (sym.get_full_name ()));
5570 return new InvalidType ();
5573 return type;
5576 public CCodeExpression? default_value_for_type (DataType type, bool initializer_expression) {
5577 var st = type.data_type as Struct;
5578 var array_type = type as ArrayType;
5579 if (initializer_expression && !type.nullable &&
5580 ((st != null && !st.is_simple_type ()) ||
5581 (array_type != null && array_type.fixed_length))) {
5582 // 0-initialize struct with struct initializer { 0 }
5583 // only allowed as initializer expression in C
5584 var clist = new CCodeInitializerList ();
5585 clist.append (new CCodeConstant ("0"));
5586 return clist;
5587 } else if ((type.data_type != null && type.data_type.is_reference_type ())
5588 || type.nullable
5589 || type is PointerType || type is DelegateType
5590 || (array_type != null && !array_type.fixed_length)) {
5591 return new CCodeConstant ("NULL");
5592 } else if (type.data_type != null && type.data_type.get_default_value () != null) {
5593 return new CCodeConstant (type.data_type.get_default_value ());
5594 } else if (type.type_parameter != null) {
5595 return new CCodeConstant ("NULL");
5596 } else if (type is ErrorType) {
5597 return new CCodeConstant ("NULL");
5599 return null;
5602 private void create_property_type_check_statement (Property prop, bool check_return_type, TypeSymbol t, bool non_null, string var_name) {
5603 if (check_return_type) {
5604 create_type_check_statement (prop, prop.property_type, t, non_null, var_name);
5605 } else {
5606 create_type_check_statement (prop, new VoidType (), t, non_null, var_name);
5610 public void create_type_check_statement (CodeNode method_node, DataType ret_type, TypeSymbol t, bool non_null, string var_name) {
5611 var ccheck = new CCodeFunctionCall ();
5613 if (!context.assert) {
5614 return;
5615 } else if (context.checking && ((t is Class && !((Class) t).is_compact) || t is Interface)) {
5616 var ctype_check = new CCodeFunctionCall (new CCodeIdentifier (get_type_check_function (t)));
5617 ctype_check.add_argument (new CCodeIdentifier (var_name));
5619 CCodeExpression cexpr = ctype_check;
5620 if (!non_null) {
5621 var cnull = new CCodeBinaryExpression (CCodeBinaryOperator.EQUALITY, new CCodeIdentifier (var_name), new CCodeConstant ("NULL"));
5623 cexpr = new CCodeBinaryExpression (CCodeBinaryOperator.OR, cnull, ctype_check);
5625 ccheck.add_argument (cexpr);
5626 } else if (!non_null) {
5627 return;
5628 } else if (t == glist_type || t == gslist_type) {
5629 // NULL is empty list
5630 return;
5631 } else {
5632 var cnonnull = new CCodeBinaryExpression (CCodeBinaryOperator.INEQUALITY, new CCodeIdentifier (var_name), new CCodeConstant ("NULL"));
5633 ccheck.add_argument (cnonnull);
5636 var cm = method_node as CreationMethod;
5637 if (cm != null && cm.parent_symbol is ObjectTypeSymbol) {
5638 ccheck.call = new CCodeIdentifier ("g_return_val_if_fail");
5639 ccheck.add_argument (new CCodeConstant ("NULL"));
5640 } else if (ret_type is VoidType) {
5641 /* void function */
5642 ccheck.call = new CCodeIdentifier ("g_return_if_fail");
5643 } else {
5644 ccheck.call = new CCodeIdentifier ("g_return_val_if_fail");
5646 var cdefault = default_value_for_type (ret_type, false);
5647 if (cdefault != null) {
5648 ccheck.add_argument (cdefault);
5649 } else {
5650 return;
5654 ccode.add_expression (ccheck);
5657 public int get_param_pos (double param_pos, bool ellipsis = false) {
5658 if (!ellipsis) {
5659 if (param_pos >= 0) {
5660 return (int) (param_pos * 1000);
5661 } else {
5662 return (int) ((100 + param_pos) * 1000);
5664 } else {
5665 if (param_pos >= 0) {
5666 return (int) ((100 + param_pos) * 1000);
5667 } else {
5668 return (int) ((200 + param_pos) * 1000);
5673 public CCodeExpression? get_ccodenode (Expression node) {
5674 if (get_cvalue (node) == null) {
5675 node.emit (this);
5677 return get_cvalue (node);
5680 public override void visit_class (Class cl) {
5683 public void create_postcondition_statement (Expression postcondition) {
5684 var cassert = new CCodeFunctionCall (new CCodeIdentifier ("g_warn_if_fail"));
5686 postcondition.emit (this);
5688 cassert.add_argument (get_cvalue (postcondition));
5690 ccode.add_expression (cassert);
5693 public virtual bool is_gobject_property (Property prop) {
5694 return false;
5697 public DataType? get_this_type () {
5698 if (current_method != null && current_method.binding == MemberBinding.INSTANCE) {
5699 return current_method.this_parameter.variable_type;
5700 } else if (current_property_accessor != null && current_property_accessor.prop.binding == MemberBinding.INSTANCE) {
5701 return current_property_accessor.prop.this_parameter.variable_type;
5703 return null;
5706 public CCodeFunctionCall generate_instance_cast (CCodeExpression expr, TypeSymbol type) {
5707 var result = new CCodeFunctionCall (new CCodeIdentifier (type.get_upper_case_cname (null)));
5708 result.add_argument (expr);
5709 return result;
5712 void generate_struct_destroy_function (Struct st) {
5713 if (cfile.add_declaration (st.get_destroy_function ())) {
5714 // only generate function once per source file
5715 return;
5718 var function = new CCodeFunction (st.get_destroy_function (), "void");
5719 function.modifiers = CCodeModifiers.STATIC;
5720 function.add_parameter (new CCodeParameter ("self", st.get_cname () + "*"));
5722 push_function (function);
5724 foreach (Field f in st.get_fields ()) {
5725 if (f.binding == MemberBinding.INSTANCE) {
5726 if (requires_destroy (f.variable_type)) {
5727 var lhs = new CCodeMemberAccess.pointer (new CCodeIdentifier ("self"), f.get_cname ());
5729 var this_access = new MemberAccess.simple ("this");
5730 this_access.value_type = get_data_type_for_symbol ((TypeSymbol) f.parent_symbol);
5731 set_cvalue (this_access, new CCodeIdentifier ("(*self)"));
5733 var ma = new MemberAccess (this_access, f.name);
5734 ma.symbol_reference = f;
5735 ma.value_type = f.variable_type.copy ();
5736 visit_member_access (ma);
5737 ccode.add_expression (get_unref_expression (lhs, f.variable_type, ma));
5742 pop_function ();
5744 cfile.add_function_declaration (function);
5745 cfile.add_function (function);
5748 void generate_struct_copy_function (Struct st) {
5749 if (cfile.add_declaration (st.get_copy_function ())) {
5750 // only generate function once per source file
5751 return;
5754 var function = new CCodeFunction (st.get_copy_function (), "void");
5755 function.modifiers = CCodeModifiers.STATIC;
5756 function.add_parameter (new CCodeParameter ("self", "const " + st.get_cname () + "*"));
5757 function.add_parameter (new CCodeParameter ("dest", st.get_cname () + "*"));
5759 push_context (new EmitContext ());
5760 push_function (function);
5762 foreach (Field f in st.get_fields ()) {
5763 if (f.binding == MemberBinding.INSTANCE) {
5764 CCodeExpression copy = new CCodeMemberAccess.pointer (new CCodeIdentifier ("self"), f.name);
5765 if (requires_copy (f.variable_type)) {
5766 var this_access = new MemberAccess.simple ("this");
5767 this_access.value_type = get_data_type_for_symbol ((TypeSymbol) f.parent_symbol);
5768 set_cvalue (this_access, new CCodeIdentifier ("(*self)"));
5769 var ma = new MemberAccess (this_access, f.name);
5770 ma.symbol_reference = f;
5771 ma.value_type = f.variable_type.copy ();
5772 visit_member_access (ma);
5773 copy = get_ref_cexpression (f.variable_type, copy, ma, f);
5775 var dest = new CCodeMemberAccess.pointer (new CCodeIdentifier ("dest"), f.name);
5777 var array_type = f.variable_type as ArrayType;
5778 if (array_type != null && array_type.fixed_length) {
5779 // fixed-length (stack-allocated) arrays
5780 cfile.add_include ("string.h");
5782 var sizeof_call = new CCodeFunctionCall (new CCodeIdentifier ("sizeof"));
5783 sizeof_call.add_argument (new CCodeIdentifier (array_type.element_type.get_cname ()));
5784 var size = new CCodeBinaryExpression (CCodeBinaryOperator.MUL, new CCodeConstant ("%d".printf (array_type.length)), sizeof_call);
5786 var array_copy_call = new CCodeFunctionCall (new CCodeIdentifier ("memcpy"));
5787 array_copy_call.add_argument (dest);
5788 array_copy_call.add_argument (copy);
5789 array_copy_call.add_argument (size);
5790 ccode.add_expression (array_copy_call);
5791 } else {
5792 ccode.add_expression (new CCodeAssignment (dest, copy));
5794 if (array_type != null && !f.no_array_length) {
5795 for (int dim = 1; dim <= array_type.rank; dim++) {
5796 var len_src = new CCodeMemberAccess.pointer (new CCodeIdentifier ("self"), get_array_length_cname (f.name, dim));
5797 var len_dest = new CCodeMemberAccess.pointer (new CCodeIdentifier ("dest"), get_array_length_cname (f.name, dim));
5798 ccode.add_expression (new CCodeAssignment (len_dest, len_src));
5805 pop_function ();
5806 pop_context ();
5808 cfile.add_function_declaration (function);
5809 cfile.add_function (function);
5812 public void return_default_value (DataType return_type) {
5813 ccode.add_return (default_value_for_type (return_type, false));
5816 public virtual string? get_custom_creturn_type (Method m) {
5817 return null;
5820 public virtual void generate_dynamic_method_wrapper (DynamicMethod method) {
5823 public virtual bool method_has_wrapper (Method method) {
5824 return false;
5827 public virtual CCodeFunctionCall get_param_spec (Property prop) {
5828 return new CCodeFunctionCall (new CCodeIdentifier (""));
5831 public virtual CCodeFunctionCall get_signal_creation (Signal sig, TypeSymbol type) {
5832 return new CCodeFunctionCall (new CCodeIdentifier (""));
5835 public virtual void register_dbus_info (ObjectTypeSymbol bindable) {
5838 public virtual string get_dynamic_property_getter_cname (DynamicProperty node) {
5839 Report.error (node.source_reference, "dynamic properties are not supported for %s".printf (node.dynamic_type.to_string ()));
5840 return "";
5843 public virtual string get_dynamic_property_setter_cname (DynamicProperty node) {
5844 Report.error (node.source_reference, "dynamic properties are not supported for %s".printf (node.dynamic_type.to_string ()));
5845 return "";
5848 public virtual string get_dynamic_signal_cname (DynamicSignal node) {
5849 return "";
5852 public virtual string get_dynamic_signal_connect_wrapper_name (DynamicSignal node) {
5853 return "";
5856 public virtual string get_dynamic_signal_connect_after_wrapper_name (DynamicSignal node) {
5857 return "";
5860 public virtual string get_dynamic_signal_disconnect_wrapper_name (DynamicSignal node) {
5861 return "";
5864 public virtual void generate_marshaller (List<Parameter> params, DataType return_type, bool dbus = false) {
5867 public virtual string get_marshaller_function (List<Parameter> params, DataType return_type, string? prefix = null, bool dbus = false) {
5868 return "";
5871 public virtual string get_array_length_cname (string array_cname, int dim) {
5872 return "";
5875 public virtual string get_parameter_array_length_cname (Parameter param, int dim) {
5876 return "";
5879 public virtual CCodeExpression get_array_length_cexpression (Expression array_expr, int dim = -1) {
5880 return new CCodeConstant ("");
5883 public virtual CCodeExpression get_array_length_cvalue (TargetValue value, int dim = -1) {
5884 return new CCodeInvalidExpression ();
5887 public virtual string get_array_size_cname (string array_cname) {
5888 return "";
5891 public virtual void add_simple_check (CodeNode node, bool always_fails = false) {
5894 public CCodeExpression? get_cvalue (Expression expr) {
5895 if (expr.target_value == null) {
5896 return null;
5898 var glib_value = (GLibValue) expr.target_value;
5899 return glib_value.cvalue;
5902 public CCodeExpression? get_cvalue_ (TargetValue value) {
5903 var glib_value = (GLibValue) value;
5904 return glib_value.cvalue;
5907 public void set_cvalue (Expression expr, CCodeExpression? cvalue) {
5908 var glib_value = (GLibValue) expr.target_value;
5909 if (glib_value == null) {
5910 glib_value = new GLibValue (expr.value_type);
5911 expr.target_value = glib_value;
5913 glib_value.cvalue = cvalue;
5916 public CCodeExpression? get_array_size_cvalue (TargetValue value) {
5917 var glib_value = (GLibValue) value;
5918 return glib_value.array_size_cvalue;
5921 public void set_array_size_cvalue (TargetValue value, CCodeExpression? cvalue) {
5922 var glib_value = (GLibValue) value;
5923 glib_value.array_size_cvalue = cvalue;
5926 public CCodeExpression? get_delegate_target (Expression expr) {
5927 if (expr.target_value == null) {
5928 return null;
5930 var glib_value = (GLibValue) expr.target_value;
5931 return glib_value.delegate_target_cvalue;
5934 public void set_delegate_target (Expression expr, CCodeExpression? delegate_target) {
5935 var glib_value = (GLibValue) expr.target_value;
5936 if (glib_value == null) {
5937 glib_value = new GLibValue (expr.value_type);
5938 expr.target_value = glib_value;
5940 glib_value.delegate_target_cvalue = delegate_target;
5943 public CCodeExpression? get_delegate_target_destroy_notify (Expression expr) {
5944 if (expr.target_value == null) {
5945 return null;
5947 var glib_value = (GLibValue) expr.target_value;
5948 return glib_value.delegate_target_destroy_notify_cvalue;
5951 public void set_delegate_target_destroy_notify (Expression expr, CCodeExpression? destroy_notify) {
5952 var glib_value = (GLibValue) expr.target_value;
5953 if (glib_value == null) {
5954 glib_value = new GLibValue (expr.value_type);
5955 expr.target_value = glib_value;
5957 glib_value.delegate_target_destroy_notify_cvalue = destroy_notify;
5960 public void append_array_size (Expression expr, CCodeExpression size) {
5961 var glib_value = (GLibValue) expr.target_value;
5962 if (glib_value == null) {
5963 glib_value = new GLibValue (expr.value_type);
5964 expr.target_value = glib_value;
5966 glib_value.append_array_length_cvalue (size);
5969 public List<CCodeExpression>? get_array_sizes (Expression expr) {
5970 var glib_value = (GLibValue) expr.target_value;
5971 if (glib_value == null) {
5972 glib_value = new GLibValue (expr.value_type);
5973 expr.target_value = glib_value;
5975 return glib_value.array_length_cvalues;
5979 public class Vala.GLibValue : TargetValue {
5980 public CCodeExpression cvalue;
5982 public List<CCodeExpression> array_length_cvalues;
5983 public CCodeExpression? array_size_cvalue;
5985 public CCodeExpression? delegate_target_cvalue;
5986 public CCodeExpression? delegate_target_destroy_notify_cvalue;
5988 public GLibValue (DataType? value_type = null, CCodeExpression? cvalue = null) {
5989 base (value_type);
5990 this.cvalue = cvalue;
5993 public void append_array_length_cvalue (CCodeExpression length_cvalue) {
5994 if (array_length_cvalues == null) {
5995 array_length_cvalues = new ArrayList<CCodeExpression> ();
5997 array_length_cvalues.add (length_cvalue);