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 }