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