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;
82 bool hasErrors = false;
84 /// Called when encountering an error / unrecognized mangle.
86 /// Currently, errors simply make `demangle` return
87 /// the input string, but the `msg` string can be used for debugging.
88 /// As a future enhancement, error handlers can be supplied through `Hooks`
89 void error(string msg = "error")
94 //////////////////////////////////////////////////////////////////////////
95 // Type Testing and Conversion
96 //////////////////////////////////////////////////////////////////////////
99 static bool isAlpha( char val )
101 return ('a' <= val && 'z' >= val) ||
102 ('A' <= val && 'Z' >= val) ||
103 (0x80 & val); // treat all unicode as alphabetic
107 static bool isDigit( char val ) nothrow
109 return '0' <= val && '9' >= val;
113 static bool isHexDigit( char val )
115 return ('0' <= val && '9' >= val) ||
116 ('a' <= val && 'f' >= val) ||
117 ('A' <= val && 'F' >= val);
121 static ubyte ascii2hex( out bool errStatus, char val ) nothrow
123 if (val >= 'a' && val <= 'f')
124 return cast(ubyte)(val - 'a' + 10);
125 if (val >= 'A' && val <= 'F')
126 return cast(ubyte)(val - 'A' + 10);
127 if (val >= '0' && val <= '9')
128 return cast(ubyte)(val - '0');
134 BufSlice shift(scope const BufSlice val) return scope
137 return dst.bslice_empty;
138 return dst.shift(val);
141 void putComma(size_t n)
143 version (DigitalMars) pragma(inline, false);
148 void put(char c) return scope
154 void put(scope BufSlice val) return scope
159 void put(scope const(char)[] val) return scope nothrow
167 void putAsHex( size_t val, int width = 0 )
169 import core.internal.string;
171 UnsignedStringBuf buf = void;
173 auto s = unsignedToTempString!16(val, buf);
174 int slen = cast(int)s.length;
177 foreach (i; slen .. width)
184 void pad( const(char)[] val )
194 void silent( out bool err_status, void delegate(out bool err_status) pure @safe nothrow dg ) nothrow
196 debug(trace) printf( "silent+\n" );
197 debug(trace) scope(success) printf( "silent-\n" );
205 //////////////////////////////////////////////////////////////////////////
207 //////////////////////////////////////////////////////////////////////////
209 @property bool empty()
211 return pos >= buf.length;
214 @property char front()
216 if ( pos < buf.length )
221 char peek( size_t n )
223 if ( pos + n < buf.length )
229 bool test( char val ) nothrow
234 void popFront() nothrow
236 if ( pos++ >= buf.length )
241 void popFront(int i) nothrow
248 bool match( char val ) nothrow
259 bool match( const(char)[] val ) nothrow
261 foreach (char e; val )
275 bool isSymbolNameFront(out bool errStatus) nothrow
278 if ( isDigit( val ) || val == '_' )
283 // check the back reference encoding after 'Q'
287 // invalid back reference
292 return isDigit( val ); // identifier ref
295 // return the first character at the back reference
296 char peekBackref() nothrow
298 assert( front == 'Q' );
299 auto n = decodeBackref!1();
301 return 0; // invalid back reference
306 size_t decodeBackref(size_t peekAt = 0)() nothrow
310 for (size_t p; ; p++)
313 static if (peekAt > 0)
315 t = peek(peekAt + p);
322 if (t < 'A' || t > 'Z')
324 if (t < 'a' || t > 'z')
325 return 0; // invalid back reference
327 n = base * n + t - 'a';
330 n = base * n + t - 'A';
334 //////////////////////////////////////////////////////////////////////////
335 // Parsing Implementation
336 //////////////////////////////////////////////////////////////////////////
344 const(char)[] sliceNumber() return scope
346 debug(trace) printf( "sliceNumber+\n" );
347 debug(trace) scope(success) printf( "sliceNumber-\n" );
354 if (t >= '0' && t <= '9')
357 return buf[beg .. pos];
362 size_t decodeNumber(out bool errStatus) scope nothrow
364 debug(trace) printf( "decodeNumber+\n" );
365 debug(trace) scope(success) printf( "decodeNumber-\n" );
367 return decodeNumber( errStatus, sliceNumber() );
370 size_t decodeNumber( out bool errStatus, scope const(char)[] num ) scope nothrow
372 debug(trace) printf( "decodeNumber+\n" );
373 debug(trace) scope(success) printf( "decodeNumber-\n" );
379 import core.checkedint : mulu, addu;
381 bool overflow = false;
382 val = mulu(val, 10, overflow);
383 val = addu(val, c - '0', overflow);
393 void parseReal(out bool errStatus) scope nothrow
395 debug(trace) printf( "parseReal+\n" );
396 debug(trace) scope(success) printf( "parseReal-\n" );
398 char[64] tbuf = void;
411 put( "real.infinity" );
421 put( "-real.infinity" );
436 errStatus = !isHexDigit( front );
438 return; // Expected hex digit
440 tbuf[tlen++] = front;
444 while ( isHexDigit( front ) )
446 if (tlen >= tbuf.length)
447 return onError(); // Too many hex float digits
448 tbuf[tlen++] = front;
463 while ( isDigit( front ) )
465 tbuf[tlen++] = front;
470 debug(info) printf( "got (%s)\n", tbuf.ptr );
471 pureReprintReal( tbuf[] );
472 debug(info) printf( "converted (%.*s)\n", cast(int) tlen, tbuf.ptr );
473 put( tbuf[0 .. tlen] );
497 void parseLName(out string errMsg) scope nothrow
499 debug(trace) printf( "parseLName+\n" );
500 debug(trace) scope(success) printf( "parseLName-\n" );
502 static if (__traits(hasMember, Hooks, "parseLName"))
504 auto r = hooks.parseLName(errMsg, this);
510 void error(string msg)
517 // back reference to LName
520 size_t n = decodeBackref();
521 if (!n || n > refPos)
522 return error("Invalid LName back reference");
527 scope(exit) pos = savePos;
535 auto n = decodeNumber(err_flag);
537 return error("Number overflow");
541 put( "__anonymous" );
544 if ( n > buf.length || n > buf.length - pos )
545 return error("LName must be at least 1 character");
547 if ( '_' != front && !isAlpha( front ) )
548 return error("Invalid character in LName");
550 foreach (char e; buf[pos + 1 .. pos + n] )
552 if ( '_' != e && !isAlpha( e ) && !isDigit( e ) )
553 return error("Invalid character in LName");
556 put( buf[pos .. pos + n] );
635 CallConvention FuncAttrs Arguments ArgClose Type
736 BufSlice parseType() return scope nothrow
738 static immutable string[23] primitives = [
764 static if (__traits(hasMember, Hooks, "parseType"))
766 auto n = hooks.parseType(this, null);
768 return dst.bslice_empty;
771 return BufSlice(n, 0, n.length);
774 debug(trace) printf( "parseType+\n" );
775 debug(trace) scope(success) printf( "parseType-\n" );
776 auto beg = dst.length;
779 BufSlice parseBackrefType(scope BufSlice delegate() pure @safe nothrow parseDg) pure @safe nothrow
783 this.error("recursive back reference");
784 return dst.bslice_empty;
789 auto n = decodeBackref();
790 if (n == 0 || n > pos)
792 this.error("invalid back reference");
793 return dst.bslice_empty;
797 return dst.bslice_empty;
800 scope(success) { pos = savePos; brp = saveBrp; }
804 auto ret = parseDg();
810 case 'Q': // Type back reference
811 auto r = parseBackrefType(() => parseType());
813 case 'O': // Shared (O Type)
818 return dst[beg .. $];
819 case 'x': // Const (x Type)
824 return dst[beg .. $];
825 case 'y': // Immutable (y Type)
830 return dst[beg .. $];
835 case 'n': // Noreturn
838 return dst[beg .. $];
839 case 'g': // Wild (Ng Type)
841 // TODO: Anything needed here?
845 return dst[beg .. $];
846 case 'h': // TypeVector (Nh Type)
851 return dst[beg .. $];
854 return dst.bslice_empty;
856 case 'A': // TypeArray (A Type)
860 return dst[beg .. $];
861 case 'G': // TypeStaticArray (G Number Type)
863 auto num = sliceNumber();
868 return dst[beg .. $];
869 case 'H': // TypeAssocArray (H Type Type)
872 auto tx = parseType();
874 return dst.bslice_empty;
879 return dst[beg .. $];
880 case 'P': // TypePointer (P Type)
884 return dst[beg .. $];
885 case 'F': case 'U': case 'W': case 'V': case 'R': // TypeFunction
887 auto r = parseTypeFunction(errStatus);
891 return dst.bslice_empty;
894 case 'C': // TypeClass (C LName)
895 case 'S': // TypeStruct (S LName)
896 case 'E': // TypeEnum (E LName)
897 case 'T': // TypeTypedef (T LName)
900 parseQualifiedName(errStatus);
902 return dst.bslice_empty;
903 return dst[beg .. $];
904 case 'D': // TypeDelegate (D TypeFunction)
906 auto modifiers = parseModifier();
910 auto r = parseBackrefType(() => parseTypeFunction(errStatus, IsDelegate.yes));
914 return dst.bslice_empty;
921 parseTypeFunction(errStatus, IsDelegate.yes);
922 if (this.hasErrors || errStatus)
923 return dst.bslice_empty;
928 // write modifiers behind the function arguments
929 while (auto str = typeCtors.toStringConsume(modifiers))
935 return dst[beg .. $];
936 case 'n': // TypeNone (n)
938 // TODO: Anything needed here?
939 return dst[beg .. $];
940 case 'B': // TypeTuple (B Number Arguments)
942 // TODO: Handle this.
943 return dst[beg .. $];
944 case 'Z': // Internal symbol
945 // This 'type' is used for untyped internal symbols, i.e.:
953 return dst[beg .. $];
955 if (t >= 'a' && t <= 'w')
958 put( primitives[cast(size_t)(t - 'a')] );
959 return dst[beg .. $];
969 return dst[beg .. $];
973 return dst[beg .. $];
975 error("unknown type");
976 return dst.bslice_empty;
979 error("unknown type");
980 return dst.bslice_empty;
987 CallConvention FuncAttrs Arguments ArgClose Type
1043 M Argument2 // scope
1052 X // variadic T t,...) style
1053 Y // variadic T t...) style
1056 void parseCallConvention(out bool errStatus) nothrow
1066 put( "extern (C) " );
1068 case 'W': // Windows
1070 put( "extern (Windows) " );
1074 put( "extern (C++) " );
1081 /// Returns: Flags of `TypeCtor`
1082 ushort parseModifier()
1084 TypeCtor res = TypeCtor.None;
1089 return TypeCtor.Immutable;
1092 res |= TypeCtor.Shared;
1097 return TypeCtor.Shared;
1099 if (peek( 1 ) != 'g')
1103 res |= TypeCtor.InOut;
1109 res |= TypeCtor.Const;
1111 default: return TypeCtor.None;
1115 ushort parseFuncAttr(out bool errStatus) nothrow
1119 while ('N' == front)
1124 case 'a': // FuncAttrPure
1126 result |= FuncAttributes.Pure;
1128 case 'b': // FuncAttrNoThrow
1130 result |= FuncAttributes.Nothrow;
1132 case 'c': // FuncAttrRef
1134 result |= FuncAttributes.Ref;
1136 case 'd': // FuncAttrProperty
1138 result |= FuncAttributes.Property;
1140 case 'e': // FuncAttrTrusted
1142 result |= FuncAttributes.Trusted;
1144 case 'f': // FuncAttrSafe
1146 result |= FuncAttributes.Safe;
1152 // NOTE: The inout parameter type is represented as "Ng".
1153 // The vector parameter type is represented as "Nh".
1154 // The return parameter type is represented as "Nk".
1155 // The noreturn parameter type is represented as "Nn".
1156 // These make it look like a FuncAttr, but infact
1157 // if we see these, then we know we're really in
1158 // the parameter list. Rewind and break.
1161 case 'i': // FuncAttrNogc
1163 result |= FuncAttributes.NoGC;
1165 case 'j': // FuncAttrReturn
1167 if (this.peek(0) == 'N' && this.peek(1) == 'l')
1169 result |= FuncAttributes.ReturnScope;
1173 result |= FuncAttributes.Return;
1176 case 'l': // FuncAttrScope
1178 if (this.peek(0) == 'N' && this.peek(1) == 'j')
1180 result |= FuncAttributes.ScopeReturn;
1184 result |= FuncAttributes.Scope;
1187 case 'm': // FuncAttrLive
1189 result |= FuncAttributes.Live;
1199 void parseFuncArguments(out bool errStatus) scope nothrow
1202 for ( size_t n = 0; true; n++ )
1204 debug(info) printf( "tok (%c)\n", front );
1207 case 'X': // ArgClose (variadic T t...) style)
1211 case 'Y': // ArgClose (variadic T t,...) style)
1215 case 'Z': // ArgClose (not variadic)
1223 /* Do special return, scope, ref, out combinations
1226 if ( 'M' == front && peek(1) == 'N' && peek(2) == 'k')
1231 put("scope return out "); // MNkJ
1236 put("scope return ref "); // MNkK
1240 else if ('N' == front && peek(1) == 'k')
1245 put("return out "); // NkJ
1250 put("return ref "); // NkK
1258 put("return scope out "); // NkMJ
1263 put("return scope ref "); // NkMK
1268 put("return scope "); // NkM
1283 if ( 'k' == front ) // Return (Nk Parameter2)
1294 case 'I': // in (I Type)
1301 case 'K': // ref (K Type)
1306 case 'J': // out (J Type)
1311 case 'L': // lazy (L Type)
1322 enum IsDelegate { no, yes }
1326 CallConvention FuncAttrs Arguments ArgClose Type
1328 BufSlice parseTypeFunction(out bool errStatus, IsDelegate isdg = IsDelegate.no) return scope nothrow
1330 debug(trace) printf( "parseTypeFunction+\n" );
1331 debug(trace) scope(success) printf( "parseTypeFunction-\n" );
1332 auto beg = dst.length;
1334 parseCallConvention(errStatus);
1336 return dst.bslice_empty;
1338 auto attributes = parseFuncAttr(errStatus);
1340 return dst.bslice_empty;
1342 auto argbeg = dst.length;
1343 put(IsDelegate.yes == isdg ? "delegate" : "function");
1345 parseFuncArguments(errStatus);
1347 return dst.bslice_empty;
1351 // write function attributes behind arguments
1352 while (auto str = funcAttrs.toStringConsume(attributes))
1359 // A function / delegate return type is located at the end of its mangling
1360 // Write it in order, then shift it back to 'code order'
1361 // e.g. `delegate(int) @safedouble ' => 'double delegate(int) @safe'
1363 auto retbeg = dst.length;
1366 return dst.bslice_empty;
1368 shift(dst[argbeg .. retbeg]);
1371 return dst[beg .. $];
1374 static bool isCallConvention( char ch )
1378 case 'F', 'U', 'V', 'W', 'R':
1392 c HexFloat c HexFloat
1399 N HexDigits P Exponent
1400 HexDigits P Exponent
1420 void parseValue(out bool errStatus) scope nothrow
1422 parseValue(errStatus, dst.bslice_empty);
1425 void parseValue(out bool errStatus, scope BufSlice name, char type = '\0' ) scope nothrow
1427 debug(trace) printf( "parseValue+\n" );
1428 debug(trace) scope(success) printf( "parseValue-\n" );
1435 // printf( "*** %c\n", front );
1444 if ('0' > front || '9' < front)
1445 return onError(); // Number expected
1447 case '0': .. case '9':
1448 parseIntegerValue( errStatus, name, type );
1453 parseIntegerValue( errStatus, name, type );
1457 parseReal(errStatus);
1461 parseReal(errStatus);
1467 parseReal(errStatus);
1472 case 'a': case 'w': case 'd':
1475 auto n = decodeNumber(errStatus);
1483 auto a = ascii2hex( errStatus, front );
1488 auto b = ascii2hex( errStatus, front );
1493 auto v = cast(char)((a << 4) | b);
1494 if (' ' <= v && v <= '~') // ASCII printable
1509 // NOTE: This is kind of a hack. An associative array literal
1510 // [1:2, 3:4] is represented as HiiA2i1i2i3i4, so the type
1511 // is "Hii" and the value is "A2i1i2i3i4". Thus the only
1512 // way to determine that this is an AA value rather than an
1513 // array value is for the caller to supply the type char.
1514 // Hopefully, this will change so that the value is
1515 // "H2i1i2i3i4", rendering this unnecesary.
1518 // A Number Value...
1519 // An array literal. Value is repeated Number times.
1522 auto n = decodeNumber(errStatus);
1525 foreach ( i; 0 .. n )
1528 parseValue(errStatus);
1536 // H Number Value...
1537 // An associative array literal. Value is repeated 2*Number times.
1540 auto n = decodeNumber(errStatus);
1543 foreach ( i; 0 .. n )
1546 parseValue(errStatus);
1550 parseValue(errStatus);
1557 // S Number Value...
1558 // A struct literal. Value is repeated Number times.
1563 auto n = decodeNumber(errStatus);
1566 foreach ( i; 0 .. n )
1569 parseValue(errStatus);
1577 // A function literal symbol
1579 parseMangledName(false, 1);
1588 void parseIntegerValue( out bool errStatus, scope BufSlice name, char type = '\0' ) scope nothrow
1590 debug(trace) printf( "parseIntegerValue+\n" );
1591 debug(trace) scope(success) printf( "parseIntegerValue-\n" );
1599 auto val = sliceNumber();
1600 auto num = decodeNumber( errStatus, val );
1638 if ( num >= 0x20 && num < 0x7F )
1641 put( cast(char)num );
1664 auto d = decodeNumber(errStatus);
1667 put( d ? "true" : "false" );
1669 case 'h', 't', 'k': // ubyte, ushort, uint
1670 put( sliceNumber() );
1674 put( sliceNumber() );
1678 put( sliceNumber() );
1682 put( sliceNumber() );
1691 TemplateArg TemplateArgs
1700 S Number_opt QualifiedName
1701 X ExternallyMangledName
1703 void parseTemplateArgs(out bool errStatus) scope nothrow
1705 debug(trace) printf( "parseTemplateArgs+\n" );
1706 debug(trace) scope(success) printf( "parseTemplateArgs-\n" );
1709 for ( size_t n = 0; true; n++ )
1729 // NOTE: In the few instances where the type is actually
1730 // desired in the output it should precede the value
1731 // generated by parseValue, so it is safe to simply
1732 // decrement len and let put/append do its thing.
1733 char t = front; // peek at type for parseValue
1739 // invalid back reference
1744 BufSlice name = dst.bslice_empty;
1745 silent( errStatus, delegate void(out bool e_flg) nothrow { name = parseType(); } );
1748 parseValue( errStatus, name, t );
1756 if ( mayBeMangledNameArg() )
1758 auto l = dst.length;
1762 debug(trace) printf( "may be mangled name arg\n" );
1764 if (parseMangledNameArg())
1771 debug(trace) printf( "not a mangled name arg\n" );
1774 if ( isDigit( front ) && isDigit( peek( 1 ) ) )
1776 // ambiguity: length followed by qualified name (starting with number)
1777 // try all possible pairs of numbers
1778 auto qlen = decodeNumber(errStatus);
1782 qlen /= 10; // last digit needed for QualifiedName
1784 auto l = dst.length;
1790 parseQualifiedName(errStatus);
1794 if ( pos == p + qlen )
1798 qlen /= 10; // retry with one digit less
1805 parseQualifiedName(errStatus);
1826 bool mayBeMangledNameArg() nothrow
1828 debug(trace) printf( "mayBeMangledNameArg+\n" );
1829 debug(trace) scope(success) printf( "mayBeMangledNameArg-\n" );
1833 scope(exit) pos = p;
1835 if ( isDigit( buf[pos] ) )
1837 auto n = decodeNumber(errStatus);
1839 return !errStatus && n >= 4 &&
1840 pos < buf.length && '_' == buf[pos++] &&
1841 pos < buf.length && 'D' == buf[pos++] &&
1842 isDigit( buf[pos] );
1846 const isSNF = isSymbolNameFront(errStatus);
1848 return !errStatus &&
1849 pos < buf.length && '_' == buf[pos++] &&
1850 pos < buf.length && 'D' == buf[pos++] &&
1855 bool parseMangledNameArg() nothrow
1857 debug(trace) printf( "parseMangledNameArg+\n" );
1858 debug(trace) scope(success) printf( "parseMangledNameArg-\n" );
1861 if ( isDigit( front ) )
1864 n = decodeNumber(errStatus);
1872 parseMangledName(false, n);
1873 return !this.hasErrors;
1877 TemplateInstanceName:
1878 Number __T LName TemplateArgs Z
1880 void parseTemplateInstanceName(out bool errStatus, bool hasNumber) scope nothrow
1882 debug(trace) printf( "parseTemplateInstanceName+\n" );
1883 debug(trace) scope(success) printf( "parseTemplateInstanceName-\n" );
1898 n = decodeNumber(errStatus);
1904 errStatus = !match( "__T" );
1911 if (errMsg !is null)
1917 parseTemplateArgs(errStatus);
1924 if ( hasNumber && pos - beg != n )
1926 // Template name length mismatch
1934 bool mayBeTemplateInstanceName() scope nothrow
1936 debug(trace) printf( "mayBeTemplateInstanceName+\n" );
1937 debug(trace) scope(success) printf( "mayBeTemplateInstanceName-\n" );
1940 scope(exit) pos = p;
1943 auto n = decodeNumber(errStatus);
1948 pos < buf.length && '_' == buf[pos++] &&
1949 pos < buf.length && '_' == buf[pos++] &&
1950 pos < buf.length && 'T' == buf[pos++];
1957 TemplateInstanceName
1959 void parseSymbolName(out bool errStatus) scope nothrow
1961 debug(trace) printf( "parseSymbolName+\n" );
1962 debug(trace) scope(success) printf( "parseSymbolName-\n" );
1965 // TemplateInstanceName -> Number "__T"
1969 // no length encoding for templates for new mangling
1970 parseTemplateInstanceName(errStatus, false);
1973 case '0': .. case '9':
1974 if ( mayBeTemplateInstanceName() )
1976 auto t = dst.length;
1978 debug(trace) printf( "may be template instance name\n" );
1979 parseTemplateInstanceName(errStatus, true);
1984 debug(trace) printf( "not a template instance name\n" );
1992 errStatus = errMsg !is null;
1999 // parse optional function arguments as part of a symbol name, i.e without return type
2000 // if keepAttr, the calling convention and function attributes are not discarded, but returned
2001 BufSlice parseFunctionTypeNoReturn( bool keepAttr = false ) return scope nothrow
2003 // try to demangle a function, in case we are pointing to some function local
2005 auto prevlen = dst.length;
2010 // do not emit "needs this"
2012 auto modifiers = parseModifier();
2013 while (auto str = typeCtors.toStringConsume(modifiers))
2019 if ( isCallConvention( front ) )
2021 BufSlice attr = dst.bslice_empty;
2022 // we don't want calling convention and attributes in the qualified name
2024 parseCallConvention(errStatus);
2027 auto attributes = parseFuncAttr(errStatus);
2031 while (auto str = funcAttrs.toStringConsume(attributes))
2036 attr = dst[prevlen .. $];
2040 parseFuncArguments(errStatus);
2042 return dst.bslice_empty;
2048 // not part of a qualified name, so back up
2054 return dst.bslice_empty;
2060 SymbolName QualifiedName
2062 void parseQualifiedName(out bool errStatus) return scope nothrow
2064 debug(trace) printf( "parseQualifiedName+\n" );
2065 debug(trace) scope(success) printf( "parseQualifiedName-\n" );
2068 bool is_sym_name_front;
2075 parseSymbolName(errStatus);
2079 parseFunctionTypeNoReturn();
2081 is_sym_name_front = isSymbolNameFront(errStatus);
2085 } while ( is_sym_name_front );
2091 _D QualifiedName Type
2092 _D QualifiedName M Type
2094 void parseMangledName(bool displayType, size_t n = 0 ) scope nothrow
2096 debug(trace) printf( "parseMangledName+\n" );
2097 debug(trace) scope(success) printf( "parseMangledName-\n" );
2098 BufSlice name = dst.bslice_empty;
2108 size_t beg = dst.length;
2109 size_t nameEnd = dst.length;
2110 BufSlice attr = dst.bslice_empty;
2111 bool is_sym_name_front;
2116 dst.remove(attr); // dump attributes of parent symbols
2117 if (beg != dst.length)
2121 parseSymbolName(errStatus);
2125 nameEnd = dst.length;
2126 attr = parseFunctionTypeNoReturn( displayType );
2128 is_sym_name_front = isSymbolNameFront(errStatus);
2131 } while (is_sym_name_front);
2135 attr = shift( attr );
2136 nameEnd = dst.length - attr.length; // name includes function arguments
2138 name = dst[beg .. nameEnd];
2140 debug(info) printf( "name (%.*s)\n", cast(int) name.length, name.ptr );
2142 popFront(); // has 'this' pointer
2144 auto lastlen = dst.length;
2145 auto type = parseType();
2153 // sort (name,attr,type) -> (attr,type,name)
2159 assert( attr.length == 0 );
2162 if ( pos >= buf.length || (n != 0 && pos >= end) )
2167 case 'T': // terminators when used as template alias parameter
2179 void parseMangledName() nothrow
2181 parseMangledName(AddType.yes == addType);
2184 char[] doDemangle(alias FUNC)() return scope nothrow
2188 debug(info) printf( "demangle(%.*s)\n", cast(int) buf.length, buf.ptr );
2191 if (!this.hasErrors)
2193 return dst[0 .. $].getSlice;
2197 debug(info) printf( "error" );
2199 return dst.copyInput(buf);
2204 char[] demangleName() nothrow
2206 return doDemangle!parseMangledName();
2209 char[] demangleType() nothrow
2211 return doDemangle!parseType();
2217 * Demangles D/C++ mangled names. If it is not a D/C++ mangled name, it
2218 * returns its argument name.
2221 * buf = The string to demangle.
2222 * dst = An optional destination buffer.
2223 * __cxa_demangle = optional C++ demangler
2226 * The demangled name or the original string if the name is not a mangled
2229 char[] demangle(return scope const(char)[] buf, return scope char[] dst = null, CXX_DEMANGLER __cxa_demangle = null) nothrow pure @safe
2231 if (__cxa_demangle && buf.length > 2 && buf[0..2] == "_Z")
2232 return demangleCXX(buf, __cxa_demangle, dst);
2233 auto d = Demangle!()(buf, dst);
2234 // fast path (avoiding throwing & catching exception) for obvious
2235 // non-D mangled names
2236 if (buf.length < 2 || !(buf[0] == 'D' || buf[0..2] == "_D"))
2237 return d.dst.copyInput(buf);
2238 return d.demangleName();
2243 * Demangles a D mangled type.
2246 * buf = The string to demangle.
2247 * dst = An optional destination buffer.
2250 * The demangled type name or the original string if the name is not a
2253 char[] demangleType( const(char)[] buf, char[] dst = null ) nothrow pure @safe
2255 auto d = Demangle!()(buf, dst);
2256 return d.demangleType();
2260 * reencode a mangled symbol name that might include duplicate occurrences
2261 * of the same identifier by replacing all but the first occurence with
2265 * mangled = The mangled string representing the type
2268 * The mangled name with deduplicated identifiers
2270 char[] reencodeMangled(return scope const(char)[] mangled) nothrow pure @safe
2272 static struct PrependHooks
2276 size_t[const(char)[]] idpos; // identifier positions
2278 static struct Replacement
2280 size_t pos; // postion in original mangled string
2281 size_t respos; // postion in result string
2283 Replacement [] replacements;
2286 size_t positionInResult(size_t pos) scope
2288 foreach_reverse (r; replacements)
2290 return r.respos + pos - r.pos;
2294 alias Remangle = Demangle!(PrependHooks);
2296 void flushPosition(ref Remangle d) scope
2298 if (lastpos < d.pos)
2300 result ~= d.buf[lastpos .. d.pos];
2302 else if (lastpos > d.pos)
2304 // roll back to earlier position
2305 while (replacements.length > 0 && replacements[$-1].pos > d.pos)
2306 replacements = replacements[0 .. $-1];
2308 if (replacements.length > 0)
2309 result.length = replacements[$-1].respos + d.pos - replacements[$-1].pos;
2311 result.length = d.pos;
2315 bool parseLName(out string errMsg, scope ref Remangle d) scope @trusted nothrow
2317 bool error(string msg)
2324 auto reslen = result.length;
2325 auto refpos = d.pos;
2330 scope(exit) result.length = reslen; // remove all intermediate additions
2331 // only support identifier back references
2333 size_t n = d.decodeBackref();
2334 if (!n || n > refpos)
2335 return error("invalid back reference");
2337 auto savepos = d.pos;
2338 scope(exit) d.pos = savepos;
2339 size_t srcpos = refpos - n;
2342 auto idlen = d.decodeNumber(errStatus);
2344 return error("invalid number");
2346 if (d.pos + idlen > d.buf.length)
2347 return error("invalid back reference");
2349 auto id = d.buf[d.pos .. d.pos + idlen];
2350 auto pid = id in idpos;
2352 return error("invalid back reference");
2354 npos = positionInResult(*pid);
2356 encodeBackref(reslen - npos);
2357 const pos = d.pos; // work around issues.dlang.org/show_bug.cgi?id=20675
2358 replacements ~= Replacement(pos, result.length);
2363 auto n = d.decodeNumber(errStatus);
2365 return error("invalid number");
2367 if (!n || n > d.buf.length || n > d.buf.length - d.pos)
2368 return error("LName too short or too long");
2370 auto id = d.buf[d.pos .. d.pos + n];
2372 if (auto pid = id in idpos)
2374 size_t npos = positionInResult(*pid);
2375 result.length = reslen;
2376 encodeBackref(reslen - npos);
2377 const pos = d.pos; // work around issues.dlang.org/show_bug.cgi?id=20675
2378 replacements ~= Replacement(pos, result.length);
2382 idpos[id] = refpos; //! scope variable id used as AA key, makes this function @trusted
2383 result ~= d.buf[refpos .. d.pos];
2390 char[] parseType(ref Remangle d, char[] name) return scope nothrow
2398 auto refPos = d.pos;
2400 auto n = d.decodeBackref();
2401 if (n == 0 || n > refPos)
2403 d.error("invalid back reference");
2407 size_t npos = positionInResult(refPos - n);
2408 size_t reslen = result.length;
2409 encodeBackref(reslen - npos);
2412 return result[reslen .. $]; // anything but null
2415 void encodeBackref(size_t relpos) scope
2420 while (relpos >= div * base)
2424 auto dig = (relpos / div);
2425 result ~= cast(char)('A' + dig);
2426 relpos -= dig * div;
2429 result ~= cast(char)('a' + relpos);
2433 auto d = Demangle!(PrependHooks)(mangled, null);
2434 d.hooks = PrependHooks();
2435 d.mute = true; // no demangled output
2438 d.parseMangledName(errStatus);
2441 // error cannot occur
2445 if (d.hooks.lastpos < d.pos)
2446 d.hooks.result ~= d.buf[d.hooks.lastpos .. d.pos];
2447 return d.hooks.result;
2451 * Mangles a D symbol.
2454 * T = The type of the symbol.
2455 * fqn = The fully qualified name of the symbol.
2456 * dst = An optional destination buffer.
2459 * The mangled name for a symbols of type T and the given fully
2462 char[] mangle(T)(return scope const(char)[] fqn, return scope char[] dst = null) @safe pure nothrow
2464 import core.internal.string : numDigits, unsignedToTempString;
2466 static struct DotSplitter
2471 @property bool empty() const { return !s.length; }
2473 @property const(char)[] front() const return scope
2475 immutable i = indexOfDot();
2476 return i == -1 ? s[0 .. $] : s[0 .. i];
2479 void popFront() scope
2481 immutable i = indexOfDot();
2482 s = i == -1 ? s[$ .. $] : s[i+1 .. $];
2485 private ptrdiff_t indexOfDot() const scope
2487 foreach (i, c; s) if (c == '.') return i;
2492 size_t len = "_D".length;
2493 foreach (comp; DotSplitter(fqn))
2494 len += numDigits(comp.length) + comp.length;
2495 len += T.mangleof.length;
2496 if (dst.length < len) dst.length = len;
2498 size_t i = "_D".length;
2500 foreach (comp; DotSplitter(fqn))
2502 const ndigits = numDigits(comp.length);
2503 unsignedToTempString(comp.length, dst[i .. i + ndigits]);
2505 dst[i .. i + comp.length] = comp[];
2508 dst[i .. i + T.mangleof.length] = T.mangleof[];
2509 i += T.mangleof.length;
2511 static if (hasTypeBackRef)
2512 return reencodeMangled(dst[0 .. i]);
2519 @safe pure nothrow unittest
2521 assert(mangle!int("a.b") == "_D1a1bi");
2522 assert(mangle!(char[])("test.foo") == "_D4test3fooAa");
2523 assert(mangle!(int function(int))("a.b") == "_D1a1bPFiZi");
2526 @safe pure nothrow unittest
2528 static assert(mangle!int("a.b") == "_D1a1bi");
2530 auto buf = new char[](10);
2531 buf = mangle!int("a.b", buf);
2532 assert(buf == "_D1a1bi");
2533 buf = mangle!(char[])("test.foo", buf);
2534 assert(buf == "_D4test3fooAa");
2535 buf = mangle!(real delegate(int))("modµ.dg");
2536 assert(buf == "_D5modµ2dgDFiZe", buf);
2541 * Mangles a D function.
2544 * T = function pointer type.
2545 * fqn = The fully qualified name of the symbol.
2546 * dst = An optional destination buffer.
2549 * The mangled name for a function with function pointer type T and
2550 * the given fully qualified name.
2552 char[] mangleFunc(T:FT*, FT)(return scope const(char)[] fqn, return scope char[] dst = null) @safe pure nothrow if (is(FT == function))
2554 static if (isExternD!FT)
2556 return mangle!FT(fqn, dst);
2558 else static if (hasPlainMangling!FT)
2560 dst.length = fqn.length;
2564 else static if (isExternCPP!FT)
2566 static assert(0, "Can't mangle extern(C++) functions.");
2570 static assert(0, "Can't mangle function with unknown linkage ("~FT.stringof~").");
2574 private enum hasTypeBackRef = (int function(void**,void**)).mangleof[$-4 .. $] == "QdZi";
2576 @safe pure nothrow unittest
2578 assert(mangleFunc!(int function(int))("a.b") == "_D1a1bFiZi");
2579 static if (hasTypeBackRef)
2581 assert(mangleFunc!(int function(Object))("object.Object.opEquals") == "_D6object6Object8opEqualsFCQsZi");
2582 assert(mangleFunc!(int function(Object, Object))("object.Object.opEquals") == "_D6object6Object8opEqualsFCQsQdZi");
2586 auto mngl = mangleFunc!(int function(Object))("object.Object.opEquals");
2587 assert(mngl == "_D6object6Object8opEqualsFC6ObjectZi");
2588 auto remngl = reencodeMangled(mngl);
2589 assert(remngl == "_D6object6Object8opEqualsFCQsZi");
2591 // trigger back tracking with ambiguity on '__T', template or identifier
2592 assert(reencodeMangled("_D3std4conv4conv7__T3std4convi") == "_D3std4convQf7__T3stdQpi");
2595 @safe pure nothrow unittest
2597 int function(lazy int[], ...) fp;
2598 assert(mangle!(typeof(fp))("demangle.test") == "_D8demangle4testPFLAiYi");
2599 assert(mangle!(typeof(*fp))("demangle.test") == "_D8demangle4testFLAiYi");
2602 private template isExternD(FT) if (is(FT == function))
2604 enum isExternD = __traits(getLinkage, FT) == "D";
2607 private template isExternCPP(FT) if (is(FT == function))
2609 enum isExternCPP = __traits(getLinkage, FT) == "C++";
2612 private template hasPlainMangling(FT) if (is(FT == function))
2614 enum lnk = __traits(getLinkage, FT);
2616 enum hasPlainMangling = lnk == "C" || lnk == "Windows" || lnk == "System";
2619 @safe pure nothrow unittest
2621 static extern(D) void fooD();
2622 static extern(C) void fooC();
2623 static extern(Windows) void fooW();
2624 static extern(C++) void fooCPP();
2626 bool check(FT)(bool isD, bool isCPP, bool isPlain)
2628 return isExternD!FT == isD && isExternCPP!FT == isCPP &&
2629 hasPlainMangling!FT == isPlain;
2631 static assert(check!(typeof(fooD))(true, false, false));
2632 static assert(check!(typeof(fooC))(false, false, true));
2633 static assert(check!(typeof(fooW))(false, false, true));
2634 static assert(check!(typeof(fooCPP))(false, true, false));
2636 static assert(__traits(compiles, mangleFunc!(typeof(&fooD))("")));
2637 static assert(__traits(compiles, mangleFunc!(typeof(&fooC))("")));
2638 static assert(__traits(compiles, mangleFunc!(typeof(&fooW))("")));
2639 static assert(!__traits(compiles, mangleFunc!(typeof(&fooCPP))("")));
2643 * C name mangling is done by adding a prefix on some platforms.
2646 enum string cPrefix = "_";
2647 else version (Darwin)
2648 enum string cPrefix = "_";
2650 enum string cPrefix = "";
2652 @safe pure nothrow unittest
2654 immutable string[2][] table =
2656 ["printf", "printf"],
2659 ["_D3fooQeFIAyaZv", "void foo.foo(in immutable(char)[])" ],
2660 ["_D3barQeFIKAyaZv", "void bar.bar(in ref immutable(char)[])" ],
2661 ["_D4test3fooAa", "char[] test.foo"],
2662 ["_D8demangle8demangleFAaZAa", "char[] demangle.demangle(char[])"],
2663 ["_D6object6Object8opEqualsFC6ObjectZi", "int object.Object.opEquals(Object)"],
2664 ["_D4test2dgDFiYd", "double delegate(int, ...) test.dg"],
2665 ["_D4test2dgDxFNfiYd", "double delegate(int, ...) @safe const test.dg"],
2666 //["_D4test58__T9factorialVde67666666666666860140VG5aa5_68656c6c6fVPvnZ9factorialf", ""],
2667 //["_D4test101__T9factorialVde67666666666666860140Vrc9a999999999999d9014000000000000000c00040VG5aa5_68656c6c6fVPvnZ9factorialf", ""],
2668 ["_D4test34__T3barVG3uw3_616263VG3wd3_646566Z1xi", "int test.bar!(\"abc\"w, \"def\"d).x"],
2669 ["_D8demangle4testFLC6ObjectLDFLiZiZi", "int demangle.test(lazy Object, lazy int delegate(lazy int))"],
2670 ["_D8demangle4testFAiXi", "int demangle.test(int[]...)"],
2671 ["_D8demangle4testFAiYi", "int demangle.test(int[], ...)"],
2672 ["_D8demangle4testFLAiXi", "int demangle.test(lazy int[]...)"],
2673 ["_D8demangle4testFLAiYi", "int demangle.test(lazy int[], ...)"],
2674 ["_D6plugin8generateFiiZAya", "immutable(char)[] plugin.generate(int, int)"],
2675 ["_D6plugin8generateFiiZAxa", "const(char)[] plugin.generate(int, int)"],
2676 ["_D6plugin8generateFiiZAOa", "shared(char)[] plugin.generate(int, int)"],
2677 ["_D8demangle3fnAFZ3fnBMFZv", "void demangle.fnA().fnB()"],
2678 ["_D8demangle4mainFZ1S3fnCMFZv", "void demangle.main().S.fnC()"],
2679 ["_D8demangle4mainFZ1S3fnDMFZv", "void demangle.main().S.fnD()"],
2680 ["_D8demangle20__T2fnVAiA4i1i2i3i4Z2fnFZv", "void demangle.fn!([1, 2, 3, 4]).fn()"],
2681 ["_D8demangle10__T2fnVi1Z2fnFZv", "void demangle.fn!(1).fn()"],
2682 ["_D8demangle26__T2fnVS8demangle1SS2i1i2Z2fnFZv", "void demangle.fn!(demangle.S(1, 2)).fn()"],
2683 ["_D8demangle13__T2fnVeeNANZ2fnFZv", "void demangle.fn!(real.nan).fn()"],
2684 ["_D8demangle14__T2fnVeeNINFZ2fnFZv", "void demangle.fn!(-real.infinity).fn()"],
2685 ["_D8demangle13__T2fnVeeINFZ2fnFZv", "void demangle.fn!(real.infinity).fn()"],
2686 ["_D8demangle21__T2fnVHiiA2i1i2i3i4Z2fnFZv", "void demangle.fn!([1:2, 3:4]).fn()"],
2687 ["_D8demangle2fnFNgiZNgi", "inout(int) demangle.fn(inout(int))"],
2688 ["_D8demangle29__T2fnVa97Va9Va0Vu257Vw65537Z2fnFZv", "void demangle.fn!('a', '\\t', \\x00, '\\u0101', '\\U00010001').fn()"],
2689 ["_D2gc11gctemplates56__T8mkBitmapTS3std5range13__T4iotaTiTiZ4iotaFiiZ6ResultZ8mkBitmapFNbNiNfPmmZv",
2690 "nothrow @nogc @safe void gc.gctemplates.mkBitmap!(std.range.iota!(int, int).iota(int, int).Result).mkBitmap(ulong*, ulong)"],
2691 ["_D8serenity9persister6Sqlite69__T15SqlitePersisterTS8serenity9persister6Sqlite11__unittest6FZ4TestZ15SqlitePersister12__T7opIndexZ7opIndexMFmZS8serenity9persister6Sqlite11__unittest6FZ4Test",
2692 "serenity.persister.Sqlite.__unittest6().Test serenity.persister.Sqlite.SqlitePersister!(serenity.persister.Sqlite.__unittest6().Test).SqlitePersister.opIndex!().opIndex(ulong)"],
2693 ["_D8bug100274mainFZ5localMFZi","int bug10027.main().local()"],
2694 ["_D8demangle4testFNhG16gZv", "void demangle.test(__vector(byte[16]))"],
2695 ["_D8demangle4testFNhG8sZv", "void demangle.test(__vector(short[8]))"],
2696 ["_D8demangle4testFNhG4iZv", "void demangle.test(__vector(int[4]))"],
2697 ["_D8demangle4testFNhG2lZv", "void demangle.test(__vector(long[2]))"],
2698 ["_D8demangle4testFNhG4fZv", "void demangle.test(__vector(float[4]))"],
2699 ["_D8demangle4testFNhG2dZv", "void demangle.test(__vector(double[2]))"],
2700 ["_D8demangle4testFNhG4fNhG4fZv", "void demangle.test(__vector(float[4]), __vector(float[4]))"],
2701 ["_D8bug1119234__T3fooS23_D8bug111924mainFZ3bariZ3fooMFZv","void bug11192.foo!(bug11192.main().bar).foo()"],
2702 ["_D13libd_demangle12__ModuleInfoZ", "libd_demangle.__ModuleInfo"],
2703 ["_D15TypeInfo_Struct6__vtblZ", "TypeInfo_Struct.__vtbl"],
2704 ["_D3std5stdio12__ModuleInfoZ", "std.stdio.__ModuleInfo"],
2705 ["_D3std6traits15__T8DemangleTkZ8Demangle6__initZ", "std.traits.Demangle!(uint).Demangle.__init"],
2706 ["_D3foo3Bar7__ClassZ", "foo.Bar.__Class"],
2707 ["_D3foo3Bar6__vtblZ", "foo.Bar.__vtbl"],
2708 ["_D3foo3Bar11__interfaceZ", "foo.Bar.__interface"],
2709 ["_D3foo7__arrayZ", "foo.__array"],
2710 ["_D8link657428__T3fooVE8link65746Methodi0Z3fooFZi", "int link6574.foo!(0).foo()"],
2711 ["_D8link657429__T3fooHVE8link65746Methodi0Z3fooFZi", "int link6574.foo!(0).foo()"],
2712 ["_D4test22__T4funcVAyaa3_610a62Z4funcFNaNbNiNmNfZAya", `pure nothrow @nogc @live @safe immutable(char)[] test.func!("a\x0ab").func()`],
2713 ["_D3foo3barFzkZzi", "cent foo.bar(ucent)"],
2714 ["_D5bug145Class3fooMFNlZPv", "scope void* bug14.Class.foo()"],
2715 ["_D5bug145Class3barMFNjZPv", "return void* bug14.Class.bar()"],
2716 ["_D5bug143fooFMPvZPv", "void* bug14.foo(scope void*)"],
2717 ["_D5bug143barFMNkPvZPv", "void* bug14.bar(scope return void*)"],
2718 ["_D3std5range15__T4iotaTtTtTtZ4iotaFtttZ6Result7opIndexMNgFNaNbNiNfmZNgt",
2719 "inout pure nothrow @nogc @safe inout(ushort) std.range.iota!(ushort, ushort, ushort).iota(ushort, ushort, ushort).Result.opIndex(ulong)"],
2720 ["_D3std6format77__T6getNthVAyaa13_696e7465676572207769647468S233std6traits10isIntegralTiTkTkZ6getNthFNaNfkkkZi",
2721 "pure @safe int std.format.getNth!(\"integer width\", std.traits.isIntegral, int, uint, uint).getNth(uint, uint, uint)"],
2722 ["_D3std11parallelism42__T16RoundRobinBufferTDFKAaZvTDxFNaNdNeZbZ16RoundRobinBuffer5primeMFZv",
2723 "void std.parallelism.RoundRobinBuffer!(void delegate(ref char[]), bool delegate() pure @property @trusted const).RoundRobinBuffer.prime()"],
2724 ["_D6mangle__T8fun21753VSQv6S21753S1f_DQBj10__lambda71MFNaNbNiNfZvZQCbQp",
2725 "void function() pure nothrow @nogc @safe mangle.fun21753!(mangle.S21753(mangle.__lambda71())).fun21753"],
2727 ["_D3std9algorithm9iteration__T9MapResultSQBmQBlQBe005stripTAAyaZQBi7opSliceMFNaNbNiNfmmZSQDiQDhQDa__TQCtSQDyQDxQDq00QCmTQCjZQDq",
2728 "pure nothrow @nogc @safe std.algorithm.iteration.MapResult!(std.algorithm.iteration.__anonymous.strip, "
2729 ~"immutable(char)[][]).MapResult std.algorithm.iteration.MapResult!(std.algorithm.iteration.strip, immutable(char)[][]).MapResult.opSlice(ulong, ulong)"],
2732 ["_D4core4stdc5errnoQgFZi", "int core.stdc.errno.errno()"], // identifier back reference
2733 ["_D4testFS10structnameQnZb", "bool test(structname, structname)"], // type back reference
2734 ["_D3std11parallelism__T4TaskS8unittest3cmpTAyaTQeZQBb6__dtorMFNfZv",
2735 "@safe void std.parallelism.Task!(unittest.cmp, immutable(char)[], immutable(char)[]).Task.__dtor()"],
2736 // 1.s.s.foo from https://issues.dlang.org/show_bug.cgi?id=15831
2737 ["_D13testexpansion44__T1sTS13testexpansion8__T1sTiZ1sFiZ6ResultZ1sFS13testexpansion8__T1sTiZ1sFiZ6ResultZ6Result3fooMFNaNfZv",
2738 "pure @safe void testexpansion.s!(testexpansion.s!(int).s(int).Result).s(testexpansion.s!(int).s(int).Result).Result.foo()"],
2739 ["_D13testexpansion__T1sTSQw__TQjTiZQoFiZ6ResultZQBbFQBcZQq3fooMFNaNfZv",
2740 "pure @safe void testexpansion.s!(testexpansion.s!(int).s(int).Result).s(testexpansion.s!(int).s(int).Result).Result.foo()"],
2741 // formerly ambiguous on 'V', template value argument or pascal function
2742 // pascal functions have now been removed (in v2.095.0)
2743 ["_D3std4conv__T7enumRepTyAaTEQBa12experimental9allocator15building_blocks15stats_collector7OptionsVQCti64ZQDnyQDh",
2744 "immutable(char[]) std.conv.enumRep!(immutable(char[]), std.experimental.allocator.building_blocks.stats_collector.Options, 64).enumRep"],
2745 // symbol back reference to location with symbol back reference
2746 ["_D3std12experimental9allocator6common__T10reallocateTSQCaQBzQBo15building_blocks17kernighan_ritchie__T8KRRegionTSQEhQEgQDvQCh14null_allocator13NullAllocatorZQCdZQErFNaNbNiKQEpKAvmZb",
2747 "pure nothrow @nogc bool std.experimental.allocator.common.reallocate!(std.experimental.allocator.building_blocks.kernighan_ritchie.KRRegion!("
2748 ~"std.experimental.allocator.building_blocks.null_allocator.NullAllocator).KRRegion).reallocate(ref "
2749 ~"std.experimental.allocator.building_blocks.kernighan_ritchie.KRRegion!(std.experimental.allocator.building_blocks.null_allocator.NullAllocator).KRRegion, ref void[], ulong)"],
2750 ["_D3std9exception__T11doesPointToTASQBh5regex8internal2ir10NamedGroupTQBkTvZQCeFNaNbNiNeKxASQDlQCeQCbQBvQBvKxQtZb",
2751 "pure nothrow @nogc @trusted bool std.exception.doesPointTo!(std.regex.internal.ir.NamedGroup[], "
2752 ~"std.regex.internal.ir.NamedGroup[], void).doesPointTo(ref const(std.regex.internal.ir.NamedGroup[]), ref const(std.regex.internal.ir.NamedGroup[]))"],
2753 ["_D3std9algorithm9iteration__T14SplitterResultS_DQBu3uni7isWhiteFNaNbNiNfwZbTAyaZQBz9__xtoHashFNbNeKxSQDvQDuQDn__TQDgS_DQEnQCtQCsQCnTQCeZQEdZm",
2754 "nothrow @trusted ulong std.algorithm.iteration.SplitterResult!(std.uni.isWhite(dchar), immutable(char)[]).SplitterResult."
2755 ~"__xtoHash(ref const(std.algorithm.iteration.SplitterResult!(std.uni.isWhite, immutable(char)[]).SplitterResult))"],
2756 ["_D3std8typecons__T7TypedefTCQBaQz19__unittestL6513_208FNfZ7MyClassVQBonVAyanZQCh6__ctorMFNaNbNcNiNfQCuZSQDyQDx__TQDrTQDmVQDqnVQCcnZQEj",
2757 "pure nothrow ref @nogc @safe std.typecons.Typedef!(std.typecons.__unittestL6513_208().MyClass, null, null).Typedef "
2758 ~"std.typecons.Typedef!(std.typecons.__unittestL6513_208().MyClass, null, null).Typedef.__ctor(std.typecons.__unittestL6513_208().MyClass)"],
2759 ["_D3std6getopt__TQkTAyaTDFNaNbNiNfQoZvTQtTDQsZQBnFNfKAQBiQBlQBkQBrQyZSQCpQCo12GetoptResult",
2760 "@safe std.getopt.GetoptResult std.getopt.getopt!(immutable(char)[], void delegate(immutable(char)[]) pure nothrow @nogc @safe, "
2761 ~"immutable(char)[], void delegate(immutable(char)[]) pure nothrow @nogc @safe)."
2762 ~"getopt(ref immutable(char)[][], immutable(char)[], void delegate(immutable(char)[]) pure nothrow @nogc @safe, "
2763 ~"immutable(char)[], void delegate(immutable(char)[]) pure nothrow @nogc @safe)"],
2764 ["_D3std5regex8internal9kickstart__T7ShiftOrTaZQl11ShiftThread__T3setS_DQCqQCpQCmQCg__TQBzTaZQCfQBv10setInvMaskMFNaNbNiNfkkZvZQCjMFNaNfwZv",
2765 "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)"],
2766 ["_D3std5stdio4File__T8lockImplX10LockFileExTykZQBaMFmmykZi", // C function as template alias parameter
2767 "int std.stdio.File.lockImpl!(LockFileEx, immutable(uint)).lockImpl(ulong, ulong, immutable(uint))"],
2768 // back reference for type in template AA parameter value
2769 ["_D3std9algorithm9iteration__T12FilterResultSQBq8typecons__T5TupleTiVAyaa1_61TiVQla1_62TiVQva1_63ZQBm__T6renameVHiQBtA2i0a1_63i2a1_61ZQBeMFNcZ9__lambda1TAiZQEw9__xtoHashFNbNeKxSQGsQGrQGk__TQGdSQHiQFs__TQFmTiVQFja1_61TiVQFua1_62TiVQGfa1_63ZQGx__TQFlVQFhA2i0a1_63i2a1_61ZQGjMFNcZQFfTQEyZQJvZm",
2770 `nothrow @trusted ulong std.algorithm.iteration.FilterResult!(std.typecons.Tuple!(int, "a", int, "b", int, "c").`
2771 ~`Tuple.rename!([0:"c", 2:"a"]).rename().__lambda1, int[]).FilterResult.__xtoHash(ref const(std.algorithm.iteration.`
2772 ~`FilterResult!(std.typecons.Tuple!(int, "a", int, "b", int, "c").Tuple.rename!([0:"c", 2:"a"]).rename().__lambda1, int[]).FilterResult))`],
2774 ["_D4test4rrs1FKPiZv", "void test.rrs1(ref int*)"],
2775 ["_D4test4rrs1FMNkJPiZv", "void test.rrs1(scope return out int*)"],
2776 ["_D4test4rrs1FMNkKPiZv", "void test.rrs1(scope return ref int*)"],
2777 ["_D4test4rrs1FNkJPiZv", "void test.rrs1(return out int*)"],
2778 ["_D4test4rrs1FNkKPiZv", "void test.rrs1(return ref int*)"],
2779 ["_D4test4rrs1FNkMJPiZv", "void test.rrs1(return scope out int*)"],
2780 ["_D4test4rrs1FNkMKPiZv", "void test.rrs1(return scope ref int*)"],
2781 ["_D4test4rrs1FNkMPiZv", "void test.rrs1(return scope int*)"],
2783 // `scope` and `return` combinations
2784 ["_D3foo3Foo3barMNgFNjNlNfZNgPv", "inout return scope @safe inout(void*) foo.Foo.bar()"],
2785 ["_D3foo3FooQiMNgFNlNfZv", "inout scope @safe void foo.Foo.foo()"],
2786 ["_D3foo3Foo4foorMNgFNjNfZv", "inout return @safe void foo.Foo.foor()"],
2787 ["_D3foo3Foo3rabMNgFNlNjNfZv", "inout scope return @safe void foo.Foo.rab()"],
2789 // Hex float digit overflow
2790 ["_D3foo__T1fVdeFA3D0FBFB72A3C33FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", "_D3foo__T1fVdeFA3D0FBFB72A3C33FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"],
2794 template staticIota(int x)
2796 template Seq(T...){ alias Seq = T; }
2799 alias staticIota = Seq!();
2801 alias staticIota = Seq!(staticIota!(x - 1), x - 1);
2803 foreach ( i, name; table )
2805 auto r = demangle( name[0] );
2806 assert( r == name[1],
2807 "demangled `" ~ name[0] ~ "` as `" ~ r ~ "` but expected `" ~ name[1] ~ "`");
2809 foreach ( i; staticIota!(table.length) )
2811 enum r = demangle( table[i][0] );
2812 static assert( r == table[i][1],
2813 "demangled `" ~ table[i][0] ~ "` as `" ~ r ~ "` but expected `" ~ table[i][1] ~ "`");
2817 // https://issues.dlang.org/show_bug.cgi?id=18531
2818 auto symbol = `_D3std3uni__T6toCaseS_DQvQt12toLowerIndexFNaNbNiNewZtVii1043S_DQCjQCi10toLowerTabFNaNbNiNemZwSQDo5ascii7toLowerTAyaZQDzFNaNeQmZ14__foreachbody2MFNaNeKmKwZ14__foreachbody3MFNaNeKwZi`;
2819 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)`;
2820 auto dst = new char[200];
2821 auto ret = demangle( symbol, dst);
2822 assert( ret == demangled );
2828 // https://issues.dlang.org/show_bug.cgi?id=18300
2829 string s = demangle.mangleof;
2832 char[] buf = new char[i];
2833 auto ds = demangle(s, buf);
2834 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*)" ||
2835 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*)" ||
2836 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*)" ||
2837 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);
2843 // https://issues.dlang.org/show_bug.cgi?id=18300
2845 string expected = "int ";
2846 foreach (_; 0..10_000)
2853 assert(s.demangle == expected);
2855 // https://issues.dlang.org/show_bug.cgi?id=23562
2856 assert(demangle("_Zv") == "_Zv");
2859 // https://issues.dlang.org/show_bug.cgi?id=22235
2862 enum parent = __MODULE__ ~ '.' ~ __traits(identifier, __traits(parent, {}));
2864 static noreturn abort() { assert(false); }
2865 assert(demangle(abort.mangleof) == "pure nothrow @nogc @safe noreturn " ~ parent ~ "().abort()");
2867 static void accept(noreturn) {}
2868 assert(demangle(accept.mangleof) == "pure nothrow @nogc @safe void " ~ parent ~ "().accept(noreturn)");
2870 static void templ(T)(T, T) {}
2871 assert(demangle(templ!noreturn.mangleof) == "pure nothrow @nogc @safe void " ~ parent ~ "().templ!(noreturn).templ(noreturn, noreturn)");
2873 static struct S(T) {}
2874 static void aggr(S!noreturn) { assert(0); }
2875 assert(demangle(aggr.mangleof) == "pure nothrow @nogc @safe void " ~ parent ~ "().aggr(" ~ parent ~ "().S!(noreturn).S)");
2879 * Expand an OMF, DMD-generated compressed identifier into its full form
2881 * This function only has a visible effect for OMF binaries (Win32),
2882 * as compression is otherwise not used.
2884 * See_Also: `compiler/src/dmd/backend/compress.d`
2886 string decodeDmdString( const(char)[] ln, ref size_t p ) nothrow pure @safe
2891 // decompress symbol
2892 while ( p < ln.length )
2894 int ch = cast(ubyte) ln[p++];
2895 if ( (ch & 0xc0) == 0xc0 )
2897 zlen = (ch & 0x7) + 1;
2898 zpos = ((ch >> 3) & 7) + 1; // + zlen;
2899 if ( zpos > s.length )
2901 s ~= s[$ - zpos .. $ - zpos + zlen];
2903 else if ( ch >= 0x80 )
2905 if ( p >= ln.length )
2907 int ch2 = cast(ubyte) ln[p++];
2908 zlen = (ch2 & 0x7f) | ((ch & 0x38) << 4);
2909 if ( p >= ln.length )
2911 int ch3 = cast(ubyte) ln[p++];
2912 zpos = (ch3 & 0x7f) | ((ch & 7) << 7);
2913 if ( zpos > s.length )
2915 s ~= s[$ - zpos .. $ - zpos + zlen];
2917 else if ( Demangle!().isAlpha(cast(char)ch) || Demangle!().isDigit(cast(char)ch) || ch == '_' )
2928 // locally purified for internal use here only
2931 pure @trusted @nogc nothrow pragma(mangle, "fakePureReprintReal") void pureReprintReal(char[] nptr);
2933 void fakePureReprintReal(char[] nptr)
2935 import core.stdc.stdlib : strtold;
2936 import core.stdc.stdio : snprintf;
2937 import core.stdc.errno : errno;
2940 real val = strtold(nptr.ptr, null);
2941 snprintf(nptr.ptr, nptr.length, "%#Lg", val);
2946 private struct ManglingFlagInfo
2948 /// The flag value to use
2951 /// Human-readable representation
2955 private enum TypeCtor : ushort {
2960 Immutable = (1 << 2),
2967 private immutable ManglingFlagInfo[] typeCtors = [
2968 ManglingFlagInfo(TypeCtor.Immutable, "immutable"),
2969 ManglingFlagInfo(TypeCtor.Shared, "shared"),
2970 ManglingFlagInfo(TypeCtor.InOut, "inout"),
2971 ManglingFlagInfo(TypeCtor.Const, "const"),
2974 private enum FuncAttributes : ushort {
2983 Property = (1 << 4),
2997 /// Their order matter
2998 ReturnScope = (1 << 11),
2999 ScopeReturn = (1 << 12),
3002 // The order in which we process is the same as in compiler/dmd/src/dmangle.d
3003 private immutable ManglingFlagInfo[] funcAttrs = [
3004 ManglingFlagInfo(FuncAttributes.Pure, "pure"),
3005 ManglingFlagInfo(FuncAttributes.Nothrow, "nothrow"),
3006 ManglingFlagInfo(FuncAttributes.Ref, "ref"),
3007 ManglingFlagInfo(FuncAttributes.Property, "@property"),
3008 ManglingFlagInfo(FuncAttributes.NoGC, "@nogc"),
3010 ManglingFlagInfo(FuncAttributes.ReturnScope, "return scope"),
3011 ManglingFlagInfo(FuncAttributes.ScopeReturn, "scope return"),
3013 ManglingFlagInfo(FuncAttributes.Return, "return"),
3014 ManglingFlagInfo(FuncAttributes.Scope, "scope"),
3016 ManglingFlagInfo(FuncAttributes.Live, "@live"),
3017 ManglingFlagInfo(FuncAttributes.Trusted, "@trusted"),
3018 ManglingFlagInfo(FuncAttributes.Safe, "@safe"),
3021 private string toStringConsume (immutable ManglingFlagInfo[] infos, ref ushort base)
3022 @safe pure nothrow @nogc
3024 foreach (const ref info; infos)
3026 if ((base & info.flag) == info.flag)
3035 private shared CXX_DEMANGLER __cxa_demangle;
3039 * a CXX_DEMANGLER if a C++ stdlib is loaded
3042 CXX_DEMANGLER getCXXDemangler() nothrow @trusted
3044 import core.atomic : atomicLoad, atomicStore;
3045 if (__cxa_demangle is null)
3048 import core.sys.posix.dlfcn : dlsym;
3049 version (DragonFlyBSD) import core.sys.dragonflybsd.dlfcn : RTLD_DEFAULT;
3050 version (FreeBSD) import core.sys.freebsd.dlfcn : RTLD_DEFAULT;
3051 version (linux) import core.sys.linux.dlfcn : RTLD_DEFAULT;
3052 version (NetBSD) import core.sys.netbsd.dlfcn : RTLD_DEFAULT;
3053 version (OpenBSD) import core.sys.openbsd.dlfcn : RTLD_DEFAULT;
3054 version (Darwin) import core.sys.darwin.dlfcn : RTLD_DEFAULT;
3055 version (Solaris) import core.sys.solaris.dlfcn : RTLD_DEFAULT;
3057 if (auto found = cast(CXX_DEMANGLER) dlsym(RTLD_DEFAULT, "__cxa_demangle"))
3058 atomicStore(__cxa_demangle, found);
3061 if (__cxa_demangle is null)
3063 static extern(C) char* _(const char* mangled_name, char* output_buffer,
3064 size_t* length, int* status) nothrow pure @trusted
3069 atomicStore(__cxa_demangle, &_);
3072 return atomicLoad(__cxa_demangle);
3076 * Demangles C++ mangled names. If it is not a C++ mangled name, it
3077 * returns its argument name.
3080 * buf = The string to demangle.
3081 * __cxa_demangle = C++ demangler
3082 * dst = An optional destination buffer.
3085 * The demangled name or the original string if the name is not a mangled
3088 private char[] demangleCXX(return scope const(char)[] buf, CXX_DEMANGLER __cxa_demangle, return scope char[] dst = null,) nothrow pure @trusted
3090 char[] c_string = dst; // temporarily use dst buffer if possible
3091 c_string.length = buf.length + 1;
3092 c_string[0 .. buf.length] = buf[0 .. buf.length];
3093 c_string[buf.length] = '\0';
3096 size_t demangled_length;
3097 auto demangled = __cxa_demangle(&c_string[0], null, &demangled_length, &status);
3100 pureFree(cast(void*) demangled);
3104 dst.length = demangled_length;
3105 dst[] = demangled[0 .. demangled_length];
3109 dst.length = buf.length;
3114 private struct Buffer
3116 enum size_t minSize = 4000;
3123 public alias opDollar = len;
3125 public size_t length () const scope @safe pure nothrow @nogc
3130 public BufSlice opSlice (size_t from, size_t to)
3131 return scope @safe pure nothrow @nogc
3133 return bslice(from, to);
3136 static bool contains(scope const(char)[] a, scope const BufSlice b) @safe nothrow
3139 b.from < a.length &&
3143 char[] copyInput(scope const(char)[] buf)
3144 return scope nothrow
3146 if (dst.length < buf.length)
3147 dst.length = buf.length;
3148 char[] r = dst[0 .. buf.length];
3153 private void checkAndStretchBuf(size_t len_to_add) scope nothrow
3155 const required = len + len_to_add;
3157 if (required > dst.length)
3158 dst.length = dst.length + len_to_add;
3161 // move val to the end of the dst buffer
3162 BufSlice shift(scope const BufSlice val) return scope nothrow
3164 version (DigitalMars) pragma(inline, false); // tame dmd inliner
3168 const ptrdiff_t s = val.from;
3169 const size_t f = len;
3171 assert(contains( dst[0 .. len], val ),
3172 "\ndst=\""~dst[0 .. len]~"\"\n"~
3173 "val=\""~val.getSlice~"\"\n"
3176 checkAndStretchBuf(val.length);
3178 // store value temporary over len index
3179 dst[len .. len + val.length] = val.getSlice();
3181 // shift all chars including temporary saved above
3182 // if buf was allocated above it will be leave for further usage
3183 for (size_t p = s; p < f; p++)
3184 dst[p] = dst[p + val.length];
3186 return bslice(len - val.length, len);
3189 return bslice_empty;
3192 // remove val from dst buffer
3193 void remove(scope BufSlice val) scope nothrow
3195 version (DigitalMars) pragma(inline, false); // tame dmd inliner
3199 assert( contains( dst[0 .. len], val ) );
3201 assert( len >= val.length && len <= dst.length );
3203 for (size_t p = val.from; p < len; p++)
3204 dst[p] = dst[p + val.length];
3208 void append(scope const(char)[] val) scope nothrow
3210 version (DigitalMars) pragma(inline, false); // tame dmd inliner
3215 dst.length = minSize;
3217 debug(info) printf( "appending (%.*s)\n", cast(int) val.length, val.ptr );
3219 checkAndStretchBuf(val.length);
3221 // data is already not in place?
3222 if ( &dst[len] != &val[0] )
3223 dst[len .. len + val.length] = val[];
3231 private scope bslice(size_t from, size_t to) nothrow
3233 return BufSlice(dst, from, to);
3236 private static scope bslice_empty() nothrow
3238 return BufSlice.init;
3242 private struct BufSlice
3254 this(return scope char[] buf) scope nothrow @nogc
3259 this(return scope char[] buf, size_t from, size_t to, bool lastArgIsLen = false) scope nothrow @nogc
3265 this.to = from + to;
3281 auto getSlice() inout nothrow scope { return buf[from .. to]; }
3282 size_t length() const scope { return to - from; }