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