1 /* 2 * DQt - D bindings for the Qt Toolkit 3 * 4 * GNU Lesser General Public License Usage 5 * This file may be used under the terms of the GNU Lesser 6 * General Public License version 3 as published by the Free Software 7 * Foundation and appearing in the file LICENSE.LGPL3 included in the 8 * packaging of this file. Please review the following information to 9 * ensure the GNU Lesser General Public License version 3 requirements 10 * will be met: https://www.gnu.org/licenses/lgpl-3.0.html. 11 */ 12 module qt.helpers; 13 14 import std.traits; 15 import std.conv; 16 import std.array; 17 import std.meta; 18 19 private string nextCodePart(string code) 20 { 21 import std.ascii; 22 23 size_t i; 24 char c = code[0]; 25 if(c == '"' || c == '\'') 26 { 27 char stringDelim = c; 28 i++; 29 while(i < code.length && code[i] != stringDelim) 30 { 31 if(code[i] == '\\') 32 i++; 33 i++; 34 } 35 } 36 else if(c == '/' && i + 1 < code.length && code[i + 1] == '/') 37 { 38 i++; 39 i++; 40 while(i < code.length && code[i] != '\n') 41 i++; 42 } 43 else if(c == '/' && i + 1 < code.length && code[i + 1] == '*') 44 { 45 i++; 46 i++; 47 while(i < code.length) 48 { 49 if(i + 1 < code.length && code[i] == '*' && code[i+1] == '/') 50 { 51 i++; 52 break; 53 } 54 i++; 55 } 56 } 57 else if(c == '/' && i + 1 < code.length && code[i + 1] == '+') 58 { 59 i++; 60 i++; 61 size_t nesting = 1; 62 while(i < code.length && nesting) 63 { 64 if(i + 1 < code.length && code[i] == '+' && code[i+1] == '/') 65 { 66 nesting--; 67 i++; 68 if(nesting == 0) 69 break; 70 } 71 else if(i + 1 < code.length && code[i] == '/' && code[i+1] == '+') 72 { 73 nesting++; 74 i++; 75 } 76 i++; 77 } 78 } 79 else if(isAlphaNum(c) || c == '_') 80 { 81 while(i + 1 < code.length && (isAlphaNum(code[i + 1]) || code[i + 1] == '_')) 82 i++; 83 } 84 else if(isWhite(c)) 85 { 86 while(i + 1 < code.length && isWhite(code[i + 1])) 87 i++; 88 } 89 i++; 90 return code[0..i]; 91 } 92 93 string interpolateMixin(string code) 94 { 95 string r = "\""; 96 for(size_t i=0; i<code.length;) 97 { 98 string part = nextCodePart(code[i..$]); 99 if(i < code.length + 1 && code[i] == '$' && code[i+1] == '(') 100 { 101 r ~= "\" ~ "; 102 i += 2; 103 size_t numParens = 1; 104 while(i < code.length) 105 { 106 if(i + 1 < code.length && code[i] == 'q' && code[i+1] == '{') 107 { 108 i += 2; 109 size_t start = i; 110 size_t numBraces = 1; 111 while(i < code.length) 112 { 113 string part2 = nextCodePart(code[i..$]); 114 if(code[i] == '{') 115 numBraces++; 116 else if(code[i] == '}') 117 { 118 numBraces--; 119 if(numBraces == 0) 120 { 121 r ~= interpolateMixin(code[start..i]); 122 i++; 123 break; 124 } 125 } 126 i += part2.length; 127 } 128 continue; 129 } 130 if(code[i] == ')') 131 { 132 numParens--; 133 if(numParens == 0) 134 { 135 i++; 136 break; 137 } 138 } 139 else if(code[i] == '(') 140 numParens++; 141 string part2 = nextCodePart(code[i..$]); 142 r ~= part2; 143 i += part2.length; 144 } 145 r ~= " ~ \""; 146 continue; 147 } 148 else 149 { 150 foreach(char c; part) 151 { 152 if(c == '\\') 153 r ~= "\\\\"; 154 else if(c == '"') 155 r ~= "\\\""; 156 else 157 r ~= c; 158 } 159 } 160 i += part.length; 161 } 162 r ~= "\""; 163 return r; 164 } 165 166 string stringifyMacroParameter(string s) 167 { 168 string r = "\""; 169 bool hasWS; 170 for(size_t i=0; i<s.length;) 171 { 172 string part = nextCodePart(s[i..$]); 173 if(part[0] == ' ' || part[0] == '\t' || part[0] == '\r' || part[0] == '\n' 174 || (part.length >= 2 && part[0] == '/' && (part[1] == '/' || part[1] == '*'))) 175 hasWS = true; 176 else 177 { 178 if(r.length > 1 && hasWS) 179 r ~= " "; 180 hasWS = false; 181 foreach(char c; part) 182 { 183 if(c == '\"') 184 r ~= "\\\""; 185 else if(c == '\\') 186 r ~= "\\\\"; 187 else 188 r ~= c; 189 } 190 } 191 i += part.length; 192 } 193 r ~= "\""; 194 return r; 195 } 196 197 template ExternCFunc(F)// if(is(F == function)) 198 { 199 static if(variadicFunctionStyle!F == Variadic.c) 200 alias ExternCFunc = extern(C) ReturnType!F function(ParameterTypeTuple!F, ...); 201 else 202 alias ExternCFunc = extern(C) ReturnType!F function(ParameterTypeTuple!F); 203 } 204 205 template ExternCPPFunc(F)// if(is(F == function)) 206 { 207 static if(variadicFunctionStyle!F == Variadic.c) 208 alias ExternCPPFunc = extern(C++) ReturnType!F function(ParameterTypeTuple!F, ...); 209 else 210 alias ExternCPPFunc = extern(C++) ReturnType!F function(ParameterTypeTuple!F); 211 } 212 213 T static_cast(T, S)(S x) 214 { 215 static if(isCallable!T && isCallable!S) 216 { 217 static assert(functionLinkage!T == functionLinkage!S); 218 } 219 return cast(T)x; 220 } 221 222 T reinterpret_cast(T, S)(S x) 223 { 224 return cast(T)x; 225 } 226 227 T const_cast(T, S)(S x) 228 { 229 return cast(T)x; 230 } 231 232 private template globalInitVarImpl(T) 233 { 234 immutable __gshared T globalInitVar = immutable(T).init; 235 shared static this() 236 { 237 (*cast(T*)&globalInitVar).rawConstructor; 238 } 239 } 240 241 template globalInitVar(T) 242 { 243 static if(__traits(hasMember, T, "rawConstructor")) 244 { 245 version(Windows) 246 alias globalInitVar = globalInitVarImpl!T.globalInitVar; 247 else 248 static assert(0, "globalInitVar!"~T.stringof~" needs complex construction"); 249 } 250 else static if(__traits(compiles, {T x;})) 251 immutable __gshared T globalInitVar/* = immutable(T).init*/; 252 else 253 static assert(false, typeof({T x;})); 254 } 255 256 private struct FunctionManglingCpp 257 { 258 string prefix; 259 string[] nameParts; 260 string flags; 261 string flags2; 262 string returnType; 263 string[] parameters; 264 string suffix; 265 } 266 267 version(Windows) 268 { 269 private string parseTypeManglingWin(ref string mangling, bool is64bit) 270 { 271 import std.exception, std.ascii, std.algorithm; 272 bool inPointer; 273 size_t i = 0; 274 while(true) 275 { 276 enforce(i < mangling.length, text("Unexpected mangling ", __FILE__, ":", __LINE__, ": ", mangling[0..i], " | ", mangling[i..$])); 277 if(mangling[i] == '_') 278 { 279 enforce(i + 1 < mangling.length, text("Unexpected mangling ", __FILE__, ":", __LINE__, ": ", mangling[0..i], " | ", mangling[i..$])); 280 if((mangling[i + 1] >= 'D' && mangling[i + 1] <= 'N') || mangling[i + 1] == 'Q' || mangling[i + 1] == 'S' || mangling[i + 1] == 'U' || mangling[i + 1] == 'W') 281 { 282 // basic types 283 i += 2; 284 break; 285 } 286 } 287 else if((mangling[i] >= 'C' && mangling[i] <= 'O') || mangling[i] == 'X') 288 { 289 // basic types 290 i++; 291 break; 292 } 293 else if(inPointer && mangling[i].among('6')) 294 { 295 // function type 296 i++; 297 enforce(i < mangling.length, text("Unexpected mangling ", __FILE__, ":", __LINE__, ": ", mangling[0..i], " | ", mangling[i..$])); 298 enforce(mangling[i] == 'A', text("Unexpected mangling ", __FILE__, ":", __LINE__, ": ", mangling[0..i], " | ", mangling[i..$])); 299 // return type and arguments 300 while(true) 301 { 302 enforce(i < mangling.length, text("Unexpected mangling ", __FILE__, ":", __LINE__, ": ", mangling[0..i], " | ", mangling[i..$])); 303 if(mangling[i] == 'Z') 304 { 305 i++; 306 break; 307 } 308 string mangling2 = mangling[i..$]; 309 string arg = parseTypeManglingWin(mangling2, is64bit); 310 i += arg.length; 311 } 312 break; 313 } 314 else if(mangling[i] >= '0' && mangling[i] <= '9') 315 { 316 // back references 317 i++; 318 break; 319 } 320 else if(mangling[i].among('A', 'B', 'P', 'Q', 'R', 'S')) 321 { 322 // modifiers 323 if(mangling[i].among('P', 'Q')) 324 inPointer = true; 325 i++; 326 if(is64bit) 327 { 328 if(i < mangling.length && mangling[i] == 'E') 329 i++; 330 } 331 continue; 332 } 333 else if(mangling[i].among('T', 'U', 'V')) 334 { 335 // complex types 336 i++; 337 if(mangling[i] >= '0' && mangling[i] < '9') 338 { 339 // back reference 340 i++; 341 enforce(i < mangling.length && mangling[i] == '@', text("Unexpected mangling ", __FILE__, ":", __LINE__, ": ", mangling[0..i], " | ", mangling[i..$])); 342 i++; 343 break; 344 } 345 while(true) 346 { 347 if(i + 1 < mangling.length && mangling[i] == '?' && mangling[i + 1] == '$') 348 { 349 // template 350 i += 2; 351 352 // template name 353 while(true) 354 { 355 enforce(i < mangling.length, text("Unexpected mangling ", __FILE__, ":", __LINE__, ": ", mangling[0..i], " | ", mangling[i..$])); 356 enforce(mangling[i] == '@' || mangling[i] == '_' || isAlphaNum(mangling[i]), text("Unexpected mangling ", __FILE__, ":", __LINE__, ": ", mangling[0..i], " | ", mangling[i..$])); 357 if(mangling[i] == '@') 358 { 359 i++; 360 break; 361 } 362 i++; 363 } 364 365 // template arguments 366 while(true) 367 { 368 enforce(i < mangling.length, text("Unexpected mangling ", __FILE__, ":", __LINE__, ": ", mangling[0..i], " | ", mangling[i..$])); 369 if(mangling[i] == '@') 370 { 371 break; 372 } 373 string mangling2 = mangling[i..$]; 374 string arg = parseTypeManglingWin(mangling2, is64bit); 375 i += arg.length; 376 } 377 } 378 else if(i + 1 == mangling.length && mangling[i].among('A', 'B')) 379 { 380 break; 381 } 382 else if(i + 1 < mangling.length && mangling[i] >= '0' && mangling[i] <= '9' && mangling[i + 1] == '@') 383 { 384 i += 2; 385 break; 386 } 387 else 388 { 389 enforce(mangling[i] == '@' || mangling[i] == '_' || isAlphaNum(mangling[i]), text("Unexpected mangling ", __FILE__, ":", __LINE__, ": ", mangling[0..i], " | ", mangling[i..$])); 390 if(i + 1 < mangling.length && mangling[i] == '@' && mangling[i + 1] == '@') 391 { 392 i += 2; 393 break; 394 } 395 i++; 396 } 397 } 398 break; 399 } 400 else if(mangling[i] == 'W') 401 { 402 // enum type 403 i++; 404 enforce(i < mangling.length, text("Unexpected mangling ", __FILE__, ":", __LINE__, ": ", mangling[0..i], " | ", mangling[i..$])); 405 enforce(mangling[i] >= '0' && mangling[i] <= '7', text("Unexpected mangling ", __FILE__, ":", __LINE__, ": ", mangling[0..i], " | ", mangling[i..$])); 406 i++; 407 if(mangling[i] >= '0' && mangling[i] < '9') 408 { 409 // back reference 410 i++; 411 enforce(i < mangling.length && mangling[i] == '@', text("Unexpected mangling ", __FILE__, ":", __LINE__, ": ", mangling[0..i], " | ", mangling[i..$])); 412 i++; 413 break; 414 } 415 while(true) 416 { 417 enforce(i + 1 < mangling.length, text("Unexpected mangling ", __FILE__, ":", __LINE__, ": ", mangling[0..i], " | ", mangling[i..$])); 418 enforce(mangling[i] == '@' || mangling[i] == '_' || isAlphaNum(mangling[i]), text("Unexpected mangling ", __FILE__, ":", __LINE__, ": ", mangling[0..i], " | ", mangling[i..$])); 419 if(mangling[i] >= '0' && mangling[i] <= '9' && mangling[i + 1] == '@') 420 { 421 i += 2; 422 break; 423 } 424 if(mangling[i] == '@' && mangling[i + 1] == '@') 425 { 426 i += 2; 427 break; 428 } 429 i++; 430 } 431 break; 432 } 433 enforce(false, text("Unexpected mangling ", __FILE__, ":", __LINE__, ": ", mangling[0..i], " | ", mangling[i..$])); 434 } 435 assert(i < mangling.length); 436 string r = mangling[0..i]; 437 mangling = mangling[i..$]; 438 return r; 439 } 440 /*private*/ FunctionManglingCpp parseFunctionManglingWin(string mangling, bool is64bit) 441 { 442 import std.exception, std.ascii, std.algorithm; 443 FunctionManglingCpp r; 444 enforce(mangling.length && mangling[0] == '?', text("Unexpected mangling ", __FILE__, ":", __LINE__, ": ", mangling)); 445 size_t i = 1; 446 while(true) 447 { 448 enforce(i + 1 < mangling.length, text("Unexpected mangling ", __FILE__, ":", __LINE__, ": ", mangling[0..i], " | ", mangling[i..$])); 449 if(mangling[i] == '?' && mangling[i + 1].among('0', '1')) 450 { 451 i += 2; 452 continue; 453 } 454 if(mangling[i] == '@' && mangling[i + 1] == '@') 455 { 456 i += 2; 457 break; 458 } 459 enforce(mangling[i] == '@' || mangling[i] == '_' || isAlphaNum(mangling[i]), text("Unexpected mangling ", __FILE__, ":", __LINE__, ": ", mangling[0..i], " | ", mangling[i..$])); 460 i++; 461 } 462 r.nameParts = [mangling[0..i]]; 463 mangling = mangling[i..$]; 464 465 enforce(mangling.length >= 2, text("Unexpected mangling ", __FILE__, ":", __LINE__, ": ", mangling)); 466 if(mangling[0] >= 'A' && mangling[0] <= 'Z') 467 { 468 // function 469 // private / protected / public / none 470 uint accessLevel = (mangling[0] - 'A') / 8; 471 // none / static / virtual / thunk 472 uint functionType = ((mangling[0] - 'A') % 8) / 2; 473 i = 1; 474 if(is64bit && mangling[i] == 'E') 475 { 476 enforce(i + 1 < mangling.length, text("Unexpected mangling ", __FILE__, ":", __LINE__, ": ", mangling)); 477 enforce(mangling[i] == 'E', text("Unexpected mangling ", __FILE__, ":", __LINE__, ": ", mangling)); 478 i++; 479 } 480 if(functionType != 1) 481 { 482 enforce(i + 1 < mangling.length, text("Unexpected mangling ", __FILE__, ":", __LINE__, ": ", mangling)); 483 enforce(mangling[i].among('A', 'B'), text("Unexpected mangling ", __FILE__, ":", __LINE__, ": ", mangling)); 484 i++; 485 } 486 enforce(mangling[i].among('A', 'E'), text("Unexpected mangling ", __FILE__, ":", __LINE__, ": ", mangling)); 487 i++; 488 r.flags = mangling[0..i]; 489 mangling = mangling[i..$]; 490 if(mangling.startsWith("?A") || mangling.startsWith("?B")) 491 { 492 r.flags2 = mangling[0..2]; 493 mangling = mangling[2..$]; 494 } 495 496 if(r.nameParts[$-1].startsWith("??1")) // destructor 497 { 498 enforce(mangling == "@XZ"); 499 r.suffix = mangling; 500 } 501 else 502 { 503 r.returnType = parseTypeManglingWin(mangling, is64bit); 504 while(true) 505 { 506 enforce(mangling.length, text("Unexpected mangling ", __FILE__, ":", __LINE__, ": ", mangling)); 507 if(mangling[0].among('Z', 'X', '@')) 508 break; 509 r.parameters ~= parseTypeManglingWin(mangling, is64bit); 510 } 511 foreach(char c; mangling) 512 enforce(c.among('Z', 'X', '@'), text("Unexpected mangling ", __FILE__, ":", __LINE__, ": ", mangling)); 513 r.suffix = mangling; 514 } 515 } 516 else if(mangling[0] >= '0' && mangling[0] <= '4') 517 { 518 // variable 519 r.flags = mangling[0..1]; 520 mangling = mangling[1..$]; 521 r.returnType = parseTypeManglingWin(mangling, is64bit); 522 enforce(mangling.among("A", "B", "EA", "EB"), text("Unexpected mangling ", __FILE__, ":", __LINE__, ": ", mangling)); 523 r.suffix = mangling; 524 } 525 else 526 enforce(false, text("Unexpected mangling ", __FILE__, ":", __LINE__, ": ", mangling)); 527 528 return r; 529 } 530 } 531 else 532 { 533 /*private*/ FunctionManglingCpp parseFunctionManglingItanium(string mangling, bool is64bit) 534 { 535 import std.exception, std.ascii, std.algorithm, std.conv; 536 FunctionManglingCpp r; 537 enforce(mangling.startsWith("_ZN"), text("Unexpected mangling ", __FILE__, ":", __LINE__, ": ", mangling)); 538 r.prefix = mangling[0..3]; 539 mangling = mangling[3..$]; 540 541 while(mangling.length) 542 { 543 if(mangling[0] >= '0' && mangling[0] <= '9') 544 { 545 size_t lengthBytes = 1; 546 while(lengthBytes < mangling.length && mangling[lengthBytes] >= '0' && mangling[lengthBytes] <= '9') 547 lengthBytes++; 548 size_t length = to!size_t(mangling[0..lengthBytes]); 549 enforce(mangling.length >= lengthBytes + length, text("Unexpected mangling ", __FILE__, ":", __LINE__, ": ", mangling)); 550 r.nameParts ~= mangling[0..lengthBytes + length]; 551 mangling = mangling[lengthBytes + length..$]; 552 } 553 else if(mangling.length >= 2 && mangling[0].among('C', 'D') && mangling[1].among('0', '1')) 554 { 555 r.nameParts ~= mangling[0..2]; 556 mangling = mangling[2..$]; 557 } 558 else 559 break; 560 } 561 562 r.suffix = mangling; 563 564 return r; 565 } 566 } 567 568 alias defaultConstructorMangling = function string(string name) 569 { 570 version(Windows) 571 static if(size_t.sizeof == 8) 572 return "??0" ~ name ~ "@@QEAA@XZ"; 573 else 574 return "??0" ~ name ~ "@@QAE@XZ"; 575 else 576 return text("_ZN", name.length, name, "C1Ev"); 577 }; 578 579 private string replaceStart(string str, string from, string to) 580 { 581 import std.algorithm; 582 if(str.startsWith(from)) 583 str = to ~ str[from.length..$]; 584 return str; 585 } 586 587 // Changes the mangling only on Windows. 588 // On Win32 it also converts it from 64 bit. 589 alias mangleWindows = function string(string mangling, string code) 590 { 591 version(Windows) 592 { 593 import std.algorithm; 594 static if(size_t.sizeof == 4) 595 { 596 auto parsed = parseFunctionManglingWin(mangling, true); 597 static if(size_t.sizeof == 4) 598 { 599 if(parsed.flags[0] >= 'A' && parsed.flags[0] <= 'Z' && parsed.flags[1] == 'E') 600 { 601 parsed.flags = parsed.flags[0..1] ~ parsed.flags[2..$]; 602 if(parsed.flags[$-1] == 'A') 603 parsed.flags = parsed.flags[0..$-1] ~ 'E'; 604 } 605 parsed.returnType = parsed.returnType.replaceStart("PE", "P"); 606 parsed.returnType = parsed.returnType.replaceStart("QE", "Q"); 607 parsed.returnType = parsed.returnType.replaceStart("RE", "R"); 608 parsed.returnType = parsed.returnType.replaceStart("SE", "S"); 609 parsed.returnType = parsed.returnType.replaceStart("AE", "A"); 610 parsed.returnType = parsed.returnType.replaceStart("BE", "B"); 611 } 612 foreach(ref param; parsed.parameters) 613 { 614 static if(size_t.sizeof == 4) 615 { 616 param = param.replaceStart("PE", "P"); 617 param = param.replaceStart("QE", "Q"); 618 param = param.replaceStart("RE", "R"); 619 param = param.replaceStart("SE", "S"); 620 param = param.replaceStart("AE", "A"); 621 param = param.replaceStart("BE", "B"); 622 } 623 } 624 static if(size_t.sizeof == 4) 625 { 626 parsed.suffix = parsed.suffix.replaceStart("EA", "A"); 627 } 628 mangling = recreateCppMangling(parsed); 629 } 630 return "pragma(mangle, \"" ~ mangling ~ "\") " ~ code; 631 } 632 else 633 return code; 634 }; 635 636 // Changes the mangling only on platforms with Itanium C++ ABI. 637 alias mangleItanium = function string(string mangling, string code) 638 { 639 version(Windows) 640 return code; 641 else 642 { 643 return "pragma(mangle, \"" ~ mangling ~ "\") " ~ code; 644 } 645 }; 646 647 alias mangleOpLess = function string(string name) 648 { 649 version(Windows) 650 static if(size_t.sizeof == 8) 651 return "??M" ~ name ~ "@@UEBA_NAEBV0@@Z"; 652 else 653 return "??M" ~ name ~ "@@UBE_NABV0@@Z"; 654 else 655 return text("_ZNK", name.length, name, "ltERKS_"); 656 }; 657 658 659 package FunctionManglingCpp splitCppMangling(bool isClass, string attributes, string attributes2, string name, string dummyFunctionName, size_t numParameters, string mangling) 660 { 661 import std.algorithm, std.exception, std.conv; 662 663 version(Windows) 664 { 665 auto parsed = parseFunctionManglingWin(mangling, (void*).sizeof == 8); 666 if(name == "~this") 667 parsed.nameParts[0] = parsed.nameParts[0].replace(dummyFunctionName, "this"); 668 else 669 parsed.nameParts[0] = parsed.nameParts[0].replace(dummyFunctionName, name); 670 assert(parsed.parameters.length == numParameters); 671 if(name == "this") 672 { 673 parsed.nameParts[$-1] = parsed.nameParts[$-1].replaceStart("?this@", "??0"); 674 assert(parsed.returnType == "X"); 675 parsed.returnType = "@"; 676 foreach(ref param; parsed.parameters) 677 { 678 if(param.length >= 2 && param[$-1] == '@' && param[$-2] >= '1' && param[$-2] <= '9') 679 { 680 // References have to be decremented, because the dummy function has a return type, but not the real constructor. 681 param = param[0..$-2] ~ cast(char)(param[$-2] - 1) ~ param[$-1]; 682 } 683 } 684 } 685 if(name == "~this") 686 { 687 parsed.nameParts[$-1] = parsed.nameParts[$-1].replaceStart("?this@", "??1"); 688 assert(parsed.returnType == "X"); 689 parsed.returnType = "@"; 690 } 691 if(!attributes.canFind("static")) 692 { 693 // private / protected / public / none 694 uint accessLevel = (parsed.flags[0] - 'A') / 8; 695 // none / static / virtual / thunk 696 uint functionType = ((parsed.flags[0] - 'A') % 8) / 2; 697 698 if(attributes.canFind("static")) 699 functionType = 1; 700 else if(attributes.canFind("final") || !isClass || name == "this") 701 functionType = 0; 702 else 703 functionType = 2; 704 705 parsed.flags = [cast(char)('A' + accessLevel * 8 + functionType * 2)]; 706 if(attributes2.canFind("const")) 707 { 708 static if(size_t.sizeof == 8) 709 parsed.flags ~= "EBA"; 710 else 711 parsed.flags ~= "BE"; 712 } 713 else 714 { 715 static if(size_t.sizeof == 8) 716 parsed.flags ~= "EAA"; 717 else 718 parsed.flags ~= "AE"; 719 } 720 } 721 } 722 else 723 { 724 FunctionManglingCpp parsed = parseFunctionManglingItanium(mangling, (void*).sizeof == 8); 725 enforce(parsed.nameParts[$-1].endsWith(dummyFunctionName)); 726 if(name == "this") 727 parsed.nameParts[$-1] = "C1"; 728 else if(name == "~this") 729 parsed.nameParts[$-1] = "D1"; 730 else 731 parsed.nameParts[$-1] = text(name.length, name); 732 733 if(!attributes.canFind("static")) 734 { 735 if(attributes2.canFind("const")) 736 parsed.prefix ~= "K"; 737 } 738 } 739 return parsed; 740 } 741 742 // Workaround for https://issues.dlang.org/show_bug.cgi?id=22550 743 package FunctionManglingCpp mangleClassesTailConst(FunctionManglingCpp parsed) 744 { 745 version(Windows) 746 { 747 string makeTailConst(string mangling) 748 { 749 static if(size_t.sizeof == 8) 750 { 751 mangling = mangling.replaceStart("QEB", "PEB"); 752 } 753 else 754 { 755 mangling = mangling.replaceStart("QB", "PB"); 756 } 757 return mangling; 758 } 759 parsed.returnType = makeTailConst(parsed.returnType); 760 foreach(ref param; parsed.parameters) 761 { 762 param = makeTailConst(param); 763 } 764 } 765 return parsed; 766 } 767 768 package FunctionManglingCpp mangleChangeAccess(FunctionManglingCpp parsed, string access) 769 { 770 version(Windows) 771 { 772 // private / protected / public / none 773 uint accessLevel = (parsed.flags[0] - 'A') / 8; 774 // none / static / virtual / thunk 775 uint functionType = ((parsed.flags[0] - 'A') % 8) / 2; 776 777 if(access == "private") 778 accessLevel = 0; 779 else if(access == "protected") 780 accessLevel = 1; 781 else if(access == "public") 782 accessLevel = 2; 783 else 784 assert(false); 785 786 parsed.flags = cast(char)('A' + accessLevel * 8 + functionType * 2) ~ parsed.flags[1..$]; 787 } 788 return parsed; 789 } 790 791 792 package FunctionManglingCpp mangleChangeFunctionType(FunctionManglingCpp parsed, string functionTypeStr) 793 { 794 version(Windows) 795 { 796 // private / protected / public / none 797 uint accessLevel = (parsed.flags[0] - 'A') / 8; 798 // none / static / virtual / thunk 799 uint functionType = ((parsed.flags[0] - 'A') % 8) / 2; 800 801 if(functionTypeStr == "none") 802 functionType = 0; 803 else if(functionTypeStr == "static") 804 functionType = 1; 805 else if(functionTypeStr == "virtual") 806 functionType = 2; 807 else 808 assert(false); 809 810 parsed.flags = cast(char)('A' + accessLevel * 8 + functionType * 2) ~ parsed.flags[1..$]; 811 } 812 return parsed; 813 } 814 815 // Workaround for https://issues.dlang.org/show_bug.cgi?id=22636 816 package FunctionManglingCpp mangleConstructorBaseObject(FunctionManglingCpp parsed) 817 { 818 version(Windows) 819 {} 820 else 821 { 822 import std.exception; 823 enforce(parsed.nameParts[$-1] == "C1"); 824 parsed.nameParts[$-1] = "C2"; 825 } 826 return parsed; 827 } 828 829 string recreateCppMangling(FunctionManglingCpp parsed) 830 { 831 string mangling; 832 mangling = parsed.prefix; 833 foreach(part; parsed.nameParts) 834 mangling ~= part; 835 mangling ~= parsed.flags; 836 mangling ~= parsed.flags2; 837 mangling ~= parsed.returnType; 838 foreach(param; parsed.parameters) 839 { 840 mangling ~= param; 841 } 842 mangling ~= parsed.suffix; 843 return mangling; 844 } 845 846 /* 847 Changes the mangling of function declarations. 848 It only uses a very simple parser and will only work for some declarations. 849 The mangling could be wrong for some functions. 850 */ 851 package string changeCppMangling(bool debugHere = false)(string changeFuncs, string declaration, size_t line = __LINE__) 852 { 853 import std.ascii, std.algorithm; 854 855 string code; 856 857 struct StartEnd 858 { 859 size_t start, end; 860 } 861 StartEnd[] parts; 862 size_t parenCount; 863 bool afterCombiner; 864 bool afterSemicolon; 865 for(size_t i = 0; i < declaration.length;) 866 { 867 string part = nextCodePart(declaration[i..$]); 868 bool mergeToLastPart; 869 if(isWhite(part[0]) || part.startsWith("//") || part.startsWith("/*") || part.startsWith("/+")) 870 { 871 i += part.length; 872 continue; 873 } 874 else 875 { 876 assert(!afterSemicolon); 877 if(afterCombiner) 878 { 879 mergeToLastPart = true; 880 afterCombiner = false; 881 } 882 883 if(part == "(") 884 { 885 if(parenCount) 886 mergeToLastPart = true; 887 parenCount++; 888 } 889 else if(part == ")") 890 { 891 assert(parenCount); 892 mergeToLastPart = true; 893 parenCount--; 894 } 895 else if(parenCount) 896 { 897 mergeToLastPart = true; 898 } 899 else if(part == "." || part == "!") 900 { 901 mergeToLastPart = true; 902 afterCombiner = true; 903 } 904 else if(part == "@") 905 { 906 afterCombiner = true; 907 } 908 else if(part == "~") 909 { 910 afterCombiner = true; 911 } 912 else if(part == ";") 913 { 914 afterSemicolon = true; 915 } 916 else 917 { 918 } 919 } 920 921 if(mergeToLastPart) 922 parts[$-1].end = i + part.length; 923 else 924 parts ~= StartEnd(i, i + part.length); 925 926 //code ~= "pragma(msg, q{" ~ part ~ "});"; 927 i += part.length; 928 } 929 930 /*foreach(i, part; parts) 931 code ~= "pragma(msg, q{" ~ text(i, ": ") ~ declaration[part.start..part.end] ~ "});";*/ 932 933 size_t attributesEnd; 934 string usedAttributes; 935 string attributesNoComments; 936 while(parts.length) 937 { 938 string part = declaration[parts[0].start..parts[0].end]; 939 if(part.among("static", "override", "final", "extern", "export", "__gshared", 940 "private", "protected", "public", "package") 941 || part[0] == '@') 942 { 943 attributesEnd = parts[0].end; 944 attributesNoComments ~= part ~ " "; 945 } 946 else if(part.among("const", "immutable", "shared")) 947 { 948 usedAttributes ~= part ~ " "; 949 attributesEnd = parts[0].end; 950 attributesNoComments ~= part ~ " "; 951 } 952 else 953 break; 954 parts = parts[1..$]; 955 } 956 string attributes = declaration[0..attributesEnd]; 957 assert(parts.length >= 3, text(parts.length)); 958 string returnType; 959 if(declaration[parts[0].start..parts[0].end] != "this" && declaration[parts[0].start..parts[0].end] != "~this") 960 { 961 returnType = declaration[attributesEnd..parts[0].end]; 962 parts = parts[1..$]; 963 } 964 assert(parts.length >= 3, text(parts.length)); 965 string name = declaration[parts[0].start..parts[0].end]; 966 parts = parts[1..$]; 967 assert(isAlphaNum(name[0]) || name[0] == '_' || name == "~this"); 968 string params = declaration[parts[0].start..parts[0].end]; 969 parts = parts[1..$]; 970 assert(params.startsWith("(")); 971 string attributesUsedAfter; 972 while(parts.length >= 2) 973 { 974 string part = declaration[parts[0].start..parts[0].end]; 975 assert(part.among("const", "immutable", "shared")); 976 attributesUsedAfter ~= part ~ " "; 977 parts = parts[1..$]; 978 } 979 assert(parts.length == 1); 980 assert(declaration[parts[0].start..parts[0].end].startsWith(";")); 981 982 /*code ~= "pragma(msg, q{" ~ attributes ~ "});"; 983 code ~= "pragma(msg, q{" ~ returnType ~ "});"; 984 code ~= "pragma(msg, q{" ~ name ~ "});"; 985 code ~= "pragma(msg, q{" ~ params ~ "});";*/ 986 987 string dummyFunctionName; 988 if(name == "~this") 989 dummyFunctionName = text("dummyFunctionForChangingMangling", line, "_destructor"); 990 else 991 dummyFunctionName = text("dummyFunctionForChangingMangling", line, "_", name); 992 993 code ~= "static"; 994 code ~= " " ~ usedAttributes; 995 code ~= " " ~ returnType; 996 if(name == "this" || name == "~this") 997 code ~= "void"; 998 code ~= " " ~ dummyFunctionName; 999 code ~= params; 1000 //code ~= " " ~ attributesUsedAfter; 1001 code ~= ";\n"; 1002 if(debugHere) 1003 { 1004 code ~= "pragma(msg, " ~ dummyFunctionName ~ ".mangleof);\n"; 1005 1006 version(Windows) 1007 code ~= "pragma(msg, parseFunctionManglingWin(" ~ dummyFunctionName ~ ".mangleof, (void*).sizeof == 8));\n"; 1008 else 1009 code ~= "pragma(msg, parseFunctionManglingItanium(" ~ dummyFunctionName ~ ".mangleof, (void*).sizeof == 8));\n"; 1010 1011 code ~= "pragma(msg, splitCppMangling(is(typeof(this) == class), "; 1012 code ~= "q{" ~ attributesNoComments ~ "}, "; 1013 code ~= "q{" ~ attributesUsedAfter ~ "}, "; 1014 code ~= "q{" ~ name ~ "}, "; 1015 code ~= "q{" ~ dummyFunctionName ~ "}, "; 1016 code ~= "qt.helpers.FunctionParameters!" ~ dummyFunctionName ~ ".length, "; 1017 code ~= dummyFunctionName ~ ".mangleof"; 1018 code ~= ")"; 1019 code ~= ");\n"; 1020 1021 code ~= "pragma(msg, splitCppMangling(is(typeof(this) == class), "; 1022 code ~= "q{" ~ attributesNoComments ~ "}, "; 1023 code ~= "q{" ~ attributesUsedAfter ~ "}, "; 1024 code ~= "q{" ~ name ~ "}, "; 1025 code ~= "q{" ~ dummyFunctionName ~ "}, "; 1026 code ~= "qt.helpers.FunctionParameters!" ~ dummyFunctionName ~ ".length, "; 1027 code ~= dummyFunctionName ~ ".mangleof"; 1028 code ~= ")"; 1029 code ~= "." ~ changeFuncs; 1030 code ~= ");\n"; 1031 1032 code ~= "pragma(msg, splitCppMangling(is(typeof(this) == class), "; 1033 code ~= "q{" ~ attributesNoComments ~ "}, "; 1034 code ~= "q{" ~ attributesUsedAfter ~ "}, "; 1035 code ~= "q{" ~ name ~ "}, "; 1036 code ~= "q{" ~ dummyFunctionName ~ "}, "; 1037 code ~= "qt.helpers.FunctionParameters!" ~ dummyFunctionName ~ ".length, "; 1038 code ~= dummyFunctionName ~ ".mangleof"; 1039 code ~= ")"; 1040 code ~= "." ~ changeFuncs; 1041 code ~= ".recreateCppMangling"; 1042 code ~= ");\n"; 1043 } 1044 code ~= "pragma(mangle, splitCppMangling(is(typeof(this) == class), "; 1045 code ~= "q{" ~ attributesNoComments ~ "}, "; 1046 code ~= "q{" ~ attributesUsedAfter ~ "}, "; 1047 code ~= "q{" ~ name ~ "}, "; 1048 code ~= "q{" ~ dummyFunctionName ~ "}, "; 1049 code ~= "qt.helpers.FunctionParameters!" ~ dummyFunctionName ~ ".length, "; 1050 code ~= dummyFunctionName ~ ".mangleof"; 1051 code ~= ")"; 1052 code ~= "." ~ changeFuncs; 1053 code ~= ".recreateCppMangling"; 1054 code ~= ")\n"; 1055 1056 code ~= declaration; 1057 return code; 1058 } 1059 package string changeCppManglingNoop(bool debugHere = false)(string changeFuncs, string declaration, size_t line = __LINE__) 1060 { 1061 return declaration; 1062 } 1063 version(Windows) 1064 { 1065 alias changeWindowsMangling = changeCppMangling; 1066 alias changeItaniumMangling = changeCppManglingNoop; 1067 } 1068 else 1069 { 1070 alias changeWindowsMangling = changeCppManglingNoop; 1071 alias changeItaniumMangling = changeCppMangling; 1072 } 1073 1074 // Workaround for https://issues.dlang.org/show_bug.cgi?id=19660, which can be removed later. 1075 version(Windows) 1076 enum exportOnWindows = " export "; 1077 else 1078 enum exportOnWindows = ""; 1079 1080 /* Same as imported from druntime object.d. 1081 * Using a custom implementation also works with older druntime 1082 * and works around https://issues.dlang.org/show_bug.cgi?id=22651 1083 */ 1084 template dqtimported(string moduleName) 1085 { 1086 mixin("import dqtimported = " ~ moduleName ~ ";"); 1087 } 1088 1089 package template FunctionParameters(alias F) 1090 { 1091 static if(is(typeof(F) P == __parameters)) 1092 alias FunctionParameters = P; 1093 } 1094 1095 template isParamConstStructRef(alias F, size_t i) 1096 { 1097 enum isParamConstStructRef = is(FunctionParameters!F[i] == struct) && is(FunctionParameters!F[i] == const) && [__traits(getParameterStorageClasses, F, i)] == ["ref"]; 1098 } 1099 1100 template anyParamConstStructRef(alias F, size_t i = 0) 1101 { 1102 static if(i >= FunctionParameters!F.length) 1103 enum anyParamConstStructRef = false; 1104 else static if(isParamConstStructRef!(F, i)) 1105 enum anyParamConstStructRef = true; 1106 else 1107 enum anyParamConstStructRef = anyParamConstStructRef!(F, i + 1); 1108 } 1109 1110 template dummyFunctionWithSameArgs(alias F) 1111 { 1112 static if(is(typeof(F) Params == __parameters)) 1113 void dummyFunctionWithSameArgs(Params params); 1114 } 1115 1116 template callableWithNParameters(alias F, size_t n) 1117 { 1118 enum callableWithNParameters = __traits(compiles, (){ 1119 FunctionParameters!F[0..n] params = void; 1120 dummyFunctionWithSameArgs!F(params); 1121 }); 1122 } 1123 1124 enum isWrapperCallable(alias F, Params...) = () 1125 { 1126 static if(Params.length > FunctionParameters!F.length) 1127 return false; 1128 else static if(!callableWithNParameters!(F, Params.length)) 1129 return false; 1130 else 1131 { 1132 bool r = true; 1133 // The parameter types have to be checked with if to work around https://issues.dlang.org/show_bug.cgi?id=13140 1134 static foreach(i; 0..Params.length) 1135 { 1136 if(!is(Params[i] : FunctionParameters!F[i])) 1137 r = false; 1138 } 1139 // Workaround for https://issues.dlang.org/show_bug.cgi?id=12672 1140 bool anyParamNeedsTemp; 1141 static foreach(i; 0..Params.length) 1142 { 1143 static if(isParamConstStructRef!(F, i)) 1144 { 1145 static if(!__traits(isRef, Params[i])) 1146 anyParamNeedsTemp = true; 1147 } 1148 // Ref parameters, which are not const structs should not use a temporary. 1149 else if([__traits(getParameterStorageClasses, F, i)] == ["ref"] && !__traits(isRef, Params[i])) 1150 r = false; 1151 } 1152 if(!anyParamNeedsTemp) 1153 r = false; 1154 return r; 1155 } 1156 }(); 1157 1158 // Creates wrapper functions for every function in the current class / struct, which are callable with rvalues. 1159 enum CREATE_CONVENIENCE_WRAPPERS = q{ 1160 static foreach(member; __traits(derivedMembers, typeof(this))) 1161 { 1162 static if((!(member.length >= 2 && member[0..2] == "__") || member == "__ctor") 1163 && !(member.length >= 32 && member[0..32] == "dummyFunctionForChangingMangling") 1164 && member != "rawConstructor") 1165 static foreach(F; __traits(getOverloads, typeof(this), member)) 1166 { 1167 static if(__traits(getVisibility, F) == "public" && anyParamConstStructRef!F) 1168 { 1169 mixin((){ 1170 string code; 1171 code ~= "public extern(D) pragma(inline, true) "; 1172 if(member == "__ctor") 1173 code ~= "this"; 1174 else 1175 { 1176 static if(__traits(isStaticFunction, F)) 1177 code ~= "static "; 1178 else static if(is(typeof(this) == class)) 1179 code ~= "final "; 1180 code ~= "auto " ~ member; 1181 } 1182 code ~= "(Params...)(auto ref Params params)"; 1183 code ~= "if(isWrapperCallable!(F, Params))"; 1184 code ~= "{"; 1185 if(member == "__ctor") 1186 code ~= "this(params);"; 1187 else 1188 code ~= "return " ~ member ~ "(params);"; 1189 code ~= "}"; 1190 return code; 1191 }()); 1192 } 1193 } 1194 } 1195 }; 1196 1197 private alias parentOf(alias sym) = __traits(parent, sym); 1198 private alias parentOf(alias sym : T!Args, alias T, Args...) = __traits(parent, T); 1199 1200 // Like std.traits.packageName, but allows modules without package. 1201 package template packageName(alias T) 1202 { 1203 import std.algorithm.searching : startsWith; 1204 1205 enum bool isNotFunc = !isSomeFunction!(T); 1206 1207 static if (__traits(compiles, parentOf!T)) 1208 enum parent = packageName!(parentOf!T); 1209 else 1210 enum string parent = null; 1211 1212 static if (isNotFunc && T.stringof.startsWith("package ")) 1213 enum packageName = (parent.length ? parent ~ '.' : "") ~ T.stringof[8 .. $]; 1214 else static if (parent) 1215 enum packageName = parent; 1216 else 1217 enum packageName = ""; 1218 } 1219 1220 package template IsInQtPackage(alias S) 1221 { 1222 enum IsInQtPackage = packageName!(S).length > 3 && packageName!(S)[0..3] == "qt."; 1223 }