valacodegenerator.vala 95.5 KB
Newer Older
1
2
/* valacodegenerator.vala
 *
3
 * Copyright (C) 2006-2007  Jürg Billeter, Raffaele Sandrini
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.

 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.

 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301  USA
 *
 * Author:
 * 	Jürg Billeter <j@bitron.ch>
21
 *	Raffaele Sandrini <rasa@gmx.ch>
22
23
24
 */

using GLib;
25
using Gee;
26

27
28
29
30
31
32
33
34
35
/**
 * Code visitor generating C Code.
 */
public class Vala.CodeGenerator : CodeVisitor {
	/**
	 * Specifies whether automatic memory management is active.
	 */
	public bool memory_management { get; set; }
	
36
37
	private CodeContext context;
	
38
	Symbol root_symbol;
39
40
	Symbol current_symbol;
	Symbol current_type_symbol;
41
	Class current_class;
42
	Method current_method;
43
	TypeReference current_return_type;
44
	TryStatement current_try;
45
46
47
48
49
50
51
52

	CCodeFragment header_begin;
	CCodeFragment header_type_declaration;
	CCodeFragment header_type_definition;
	CCodeFragment header_type_member_declaration;
	CCodeFragment source_begin;
	CCodeFragment source_include_directives;
	CCodeFragment source_type_member_declaration;
53
	CCodeFragment source_signal_marshaller_declaration;
54
	CCodeFragment source_type_member_definition;
55
	CCodeFragment class_init_fragment;
56
57
	CCodeFragment instance_init_fragment;
	CCodeFragment instance_dispose_fragment;
58
	CCodeFragment source_signal_marshaller_definition;
59
	CCodeFragment module_init_fragment;
60
61
62
63
64
65
66
67
68
69
	
	CCodeStruct instance_struct;
	CCodeStruct type_struct;
	CCodeStruct instance_priv_struct;
	CCodeEnum prop_enum;
	CCodeEnum cenum;
	CCodeFunction function;
	CCodeBlock block;
	
	/* all temporary variables */
70
	ArrayList<VariableDeclarator> temp_vars = new ArrayList<VariableDeclarator> ();
71
	/* temporary variables that own their content */
72
	ArrayList<VariableDeclarator> temp_ref_vars = new ArrayList<VariableDeclarator> ();
73
	/* cache to check whether a certain marshaller has been created yet */
74
	Gee.Set<string> user_marshal_set;
75
	/* (constant) hash table with all predefined marshallers */
76
	Gee.Set<string> predefined_marshal_set;
77
	/* (constant) hash table with all C keywords */
78
	Gee.Set<string> c_keywords;
79
80
	
	private int next_temp_var_id = 0;
81
	private int current_try_id = 0;
82
	private int next_try_id = 0;
83
	private bool in_creation_method = false;
84
	private bool current_method_inner_error = false;
85

86
	TypeReference bool_type;
87
88
89
90
	TypeReference char_type;
	TypeReference unichar_type;
	TypeReference short_type;
	TypeReference ushort_type;
91
	TypeReference int_type;
92
93
94
	TypeReference uint_type;
	TypeReference long_type;
	TypeReference ulong_type;
95
96
	TypeReference int64_type;
	TypeReference uint64_type;
97
	TypeReference string_type;
98
99
	TypeReference float_type;
	TypeReference double_type;
100
	DataType gobject_type;
101
	DataType gerror_type;
102
103
	DataType glist_type;
	DataType gslist_type;
Jürg Billeter's avatar
Jürg Billeter committed
104
	DataType gstring_type;
105
	DataType garray_type;
106
	TypeReference mutex_type;
107
	DataType type_module_type;
108
109
	DataType iterable_type;
	DataType iterator_type;
110
111
	DataType list_type;
	DataType map_type;
112
	DataType connection_type;
113

114
115
	Method substring_method;

116
117
	private bool in_plugin = false;
	private string module_init_param_name;
118
119
	
	private bool string_h_needed;
Jürg Billeter's avatar
Jürg Billeter committed
120
121
	private bool requires_free_checked;
	private bool requires_array_free;
Jürg Billeter's avatar
Jürg Billeter committed
122
	private bool requires_array_move;
123

124
	public CodeGenerator (bool manage_memory = true) {
125
		memory_management = manage_memory;
126
127
128
	}
	
	construct {
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
		predefined_marshal_set = new HashSet<string> (str_hash, str_equal);
		predefined_marshal_set.add ("VOID:VOID");
		predefined_marshal_set.add ("VOID:BOOLEAN");
		predefined_marshal_set.add ("VOID:CHAR");
		predefined_marshal_set.add ("VOID:UCHAR");
		predefined_marshal_set.add ("VOID:INT");
		predefined_marshal_set.add ("VOID:UINT");
		predefined_marshal_set.add ("VOID:LONG");
		predefined_marshal_set.add ("VOID:ULONG");
		predefined_marshal_set.add ("VOID:ENUM");
		predefined_marshal_set.add ("VOID:FLAGS");
		predefined_marshal_set.add ("VOID:FLOAT");
		predefined_marshal_set.add ("VOID:DOUBLE");
		predefined_marshal_set.add ("VOID:STRING");
		predefined_marshal_set.add ("VOID:POINTER");
		predefined_marshal_set.add ("VOID:OBJECT");
		predefined_marshal_set.add ("STRING:OBJECT,POINTER");
		predefined_marshal_set.add ("VOID:UINT,POINTER");
		predefined_marshal_set.add ("BOOLEAN:FLAGS");

		c_keywords = new HashSet<string> (str_hash, str_equal);
150
151

		// C99 keywords
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
		c_keywords.add ("_Bool");
		c_keywords.add ("_Complex");
		c_keywords.add ("_Imaginary");
		c_keywords.add ("auto");
		c_keywords.add ("break");
		c_keywords.add ("case");
		c_keywords.add ("char");
		c_keywords.add ("const");
		c_keywords.add ("continue");
		c_keywords.add ("default");
		c_keywords.add ("do");
		c_keywords.add ("double");
		c_keywords.add ("else");
		c_keywords.add ("enum");
		c_keywords.add ("extern");
		c_keywords.add ("float");
		c_keywords.add ("for");
		c_keywords.add ("goto");
		c_keywords.add ("if");
		c_keywords.add ("inline");
		c_keywords.add ("int");
		c_keywords.add ("long");
		c_keywords.add ("register");
		c_keywords.add ("restrict");
		c_keywords.add ("return");
		c_keywords.add ("short");
		c_keywords.add ("signed");
		c_keywords.add ("sizeof");
		c_keywords.add ("static");
		c_keywords.add ("struct");
		c_keywords.add ("switch");
		c_keywords.add ("typedef");
		c_keywords.add ("union");
		c_keywords.add ("unsigned");
		c_keywords.add ("void");
		c_keywords.add ("volatile");
		c_keywords.add ("while");
189
190

		// MSVC keywords
191
		c_keywords.add ("cdecl");
192
	}
193

194
195
196
197
198
199
	/**
	 * Generate and emit C code for the specified code context.
	 *
	 * @param context a code context
	 */
	public void emit (CodeContext! context) {
200
201
		this.context = context;
	
202
		context.find_header_cycles ();
203

204
		root_symbol = context.root;
205
206

		bool_type = new TypeReference ();
207
		bool_type.data_type = (DataType) root_symbol.scope.lookup ("bool");
208

209
		char_type = new TypeReference ();
210
		char_type.data_type = (DataType) root_symbol.scope.lookup ("char");
211
212

		unichar_type = new TypeReference ();
213
		unichar_type.data_type = (DataType) root_symbol.scope.lookup ("unichar");
214
215

		short_type = new TypeReference ();
216
		short_type.data_type = (DataType) root_symbol.scope.lookup ("short");
217
218
		
		ushort_type = new TypeReference ();
219
		ushort_type.data_type = (DataType) root_symbol.scope.lookup ("ushort");
220

221
		int_type = new TypeReference ();
222
		int_type.data_type = (DataType) root_symbol.scope.lookup ("int");
223
224
		
		uint_type = new TypeReference ();
225
		uint_type.data_type = (DataType) root_symbol.scope.lookup ("uint");
226
227
		
		long_type = new TypeReference ();
228
		long_type.data_type = (DataType) root_symbol.scope.lookup ("long");
229
230
		
		ulong_type = new TypeReference ();
231
		ulong_type.data_type = (DataType) root_symbol.scope.lookup ("ulong");
232

233
		int64_type = new TypeReference ();
234
		int64_type.data_type = (DataType) root_symbol.scope.lookup ("int64");
235
236
		
		uint64_type = new TypeReference ();
237
		uint64_type.data_type = (DataType) root_symbol.scope.lookup ("uint64");
238
		
239
		float_type = new TypeReference ();
240
		float_type.data_type = (DataType) root_symbol.scope.lookup ("float");
241
242

		double_type = new TypeReference ();
243
		double_type.data_type = (DataType) root_symbol.scope.lookup ("double");
244

245
		string_type = new TypeReference ();
246
247
		string_type.data_type = (DataType) root_symbol.scope.lookup ("string");
		substring_method = (Method) string_type.data_type.scope.lookup ("substring");
248

249
		var glib_ns = root_symbol.scope.lookup ("GLib");
250
		
251
		gobject_type = (DataType) glib_ns.scope.lookup ("Object");
252
		gerror_type = (DataType) glib_ns.scope.lookup ("Error");
253
254
		glist_type = (DataType) glib_ns.scope.lookup ("List");
		gslist_type = (DataType) glib_ns.scope.lookup ("SList");
Jürg Billeter's avatar
Jürg Billeter committed
255
		gstring_type = (DataType) glib_ns.scope.lookup ("String");
256
		garray_type = (DataType) glib_ns.scope.lookup ("Array");
257
258
		
		mutex_type = new TypeReference ();
259
		mutex_type.data_type = (DataType) glib_ns.scope.lookup ("Mutex");
260
		
261
		type_module_type = (DataType) glib_ns.scope.lookup ("TypeModule");
262
263
264
265
266
267
268
269
270
271
272

		if (context.module_init_method != null) {
			module_init_fragment = new CCodeFragment ();
			foreach (FormalParameter parameter in context.module_init_method.get_parameters ()) {
				if (parameter.type_reference.data_type == type_module_type) {
					in_plugin = true;
					module_init_param_name = parameter.name;
					break;
				}
			}
		}
273
274
275
276
277

		var gee_ns = root_symbol.scope.lookup ("Gee");
		if (gee_ns != null) {
			iterable_type = (DataType) gee_ns.scope.lookup ("Iterable");
			iterator_type = (DataType) gee_ns.scope.lookup ("Iterator");
278
279
			list_type = (DataType) gee_ns.scope.lookup ("List");
			map_type = (DataType) gee_ns.scope.lookup ("Map");
280
		}
281
282
283
284
285

		var dbus_ns = root_symbol.scope.lookup ("DBus");
		if (dbus_ns != null) {
			connection_type = (DataType) dbus_ns.scope.lookup ("Connection");
		}
286
287
288
289
290
291
	
		/* we're only interested in non-pkg source files */
		var source_files = context.get_source_files ();
		foreach (SourceFile file in source_files) {
			if (!file.pkg) {
				file.accept (this);
292
			}
293
		}
294
	}
295

296
	public override void visit_enum (Enum! en) {
297
		cenum = new CCodeEnum (en.get_cname ());
298

299
		if (en.source_reference.comment != null) {
300
			header_type_definition.append (new CCodeComment (en.source_reference.comment));
301
		}
302
		header_type_definition.append (cenum);
303
304

		en.accept_children (this);
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324

		if (en.error_domain) {
			string quark_fun_name = en.get_lower_case_cprefix () + "quark";

			var error_domain_define = new CCodeMacroReplacement (en.get_upper_case_cname (), quark_fun_name + " ()");
			header_type_definition.append (error_domain_define);

			var cquark_fun = new CCodeFunction (quark_fun_name, "GQuark");
			var cquark_block = new CCodeBlock ();

			var cquark_call = new CCodeFunctionCall (new CCodeIdentifier ("g_quark_from_static_string"));
			cquark_call.add_argument (new CCodeConstant ("\"" + en.get_lower_case_cname () + "-quark\""));

			cquark_block.add_statement (new CCodeReturnStatement (cquark_call));

			header_type_member_declaration.append (cquark_fun.copy ());

			cquark_fun.block = cquark_block;
			source_type_member_definition.append (cquark_fun);
		}
325
	}
326

327
	public override void visit_enum_value (EnumValue! ev) {
328
329
330
331
332
333
334
335
		string val;
		if (ev.value is LiteralExpression) {
			var lit = ((LiteralExpression) ev.value).literal;
			if (lit is IntegerLiteral) {
				val = ((IntegerLiteral) lit).value;
			}
		}
		cenum.add_value (ev.get_cname (), val);
336
	}
337

338
339
340
	public override void visit_callback (Callback! cb) {
		cb.accept_children (this);

341
		var cfundecl = new CCodeFunctionDeclarator (cb.get_cname ());
342
343
344
		foreach (FormalParameter param in cb.get_parameters ()) {
			cfundecl.add_parameter ((CCodeFormalParameter) param.ccodenode);
		}
345
346
		
		var ctypedef = new CCodeTypeDefinition (cb.return_type.get_cname (), cfundecl);
347
		
348
		if (!cb.is_internal_symbol ()) {
349
			header_type_definition.append (ctypedef);
350
351
		} else {
			source_type_member_declaration.append (ctypedef);
352
		}
353
	}
354
355
356
357
	
	public override void visit_member (Member! m) {
		/* stuff meant for all lockable members */
		if (m is Lockable && ((Lockable)m).get_lock_used ()) {
358
			instance_priv_struct.add_field (mutex_type.get_cname (), get_symbol_lock_name (m));
359
360
361
362
363
364
			
			instance_init_fragment.append (
				new CCodeExpressionStatement (
					new CCodeAssignment (
						new CCodeMemberAccess.pointer (
							new CCodeMemberAccess.pointer (new CCodeIdentifier ("self"), "priv"),
365
							get_symbol_lock_name (m)),
366
367
					new CCodeFunctionCall (new CCodeIdentifier (((Struct)mutex_type.data_type).default_construction_method.get_cname ())))));
			
Jürg Billeter's avatar
Jürg Billeter committed
368
			requires_free_checked = true;
369
370
371
372
			var fc = new CCodeFunctionCall (new CCodeIdentifier ("VALA_FREE_CHECKED"));
			fc.add_argument (
				new CCodeMemberAccess.pointer (
					new CCodeMemberAccess.pointer (new CCodeIdentifier ("self"), "priv"),
373
					get_symbol_lock_name (m)));
374
375
376
377
			if (mutex_type.data_type.get_free_function () == null) {
				Report.error (mutex_type.data_type.source_reference, "The type `%s` doesn't contain a free function".printf (mutex_type.data_type.get_full_name ()));
				return;
			}
378
			fc.add_argument (new CCodeIdentifier (mutex_type.data_type.get_free_function ()));
379
380
381
			if (instance_dispose_fragment != null) {
				instance_dispose_fragment.append (new CCodeExpressionStatement (fc));
			}
382
383
		}
	}
384

385
	public override void visit_constant (Constant! c) {
386
387
		c.accept_children (this);

388
389
		if (c.parent_symbol is DataType) {
			var t = (DataType) c.parent_symbol;
390
			var cdecl = new CCodeDeclaration (c.type_reference.get_const_cname ());
391
			var arr = "";
392
			if (c.type_reference.data_type is Array) {
393
				arr = "[]";
394
			}
395
			cdecl.add_declarator (new CCodeVariableDeclarator.with_initializer ("%s%s".printf (c.get_cname (), arr), (CCodeExpression) c.initializer.ccodenode));
396
			cdecl.modifiers = CCodeModifiers.STATIC;
397
			
398
			if (!c.is_internal_symbol ()) {
399
400
401
402
				header_type_member_declaration.append (cdecl);
			} else {
				source_type_member_declaration.append (cdecl);
			}
403
		}
404
405
406
	}
	
	public override void visit_field (Field! f) {
407
408
		f.accept_children (this);

409
		CCodeExpression lhs = null;
410
		CCodeStruct st = null;
411
		
412
		if (f.access != SymbolAccessibility.PRIVATE) {
413
			st = instance_struct;
414
415
			if (f.instance) {
				lhs = new CCodeMemberAccess.pointer (new CCodeIdentifier ("self"), f.get_cname ());
416
417
418
419
420
421
422
423
424
425
426
427
428
429
			} else {
				var cdecl = new CCodeDeclaration (f.type_reference.get_cname ());
				cdecl.add_declarator (new CCodeVariableDeclarator (f.get_cname ()));
				header_type_member_declaration.append (cdecl);

				cdecl = new CCodeDeclaration (f.type_reference.get_cname ());
				var var_decl = new CCodeVariableDeclarator (f.get_cname ());
				if (f.initializer != null) {
					var init = (CCodeExpression) f.initializer.ccodenode;
					if (is_constant_ccode_expression (init)) {
						var_decl.initializer = init;
					}
				}
				cdecl.add_declarator (var_decl);
430
				cdecl.modifiers = CCodeModifiers.EXTERN;
431
432
433
				source_type_member_declaration.append (cdecl);

				lhs = new CCodeIdentifier (f.get_cname ());
434
			}
435
		} else if (f.access == SymbolAccessibility.PRIVATE) {
436
			if (f.instance) {
437
				st = instance_priv_struct;
438
				lhs = new CCodeMemberAccess.pointer (new CCodeMemberAccess.pointer (new CCodeIdentifier ("self"), "priv"), f.get_cname ());
439
			} else {
440
441
442
443
444
445
				var cdecl = new CCodeDeclaration (f.type_reference.get_cname ());
				var var_decl = new CCodeVariableDeclarator (f.get_cname ());
				if (f.initializer != null) {
					var init = (CCodeExpression) f.initializer.ccodenode;
					if (is_constant_ccode_expression (init)) {
						var_decl.initializer = init;
Jürg Billeter's avatar
Jürg Billeter committed
446
					}
447
				}
448
449
450
451
452
				cdecl.add_declarator (var_decl);
				cdecl.modifiers = CCodeModifiers.STATIC;
				source_type_member_declaration.append (cdecl);

				lhs = new CCodeIdentifier (f.get_cname ());
453
			}
454
		}
455

Jürg Billeter's avatar
Jürg Billeter committed
456
457
458
459
460
461
462
463
464
		if (f.instance)  {
			st.add_field (f.type_reference.get_cname (), f.get_cname ());
			if (f.type_reference.data_type is Array && !f.no_array_length) {
				// create fields to store array dimensions
				var arr = (Array) f.type_reference.data_type;
				
				for (int dim = 1; dim <= arr.rank; dim++) {
					var len_type = new TypeReference ();
					len_type.data_type = int_type.data_type;
465

Jürg Billeter's avatar
Jürg Billeter committed
466
467
					st.add_field (len_type.get_cname (), get_array_length_cname (f.name, dim));
				}
468
			}
Jürg Billeter's avatar
Jürg Billeter committed
469

Jürg Billeter's avatar
Jürg Billeter committed
470
			if (f.initializer != null) {
471
472
473
474
475
476
477
478
479
480
481
				var rhs = (CCodeExpression) f.initializer.ccodenode;
				if (f.type_reference.data_type != null
				    && f.initializer.static_type.data_type != null
				    && f.type_reference.data_type.is_reference_type ()
				    && f.initializer.static_type.data_type != f.type_reference.data_type) {
					// FIXME: use C cast if debugging disabled
					rhs = new InstanceCast (rhs, f.type_reference.data_type);
				}

				instance_init_fragment.append (new CCodeExpressionStatement (new CCodeAssignment (lhs, rhs)));

Jürg Billeter's avatar
Jürg Billeter committed
482
483
				if (f.type_reference.data_type is Array && !f.no_array_length &&
				    f.initializer is ArrayCreationExpression) {
484
					var array = (Array) f.type_reference.data_type;
Jürg Billeter's avatar
Jürg Billeter committed
485
					var ma = new MemberAccess.simple (f.name);
486
					ma.symbol_reference = f;
Jürg Billeter's avatar
Jürg Billeter committed
487
					
488
					Gee.List<Expression> sizes = ((ArrayCreationExpression) f.initializer).get_sizes ();
489
490
491
492
493
					for (int dim = 1; dim <= array.rank; dim++) {
						var array_len_lhs = get_array_length_cexpression (ma, dim);
						var size = sizes[dim - 1];
						instance_init_fragment.append (new CCodeExpressionStatement (new CCodeAssignment (array_len_lhs, (CCodeExpression) size.ccodenode)));
					}
Jürg Billeter's avatar
Jürg Billeter committed
494
				}
495
			}
496
			
Jürg Billeter's avatar
Jürg Billeter committed
497
			if (f.type_reference.takes_ownership && instance_dispose_fragment != null) {
Jürg Billeter's avatar
Jürg Billeter committed
498
499
500
				var ma = new MemberAccess.simple (f.name);
				ma.symbol_reference = f;
				instance_dispose_fragment.append (new CCodeExpressionStatement (get_unref_expression (lhs, f.type_reference, ma)));
501
			}
502
503
504
505
506
507
508
509
510
511
512
513
514
		} else {
			if (f.initializer != null) {
				var rhs = (CCodeExpression) f.initializer.ccodenode;
				if (!is_constant_ccode_expression (rhs)) {
					if (f.parent_symbol is Class) {
						class_init_fragment.append (new CCodeExpressionStatement (new CCodeAssignment (lhs, rhs)));
					} else {
						f.error = true;
						Report.error (f.source_reference, "Non-constant field initializers not supported in this context");
						return;
					}
				}
			}
515
		}
516
	}
517

518
519
520
521
	private bool is_constant_ccode_expression (CCodeExpression! cexpr) {
		return (cexpr is CCodeConstant);
	}

522
	public override void visit_formal_parameter (FormalParameter! p) {
523
524
		p.accept_children (this);

525
		if (!p.ellipsis) {
526
			p.ccodenode = new CCodeFormalParameter (p.name, p.type_reference.get_cname (false, !p.type_reference.takes_ownership));
527
		}
528
	}
529

530
531
532
	public override void visit_property (Property! prop) {
		prop.accept_children (this);

533
534
535
		if (prop.parent_symbol is Class) {
			prop_enum.add_value (prop.get_upper_case_cname (), null);
		}
536
	}
537

538
	public override void visit_property_accessor (PropertyAccessor! acc) {
539
		var prop = (Property) acc.prop;
540
541
542
543
544
545
546
547
		
		if (acc.readable) {
			current_return_type = prop.type_reference;
		} else {
			// void
			current_return_type = new TypeReference ();
		}

548
		acc.accept_children (this);
549
550

		current_return_type = null;
551

552
		var t = (DataType) prop.parent_symbol;
553

554
		var this_type = new TypeReference ();
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
		this_type.data_type = t;
		var cselfparam = new CCodeFormalParameter ("self", this_type.get_cname ());
		var cvalueparam = new CCodeFormalParameter ("value", prop.type_reference.get_cname (false, true));

		if (prop.is_abstract || prop.is_virtual) {
			if (acc.readable) {
				function = new CCodeFunction ("%s_get_%s".printf (t.get_lower_case_cname (null), prop.name), prop.type_reference.get_cname ());
			} else {
				function = new CCodeFunction ("%s_set_%s".printf (t.get_lower_case_cname (null), prop.name), "void");
			}
			function.add_parameter (cselfparam);
			if (acc.writable || acc.construction) {
				function.add_parameter (cvalueparam);
			}
			
570
			if (!prop.is_internal_symbol ()) {
571
572
573
574
575
				header_type_member_declaration.append (function.copy ());
			} else {
				function.modifiers |= CCodeModifiers.STATIC;
				source_type_member_declaration.append (function.copy ());
			}
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
			
			var block = new CCodeBlock ();
			function.block = block;

			if (acc.readable) {
				// declare temporary variable to save the property value
				var decl = new CCodeDeclaration (prop.type_reference.get_cname ());
				decl.add_declarator (new CCodeVariableDeclarator ("value"));
				block.add_statement (decl);
			
				var ccall = new CCodeFunctionCall (new CCodeIdentifier ("g_object_get"));
			
				var ccast = new CCodeFunctionCall (new CCodeIdentifier ("G_OBJECT"));
				ccast.add_argument (new CCodeIdentifier ("self"));
				ccall.add_argument (ccast);
				
				// property name is second argument of g_object_get
				ccall.add_argument (prop.get_canonical_cconstant ());

				ccall.add_argument (new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, new CCodeIdentifier ("value")));

				ccall.add_argument (new CCodeConstant ("NULL"));
				
				block.add_statement (new CCodeExpressionStatement (ccall));
				block.add_statement (new CCodeReturnStatement (new CCodeIdentifier ("value")));
			} else {
				var ccall = new CCodeFunctionCall (new CCodeIdentifier ("g_object_set"));
			
				var ccast = new CCodeFunctionCall (new CCodeIdentifier ("G_OBJECT"));
				ccast.add_argument (new CCodeIdentifier ("self"));
				ccall.add_argument (ccast);
				
				// property name is second argument of g_object_set
				ccall.add_argument (prop.get_canonical_cconstant ());

				ccall.add_argument (new CCodeIdentifier ("value"));

				ccall.add_argument (new CCodeConstant ("NULL"));
				
				block.add_statement (new CCodeExpressionStatement (ccall));
			}

			source_type_member_definition.append (function);
619
		}
620

621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
		if (!prop.is_abstract) {
			bool is_virtual = prop.base_property != null || prop.base_interface_property != null;

			string prefix = t.get_lower_case_cname (null);
			if (is_virtual) {
				prefix += "_real";
			}
			if (acc.readable) {
				function = new CCodeFunction ("%s_get_%s".printf (prefix, prop.name), prop.type_reference.get_cname ());
			} else {
				function = new CCodeFunction ("%s_set_%s".printf (prefix, prop.name), "void");
			}
			if (is_virtual) {
				function.modifiers |= CCodeModifiers.STATIC;
			}
			function.add_parameter (cselfparam);
			if (acc.writable || acc.construction) {
				function.add_parameter (cvalueparam);
			}

			if (!is_virtual) {
642
				if (!prop.is_internal_symbol ()) {
643
644
645
646
647
					header_type_member_declaration.append (function.copy ());
				} else {
					function.modifiers |= CCodeModifiers.STATIC;
					source_type_member_declaration.append (function.copy ());
				}
648
649
650
651
652
653
654
655
656
			}
			
			if (acc.body != null) {
				function.block = (CCodeBlock) acc.body.ccodenode;

				function.block.prepend_statement (create_property_type_check_statement (prop, acc.readable, t, true, "self"));
			}
			
			source_type_member_definition.append (function);
657
658
		}
	}
Jürg Billeter's avatar
Jürg Billeter committed
659

660
	public override void visit_constructor (Constructor! c) {
661
662
		current_method_inner_error = false;

663
664
		c.accept_children (this);

665
		var cl = (Class) c.parent_symbol;
666
	
667
		function = new CCodeFunction ("%s_constructor".printf (cl.get_lower_case_cname (null)), "GObject *");
668
669
		function.modifiers = CCodeModifiers.STATIC;
		
670
671
672
		function.add_parameter (new CCodeFormalParameter ("type", "GType"));
		function.add_parameter (new CCodeFormalParameter ("n_construct_properties", "guint"));
		function.add_parameter (new CCodeFormalParameter ("construct_properties", "GObjectConstructParam *"));
673
674
		
		source_type_member_declaration.append (function.copy ());
675
676


677
		var cblock = new CCodeBlock ();
678
679
		var cdecl = new CCodeDeclaration ("GObject *");
		cdecl.add_declarator (new CCodeVariableDeclarator ("obj"));
680
		cblock.add_statement (cdecl);
681

682
683
		cdecl = new CCodeDeclaration ("%sClass *".printf (cl.get_cname ()));
		cdecl.add_declarator (new CCodeVariableDeclarator ("klass"));
684
		cblock.add_statement (cdecl);
685

686
687
		cdecl = new CCodeDeclaration ("GObjectClass *");
		cdecl.add_declarator (new CCodeVariableDeclarator ("parent_class"));
688
		cblock.add_statement (cdecl);
689
690


691
692
693
		var ccall = new CCodeFunctionCall (new CCodeIdentifier ("g_type_class_peek"));
		ccall.add_argument (new CCodeIdentifier (cl.get_upper_case_cname ("TYPE_")));
		var ccast = new CCodeFunctionCall (new CCodeIdentifier ("%s_CLASS".printf (cl.get_upper_case_cname (null))));
694
		ccast.add_argument (ccall);
695
		cblock.add_statement (new CCodeExpressionStatement (new CCodeAssignment (new CCodeIdentifier ("klass"), ccast)));
696

697
698
699
		ccall = new CCodeFunctionCall (new CCodeIdentifier ("g_type_class_peek_parent"));
		ccall.add_argument (new CCodeIdentifier ("klass"));
		ccast = new CCodeFunctionCall (new CCodeIdentifier ("G_OBJECT_CLASS"));
700
		ccast.add_argument (ccall);
701
		cblock.add_statement (new CCodeExpressionStatement (new CCodeAssignment (new CCodeIdentifier ("parent_class"), ccast)));
702

703
		
704
705
706
707
708
		ccall = new CCodeFunctionCall (new CCodeMemberAccess.pointer (new CCodeIdentifier ("parent_class"), "constructor"));
		ccall.add_argument (new CCodeIdentifier ("type"));
		ccall.add_argument (new CCodeIdentifier ("n_construct_properties"));
		ccall.add_argument (new CCodeIdentifier ("construct_properties"));
		cblock.add_statement (new CCodeExpressionStatement (new CCodeAssignment (new CCodeIdentifier ("obj"), ccall)));
709
710


711
712
		ccall = new CCodeFunctionCall (new CCodeIdentifier (cl.get_upper_case_cname (null)));
		ccall.add_argument (new CCodeIdentifier ("obj"));
713
		
714
715
		cdecl = new CCodeDeclaration ("%s *".printf (cl.get_cname ()));
		cdecl.add_declarator (new CCodeVariableDeclarator.with_initializer ("self", ccall));
716
717
		
		cblock.add_statement (cdecl);
718

719
720
721
722
723
724
725
726
727
		if (current_method_inner_error) {
			/* always separate error parameter and inner_error local variable
			 * as error may be set to NULL but we're always interested in inner errors
			 */
			var cdecl = new CCodeDeclaration ("GError *");
			cdecl.add_declarator (new CCodeVariableDeclarator.with_initializer ("inner_error", new CCodeConstant ("NULL")));
			cblock.add_statement (cdecl);
		}

728
729
730

		cblock.add_statement (c.body.ccodenode);
		
731
		cblock.add_statement (new CCodeReturnStatement (new CCodeIdentifier ("obj")));
732
733
734
735
		
		function.block = cblock;

		if (c.source_reference.comment != null) {
736
			source_type_member_definition.append (new CCodeComment (c.source_reference.comment));
737
		}
738
739
		source_type_member_definition.append (function);
	}
740

741
742
743
744
	public override void visit_destructor (Destructor! d) {
		d.accept_children (this);
	}

745
	public override void visit_begin_block (Block! b) {
746
		current_symbol = b;
747
	}
748

749
750
751
	public override void visit_end_block (Block! b) {
		var local_vars = b.get_local_variables ();
		foreach (VariableDeclarator decl in local_vars) {
752
			decl.active = false;
753
		}
754
		
755
		var cblock = new CCodeBlock ();
756
		
757
		foreach (CodeNode stmt in b.get_statements ()) {
758
759
			var src = stmt.source_reference;
			if (src != null && src.comment != null) {
760
				cblock.add_statement (new CCodeComment (src.comment));
761
			}
762
			
763
			if (stmt.ccodenode is CCodeFragment) {
764
				foreach (CCodeStatement cstmt in ((CCodeFragment) stmt.ccodenode).get_children ()) {
765
					cblock.add_statement (cstmt);
766
				}
767
768
			} else {
				cblock.add_statement ((CCodeStatement) stmt.ccodenode);
769
			}
770
771
772
773
		}
		
		if (memory_management) {
			foreach (VariableDeclarator decl in local_vars) {
774
				if (decl.type_reference.takes_ownership) {
Jürg Billeter's avatar
Jürg Billeter committed
775
776
777
					var ma = new MemberAccess.simple (decl.name);
					ma.symbol_reference = decl;
					cblock.add_statement (new CCodeExpressionStatement (get_unref_expression (new CCodeIdentifier (get_variable_cname (decl.name)), decl.type_reference, ma)));
778
779
				}
			}
780
		}
781
782
		
		b.ccodenode = cblock;
783

784
785
		current_symbol = current_symbol.parent_symbol;
	}
786

787
788
789
	public override void visit_empty_statement (EmptyStatement! stmt) {
		stmt.ccodenode = new CCodeEmptyStatement ();
	}
790
791
792
793
794
795
796
797
798
799
	
	private bool struct_has_instance_fields (Struct! st) {
		foreach (Field f in st.get_fields ()) {
			if (f.instance) {
				return true;
			}
		}
		
		return false;
	}
800

801
802
803
804
805
806
	public override void visit_declaration_statement (DeclarationStatement! stmt) {
		/* split declaration statement as var declarators
		 * might have different types */
	
		var cfrag = new CCodeFragment ();
		
807
		foreach (VariableDeclarator decl in stmt.declaration.get_variable_declarators ()) {
808
			var cdecl = new CCodeDeclaration (decl.type_reference.get_cname (false, !decl.type_reference.takes_ownership));
809
810
		
			cdecl.add_declarator ((CCodeVariableDeclarator) decl.ccodenode);
811

812
			cfrag.append (cdecl);
813
814
815
816
817

			if (decl.initializer != null && decl.initializer.can_fail) {
				add_simple_check (decl.initializer, cfrag);
			}

818
			/* try to initialize uninitialized variables */
819
			if (decl.initializer == null && decl.type_reference.data_type is Struct) {
820
821
822
823
824
825
826
827
				if (decl.type_reference.data_type.is_reference_type ()) {
					((CCodeVariableDeclarator) decl.ccodenode).initializer = new CCodeConstant ("NULL");
				} else if (decl.type_reference.data_type.get_default_value () != null) {
					((CCodeVariableDeclarator) decl.ccodenode).initializer = new CCodeConstant (decl.type_reference.data_type.get_default_value ());
				} else if (decl.type_reference.data_type is Struct &&
				           struct_has_instance_fields ((Struct) decl.type_reference.data_type)) {
					var st = (Struct) decl.type_reference.data_type;

828
829
					/* memset needs string.h */
					string_h_needed = true;
830

831
					var czero = new CCodeFunctionCall (new CCodeIdentifier ("memset"));
832
					czero.add_argument (new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, new CCodeIdentifier (get_variable_cname (decl.name))));
833
834
					czero.add_argument (new CCodeConstant ("0"));
					czero.add_argument (new CCodeIdentifier ("sizeof (%s)".printf (decl.type_reference.get_cname ())));
835

836
					cfrag.append (new CCodeExpressionStatement (czero));
837
				} else {
838
					Report.warning (decl.source_reference, "unable to initialize a variable of type `%s'".printf (decl.type_reference.data_type.get_full_name ()));
839
840
				}
			}
841
		}
842
843
		
		stmt.ccodenode = cfrag;
844

845
		foreach (VariableDeclarator decl in stmt.declaration.get_variable_declarators ()) {
846
			if (decl.initializer != null) {
847
				create_temp_decl (stmt, decl.initializer.temp_vars);
848
849
			}
		}
850
851

		create_temp_decl (stmt, temp_vars);
852
		temp_vars.clear ();
853
	}
854

855
	private string! get_variable_cname (string! name) {
856
		if (c_keywords.contains (name)) {
857
858
859
860
861
862
			return name + "_";
		} else {
			return name;
		}
	}

863
	public override void visit_variable_declarator (VariableDeclarator! decl) {
864
865
		decl.accept_children (this);

866
867
868
869
870
871
872
873
874
		if (decl.type_reference.data_type is Array) {
			// create variables to store array dimensions
			var arr = (Array) decl.type_reference.data_type;
			
			for (int dim = 1; dim <= arr.rank; dim++) {
				var len_decl = new VariableDeclarator (get_array_length_cname (decl.name, dim));
				len_decl.type_reference = new TypeReference ();
				len_decl.type_reference.data_type = int_type.data_type;

875
				temp_vars.insert (0, len_decl);
876
877
878
			}
		}
	
879
880
881
882
		CCodeExpression rhs = null;
		if (decl.initializer != null) {
			rhs = (CCodeExpression) decl.initializer.ccodenode;
			
883
884
885
886
			if (decl.type_reference.data_type != null
			    && decl.initializer.static_type.data_type != null
			    && decl.type_reference.data_type.is_reference_type ()
			    && decl.initializer.static_type.data_type != decl.type_reference.data_type) {
887
				// FIXME: use C cast if debugging disabled
888
				rhs = new InstanceCast (rhs, decl.type_reference.data_type);
889
			}
890
891

			if (decl.type_reference.data_type is Array) {
892
893
				var arr = (Array) decl.type_reference.data_type;

894
				var ccomma = new CCodeCommaExpression ();
895

896
				var temp_decl = get_temp_variable_declarator (decl.type_reference);
897
				temp_vars.insert (0, temp_decl);
898
				ccomma.append_expression (new CCodeAssignment (new CCodeIdentifier (temp_decl.name), rhs));
899
900
901
902
903
904

				for (int dim = 1; dim <= arr.rank; dim++) {
					var lhs_array_len = new CCodeIdentifier (get_array_length_cname (decl.name, dim));
					var rhs_array_len = get_array_length_cexpression (decl.initializer, dim);
					ccomma.append_expression (new CCodeAssignment (lhs_array_len, rhs_array_len));
				}
905
906
907
908
909
				
				ccomma.append_expression (new CCodeIdentifier (temp_decl.name));
				
				rhs = ccomma;
			}
910
		} else if (decl.type_reference.data_type != null && decl.type_reference.data_type.is_reference_type ()) {
911
			rhs = new CCodeConstant ("NULL");
912
		}
913
			
914
		decl.ccodenode = new CCodeVariableDeclarator.with_initializer (get_variable_cname (decl.name), rhs);
915

916
		decl.active = true;
917
918
	}

919
920
921
	public override void visit_initializer_list (InitializerList! list) {
		list.accept_children (this);

922
923
924
925
926
927
928
929
		if (list.expected_type != null && list.expected_type.data_type is Array) {
			/* TODO */
		} else {
			var clist = new CCodeInitializerList ();
			foreach (Expression expr in list.get_initializers ()) {
				clist.append ((CCodeExpression) expr.ccodenode);
			}
			list.ccodenode = clist;
930
		}
931
932
	}
	
933
	private VariableDeclarator get_temp_variable_declarator (TypeReference! type, bool takes_ownership = true) {
934
		var decl = new VariableDeclarator ("__temp%d".printf (next_temp_var_id));
935
		decl.type_reference = type.copy ();
936
		decl.type_reference.is_ref = false;
937
		decl.type_reference.is_out = false;
938
		decl.type_reference.takes_ownership = takes_ownership;
939
		
940
941
942
943
		next_temp_var_id++;
		
		return decl;
	}
944

945
946
947
948
949
950
951
952
953
954
955
	private CCodeExpression get_dup_func_expression (TypeReference! type) {
		if (type.data_type != null) {
			string dup_function;
			if (type.data_type.is_reference_counting ()) {
				dup_function = type.data_type.get_ref_function ();
			} else {
				if (type.data_type != string_type.data_type) {
					// duplicating non-reference counted structs may cause side-effects (and performance issues)
					Report.warning (type.source_reference, "duplicating %s instance, use weak variable or explicitly invoke copy method".printf (type.data_type.name));
				}
				dup_function = type.data_type.get_dup_function ();
956
957
958
				if (dup_function == null) {
					Report.error (type.data_type.source_reference, "The type `%s` doesn't contain a copy function".printf (type.data_type.get_full_name ()));
				}
959
			}
960
961
962
963
964

			if (null != dup_function)
				return new CCodeIdentifier (dup_function);

			return null;
965
966
967
968
969
970
971
972
		} else if (type.type_parameter != null && current_type_symbol is Class) {
			string func_name = "%s_dup_func".printf (type.type_parameter.name.down ());
			return new CCodeMemberAccess.pointer (new CCodeMemberAccess.pointer (new CCodeIdentifier ("self"), "priv"), func_name);
		} else {
			return new CCodeConstant ("NULL");
		}
	}

973
974
975
976
977
978
979
980