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) && type != GirFunctionType.Constructor )
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 			GirParam instance = instanceParam ? instanceParam : params[0];
448 			GirStruct dType = strct.pack.getStruct(instance.type.name);
449 
450 			if ( dType.cType != instance.type.cType.removePtr() && !instance.type.cType.among("gpointer", "gconstpointer") )
451 				gtkCall ~= "cast("~ stringToGtkD(instance.type.cType, wrapper.aliasses, localAliases()) ~")";
452 
453 			if ( instance && instance.type.name in strct.structWrap )
454 			{
455 				GirStruct insType = strct.pack.getStruct(strct.structWrap[instance.type.name]);
456 
457 				if ( insType )
458 					dType = insType;
459 			}
460 
461 			if ( type == GirFunctionType.Constructor || strct.isNamespace() || strct.noNamespace )
462 			{
463 				string id = tokenToGtkD(instance.name, wrapper.aliasses, localAliases());
464 
465 				if ( dType && !(dType.isNamespace() || dType.noNamespace) )
466 					gtkCall ~= "("~ id ~" is null) ? null : "~ id ~"."~ dType.getHandleFunc() ~"()";
467 				else
468 					gtkCall ~= id;
469 			}
470 			else if ( dType.type == GirStructType.Interface || dType.lookupInterface )
471 			{
472 				gtkCall ~= strct.getHandleFunc() ~"()";
473 			}
474 			else
475 			{
476 				gtkCall ~= strct.getHandleVar();
477 			}
478 		}
479 
480 		foreach( i, param; params )
481 		{
482 			GirStruct dType;
483 			string id = tokenToGtkD(param.name, wrapper.aliasses, localAliases());
484 
485 			if ( param.type.isArray() )
486 				dType = strct.pack.getStruct(param.type.elementType.name);
487 			else if ( auto dStrct = strct.pack.getStruct(strct.structWrap.get(param.type.name, "")) )
488 				dType = dStrct;
489 			else
490 				dType = strct.pack.getStruct(param.type.name);
491 
492 			if ( i == 0 && isInstanceParam(param) )
493 				continue;
494 
495 			if ( instanceParam || i > 0 )
496 				gtkCall ~= ", ";
497 
498 			if ( param.type.isString() )
499 			{
500 				if ( isStringArray(param.type, param.direction) )
501 				{
502 					// out string[], ref string[]
503 					if ( param.direction != GirParamDirection.Default )
504 					{
505 						buff ~= "char** out"~ id ~" = ";
506 
507 						if ( param.direction == GirParamDirection.Out )
508 							buff[$-1] ~= "null;";
509 						else
510 							buff[$-1] ~= "Str.toStringzArray("~ id ~");";
511 
512 						string len = lenId(param.type);
513 						if ( !len.empty )
514 							len = ", "~ len;
515 
516 						gtkCall ~= "&out"~ id;
517 						outToD ~= id ~" = Str.toStringArray(out"~ id ~ len ~");";
518 					}
519 					// string[]
520 					else
521 					{
522 						gtkCall ~= "Str.toStringzArray("~ id ~")";
523 					}
524 				}
525 				else
526 				{
527 					if ( param.direction != GirParamDirection.Default )
528 					{
529 						string len = lenId(param.type);
530 
531 						// A buffer to fill.
532 						if ( !param.type.cType.endsWith("**") )
533 						{
534 							gtkCall ~= id ~".ptr";
535 
536 							if ( !len.empty && params[param.type.length].direction != GirParamDirection.Default )
537 								outToD ~= id ~" = "~ id ~"[0.."~ len ~"];";
538 						}
539 						// out string, ref string
540 						else
541 						{
542 							buff ~= "char* out"~ id ~" = ";
543 
544 							if ( param.direction == GirParamDirection.Out )
545 								buff[$-1] ~= "null;";
546 							else
547 								buff[$-1] ~= "Str.toStringz("~ id ~");";
548 
549 							if ( !len.empty )
550 								len = ", "~ len;
551 
552 							gtkCall ~= "&out"~ id;
553 							outToD ~= id ~" = Str.toString(out"~ id ~ len ~");";
554 						}
555 					}
556 					// string
557 					else
558 					{
559 						gtkCall ~= "Str.toStringz("~ id ~")";
560 					}
561 				}
562 			}
563 			else if ( dType && dType.isDClass() )
564 			{
565 				if ( param.type.isArray() )
566 				{
567 					GirType elementType = param.type.elementType;
568 					GirStruct dElementType = strct.pack.getStruct(elementType.name);
569 
570 					if ( elementType.cType.empty )
571 						elementType.cType = stringToGtkD(param.type.cType, wrapper.aliasses, localAliases())[0 .. $-1];
572 
573 					// out gtkdType[], ref gtkdType[]
574 					if ( param.direction != GirParamDirection.Default )
575 					{
576 						if ( param.direction == GirParamDirection.Out )
577 						{
578 							if ( param.type.size > 0 )
579 								buff ~= elementType.cType ~"* out"~ id ~" = cast("~ elementType.cType ~"*)sliceAlloc0("~ elementType.cType ~".sizeof * "~ to!string(param.type.size) ~");";
580 							else if ( !elementType.cType.endsWith("*") )
581 								buff ~= elementType.cType ~"* out"~ id ~" = null;";
582 							else
583 								buff ~= elementType.cType ~" out"~ id ~" = null;";
584 						}
585 						else
586 						{
587 							if ( !buff.empty )
588 								buff ~= "";
589 							buff ~= elementType.cType.removePtr() ~ "**[] inout"~ id ~" = new "~ elementType.cType.removePtr() ~"*["~ id ~".length];";
590 							buff ~= "for ( int i = 0; i < "~ id ~".length; i++ )";
591 							buff ~= "{";
592 							buff ~= "inout"~ id ~"[i] = "~ id~ "[i]."~ dElementType.getHandleFunc() ~"();";
593 							buff ~= "}";
594 							buff ~= "";
595 							buff ~= elementType.cType.removePtr() ~ "** out"~ id ~" = inout"~ id ~".ptr;";
596 						}
597 
598 						if ( !elementType.cType.endsWith("*") )
599 							gtkCall ~= "out"~ id;
600 						else
601 							gtkCall ~= "&out"~ id;
602 
603 						if ( !outToD.empty )
604 							outToD ~= "";
605 						if ( param.type.size <= 0 )
606 							outToD ~= id ~" = new "~ dElementType.name ~"["~ lenId(param.type, "out"~ id) ~"];";
607 						outToD ~= "for(size_t i = 0; i < "~ lenId(param.type, "out"~ id) ~"; i++)";
608 						outToD ~= "{";
609 						if ( elementType.cType.endsWith("**") )
610 							outToD ~= id ~"[i] = " ~ construct(elementType.name) ~ "(cast(" ~ elementType.cType[0..$-1] ~ ") out"~ id ~"[i]);";
611 						else if ( !elementType.cType.endsWith("*") )
612 							outToD ~= id ~"[i] = " ~ construct(elementType.name) ~ "(cast(" ~ elementType.cType ~ "*) &out"~ id ~"[i]);";
613 						else
614 							outToD ~= id ~"[i] = " ~ construct(elementType.name) ~ "(cast(" ~ elementType.cType ~ ") &out"~ id ~"[i]);";
615 						outToD ~= "}";
616 					}
617 					// gtkdType[]
618 					else
619 					{
620 						//TODO: zero-terminated see: g_signal_chain_from_overridden
621 						if ( !buff.empty )
622 							buff ~= "";
623 						buff ~= elementType.cType ~ "[] "~ id ~"Array = new "~ elementType.cType ~"["~ id ~".length];";
624 						buff ~= "for ( int i = 0; i < "~ id ~".length; i++ )";
625 						buff ~= "{";
626 						if ( elementType.cType.endsWith("*") )
627 							buff ~= id ~"Array[i] = "~ id ~"[i]."~ dElementType.getHandleFunc() ~"();";
628 						else
629 							buff ~= id ~"Array[i] = *("~ id ~"[i]."~ dElementType.getHandleFunc() ~"());";
630 						buff ~= "}";
631 						buff ~= "";
632 
633 						gtkCall ~= id ~"Array.ptr";
634 					}
635 				}
636 				else
637 				{
638 					// out gtkdType, ref gtkdType
639 					if ( param.direction != GirParamDirection.Default && param.type.cType.endsWith("**") )
640 					{
641 						buff ~= param.type.cType.removePtr() ~"* out"~ id ~" = ";
642 
643 						if ( param.direction == GirParamDirection.Out )
644 							buff[$-1] ~= "null;";
645 						else
646 							buff[$-1] ~= id ~"."~ dType.getHandleFunc() ~"();";
647 
648 						gtkCall ~= "&out"~ id;
649 
650 						outToD ~= id ~" = "~ construct(param.type.name) ~"(out"~ id ~");";
651 					}
652 					else if ( param.direction == GirParamDirection.Out )
653 					{
654 						buff ~= param.type.cType.removePtr() ~"* out"~ id ~" = sliceNew!"~ param.type.cType.removePtr() ~"();";
655 
656 						gtkCall ~= "out"~ id;
657 
658 						outToD ~= id ~" = "~ construct(param.type.name) ~"(out"~ id ~", true);";
659 					}
660 					// gtkdType
661 					else
662 					{
663 						gtkCall ~= "("~ id ~" is null) ? null : ";
664 						if ( dType.cType != param.type.cType.removePtr() && !param.type.cType.among("gpointer", "gconstpointer") )
665 							gtkCall ~= "cast("~ stringToGtkD(param.type.cType, wrapper.aliasses, localAliases()) ~")";
666 
667 						if ( param.ownership == GirTransferOwnership.Full && dType.shouldFree() )
668 							gtkCall ~= id ~"."~ dType.getHandleFunc ~"(true)";
669 						else
670 							gtkCall ~= id ~"."~ dType.getHandleFunc ~"()";
671 					}
672 				}
673 			}
674 			else if ( param.lengthFor || (returnType.length == i && param.direction != GirParamDirection.Default ) )
675 			{
676 				string arrId;
677 				string lenType = tokenToGtkD(param.type.cType.removePtr(), wrapper.aliasses, localAliases());
678 
679 				if ( param.lengthFor )
680 					arrId = tokenToGtkD(param.lengthFor.name, wrapper.aliasses, localAliases());
681 
682 				final switch ( param.direction ) with (GirParamDirection)
683 				{
684 					case Default:
685 						gtkCall ~= "cast("~ lenType ~")"~ arrId ~".length";
686 						break;
687 					case Out:
688 						buff ~= lenType ~" "~ id ~";";
689 						gtkCall ~= "&"~id;
690 						break;
691 					case InOut:
692 						buff ~= lenType ~" "~ id ~" = cast("~ lenType ~")"~ arrId ~".length;";
693 						gtkCall ~= "&"~id;
694 						break;
695 				}
696 			}
697 			else if ( param.type.name.among("bool", "gboolean") || ( param.type.isArray && param.type.elementType.name.among("bool", "gboolean") ) )
698 			{
699 				if ( param.type.isArray() )
700 				{
701 					// out bool[], ref bool[]
702 					if ( param.direction != GirParamDirection.Default )
703 					{
704 						if ( param.direction == GirParamDirection.Out )
705 						{
706 							buff ~= "int* out"~ id ~" = null;";
707 						}
708 						else
709 						{
710 							if ( !buff.empty )
711 								buff ~= "";
712 							buff ~= "int[] inout"~ id ~" = new int["~ id ~".length];";
713 							buff ~= "for ( int i = 0; i < "~ id ~".length; i++ )";
714 							buff ~= "{";
715 							buff ~= "inout"~ id ~"[i] = "~ id~ "[i] ? 1 : 0;";
716 							buff ~= "}";
717 							buff ~= "";
718 							buff ~= "int* out"~ id ~" = inout"~ id ~".ptr;";
719 						}
720 
721 						gtkCall ~= "&out"~ id;
722 
723 						if ( !outToD.empty )
724 							outToD ~= "";
725 						outToD ~= id ~" = new bool["~ lenId(param.type, "out"~ id) ~"];";
726 						outToD ~= "for(size_t i = 0; i < "~ lenId(param.type, "out"~ id) ~"; i++)";
727 						outToD ~= "{";
728 						outToD ~= id ~"[i] = out"~ id ~"[i] != 0);";
729 						outToD ~= "}";
730 					}
731 					// bool[]
732 					else
733 					{
734 						if ( !buff.empty )
735 							buff ~= "";
736 						buff ~= "int[] "~ id ~"Array = new int["~ id ~".length];";
737 						buff ~= "for ( int i = 0; i < "~ id ~".length; i++ )";
738 						buff ~= "{";
739 						buff ~= id ~"Array[i] = "~ id ~"[i] ? 1 : 0;";
740 						buff ~= "}";
741 						buff ~= "";
742 
743 						gtkCall ~= id ~"Array.ptr";
744 					}
745 				}
746 				else
747 				{
748 					// out bool, ref bool
749 					if ( param.direction != GirParamDirection.Default )
750 					{
751 						buff ~= "int out"~ id;
752 
753 						if ( param.direction == GirParamDirection.Out )
754 							buff[$-1] ~= ";";
755 						else
756 							buff[$-1] ~= " = ("~ id ~" ? 1 : 0);";
757 
758 						gtkCall ~= "&out"~ id ~"";
759 						outToD ~= id ~" = (out"~ id ~" == 1);";
760 					}
761 					// bool
762 					else
763 					{
764 						gtkCall ~= id;
765 					}
766 				}
767 			}
768 			else
769 			{
770 				if ( param.type.isArray() )
771 				{
772 					// out T[], ref T[]
773 					if ( param.direction != GirParamDirection.Default )
774 					{
775 						string outType = param.type.elementType.cType;
776 						string outName = "out" ~ id;
777 
778 						if ( outType.empty )
779 							outType = param.type.elementType.name ~"*";
780 						else if ( param.type.isArray() && param.type.size > 0)
781 							outType = param.type.elementType.name ~ "[" ~ to!string(param.type.size) ~ "]";
782 
783 						buff ~= stringToGtkD(outType, wrapper.aliasses, localAliases) ~" " ~ outName;
784 
785 						if ( param.direction == GirParamDirection.Out )
786 							buff[$-1] ~= ";";
787 						else
788 							buff[$-1] ~=" = "~ id ~".ptr";
789 
790 						if ( param.type.elementType.cType.empty )
791 							gtkCall ~= "cast("~stringToGtkD(param.type.cType, wrapper.aliasses, localAliases) ~")&" ~ outName;
792 						else if ( param.type.isArray() && param.type.size > 0)
793 							gtkCall ~= outName ~ ".ptr";
794 						else
795 							gtkCall ~= "&" ~ outName;
796 
797 						outToD ~= id ~" = "~ outName ~"[0 .. "~ lenId(param.type, outName) ~"];";
798 					}
799 					// T[]
800 					else
801 					{
802 						gtkCall ~= id ~".ptr";
803 					}
804 				}
805 				else
806 				{
807 					if ( param.type.name in strct.structWrap )
808 					{
809 						gtkCall ~= "("~ id ~" is null) ? null : "~ id ~".get"~ strct.structWrap[param.type.name] ~"Struct()";
810 					}
811 					else
812 					{
813 						// out T, ref T
814 						if ( param.direction != GirParamDirection.Default )
815 						{
816 							gtkCall ~= "&"~ id;
817 						}
818 						// T
819 						else
820 						{
821 							gtkCall ~= id;
822 						}
823 					}
824 				}
825 			}
826 		}
827 
828 		if ( throws )
829 		{
830 			buff ~= "GError* err = null;";
831 			gtkCall ~= ", &err";
832 		}
833 
834 		enum throwGException = [
835 			"",
836 			"if (err !is null)",
837 			"{",
838 			"throw new GException( new ErrorG(err) );",
839 			"}"];
840 
841 		gtkCall ~= ")";
842 
843 		if ( !buff.empty && buff[$-1] != "" )
844 			buff ~= "";
845 
846 		if ( returnType.name == "none" )
847 		{
848 			buff ~= gtkCall ~";";
849 
850 			if ( throws )
851 			{
852 				buff ~= throwGException;
853 			}
854 
855 			if ( !outToD.empty )
856 			{
857 				buff ~= "";
858 				buff ~= outToD;
859 			}
860 
861 			if ( name == "free" && strct && strct.shouldFree() )
862 			{
863 				buff ~= "ownedRef = false;";
864 			}
865 
866 			return buff;
867 		}
868 		else if ( type == GirFunctionType.Constructor )
869 		{
870 			buff ~= "auto __p = " ~ gtkCall ~";";
871 
872 			if ( throws )
873 			{
874 				buff ~= throwGException;
875 			}
876 
877 			buff ~= "";
878 			buff ~= "if(__p is null)";
879 			buff ~= "{";
880 			buff ~= "throw new ConstructionException(\"null returned by " ~ name ~ "\");";
881 			buff ~= "}";
882 			buff ~= "";
883 
884 			if ( !outToD.empty )
885 			{
886 				buff ~= outToD;
887 				buff ~= "";
888 			}
889 
890 			/*
891 			 * Casting is needed because some GTK+ functions
892 			 * can return void pointers or base types.
893 			 */
894 			if ( returnOwnership == GirTransferOwnership.Full && strct.getAncestor().name == "ObjectG" )
895 				buff ~= "this(cast(" ~ strct.cType ~ "*) __p, true);";
896 			else
897 				buff ~= "this(cast(" ~ strct.cType ~ "*) __p);";
898 
899 			return buff;
900 		}
901 		else if ( returnType.isString() )
902 		{
903 			if ( outToD.empty && !throws &&
904 			     !(returnOwnership == GirTransferOwnership.Full || returnOwnership == GirTransferOwnership.Container) )
905 			{
906 				if ( isStringArray(returnType) )
907 					buff ~= "return Str.toStringArray(" ~ gtkCall ~");";
908 				else
909 					buff ~= "return Str.toString(" ~ gtkCall ~");";
910 
911 				return buff;
912 			}
913 
914 			buff ~= "auto retStr = "~ gtkCall ~";";
915 
916 			if ( throws )
917 			{
918 				buff ~= throwGException;
919 			}
920 
921 			buff ~= "";
922 
923 			if ( !outToD.empty )
924 			{
925 				buff ~= outToD;
926 				buff ~= "";
927 			}
928 
929 			if ( returnOwnership == GirTransferOwnership.Full )
930 			{
931 				if ( isStringArray(returnType) )
932 					buff ~= "scope(exit) Str.freeStringArray(retStr);";
933 				else
934 					buff ~= "scope(exit) Str.freeString(retStr);";
935 			} else if ( returnOwnership == GirTransferOwnership.Container )
936 			{
937 				if ( isStringArray(returnType) )
938 					buff ~= "scope(exit) g_free(retStr);";
939 			}
940 
941 			string len = lenId(returnType);
942 			if ( !len.empty )
943 				len = ", "~ len;
944 
945 			if ( isStringArray(returnType) )
946 				buff ~= "return Str.toStringArray(retStr"~ len ~");";
947 			else
948 				buff ~= "return Str.toString(retStr"~ len ~");";
949 
950 			return buff;
951 		}
952 		else if ( returnDType && returnDType.isDClass() )
953 		{
954 			buff ~= "auto __p = "~ gtkCall ~";";
955 
956 			if ( throws )
957 			{
958 				buff ~= throwGException;
959 			}
960 
961 			if ( !outToD.empty )
962 			{
963 				buff ~= "";
964 				buff ~= outToD;
965 			}
966 
967 			buff ~= "";
968 			buff ~= "if(__p is null)";
969 			buff ~= "{";
970 			buff ~= "return null;";
971 			buff ~= "}";
972 			buff ~= "";
973 
974 			if ( returnType.isArray() )
975 			{
976 				buff ~= returnDType.name ~"[] arr = new "~ returnDType.name ~"["~ lenId(returnType) ~"];";
977 				buff ~= "for(int i = 0; i < "~ lenId(returnType) ~"; i++)";
978 				buff ~= "{";
979 				if ( returnType.elementType.cType.endsWith("*") )
980 					buff ~= "\tarr[i] = "~ construct(returnType.elementType.name) ~"(cast("~ returnType.elementType.cType ~") __p[i]);";
981 				else
982 					buff ~= "\tarr[i] = "~ construct(returnType.elementType.name) ~"(cast("~ returnType.elementType.cType ~"*) &__p[i]);";
983 				buff ~= "}";
984 				buff ~= "";
985 				buff ~= "return arr;";
986 			}
987 			else
988 			{
989 				if ( returnOwnership == GirTransferOwnership.Full && !(returnDType.pack.name == "cairo") )
990 					buff ~= "return "~ construct(returnType.name) ~"(cast("~ returnDType.cType ~"*) __p, true);";
991 				else
992 					buff ~= "return "~ construct(returnType.name) ~"(cast("~ returnDType.cType ~"*) __p);";
993 			}
994 
995 			return buff;
996 		}
997 		else
998 		{
999 			if ( returnType.name == "gboolean" )
1000 				gtkCall ~= " != 0";
1001 
1002 			if ( !returnType.isArray && outToD.empty && !throws )
1003 			{
1004 				buff ~= "return "~ gtkCall ~";";
1005 				return buff;
1006 			}
1007 
1008 			buff ~= "auto __p = "~ gtkCall ~";";
1009 
1010 			if ( throws )
1011 			{
1012 				buff ~= throwGException;
1013 			}
1014 
1015 			if ( !outToD.empty )
1016 			{
1017 				buff ~= "";
1018 				buff ~= outToD;
1019 			}
1020 
1021 			buff ~= "";
1022 			if ( returnType.isArray() )
1023 			{
1024 				if ( returnType.elementType.name == "gboolean" )
1025 				{
1026 					buff ~= "bool[] r = new bool["~ lenId(returnType) ~"];";
1027 					buff ~= "for(size_t i = 0; i < "~ lenId(returnType) ~"; i++)";
1028 					buff ~= "{";
1029 					buff ~= "r[i] = __p[i] != 0;";
1030 					buff ~= "}";
1031 					buff ~= "return r;";
1032 				}
1033 				else if ( returnType.elementType.cType.empty && returnType.cType[0..$-1] != returnType.elementType.name )
1034 				{
1035 					buff ~= "return cast("~ getType(returnType) ~")__p[0 .. "~ lenId(returnType) ~"];";
1036 				}
1037 				else
1038 				{
1039 					buff ~= "return __p[0 .. "~ lenId(returnType) ~"];";
1040 				}
1041 			}
1042 			else
1043 				buff ~= "return __p;";
1044 
1045 			return buff;
1046 		}
1047 
1048 		assert(false, "Unexpected function: "~ name);
1049 	}
1050 
1051 	string getSignalName()
1052 	{
1053 		assert(type == GirFunctionType.Signal);
1054 
1055 		char pc;
1056 		string signalName;
1057 
1058 		foreach ( size_t count, char c; name )
1059 		{
1060 			if ( count == 0 && c != '-')
1061 			{
1062 				signalName ~= toUpper(c);
1063 			}
1064 			else
1065 			{
1066 				if ( c!='-' && c!='_' )
1067 				{
1068 					if ( pc=='-' || pc=='_' )
1069 						signalName ~= toUpper(c);
1070 					else
1071 						signalName ~= c;
1072 				}
1073 			}
1074 			pc = c;
1075 		}
1076 
1077 		if ( !signalName.among("Event", "MapEvent", "UnmapEvent", "DestroyEvent") &&
1078 		    endsWith(signalName, "Event") )
1079 		{
1080 			signalName = signalName[0..signalName.length-5];
1081 		}
1082 
1083 		return signalName;
1084 	}
1085 
1086 	string getDelegateDeclaration()
1087 	{
1088 		assert(type == GirFunctionType.Signal);
1089 
1090 		string buff = getType(returnType) ~ " delegate(";
1091 
1092 		foreach ( param; params )
1093 		{
1094 			//TODO: Signals with arrays.
1095 			if ( param.type.cType == "gpointer" && param.type.isArray() )
1096 				buff ~= "void*, ";
1097 			else
1098 				buff ~= getType(param.type) ~ ", ";
1099 		}
1100 
1101 		if ( strct.type == GirStructType.Interface )
1102 			buff ~= strct.name ~"IF)";
1103 		else
1104 			buff ~= strct.name ~")";
1105 
1106 		return buff;
1107 	}
1108 
1109 	string[] getAddListenerDeclaration()
1110 	{
1111 		string[] buff;
1112 
1113 		writeDocs(buff);
1114 		buff ~= "gulong addOn"~ getSignalName() ~"("~ getDelegateDeclaration() ~" dlg, ConnectFlags connectFlags=cast(ConnectFlags)0)";
1115 
1116 		return buff;
1117 	}
1118 
1119 	string[] getAddListenerBody()
1120 	{
1121 		string[] buff;
1122 
1123 		buff ~= "{";
1124 
1125 		if ( strct.hasFunction("add_events") )
1126 		{
1127 			switch ( name )
1128 			{
1129 				case  "button-press-event":      buff ~= "addEvents(EventMask.BUTTON_PRESS_MASK);";      break;
1130 				case  "button-release-event":    buff ~= "addEvents(EventMask.BUTTON_RELEASE_MASK);";    break;
1131 				case  "enter-notify-event":      buff ~= "addEvents(EventMask.ENTER_NOTIFY_MASK);";      break;
1132 				case  "focus-in-event":          buff ~= "addEvents(EventMask.FOCUS_CHANGE_MASK);";      break;
1133 				case  "focus-out-event":         buff ~= "addEvents(EventMask.FOCUS_CHANGE_MASK);";      break;
1134 				case  "key-press-event":         buff ~= "addEvents(EventMask.KEY_PRESS_MASK);";         break;
1135 				case  "key-release-event":       buff ~= "addEvents(EventMask.KEY_RELEASE_MASK);";       break;
1136 				case  "leave-notify-event":      buff ~= "addEvents(EventMask.LEAVE_NOTIFY_MASK);";      break;
1137 				case  "motion-notify-event":     buff ~= "addEvents(EventMask.POINTER_MOTION_MASK);";    break;
1138 				case  "property-notify-event":   buff ~= "addEvents(EventMask.PROPERTY_CHANGE_MASK);";   break;
1139 				case  "proximity-in-event":      buff ~= "addEvents(EventMask.PROXIMITY_IN_MASK);";      break;
1140 				case  "proximity-out-event":     buff ~= "addEvents(EventMask.PROXIMITY_OUT_MASK);";     break;
1141 				case  "scroll-event":            buff ~= "addEvents(EventMask.SCROLL_MASK);";            break;
1142 				case  "visibility-notify-event": buff ~= "addEvents(EventMask.VISIBILITY_NOTIFY_MASK);"; break;
1143 
1144 				default: break;
1145 			}
1146 		}
1147 
1148 		buff ~= "return Signals.connect(this, \""~ name ~"\", dlg, connectFlags ^ ConnectFlags.SWAPPED);";
1149 		buff ~= "}";
1150 
1151 		return buff;
1152 	}
1153 
1154 	
1155 	void writeDocs(ref string[] buff)
1156 	{
1157 		if ( (doc || returnType.doc) && wrapper.includeComments )
1158 		{
1159 			buff ~= "/**";
1160 			foreach ( line; doc.splitLines() )
1161 				buff ~= " * "~ line.strip();
1162 
1163 			if ( !params.empty || (instanceParam && type == GirFunctionType.Constructor) )
1164 			{
1165 				buff ~= " *";
1166 				buff ~= " * Params:";
1167 
1168 				if ( type == GirFunctionType.Constructor && instanceParam && !instanceParam.doc.empty )
1169 				{
1170 					string[] lines = instanceParam.doc.splitLines();
1171 					buff ~= " *     "~ tokenToGtkD(instanceParam.name, wrapper.aliasses, localAliases()) ~" = "~ lines[0];
1172 					foreach( line; lines[1..$] )
1173 						buff ~= " *         "~ line.strip();
1174 				}
1175 
1176 				foreach ( param; params )
1177 				{
1178 					if ( param.doc.empty )
1179 						continue;
1180 
1181 					if ( param.lengthFor )
1182 						continue;
1183 
1184 					if ( returnType.length > -1 && param == params[returnType.length] && params[returnType.length].direction != GirParamDirection.Default )
1185 						continue;
1186 
1187 					if ( isInstanceParam(param) )
1188 						continue;
1189 
1190 					string[] lines = param.doc.splitLines();
1191 					buff ~= " *     "~ tokenToGtkD(param.name, wrapper.aliasses, localAliases()) ~" = "~ lines[0];
1192 					foreach( line; lines[1..$] )
1193 						buff ~= " *         "~ line.strip();
1194 				}
1195 
1196 				if ( buff.endsWith(" * Params:") )
1197 					buff = buff[0 .. $-2];
1198 			}
1199 
1200 			if ( returnType.doc )
1201 			{
1202 				string[] lines = returnType.doc.splitLines();
1203 				if ( doc )
1204 					buff ~= " *";
1205 				buff ~= " * Returns: "~ lines[0];
1206 
1207 				foreach( line; lines[1..$] )
1208 					buff ~= " *     "~ line.strip();
1209 			}
1210 
1211 			if ( libVersion )
1212 			{
1213 				buff ~= " *";
1214 				buff ~= " * Since: "~ libVersion;
1215 			}
1216 
1217 			if ( throws || type == GirFunctionType.Constructor )
1218 				buff ~= " *";
1219 
1220 			if ( throws )
1221 				buff ~= " * Throws: GException on failure.";
1222 
1223 			if ( type == GirFunctionType.Constructor )
1224 				buff ~= " * Throws: ConstructionException GTK+ fails to create the object.";
1225 
1226 			buff ~= " */";
1227 		}
1228 		else if ( wrapper.includeComments )
1229 		{
1230 			buff ~= "/** */\n";
1231 		}
1232 	}
1233 
1234 	private void resolveLength()
1235 	{
1236 		foreach( param; params )
1237 		{
1238 			if ( param.type.length > -1 )
1239 				params[param.type.length].lengthFor = param;
1240 		}
1241 	}
1242 
1243 	private string[string] localAliases()
1244 	{
1245 		if ( strct )
1246 			return strct.aliases;
1247 
1248 		return null;
1249 	}
1250 
1251 	/**
1252 	 * Get an string representation of the type.
1253 	 */
1254 	private string getType(GirType type, GirParamDirection direction = GirParamDirection.Default)
1255 	{
1256 		if ( type.isString() )
1257 		{
1258 			if ( direction != GirParamDirection.Default && !type.cType.endsWith("**") )
1259 				return "char[]";
1260 			else if ( direction == GirParamDirection.Default && type.cType.endsWith("***") )
1261 				return "string[][]";
1262 			else if ( type.isArray && isStringArray(type.elementType, direction) )
1263 				return getType(type.elementType, direction) ~"[]";
1264 			else if ( isStringArray(type, direction) )
1265 				return "string[]";
1266 
1267 			return "string";
1268 		}
1269 		else if ( type.isArray() )
1270 		{
1271 			string size;
1272 
1273 			//Special case for GBytes and GVariant.
1274 			if ( type.cType == "gconstpointer" && type.elementType.cType == "gconstpointer" )
1275 				return "void[]";
1276 
1277 			if ( type.cType == "guchar*" )
1278 				return "char[]";
1279 
1280 			if ( type.size > -1 )
1281 				size = to!string(type.size);
1282 
1283 			string elmType = getType(type.elementType, direction);
1284 
1285 			if ( elmType == type.cType && elmType.endsWith("*") )
1286 				elmType = elmType[0..$-1];
1287 
1288 			return elmType ~"["~ size ~"]";
1289 		}
1290 		else
1291 		{
1292 			if ( type is null || type.name == "none" )
1293 				return "void";
1294 			else if ( type.name in strct.structWrap )
1295 				return strct.structWrap[type.name];
1296 			else if ( type.name == type.cType )
1297 				return stringToGtkD(type.name, wrapper.aliasses, localAliases());
1298 
1299 			GirStruct dType = strct.pack.getStruct(type.name);
1300 
1301 			if ( dType && dType.isDClass() )
1302 			{
1303 			    if ( dType.type == GirStructType.Interface )
1304 					return dType.name ~"IF";
1305 			    else
1306 			    	return dType.name;
1307 			}
1308 			else if ( type.cType.empty && dType && dType.type == GirStructType.Record )
1309 				return dType.cType ~ "*";
1310 		}
1311 
1312 		if ( type.cType.empty )
1313 		{
1314 			if ( auto enum_ = strct.pack.getEnum(type.name) )
1315 				return enum_.cName;
1316 
1317 			return stringToGtkD(type.name, wrapper.aliasses, localAliases());
1318 		}
1319 
1320 		if ( direction != GirParamDirection.Default && type.cType.endsWith("*") )
1321 			return stringToGtkD(type.cType[0..$-1], wrapper.aliasses, localAliases());
1322 
1323 		return stringToGtkD(type.cType, wrapper.aliasses, localAliases());
1324 	}
1325 
1326 	private bool isStringArray(GirType type, GirParamDirection direction = GirParamDirection.Default)
1327 	{
1328 		if ( direction == GirParamDirection.Default && type.cType.endsWith("**") )
1329 			return true;
1330 		if ( type.elementType is null )
1331 			return false;
1332 		if ( !type.elementType.cType.endsWith("*") )
1333 			return false;
1334 		if ( direction != GirParamDirection.Default && type.cType.among("char**", "gchar**", "guchar**") )
1335 			return false;
1336 
1337 		return true;
1338 	}
1339 
1340 	private bool isInstanceParam(GirParam param)
1341 	{
1342 		if ( param !is params[0] )
1343 			return false;
1344 		if ( strct is null || strct.type != GirStructType.Record )
1345 			return false;
1346 		if ( !(strct.lookupClass || strct.lookupInterface) )
1347 			return false;
1348 		if ( param.direction != GirParamDirection.Default )
1349 			return false;
1350 		if ( param.lengthFor !is null )
1351 			return false;
1352 		if ( strct.cType is null )
1353 			return false;
1354 		if ( param.type.cType == strct.cType ~"*" )
1355 			return true;
1356 
1357 		return false;
1358 	}
1359 
1360 	private string lenId(GirType type, string paramName = "__p")
1361 	{
1362 		if ( type.length > -1 && params[type.length].direction == GirParamDirection.Default && paramName != "__p" )
1363 			return "cast("~ tokenToGtkD(params[type.length].type.cType.removePtr(), wrapper.aliasses, localAliases()) ~")"~ paramName.replaceFirst("out", "") ~".length";
1364 		else if ( type.length > -1 )
1365 			return tokenToGtkD(params[type.length].name, wrapper.aliasses, localAliases());
1366 		//The c function returns the length.
1367 		else if ( type.length == -2 )
1368 			return "__p";
1369 		else if ( type.size > -1 )
1370 			return to!string(type.size);
1371 
1372 		if ( type.isString() )
1373 			return null;
1374 
1375 		return "getArrayLength("~ paramName ~")";
1376 	}
1377 
1378 	/**
1379 	 * Check if any of the ancestors contain the function functionName.
1380 	 */
1381 	private bool checkOverride()
1382 	{
1383 		if ( name == "get_type" )
1384 			return false;
1385 		if ( name == "to_string" && params.empty )
1386 			return true;
1387 
1388 		GirStruct ancestor = strct.getParent();
1389 
1390 		while(ancestor)
1391 		{
1392 			if ( name in ancestor.functions && name !in strct.aliases )
1393 			{
1394 				GirFunction func = ancestor.functions[name];
1395 
1396 				if ( !(func.noCode || func.isVariadic() || func.type == GirFunctionType.Callback) && paramsEqual(func) )
1397 					return true;
1398 			}
1399 
1400 			ancestor = ancestor.getParent();
1401 		}
1402 
1403 		return false;
1404 	}
1405 
1406 	/**
1407 	 * Return true if the params of func match the params of this function.
1408 	 */
1409 	private bool paramsEqual(GirFunction func)
1410 	{
1411 		if ( params.length != func.params.length )
1412 			return false;
1413 
1414 		foreach ( i, param; params )
1415 		{
1416 			if ( getType(param.type) != getType(func.params[i].type) )
1417 				return false;
1418 		}
1419 
1420 		return true;
1421 	}
1422 
1423 	private string construct(string type)
1424 	{
1425 		GirStruct dType = strct.pack.getStruct(type);
1426 		debug assert(dType, "Only call construct for valid GtkD types");
1427 		string name = dType.name;
1428 
1429 		if ( type in strct.structWrap )
1430 			name = strct.structWrap[type];
1431 
1432 		if ( dType.pack.name.among("cairo", "glib", "gthread") )
1433 			return "new "~name;
1434 		else if( dType.type == GirStructType.Interface )
1435 			return "ObjectG.getDObject!("~ name ~"IF)";
1436 		else
1437 			return "ObjectG.getDObject!("~ name ~")";
1438 	}
1439 }
1440 
1441 enum GirParamDirection : string
1442 {
1443 	Default = "",
1444 	Out = "out",
1445 	InOut = "inout",
1446 }
1447 
1448 final class GirParam
1449 {
1450 	string doc;
1451 	string name;
1452 	GirType type;
1453 	GirTransferOwnership ownership = GirTransferOwnership.None;
1454 	GirParamDirection direction = GirParamDirection.Default;
1455 
1456 	GirParam lengthFor;
1457 	GirWrapper wrapper;
1458 
1459 	this(GirWrapper wrapper)
1460 	{
1461 		this.wrapper = wrapper;
1462 	}
1463 
1464 	void parse(T)(XMLReader!T reader)
1465 	{
1466 		name = reader.front.attributes["name"];
1467 
1468 		if ( "transfer-ownership" in reader.front.attributes )
1469 			ownership = cast(GirTransferOwnership)reader.front.attributes["transfer-ownership"];
1470 		if ( "direction" in reader.front.attributes )
1471 			direction = cast(GirParamDirection)reader.front.attributes["direction"];
1472 
1473 		reader.popFront();
1474 
1475 		while( !reader.empty && !reader.endTag("parameter", "instance-parameter") )
1476 		{
1477 			if ( reader.front.type == XMLNodeType.EndTag )
1478 			{
1479 				reader.popFront();
1480 				continue;
1481 			}
1482 
1483 			switch(reader.front.value)
1484 			{
1485 				case "doc":
1486 					reader.popFront();
1487 					doc ~= reader.front.value;
1488 					reader.popFront();
1489 					break;
1490 				case "doc-deprecated":
1491 					reader.popFront();
1492 					doc ~= "\n\nDeprecated: "~ reader.front.value;
1493 					reader.popFront();
1494 					break;
1495 				case "array":
1496 				case "type":
1497 					type = new GirType(wrapper);
1498 					type.parse(reader);
1499 					break;
1500 				case "varargs":
1501 					type = new GirType(wrapper);
1502 					type.name = "...";
1503 					type.cType = "...";
1504 					break;
1505 				case "source-position":
1506 					reader.skipTag();
1507 					break;
1508 				default:
1509 					warning("Unexpected tag: ", reader.front.value, " in GirParam: ", name, reader);
1510 			}
1511 
1512 			reader.popFront();
1513 		}
1514 
1515 		if ( direction != GirParamDirection.Default && !type.cType.endsWith("*") )
1516 			direction = GirParamDirection.Default;
1517 	}
1518 }
1519 
1520 private string removePtr(string cType)
1521 {
1522 	while ( !cType.empty && cType.back == '*' )
1523 		cType.popBack();
1524 
1525 	return cType;
1526 }