2 * The demangle module converts mangled D symbols to a representation similar
3 * to what would have existed in code.
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)
10 * Source: $(DRUNTIMESRC core/_demangle.d)
21 else version (WatchOS)
24 debug(trace) import core.stdc.stdio : printf;
25 debug(info) import core.stdc.stdio : printf;
27 extern (C) alias CXX_DEMANGLER = char* function (const char* mangled_name,
30 int* status) nothrow pure @trusted;
32 private struct NoHooks
35 // static bool parseLName(ref Demangle);
36 // static char[] parseType(ref Demangle, char[])
39 private struct Demangle(Hooks = NoHooks)
41 // NOTE: This implementation currently only works with mangled function
42 // names as they exist in an object file. Type names mangled via
43 // the .mangleof property are effectively incomplete as far as the
44 // ABI is concerned and so are not considered to be mangled symbol
47 // NOTE: This implementation builds the demangled buffer in place by
48 // writing data as it is decoded and then rearranging it later as
49 // needed. In practice this results in very little data movement,
50 // and the performance cost is more than offset by the gain from
51 // not allocating dynamic memory to assemble the name piecemeal.
53 // If the destination buffer is too small, parsing will restart
54 // with a larger buffer. Since this generally means only one
55 // allocation during the course of a parsing run, this is still
56 // faster than assembling the result piecemeal.
59 enum AddType { no, yes }
62 this( return scope const(char)[] buf_, return scope char[] dst_ = null )
64 this( buf_, AddType.yes, dst_ );
68 this( return scope const(char)[] buf_, AddType addType_, return scope char[] dst_ = null )
75 const(char)[] buf = null;
78 size_t brp = 0; // current back reference pos
79 AddType addType = AddType.yes;
83 //////////////////////////////////////////////////////////////////////////
84 // Type Testing and Conversion
85 //////////////////////////////////////////////////////////////////////////
88 static bool isAlpha( char val )
90 return ('a' <= val && 'z' >= val) ||
91 ('A' <= val && 'Z' >= val) ||
92 (0x80 & val); // treat all unicode as alphabetic
96 static bool isDigit( char val ) nothrow
98 return '0' <= val && '9' >= val;
102 static bool isHexDigit( char val )
104 return ('0' <= val && '9' >= val) ||
105 ('a' <= val && 'f' >= val) ||
106 ('A' <= val && 'F' >= val);
110 static ubyte ascii2hex( out bool errStatus, char val ) nothrow
112 if (val >= 'a' && val <= 'f')
113 return cast(ubyte)(val - 'a' + 10);
114 if (val >= 'A' && val <= 'F')
115 return cast(ubyte)(val - 'A' + 10);
116 if (val >= '0' && val <= '9')
117 return cast(ubyte)(val - '0');
123 BufSlice shift(scope const BufSlice val) return scope
126 return dst.bslice_empty;
127 return dst.shift(val);
130 void putComma(size_t n)
132 version (DigitalMars) pragma(inline, false);
137 void put(char c) return scope
143 void put(scope BufSlice val) return scope
148 void put(scope const(char)[] val) return scope nothrow
156 void putAsHex( size_t val, int width = 0 )
158 import core.internal.string;
160 UnsignedStringBuf buf = void;
162 auto s = unsignedToTempString!16(val, buf);
163 int slen = cast(int)s.length;
166 foreach (i; slen .. width)
173 void pad( const(char)[] val )
183 void silent( out bool err_status, void delegate(out bool err_status) pure @safe nothrow dg ) nothrow
185 debug(trace) printf( "silent+\n" );
186 debug(trace) scope(success) printf( "silent-\n" );
194 //////////////////////////////////////////////////////////////////////////
196 //////////////////////////////////////////////////////////////////////////
198 @property bool empty()
200 return pos >= buf.length;
203 @property char front()
205 if ( pos < buf.length )
210 char peek( size_t n )
212 if ( pos + n < buf.length )
218 bool test( char val ) nothrow
223 void popFront() nothrow
225 if ( pos++ >= buf.length )
230 void popFront(int i) nothrow
237 bool match( char val ) nothrow
248 bool match( const(char)[] val ) nothrow
250 foreach (char e; val )
264 bool isSymbolNameFront(out bool errStatus) nothrow
267 if ( isDigit( val ) || val == '_' )
272 // check the back reference encoding after 'Q'
276 // invalid back reference
281 return isDigit( val ); // identifier ref
284 // return the first character at the back reference
285 char peekBackref() nothrow
287 assert( front == 'Q' );
288 auto n = decodeBackref!1();
290 return 0; // invalid back reference
295 size_t decodeBackref(size_t peekAt = 0)() nothrow
299 for (size_t p; ; p++)
302 static if (peekAt > 0)
304 t = peek(peekAt + p);
311 if (t < 'A' || t > 'Z')
313 if (t < 'a' || t > 'z')
314 return 0; // invalid back reference
316 n = base * n + t - 'a';
319 n = base * n + t - 'A';
323 //////////////////////////////////////////////////////////////////////////
324 // Parsing Implementation
325 //////////////////////////////////////////////////////////////////////////
333 const(char)[] sliceNumber() return scope
335 debug(trace) printf( "sliceNumber+\n" );
336 debug(trace) scope(success) printf( "sliceNumber-\n" );
343 if (t >= '0' && t <= '9')
346 return buf[beg .. pos];
351 size_t decodeNumber(out bool errStatus) scope nothrow
353 debug(trace) printf( "decodeNumber+\n" );
354 debug(trace) scope(success) printf( "decodeNumber-\n" );
356 return decodeNumber( errStatus, sliceNumber() );
359 size_t decodeNumber( out bool errStatus, scope const(char)[] num ) scope nothrow
361 debug(trace) printf( "decodeNumber+\n" );
362 debug(trace) scope(success) printf( "decodeNumber-\n" );
368 import core.checkedint : mulu, addu;
370 bool overflow = false;
371 val = mulu(val, 10, overflow);
372 val = addu(val, c - '0', overflow);
382 void parseReal(out bool errStatus) scope nothrow
384 debug(trace) printf( "parseReal+\n" );
385 debug(trace) scope(success) printf( "parseReal-\n" );
387 char[64] tbuf = void;
400 put( "real.infinity" );
410 put( "-real.infinity" );
425 errStatus = !isHexDigit( front );
427 return; // Expected hex digit
429 tbuf[tlen++] = front;
433 while ( isHexDigit( front ) )
435 if (tlen >= tbuf.length)
436 return onError(); // Too many hex float digits
437 tbuf[tlen++] = front;
452 while ( isDigit( front ) )
454 tbuf[tlen++] = front;
459 debug(info) printf( "got (%s)\n", tbuf.ptr );
460 pureReprintReal( tbuf[] );
461 debug(info) printf( "converted (%.*s)\n", cast(int) tlen, tbuf.ptr );
462 put( tbuf[0 .. tlen] );
486 void parseLName(out string errMsg) scope nothrow
488 debug(trace) printf( "parseLName+\n" );
489 debug(trace) scope(success) printf( "parseLName-\n" );
491 static if (__traits(hasMember, Hooks, "parseLName"))
493 auto r = hooks.parseLName(errMsg, this);
499 void error(string msg)
506 // back reference to LName
509 size_t n = decodeBackref();
510 if (!n || n > refPos)
511 return error("Invalid LName back reference");
516 scope(exit) pos = savePos;
524 auto n = decodeNumber(err_flag);
526 return error("Number overflow");
530 put( "__anonymous" );
533 if ( n > buf.length || n > buf.length - pos )
534 return error("LName must be at least 1 character");
536 if ( '_' != front && !isAlpha( front ) )
537 return error("Invalid character in LName");
539 foreach (char e; buf[pos + 1 .. pos + n] )
541 if ( '_' != e && !isAlpha( e ) && !isDigit( e ) )
542 return error("Invalid character in LName");
545 put( buf[pos .. pos + n] );
624 CallConvention FuncAttrs Arguments ArgClose Type
725 BufSlice parseType(out bool errStatus) return scope nothrow
727 static immutable string[23] primitives = [
753 static if (__traits(hasMember, Hooks, "parseType"))
755 auto n = hooks.parseType(errStatus, this, null);
757 return dst.bslice_empty;
760 return BufSlice(n, 0, n.length);
763 debug(trace) printf( "parseType+\n" );
764 debug(trace) scope(success) printf( "parseType-\n" );
765 auto beg = dst.length;
768 BufSlice parseBackrefType(out string errStatus, scope BufSlice delegate(bool err_flag) pure @safe nothrow parseDg) pure @safe nothrow
772 errStatus = "recursive back reference";
773 return dst.bslice_empty;
778 auto n = decodeBackref();
779 if (n == 0 || n > pos)
781 errStatus = "invalid back reference";
782 return dst.bslice_empty;
786 return dst.bslice_empty;
789 scope(success) { pos = savePos; brp = saveBrp; }
794 auto ret = parseDg(err_flag);
797 errStatus = "parseDg error";
798 return dst.bslice_empty;
804 // call parseType() and return error if occured
805 enum parseTypeOrF = "parseType(errStatus); if (errStatus) return dst.bslice_empty;";
809 case 'Q': // Type back reference
811 auto r = parseBackrefType(errMsg, (e_flag) => parseType(e_flag));
813 return dst.bslice_empty;
815 case 'O': // Shared (O Type)
820 return dst[beg .. $];
821 case 'x': // Const (x Type)
826 return dst[beg .. $];
827 case 'y': // Immutable (y Type)
832 return dst[beg .. $];
837 case 'n': // Noreturn
840 return dst[beg .. $];
841 case 'g': // Wild (Ng Type)
843 // TODO: Anything needed here?
847 return dst[beg .. $];
848 case 'h': // TypeVector (Nh Type)
853 return dst[beg .. $];
856 return dst.bslice_empty;
858 case 'A': // TypeArray (A Type)
862 return dst[beg .. $];
863 case 'G': // TypeStaticArray (G Number Type)
865 auto num = sliceNumber();
870 return dst[beg .. $];
871 case 'H': // TypeAssocArray (H Type Type)
874 auto tx = parseType(errStatus);
876 return dst.bslice_empty;
881 return dst[beg .. $];
882 case 'P': // TypePointer (P Type)
886 return dst[beg .. $];
887 case 'F': case 'U': case 'W': case 'V': case 'R': // TypeFunction
888 auto r = parseTypeFunction(errStatus);
890 return dst.bslice_empty;
892 case 'C': // TypeClass (C LName)
893 case 'S': // TypeStruct (S LName)
894 case 'E': // TypeEnum (E LName)
895 case 'T': // TypeTypedef (T LName)
897 parseQualifiedName(errStatus);
899 return dst.bslice_empty;
900 return dst[beg .. $];
901 case 'D': // TypeDelegate (D TypeFunction)
903 auto modifiers = parseModifier();
907 auto r = parseBackrefType(errMsg, (e_flag) => parseTypeFunction(e_flag, IsDelegate.yes));
909 return dst.bslice_empty;
914 parseTypeFunction(errStatus, IsDelegate.yes);
916 return dst.bslice_empty;
921 // write modifiers behind the function arguments
922 while (auto str = typeCtors.toStringConsume(modifiers))
928 return dst[beg .. $];
929 case 'n': // TypeNone (n)
931 // TODO: Anything needed here?
932 return dst[beg .. $];
933 case 'B': // TypeTuple (B Number Arguments)
935 // TODO: Handle this.
936 return dst[beg .. $];
937 case 'Z': // Internal symbol
938 // This 'type' is used for untyped internal symbols, i.e.:
946 return dst[beg .. $];
948 if (t >= 'a' && t <= 'w')
951 put( primitives[cast(size_t)(t - 'a')] );
952 return dst[beg .. $];
962 return dst[beg .. $];
966 return dst[beg .. $];
969 return dst.bslice_empty;
973 return dst.bslice_empty;
980 CallConvention FuncAttrs Arguments ArgClose Type
1036 M Argument2 // scope
1045 X // variadic T t,...) style
1046 Y // variadic T t...) style
1049 void parseCallConvention(out bool errStatus) nothrow
1059 put( "extern (C) " );
1061 case 'W': // Windows
1063 put( "extern (Windows) " );
1067 put( "extern (C++) " );
1074 /// Returns: Flags of `TypeCtor`
1075 ushort parseModifier()
1077 TypeCtor res = TypeCtor.None;
1082 return TypeCtor.Immutable;
1085 res |= TypeCtor.Shared;
1090 return TypeCtor.Shared;
1092 if (peek( 1 ) != 'g')
1096 res |= TypeCtor.InOut;
1102 res |= TypeCtor.Const;
1104 default: return TypeCtor.None;
1108 ushort parseFuncAttr(out bool errStatus) nothrow
1112 while ('N' == front)
1117 case 'a': // FuncAttrPure
1119 result |= FuncAttributes.Pure;
1121 case 'b': // FuncAttrNoThrow
1123 result |= FuncAttributes.Nothrow;
1125 case 'c': // FuncAttrRef
1127 result |= FuncAttributes.Ref;
1129 case 'd': // FuncAttrProperty
1131 result |= FuncAttributes.Property;
1133 case 'e': // FuncAttrTrusted
1135 result |= FuncAttributes.Trusted;
1137 case 'f': // FuncAttrSafe
1139 result |= FuncAttributes.Safe;
1145 // NOTE: The inout parameter type is represented as "Ng".
1146 // The vector parameter type is represented as "Nh".
1147 // The return parameter type is represented as "Nk".
1148 // The noreturn parameter type is represented as "Nn".
1149 // These make it look like a FuncAttr, but infact
1150 // if we see these, then we know we're really in
1151 // the parameter list. Rewind and break.
1154 case 'i': // FuncAttrNogc
1156 result |= FuncAttributes.NoGC;
1158 case 'j': // FuncAttrReturn
1160 if (this.peek(0) == 'N' && this.peek(1) == 'l')
1162 result |= FuncAttributes.ReturnScope;
1166 result |= FuncAttributes.Return;
1169 case 'l': // FuncAttrScope
1171 if (this.peek(0) == 'N' && this.peek(1) == 'j')
1173 result |= FuncAttributes.ScopeReturn;
1177 result |= FuncAttributes.Scope;
1180 case 'm': // FuncAttrLive
1182 result |= FuncAttributes.Live;
1192 void parseFuncArguments(out bool errStatus) scope nothrow
1195 for ( size_t n = 0; true; n++ )
1197 debug(info) printf( "tok (%c)\n", front );
1200 case 'X': // ArgClose (variadic T t...) style)
1204 case 'Y': // ArgClose (variadic T t,...) style)
1208 case 'Z': // ArgClose (not variadic)
1216 /* Do special return, scope, ref, out combinations
1219 if ( 'M' == front && peek(1) == 'N' && peek(2) == 'k')
1224 put("scope return out "); // MNkJ
1229 put("scope return ref "); // MNkK
1233 else if ('N' == front && peek(1) == 'k')
1238 put("return out "); // NkJ
1243 put("return ref "); // NkK
1251 put("return scope out "); // NkMJ
1256 put("return scope ref "); // NkMK
1261 put("return scope "); // NkM
1276 if ( 'k' == front ) // Return (Nk Parameter2)
1285 // call parseType() and return error if occured
1286 enum parseTypeOrF = "parseType(errStatus); if (errStatus) return;";
1290 case 'I': // in (I Type)
1295 mixin(parseTypeOrF);
1297 case 'K': // ref (K Type)
1300 mixin(parseTypeOrF);
1302 case 'J': // out (J Type)
1305 mixin(parseTypeOrF);
1307 case 'L': // lazy (L Type)
1310 mixin(parseTypeOrF);
1313 mixin(parseTypeOrF);
1318 enum IsDelegate { no, yes }
1322 CallConvention FuncAttrs Arguments ArgClose Type
1324 BufSlice parseTypeFunction(out bool errStatus, IsDelegate isdg = IsDelegate.no) return scope nothrow
1326 debug(trace) printf( "parseTypeFunction+\n" );
1327 debug(trace) scope(success) printf( "parseTypeFunction-\n" );
1328 auto beg = dst.length;
1330 parseCallConvention(errStatus);
1332 return dst.bslice_empty;
1334 auto attributes = parseFuncAttr(errStatus);
1336 return dst.bslice_empty;
1338 auto argbeg = dst.length;
1339 put(IsDelegate.yes == isdg ? "delegate" : "function");
1341 parseFuncArguments(errStatus);
1343 return dst.bslice_empty;
1347 // write function attributes behind arguments
1348 while (auto str = funcAttrs.toStringConsume(attributes))
1355 // A function / delegate return type is located at the end of its mangling
1356 // Write it in order, then shift it back to 'code order'
1357 // e.g. `delegate(int) @safedouble ' => 'double delegate(int) @safe'
1359 auto retbeg = dst.length;
1360 parseType(errStatus);
1362 return dst.bslice_empty;
1364 shift(dst[argbeg .. retbeg]);
1367 return dst[beg .. $];
1370 static bool isCallConvention( char ch )
1374 case 'F', 'U', 'V', 'W', 'R':
1388 c HexFloat c HexFloat
1395 N HexDigits P Exponent
1396 HexDigits P Exponent
1416 void parseValue(out bool errStatus) scope nothrow
1418 parseValue(errStatus, dst.bslice_empty);
1421 void parseValue(out bool errStatus, scope BufSlice name, char type = '\0' ) scope nothrow
1423 debug(trace) printf( "parseValue+\n" );
1424 debug(trace) scope(success) printf( "parseValue-\n" );
1431 // printf( "*** %c\n", front );
1440 if ('0' > front || '9' < front)
1441 return onError(); // Number expected
1443 case '0': .. case '9':
1444 parseIntegerValue( errStatus, name, type );
1449 parseIntegerValue( errStatus, name, type );
1453 parseReal(errStatus);
1457 parseReal(errStatus);
1463 parseReal(errStatus);
1468 case 'a': case 'w': case 'd':
1471 auto n = decodeNumber(errStatus);
1479 auto a = ascii2hex( errStatus, front );
1484 auto b = ascii2hex( errStatus, front );
1489 auto v = cast(char)((a << 4) | b);
1490 if (' ' <= v && v <= '~') // ASCII printable
1505 // NOTE: This is kind of a hack. An associative array literal
1506 // [1:2, 3:4] is represented as HiiA2i1i2i3i4, so the type
1507 // is "Hii" and the value is "A2i1i2i3i4". Thus the only
1508 // way to determine that this is an AA value rather than an
1509 // array value is for the caller to supply the type char.
1510 // Hopefully, this will change so that the value is
1511 // "H2i1i2i3i4", rendering this unnecesary.
1514 // A Number Value...
1515 // An array literal. Value is repeated Number times.
1518 auto n = decodeNumber(errStatus);
1521 foreach ( i; 0 .. n )
1524 parseValue(errStatus);
1532 // H Number Value...
1533 // An associative array literal. Value is repeated 2*Number times.
1536 auto n = decodeNumber(errStatus);
1539 foreach ( i; 0 .. n )
1542 parseValue(errStatus);
1546 parseValue(errStatus);
1553 // S Number Value...
1554 // A struct literal. Value is repeated Number times.
1559 auto n = decodeNumber(errStatus);
1562 foreach ( i; 0 .. n )
1565 parseValue(errStatus);
1573 // A function literal symbol
1575 parseMangledName(errStatus, false, 1);
1582 void parseIntegerValue( out bool errStatus, scope BufSlice name, char type = '\0' ) scope nothrow
1584 debug(trace) printf( "parseIntegerValue+\n" );
1585 debug(trace) scope(success) printf( "parseIntegerValue-\n" );
1593 auto val = sliceNumber();
1594 auto num = decodeNumber( errStatus, val );
1632 if ( num >= 0x20 && num < 0x7F )
1635 put( cast(char)num );
1658 auto d = decodeNumber(errStatus);
1661 put( d ? "true" : "false" );
1663 case 'h', 't', 'k': // ubyte, ushort, uint
1664 put( sliceNumber() );
1668 put( sliceNumber() );
1672 put( sliceNumber() );
1676 put( sliceNumber() );
1685 TemplateArg TemplateArgs
1694 S Number_opt QualifiedName
1695 X ExternallyMangledName
1697 void parseTemplateArgs(out bool errStatus) scope nothrow
1699 debug(trace) printf( "parseTemplateArgs+\n" );
1700 debug(trace) scope(success) printf( "parseTemplateArgs-\n" );
1703 for ( size_t n = 0; true; n++ )
1713 parseType(errStatus);
1720 // NOTE: In the few instances where the type is actually
1721 // desired in the output it should precede the value
1722 // generated by parseValue, so it is safe to simply
1723 // decrement len and let put/append do its thing.
1724 char t = front; // peek at type for parseValue
1730 // invalid back reference
1735 BufSlice name = dst.bslice_empty;
1736 silent( errStatus, delegate void(out bool e_flg) nothrow { name = parseType(e_flg); } );
1739 parseValue( errStatus, name, t );
1747 if ( mayBeMangledNameArg() )
1749 auto l = dst.length;
1753 debug(trace) printf( "may be mangled name arg\n" );
1755 if (parseMangledNameArg())
1762 debug(trace) printf( "not a mangled name arg\n" );
1765 if ( isDigit( front ) && isDigit( peek( 1 ) ) )
1767 // ambiguity: length followed by qualified name (starting with number)
1768 // try all possible pairs of numbers
1769 auto qlen = decodeNumber(errStatus);
1773 qlen /= 10; // last digit needed for QualifiedName
1775 auto l = dst.length;
1781 parseQualifiedName(errStatus);
1785 if ( pos == p + qlen )
1789 qlen /= 10; // retry with one digit less
1796 parseQualifiedName(errStatus);
1817 bool mayBeMangledNameArg() nothrow
1819 debug(trace) printf( "mayBeMangledNameArg+\n" );
1820 debug(trace) scope(success) printf( "mayBeMangledNameArg-\n" );
1824 scope(exit) pos = p;
1826 if ( isDigit( buf[pos] ) )
1828 auto n = decodeNumber(errStatus);
1830 return !errStatus && n >= 4 &&
1831 pos < buf.length && '_' == buf[pos++] &&
1832 pos < buf.length && 'D' == buf[pos++] &&
1833 isDigit( buf[pos] );
1837 const isSNF = isSymbolNameFront(errStatus);
1839 return !errStatus &&
1840 pos < buf.length && '_' == buf[pos++] &&
1841 pos < buf.length && 'D' == buf[pos++] &&
1846 bool parseMangledNameArg() nothrow
1848 debug(trace) printf( "parseMangledNameArg+\n" );
1849 debug(trace) scope(success) printf( "parseMangledNameArg-\n" );
1854 if ( isDigit( front ) )
1856 n = decodeNumber(errStatus);
1862 parseMangledName(errStatus, false, n );
1868 TemplateInstanceName:
1869 Number __T LName TemplateArgs Z
1871 void parseTemplateInstanceName(out bool errStatus, bool hasNumber) scope nothrow
1873 debug(trace) printf( "parseTemplateInstanceName+\n" );
1874 debug(trace) scope(success) printf( "parseTemplateInstanceName-\n" );
1889 n = decodeNumber(errStatus);
1895 errStatus = !match( "__T" );
1902 if (errMsg !is null)
1908 parseTemplateArgs(errStatus);
1915 if ( hasNumber && pos - beg != n )
1917 // Template name length mismatch
1925 bool mayBeTemplateInstanceName() scope nothrow
1927 debug(trace) printf( "mayBeTemplateInstanceName+\n" );
1928 debug(trace) scope(success) printf( "mayBeTemplateInstanceName-\n" );
1931 scope(exit) pos = p;
1934 auto n = decodeNumber(errStatus);
1939 pos < buf.length && '_' == buf[pos++] &&
1940 pos < buf.length && '_' == buf[pos++] &&
1941 pos < buf.length && 'T' == buf[pos++];
1948 TemplateInstanceName
1950 void parseSymbolName(out bool errStatus) scope nothrow
1952 debug(trace) printf( "parseSymbolName+\n" );
1953 debug(trace) scope(success) printf( "parseSymbolName-\n" );
1956 // TemplateInstanceName -> Number "__T"
1960 // no length encoding for templates for new mangling
1961 parseTemplateInstanceName(errStatus, false);
1964 case '0': .. case '9':
1965 if ( mayBeTemplateInstanceName() )
1967 auto t = dst.length;
1969 debug(trace) printf( "may be template instance name\n" );
1970 parseTemplateInstanceName(errStatus, true);
1975 debug(trace) printf( "not a template instance name\n" );
1983 errStatus = errMsg !is null;
1990 // parse optional function arguments as part of a symbol name, i.e without return type
1991 // if keepAttr, the calling convention and function attributes are not discarded, but returned
1992 BufSlice parseFunctionTypeNoReturn( bool keepAttr = false ) return scope nothrow
1994 // try to demangle a function, in case we are pointing to some function local
1996 auto prevlen = dst.length;
2001 // do not emit "needs this"
2003 auto modifiers = parseModifier();
2004 while (auto str = typeCtors.toStringConsume(modifiers))
2010 if ( isCallConvention( front ) )
2012 BufSlice attr = dst.bslice_empty;
2013 // we don't want calling convention and attributes in the qualified name
2015 parseCallConvention(errStatus);
2018 auto attributes = parseFuncAttr(errStatus);
2022 while (auto str = funcAttrs.toStringConsume(attributes))
2027 attr = dst[prevlen .. $];
2031 parseFuncArguments(errStatus);
2033 return dst.bslice_empty;
2039 // not part of a qualified name, so back up
2045 return dst.bslice_empty;
2051 SymbolName QualifiedName
2053 void parseQualifiedName(out bool errStatus) return scope nothrow
2055 debug(trace) printf( "parseQualifiedName+\n" );
2056 debug(trace) scope(success) printf( "parseQualifiedName-\n" );
2059 bool is_sym_name_front;
2066 parseSymbolName(errStatus);
2070 parseFunctionTypeNoReturn();
2072 is_sym_name_front = isSymbolNameFront(errStatus);
2076 } while ( is_sym_name_front );
2082 _D QualifiedName Type
2083 _D QualifiedName M Type
2085 void parseMangledName( out bool errStatus, bool displayType, size_t n = 0 ) scope nothrow
2087 debug(trace) printf( "parseMangledName+\n" );
2088 debug(trace) scope(success) printf( "parseMangledName-\n" );
2089 BufSlice name = dst.bslice_empty;
2094 errStatus = !match( 'D' );
2100 size_t beg = dst.length;
2101 size_t nameEnd = dst.length;
2102 BufSlice attr = dst.bslice_empty;
2103 bool is_sym_name_front;
2108 dst.remove(attr); // dump attributes of parent symbols
2109 if (beg != dst.length)
2112 parseSymbolName(errStatus);
2116 nameEnd = dst.length;
2117 attr = parseFunctionTypeNoReturn( displayType );
2119 is_sym_name_front = isSymbolNameFront(errStatus);
2122 } while (is_sym_name_front);
2126 attr = shift( attr );
2127 nameEnd = dst.length - attr.length; // name includes function arguments
2129 name = dst[beg .. nameEnd];
2131 debug(info) printf( "name (%.*s)\n", cast(int) name.length, name.ptr );
2133 popFront(); // has 'this' pointer
2135 auto lastlen = dst.length;
2136 auto type = parseType(errStatus);
2144 // sort (name,attr,type) -> (attr,type,name)
2150 assert( attr.length == 0 );
2153 if ( pos >= buf.length || (n != 0 && pos >= end) )
2158 case 'T': // terminators when used as template alias parameter
2170 void parseMangledName(out bool errStatus) nothrow
2172 parseMangledName(errStatus, AddType.yes == addType);
2175 char[] doDemangle(alias FUNC)() return scope nothrow
2179 debug(info) printf( "demangle(%.*s)\n", cast(int) buf.length, buf.ptr );
2185 return dst[0 .. $].getSlice;
2189 debug(info) printf( "error" );
2191 return dst.copyInput(buf);
2196 char[] demangleName() nothrow
2198 return doDemangle!parseMangledName();
2201 char[] demangleType() nothrow
2203 return doDemangle!parseType();
2209 * Demangles D/C++ mangled names. If it is not a D/C++ mangled name, it
2210 * returns its argument name.
2213 * buf = The string to demangle.
2214 * dst = An optional destination buffer.
2215 * __cxa_demangle = optional C++ demangler
2218 * The demangled name or the original string if the name is not a mangled
2221 char[] demangle(return scope const(char)[] buf, return scope char[] dst = null, CXX_DEMANGLER __cxa_demangle = null) nothrow pure @safe
2223 if (__cxa_demangle && buf.length > 2 && buf[0..2] == "_Z")
2224 return demangleCXX(buf, __cxa_demangle, dst);
2225 auto d = Demangle!()(buf, dst);
2226 // fast path (avoiding throwing & catching exception) for obvious
2227 // non-D mangled names
2228 if (buf.length < 2 || !(buf[0] == 'D' || buf[0..2] == "_D"))
2229 return d.dst.copyInput(buf);
2230 return d.demangleName();
2235 * Demangles a D mangled type.
2238 * buf = The string to demangle.
2239 * dst = An optional destination buffer.
2242 * The demangled type name or the original string if the name is not a
2245 char[] demangleType( const(char)[] buf, char[] dst = null ) nothrow pure @safe
2247 auto d = Demangle!()(buf, dst);
2248 return d.demangleType();
2252 * reencode a mangled symbol name that might include duplicate occurrences
2253 * of the same identifier by replacing all but the first occurence with
2257 * mangled = The mangled string representing the type
2260 * The mangled name with deduplicated identifiers
2262 char[] reencodeMangled(return scope const(char)[] mangled) nothrow pure @safe
2264 static struct PrependHooks
2268 size_t[const(char)[]] idpos; // identifier positions
2270 static struct Replacement
2272 size_t pos; // postion in original mangled string
2273 size_t respos; // postion in result string
2275 Replacement [] replacements;
2278 size_t positionInResult(size_t pos) scope
2280 foreach_reverse (r; replacements)
2282 return r.respos + pos - r.pos;
2286 alias Remangle = Demangle!(PrependHooks);
2288 void flushPosition(ref Remangle d) scope
2290 if (lastpos < d.pos)
2292 result ~= d.buf[lastpos .. d.pos];
2294 else if (lastpos > d.pos)
2296 // roll back to earlier position
2297 while (replacements.length > 0 && replacements[$-1].pos > d.pos)
2298 replacements = replacements[0 .. $-1];
2300 if (replacements.length > 0)
2301 result.length = replacements[$-1].respos + d.pos - replacements[$-1].pos;
2303 result.length = d.pos;
2307 bool parseLName(out string errMsg, scope ref Remangle d) scope @trusted nothrow
2309 bool error(string msg)
2316 auto reslen = result.length;
2317 auto refpos = d.pos;
2322 scope(exit) result.length = reslen; // remove all intermediate additions
2323 // only support identifier back references
2325 size_t n = d.decodeBackref();
2326 if (!n || n > refpos)
2327 return error("invalid back reference");
2329 auto savepos = d.pos;
2330 scope(exit) d.pos = savepos;
2331 size_t srcpos = refpos - n;
2334 auto idlen = d.decodeNumber(errStatus);
2336 return error("invalid number");
2338 if (d.pos + idlen > d.buf.length)
2339 return error("invalid back reference");
2341 auto id = d.buf[d.pos .. d.pos + idlen];
2342 auto pid = id in idpos;
2344 return error("invalid back reference");
2346 npos = positionInResult(*pid);
2348 encodeBackref(reslen - npos);
2349 const pos = d.pos; // work around issues.dlang.org/show_bug.cgi?id=20675
2350 replacements ~= Replacement(pos, result.length);
2355 auto n = d.decodeNumber(errStatus);
2357 return error("invalid number");
2359 if (!n || n > d.buf.length || n > d.buf.length - d.pos)
2360 return error("LName too short or too long");
2362 auto id = d.buf[d.pos .. d.pos + n];
2364 if (auto pid = id in idpos)
2366 size_t npos = positionInResult(*pid);
2367 result.length = reslen;
2368 encodeBackref(reslen - npos);
2369 const pos = d.pos; // work around issues.dlang.org/show_bug.cgi?id=20675
2370 replacements ~= Replacement(pos, result.length);
2374 idpos[id] = refpos; //! scope variable id used as AA key, makes this function @trusted
2375 result ~= d.buf[refpos .. d.pos];
2382 char[] parseType( out bool errStatus, ref Remangle d, char[] name ) return scope nothrow
2389 auto refPos = d.pos;
2391 auto n = d.decodeBackref();
2392 if (n == 0 || n > refPos)
2394 // invalid back reference
2399 size_t npos = positionInResult(refPos - n);
2400 size_t reslen = result.length;
2401 encodeBackref(reslen - npos);
2404 return result[reslen .. $]; // anything but null
2407 void encodeBackref(size_t relpos) scope
2412 while (relpos >= div * base)
2416 auto dig = (relpos / div);
2417 result ~= cast(char)('A' + dig);
2418 relpos -= dig * div;
2421 result ~= cast(char)('a' + relpos);
2425 auto d = Demangle!(PrependHooks)(mangled, null);
2426 d.hooks = PrependHooks();
2427 d.mute = true; // no demangled output
2430 d.parseMangledName(errStatus);
2433 // error cannot occur
2437 if (d.hooks.lastpos < d.pos)
2438 d.hooks.result ~= d.buf[d.hooks.lastpos .. d.pos];
2439 return d.hooks.result;
2443 * Mangles a D symbol.
2446 * T = The type of the symbol.
2447 * fqn = The fully qualified name of the symbol.
2448 * dst = An optional destination buffer.
2451 * The mangled name for a symbols of type T and the given fully
2454 char[] mangle(T)(return scope const(char)[] fqn, return scope char[] dst = null) @safe pure nothrow
2456 import core.internal.string : numDigits, unsignedToTempString;
2458 static struct DotSplitter
2463 @property bool empty() const { return !s.length; }
2465 @property const(char)[] front() const return scope
2467 immutable i = indexOfDot();
2468 return i == -1 ? s[0 .. $] : s[0 .. i];
2471 void popFront() scope
2473 immutable i = indexOfDot();
2474 s = i == -1 ? s[$ .. $] : s[i+1 .. $];
2477 private ptrdiff_t indexOfDot() const scope
2479 foreach (i, c; s) if (c == '.') return i;
2484 size_t len = "_D".length;
2485 foreach (comp; DotSplitter(fqn))
2486 len += numDigits(comp.length) + comp.length;
2487 len += T.mangleof.length;
2488 if (dst.length < len) dst.length = len;
2490 size_t i = "_D".length;
2492 foreach (comp; DotSplitter(fqn))
2494 const ndigits = numDigits(comp.length);
2495 unsignedToTempString(comp.length, dst[i .. i + ndigits]);
2497 dst[i .. i + comp.length] = comp[];
2500 dst[i .. i + T.mangleof.length] = T.mangleof[];
2501 i += T.mangleof.length;
2503 static if (hasTypeBackRef)
2504 return reencodeMangled(dst[0 .. i]);
2511 @safe pure nothrow unittest
2513 assert(mangle!int("a.b") == "_D1a1bi");
2514 assert(mangle!(char[])("test.foo") == "_D4test3fooAa");
2515 assert(mangle!(int function(int))("a.b") == "_D1a1bPFiZi");
2518 @safe pure nothrow unittest
2520 static assert(mangle!int("a.b") == "_D1a1bi");
2522 auto buf = new char[](10);
2523 buf = mangle!int("a.b", buf);
2524 assert(buf == "_D1a1bi");
2525 buf = mangle!(char[])("test.foo", buf);
2526 assert(buf == "_D4test3fooAa");
2527 buf = mangle!(real delegate(int))("modµ.dg");
2528 assert(buf == "_D5modµ2dgDFiZe", buf);
2533 * Mangles a D function.
2536 * T = function pointer type.
2537 * fqn = The fully qualified name of the symbol.
2538 * dst = An optional destination buffer.
2541 * The mangled name for a function with function pointer type T and
2542 * the given fully qualified name.
2544 char[] mangleFunc(T:FT*, FT)(return scope const(char)[] fqn, return scope char[] dst = null) @safe pure nothrow if (is(FT == function))
2546 static if (isExternD!FT)
2548 return mangle!FT(fqn, dst);
2550 else static if (hasPlainMangling!FT)
2552 dst.length = fqn.length;
2556 else static if (isExternCPP!FT)
2558 static assert(0, "Can't mangle extern(C++) functions.");
2562 static assert(0, "Can't mangle function with unknown linkage ("~FT.stringof~").");
2566 private enum hasTypeBackRef = (int function(void**,void**)).mangleof[$-4 .. $] == "QdZi";
2568 @safe pure nothrow unittest
2570 assert(mangleFunc!(int function(int))("a.b") == "_D1a1bFiZi");
2571 static if (hasTypeBackRef)
2573 assert(mangleFunc!(int function(Object))("object.Object.opEquals") == "_D6object6Object8opEqualsFCQsZi");
2574 assert(mangleFunc!(int function(Object, Object))("object.Object.opEquals") == "_D6object6Object8opEqualsFCQsQdZi");
2578 auto mngl = mangleFunc!(int function(Object))("object.Object.opEquals");
2579 assert(mngl == "_D6object6Object8opEqualsFC6ObjectZi");
2580 auto remngl = reencodeMangled(mngl);
2581 assert(remngl == "_D6object6Object8opEqualsFCQsZi");
2583 // trigger back tracking with ambiguity on '__T', template or identifier
2584 assert(reencodeMangled("_D3std4conv4conv7__T3std4convi") == "_D3std4convQf7__T3stdQpi");
2587 @safe pure nothrow unittest
2589 int function(lazy int[], ...) fp;
2590 assert(mangle!(typeof(fp))("demangle.test") == "_D8demangle4testPFLAiYi");
2591 assert(mangle!(typeof(*fp))("demangle.test") == "_D8demangle4testFLAiYi");
2594 private template isExternD(FT) if (is(FT == function))
2596 enum isExternD = __traits(getLinkage, FT) == "D";
2599 private template isExternCPP(FT) if (is(FT == function))
2601 enum isExternCPP = __traits(getLinkage, FT) == "C++";
2604 private template hasPlainMangling(FT) if (is(FT == function))
2606 enum lnk = __traits(getLinkage, FT);
2608 enum hasPlainMangling = lnk == "C" || lnk == "Windows" || lnk == "System";
2611 @safe pure nothrow unittest
2613 static extern(D) void fooD();
2614 static extern(C) void fooC();
2615 static extern(Windows) void fooW();
2616 static extern(C++) void fooCPP();
2618 bool check(FT)(bool isD, bool isCPP, bool isPlain)
2620 return isExternD!FT == isD && isExternCPP!FT == isCPP &&
2621 hasPlainMangling!FT == isPlain;
2623 static assert(check!(typeof(fooD))(true, false, false));
2624 static assert(check!(typeof(fooC))(false, false, true));
2625 static assert(check!(typeof(fooW))(false, false, true));
2626 static assert(check!(typeof(fooCPP))(false, true, false));
2628 static assert(__traits(compiles, mangleFunc!(typeof(&fooD))("")));
2629 static assert(__traits(compiles, mangleFunc!(typeof(&fooC))("")));
2630 static assert(__traits(compiles, mangleFunc!(typeof(&fooW))("")));
2631 static assert(!__traits(compiles, mangleFunc!(typeof(&fooCPP))("")));
2635 * C name mangling is done by adding a prefix on some platforms.
2638 enum string cPrefix = "_";
2639 else version (Darwin)
2640 enum string cPrefix = "_";
2642 enum string cPrefix = "";
2644 @safe pure nothrow unittest
2646 immutable string[2][] table =
2648 ["printf", "printf"],
2651 ["_D3fooQeFIAyaZv", "void foo.foo(in immutable(char)[])" ],
2652 ["_D3barQeFIKAyaZv", "void bar.bar(in ref immutable(char)[])" ],
2653 ["_D4test3fooAa", "char[] test.foo"],
2654 ["_D8demangle8demangleFAaZAa", "char[] demangle.demangle(char[])"],
2655 ["_D6object6Object8opEqualsFC6ObjectZi", "int object.Object.opEquals(Object)"],
2656 ["_D4test2dgDFiYd", "double delegate(int, ...) test.dg"],
2657 ["_D4test2dgDxFNfiYd", "double delegate(int, ...) @safe const test.dg"],
2658 //["_D4test58__T9factorialVde67666666666666860140VG5aa5_68656c6c6fVPvnZ9factorialf", ""],
2659 //["_D4test101__T9factorialVde67666666666666860140Vrc9a999999999999d9014000000000000000c00040VG5aa5_68656c6c6fVPvnZ9factorialf", ""],
2660 ["_D4test34__T3barVG3uw3_616263VG3wd3_646566Z1xi", "int test.bar!(\"abc\"w, \"def\"d).x"],
2661 ["_D8demangle4testFLC6ObjectLDFLiZiZi", "int demangle.test(lazy Object, lazy int delegate(lazy int))"],
2662 ["_D8demangle4testFAiXi", "int demangle.test(int[]...)"],
2663 ["_D8demangle4testFAiYi", "int demangle.test(int[], ...)"],
2664 ["_D8demangle4testFLAiXi", "int demangle.test(lazy int[]...)"],
2665 ["_D8demangle4testFLAiYi", "int demangle.test(lazy int[], ...)"],
2666 ["_D6plugin8generateFiiZAya", "immutable(char)[] plugin.generate(int, int)"],
2667 ["_D6plugin8generateFiiZAxa", "const(char)[] plugin.generate(int, int)"],
2668 ["_D6plugin8generateFiiZAOa", "shared(char)[] plugin.generate(int, int)"],
2669 ["_D8demangle3fnAFZ3fnBMFZv", "void demangle.fnA().fnB()"],
2670 ["_D8demangle4mainFZ1S3fnCMFZv", "void demangle.main().S.fnC()"],
2671 ["_D8demangle4mainFZ1S3fnDMFZv", "void demangle.main().S.fnD()"],
2672 ["_D8demangle20__T2fnVAiA4i1i2i3i4Z2fnFZv", "void demangle.fn!([1, 2, 3, 4]).fn()"],
2673 ["_D8demangle10__T2fnVi1Z2fnFZv", "void demangle.fn!(1).fn()"],
2674 ["_D8demangle26__T2fnVS8demangle1SS2i1i2Z2fnFZv", "void demangle.fn!(demangle.S(1, 2)).fn()"],
2675 ["_D8demangle13__T2fnVeeNANZ2fnFZv", "void demangle.fn!(real.nan).fn()"],
2676 ["_D8demangle14__T2fnVeeNINFZ2fnFZv", "void demangle.fn!(-real.infinity).fn()"],
2677 ["_D8demangle13__T2fnVeeINFZ2fnFZv", "void demangle.fn!(real.infinity).fn()"],
2678 ["_D8demangle21__T2fnVHiiA2i1i2i3i4Z2fnFZv", "void demangle.fn!([1:2, 3:4]).fn()"],
2679 ["_D8demangle2fnFNgiZNgi", "inout(int) demangle.fn(inout(int))"],
2680 ["_D8demangle29__T2fnVa97Va9Va0Vu257Vw65537Z2fnFZv", "void demangle.fn!('a', '\\t', \\x00, '\\u0101', '\\U00010001').fn()"],
2681 ["_D2gc11gctemplates56__T8mkBitmapTS3std5range13__T4iotaTiTiZ4iotaFiiZ6ResultZ8mkBitmapFNbNiNfPmmZv",
2682 "nothrow @nogc @safe void gc.gctemplates.mkBitmap!(std.range.iota!(int, int).iota(int, int).Result).mkBitmap(ulong*, ulong)"],
2683 ["_D8serenity9persister6Sqlite69__T15SqlitePersisterTS8serenity9persister6Sqlite11__unittest6FZ4TestZ15SqlitePersister12__T7opIndexZ7opIndexMFmZS8serenity9persister6Sqlite11__unittest6FZ4Test",
2684 "serenity.persister.Sqlite.__unittest6().Test serenity.persister.Sqlite.SqlitePersister!(serenity.persister.Sqlite.__unittest6().Test).SqlitePersister.opIndex!().opIndex(ulong)"],
2685 ["_D8bug100274mainFZ5localMFZi","int bug10027.main().local()"],
2686 ["_D8demangle4testFNhG16gZv", "void demangle.test(__vector(byte[16]))"],
2687 ["_D8demangle4testFNhG8sZv", "void demangle.test(__vector(short[8]))"],
2688 ["_D8demangle4testFNhG4iZv", "void demangle.test(__vector(int[4]))"],
2689 ["_D8demangle4testFNhG2lZv", "void demangle.test(__vector(long[2]))"],
2690 ["_D8demangle4testFNhG4fZv", "void demangle.test(__vector(float[4]))"],
2691 ["_D8demangle4testFNhG2dZv", "void demangle.test(__vector(double[2]))"],
2692 ["_D8demangle4testFNhG4fNhG4fZv", "void demangle.test(__vector(float[4]), __vector(float[4]))"],
2693 ["_D8bug1119234__T3fooS23_D8bug111924mainFZ3bariZ3fooMFZv","void bug11192.foo!(bug11192.main().bar).foo()"],
2694 ["_D13libd_demangle12__ModuleInfoZ", "libd_demangle.__ModuleInfo"],
2695 ["_D15TypeInfo_Struct6__vtblZ", "TypeInfo_Struct.__vtbl"],
2696 ["_D3std5stdio12__ModuleInfoZ", "std.stdio.__ModuleInfo"],
2697 ["_D3std6traits15__T8DemangleTkZ8Demangle6__initZ", "std.traits.Demangle!(uint).Demangle.__init"],
2698 ["_D3foo3Bar7__ClassZ", "foo.Bar.__Class"],
2699 ["_D3foo3Bar6__vtblZ", "foo.Bar.__vtbl"],
2700 ["_D3foo3Bar11__interfaceZ", "foo.Bar.__interface"],
2701 ["_D3foo7__arrayZ", "foo.__array"],
2702 ["_D8link657428__T3fooVE8link65746Methodi0Z3fooFZi", "int link6574.foo!(0).foo()"],
2703 ["_D8link657429__T3fooHVE8link65746Methodi0Z3fooFZi", "int link6574.foo!(0).foo()"],
2704 ["_D4test22__T4funcVAyaa3_610a62Z4funcFNaNbNiNmNfZAya", `pure nothrow @nogc @live @safe immutable(char)[] test.func!("a\x0ab").func()`],
2705 ["_D3foo3barFzkZzi", "cent foo.bar(ucent)"],
2706 ["_D5bug145Class3fooMFNlZPv", "scope void* bug14.Class.foo()"],
2707 ["_D5bug145Class3barMFNjZPv", "return void* bug14.Class.bar()"],
2708 ["_D5bug143fooFMPvZPv", "void* bug14.foo(scope void*)"],
2709 ["_D5bug143barFMNkPvZPv", "void* bug14.bar(scope return void*)"],
2710 ["_D3std5range15__T4iotaTtTtTtZ4iotaFtttZ6Result7opIndexMNgFNaNbNiNfmZNgt",
2711 "inout pure nothrow @nogc @safe inout(ushort) std.range.iota!(ushort, ushort, ushort).iota(ushort, ushort, ushort).Result.opIndex(ulong)"],
2712 ["_D3std6format77__T6getNthVAyaa13_696e7465676572207769647468S233std6traits10isIntegralTiTkTkZ6getNthFNaNfkkkZi",
2713 "pure @safe int std.format.getNth!(\"integer width\", std.traits.isIntegral, int, uint, uint).getNth(uint, uint, uint)"],
2714 ["_D3std11parallelism42__T16RoundRobinBufferTDFKAaZvTDxFNaNdNeZbZ16RoundRobinBuffer5primeMFZv",
2715 "void std.parallelism.RoundRobinBuffer!(void delegate(ref char[]), bool delegate() pure @property @trusted const).RoundRobinBuffer.prime()"],
2716 ["_D6mangle__T8fun21753VSQv6S21753S1f_DQBj10__lambda71MFNaNbNiNfZvZQCbQp",
2717 "void function() pure nothrow @nogc @safe mangle.fun21753!(mangle.S21753(mangle.__lambda71())).fun21753"],
2719 ["_D3std9algorithm9iteration__T9MapResultSQBmQBlQBe005stripTAAyaZQBi7opSliceMFNaNbNiNfmmZSQDiQDhQDa__TQCtSQDyQDxQDq00QCmTQCjZQDq",
2720 "pure nothrow @nogc @safe std.algorithm.iteration.MapResult!(std.algorithm.iteration.__anonymous.strip, "
2721 ~"immutable(char)[][]).MapResult std.algorithm.iteration.MapResult!(std.algorithm.iteration.strip, immutable(char)[][]).MapResult.opSlice(ulong, ulong)"],
2724 ["_D4core4stdc5errnoQgFZi", "int core.stdc.errno.errno()"], // identifier back reference
2725 ["_D4testFS10structnameQnZb", "bool test(structname, structname)"], // type back reference
2726 ["_D3std11parallelism__T4TaskS8unittest3cmpTAyaTQeZQBb6__dtorMFNfZv",
2727 "@safe void std.parallelism.Task!(unittest.cmp, immutable(char)[], immutable(char)[]).Task.__dtor()"],
2728 // 1.s.s.foo from https://issues.dlang.org/show_bug.cgi?id=15831
2729 ["_D13testexpansion44__T1sTS13testexpansion8__T1sTiZ1sFiZ6ResultZ1sFS13testexpansion8__T1sTiZ1sFiZ6ResultZ6Result3fooMFNaNfZv",
2730 "pure @safe void testexpansion.s!(testexpansion.s!(int).s(int).Result).s(testexpansion.s!(int).s(int).Result).Result.foo()"],
2731 ["_D13testexpansion__T1sTSQw__TQjTiZQoFiZ6ResultZQBbFQBcZQq3fooMFNaNfZv",
2732 "pure @safe void testexpansion.s!(testexpansion.s!(int).s(int).Result).s(testexpansion.s!(int).s(int).Result).Result.foo()"],
2733 // formerly ambiguous on 'V', template value argument or pascal function
2734 // pascal functions have now been removed (in v2.095.0)
2735 ["_D3std4conv__T7enumRepTyAaTEQBa12experimental9allocator15building_blocks15stats_collector7OptionsVQCti64ZQDnyQDh",
2736 "immutable(char[]) std.conv.enumRep!(immutable(char[]), std.experimental.allocator.building_blocks.stats_collector.Options, 64).enumRep"],
2737 // symbol back reference to location with symbol back reference
2738 ["_D3std12experimental9allocator6common__T10reallocateTSQCaQBzQBo15building_blocks17kernighan_ritchie__T8KRRegionTSQEhQEgQDvQCh14null_allocator13NullAllocatorZQCdZQErFNaNbNiKQEpKAvmZb",
2739 "pure nothrow @nogc bool std.experimental.allocator.common.reallocate!(std.experimental.allocator.building_blocks.kernighan_ritchie.KRRegion!("
2740 ~"std.experimental.allocator.building_blocks.null_allocator.NullAllocator).KRRegion).reallocate(ref "
2741 ~"std.experimental.allocator.building_blocks.kernighan_ritchie.KRRegion!(std.experimental.allocator.building_blocks.null_allocator.NullAllocator).KRRegion, ref void[], ulong)"],
2742 ["_D3std9exception__T11doesPointToTASQBh5regex8internal2ir10NamedGroupTQBkTvZQCeFNaNbNiNeKxASQDlQCeQCbQBvQBvKxQtZb",
2743 "pure nothrow @nogc @trusted bool std.exception.doesPointTo!(std.regex.internal.ir.NamedGroup[], "
2744 ~"std.regex.internal.ir.NamedGroup[], void).doesPointTo(ref const(std.regex.internal.ir.NamedGroup[]), ref const(std.regex.internal.ir.NamedGroup[]))"],
2745 ["_D3std9algorithm9iteration__T14SplitterResultS_DQBu3uni7isWhiteFNaNbNiNfwZbTAyaZQBz9__xtoHashFNbNeKxSQDvQDuQDn__TQDgS_DQEnQCtQCsQCnTQCeZQEdZm",
2746 "nothrow @trusted ulong std.algorithm.iteration.SplitterResult!(std.uni.isWhite(dchar), immutable(char)[]).SplitterResult."
2747 ~"__xtoHash(ref const(std.algorithm.iteration.SplitterResult!(std.uni.isWhite, immutable(char)[]).SplitterResult))"],
2748 ["_D3std8typecons__T7TypedefTCQBaQz19__unittestL6513_208FNfZ7MyClassVQBonVAyanZQCh6__ctorMFNaNbNcNiNfQCuZSQDyQDx__TQDrTQDmVQDqnVQCcnZQEj",
2749 "pure nothrow ref @nogc @safe std.typecons.Typedef!(std.typecons.__unittestL6513_208().MyClass, null, null).Typedef "
2750 ~"std.typecons.Typedef!(std.typecons.__unittestL6513_208().MyClass, null, null).Typedef.__ctor(std.typecons.__unittestL6513_208().MyClass)"],
2751 ["_D3std6getopt__TQkTAyaTDFNaNbNiNfQoZvTQtTDQsZQBnFNfKAQBiQBlQBkQBrQyZSQCpQCo12GetoptResult",
2752 "@safe std.getopt.GetoptResult std.getopt.getopt!(immutable(char)[], void delegate(immutable(char)[]) pure nothrow @nogc @safe, "
2753 ~"immutable(char)[], void delegate(immutable(char)[]) pure nothrow @nogc @safe)."
2754 ~"getopt(ref immutable(char)[][], immutable(char)[], void delegate(immutable(char)[]) pure nothrow @nogc @safe, "
2755 ~"immutable(char)[], void delegate(immutable(char)[]) pure nothrow @nogc @safe)"],
2756 ["_D3std5regex8internal9kickstart__T7ShiftOrTaZQl11ShiftThread__T3setS_DQCqQCpQCmQCg__TQBzTaZQCfQBv10setInvMaskMFNaNbNiNfkkZvZQCjMFNaNfwZv",
2757 "pure @safe void std.regex.internal.kickstart.ShiftOr!(char).ShiftOr.ShiftThread.set!(std.regex.internal.kickstart.ShiftOr!(char).ShiftOr.ShiftThread.setInvMask(uint, uint)).set(dchar)"],
2758 ["_D3std5stdio4File__T8lockImplX10LockFileExTykZQBaMFmmykZi", // C function as template alias parameter
2759 "int std.stdio.File.lockImpl!(LockFileEx, immutable(uint)).lockImpl(ulong, ulong, immutable(uint))"],
2760 // back reference for type in template AA parameter value
2761 ["_D3std9algorithm9iteration__T12FilterResultSQBq8typecons__T5TupleTiVAyaa1_61TiVQla1_62TiVQva1_63ZQBm__T6renameVHiQBtA2i0a1_63i2a1_61ZQBeMFNcZ9__lambda1TAiZQEw9__xtoHashFNbNeKxSQGsQGrQGk__TQGdSQHiQFs__TQFmTiVQFja1_61TiVQFua1_62TiVQGfa1_63ZQGx__TQFlVQFhA2i0a1_63i2a1_61ZQGjMFNcZQFfTQEyZQJvZm",
2762 `nothrow @trusted ulong std.algorithm.iteration.FilterResult!(std.typecons.Tuple!(int, "a", int, "b", int, "c").`
2763 ~`Tuple.rename!([0:"c", 2:"a"]).rename().__lambda1, int[]).FilterResult.__xtoHash(ref const(std.algorithm.iteration.`
2764 ~`FilterResult!(std.typecons.Tuple!(int, "a", int, "b", int, "c").Tuple.rename!([0:"c", 2:"a"]).rename().__lambda1, int[]).FilterResult))`],
2766 ["_D4test4rrs1FKPiZv", "void test.rrs1(ref int*)"],
2767 ["_D4test4rrs1FMNkJPiZv", "void test.rrs1(scope return out int*)"],
2768 ["_D4test4rrs1FMNkKPiZv", "void test.rrs1(scope return ref int*)"],
2769 ["_D4test4rrs1FNkJPiZv", "void test.rrs1(return out int*)"],
2770 ["_D4test4rrs1FNkKPiZv", "void test.rrs1(return ref int*)"],
2771 ["_D4test4rrs1FNkMJPiZv", "void test.rrs1(return scope out int*)"],
2772 ["_D4test4rrs1FNkMKPiZv", "void test.rrs1(return scope ref int*)"],
2773 ["_D4test4rrs1FNkMPiZv", "void test.rrs1(return scope int*)"],
2775 // `scope` and `return` combinations
2776 ["_D3foo3Foo3barMNgFNjNlNfZNgPv", "inout return scope @safe inout(void*) foo.Foo.bar()"],
2777 ["_D3foo3FooQiMNgFNlNfZv", "inout scope @safe void foo.Foo.foo()"],
2778 ["_D3foo3Foo4foorMNgFNjNfZv", "inout return @safe void foo.Foo.foor()"],
2779 ["_D3foo3Foo3rabMNgFNlNjNfZv", "inout scope return @safe void foo.Foo.rab()"],
2781 // Hex float digit overflow
2782 ["_D3foo__T1fVdeFA3D0FBFB72A3C33FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", "_D3foo__T1fVdeFA3D0FBFB72A3C33FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"],
2786 template staticIota(int x)
2788 template Seq(T...){ alias Seq = T; }
2791 alias staticIota = Seq!();
2793 alias staticIota = Seq!(staticIota!(x - 1), x - 1);
2795 foreach ( i, name; table )
2797 auto r = demangle( name[0] );
2798 assert( r == name[1],
2799 "demangled `" ~ name[0] ~ "` as `" ~ r ~ "` but expected `" ~ name[1] ~ "`");
2801 foreach ( i; staticIota!(table.length) )
2803 enum r = demangle( table[i][0] );
2804 static assert( r == table[i][1],
2805 "demangled `" ~ table[i][0] ~ "` as `" ~ r ~ "` but expected `" ~ table[i][1] ~ "`");
2809 // https://issues.dlang.org/show_bug.cgi?id=18531
2810 auto symbol = `_D3std3uni__T6toCaseS_DQvQt12toLowerIndexFNaNbNiNewZtVii1043S_DQCjQCi10toLowerTabFNaNbNiNemZwSQDo5ascii7toLowerTAyaZQDzFNaNeQmZ14__foreachbody2MFNaNeKmKwZ14__foreachbody3MFNaNeKwZi`;
2811 auto demangled = `pure @trusted int std.uni.toCase!(std.uni.toLowerIndex(dchar), 1043, std.uni.toLowerTab(ulong), std.ascii.toLower, immutable(char)[]).toCase(immutable(char)[]).__foreachbody2(ref ulong, ref dchar).__foreachbody3(ref dchar)`;
2812 auto dst = new char[200];
2813 auto ret = demangle( symbol, dst);
2814 assert( ret == demangled );
2820 // https://issues.dlang.org/show_bug.cgi?id=18300
2821 string s = demangle.mangleof;
2824 char[] buf = new char[i];
2825 auto ds = demangle(s, buf);
2826 assert(ds == "pure nothrow @safe char[] core.demangle.demangle(scope return const(char)[], scope return char[], extern (C) char* function(const(char*), char*, ulong*, int*) pure nothrow @trusted*)" ||
2827 ds == "pure nothrow @safe char[] core.demangle.demangle(return scope const(char)[], return scope char[], extern (C) char* function(const(char*), char*, ulong*, int*) pure nothrow @trusted*)" ||
2828 ds == "pure nothrow @safe char[] core.demangle.demangle(scope return const(char)[], scope return char[], extern (C) char* function(const(char*), char*, uint*, int*) pure nothrow @trusted*)" ||
2829 ds == "pure nothrow @safe char[] core.demangle.demangle(return scope const(char)[], return scope char[], extern (C) char* function(const(char*), char*, uint*, int*) pure nothrow @trusted*)", ds);
2835 // https://issues.dlang.org/show_bug.cgi?id=18300
2837 string expected = "int ";
2838 foreach (_; 0..10_000)
2845 assert(s.demangle == expected);
2847 // https://issues.dlang.org/show_bug.cgi?id=23562
2848 assert(demangle("_Zv") == "_Zv");
2851 // https://issues.dlang.org/show_bug.cgi?id=22235
2854 enum parent = __MODULE__ ~ '.' ~ __traits(identifier, __traits(parent, {}));
2856 static noreturn abort() { assert(false); }
2857 assert(demangle(abort.mangleof) == "pure nothrow @nogc @safe noreturn " ~ parent ~ "().abort()");
2859 static void accept(noreturn) {}
2860 assert(demangle(accept.mangleof) == "pure nothrow @nogc @safe void " ~ parent ~ "().accept(noreturn)");
2862 static void templ(T)(T, T) {}
2863 assert(demangle(templ!noreturn.mangleof) == "pure nothrow @nogc @safe void " ~ parent ~ "().templ!(noreturn).templ(noreturn, noreturn)");
2865 static struct S(T) {}
2866 static void aggr(S!noreturn) { assert(0); }
2867 assert(demangle(aggr.mangleof) == "pure nothrow @nogc @safe void " ~ parent ~ "().aggr(" ~ parent ~ "().S!(noreturn).S)");
2871 * Expand an OMF, DMD-generated compressed identifier into its full form
2873 * This function only has a visible effect for OMF binaries (Win32),
2874 * as compression is otherwise not used.
2876 * See_Also: `compiler/src/dmd/backend/compress.d`
2878 string decodeDmdString( const(char)[] ln, ref size_t p ) nothrow pure @safe
2883 // decompress symbol
2884 while ( p < ln.length )
2886 int ch = cast(ubyte) ln[p++];
2887 if ( (ch & 0xc0) == 0xc0 )
2889 zlen = (ch & 0x7) + 1;
2890 zpos = ((ch >> 3) & 7) + 1; // + zlen;
2891 if ( zpos > s.length )
2893 s ~= s[$ - zpos .. $ - zpos + zlen];
2895 else if ( ch >= 0x80 )
2897 if ( p >= ln.length )
2899 int ch2 = cast(ubyte) ln[p++];
2900 zlen = (ch2 & 0x7f) | ((ch & 0x38) << 4);
2901 if ( p >= ln.length )
2903 int ch3 = cast(ubyte) ln[p++];
2904 zpos = (ch3 & 0x7f) | ((ch & 7) << 7);
2905 if ( zpos > s.length )
2907 s ~= s[$ - zpos .. $ - zpos + zlen];
2909 else if ( Demangle!().isAlpha(cast(char)ch) || Demangle!().isDigit(cast(char)ch) || ch == '_' )
2920 // locally purified for internal use here only
2923 pure @trusted @nogc nothrow pragma(mangle, "fakePureReprintReal") void pureReprintReal(char[] nptr);
2925 void fakePureReprintReal(char[] nptr)
2927 import core.stdc.stdlib : strtold;
2928 import core.stdc.stdio : snprintf;
2929 import core.stdc.errno : errno;
2932 real val = strtold(nptr.ptr, null);
2933 snprintf(nptr.ptr, nptr.length, "%#Lg", val);
2938 private struct ManglingFlagInfo
2940 /// The flag value to use
2943 /// Human-readable representation
2947 private enum TypeCtor : ushort {
2952 Immutable = (1 << 2),
2959 private immutable ManglingFlagInfo[] typeCtors = [
2960 ManglingFlagInfo(TypeCtor.Immutable, "immutable"),
2961 ManglingFlagInfo(TypeCtor.Shared, "shared"),
2962 ManglingFlagInfo(TypeCtor.InOut, "inout"),
2963 ManglingFlagInfo(TypeCtor.Const, "const"),
2966 private enum FuncAttributes : ushort {
2975 Property = (1 << 4),
2989 /// Their order matter
2990 ReturnScope = (1 << 11),
2991 ScopeReturn = (1 << 12),
2994 // The order in which we process is the same as in compiler/dmd/src/dmangle.d
2995 private immutable ManglingFlagInfo[] funcAttrs = [
2996 ManglingFlagInfo(FuncAttributes.Pure, "pure"),
2997 ManglingFlagInfo(FuncAttributes.Nothrow, "nothrow"),
2998 ManglingFlagInfo(FuncAttributes.Ref, "ref"),
2999 ManglingFlagInfo(FuncAttributes.Property, "@property"),
3000 ManglingFlagInfo(FuncAttributes.NoGC, "@nogc"),
3002 ManglingFlagInfo(FuncAttributes.ReturnScope, "return scope"),
3003 ManglingFlagInfo(FuncAttributes.ScopeReturn, "scope return"),
3005 ManglingFlagInfo(FuncAttributes.Return, "return"),
3006 ManglingFlagInfo(FuncAttributes.Scope, "scope"),
3008 ManglingFlagInfo(FuncAttributes.Live, "@live"),
3009 ManglingFlagInfo(FuncAttributes.Trusted, "@trusted"),
3010 ManglingFlagInfo(FuncAttributes.Safe, "@safe"),
3013 private string toStringConsume (immutable ManglingFlagInfo[] infos, ref ushort base)
3014 @safe pure nothrow @nogc
3016 foreach (const ref info; infos)
3018 if ((base & info.flag) == info.flag)
3027 private shared CXX_DEMANGLER __cxa_demangle;
3031 * a CXX_DEMANGLER if a C++ stdlib is loaded
3034 CXX_DEMANGLER getCXXDemangler() nothrow @trusted
3036 import core.atomic : atomicLoad, atomicStore;
3037 if (__cxa_demangle is null)
3040 import core.sys.posix.dlfcn : dlsym;
3041 version (DragonFlyBSD) import core.sys.dragonflybsd.dlfcn : RTLD_DEFAULT;
3042 version (FreeBSD) import core.sys.freebsd.dlfcn : RTLD_DEFAULT;
3043 version (linux) import core.sys.linux.dlfcn : RTLD_DEFAULT;
3044 version (NetBSD) import core.sys.netbsd.dlfcn : RTLD_DEFAULT;
3045 version (OpenBSD) import core.sys.openbsd.dlfcn : RTLD_DEFAULT;
3046 version (Darwin) import core.sys.darwin.dlfcn : RTLD_DEFAULT;
3047 version (Solaris) import core.sys.solaris.dlfcn : RTLD_DEFAULT;
3049 if (auto found = cast(CXX_DEMANGLER) dlsym(RTLD_DEFAULT, "__cxa_demangle"))
3050 atomicStore(__cxa_demangle, found);
3053 if (__cxa_demangle is null)
3055 static extern(C) char* _(const char* mangled_name, char* output_buffer,
3056 size_t* length, int* status) nothrow pure @trusted
3061 atomicStore(__cxa_demangle, &_);
3064 return atomicLoad(__cxa_demangle);
3068 * Demangles C++ mangled names. If it is not a C++ mangled name, it
3069 * returns its argument name.
3072 * buf = The string to demangle.
3073 * __cxa_demangle = C++ demangler
3074 * dst = An optional destination buffer.
3077 * The demangled name or the original string if the name is not a mangled
3080 private char[] demangleCXX(return scope const(char)[] buf, CXX_DEMANGLER __cxa_demangle, return scope char[] dst = null,) nothrow pure @trusted
3082 char[] c_string = dst; // temporarily use dst buffer if possible
3083 c_string.length = buf.length + 1;
3084 c_string[0 .. buf.length] = buf[0 .. buf.length];
3085 c_string[buf.length] = '\0';
3088 size_t demangled_length;
3089 auto demangled = __cxa_demangle(&c_string[0], null, &demangled_length, &status);
3092 pureFree(cast(void*) demangled);
3096 dst.length = demangled_length;
3097 dst[] = demangled[0 .. demangled_length];
3101 dst.length = buf.length;
3106 private struct Buffer
3108 enum size_t minSize = 4000;
3115 public alias opDollar = len;
3117 public size_t length () const scope @safe pure nothrow @nogc
3122 public BufSlice opSlice (size_t from, size_t to)
3123 return scope @safe pure nothrow @nogc
3125 return bslice(from, to);
3128 static bool contains(scope const(char)[] a, scope const BufSlice b) @safe nothrow
3131 b.from < a.length &&
3135 char[] copyInput(scope const(char)[] buf)
3136 return scope nothrow
3138 if (dst.length < buf.length)
3139 dst.length = buf.length;
3140 char[] r = dst[0 .. buf.length];
3145 private void checkAndStretchBuf(size_t len_to_add) scope nothrow
3147 const required = len + len_to_add;
3149 if (required > dst.length)
3150 dst.length = dst.length + len_to_add;
3153 // move val to the end of the dst buffer
3154 BufSlice shift(scope const BufSlice val) return scope nothrow
3156 version (DigitalMars) pragma(inline, false); // tame dmd inliner
3160 const ptrdiff_t s = val.from;
3161 const size_t f = len;
3163 assert(contains( dst[0 .. len], val ),
3164 "\ndst=\""~dst[0 .. len]~"\"\n"~
3165 "val=\""~val.getSlice~"\"\n"
3168 checkAndStretchBuf(val.length);
3170 // store value temporary over len index
3171 dst[len .. len + val.length] = val.getSlice();
3173 // shift all chars including temporary saved above
3174 // if buf was allocated above it will be leave for further usage
3175 for (size_t p = s; p < f; p++)
3176 dst[p] = dst[p + val.length];
3178 return bslice(len - val.length, len);
3181 return bslice_empty;
3184 // remove val from dst buffer
3185 void remove(scope BufSlice val) scope nothrow
3187 version (DigitalMars) pragma(inline, false); // tame dmd inliner
3191 assert( contains( dst[0 .. len], val ) );
3193 assert( len >= val.length && len <= dst.length );
3195 for (size_t p = val.from; p < len; p++)
3196 dst[p] = dst[p + val.length];
3200 void append(scope const(char)[] val) scope nothrow
3202 version (DigitalMars) pragma(inline, false); // tame dmd inliner
3207 dst.length = minSize;
3209 debug(info) printf( "appending (%.*s)\n", cast(int) val.length, val.ptr );
3211 checkAndStretchBuf(val.length);
3213 // data is already not in place?
3214 if ( &dst[len] != &val[0] )
3215 dst[len .. len + val.length] = val[];
3223 private scope bslice(size_t from, size_t to) nothrow
3225 return BufSlice(dst, from, to);
3228 private static scope bslice_empty() nothrow
3230 return BufSlice.init;
3234 private struct BufSlice
3246 this(return scope char[] buf) scope nothrow @nogc
3251 this(return scope char[] buf, size_t from, size_t to, bool lastArgIsLen = false) scope nothrow @nogc
3257 this.to = from + to;
3273 auto getSlice() inout nothrow scope { return buf[from .. to]; }
3274 size_t length() const scope { return to - from; }