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