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