1 /** 2 * D symbol name demangling 3 * 4 * Attempts to demangle D symbols generated by the DMD frontend. 5 * (Which is not always technically possible.) 6 * 7 * A sample program demangling the names passed as arguments. 8 * --- 9 * module demangle; 10 * import tango.core.tools.Demangler; 11 * import tango.io.Stdout; 12 * 13 * void usage(){ 14 * Stdout("demangle [--help] [--level 0-9] mangledName1 [mangledName2...]").newline; 15 * } 16 * 17 * int main(const(char)[][]args){ 18 * uint start=1; 19 * if (args.length>1) { 20 * if (args[start]=="--help"){ 21 * usage(); 22 * ++start; 23 * } 24 * if (args[start]=="--level"){ 25 * ++start; 26 * if (args.length==start || args[start].length!=1 || args[start][0]<'0' || 27 * args[start][0]>'9') { 28 * Stdout("invalid level '")((args.length==start)?"*missing*":args[start]) 29 * ("' (must be 0-9)").newline; 30 * usage(); 31 * return 2; 32 * } 33 * demangler.verbosity=args[start+1][0]-'0'; 34 * ++start; 35 * } 36 * } else { 37 * usage(); 38 * return 0; 39 * } 40 * foreach (n;args[start..$]){ 41 * Stdout(demangler.demangle(n)).newline; 42 * } 43 * return 0; 44 * } 45 * --- 46 * Copyright: Copyright (C) 2007-2008 Zygfryd (aka Hxal), Fawzi. All rights reserved. 47 * License: tango license, apache 2.0 48 * Authors: Zygfryd (aka Hxal), Fawzi 49 * 50 */ 51 52 module tango.core.tools.Demangler; 53 54 version(TangoDemangler) 55 { 56 import tango.core.Traits: ctfe_i2a; 57 import tango.stdc.string: memmove,memcpy; 58 59 debug(traceDemangler) import tango.io.Stdout; 60 61 62 63 version(DigitalMars) version(Windows) { 64 bool isMD5Hashed(const(char)[] name) { 65 if (name.length < 34 || (name.length >= 2 && name[0..2] != "_D")) { 66 return false; 67 } 68 69 foreach (c; name[$-32..$]) { 70 if ((c < '0' || c > '9') && (c < 'A' || c > 'F')) { 71 return false; 72 } 73 } 74 75 return true; 76 } 77 78 79 const(char)[] decompressOMFSymbol(const(char)[] garbled, char[]* buf) { 80 int ungarbledLength = 0; 81 bool compressed = false; 82 83 for (int ci = 0; ci < garbled.length; ++ci) { 84 char c = garbled[ci]; 85 if (0 == (c & 0x80)) { 86 ++ungarbledLength; 87 } else { 88 compressed = true; 89 int matchLen = void; 90 91 if (c & 0x40) { 92 matchLen = (c & 0b111) + 1; 93 } else { 94 if (ci+2 >= garbled.length) { 95 return garbled; 96 } else { 97 matchLen = cast(int)(c & 0x38) << 4; 98 matchLen += garbled[ci+1] & ~0x80; 99 ci += 2; 100 } 101 } 102 103 ungarbledLength += matchLen; 104 } 105 } 106 107 if (!compressed || ungarbledLength > (*buf).length) { 108 return garbled; 109 } else { 110 char[] ungarbled = (*buf)[$-ungarbledLength..$]; 111 *buf = (*buf)[0..$-ungarbledLength]; 112 int ui = 0; 113 114 for (int ci = 0; ci < garbled.length; ++ci) { 115 char c = garbled[ci]; 116 if (0 == (c & 0x80)) { 117 ungarbled[ui++] = c; 118 } else { 119 int matchOff = void; 120 int matchLen = void; 121 122 if (c & 0x40) { 123 matchOff = ((c >> 3) & 0b111) + 1; 124 matchLen = (c & 0b111) + 1; 125 } else { 126 matchOff = cast(int)(c & 0b111) << 7; 127 matchLen = cast(int)(c & 0x38) << 4; 128 matchLen += garbled[ci+1] & ~0x80; 129 matchOff += garbled[ci+2] & ~0x80; 130 ci += 2; 131 } 132 133 int matchStart = ui - matchOff; 134 if (matchStart + matchLen > ui) { 135 // fail 136 return garbled; 137 } 138 139 const(char)[] match = ungarbled[matchStart .. matchStart + matchLen]; 140 ungarbled[ui .. ui+matchLen] = match; 141 ui += matchLen; 142 } 143 } 144 145 return ungarbled; 146 } 147 } 148 } 149 150 151 /// decompresses a symbol and returns the full symbol, and possibly a reduced buffer space 152 /// (does something only on windows with DMD.) 153 const(char)[] decompressSymbol(const(char)[] func,char[] *buf){ 154 version(DigitalMars) version(Windows){ 155 if (isMD5Hashed(func)) { 156 func = func[0..$-32]; 157 } 158 func = decompressOMFSymbol(func, buf); 159 } 160 return func; 161 } 162 163 uint toUint(const(char)[] s){ 164 uint res=0; 165 for (int i=0;i<s.length;++i){ 166 if (s[i]>='0'&& s[i]<='9'){ 167 res*=10; 168 res+=s[i]-'0'; 169 } else { 170 assert(false); 171 } 172 } 173 return res; 174 } 175 176 /** 177 * Flexible demangler. 178 * Attempts to demangle D symbols generated by the DMD frontend. 179 * (Which is not always technically possible.) 180 */ 181 public class Demangler 182 { 183 /** How deeply to recurse printing template parameters, 184 * for depths greater than this, an ellipsis is used. */ 185 uint templateExpansionDepth = 1; 186 187 /** Skip default members of templates (sole members named after 188 * the template.) */ 189 bool foldDefaults = false; 190 191 /** Print types of functions being part of the main symbol. */ 192 bool expandFunctionTypes = false; 193 194 /** For composite types, print the kind (class|struct|etc.) of the type. */ 195 bool printTypeKind = false; 196 197 /** Sets the verbosity level of the demangler (template expansion level,...) */ 198 public void verbosity (uint level) 199 { 200 switch (level) 201 { 202 case 0: 203 templateExpansionDepth = 0; 204 expandFunctionTypes = false; 205 printTypeKind = false; 206 break; 207 208 case 1: 209 templateExpansionDepth = 1; 210 expandFunctionTypes = false; 211 printTypeKind = false; 212 break; 213 214 case 2: 215 templateExpansionDepth = 1; 216 expandFunctionTypes = false; 217 printTypeKind = true; 218 break; 219 220 case 3: 221 templateExpansionDepth = 1; 222 expandFunctionTypes = true; 223 printTypeKind = true; 224 break; 225 226 default: 227 templateExpansionDepth = level - 2; 228 expandFunctionTypes = true; 229 printTypeKind = true; 230 } 231 } 232 233 /** Creates a demangler. */ 234 this () 235 { 236 verbosity (1); 237 } 238 239 /** Creates a demangler with the given verbosity level. */ 240 this (uint verbosityLevel) 241 { 242 verbosity (verbosityLevel); 243 } 244 245 /** Demangles the given string. */ 246 public inout(char)[] demangle (inout(char)[] input) 247 { 248 // Special case for "_Dmain" 249 if (input == "_Dmain") 250 return "main"; 251 char[4096] buf=void; 252 auto res=DemangleInstance(this,input,buf); 253 if (res.mangledName() && res.input.length==0){ 254 return cast(inout(char)[])res.slice().dup; 255 } else { 256 if (res.slice().length) res.output.append(" "); 257 if (res.type() && res.input.length==0){ 258 return cast(inout(char)[])res.slice().dup; 259 } else { 260 return input; 261 } 262 } 263 } 264 265 /** Demangles the given string using output to hold the result. */ 266 public inout(char)[] demangle (inout(char)[] input, char[] output) 267 { 268 // Special case for "_Dmain" 269 if (input == "_Dmain") 270 return "main"; 271 auto res=DemangleInstance(this,input,output); 272 if (res.mangledName () && res.input.length==0) { 273 return cast(inout(char)[])res.slice(); 274 } else { 275 if (res.slice().length) res.output.append(" "); 276 if (res.type() && res.input.length==0) { 277 return cast(inout(char)[])res.slice(); 278 } else { 279 return input; 280 } 281 } 282 } 283 284 /// This represents a single demangling request, and is the place where the real work is done 285 /// some more cleanup would probably be in order (maybe remove Buffer.) 286 struct DemangleInstance{ 287 debug(traceDemangler) private const(char)[][] _trace; 288 private const(char)[] input; 289 private uint _templateDepth; 290 Buffer output; 291 Demangler prefs; 292 293 struct BufState{ 294 DemangleInstance* dem; 295 const(char)[] input; 296 size_t len; 297 static BufState opCall(ref DemangleInstance dem){ 298 BufState res; 299 res.dem=&dem; 300 res.len=dem.output.length; 301 res.input=dem.input; 302 return res; 303 } 304 // resets input and output buffers and returns false 305 bool reset(){ 306 dem.output.length=len; 307 dem.input=input; 308 return false; 309 } 310 // resets only the output buffer and returns false 311 bool resetOutput(){ 312 dem.output.length=len; 313 return false; 314 } 315 const(char)[] sliceFrom(){ 316 return dem.output.data[len..dem.output.length]; 317 } 318 } 319 320 BufState checkpoint(){ 321 return BufState(this); 322 } 323 324 static DemangleInstance opCall(Demangler prefs,const(char)[] input,const(char)[] output){ 325 char[] buff = new char[output.length]; 326 input = decompressSymbol(input, &buff); 327 output = buff; 328 DemangleInstance res; 329 res.prefs=prefs; 330 res.input=input; 331 res._templateDepth=0; 332 res.output.data=buff; 333 debug(traceDemangler) res._trace=null; 334 return res; 335 } 336 337 debug (traceDemangler) 338 { 339 private void trace (const(char)[] where) 340 { 341 if (_trace.length > 500) 342 throw new Exception ("Infinite recursion"); 343 344 int len=_trace.length; 345 const(char)[] spaces = " "; 346 spaces=spaces[0 .. ((len<spaces.length)?len:spaces.length)]; 347 if (input.length < 50) 348 Stdout.formatln ("{}{} : {{{}}", spaces, where, input); 349 else 350 Stdout.formatln ("{}{} : {{{}}", spaces, where, input[0..50]); 351 _trace ~= where; 352 } 353 354 private void report (T...) (const(char)[] fmt, T args) 355 { 356 int len=_trace.length; 357 const(char)[] spaces = " "; 358 spaces=spaces[0 .. ((len<spaces.length)?len:spaces.length)]; 359 Stdout (spaces); 360 Stdout.formatln (fmt, args); 361 } 362 363 private void trace (bool result) 364 { 365 //auto tmp = _trace[$-1]; 366 _trace = _trace[0..$-1]; 367 int len=_trace.length; 368 const(char)[] spaces = " "; 369 spaces=spaces[0 .. ((len<spaces.length)?len:spaces.length)]; 370 Stdout(spaces); 371 if (!result) 372 Stdout.formatln ("fail"); 373 else 374 Stdout.formatln ("success"); 375 } 376 } 377 378 const(char)[] slice(){ 379 return output.slice(); 380 } 381 382 private const(char)[] consume (uint amt) 383 { 384 const(char)[] tmp = input[0 .. amt]; 385 input = input[amt .. $]; 386 return tmp; 387 } 388 389 bool mangledName () 390 out (result) 391 { 392 debug(traceDemangler) trace (result); 393 } 394 body 395 { 396 debug(traceDemangler) trace ("mangledName"); 397 398 if (input.length<2) 399 return false; 400 if (input[0]=='D'){ 401 consume(1); 402 } else if (input[0..2] == "_D") { 403 consume(2); 404 } else { 405 return false; 406 } 407 408 if (! typedqualifiedName ()) 409 return false; 410 411 if (input.length > 0) { 412 auto pos1=checkpoint(); 413 output.append("<"); 414 if (! type ()) 415 pos1.reset(); // return false?? 416 else if (prefs.printTypeKind){ 417 output.append(">"); 418 } else { 419 pos1.resetOutput(); 420 } 421 } 422 423 //Stdout.formatln ("mangledName={}", namebuf.slice); 424 425 return true; 426 } 427 428 bool typedqualifiedName () 429 out (result) 430 { 431 debug(traceDemangler) trace (result); 432 } 433 body 434 { 435 debug(traceDemangler) trace ("typedqualifiedName"); 436 437 auto posCar=checkpoint(); 438 if (! symbolName ()) 439 return false; 440 const(char)[] car=posCar.sliceFrom(); 441 442 // undocumented 443 auto pos=checkpoint(); 444 output.append ("{"); 445 if (typeFunction ()){ 446 if (!prefs. expandFunctionTypes){ 447 pos.resetOutput(); 448 } else { 449 output.append ("}"); 450 } 451 } else { 452 pos.reset(); 453 } 454 455 pos=checkpoint(); 456 output.append ("."); 457 if (typedqualifiedName ()) 458 { 459 if (prefs.foldDefaults && car.length<pos.sliceFrom().length && 460 car==pos.sliceFrom()[1..car.length+1]){ 461 memmove(&output.data[posCar.len],&output.data[pos.len+1],output.length-pos.len); 462 output.length+=posCar.len-pos.len-1; 463 } 464 } else { 465 pos.reset(); 466 } 467 468 return true; 469 } 470 471 bool qualifiedName (bool aliasHack = false) 472 out (result) 473 { 474 debug(traceDemangler) trace (result); 475 } 476 body 477 { 478 debug(traceDemangler) trace (aliasHack ? "qualifiedNameAH" : "qualifiedName"); 479 480 auto pos=checkpoint(); 481 if (! symbolName (aliasHack)) 482 return false; 483 const(char)[] car=pos.sliceFrom(); 484 485 auto pos1=checkpoint(); 486 output.append ("."); 487 if (typedqualifiedName ()) 488 { 489 const(char)[] cdr=pos1.sliceFrom()[1..$]; 490 if (prefs.foldDefaults && cdr.length>=car.length && cdr[0..car.length]==car){ 491 memmove(&output.data[pos.len],&output.data[pos1.len+1],output.length-pos1.len); 492 output.length+=pos.len-pos1.len-1; 493 } 494 } else { 495 pos1.reset(); 496 } 497 498 return true; 499 } 500 501 bool symbolName ( bool aliasHack = false) 502 out (result) 503 { 504 debug(traceDemangler) trace (result); 505 } 506 body 507 { 508 debug(traceDemangler) trace (aliasHack ? "symbolNameAH" : "symbolName"); 509 510 // if (templateInstanceName (output)) 511 // return true; 512 513 if (aliasHack){ 514 if (lNameAliasHack ()) 515 return true; 516 } 517 else 518 { 519 if (lName ()) 520 return true; 521 } 522 523 return false; 524 } 525 526 bool lName () 527 out (result) 528 { 529 debug(traceDemangler) trace (result); 530 } 531 body 532 { 533 debug(traceDemangler) trace ("lName"); 534 auto pos=checkpoint(); 535 uint chars; 536 if (! number (chars)) 537 return false; 538 539 const(char)[] original = input; 540 version(all){ 541 if (input.length < chars) { 542 // this may happen when the symbol gets hashed by MD5 543 input = null; 544 return true; // try to continue 545 } 546 } 547 548 input = input[0 .. chars]; 549 size_t len = input.length; 550 if (templateInstanceName()) 551 { 552 input = original[len - input.length .. $]; 553 return true; 554 } 555 input = original; 556 557 if(!name (chars)){ 558 return pos.reset(); 559 } 560 return true; 561 } 562 563 /* this hack is ugly and guaranteed to break, but the symbols 564 generated for template alias parameters are broken: 565 the compiler generates a symbol of the form S(number){(number)(name)} 566 with no space between the numbers; what we do is try to match 567 different combinations of division between the concatenated numbers */ 568 569 bool lNameAliasHack () 570 out (result) 571 { 572 debug(traceDemangler) trace (result); 573 } 574 body 575 { 576 debug(traceDemangler) trace ("lNameAH"); 577 578 // uint chars; 579 // if (! number (chars)) 580 // return false; 581 582 uint chars; 583 auto pos=checkpoint(); 584 if (! numberNoParse ()) 585 return false; 586 char[10] numberBuf; 587 const(char)[] str = pos.sliceFrom(); 588 if (str.length>numberBuf.length){ 589 return pos.reset(); 590 } 591 numberBuf[0..str.length]=str; 592 str=numberBuf[0..str.length]; 593 pos.resetOutput(); 594 595 int i = 0; 596 597 bool done = false; 598 599 const(char)[] original = input; 600 const(char)[] working = input; 601 602 while (done == false) 603 { 604 if (i > 0) 605 { 606 input = working = original[0 .. toUint(str[0..i])]; 607 } 608 else 609 input = working = original; 610 611 chars = toUint(str[i..$]); 612 613 if (chars < input.length && chars > 0) 614 { 615 // cut the string from the right side to the number 616 // const(char)[] original = input; 617 // input = input[0 .. chars]; 618 // uint len = input.length; 619 debug(traceDemangler) report ("trying {}/{}", chars, input.length); 620 done = templateInstanceName (); 621 //input = original[len - input.length .. $]; 622 623 if (!done) 624 { 625 input = working; 626 debug(traceDemangler) report ("trying {}/{}", chars, input.length); 627 done = name (chars); 628 } 629 630 if (done) 631 { 632 input = original[working.length - input.length .. $]; 633 return true; 634 } 635 else 636 input = original; 637 } 638 639 i += 1; 640 if (i == str.length) 641 return false; 642 } 643 644 return true; 645 } 646 647 bool number (ref uint value) 648 out (result) 649 { 650 debug(traceDemangler) trace (result); 651 } 652 body 653 { 654 debug(traceDemangler) trace ("number"); 655 656 if (input.length == 0) 657 return false; 658 659 value = 0; 660 if (input[0] >= '0' && input[0] <= '9') 661 { 662 while (input.length > 0 && input[0] >= '0' && input[0] <= '9') 663 { 664 value = value * 10 + cast(uint) (input[0] - '0'); 665 consume (1); 666 } 667 return true; 668 } 669 else 670 return false; 671 } 672 673 bool numberNoParse () 674 out (result) 675 { 676 debug(traceDemangler) trace (result); 677 } 678 body 679 { 680 debug(traceDemangler) trace ("numberNP"); 681 682 if (input.length == 0) 683 return false; 684 685 if (input[0] >= '0' && input[0] <= '9') 686 { 687 while (input.length > 0 && input[0] >= '0' && input[0] <= '9') 688 { 689 output.append (input[0]); 690 consume (1); 691 } 692 return true; 693 } 694 else 695 return false; 696 } 697 698 bool name (uint count) 699 out (result) 700 { 701 debug(traceDemangler) trace (result); 702 } 703 body 704 { 705 debug(traceDemangler) trace ("name"); 706 707 //if (input.length >= 3 && input[0 .. 3] == "__T") 708 // return false; // workaround 709 710 if (count > input.length) 711 return false; 712 713 const(char)[] name = consume (count); 714 output.append (name); 715 debug(traceDemangler) report (">>> name={}", name); 716 717 return count > 0; 718 } 719 720 bool type () 721 out (result) 722 { 723 debug(traceDemangler) trace (result); 724 } 725 body 726 { 727 debug(traceDemangler) trace ("type"); 728 if (! input.length) return false; 729 auto pos=checkpoint(); 730 switch (input[0]) 731 { 732 case 'x': 733 consume (1); 734 output.append ("const "); 735 if (!type ()) return pos.reset(); 736 return true; 737 738 case 'y': 739 consume (1); 740 output.append ("invariant "); 741 if (!type ()) return pos.reset(); 742 return true; 743 744 case 'A': 745 consume (1); 746 if (type ()) 747 { 748 output.append ("[]"); 749 return true; 750 } 751 return pos.reset(); 752 753 case 'G': 754 consume (1); 755 uint size; 756 if (! number (size)) 757 return false; 758 if (type ()) { 759 output.append ("[" ~ ctfe_i2a(size) ~ "]"); 760 return true; 761 } 762 return pos.reset(); 763 764 case 'H': 765 consume (1); 766 auto pos2=checkpoint(); 767 if (! type ()) 768 return false; 769 const(char)[] keytype=pos2.sliceFrom(); 770 output.append ("["); 771 auto pos3=checkpoint(); 772 if (type ()) 773 { 774 const(char)[] subtype=pos3.sliceFrom(); 775 output.append ("]"); 776 if (subtype.length<=keytype.length){ 777 auto pos4=checkpoint(); 778 output.append (keytype); 779 memmove(&output.data[pos2.len],&output.data[pos3.len],subtype.length); 780 output.data[pos2.len+keytype.length]='['; 781 memcpy(&output.data[pos2.len],&output.data[pos4.len],keytype.length); 782 pos4.reset(); 783 } 784 return true; 785 } 786 return pos.reset(); 787 788 case 'P': 789 consume (1); 790 if (type ()) 791 { 792 output.append ("*"); 793 return true; 794 } 795 return false; 796 case 'F': case 'U': case 'W': case 'V': case 'R': case 'D': case 'M': 797 return typeFunction (); 798 case 'I': case 'C': case 'S': case 'E': case 'T': 799 return typeNamed (); 800 case 'n': 801 consume (1); 802 output.append ("none"); 803 return true; 804 case 'v': 805 consume (1); 806 output.append ("void"); 807 return true; 808 case 'g': 809 consume (1); 810 output.append ("byte"); 811 return true; 812 case 'h': 813 consume (1); 814 output.append ("ubyte"); 815 return true; 816 case 's': 817 consume (1); 818 output.append ("short"); 819 return true; 820 case 't': 821 consume (1); 822 output.append ("ushort"); 823 return true; 824 case 'i': 825 consume (1); 826 output.append ("int"); 827 return true; 828 case 'k': 829 consume (1); 830 output.append ("uint"); 831 return true; 832 case 'l': 833 consume (1); 834 output.append ("long"); 835 return true; 836 case 'm': 837 consume (1); 838 output.append ("ulong"); 839 return true; 840 case 'f': 841 consume (1); 842 output.append ("float"); 843 return true; 844 case 'd': 845 consume (1); 846 output.append ("double"); 847 return true; 848 case 'e': 849 consume (1); 850 output.append ("real"); 851 return true; 852 case 'q': 853 consume(1); 854 output.append ("cfloat"); 855 return true; 856 case 'r': 857 consume(1); 858 output.append ("cdouble"); 859 return true; 860 case 'c': 861 consume(1); 862 output.append ("creal"); 863 return true; 864 case 'o': 865 consume(1); 866 output.append ("ifloat"); 867 return true; 868 case 'p': 869 consume(1); 870 output.append ("idouble"); 871 return true; 872 case 'j': 873 consume(1); 874 output.append ("ireal"); 875 return true; 876 case 'b': 877 consume (1); 878 output.append ("bool"); 879 return true; 880 case 'a': 881 consume (1); 882 output.append ("char"); 883 return true; 884 case 'u': 885 consume (1); 886 output.append ("wchar"); 887 return true; 888 case 'w': 889 consume (1); 890 output.append ("dchar"); 891 return true; 892 case 'B': 893 consume (1); 894 uint count; 895 if (! number (count)) 896 return pos.reset(); 897 output.append ('('); 898 if (! arguments ()) 899 return pos.reset(); 900 output.append (')'); 901 return true; 902 903 default: 904 return pos.reset(); 905 } 906 907 //return true; 908 } 909 910 bool typeFunction () 911 out (result) 912 { 913 debug(traceDemangler) trace (result); 914 } 915 body 916 { 917 debug(traceDemangler) trace ("typeFunction"); 918 919 auto pos=checkpoint(); 920 bool isMethod = false; 921 bool isDelegate = false; 922 923 if (input.length == 0) 924 return false; 925 926 if (input[0] == 'M') 927 { 928 consume (1); 929 isMethod = true; 930 } 931 if (input[0] == 'D') 932 { 933 consume (1); 934 isDelegate = true; 935 assert (! isMethod); 936 } 937 938 switch (input[0]) 939 { 940 case 'F': 941 consume (1); 942 break; 943 944 case 'U': 945 consume (1); 946 output.append ("extern(C) "); 947 break; 948 949 case 'W': 950 consume (1); 951 output.append ("extern(Windows) "); 952 break; 953 954 case 'V': 955 consume (1); 956 output.append ("extern(Pascal) "); 957 break; 958 959 case 'R': 960 consume (1); 961 output.append ("extern(C++) "); 962 break; 963 964 default: 965 return pos.reset(); 966 } 967 968 auto pos2=checkpoint(); 969 if (isMethod) 970 output.append (" method ("); 971 else if (isDelegate) 972 output.append (" delegate ("); 973 else 974 output.append (" function ("); 975 976 arguments (); 977 version (all){ 978 if (0 == input.length) { 979 // probably MD5 symbol hashing. try to continue 980 return true; 981 } 982 } 983 switch (input[0]) 984 { 985 case 'X': case 'Y': case 'Z': 986 consume (1); 987 break; 988 default: 989 return pos.reset(); 990 } 991 output.append (")"); 992 993 auto pos3=checkpoint(); 994 if (! type ()) 995 return pos.reset(); 996 const(char)[] retT=pos3.sliceFrom(); 997 auto pos4=checkpoint(); 998 output.append(retT); 999 memmove(&output.data[pos2.len+retT.length],&output.data[pos2.len],pos3.len-pos2.len+1); 1000 memcpy(&output.data[pos2.len],&output.data[pos4.len],retT.length); 1001 pos4.reset(); 1002 return true; 1003 } 1004 1005 bool arguments () 1006 out (result) 1007 { 1008 debug(traceDemangler) trace (result); 1009 } 1010 body 1011 { 1012 debug(traceDemangler) trace ("arguments"); 1013 1014 if (! argument ()) 1015 return false; 1016 1017 auto pos=checkpoint(); 1018 output.append (", "); 1019 if (!arguments ()){ 1020 pos.reset(); 1021 } 1022 1023 return true; 1024 } 1025 1026 bool argument () 1027 out (result) 1028 { 1029 debug(traceDemangler) trace (result); 1030 } 1031 body 1032 { 1033 debug(traceDemangler) trace ("argument"); 1034 1035 if (input.length == 0) 1036 return false; 1037 auto pos=checkpoint(); 1038 switch (input[0]) 1039 { 1040 case 'K': 1041 consume (1); 1042 output.append ("ref "); 1043 break; 1044 1045 case 'J': 1046 consume (1); 1047 output.append ("out "); 1048 break; 1049 1050 case 'L': 1051 consume (1); 1052 output.append ("lazy "); 1053 break; 1054 1055 default: 1056 } 1057 1058 if (! type ()) 1059 return pos.reset(); 1060 1061 return true; 1062 } 1063 1064 bool typeNamed () 1065 out (result) 1066 { 1067 debug(traceDemangler) trace (result); 1068 } 1069 body 1070 { 1071 debug(traceDemangler) trace ("typeNamed"); 1072 auto pos=checkpoint(); 1073 const(char)[] kind; 1074 switch (input[0]) 1075 { 1076 case 'I': 1077 consume (1); 1078 kind = "interface"; 1079 break; 1080 1081 case 'S': 1082 consume (1); 1083 kind = "struct"; 1084 break; 1085 1086 case 'C': 1087 consume (1); 1088 kind = "class"; 1089 break; 1090 1091 case 'E': 1092 consume (1); 1093 kind = "enum"; 1094 break; 1095 1096 case 'T': 1097 consume (1); 1098 kind = "typedef"; 1099 break; 1100 1101 default: 1102 return false; 1103 } 1104 1105 //output.append (kind); 1106 //output.append ("="); 1107 1108 if (! qualifiedName ()) 1109 return pos.reset(); 1110 1111 if (prefs. printTypeKind) 1112 { 1113 output.append ("<"); 1114 output.append (kind); 1115 output.append (">"); 1116 } 1117 1118 return true; 1119 } 1120 1121 bool templateInstanceName () 1122 out (result) 1123 { 1124 debug(traceDemangler) trace (result); 1125 } 1126 body 1127 { 1128 debug(traceDemangler) trace ("templateInstanceName"); 1129 auto pos=checkpoint(); 1130 if (input.length < 4 || input[0..3] != "__T") 1131 return false; 1132 1133 consume (3); 1134 1135 if (! lName ()) 1136 return checkpoint().reset(); 1137 1138 output.append ("!("); 1139 1140 _templateDepth++; 1141 if (_templateDepth <= prefs.templateExpansionDepth) { 1142 templateArgs (); 1143 } else { 1144 auto pos2=checkpoint(); 1145 templateArgs (); 1146 pos2.resetOutput(); 1147 output.append ("..."); 1148 } 1149 _templateDepth--; 1150 1151 if (input.length > 0 && input[0] != 'Z') 1152 return pos.reset(); 1153 1154 output.append (")"); 1155 1156 consume (1); 1157 return true; 1158 } 1159 1160 bool templateArgs () 1161 out (result) 1162 { 1163 debug(traceDemangler) trace (result); 1164 } 1165 body 1166 { 1167 debug(traceDemangler) trace ("templateArgs"); 1168 1169 if (! templateArg ()) 1170 return false; 1171 auto pos1=checkpoint(); 1172 output.append (", "); 1173 if (! templateArgs ()) 1174 { 1175 pos1.reset(); 1176 } 1177 1178 return true; 1179 } 1180 1181 bool templateArg () 1182 out (result) 1183 { 1184 debug(traceDemangler) trace (result); 1185 } 1186 body 1187 { 1188 debug(traceDemangler) trace ("templateArg"); 1189 1190 if (input.length == 0) 1191 return false; 1192 auto pos=checkpoint(); 1193 switch (input[0]) 1194 { 1195 case 'T': 1196 consume (1); 1197 if (! type ()) 1198 return pos.reset(); 1199 return true; 1200 1201 case 'V': 1202 consume (1); 1203 auto pos2=checkpoint(); 1204 if (! type ()) 1205 return pos.reset(); 1206 pos2.resetOutput(); 1207 if (! value ()) 1208 return pos.reset(); 1209 return true; 1210 1211 case 'S': 1212 consume (1); 1213 if (! qualifiedName (true)) 1214 return pos.reset(); 1215 return true; 1216 1217 default: 1218 return pos.reset(); 1219 } 1220 1221 //return pos.reset; 1222 } 1223 1224 bool value () 1225 out (result) 1226 { 1227 debug(traceDemangler) trace (result); 1228 } 1229 body 1230 { 1231 debug(traceDemangler) trace ("value"); 1232 1233 if (input.length == 0) 1234 return false; 1235 1236 auto pos=checkpoint(); 1237 1238 switch (input[0]) 1239 { 1240 case 'n': 1241 consume (1); 1242 return true; 1243 1244 case 'N': 1245 consume (1); 1246 output.append ('-'); 1247 if (! numberNoParse ()) 1248 return pos.reset(); 1249 return true; 1250 1251 case 'e': 1252 consume (1); 1253 if (! hexFloat ()) 1254 return pos.reset(); 1255 return true; 1256 1257 case 'c': //TODO 1258 1259 case 'A': 1260 consume (1); 1261 uint count; 1262 if (! number (count)) 1263 return pos.reset(); 1264 if (count>0) { 1265 output.append ("["); 1266 for (uint i = 0; i < count-1; i++) 1267 { 1268 if (! value ()) 1269 return pos.reset(); 1270 output.append (", "); 1271 } 1272 if (! value ()) 1273 return pos.reset(); 1274 } 1275 output.append ("]"); 1276 return true; 1277 1278 default: 1279 if (! numberNoParse ()) 1280 return pos.reset(); 1281 return true; 1282 } 1283 1284 //return pos.reset(); 1285 } 1286 1287 bool hexFloat () 1288 out (result) 1289 { 1290 debug(traceDemangler) trace (result); 1291 } 1292 body 1293 { 1294 debug(traceDemangler) trace ("hexFloat"); 1295 1296 auto pos=checkpoint(); 1297 if (input[0 .. 3] == "NAN") 1298 { 1299 consume (3); 1300 output.append ("nan"); 1301 return true; 1302 } 1303 else if (input[0 .. 3] == "INF") 1304 { 1305 consume (3); 1306 output.append ("+inf"); 1307 return true; 1308 } 1309 else if (input[0 .. 3] == "NINF") 1310 { 1311 consume (3); 1312 output.append ("-inf"); 1313 return true; 1314 } 1315 1316 bool negative = false; 1317 if (input[0] == 'N') 1318 { 1319 consume (1); 1320 negative = true; 1321 } 1322 1323 ulong num; 1324 if (! hexNumber (num)) 1325 return false; 1326 1327 if (input[0] != 'P') 1328 return false; 1329 consume (1); 1330 1331 bool negative_exponent = false; 1332 if (input[0] == 'N') 1333 { 1334 consume (1); 1335 negative_exponent = true; 1336 } 1337 1338 uint exponent; 1339 if (! number (exponent)) 1340 return pos.reset(); 1341 1342 return true; 1343 } 1344 1345 static bool isHexDigit (char c) 1346 { 1347 return (c > '0' && c <'9') || (c > 'a' && c < 'f') || (c > 'A' && c < 'F'); 1348 } 1349 1350 bool hexNumber (ref ulong value) 1351 out (result) 1352 { 1353 debug(traceDemangler) trace (result); 1354 } 1355 body 1356 { 1357 debug(traceDemangler) trace ("hexFloat"); 1358 1359 if (isHexDigit (input[0])) 1360 { 1361 while (isHexDigit (input[0])) 1362 { 1363 //output.append (input[0]); 1364 consume (1); 1365 } 1366 return true; 1367 } 1368 else 1369 return false; 1370 } 1371 } 1372 } 1373 1374 1375 private struct Buffer 1376 { 1377 char[] data; 1378 size_t length; 1379 1380 void append (const(char)[] s) 1381 { 1382 assert(this.length+s.length<=data.length); 1383 size_t len=this.length+s.length; 1384 if (len>data.length) len=data.length; 1385 data[this.length .. len] = s[0..len-this.length]; 1386 this.length = len; 1387 } 1388 1389 void append (char c) 1390 { 1391 assert(this.length<data.length); 1392 data[this.length .. this.length + 1] = c; 1393 this.length += 1; 1394 } 1395 1396 void append (Buffer b) 1397 { 1398 append (b.slice()); 1399 } 1400 1401 const(char)[] slice() () 1402 { 1403 return data[0 .. this.length]; 1404 } 1405 } 1406 1407 /// The default demangler. 1408 static Demangler demangler; 1409 1410 static this(){ 1411 demangler=new Demangler(1); 1412 } 1413 1414 } 1415 else 1416 { 1417 import core = core.demangle; 1418 1419 public class Demangler 1420 { 1421 /** Demangles the given string. */ 1422 public inout(char)[] demangle (inout(char)[] input) 1423 { 1424 return cast(typeof(return))core.demangle(input); 1425 } 1426 1427 /** Demangles the given string using output to hold the result. */ 1428 public inout(char)[] demangle (inout(char)[] input, char[] output) 1429 { 1430 return cast(typeof(return))core.demangle(input, output); 1431 } 1432 } 1433 1434 /// The default demangler. 1435 static Demangler demangler; 1436 1437 static this(){ 1438 demangler=new Demangler; 1439 } 1440 1441 }