1 /*
2  * This file is part of gir-to-d.
3  *
4  * gir-to-d is free software: you can redistribute it and/or modify
5  * it under the terms of the GNU Lesser General Public License
6  * as published by the Free Software Foundation, either version 3
7  * of the License, or (at your option) any later version.
8  *
9  * gir-to-d is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public License
15  * along with gir-to-d.  If not, see <http://www.gnu.org/licenses/>.
16  */
17 
18 module gtd.GirFunction;
19 
20 import std.algorithm: among, startsWith, endsWith;
21 import std.conv;
22 import std.range;
23 import std.string : chomp, splitLines, strip;
24 import std.uni: toUpper, toLower;
25 
26 import gtd.GirEnum;
27 import gtd.GirStruct;
28 import gtd.GirType;
29 import gtd.GirVersion;
30 import gtd.GirWrapper;
31 import gtd.Log;
32 import gtd.XMLReader;
33 
34 enum GirFunctionType : string
35 {
36 	Constructor = "constructor",
37 	Method = "method",
38 	Function = "function",
39 	Callback = "callback",
40 	Signal = "glib:signal"
41 }
42 
43 enum GirTransferOwnership : string
44 {
45 	None = "none",          /// Gtk owns the returned reference.
46 	Full = "full",          /// We own the returned reference.
47 	Container = "container" /// The container in which the references reside has ownership.
48 }
49 
50 final class GirFunction
51 {
52 	string name;
53 	GirFunctionType type;
54 	string doc;
55 	string cType;
56 	string libVersion;
57 	string movedTo;
58 	bool virtual = false;
59 	bool throws = false;
60 	bool lookupOverride; /// Force marking this function with override.
61 	bool noCode; /// Don't generate any class code for this function.
62 
63 	GirType returnType;
64 	GirTransferOwnership returnOwnership = GirTransferOwnership.None;
65 	GirParam instanceParam;
66 	GirParam[] params;
67 
68 	GirWrapper wrapper;
69 	GirStruct strct;
70 
71 	this (GirWrapper wrapper, GirStruct strct)
72 	{
73 		this.wrapper = wrapper;
74 		this.strct = strct;
75 	}
76 
77 	GirFunction dup()
78 	{
79 		GirFunction copy = new GirFunction(wrapper, strct);
80 		
81 		foreach ( i, field; this.tupleof )
82 			copy.tupleof[i] = field;
83 		
84 		return copy;
85 	}
86 
87 	void parse(T)(XMLReader!T reader)
88 	{
89 		name = reader.front.attributes["name"];
90 		// Special case for g_iconv wich doesnt have a name.
91 		if ( name.empty && "moved-to" in reader.front.attributes )
92 			name = reader.front.attributes["moved-to"];
93 
94 		type = cast(GirFunctionType)reader.front.value;
95 
96 		if ( "c:type" in reader.front.attributes )
97 			cType = reader.front.attributes["c:type"];
98 		if ( "c:identifier" in reader.front.attributes )
99 			cType = reader.front.attributes["c:identifier"];
100 		if ( "version" in reader.front.attributes )
101 		{
102 			libVersion = reader.front.attributes["version"];
103 			if ( strct )
104 				strct.pack.checkVersion(libVersion);
105 		}
106 		if ( "throws" in reader.front.attributes )
107 			throws = reader.front.attributes["throws"] == "1";
108 		if ( "moved-to" in reader.front.attributes )
109 			movedTo = reader.front.attributes["moved-to"];
110 
111 		reader.popFront();
112 
113 		while( !reader.empty && !reader.endTag("constructor", "method", "function", "callback", "glib:signal") )
114 		{
115 			switch ( reader.front.value )
116 			{
117 				case "attribute":
118 					//TODO: Do we need these attibutes?
119 					//dbus.name ccode.ordering deprecated replacement.
120 					reader.skipTag();
121 					break;
122 				case "doc":
123 				case "doc-stability":
124 					reader.popFront();
125 					doc ~= reader.front.value;
126 					reader.popFront();
127 					break;
128 				case "doc-deprecated":
129 					reader.popFront();
130 					doc ~= "\n\nDeprecated: "~ reader.front.value;
131 					reader.popFront();
132 					break;
133 				case "doc-version":
134 					reader.skipTag();
135 					break;
136 				case "return-value":
137 					if ( "transfer-ownership" in reader.front.attributes )
138 						returnOwnership = cast(GirTransferOwnership)reader.front.attributes["transfer-ownership"];
139 
140 					returnType = new GirType(wrapper);
141 					reader.popFront();
142 
143 					while( !reader.empty && !reader.endTag("return-value") )
144 					{
145 						switch ( reader.front.value )
146 						{
147 							case "attribute":
148 								//TODO: Do we need these attibutes?
149 								//dbus.name ccode.ordering deprecated replacement.
150 								reader.skipTag();
151 								break;
152 							case "doc":
153 								reader.popFront();
154 								returnType.doc ~= reader.front.value;
155 								reader.popFront();
156 								break;
157 							case "array":
158 							case "type":
159 								returnType.parse(reader);
160 								break;
161 							default:
162 								warning("Unexpected tag: ", reader.front.value, " in GirFunction: ", name, reader);
163 						}
164 						reader.popFront();
165 					}
166 					break;
167 				case "parameters":
168 					reader.popFront();
169 					while( !reader.empty && !reader.endTag("parameters") )
170 					{
171 						switch ( reader.front.value )
172 						{
173 							case "instance-parameter":
174 								instanceParam = new GirParam(wrapper);
175 								instanceParam.parse(reader);
176 								break;
177 							case "parameter":
178 								GirParam param = new GirParam(wrapper);
179 								param.parse(reader);
180 								params ~= param;
181 								break;
182 							default:
183 								warning("Unexpected tag: ", reader.front.value, " in GirFunction: ", name, reader);
184 						}
185 						reader.popFront();
186 					}
187 					break;
188 				case "source-position":
189 					reader.skipTag();
190 					break;
191 				default:
192 					warning("Unexpected tag: ", reader.front.value, " in GirFunction: ", name, reader);
193 			}
194 			reader.popFront();
195 		}
196 
197 		if ( type == GirFunctionType.Function && name.startsWith("new") && returnType.cType != "void" )
198 			type = GirFunctionType.Constructor;
199 
200 		// For the case where a param is `const gchar* name[]` whitch ends up in the gir files
201 		// as an array with elementType name=utf8 c:type=gchar, missing the [].
202 		switch ( cType )
203 		{
204 			case "gtk_icon_theme_choose_icon":
205 			case "gtk_icon_theme_choose_icon_for_scale":
206 				params[0].type.cType = "char**";
207 				params[0].type.elementType.cType = "char*";
208 				break;
209 			case "g_object_getv":
210 			case "g_object_setv":
211 				params[1].type.cType = "char**";
212 				params[1].type.elementType.cType = "char*";
213 				break;
214 			case "gst_init":
215 			case "gst_init_check":
216 				params[1].type.cType = "char***";
217 				params[1].type.elementType.cType = "char**";
218 				break;
219 			case "g_object_new_with_properties":
220 				params[2].type.cType = "char**";
221 				params[2].type.elementType.cType = "char*";
222 				break;
223 			case "g_key_file_set_locale_string_list":
224 				params[3].type.cType = "char**";
225 				params[3].type.elementType.cType = "char*";
226 				break;
227 			default: break;
228 		}
229 	}
230 
231 	bool isVariadic()
232 	{
233 		if ( params.empty )
234 			return false;
235 		else if ( params[$-1].name == "..." )
236 			return true;
237 
238 		return false;
239 	}
240 
241 	/**
242 	 * Is this function a static function.
243 	 */
244 	bool isStatic()
245 	{
246 		if ( strct.noNamespace )
247 			return false;
248 
249 		if ( type == GirFunctionType.Function && !(!params.empty && isInstanceParam(params[0])) )
250 			return true;
251 
252 		if ( type == GirFunctionType.Method && strct.isNamespace() )
253 			return true;
254 
255 		return false;
256 	}
257 
258 	string[] getCallbackDeclaration()
259 	{
260 		string[] buff;
261 
262 		writeDocs(buff);
263 		buff ~= "public alias extern(C) "~ getExternalFunctionType() ~" "~ tokenToGtkD(cType, wrapper.aliasses, localAliases()) ~";";
264 
265 		return buff;
266 	}
267 
268 	string[] getFunctionPointerDeclaration()
269 	{
270 		string[] buff;
271 
272 		writeDocs(buff);
273 		buff ~= "extern(C) "~ getExternalFunctionType() ~" "~ tokenToGtkD(name, wrapper.aliasses, localAliases()) ~";";
274 
275 		return buff;
276 	}
277 
278 	string getLinkerExternal()
279 	{
280 		assert(type != GirFunctionType.Callback);
281 		assert(type != GirFunctionType.Signal);
282 
283 		if (strct.pack.name == "glgdk")
284 			return getExternalFunctionType() ~" glc_"~ cType ~";";
285 		else
286 			return getExternalFunctionType() ~" c_"~ cType ~";";
287 	}
288 
289 	string getExternal()
290 	{
291 		assert(type != GirFunctionType.Callback);
292 		assert(type != GirFunctionType.Signal);
293 
294 		string ext;
295 		string type = stringToGtkD(returnType.cType, wrapper.aliasses, localAliases());
296 
297 		if ( type.startsWith("bool") )
298 			ext ~= type.replaceFirst("bool", "int");
299 		else
300 			ext ~= type;
301 
302 		ext ~= " "~ cType ~"("~ getExternalParameters() ~");";
303 
304 		return ext;
305 	}
306 
307 	private string getExternalFunctionType()
308 	{
309 		string ext;
310 		string type = stringToGtkD(returnType.cType, wrapper.aliasses, localAliases());
311 
312 		if ( type.startsWith("bool") )
313 			ext ~= type.replaceFirst("bool", "int");
314 		else
315 			ext ~= type;
316 
317 		ext ~= " function("~ getExternalParameters() ~")";
318 
319 		return ext;
320 	}
321 
322 	private string getExternalParameters()
323 	{
324 		string ext, type;
325 
326 		if ( instanceParam )
327 		{
328 			ext ~= stringToGtkD(instanceParam.type.cType, wrapper.aliasses, localAliases(), false);
329 			ext ~= " ";
330 			ext ~= tokenToGtkD(instanceParam.name, wrapper.aliasses, localAliases());
331 		}
332 
333 		foreach ( i, param; params )
334 		{
335 			if ( i > 0 || instanceParam )
336 				ext ~= ", ";
337 
338 			type = stringToGtkD(param.type.cType, wrapper.aliasses, localAliases(), false);
339 
340 			if ( type.startsWith("bool") )
341 				ext ~= type.replaceFirst("bool", "int");
342 			else
343 				ext ~= type;
344 
345 			// Treat C fixed-size array parameters like pointers
346 			if ( param.type.isArray() && param.type.size > 0 && !type.endsWith("*") )
347 				ext ~= "*";
348 
349 			ext ~= " ";
350 			//Both name and type are ... for Variadic functions.
351 			if ( param.name != "..." )
352 				ext ~= tokenToGtkD(param.name, wrapper.aliasses, localAliases());
353 		}
354 
355 		if ( throws )
356 			ext ~= ", GError** err";
357 
358 		return ext;
359 	}
360 
361 	string[] getDeclaration()
362 	{
363 		string[] buff;
364 		string dec = "public ";
365 
366 		resolveLength();
367 		writeDocs(buff);
368 
369 		if ( type == GirFunctionType.Constructor )
370 		{
371 			dec ~= "this(";
372 		}
373 		else
374 		{
375 			if ( isStatic() )
376 				dec ~= "static ";
377 
378 			if ( lookupOverride || checkOverride() )
379 				dec ~= "override ";
380 
381 			dec ~= getType(returnType) ~" ";
382 			dec ~= tokenToGtkD(name, wrapper.aliasses, localAliases()) ~"(";
383 		}
384 
385 		size_t paramCount;
386 
387 		if ( instanceParam && ((type == GirFunctionType.Method && (strct.isNamespace() || strct.noNamespace )) || type == GirFunctionType.Constructor) )
388 		{
389 			dec ~= getType(instanceParam.type) ~" "~ tokenToGtkD(instanceParam.name, wrapper.aliasses, localAliases());
390 			paramCount++;
391 		}
392 
393 		foreach( param; params )
394 		{
395 			if ( param.lengthFor )
396 				continue;
397 
398 			if ( returnType.length > -1 && param == params[returnType.length] && params[returnType.length].direction != GirParamDirection.Default )
399 				continue;
400 
401 			if ( paramCount == 0 && strct.type == GirStructType.Record && isInstanceParam(param) )
402 				continue;
403 
404 			if ( paramCount++ > 0 )
405 				dec ~= ", ";
406 
407 			if ( param.direction == GirParamDirection.Out )
408 				dec ~= "out ";
409 			else if ( param.direction == GirParamDirection.InOut )
410 				dec ~= "ref ";
411 
412 			dec ~= getType(param.type, param.direction) ~" ";
413 			dec ~= tokenToGtkD(param.name, wrapper.aliasses, localAliases());
414 		}
415 
416 		dec ~= ")";
417 		buff ~= dec;
418 
419 		return buff;
420 	}
421 
422 	string[] getBody()
423 	{
424 		string[] buff;
425 		string[] outToD;
426 		string gtkCall = cType ~"(";
427 
428 		GirStruct returnDType;
429 
430 		if ( returnType.isArray() )
431 		{
432 			returnDType = strct.pack.getStruct(returnType.elementType.name);
433 
434 			if ( returnDType && returnType.elementType.cType.empty )
435 				returnType.elementType.cType = returnDType.cType ~"*";
436 		}
437 		else
438 		{
439 			returnDType = strct.pack.getStruct(returnType.name);
440 
441 			if ( returnDType && returnType.cType.empty )
442 				returnType.cType = returnDType.cType ~"*";
443 		}
444 
445 		if ( instanceParam || ( !params.empty && isInstanceParam(params[0])) )
446 		{
447 			GirStruct dType;
448 
449 			if ( instanceParam )
450 			{
451 				dType = strct.pack.getStruct(instanceParam.type.name);
452 
453 				if ( dType.cType != instanceParam.type.cType.removePtr() && !instanceParam.type.cType.among("gpointer", "gconstpointer") )
454 					gtkCall ~= "cast("~ stringToGtkD(instanceParam.type.cType, wrapper.aliasses, localAliases()) ~")";
455 			}
456 			else
457 			{
458 				dType = strct.pack.getStruct(params[0].type.name);
459 
460 				if ( dType.cType != params[0].type.cType.removePtr() && !params[0].type.cType.among("gpointer", "gconstpointer") )
461 					gtkCall ~= "cast("~ stringToGtkD(params[0].type.cType, wrapper.aliasses, localAliases()) ~")";
462 			}
463 
464 			if ( instanceParam && instanceParam.type.name in strct.structWrap )
465 			{
466 				GirStruct insType = strct.pack.getStruct(strct.structWrap[instanceParam.type.name]);
467 
468 				if ( insType )
469 					dType = insType;
470 			}
471 
472 			if ( type == GirFunctionType.Constructor || strct.isNamespace() || strct.noNamespace )
473 			{
474 				string id = tokenToGtkD(instanceParam.name, wrapper.aliasses, localAliases());
475 
476 				if ( dType && !(dType.isNamespace() || dType.noNamespace) )
477 					gtkCall ~= "("~ id ~" is null) ? null : "~ id ~"."~ dType.getHandleFunc() ~"()";
478 				else
479 					gtkCall ~= id;
480 			}
481 			else if ( dType.type == GirStructType.Interface || dType.lookupInterface )
482 			{
483 				gtkCall ~= strct.getHandleFunc() ~"()";
484 			}
485 			else
486 			{
487 				gtkCall ~= strct.getHandleVar();
488 			}
489 		}
490 
491 		foreach( i, param; params )
492 		{
493 			GirStruct dType;
494 			string id = tokenToGtkD(param.name, wrapper.aliasses, localAliases());
495 
496 			if ( param.type.isArray() )
497 				dType = strct.pack.getStruct(param.type.elementType.name);
498 			else if ( auto dStrct = strct.pack.getStruct(strct.structWrap.get(param.type.name, "")) )
499 				dType = dStrct;
500 			else
501 				dType = strct.pack.getStruct(param.type.name);
502 
503 			if ( i == 0 && isInstanceParam(param) )
504 				continue;
505 
506 			if ( instanceParam || i > 0 )
507 				gtkCall ~= ", ";
508 
509 			if ( param.type.isString() )
510 			{
511 				if ( isStringArray(param.type, param.direction) )
512 				{
513 					// out string[], ref string[]
514 					if ( param.direction != GirParamDirection.Default )
515 					{
516 						buff ~= "char** out"~ id ~" = ";
517 
518 						if ( param.direction == GirParamDirection.Out )
519 							buff[$-1] ~= "null;";
520 						else
521 							buff[$-1] ~= "Str.toStringzArray("~ id ~");";
522 
523 						string len = lenId(param.type);
524 						if ( !len.empty )
525 							len = ", "~ len;
526 
527 						gtkCall ~= "&out"~ id;
528 						outToD ~= id ~" = Str.toStringArray(out"~ id ~ len ~");";
529 					}
530 					// string[]
531 					else
532 					{
533 						gtkCall ~= "Str.toStringzArray("~ id ~")";
534 					}
535 				}
536 				else
537 				{
538 					if ( param.direction != GirParamDirection.Default )
539 					{
540 						string len = lenId(param.type);
541 
542 						// A buffer to fill.
543 						if ( !param.type.cType.endsWith("**") )
544 						{
545 							gtkCall ~= id ~".ptr";
546 
547 							if ( !len.empty && params[param.type.length].direction != GirParamDirection.Default )
548 								outToD ~= id ~" = "~ id ~"[0.."~ len ~"];";
549 						}
550 						// out string, ref string
551 						else
552 						{
553 							buff ~= "char* out"~ id ~" = ";
554 
555 							if ( param.direction == GirParamDirection.Out )
556 								buff[$-1] ~= "null;";
557 							else
558 								buff[$-1] ~= "Str.toStringz("~ id ~");";
559 
560 							if ( !len.empty )
561 								len = ", "~ len;
562 
563 							gtkCall ~= "&out"~ id;
564 							outToD ~= id ~" = Str.toString(out"~ id ~ len ~");";
565 						}
566 					}
567 					// string
568 					else
569 					{
570 						gtkCall ~= "Str.toStringz("~ id ~")";
571 					}
572 				}
573 			}
574 			else if ( dType && dType.isDClass() )
575 			{
576 				if ( param.type.isArray() )
577 				{
578 					GirType elementType = param.type.elementType;
579 					GirStruct dElementType = strct.pack.getStruct(elementType.name);
580 
581 					if ( elementType.cType.empty )
582 						elementType.cType = stringToGtkD(param.type.cType, wrapper.aliasses, localAliases())[0 .. $-1];
583 
584 					// out gtkdType[], ref gtkdType[]
585 					if ( param.direction != GirParamDirection.Default )
586 					{
587 						if ( param.direction == GirParamDirection.Out )
588 						{
589 							if ( param.type.size > 0 )
590 								buff ~= elementType.cType ~"* out"~ id ~" = cast("~ elementType.cType ~"*)sliceAlloc0("~ elementType.cType ~".sizeof * "~ to!string(param.type.size) ~");";
591 							else if ( !elementType.cType.endsWith("*") )
592 								buff ~= elementType.cType ~"* out"~ id ~" = null;";
593 							else
594 								buff ~= elementType.cType ~" out"~ id ~" = null;";
595 						}
596 						else
597 						{
598 							if ( !buff.empty )
599 								buff ~= "";
600 							buff ~= elementType.cType.removePtr() ~ "**[] inout"~ id ~" = new "~ elementType.cType.removePtr() ~"*["~ id ~".length];";
601 							buff ~= "for ( int i = 0; i < "~ id ~".length; i++ )";
602 							buff ~= "{";
603 							buff ~= "inout"~ id ~"[i] = "~ id~ "[i]."~ dElementType.getHandleFunc() ~"();";
604 							buff ~= "}";
605 							buff ~= "";
606 							buff ~= elementType.cType.removePtr() ~ "** out"~ id ~" = inout"~ id ~".ptr;";
607 						}
608 
609 						if ( !elementType.cType.endsWith("*") )
610 							gtkCall ~= "out"~ id;
611 						else
612 							gtkCall ~= "&out"~ id;
613 
614 						if ( !outToD.empty )
615 							outToD ~= "";
616 						if ( param.type.size <= 0 )
617 							outToD ~= id ~" = new "~ dElementType.name ~"["~ lenId(param.type, "out"~ id) ~"];";
618 						outToD ~= "for(size_t i = 0; i < "~ lenId(param.type, "out"~ id) ~"; i++)";
619 						outToD ~= "{";
620 						if ( elementType.cType.endsWith("**") )
621 							outToD ~= id ~"[i] = " ~ construct(elementType.name) ~ "(cast(" ~ elementType.cType[0..$-1] ~ ") out"~ id ~"[i]);";
622 						else if ( !elementType.cType.endsWith("*") )
623 							outToD ~= id ~"[i] = " ~ construct(elementType.name) ~ "(cast(" ~ elementType.cType ~ "*) &out"~ id ~"[i]);";
624 						else
625 							outToD ~= id ~"[i] = " ~ construct(elementType.name) ~ "(cast(" ~ elementType.cType ~ ") &out"~ id ~"[i]);";
626 						outToD ~= "}";
627 					}
628 					// gtkdType[]
629 					else
630 					{
631 						//TODO: zero-terminated see: g_signal_chain_from_overridden
632 						if ( !buff.empty )
633 							buff ~= "";
634 						buff ~= elementType.cType ~ "[] "~ id ~"Array = new "~ elementType.cType ~"["~ id ~".length];";
635 						buff ~= "for ( int i = 0; i < "~ id ~".length; i++ )";
636 						buff ~= "{";
637 						if ( elementType.cType.endsWith("*") )
638 							buff ~= id ~"Array[i] = "~ id ~"[i]."~ dElementType.getHandleFunc() ~"();";
639 						else
640 							buff ~= id ~"Array[i] = *("~ id ~"[i]."~ dElementType.getHandleFunc() ~"());";
641 						buff ~= "}";
642 						buff ~= "";
643 
644 						gtkCall ~= id ~"Array.ptr";
645 					}
646 				}
647 				else
648 				{
649 					// out gtkdType, ref gtkdType
650 					if ( param.direction != GirParamDirection.Default && param.type.cType.endsWith("**") )
651 					{
652 						buff ~= param.type.cType.removePtr() ~"* out"~ id ~" = ";
653 
654 						if ( param.direction == GirParamDirection.Out )
655 							buff[$-1] ~= "null;";
656 						else
657 							buff[$-1] ~= id ~"."~ dType.getHandleFunc() ~"();";
658 
659 						gtkCall ~= "&out"~ id;
660 
661 						outToD ~= id ~" = "~ construct(param.type.name) ~"(out"~ id ~");";
662 					}
663 					else if ( param.direction == GirParamDirection.Out )
664 					{
665 						buff ~= param.type.cType.removePtr() ~"* out"~ id ~" = sliceNew!"~ param.type.cType.removePtr() ~"();";
666 
667 						gtkCall ~= "out"~ id;
668 
669 						outToD ~= id ~" = "~ construct(param.type.name) ~"(out"~ id ~", true);";
670 					}
671 					// gtkdType
672 					else
673 					{
674 						gtkCall ~= "("~ id ~" is null) ? null : ";
675 						if ( dType.cType != param.type.cType.removePtr() && !param.type.cType.among("gpointer", "gconstpointer") )
676 							gtkCall ~= "cast("~ stringToGtkD(param.type.cType, wrapper.aliasses, localAliases()) ~")";
677 
678 						if ( param.ownership == GirTransferOwnership.Full && dType.shouldFree() )
679 							gtkCall ~= id ~"."~ dType.getHandleFunc ~"(true)";
680 						else
681 							gtkCall ~= id ~"."~ dType.getHandleFunc ~"()";
682 					}
683 				}
684 			}
685 			else if ( param.lengthFor || (returnType.length == i && param.direction != GirParamDirection.Default ) )
686 			{
687 				string arrId;
688 				string lenType = tokenToGtkD(param.type.cType.removePtr(), wrapper.aliasses, localAliases());
689 
690 				if ( param.lengthFor )
691 					arrId = tokenToGtkD(param.lengthFor.name, wrapper.aliasses, localAliases());
692 
693 				final switch ( param.direction ) with (GirParamDirection)
694 				{
695 					case Default:
696 						gtkCall ~= "cast("~ lenType ~")"~ arrId ~".length";
697 						break;
698 					case Out:
699 						buff ~= lenType ~" "~ id ~";";
700 						gtkCall ~= "&"~id;
701 						break;
702 					case InOut:
703 						buff ~= lenType ~" "~ id ~" = cast("~ lenType ~")"~ arrId ~".length;";
704 						gtkCall ~= "&"~id;
705 						break;
706 				}
707 			}
708 			else if ( param.type.name.among("bool", "gboolean") || ( param.type.isArray && param.type.elementType.name.among("bool", "gboolean") ) )
709 			{
710 				if ( param.type.isArray() )
711 				{
712 					// out bool[], ref bool[]
713 					if ( param.direction != GirParamDirection.Default )
714 					{
715 						if ( param.direction == GirParamDirection.Out )
716 						{
717 							buff ~= "int* out"~ id ~" = null;";
718 						}
719 						else
720 						{
721 							if ( !buff.empty )
722 								buff ~= "";
723 							buff ~= "int[] inout"~ id ~" = new int["~ id ~".length];";
724 							buff ~= "for ( int i = 0; i < "~ id ~".length; i++ )";
725 							buff ~= "{";
726 							buff ~= "inout"~ id ~"[i] = "~ id~ "[i] ? 1 : 0;";
727 							buff ~= "}";
728 							buff ~= "";
729 							buff ~= "int* out"~ id ~" = inout"~ id ~".ptr;";
730 						}
731 
732 						gtkCall ~= "&out"~ id;
733 
734 						if ( !outToD.empty )
735 							outToD ~= "";
736 						outToD ~= id ~" = new bool["~ lenId(param.type, "out"~ id) ~"];";
737 						outToD ~= "for(size_t i = 0; i < "~ lenId(param.type, "out"~ id) ~"; i++)";
738 						outToD ~= "{";
739 						outToD ~= id ~"[i] = out"~ id ~"[i] != 0);";
740 						outToD ~= "}";
741 					}
742 					// bool[]
743 					else
744 					{
745 						if ( !buff.empty )
746 							buff ~= "";
747 						buff ~= "int[] "~ id ~"Array = new int["~ id ~".length];";
748 						buff ~= "for ( int i = 0; i < "~ id ~".length; i++ )";
749 						buff ~= "{";
750 						buff ~= id ~"Array[i] = "~ id ~"[i] ? 1 : 0;";
751 						buff ~= "}";
752 						buff ~= "";
753 
754 						gtkCall ~= id ~"Array.ptr";
755 					}
756 				}
757 				else
758 				{
759 					// out bool, ref bool
760 					if ( param.direction != GirParamDirection.Default )
761 					{
762 						buff ~= "int out"~ id;
763 
764 						if ( param.direction == GirParamDirection.Out )
765 							buff[$-1] ~= ";";
766 						else
767 							buff[$-1] ~= " = ("~ id ~" ? 1 : 0);";
768 
769 						gtkCall ~= "&out"~ id ~"";
770 						outToD ~= id ~" = (out"~ id ~" == 1);";
771 					}
772 					// bool
773 					else
774 					{
775 						gtkCall ~= id;
776 					}
777 				}
778 			}
779 			else
780 			{
781 				if ( param.type.isArray() )
782 				{
783 					// out T[], ref T[]
784 					if ( param.direction != GirParamDirection.Default )
785 					{
786 						string outType = param.type.elementType.cType;
787 						string outName = "out" ~ id;
788 
789 						if ( outType.empty )
790 							outType = param.type.elementType.name ~"*";
791 						else if ( param.type.isArray() && param.type.size > 0)
792 							outType = param.type.elementType.name ~ "[" ~ to!string(param.type.size) ~ "]";
793 
794 						buff ~= stringToGtkD(outType, wrapper.aliasses, localAliases) ~" " ~ outName;
795 
796 						if ( param.direction == GirParamDirection.Out )
797 							buff[$-1] ~= ";";
798 						else
799 							buff[$-1] ~=" = "~ id ~".ptr";
800 
801 						if ( param.type.elementType.cType.empty )
802 							gtkCall ~= "cast("~stringToGtkD(param.type.cType, wrapper.aliasses, localAliases) ~")&" ~ outName;
803 						else if ( param.type.isArray() && param.type.size > 0)
804 							gtkCall ~= outName ~ ".ptr";
805 						else
806 							gtkCall ~= "&" ~ outName;
807 
808 						outToD ~= id ~" = "~ outName ~"[0 .. "~ lenId(param.type, outName) ~"];";
809 					}
810 					// T[]
811 					else
812 					{
813 						gtkCall ~= id ~".ptr";
814 					}
815 				}
816 				else
817 				{
818 					if ( param.type.name in strct.structWrap )
819 					{
820 						gtkCall ~= "("~ id ~" is null) ? null : "~ id ~".get"~ strct.structWrap[param.type.name] ~"Struct()";
821 					}
822 					else
823 					{
824 						// out T, ref T
825 						if ( param.direction != GirParamDirection.Default )
826 						{
827 							gtkCall ~= "&"~ id;
828 						}
829 						// T
830 						else
831 						{
832 							gtkCall ~= id;
833 						}
834 					}
835 				}
836 			}
837 		}
838 
839 		if ( throws )
840 		{
841 			buff ~= "GError* err = null;";
842 			gtkCall ~= ", &err";
843 		}
844 
845 		enum throwGException = [
846 			"",
847 			"if (err !is null)",
848 			"{",
849 			"throw new GException( new ErrorG(err) );",
850 			"}"];
851 
852 		gtkCall ~= ")";
853 
854 		if ( !buff.empty && buff[$-1] != "" )
855 			buff ~= "";
856 
857 		if ( returnType.name == "none" )
858 		{
859 			buff ~= gtkCall ~";";
860 
861 			if ( throws )
862 			{
863 				buff ~= throwGException;
864 			}
865 
866 			if ( !outToD.empty )
867 			{
868 				buff ~= "";
869 				buff ~= outToD;
870 			}
871 
872 			if ( name == "free" && strct && strct.shouldFree() )
873 			{
874 				buff ~= "ownedRef = false;";
875 			}
876 
877 			return buff;
878 		}
879 		else if ( type == GirFunctionType.Constructor )
880 		{
881 			buff ~= "auto __p = " ~ gtkCall ~";";
882 
883 			if ( throws )
884 			{
885 				buff ~= throwGException;
886 			}
887 
888 			buff ~= "";
889 			buff ~= "if(__p is null)";
890 			buff ~= "{";
891 			buff ~= "throw new ConstructionException(\"null returned by " ~ name ~ "\");";
892 			buff ~= "}";
893 			buff ~= "";
894 
895 			if ( !outToD.empty )
896 			{
897 				buff ~= outToD;
898 				buff ~= "";
899 			}
900 
901 			/*
902 			 * Casting is needed because some GTK+ functions
903 			 * can return void pointers or base types.
904 			 */
905 			if ( returnOwnership == GirTransferOwnership.Full && strct.getAncestor().name == "ObjectG" )
906 				buff ~= "this(cast(" ~ strct.cType ~ "*) __p, true);";
907 			else
908 				buff ~= "this(cast(" ~ strct.cType ~ "*) __p);";
909 
910 			return buff;
911 		}
912 		else if ( returnType.isString() )
913 		{
914 			if ( outToD.empty && !throws &&
915 			     !(returnOwnership == GirTransferOwnership.Full || returnOwnership == GirTransferOwnership.Container) )
916 			{
917 				if ( isStringArray(returnType) )
918 					buff ~= "return Str.toStringArray(" ~ gtkCall ~");";
919 				else
920 					buff ~= "return Str.toString(" ~ gtkCall ~");";
921 
922 				return buff;
923 			}
924 
925 			buff ~= "auto retStr = "~ gtkCall ~";";
926 
927 			if ( throws )
928 			{
929 				buff ~= throwGException;
930 			}
931 
932 			buff ~= "";
933 
934 			if ( !outToD.empty )
935 			{
936 				buff ~= outToD;
937 				buff ~= "";
938 			}
939 
940 			if ( returnOwnership == GirTransferOwnership.Full )
941 			{
942 				if ( isStringArray(returnType) )
943 					buff ~= "scope(exit) Str.freeStringArray(retStr);";
944 				else
945 					buff ~= "scope(exit) Str.freeString(retStr);";
946 			} else if ( returnOwnership == GirTransferOwnership.Container )
947 			{
948 				if ( isStringArray(returnType) )
949 					buff ~= "scope(exit) g_free(retStr);";
950 			}
951 
952 			string len = lenId(returnType);
953 			if ( !len.empty )
954 				len = ", "~ len;
955 
956 			if ( isStringArray(returnType) )
957 				buff ~= "return Str.toStringArray(retStr"~ len ~");";
958 			else
959 				buff ~= "return Str.toString(retStr"~ len ~");";
960 
961 			return buff;
962 		}
963 		else if ( returnDType && returnDType.isDClass() )
964 		{
965 			buff ~= "auto __p = "~ gtkCall ~";";
966 
967 			if ( throws )
968 			{
969 				buff ~= throwGException;
970 			}
971 
972 			if ( !outToD.empty )
973 			{
974 				buff ~= "";
975 				buff ~= outToD;
976 			}
977 
978 			buff ~= "";
979 			buff ~= "if(__p is null)";
980 			buff ~= "{";
981 			buff ~= "return null;";
982 			buff ~= "}";
983 			buff ~= "";
984 
985 			if ( returnType.isArray() )
986 			{
987 				buff ~= returnDType.name ~"[] arr = new "~ returnDType.name ~"["~ lenId(returnType) ~"];";
988 				buff ~= "for(int i = 0; i < "~ lenId(returnType) ~"; i++)";
989 				buff ~= "{";
990 				if ( returnType.elementType.cType.endsWith("*") )
991 					buff ~= "\tarr[i] = "~ construct(returnType.elementType.name) ~"(cast("~ returnType.elementType.cType ~") __p[i]);";
992 				else
993 					buff ~= "\tarr[i] = "~ construct(returnType.elementType.name) ~"(cast("~ returnType.elementType.cType ~"*) &__p[i]);";
994 				buff ~= "}";
995 				buff ~= "";
996 				buff ~= "return arr;";
997 			}
998 			else
999 			{
1000 				if ( returnOwnership == GirTransferOwnership.Full && !(returnDType.pack.name == "cairo") )
1001 					buff ~= "return "~ construct(returnType.name) ~"(cast("~ returnDType.cType ~"*) __p, true);";
1002 				else
1003 					buff ~= "return "~ construct(returnType.name) ~"(cast("~ returnDType.cType ~"*) __p);";
1004 			}
1005 
1006 			return buff;
1007 		}
1008 		else
1009 		{
1010 			if ( returnType.name == "gboolean" )
1011 				gtkCall ~= " != 0";
1012 
1013 			if ( !returnType.isArray && outToD.empty && !throws )
1014 			{
1015 				buff ~= "return "~ gtkCall ~";";
1016 				return buff;
1017 			}
1018 
1019 			buff ~= "auto __p = "~ gtkCall ~";";
1020 
1021 			if ( throws )
1022 			{
1023 				buff ~= throwGException;
1024 			}
1025 
1026 			if ( !outToD.empty )
1027 			{
1028 				buff ~= "";
1029 				buff ~= outToD;
1030 			}
1031 
1032 			buff ~= "";
1033 			if ( returnType.isArray() )
1034 			{
1035 				if ( returnType.elementType.name == "gboolean" )
1036 				{
1037 					buff ~= "bool[] r = new bool["~ lenId(returnType) ~"];";
1038 					buff ~= "for(size_t i = 0; i < "~ lenId(returnType) ~"; i++)";
1039 					buff ~= "{";
1040 					buff ~= "r[i] = __p[i] != 0;";
1041 					buff ~= "}";
1042 					buff ~= "return r;";
1043 				}
1044 				else if ( returnType.elementType.cType.empty && returnType.cType[0..$-1] != returnType.elementType.name )
1045 				{
1046 					buff ~= "return cast("~ getType(returnType) ~")__p[0 .. "~ lenId(returnType) ~"];";
1047 				}
1048 				else
1049 				{
1050 					buff ~= "return __p[0 .. "~ lenId(returnType) ~"];";
1051 				}
1052 			}
1053 			else
1054 				buff ~= "return __p;";
1055 
1056 			return buff;
1057 		}
1058 
1059 		assert(false, "Unexpected function: "~ name);
1060 	}
1061 
1062 	string getSignalName()
1063 	{
1064 		assert(type == GirFunctionType.Signal);
1065 
1066 		char pc;
1067 		string signalName;
1068 
1069 		foreach ( size_t count, char c; name )
1070 		{
1071 			if ( count == 0 && c != '-')
1072 			{
1073 				signalName ~= toUpper(c);
1074 			}
1075 			else
1076 			{
1077 				if ( c!='-' && c!='_' )
1078 				{
1079 					if ( pc=='-' || pc=='_' )
1080 						signalName ~= toUpper(c);
1081 					else
1082 						signalName ~= c;
1083 				}
1084 			}
1085 			pc = c;
1086 		}
1087 
1088 		if ( !signalName.among("Event", "MapEvent", "UnmapEvent", "DestroyEvent") &&
1089 		    endsWith(signalName, "Event") )
1090 		{
1091 			signalName = signalName[0..signalName.length-5];
1092 		}
1093 
1094 		return signalName;
1095 	}
1096 
1097 	string getDelegateDeclaration()
1098 	{
1099 		assert(type == GirFunctionType.Signal);
1100 
1101 		string buff = getType(returnType) ~ " delegate(";
1102 
1103 		foreach ( param; params )
1104 		{
1105 			//TODO: Signals with arrays.
1106 			if ( param.type.cType == "gpointer" && param.type.isArray() )
1107 				buff ~= "void*, ";
1108 			else
1109 				buff ~= getType(param.type) ~ ", ";
1110 		}
1111 
1112 		if ( strct.type == GirStructType.Interface )
1113 			buff ~= strct.name ~"IF)";
1114 		else
1115 			buff ~= strct.name ~")";
1116 
1117 		return buff;
1118 	}
1119 
1120 	string[] getAddListenerDeclaration()
1121 	{
1122 		string[] buff;
1123 
1124 		writeDocs(buff);
1125 		buff ~= "gulong addOn"~ getSignalName() ~"("~ getDelegateDeclaration() ~" dlg, ConnectFlags connectFlags=cast(ConnectFlags)0)";
1126 
1127 		return buff;
1128 	}
1129 
1130 	string[] getAddListenerBody()
1131 	{
1132 		string[] buff;
1133 
1134 		buff ~= "{";
1135 
1136 		if ( strct.hasFunction("add_events") )
1137 		{
1138 			switch ( name )
1139 			{
1140 				case  "button-press-event":      buff ~= "addEvents(EventMask.BUTTON_PRESS_MASK);";      break;
1141 				case  "button-release-event":    buff ~= "addEvents(EventMask.BUTTON_RELEASE_MASK);";    break;
1142 				case  "enter-notify-event":      buff ~= "addEvents(EventMask.ENTER_NOTIFY_MASK);";      break;
1143 				case  "focus-in-event":          buff ~= "addEvents(EventMask.FOCUS_CHANGE_MASK);";      break;
1144 				case  "focus-out-event":         buff ~= "addEvents(EventMask.FOCUS_CHANGE_MASK);";      break;
1145 				case  "key-press-event":         buff ~= "addEvents(EventMask.KEY_PRESS_MASK);";         break;
1146 				case  "key-release-event":       buff ~= "addEvents(EventMask.KEY_RELEASE_MASK);";       break;
1147 				case  "leave-notify-event":      buff ~= "addEvents(EventMask.LEAVE_NOTIFY_MASK);";      break;
1148 				case  "motion-notify-event":     buff ~= "addEvents(EventMask.POINTER_MOTION_MASK);";    break;
1149 				case  "property-notify-event":   buff ~= "addEvents(EventMask.PROPERTY_CHANGE_MASK);";   break;
1150 				case  "proximity-in-event":      buff ~= "addEvents(EventMask.PROXIMITY_IN_MASK);";      break;
1151 				case  "proximity-out-event":     buff ~= "addEvents(EventMask.PROXIMITY_OUT_MASK);";     break;
1152 				case  "scroll-event":            buff ~= "addEvents(EventMask.SCROLL_MASK);";            break;
1153 				case  "visibility-notify-event": buff ~= "addEvents(EventMask.VISIBILITY_NOTIFY_MASK);"; break;
1154 
1155 				default: break;
1156 			}
1157 		}
1158 
1159 		buff ~= "return Signals.connect(this, \""~ name ~"\", dlg, connectFlags ^ ConnectFlags.SWAPPED);";
1160 		buff ~= "}";
1161 
1162 		return buff;
1163 	}
1164 
1165 	
1166 	void writeDocs(ref string[] buff)
1167 	{
1168 		if ( (doc || returnType.doc) && wrapper.includeComments )
1169 		{
1170 			buff ~= "/**";
1171 			foreach ( line; doc.splitLines() )
1172 				buff ~= " * "~ line.strip();
1173 
1174 			if ( !params.empty || (instanceParam && type == GirFunctionType.Constructor) )
1175 			{
1176 				buff ~= " *";
1177 				buff ~= " * Params:";
1178 
1179 				if ( type == GirFunctionType.Constructor && instanceParam && !instanceParam.doc.empty )
1180 				{
1181 					string[] lines = instanceParam.doc.splitLines();
1182 					buff ~= " *     "~ tokenToGtkD(instanceParam.name, wrapper.aliasses, localAliases()) ~" = "~ lines[0];
1183 					foreach( line; lines[1..$] )
1184 						buff ~= " *         "~ line.strip();
1185 				}
1186 
1187 				foreach ( param; params )
1188 				{
1189 					if ( param.doc.empty )
1190 						continue;
1191 
1192 					if ( param.lengthFor )
1193 						continue;
1194 
1195 					if ( returnType.length > -1 && param == params[returnType.length] && params[returnType.length].direction != GirParamDirection.Default )
1196 						continue;
1197 
1198 					if ( isInstanceParam(param) )
1199 						continue;
1200 
1201 					string[] lines = param.doc.splitLines();
1202 					buff ~= " *     "~ tokenToGtkD(param.name, wrapper.aliasses, localAliases()) ~" = "~ lines[0];
1203 					foreach( line; lines[1..$] )
1204 						buff ~= " *         "~ line.strip();
1205 				}
1206 
1207 				if ( buff.endsWith(" * Params:") )
1208 					buff = buff[0 .. $-2];
1209 			}
1210 
1211 			if ( returnType.doc )
1212 			{
1213 				string[] lines = returnType.doc.splitLines();
1214 				if ( doc )
1215 					buff ~= " *";
1216 				buff ~= " * Returns: "~ lines[0];
1217 
1218 				foreach( line; lines[1..$] )
1219 					buff ~= " *     "~ line.strip();
1220 			}
1221 
1222 			if ( libVersion )
1223 			{
1224 				buff ~= " *";
1225 				buff ~= " * Since: "~ libVersion;
1226 			}
1227 
1228 			if ( throws || type == GirFunctionType.Constructor )
1229 				buff ~= " *";
1230 
1231 			if ( throws )
1232 				buff ~= " * Throws: GException on failure.";
1233 
1234 			if ( type == GirFunctionType.Constructor )
1235 				buff ~= " * Throws: ConstructionException GTK+ fails to create the object.";
1236 
1237 			buff ~= " */";
1238 		}
1239 		else if ( wrapper.includeComments )
1240 		{
1241 			buff ~= "/** */\n";
1242 		}
1243 	}
1244 
1245 	private void resolveLength()
1246 	{
1247 		foreach( param; params )
1248 		{
1249 			if ( param.type.length > -1 )
1250 				params[param.type.length].lengthFor = param;
1251 		}
1252 	}
1253 
1254 	private string[string] localAliases()
1255 	{
1256 		if ( strct )
1257 			return strct.aliases;
1258 
1259 		return null;
1260 	}
1261 
1262 	/**
1263 	 * Get an string representation of the type.
1264 	 */
1265 	private string getType(GirType type, GirParamDirection direction = GirParamDirection.Default)
1266 	{
1267 		if ( type.isString() )
1268 		{
1269 			if ( direction != GirParamDirection.Default && !type.cType.endsWith("**") )
1270 				return "char[]";
1271 			else if ( direction == GirParamDirection.Default && type.cType.endsWith("***") )
1272 				return "string[][]";
1273 			else if ( type.isArray && isStringArray(type.elementType, direction) )
1274 				return getType(type.elementType, direction) ~"[]";
1275 			else if ( isStringArray(type, direction) )
1276 				return "string[]";
1277 
1278 			return "string";
1279 		}
1280 		else if ( type.isArray() )
1281 		{
1282 			string size;
1283 
1284 			//Special case for GBytes and GVariant.
1285 			if ( type.cType == "gconstpointer" && type.elementType.cType == "gconstpointer" )
1286 				return "void[]";
1287 
1288 			if ( type.cType == "guchar*" )
1289 				return "char[]";
1290 
1291 			if ( type.size > -1 )
1292 				size = to!string(type.size);
1293 
1294 			string elmType = getType(type.elementType, direction);
1295 
1296 			if ( elmType == type.cType && elmType.endsWith("*") )
1297 				elmType = elmType[0..$-1];
1298 
1299 			return elmType ~"["~ size ~"]";
1300 		}
1301 		else
1302 		{
1303 			if ( type is null || type.name == "none" )
1304 				return "void";
1305 			else if ( type.name in strct.structWrap )
1306 				return strct.structWrap[type.name];
1307 			else if ( type.name == type.cType )
1308 				return stringToGtkD(type.name, wrapper.aliasses, localAliases());
1309 
1310 			GirStruct dType = strct.pack.getStruct(type.name);
1311 
1312 			if ( dType && dType.isDClass() )
1313 			{
1314 			    if ( dType.type == GirStructType.Interface )
1315 					return dType.name ~"IF";
1316 			    else
1317 			    	return dType.name;
1318 			}
1319 			else if ( type.cType.empty && dType && dType.type == GirStructType.Record )
1320 				return dType.cType ~ "*";
1321 		}
1322 
1323 		if ( type.cType.empty )
1324 		{
1325 			if ( auto enum_ = strct.pack.getEnum(type.name) )
1326 				return enum_.cName;
1327 
1328 			return stringToGtkD(type.name, wrapper.aliasses, localAliases());
1329 		}
1330 
1331 		if ( direction != GirParamDirection.Default && type.cType.endsWith("*") )
1332 			return stringToGtkD(type.cType[0..$-1], wrapper.aliasses, localAliases());
1333 
1334 		return stringToGtkD(type.cType, wrapper.aliasses, localAliases());
1335 	}
1336 
1337 	private bool isStringArray(GirType type, GirParamDirection direction = GirParamDirection.Default)
1338 	{
1339 		if ( direction == GirParamDirection.Default && type.cType.endsWith("**") )
1340 			return true;
1341 		if ( type.elementType is null )
1342 			return false;
1343 		if ( !type.elementType.cType.endsWith("*") )
1344 			return false;
1345 		if ( direction != GirParamDirection.Default && type.cType.among("char**", "gchar**", "guchar**") )
1346 			return false;
1347 
1348 		return true;
1349 	}
1350 
1351 	private bool isInstanceParam(GirParam param)
1352 	{
1353 		if ( param !is params[0] )
1354 			return false;
1355 		if ( strct is null || strct.type != GirStructType.Record )
1356 			return false;
1357 		if ( !(strct.lookupClass || strct.lookupInterface) )
1358 			return false;
1359 		if ( param.direction != GirParamDirection.Default )
1360 			return false;
1361 		if ( param.lengthFor !is null )
1362 			return false;
1363 		if ( strct.cType is null )
1364 			return false;
1365 		if ( param.type.cType == strct.cType ~"*" )
1366 			return true;
1367 
1368 		return false;
1369 	}
1370 
1371 	private string lenId(GirType type, string paramName = "__p")
1372 	{
1373 		if ( type.length > -1 && params[type.length].direction == GirParamDirection.Default && paramName != "__p" )
1374 			return "cast("~ tokenToGtkD(params[type.length].type.cType.removePtr(), wrapper.aliasses, localAliases()) ~")"~ paramName.replaceFirst("out", "") ~".length";
1375 		else if ( type.length > -1 )
1376 			return tokenToGtkD(params[type.length].name, wrapper.aliasses, localAliases());
1377 		//The c function returns the length.
1378 		else if ( type.length == -2 )
1379 			return "__p";
1380 		else if ( type.size > -1 )
1381 			return to!string(type.size);
1382 
1383 		if ( type.isString() )
1384 			return null;
1385 
1386 		return "getArrayLength("~ paramName ~")";
1387 	}
1388 
1389 	/**
1390 	 * Check if any of the ancestors contain the function functionName.
1391 	 */
1392 	private bool checkOverride()
1393 	{
1394 		if ( name == "get_type" )
1395 			return false;
1396 		if ( name == "to_string" && params.empty )
1397 			return true;
1398 
1399 		GirStruct ancestor = strct.getParent();
1400 
1401 		while(ancestor)
1402 		{
1403 			if ( name in ancestor.functions && name !in strct.aliases )
1404 			{
1405 				GirFunction func = ancestor.functions[name];
1406 
1407 				if ( !(func.noCode || func.isVariadic() || func.type == GirFunctionType.Callback) && paramsEqual(func) )
1408 					return true;
1409 			}
1410 
1411 			ancestor = ancestor.getParent();
1412 		}
1413 
1414 		return false;
1415 	}
1416 
1417 	/**
1418 	 * Return true if the params of func match the params of this function.
1419 	 */
1420 	private bool paramsEqual(GirFunction func)
1421 	{
1422 		if ( params.length != func.params.length )
1423 			return false;
1424 
1425 		foreach ( i, param; params )
1426 		{
1427 			if ( getType(param.type) != getType(func.params[i].type) )
1428 				return false;
1429 		}
1430 
1431 		return true;
1432 	}
1433 
1434 	private string construct(string type)
1435 	{
1436 		GirStruct dType = strct.pack.getStruct(type);
1437 		debug assert(dType, "Only call construct for valid GtkD types");
1438 		string name = dType.name;
1439 
1440 		if ( type in strct.structWrap )
1441 			name = strct.structWrap[type];
1442 
1443 		if ( dType.pack.name.among("cairo", "glib", "gthread") )
1444 			return "new "~name;
1445 		else if( dType.type == GirStructType.Interface )
1446 			return "ObjectG.getDObject!("~ name ~"IF)";
1447 		else
1448 			return "ObjectG.getDObject!("~ name ~")";
1449 	}
1450 }
1451 
1452 enum GirParamDirection : string
1453 {
1454 	Default = "",
1455 	Out = "out",
1456 	InOut = "inout",
1457 }
1458 
1459 final class GirParam
1460 {
1461 	string doc;
1462 	string name;
1463 	GirType type;
1464 	GirTransferOwnership ownership = GirTransferOwnership.None;
1465 	GirParamDirection direction = GirParamDirection.Default;
1466 
1467 	GirParam lengthFor;
1468 	GirWrapper wrapper;
1469 
1470 	this(GirWrapper wrapper)
1471 	{
1472 		this.wrapper = wrapper;
1473 	}
1474 
1475 	void parse(T)(XMLReader!T reader)
1476 	{
1477 		name = reader.front.attributes["name"];
1478 
1479 		if ( "transfer-ownership" in reader.front.attributes )
1480 			ownership = cast(GirTransferOwnership)reader.front.attributes["transfer-ownership"];
1481 		if ( "direction" in reader.front.attributes )
1482 			direction = cast(GirParamDirection)reader.front.attributes["direction"];
1483 
1484 		reader.popFront();
1485 
1486 		while( !reader.empty && !reader.endTag("parameter", "instance-parameter") )
1487 		{
1488 			if ( reader.front.type == XMLNodeType.EndTag )
1489 			{
1490 				reader.popFront();
1491 				continue;
1492 			}
1493 
1494 			switch(reader.front.value)
1495 			{
1496 				case "doc":
1497 					reader.popFront();
1498 					doc ~= reader.front.value;
1499 					reader.popFront();
1500 					break;
1501 				case "doc-deprecated":
1502 					reader.popFront();
1503 					doc ~= "\n\nDeprecated: "~ reader.front.value;
1504 					reader.popFront();
1505 					break;
1506 				case "array":
1507 				case "type":
1508 					type = new GirType(wrapper);
1509 					type.parse(reader);
1510 					break;
1511 				case "varargs":
1512 					type = new GirType(wrapper);
1513 					type.name = "...";
1514 					type.cType = "...";
1515 					break;
1516 				case "source-position":
1517 					reader.skipTag();
1518 					break;
1519 				default:
1520 					warning("Unexpected tag: ", reader.front.value, " in GirParam: ", name, reader);
1521 			}
1522 
1523 			reader.popFront();
1524 		}
1525 
1526 		if ( direction != GirParamDirection.Default && !type.cType.endsWith("*") )
1527 			direction = GirParamDirection.Default;
1528 	}
1529 }
1530 
1531 private string removePtr(string cType)
1532 {
1533 	while ( !cType.empty && cType.back == '*' )
1534 		cType.popBack();
1535 
1536 	return cType;
1537 }