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