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