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