]> git.ipfire.org Git - thirdparty/gcc.git/blob - libphobos/libdruntime/core/demangle.d
272ee1e1ba4b9fdb750e69b18302ee87ef493957
[thirdparty/gcc.git] / libphobos / libdruntime / core / demangle.d
1 /**
2 * The demangle module converts mangled D symbols to a representation similar
3 * to what would have existed in code.
4 *
5 * Copyright: Copyright Sean Kelly 2010 - 2014.
6 * License: Distributed under the
7 * $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost Software License 1.0).
8 * (See accompanying file LICENSE)
9 * Authors: Sean Kelly
10 * Source: $(DRUNTIMESRC core/_demangle.d)
11 */
12
13 module core.demangle;
14
15 version (OSX)
16 version = Darwin;
17 else version (iOS)
18 version = Darwin;
19 else version (TVOS)
20 version = Darwin;
21 else version (WatchOS)
22 version = Darwin;
23
24 debug(trace) import core.stdc.stdio : printf;
25 debug(info) import core.stdc.stdio : printf;
26
27 extern (C) alias CXX_DEMANGLER = char* function (const char* mangled_name,
28 char* output_buffer,
29 size_t* length,
30 int* status) nothrow pure @trusted;
31
32 private struct NoHooks
33 {
34 // supported hooks
35 // static bool parseLName(ref Demangle);
36 // static char[] parseType(ref Demangle, char[])
37 }
38
39 private struct Demangle(Hooks = NoHooks)
40 {
41 // NOTE: This implementation currently only works with mangled function
42 // names as they exist in an object file. Type names mangled via
43 // the .mangleof property are effectively incomplete as far as the
44 // ABI is concerned and so are not considered to be mangled symbol
45 // names.
46
47 // NOTE: This implementation builds the demangled buffer in place by
48 // writing data as it is decoded and then rearranging it later as
49 // needed. In practice this results in very little data movement,
50 // and the performance cost is more than offset by the gain from
51 // not allocating dynamic memory to assemble the name piecemeal.
52 //
53 // If the destination buffer is too small, parsing will restart
54 // with a larger buffer. Since this generally means only one
55 // allocation during the course of a parsing run, this is still
56 // faster than assembling the result piecemeal.
57
58 pure @safe:
59 enum AddType { no, yes }
60
61
62 this( return scope const(char)[] buf_, return scope char[] dst_ = null )
63 {
64 this( buf_, AddType.yes, dst_ );
65 }
66
67
68 this( return scope const(char)[] buf_, AddType addType_, return scope char[] dst_ = null )
69 {
70 buf = buf_;
71 addType = addType_;
72 dst.dst = dst_;
73 }
74
75 const(char)[] buf = null;
76 Buffer dst;
77 size_t pos = 0;
78 size_t brp = 0; // current back reference pos
79 AddType addType = AddType.yes;
80 bool mute = false;
81 Hooks hooks;
82
83 //////////////////////////////////////////////////////////////////////////
84 // Type Testing and Conversion
85 //////////////////////////////////////////////////////////////////////////
86
87
88 static bool isAlpha( char val )
89 {
90 return ('a' <= val && 'z' >= val) ||
91 ('A' <= val && 'Z' >= val) ||
92 (0x80 & val); // treat all unicode as alphabetic
93 }
94
95
96 static bool isDigit( char val ) nothrow
97 {
98 return '0' <= val && '9' >= val;
99 }
100
101
102 static bool isHexDigit( char val )
103 {
104 return ('0' <= val && '9' >= val) ||
105 ('a' <= val && 'f' >= val) ||
106 ('A' <= val && 'F' >= val);
107 }
108
109
110 static ubyte ascii2hex( out bool errStatus, char val ) nothrow
111 {
112 if (val >= 'a' && val <= 'f')
113 return cast(ubyte)(val - 'a' + 10);
114 if (val >= 'A' && val <= 'F')
115 return cast(ubyte)(val - 'A' + 10);
116 if (val >= '0' && val <= '9')
117 return cast(ubyte)(val - '0');
118
119 errStatus = true;
120 return 0;
121 }
122
123 BufSlice shift(scope const BufSlice val) return scope
124 {
125 if (mute)
126 return dst.bslice_empty;
127 return dst.shift(val);
128 }
129
130 void putComma(size_t n)
131 {
132 version (DigitalMars) pragma(inline, false);
133 if (n)
134 put(", ");
135 }
136
137 void put(char c) return scope
138 {
139 char[1] val = c;
140 put(val[]);
141 }
142
143 void put(scope BufSlice val) return scope
144 {
145 put(val.getSlice);
146 }
147
148 void put(scope const(char)[] val) return scope nothrow
149 {
150 if (mute)
151 return;
152 dst.append(val);
153 }
154
155
156 void putAsHex( size_t val, int width = 0 )
157 {
158 import core.internal.string;
159
160 UnsignedStringBuf buf = void;
161
162 auto s = unsignedToTempString!16(val, buf);
163 int slen = cast(int)s.length;
164 if (slen < width)
165 {
166 foreach (i; slen .. width)
167 put('0');
168 }
169 put(s);
170 }
171
172
173 void pad( const(char)[] val )
174 {
175 if ( val.length )
176 {
177 put(" ");
178 put( val );
179 }
180 }
181
182
183 void silent( out bool err_status, void delegate(out bool err_status) pure @safe nothrow dg ) nothrow
184 {
185 debug(trace) printf( "silent+\n" );
186 debug(trace) scope(success) printf( "silent-\n" );
187 auto n = dst.length;
188 dg(err_status);
189 if(!err_status)
190 dst.len = n;
191 }
192
193
194 //////////////////////////////////////////////////////////////////////////
195 // Parsing Utility
196 //////////////////////////////////////////////////////////////////////////
197
198 @property bool empty()
199 {
200 return pos >= buf.length;
201 }
202
203 @property char front()
204 {
205 if ( pos < buf.length )
206 return buf[pos];
207 return char.init;
208 }
209
210 char peek( size_t n )
211 {
212 if ( pos + n < buf.length )
213 return buf[pos + n];
214 return char.init;
215 }
216
217
218 bool test( char val ) nothrow
219 {
220 return val == front;
221 }
222
223 void popFront() nothrow
224 {
225 if ( pos++ >= buf.length )
226 assert(false);
227 }
228
229
230 void popFront(int i) nothrow
231 {
232 while (i--)
233 popFront();
234 }
235
236
237 bool match( char val ) nothrow
238 {
239 if (!test(val))
240 return false;
241 else
242 {
243 popFront();
244 return true;
245 }
246 }
247
248 bool match( const(char)[] val ) nothrow
249 {
250 foreach (char e; val )
251 if (!match( e ))
252 return false;
253
254 return true;
255 }
256
257
258 void eat( char val )
259 {
260 if ( val == front )
261 popFront();
262 }
263
264 bool isSymbolNameFront(out bool errStatus) nothrow
265 {
266 char val = front;
267 if ( isDigit( val ) || val == '_' )
268 return true;
269 if ( val != 'Q' )
270 return false;
271
272 // check the back reference encoding after 'Q'
273 val = peekBackref();
274 if (val == 0)
275 {
276 // invalid back reference
277 errStatus = true;
278 return false;
279 }
280
281 return isDigit( val ); // identifier ref
282 }
283
284 // return the first character at the back reference
285 char peekBackref() nothrow
286 {
287 assert( front == 'Q' );
288 auto n = decodeBackref!1();
289 if (!n || n > pos)
290 return 0; // invalid back reference
291
292 return buf[pos - n];
293 }
294
295 size_t decodeBackref(size_t peekAt = 0)() nothrow
296 {
297 enum base = 26;
298 size_t n = 0;
299 for (size_t p; ; p++)
300 {
301 char t;
302 static if (peekAt > 0)
303 {
304 t = peek(peekAt + p);
305 }
306 else
307 {
308 t = front;
309 popFront();
310 }
311 if (t < 'A' || t > 'Z')
312 {
313 if (t < 'a' || t > 'z')
314 return 0; // invalid back reference
315
316 n = base * n + t - 'a';
317 return n;
318 }
319 n = base * n + t - 'A';
320 }
321 }
322
323 //////////////////////////////////////////////////////////////////////////
324 // Parsing Implementation
325 //////////////////////////////////////////////////////////////////////////
326
327
328 /*
329 Number:
330 Digit
331 Digit Number
332 */
333 const(char)[] sliceNumber() return scope
334 {
335 debug(trace) printf( "sliceNumber+\n" );
336 debug(trace) scope(success) printf( "sliceNumber-\n" );
337
338 auto beg = pos;
339
340 while ( true )
341 {
342 auto t = front;
343 if (t >= '0' && t <= '9')
344 popFront();
345 else
346 return buf[beg .. pos];
347 }
348 }
349
350
351 size_t decodeNumber(out bool errStatus) scope nothrow
352 {
353 debug(trace) printf( "decodeNumber+\n" );
354 debug(trace) scope(success) printf( "decodeNumber-\n" );
355
356 return decodeNumber( errStatus, sliceNumber() );
357 }
358
359 size_t decodeNumber( out bool errStatus, scope const(char)[] num ) scope nothrow
360 {
361 debug(trace) printf( "decodeNumber+\n" );
362 debug(trace) scope(success) printf( "decodeNumber-\n" );
363
364 size_t val = 0;
365
366 foreach ( c; num )
367 {
368 import core.checkedint : mulu, addu;
369
370 bool overflow = false;
371 val = mulu(val, 10, overflow);
372 val = addu(val, c - '0', overflow);
373 if (overflow)
374 {
375 errStatus = true;
376 return 0;
377 }
378 }
379 return val;
380 }
381
382 void parseReal(out bool errStatus) scope nothrow
383 {
384 debug(trace) printf( "parseReal+\n" );
385 debug(trace) scope(success) printf( "parseReal-\n" );
386
387 char[64] tbuf = void;
388 size_t tlen = 0;
389 real val = void;
390
391 void onError()
392 {
393 errStatus = true;
394 }
395
396 if ( 'I' == front )
397 {
398 if (!match("INF"))
399 return onError();
400 put( "real.infinity" );
401 return;
402 }
403 if ( 'N' == front )
404 {
405 popFront();
406 if ( 'I' == front )
407 {
408 if (!match("INF"))
409 return onError();
410 put( "-real.infinity" );
411 return;
412 }
413 if ( 'A' == front )
414 {
415 if (!match("AN"))
416 return onError();
417 put( "real.nan" );
418 return;
419 }
420 tbuf[tlen++] = '-';
421 }
422
423 tbuf[tlen++] = '0';
424 tbuf[tlen++] = 'X';
425 errStatus = !isHexDigit( front );
426 if (errStatus)
427 return; // Expected hex digit
428
429 tbuf[tlen++] = front;
430 tbuf[tlen++] = '.';
431 popFront();
432
433 while ( isHexDigit( front ) )
434 {
435 if (tlen >= tbuf.length)
436 return onError(); // Too many hex float digits
437 tbuf[tlen++] = front;
438 popFront();
439 }
440 if (!match('P'))
441 return onError();
442 tbuf[tlen++] = 'p';
443 if ( 'N' == front )
444 {
445 tbuf[tlen++] = '-';
446 popFront();
447 }
448 else
449 {
450 tbuf[tlen++] = '+';
451 }
452 while ( isDigit( front ) )
453 {
454 tbuf[tlen++] = front;
455 popFront();
456 }
457
458 tbuf[tlen] = 0;
459 debug(info) printf( "got (%s)\n", tbuf.ptr );
460 pureReprintReal( tbuf[] );
461 debug(info) printf( "converted (%.*s)\n", cast(int) tlen, tbuf.ptr );
462 put( tbuf[0 .. tlen] );
463 }
464
465
466 /*
467 LName:
468 Number Name
469
470 Name:
471 Namestart
472 Namestart Namechars
473
474 Namestart:
475 _
476 Alpha
477
478 Namechar:
479 Namestart
480 Digit
481
482 Namechars:
483 Namechar
484 Namechar Namechars
485 */
486 void parseLName(out string errMsg) scope nothrow
487 {
488 debug(trace) printf( "parseLName+\n" );
489 debug(trace) scope(success) printf( "parseLName-\n" );
490
491 static if (__traits(hasMember, Hooks, "parseLName"))
492 {
493 auto r = hooks.parseLName(errMsg, this);
494 if (errMsg !is null)
495 return;
496 if (r) return;
497 }
498
499 void error(string msg)
500 {
501 errMsg = msg;
502 }
503
504 if ( front == 'Q' )
505 {
506 // back reference to LName
507 auto refPos = pos;
508 popFront();
509 size_t n = decodeBackref();
510 if (!n || n > refPos)
511 return error("Invalid LName back reference");
512
513 if ( !mute )
514 {
515 auto savePos = pos;
516 scope(exit) pos = savePos;
517 pos = refPos - n;
518 parseLName(errMsg);
519 }
520 return;
521 }
522
523 bool err_flag;
524 auto n = decodeNumber(err_flag);
525 if (err_flag)
526 return error("Number overflow");
527
528 if ( n == 0 )
529 {
530 put( "__anonymous" );
531 return;
532 }
533 if ( n > buf.length || n > buf.length - pos )
534 return error("LName must be at least 1 character");
535
536 if ( '_' != front && !isAlpha( front ) )
537 return error("Invalid character in LName");
538
539 foreach (char e; buf[pos + 1 .. pos + n] )
540 {
541 if ( '_' != e && !isAlpha( e ) && !isDigit( e ) )
542 return error("Invalid character in LName");
543 }
544
545 put( buf[pos .. pos + n] );
546 pos += n;
547 }
548
549
550 /*
551 Type:
552 Shared
553 Const
554 Immutable
555 Wild
556 TypeArray
557 TypeVector
558 TypeStaticArray
559 TypeAssocArray
560 TypePointer
561 TypeFunction
562 TypeIdent
563 TypeClass
564 TypeStruct
565 TypeEnum
566 TypeTypedef
567 TypeDelegate
568 TypeNone
569 TypeVoid
570 TypeNoreturn
571 TypeByte
572 TypeUbyte
573 TypeShort
574 TypeUshort
575 TypeInt
576 TypeUint
577 TypeLong
578 TypeUlong
579 TypeCent
580 TypeUcent
581 TypeFloat
582 TypeDouble
583 TypeReal
584 TypeIfloat
585 TypeIdouble
586 TypeIreal
587 TypeCfloat
588 TypeCdouble
589 TypeCreal
590 TypeBool
591 TypeChar
592 TypeWchar
593 TypeDchar
594 TypeTuple
595
596 Shared:
597 O Type
598
599 Const:
600 x Type
601
602 Immutable:
603 y Type
604
605 Wild:
606 Ng Type
607
608 TypeArray:
609 A Type
610
611 TypeVector:
612 Nh Type
613
614 TypeStaticArray:
615 G Number Type
616
617 TypeAssocArray:
618 H Type Type
619
620 TypePointer:
621 P Type
622
623 TypeFunction:
624 CallConvention FuncAttrs Arguments ArgClose Type
625
626 TypeIdent:
627 I LName
628
629 TypeClass:
630 C LName
631
632 TypeStruct:
633 S LName
634
635 TypeEnum:
636 E LName
637
638 TypeTypedef:
639 T LName
640
641 TypeDelegate:
642 D TypeFunction
643
644 TypeNone:
645 n
646
647 TypeVoid:
648 v
649
650 TypeNoreturn
651 Nn
652
653 TypeByte:
654 g
655
656 TypeUbyte:
657 h
658
659 TypeShort:
660 s
661
662 TypeUshort:
663 t
664
665 TypeInt:
666 i
667
668 TypeUint:
669 k
670
671 TypeLong:
672 l
673
674 TypeUlong:
675 m
676
677 TypeCent
678 zi
679
680 TypeUcent
681 zk
682
683 TypeFloat:
684 f
685
686 TypeDouble:
687 d
688
689 TypeReal:
690 e
691
692 TypeIfloat:
693 o
694
695 TypeIdouble:
696 p
697
698 TypeIreal:
699 j
700
701 TypeCfloat:
702 q
703
704 TypeCdouble:
705 r
706
707 TypeCreal:
708 c
709
710 TypeBool:
711 b
712
713 TypeChar:
714 a
715
716 TypeWchar:
717 u
718
719 TypeDchar:
720 w
721
722 TypeTuple:
723 B Number Arguments
724 */
725 BufSlice parseType(out bool errStatus) return scope nothrow
726 {
727 static immutable string[23] primitives = [
728 "char", // a
729 "bool", // b
730 "creal", // c
731 "double", // d
732 "real", // e
733 "float", // f
734 "byte", // g
735 "ubyte", // h
736 "int", // i
737 "ireal", // j
738 "uint", // k
739 "long", // l
740 "ulong", // m
741 null, // n
742 "ifloat", // o
743 "idouble", // p
744 "cfloat", // q
745 "cdouble", // r
746 "short", // s
747 "ushort", // t
748 "wchar", // u
749 "void", // v
750 "dchar", // w
751 ];
752
753 static if (__traits(hasMember, Hooks, "parseType"))
754 {
755 auto n = hooks.parseType(errStatus, this, null);
756 if (errStatus)
757 return dst.bslice_empty;
758 else
759 if (n !is null)
760 return BufSlice(n, 0, n.length);
761 }
762
763 debug(trace) printf( "parseType+\n" );
764 debug(trace) scope(success) printf( "parseType-\n" );
765 auto beg = dst.length;
766 auto t = front;
767
768 BufSlice parseBackrefType(out string errStatus, scope BufSlice delegate(bool err_flag) pure @safe nothrow parseDg) pure @safe nothrow
769 {
770 if (pos == brp)
771 {
772 errStatus = "recursive back reference";
773 return dst.bslice_empty;
774 }
775
776 auto refPos = pos;
777 popFront();
778 auto n = decodeBackref();
779 if (n == 0 || n > pos)
780 {
781 errStatus = "invalid back reference";
782 return dst.bslice_empty;
783 }
784
785 if ( mute )
786 return dst.bslice_empty;
787 auto savePos = pos;
788 auto saveBrp = brp;
789 scope(success) { pos = savePos; brp = saveBrp; }
790 pos = refPos - n;
791 brp = refPos;
792
793 bool err_flag;
794 auto ret = parseDg(err_flag);
795 if (err_flag)
796 {
797 errStatus = "parseDg error";
798 return dst.bslice_empty;
799 }
800
801 return ret;
802 }
803
804 // call parseType() and return error if occured
805 enum parseTypeOrF = "parseType(errStatus); if (errStatus) return dst.bslice_empty;";
806
807 switch ( t )
808 {
809 case 'Q': // Type back reference
810 string errMsg;
811 auto r = parseBackrefType(errMsg, (e_flag) => parseType(e_flag));
812 if (errMsg !is null)
813 return dst.bslice_empty;
814 return r;
815 case 'O': // Shared (O Type)
816 popFront();
817 put( "shared(" );
818 mixin(parseTypeOrF);
819 put( ')' );
820 return dst[beg .. $];
821 case 'x': // Const (x Type)
822 popFront();
823 put( "const(" );
824 mixin(parseTypeOrF);
825 put( ')' );
826 return dst[beg .. $];
827 case 'y': // Immutable (y Type)
828 popFront();
829 put( "immutable(" );
830 mixin(parseTypeOrF);
831 put( ')' );
832 return dst[beg .. $];
833 case 'N':
834 popFront();
835 switch ( front )
836 {
837 case 'n': // Noreturn
838 popFront();
839 put("noreturn");
840 return dst[beg .. $];
841 case 'g': // Wild (Ng Type)
842 popFront();
843 // TODO: Anything needed here?
844 put( "inout(" );
845 mixin(parseTypeOrF);
846 put( ')' );
847 return dst[beg .. $];
848 case 'h': // TypeVector (Nh Type)
849 popFront();
850 put( "__vector(" );
851 mixin(parseTypeOrF);
852 put( ')' );
853 return dst[beg .. $];
854 default:
855 errStatus = true;
856 return dst.bslice_empty;
857 }
858 case 'A': // TypeArray (A Type)
859 popFront();
860 mixin(parseTypeOrF);
861 put( "[]" );
862 return dst[beg .. $];
863 case 'G': // TypeStaticArray (G Number Type)
864 popFront();
865 auto num = sliceNumber();
866 mixin(parseTypeOrF);
867 put( '[' );
868 put( num );
869 put( ']' );
870 return dst[beg .. $];
871 case 'H': // TypeAssocArray (H Type Type)
872 popFront();
873 // skip t1
874 auto tx = parseType(errStatus);
875 if (errStatus)
876 return dst.bslice_empty;
877 mixin(parseTypeOrF);
878 put( '[' );
879 shift(tx);
880 put( ']' );
881 return dst[beg .. $];
882 case 'P': // TypePointer (P Type)
883 popFront();
884 mixin(parseTypeOrF);
885 put( '*' );
886 return dst[beg .. $];
887 case 'F': case 'U': case 'W': case 'V': case 'R': // TypeFunction
888 auto r = parseTypeFunction(errStatus);
889 if (errStatus)
890 return dst.bslice_empty;
891 return r;
892 case 'C': // TypeClass (C LName)
893 case 'S': // TypeStruct (S LName)
894 case 'E': // TypeEnum (E LName)
895 case 'T': // TypeTypedef (T LName)
896 popFront();
897 parseQualifiedName(errStatus);
898 if (errStatus)
899 return dst.bslice_empty;
900 return dst[beg .. $];
901 case 'D': // TypeDelegate (D TypeFunction)
902 popFront();
903 auto modifiers = parseModifier();
904 if ( front == 'Q' )
905 {
906 string errMsg;
907 auto r = parseBackrefType(errMsg, (e_flag) => parseTypeFunction(e_flag, IsDelegate.yes));
908 if (errMsg !is null)
909 return dst.bslice_empty;
910 return r;
911 }
912 else
913 {
914 parseTypeFunction(errStatus, IsDelegate.yes);
915 if (errStatus)
916 return dst.bslice_empty;
917 }
918
919 if (modifiers)
920 {
921 // write modifiers behind the function arguments
922 while (auto str = typeCtors.toStringConsume(modifiers))
923 {
924 put(' ');
925 put(str);
926 }
927 }
928 return dst[beg .. $];
929 case 'n': // TypeNone (n)
930 popFront();
931 // TODO: Anything needed here?
932 return dst[beg .. $];
933 case 'B': // TypeTuple (B Number Arguments)
934 popFront();
935 // TODO: Handle this.
936 return dst[beg .. $];
937 case 'Z': // Internal symbol
938 // This 'type' is used for untyped internal symbols, i.e.:
939 // __array
940 // __init
941 // __vtbl
942 // __Class
943 // __Interface
944 // __ModuleInfo
945 popFront();
946 return dst[beg .. $];
947 default:
948 if (t >= 'a' && t <= 'w')
949 {
950 popFront();
951 put( primitives[cast(size_t)(t - 'a')] );
952 return dst[beg .. $];
953 }
954 else if (t == 'z')
955 {
956 popFront();
957 switch ( front )
958 {
959 case 'i':
960 popFront();
961 put( "cent" );
962 return dst[beg .. $];
963 case 'k':
964 popFront();
965 put( "ucent" );
966 return dst[beg .. $];
967 default:
968 errStatus = true;
969 return dst.bslice_empty;
970 }
971 }
972 errStatus = true;
973 return dst.bslice_empty;
974 }
975 }
976
977
978 /*
979 TypeFunction:
980 CallConvention FuncAttrs Arguments ArgClose Type
981
982 CallConvention:
983 F // D
984 U // C
985 W // Windows
986 R // C++
987
988 FuncAttrs:
989 FuncAttr
990 FuncAttr FuncAttrs
991
992 FuncAttr:
993 empty
994 FuncAttrPure
995 FuncAttrNothrow
996 FuncAttrProperty
997 FuncAttrRef
998 FuncAttrReturn
999 FuncAttrScope
1000 FuncAttrTrusted
1001 FuncAttrSafe
1002
1003 FuncAttrPure:
1004 Na
1005
1006 FuncAttrNothrow:
1007 Nb
1008
1009 FuncAttrRef:
1010 Nc
1011
1012 FuncAttrProperty:
1013 Nd
1014
1015 FuncAttrTrusted:
1016 Ne
1017
1018 FuncAttrSafe:
1019 Nf
1020
1021 FuncAttrNogc:
1022 Ni
1023
1024 FuncAttrReturn:
1025 Nj
1026
1027 FuncAttrScope:
1028 Nl
1029
1030 Arguments:
1031 Argument
1032 Argument Arguments
1033
1034 Argument:
1035 Argument2
1036 M Argument2 // scope
1037
1038 Argument2:
1039 Type
1040 J Type // out
1041 K Type // ref
1042 L Type // lazy
1043
1044 ArgClose
1045 X // variadic T t,...) style
1046 Y // variadic T t...) style
1047 Z // not variadic
1048 */
1049 void parseCallConvention(out bool errStatus) nothrow
1050 {
1051 // CallConvention
1052 switch ( front )
1053 {
1054 case 'F': // D
1055 popFront();
1056 break;
1057 case 'U': // C
1058 popFront();
1059 put( "extern (C) " );
1060 break;
1061 case 'W': // Windows
1062 popFront();
1063 put( "extern (Windows) " );
1064 break;
1065 case 'R': // C++
1066 popFront();
1067 put( "extern (C++) " );
1068 break;
1069 default:
1070 errStatus = true;
1071 }
1072 }
1073
1074 /// Returns: Flags of `TypeCtor`
1075 ushort parseModifier()
1076 {
1077 TypeCtor res = TypeCtor.None;
1078 switch ( front )
1079 {
1080 case 'y':
1081 popFront();
1082 return TypeCtor.Immutable;
1083 case 'O':
1084 popFront();
1085 res |= TypeCtor.Shared;
1086 if (front == 'x')
1087 goto case 'x';
1088 if (front == 'N')
1089 goto case 'N';
1090 return TypeCtor.Shared;
1091 case 'N':
1092 if (peek( 1 ) != 'g')
1093 return res;
1094 popFront();
1095 popFront();
1096 res |= TypeCtor.InOut;
1097 if ( front == 'x' )
1098 goto case 'x';
1099 return res;
1100 case 'x':
1101 popFront();
1102 res |= TypeCtor.Const;
1103 return res;
1104 default: return TypeCtor.None;
1105 }
1106 }
1107
1108 ushort parseFuncAttr(out bool errStatus) nothrow
1109 {
1110 // FuncAttrs
1111 ushort result;
1112 while ('N' == front)
1113 {
1114 popFront();
1115 switch ( front )
1116 {
1117 case 'a': // FuncAttrPure
1118 popFront();
1119 result |= FuncAttributes.Pure;
1120 continue;
1121 case 'b': // FuncAttrNoThrow
1122 popFront();
1123 result |= FuncAttributes.Nothrow;
1124 continue;
1125 case 'c': // FuncAttrRef
1126 popFront();
1127 result |= FuncAttributes.Ref;
1128 continue;
1129 case 'd': // FuncAttrProperty
1130 popFront();
1131 result |= FuncAttributes.Property;
1132 continue;
1133 case 'e': // FuncAttrTrusted
1134 popFront();
1135 result |= FuncAttributes.Trusted;
1136 continue;
1137 case 'f': // FuncAttrSafe
1138 popFront();
1139 result |= FuncAttributes.Safe;
1140 continue;
1141 case 'g':
1142 case 'h':
1143 case 'k':
1144 case 'n':
1145 // NOTE: The inout parameter type is represented as "Ng".
1146 // The vector parameter type is represented as "Nh".
1147 // The return parameter type is represented as "Nk".
1148 // The noreturn parameter type is represented as "Nn".
1149 // These make it look like a FuncAttr, but infact
1150 // if we see these, then we know we're really in
1151 // the parameter list. Rewind and break.
1152 pos--;
1153 return result;
1154 case 'i': // FuncAttrNogc
1155 popFront();
1156 result |= FuncAttributes.NoGC;
1157 continue;
1158 case 'j': // FuncAttrReturn
1159 popFront();
1160 if (this.peek(0) == 'N' && this.peek(1) == 'l')
1161 {
1162 result |= FuncAttributes.ReturnScope;
1163 popFront();
1164 popFront();
1165 } else {
1166 result |= FuncAttributes.Return;
1167 }
1168 continue;
1169 case 'l': // FuncAttrScope
1170 popFront();
1171 if (this.peek(0) == 'N' && this.peek(1) == 'j')
1172 {
1173 result |= FuncAttributes.ScopeReturn;
1174 popFront();
1175 popFront();
1176 } else {
1177 result |= FuncAttributes.Scope;
1178 }
1179 continue;
1180 case 'm': // FuncAttrLive
1181 popFront();
1182 result |= FuncAttributes.Live;
1183 continue;
1184 default:
1185 errStatus = true;
1186 return 0;
1187 }
1188 }
1189 return result;
1190 }
1191
1192 void parseFuncArguments(out bool errStatus) scope nothrow
1193 {
1194 // Arguments
1195 for ( size_t n = 0; true; n++ )
1196 {
1197 debug(info) printf( "tok (%c)\n", front );
1198 switch ( front )
1199 {
1200 case 'X': // ArgClose (variadic T t...) style)
1201 popFront();
1202 put( "..." );
1203 return;
1204 case 'Y': // ArgClose (variadic T t,...) style)
1205 popFront();
1206 put( ", ..." );
1207 return;
1208 case 'Z': // ArgClose (not variadic)
1209 popFront();
1210 return;
1211 default:
1212 break;
1213 }
1214 putComma(n);
1215
1216 /* Do special return, scope, ref, out combinations
1217 */
1218 int npops;
1219 if ( 'M' == front && peek(1) == 'N' && peek(2) == 'k')
1220 {
1221 const c3 = peek(3);
1222 if (c3 == 'J')
1223 {
1224 put("scope return out "); // MNkJ
1225 npops = 4;
1226 }
1227 else if (c3 == 'K')
1228 {
1229 put("scope return ref "); // MNkK
1230 npops = 4;
1231 }
1232 }
1233 else if ('N' == front && peek(1) == 'k')
1234 {
1235 const c2 = peek(2);
1236 if (c2 == 'J')
1237 {
1238 put("return out "); // NkJ
1239 npops = 3;
1240 }
1241 else if (c2 == 'K')
1242 {
1243 put("return ref "); // NkK
1244 npops = 3;
1245 }
1246 else if (c2 == 'M')
1247 {
1248 const c3 = peek(3);
1249 if (c3 == 'J')
1250 {
1251 put("return scope out "); // NkMJ
1252 npops = 4;
1253 }
1254 else if (c3 == 'K')
1255 {
1256 put("return scope ref "); // NkMK
1257 npops = 4;
1258 }
1259 else
1260 {
1261 put("return scope "); // NkM
1262 npops = 3;
1263 }
1264 }
1265 }
1266 popFront(npops);
1267
1268 if ( 'M' == front )
1269 {
1270 popFront();
1271 put( "scope " );
1272 }
1273 if ( 'N' == front )
1274 {
1275 popFront();
1276 if ( 'k' == front ) // Return (Nk Parameter2)
1277 {
1278 popFront();
1279 put( "return " );
1280 }
1281 else
1282 pos--;
1283 }
1284
1285 // call parseType() and return error if occured
1286 enum parseTypeOrF = "parseType(errStatus); if (errStatus) return;";
1287
1288 switch ( front )
1289 {
1290 case 'I': // in (I Type)
1291 popFront();
1292 put("in ");
1293 if (front == 'K')
1294 goto case;
1295 mixin(parseTypeOrF);
1296 continue;
1297 case 'K': // ref (K Type)
1298 popFront();
1299 put( "ref " );
1300 mixin(parseTypeOrF);
1301 continue;
1302 case 'J': // out (J Type)
1303 popFront();
1304 put( "out " );
1305 mixin(parseTypeOrF);
1306 continue;
1307 case 'L': // lazy (L Type)
1308 popFront();
1309 put( "lazy " );
1310 mixin(parseTypeOrF);
1311 continue;
1312 default:
1313 mixin(parseTypeOrF);
1314 }
1315 }
1316 }
1317
1318 enum IsDelegate { no, yes }
1319
1320 /*
1321 TypeFunction:
1322 CallConvention FuncAttrs Arguments ArgClose Type
1323 */
1324 BufSlice parseTypeFunction(out bool errStatus, IsDelegate isdg = IsDelegate.no) return scope nothrow
1325 {
1326 debug(trace) printf( "parseTypeFunction+\n" );
1327 debug(trace) scope(success) printf( "parseTypeFunction-\n" );
1328 auto beg = dst.length;
1329
1330 parseCallConvention(errStatus);
1331 if (errStatus)
1332 return dst.bslice_empty;
1333
1334 auto attributes = parseFuncAttr(errStatus);
1335 if (errStatus)
1336 return dst.bslice_empty;
1337
1338 auto argbeg = dst.length;
1339 put(IsDelegate.yes == isdg ? "delegate" : "function");
1340 put( '(' );
1341 parseFuncArguments(errStatus);
1342 if (errStatus)
1343 return dst.bslice_empty;
1344 put( ')' );
1345 if (attributes)
1346 {
1347 // write function attributes behind arguments
1348 while (auto str = funcAttrs.toStringConsume(attributes))
1349 {
1350 put(' ');
1351 put(str);
1352 }
1353 }
1354
1355 // A function / delegate return type is located at the end of its mangling
1356 // Write it in order, then shift it back to 'code order'
1357 // e.g. `delegate(int) @safedouble ' => 'double delegate(int) @safe'
1358 {
1359 auto retbeg = dst.length;
1360 parseType(errStatus);
1361 if (errStatus)
1362 return dst.bslice_empty;
1363 put(' ');
1364 shift(dst[argbeg .. retbeg]);
1365 }
1366
1367 return dst[beg .. $];
1368 }
1369
1370 static bool isCallConvention( char ch )
1371 {
1372 switch ( ch )
1373 {
1374 case 'F', 'U', 'V', 'W', 'R':
1375 return true;
1376 default:
1377 return false;
1378 }
1379 }
1380
1381 /*
1382 Value:
1383 n
1384 Number
1385 i Number
1386 N Number
1387 e HexFloat
1388 c HexFloat c HexFloat
1389 A Number Value...
1390
1391 HexFloat:
1392 NAN
1393 INF
1394 NINF
1395 N HexDigits P Exponent
1396 HexDigits P Exponent
1397
1398 Exponent:
1399 N Number
1400 Number
1401
1402 HexDigits:
1403 HexDigit
1404 HexDigit HexDigits
1405
1406 HexDigit:
1407 Digit
1408 A
1409 B
1410 C
1411 D
1412 E
1413 F
1414 */
1415
1416 void parseValue(out bool errStatus) scope nothrow
1417 {
1418 parseValue(errStatus, dst.bslice_empty);
1419 }
1420
1421 void parseValue(out bool errStatus, scope BufSlice name, char type = '\0' ) scope nothrow
1422 {
1423 debug(trace) printf( "parseValue+\n" );
1424 debug(trace) scope(success) printf( "parseValue-\n" );
1425
1426 void onError()
1427 {
1428 errStatus = true;
1429 }
1430
1431 // printf( "*** %c\n", front );
1432 switch ( front )
1433 {
1434 case 'n':
1435 popFront();
1436 put( "null" );
1437 return;
1438 case 'i':
1439 popFront();
1440 if ('0' > front || '9' < front)
1441 return onError(); // Number expected
1442 goto case;
1443 case '0': .. case '9':
1444 parseIntegerValue( errStatus, name, type );
1445 return;
1446 case 'N':
1447 popFront();
1448 put( '-' );
1449 parseIntegerValue( errStatus, name, type );
1450 return;
1451 case 'e':
1452 popFront();
1453 parseReal(errStatus);
1454 return;
1455 case 'c':
1456 popFront();
1457 parseReal(errStatus);
1458 if (errStatus)
1459 return;
1460 put( '+' );
1461 if (!match('c'))
1462 return onError();
1463 parseReal(errStatus);
1464 if (errStatus)
1465 return;
1466 put( 'i' );
1467 return;
1468 case 'a': case 'w': case 'd':
1469 char t = front;
1470 popFront();
1471 auto n = decodeNumber(errStatus);
1472 if (errStatus)
1473 return;
1474 if (!match('_'))
1475 return onError();
1476 put( '"' );
1477 foreach (i; 0..n)
1478 {
1479 auto a = ascii2hex( errStatus, front );
1480 if (errStatus)
1481 return;
1482 popFront();
1483
1484 auto b = ascii2hex( errStatus, front );
1485 if (errStatus)
1486 return;
1487 popFront();
1488
1489 auto v = cast(char)((a << 4) | b);
1490 if (' ' <= v && v <= '~') // ASCII printable
1491 {
1492 put(v);
1493 }
1494 else
1495 {
1496 put("\\x");
1497 putAsHex(v, 2);
1498 }
1499 }
1500 put( '"' );
1501 if ( 'a' != t )
1502 put(t);
1503 return;
1504 case 'A':
1505 // NOTE: This is kind of a hack. An associative array literal
1506 // [1:2, 3:4] is represented as HiiA2i1i2i3i4, so the type
1507 // is "Hii" and the value is "A2i1i2i3i4". Thus the only
1508 // way to determine that this is an AA value rather than an
1509 // array value is for the caller to supply the type char.
1510 // Hopefully, this will change so that the value is
1511 // "H2i1i2i3i4", rendering this unnecesary.
1512 if ( 'H' == type )
1513 goto LassocArray;
1514 // A Number Value...
1515 // An array literal. Value is repeated Number times.
1516 popFront();
1517 put( '[' );
1518 auto n = decodeNumber(errStatus);
1519 if (errStatus)
1520 return;
1521 foreach ( i; 0 .. n )
1522 {
1523 putComma(i);
1524 parseValue(errStatus);
1525 if (errStatus)
1526 return;
1527 }
1528 put( ']' );
1529 return;
1530 case 'H':
1531 LassocArray:
1532 // H Number Value...
1533 // An associative array literal. Value is repeated 2*Number times.
1534 popFront();
1535 put( '[' );
1536 auto n = decodeNumber(errStatus);
1537 if (errStatus)
1538 return;
1539 foreach ( i; 0 .. n )
1540 {
1541 putComma(i);
1542 parseValue(errStatus);
1543 if (errStatus)
1544 return;
1545 put(':');
1546 parseValue(errStatus);
1547 if (errStatus)
1548 return;
1549 }
1550 put( ']' );
1551 return;
1552 case 'S':
1553 // S Number Value...
1554 // A struct literal. Value is repeated Number times.
1555 popFront();
1556 if ( name.length )
1557 put( name );
1558 put( '(' );
1559 auto n = decodeNumber(errStatus);
1560 if (errStatus)
1561 return;
1562 foreach ( i; 0 .. n )
1563 {
1564 putComma(i);
1565 parseValue(errStatus);
1566 if (errStatus)
1567 return;
1568 }
1569 put( ')' );
1570 return;
1571 case 'f':
1572 // f MangledName
1573 // A function literal symbol
1574 popFront();
1575 parseMangledName(errStatus, false, 1);
1576 return;
1577 default:
1578 errStatus = true;
1579 }
1580 }
1581
1582 void parseIntegerValue( out bool errStatus, scope BufSlice name, char type = '\0' ) scope nothrow
1583 {
1584 debug(trace) printf( "parseIntegerValue+\n" );
1585 debug(trace) scope(success) printf( "parseIntegerValue-\n" );
1586
1587 switch ( type )
1588 {
1589 case 'a': // char
1590 case 'u': // wchar
1591 case 'w': // dchar
1592 {
1593 auto val = sliceNumber();
1594 auto num = decodeNumber( errStatus, val );
1595 if (errStatus)
1596 return;
1597
1598 switch ( num )
1599 {
1600 case '\'':
1601 put( "'\\''" );
1602 return;
1603 // \", \?
1604 case '\\':
1605 put( "'\\\\'" );
1606 return;
1607 case '\a':
1608 put( "'\\a'" );
1609 return;
1610 case '\b':
1611 put( "'\\b'" );
1612 return;
1613 case '\f':
1614 put( "'\\f'" );
1615 return;
1616 case '\n':
1617 put( "'\\n'" );
1618 return;
1619 case '\r':
1620 put( "'\\r'" );
1621 return;
1622 case '\t':
1623 put( "'\\t'" );
1624 return;
1625 case '\v':
1626 put( "'\\v'" );
1627 return;
1628 default:
1629 switch ( type )
1630 {
1631 case 'a':
1632 if ( num >= 0x20 && num < 0x7F )
1633 {
1634 put( '\'' );
1635 put( cast(char)num );
1636 put( '\'' );
1637 return;
1638 }
1639 put( "\\x" );
1640 putAsHex( num, 2 );
1641 return;
1642 case 'u':
1643 put( "'\\u" );
1644 putAsHex( num, 4 );
1645 put( '\'' );
1646 return;
1647 case 'w':
1648 put( "'\\U" );
1649 putAsHex( num, 8 );
1650 put( '\'' );
1651 return;
1652 default:
1653 assert( 0 );
1654 }
1655 }
1656 }
1657 case 'b': // bool
1658 auto d = decodeNumber(errStatus);
1659 if (errStatus)
1660 return;
1661 put( d ? "true" : "false" );
1662 return;
1663 case 'h', 't', 'k': // ubyte, ushort, uint
1664 put( sliceNumber() );
1665 put( 'u' );
1666 return;
1667 case 'l': // long
1668 put( sliceNumber() );
1669 put( 'L' );
1670 return;
1671 case 'm': // ulong
1672 put( sliceNumber() );
1673 put( "uL" );
1674 return;
1675 default:
1676 put( sliceNumber() );
1677 return;
1678 }
1679 }
1680
1681
1682 /*
1683 TemplateArgs:
1684 TemplateArg
1685 TemplateArg TemplateArgs
1686
1687 TemplateArg:
1688 TemplateArgX
1689 H TemplateArgX
1690
1691 TemplateArgX:
1692 T Type
1693 V Type Value
1694 S Number_opt QualifiedName
1695 X ExternallyMangledName
1696 */
1697 void parseTemplateArgs(out bool errStatus) scope nothrow
1698 {
1699 debug(trace) printf( "parseTemplateArgs+\n" );
1700 debug(trace) scope(success) printf( "parseTemplateArgs-\n" );
1701
1702 L_nextArg:
1703 for ( size_t n = 0; true; n++ )
1704 {
1705 if ( front == 'H' )
1706 popFront();
1707
1708 switch ( front )
1709 {
1710 case 'T':
1711 popFront();
1712 putComma(n);
1713 parseType(errStatus);
1714 if (errStatus)
1715 return;
1716 continue;
1717 case 'V':
1718 popFront();
1719 putComma(n);
1720 // NOTE: In the few instances where the type is actually
1721 // desired in the output it should precede the value
1722 // generated by parseValue, so it is safe to simply
1723 // decrement len and let put/append do its thing.
1724 char t = front; // peek at type for parseValue
1725 if ( t == 'Q' )
1726 {
1727 t = peekBackref();
1728 if (t == 0)
1729 {
1730 // invalid back reference
1731 errStatus = true;
1732 return;
1733 }
1734 }
1735 BufSlice name = dst.bslice_empty;
1736 silent( errStatus, delegate void(out bool e_flg) nothrow { name = parseType(e_flg); } );
1737 if (errStatus)
1738 return;
1739 parseValue( errStatus, name, t );
1740 if (errStatus)
1741 return;
1742 continue;
1743 case 'S':
1744 popFront();
1745 putComma(n);
1746
1747 if ( mayBeMangledNameArg() )
1748 {
1749 auto l = dst.length;
1750 auto p = pos;
1751 auto b = brp;
1752
1753 debug(trace) printf( "may be mangled name arg\n" );
1754
1755 if (parseMangledNameArg())
1756 continue;
1757 else
1758 {
1759 dst.len = l;
1760 pos = p;
1761 brp = b;
1762 debug(trace) printf( "not a mangled name arg\n" );
1763 }
1764 }
1765 if ( isDigit( front ) && isDigit( peek( 1 ) ) )
1766 {
1767 // ambiguity: length followed by qualified name (starting with number)
1768 // try all possible pairs of numbers
1769 auto qlen = decodeNumber(errStatus);
1770 if (errStatus)
1771 return;
1772
1773 qlen /= 10; // last digit needed for QualifiedName
1774 pos--;
1775 auto l = dst.length;
1776 auto p = pos;
1777 auto b = brp;
1778 while ( qlen > 0 )
1779 {
1780 errStatus = false;
1781 parseQualifiedName(errStatus);
1782
1783 if (!errStatus)
1784 {
1785 if ( pos == p + qlen )
1786 continue L_nextArg;
1787 }
1788
1789 qlen /= 10; // retry with one digit less
1790 pos = --p;
1791 dst.len = l;
1792 brp = b;
1793 }
1794 }
1795
1796 parseQualifiedName(errStatus);
1797 if (errStatus)
1798 return;
1799 continue;
1800 case 'X':
1801 popFront();
1802 putComma(n);
1803 {
1804 string errMsg;
1805 parseLName(errMsg);
1806 if (errMsg)
1807 return;
1808 }
1809 continue;
1810 default:
1811 return;
1812 }
1813 }
1814 }
1815
1816
1817 bool mayBeMangledNameArg() nothrow
1818 {
1819 debug(trace) printf( "mayBeMangledNameArg+\n" );
1820 debug(trace) scope(success) printf( "mayBeMangledNameArg-\n" );
1821
1822 bool errStatus;
1823 auto p = pos;
1824 scope(exit) pos = p;
1825
1826 if ( isDigit( buf[pos] ) )
1827 {
1828 auto n = decodeNumber(errStatus);
1829
1830 return !errStatus && n >= 4 &&
1831 pos < buf.length && '_' == buf[pos++] &&
1832 pos < buf.length && 'D' == buf[pos++] &&
1833 isDigit( buf[pos] );
1834 }
1835 else
1836 {
1837 const isSNF = isSymbolNameFront(errStatus);
1838
1839 return !errStatus &&
1840 pos < buf.length && '_' == buf[pos++] &&
1841 pos < buf.length && 'D' == buf[pos++] &&
1842 isSNF;
1843 }
1844 }
1845
1846 bool parseMangledNameArg() nothrow
1847 {
1848 debug(trace) printf( "parseMangledNameArg+\n" );
1849 debug(trace) scope(success) printf( "parseMangledNameArg-\n" );
1850
1851 bool errStatus;
1852
1853 size_t n = 0;
1854 if ( isDigit( front ) )
1855 {
1856 n = decodeNumber(errStatus);
1857
1858 if (errStatus)
1859 return false;
1860 }
1861
1862 parseMangledName(errStatus, false, n );
1863
1864 return !errStatus;
1865 }
1866
1867 /*
1868 TemplateInstanceName:
1869 Number __T LName TemplateArgs Z
1870 */
1871 void parseTemplateInstanceName(out bool errStatus, bool hasNumber) scope nothrow
1872 {
1873 debug(trace) printf( "parseTemplateInstanceName+\n" );
1874 debug(trace) scope(success) printf( "parseTemplateInstanceName-\n" );
1875
1876 auto sav = pos;
1877 auto saveBrp = brp;
1878
1879 void onError()
1880 {
1881 errStatus = true;
1882 pos = sav;
1883 brp = saveBrp;
1884 }
1885
1886 size_t n = 0;
1887 if (hasNumber)
1888 {
1889 n = decodeNumber(errStatus);
1890 if (errStatus)
1891 return onError();
1892 }
1893
1894 auto beg = pos;
1895 errStatus = !match( "__T" );
1896 if (errStatus)
1897 return onError();
1898
1899 {
1900 string errMsg;
1901 parseLName(errMsg);
1902 if (errMsg !is null)
1903 return onError();
1904 }
1905
1906 put( "!(" );
1907
1908 parseTemplateArgs(errStatus);
1909 if (errStatus)
1910 return onError();
1911
1912 if (!match('Z'))
1913 return onError();
1914
1915 if ( hasNumber && pos - beg != n )
1916 {
1917 // Template name length mismatch
1918 return onError();
1919 }
1920
1921 put( ')' );
1922 }
1923
1924
1925 bool mayBeTemplateInstanceName() scope nothrow
1926 {
1927 debug(trace) printf( "mayBeTemplateInstanceName+\n" );
1928 debug(trace) scope(success) printf( "mayBeTemplateInstanceName-\n" );
1929
1930 auto p = pos;
1931 scope(exit) pos = p;
1932
1933 bool errStatus;
1934 auto n = decodeNumber(errStatus);
1935 if (errStatus)
1936 return false;
1937
1938 return n >= 5 &&
1939 pos < buf.length && '_' == buf[pos++] &&
1940 pos < buf.length && '_' == buf[pos++] &&
1941 pos < buf.length && 'T' == buf[pos++];
1942 }
1943
1944
1945 /*
1946 SymbolName:
1947 LName
1948 TemplateInstanceName
1949 */
1950 void parseSymbolName(out bool errStatus) scope nothrow
1951 {
1952 debug(trace) printf( "parseSymbolName+\n" );
1953 debug(trace) scope(success) printf( "parseSymbolName-\n" );
1954
1955 // LName -> Number
1956 // TemplateInstanceName -> Number "__T"
1957 switch ( front )
1958 {
1959 case '_':
1960 // no length encoding for templates for new mangling
1961 parseTemplateInstanceName(errStatus, false);
1962 return;
1963
1964 case '0': .. case '9':
1965 if ( mayBeTemplateInstanceName() )
1966 {
1967 auto t = dst.length;
1968
1969 debug(trace) printf( "may be template instance name\n" );
1970 parseTemplateInstanceName(errStatus, true);
1971 if (!errStatus)
1972 return;
1973 else
1974 {
1975 debug(trace) printf( "not a template instance name\n" );
1976 dst.len = t;
1977 }
1978 }
1979 goto case;
1980 case 'Q':
1981 string errMsg;
1982 parseLName(errMsg);
1983 errStatus = errMsg !is null;
1984 return;
1985 default:
1986 errStatus = true;
1987 }
1988 }
1989
1990 // parse optional function arguments as part of a symbol name, i.e without return type
1991 // if keepAttr, the calling convention and function attributes are not discarded, but returned
1992 BufSlice parseFunctionTypeNoReturn( bool keepAttr = false ) return scope nothrow
1993 {
1994 // try to demangle a function, in case we are pointing to some function local
1995 auto prevpos = pos;
1996 auto prevlen = dst.length;
1997 auto prevbrp = brp;
1998
1999 if ( 'M' == front )
2000 {
2001 // do not emit "needs this"
2002 popFront();
2003 auto modifiers = parseModifier();
2004 while (auto str = typeCtors.toStringConsume(modifiers))
2005 {
2006 put(str);
2007 put(' ');
2008 }
2009 }
2010 if ( isCallConvention( front ) )
2011 {
2012 BufSlice attr = dst.bslice_empty;
2013 // we don't want calling convention and attributes in the qualified name
2014 bool errStatus;
2015 parseCallConvention(errStatus);
2016 if (!errStatus)
2017 {
2018 auto attributes = parseFuncAttr(errStatus);
2019 if (!errStatus)
2020 {
2021 if (keepAttr) {
2022 while (auto str = funcAttrs.toStringConsume(attributes))
2023 {
2024 put(str);
2025 put(' ');
2026 }
2027 attr = dst[prevlen .. $];
2028 }
2029
2030 put( '(' );
2031 parseFuncArguments(errStatus);
2032 if (errStatus)
2033 return dst.bslice_empty;
2034 put( ')' );
2035 return attr;
2036 }
2037 }
2038
2039 // not part of a qualified name, so back up
2040 pos = prevpos;
2041 dst.len = prevlen;
2042 brp = prevbrp;
2043 }
2044
2045 return dst.bslice_empty;
2046 }
2047
2048 /*
2049 QualifiedName:
2050 SymbolName
2051 SymbolName QualifiedName
2052 */
2053 void parseQualifiedName(out bool errStatus) return scope nothrow
2054 {
2055 debug(trace) printf( "parseQualifiedName+\n" );
2056 debug(trace) scope(success) printf( "parseQualifiedName-\n" );
2057
2058 size_t n = 0;
2059 bool is_sym_name_front;
2060
2061 do
2062 {
2063 if ( n++ )
2064 put( '.' );
2065
2066 parseSymbolName(errStatus);
2067 if (errStatus)
2068 return;
2069
2070 parseFunctionTypeNoReturn();
2071
2072 is_sym_name_front = isSymbolNameFront(errStatus);
2073 if (errStatus)
2074 return;
2075
2076 } while ( is_sym_name_front );
2077 }
2078
2079
2080 /*
2081 MangledName:
2082 _D QualifiedName Type
2083 _D QualifiedName M Type
2084 */
2085 void parseMangledName( out bool errStatus, bool displayType, size_t n = 0 ) scope nothrow
2086 {
2087 debug(trace) printf( "parseMangledName+\n" );
2088 debug(trace) scope(success) printf( "parseMangledName-\n" );
2089 BufSlice name = dst.bslice_empty;
2090
2091 auto end = pos + n;
2092
2093 eat( '_' );
2094 errStatus = !match( 'D' );
2095 if (errStatus)
2096 return;
2097
2098 do
2099 {
2100 size_t beg = dst.length;
2101 size_t nameEnd = dst.length;
2102 BufSlice attr = dst.bslice_empty;
2103 bool is_sym_name_front;
2104
2105 do
2106 {
2107 if ( attr.length )
2108 dst.remove(attr); // dump attributes of parent symbols
2109 if (beg != dst.length)
2110 put( '.' );
2111
2112 parseSymbolName(errStatus);
2113 if (errStatus)
2114 return;
2115
2116 nameEnd = dst.length;
2117 attr = parseFunctionTypeNoReturn( displayType );
2118
2119 is_sym_name_front = isSymbolNameFront(errStatus);
2120 if (errStatus)
2121 return;
2122 } while (is_sym_name_front);
2123
2124 if ( displayType )
2125 {
2126 attr = shift( attr );
2127 nameEnd = dst.length - attr.length; // name includes function arguments
2128 }
2129 name = dst[beg .. nameEnd];
2130
2131 debug(info) printf( "name (%.*s)\n", cast(int) name.length, name.ptr );
2132 if ( 'M' == front )
2133 popFront(); // has 'this' pointer
2134
2135 auto lastlen = dst.length;
2136 auto type = parseType(errStatus);
2137 if (errStatus)
2138 return;
2139
2140 if ( displayType )
2141 {
2142 if ( type.length )
2143 put( ' ' );
2144 // sort (name,attr,type) -> (attr,type,name)
2145 shift( name );
2146 }
2147 else
2148 {
2149 // remove type
2150 assert( attr.length == 0 );
2151 dst.len = lastlen;
2152 }
2153 if ( pos >= buf.length || (n != 0 && pos >= end) )
2154 return;
2155
2156 switch ( front )
2157 {
2158 case 'T': // terminators when used as template alias parameter
2159 case 'V':
2160 case 'S':
2161 case 'Z':
2162 return;
2163 default:
2164 }
2165 put( '.' );
2166
2167 } while ( true );
2168 }
2169
2170 void parseMangledName(out bool errStatus) nothrow
2171 {
2172 parseMangledName(errStatus, AddType.yes == addType);
2173 }
2174
2175 char[] doDemangle(alias FUNC)() return scope nothrow
2176 {
2177 while ( true )
2178 {
2179 debug(info) printf( "demangle(%.*s)\n", cast(int) buf.length, buf.ptr );
2180
2181 bool errStatus;
2182 FUNC(errStatus);
2183 if (!errStatus)
2184 {
2185 return dst[0 .. $].getSlice;
2186 }
2187 else
2188 {
2189 debug(info) printf( "error" );
2190
2191 return dst.copyInput(buf);
2192 }
2193 }
2194 }
2195
2196 char[] demangleName() nothrow
2197 {
2198 return doDemangle!parseMangledName();
2199 }
2200
2201 char[] demangleType() nothrow
2202 {
2203 return doDemangle!parseType();
2204 }
2205 }
2206
2207
2208 /**
2209 * Demangles D/C++ mangled names. If it is not a D/C++ mangled name, it
2210 * returns its argument name.
2211 *
2212 * Params:
2213 * buf = The string to demangle.
2214 * dst = An optional destination buffer.
2215 * __cxa_demangle = optional C++ demangler
2216 *
2217 * Returns:
2218 * The demangled name or the original string if the name is not a mangled
2219 * D/C++ name.
2220 */
2221 char[] demangle(return scope const(char)[] buf, return scope char[] dst = null, CXX_DEMANGLER __cxa_demangle = null) nothrow pure @safe
2222 {
2223 if (__cxa_demangle && buf.length > 2 && buf[0..2] == "_Z")
2224 return demangleCXX(buf, __cxa_demangle, dst);
2225 auto d = Demangle!()(buf, dst);
2226 // fast path (avoiding throwing & catching exception) for obvious
2227 // non-D mangled names
2228 if (buf.length < 2 || !(buf[0] == 'D' || buf[0..2] == "_D"))
2229 return d.dst.copyInput(buf);
2230 return d.demangleName();
2231 }
2232
2233
2234 /**
2235 * Demangles a D mangled type.
2236 *
2237 * Params:
2238 * buf = The string to demangle.
2239 * dst = An optional destination buffer.
2240 *
2241 * Returns:
2242 * The demangled type name or the original string if the name is not a
2243 * mangled D type.
2244 */
2245 char[] demangleType( const(char)[] buf, char[] dst = null ) nothrow pure @safe
2246 {
2247 auto d = Demangle!()(buf, dst);
2248 return d.demangleType();
2249 }
2250
2251 /**
2252 * reencode a mangled symbol name that might include duplicate occurrences
2253 * of the same identifier by replacing all but the first occurence with
2254 * a back reference.
2255 *
2256 * Params:
2257 * mangled = The mangled string representing the type
2258 *
2259 * Returns:
2260 * The mangled name with deduplicated identifiers
2261 */
2262 char[] reencodeMangled(return scope const(char)[] mangled) nothrow pure @safe
2263 {
2264 static struct PrependHooks
2265 {
2266 size_t lastpos;
2267 char[] result;
2268 size_t[const(char)[]] idpos; // identifier positions
2269
2270 static struct Replacement
2271 {
2272 size_t pos; // postion in original mangled string
2273 size_t respos; // postion in result string
2274 }
2275 Replacement [] replacements;
2276
2277 pure @safe:
2278 size_t positionInResult(size_t pos) scope
2279 {
2280 foreach_reverse (r; replacements)
2281 if (pos >= r.pos)
2282 return r.respos + pos - r.pos;
2283 return pos;
2284 }
2285
2286 alias Remangle = Demangle!(PrependHooks);
2287
2288 void flushPosition(ref Remangle d) scope
2289 {
2290 if (lastpos < d.pos)
2291 {
2292 result ~= d.buf[lastpos .. d.pos];
2293 }
2294 else if (lastpos > d.pos)
2295 {
2296 // roll back to earlier position
2297 while (replacements.length > 0 && replacements[$-1].pos > d.pos)
2298 replacements = replacements[0 .. $-1];
2299
2300 if (replacements.length > 0)
2301 result.length = replacements[$-1].respos + d.pos - replacements[$-1].pos;
2302 else
2303 result.length = d.pos;
2304 }
2305 }
2306
2307 bool parseLName(out string errMsg, scope ref Remangle d) scope @trusted nothrow
2308 {
2309 bool error(string msg)
2310 {
2311 errMsg = msg;
2312 return false;
2313 }
2314 flushPosition(d);
2315
2316 auto reslen = result.length;
2317 auto refpos = d.pos;
2318 if (d.front == 'Q')
2319 {
2320 size_t npos;
2321 {
2322 scope(exit) result.length = reslen; // remove all intermediate additions
2323 // only support identifier back references
2324 d.popFront();
2325 size_t n = d.decodeBackref();
2326 if (!n || n > refpos)
2327 return error("invalid back reference");
2328
2329 auto savepos = d.pos;
2330 scope(exit) d.pos = savepos;
2331 size_t srcpos = refpos - n;
2332
2333 bool errStatus;
2334 auto idlen = d.decodeNumber(errStatus);
2335 if (errStatus)
2336 return error("invalid number");
2337
2338 if (d.pos + idlen > d.buf.length)
2339 return error("invalid back reference");
2340
2341 auto id = d.buf[d.pos .. d.pos + idlen];
2342 auto pid = id in idpos;
2343 if (!pid)
2344 return error("invalid back reference");
2345
2346 npos = positionInResult(*pid);
2347 }
2348 encodeBackref(reslen - npos);
2349 const pos = d.pos; // work around issues.dlang.org/show_bug.cgi?id=20675
2350 replacements ~= Replacement(pos, result.length);
2351 }
2352 else
2353 {
2354 bool errStatus;
2355 auto n = d.decodeNumber(errStatus);
2356 if (errStatus)
2357 return error("invalid number");
2358
2359 if (!n || n > d.buf.length || n > d.buf.length - d.pos)
2360 return error("LName too short or too long");
2361
2362 auto id = d.buf[d.pos .. d.pos + n];
2363 d.pos += n;
2364 if (auto pid = id in idpos)
2365 {
2366 size_t npos = positionInResult(*pid);
2367 result.length = reslen;
2368 encodeBackref(reslen - npos);
2369 const pos = d.pos; // work around issues.dlang.org/show_bug.cgi?id=20675
2370 replacements ~= Replacement(pos, result.length);
2371 }
2372 else
2373 {
2374 idpos[id] = refpos; //! scope variable id used as AA key, makes this function @trusted
2375 result ~= d.buf[refpos .. d.pos];
2376 }
2377 }
2378 lastpos = d.pos;
2379 return true;
2380 }
2381
2382 char[] parseType( out bool errStatus, ref Remangle d, char[] name ) return scope nothrow
2383 {
2384 if (d.front != 'Q')
2385 return null;
2386
2387 flushPosition(d);
2388
2389 auto refPos = d.pos;
2390 d.popFront();
2391 auto n = d.decodeBackref();
2392 if (n == 0 || n > refPos)
2393 {
2394 // invalid back reference
2395 errStatus = true;
2396 return null;
2397 }
2398
2399 size_t npos = positionInResult(refPos - n);
2400 size_t reslen = result.length;
2401 encodeBackref(reslen - npos);
2402
2403 lastpos = d.pos;
2404 return result[reslen .. $]; // anything but null
2405 }
2406
2407 void encodeBackref(size_t relpos) scope
2408 {
2409 result ~= 'Q';
2410 enum base = 26;
2411 size_t div = 1;
2412 while (relpos >= div * base)
2413 div *= base;
2414 while (div >= base)
2415 {
2416 auto dig = (relpos / div);
2417 result ~= cast(char)('A' + dig);
2418 relpos -= dig * div;
2419 div /= base;
2420 }
2421 result ~= cast(char)('a' + relpos);
2422 }
2423 }
2424
2425 auto d = Demangle!(PrependHooks)(mangled, null);
2426 d.hooks = PrependHooks();
2427 d.mute = true; // no demangled output
2428
2429 bool errStatus;
2430 d.parseMangledName(errStatus);
2431 if (errStatus)
2432 {
2433 // error cannot occur
2434 return mangled.dup;
2435 }
2436
2437 if (d.hooks.lastpos < d.pos)
2438 d.hooks.result ~= d.buf[d.hooks.lastpos .. d.pos];
2439 return d.hooks.result;
2440 }
2441
2442 /**
2443 * Mangles a D symbol.
2444 *
2445 * Params:
2446 * T = The type of the symbol.
2447 * fqn = The fully qualified name of the symbol.
2448 * dst = An optional destination buffer.
2449 *
2450 * Returns:
2451 * The mangled name for a symbols of type T and the given fully
2452 * qualified name.
2453 */
2454 char[] mangle(T)(return scope const(char)[] fqn, return scope char[] dst = null) @safe pure nothrow
2455 {
2456 import core.internal.string : numDigits, unsignedToTempString;
2457
2458 static struct DotSplitter
2459 {
2460 @safe pure nothrow:
2461 const(char)[] s;
2462
2463 @property bool empty() const { return !s.length; }
2464
2465 @property const(char)[] front() const return scope
2466 {
2467 immutable i = indexOfDot();
2468 return i == -1 ? s[0 .. $] : s[0 .. i];
2469 }
2470
2471 void popFront() scope
2472 {
2473 immutable i = indexOfDot();
2474 s = i == -1 ? s[$ .. $] : s[i+1 .. $];
2475 }
2476
2477 private ptrdiff_t indexOfDot() const scope
2478 {
2479 foreach (i, c; s) if (c == '.') return i;
2480 return -1;
2481 }
2482 }
2483
2484 size_t len = "_D".length;
2485 foreach (comp; DotSplitter(fqn))
2486 len += numDigits(comp.length) + comp.length;
2487 len += T.mangleof.length;
2488 if (dst.length < len) dst.length = len;
2489
2490 size_t i = "_D".length;
2491 dst[0 .. i] = "_D";
2492 foreach (comp; DotSplitter(fqn))
2493 {
2494 const ndigits = numDigits(comp.length);
2495 unsignedToTempString(comp.length, dst[i .. i + ndigits]);
2496 i += ndigits;
2497 dst[i .. i + comp.length] = comp[];
2498 i += comp.length;
2499 }
2500 dst[i .. i + T.mangleof.length] = T.mangleof[];
2501 i += T.mangleof.length;
2502
2503 static if (hasTypeBackRef)
2504 return reencodeMangled(dst[0 .. i]);
2505 else
2506 return dst[0 .. i];
2507 }
2508
2509
2510 ///
2511 @safe pure nothrow unittest
2512 {
2513 assert(mangle!int("a.b") == "_D1a1bi");
2514 assert(mangle!(char[])("test.foo") == "_D4test3fooAa");
2515 assert(mangle!(int function(int))("a.b") == "_D1a1bPFiZi");
2516 }
2517
2518 @safe pure nothrow unittest
2519 {
2520 static assert(mangle!int("a.b") == "_D1a1bi");
2521
2522 auto buf = new char[](10);
2523 buf = mangle!int("a.b", buf);
2524 assert(buf == "_D1a1bi");
2525 buf = mangle!(char[])("test.foo", buf);
2526 assert(buf == "_D4test3fooAa");
2527 buf = mangle!(real delegate(int))("modµ.dg");
2528 assert(buf == "_D5modµ2dgDFiZe", buf);
2529 }
2530
2531
2532 /**
2533 * Mangles a D function.
2534 *
2535 * Params:
2536 * T = function pointer type.
2537 * fqn = The fully qualified name of the symbol.
2538 * dst = An optional destination buffer.
2539 *
2540 * Returns:
2541 * The mangled name for a function with function pointer type T and
2542 * the given fully qualified name.
2543 */
2544 char[] mangleFunc(T:FT*, FT)(return scope const(char)[] fqn, return scope char[] dst = null) @safe pure nothrow if (is(FT == function))
2545 {
2546 static if (isExternD!FT)
2547 {
2548 return mangle!FT(fqn, dst);
2549 }
2550 else static if (hasPlainMangling!FT)
2551 {
2552 dst.length = fqn.length;
2553 dst[] = fqn[];
2554 return dst;
2555 }
2556 else static if (isExternCPP!FT)
2557 {
2558 static assert(0, "Can't mangle extern(C++) functions.");
2559 }
2560 else
2561 {
2562 static assert(0, "Can't mangle function with unknown linkage ("~FT.stringof~").");
2563 }
2564 }
2565
2566 private enum hasTypeBackRef = (int function(void**,void**)).mangleof[$-4 .. $] == "QdZi";
2567
2568 @safe pure nothrow unittest
2569 {
2570 assert(mangleFunc!(int function(int))("a.b") == "_D1a1bFiZi");
2571 static if (hasTypeBackRef)
2572 {
2573 assert(mangleFunc!(int function(Object))("object.Object.opEquals") == "_D6object6Object8opEqualsFCQsZi");
2574 assert(mangleFunc!(int function(Object, Object))("object.Object.opEquals") == "_D6object6Object8opEqualsFCQsQdZi");
2575 }
2576 else
2577 {
2578 auto mngl = mangleFunc!(int function(Object))("object.Object.opEquals");
2579 assert(mngl == "_D6object6Object8opEqualsFC6ObjectZi");
2580 auto remngl = reencodeMangled(mngl);
2581 assert(remngl == "_D6object6Object8opEqualsFCQsZi");
2582 }
2583 // trigger back tracking with ambiguity on '__T', template or identifier
2584 assert(reencodeMangled("_D3std4conv4conv7__T3std4convi") == "_D3std4convQf7__T3stdQpi");
2585 }
2586
2587 @safe pure nothrow unittest
2588 {
2589 int function(lazy int[], ...) fp;
2590 assert(mangle!(typeof(fp))("demangle.test") == "_D8demangle4testPFLAiYi");
2591 assert(mangle!(typeof(*fp))("demangle.test") == "_D8demangle4testFLAiYi");
2592 }
2593
2594 private template isExternD(FT) if (is(FT == function))
2595 {
2596 enum isExternD = __traits(getLinkage, FT) == "D";
2597 }
2598
2599 private template isExternCPP(FT) if (is(FT == function))
2600 {
2601 enum isExternCPP = __traits(getLinkage, FT) == "C++";
2602 }
2603
2604 private template hasPlainMangling(FT) if (is(FT == function))
2605 {
2606 enum lnk = __traits(getLinkage, FT);
2607 // C || Windows
2608 enum hasPlainMangling = lnk == "C" || lnk == "Windows" || lnk == "System";
2609 }
2610
2611 @safe pure nothrow unittest
2612 {
2613 static extern(D) void fooD();
2614 static extern(C) void fooC();
2615 static extern(Windows) void fooW();
2616 static extern(C++) void fooCPP();
2617
2618 bool check(FT)(bool isD, bool isCPP, bool isPlain)
2619 {
2620 return isExternD!FT == isD && isExternCPP!FT == isCPP &&
2621 hasPlainMangling!FT == isPlain;
2622 }
2623 static assert(check!(typeof(fooD))(true, false, false));
2624 static assert(check!(typeof(fooC))(false, false, true));
2625 static assert(check!(typeof(fooW))(false, false, true));
2626 static assert(check!(typeof(fooCPP))(false, true, false));
2627
2628 static assert(__traits(compiles, mangleFunc!(typeof(&fooD))("")));
2629 static assert(__traits(compiles, mangleFunc!(typeof(&fooC))("")));
2630 static assert(__traits(compiles, mangleFunc!(typeof(&fooW))("")));
2631 static assert(!__traits(compiles, mangleFunc!(typeof(&fooCPP))("")));
2632 }
2633
2634 /***
2635 * C name mangling is done by adding a prefix on some platforms.
2636 */
2637 version (Win32)
2638 enum string cPrefix = "_";
2639 else version (Darwin)
2640 enum string cPrefix = "_";
2641 else
2642 enum string cPrefix = "";
2643
2644 @safe pure nothrow unittest
2645 {
2646 immutable string[2][] table =
2647 [
2648 ["printf", "printf"],
2649 ["_foo", "_foo"],
2650 ["_D88", "_D88"],
2651 ["_D3fooQeFIAyaZv", "void foo.foo(in immutable(char)[])" ],
2652 ["_D3barQeFIKAyaZv", "void bar.bar(in ref immutable(char)[])" ],
2653 ["_D4test3fooAa", "char[] test.foo"],
2654 ["_D8demangle8demangleFAaZAa", "char[] demangle.demangle(char[])"],
2655 ["_D6object6Object8opEqualsFC6ObjectZi", "int object.Object.opEquals(Object)"],
2656 ["_D4test2dgDFiYd", "double delegate(int, ...) test.dg"],
2657 ["_D4test2dgDxFNfiYd", "double delegate(int, ...) @safe const test.dg"],
2658 //["_D4test58__T9factorialVde67666666666666860140VG5aa5_68656c6c6fVPvnZ9factorialf", ""],
2659 //["_D4test101__T9factorialVde67666666666666860140Vrc9a999999999999d9014000000000000000c00040VG5aa5_68656c6c6fVPvnZ9factorialf", ""],
2660 ["_D4test34__T3barVG3uw3_616263VG3wd3_646566Z1xi", "int test.bar!(\"abc\"w, \"def\"d).x"],
2661 ["_D8demangle4testFLC6ObjectLDFLiZiZi", "int demangle.test(lazy Object, lazy int delegate(lazy int))"],
2662 ["_D8demangle4testFAiXi", "int demangle.test(int[]...)"],
2663 ["_D8demangle4testFAiYi", "int demangle.test(int[], ...)"],
2664 ["_D8demangle4testFLAiXi", "int demangle.test(lazy int[]...)"],
2665 ["_D8demangle4testFLAiYi", "int demangle.test(lazy int[], ...)"],
2666 ["_D6plugin8generateFiiZAya", "immutable(char)[] plugin.generate(int, int)"],
2667 ["_D6plugin8generateFiiZAxa", "const(char)[] plugin.generate(int, int)"],
2668 ["_D6plugin8generateFiiZAOa", "shared(char)[] plugin.generate(int, int)"],
2669 ["_D8demangle3fnAFZ3fnBMFZv", "void demangle.fnA().fnB()"],
2670 ["_D8demangle4mainFZ1S3fnCMFZv", "void demangle.main().S.fnC()"],
2671 ["_D8demangle4mainFZ1S3fnDMFZv", "void demangle.main().S.fnD()"],
2672 ["_D8demangle20__T2fnVAiA4i1i2i3i4Z2fnFZv", "void demangle.fn!([1, 2, 3, 4]).fn()"],
2673 ["_D8demangle10__T2fnVi1Z2fnFZv", "void demangle.fn!(1).fn()"],
2674 ["_D8demangle26__T2fnVS8demangle1SS2i1i2Z2fnFZv", "void demangle.fn!(demangle.S(1, 2)).fn()"],
2675 ["_D8demangle13__T2fnVeeNANZ2fnFZv", "void demangle.fn!(real.nan).fn()"],
2676 ["_D8demangle14__T2fnVeeNINFZ2fnFZv", "void demangle.fn!(-real.infinity).fn()"],
2677 ["_D8demangle13__T2fnVeeINFZ2fnFZv", "void demangle.fn!(real.infinity).fn()"],
2678 ["_D8demangle21__T2fnVHiiA2i1i2i3i4Z2fnFZv", "void demangle.fn!([1:2, 3:4]).fn()"],
2679 ["_D8demangle2fnFNgiZNgi", "inout(int) demangle.fn(inout(int))"],
2680 ["_D8demangle29__T2fnVa97Va9Va0Vu257Vw65537Z2fnFZv", "void demangle.fn!('a', '\\t', \\x00, '\\u0101', '\\U00010001').fn()"],
2681 ["_D2gc11gctemplates56__T8mkBitmapTS3std5range13__T4iotaTiTiZ4iotaFiiZ6ResultZ8mkBitmapFNbNiNfPmmZv",
2682 "nothrow @nogc @safe void gc.gctemplates.mkBitmap!(std.range.iota!(int, int).iota(int, int).Result).mkBitmap(ulong*, ulong)"],
2683 ["_D8serenity9persister6Sqlite69__T15SqlitePersisterTS8serenity9persister6Sqlite11__unittest6FZ4TestZ15SqlitePersister12__T7opIndexZ7opIndexMFmZS8serenity9persister6Sqlite11__unittest6FZ4Test",
2684 "serenity.persister.Sqlite.__unittest6().Test serenity.persister.Sqlite.SqlitePersister!(serenity.persister.Sqlite.__unittest6().Test).SqlitePersister.opIndex!().opIndex(ulong)"],
2685 ["_D8bug100274mainFZ5localMFZi","int bug10027.main().local()"],
2686 ["_D8demangle4testFNhG16gZv", "void demangle.test(__vector(byte[16]))"],
2687 ["_D8demangle4testFNhG8sZv", "void demangle.test(__vector(short[8]))"],
2688 ["_D8demangle4testFNhG4iZv", "void demangle.test(__vector(int[4]))"],
2689 ["_D8demangle4testFNhG2lZv", "void demangle.test(__vector(long[2]))"],
2690 ["_D8demangle4testFNhG4fZv", "void demangle.test(__vector(float[4]))"],
2691 ["_D8demangle4testFNhG2dZv", "void demangle.test(__vector(double[2]))"],
2692 ["_D8demangle4testFNhG4fNhG4fZv", "void demangle.test(__vector(float[4]), __vector(float[4]))"],
2693 ["_D8bug1119234__T3fooS23_D8bug111924mainFZ3bariZ3fooMFZv","void bug11192.foo!(bug11192.main().bar).foo()"],
2694 ["_D13libd_demangle12__ModuleInfoZ", "libd_demangle.__ModuleInfo"],
2695 ["_D15TypeInfo_Struct6__vtblZ", "TypeInfo_Struct.__vtbl"],
2696 ["_D3std5stdio12__ModuleInfoZ", "std.stdio.__ModuleInfo"],
2697 ["_D3std6traits15__T8DemangleTkZ8Demangle6__initZ", "std.traits.Demangle!(uint).Demangle.__init"],
2698 ["_D3foo3Bar7__ClassZ", "foo.Bar.__Class"],
2699 ["_D3foo3Bar6__vtblZ", "foo.Bar.__vtbl"],
2700 ["_D3foo3Bar11__interfaceZ", "foo.Bar.__interface"],
2701 ["_D3foo7__arrayZ", "foo.__array"],
2702 ["_D8link657428__T3fooVE8link65746Methodi0Z3fooFZi", "int link6574.foo!(0).foo()"],
2703 ["_D8link657429__T3fooHVE8link65746Methodi0Z3fooFZi", "int link6574.foo!(0).foo()"],
2704 ["_D4test22__T4funcVAyaa3_610a62Z4funcFNaNbNiNmNfZAya", `pure nothrow @nogc @live @safe immutable(char)[] test.func!("a\x0ab").func()`],
2705 ["_D3foo3barFzkZzi", "cent foo.bar(ucent)"],
2706 ["_D5bug145Class3fooMFNlZPv", "scope void* bug14.Class.foo()"],
2707 ["_D5bug145Class3barMFNjZPv", "return void* bug14.Class.bar()"],
2708 ["_D5bug143fooFMPvZPv", "void* bug14.foo(scope void*)"],
2709 ["_D5bug143barFMNkPvZPv", "void* bug14.bar(scope return void*)"],
2710 ["_D3std5range15__T4iotaTtTtTtZ4iotaFtttZ6Result7opIndexMNgFNaNbNiNfmZNgt",
2711 "inout pure nothrow @nogc @safe inout(ushort) std.range.iota!(ushort, ushort, ushort).iota(ushort, ushort, ushort).Result.opIndex(ulong)"],
2712 ["_D3std6format77__T6getNthVAyaa13_696e7465676572207769647468S233std6traits10isIntegralTiTkTkZ6getNthFNaNfkkkZi",
2713 "pure @safe int std.format.getNth!(\"integer width\", std.traits.isIntegral, int, uint, uint).getNth(uint, uint, uint)"],
2714 ["_D3std11parallelism42__T16RoundRobinBufferTDFKAaZvTDxFNaNdNeZbZ16RoundRobinBuffer5primeMFZv",
2715 "void std.parallelism.RoundRobinBuffer!(void delegate(ref char[]), bool delegate() pure @property @trusted const).RoundRobinBuffer.prime()"],
2716 ["_D6mangle__T8fun21753VSQv6S21753S1f_DQBj10__lambda71MFNaNbNiNfZvZQCbQp",
2717 "void function() pure nothrow @nogc @safe mangle.fun21753!(mangle.S21753(mangle.__lambda71())).fun21753"],
2718 // Lname '0'
2719 ["_D3std9algorithm9iteration__T9MapResultSQBmQBlQBe005stripTAAyaZQBi7opSliceMFNaNbNiNfmmZSQDiQDhQDa__TQCtSQDyQDxQDq00QCmTQCjZQDq",
2720 "pure nothrow @nogc @safe std.algorithm.iteration.MapResult!(std.algorithm.iteration.__anonymous.strip, "
2721 ~"immutable(char)[][]).MapResult std.algorithm.iteration.MapResult!(std.algorithm.iteration.strip, immutable(char)[][]).MapResult.opSlice(ulong, ulong)"],
2722
2723 // back references
2724 ["_D4core4stdc5errnoQgFZi", "int core.stdc.errno.errno()"], // identifier back reference
2725 ["_D4testFS10structnameQnZb", "bool test(structname, structname)"], // type back reference
2726 ["_D3std11parallelism__T4TaskS8unittest3cmpTAyaTQeZQBb6__dtorMFNfZv",
2727 "@safe void std.parallelism.Task!(unittest.cmp, immutable(char)[], immutable(char)[]).Task.__dtor()"],
2728 // 1.s.s.foo from https://issues.dlang.org/show_bug.cgi?id=15831
2729 ["_D13testexpansion44__T1sTS13testexpansion8__T1sTiZ1sFiZ6ResultZ1sFS13testexpansion8__T1sTiZ1sFiZ6ResultZ6Result3fooMFNaNfZv",
2730 "pure @safe void testexpansion.s!(testexpansion.s!(int).s(int).Result).s(testexpansion.s!(int).s(int).Result).Result.foo()"],
2731 ["_D13testexpansion__T1sTSQw__TQjTiZQoFiZ6ResultZQBbFQBcZQq3fooMFNaNfZv",
2732 "pure @safe void testexpansion.s!(testexpansion.s!(int).s(int).Result).s(testexpansion.s!(int).s(int).Result).Result.foo()"],
2733 // formerly ambiguous on 'V', template value argument or pascal function
2734 // pascal functions have now been removed (in v2.095.0)
2735 ["_D3std4conv__T7enumRepTyAaTEQBa12experimental9allocator15building_blocks15stats_collector7OptionsVQCti64ZQDnyQDh",
2736 "immutable(char[]) std.conv.enumRep!(immutable(char[]), std.experimental.allocator.building_blocks.stats_collector.Options, 64).enumRep"],
2737 // symbol back reference to location with symbol back reference
2738 ["_D3std12experimental9allocator6common__T10reallocateTSQCaQBzQBo15building_blocks17kernighan_ritchie__T8KRRegionTSQEhQEgQDvQCh14null_allocator13NullAllocatorZQCdZQErFNaNbNiKQEpKAvmZb",
2739 "pure nothrow @nogc bool std.experimental.allocator.common.reallocate!(std.experimental.allocator.building_blocks.kernighan_ritchie.KRRegion!("
2740 ~"std.experimental.allocator.building_blocks.null_allocator.NullAllocator).KRRegion).reallocate(ref "
2741 ~"std.experimental.allocator.building_blocks.kernighan_ritchie.KRRegion!(std.experimental.allocator.building_blocks.null_allocator.NullAllocator).KRRegion, ref void[], ulong)"],
2742 ["_D3std9exception__T11doesPointToTASQBh5regex8internal2ir10NamedGroupTQBkTvZQCeFNaNbNiNeKxASQDlQCeQCbQBvQBvKxQtZb",
2743 "pure nothrow @nogc @trusted bool std.exception.doesPointTo!(std.regex.internal.ir.NamedGroup[], "
2744 ~"std.regex.internal.ir.NamedGroup[], void).doesPointTo(ref const(std.regex.internal.ir.NamedGroup[]), ref const(std.regex.internal.ir.NamedGroup[]))"],
2745 ["_D3std9algorithm9iteration__T14SplitterResultS_DQBu3uni7isWhiteFNaNbNiNfwZbTAyaZQBz9__xtoHashFNbNeKxSQDvQDuQDn__TQDgS_DQEnQCtQCsQCnTQCeZQEdZm",
2746 "nothrow @trusted ulong std.algorithm.iteration.SplitterResult!(std.uni.isWhite(dchar), immutable(char)[]).SplitterResult."
2747 ~"__xtoHash(ref const(std.algorithm.iteration.SplitterResult!(std.uni.isWhite, immutable(char)[]).SplitterResult))"],
2748 ["_D3std8typecons__T7TypedefTCQBaQz19__unittestL6513_208FNfZ7MyClassVQBonVAyanZQCh6__ctorMFNaNbNcNiNfQCuZSQDyQDx__TQDrTQDmVQDqnVQCcnZQEj",
2749 "pure nothrow ref @nogc @safe std.typecons.Typedef!(std.typecons.__unittestL6513_208().MyClass, null, null).Typedef "
2750 ~"std.typecons.Typedef!(std.typecons.__unittestL6513_208().MyClass, null, null).Typedef.__ctor(std.typecons.__unittestL6513_208().MyClass)"],
2751 ["_D3std6getopt__TQkTAyaTDFNaNbNiNfQoZvTQtTDQsZQBnFNfKAQBiQBlQBkQBrQyZSQCpQCo12GetoptResult",
2752 "@safe std.getopt.GetoptResult std.getopt.getopt!(immutable(char)[], void delegate(immutable(char)[]) pure nothrow @nogc @safe, "
2753 ~"immutable(char)[], void delegate(immutable(char)[]) pure nothrow @nogc @safe)."
2754 ~"getopt(ref immutable(char)[][], immutable(char)[], void delegate(immutable(char)[]) pure nothrow @nogc @safe, "
2755 ~"immutable(char)[], void delegate(immutable(char)[]) pure nothrow @nogc @safe)"],
2756 ["_D3std5regex8internal9kickstart__T7ShiftOrTaZQl11ShiftThread__T3setS_DQCqQCpQCmQCg__TQBzTaZQCfQBv10setInvMaskMFNaNbNiNfkkZvZQCjMFNaNfwZv",
2757 "pure @safe void std.regex.internal.kickstart.ShiftOr!(char).ShiftOr.ShiftThread.set!(std.regex.internal.kickstart.ShiftOr!(char).ShiftOr.ShiftThread.setInvMask(uint, uint)).set(dchar)"],
2758 ["_D3std5stdio4File__T8lockImplX10LockFileExTykZQBaMFmmykZi", // C function as template alias parameter
2759 "int std.stdio.File.lockImpl!(LockFileEx, immutable(uint)).lockImpl(ulong, ulong, immutable(uint))"],
2760 // back reference for type in template AA parameter value
2761 ["_D3std9algorithm9iteration__T12FilterResultSQBq8typecons__T5TupleTiVAyaa1_61TiVQla1_62TiVQva1_63ZQBm__T6renameVHiQBtA2i0a1_63i2a1_61ZQBeMFNcZ9__lambda1TAiZQEw9__xtoHashFNbNeKxSQGsQGrQGk__TQGdSQHiQFs__TQFmTiVQFja1_61TiVQFua1_62TiVQGfa1_63ZQGx__TQFlVQFhA2i0a1_63i2a1_61ZQGjMFNcZQFfTQEyZQJvZm",
2762 `nothrow @trusted ulong std.algorithm.iteration.FilterResult!(std.typecons.Tuple!(int, "a", int, "b", int, "c").`
2763 ~`Tuple.rename!([0:"c", 2:"a"]).rename().__lambda1, int[]).FilterResult.__xtoHash(ref const(std.algorithm.iteration.`
2764 ~`FilterResult!(std.typecons.Tuple!(int, "a", int, "b", int, "c").Tuple.rename!([0:"c", 2:"a"]).rename().__lambda1, int[]).FilterResult))`],
2765
2766 ["_D4test4rrs1FKPiZv", "void test.rrs1(ref int*)"],
2767 ["_D4test4rrs1FMNkJPiZv", "void test.rrs1(scope return out int*)"],
2768 ["_D4test4rrs1FMNkKPiZv", "void test.rrs1(scope return ref int*)"],
2769 ["_D4test4rrs1FNkJPiZv", "void test.rrs1(return out int*)"],
2770 ["_D4test4rrs1FNkKPiZv", "void test.rrs1(return ref int*)"],
2771 ["_D4test4rrs1FNkMJPiZv", "void test.rrs1(return scope out int*)"],
2772 ["_D4test4rrs1FNkMKPiZv", "void test.rrs1(return scope ref int*)"],
2773 ["_D4test4rrs1FNkMPiZv", "void test.rrs1(return scope int*)"],
2774
2775 // `scope` and `return` combinations
2776 ["_D3foo3Foo3barMNgFNjNlNfZNgPv", "inout return scope @safe inout(void*) foo.Foo.bar()"],
2777 ["_D3foo3FooQiMNgFNlNfZv", "inout scope @safe void foo.Foo.foo()"],
2778 ["_D3foo3Foo4foorMNgFNjNfZv", "inout return @safe void foo.Foo.foor()"],
2779 ["_D3foo3Foo3rabMNgFNlNjNfZv", "inout scope return @safe void foo.Foo.rab()"],
2780
2781 // Hex float digit overflow
2782 ["_D3foo__T1fVdeFA3D0FBFB72A3C33FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", "_D3foo__T1fVdeFA3D0FBFB72A3C33FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"],
2783 ];
2784
2785
2786 template staticIota(int x)
2787 {
2788 template Seq(T...){ alias Seq = T; }
2789
2790 static if (x == 0)
2791 alias staticIota = Seq!();
2792 else
2793 alias staticIota = Seq!(staticIota!(x - 1), x - 1);
2794 }
2795 foreach ( i, name; table )
2796 {
2797 auto r = demangle( name[0] );
2798 assert( r == name[1],
2799 "demangled `" ~ name[0] ~ "` as `" ~ r ~ "` but expected `" ~ name[1] ~ "`");
2800 }
2801 foreach ( i; staticIota!(table.length) )
2802 {
2803 enum r = demangle( table[i][0] );
2804 static assert( r == table[i][1],
2805 "demangled `" ~ table[i][0] ~ "` as `" ~ r ~ "` but expected `" ~ table[i][1] ~ "`");
2806 }
2807
2808 {
2809 // https://issues.dlang.org/show_bug.cgi?id=18531
2810 auto symbol = `_D3std3uni__T6toCaseS_DQvQt12toLowerIndexFNaNbNiNewZtVii1043S_DQCjQCi10toLowerTabFNaNbNiNemZwSQDo5ascii7toLowerTAyaZQDzFNaNeQmZ14__foreachbody2MFNaNeKmKwZ14__foreachbody3MFNaNeKwZi`;
2811 auto demangled = `pure @trusted int std.uni.toCase!(std.uni.toLowerIndex(dchar), 1043, std.uni.toLowerTab(ulong), std.ascii.toLower, immutable(char)[]).toCase(immutable(char)[]).__foreachbody2(ref ulong, ref dchar).__foreachbody3(ref dchar)`;
2812 auto dst = new char[200];
2813 auto ret = demangle( symbol, dst);
2814 assert( ret == demangled );
2815 }
2816 }
2817
2818 unittest
2819 {
2820 // https://issues.dlang.org/show_bug.cgi?id=18300
2821 string s = demangle.mangleof;
2822 foreach (i; 1..77)
2823 {
2824 char[] buf = new char[i];
2825 auto ds = demangle(s, buf);
2826 assert(ds == "pure nothrow @safe char[] core.demangle.demangle(scope return const(char)[], scope return char[], extern (C) char* function(const(char*), char*, ulong*, int*) pure nothrow @trusted*)" ||
2827 ds == "pure nothrow @safe char[] core.demangle.demangle(return scope const(char)[], return scope char[], extern (C) char* function(const(char*), char*, ulong*, int*) pure nothrow @trusted*)" ||
2828 ds == "pure nothrow @safe char[] core.demangle.demangle(scope return const(char)[], scope return char[], extern (C) char* function(const(char*), char*, uint*, int*) pure nothrow @trusted*)" ||
2829 ds == "pure nothrow @safe char[] core.demangle.demangle(return scope const(char)[], return scope char[], extern (C) char* function(const(char*), char*, uint*, int*) pure nothrow @trusted*)", ds);
2830 }
2831 }
2832
2833 unittest
2834 {
2835 // https://issues.dlang.org/show_bug.cgi?id=18300
2836 string s = "_D1";
2837 string expected = "int ";
2838 foreach (_; 0..10_000)
2839 {
2840 s ~= "a1";
2841 expected ~= "a.";
2842 }
2843 s ~= "FiZi";
2844 expected ~= "F";
2845 assert(s.demangle == expected);
2846
2847 // https://issues.dlang.org/show_bug.cgi?id=23562
2848 assert(demangle("_Zv") == "_Zv");
2849 }
2850
2851 // https://issues.dlang.org/show_bug.cgi?id=22235
2852 unittest
2853 {
2854 enum parent = __MODULE__ ~ '.' ~ __traits(identifier, __traits(parent, {}));
2855
2856 static noreturn abort() { assert(false); }
2857 assert(demangle(abort.mangleof) == "pure nothrow @nogc @safe noreturn " ~ parent ~ "().abort()");
2858
2859 static void accept(noreturn) {}
2860 assert(demangle(accept.mangleof) == "pure nothrow @nogc @safe void " ~ parent ~ "().accept(noreturn)");
2861
2862 static void templ(T)(T, T) {}
2863 assert(demangle(templ!noreturn.mangleof) == "pure nothrow @nogc @safe void " ~ parent ~ "().templ!(noreturn).templ(noreturn, noreturn)");
2864
2865 static struct S(T) {}
2866 static void aggr(S!noreturn) { assert(0); }
2867 assert(demangle(aggr.mangleof) == "pure nothrow @nogc @safe void " ~ parent ~ "().aggr(" ~ parent ~ "().S!(noreturn).S)");
2868 }
2869
2870 /*
2871 * Expand an OMF, DMD-generated compressed identifier into its full form
2872 *
2873 * This function only has a visible effect for OMF binaries (Win32),
2874 * as compression is otherwise not used.
2875 *
2876 * See_Also: `compiler/src/dmd/backend/compress.d`
2877 */
2878 string decodeDmdString( const(char)[] ln, ref size_t p ) nothrow pure @safe
2879 {
2880 string s;
2881 uint zlen, zpos;
2882
2883 // decompress symbol
2884 while ( p < ln.length )
2885 {
2886 int ch = cast(ubyte) ln[p++];
2887 if ( (ch & 0xc0) == 0xc0 )
2888 {
2889 zlen = (ch & 0x7) + 1;
2890 zpos = ((ch >> 3) & 7) + 1; // + zlen;
2891 if ( zpos > s.length )
2892 break;
2893 s ~= s[$ - zpos .. $ - zpos + zlen];
2894 }
2895 else if ( ch >= 0x80 )
2896 {
2897 if ( p >= ln.length )
2898 break;
2899 int ch2 = cast(ubyte) ln[p++];
2900 zlen = (ch2 & 0x7f) | ((ch & 0x38) << 4);
2901 if ( p >= ln.length )
2902 break;
2903 int ch3 = cast(ubyte) ln[p++];
2904 zpos = (ch3 & 0x7f) | ((ch & 7) << 7);
2905 if ( zpos > s.length )
2906 break;
2907 s ~= s[$ - zpos .. $ - zpos + zlen];
2908 }
2909 else if ( Demangle!().isAlpha(cast(char)ch) || Demangle!().isDigit(cast(char)ch) || ch == '_' )
2910 s ~= cast(char) ch;
2911 else
2912 {
2913 p--;
2914 break;
2915 }
2916 }
2917 return s;
2918 }
2919
2920 // locally purified for internal use here only
2921 extern (C) private
2922 {
2923 pure @trusted @nogc nothrow pragma(mangle, "fakePureReprintReal") void pureReprintReal(char[] nptr);
2924
2925 void fakePureReprintReal(char[] nptr)
2926 {
2927 import core.stdc.stdlib : strtold;
2928 import core.stdc.stdio : snprintf;
2929 import core.stdc.errno : errno;
2930
2931 const err = errno;
2932 real val = strtold(nptr.ptr, null);
2933 snprintf(nptr.ptr, nptr.length, "%#Lg", val);
2934 errno = err;
2935 }
2936 }
2937
2938 private struct ManglingFlagInfo
2939 {
2940 /// The flag value to use
2941 ushort flag;
2942
2943 /// Human-readable representation
2944 string value;
2945 }
2946
2947 private enum TypeCtor : ushort {
2948 None = 0,
2949 //// 'x'
2950 Const = (1 << 1),
2951 /// 'y'
2952 Immutable = (1 << 2),
2953 /// 'O'
2954 Shared = (1 << 3),
2955 ///
2956 InOut = (1 << 4),
2957 }
2958
2959 private immutable ManglingFlagInfo[] typeCtors = [
2960 ManglingFlagInfo(TypeCtor.Immutable, "immutable"),
2961 ManglingFlagInfo(TypeCtor.Shared, "shared"),
2962 ManglingFlagInfo(TypeCtor.InOut, "inout"),
2963 ManglingFlagInfo(TypeCtor.Const, "const"),
2964 ];
2965
2966 private enum FuncAttributes : ushort {
2967 None = 0,
2968 //// 'a'
2969 Pure = (1 << 1),
2970 //// 'b'
2971 Nothrow = (1 << 2),
2972 //// 'c'
2973 Ref = (1 << 3),
2974 //// 'd'
2975 Property = (1 << 4),
2976 //// 'e'
2977 Trusted = (1 << 5),
2978 //// 'f'
2979 Safe = (1 << 6),
2980 //// 'i'
2981 NoGC = (1 << 7),
2982 //// 'j'
2983 Return = (1 << 8),
2984 //// 'l'
2985 Scope = (1 << 9),
2986 //// 'm'
2987 Live = (1 << 10),
2988
2989 /// Their order matter
2990 ReturnScope = (1 << 11),
2991 ScopeReturn = (1 << 12),
2992 }
2993
2994 // The order in which we process is the same as in compiler/dmd/src/dmangle.d
2995 private immutable ManglingFlagInfo[] funcAttrs = [
2996 ManglingFlagInfo(FuncAttributes.Pure, "pure"),
2997 ManglingFlagInfo(FuncAttributes.Nothrow, "nothrow"),
2998 ManglingFlagInfo(FuncAttributes.Ref, "ref"),
2999 ManglingFlagInfo(FuncAttributes.Property, "@property"),
3000 ManglingFlagInfo(FuncAttributes.NoGC, "@nogc"),
3001
3002 ManglingFlagInfo(FuncAttributes.ReturnScope, "return scope"),
3003 ManglingFlagInfo(FuncAttributes.ScopeReturn, "scope return"),
3004
3005 ManglingFlagInfo(FuncAttributes.Return, "return"),
3006 ManglingFlagInfo(FuncAttributes.Scope, "scope"),
3007
3008 ManglingFlagInfo(FuncAttributes.Live, "@live"),
3009 ManglingFlagInfo(FuncAttributes.Trusted, "@trusted"),
3010 ManglingFlagInfo(FuncAttributes.Safe, "@safe"),
3011 ];
3012
3013 private string toStringConsume (immutable ManglingFlagInfo[] infos, ref ushort base)
3014 @safe pure nothrow @nogc
3015 {
3016 foreach (const ref info; infos)
3017 {
3018 if ((base & info.flag) == info.flag)
3019 {
3020 base &= ~info.flag;
3021 return info.value;
3022 }
3023 }
3024 return null;
3025 }
3026
3027 private shared CXX_DEMANGLER __cxa_demangle;
3028
3029 /**
3030 * Returns:
3031 * a CXX_DEMANGLER if a C++ stdlib is loaded
3032 */
3033
3034 CXX_DEMANGLER getCXXDemangler() nothrow @trusted
3035 {
3036 import core.atomic : atomicLoad, atomicStore;
3037 if (__cxa_demangle is null)
3038 version (Posix)
3039 {
3040 import core.sys.posix.dlfcn : dlsym;
3041 version (DragonFlyBSD) import core.sys.dragonflybsd.dlfcn : RTLD_DEFAULT;
3042 version (FreeBSD) import core.sys.freebsd.dlfcn : RTLD_DEFAULT;
3043 version (linux) import core.sys.linux.dlfcn : RTLD_DEFAULT;
3044 version (NetBSD) import core.sys.netbsd.dlfcn : RTLD_DEFAULT;
3045 version (OpenBSD) import core.sys.openbsd.dlfcn : RTLD_DEFAULT;
3046 version (Darwin) import core.sys.darwin.dlfcn : RTLD_DEFAULT;
3047 version (Solaris) import core.sys.solaris.dlfcn : RTLD_DEFAULT;
3048
3049 if (auto found = cast(CXX_DEMANGLER) dlsym(RTLD_DEFAULT, "__cxa_demangle"))
3050 atomicStore(__cxa_demangle, found);
3051 }
3052
3053 if (__cxa_demangle is null)
3054 {
3055 static extern(C) char* _(const char* mangled_name, char* output_buffer,
3056 size_t* length, int* status) nothrow pure @trusted
3057 {
3058 *status = -1;
3059 return null;
3060 }
3061 atomicStore(__cxa_demangle, &_);
3062 }
3063
3064 return atomicLoad(__cxa_demangle);
3065 }
3066
3067 /**
3068 * Demangles C++ mangled names. If it is not a C++ mangled name, it
3069 * returns its argument name.
3070 *
3071 * Params:
3072 * buf = The string to demangle.
3073 * __cxa_demangle = C++ demangler
3074 * dst = An optional destination buffer.
3075 *
3076 * Returns:
3077 * The demangled name or the original string if the name is not a mangled
3078 * C++ name.
3079 */
3080 private char[] demangleCXX(return scope const(char)[] buf, CXX_DEMANGLER __cxa_demangle, return scope char[] dst = null,) nothrow pure @trusted
3081 {
3082 char[] c_string = dst; // temporarily use dst buffer if possible
3083 c_string.length = buf.length + 1;
3084 c_string[0 .. buf.length] = buf[0 .. buf.length];
3085 c_string[buf.length] = '\0';
3086
3087 int status;
3088 size_t demangled_length;
3089 auto demangled = __cxa_demangle(&c_string[0], null, &demangled_length, &status);
3090 scope (exit) {
3091 import core.memory;
3092 pureFree(cast(void*) demangled);
3093 }
3094 if (status == 0)
3095 {
3096 dst.length = demangled_length;
3097 dst[] = demangled[0 .. demangled_length];
3098 return dst;
3099 }
3100
3101 dst.length = buf.length;
3102 dst[] = buf[];
3103 return dst;
3104 }
3105
3106 private struct Buffer
3107 {
3108 enum size_t minSize = 4000;
3109
3110 @safe pure:
3111
3112 private char[] dst;
3113 private size_t len;
3114
3115 public alias opDollar = len;
3116
3117 public size_t length () const scope @safe pure nothrow @nogc
3118 {
3119 return this.len;
3120 }
3121
3122 public BufSlice opSlice (size_t from, size_t to)
3123 return scope @safe pure nothrow @nogc
3124 {
3125 return bslice(from, to);
3126 }
3127
3128 static bool contains(scope const(char)[] a, scope const BufSlice b) @safe nothrow
3129 {
3130 return
3131 b.from < a.length &&
3132 b.to <= a.length;
3133 }
3134
3135 char[] copyInput(scope const(char)[] buf)
3136 return scope nothrow
3137 {
3138 if (dst.length < buf.length)
3139 dst.length = buf.length;
3140 char[] r = dst[0 .. buf.length];
3141 r[] = buf[];
3142 return r;
3143 }
3144
3145 private void checkAndStretchBuf(size_t len_to_add) scope nothrow
3146 {
3147 const required = len + len_to_add;
3148
3149 if (required > dst.length)
3150 dst.length = dst.length + len_to_add;
3151 }
3152
3153 // move val to the end of the dst buffer
3154 BufSlice shift(scope const BufSlice val) return scope nothrow
3155 {
3156 version (DigitalMars) pragma(inline, false); // tame dmd inliner
3157
3158 if (val.length)
3159 {
3160 const ptrdiff_t s = val.from;
3161 const size_t f = len;
3162
3163 assert(contains( dst[0 .. len], val ),
3164 "\ndst=\""~dst[0 .. len]~"\"\n"~
3165 "val=\""~val.getSlice~"\"\n"
3166 );
3167
3168 checkAndStretchBuf(val.length);
3169
3170 // store value temporary over len index
3171 dst[len .. len + val.length] = val.getSlice();
3172
3173 // shift all chars including temporary saved above
3174 // if buf was allocated above it will be leave for further usage
3175 for (size_t p = s; p < f; p++)
3176 dst[p] = dst[p + val.length];
3177
3178 return bslice(len - val.length, len);
3179 }
3180
3181 return bslice_empty;
3182 }
3183
3184 // remove val from dst buffer
3185 void remove(scope BufSlice val) scope nothrow
3186 {
3187 version (DigitalMars) pragma(inline, false); // tame dmd inliner
3188
3189 if ( val.length )
3190 {
3191 assert( contains( dst[0 .. len], val ) );
3192
3193 assert( len >= val.length && len <= dst.length );
3194 len -= val.length;
3195 for (size_t p = val.from; p < len; p++)
3196 dst[p] = dst[p + val.length];
3197 }
3198 }
3199
3200 void append(scope const(char)[] val) scope nothrow
3201 {
3202 version (DigitalMars) pragma(inline, false); // tame dmd inliner
3203
3204 if (val.length)
3205 {
3206 if ( !dst.length )
3207 dst.length = minSize;
3208
3209 debug(info) printf( "appending (%.*s)\n", cast(int) val.length, val.ptr );
3210
3211 checkAndStretchBuf(val.length);
3212
3213 // data is already not in place?
3214 if ( &dst[len] != &val[0] )
3215 dst[len .. len + val.length] = val[];
3216
3217 len += val.length;
3218 }
3219 }
3220
3221 @nogc:
3222
3223 private scope bslice(size_t from, size_t to) nothrow
3224 {
3225 return BufSlice(dst, from, to);
3226 }
3227
3228 private static scope bslice_empty() nothrow
3229 {
3230 return BufSlice.init;
3231 }
3232 }
3233
3234 private struct BufSlice
3235 {
3236 char[] buf;
3237 size_t from;
3238 size_t to;
3239
3240 @safe:
3241 pure:
3242 nothrow:
3243
3244 @disable this();
3245
3246 this(return scope char[] buf) scope nothrow @nogc
3247 {
3248 this(buf, 0, 0);
3249 }
3250
3251 this(return scope char[] buf, size_t from, size_t to, bool lastArgIsLen = false) scope nothrow @nogc
3252 {
3253 this.buf = buf;
3254 this.from = from;
3255
3256 if (lastArgIsLen)
3257 this.to = from + to;
3258 else
3259 this.to = to;
3260 }
3261
3262 invariant()
3263 {
3264 if (buf is null)
3265 {
3266 assert(from == 0);
3267 assert(to == 0);
3268 }
3269
3270 assert(from <= to);
3271 }
3272
3273 auto getSlice() inout nothrow scope { return buf[from .. to]; }
3274 size_t length() const scope { return to - from; }
3275 }