"Fossies" - the Fresh Open Source Software Archive

Member "dmd2/src/druntime/src/core/demangle.d" (20 Nov 2020, 78860 Bytes) of package /linux/misc/dmd.2.094.2.linux.tar.xz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) D source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file.

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