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 }