1 /** ELF Symbol Name (De)Mangling. 2 Copyright: Per Nordlöw 2018-. 3 License: $(WEB boost.org/LICENSE_1_0.txt, Boost License 1.0). 4 Authors: $(WEB Per Nordlöw) 5 See_Also: https://mentorembedded.github.io/cxx-abi/abi.html 6 7 TODO: Only check for emptyness before any optionals. 8 9 TODO: Search for pattern "X> <Y" and assure that they all use 10 return r.tryEvery(X, Y). 11 12 TODO: 1. Replace calls to decode ~ decode with separate decodes 13 TODO: 2 : Replace calls to decode ~ decode with a sequence call. 14 15 TODO: Detect recursion: 16 See: http://forum.dlang.org/thread/edaduxaxmihvzkoudeqa@forum.dlang.org#post-edaduxaxmihvzkoudeqa:40forum.dlang.org 17 See: http://code.dlang.org/packages/backtrace-d 18 19 TODO: What role does _ZL have? See localFlag for details. 20 */ 21 module nxt.mangling; 22 23 import std.range.primitives: empty, front, popFront, moveFront, popFrontExactly, isInputRange; 24 import std.algorithm: joiner, min; 25 import std.conv: to; 26 import std.ascii: isDigit; 27 28 import std.algorithm.comparison : either; 29 import nxt.array_algorithm : startsWith, skipOver; 30 import nxt.algorithm_ex: tryEvery, split, splitBefore; 31 import nxt.languages : Lang; 32 import nxt.dbgio : dbg; 33 34 /** C++ Demangler. */ 35 class Demangler(R) 36 if (isInputRange!R) 37 { 38 this(R r, 39 bool explicitVoidParameter = false, 40 bool show = false) 41 { 42 this.r = r; 43 this.explicitVoidParameter = explicitVoidParameter; 44 this.show = show; 45 } 46 R r; 47 bool show = false; 48 bool explicitVoidParameter = false; // set to true make void parameters explicit 49 private: 50 string[] sourceNames; 51 CxxType[] ids; // ids demangled so far 52 R scopeSeparator = "::"; 53 } 54 auto demangler(T...)(T args) if (isInputRange!(T[0])) => new Demangler!(T[0])(args); 55 56 /** Like $(D skipOver) but return $(D string) instead of $(D bool). 57 Bool-conversion of returned value gives same result as r.skipOver(lit). 58 */ 59 string skipLiteral(R, E)(Demangler!R x, E lit) if (isInputRange!R) => x.r.skipOver(lit) ? "" : null; 60 61 /** Decode Unqualified C++ Type at $(D r). 62 See_Also: https://mentorembedded.github.io/cxx-abi/abi.html#mangling-type 63 */ 64 R decodeCxxUnqualifiedType(R)(Demangler!R x) if (isInputRange!R) 65 { 66 if (x.show) dbg("rest: ", x.r); 67 return either(x.decodeCxxBuiltinType(), 68 x.decodeCxxSubstitution(), 69 x.decodeCxxFunctionType()); 70 } 71 72 struct CxxType 73 { 74 string typeName; 75 bool isRef = false; // & <ref-qualifier> 76 bool isRvalueRef = false; // && ref-qualifier (C++11) 77 bool isComplexPair = false; // complex pair (C 2000) 78 bool isImaginary = false; // imaginary (C 2000) 79 byte pointyness = 0; // pointer level 80 CXXCVQualifiers cvQ; 81 82 void toString(Sink)(ref scope Sink sink) const 83 { 84 if (cvQ.isVolatile) { sink("volatile "); } 85 if (cvQ.isRestrict) { sink("restrict "); } 86 87 sink(typeName); 88 89 if (cvQ.isConst) { sink(" const"); } 90 91 // suffix qualifiers 92 foreach (immutable _; 0 .. pointyness) 93 { 94 sink(`*`);// str ~= "*".replicate(pointyness); 95 } 96 if (isRef) { sink(`&`); } 97 if (isRvalueRef) { sink(`&&`); } 98 } 99 } 100 101 /** Decode C++ Type at $(D r). 102 See_Also: https://mentorembedded.github.io/cxx-abi/abi.html#mangling-type 103 */ 104 R decodeCxxType(R)(Demangler!R x) 105 if (isInputRange!R) 106 { 107 if (x.show) dbg("rest: ", x.r); 108 109 const packExpansion = x.r.skipOver(`Dp`); // (C++11) 110 111 CxxType cxxType; 112 113 while (!x.r.empty) 114 { 115 if (const cvQ_ = x.r.decodeCxxCVQualifiers()) // TODO: Optimize 116 { 117 cxxType.cvQ.isRestrict |= cvQ_.isRestrict; 118 cxxType.cvQ.isVolatile |= cvQ_.isVolatile; 119 cxxType.cvQ.isConst |= cvQ_.isConst; 120 continue; 121 } 122 else 123 { 124 auto miss = false; 125 switch (x.r[0]) 126 { 127 case 'P': x.r.popFront(); cxxType.pointyness++; break; 128 // <ref-qualifier>: https://mentorembedded.github.io/cxx-abi/abi.html#mangle.ref-qualifier 129 case 'R': x.r.popFront(); cxxType.isRef = true; break; 130 case 'O': x.r.popFront(); cxxType.isRvalueRef = true; break; 131 case 'C': x.r.popFront(); cxxType.isComplexPair = true; dbg("TODO: Handle complex pair (C 2000)"); break; 132 case 'G': x.r.popFront(); cxxType.isImaginary = true; dbg("TODO: Handle imaginary (C 2000)"); break; 133 case 'U': x.r.popFront(); 134 const sourceName = x.decodeCxxSourceName(); 135 cxxType.typeName = sourceName ~ x.decodeCxxType(); 136 dbg("TODO: Handle vendor extended type qualifier <source-name>", x.r); 137 break; 138 default: miss = true; break; 139 } 140 if (miss) 141 { 142 break; 143 } 144 } 145 } 146 assert(!(cxxType.isRef && cxxType.isRvalueRef)); 147 148 if (x.r.empty) { return cxxType.to!string; } 149 150 cxxType.typeName = either(x.decodeCxxBuiltinType(), 151 x.decodeCxxFunctionType(), 152 x.decodeCxxClassEnumType(), 153 x.decodeCxxArrayType(), 154 x.decodeCxxPointerToMemberType(), 155 x.decodeCxxTemplateTemplateParamAndArgs(), 156 x.decodeCxxDecltype(), 157 x.decodeCxxSubstitution()); 158 159 x.ids ~= cxxType; 160 161 return cxxType.to!string; 162 } 163 164 /** See_Also: https://mentorembedded.github.io/cxx-abi/abi.html#mangle.class-enum-type */ 165 R decodeCxxClassEnumType(R)(Demangler!R x) 166 if (isInputRange!R) 167 { 168 if (x.show) dbg("rest: ", x.r); 169 R type; 170 R prefix; 171 enum n = 2; 172 if (x.r.length >= n) 173 { 174 switch (x.r[0..n]) 175 { 176 case `Ts`: prefix = `struct `; break; 177 case `Tu`: prefix = `union `; break; 178 case `Te`: prefix = `enum `; break; 179 default: break; 180 } 181 if (prefix) 182 { 183 x.r.popFrontExactly(n); 184 } 185 } 186 const name = x.decodeCxxName(); 187 if (name) 188 { 189 type = prefix ~ name; 190 } 191 else 192 { 193 assert(!prefix); // if we failed to decode name prefix should not have existed either 194 } 195 return type; 196 } 197 198 R decodeCxxExpression(R)(Demangler!R x) 199 if (isInputRange!R) 200 { 201 if (x.show) dbg("rest: ", x.r); 202 R exp; 203 assert(0, "TODO"); 204 } 205 206 /** See_Also: https://mentorembedded.github.io/cxx-abi/abi.html#mangle.array-type */ 207 R decodeCxxArrayType(R)(Demangler!R x) 208 if (isInputRange!R) 209 { 210 if (x.show) dbg("rest: ", x.r); 211 R type; 212 if (x.r.skipOver('A')) 213 { 214 if (const num = x.decodeCxxNumber()) 215 { 216 assert(x.r.skipOver('_')); 217 type = x.decodeCxxType() ~ `[]` ~ num ~ `[]`; 218 } 219 else 220 { 221 const dimensionExpression = x.decodeCxxExpression(); 222 assert(x.r.skipOver('_')); 223 type = x.decodeCxxType() ~ `[]` ~ dimensionExpression ~ `[]`; 224 } 225 } 226 return type; 227 } 228 229 /** See_Also: https://mentorembedded.github.io/cxx-abi/abi.html#mangle.pointer-to-member-type */ 230 R decodeCxxPointerToMemberType(R)(Demangler!R x) 231 if (isInputRange!R) 232 { 233 if (x.show) dbg("rest: ", x.r); 234 R type; 235 if (x.r.skipOver('M')) 236 { 237 const classType = x.decodeCxxType(); // <class type> 238 const memberType = x.decodeCxxType(); // <mmeber type> 239 type = classType ~ memberType; 240 } 241 return type; 242 } 243 244 /** See_Also: https://mentorembedded.github.io/cxx-abi/abi.html#mangle.template-param */ 245 R decodeCxxTemplateParam(R)(Demangler!R x) 246 if (isInputRange!R) 247 { 248 if (x.show) dbg("rest: ", x.r); 249 R param; 250 if (x.r.skipOver('T')) 251 { 252 if (x.r.skipOver('_')) 253 { 254 param = `first template parameter`; 255 } 256 else 257 { 258 param = x.decodeCxxNumber(); 259 assert(x.r.skipOver('_')); 260 } 261 } 262 return param; 263 } 264 265 R decodeCxxTemplateTemplateParamAndArgs(R)(Demangler!R x) 266 if (isInputRange!R) 267 { 268 if (x.show) dbg("rest: ", x.r); 269 R value; 270 if (const param = either(x.decodeCxxTemplateParam(), 271 x.decodeCxxSubstitution())) 272 { 273 auto args = x.decodeCxxTemplateArgs(); 274 value = param ~ args.joiner(`, `).to!R; 275 } 276 return value; 277 } 278 279 /** See_Also: https://mentorembedded.github.io/cxx-abi/abi.html#mangle.decltype */ 280 R decodeCxxDecltype(R)(Demangler!R x) 281 if (isInputRange!R) 282 { 283 if (x.show) dbg("rest: ", x.r); 284 R type; 285 if (x.r.skipOver(`Dt`) || 286 x.r.skipOver(`DT`)) 287 { 288 type = x.decodeCxxExpression(); 289 assert(x.r.skipOver('E')); 290 } 291 return type; 292 } 293 294 R decodeCxxDigit(R)(Demangler!R x) 295 if (isInputRange!R) 296 { 297 if (x.show) dbg("rest: ", x.r); 298 auto digit = x.r[0..1]; 299 x.r.popFront(); 300 return digit; 301 } 302 303 /** Try to Decode C++ Operator at $(D r). 304 See_Also: https://mentorembedded.github.io/cxx-abi/abi.html#mangling-operator 305 */ 306 R decodeCxxOperatorName(R)(Demangler!R x) 307 if (isInputRange!R) 308 { 309 if (x.show) dbg("rest: ", x.r); 310 311 if (x.r.skipOver('v')) // vendor extended operator 312 { 313 const digit = x.decodeCxxDigit(); 314 const sourceName = x.decodeCxxSourceName(); 315 return digit ~ sourceName; 316 } 317 318 R op; 319 enum n = 2; 320 if (x.r.length < n) { return typeof(return).init; } 321 const code = x.r[0..n]; 322 switch (code) 323 { 324 case `nw`: op = `operator new`; break; 325 case `na`: op = `operator new[]`; break; 326 case `dl`: op = `operator delete`; break; 327 case `da`: op = `operator delete[]`; break; 328 case `ps`: op = `operator+`; break; // unary plus 329 case `ng`: op = `operator-`; break; // unary minus 330 331 case `ad`: op = `operator&`; break; // address of 332 case `de`: op = `operator*`; break; // dereference 333 334 case `co`: op = `operator~`; break; // bitwise complement 335 case `pl`: op = `operator+`; break; // plus 336 case `mi`: op = `operator-`; break; // minus 337 338 case `ml`: op = `operator*`; break; // multiplication 339 case `dv`: op = `operator/`; break; // division 340 case `rm`: op = `operator%`; break; // remainder 341 342 case `an`: op = `operator&`; break; // bitwise and 343 case `or`: op = `operator|`; break; // bitwise of 344 345 case `eo`: op = `operator^`; break; 346 case `aS`: op = `operator=`; break; 347 348 case `pL`: op = `operator+=`; break; 349 case `mI`: op = `operator-=`; break; 350 case `mL`: op = `operator*=`; break; 351 case `dV`: op = `operator/=`; break; 352 case `rM`: op = `operator%=`; break; 353 354 case `aN`: op = `operator&=`; break; 355 case `oR`: op = `operator|=`; break; 356 case `eO`: op = `operator^=`; break; 357 358 case `ls`: op = `operator<<`; break; 359 case `rs`: op = `operator>>`; break; 360 case `lS`: op = `operator<<=`; break; 361 case `rS`: op = `operator>>=`; break; 362 363 case `eq`: op = `operator==`; break; 364 case `ne`: op = `operator!=`; break; 365 case `lt`: op = `operator<`; break; 366 case `gt`: op = `operator>`; break; 367 case `le`: op = `operator<=`; break; 368 case `ge`: op = `operator>=`; break; 369 370 case `nt`: op = `operator!`; break; 371 case `aa`: op = `operator&&`; break; 372 case `oo`: op = `operator||`; break; 373 374 case `pp`: op = `operator++`; break; // (postfix in <expression> context) 375 case `mm`: op = `operator--`; break; // (postfix in <expression> context) 376 377 case `cm`: op = `operator,`; break; 378 379 case `pm`: op = `operator->*`; break; 380 case `pt`: op = `operator->`; break; 381 382 case `cl`: op = `operator()`; break; 383 case `ix`: op = `operator[]`; break; 384 case `qu`: op = `operator?`; break; 385 case `cv`: op = `(cast)`; break; 386 case `li`: op = `operator""`; break; 387 default: break; 388 } 389 390 if (op) 391 { 392 x.r.popFrontExactly(n); // digest it 393 } 394 395 switch (code) 396 { 397 case `cv`: op = '(' ~ x.decodeCxxType() ~ ')'; break; 398 case `li`: op = (`operator ""` ~ x.decodeCxxSourceName()); break; 399 default: break; 400 } 401 402 return op; 403 } 404 405 /** Try to Decode C++ Builtin Type at $(D r). 406 See_Also: https://mentorembedded.github.io/cxx-abi/abi.html#mangle.builtin-type 407 */ 408 R decodeCxxBuiltinType(R)(Demangler!R x) 409 if (isInputRange!R) 410 { 411 if (x.show) dbg("rest: ", x.r); 412 R type; 413 enum n = 1; 414 if (x.r.length < n) { return type; } 415 switch (x.r[0]) 416 { 417 case 'v': x.r.popFront(); type = `void`; break; 418 case 'w': x.r.popFront(); type = `wchar_t`; break; 419 420 case 'b': x.r.popFront(); type = `bool`; break; 421 422 case 'c': x.r.popFront(); type = `char`; break; 423 case 'a': x.r.popFront(); type = `signed char`; break; 424 case 'h': x.r.popFront(); type = `unsigned char`; break; 425 426 case 's': x.r.popFront(); type = `short`; break; 427 case 't': x.r.popFront(); type = `unsigned short`; break; 428 429 case 'i': x.r.popFront(); type = `int`; break; 430 case 'j': x.r.popFront(); type = `unsigned int`; break; 431 432 case 'l': x.r.popFront(); type = `long`; break; 433 case 'm': x.r.popFront(); type = `unsigned long`; break; 434 435 case 'x': x.r.popFront(); type = `long long`; break; // __int64 436 case 'y': x.r.popFront(); type = `unsigned long long`; break; // __int64 437 438 case 'n': x.r.popFront(); type = `__int128`; break; 439 case 'o': x.r.popFront(); type = `unsigned __int128`; break; 440 441 case 'f': x.r.popFront(); type = `float`; break; 442 case 'd': x.r.popFront(); type = `double`; break; 443 case 'e': x.r.popFront(); type = `long double`; break; // __float80 444 case 'g': x.r.popFront(); type = `__float128`; break; 445 446 case 'z': x.r.popFront(); type = `...`; break; // ellipsis 447 448 case 'D': 449 x.r.popFront(); 450 assert(!x.r.empty); // need one more 451 switch (x.r[0]) 452 { 453 case 'd': x.r.popFront(); type = `IEEE 754r decimal floating point (64 bits)`; break; 454 case 'e': x.r.popFront(); type = `IEEE 754r decimal floating point (128 bits)`; break; 455 case 'f': x.r.popFront(); type = `IEEE 754r decimal floating point (32 bits)`; break; 456 case 'h': x.r.popFront(); type = `IEEE 754r half-precision floating point (16 bits)`; break; 457 case 'i': x.r.popFront(); type = `char32_t`; break; 458 case 's': x.r.popFront(); type = `char16_t`; break; 459 case 'a': x.r.popFront(); type = `auto`; break; 460 case 'c': x.r.popFront(); type = `decltype(auto)`; break; 461 case 'n': x.r.popFront(); type = `std::nullptr_t`; break; // (i.e., decltype(nullptr)) 462 default: dbg(`TODO: Handle `, x.r); 463 } 464 break; 465 466 /* TODO: */ 467 /* ::= u <source-name> # vendor extended type */ 468 469 default: 470 break; 471 } 472 473 return type; 474 } 475 476 /** Decode C++ Substitution Type at $(D r). 477 See_Also: https://mentorembedded.github.io/cxx-abi/abi.html#mangle.substitution 478 */ 479 R decodeCxxSubstitution(R)(Demangler!R x, R stdPrefix = `::std::`) 480 if (isInputRange!R) 481 { 482 if (x.show) dbg("rest: ", x.r); 483 R type; 484 if (x.r.skipOver('S')) 485 { 486 if (x.r.front == '_') // See_Also: https://mentorembedded.github.io/cxx-abi/abi.html#mangle.seq-id 487 { 488 type = x.ids[0].to!R; 489 x.r.popFront(); 490 } 491 else if ('0' <= x.r.front && x.r.front <= '9') // See_Also: https://mentorembedded.github.io/cxx-abi/abi.html#mangle.seq-id 492 { 493 const ix = (x.r.front - '0'); 494 auto ids_ = x.ids[min(x.ids.length - 1, ix + 1)]; // TODO: Use of min here is hacky. Investigate. 495 if (ix == 0) 496 { 497 /* NOTE: Undocumented: decrease pointyness. 498 See for example: parse_arch(size_t argc, const char** argv, const char* arch) 499 in dmd/src/mars.c 500 */ 501 ids_.pointyness = ids_.pointyness >= 1 ? cast(byte)(ids_.pointyness - 1): 0; 502 } 503 type = ids_.to!R; 504 x.r.popFront(); 505 x.r.skipOver('_'); // TODO: Relaxed this to optional by removing surrounding assert. Investigate. 506 } 507 else if ('A' <= x.r.front && x.r.front <= 'Z') // See_Also: https://mentorembedded.github.io/cxx-abi/abi.html#mangle.seq-id 508 { 509 const ix = (x.r.front - 'A' + 11); 510 type = x.ids[ix].to!R; 511 x.r.popFront(); 512 assert(x.r.skipOver('_')); 513 } 514 else 515 { 516 type = stdPrefix; 517 switch (x.r.front) 518 { 519 case 't': x.r.popFront(); type ~= `ostream`; break; 520 case 'a': x.r.popFront(); type ~= `allocator`; break; 521 case 'b': x.r.popFront(); type ~= `basic_string`; break; 522 case 's': x.r.popFront(); type ~= `basic_string<char, std::char_traits<char>, std::allocator<char> >`; break; 523 case 'i': x.r.popFront(); type ~= `istream`; break; 524 case 'o': x.r.popFront(); type ~= `ostream`; break; 525 case 'd': x.r.popFront(); type ~= `iostream`; break; 526 527 default: 528 dbg(`Cannot handle C++ standard prefix character: '`, x.r.front, `'`); 529 x.r.popFront(); 530 break; 531 } 532 } 533 } 534 return type; 535 } 536 537 /** Try to Decode C++ Function Type at $(D r). 538 See_Also: https://mentorembedded.github.io/cxx-abi/abi.html#mangle.function-type 539 */ 540 R decodeCxxFunctionType(R)(Demangler!R x) 541 if (isInputRange!R) 542 { 543 if (x.show) dbg("rest: ", x.r); 544 auto restLookAhead = x.r; // needed for lookahead parsing of CV-qualifiers 545 const cvQ = restLookAhead.decodeCxxCVQualifiers(); 546 R type; 547 if (restLookAhead.skipOver('F')) 548 { 549 x.r = restLookAhead; // we have found it 550 x.r.skipOver('Y'); // optional 551 type = x.decodeCxxBareFunctionType().to!R; 552 const refQ = x.decodeCxxRefQualifier(); 553 type ~= refQ.toCxxString; 554 555 } 556 return type; 557 } 558 559 struct CxxBareFunctionType(R) 560 if (isInputRange!R) 561 { 562 R[] types; // optional return and parameter types 563 bool explicitVoidParameter = false; // set to true make void parameters explicit 564 565 R toString() // TODO: use sink 566 @safe pure 567 { 568 R value; 569 if (!types.empty) 570 { 571 value ~= `(`; 572 if (this.explicitVoidParameter || 573 !(types.length == 1 && types.front == "void")) 574 { 575 value ~= types.joiner(`, `).to!R; 576 } 577 value ~= `)`; 578 } 579 return value; 580 } 581 } 582 583 /** See_Also: https://mentorembedded.github.io/cxx-abi/abi.html#mangle.bare-function-type */ 584 CxxBareFunctionType!R decodeCxxBareFunctionType(R)(Demangler!R x) 585 if (isInputRange!R) 586 { 587 if (x.show) dbg("rest: ", x.r); 588 typeof(return) bareFunctionType; 589 bareFunctionType.explicitVoidParameter = x.explicitVoidParameter; 590 591 /* TODO: This behaviour may not follow grammar. */ 592 if (const firstType = x.decodeCxxType()) 593 { 594 bareFunctionType.types ~= firstType; 595 } 596 597 while (!x.r.empty) 598 { 599 auto type = x.decodeCxxType(); 600 if (type) 601 { 602 bareFunctionType.types ~= type; 603 } 604 else 605 { 606 break; 607 } 608 } 609 610 return bareFunctionType; 611 } 612 613 struct CXXCVQualifiers 614 { 615 bool isRestrict; // (C99) 616 bool isVolatile; // volatile 617 bool isConst; // const 618 619 auto opCast(T : bool)() @safe pure nothrow const 620 => (isRestrict || 621 isVolatile || 622 isConst); 623 624 void toString(Sink)(ref scope Sink sink) const 625 { 626 if (isRestrict) sink(`restrict `); 627 if (isVolatile) sink(`volatile `); 628 if (isConst) sink(`const `); 629 } 630 } 631 632 /** Decode <CV-qualifiers> 633 See_Also: https://mentorembedded.github.io/cxx-abi/abi.html#mangle.CV-qualifiers 634 */ 635 CXXCVQualifiers decodeCxxCVQualifiers(R)(ref R r) 636 if (isInputRange!R) 637 { 638 typeof(return) cvQ; 639 if (r.skipOver('r')) { cvQ.isRestrict = true; } 640 if (r.skipOver('V')) { cvQ.isVolatile = true; } 641 if (r.skipOver('K')) { cvQ.isConst = true; } 642 return cvQ; 643 } 644 645 enum CxxRefQualifier 646 { 647 none, 648 normalRef, 649 rvalueRef 650 } 651 652 /* See_Also: http://forum.dlang.org/thread/cvhapzsrhjdnpkdspavg@forum.dlang.org#post-cvhapzsrhjdnpkdspavg:40forum.dlang.org */ 653 string toCxxString(CxxRefQualifier refQ) 654 @safe pure nothrow 655 { 656 final switch (refQ) 657 { 658 case CxxRefQualifier.none: return ""; 659 case CxxRefQualifier.normalRef: return "&"; 660 case CxxRefQualifier.rvalueRef: return "&&"; 661 } 662 } 663 664 /** Decode <ref-qualifier> 665 See_Also: https://mentorembedded.github.io/cxx-abi/abi.html#mangle.ref-qualifier 666 */ 667 CxxRefQualifier decodeCxxRefQualifier(R)(Demangler!R x) 668 if (isInputRange!R) 669 { 670 if (x.show) dbg("rest: ", x.r); 671 if (x.r.skipOver('R')) 672 return CxxRefQualifier.normalRef; 673 else if (x.r.skipOver('O')) 674 return CxxRefQualifier.rvalueRef; 675 else 676 return CxxRefQualifier.none; 677 } 678 679 /** Decode Identifier <source-name>. 680 See_Also: https://mentorembedded.github.io/cxx-abi/abi.html#mangle.source-name 681 */ 682 R decodeCxxSourceName(R)(Demangler!R x) 683 if (isInputRange!R) 684 { 685 if (x.show) dbg("rest: ", x.r); 686 R id; 687 const sign = x.r.skipOver('n'); // if negative number 688 assert(!sign); 689 const match = x.r.splitBefore!(a => !a.isDigit); 690 const digits = match[0]; 691 x.r = match[1]; 692 if (!digits.empty) // digit prefix 693 { 694 // TODO: Functionize these three lines 695 const num = digits.to!uint; 696 id = x.r[0..num]; // identifier, x.r.take(num) 697 x.r = x.r[num..$]; // x.r.drop(num); 698 x.sourceNames ~= id; 699 } 700 return id; 701 } 702 703 /** See_Also: https://mentorembedded.github.io/cxx-abi/abi.html#mangle.nested-name 704 Note: Second alternative 705 <template-prefix> <template-args> 706 in 707 <nested-name> 708 is redundant as it is included in <prefix> and is skipped here. 709 */ 710 R decodeCxxNestedName(R)(Demangler!R x) 711 if (isInputRange!R) 712 { 713 if (x.show) dbg("rest: ", x.r); 714 if (x.r.skipOver('N')) // nested name: https://mentorembedded.github.io/cxx-abi/abi.html#mangle.nested-name 715 { 716 const cvQ = x.r.decodeCxxCVQualifiers(); 717 const refQ = x.decodeCxxRefQualifier(); 718 const prefix = x.decodeCxxPrefix(); 719 const name = x.decodeCxxUnqualifiedName(); 720 assert(x.r.skipOver('E')); 721 auto ret = (cvQ.to!R ~ 722 prefix ~ 723 name ~ 724 refQ.toCxxString); 725 return ret; 726 } 727 return null; 728 } 729 730 /** TODO: Use this 731 See_Also: https://mentorembedded.github.io/cxx-abi/abi.html#mangle.ctor-dtor-name 732 */ 733 enum CtorDtorName 734 { 735 completeObjectConstructor, 736 baseObjectConstructor, 737 completeObjectAllocatingConstructor, 738 deletingDestructor, 739 completeObjectDestructor, 740 baseObjectDestructor 741 } 742 743 /** See_Also: https://mentorembedded.github.io/cxx-abi/abi.html#mangle.ctor-dtor-name */ 744 R decodeCxxCtorDtorName(R)(Demangler!R x) 745 if (isInputRange!R) 746 { 747 if (x.show) dbg("rest: ", x.r); 748 R name; 749 enum n = 2; 750 if (x.r.length < n) { return typeof(return).init; } 751 import std.array: back; 752 switch (x.r[0..n]) 753 { 754 case `C1`: name = x.sourceNames.back; break; // complete object constructor 755 case `C2`: name = x.sourceNames.back; break; // base object constructor 756 case `C3`: name = x.sourceNames.back; break; // complete object allocating constructor 757 case `D0`: name = '~' ~ x.sourceNames.back; break; // deleting destructor 758 case `D1`: name = '~' ~ x.sourceNames.back; break; // complete object destructor 759 case `D2`: name = '~' ~ x.sourceNames.back; break; // base object destructor 760 default: break; 761 } 762 if (name) 763 x.r.popFrontExactly(n); 764 return name; 765 } 766 767 /** https://mentorembedded.github.io/cxx-abi/abi.html#mangle.unqualified-name */ 768 R decodeCxxUnqualifiedName(R)(Demangler!R x) 769 if (isInputRange!R) 770 { 771 if (x.show) dbg("rest: ", x.r); 772 return either(x.decodeCxxOperatorName(), 773 x.decodeCxxSourceName(), 774 x.decodeCxxCtorDtorName(), 775 x.decodeCxxUnnamedTypeName()); 776 } 777 778 /** See_Also: https://mentorembedded.github.io/cxx-abi/abi.html#mangle.unnamed-type-name */ 779 R decodeCxxUnnamedTypeName(R)(Demangler!R x) 780 if (isInputRange!R) 781 { 782 if (x.show) dbg("rest: ", x.r); 783 R type; 784 if (x.r.skipOver(`Ut`)) 785 { 786 type = x.decodeCxxNumber(); 787 assert(x.r.skipOver('_')); 788 } 789 return type; 790 } 791 792 /** See_Also: https://mentorembedded.github.io/cxx-abi/abi.html#mangle.template-prefix 793 */ 794 R decodeCxxTemplatePrefix(R)(Demangler!R x) 795 if (isInputRange!R) 796 { 797 if (x.show) dbg("rest: ", x.r); 798 // NOTE: Removed <prefix> because of recursion 799 return either(x.decodeCxxUnqualifiedName(), 800 x.decodeCxxTemplateParam(), 801 x.decodeCxxSubstitution()); 802 } 803 804 /** See_Also: https://mentorembedded.github.io/cxx-abi/abi.html#mangle.template-args */ 805 R[] decodeCxxTemplateArgs(R)(Demangler!R x) 806 if (isInputRange!R) 807 { 808 if (x.show) dbg("rest: ", x.r); 809 typeof(return) args; 810 if (x.r.skipOver('I')) 811 { 812 args ~= x.decodeCxxTemplateArg(); 813 while (!x.r.empty) 814 { 815 auto arg = x.decodeCxxTemplateArg(); 816 if (arg) 817 args ~= arg; 818 else 819 break; 820 } 821 assert(x.r.skipOver('E')); 822 } 823 return args; 824 } 825 826 /** See_Also: https://mentorembedded.github.io/cxx-abi/abi.html#mangle.mangled-name */ 827 R decodeCxxMangledName(R)(Demangler!R x) 828 if (isInputRange!R) 829 { 830 if (x.show) dbg("rest: ", x.r); 831 R name; 832 if (x.r.skipOver(`_Z`)) 833 return x.decodeCxxEncoding(); 834 return name; 835 } 836 837 /** See_Also: https://mentorembedded.github.io/cxx-abi/abi.html#mangle.expr-primary */ 838 R decodeCxxExprPrimary(R)(Demangler!R x) 839 if (isInputRange!R) 840 { 841 if (x.show) dbg("rest: ", x.r); 842 R expr; 843 if (x.r.skipOver('L')) 844 { 845 expr = x.decodeCxxMangledName(); 846 if (!expr) 847 { 848 auto number = x.decodeCxxNumber(); 849 // TODO: Howto demangle <float>? 850 // TODO: Howto demangle <float> _ <float> E 851 expr = x.decodeCxxType(); // <R>, <nullptr>, <pointer> type 852 bool pointerType = x.r.skipOver('0'); // null pointer template argument 853 } 854 assert(x.r.skipOver('E')); 855 } 856 return expr; 857 } 858 859 /** See_Also: https://mentorembedded.github.io/cxx-abi/abi.html#mangle.template-arg */ 860 R decodeCxxTemplateArg(R)(Demangler!R x) 861 if (isInputRange!R) 862 { 863 if (x.show) dbg("rest: ", x.r); 864 R arg; 865 if (x.r.skipOver('X')) 866 { 867 arg = x.decodeCxxExpression(); 868 assert(x.r.skipOver('E')); 869 } 870 else if (x.r.skipOver('J')) 871 { 872 R[] args; 873 while (!x.r.empty) 874 { 875 const subArg = x.decodeCxxTemplateArg(); 876 if (subArg) 877 args ~= subArg; 878 else 879 break; 880 } 881 arg = args.joiner(`, `).to!R; 882 assert(x.r.skipOver('E')); 883 } 884 else 885 arg = either(x.decodeCxxExprPrimary(), 886 x.decodeCxxType()); 887 return arg; 888 } 889 890 R decodeCxxTemplatePrefixAndArgs(R)(Demangler!R x) 891 if (isInputRange!R) 892 { 893 if (x.show) dbg("rest: ", x.r); 894 auto restBackup = x.r; 895 if (const prefix = x.decodeCxxTemplatePrefix()) 896 { 897 auto args = x.decodeCxxTemplateArgs(); 898 if (args) 899 return prefix ~ args.joiner(`, `).to!R; 900 } 901 x.r = restBackup; // restore upon failure 902 return typeof(return).init; 903 } 904 905 /** See_Also: https://mentorembedded.github.io/cxx-abi/abi.html#mangle.prefix */ 906 R decodeCxxPrefix(R)(Demangler!R x) 907 if (isInputRange!R) 908 { 909 if (x.show) dbg("rest: ", x.r); 910 typeof(return) prefix; 911 for (size_t i = 0; !x.r.empty; ++i) // NOTE: Turned self-recursion into iteration 912 { 913 if (const name = x.decodeCxxUnqualifiedName()) 914 { 915 if (i >= 1) 916 prefix ~= x.scopeSeparator; 917 prefix ~= name; 918 continue; 919 } 920 else if (const name = x.decodeCxxTemplatePrefixAndArgs()) 921 { 922 prefix ~= name; 923 continue; 924 } 925 else if (const templateParam = x.decodeCxxTemplateParam()) 926 { 927 prefix ~= templateParam; 928 continue; 929 } 930 else if (const decltype = x.decodeCxxDecltype()) 931 { 932 prefix ~= decltype; 933 continue; 934 } 935 else if (const subst = x.decodeCxxSubstitution()) 936 { 937 prefix ~= subst; 938 continue; 939 } 940 else 941 break; 942 } 943 return prefix; 944 } 945 946 /** See_Also: https://mentorembedded.github.io/cxx-abi/abi.html#mangle.unscoped-name */ 947 R decodeCxxUnscopedName(R)(Demangler!R x) 948 if (isInputRange!R) 949 { 950 if (x.show) dbg("rest: ", x.r); 951 auto restBackup = x.r; 952 const prefix = x.r.skipOver(`St`) ? "::std::" : null; 953 if (const name = x.decodeCxxUnqualifiedName()) 954 return prefix ~ name; 955 else 956 { 957 x.r = restBackup; // restore 958 return typeof(return).init; 959 } 960 } 961 962 /** See_Also: https://mentorembedded.github.io/cxx-abi/abi.html#mangle.unscoped-template-name */ 963 R decodeCxxUnscopedTemplateName(R)(Demangler!R x) 964 if (isInputRange!R) 965 { 966 if (x.show) dbg("rest: ", x.r); 967 return either(x.decodeCxxSubstitution(), // faster backtracking with substitution 968 x.decodeCxxUnscopedName()); 969 } 970 971 /** See_Also: https://mentorembedded.github.io/cxx-abi/abi.html#mangle.unscoped-template-name */ 972 R decodeCxxUnscopedTemplateNameAndArgs(R)(Demangler!R x) 973 if (isInputRange!R) 974 { 975 if (x.show) dbg("rest: ", x.r); 976 R nameAndArgs; 977 if (const name = x.decodeCxxUnscopedTemplateName()) 978 { 979 nameAndArgs = name; 980 if (auto args = x.decodeCxxTemplateArgs()) 981 nameAndArgs ~= args.joiner(`, `).to!R; 982 } 983 return nameAndArgs; 984 } 985 986 /** See_Also: https://mentorembedded.github.io/cxx-abi/abi.html#mangle.number */ 987 R decodeCxxNumber(R)(Demangler!R x) 988 if (isInputRange!R) 989 { 990 if (x.show) dbg("rest: ", x.r); 991 R number; 992 const prefix = x.r.skipOver('n'); // optional prefix 993 auto split = x.r.splitBefore!(a => !a.isDigit()); 994 if (prefix || !split[0].empty) // if complete match 995 { 996 x.r = split[1]; 997 number = split[0]; 998 } 999 return number; 1000 } 1001 1002 /** See_Also: https://mentorembedded.github.io/cxx-abi/abi.html#mangle.discriminator */ 1003 R decodeCxxDescriminator(R)(Demangler!R x) 1004 if (isInputRange!R) 1005 { 1006 if (x.show) dbg("rest: ", x.r); 1007 R descriminator; 1008 if (x.r.skipOver('_')) 1009 { 1010 if (x.r.skipOver('_')) // number >= 10 1011 { 1012 descriminator = x.decodeCxxNumber(); 1013 assert(x.r.skipOver('_')); // suffix 1014 } 1015 else // number < 10 1016 { 1017 x.r.skipOver('n'); // optional prefix 1018 /* TODO: Merge these two into a variant of popFront() that returns 1019 the popped element. What is best out of: 1020 - General: x.r.takeOne().to!R 1021 - Arrays only: r[0..1] 1022 - Needs cast: x.r.front 1023 and are we in need of a combined variant of front() and popFront() 1024 say takeFront() that may fail and requires a cast. 1025 */ 1026 /* descriminator = r[0..1]; // single digit */ 1027 /* x.r.popFront(); */ 1028 descriminator = x.r.moveFront().to!R; 1029 } 1030 } 1031 return descriminator; 1032 } 1033 1034 /** See_Also: https://mentorembedded.github.io/cxx-abi/abi.html#mangle.local-name */ 1035 R decodeCxxLocalName(R)(Demangler!R x) 1036 if (isInputRange!R) 1037 { 1038 if (x.show) dbg("rest: ", x.r); 1039 if (x.r.skipOver('Z')) 1040 { 1041 const functionEncoding = x.decodeCxxEncoding(); 1042 x.r.skipOver('E'); 1043 if (x.r.skipOver('D')) 1044 assert(0, "TODO: Decode C++0x Closure Type (lambda)"); // see https://mentorembedded.github.io/cxx-abi/abi.html#closure-types 1045 else 1046 { 1047 const entityNameMaybe = either(x.skipLiteral('s'), // NOTE: Literal first to speed up 1048 x.decodeCxxName()); 1049 const discriminator = x.decodeCxxDescriminator(); // optional 1050 return (functionEncoding ~ 1051 x.scopeSeparator ~ 1052 entityNameMaybe ~ 1053 discriminator.to!R); // TODO: Optional 1054 } 1055 } 1056 return R.init; 1057 } 1058 1059 /** See_Also: https://mentorembedded.github.io/cxx-abi/abi.html#mangle.name */ 1060 R decodeCxxName(R)(Demangler!R x) 1061 if (isInputRange!R) 1062 { 1063 if (x.show) dbg("rest: ", x.r); 1064 return either(x.decodeCxxNestedName(), 1065 x.decodeCxxUnscopedName(), 1066 x.decodeCxxLocalName(), // TODO: order flipped 1067 x.decodeCxxUnscopedTemplateNameAndArgs()); // NOTE: order flipped 1068 } 1069 1070 R decodeCxxNVOffset(R)(Demangler!R x) 1071 if (isInputRange!R) 1072 { 1073 if (x.show) dbg("rest: ", x.r); 1074 return x.decodeCxxNumber(); 1075 } 1076 1077 R decodeCxxVOffset(R)(Demangler!R x) 1078 if (isInputRange!R) 1079 { 1080 if (x.show) dbg("rest: ", x.r); 1081 auto offset = x.decodeCxxNumber(); 1082 assert(x.r.skipOver('_')); 1083 return offset ~ x.decodeCxxNumber(); 1084 } 1085 1086 /** See_Also: https://mentorembedded.github.io/cxx-abi/abi.html#mangle.call-offset */ 1087 R decodeCxxCallOffset(R)(Demangler!R x) 1088 if (isInputRange!R) 1089 { 1090 if (x.show) dbg("rest: ", x.r); 1091 typeof(return) offset; 1092 if (x.r.skipOver('h')) 1093 { 1094 offset = x.decodeCxxNVOffset(); 1095 assert(x.r.skipOver('_')); 1096 } 1097 else if (x.r.skipOver('v')) 1098 { 1099 offset = x.decodeCxxVOffset(); 1100 assert(x.r.skipOver('_')); 1101 } 1102 return offset; 1103 } 1104 1105 /** See_Also: https://mentorembedded.github.io/cxx-abi/abi.html#mangle.special-name */ 1106 R decodeCxxSpecialName(R)(Demangler!R x) 1107 if (isInputRange!R) 1108 { 1109 if (x.show) dbg("rest: ", x.r); 1110 auto restBackup = x.r; 1111 typeof(return) name; 1112 if (x.r.skipOver('S')) 1113 { 1114 switch (x.r.moveFront) 1115 { 1116 case 'V': name = "virtual table: "; break; 1117 case 'T': name = "VTT structure: "; break; 1118 case 'I': name = "typeinfo structure: "; break; 1119 case 'S': name = "typeinfo name (null-terminated byte R): "; break; 1120 default: 1121 x.r = restBackup; // restore 1122 return name; 1123 } 1124 name ~= x.decodeCxxType(); 1125 } 1126 else if (x.r.skipOver(`GV`)) 1127 name = x.decodeCxxName(); 1128 else if (x.r.skipOver('T')) 1129 { 1130 if (x.r.skipOver('c')) 1131 name = x.r.tryEvery(x.decodeCxxCallOffset(), 1132 x.decodeCxxCallOffset(), 1133 x.decodeCxxEncoding()).joiner(` `).to!R; 1134 else 1135 name = x.r.tryEvery(x.decodeCxxCallOffset(), 1136 x.decodeCxxEncoding()).joiner(` `).to!R; 1137 } 1138 return name; 1139 } 1140 1141 /* Decode C++ Symbol. 1142 See_Also: https://mentorembedded.github.io/cxx-abi/abi.html#mangle.encoding 1143 */ 1144 R decodeCxxEncoding(R)(Demangler!R x) /* @safe pure nothrow @nogc */ if (isInputRange!R) 1145 { 1146 if (x.show) dbg("rest: ", x.r); 1147 const localFlag = x.r.skipOver('L'); // TODO: What role does the L have in symbols starting with _ZL have? 1148 if (const name = x.decodeCxxSpecialName()) 1149 return name; 1150 const name = x.decodeCxxName(); 1151 auto type = x.decodeCxxBareFunctionType(); 1152 return name ~ type.to!R; 1153 } 1154 1155 /** Demangled Expression. */ 1156 alias Expr = string; 1157 1158 struct Demangling 1159 { 1160 Lang language; 1161 Expr unmangled; 1162 auto opCast(T : bool)() @safe pure nothrow const => !expr.empty; 1163 } 1164 1165 /** Demangle Symbol $(D r) and Detect Language. 1166 See_Also: https://en.wikipedia.org/wiki/Name_mangling 1167 See_Also: https://mentorembedded.github.io/cxx-abi/abi.html#mangling 1168 See_Also: https://gcc.gnu.org/onlinedocs/libstdc++/manual/ext_demangling.html 1169 */ 1170 Demangling decodeSymbol(R)(Demangler!R x) /* @safe pure nothrow @nogc */ 1171 if (isInputRange!R) 1172 { 1173 if (x.r.empty) 1174 return Demangling(Lang.init, x.r); 1175 if (!x.r.startsWith('_')) 1176 return Demangling(Lang.c, x.r); // assume C 1177 // See_Also: https://mentorembedded.github.io/cxx-abi/abi.html#mangle.mangled-name 1178 if (x.r.skipOver(`_Z`)) 1179 return Demangling(Lang.cxx, x.decodeCxxEncoding()); 1180 else 1181 { 1182 import core.demangle: demangle; 1183 const symAsD = x.r.demangle; 1184 import std.conv: to; 1185 if (symAsD != x.r) // TODO: Why doesn't (symAsD is r) work here? 1186 return Demangling(Lang.d, symAsD.to!R); 1187 else 1188 return Demangling(Lang.init, x.r); 1189 } 1190 } 1191 1192 unittest 1193 { 1194 import nxt.assert_ex; 1195 1196 assertEqual(demangler(`memcpy`).decodeSymbol(), 1197 Demangling(Lang.c, `memcpy`)); 1198 1199 assertEqual(demangler(`memcpy`).decodeSymbol(), 1200 Demangling(Lang.c, `memcpy`)); 1201 1202 assertEqual(demangler(`_Z1hi`).decodeSymbol(), 1203 Demangling(Lang.cxx, `h(int)`)); 1204 1205 assertEqual(demangler(`_Z3foo3bar`).decodeSymbol(), 1206 Demangling(Lang.cxx, `foo(bar)`)); 1207 1208 assertEqual(demangler(`_ZN1N1fE`).decodeSymbol(), 1209 Demangling(Lang.cxx, `N::f`)); 1210 1211 assertEqual(demangler(`_ZN3Foo3BarEv`).decodeSymbol(), 1212 Demangling(Lang.cxx, `Foo::Bar()`)); 1213 1214 assertEqual(demangler(`_ZN3FooC1Ev`).decodeSymbol(), 1215 Demangling(Lang.cxx, `Foo::Foo()`)); 1216 1217 assertEqual(demangler(`_ZN9wikipedia7article6formatE`).decodeSymbol(), 1218 Demangling(Lang.cxx, `wikipedia::article::format`)); 1219 1220 assertEqual(demangler(`_ZSt5state`).decodeSymbol(), 1221 Demangling(Lang.cxx, `::std::state`)); 1222 1223 assertEqual(demangler(`_ZN9wikipedia7article8print_toERSo`).decodeSymbol(), 1224 Demangling(Lang.cxx, `wikipedia::article::print_to(::std::ostream&)`)); 1225 1226 assertEqual(demangler(`_ZN9wikipedia7article8print_toEOSo`).decodeSymbol(), 1227 Demangling(Lang.cxx, `wikipedia::article::print_to(::std::ostream&&)`)); 1228 1229 assertEqual(demangler(`_ZN9wikipedia7article6formatEv`, true).decodeSymbol(), 1230 Demangling(Lang.cxx, `wikipedia::article::format(void)`)); 1231 1232 assertEqual(demangler(`_ZN9wikipedia7article6formatEv`, false).decodeSymbol(), 1233 Demangling(Lang.cxx, `wikipedia::article::format()`)); 1234 1235 assertEqual(demangler(`_ZL8next_argRPPc`).decodeSymbol(), 1236 Demangling(Lang.cxx, `next_arg(char**&)`)); 1237 1238 assertEqual(demangler(`_ZL10parse_archmPPKcS0_`, true).decodeSymbol(), 1239 Demangling(Lang.cxx, `parse_arch(unsigned long, char const**, char const*)`)); 1240 1241 assertEqual(demangler(`_ZN5LexerC2EP6ModulePKhmmii`, true).decodeSymbol(), 1242 Demangling(Lang.cxx, `Lexer::Lexer(Module*, unsigned char const*, unsigned long, unsigned long, int, int)`)); 1243 1244 assertEqual(demangler(`_Zrm1XS_`).decodeSymbol(), 1245 Demangling(Lang.cxx, `operator%(X, X)`)); 1246 1247 assertEqual(demangler(`_ZZL8next_argRPPcE4keys`).decodeSymbol(), 1248 Demangling(Lang.cxx, `next_arg(char**&)::keys`)); 1249 1250 assertEqual(demangler(`_ZN12ExpStatement9scopeCodeEP5ScopePP9StatementS4_S4`).decodeSymbol(), 1251 Demangling(Lang.cxx, `ExpStatement::scopeCode(Scope*, Statement**, Statement**, Statement**)`)); 1252 1253 assertEqual(demangler(`_ZZ8genCmainP5ScopeE9cmaincode`).decodeSymbol(), 1254 Demangling(Lang.cxx, `genCmain(Scope*)::cmaincode`)); 1255 1256 assertEqual(demangler("_Z7DtoLValP6DValue").decodeSymbol(), 1257 Demangling(Lang.cxx, `DtoLVal(DValue*)`)); 1258 }