1/* valaccodemethodmodule.vala
2 *
3 * Copyright (C) 2007-2010  Jürg Billeter
4 *
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public
7 * License as published by the Free Software Foundation; either
8 * version 2.1 of the License, or (at your option) any later version.
9
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13 * Lesser General Public License for more details.
14
15 * You should have received a copy of the GNU Lesser General Public
16 * License along with this library; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301  USA
18 *
19 * Author:
20 * 	Jürg Billeter <j@bitron.ch>
21 *	Raffaele Sandrini <raffaele@sandrini.ch>
22 */
23
24using GLib;
25
26/**
27 * The link between a method and generated code.
28 */
29public abstract class Vala.CCodeMethodModule : CCodeStructModule {
30
31	private bool ellipses_to_valist = false;
32
33	public override bool method_has_wrapper (Method method) {
34		return (method.get_attribute ("NoWrapper") == null);
35	}
36
37	string get_creturn_type (Method m, string default_value) {
38		string type = get_ccode_type (m);
39		if (type == null) {
40			return default_value;
41		}
42		return type;
43	}
44
45	bool is_gtypeinstance_creation_method (Method m) {
46		bool result = false;
47
48		var cl = m.parent_symbol as Class;
49		if (m is CreationMethod && cl != null && !cl.is_compact) {
50			result = true;
51		}
52
53		return result;
54	}
55
56	public virtual void generate_method_result_declaration (Method m, CCodeFile decl_space, CCodeFunction cfunc, Map<int,CCodeParameter> cparam_map, Map<int,CCodeExpression>? carg_map) {
57		var creturn_type = get_callable_creturn_type (m);
58		cfunc.return_type = get_creturn_type (m, get_ccode_name (creturn_type));
59
60		generate_type_declaration (m.return_type, decl_space);
61
62		if (m.return_type.is_real_non_null_struct_type ()) {
63			// structs are returned via out parameter
64			var cparam = new CCodeParameter ("result", get_ccode_name (m.return_type) + "*");
65			cparam_map.set (get_param_pos (-3), cparam);
66			if (carg_map != null) {
67				carg_map.set (get_param_pos (-3), get_cexpression ("result"));
68			}
69		} else if (get_ccode_array_length (m) && m.return_type is ArrayType) {
70			// return array length if appropriate
71			var array_type = (ArrayType) m.return_type;
72			var length_ctype = get_ccode_array_length_type (m) + "*";
73
74			for (int dim = 1; dim <= array_type.rank; dim++) {
75				var cparam = new CCodeParameter (get_array_length_cname ("result", dim), length_ctype);
76				cparam_map.set (get_param_pos (get_ccode_array_length_pos (m) + 0.01 * dim), cparam);
77				if (carg_map != null) {
78					carg_map.set (get_param_pos (get_ccode_array_length_pos (m) + 0.01 * dim), get_cexpression (cparam.name));
79				}
80			}
81		} else if (get_ccode_delegate_target (m) && m.return_type is DelegateType) {
82			// return delegate target if appropriate
83			var deleg_type = (DelegateType) m.return_type;
84			if (deleg_type.delegate_symbol.has_target) {
85				var cparam = new CCodeParameter (get_delegate_target_cname ("result"), get_ccode_name (delegate_target_type) + "*");
86				cparam_map.set (get_param_pos (get_ccode_delegate_target_pos (m)), cparam);
87				if (carg_map != null) {
88					carg_map.set (get_param_pos (get_ccode_delegate_target_pos (m)), get_cexpression (cparam.name));
89				}
90				if (deleg_type.is_disposable ()) {
91					cparam = new CCodeParameter (get_delegate_target_destroy_notify_cname ("result"), get_ccode_name (delegate_target_destroy_type) + "*");
92					cparam_map.set (get_param_pos (get_ccode_destroy_notify_pos (m)), cparam);
93					if (carg_map != null) {
94						carg_map.set (get_param_pos (get_ccode_destroy_notify_pos (m)), get_cexpression (cparam.name));
95					}
96				}
97			}
98		}
99
100		if (m.has_error_type_parameter ()) {
101			var error_types = new ArrayList<DataType> ();
102			m.get_error_types (error_types);
103			foreach (DataType error_type in error_types) {
104				generate_type_declaration (error_type, decl_space);
105			}
106
107			var cparam = new CCodeParameter ("error", "GError**");
108			cparam_map.set (get_param_pos (get_ccode_error_pos (m)), cparam);
109			if (carg_map != null) {
110				carg_map.set (get_param_pos (get_ccode_error_pos (m)), new CCodeIdentifier (cparam.name));
111			}
112		}
113	}
114
115	public void complete_async () {
116		var data_var = new CCodeIdentifier ("_data_");
117		var async_result_expr = new CCodeMemberAccess.pointer (data_var, "_async_result");
118
119		var finish_call = new CCodeFunctionCall (new CCodeIdentifier ("g_task_return_pointer"));
120		finish_call.add_argument (async_result_expr);
121		finish_call.add_argument (data_var);
122		finish_call.add_argument (new CCodeConstant ("NULL"));
123		ccode.add_expression (finish_call);
124
125		// Preserve the "complete now" behavior if state != 0, do so by
126		//  iterating the GTask's main context till the task is complete.
127		var state = new CCodeMemberAccess.pointer (data_var, "_state_");
128		var zero = new CCodeConstant ("0");
129		var state_is_not_zero = new CCodeBinaryExpression (CCodeBinaryOperator.INEQUALITY, state, zero);
130		ccode.open_if (state_is_not_zero);
131
132		CCodeExpression task_is_complete;
133
134		var task_complete = new CCodeFunctionCall (new CCodeIdentifier ("g_task_get_completed"));
135		task_complete.add_argument (async_result_expr);
136		task_is_complete = new CCodeUnaryExpression (CCodeUnaryOperator.LOGICAL_NEGATION, task_complete);
137
138		ccode.open_while (task_is_complete);
139		var task_context = new CCodeFunctionCall (new CCodeIdentifier ("g_task_get_context"));
140		task_context.add_argument (async_result_expr);
141		var iterate_context = new CCodeFunctionCall (new CCodeIdentifier ("g_main_context_iteration"));
142		iterate_context.add_argument (task_context);
143		iterate_context.add_argument (new CCodeConstant ("TRUE"));
144		ccode.add_expression (iterate_context);
145		ccode.close ();
146
147		ccode.close ();
148
149		var unref = new CCodeFunctionCall (new CCodeIdentifier ("g_object_unref"));
150		unref.add_argument (async_result_expr);
151		ccode.add_expression (unref);
152
153		ccode.add_return (new CCodeConstant ("FALSE"));
154	}
155
156	public override bool generate_method_declaration (Method m, CCodeFile decl_space) {
157		if (m.is_async_callback) {
158			return false;
159		}
160		if ((m.is_abstract || m.is_virtual) && get_ccode_no_wrapper (m)) {
161			return false;
162		}
163		if (add_symbol_declaration (decl_space, m, get_ccode_name (m))) {
164			return false;
165		}
166
167		generate_type_declaration (new MethodType (m), decl_space);
168
169		var function = new CCodeFunction (get_ccode_name (m));
170
171		if (m.is_private_symbol () && !m.external) {
172			function.modifiers |= CCodeModifiers.STATIC;
173			if (m.is_inline) {
174				function.modifiers |= CCodeModifiers.INLINE;
175			}
176		} else if (context.hide_internal && m.is_internal_symbol () && !m.external) {
177			function.modifiers |= CCodeModifiers.INTERNAL;
178		}
179
180		if (m.entry_point) {
181			function.modifiers |= CCodeModifiers.STATIC;
182		}
183
184		if (m.version.deprecated) {
185			if (context.profile == Profile.GOBJECT) {
186				decl_space.add_include ("glib.h");
187			}
188			function.modifiers |= CCodeModifiers.DEPRECATED;
189		}
190
191		var cparam_map = new HashMap<int,CCodeParameter> (direct_hash, direct_equal);
192		var carg_map = new HashMap<int,CCodeExpression> (direct_hash, direct_equal);
193
194		var cl = m.parent_symbol as Class;
195
196		// do not generate _new functions for creation methods of abstract classes
197		if (!(m is CreationMethod && cl != null && cl.is_abstract && !cl.is_compact)) {
198			bool etv_tmp = ellipses_to_valist;
199			ellipses_to_valist = false;
200			generate_cparameters (m, decl_space, cparam_map, function, null, carg_map, new CCodeFunctionCall (new CCodeIdentifier ("fake")));
201			ellipses_to_valist = etv_tmp;
202
203			decl_space.add_function_declaration (function);
204		}
205
206		if (is_gtypeinstance_creation_method (m)) {
207			// _construct function
208			function = new CCodeFunction (get_ccode_real_name (m));
209
210			if (m.is_private_symbol ()) {
211				function.modifiers |= CCodeModifiers.STATIC;
212			} else if (context.hide_internal && m.is_internal_symbol ()) {
213				function.modifiers |= CCodeModifiers.INTERNAL;
214			}
215
216			cparam_map = new HashMap<int,CCodeParameter> (direct_hash, direct_equal);
217			bool etv_tmp = ellipses_to_valist;
218			ellipses_to_valist = false;
219			generate_cparameters (m, decl_space, cparam_map, function);
220			ellipses_to_valist = etv_tmp;
221
222			decl_space.add_function_declaration (function);
223
224			if (m.is_variadic ()) {
225				// _constructv function
226				function = new CCodeFunction (get_ccode_constructv_name ((CreationMethod) m));
227
228				cparam_map = new HashMap<int,CCodeParameter> (direct_hash, direct_equal);
229				generate_cparameters (m, decl_space, cparam_map, function);
230
231				decl_space.add_function_declaration (function);
232			}
233		}
234
235		return true;
236	}
237
238	void register_plugin_types (Symbol sym, Set<Symbol> registered_types) {
239		var ns = sym as Namespace;
240		var cl = sym as Class;
241		var iface = sym as Interface;
242		if (ns != null) {
243			foreach (var ns_ns in ns.get_namespaces ()) {
244				register_plugin_types (ns_ns, registered_types);
245			}
246			foreach (var ns_cl in ns.get_classes ()) {
247				register_plugin_types (ns_cl, registered_types);
248			}
249			foreach (var ns_iface in ns.get_interfaces ()) {
250				register_plugin_types (ns_iface, registered_types);
251			}
252		} else if (cl != null) {
253			register_plugin_type (cl, registered_types);
254			foreach (var cl_cl in cl.get_classes ()) {
255				register_plugin_types (cl_cl, registered_types);
256			}
257		} else if (iface != null) {
258			register_plugin_type (iface, registered_types);
259			foreach (var iface_cl in iface.get_classes ()) {
260				register_plugin_types (iface_cl, registered_types);
261			}
262		}
263	}
264
265	void register_plugin_type (ObjectTypeSymbol type_symbol, Set<Symbol> registered_types) {
266		if (type_symbol.external_package) {
267			return;
268		}
269
270		if (!registered_types.add (type_symbol)) {
271			// already registered
272			return;
273		}
274
275		var cl = type_symbol as Class;
276		if (cl != null) {
277			if (cl.is_compact) {
278				return;
279			}
280
281			// register base types first
282			foreach (var base_type in cl.get_base_types ()) {
283				register_plugin_type ((ObjectTypeSymbol) base_type.type_symbol, registered_types);
284			}
285		}
286
287		// Add function prototypes for required register-type-calls which are likely external
288		if (type_symbol.source_reference.file != cfile.file) {
289			// TODO Duplicated source with TypeRegisterFunction.init_from_type()
290			var register_func = new CCodeFunction ("%s_register_type".printf (get_ccode_lower_case_name (type_symbol, null)), "GType");
291			register_func.add_parameter (new CCodeParameter ("module", "GTypeModule *"));
292			register_func.is_declaration = true;
293			cfile.add_function_declaration (register_func);
294		}
295
296		var register_call = new CCodeFunctionCall (new CCodeIdentifier ("%s_register_type".printf (get_ccode_lower_case_name (type_symbol, null))));
297		register_call.add_argument (new CCodeIdentifier (module_init_param_name));
298		ccode.add_expression (register_call);
299
300		var iface = type_symbol as Interface;
301		if (iface != null) {
302			string? dbus_name = GDBusModule.get_dbus_name(type_symbol);
303
304			if (dbus_name != null) {
305				string proxy_cname = get_ccode_lower_case_prefix (type_symbol) + "proxy";
306				var register_proxy = new CCodeFunctionCall (new CCodeIdentifier ("%s_register_dynamic_type".printf (proxy_cname)));
307				register_proxy.add_argument (new CCodeIdentifier (module_init_param_name));
308				ccode.add_expression (register_proxy);
309			}
310		}
311	}
312
313	/**
314	 * This function generates the code the given method. If the method is
315	 * a constructor, _construct is generated, unless it's variadic, in which
316	 * case _constructv is generated (and _construct is generated together
317	 * with _new in visit_creation_method).
318	 */
319	public override void visit_method (Method m) {
320		string real_name = get_ccode_real_name (m);
321		if (m is CreationMethod && m.is_variadic ()) {
322			real_name = get_ccode_constructv_name ((CreationMethod) m);
323		}
324
325		push_context (new EmitContext (m));
326		push_line (m.source_reference);
327
328		bool in_gobject_creation_method = false;
329		bool in_fundamental_creation_method = false;
330
331		bool profile = m.get_attribute ("Profile") != null;
332
333		if (m is CreationMethod) {
334			var cl = current_type_symbol as Class;
335			if (cl != null && !cl.is_compact) {
336				if (cl.base_class == null) {
337					in_fundamental_creation_method = true;
338				} else if (gobject_type != null && cl.is_subtype_of (gobject_type)) {
339					in_gobject_creation_method = true;
340				}
341			}
342		}
343
344		var creturn_type = get_callable_creturn_type (m);
345
346		foreach (Parameter param in m.get_parameters ()) {
347			param.accept (this);
348		}
349
350		// do not declare overriding methods and interface implementations
351		if ((m.is_abstract || m.is_virtual
352		    || (m.base_method == null && m.base_interface_method == null))
353		    && m.signal_reference == null) {
354			generate_method_declaration (m, cfile);
355
356			if (!m.is_internal_symbol ()) {
357				generate_method_declaration (m, header_file);
358			}
359			if (!m.is_private_symbol ()) {
360				generate_method_declaration (m, internal_header_file);
361			}
362		}
363
364		if (profile) {
365			string prefix = "_vala_prof_%s".printf (real_name);
366
367			cfile.add_include ("stdio.h");
368
369			var counter = new CCodeIdentifier (prefix + "_counter");
370			var counter_decl = new CCodeDeclaration (get_ccode_name (int_type));
371			counter_decl.add_declarator (new CCodeVariableDeclarator (counter.name));
372			counter_decl.modifiers = CCodeModifiers.STATIC;
373			cfile.add_type_member_declaration (counter_decl);
374
375			// nesting level for recursive functions
376			var level = new CCodeIdentifier (prefix + "_level");
377			var level_decl = new CCodeDeclaration (get_ccode_name (int_type));
378			level_decl.add_declarator (new CCodeVariableDeclarator (level.name));
379			level_decl.modifiers = CCodeModifiers.STATIC;
380			cfile.add_type_member_declaration (level_decl);
381
382			var timer = new CCodeIdentifier (prefix + "_timer");
383			var timer_decl = new CCodeDeclaration ("GTimer *");
384			timer_decl.add_declarator (new CCodeVariableDeclarator (timer.name));
385			timer_decl.modifiers = CCodeModifiers.STATIC;
386			cfile.add_type_member_declaration (timer_decl);
387
388			var constructor = new CCodeFunction (prefix + "_init");
389			constructor.modifiers = CCodeModifiers.STATIC | CCodeModifiers.CONSTRUCTOR;
390			cfile.add_function_declaration (constructor);
391			push_function (constructor);
392
393			ccode.add_assignment (timer, new CCodeFunctionCall (new CCodeIdentifier ("g_timer_new")));
394
395			var stop_call = new CCodeFunctionCall (new CCodeIdentifier ("g_timer_stop"));
396			stop_call.add_argument (timer);
397			ccode.add_expression (stop_call);
398
399			pop_function ();
400			cfile.add_function (constructor);
401
402
403			var destructor = new CCodeFunction (prefix + "_exit");
404			destructor.modifiers = CCodeModifiers.STATIC | CCodeModifiers.DESTRUCTOR;
405			cfile.add_function_declaration (destructor);
406			push_function (destructor);
407
408			var elapsed_call = new CCodeFunctionCall (new CCodeIdentifier ("g_timer_elapsed"));
409			elapsed_call.add_argument (timer);
410			elapsed_call.add_argument (new CCodeConstant ("NULL"));
411
412			var print_call = new CCodeFunctionCall (new CCodeIdentifier ("fprintf"));
413			print_call.add_argument (new CCodeIdentifier ("stderr"));
414			print_call.add_argument (new CCodeConstant ("\"%s: %%gs (%%d calls)\\n\"".printf (m.get_full_name ())));
415			print_call.add_argument (elapsed_call);
416			print_call.add_argument (counter);
417			ccode.add_expression (print_call);
418
419			pop_function ();
420			cfile.add_function (destructor);
421		}
422
423		CCodeFunction function;
424		function = new CCodeFunction (real_name);
425
426		if (m.is_inline) {
427			function.modifiers |= CCodeModifiers.INLINE;
428		}
429
430		if (m.entry_point) {
431			function.modifiers |= CCodeModifiers.STATIC;
432		}
433
434		var cparam_map = new HashMap<int,CCodeParameter> (direct_hash, direct_equal);
435
436		generate_cparameters (m, cfile, cparam_map, function);
437
438		// generate *_real_* functions for virtual methods
439		// also generate them for abstract methods of classes to prevent faulty subclassing
440		if (!m.is_abstract || (m.is_abstract && current_type_symbol is Class)) {
441			if (!m.coroutine) {
442				if (m.base_method != null || m.base_interface_method != null) {
443					// declare *_real_* function
444					function.modifiers |= CCodeModifiers.STATIC;
445					cfile.add_function_declaration (function);
446				} else if (m.is_private_symbol ()) {
447					function.modifiers |= CCodeModifiers.STATIC;
448				} else if (context.hide_internal && m.is_internal_symbol ()) {
449					function.modifiers |= CCodeModifiers.INTERNAL;
450				}
451			} else {
452				if (m.body != null) {
453					function = new CCodeFunction (real_name + "_co", get_ccode_name (bool_type));
454
455					// data struct to hold parameters, local variables, and the return value
456					function.add_parameter (new CCodeParameter ("_data_", Symbol.lower_case_to_camel_case (get_ccode_const_name (m)) + "Data*"));
457					function.modifiers |= CCodeModifiers.STATIC;
458					cfile.add_function_declaration (function);
459				}
460			}
461		}
462
463		if (m.comment != null) {
464			cfile.add_type_member_definition (new CCodeComment (m.comment.content));
465		}
466
467		push_function (function);
468
469		unowned CCodeBlock? co_switch_block = null;
470
471		// generate *_real_* functions for virtual methods
472		// also generate them for abstract methods of classes to prevent faulty subclassing
473		if (!m.is_abstract || (m.is_abstract && current_type_symbol is Class)) {
474			if (m.body != null) {
475				if (m.coroutine) {
476					ccode.open_switch (new CCodeMemberAccess.pointer (new CCodeIdentifier ("_data_"), "_state_"));
477
478					// initial coroutine state
479					ccode.add_case (new CCodeConstant ("0"));
480					ccode.add_goto ("_state_0");
481
482					co_switch_block = ccode.current_block;
483
484					ccode.close ();
485
486					// coroutine body
487					ccode.add_label ("_state_0");
488				}
489
490				if (m.closure) {
491					// add variables for parent closure blocks
492					// as closures only have one parameter for the innermost closure block
493					var closure_block = current_closure_block;
494					int block_id = get_block_id (closure_block);
495					while (true) {
496						var parent_closure_block = next_closure_block (closure_block.parent_symbol);
497						if (parent_closure_block == null) {
498							break;
499						}
500						int parent_block_id = get_block_id (parent_closure_block);
501
502						var parent_data = new CCodeMemberAccess.pointer (new CCodeIdentifier ("_data%d_".printf (block_id)), "_data%d_".printf (parent_block_id));
503						ccode.add_declaration ("Block%dData*".printf (parent_block_id), new CCodeVariableDeclarator ("_data%d_".printf (parent_block_id)));
504						ccode.add_assignment (new CCodeIdentifier ("_data%d_".printf (parent_block_id)), parent_data);
505
506						closure_block = parent_closure_block;
507						block_id = parent_block_id;
508					}
509
510					// add self variable for closures
511					// as closures have block data parameter
512					if (m.binding == MemberBinding.INSTANCE) {
513						var cself = new CCodeMemberAccess.pointer (new CCodeIdentifier ("_data%d_".printf (block_id)), "self");
514						ccode.add_declaration (get_ccode_name (SemanticAnalyzer.get_data_type_for_symbol (current_type_symbol)), new CCodeVariableDeclarator ("self"));
515						ccode.add_assignment (new CCodeIdentifier ("self"), cself);
516					}
517
518					// allow capturing generic type parameters
519					foreach (var type_param in m.get_type_parameters ()) {
520						string func_name;
521
522						func_name = "%s_type".printf (type_param.name.ascii_down ());
523						ccode.add_declaration ("GType", new CCodeVariableDeclarator (func_name));
524						ccode.add_assignment (new CCodeIdentifier (func_name), new CCodeMemberAccess.pointer (get_variable_cexpression ("_data%d_".printf (block_id)), func_name));
525
526						func_name = "%s_dup_func".printf (type_param.name.ascii_down ());
527						ccode.add_declaration ("GBoxedCopyFunc", new CCodeVariableDeclarator (func_name));
528						ccode.add_assignment (new CCodeIdentifier (func_name), new CCodeMemberAccess.pointer (get_variable_cexpression ("_data%d_".printf (block_id)), func_name));
529
530						func_name = "%s_destroy_func".printf (type_param.name.ascii_down ());
531						ccode.add_declaration ("GDestroyNotify", new CCodeVariableDeclarator (func_name));
532						ccode.add_assignment (new CCodeIdentifier (func_name), new CCodeMemberAccess.pointer (get_variable_cexpression ("_data%d_".printf (block_id)), func_name));
533					}
534				} else if (m.parent_symbol is Class && !m.coroutine) {
535					var cl = (Class) m.parent_symbol;
536					if (m.overrides || (m.base_interface_method != null && !m.is_abstract && !m.is_virtual)) {
537						Method base_method;
538						ReferenceType base_expression_type;
539						if (m.overrides && m.base_method != null) {
540							base_method = m.base_method;
541							base_expression_type = new ObjectType ((Class) base_method.parent_symbol);
542						} else {
543							base_method = m.base_interface_method;
544							base_expression_type = new ObjectType ((Interface) base_method.parent_symbol);
545						}
546						var self_target_type = new ObjectType (cl);
547						CCodeExpression cself = get_cvalue_ (transform_value (new GLibValue (base_expression_type, new CCodeIdentifier ("base"), true), self_target_type, m));
548
549						ccode.add_declaration ("%s *".printf (get_ccode_name (cl)), new CCodeVariableDeclarator ("self"));
550						ccode.add_assignment (new CCodeIdentifier ("self"), cself);
551					} else if (m.binding == MemberBinding.INSTANCE
552							   && !(m is CreationMethod)
553							   && m.base_method == null && m.base_interface_method == null) {
554						create_method_type_check_statement (m, creturn_type, cl, true, "self");
555					}
556				}
557
558				foreach (Parameter param in m.get_parameters ()) {
559					if (param.ellipsis || param.params_array) {
560						if (param.params_array) {
561							append_params_array (m);
562						}
563						break;
564					}
565
566					if (param.direction != ParameterDirection.OUT) {
567						unowned TypeSymbol? t = param.variable_type.type_symbol;
568						if (t != null && (t.is_reference_type () || param.variable_type.is_real_struct_type ())) {
569							var cname = get_ccode_name (param);
570							if (param.direction == ParameterDirection.REF && !param.variable_type.is_real_struct_type ()) {
571								cname = "*"+cname;
572							}
573							create_method_type_check_statement (m, creturn_type, t, !param.variable_type.nullable, cname);
574						}
575					} else if (!m.coroutine) {
576						// declare local variable for out parameter to allow assignment even when caller passes NULL
577						var vardecl = new CCodeVariableDeclarator.zero ("_vala_%s".printf (get_ccode_name (param)), default_value_for_type (param.variable_type, true), get_ccode_declarator_suffix (param.variable_type));
578						ccode.add_declaration (get_ccode_name (param.variable_type), vardecl);
579
580						if (param.variable_type is ArrayType) {
581							// create variables to store array dimensions
582							var array_type = (ArrayType) param.variable_type;
583
584							if (!array_type.fixed_length) {
585								var length_ctype = get_ccode_array_length_type (param);
586								for (int dim = 1; dim <= array_type.rank; dim++) {
587									vardecl = new CCodeVariableDeclarator.zero (get_array_length_cname ("_vala_%s".printf (get_ccode_name (param)), dim), new CCodeConstant ("0"));
588									ccode.add_declaration (length_ctype, vardecl);
589								}
590							}
591						} else if (param.variable_type is DelegateType) {
592							var deleg_type = (DelegateType) param.variable_type;
593							if (deleg_type.delegate_symbol.has_target) {
594								// create variable to store delegate target
595								vardecl = new CCodeVariableDeclarator.zero ("_vala_%s".printf (get_ccode_delegate_target_name (param)), new CCodeConstant ("NULL"));
596								ccode.add_declaration (get_ccode_name (delegate_target_type), vardecl);
597
598								if (deleg_type.is_disposable ()) {
599									vardecl = new CCodeVariableDeclarator.zero ("_vala_%s".printf (get_ccode_delegate_target_destroy_notify_name (param)), new CCodeConstant ("NULL"));
600									ccode.add_declaration (get_ccode_name (delegate_target_destroy_type), vardecl);
601								}
602							}
603						}
604					}
605				}
606
607				if (m is CreationMethod) {
608					if (in_gobject_creation_method) {
609						if (!m.coroutine) {
610							ccode.add_declaration ("%s *".printf (get_ccode_name (current_type_symbol)), new CCodeVariableDeclarator.zero ("self", new CCodeConstant ("NULL")));
611						}
612					} else if (is_gtypeinstance_creation_method (m)) {
613						var cl = (Class) m.parent_symbol;
614						ccode.add_declaration (get_ccode_name (cl) + "*", new CCodeVariableDeclarator.zero ("self", new CCodeConstant ("NULL")));
615
616						if (cl.is_fundamental () && !((CreationMethod) m).chain_up) {
617							var ccall = new CCodeFunctionCall (new CCodeIdentifier ("g_type_create_instance"));
618							ccall.add_argument (get_variable_cexpression ("object_type"));
619							ccode.add_assignment (get_this_cexpression (), new CCodeCastExpression (ccall, get_ccode_name (cl) + "*"));
620
621							/* type, dup func, and destroy func fields for generic types */
622							foreach (TypeParameter type_param in current_class.get_type_parameters ()) {
623								CCodeIdentifier param_name;
624								CCodeAssignment assign;
625
626								var priv_access = new CCodeMemberAccess.pointer (new CCodeIdentifier ("self"), "priv");
627
628								param_name = new CCodeIdentifier ("%s_type".printf (type_param.name.ascii_down ()));
629								assign = new CCodeAssignment (new CCodeMemberAccess.pointer (priv_access, param_name.name), param_name);
630								ccode.add_expression (assign);
631
632								param_name = new CCodeIdentifier ("%s_dup_func".printf (type_param.name.ascii_down ()));
633								assign = new CCodeAssignment (new CCodeMemberAccess.pointer (priv_access, param_name.name), param_name);
634								ccode.add_expression (assign);
635
636								param_name = new CCodeIdentifier ("%s_destroy_func".printf (type_param.name.ascii_down ()));
637								assign = new CCodeAssignment (new CCodeMemberAccess.pointer (priv_access, param_name.name), param_name);
638								ccode.add_expression (assign);
639							}
640						}
641					} else if (current_type_symbol is Class) {
642						var cl = (Class) m.parent_symbol;
643						if (!m.coroutine) {
644							ccode.add_declaration (get_ccode_name (cl) + "*", new CCodeVariableDeclarator ("self"));
645						}
646
647						if (!((CreationMethod) m).chain_up) {
648							// TODO implicitly chain up to base class as in add_object_creation
649							// g_slice_new0 needs glib.h
650							cfile.add_include ("glib.h");
651							var ccall = new CCodeFunctionCall (new CCodeIdentifier ("g_slice_new0"));
652							ccall.add_argument (new CCodeIdentifier (get_ccode_name (cl)));
653							ccode.add_assignment (get_this_cexpression (), ccall);
654						}
655
656						if (cl.base_class == null && !(((CreationMethod) m).chain_up && cl.is_compact)) {
657							var cinitcall = new CCodeFunctionCall (new CCodeIdentifier ("%s_instance_init".printf (get_ccode_lower_case_name (cl, null))));
658							cinitcall.add_argument (get_this_cexpression ());
659							if (!cl.is_compact) {
660								cinitcall.add_argument (new CCodeConstant ("NULL"));
661							}
662							ccode.add_expression (cinitcall);
663						}
664					} else if (m.parent_symbol is Struct) {
665						unowned Struct st = (Struct) m.parent_symbol;
666						if (st.is_simple_type ()) {
667							var vardecl = new CCodeVariableDeclarator ("self", default_value_for_type (creturn_type, true));
668							vardecl.init0 = true;
669							ccode.add_declaration (get_ccode_name (creturn_type), vardecl);
670						} else {
671							// memset needs string.h
672							cfile.add_include ("string.h");
673							var czero = new CCodeFunctionCall (new CCodeIdentifier ("memset"));
674							czero.add_argument (new CCodeIdentifier ("self"));
675							czero.add_argument (new CCodeConstant ("0"));
676							czero.add_argument (new CCodeIdentifier ("sizeof (%s)".printf (get_ccode_name (st))));
677							ccode.add_expression (czero);
678						}
679					} else {
680						Report.error (m.source_reference, "internal: creation method not supported in `%s'".printf (m.parent_symbol.get_full_name ()));
681					}
682				}
683
684				if (context.module_init_method == m && in_plugin) {
685					// GTypeModule-based plug-in, register types
686					register_plugin_types (context.root, new HashSet<Symbol> ());
687				}
688
689				foreach (Expression precondition in m.get_preconditions ()) {
690					create_precondition_statement (m, creturn_type, precondition);
691				}
692			}
693		}
694
695		if (profile) {
696			string prefix = "_vala_prof_%s".printf (real_name);
697
698			var level = new CCodeIdentifier (prefix + "_level");
699			ccode.open_if (new CCodeUnaryExpression (CCodeUnaryOperator.LOGICAL_NEGATION, new CCodeUnaryExpression (CCodeUnaryOperator.POSTFIX_INCREMENT, level)));
700
701			var counter = new CCodeIdentifier (prefix + "_counter");
702			ccode.add_expression (new CCodeUnaryExpression (CCodeUnaryOperator.POSTFIX_INCREMENT, counter));
703
704			var timer = new CCodeIdentifier (prefix + "_timer");
705			var cont_call = new CCodeFunctionCall (new CCodeIdentifier ("g_timer_continue"));
706			cont_call.add_argument (timer);
707			ccode.add_expression (cont_call);
708
709			ccode.close ();
710		}
711
712		if (m.body != null) {
713			m.body.emit (this);
714
715			if (co_switch_block != null) {
716				// after counting the number of yields for coroutines, append the case statements to the switch
717				var old_block = ccode.current_block;
718				ccode.current_block = co_switch_block;
719
720				for (int state = 1; state < emit_context.next_coroutine_state; state++) {
721					ccode.add_case (new CCodeConstant (state.to_string ()));
722					ccode.add_goto ("_state_%d".printf (state));
723				}
724
725				// let gcc know that this can't happen
726				ccode.add_default ();
727				ccode.add_expression (new CCodeFunctionCall (new CCodeIdentifier ("g_assert_not_reached")));
728
729				ccode.current_block = old_block;
730				co_switch_block = null;
731			}
732		}
733
734		// we generate the same code if we see a return statement, this handles the case without returns
735		if (profile && m.return_type is VoidType) {
736			string prefix = "_vala_prof_%s".printf (real_name);
737
738			var level = new CCodeIdentifier (prefix + "_level");
739			ccode.open_if (new CCodeUnaryExpression (CCodeUnaryOperator.LOGICAL_NEGATION, new CCodeUnaryExpression (CCodeUnaryOperator.PREFIX_DECREMENT, level)));
740
741			var timer = new CCodeIdentifier (prefix + "_timer");
742
743			var stop_call = new CCodeFunctionCall (new CCodeIdentifier ("g_timer_stop"));
744			stop_call.add_argument (timer);
745			ccode.add_expression (stop_call);
746
747			ccode.close ();
748		}
749
750		// generate *_real_* functions for virtual methods
751		// also generate them for abstract methods of classes to prevent faulty subclassing
752		if (!m.is_abstract || (m.is_abstract && current_type_symbol is Class)) {
753			/* Methods imported from a plain C file don't
754			 * have a body, e.g. Vala.Parser.parse_file () */
755			if (m.body != null) {
756				if (current_method_inner_error) {
757					cfile.add_include ("glib.h");
758					/* always separate error parameter and inner_error local variable
759					 * as error may be set to NULL but we're always interested in inner errors
760					 */
761					if (m.coroutine) {
762						// no initialization necessary, closure struct is zeroed
763						closure_struct.add_field ("GError*", "_inner_error%d_".printf (current_inner_error_id));
764					} else {
765						ccode.add_declaration ("GError*", new CCodeVariableDeclarator.zero ("_inner_error%d_".printf (current_inner_error_id), new CCodeConstant ("NULL")));
766					}
767				}
768
769				// For non-void return-types GAsyncModule.visit_return_statement () will take care of this
770				if (m.return_type is VoidType && m.coroutine) {
771					// epilogue
772					complete_async ();
773				}
774
775				if (m is CreationMethod) {
776					if (current_type_symbol is Class && !m.coroutine) {
777						CCodeExpression cresult = new CCodeIdentifier ("self");
778						if (get_ccode_type (m) != null) {
779							cresult = new CCodeCastExpression (cresult, get_ccode_type (m));
780						}
781
782						ccode.add_return (cresult);
783					} else if (current_type_symbol is Struct && ((Struct) current_type_symbol).is_simple_type ()) {
784						// constructors return simple type structs by value
785						ccode.add_return (new CCodeIdentifier ("self"));
786					}
787				}
788
789				cfile.add_function (ccode);
790			}
791		}
792
793		if (m.is_abstract && current_type_symbol is Class) {
794			// generate helpful error message if a sublcass does not implement an abstract method.
795			// This is only meaningful for subclasses implemented in C since the vala compiler would
796			// complain during compile time of such en error.
797
798			// add critical warning that this method should not have been called
799			var cerrorcall = new CCodeFunctionCall (new CCodeIdentifier ("g_critical"));
800			if (!((Class) current_type_symbol).is_compact) {
801				var type_from_instance_call = new CCodeFunctionCall (new CCodeIdentifier ("G_TYPE_FROM_INSTANCE"));
802				type_from_instance_call.add_argument (new CCodeIdentifier ("self"));
803
804				var type_name_call = new CCodeFunctionCall (new CCodeIdentifier ("g_type_name"));
805				type_name_call.add_argument (type_from_instance_call);
806
807				cerrorcall.add_argument (new CCodeConstant ("\"Type `%%s' does not implement abstract method `%s'\"".printf (get_ccode_name (m))));
808				cerrorcall.add_argument (type_name_call);
809			} else {
810				cerrorcall.add_argument (new CCodeConstant ("\"Abstract method `%s' is not implemented\"".printf (get_ccode_name (m))));
811			}
812
813			ccode.add_expression (cerrorcall);
814
815			// add return statement
816			return_default_value (creturn_type);
817
818			cfile.add_function (ccode);
819		}
820
821		if (current_method_return && !(m.return_type is VoidType) && !m.return_type.is_real_non_null_struct_type () && !m.coroutine) {
822			var vardecl = new CCodeVariableDeclarator ("result", default_value_for_type (m.return_type, true));
823			vardecl.init0 = true;
824			ccode.add_declaration (get_ccode_name (m.return_type), vardecl);
825		}
826
827		pop_context ();
828
829		if ((m.is_abstract || m.is_virtual) && !m.coroutine
830		    && !get_ccode_no_wrapper (m)
831		    // If the method is a signal handler, the declaration is not needed.
832		    // the name should be reserved for the emitter!
833		    && m.signal_reference == null) {
834
835			cparam_map = new HashMap<int,CCodeParameter> (direct_hash, direct_equal);
836			var carg_map = new HashMap<int,CCodeExpression> (direct_hash, direct_equal);
837
838			generate_vfunc (m, creturn_type, cparam_map, carg_map);
839		}
840
841		if (m.entry_point) {
842			// m is possible entry point, add appropriate startup code
843			var cmain = new CCodeFunction ("main", "int");
844			cmain.line = function.line;
845			cmain.add_parameter (new CCodeParameter ("argc", "int"));
846			cmain.add_parameter (new CCodeParameter ("argv", "char **"));
847			push_function (cmain);
848
849			if (context.profile == Profile.GOBJECT) {
850				if (context.mem_profiler) {
851					var mem_profiler_init_call = new CCodeFunctionCall (new CCodeIdentifier ("g_mem_set_vtable"));
852					mem_profiler_init_call.line = cmain.line;
853					mem_profiler_init_call.add_argument (new CCodeConstant ("glib_mem_profiler_table"));
854					ccode.add_expression (mem_profiler_init_call);
855				}
856			}
857
858			var main_call = new CCodeFunctionCall (new CCodeIdentifier (function.name));
859			if (m.get_parameters ().size == 1) {
860				main_call.add_argument (new CCodeIdentifier ("argv"));
861				main_call.add_argument (new CCodeIdentifier ("argc"));
862			}
863			if (m.return_type is VoidType) {
864				// method returns void, always use 0 as exit code
865				ccode.add_expression (main_call);
866				ccode.add_return (new CCodeConstant ("0"));
867			} else {
868				ccode.add_return (main_call);
869			}
870			pop_function ();
871			cfile.add_function (cmain);
872		}
873
874		pop_line ();
875	}
876
877	public virtual CCodeParameter generate_parameter (Parameter param, CCodeFile decl_space, Map<int,CCodeParameter> cparam_map, Map<int,CCodeExpression>? carg_map) {
878		CCodeParameter cparam;
879		if (!param.ellipsis && !param.params_array) {
880			string ctypename = get_ccode_name (param.variable_type);
881
882			generate_type_declaration (param.variable_type, decl_space);
883
884			// pass non-simple structs always by reference
885			unowned Struct? st = param.variable_type.type_symbol as Struct;
886			if (st != null) {
887				if (!st.is_simple_type () && param.direction == ParameterDirection.IN) {
888					if (st.is_immutable && !param.variable_type.value_owned) {
889						ctypename = "const " + ctypename;
890					}
891
892					if (!param.variable_type.nullable) {
893						ctypename += "*";
894					}
895				}
896			}
897
898			if (param.direction != ParameterDirection.IN) {
899				ctypename += "*";
900			}
901
902			cparam = new CCodeParameter (get_ccode_name (param), ctypename);
903			if (param.format_arg) {
904				cparam.modifiers = CCodeModifiers.FORMAT_ARG;
905			}
906		} else {
907			var va_list_name = "_vala_va_list";
908
909			// Add _first_* parameter for the params array parameter
910			if (param.params_array) {
911				var param_type = ((ArrayType) param.variable_type).element_type;
912				string ctypename = get_ccode_name (param_type);
913
914				generate_type_declaration (param_type, decl_space);
915
916				// pass non-simple structs always by reference
917				if (param_type.type_symbol is Struct) {
918					var st = (Struct) param_type.type_symbol;
919					if (!st.is_simple_type () && param.direction == ParameterDirection.IN) {
920						if (st.is_immutable && !param.variable_type.value_owned) {
921							ctypename = "const " + ctypename;
922						}
923
924						if (!param_type.nullable) {
925							ctypename += "*";
926						}
927					}
928				}
929
930				cparam = new CCodeParameter ("_first_%s".printf (get_ccode_name (param)), ctypename);
931				cparam_map.set (get_param_pos (get_ccode_pos (param) - 0.1, true), cparam);
932
933				va_list_name = "_va_list_%s".printf (get_ccode_name (param));
934			}
935
936			if (ellipses_to_valist) {
937				cparam = new CCodeParameter (va_list_name, "va_list");
938			} else {
939				cparam = new CCodeParameter.with_ellipsis ();
940			}
941		}
942
943		cparam_map.set (get_param_pos (get_ccode_pos (param), param.ellipsis || param.params_array), cparam);
944		if (carg_map != null && !param.ellipsis && !param.params_array) {
945			carg_map.set (get_param_pos (get_ccode_pos (param), param.ellipsis || param.params_array), get_parameter_cexpression (param));
946		}
947
948		return cparam;
949	}
950
951	public override 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) {
952		if (m.closure) {
953			var closure_block = current_closure_block;
954			int block_id = get_block_id (closure_block);
955			var instance_param = new CCodeParameter ("_data%d_".printf (block_id), "Block%dData*".printf (block_id));
956			cparam_map.set (get_param_pos (get_ccode_instance_pos (m)), instance_param);
957		} else if (m.parent_symbol is Class && m is CreationMethod) {
958			var cl = (Class) m.parent_symbol;
959			if (!cl.is_compact && vcall == null && (direction & 1) == 1) {
960				cparam_map.set (get_param_pos (get_ccode_instance_pos (m)), new CCodeParameter ("object_type", "GType"));
961			}
962		} else if (m.binding == MemberBinding.INSTANCE && (direction != 2 || get_ccode_finish_instance (m))) {
963			var this_type = SemanticAnalyzer.get_this_type (m);
964
965			generate_type_declaration (this_type, decl_space);
966
967			CCodeParameter instance_param = null;
968			if (m.base_interface_method != null && !m.is_abstract && !m.is_virtual) {
969				var base_type = new ObjectType ((Interface) m.base_interface_method.parent_symbol);
970				instance_param = new CCodeParameter ("base", get_ccode_name (base_type));
971			} else if (m.overrides) {
972				var base_type = new ObjectType ((Class) m.base_method.parent_symbol);
973				instance_param = new CCodeParameter ("base", get_ccode_name (base_type));
974			} else {
975				unowned Struct? st = m.parent_symbol as Struct;
976				if (st != null && !st.is_simple_type ()) {
977					instance_param = new CCodeParameter ("*self", get_ccode_name (this_type));
978				} else if (st != null && st.is_simple_type () && m is CreationMethod) {
979					// constructors return simple type structs by value
980				} else {
981					instance_param = new CCodeParameter ("self", get_ccode_name (this_type));
982				}
983			}
984			if (instance_param != null) {
985				cparam_map.set (get_param_pos (get_ccode_instance_pos (m)), instance_param);
986			}
987		} else if (m.binding == MemberBinding.CLASS) {
988			var this_type = SemanticAnalyzer.get_this_type (m);
989			var class_param = new CCodeParameter ("klass", get_ccode_name (this_type));
990			cparam_map.set (get_param_pos (get_ccode_instance_pos (m)), class_param);
991		}
992
993		if (is_gtypeinstance_creation_method (m) && (direction & 1) == 1) {
994			// memory management for generic types
995			int type_param_index = 0;
996			var cl = (Class) m.parent_symbol;
997			foreach (TypeParameter type_param in cl.get_type_parameters ()) {
998				cparam_map.set (get_param_pos (0.1 * type_param_index + 0.01), new CCodeParameter ("%s_type".printf (type_param.name.ascii_down ()), "GType"));
999				cparam_map.set (get_param_pos (0.1 * type_param_index + 0.02), new CCodeParameter ("%s_dup_func".printf (type_param.name.ascii_down ()), "GBoxedCopyFunc"));
1000				cparam_map.set (get_param_pos (0.1 * type_param_index + 0.03), new CCodeParameter ("%s_destroy_func".printf (type_param.name.ascii_down ()), "GDestroyNotify"));
1001				if (carg_map != null) {
1002					carg_map.set (get_param_pos (0.1 * type_param_index + 0.01), new CCodeIdentifier ("%s_type".printf (type_param.name.ascii_down ())));
1003					carg_map.set (get_param_pos (0.1 * type_param_index + 0.02), new CCodeIdentifier ("%s_dup_func".printf (type_param.name.ascii_down ())));
1004					carg_map.set (get_param_pos (0.1 * type_param_index + 0.03), new CCodeIdentifier ("%s_destroy_func".printf (type_param.name.ascii_down ())));
1005				}
1006				type_param_index++;
1007			}
1008		} else if (!m.closure && (direction & 1) == 1) {
1009			int type_param_index = 0;
1010			foreach (var type_param in m.get_type_parameters ()) {
1011				cparam_map.set (get_param_pos (0.1 * type_param_index + 0.01), new CCodeParameter ("%s_type".printf (type_param.name.ascii_down ()), "GType"));
1012				cparam_map.set (get_param_pos (0.1 * type_param_index + 0.02), new CCodeParameter ("%s_dup_func".printf (type_param.name.ascii_down ()), "GBoxedCopyFunc"));
1013				cparam_map.set (get_param_pos (0.1 * type_param_index + 0.03), new CCodeParameter ("%s_destroy_func".printf (type_param.name.ascii_down ()), "GDestroyNotify"));
1014				if (carg_map != null) {
1015					carg_map.set (get_param_pos (0.1 * type_param_index + 0.01), new CCodeIdentifier ("%s_type".printf (type_param.name.ascii_down ())));
1016					carg_map.set (get_param_pos (0.1 * type_param_index + 0.02), new CCodeIdentifier ("%s_dup_func".printf (type_param.name.ascii_down ())));
1017					carg_map.set (get_param_pos (0.1 * type_param_index + 0.03), new CCodeIdentifier ("%s_destroy_func".printf (type_param.name.ascii_down ())));
1018				}
1019				type_param_index++;
1020			}
1021		}
1022
1023		var needs_format_arg = m.get_format_arg_index () < 0 && (m.printf_format || m.scanf_format);
1024
1025		CCodeParameter? prev_cparam = null;
1026		foreach (Parameter param in m.get_parameters ()) {
1027			if (param.direction != ParameterDirection.OUT) {
1028				if ((direction & 1) == 0) {
1029					// no in parameters
1030					continue;
1031				}
1032			} else {
1033				if ((direction & 2) == 0) {
1034					// no out parameters
1035					continue;
1036				}
1037			}
1038
1039			var cparam = generate_parameter (param, decl_space, cparam_map, carg_map);
1040
1041			// if there is no explicit FormatArg annotation while this method throws an error
1042			// it is required to mark the parameter located right before ellipsis as format-arg
1043			// to account for the parameter shifting caused by the inserted GError parameter
1044			if (needs_format_arg) {
1045				if (prev_cparam != null && cparam.ellipsis) {
1046					prev_cparam.modifiers |= CCodeModifiers.FORMAT_ARG;
1047				}
1048				prev_cparam = cparam;
1049			}
1050		}
1051
1052		if ((direction & 2) != 0) {
1053			generate_method_result_declaration (m, decl_space, func, cparam_map, carg_map);
1054		}
1055
1056		// append C parameters in the right order
1057		int last_pos = -1;
1058		int min_pos;
1059		while (true) {
1060			min_pos = -1;
1061			foreach (int pos in cparam_map.get_keys ()) {
1062				if (pos > last_pos && (min_pos == -1 || pos < min_pos)) {
1063					min_pos = pos;
1064				}
1065			}
1066			if (min_pos == -1) {
1067				break;
1068			}
1069			func.add_parameter (cparam_map.get (min_pos));
1070			if (vdeclarator != null) {
1071				vdeclarator.add_parameter (cparam_map.get (min_pos));
1072			}
1073			if (vcall != null) {
1074				var arg = carg_map.get (min_pos);
1075				if (arg != null) {
1076					vcall.add_argument (arg);
1077				}
1078			}
1079			last_pos = min_pos;
1080		}
1081
1082		if (m.printf_format) {
1083			func.modifiers |= CCodeModifiers.PRINTF;
1084		} else if (m.scanf_format) {
1085			func.modifiers |= CCodeModifiers.SCANF;
1086		}
1087
1088		if (m.version.deprecated) {
1089			func.modifiers |= CCodeModifiers.DEPRECATED;
1090		}
1091	}
1092
1093	public void generate_vfunc (Method m, DataType return_type, Map<int,CCodeParameter> cparam_map, Map<int,CCodeExpression> carg_map, string suffix = "", int direction = 3) {
1094		push_context (new EmitContext ());
1095
1096		CCodeFunction vfunc;
1097		if (suffix == "_finish") {
1098			vfunc = new CCodeFunction (get_ccode_finish_name (m));
1099		} else {
1100			vfunc = new CCodeFunction (get_ccode_name (m));
1101		}
1102
1103		CCodeExpression vcast;
1104		if (m.parent_symbol is Interface) {
1105			var iface = (Interface) m.parent_symbol;
1106
1107			vcast = new CCodeFunctionCall (new CCodeIdentifier (get_ccode_type_get_function (iface)));
1108			((CCodeFunctionCall) vcast).add_argument (new CCodeIdentifier ("self"));
1109		} else {
1110			var cl = (Class) m.parent_symbol;
1111			if (!cl.is_compact) {
1112				vcast = new CCodeFunctionCall (new CCodeIdentifier (get_ccode_type_get_function (cl)));
1113				((CCodeFunctionCall) vcast).add_argument (new CCodeIdentifier ("self"));
1114			} else {
1115				vcast = new CCodeIdentifier ("self");
1116			}
1117		}
1118
1119		CCodeFunctionCall vcall;
1120		if (suffix == "_finish") {
1121			vcall = new CCodeFunctionCall (new CCodeMemberAccess.pointer (vcast, get_ccode_finish_vfunc_name (m)));
1122		} else {
1123			vcall = new CCodeFunctionCall (new CCodeMemberAccess.pointer (vcast, get_ccode_vfunc_name (m)));
1124		}
1125
1126		carg_map.set (get_param_pos (get_ccode_instance_pos (m)), new CCodeIdentifier ("self"));
1127
1128		generate_cparameters (m, cfile, cparam_map, vfunc, null, carg_map, vcall, direction);
1129
1130		push_function (vfunc);
1131
1132		if (context.assert && m.return_type.type_symbol is Struct && ((Struct) m.return_type.type_symbol).is_simple_type () && default_value_for_type (m.return_type, false) == null) {
1133			// the type check will use the result variable
1134			var vardecl = new CCodeVariableDeclarator ("result", default_value_for_type (m.return_type, true));
1135			vardecl.init0 = true;
1136			ccode.add_declaration (get_ccode_name (m.return_type), vardecl);
1137		}
1138
1139		// add a typecheck statement for "self"
1140		create_method_type_check_statement (m, return_type, (TypeSymbol) m.parent_symbol, true, "self");
1141
1142		foreach (Expression precondition in m.get_preconditions ()) {
1143			create_precondition_statement (m, return_type, precondition);
1144		}
1145
1146		if (return_type is VoidType || return_type.is_real_non_null_struct_type ()) {
1147			ccode.add_expression (vcall);
1148		} else if (m.get_postconditions ().size == 0) {
1149			/* pass method return value */
1150			ccode.add_return (vcall);
1151		} else {
1152			/* store method return value for postconditions */
1153			ccode.add_declaration (get_creturn_type (m, get_ccode_name (return_type)), new CCodeVariableDeclarator ("result"));
1154			ccode.add_assignment (new CCodeIdentifier ("result"), vcall);
1155		}
1156
1157		if (m.get_postconditions ().size > 0) {
1158			foreach (Expression postcondition in m.get_postconditions ()) {
1159				create_postcondition_statement (postcondition);
1160			}
1161
1162			if (!(return_type is VoidType)) {
1163				ccode.add_return (new CCodeIdentifier ("result"));
1164			}
1165		}
1166
1167		if (m.printf_format) {
1168			vfunc.modifiers |= CCodeModifiers.PRINTF;
1169		} else if (m.scanf_format) {
1170			vfunc.modifiers |= CCodeModifiers.SCANF;
1171		}
1172
1173		if (m.version.deprecated) {
1174			vfunc.modifiers |= CCodeModifiers.DEPRECATED;
1175		}
1176
1177		cfile.add_function (vfunc);
1178
1179		pop_context ();
1180	}
1181
1182	private void create_method_type_check_statement (Method m, DataType return_type, TypeSymbol t, bool non_null, string var_name) {
1183		if (!m.coroutine) {
1184			create_type_check_statement (m, return_type, t, non_null, var_name);
1185		}
1186	}
1187
1188	private void create_precondition_statement (Method m, DataType ret_type, Expression precondition) {
1189		is_in_method_precondition = true;
1190
1191		var ccheck = new CCodeFunctionCall ();
1192
1193		precondition.emit (this);
1194
1195		ccheck.add_argument (get_cvalue (precondition));
1196
1197		string message = ((string) precondition.source_reference.begin.pos).substring (0, (int) (precondition.source_reference.end.pos - precondition.source_reference.begin.pos));
1198		ccheck.add_argument (new CCodeConstant ("\"%s\"".printf (message.replace ("\n", " ").escape (""))));
1199		requires_assert = true;
1200
1201		if (m is CreationMethod) {
1202			if (m.parent_symbol is Class) {
1203				ccheck.call = new CCodeIdentifier ("_vala_return_val_if_fail");
1204				ccheck.add_argument (new CCodeConstant ("NULL"));
1205			} else {
1206				// creation method of struct
1207				ccheck.call = new CCodeIdentifier ("_vala_return_if_fail");
1208			}
1209		} else if (m.coroutine) {
1210			// _co function
1211			ccheck.call = new CCodeIdentifier ("_vala_return_val_if_fail");
1212			ccheck.add_argument (new CCodeConstant ("FALSE"));
1213		} else if (ret_type is VoidType) {
1214			/* void function */
1215			ccheck.call = new CCodeIdentifier ("_vala_return_if_fail");
1216		} else {
1217			ccheck.call = new CCodeIdentifier ("_vala_return_val_if_fail");
1218
1219			var cdefault = default_value_for_type (ret_type, false);
1220			if (cdefault != null) {
1221				ccheck.add_argument (cdefault);
1222			} else {
1223				return;
1224			}
1225		}
1226
1227		ccode.add_expression (ccheck);
1228
1229		current_method_return = true;
1230		is_in_method_precondition = false;
1231	}
1232
1233	public override void visit_creation_method (CreationMethod m) {
1234		push_line (m.source_reference);
1235
1236		ellipses_to_valist = true;
1237		visit_method (m);
1238		ellipses_to_valist = false;
1239
1240		if (m.source_type == SourceFileType.FAST) {
1241			pop_line ();
1242			return;
1243		}
1244
1245		// do not generate _new functions for creation methods of abstract classes
1246		if (current_type_symbol is Class && !current_class.is_compact && !current_class.is_abstract) {
1247			// _new function
1248			create_aux_constructor (m, get_ccode_name (m), false);
1249
1250			// _construct function (if visit_method generated _constructv)
1251			if (m.is_variadic ()) {
1252				create_aux_constructor (m, get_ccode_real_name (m), true);
1253			}
1254		}
1255
1256		pop_line ();
1257	}
1258
1259	private void create_aux_constructor (CreationMethod m, string func_name, bool self_as_first_parameter) {
1260		var vfunc = new CCodeFunction (func_name);
1261		if (m.is_private_symbol ()) {
1262			vfunc.modifiers |= CCodeModifiers.STATIC;
1263		} else if (context.hide_internal && m.is_internal_symbol ()) {
1264			vfunc.modifiers |= CCodeModifiers.INTERNAL;
1265		}
1266
1267		var cparam_map = new HashMap<int,CCodeParameter> (direct_hash, direct_equal);
1268		var carg_map = new HashMap<int,CCodeExpression> (direct_hash, direct_equal);
1269
1270		push_function (vfunc);
1271
1272		string constructor = (m.is_variadic ()) ? get_ccode_constructv_name (m) : get_ccode_real_name (m);
1273		var vcall = new CCodeFunctionCall (new CCodeIdentifier (constructor));
1274
1275		if (self_as_first_parameter) {
1276			cparam_map.set (get_param_pos (get_ccode_instance_pos (m)), new CCodeParameter ("object_type", "GType"));
1277			vcall.add_argument (get_variable_cexpression ("object_type"));
1278		} else {
1279			vcall.add_argument (new CCodeIdentifier (get_ccode_type_id (current_class)));
1280		}
1281
1282
1283		generate_cparameters (m, cfile, cparam_map, vfunc, null, carg_map, vcall);
1284
1285		if (m.is_variadic ()) {
1286			int last_pos = -1;
1287			int second_last_pos = -1;
1288			foreach (int pos in cparam_map.get_keys ()) {
1289				if (pos > last_pos) {
1290					second_last_pos = last_pos;
1291					last_pos = pos;
1292				} else if (pos > second_last_pos) {
1293					second_last_pos = pos;
1294				}
1295			}
1296
1297			var carg = carg_map.get (second_last_pos);
1298			if (carg == null) {
1299				// params arrays have an implicit first argument, refer to the cparameter name
1300				carg = new CCodeIdentifier (cparam_map.get (second_last_pos).name);
1301				vcall.add_argument (carg);
1302			}
1303
1304			var va_start = new CCodeFunctionCall (new CCodeIdentifier ("va_start"));
1305			va_start.add_argument (new CCodeIdentifier ("_vala_va_list_obj"));
1306			va_start.add_argument (carg);
1307
1308			ccode.add_declaration ("va_list", new CCodeVariableDeclarator ("_vala_va_list_obj"));
1309			ccode.add_expression (va_start);
1310
1311			vcall.add_argument (new CCodeIdentifier("_vala_va_list_obj"));
1312		}
1313
1314		ccode.add_return (vcall);
1315
1316		pop_function ();
1317
1318		cfile.add_function (vfunc);
1319	}
1320}
1321
1322// vim:sw=8 noet
1323