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