Schemes¶
Each mangling scheme is a plugin, so any of them can be developed, replaced or shipped separately -- see Adding a scheme and Architecture.
Each section renders what the scheme's package exports, then the modules behind it: its options, its node kinds and, where it is worth reading, its parser. Each object appears once, with the module that says most about it -- an options class with its options.
Itanium C++ ABI¶
demangle.schemes.itanium
¶
The Itanium C++ ABI mangling scheme.
Used by GCC, Clang, ICC and essentially every C++ toolchain outside the Microsoft ecosystem, on every platform they target. Rust's legacy scheme is a dialect of it.
Reference: https://itanium-cxx-abi.github.io/cxx-abi/abi.html section 5.1.
PLUGIN
module-attribute
¶
PLUGIN = LanguagePlugin(
name="itanium",
node_kinds=(
"array",
"builtin",
"decorated",
"expression",
"function",
"member_pointer",
"name",
"pack",
"parameter_pack",
"pointer",
"qualified",
"qualify",
"raw",
"reference",
"rvalue_reference",
"special",
"template",
"vendor_qualify",
),
detect=detect,
parse=parse,
parse_type=parse_type,
description="Itanium C++ ABI (GCC, Clang, and compatible toolchains)",
aliases=("gnu", "gcc", "clang", "cxx", "c++"),
options_type=ItaniumOptions,
symbol_table_decorations=True,
first_characters="_",
priority=200,
)
The scheme as the registry holds it, registered when this package is imported.
ItaniumParser
¶
ItaniumParser(
mangled,
builder,
limits=DEFAULT_LIMITS,
options=DEFAULT_OPTIONS,
)
Parses one mangled name into one builder. Single use.
State -- cursor, substitution table, template scope -- is per name, so an instance is cheap and never shared. The builder, by contrast, is stateless and shared.
Source code in src/demangle/schemes/itanium/parser.py
440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 | |
parse
¶
parse()
Source code in src/demangle/schemes/itanium/parser.py
663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 | |
block_invocation
¶
block_invocation()
___Z <encoding> _block_invoke [[_] <digits>] [. <suffix>], a Clang extension.
The function a block's body compiles to. Its name is the enclosing function's,
with an underscore in front for the block and another for the leading one a
Mach-O symbol table adds -- which is why ___Z is three underscores and
____Z is four.
The reference finds the encoding's end by parsing until a type will not read and
then requiring the literal; this bounds the encoding at the literal instead,
which is the same split and does not need the parameter loop to swallow an
error. Bounded at the last occurrence, because a function may be called
_block_invoke itself and the encoding is as long as it can be.
Neither the trailing number nor a . suffix is printed: the number distinguishes
several blocks in one function, and what a caller is told is which function the
block was written in.
Source code in src/demangle/schemes/itanium/parser.py
717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 | |
encoding
¶
encoding()
Every path out of here records what the entity was in _entity_shape, for
_entity_operand to read back. An encoding nested inside this one -- a
L _Z... E in the template arguments -- writes it first and this overwrites it,
which is the order that leaves the outermost answer standing.
Source code in src/demangle/schemes/itanium/parser.py
754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 | |
bare_function_type
¶
bare_function_type(
name, quals=(), ref_qualifier="", is_template=False
)
The first type is the return type only for a template specialisation: a plain function does not encode its return type, because overloads cannot differ by it. For a template they can, so it is part of the signature (5.1.5.3).
Source code in src/demangle/schemes/itanium/parser.py
786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 | |
special_name
¶
special_name()
Vtables, typeinfo, thunks, guard variables. None if this is not one.
Guarded: several of these productions contain an <encoding>, which can be
another special name, so _Z followed by GA repeated is unbounded recursion.
Source code in src/demangle/schemes/itanium/parser.py
898 899 900 901 902 903 904 905 906 907 908 909 910 | |
call_offset
¶
call_offset()
Source code in src/demangle/schemes/itanium/parser.py
1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 | |
name
¶
name(as_type=False)
Returns (handle, cv-qualifiers, ref-qualifier, is-template-specialisation). The qualifiers belong to the function the name introduces rather than to the name, but the nested-name production is where they are encoded, so they travel back out with it.
Source code in src/demangle/schemes/itanium/parser.py
1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 | |
apply_template_args
¶
apply_template_args(base)
Attach
Deliberately records nothing. A function template specialisation is not a
substitution candidate: 5.1.10 excludes function names, and both reference
demanglers reject _Z1fIiEvS0_, proving the table for _Z1fIiEv... holds only
f. Where the specialisation is a type rather than a function, _type()
records it, because there the candidate is
Source code in src/demangle/schemes/itanium/parser.py
1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 | |
nested_name
¶
nested_name(as_type=False)
A qualified name.
<nested-name> ::= N [<CV-qualifiers>] [<ref-qualifier>] <prefix> <unqualified-name> E
| N [<CV-qualifiers>] [<ref-qualifier>] <template-prefix> <template-args> E
Source code in src/demangle/schemes/itanium/parser.py
1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 1298 1299 1300 1301 1302 1303 1304 1305 1306 1307 1308 1309 1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 1323 1324 1325 1326 1327 1328 1329 1330 1331 1332 1333 1334 1335 1336 1337 1338 1339 1340 1341 1342 1343 1344 1345 1346 1347 1348 1349 1350 1351 1352 1353 1354 1355 1356 1357 | |
prefix_component
¶
prefix_component(parts, as_type=False, module='')
One component of a parts.
Returns (is-template-specialisation, module). Only the first answer for the
final component reaches the caller, and this is where it is known. The second is
a C++20 module name this component turned out to be rather than contain, which
decorates the component after it.
Every E is in sight,
so the check is "is the next character E".
Source code in src/demangle/schemes/itanium/parser.py
1383 1384 1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 1396 1397 1398 1399 1400 1401 1402 1403 1404 1405 1406 1407 1408 1409 1410 1411 1412 1413 1414 1415 1416 1417 1418 1419 1420 1421 1422 1423 1424 1425 1426 1427 1428 1429 1430 1431 1432 1433 1434 1435 1436 1437 1438 1439 1440 1441 1442 1443 1444 1445 1446 1447 1448 1449 1450 1451 1452 1453 1454 1455 1456 1457 1458 1459 1460 1461 1462 1463 1464 1465 1466 1467 1468 1469 1470 1471 1472 1473 1474 1475 1476 1477 1478 1479 1480 1481 1482 1483 1484 1485 1486 1487 1488 1489 1490 1491 1492 1493 1494 1495 1496 1497 1498 1499 1500 1501 1502 1503 1504 1505 1506 1507 1508 1509 1510 1511 1512 1513 | |
local_name
¶
local_name(as_type=False)
Source code in src/demangle/schemes/itanium/parser.py
1558 1559 1560 1561 1562 1563 1564 1565 1566 1567 1568 1569 1570 1571 1572 1573 1574 1575 1576 1577 1578 1579 1580 1581 1582 1583 1584 1585 1586 1587 1588 1589 1590 1591 1592 1593 1594 1595 1596 1597 1598 1599 1600 1601 1602 1603 1604 1605 1606 1607 1608 1609 1610 1611 1612 1613 1614 1615 1616 1617 1618 1619 1620 1621 1622 1623 1624 1625 1626 1627 1628 1629 1630 1631 1632 1633 1634 1635 1636 1637 1638 1639 1640 1641 1642 1643 1644 1645 1646 1647 1648 1649 | |
discriminator
¶
discriminator()
Tells apart same-named entities in one function.
<discriminator> ::= _ <non-negative number> | __ <number> _
Tells apart same-named entities in one function. It carries no spelling, but it has to be consumed or it looks like trailing junk.
Source code in src/demangle/schemes/itanium/parser.py
1672 1673 1674 1675 1676 1677 1678 1679 1680 1681 1682 1683 1684 1685 1686 1687 1688 1689 1690 1691 1692 1693 1694 1695 1696 1697 1698 | |
unqualified_name
¶
unqualified_name(scope=None, module='', internal=False)
<name body> ::= <operator-name> | <ctor-dtor-name> | <source-name>
| <unnamed-type-name> | DC <source-name>+ E
module is a C++20 module name the caller already read -- a Foo@MOD.
Source code in src/demangle/schemes/itanium/parser.py
1702 1703 1704 1705 1706 1707 1708 1709 1710 1711 1712 1713 1714 1715 1716 1717 1718 1719 1720 1721 1722 1723 1724 1725 1726 1727 1728 1729 1730 1731 1732 1733 1734 1735 1736 1737 1738 1739 1740 1741 1742 1743 1744 1745 1746 1747 1748 1749 1750 1751 1752 1753 1754 1755 1756 1757 1758 1759 1760 1761 1762 1763 1764 1765 1766 1767 1768 1769 1770 1771 1772 1773 1774 1775 1776 1777 1778 1779 1780 1781 1782 1783 1784 1785 1786 1787 1788 1789 1790 1791 1792 1793 1794 1795 1796 1797 1798 1799 1800 1801 1802 1803 1804 1805 1806 1807 1808 1809 1810 1811 1812 1813 1814 1815 1816 1817 1818 1819 1820 1821 1822 1823 1824 1825 | |
constructor_name
¶
constructor_name(scope)
A constructor name.
<ctor-dtor-name> ::= C1 | C2 | C3 | CI1 <base class type> | CI2 <base class type>
The class's bare name, with no module attached: _ZNW4llvm6ModuleC1Ev is
Module@llvm::Module() to both references -- CtorDtorName prints the scope's
getBaseName(), and a ModuleEntity's base name is the name inside it -- not
Module@llvm::Module@llvm(). The destructor the same.
Source code in src/demangle/schemes/itanium/parser.py
1838 1839 1840 1841 1842 1843 1844 1845 1846 1847 1848 1849 1850 1851 1852 1853 1854 1855 1856 1857 1858 1859 1860 1861 1862 1863 1864 1865 1866 1867 1868 1869 1870 1871 1872 1873 1874 1875 1876 1877 | |
enclosing_class_name
¶
enclosing_class_name(scope)
The bare class name that a constructor or destructor repeats.
Foo::Foo is encoded as "the class, then a constructor marker" -- the marker
has no spelling of its own, so the name is read back off the scope we are
standing in. A specialisation's constructor drops the arguments: the constructor
of Foo<int> is spelled Foo<int>::Foo, not Foo<int>::Foo<int>.
Source code in src/demangle/schemes/itanium/parser.py
1879 1880 1881 1882 1883 1884 1885 1886 1887 1888 1889 1890 1891 1892 1893 1894 1895 1896 1897 1898 1899 1900 1901 1902 1903 1904 1905 1906 1907 1908 1909 1910 1911 1912 1913 1914 1915 1916 1917 1918 1919 1920 1921 1922 1923 | |
source_name
¶
source_name()
Source code in src/demangle/schemes/itanium/parser.py
1942 1943 1944 1945 | |
plain_source_name
¶
plain_source_name()
A
Split out because a module name goes between the two: 3FooB3ABI inside
module MOD is Foo@MOD[abi:ABI], not Foo[abi:ABI]@MOD.
Source code in src/demangle/schemes/itanium/parser.py
1947 1948 1949 1950 1951 1952 1953 1954 1955 1956 1957 1958 1959 1960 1961 1962 1963 1964 1965 1966 1967 1968 1969 1970 1971 1972 1973 1974 1975 1976 1977 1978 1979 1980 1981 1982 1983 | |
module_name
¶
module_name(module='')
A C++20 module name, as the text that goes after the @.
<module-name> ::= <module-subname>+ | <substitution>
<module-subname> ::= W <source-name> | W P <source-name>
Submodules join with . and a partition with :, so W3FooWP3BarW3Baz is
Foo:Bar.Baz. Every prefix of the run is a substitution candidate in its own
right, which is how S1_ comes to stand for FOO.BAR in a later parameter.
Newer than the grammar snapshot in docs/specs/.
Source code in src/demangle/schemes/itanium/parser.py
2003 2004 2005 2006 2007 2008 2009 2010 2011 2012 2013 2014 2015 2016 2017 2018 2019 2020 2021 2022 2023 2024 2025 2026 2027 2028 2029 2030 2031 2032 2033 2034 2035 | |
abi_tags
¶
abi_tags()
ABI tags, spelled [abi:tag].
<abi-tags> ::= <abi-tag>* <abi-tag> ::= B <source-name>
Callers on a hot path ask reader.peek() == "B" before calling: the answer is no
for all but a handful of names in a symbol table, and asking it here costs a whole
interpreter frame to return the empty string -- five times per name over the
Itanium corpus, which is 9% of every peek the parser makes. The test is
repeated below so that a caller who does not ask is still right.
Source code in src/demangle/schemes/itanium/parser.py
2045 2046 2047 2048 2049 2050 2051 2052 2053 2054 2055 2056 2057 2058 2059 2060 2061 2062 2063 2064 2065 | |
unnamed_type_name
¶
unnamed_type_name(lambda_expression=False)
A closure or unnamed type.
lambda_expression spells the closure as the expression that made it rather
than as its type -- [](){...}, which is what a lambda written as a template
argument reads as.
<unnamed-type-name> ::= Ut [<number>] _ | <closure-type-name>
<closure-type-name> ::= Ul <lambda-sig> E [<number>] _
Source code in src/demangle/schemes/itanium/parser.py
2067 2068 2069 2070 2071 2072 2073 2074 2075 2076 2077 2078 2079 2080 2081 2082 2083 2084 2085 2086 2087 2088 2089 2090 2091 2092 2093 2094 2095 2096 2097 2098 2099 2100 2101 2102 2103 2104 2105 2106 2107 2108 2109 2110 2111 2112 2113 2114 2115 2116 2117 2118 2119 2120 2121 2122 2123 2124 2125 2126 2127 2128 2129 2130 2131 2132 2133 2134 2135 2136 2137 2138 2139 2140 2141 2142 2143 2144 2145 2146 2147 2148 2149 2150 2151 2152 2153 2154 2155 2156 2157 2158 2159 2160 | |
operator_name
¶
operator_name()
Source code in src/demangle/schemes/itanium/parser.py
2180 2181 2182 2183 2184 2185 2186 2187 2188 2189 2190 2191 2192 2193 2194 2195 2196 2197 2198 2199 2200 2201 2202 2203 2204 2205 2206 2207 2208 2209 2210 2211 2212 2213 2214 2215 2216 2217 2218 2219 2220 2221 2222 2223 2224 2225 2226 | |
substitution
¶
substitution(expanded=False)
A back-reference or a predefined abbreviation.
<substitution> ::= S <seq-id> _ | S_ | St | Sa | Sb | Ss | Si | So | Sd
expanded forces the full spelling of an abbreviation regardless of style,
which is what a constructor's scope needs: a constructor is named for its class,
and the class is the template rather than the typedef.
Source code in src/demangle/schemes/itanium/parser.py
2254 2255 2256 2257 2258 2259 2260 2261 2262 2263 2264 2265 2266 2267 2268 2269 2270 2271 2272 2273 2274 2275 2276 2277 2278 2279 2280 2281 2282 2283 2284 2285 2286 2287 2288 2289 2290 2291 2292 2293 2294 2295 2296 2297 2298 2299 2300 2301 2302 2303 2304 2305 2306 2307 2308 2309 2310 2311 2312 2313 2314 2315 | |
template_param
¶
template_param()
A reference to a template parameter, resolved against the scope in force.
Source code in src/demangle/schemes/itanium/parser.py
2387 2388 2389 | |
template_param_binding
¶
template_param_binding()
A
<template-param> ::= T_ | T <parameter-2 non-negative number> _
| TL <level> _ [<parameter-2 non-negative number>] _
The TL form names a parameter of an enclosing template by level as well as by
index, which Clang emits inside the constraints of a nested template. It is
newer than the grammar snapshot in docs/specs/.
Returns (handle, reference). The reference is what a caller recording this
parameter as a substitution candidate must store -- see ParameterReference for
why the resolved handle is the wrong thing to keep. It is None inside a
constraint, where the parameter is spelled by its own mangled text and so cannot
mean anything different when the entry is read again.
Source code in src/demangle/schemes/itanium/parser.py
2391 2392 2393 2394 2395 2396 2397 2398 2399 2400 2401 2402 2403 2404 2405 2406 2407 2408 2409 2410 2411 2412 2413 2414 2415 2416 2417 2418 2419 2420 2421 2422 2423 2424 2425 2426 2427 2428 2429 2430 2431 2432 2433 2434 2435 2436 2437 2438 2439 2440 2441 2442 2443 2444 2445 2446 2447 2448 | |
bind_template_param
¶
bind_template_param(index, level=0)
What TL<level>_<index>_ names under the arguments currently in scope.
Source code in src/demangle/schemes/itanium/parser.py
2450 2451 2452 2453 2454 2455 2456 2457 2458 2459 2460 2461 2462 2463 2464 2465 2466 2467 2468 2469 2470 2471 2472 2473 2474 2475 2476 2477 2478 2479 2480 2481 2482 2483 2484 2485 2486 2487 2488 2489 2490 2491 2492 2493 2494 2495 2496 2497 2498 2499 2500 2501 2502 2503 | |
decltype_
¶
decltype_()
Source code in src/demangle/schemes/itanium/parser.py
2509 2510 2511 2512 2513 2514 2515 2516 2517 2518 2519 | |
cv_qualifiers
¶
cv_qualifiers()
cv-qualifiers, returned in C++'s canonical order.
<CV-qualifiers> ::= [r] [V] [K]
At most one of each, in the order the grammar gives. Consuming a whole run
instead would fold KK into a single qualified type where the ABI has two
nested ones -- one substitution entry rather than two, shifting every later
back-reference in the name.
Source code in src/demangle/schemes/itanium/parser.py
2523 2524 2525 2526 2527 2528 2529 2530 2531 2532 2533 2534 2535 2536 2537 2538 2539 2540 2541 2542 2543 2544 2545 2546 2547 2548 2549 | |
extended_type
¶
extended_type()
The D-introduced types: extended builtins, decltype, packs, vectors.
Source code in src/demangle/schemes/itanium/parser.py
2773 2774 2775 2776 2777 2778 2779 2780 2781 2782 2783 2784 2785 2786 2787 2788 2789 2790 2791 2792 2793 2794 2795 2796 2797 2798 2799 2800 2801 2802 2803 2804 2805 2806 2807 2808 2809 2810 2811 2812 2813 2814 2815 2816 2817 2818 2819 2820 2821 2822 2823 2824 2825 2826 2827 2828 2829 2830 2831 2832 2833 2834 2835 2836 2837 2838 2839 2840 2841 2842 2843 2844 2845 2846 2847 2848 2849 2850 2851 2852 2853 2854 2855 2856 2857 2858 2859 2860 2861 2862 2863 2864 2865 2866 2867 2868 2869 2870 2871 2872 2873 2874 2875 2876 2877 2878 2879 2880 2881 2882 2883 2884 2885 2886 2887 2888 2889 2890 2891 2892 2893 2894 2895 2896 2897 2898 2899 2900 | |
fixed_point_type
¶
fixed_point_type()
Embedded C's saturating and non-saturating fixed-point types (N1169), added to
the ABI in 2023. DS is the _Sat qualifier and comes first.
Source code in src/demangle/schemes/itanium/parser.py
2902 2903 2904 2905 2906 2907 2908 2909 2910 2911 2912 2913 2914 2915 2916 2917 2918 | |
vector_type
¶
vector_type()
And two shapes the ABI text does not have. Clang writes a dependent size with
no underscore before it, Dv <expression> _ <type> -- mangleType for a
DependentSizedExtVectorType is Out << "Dv"; mangleExpression(Size); Out <<
'_' -- which is what llvm-cxxfilt reads and c++filt refuses; the form with
the underscore is what c++filt reads and llvm-cxxfilt refuses. Both are
read. And AltiVec's __vector pixel is Dv <number> _ p, a p where the
element type would be; Dv4_b is __vector bool to the mangler and bool to
every demangler, so it is left as the type letter it also is.
Source code in src/demangle/schemes/itanium/parser.py
2920 2921 2922 2923 2924 2925 2926 2927 2928 2929 2930 2931 2932 2933 2934 2935 2936 2937 2938 2939 2940 2941 2942 2943 2944 2945 2946 2947 2948 2949 2950 2951 2952 2953 2954 | |
class_enum_type
¶
class_enum_type()
as_type matters for a Z <encoding> E <entity> reached as an
Source code in src/demangle/schemes/itanium/parser.py
2956 2957 2958 2959 2960 2961 2962 2963 2964 2965 2966 | |
qualified_type
¶
qualified_type()
A vendor qualifier and the cv-qualifiers underneath it are read together, so
U3AS1Ki enters one substitution and not two. That matters for every later
back-reference in the name: with two, the S0_ in _Z1fPU3AS1KiS0_ names
int const AS1 where it should name int const AS1*, and the second parameter
loses its pointer.
Source code in src/demangle/schemes/itanium/parser.py
2988 2989 2990 2991 2992 2993 2994 2995 2996 2997 2998 2999 3000 3001 3002 3003 3004 3005 3006 3007 3008 3009 3010 3011 3012 3013 3014 3015 3016 3017 3018 3019 3020 3021 3022 3023 3024 3025 3026 3027 3028 3029 3030 3031 3032 3033 3034 3035 3036 3037 3038 3039 3040 3041 | |
function_type_production
¶
function_type_production()
The whole of [<CV-qualifiers>] [<exception-spec>] [Dx] F ... E.
One grammar component, and so one substitution entry, however many of the optional prefixes are present. Reading the exception specification as a type in its own right and then qualifying the result would enter two, shifting every later back-reference in the name.
The pieces are spelled in the order C++ writes them -- cv-qualifiers, then the
ref-qualifier, then the exception specification -- which is not the order the
scheme encodes them in: M1XKFivOE is int (X::*)() const &&, with the K read
first and printed second.
Source code in src/demangle/schemes/itanium/parser.py
3043 3044 3045 3046 3047 3048 3049 3050 3051 3052 3053 3054 3055 3056 3057 3058 3059 3060 3061 3062 3063 3064 3065 3066 3067 3068 3069 3070 3071 3072 3073 3074 3075 3076 3077 3078 3079 3080 3081 3082 3083 3084 | |
function_type
¶
function_type(exception_spec='', cv_qualifiers=())
A function type.
<function-type> ::= [<CV-qualifiers>] [<exception-spec>] [Dx] F [Y]
<bare-function-type> [<ref-qualifier>] E
exception_spec is already-spelled text from the caller, which read it before
the F because that is where the grammar puts it.
Source code in src/demangle/schemes/itanium/parser.py
3086 3087 3088 3089 3090 3091 3092 3093 3094 3095 3096 3097 3098 3099 3100 3101 3102 3103 3104 3105 3106 3107 3108 3109 3110 3111 3112 3113 3114 3115 3116 3117 3118 3119 3120 3121 3122 3123 3124 3125 3126 3127 3128 3129 3130 3131 3132 3133 3134 3135 3136 3137 3138 3139 3140 3141 3142 3143 3144 3145 | |
array_type
¶
array_type()
An array type.
<array-type> ::= A [<number>] _ <type> | A <expression> _ <type>
Source code in src/demangle/schemes/itanium/parser.py
3147 3148 3149 3150 3151 3152 3153 3154 3155 3156 3157 3158 3159 3160 3161 3162 3163 3164 3165 | |
member_pointer_type
¶
member_pointer_type()
Source code in src/demangle/schemes/itanium/parser.py
3167 3168 3169 3170 3171 3172 3173 | |
template_param_decl
¶
template_param_decl(ellipsis='', params=None, named=True)
Guarded wrapper: Tp and Tt both recurse into this production.
Source code in src/demangle/schemes/itanium/parser.py
3177 3178 3179 3180 3181 3182 3183 3184 3185 | |
template_arguments
¶
template_arguments(install_scope=False)
A template argument list.
<template-args> ::= I <template-arg>+ E
T_ names a parameter of the innermost enclosing template declaration, so
which argument list is in scope matters and the two callers differ:
install_scope=True -- these arguments belong to the entity being named, so
they become the T_ scope, replacing any outer one. In
Class<char>::method<char const*>, T_ inside the signature is char const*,
not char. They are installed one at a time because a later argument may
reference an earlier one.
install_scope=False -- these arguments belong to a type mentioned in
passing, such as the std::allocator<char> inside a parameter list. They are
not template parameters of anything being declared here, so they leave the
scope untouched; letting them append would corrupt every later T_.
Source code in src/demangle/schemes/itanium/parser.py
3328 3329 3330 3331 3332 3333 3334 3335 3336 3337 3338 3339 3340 3341 3342 3343 3344 3345 3346 3347 3348 3349 3350 3351 3352 3353 3354 3355 3356 3357 3358 3359 3360 3361 3362 3363 3364 3365 3366 3367 3368 3369 3370 3371 3372 3373 3374 3375 3376 3377 3378 3379 3380 3381 3382 3383 3384 3385 3386 3387 3388 3389 3390 3391 3392 3393 3394 3395 3396 3397 3398 3399 3400 3401 3402 3403 3404 3405 3406 3407 3408 3409 3410 3411 3412 3413 3414 3415 3416 3417 3418 3419 3420 3421 3422 | |
template_arg
¶
template_arg()
One template argument.
<template-arg> ::= <type> | X <expression> E | <expr-primary> | J <template-arg>* E
An argument pack is also read from I <template-arg>* E: the form g++ wrote for
one under -fabi-version 2 through 5, the default of GCC 3.4 through 4.9, and
still writes as a compatibility alias beside the J form when asked for those
versions. Nothing else can stand where an argument does and begin with I, so
there is nothing to tell it from; libiberty's d_template_arg reads I and
J alike, and llvm-cxxfilt refuses the older one.
Returns (handle, is_empty_pack). The handle is None for a
Emptiness is returned rather than recorded on the parser because argument lists
nest: an empty pack inside AnalysisManager<Module, JE> would otherwise still be
flagged when the enclosing PassManager<Function, AnalysisManager<...>> finished
its own argument, and the enclosing argument would be dropped.
Source code in src/demangle/schemes/itanium/parser.py
3424 3425 3426 3427 3428 3429 3430 3431 3432 3433 3434 3435 3436 3437 3438 3439 3440 3441 3442 3443 3444 3445 3446 3447 3448 3449 3450 3451 3452 3453 3454 3455 3456 3457 3458 3459 3460 3461 3462 3463 3464 3465 3466 3467 3468 3469 3470 3471 3472 3473 3474 3475 3476 3477 3478 3479 3480 3481 3482 3483 3484 3485 3486 3487 3488 3489 3490 3491 3492 3493 3494 3495 3496 3497 3498 3499 3500 3501 3502 3503 3504 3505 3506 3507 3508 3509 3510 3511 3512 3513 3514 3515 3516 3517 3518 3519 3520 3521 3522 3523 3524 3525 3526 3527 3528 3529 3530 3531 3532 3533 3534 3535 3536 3537 3538 3539 3540 3541 3542 3543 3544 3545 | |
expr_primary
¶
expr_primary()
A literal or a reference to a declared entity.
<expr-primary> ::= L <type> <value number> E | L <mangled-name> E | L _Z <encoding> E
Source code in src/demangle/schemes/itanium/parser.py
3549 3550 3551 3552 3553 3554 3555 3556 3557 3558 3559 3560 3561 3562 3563 3564 3565 3566 3567 3568 3569 3570 3571 3572 3573 3574 3575 3576 3577 3578 3579 3580 3581 3582 3583 3584 3585 3586 3587 3588 3589 3590 3591 3592 3593 3594 3595 3596 3597 3598 3599 3600 3601 3602 3603 3604 3605 3606 3607 3608 3609 3610 3611 3612 3613 3614 3615 3616 3617 3618 3619 3620 3621 3622 3623 3624 3625 3626 3627 3628 3629 3630 3631 3632 3633 3634 3635 3636 3637 3638 3639 3640 3641 3642 3643 3644 | |
spell_float_literal
¶
spell_float_literal(kind, value)
L <d|e|f> <hex> E: the value's bytes, most significant first, in hex.
llvm-cxxfilt decodes them and prints the number with C's %a, f after a
float and L after a long double; c++filt writes the hex as it stands, in
brackets after the type: (double)[4048f5c28f5c28f6]. Each spelling here is one
of those.
The width is the type's: eight digits for a float and sixteen for a double, and
for a long double whichever the target has -- sixteen where it is a double,
twenty for the x87 extended format and thirty-two for the IEEE quad, plus the
x87 format as g++ pads it to the type's size, twenty-four digits on i386 and
thirty-two on x86-64; see _c_hex_float. llvm-cxxfilt insists on the width
of the machine it runs on and refuses the rest; c++filt brackets any run of
characters at all. Neither is a reading of Ld4048E, which is no value, so the
width has to be one of those and every character a hex digit -- a lowercase
one, as the ABI says and as LLVM's main branch requires.
Source code in src/demangle/schemes/itanium/parser.py
3646 3647 3648 3649 3650 3651 3652 3653 3654 3655 3656 3657 3658 3659 3660 3661 3662 3663 3664 3665 3666 3667 3668 3669 3670 3671 3672 3673 | |
spell_literal
staticmethod
¶
spell_literal(kind, value)
Render a literal the way the reference demanglers do.
The scheme encodes the value as digits with a leading n for negative, and
leaves the suffix implied by the type. Reproducing the suffix matters: 1 and
1u are different template arguments and must not print alike.
Source code in src/demangle/schemes/itanium/parser.py
3736 3737 3738 3739 3740 3741 3742 3743 3744 3745 3746 3747 3748 3749 3750 3751 3752 3753 3754 3755 3756 3757 3758 3759 3760 3761 3762 3763 3764 3765 | |
unresolved_name
¶
unresolved_name()
A name the compiler could not resolve.
<unresolved-name> ::= [gs] <base-unresolved-name>
| sr <unresolved-type> [<template-args>] <base-unresolved-name>
| srN <unresolved-type> [<template-args>]
<unresolved-qualifier-level>* E <base-unresolved-name>
| [gs] sr <unresolved-qualifier-level>+ E <base-unresolved-name>
A name written in a template that the compiler could not resolve, because it
depends on a parameter: std::is_signed_v<T> inside an enable_if. These reach
a mangled name through SFINAE return types, which is why they are everywhere in
heavily templated C++ and absent from simple test cases.
The two sr forms without N are told apart by what follows: an
T, D or S.
Source code in src/demangle/schemes/itanium/parser.py
3769 3770 3771 3772 3773 3774 3775 3776 3777 3778 3779 3780 3781 3782 3783 3784 3785 3786 3787 3788 3789 3790 3791 3792 3793 3794 3795 3796 3797 3798 3799 3800 3801 3802 3803 3804 3805 3806 3807 3808 3809 3810 3811 3812 3813 3814 3815 3816 3817 3818 3819 3820 3821 3822 3823 3824 3825 3826 3827 3828 3829 3830 3831 | |
unresolved_type
¶
unresolved_type()
A dependent type at the head of an unresolved name.
<unresolved-type> ::= <template-param> | <decltype> | <substitution>
Source code in src/demangle/schemes/itanium/parser.py
3880 3881 3882 3883 3884 3885 3886 3887 3888 3889 3890 3891 3892 3893 3894 3895 | |
simple_id
¶
simple_id()
<simple-id> ::= <source-name> [<template-args>]
Source code in src/demangle/schemes/itanium/parser.py
3897 3898 3899 3900 3901 3902 3903 3904 3905 3906 3907 3908 3909 3910 | |
spelled_template_arguments
¶
spelled_template_arguments()
A template argument list rendered as text, for use inside a name.
Source code in src/demangle/schemes/itanium/parser.py
3912 3913 3914 3915 | |
base_unresolved_name
¶
base_unresolved_name(qualified=False)
qualified says an sr scope precedes it, which is what decides whether an
operator name is a plain operand -- see below.
Source code in src/demangle/schemes/itanium/parser.py
3929 3930 3931 3932 3933 3934 3935 3936 3937 3938 3939 3940 3941 3942 3943 3944 3945 3946 3947 3948 3949 3950 3951 3952 3953 3954 3955 3956 3957 | |
destructor_name
¶
destructor_name()
Source code in src/demangle/schemes/itanium/parser.py
3959 3960 3961 3962 3963 | |
expression_name
¶
expression_name()
A name appearing in an expression -- the callee of a call, most often.
Records nothing for the name itself. Section 5.1.10 excludes "function and operator names other than extern "C" functions" from the candidate set, and adds that "we do not substitute for expressions, though names appearing in them might be substituted": a name in an expression may refer to an existing entry, but it does not create one.
Template arguments applied to it still record their own types, which is how
_ZSt12construct_atIcJRKcEE...cl7declvalIT0_EE... reaches S4_ -- that entry is
the T0_ inside declval's argument list, not declval itself.
Source code in src/demangle/schemes/itanium/parser.py
3965 3966 3967 3968 3969 3970 3971 3972 3973 3974 3975 3976 3977 3978 3979 3980 3981 3982 3983 3984 3985 3986 3987 3988 3989 3990 3991 3992 | |
initialiser
¶
initialiser()
A parenthesised initialiser list.
<initializer> ::= pi <expression>* E
Source code in src/demangle/schemes/itanium/parser.py
3994 3995 3996 3997 3998 3999 4000 4001 4002 4003 4004 4005 4006 4007 4008 4009 4010 4011 4012 4013 4014 4015 4016 | |
expression
¶
expression()
A constant expression appearing in a type or template argument.
Returns a builder handle, so an expression is structure to a builder that wants structure and text to one that wants text. Only the shapes a compiler actually emits into a name are modelled: the full expression grammar is enormous and most of it cannot reach a mangled name, because it is not part of any signature.
Sets _precedence, which a containing operator reads immediately afterwards to
decide whether this operand needs brackets.
Source code in src/demangle/schemes/itanium/parser.py
4018 4019 4020 4021 4022 4023 4024 4025 4026 4027 4028 4029 4030 4031 4032 4033 4034 4035 4036 | |
constraint_expression
¶
constraint_expression()
Q <expression>, the C++20 requires-clause.
Every enclosing template's parameters are in scope inside it, and this parser
does not track them all -- so a <template-param> inside a clause is spelled by
its own mangled name rather than by whatever it is bound to, which is what the
reference does for the same reason.
Source code in src/demangle/schemes/itanium/parser.py
4038 4039 4040 4041 4042 4043 4044 4045 4046 4047 4048 4049 4050 4051 4052 4053 4054 4055 4056 4057 | |
expression_text
¶
expression_text()
An expression where the grammar around it needs characters, not a shape.
An array bound and a throw(...) specification are spelled into a type by
productions that take text, so those ask for text. Everything reachable from
_expression keeps the handle.
Source code in src/demangle/schemes/itanium/parser.py
4059 4060 4061 4062 4063 4064 4065 4066 | |
requires_expression
¶
requires_expression()
A C++20 requires-expression.
<expression> ::= rq <requirement>+ E
| rQ <bare-function-type> _ <requirement>+ E
<requirement> ::= X <expression> [N] [R <type-constraint>]
| T <type>
| Q <constraint-expression>
rQ is the form with a parameter list, requires (T) { ... }. Each requirement
is written with a leading space and a closing semicolon, which is how the
reference lays the braces out.
Source code in src/demangle/schemes/itanium/parser.py
4111 4112 4113 4114 4115 4116 4117 4118 4119 4120 4121 4122 4123 4124 4125 4126 4127 4128 4129 4130 4131 4132 4133 4134 4135 4136 4137 4138 4139 4140 4141 4142 4143 4144 4145 4146 4147 4148 4149 4150 4151 4152 4153 4154 4155 4156 4157 4158 4159 4160 4161 4162 4163 4164 4165 | |
detect
¶
detect(name)
Cheap test for "is this plausibly an Itanium mangled name".
Runs on every symbol a caller passes, including the overwhelming majority that are not mangled at all, so it does no work beyond a prefix comparison.
_GLOBAL__ is deliberately not one of the prefixes: parse does not read those
names -- GNU's "global constructors keyed to ..." extension -- and claiming them
here would only hand them back unchanged one step later.
Source code in src/demangle/schemes/itanium/parser.py
317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 | |
parse
¶
parse(
mangled,
builder,
limits=DEFAULT_LIMITS,
options=DEFAULT_OPTIONS,
)
Parse an Itanium mangled name into builder, returning its handle.
Read twice when the first reading took an sr the modern way and then failed:
the second reads every ambiguous sr as the old sr <type> <name> that g++ 13
still writes -- see _qualifier_levels. That is libiberty's unresolved_name_state
exactly, a whole-name retry, so a name that reads either way reads as the
reference reads it. A name that reads neither way reports the first reading's
error, which is the one the modern grammar gives.
Read twice, too, when a back-reference ran past the substitution table and the name
carries an undeduced auto: the table was numbered by the other of the two rules
compilers apply to that type -- see ItaniumOptions.undeduced_auto_substitution --
and the second reading applies it. Only where the option left the rule to the name's
form; a caller who chose one is not second-guessed.
And read twice when a back-reference named a closure prefix, or a template with no
arguments, as a type, in a name that has a closure prefix: GCC 12 and earlier left
the prefix out of the table, so every reference after it is one entry early -- see
ItaniumOptions.closure_prefix_substitution -- and the second reading leaves it out
too. The same rule about a caller who chose applies.
Source code in src/demangle/schemes/itanium/parser.py
4934 4935 4936 4937 4938 4939 4940 4941 4942 4943 4944 4945 4946 4947 4948 4949 4950 4951 4952 4953 4954 4955 4956 4957 4958 4959 4960 4961 4962 4963 4964 4965 4966 4967 4968 4969 4970 4971 4972 4973 4974 4975 4976 4977 4978 4979 4980 4981 4982 4983 4984 4985 4986 4987 4988 4989 4990 4991 4992 4993 4994 4995 4996 4997 4998 4999 5000 5001 | |
parse_type
¶
parse_type(
mangled,
builder,
limits=DEFAULT_LIMITS,
options=DEFAULT_OPTIONS,
)
Parse a bare <type> -- Pi, PKFvRiE -- rather than a whole symbol.
What __cxa_demangle reads and llvm-cxxfilt refuses, and what a typeinfo name
or an RTTI descriptor carries. It is a separate entry point rather than a fallback
inside parse for the reason llvm-cxxfilt refuses it there: a demangler offered
every symbol in a binary and willing to read i as int will rename half a C
library. Asked for deliberately, it is exactly what the caller wants.
Source code in src/demangle/schemes/itanium/parser.py
5004 5005 5006 5007 5008 5009 5010 5011 5012 5013 5014 5015 5016 5017 5018 5019 5020 5021 5022 | |
Options¶
demangle.schemes.itanium.options
¶
Caller-selectable policy for Itanium output.
Some questions a demangler faces have no single right answer -- the two reference implementations answer them differently, and both are correct. Those belong here, as explicit options with a documented default, rather than being decided silently in the parser.
Frozen, so an options object can be shared between threads and used as a cache key.
DEFAULT_OPTIONS
module-attribute
¶
DEFAULT_OPTIONS = ItaniumOptions()
The llvm style's options: every field at its default.
GNU_OPTIONS
module-attribute
¶
GNU_OPTIONS = ItaniumOptions(
expand_std_abbreviations=True,
gnu_nullptr_spelling=True,
gnu_closure_spelling=True,
local_name_return_type=False,
gnu_expression_spelling=True,
symbolic_constraint_parameters=False,
gnu_complex_spelling=True,
gnu_angle_spacing=True,
gnu_vector_spelling=True,
gnu_special_name_spelling=True,
gnu_entity_operand_spelling=True,
gnu_default_argument_scope=True,
gnu_friend_spelling=True,
gnu_unresolved_scope_substitution=True,
gnu_empty_pack_spelling=True,
gnu_objc_protocol_spelling=True,
gnu_exception_spec_first=True,
)
The gnu style's options: every field on which GNU c++filt differs, set its way.
ItaniumOptions
dataclass
¶
ItaniumOptions(
expand_std_abbreviations=False,
gnu_closure_spelling=False,
gnu_expression_spelling=False,
symbolic_constraint_parameters=True,
undeduced_auto_substitution=None,
closure_prefix_substitution=None,
inherited_constructor_substitution=None,
gnu_nullptr_spelling=False,
gnu_angle_spacing=False,
gnu_complex_spelling=False,
gnu_vector_spelling=False,
gnu_special_name_spelling=False,
gnu_entity_operand_spelling=False,
gnu_default_argument_scope=False,
gnu_friend_spelling=False,
gnu_unresolved_scope_substitution=False,
gnu_empty_pack_spelling=False,
gnu_exception_spec_first=False,
gnu_objc_protocol_spelling=False,
local_name_return_type=True,
)
Output policy for the Itanium demangler.
expand_std_abbreviations
class-attribute
instance-attribute
¶
expand_std_abbreviations = False
Spell Ss as std::basic_string<char, std::char_traits<char>, ...>.
False (the default) gives llvm-cxxfilt's short form, std::string. True gives GNU
c++filt's expansion. The types are identical; only the spelling differs.
gnu_closure_spelling
class-attribute
instance-attribute
¶
gnu_closure_spelling = False
Spell lambdas and unnamed types the way GNU c++filt does.
False gives llvm-cxxfilt's 'lambda'(int) and 'unnamed'. True gives GNU's
{lambda(int)#1} and {unnamed type#1}, which number from one rather than
suffixing the raw index.
It also numbers a generic lambda's invented parameters. [](auto a, auto b) is
mangled as references to parameters the closure never declared (ABI 5.1.8):
llvm-cxxfilt spells every one of them auto, and GNU spells them auto:1 and
auto:2 by index -- so Ul T0_ T_ E is {lambda(auto:2, auto:1)#1}. The number is
the only thing that tells two of them apart.
It also names a constructor or destructor of a closure or unnamed type the way
libiberty does, after the last source name it read: A::{unnamed type#1}::~A(),
where llvm-cxxfilt writes A::'unnamed'::~() because the type has no name to
repeat. ICU ships the shape in MicroProps::{unnamed type#1}::~MicroProps().
gnu_expression_spelling
class-attribute
instance-attribute
¶
gnu_expression_spelling = False
Spell expressions inside types the way GNU c++filt does.
The two references diverge throughout expression syntax, and neither is more correct
-- they are printing the same expression. GNU writes decltype ({parm#1}+{parm#1})
where LLVM writes decltype(fp + fp): a space after the keyword, no spaces around
binary operators, function parameters numbered from one, and a call's callee
parenthesised. A floating-point literal is the hex the name carries, in brackets
after the type -- (double)[4048f5c28f5c28f6] -- where LLVM decodes it and prints
0x1.8f5c28f5c28f6p+5.
symbolic_constraint_parameters
class-attribute
instance-attribute
¶
symbolic_constraint_parameters = True
Spell a template parameter inside a requires-clause by name rather than by value.
llvm-cxxfilt prints T where GNU c++filt substitutes the bound argument. The clause
itself is never printed, but it contributes entries to the substitution table that
the signature refers back to, so the choice is visible in the output.
A clause names parameters of enclosing templates, and not all of them are in scope -- which is the reason llvm-cxxfilt spells them symbolically in the first place. Where there is nothing to substitute, this falls back to that spelling rather than refusing the name: an option chooses how a name is spelled and must not change which names read at all.
undeduced_auto_substitution
class-attribute
instance-attribute
¶
undeduced_auto_substitution = None
Count an undeduced auto -- Da, and decltype(auto), Dc -- as a substitution
candidate, the way Apple's clang does.
Not a spelling but a numbering, and the one place two compilers number the same
name differently. The ABI leaves builtin types out of the substitution table, and
GCC and upstream clang leave auto out with them; Clang through 6.0 counted an
undeduced auto by accident, -fclang-abi-compat=6 still does, and Apple's clang
has kept that rule in every version since, so every S<n>_ after a deduced return
type in a Mach-O symbol is one higher than GCC or upstream clang would write. Read
by the wrong rule, such a name resolves its back-references to the wrong entries:
of the 17,310 names carrying a Da in Homebrew's bottles of Boost, folly, protobuf,
ceres and the rest, 6,385 come back as a plausible-looking wrong declaration and
2,300 refuse, under llvm-cxxfilt and c++filt alike.
None, the default, decides by the symbol's form: a name with the extra leading
underscore a Mach-O symbol table carries, __Z..., is read by Apple's rule, and a
bare _Z... by everyone else's -- and either way a name whose back-references run
past the table under one rule is read again under the other, since that is the one
rule known to move them. True or False forces a rule and skips the retry. Nothing in
the name itself says which compiler wrote it, so a Mach-O name from upstream clang
with a back-reference in range under both rules is read by Apple's; set False for
those.
closure_prefix_substitution
class-attribute
instance-attribute
¶
closure_prefix_substitution = None
Count a closure prefix -- the ns::g1 of ns::g1::'lambda'(...), a lambda in a
variable's or a member's initialiser, written 2ns2g1M... -- as a substitution
candidate, the way the ABI says and upstream clang and GCC 13 do.
The other numbering, and the other place two compilers number the same name
differently. GCC through 12 wrote the M but never entered the prefix before it in
the table (-fabi-version=17 and below; 18, GCC 13's default, fixed it), and
Apple's clang never has: Homebrew's macOS bottles of Apache Arrow, DuckDB and
RocksDB carry 663 names that read only with the prefix left out and none that read
only with it in, where upstream clang 13 through 18 -- targeting Darwin included --
enter it. So every S<n>_ after the lambda's opening in such a name is one lower
than the ABI says. Read by the ABI's rule, the back-references resolve one entry
early, and what comes out is a lie every demangler tells:
_ZNK2ns2g3MUlNS_3BoxIiEES1_E_clES1_S1_ is ns::g3::'lambda'(ns::Box<int>,
ns::Box<int>)::operator()(ns::Box<int>, ns::Box<int>) const -- and llvm-cxxfilt 18,
LLVM's main branch and c++filt 2.42 all print ns::Box bare, a template with no
arguments, as the second parameter; RocksDB's [](const Endpoint&, const Endpoint&)
comes back (rocksdb::Endpoint const&, rocksdb::Endpoint const). GCC 13 still
emits the old spelling as an alias beside the new, so a binary built today can
carry both.
None, the default, decides by the symbol's form, as undeduced_auto_substitution
does: a name with the extra leading underscore a Mach-O symbol table carries,
__Z..., is read by Apple's rule, and a bare _Z... by the ABI's -- and either way
a name whose back-references then run past the table, or name something no type
can be (the closure prefix itself, or a template with no arguments after it), is
read again under the other rule. That catches every misnumbered name whose shifted
references land on one of those; a name that reads either way is one the retry
never touches. True or False forces a rule and skips the retry: False for an ELF
binary known to be GCC 12's or older, True for a Mach-O one known to be upstream
clang's, since either's shifted references may land on a type and read as a
different type without tripping anything.
inherited_constructor_substitution
class-attribute
instance-attribute
¶
inherited_constructor_substitution = None
Count the <base class type> an inheriting constructor names -- the 1C of
CI2 1C -- as a substitution candidate.
The third place two compilers number the same name differently, and the one whose two sides each refuse the other's output. For
struct C { struct Kind { int v; }; C(Kind, Kind); };
struct D : C { using C::C; };
g++ 13.3 writes _ZN1DCI21CENS0_4KindES1_ -- S0_ is the base type's own entry --
and clang++ 18.1.3 writes _ZN1DCI21CEN1C4KindES1_, spelling C again because it
has no entry for it. Each is unreadable under the other's rule: llvm-cxxfilt 18
refuses g++'s name outright, and g++'s rule reads clang's S1_ as C where the
declaration says C::Kind.
None, the default, is clang's rule with a retry under g++'s, which is the way round
that catches the most: a g++ name refers to the base's entry as the highest index in
use at that point, so under clang's rule that reference runs past the table and the
retry fixes it, while a clang name gives no such signal at all and has to be right
the first time. True or False forces a rule and skips the retry. What neither can
catch is a name whose shifted references all land on some other entry -- S1_ for a
parameter's own type rather than for the base -- which reads as a different type
without tripping anything, and is the same residual ambiguity
closure_prefix_substitution has.
gnu_nullptr_spelling
class-attribute
instance-attribute
¶
gnu_nullptr_spelling = False
Spell Dn as decltype(nullptr) rather than std::nullptr_t.
False gives llvm-cxxfilt's spelling. True gives GNU c++filt's, which writes the expression the type is defined as rather than the library typedef for it. Both name the same type.
gnu_angle_spacing
class-attribute
instance-attribute
¶
gnu_angle_spacing = False
Separate two consecutive closing angle brackets with a space.
C++03 needed it -- A<B<int> >, because >> was the shift operator -- and GNU
c++filt still writes it where llvm-cxxfilt does not. It belongs here as well as on
the style's spelling builder because a template argument list spelled into text,
inside an unresolved name or a vendor qualifier, never reaches the builder.
gnu_complex_spelling
class-attribute
instance-attribute
¶
gnu_complex_spelling = False
Spell C99's _Complex and _Imaginary the way GNU c++filt does.
Both references write the qualifier after the type it qualifies -- double complex*
is a pointer to a complex double, not a complex pointer -- and differ only in the
word. False (the default) gives llvm-cxxfilt's double complex and
double imaginary; True gives GNU c++filt's double _Complex and
double _Imaginary, which are the keywords C99 actually spells.
gnu_vector_spelling
class-attribute
instance-attribute
¶
gnu_vector_spelling = False
Spell a Dv vector type the way GNU c++filt does.
False (the default) gives llvm-cxxfilt's int vector[4]; True gives GNU c++filt's
int __vector(4), which is how GCC's own __attribute__((vector_size)) diagnostics
write it. The distinction matters more than most: SIMD code mangles Dv constantly,
so this is not an obscure corner of the grammar.
gnu_special_name_spelling
class-attribute
instance-attribute
¶
gnu_special_name_spelling = False
Spell the thread-local special names the way GNU c++filt does.
False gives llvm-cxxfilt's thread-local initialization routine for x and
thread-local wrapper routine for x; True gives GNU's TLS init function for x and
TLS wrapper function for x. They name the same entity. Only TH and TW differ:
every other special name is spelled identically by both references. It also numbers
a reference temporary by its seq-id, reference temporary #0 for f()::x, where
llvm-cxxfilt writes reference temporary for f()::x.
gnu_entity_operand_spelling
class-attribute
instance-attribute
¶
gnu_entity_operand_spelling = False
Spell an embedded <mangled-name> under a unary operator the way GNU does.
X ad L _Z... E E -- the address of a function, passed as a template argument -- is
where nearly all of these appear. llvm-cxxfilt prints the whole declaration after
the &: &llvm::sandboxir::SwitchInst::setCondition(llvm::sandboxir::Value*). GNU
c++filt prints &llvm::sandboxir::SwitchInst::setCondition, which is what the
source wrote, and falls back to bracketing the declaration -- &(A::f() const) --
for every shape that is not a bare qualified function name.
The rule, read off c++filt rather than guessed: the name alone for & of a
function whose name is qualified and which carries no return type, no cv- or
ref-qualifier and no requires-clause; brackets around anything else that is not a
plain data name. So &A::f and &std::f and &A::~A, but &(f()) for an
unqualified one, &(void A::f<int>()) for a template, &(f()::x) for a local
entity and &(vtable for A) for a special name.
A call is the other place one appears, decltype(h(t)) with h resolved --
clL_Z1hiEfp_E. libiberty prints the callee through its name alone, "function
call used in an expression should not have printed types of the function
arguments", so c++filt writes h({parm#1}) and A::s({parm#1}) where llvm-cxxfilt
writes h(int)(fp). The name is an operand and bracketed unless it is a plain
one: (h<int>)({parm#1}), (A::s const)({parm#1}), (h()::x)({parm#1}).
gnu_default_argument_scope
class-attribute
instance-attribute
¶
gnu_default_argument_scope = False
Name the default argument a local entity was declared in.
Z <encoding> Ed [<number>] _ <entity> says the entity -- usually a lambda -- was
written in a default argument rather than in the function body. GNU c++filt spells
that scope, f(int, double)::{default arg#1}::x; llvm-cxxfilt drops it and prints
f(int, double)::x, which is the same name for two different entities if a function
has one lambda in its body and another in a default argument.
gnu_friend_spelling
class-attribute
instance-attribute
¶
gnu_friend_spelling = False
Mark a friend declared inside its class the way GNU c++filt does.
<unqualified-name> ::= F <name> says the function was declared inside the class it
is a friend of. False (the default) gives llvm-cxxfilt's A::friend f(), the word
before the name; True gives GNU c++filt's A::f[friend](), a bracketed suffix after
the name and its ABI tags but before its template arguments -- A::f[friend]<int>.
gnu_unresolved_scope_substitution
class-attribute
instance-attribute
¶
gnu_unresolved_scope_substitution = False
Record the scope of an srN unresolved name as a substitution, the way g++ does.
srN T_ 3foo E 1v is T::foo::v. The ABI says the qualifier levels of an
unresolved name are not substitution candidates, Clang writes names that way, and
llvm-cxxfilt reads them that way: T is recorded, T::foo is not. g++ mangles the
scope as a nested-name type and records it, so the parameter after
decltype(T::foo::v + 1) in _Z2e1I1QEDTplsrNT_3fooE1vLi1EES1_S2_ is S2_,
Q::foo, where Clang writes it out again as NS1_3fooE. GNU c++filt reads every
srN as a type and so numbers as g++ does.
Neither reader can be right for both compilers: a back-reference written after the
scope counts one entry more under g++ than under Clang. False (the default) reads
as the ABI and llvm-cxxfilt do, and takes S2_ in the g++ name above to be the
decltype itself. True reads as c++filt does.
gnu_empty_pack_spelling
class-attribute
instance-attribute
¶
gnu_empty_pack_spelling = False
Print an expansion over an empty pack as an empty entry, the way GNU c++filt does.
std::thread::thread<F, Args...> instantiated with no arguments is
_ZNSt6threadC1IZ4mainEUlvE_JEvEEOT_DpOT0_, and every program that starts a thread
on a no-argument callable carries it. llvm-cxxfilt drops the empty pack from the
list, thread<main::'lambda'(), void>; c++filt prints what it expands to, which
is nothing, and keeps the comma: thread<main::{lambda()#1}, , void>. It does the
same in a parameter list, f(, int), and in a call's arguments, g(, int) --
every comma-separated list -- and drops the empty entries at the end of a list,
so f<int, JE> is f<int> there as here.
False (the default) drops every one, as llvm-cxxfilt does. True keeps c++filt's.
gnu_exception_spec_first
class-attribute
instance-attribute
¶
gnu_exception_spec_first = False
Write a function type's exception specification before its qualifiers.
M1AKDoFvvRE is void (A::*)() const & noexcept to llvm-cxxfilt and
void (A::*)() noexcept const & to GNU c++filt, which prints the specification --
noexcept, noexcept(...), throw(...), transaction_safe -- first. Neither
order is the one C++ declares them in for a member function. False (the default)
gives llvm-cxxfilt's, True c++filt's.
gnu_objc_protocol_spelling
class-attribute
instance-attribute
¶
gnu_objc_protocol_spelling = False
Spell an Objective-C protocol qualifier as the vendor qualifier it is written as.
PU13objcproto3Bar11objc_object is id<Bar> to llvm-cxxfilt, and Foo<Bar>* for
a class; GNU c++filt knows nothing of the convention and prints the qualifier as it
prints any vendor qualifier, after the type: objc_object objcproto3Bar* and
Foo objcproto3Bar*. False (the default) gives the former, True the latter.
local_name_return_type
class-attribute
instance-attribute
¶
local_name_return_type = True
Show the return type of the function enclosing a local name.
For Z <function encoding> E <entity>, llvm-cxxfilt prints the enclosing template
function's return type and GNU c++filt does not. True (the default) follows LLVM.
Substitutions¶
demangle.schemes.itanium.substitutions
¶
Back-reference tables for the Itanium ABI.
The scheme compresses a name by letting any component that has already appeared be
written back as S_, S0_, S1_. Nothing in the name says what those indices refer
to: the numbering is implicit, determined entirely by which components the encoder
considered substitutable and the order it met them. Get the candidate set wrong by one
entry and every later back-reference in that name resolves to the wrong component --
usually producing output that still looks like a plausible C++ name, which is the worst
kind of wrong.
So this is not a cache. It is a load-bearing part of the grammar, and it lives in its own module, with the specification quoted at each decision, so it can be tested directly rather than only through whole-name round trips.
Reference: Itanium C++ ABI section 5.1.10, "Compression".
SubstitutionOverrun
¶
SubstitutionOverrun(
mangled, position=None, message="malformed mangled name"
)
Bases: ParseError
A S<n>_ past the end of the table: the name was numbered by a rule this reading
did not apply, or it is not a name. parse retries the one rule that is known to
differ between compilers before giving up; see
ItaniumOptions.undeduced_auto_substitution.
Source code in src/demangle/core/errors.py
44 45 46 47 48 | |
SubstitutionMisuse
¶
SubstitutionMisuse(
mangled, position=None, message="malformed mangled name"
)
Bases: ParseError
A S<n>_ naming an entry that cannot stand where it was read: a closure prefix,
or a template name with no arguments after it, used as a type. Neither is a type --
the one is a variable's name and the other a template's -- so the name was numbered
by a rule this reading did not apply, or it is not a name. parse retries the one
rule known to move a closure prefix's neighbours before giving up; see
ItaniumOptions.closure_prefix_substitution.
Source code in src/demangle/core/errors.py
44 45 46 47 48 | |
ParameterReference
¶
ParameterReference(index, level=0, symbolic=None)
A recorded <template-param>, kept as the reference it is rather than resolved.
A <template-param> is a substitution candidate in its own right (5.1.10), and the
entry it contributes is the parameter, not the argument bound to it at the moment
it was written. The two differ whenever the back-reference is read under a different
template scope, which happens in any name that mentions a local entity: the
signature of the enclosing function is written against that function's parameters,
so a T_ inside it enters the table, and a later S<n>_ naming that entry belongs
to the outer template.
Settled against the mangler rather than a demangler. For
template <class T> void legalize(Update<T>*);
template <class I, class C> void insort(I, I, C);
with insort<Update<BB*>*, Wrap<lambda-in-legalize>> instantiated from inside
legalize<BB*>, g++ 13.3 emits ...EEvS8_S8_T0_, where S8_ is the entry the T_
inside legalize's signature contributed. The parameters of insort are of type
I -- nn::Update<nn::BB*>* -- and GNU c++filt prints exactly that. Resolving the
entry to what T_ meant where it was recorded gives nn::BB* instead: a different
type, spelled plausibly, which is the failure this package exists to avoid.
llvm-cxxfilt 18 has that bug, and differs from this on 322 of the 217,730 distinct
Itanium symbols in the shared libraries of a stock Ubuntu 24.04.
The level is carried as well as the index, because TL<k>_<n>_ names a parameter of
an enclosing template and the entry stands for that parameter, not for level 0's.
Held opaque by the table -- it is the parser that owns the template scope, so it is the parser that turns one of these back into a handle.
Source code in src/demangle/schemes/itanium/substitutions.py
90 91 92 93 94 95 | |
DeferredProduction
¶
DeferredProduction(start, end)
A recorded component built over a <template-param>, kept as its input span.
ParameterReference covers the bare parameter. This covers everything wrapped
around one -- R T_, P N S1_ I T_ E E, a template applied to it -- which is
scope-dependent for exactly the same reason and which the mangler reuses across
scopes for exactly the same reason: it canonicalises a template type parameter by
level and index, so the composite over one canonicalises the same way.
From insort(Update<I>*, Update<I>*, C) instantiated inside legalize<T>, g++ 13.3
emits parameters written SA_, the entry P N S1_ I T_ E E contributed inside
legalize's own signature. Under insort that is nn::Update<nn::Update<nn::BB*>*>*,
which is what the declaration says and what GNU c++filt prints; frozen where it was
recorded it is nn::Update<nn::BB*>*, which is a different type.
There is nothing to freeze or to defer in the handle itself, because a handle is
already-built output. What is kept instead is where the production was written, so
the parser can read it again under the scope now in force. The span is a complete
<type>, and a <type> can only back-reference entries recorded before it, so
re-reading one terminates: each step moves strictly earlier in the table.
Source code in src/demangle/schemes/itanium/substitutions.py
126 127 128 | |
SubstitutionTable
¶
SubstitutionTable(mangled, limit=8192)
The S_ dictionary for one mangled name.
One instance per parse. Most entries hold builder handles, so what is stored depends
on which builder is driving -- the table itself is deliberately ignorant of that. Two
kinds of entry are not handles at all: ParameterReference and
DeferredProduction, which stand for a component whose spelling depends on the
template scope it is read under rather than the one it was written under. The table
holds them opaquely; it is the parser that owns the scope, so it is the parser that
turns one back into a handle.
On not deduplicating¶
The specification says "No entity is added to the dictionary twice", which reads like an instruction to deduplicate. For a demangler it is the opposite.
The encoder only spells a component out in full when it has not seen it before; a repeat is written as a back-reference. So a well-formed name never spells the same component twice, and every spelled-out component we meet is one the encoder had just added. Appending unconditionally reproduces its numbering exactly.
Deduplicating on our side would actively break names the specification calls out: "The type of a non-static member function is considered to be different, for the purposes of substitution, from the type of a namespace-scope or static member function whose type appears similar." Those two spell identically and both get entries. Keyed on spelling, a deduplicating table would collapse them and shift every subsequent index.
Source code in src/demangle/schemes/itanium/substitutions.py
165 166 167 168 169 170 171 | |
remember
¶
remember(handle, production='type')
Record a substitutable component and return it unchanged.
production names the grammar non-terminal being recorded. It is checked
against the specification's candidate set, so a parser that records something
the ABI does not fails loudly here instead of silently renumbering the table.
Source code in src/demangle/schemes/itanium/substitutions.py
173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 | |
defer_last
¶
defer_last(start, end)
Replace the entry just recorded with the span it was written at.
Called by the production itself, once it knows a template parameter was resolved while it ran. Nothing is added or removed, so numbering is untouched -- only what the entry is changes.
Source code in src/demangle/schemes/itanium/substitutions.py
192 193 194 195 196 197 198 199 200 | |
drop_last
¶
drop_last(mark)
Forget the entry a production just recorded, if it recorded one.
mark is mark() taken before the production ran, so nothing is dropped where
nothing was added. The one caller is an inheriting constructor's base class type
under the numbering clang uses, which reads that type without entering it while
still entering the components inside it. See
ItaniumOptions.inherited_constructor_substitution.
Source code in src/demangle/schemes/itanium/substitutions.py
202 203 204 205 206 207 208 209 210 211 212 | |
mark
¶
mark()
How many entries there are, for rewind.
Source code in src/demangle/schemes/itanium/substitutions.py
219 220 221 | |
capture
¶
capture(mark)
Everything recorded since mark, for restore_from.
Source code in src/demangle/schemes/itanium/substitutions.py
223 224 225 | |
restore_from
¶
restore_from(mark, entries)
Put back exactly what capture took, discarding whatever replaced it.
Used to read one span of a name twice. A conversion operator's type is written before the template arguments that bind its parameters, so it cannot be spelled until they have been read -- and the second reading must leave the table exactly as the first did, or every later back-reference in the name shifts.
Source code in src/demangle/schemes/itanium/substitutions.py
227 228 229 230 231 232 233 234 235 | |
lookup
¶
lookup(index)
Resolve S<index>_.
Source code in src/demangle/schemes/itanium/substitutions.py
237 238 239 240 241 242 243 | |
TemplateArgumentTable
¶
TemplateArgumentTable()
The T_ dictionary: a stack of the parameter lists currently in scope.
Separate from SubstitutionTable because it is a separate mechanism with separate
numbering -- T_ indexes the enclosing template's argument list, S_ indexes every
substitutable component seen so far. Conflating them is a classic source of wrong
output, so they do not share a type.
A stack rather than one list, because <template-param> names a level as well as
an index. T_ and T<n>_ mean level 0, the innermost enclosing <template-args>.
TL<k>_<n>_ means level k + 1, which is a list a generic lambda or a template
template parameter declared -- and those nest, so a name can reach past one to the
one outside it:
[]<typename $T, template<typename $T0, $T $N> typename $TT>(...)
Here $T is level 0 index 0 and $T0 is level 1 index 0, and the $N declaration
reaches both. Held flat, the two levels would overwrite each other and the parameter
would be spelled T, which names nothing.
A level with nothing in it is still a level: a generic lambda that declared no
parameters occupies one, because its auto parameters are numbered against it and a
TL reference from inside it counts through it.
Scopes nest in the other direction too -- a local entity has parameters of its own
while the enclosing ones stay out of reach -- so snapshot() and restore() save
and put back the whole stack.
Source code in src/demangle/schemes/itanium/substitutions.py
288 289 290 291 292 | |
install
¶
install()
Begin a fresh level 0. These arguments replace every level in scope.
Source code in src/demangle/schemes/itanium/substitutions.py
296 297 298 299 | |
add
¶
add(handle)
Bind the next argument of level 0, as an argument list is read.
Source code in src/demangle/schemes/itanium/substitutions.py
301 302 303 304 305 306 307 | |
outer
¶
outer()
Level 0's bindings, for a caller that has to look at all of them at once.
Source code in src/demangle/schemes/itanium/substitutions.py
309 310 311 | |
push
¶
push(declared)
Enter a nested level, which fills up as its declarations are read.
Source code in src/demangle/schemes/itanium/substitutions.py
315 316 317 318 | |
clear
¶
clear()
Drop every level. A lambda that is the entity being named starts fresh.
Source code in src/demangle/schemes/itanium/substitutions.py
327 328 329 330 | |
lookup
¶
lookup(index, level=0)
Resolve TL<level>_<index>_, or None when the binding is not in scope.
Returning None rather than raising is deliberate. A generic lambda's auto
parameter is mangled as a reference to a parameter it never declared (ABI
5.1.8), so a miss is a normal reading and the parser decides what it means.
Source code in src/demangle/schemes/itanium/substitutions.py
332 333 334 335 336 337 338 339 340 341 342 343 344 | |
Parser¶
demangle.schemes.itanium.parser
¶
A recursive-descent parser for the Itanium C++ ABI mangling scheme.
This is the scheme GCC and Clang emit, and by extension almost every C++ toolchain outside the Microsoft ecosystem. It is also used, in its older form, by Rust's legacy mangling.
The parser is written against core.builder.Builder: it recognises grammar productions
and reports them, never constructing output itself. See ARCHITECTURE.md for why.
Structure of this file mirrors the specification's own ordering -- names, then types, then template arguments, then expressions -- so a production can be found by its section number. Grammar comments quote the ABI verbatim.
Reference: Itanium C++ ABI section 5.1, https://itanium-cxx-abi.github.io/cxx-abi/abi.html A transcription of the productions is vendored at docs/specs/itanium-grammar.txt.
Microsoft Visual C++¶
demangle.schemes.msvc
¶
Microsoft's decorated name scheme.
The format UnDecorateSymbolName reverses, emitted by MSVC and by clang-cl. Microsoft
publishes no complete grammar for it, so this implementation follows llvm-undname
and is validated against LLVM's own demangler corpus; the reference output is recorded
next to every name in tests/conformance.
Structured output¶
This parser builds its own nodes rather than writing to a builder. It cannot: MSVC
spells a declaration differently enough that the shared spelling builder has nowhere to
put a calling convention, and the alternative to a scheme-specific renderer would be a
scheme-specific branch in core.
What it does instead is build its own tree, of nodes that are core.ast.Nodes, and
spell that (see nodes.py). So parse() hands back a real tree to walk and demangle()
hands back text, exactly as they do for the other schemes -- the seam is which of the two
this module produces, rather than which builder the parser wrote to.
PLUGIN
module-attribute
¶
PLUGIN = LanguagePlugin(
name="msvc",
node_kinds=(
"array",
"declaration",
"function",
"indirection",
"name",
"pointer_auth",
"raw",
),
detect=detect,
parse=parse,
parse_type=parse_type,
description="Microsoft Visual C++ decorated names (MSVC, clang-cl)",
options_type=MsvcOptions,
aliases=("microsoft", "ms", "vc"),
first_characters="?.",
priority=100,
)
The scheme as the registry holds it, registered when this package is imported.
detect
¶
detect(name)
A decorated name opens with ?; a type descriptor's name opens with ..
A symbol table is full of other names that open with a dot -- .text, .L1234,
.refptr.foo, .constprop.0, .ARM.exidx, .CRT$XCU -- so the . form is claimed
only where what follows it opens the way a whole type encoding does; see
_opens_a_type. Claiming is still not reading: the type has to parse to the last
character before anything is said about it.
Source code in src/demangle/schemes/msvc/__init__.py
98 99 100 101 102 103 104 105 106 107 | |
parse
¶
parse(
mangled,
builder,
limits=DEFAULT_LIMITS,
options=DEFAULT_OPTIONS,
)
Parse an MSVC decorated name into builder.
Source code in src/demangle/schemes/msvc/__init__.py
181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 | |
parse_type
¶
parse_type(
mangled,
builder,
limits=DEFAULT_LIMITS,
options=DEFAULT_OPTIONS,
)
Parse a bare type encoding -- PEAX, PEAVFoo@@ -- rather than a whole symbol.
What an RTTI type descriptor carries, and what UnDecorateSymbolName's
UNDNAME_TYPE_ONLY asks for. A leading . is accepted and dropped, because that is
how the descriptor's own symbol spells it.
None of this is reachable from parse, and deliberately: a type encoding opens with
no ?, so detect cannot see one coming and a demangler that guessed would read
plain C symbols as types. It is readable only because the caller named the scheme.
Source code in src/demangle/schemes/msvc/__init__.py
244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 | |
Options¶
demangle.schemes.msvc.options
¶
What to leave out of an MSVC spelling.
A decorated name expands to a great deal more than the name: public: virtual int
__cdecl C::f(int) const is one identifier and five pieces of declaration around it.
An analyst grepping a binary usually wants fewer of them, which is why
UnDecorateSymbolName takes a mask and llvm-undname takes flags.
Both references are represented here, and which one defines a field decides how far the field reaches -- because the two disagree about that, measurably, and neither is wrong.
The five llvm-undname flags are declaration-level. They apply to the declaration and
to the types written inside it, and stop at the edge of the symbol: a symbol naming a
scope keeps its full spelling, and a function reached as a pointer's pointee keeps the
convention the pointer prints for it. So --no-calling-convention over int (__cdecl *
__cdecl fn(void))(int) drops the convention fn itself carries and keeps the one
belonging to the function pointer it returns.
The four UnDecorateSymbolName flags are lexical. They reach every occurrence of what
they name, wherever it stands -- inside a template argument, inside a parameter of
function-pointer type, and inside the enclosing symbol a local name is scoped by.
ms_keywords over the same name drops both conventions.
True keeps the piece, which is what either reference prints by default.
tests/conformance/msvc-suppressions.txt scores the first five against llvm-undname,
over the 609 names of msvc-llvm-corpus.txt. tests/conformance/msvc-dbghelp.txt scores
the last four against dbghelp.dll, over the 478 of those names the two references
already spell alike unflagged -- the rest are left out rather than reconciled, because
the two spell a name differently and a corpus cannot ask for both houses at once.
DEFAULT_OPTIONS
module-attribute
¶
DEFAULT_OPTIONS = MsvcOptions()
What both shipped styles use: every piece kept, as both references print by default.
MsvcOptions
dataclass
¶
MsvcOptions(
calling_convention=True,
access_specifier=True,
member_type=True,
return_type=True,
variable_type=True,
ms_keywords=True,
leading_underscores=True,
this_type=True,
tag_kind=True,
)
Which parts of a decorated name's expansion to print.
calling_convention
class-attribute
instance-attribute
¶
calling_convention = True
__cdecl, __stdcall, __thiscall. llvm-undname --no-calling-convention.
access_specifier
class-attribute
instance-attribute
¶
access_specifier = True
public:, protected:, private:. llvm-undname --no-access-specifier.
member_type
class-attribute
instance-attribute
¶
member_type = True
static and virtual. llvm-undname --no-member-type.
return_type
class-attribute
instance-attribute
¶
return_type = True
The type before the name. llvm-undname --no-return-type.
Not the same as dropping a word: a return type wraps around the declarator in C, so suppressing it means spelling the declaration the way a constructor is spelled -- convention, name, parameters -- rather than cutting a prefix off the text.
variable_type
class-attribute
instance-attribute
¶
variable_type = True
The type of a data symbol. llvm-undname --no-variable-type.
Only a variable's: a function keeps its return type, and so do the vtable and RTTI names, which are not variables however much they look like one.
ms_keywords
class-attribute
instance-attribute
¶
ms_keywords = True
Every Microsoft keyword. UnDecorateSymbolName's UNDNAME_NO_MS_KEYWORDS.
The calling convention, __restrict and __unaligned -- and __ptr64, which this
library never prints because llvm-undname does not. Not the same cut as
calling_convention: that one is the declaration's own convention, this one is every
convention in the spelling, so a function-pointer parameter loses its own too.
__int64 stays. It names a type rather than qualifying one, and the reference keeps
it.
leading_underscores
class-attribute
instance-attribute
¶
leading_underscores = True
The underscores on those keywords. UNDNAME_NO_LEADING_UNDERSCORES.
__cdecl becomes cdecl and __restrict becomes restrict, over the same words
ms_keywords drops entirely. What comes out is not C++ -- it is what a linker map
from the 16-bit era spelled -- and it is here because the mask has it.
LLVM's __attribute__((__swiftcall__)) is left alone. The reference spells that
convention __swift_1 and strips it to swift_1; it has no answer for LLVM's
spelling, and stripping the underscores off __attribute__ would invent one.
this_type
class-attribute
instance-attribute
¶
this_type = True
What a member function writes after its parameter list. UNDNAME_NO_THISTYPE.
The whole group, not just the cv: const, volatile, __restrict, __unaligned
and the &/&& ref-qualifier all describe the implicit this, and the reference
drops them together.
tag_kind
class-attribute
instance-attribute
¶
tag_kind = True
class, struct, union, enum before a tag name. UNDNAME_NO_ECSU.
What C++ calls an elaborated type specifier. MSVC writes one on every user-defined
type it mangles, so a spelling full of them reads as struct S const * where the
source said S const *.
for_a_scope
¶
for_a_scope()
These options as they apply to a symbol that names a scope.
?1??f@@YAXXZ@ says "inside the second scope of void __cdecl f(void)", and the
two references disagree about whether a flag reaches that inner symbol.
llvm-undname applies its five to the symbol being named and not to the ones
saying where it lives; UnDecorateSymbolName applies its four to the text
throughout. Each flag follows the reference that defines it, so what a scope
keeps is the five and what it loses is the four.
Source code in src/demangle/schemes/msvc/options.py
105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 | |
keyword
¶
keyword(word)
word as this spelling wants it: as written, without underscores, or not at all.
The one place a Microsoft keyword becomes text, so that ms_keywords and
leading_underscores are applied in a single spot rather than at each of the
places the parser reads one.
Source code in src/demangle/schemes/msvc/options.py
124 125 126 127 128 129 130 131 132 133 134 135 136 137 | |
Nodes¶
demangle.schemes.msvc.nodes
¶
The MSVC type tree, and the declarator spelling that belongs to it.
core/spelling.py places a declarator the way a C declaration wants it, and for the
Itanium schemes that is the whole story. It is not the whole story here. MSVC writes a
calling convention inside the parentheses of a function pointer -- int (__cdecl
*)(void) -- doubles the space after a convention spelled as an attribute, carries
__unaligned along with const and volatile through every indirection, and still leaves
room for a convention that spells as nothing at all: int ( *)(). None of that can be
said through the shared builder without teaching core about one scheme, so the
placement rules live here, next to the nodes they walk.
The nodes are otherwise ordinary core.ast.Nodes, so .walk(), .find(kind) and
.spell() work as they do for any other scheme. Where a core node already means exactly
what this scheme means -- a fixed piece of text, an array, an identifier -- it is used
rather than copied. render below spells those the MSVC way as part of a declaration;
asking one of them for its own spelling in isolation goes through the shared builder,
which writes int [2] where this scheme writes int[2]. Nothing that is spelled as a
whole symbol takes that path.
Array extents nest outermost first, so int[2][3] is an array of two arrays of three
and each node's dimension means what it says on its own.
What the tree does not take apart is a name. Namespaces, template arguments and the class a member belongs to arrive here already spelled, because the mangling resolves its back-references against text and the parser has to render each one as it goes to number the next correctly. So the structure recovered is the declaration's shape -- what it declares, its indirections, its parameter list, its return type -- over leaves that are text.
Indirection
¶
Indirection(sigil, qualifiers, inner, pointer_auth=None)
Bases: _Spelled
A pointer, a reference, or a pointer into a class.
sigil is *, &, &&, or Owner::* for a pointer to member -- the owner is part
of the declarator in MSVC's spelling, not a type of its own. qualifiers are the
indirection's own, the ones written to its right in char *const; what it points at
carries its own.
Source code in src/demangle/schemes/msvc/nodes.py
105 106 107 108 109 | |
points_into_class
property
¶
points_into_class
Whether this is a pointer to member rather than an ordinary pointer.
FunctionType
¶
FunctionType(
convention, parameters, returns=None, member_cv=""
)
Bases: _Spelled
A function type: its calling convention, parameters, return type and member cv.
The declarator is deliberately absent. MSVC interleaves it with the convention --
int (__cdecl *p)(void) puts the pointer between them -- so the name a function
declares belongs to the Declaration around this node, and render threads it
through rather than storing it here.
returns is None for the forms the scheme writes no return type for: a constructor,
a destructor, an operator that leaves the slot empty. member_cv is what trails the
parameter list of a member function, already spelled with its leading space.
Source code in src/demangle/schemes/msvc/nodes.py
140 141 142 143 144 | |
Declaration
¶
Declaration(
prefix,
declarator,
type_,
suffix="",
access="",
member_type="",
)
Bases: _Spelled
A whole symbol: what it declares, and what MSVC writes around it.
What the spelling opens with is three pieces rather than one, because each can be
left out on its own: prefix is what comes before everything ("[thunk]: ", an
extern "C"), access is "public: " and its siblings, member_type is "static "
or "virtual ". suffix is the qualifiers that trail a member function's parameter
list. All four stay text because none is part of the type: they say how the symbol is
reached, not what it is. declarator is the name, which lands in the hole its type
leaves.
Source code in src/demangle/schemes/msvc/nodes.py
166 167 168 169 170 171 172 | |
declares_a_function
property
¶
declares_a_function
Whether this declares a function rather than data.
A variable of function-pointer type is data: int (__cdecl *p)(int) has a
FunctionType in it, but not as the thing being declared, which is why the
reference leaves its convention alone under --no-calling-convention.
PointerAuth
¶
PointerAuth(key, address_discriminated, discriminator)
Bases: _Spelled
An ARM64e pointer authentication qualifier: __ptrauth(key, address_discriminated, discriminator).
Source code in src/demangle/schemes/msvc/nodes.py
81 82 83 84 | |
render
¶
render(
node,
declarator="",
declarator_is_function=False,
options=DEFAULT_OPTIONS,
member_cv="",
as_pointee=False,
sigil=False,
)
Spell a type around a declarator, the way C nests one inside the other.
A pointer or array binds to the declarator built so far, and a name therefore ends up
inside its type: int (*j)[2], not int (*)[2] j.
options says which parts of a declaration to print. It reaches the declaration's
own function type and stops there: a parameter, a template argument and a variable's
pointee are all rendered with the defaults, which is what the reference does and why
--no-calling-convention leaves int (__cdecl *)(int) alone in a parameter list.
sigil says the declarator opens with an indirection's, which an array has to
bracket and cannot always see in the text: * and & it can, A::* it cannot,
since a qualified data name opens the same way. int (A::*)[4], not int A::*[4].
Source code in src/demangle/schemes/msvc/nodes.py
188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 | |
spelled_after
¶
spelled_after(convention, text)
Join a calling convention to what follows it, skipping the ones spelled with nothing.
Source code in src/demangle/schemes/msvc/nodes.py
310 311 312 313 | |
prefixed
¶
prefixed(text, node)
Put text in front of a whole symbol, without disturbing the shape underneath it.
The one caller is extern "C", and it lands in the declaration's member_type
rather than ahead of everything: the reference groups it with static and virtual,
so --no-member-type drops all three together.
After them and not before, which is where the reference writes it: ?fn@@$$J0EAAHH@Z
is private: virtual extern "C" int __cdecl fn(int), not extern "C" virtual.
Only a name carrying both shows the difference, and every $$J in the corpora is on
a free function or an ordinary member, where member_type is empty and the two
orders are the same string.
Source code in src/demangle/schemes/msvc/nodes.py
316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 | |
merge_qualifiers
¶
merge_qualifiers(left, right)
Source code in src/demangle/schemes/msvc/nodes.py
336 337 338 | |
ordered_qualifiers
¶
ordered_qualifiers(quals)
quals in _QUALIFIER_ORDER, deduplicated, in whatever spelling they arrived in.
Ranked by the word without its underscores, so a run that is dropping them orders the same qualifiers the same way. The spelling kept is the one that was passed in: this decides an order, not a wording.
Source code in src/demangle/schemes/msvc/nodes.py
349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 | |
apply_qualifiers
¶
apply_qualifiers(node, quals)
Qualify a named type, as a pointee qualifier or a $$C wrapper does.
Only ever reached with a named type: an indirection merges its qualifiers as it is built, and a back-reference declines rather than accept one.
In _QUALIFIER_ORDER and not in the order they arrived. Appending would make a
type qualified twice -- a pointee qualifier and then the variable's own, which
?s4@PR13182@@3PCDD is -- come out char volatile const * where the reference
writes char const volatile *. So any already at the end of the text are taken back
off and the whole set is written in one order.
Source code in src/demangle/schemes/msvc/nodes.py
366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 | |
is_member_function_pointer
¶
is_member_function_pointer(node)
Source code in src/demangle/schemes/msvc/nodes.py
388 389 | |
qualify_declared
¶
qualify_declared(node, quals)
Place a data symbol's trailing qualifier on what the symbol declares.
It belongs one level inside an outermost pointer rather than on the pointer - "?s@@3PADB" is "char const s" and "?s@@3PAPADB" is "char const s" - and an array passes it on to its element, the way C spells one: "?arr@@3QAY01HB" is "int const (const arr)[2]".
Source code in src/demangle/schemes/msvc/nodes.py
392 393 394 395 396 397 398 399 400 401 402 403 404 | |
Rust¶
demangle.schemes.rust
¶
Rust symbol mangling: the legacy _ZN scheme and the v0 _R scheme.
Rust has used two schemes. The legacy one wraps a hashed path in Itanium mangling, so a
legacy Rust symbol is also a valid Itanium symbol -- which is why this plugin is offered
names first. The v0 scheme, specified in RFC 2603 and stabilised behind
-Csymbol-mangling-version=v0, is a scheme of its own.
Derived from MIT-licensed code, with both grammars substantially reworked; see NOTICE.
Structured output¶
parse() returns a tree: a symbol holding a path of name components, with impl,
template, type and literal nodes for what a path carries. See nodes.py for why
the tree cannot spell a symbol differently from demangle().
DEFAULT_OPTIONS
module-attribute
¶
DEFAULT_OPTIONS = RustOptions()
What both shipped styles use: the hash left out, as rustfilt prints it.
PLUGIN
module-attribute
¶
PLUGIN = LanguagePlugin(
name="rust",
node_kinds=(
"decorated",
"impl",
"literal",
"name",
"namespace",
"path",
"symbol",
"template",
"type",
),
detect=detect,
parse=parse,
options_type=RustOptions,
description="Rust legacy (_ZN) and v0 (_R) symbol mangling",
aliases=("rs",),
symbol_table_decorations=False,
first_characters="_Z",
priority=50,
)
The scheme as the registry holds it, registered when this package is imported.
ManglingType
¶
Bases: Enum
Which of Rust's two manglings a name uses: legacy (_ZN...E) or v0 (_R...).
RustDemangler
¶
Routes a name to the scheme that mangled it.
Holds no state of its own, and hands out a fresh parser per name rather than
reusing one. That is not fastidiousness: both parsers keep the name they are
reading, its suffix and its path spans on self, so a single shared instance
lets two threads overwrite each other's parse -- and the failure is silent. It
does not raise; it returns another symbol's name, which is then memoised under
the first symbol's key.
A parser is two attribute stores to allocate, against a parse that is tens of microseconds, so per-name construction does not show up in the benchmark.
demangle
¶
demangle(
inpstr,
limit,
keep_hash=False,
max_depth=_DEFAULT_MAX_DEPTH,
)
Spell inpstr, in whichever of the two manglings it uses.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
inpstr
|
str
|
the mangled name. |
required |
limit
|
int
|
the most characters the printer may write. |
required |
keep_hash
|
bool
|
spell the disambiguating hash rather than dropping it. |
False
|
max_depth
|
int
|
the deepest v0 nesting to read, as |
_DEFAULT_MAX_DEPTH
|
Source code in src/demangle/schemes/rust/_dispatch.py
38 39 40 41 42 43 44 45 46 47 | |
structure
¶
structure(
inpstr,
limit,
keep_hash=False,
max_depth=_DEFAULT_MAX_DEPTH,
)
Demangle to a tree rather than to text.
Same parser, same pass; only what it emits into differs. The tree renders to
exactly what demangle returns for the same input -- keep_hash included, since
both are one stream of fragments.
Source code in src/demangle/schemes/rust/_dispatch.py
49 50 51 52 53 54 55 56 | |
determine_type
¶
determine_type(inpstr)
Say which of the two manglings inpstr uses, by its prefix alone.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
inpstr
|
str
|
the mangled name. |
required |
Returns:
| Type | Description |
|---|---|
ManglingType
|
|
Raises:
| Type | Description |
|---|---|
TypeNotFoundError
|
|
Note
A bare R is accepted here, like the bare ZN below it: some symbol tables
have had the leading underscore stripped before the name got here, and the
reference reads that form too. It stays out of detect, which is deliberately
narrower -- a bare R is too broad a claim to make about every symbol in a
binary -- so auto-detection still needs _R or __R.
Source code in src/demangle/schemes/rust/_dispatch.py
63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 | |
RustOptions
dataclass
¶
RustOptions(keep_hash=False)
How much of a Rust name to spell.
keep_hash
class-attribute
instance-attribute
¶
keep_hash = False
Spell the disambiguating hash rather than dropping it.
Off by default, which is what rustfilt and every corpus here is scored against.
detect
¶
detect(name)
Cheap test for a Rust mangled name.
v0 needs its prefix and the letter that opens a <path>, which is one of seven.
_R on its own is not enough: CodeWarrior writes __RTTI__40TObjOwnerDerivedFromIObj
<12CStringTable>, and claiming it would make detect name the wrong scheme for a
symbol in this package's own corpus. The parse would fail and demangle fall
through to the scheme that owns it, so the spelling would be right and the label
would not -- but detect is a public answer in its own right, and a caller
labelling a symbol table gets that answer and no second chance. Nothing is lost by
the narrower test: a v0 name that does not open a <path> is one the parser refuses
on its next step.
Legacy shares Itanium's _ZN prefix, so it additionally requires evidence that the
name is Rust's -- without that this plugin would claim every C++ symbol it was
offered.
Two kinds of evidence, either of which is enough:
- the hash component,
17hand sixteen hex digits, that rustc appends as the last element of the path. It is looked for by its marker rather than at a fixed offset from the end, because real symbols carry things after it: a.0for a promoted constant, a.llvm.<hash>from LLVM's internaliser. Anchoring to the end would miss every one of those, and the C++ demangler would then claim them and produce a plausible-looking but quite wrong spelling. - a
$...$escape, which legacy Rust uses for characters the Itanium alphabet has no room for._ZN8$RF$testEis&test; read as C++ it spells$RF$test, which is not a name anything has.
The leading underscore is optional -- some symbol tables have already had it
stripped, and rustc-demangle reads ZN4testE as test. Without the underscore
nothing else claims the name, so the evidence asked for there is the grammar itself:
ZN, a digit, and a path the legacy reader consumes whole. None of the 652,000
symbols in this box's libraries and binaries starts ZN at all.
Deliberately narrower than rustc-demangle, which accepts any _ZN name and treats
the hash as optional. It can afford to: it is only ever handed names a caller has
already decided are Rust's. This plugin is offered every symbol in a binary, and the
same rule here would claim _ZN1a4hbadE -- an ordinary C++ a::hbad -- and print
a, having read hbad as a hash and dropped it. The cost of being narrow is that a
Rust symbol carrying neither mark reads as C++; the cost of being wide is that C++
symbols read as Rust. The first loses information, the second invents it.
Source code in src/demangle/schemes/rust/__init__.py
46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 | |
parse
¶
parse(
mangled, builder, limits=DEFAULT_LIMITS, options=None
)
Parse a Rust mangled name into builder.
Source code in src/demangle/schemes/rust/__init__.py
147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 | |
Options¶
demangle.schemes.rust.options
¶
Whether to spell the hash rustc writes into a symbol to keep it unique.
Both Rust manglings carry one and neither spells it by default, because it is noise to a
reader: core::fmt::write says what the function is and
core::fmt::write::h05af221e174051e9 says the same thing with a machine's bookkeeping
after it. But it is the only thing telling two monomorphisations of one generic
function apart, so a caller symbolising a profile or diffing two builds needs it.
This is rustc-demangle's own choice, spelled as a formatting flag rather than an option:
{} on its Demangle prints the hash and {:#} suppresses it. rustfilt prints the
suppressed form, and so does this by default. What the flag reaches differs between the
two manglings, which is why it is one option and not two:
- legacy appends the path's trailing
17h<16 hex>component --a::f::h05af221e174051e9; - v0 prints the crate's disambiguator wherever a crate root is spelled --
features[9f05e0465351d495]::apply_hrtb, at every occurrence rather than the first.
DEFAULT_OPTIONS
module-attribute
¶
DEFAULT_OPTIONS = RustOptions()
What both shipped styles use: the hash left out, as rustfilt prints it.
Nodes¶
demangle.schemes.rust.nodes
¶
The Rust symbol tree.
Rust's spelling has no declarator syntax at all. A C++ type wraps the name it declares
-- int (*)(char) -- which is why core/spelling.py carries a left/right pair and why
MSVC needs a renderer of its own. Rust writes &mut [u8; 4] strictly left to right, and
a path is just its components joined by ::. So the structure worth recovering here is
not a declarator shape: it is which part of the name is which -- the crate, the module
path, the generic arguments, the trait an impl is for, the closure disambiguator.
Every node holds parts: a tuple of plain strings and child nodes, in output order.
Rendering concatenates them. That is deliberate and is the whole correctness argument
for this module -- the printer in _v0.py emits one linear stream of fragments whether
it is building text or a tree, so a tree's rendering cannot drift from the text path.
It is not that the two are tested to agree; there is only one stream, and the tree is
that stream with the boundaries remembered.
Kinds are shared with the other schemes where they mean the same thing, so a tool can
ask any tree for its name or template nodes without knowing which language produced
it. impl and namespace are Rust's own: nothing in the C-family schemes means either.
RustName
¶
RustName(parts)
Bases: _Rust
A single identifier: a crate name, a module, a function, a field.
Named RustName rather than Name because core.ast.Name already exists and means
the same thing to a consumer -- hence the shared kind -- but spells itself through
the shared builder, which is not this scheme's speller.
Source code in src/demangle/schemes/rust/nodes.py
53 54 55 56 57 58 | |
Path
¶
Path(parts)
Bases: _Rust
A path: std::collections::HashMap, or a crate root on its own.
Source code in src/demangle/schemes/rust/nodes.py
53 54 55 56 57 58 | |
Namespace
¶
Namespace(parts, tag, disambiguator)
Bases: _Rust
A compiler-generated path component: {closure#0}, {shim:vtable#0}.
tag is the namespace letter from the mangling -- C for a closure, S for a
shim, or a vendor's own -- and disambiguator separates several in one scope.
Source code in src/demangle/schemes/rust/nodes.py
109 110 111 112 | |
Impl
¶
Impl(parts, self_type, trait)
Bases: _Rust
An inherent or trait impl: <Foo>, <Foo as Bar>.
trait is the trait's subtree for a trait impl and None for an inherent one, so a
caller can ask what a method implements without matching on rendered text.
Source code in src/demangle/schemes/rust/nodes.py
125 126 127 128 | |
Generics
¶
Generics(parts, base, arguments)
Bases: _Rust
A generic argument application, Vec<u8> or Foo::<T>.
base is what is being applied to and arguments are the arguments themselves,
both also present in parts -- the fields name them, parts orders them.
Source code in src/demangle/schemes/rust/nodes.py
141 142 143 144 | |
Type
¶
Type(parts, form)
Bases: _Rust
A type. form says which shape: reference, pointer, array, slice,
tuple, dyn, fn, basic, path.
One class rather than nine because nothing walks these structurally -- a caller
asking "is this a reference" wants a field to test, and kind stays type so the
common query finds every one of them.
Source code in src/demangle/schemes/rust/nodes.py
159 160 161 | |
Value
¶
Value(parts, form)
Bases: _Rust
A const generic argument: an integer, a bool, a char, a structural const.
Source code in src/demangle/schemes/rust/nodes.py
170 171 172 | |
Symbol
¶
Symbol(parts, hash=None, suffix='', vendor='')
Bases: _Rust
A whole Rust symbol.
hash is the value of the legacy scheme's trailing 17h<16 hex> component -- the
sixteen digits, without the h marker or the length prefix. The mangling carries it
and the spelling drops it; it is recorded because it is the only thing telling two
monomorphisations of one generic function apart. suffix is whatever followed
the mangled name -- an LLVM .llvm.<hash> from the internaliser, a .0 from a
promoted constant.
Source code in src/demangle/schemes/rust/nodes.py
189 190 191 192 193 | |
render
¶
render(part)
Text for a part, which is either a plain fragment or a subtree.
Source code in src/demangle/schemes/rust/nodes.py
37 38 39 | |
Swift¶
demangle.schemes.swift
¶
Swift mangled names.
A port of the Swift compiler's own demangler -- lib/Demangling/Demangler.cpp and
lib/Demangling/NodePrinter.cpp -- because nothing smaller is enough. Swift's mangling
is postfix and compresses aggressively against three tables that span a whole name, so a
symbol cannot be read a piece at a time, and how a piece is spelled depends on where it
sits in the tree. _demangler.py and _printer.py are those two files; nodes.py
records the structure of the printer's traversal.
Conformance is exact against a reference built from Swift's own demangler at a pinned
main commit (see tools/swift-demangle-reference/README.md):
- every
$ssymbol in the shipped Swift runtime and Foundation -- 48,368 of them -- spelled identically, with nothing refused; - every case in the compiler's own
test/Demangle/Inputs/manglings.txt-- the 531 rows oftests/conformance/swift-upstream.txt-- which is a much harder set: SIL function types, function-signature specialisations, key paths, autodiff thunks, macro expansions, and the Swift 3 mangling.
The Swift 3 mangling -- _T followed by anything but 0 -- is a different grammar with
its own demangler in the compiler, and _old_demangler.py is a port of that one. It
still matters: the ObjC runtime holds a Swift class's name in that form, so it turns up
in any Apple binary with interop in it. It builds the same tree, so the printer spells it
with no idea which mangling it came from, and the compiler's own Swift 3 test cases come
out exactly.
PLUGIN
module-attribute
¶
PLUGIN = LanguagePlugin(
name="swift",
node_kinds=(
"extension",
"function",
"generics",
"module",
"name",
"symbol",
"template",
"type",
"variable",
),
detect=detect,
parse=parse,
parse_type=parse_type,
description="Swift symbol mangling",
options_type=SwiftOptions,
aliases=(),
first_characters="$_@a",
priority=45,
)
The scheme as the registry holds it, registered when this package is imported.
SymbolicReference
¶
SymbolicReference(raw_kind, kind, directness, offset, at)
One symbolic reference, as the bytes described it.
at is where the offset field begins, because that is what the offset is relative
to: the address a resolver wants is address_of(at) + offset. Keeping the position
rather than the resolved address is what lets this module know nothing about images.
Source code in src/demangle/schemes/swift/symbolic.py
93 94 95 96 97 98 | |
Image
¶
Image(segments, imports=None, symbols=None)
A flat view of a loaded binary: virtual address in, bytes out.
Deliberately the smallest thing a resolver can be given. A caller with a memory dump,
a debugger connection or an object format this package does not read supplies its own
read, and everything else here works unchanged.
Source code in src/demangle/schemes/swift/resolve.py
70 71 72 73 74 75 76 77 78 | |
read
¶
read(address, length)
length bytes at virtual address, or None if they are not all mapped.
Source code in src/demangle/schemes/swift/resolve.py
80 81 82 83 84 85 86 87 88 | |
cstring
¶
cstring(address, limit=1024)
The NUL-terminated string at address, or None.
Source code in src/demangle/schemes/swift/resolve.py
90 91 92 93 94 95 96 97 98 99 | |
ContextResolver
¶
ContextResolver(image, pointer_size=8)
Resolves context references by walking the descriptor they point at.
The other kinds are not resolved and are not guessed at. An accessor-function reference names a function that has to be run to say what it yields, which is not something a file can be asked; the two existential-shape kinds describe a shape rather than name a context, and spelling one would mean inventing a name the mangling never had.
Source code in src/demangle/schemes/swift/resolve.py
388 389 390 | |
fragment
¶
fragment(descriptor)
The mangled fragment naming the context descriptor at descriptor.
The walk over the descriptors comes first, because it needs nothing but the layout and is what a stripped image still has. Where it declines -- a type declared in an extension, whose parent is an extension descriptor; an opaque type descriptor, which has no name of its own -- the symbol the image defines at that address is the fragment, when the image kept its symbols.
Source code in src/demangle/schemes/swift/resolve.py
424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 | |
detect
¶
detect(name)
One of the prefixes either mangling uses.
_T covers Swift 3 as well as _T0; the two are different grammars and
demangle_symbol dispatches between them, but from the outside they are one scheme.
Source code in src/demangle/schemes/swift/__init__.py
48 49 50 51 52 53 54 55 56 57 58 59 60 61 | |
parse
¶
parse(
mangled,
builder,
limits=DEFAULT_LIMITS,
options=DEFAULT_OPTIONS,
)
Parse a Swift mangled name into builder.
Source code in src/demangle/schemes/swift/__init__.py
72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 | |
parse_type
¶
parse_type(
mangled,
builder,
limits=DEFAULT_LIMITS,
options=DEFAULT_OPTIONS,
)
Parse a Swift type mangling -- Si, SaySiG -- rather than a whole symbol.
Demangler::demangleType rather than demangleSymbol, and what a metadata typeref
holds: a type mangling carries none of the $s a symbol opens with, so nothing about
it says it is Swift, and it is only readable because the caller said so.
A type mangling that reads as nothing is refused rather than spelled. The reference
hands back the whole input wrapped in a Suffix node when nothing at all parsed,
which prints as with unmangled suffix "..." -- true, but not a demangling of
anything, so it comes back here as the ParseError it is.
Source code in src/demangle/schemes/swift/__init__.py
91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 | |
demangle_symbol
¶
demangle_symbol(name, resolver=None, *, byte_mode=False)
Read name into a Global node, or return None if it is not readable.
Swift 3's mangling is a different grammar with its own demangler in the compiler, and
it is dispatched to here on the same test the reference uses: _Tt -- or, more
exactly, _T not followed by 0, since _T0 is Swift 4.
Source code in src/demangle/schemes/swift/_demangler.py
3168 3169 3170 3171 3172 3173 3174 3175 3176 3177 3178 3179 | |
demangle_type
¶
demangle_type(name, resolver=None, *, byte_mode=False)
Read name as a type rather than a whole symbol, for _TtGSa...-style names.
This is also the entry a metadata typeref takes: those are types, and they carry no
$s prefix for demangle_symbol to find.
Source code in src/demangle/schemes/swift/_demangler.py
3182 3183 3184 3185 3186 3187 3188 3189 3190 3191 | |
demangle_old_symbol
¶
demangle_old_symbol(name, *, byte_mode=False)
Read a Swift 3 mangled name into the same tree the current mangling produces.
Source code in src/demangle/schemes/swift/_old_demangler.py
1500 1501 1502 | |
print_root
¶
print_root(root, options=DEFAULT_OPTIONS)
Spell a whole tree, or return "" if it is one the printer cannot spell.
Source code in src/demangle/schemes/swift/_printer.py
2659 2660 2661 2662 2663 2664 2665 2666 2667 2668 2669 | |
demangle_symbolic
¶
demangle_symbolic(
name, resolver=None, *, whole_symbol=None
)
Read a mangled name that may hold symbolic references, and spell it.
name is bytes, not str, because a name holding a symbolic reference is not
text: the reference's four-byte offset is arbitrary bytes. This is the entry a
metadata typeref takes, and the only one that can take it.
resolver is called with (reference, offset-of-its-offset-field-within-name) and
returns a mangled fragment naming what the reference points at, or None to decline.
Return a Unicode str for textual fragments, or bytes when a fragment contains
binary symbolic references. For compatibility, a str whose grammar reaches a symbolic
reference token or alignment padding is treated as a Latin1 byte view.
resolve.ContextResolver is one, over an Image; a caller with a debugger or a
memory dump writes its own. With no resolver at all, a name holding a reference is
refused -- which is what the reference demangler does, and better than inventing a
name for something the bytes do not carry.
whole_symbol says whether to read the name as a whole symbol or as a type. Left
None it is decided by the mangling prefix, which is what tells the two apart: a
symbol carries $s or _T, a typeref carries nothing.
Returns the spelling, or None if the name could not be read.
Source code in src/demangle/schemes/swift/__init__.py
167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 | |
typerefs
¶
typerefs(blob)
Split a metadata blob of NUL-terminated mangled names into those names.
Not blob.split(b"\0"): a symbolic reference's offset is arbitrary bytes and very
often holds a zero, so splitting cuts names in half. See symbolic.end_of_name.
Source code in src/demangle/schemes/swift/__init__.py
214 215 216 217 218 219 220 221 222 | |
scan
¶
scan(data)
Every symbolic reference in data, in order.
Reading rather than searching: a byte inside an offset can look like an introducer, and a name that has been walked properly is the only way to know it is not one.
Source code in src/demangle/schemes/swift/symbolic.py
138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 | |
end_of_name
¶
end_of_name(data, start=0)
Where the NUL-terminated mangled name beginning at start ends.
The index returned is of the terminating NUL, or of the end of data if the name
runs to it. This exists because the obvious thing does not work: a reference's
four-byte offset is arbitrary bytes and very often holds a zero, so data.index(0)
cuts a name in half. Swift's own reflection reader walks the name for the same
reason.
Source code in src/demangle/schemes/swift/symbolic.py
157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 | |
elf_image
¶
elf_image(data)
An Image over a 64-bit little-endian ELF file's PT_LOAD segments.
Raises MalformedImage -- a ValueError -- for anything that is not one, including
a file too short to hold the header it claims.
Source code in src/demangle/schemes/swift/resolve.py
121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 | |
macho_image
¶
macho_image(data)
An Image over a 64-bit Mach-O file's LC_SEGMENT_64 commands.
A fat binary is not unwrapped: pick the slice first. Only the little-endian 64-bit magic is accepted, which is every Mach-O anyone demangles Swift out of.
Raises MalformedImage for anything else, including a header whose command count or
command sizes do not fit the file.
Source code in src/demangle/schemes/swift/resolve.py
334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 | |
Options¶
demangle.schemes.swift.options
¶
How much of a Swift name to spell.
The compiler's own printer takes a DemangleOptions and consults it in fifty-one places.
This carries the ones the reference's SimplifiedUIDemangleOptions() bundle actually
changes over test/Demangle/Inputs/simplified-manglings.txt, which is what
swift-demangle --simplified prints and what Xcode and LLDB show a user.
Fields are named for the reference's, lower-cased. True is what the toolchain prints by
default, so SwiftOptions() is the full spelling and SwiftOptions.simplified() is the
bundle.
DEFAULT_OPTIONS
module-attribute
¶
DEFAULT_OPTIONS = SwiftOptions()
What both shipped styles use: the full spelling, as swift-demangle prints it.
SIMPLIFIED_OPTIONS
module-attribute
¶
SIMPLIFIED_OPTIONS = SwiftOptions.simplified()
SwiftOptions.simplified(): what swift-demangle --simplified prints.
SwiftOptions
dataclass
¶
SwiftOptions(
synthesize_sugar_on_types=True,
display_module_names=True,
show_function_argument_types=True,
display_where_clauses=True,
display_entity_types=True,
display_protocol_conformances=True,
display_generic_specializations=True,
display_extension_contexts=True,
display_unmangled_suffix=True,
show_private_discriminators=True,
shorten_partial_apply=True,
shorten_thunk=True,
show_async_resume_partial=True,
shorten_value_witness=True,
)
Which parts of a Swift name to print.
synthesize_sugar_on_types
class-attribute
instance-attribute
¶
synthesize_sugar_on_types = True
Spell Swift.Optional<T> as T?, Swift.Array<T> as [T], and the dictionary
and inline-array forms with them.
SynthesizeSugarOnTypes. The struct's own default is off; swift-demangle turns it
on unless given --disable-sugar, and so does this, because the tool's output is
what a reader has seen. Off is still reachable, and the printer itself needs it: a
specialisation's propagated function is a mangled name the reference demangles
again with the struct's defaults, so that one comes out unsugared inside a name
that is otherwise sugared.
display_module_names
class-attribute
instance-attribute
¶
display_module_names = True
Qualify a name with the module that declares it: Swift.Int, not Int.
DisplayModuleNames. The dot goes with it -- the reference prints nothing for the
module node, and the separator is written only after a context that produced output.
show_function_argument_types
class-attribute
instance-attribute
¶
show_function_argument_types = True
Spell a function's parameters and result, rather than their labels alone.
ShowFunctionArgumentTypes. False turns (Swift.Int) -> Swift.UInt into (_:): the
parameter list keeps its shape and its labels, and everything after it -- async, a
thrown error, -> result -- is left out, because what remains is a signature the way
a user refers to one rather than a type.
display_where_clauses
class-attribute
instance-attribute
¶
display_where_clauses = True
Spell the requirements of a generic signature: <A where B: Runcible>, not <A>.
DisplayWhereClauses. The parameter list stays; what goes is everything after the
where.
display_entity_types
class-attribute
instance-attribute
¶
display_entity_types = True
Spell the type a declaration has, where it is written after a colon.
DisplayEntityTypes. bar : Int becomes bar. Only the colon form: a function's
signature is written around its name rather than after it, and
show_function_argument_types is what governs that one.
display_protocol_conformances
class-attribute
instance-attribute
¶
display_protocol_conformances = True
Spell which protocol a witness conforms to, and where: bar : barrable in foo.
DisplayProtocolConformances. False leaves the type alone -- protocol witness table
for bar -- which is what identifies the entry; the conformance is what makes it long.
display_generic_specializations
class-attribute
instance-attribute
¶
display_generic_specializations = True
Spell what a specialisation was specialised with.
DisplayGenericSpecializations. False writes specialized X in place of
generic specialization <Int> of X and of the whole
function signature specialization <Arg[0] = ...> of X, once, however many
specialisation layers a symbol carries.
display_extension_contexts
class-attribute
instance-attribute
¶
display_extension_contexts = True
Spell the module an extension was written in: (extension in Foo):A.test().
DisplayExtensionContexts, and the anonymous-context form with it.
display_unmangled_suffix
class-attribute
instance-attribute
¶
display_unmangled_suffix = True
Report what followed a name the demangler could read only part of.
DisplayUnmangledSuffix. True writes with unmangled suffix "...".
show_private_discriminators
class-attribute
instance-attribute
¶
show_private_discriminators = True
Spell the file-private discriminator: (getPrivateClass in _DISC), not the name.
ShowPrivateDiscriminators.
shorten_partial_apply
class-attribute
instance-attribute
¶
shorten_partial_apply = True
Write partial apply forwarder for f rather than partial apply for f.
ShortenPartialApply, inverted, like shorten_value_witness.
shorten_thunk
class-attribute
instance-attribute
¶
shorten_thunk = True
Spell a thunk in full: reabstraction thunk helper from A to B, not thunk for B.
ShortenThunk, inverted. It reaches a dozen kinds -- the reabstraction thunks, the
autodiff ones, merged, distributed thunk, the dynamically-replaceable trio --
and in each the short form is the last child alone, or nothing at all.
show_async_resume_partial
class-attribute
instance-attribute
¶
show_async_resume_partial = True
Spell an async resume point: (1) await resume partial function for f().
ShowAsyncResumePartial. False leaves the function it resumes into.
shorten_value_witness
class-attribute
instance-attribute
¶
shorten_value_witness = True
Write destroy value witness for T rather than destroy for T.
ShortenValueWitness, inverted: the reference's flag says shorten, and every field
here says print it in full, so that SwiftOptions() is the toolchain default with
no field set to False.
simplified
classmethod
¶
simplified()
The reference's SimplifiedUIDemangleOptions(), as far as it differs here.
Upstream's bundle sets fourteen flags. The rest either match this printer's
behaviour already (SynthesizeSugarOnTypes, QualifyEntities) or change nothing
over the 217 vectors, and a flag no vector exercises is a flag with no reference
behind it -- so it is left out rather than guessed at.
Source code in src/demangle/schemes/swift/options.py
127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 | |
Demangler¶
Reading a Swift mangled name into a tree.
A port of the algorithm in the Swift compiler's lib/Demangling/Demangler.cpp, which is
the only complete description of the mangling that exists -- docs/ABI/Mangling.rst is a
grammar sketch and is explicitly not normative about the tree that results.
The mangling is postfix, and the demangler is a stack machine because of it. The main
loop reads one operator at a time and pushes what it produces; an operator that needs
operands pops them back off. _ZN-style prefix manglings can be read by recursive
descent, this cannot: in $sSiSgD the Si is pushed, Sg pops it to make Int?, and
D pops that. Nothing is known about what a component is for until the operator that
consumes it arrives.
Three pieces of state span a whole name and are what make it compress so well:
- Substitutions. Every nominal type and identifier is remembered;
Aand a letter refer back to one. A singleAoperator can push several. - Words. Each identifier contributes its capitalised words to a table, and a later
identifier can be spelled out of them --
0c6ToHostD0buildsHostToPluginMessagefrom words of the module name. This is the compression no other mangling here has. - Position.
pushBackis used as a one-character backtrack in several places, so the reader is not purely forward-only.
Failure is None throughout, exactly as the reference uses nullptr: an operator that
cannot read its operands returns None, and every caller checks. The public entry point
turns that into an exception at the boundary.
Demangler
¶
Demangler(text, resolver=None, *, byte_mode=False)
The reference's Demangler, one instance per name.
None means failure everywhere, as nullptr does in the reference. Keeping that
convention rather than raising is not a stylistic choice: several productions are
speculative, reading ahead and accepting the failure as an answer, and an exception
would have to be caught at each of them.
Source code in src/demangle/schemes/swift/_demangler.py
261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 | |
next_char
¶
next_char()
Consume and return one character, or "" at the end of input.
The cursor advances even when there was nothing to return, so push_back is the
exact inverse of this call wherever it appears, including at the end. Reading
past the end yields nothing because every test here is pos < end.
Source code in src/demangle/schemes/swift/_demangler.py
287 288 289 290 291 292 293 294 295 296 | |
pop
¶
pop(want=None)
Pop, optionally only if the top matches.
want is a kind or a predicate. A mismatch is not an error: several callers pop
optional decorations, and a None there means "there was not one".
Source code in src/demangle/schemes/swift/_demangler.py
315 316 317 318 319 320 321 322 323 324 325 326 327 | |
natural
¶
natural()
A run of digits, or None where the reference returns its -1000 sentinel.
Scanned here rather than through peek and _is_digit, which between them are
the two most-called things in the Swift demangler: 122,000 calls to _is_digit
from this method alone over the Swift corpus, and one peek beside each, for a
comparison apiece. Reading past the end still stops the run, because the bound is
the same one peek tests.
Source code in src/demangle/schemes/swift/_demangler.py
363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 | |
index
¶
index()
_ is 0 and <n>_ is n+1, so that 0 costs one character rather than two.
Source code in src/demangle/schemes/swift/_demangler.py
392 393 394 395 396 397 398 399 400 | |
demangle_symbol
¶
demangle_symbol()
demangleSymbol: read a whole name and assemble the Global node.
Source code in src/demangle/schemes/swift/_demangler.py
406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 | |
demangle_operator
¶
demangle_operator()
One operator, dispatching on its first character.
The reference writes this as a switch with a goto recur for the padding byte;
the loop here is that goto.
Source code in src/demangle/schemes/swift/_demangler.py
454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 | |
demangle_symbolic_reference
¶
demangle_symbolic_reference(raw_kind)
demangleSymbolicReference: one introducer byte and a four-byte offset.
The offset is signed, little-endian, and relative to its own first byte. What the resolver hands back is a mangled fragment rather than a spelling, which is what lets the rest of the name use it: the reference may be the base of a bound generic, or a member the next operator qualifies, and only a real node can be either. A resolver that declines refuses the whole name, exactly as a null from the reference's own resolver does.
Source code in src/demangle/schemes/swift/_demangler.py
475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 | |
demangle_fragment
¶
demangle_fragment()
Read the whole text as one node, for a resolver's answer.
Source code in src/demangle/schemes/swift/_demangler.py
531 532 533 534 535 536 | |
demangle_multi_substitutions
¶
demangle_multi_substitutions()
A, which may name several substitutions at once.
A lower-case letter says "this one, and more follow"; an upper-case letter is the
last. A number before a letter is a repeat count, so A2b pushes the same node
twice -- except that a number followed by _ is a large index instead, which is
why the count cannot be read until the character after it is known.
Source code in src/demangle/schemes/swift/_demangler.py
538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 | |
demangle_standard_substitution
¶
demangle_standard_substitution()
S and a letter: one of the standard library's common types.
Source code in src/demangle/schemes/swift/_demangler.py
583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 | |
demangle_identifier
¶
demangle_identifier()
A length-prefixed identifier, possibly built out of earlier ones.
Three forms, distinguished by what follows the first 0:
<n><n bytes>-- plain.00<n>_<n bytes>-- punycoded, for anything outside[$_A-Za-z0-9].0<letters and lengths>-- word substitutions. Each letter names a word of an earlier identifier; a lower-case one says more pieces follow, an upper-case one is the last. This is what makes a long Swift name short, and it is why an identifier cannot be read without the identifiers before it.
Source code in src/demangle/schemes/swift/_demangler.py
616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 | |
demangle_operator_identifier
¶
demangle_operator_identifier()
o -- an identifier that spells an operator, one letter per character.
Source code in src/demangle/schemes/swift/_demangler.py
719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 | |
demangle_local_identifier
¶
demangle_local_identifier()
L -- a declaration not visible outside its file or function.
Source code in src/demangle/schemes/swift/_demangler.py
738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 | |
demangle_builtin_type
¶
demangle_builtin_type()
B -- one of the primitives the compiler knows about directly.
Source code in src/demangle/schemes/swift/_demangler.py
782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 | |
pop_function_type
¶
pop_function_type(kind, has_clang_type=False)
Assemble a function type from the annotations and the two parameter lists.
The order the annotations are popped in is the order they were pushed, reversed,
and popFunctionParamLabels walks past them by that same order -- so the two
lists have to agree. They do here because both are transcribed from the reference.
Source code in src/demangle/schemes/swift/_demangler.py
865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 | |
pop_function_param_labels
¶
pop_function_param_labels(found)
The argument labels, which are mangled after the type rather than with it.
A label list is only produced when at least one parameter is labelled: an unlabelled
signature carries none, and inventing an empty one would make the printer spell
f(_:_:) where the reference spells f.
Source code in src/demangle/schemes/swift/_demangler.py
891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 | |
demangle_pack_protocol_conformance
¶
demangle_pack_protocol_conformance()
HX -- one conformance per element of a parameter pack.
Source code in src/demangle/schemes/swift/_demangler.py
1056 1057 1058 | |
demangle_dependent_protocol_conformance_opaque
¶
demangle_dependent_protocol_conformance_opaque()
HO -- the conformance an opaque result type carries.
Source code in src/demangle/schemes/swift/_demangler.py
1060 1061 1062 1063 1064 | |
demangle_dependent_conformance_index
¶
demangle_dependent_conformance_index()
The index is offset by two: 0 was never valid and 1 means "unknown".
Source code in src/demangle/schemes/swift/_demangler.py
1089 1090 1091 1092 1093 1094 1095 1096 | |
demangle_bound_generics
¶
demangle_bound_generics()
The argument lists, outermost last, and any retroactive conformances.
A nested generic type mangles one list per level, _-separated, so
Outer<A>.Inner<B> carries two. They come back innermost first, which is why
demangle_bound_generic_args walks the list backwards.
Source code in src/demangle/schemes/swift/_demangler.py
1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 | |
demangle_bound_generic_args
¶
demangle_bound_generic_args(nominal, lists, at)
Distribute the argument lists over the nesting levels of nominal.
The mangling is flat and the tree is not: the arguments belong to whichever enclosing type declared the parameters, and a context that declares none -- a closure, an initialiser -- has to be skipped without consuming a list.
Source code in src/demangle/schemes/swift/_demangler.py
1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 | |
demangle_impl_function_type
¶
demangle_impl_function_type()
I -- a lowered SIL function type, spelled @convention(...) (...) -> ....
The conventions come first and their types come last, off the stack, so the loop at the end walks the children it just added in reverse and fills each one in.
Source code in src/demangle/schemes/swift/_demangler.py
1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 1298 1299 1300 1301 1302 1303 1304 1305 1306 1307 1308 1309 1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 1323 1324 1325 1326 1327 1328 1329 1330 1331 1332 1333 1334 1335 1336 1337 1338 1339 1340 1341 1342 1343 1344 1345 1346 1347 1348 1349 1350 1351 1352 1353 1354 1355 1356 1357 1358 1359 1360 1361 1362 1363 1364 1365 1366 1367 1368 1369 1370 1371 1372 | |
demangle_metatype
¶
demangle_metatype()
M -- one of the many runtime records that describe a type.
Source code in src/demangle/schemes/swift/_demangler.py
1374 1375 1376 1377 1378 1379 1380 1381 1382 1383 1384 1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 1396 1397 1398 1399 1400 1401 1402 1403 1404 | |
demangle_archetype
¶
demangle_archetype()
Q -- a generic parameter, an associated type, or an opaque return type.
Source code in src/demangle/schemes/swift/_demangler.py
1432 1433 1434 1435 1436 1437 1438 1439 1440 1441 1442 1443 1444 1445 1446 1447 1448 1449 1450 1451 1452 1453 1454 1455 1456 1457 1458 1459 1460 1461 1462 1463 1464 1465 1466 1467 1468 1469 1470 1471 1472 1473 1474 1475 1476 1477 1478 1479 1480 1481 1482 1483 1484 1485 1486 1487 1488 1489 1490 1491 1492 1493 1494 1495 1496 1497 1498 1499 1500 | |
demangle_generic_param_index
¶
demangle_generic_param_index()
x is <0, 0>; d gives both coordinates; a bare index is depth 0.
Depth and index are both biased so that the commonest parameter -- the first of the innermost signature -- costs a single character.
Source code in src/demangle/schemes/swift/_demangler.py
1557 1558 1559 1560 1561 1562 1563 1564 1565 1566 1567 1568 1569 1570 1571 1572 | |
demangle_thunk_or_specialization
¶
demangle_thunk_or_specialization()
T -- the largest operator, covering everything the compiler synthesises.
Source code in src/demangle/schemes/swift/_demangler.py
1590 1591 1592 1593 1594 1595 1596 1597 1598 1599 1600 1601 1602 1603 1604 1605 1606 1607 1608 1609 1610 1611 1612 1613 1614 1615 1616 1617 1618 1619 1620 1621 1622 1623 1624 1625 1626 1627 1628 1629 1630 1631 1632 1633 1634 1635 1636 1637 1638 1639 1640 1641 1642 1643 1644 1645 1646 1647 1648 1649 1650 1651 1652 1653 1654 1655 1656 1657 1658 1659 1660 1661 1662 1663 1664 1665 1666 1667 1668 1669 1670 1671 1672 1673 1674 1675 1676 1677 1678 1679 1680 1681 1682 1683 1684 1685 1686 1687 1688 1689 1690 1691 1692 1693 1694 1695 1696 1697 1698 1699 1700 1701 1702 1703 1704 1705 1706 1707 1708 1709 1710 1711 | |
demangle_generic_specialization_with_dropped_arguments
¶
demangle_generic_specialization_with_dropped_arguments()
Tt<n>... -- a specialisation the optimiser dropped some arguments from.
Each t is followed by the index of a dropped argument, and a t with no digits
after it means the first. The letter that follows the run says which sort of
specialisation it is.
Source code in src/demangle/schemes/swift/_demangler.py
1890 1891 1892 1893 1894 1895 1896 1897 1898 1899 1900 1901 1902 1903 1904 1905 | |
demangle_function_specialization
¶
demangle_function_specialization()
Tf -- a copy of a function specialised for particular argument values.
The parameter kinds are mangled inline but their payloads -- a closure's name, a propagated constant -- are on the stack, so a second pass walks the parameters backwards and fills each one in from what is left.
Source code in src/demangle/schemes/swift/_demangler.py
1907 1908 1909 1910 1911 1912 1913 1914 1915 1916 1917 1918 1919 1920 1921 1922 1923 1924 1925 1926 1927 1928 1929 1930 1931 1932 1933 1934 1935 1936 1937 1938 1939 1940 1941 1942 1943 1944 1945 1946 1947 1948 1949 1950 1951 1952 1953 1954 1955 1956 1957 1958 1959 1960 1961 1962 | |
demangle_spec_attributes
¶
demangle_spec_attributes(kind)
The flags between a specialisation's letter and its pass number.
m is not in the reference; it is kept because the shipped runtime
still holds symbols carrying it -- ...Tgm5 -- and dropping it would misread
them rather than refuse them.
Source code in src/demangle/schemes/swift/_demangler.py
2028 2029 2030 2031 2032 2033 2034 2035 2036 2037 2038 2039 2040 2041 2042 2043 2044 2045 2046 2047 2048 2049 2050 2051 2052 | |
demangle_witness
¶
demangle_witness()
W -- the tables and accessors that implement a conformance.
Source code in src/demangle/schemes/swift/_demangler.py
2054 2055 2056 2057 2058 2059 2060 2061 2062 2063 2064 2065 2066 2067 2068 2069 2070 2071 2072 2073 2074 2075 2076 2077 2078 2079 2080 2081 2082 2083 2084 2085 2086 2087 2088 2089 2090 2091 2092 2093 2094 2095 2096 2097 2098 2099 2100 2101 2102 2103 2104 2105 2106 2107 2108 2109 2110 2111 2112 2113 2114 2115 2116 2117 2118 2119 2120 2121 2122 2123 2124 2125 2126 2127 2128 2129 | |
demangle_special_type
¶
demangle_special_type()
X -- types with a spelling of their own rather than a name.
Source code in src/demangle/schemes/swift/_demangler.py
2131 2132 2133 2134 2135 2136 2137 2138 2139 2140 2141 2142 2143 2144 2145 2146 2147 2148 2149 2150 2151 2152 2153 2154 2155 2156 2157 2158 2159 2160 2161 2162 2163 2164 2165 2166 2167 2168 2169 2170 2171 2172 2173 2174 2175 2176 2177 2178 2179 2180 2181 2182 2183 2184 2185 2186 2187 2188 | |
pop_associated_conformance_subject
¶
pop_associated_conformance_subject()
What an associated conformance is about: a parameter, or a path to one.
mini.Seq.A is the bare parameter x, not an associated type path, and reading
it as a path would refuse the name.
Source code in src/demangle/schemes/swift/_demangler.py
2273 2274 2275 2276 2277 2278 2279 2280 2281 2282 2283 2284 | |
demangle_integer_type
¶
demangle_integer_type()
$ -- a value bound to a value generic parameter, Slab<2, Int>.
The value is an index, negated when an n precedes it, and it stands where a
type would: $1_ is 2 and $n3_ is -4.
Source code in src/demangle/schemes/swift/_demangler.py
2286 2287 2288 2289 2290 2291 2292 2293 2294 2295 2296 2297 | |
demangle_accessor
¶
demangle_accessor(child)
A variable or subscript may be followed by which accessor of it this is.
Source code in src/demangle/schemes/swift/_demangler.py
2299 2300 2301 2302 2303 2304 2305 2306 2307 2308 2309 2310 2311 2312 2313 | |
demangle_function_entity
¶
demangle_function_entity()
f -- an initialiser, deinitialiser, closure or similar.
Source code in src/demangle/schemes/swift/_demangler.py
2315 2316 2317 2318 2319 2320 2321 2322 2323 2324 2325 2326 2327 2328 2329 2330 2331 2332 2333 2334 2335 2336 2337 2338 2339 2340 2341 2342 2343 2344 2345 2346 2347 2348 2349 2350 2351 2352 | |
demangle_generic_signature
¶
demangle_generic_signature(has_param_counts)
l or r -- the <T, U where ...> a generic declaration carries.
r spells out how many parameters each depth has; l is the shorthand for one
parameter at one depth, which is what most generic declarations look like.
Source code in src/demangle/schemes/swift/_demangler.py
2412 2413 2414 2415 2416 2417 2418 2419 2420 2421 2422 2423 2424 2425 2426 2427 2428 2429 2430 2431 2432 2433 2434 2435 2436 2437 2438 | |
demangle_generic_requirement
¶
demangle_generic_requirement()
R -- one where clause.
The operator says two things at once: what kind of constraint it is, and how the constrained type is written. The default -- no letter at all -- is the commonest pair, a generic parameter conforming to a protocol.
Source code in src/demangle/schemes/swift/_demangler.py
2440 2441 2442 2443 2444 2445 2446 2447 2448 2449 2450 2451 2452 2453 2454 2455 2456 2457 2458 2459 2460 2461 2462 2463 2464 2465 2466 2467 2468 2469 2470 2471 2472 2473 2474 2475 2476 2477 2478 2479 2480 2481 2482 2483 2484 2485 | |
The Swift 3 mangling¶
Reading the Swift 3 mangling.
Swift changed its mangling in 4.0; _T followed by anything but 0 is the older one,
and it is still what the ObjC runtime holds for a Swift class, so it turns up in any
Apple binary with interop in it. The compiler keeps a separate demangler for it --
lib/Demangling/OldDemangler.cpp -- and this is a port of that.
It is a much easier read than the current one. The old mangling is prefix, so this is
plain recursive descent: demangle_entity reads the context, then the name, then the
type, in the order they are written. There is no node stack and no word table. What it
does share is the tree it builds, so _printer.py spells the result with no idea which
mangling it came from.
Two things are its own:
- Substitutions are positional.
S_,S0_,S1_index a list of everything nominal seen so far, and a handful of one-letter codes --Sa,SS,Sq-- stand for the standard library's common types, as they do in the current mangling. - Punycode is marked by
Xin front of the length, not by a00prefix, and the encoding is the same one_punycode.pyimplements.
OldDemangler
¶
OldDemangler(text, *, byte_mode=False)
The reference's OldDemangler. None means failure, as nullptr does there.
Source code in src/demangle/schemes/swift/_old_demangler.py
184 185 186 187 188 189 190 191 192 193 | |
demangle_identifier
¶
demangle_identifier(depth, kind=None)
X in front of the length marks a punycoded name; o marks an operator.
Source code in src/demangle/schemes/swift/_old_demangler.py
265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 | |
demangle_protocol_name_impl
¶
demangle_protocol_name_impl(depth)
S is ambiguous here: it may substitute the protocol or its context, so the
two readings have to be told apart by what comes back.
Source code in src/demangle/schemes/swift/_old_demangler.py
396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 | |
demangle_entity
¶
demangle_entity(depth)
[Z] <kind> <context> <name> [<type>].
The old mangling wrote an accessor as the outer node with the variable inside
it, which is the reverse of what the printer expects, so wrap rebuilds it the
way the current mangling produces.
Source code in src/demangle/schemes/swift/_old_demangler.py
514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 | |
demangle_constrained_type
¶
demangle_constrained_type(depth)
A constrained type is always a parameter or one of its associated types, so
the q that would introduce a generic parameter elsewhere is left off.
Source code in src/demangle/schemes/swift/_old_demangler.py
695 696 697 698 699 700 701 702 703 704 705 706 707 | |
Printer¶
Spelling a Swift demangling tree.
A port of the compiler's lib/Demangling/NodePrinter.cpp. It is a separate pass from the
demangler for a reason that shows up everywhere in it: how a node is spelled depends on
where it sits. The same Class node is Foundation.NSData as a type and NSData as the
prefix of a method's name; a Type under a function's result gets -> before it and
the same node elsewhere does not.
Two mechanisms carry that context, and both are the reference's:
printreturns a node. When an entity's context cannot be printed as a prefix -- because it is an extension, or a local declaration --printEntityprints the entity first and hands the context back, and the caller spells it asin <context>. That is why the main function has a return value at all.as_prefix_context. The same node prints differently when it is theAbc.inAbc.def(), and the flag says which it is.
The options are the ones swift-demangle uses with no flags: sugar on ([Int] rather
than Array<Int>) and every "display" option left at its default of on. They are
constants here rather than a parameter because a demangling that does not match the
toolchain's is not useful, and every one of them changes the answer.
Printer
¶
Printer(options=DEFAULT_OPTIONS)
The reference's NodePrinter, and the options it consults.
Source code in src/demangle/schemes/swift/_printer.py
256 257 258 259 260 261 262 263 264 | |
print_context
¶
print_context(context)
Whether a context is worth naming.
The standard library and __C are named; a module the debugger invented is not
worth hiding, and the reference names it too. This returns False only for the
HidingCurrentModule option, which is unset here -- but the shape is kept because
the caller's control flow depends on it.
Source code in src/demangle/schemes/swift/_printer.py
303 304 305 306 307 308 309 310 311 | |
find_sugar
¶
find_sugar(node)
Which of the four sugared spellings, if any, a bound generic type has.
Source code in src/demangle/schemes/swift/_printer.py
313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 | |
print_function_parameters
¶
print_function_parameters(
labels, parameters, depth, show_types=True
)
The parameter list, with or without the types in it.
Without them the list keeps its shape and its labels and nothing else, so
(Swift.Int) -> Swift.UInt is (_:) and a labelled two-parameter function is
(from:to:) -- which is how a Swift programmer refers to it. A parameter that
carried no label at all is written _:, and there are no separators, because
(_:_:) is one token in that spelling rather than a list.
Source code in src/demangle/schemes/swift/_printer.py
376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 | |
print_impl_function_type
¶
print_impl_function_type(node, depth)
A lowered SIL type: (params) -> (results), with the attributes in front.
Source code in src/demangle/schemes/swift/_printer.py
506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 | |
print_generic_signature
¶
print_generic_signature(node, depth)
<A, B where A: P>, with the parameter counts turned back into names.
Source code in src/demangle/schemes/swift/_printer.py
562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 | |
print_function_sig_specialization_params
¶
print_function_sig_specialization_params(node, depth)
One parameter of a function-signature specialisation.
Two cursors, as the reference has: at walks the children looking for the next
kind, and reads the payload the mangling wrote inline beside it -- an integer's
digits, a string's encoding. argument walks the same children skipping every
kind and payload, and serves the operands that were popped off the stack: the
types, the identifiers.
One cursor would not do it, because a single parameter can carry several kinds --
pSSi3Si0 is a propagated struct, an integer, a struct, an integer -- and their
two sorts of operand are interleaved differently from the kinds themselves.
Source code in src/demangle/schemes/swift/_printer.py
647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 | |
print_next_param_child
¶
print_next_param_child(node, argument, kind, depth)
The next operand of a parameter: what was popped rather than written inline.
Returns where the operand cursor reached, so the caller can pass it back in -- several of the kinds take more than one.
Source code in src/demangle/schemes/swift/_printer.py
752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 | |
print
¶
print(node, depth, as_prefix_context=False)
Spell node, returning a context to be printed after it, or None.
The return value is what makes in <context> possible: an entity whose context
cannot be written as a prefix prints itself first and hands the context back.
Source code in src/demangle/schemes/swift/_printer.py
814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 | |
print_entity
¶
print_entity(
entity,
depth,
as_prefix_context,
style,
has_name,
extra_name="",
extra_index=-1,
overwrite_name="",
)
Spell a declaration: its context, its name, and its type.
The context goes in front -- Foo.bar() -- unless it cannot, in which case it is
returned for the caller to spell as in Foo. It cannot when the name has spaces
in it (closure #1 in ... reads wrongly the other way round) or when the context
is itself something that needs a type printed.
Source code in src/demangle/schemes/swift/_printer.py
873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 | |
Symbolic references¶
demangle.schemes.swift.symbolic
¶
Symbolic references: the part of the Swift mangling that points into the binary.
A mangled name in a Swift binary's metadata is not always self-contained. Where a name
would have to spell a type the image already describes, the compiler writes a symbolic
reference instead: one byte saying what kind of thing is referred to and how, then a
four-byte signed offset relative to itself. The bytes are transcribed from
Demangler.cpp::demangleSymbolicReference in the Swift 5.10 sources.
01 a context descriptor, directly
02 a context descriptor, through a pointer
09 an accessor function that yields the entity when run
0A a unique extended existential type shape
0B a non-unique extended existential type shape
03-08, 0C reserved, and refused: the reference itself refuses them
FF alignment padding in front of a reference, and skipped
Two consequences fall out of the encoding and both matter to a reader:
- A metadata blob cannot be split on NUL. The four-byte offset is arbitrary bytes
and very often contains a zero, so finding where a mangled name ends means parsing
it.
end_of_namedoes exactly that and nothing else. - A name holding one is not text. It is bytes, so the entry points here take
byteswhere the rest of the package takesstr.
Resolving a reference needs the image the name came out of, which is why this is a
separate entry point rather than something demangle() could do: see resolve.py for a
resolver that reads one, and demangle_symbolic for the parse that uses it.
read
¶
read(data, pos)
Read the reference whose introducer is at data[pos].
Returns (reference, position after it). Raises ValueError if pos does not hold
an introducer, or if the four offset bytes are not all there -- which the reference
also treats as failure rather than reading a short integer.
Source code in src/demangle/schemes/swift/symbolic.py
118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 | |
names
¶
names(data)
Split a metadata blob of NUL-terminated mangled names into those names.
Empty entries are dropped: a section is padded with them.
Source code in src/demangle/schemes/swift/symbolic.py
184 185 186 187 188 189 190 191 192 193 194 195 196 197 | |
Resolving one¶
demangle.schemes.swift.resolve
¶
Resolving a symbolic reference, which needs the image the name came out of.
A symbolic reference is an offset from itself to a descriptor somewhere else in the same
binary, so reading one means reading the binary. That is why it is here rather than in
the demangler: demangle() is given a name and nothing else, and no amount of cleverness
recovers what the name deliberately does not carry.
What a resolver has to produce is a mangled fragment in Swift's own grammar --
4demo5PointV for demo.Point -- which the demangler then reads in place. Handing back
a fragment rather than a finished spelling is what keeps the rest of the mangling
working: the reference may be the base of a bound generic type, or a member the next
operator qualifies, and only a real node can be used that way.
The descriptor layout is Swift's TargetContextDescriptor and the two that extend it:
uint32 Flags low five bits are the kind
int32 Parent a relative *indirectable* pointer: low bit set means
"the offset reaches a pointer to the descriptor"
int32 Name a relative direct pointer to a NUL-terminated name;
present on module and on nominal type descriptors alike
Walking Parent to the module and writing each name back out with its length is the
whole of it. The check that it is right is that the fragment demangles to the same name
the type carries in the symbol table.
MalformedImage
¶
Bases: ValueError
The file is not a well-formed image of the kind it claims to be.
A ValueError rather than whatever the arithmetic happened to raise. These loaders
read a file the calling tool did not produce -- the same threat model as a mangled
name -- so a truncated or hostile header has to arrive as a refusal a caller can
catch, not as an IndexError from a header field or a struct.error from an offset
that pointed past the end.
Nodes¶
demangle.schemes.swift.nodes
¶
The Swift symbol tree a caller walks.
Swift's own demangling tree has 385 node kinds, most of which say something about the
lowered representation rather than about the declaration --
ImplParameterResultDifferentiability is not what a tool wants to match on. So this
exposes a small tree in the vocabulary the other schemes here use: name, module,
type, template, function, parameters.
The tree is built during printing, not from the demangling tree. That is the point:
the printer's spelling of a node depends on where it sits -- a class is
Foundation.NSData as a type and NSData as a prefix -- so a subtree rendered on its own
would not say what it says in the whole name. Recording the boundaries of one traversal
means every node's text is exactly the run of characters it produced, and the tree cannot
spell the symbol differently from demangle().
SwiftName
¶
SwiftName(kind, swift_kind, parts)
Bases: Node
One part of a Swift symbol: the text it produced, and the nodes inside it.
parts interleaves strings and nodes in output order, so render() is a
concatenation and cannot drift from what the printer wrote.
Source code in src/demangle/schemes/swift/nodes.py
66 67 68 69 70 71 72 73 74 | |
build
¶
build(root, options=DEFAULT_OPTIONS)
Spell root while recording its structure, returning the tree.
Returns None for a tree the printer refuses, which is the same answer the text path
gives -- neither invents a partial reading.
Source code in src/demangle/schemes/swift/nodes.py
131 132 133 134 135 136 137 138 139 140 141 142 | |
D¶
demangle.schemes.d
¶
D mangled names.
Every _D symbol is unambiguously D's: no other scheme here uses that prefix, and the
grammar requires a type after the path, so a name that merely starts _D but is not one
is refused rather than guessed at.
Conformance is 100% against GNU binutils' D demangler (c++filt --format=dlang) over
every symbol it can read in the shipped libgphobos and libgdruntime -- 16,333 of
19,315 -- with nothing mis-spelled and nothing refused among them. See _parser.py for
where the grammar comes from and which of its rules had to be settled by measurement.
PLUGIN
module-attribute
¶
PLUGIN = LanguagePlugin(
name="d",
node_kinds=("name", "path", "symbol"),
detect=detect,
parse=parse,
description="D symbol mangling (dlang)",
aliases=("dlang",),
first_characters="_",
priority=40,
)
The scheme as the registry holds it, registered when this package is imported.
DSymbol
¶
DSymbol(raw, path, text, suffix='', generated='')
One parsed D symbol: its dotted path, and the type of what it declares.
Source code in src/demangle/schemes/d/_parser.py
279 280 281 282 283 284 | |
parse_d_symbol
¶
parse_d_symbol(
symbol,
max_substitutions=max_substitutions,
max_output=max_output,
)
Parse symbol, returning a DSymbol, or raise DemangleFailure.
Source code in src/demangle/schemes/d/_parser.py
1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 1298 1299 | |
detect
¶
detect(name)
_D and something after it.
Narrower than it looks: a D symbol's path components are length-prefixed, so the
character after _D is a digit for every name a compiler emits. That keeps this from
claiming an ordinary C identifier that happens to begin _D.
_Dmain is the one exception, and it has to be named: it is D's entry point, it has
no path and no type, so the digit rule would reject it -- and it is the one symbol
every D program has.
Source code in src/demangle/schemes/d/__init__.py
29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 | |
parse
¶
parse(
mangled, builder, limits=DEFAULT_LIMITS, options=None
)
Parse a D mangled name into builder.
Source code in src/demangle/schemes/d/__init__.py
66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 | |
Nodes¶
demangle.schemes.d.nodes
¶
The D symbol tree.
The same parts-in-order shape as the Rust and Go trees: a node is the fragments it was built from, so rendering concatenates and a tree cannot spell a symbol differently from the text path.
What a caller wants from a D symbol is which module it belongs to, whether it is a
method, and what a template was instantiated with -- none of which survives splitting the
spelling on ., because a template argument list contains dots of its own.
DName
¶
DName(parts)
Bases: _D
One path component.
Source code in src/demangle/schemes/d/nodes.py
24 25 26 27 28 29 | |
Path
¶
Path(parts)
Bases: _D
The dotted path: module, then the entity within it.
Source code in src/demangle/schemes/d/nodes.py
24 25 26 27 28 29 | |
Symbol
¶
Symbol(parts, generated='')
Bases: _D
A whole D symbol.
generated is the prefix the reference gives a compiler-generated symbol -- an
initializer, a vtable, a ClassInfo -- and empty for an ordinary one.
Source code in src/demangle/schemes/d/nodes.py
73 74 75 | |
build
¶
build(symbol)
Assemble the tree for a parsed DSymbol.
Source code in src/demangle/schemes/d/nodes.py
78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 | |
Go¶
demangle.schemes.go
¶
Go symbol names.
Go is the one scheme here with no marker saying "this is mine". An Itanium symbol opens
_Z, an MSVC one ?, a Rust v0 one _R; a Go one opens with a package path, and
fmt.Println is a shape a C or Pascal symbol could have too. So detection asks for
positive evidence and declines otherwise, and a caller who knows the binary is Go -- from
its build info, which is how a tool would know -- passes language="go" and gets the
whole scheme regardless.
That asymmetry is deliberate. Failing to claim a Go symbol returns it unchanged, which is what an unreadable name does anyway. Claiming a symbol that is not Go's would rewrite someone else's name into a plausible lie, and a plausible lie is the one outcome this library treats as worse than silence.
See _parser.py for where the rules come from: Go's own cmd/internal/objabi/path.go,
not another demangler's reading of it.
GENERATED_PREFIXES
module-attribute
¶
GENERATED_PREFIXES = ('go:', 'type:', 'go.', 'type.')
Prefixes the linker gives its own generated symbols.
go: and type: are current; go. and type. are what Go wrote before 1.20 and still
appear in older binaries.
PLUGIN
module-attribute
¶
PLUGIN = LanguagePlugin(
name="go",
node_kinds=(
"name",
"path",
"receiver",
"symbol",
"template",
),
detect=detect,
parse=parse,
description="Go symbol names (package paths, receivers, generic instantiations)",
aliases=("golang",),
priority=10,
)
The scheme as the registry holds it, registered when this package is imported.
GoSymbol
¶
GoSymbol(
raw,
package="",
name="",
receiver=None,
pointer_receiver=False,
generic=None,
generated="",
)
One parsed Go symbol.
package and name are decoded; raw is what was read. receiver is the type a
method is on, without its *, and pointer_receiver says which form it was written
in. generic holds the text between the brackets of an instantiation, and
generated names the linker's own prefix for a symbol that has one -- such a symbol
has no package, receiver or instantiation of its own: name is the linker's text.
Source code in src/demangle/schemes/go/_parser.py
122 123 124 125 126 127 128 129 | |
text
property
¶
text
The symbol with its escapes decoded, and nothing else changed.
Deliberately not a new spelling. Go's own tooling -- go tool nm, go tool
objdump, pprof, delve -- prints these names as they stand, so a reader
recognises them; inventing a prettier form would mean this library is the only
thing in the ecosystem showing one. What it adds is the decoding, which no Go
tool does and which is the one part that is genuinely encoded.
escape_path
¶
escape_path(path)
Escape a package path the way objabi.PathToPrefix does.
Operates on UTF-8 bytes, as the Go implementation does. This is not a detail: ü
is one Python character but two bytes, and Go writes it %c3%bc. Escaping the
character would produce %fc, which is not what any Go binary contains.
Kept beside the decoder because it is what makes the decoder testable -- for every symbol in a real binary, escaping what we decoded must give back the original bytes. A decoder checked only against examples passes while quietly mishandling whatever the examples missed.
Source code in src/demangle/schemes/go/_parser.py
59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 | |
parse_go_symbol
¶
parse_go_symbol(symbol)
Parse symbol, returning a GoSymbol, or raise.
Raises NotMangledError for anything without the shape of a Go symbol and
ParseError for something Go-shaped this cannot read.
Source code in src/demangle/schemes/go/_parser.py
214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 | |
unescape_path
¶
unescape_path(prefix)
Reverse escape_path, as objabi.PrefixToPath does.
Raises ValueError on a malformed escape, matching the Go implementation, which
refuses rather than passing the % through: a symbol carrying one is not a symbol
the Go linker wrote.
Source code in src/demangle/schemes/go/_parser.py
82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 | |
detect
¶
detect(name)
Whether name is Go-shaped beyond reasonable doubt.
Three kinds of evidence, each of which no other scheme here produces:
- a linker-generated prefix,
go:ortype:; - a package path with a
/in it, followed by a.-- an import path, which C and Pascal symbols do not have and which the C-family schemes encode rather than spell -- unless it is a file's path instead (see_is_absolute); - a method receiver written
(*Type).Method.
fmt.Println and main.main are real Go symbols and are deliberately not claimed:
nothing in them distinguishes a Go symbol from any other dotted name, and guessing
would mean rewriting names this library was not sure about. They demangle when asked
for by language.
Source code in src/demangle/schemes/go/__init__.py
32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 | |
parse
¶
parse(
mangled, builder, limits=DEFAULT_LIMITS, options=None
)
Parse a Go symbol into builder.
Source code in src/demangle/schemes/go/__init__.py
82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 | |
Nodes¶
demangle.schemes.go.nodes
¶
The Go symbol tree.
The same shape as the Rust tree, and for the same reason: a Go symbol spells strictly left to right, so a node is its parts in order and rendering concatenates them. A tree therefore cannot spell a symbol differently from the text path.
What the structure is for is the question a tool actually asks of a Go binary -- which
package is this in, is it a method and on what, is it an instantiation of a generic --
none of which can be answered by splitting the raw name on ., because a package path
may contain a dot of its own (written %2e) and a receiver may contain one too.
GoName
¶
GoName(parts)
Bases: _Go
An identifier: a function, a method, a type.
Source code in src/demangle/schemes/go/nodes.py
25 26 27 28 29 30 | |
Package
¶
Package(parts)
Bases: _Go
An import path, with its escapes already decoded.
Source code in src/demangle/schemes/go/nodes.py
25 26 27 28 29 30 | |
Receiver
¶
Receiver(parts, pointer)
Bases: _Go
The type a method is defined on.
pointer says whether it was written (*T).M or T.M, which is a real distinction:
they are different methods with different symbols.
Source code in src/demangle/schemes/go/nodes.py
75 76 77 | |
Generic
¶
Generic(parts, arguments)
Bases: _Go
A generic instantiation's arguments, [go.shape.int] or [int].
Kept as text rather than parsed further: the arguments are Go type syntax, which is a
language of its own, and nothing in a symbol table needs it taken apart. arguments
is what stood between the brackets.
Source code in src/demangle/schemes/go/nodes.py
91 92 93 | |
Symbol
¶
Symbol(parts, generated='')
Bases: _Go
A whole Go symbol.
generated is the linker's own prefix where there is one -- go: for an itab or a
string, type: for a runtime type descriptor -- and empty for an ordinary symbol.
Source code in src/demangle/schemes/go/nodes.py
106 107 108 | |
build
¶
build(symbol)
Assemble the tree for a parsed GoSymbol.
Built from the decoded pieces in output order, so Symbol.render() reproduces
GoSymbol.text exactly rather than approximately.
Source code in src/demangle/schemes/go/nodes.py
111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 | |
Nim¶
demangle.schemes.nim
¶
Nim symbol names.
Nim compiles to C, so a Nim symbol is an ordinary C identifier and there is no prefix to
key on. Nothing in the Nim toolchain reads one back either, so the grammar here is a
transcription of the compiler's mangleutils.mangle and modulegraphs.uniqueModuleName,
and the correctness argument is the one Go's uses: what this reads must re-mangle to the
bytes the compiler wrote.
Measured, over the two shipped Nim compilers (1.6.14 and 2.2.0):
- every routine name declared in either standard library -- 5,946 -- comes back exactly,
bar 8, and all 8 are the same documented loss (see
_parser.py); - every module path in either standard library -- 304 and 310 -- comes back exactly;
- over 2,000 symbols the compilers actually emitted, checked against the name each
recorded in its own
.ndidebug-mapping file.
Detection is the whole parse rather than a shape test, because there is nothing else to test: a reading has to exist that re-mangles to the symbol. Over 59,000 symbols from the other schemes' corpora and from real C, C++, D and Pascal binaries, that claims none of them.
PLUGIN
module-attribute
¶
PLUGIN = LanguagePlugin(
name="nim",
node_kinds=("name", "path", "symbol"),
detect=detect,
parse=parse,
description="Nim symbol mangling",
aliases=(),
priority=10,
)
The scheme as the registry holds it, registered when this package is imported.
NimSymbol
¶
NimSymbol(raw, text, kind, name='', module='')
One parsed Nim symbol.
Source code in src/demangle/schemes/nim/_parser.py
101 102 103 104 105 106 107 | |
detect
¶
detect(name)
Whether name is one this reads.
Deliberately the whole parse rather than a shape test. A Nim symbol is an ordinary C
identifier -- there is no prefix to key on -- so the only honest test is whether a
reading exists that re-mangles to it, and that is what parse_nim_symbol establishes.
What comes before that parse is a necessary condition rather than a guess, which is
what makes it safe to screen on: a name that is not one of the compiler's own forms
can only be <name>__<module>_[u]<id>, and <id> is a run of decimal digits at the
very end. This plugin declares no first character -- a Nim symbol is an ordinary C
identifier -- so it is offered every symbol in a binary, and running five anchored
regular expressions over each of them would cost several times any other scheme's
detection. __ alone does not screen -- std::__cxx11 has one, and so does
a fifth of the shipped libstdc++ -- and neither does a trailing digit, which 95% of
those symbols also have. The tail does: none of them survives it.
Both halves of that screen are necessary, so the order between them is free to be
chosen on cost -- and it matters more than it looks. "__" in name is one C-level
scan; _ROUTINE_TAIL.search is a regular expression whose $ anchor does not stop
search trying every position first. Testing the regex first would spend it on
every name that has no __ at all, which is most of them: 61% of the shipped
libstdc++, and every ordinary C identifier. With the membership test first, those
stop there; only the 39% carrying a __ reach the regex. This scheme declares no
first character, so it is offered every symbol in a binary and this is the bulk of
its detection cost.
_FOREIGN_PREFIXES is the other half, and it is a refusal rather than a screen: the
shape this scheme reads is one another compiler also produces, and where a name
carries that compiler's marker the marker wins. See the note there.
Source code in src/demangle/schemes/nim/_parser.py
435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 | |
parse_nim_symbol
¶
parse_nim_symbol(name)
Parse name, returning a NimSymbol, or raise DemangleFailure.
Source code in src/demangle/schemes/nim/_parser.py
407 408 409 410 411 412 413 414 415 416 417 418 419 420 | |
parse
¶
parse(
mangled, builder, limits=DEFAULT_LIMITS, options=None
)
Parse a Nim symbol into builder.
Source code in src/demangle/schemes/nim/__init__.py
43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 | |
Parser¶
Reading Nim's C symbol names.
Nim compiles to C, so what reaches the linker is whatever the code generator chose to
call a thing. There is no reference demangler -- nothing in the Nim toolchain reads one of
these back -- so the grammar here is a transcription of the compiler's own
compiler/mangleutils.nim and compiler/modulegraphs.nim, and the correctness argument
is the same shape as Go's: re-mangling what we read must reproduce the bytes the
compiler wrote, checked over every symbol.
A routine or a global is written
<mangled identifier> "__" <mangled module path> "_" ["u"] <id>
where the u appeared in Nim 2 and marks the mangling of the module path as well: Nim 1
encodes digits in a path and Nim 2 leaves them alone, so the same pureZmd53 is
pure/md5 under one and pure/md53 under the other. The u is the only thing that says
which, which is why it is read as a version marker rather than skipped.
Two things are not recoverable, and both are the compiler's own doing.
mangle drops an underscore that precedes a digit, because it reserves _<digit> for its
own disambiguation: len0_16 and len016 are the same symbol. Over every routine name
declared in either shipped standard library -- 5,946 of them -- that is the only cause
of a name this cannot return exactly, and it accounts for all 8 of them.
The escaping is also not injective. $ becomes dollar and < becomes lt, so
result -- which contains lt -- could be read as resu<. What settles it is that Nim
names have a grammar: a routine is an identifier, or a name made entirely of operator
characters, or an identifier with = after it (a setter), or one of the compiler's own
:tmp/=destroy forms. Reading result as resu< is not any of those. The decoder
proposes the readings the grammar allows, in that order, and takes the first that
re-mangles to the input.
mangle
¶
mangle(name)
mangleutils.mangle, transcribed.
Kept so that a reading can be checked by re-mangling it, which is the whole correctness argument here.
Source code in src/demangle/schemes/nim/_parser.py
110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 | |
unmangle
¶
unmangle(text)
The name text was mangled from, or None if no reading re-mangles to it.
The readings are proposed in the order Nim's own name grammar makes likely, and the
re-mangle is what accepts one. A trailing _ usually marks an escape, but a name
that is _ has one too, which is why the undecoded text is a candidate as well.
Source code in src/demangle/schemes/nim/_parser.py
201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 | |
mangle_module
¶
mangle_module(path, nim2)
modulegraphs.uniqueModuleName, transcribed.
Nim 2 leaves digits alone; Nim 1 writes them as codes like everything else that is not a lower-case letter.
Source code in src/demangle/schemes/nim/_parser.py
236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 | |
unmangle_module
¶
unmangle_module(text, nim2)
Undo uniqueModuleName.
Under Nim 1 every digit is part of a code, so this is exact. Under Nim 2 a digit may
be either, and the tie is broken towards what a real path holds -- see
_PLAUSIBLE_IN_A_PATH. Verified over every module in both shipped standard
libraries: 304 of 304 and 310 of 310.
Source code in src/demangle/schemes/nim/_parser.py
255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 | |
Nodes¶
demangle.schemes.nim.nodes
¶
The Nim symbol tree.
The same parts-in-order shape as the Rust, Go and D trees: a node is the fragments it was built from, so rendering concatenates and the tree cannot spell a symbol differently from the text path.
Splitting the spelling on . would not do: a Nim module path holds /, and a routine
name can be . itself -- dot___ops_17 is the operator ., whose demangling is
ops... The tree hands the two halves over already separated.
NimName
¶
NimName(parts)
Bases: _Nim
A routine or variable name, with its escapes undone.
Source code in src/demangle/schemes/nim/nodes.py
20 21 22 23 24 25 | |
Path
¶
Path(parts)
Bases: _Nim
The module the symbol belongs to, as a path with / separators.
Source code in src/demangle/schemes/nim/nodes.py
20 21 22 23 24 25 | |
Symbol
¶
Symbol(parts, nim_kind)
Bases: _Nim
A whole Nim symbol.
nim_kind is what the mangling said this is -- routine, type, type-info,
marker or temporary -- which the spelling alone does not always make plain.
Source code in src/demangle/schemes/nim/nodes.py
69 70 71 | |
build
¶
build(symbol)
Assemble the tree for a parsed NimSymbol.
Source code in src/demangle/schemes/nim/nodes.py
74 75 76 77 78 79 80 81 | |
Free Pascal¶
demangle.schemes.pascal
¶
Free Pascal symbol names.
Free Pascal builds a symbol out of $-delimited parts in make_mangledname
(compiler/symdef.pas), and ships nothing that reads one back. So the grammar here is a
transcription of that function and the three that feed it, and correctness rests on
re-assembly: the parts this splits out, rejoined with the compiler's own separators, must
reproduce the symbol exactly.
Measured over every symbol in the 1,074 object files of the shipped Free Pascal 3.2.2 runtime and packages -- 237,326 of them:
- 236,570 are read, and every one re-assembles to the bytes it was read from;
- the 756 refused have no
$in them at all, being plain C names from the soft-float runtime rather than Pascal symbols; - checked independently against
ppudump, which prints both the mangled name and what each unit declares, every name this reads is one the unit really declares -- bar the compiler's owninitandfinalizesections, which no source declares.
Case does not come back. Pascal is case-insensitive and the compiler upper-cases
before it mangles, so Add and ADD are the same symbol and nothing can tell them apart.
This is Free Pascal's mangling, which is what Lazarus and any {$MODE DELPHI} code
built with fpc produces. Borland and Embarcadero's own Delphi compilers use a different
scheme -- @Unit@Class@Method$qqrv -- which the delphi plugin reads.
PLUGIN
module-attribute
¶
PLUGIN = LanguagePlugin(
name="pascal",
node_kinds=("name", "parameters", "path", "symbol"),
detect=detect,
parse=parse,
description="Free Pascal symbol mangling",
aliases=("fpc", "freepascal"),
priority=20,
)
The scheme as the registry holds it, registered when this package is imported.
PascalSymbol
¶
PascalSymbol(
raw,
text,
kind,
unit="",
scope=(),
name="",
parameters=(),
result=None,
indirect=False,
raw_scope="",
raw_signature="",
elided=False,
wrapped=None,
)
One parsed Free Pascal symbol.
Source code in src/demangle/schemes/pascal/_parser.py
135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 | |
detect
¶
detect(name)
Whether name is one this reads.
The $ in a Free Pascal symbol is not legal in a C identifier on most targets, and
the separators are distinctive enough that the shape alone is a fair test -- but this
parses anyway, because the parse is what establishes that the parts rejoin to the
input.
Source code in src/demangle/schemes/pascal/_parser.py
396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 | |
parse_pascal_symbol
¶
parse_pascal_symbol(name)
Parse name, returning a PascalSymbol, or raise DemangleFailure.
Source code in src/demangle/schemes/pascal/_parser.py
295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 | |
parse
¶
parse(
mangled, builder, limits=DEFAULT_LIMITS, options=None
)
Parse a Free Pascal symbol into builder.
Source code in src/demangle/schemes/pascal/__init__.py
47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 | |
Parser¶
Reading Free Pascal's symbol names.
Free Pascal builds a symbol out of $-delimited parts in make_mangledname
(compiler/symdef.pas), and nothing in the toolchain reads one back. So the grammar here
is a transcription of that function and of the three others that feed it, and the
correctness argument is the one Go and Nim use: the parts this splits out, rejoined with
the separators the compiler uses, must reproduce the symbol exactly.
The shape, from the compiler's own comment:
[<kind> "_$"] <unit or "P$"program> ["$_$" <scope>] ["_$$_" <name and parameters>]
- kind is one of nine markers saying the symbol is a table rather than code --
VMT,RTTI,INIT,IID,IIDSTR,RESSTR,WRPR-- or that it is data rather than a routine:Ufor a variable andTCfor a typed constant. - scope is the classes and records the thing is nested in, joined by
_$_, and then any enclosing procedures. A method's scope ends in a bare_$_, which is whyTWIDGET_$__$$_AREAhas that run of underscores in it. - name and parameters is the routine name, then
$and a type per parameter, then$$and the result type.
Case does not come back. Pascal is case-insensitive and the compiler upper-cases
identifiers before it mangles them, so Add and ADD are one symbol. Nothing can undo
that, and this does not pretend to.
A long parameter list does not come back either. Over about 12 characters the
compiler replaces the whole list with $crc and a checksum, and over 100 characters it
does the same to a scope with $CRC. Both are reported as what they are rather than
guessed at.
spell_routine_name
¶
spell_routine_name(name)
$plus is operator +; $class_constructor is what it says.
Source code in src/demangle/schemes/pascal/_parser.py
238 239 240 241 242 243 244 245 246 247 | |
Nodes¶
demangle.schemes.pascal.nodes
¶
The Free Pascal symbol tree.
The same parts-in-order shape as the other schemes: a node is the fragments it was built from, so rendering concatenates and the tree cannot spell a symbol differently from the text path.
What a caller wants from one of these is the unit, the class, and the signature -- none of
which survives splitting the spelling, because a parameter type can be a generic whose own
name holds a $ and a ..
Unit
¶
Unit(parts)
Bases: _Pascal
The unit, or the program, the symbol belongs to.
Source code in src/demangle/schemes/pascal/nodes.py
20 21 22 23 24 25 | |
PascalName
¶
PascalName(parts)
Bases: _Pascal
A class, record or routine name.
Source code in src/demangle/schemes/pascal/nodes.py
20 21 22 23 24 25 | |
Parameters
¶
Parameters(parts)
Bases: _Pascal
The parameter list, as the mangling recorded it.
Source code in src/demangle/schemes/pascal/nodes.py
20 21 22 23 24 25 | |
Symbol
¶
Symbol(parts, pascal_kind)
Bases: _Pascal
A whole Free Pascal symbol.
pascal_kind is what the mangling said this is -- routine, or one of the nine
markers, or section or label -- which the spelling alone does not always say.
Source code in src/demangle/schemes/pascal/nodes.py
76 77 78 | |
build
¶
build(symbol)
Assemble the tree for a parsed PascalSymbol.
Source code in src/demangle/schemes/pascal/nodes.py
81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 | |
Pre-Itanium C++¶
demangle.schemes.gnuv2
¶
Pre-Itanium C++: GNU g++ before 3.0, cfront/ARM, Lucid, HP aCC and EDG.
Five manglings, one demangler, because that is how libiberty implements them: one
cplus-dem.c under five style flags. See _parser.py for the port and for what is
measured against the reference's own vectors.
Detection is the hard part, and the reason this scheme is offered almost last. A GNU
v2 symbol is an ordinary C identifier with a __ somewhere in it --
AtEnd__13ivRubberGroup is a name a C compiler would have accepted -- so there is no
prefix to key on, and a scheme that guessed would rewrite other people's symbols into plausible lies. Three things keep
it honest:
- it is offered after every other scheme except CodeWarrior, Itanium included, so it only ever sees names nothing else claimed;
detectruns the whole parse rather than a shape test, and a name that does not parse end to end is not claimed;- a reading that decodes nothing -- one where the demangler consumed the name and gave back what it was given -- is refused, because that is not evidence of anything.
Measured over every corpus in tests/conformance/ and over the symbol tables of the
system's own C libraries: see tests/test_gnuv2.py, which pins the count both ways.
A caller who knows which compiler built the binary should say so, with
GnuV2Options(style=...): the five styles read the same bytes differently and the name
does not say which one wrote it. style="gnu" is the default, being the one most likely
to be met.
PLUGIN
module-attribute
¶
PLUGIN = LanguagePlugin(
name="gnuv2",
node_kinds=("name", "parameters", "symbol", "type"),
detect=detect,
parse=parse,
description="Pre-Itanium C++ (g++ 2.x, cfront/ARM, Lucid, HP aCC, EDG)",
options_type=GnuV2Options,
aliases=("gnu-v2", "cfront", "cplus-dem"),
symbol_table_decorations=True,
priority=290,
)
The scheme as the registry holds it, registered when this package is imported.
STYLES
module-attribute
¶
STYLES = ('auto', 'gnu', 'lucid', 'arm', 'hp', 'edg')
The demangling styles this shares its code with, spelled as c++filt --format= does.
auto is the reference's auto_demangling, which is GNU's reading plus EDG's
parameterised-type prefixes; it is not a search over the other four.
GnuV2Symbol
¶
GnuV2Symbol(
text,
style,
qualified_name,
parameters,
arguments_text,
return_type,
qualifiers,
special,
suffix,
evidence,
)
What one of these names says, spelled and in pieces.
text is the whole of it, and the rest is what the parts-in-order tree is built
from. parameters is None where the name encodes no argument list -- a static data
member, a virtual table -- which is not the same as an empty one.
Source code in src/demangle/schemes/gnuv2/_parser.py
2260 2261 2262 2263 2264 2265 2266 2267 2268 2269 2270 2271 2272 2273 2274 2275 2276 2277 2278 2279 2280 2281 2282 2283 2284 2285 2286 | |
detect
¶
detect(name, style='gnu')
Whether name is a pre-Itanium C++ symbol, decided by reading it.
There is no prefix that says so, so the whole name is parsed and the answer is whether it parsed. Screened first, so that the great majority of symbols cost a substring search and nothing else.
Source code in src/demangle/schemes/gnuv2/__init__.py
214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 | |
parse
¶
parse(
mangled,
builder,
limits=DEFAULT_LIMITS,
options=DEFAULT_OPTIONS,
)
Parse a pre-Itanium C++ name into builder.
Source code in src/demangle/schemes/gnuv2/__init__.py
261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 | |
demangle_gnuv2
¶
demangle_gnuv2(
mangled, style="gnu", params=True, ansi=True
)
Read one pre-Itanium C++ name. Raises DemangleFailure where it is not one.
An unknown style is the caller's mistake rather than the name's, and raises
ValueError.
Source code in src/demangle/schemes/gnuv2/_parser.py
2373 2374 2375 2376 2377 2378 2379 2380 2381 2382 2383 2384 2385 2386 2387 2388 2389 2390 2391 2392 2393 2394 2395 2396 2397 2398 2399 2400 | |
Options¶
demangle.schemes.gnuv2.options
¶
Which of the five pre-Itanium manglings to read, and how much of the name to spell.
The reference is one demangler with a style flag, not five demanglers, and the flag has
to be supplied: AtEnd__13ivRubberGroup is a valid name under every one of them and they
do not all read it the same way. There is no marker in the name to key on, which is why
this is an option rather than something detection could work out.
DMGL_PARAMS and DMGL_ANSI are the reference's other two flags and are carried for the
same reason: they change what comes out, and the vectors in demangle-expected pin both
settings of the first.
DEFAULT_OPTIONS
module-attribute
¶
DEFAULT_OPTIONS = GnuV2Options()
What demangle() uses when a caller names no options: GNU's reading, spelled in full.
GnuV2Options
dataclass
¶
GnuV2Options(style='gnu', params=True, ansi=True)
How to read a pre-Itanium C++ name.
style
class-attribute
instance-attribute
¶
style = 'gnu'
Which compiler wrote it: gnu, lucid, arm, hp, edg, or auto.
gnu is g++ before 3.0 and the default, being the one of the five that a binary in
the wild is most likely to hold. arm is the encoding the Annotated Reference Manual
describes, which cfront and its descendants emit; lucid is Lucid's lcc; hp is
HP aCC; edg is the Edison Design Group front end. auto is the reference's
auto_demangling: GNU's reading plus EDG's parameterised-type prefixes, and not a
search over the other four.
params
class-attribute
instance-attribute
¶
params = True
Spell the argument list, and the qualifiers that follow it.
DMGL_PARAMS. False gives what c++filt -p prints: ivTSolver::AddAlignment rather
than ivTSolver::AddAlignment(unsigned int, ivInteractor *, ivTGlue *).
ansi
class-attribute
instance-attribute
¶
ansi = True
Spell ANSI C++ qualifiers -- const, volatile, __restrict -- on types.
DMGL_ANSI. False drops them, which is what a demangler predating the standard did.
Parser¶
The pre-Itanium C++ demangler, ported from libiberty's cplus-dem.c.
One algorithm, five styles. GNU g++ before 3.0, Lucid's lcc, the ARM/Annotated
Reference Manual encoding that cfront and its descendants emit, HP's aCC, and EDG's
front end all mangle names the same way up to a handful of flag-guarded differences, and
libiberty demangles all five with one body of code under GNU_DEMANGLING,
LUCID_DEMANGLING, ARM_DEMANGLING, HP_DEMANGLING and EDG_DEMANGLING. This is that
code, transcribed.
The reference is GCC 8.3.0's libiberty/cplus-dem.c -- the last release before the
pre-v3 demangler was dropped -- and the transcription is deliberately literal: the
function names are its function names, the control flow is its control flow, and the
quirks are kept rather than tidied. demangle_template returning failure for an empty
argument list, demangle_template_value_parm returning -1 where every caller then
treats -1 as success, do_type clearing the buffer its caller handed it: all of those
are the reference's behaviour, they are all observable in its own test vectors, and a
cleaner port would be a different demangler.
The two structural things worth knowing before reading it:
-
It builds text by prepending. There is no tree.
demangle_classprependsfoo::to what has been built so far, which is howAtEnd__13ivRubberGroupbecomesivRubberGroup::AtEnd(void)with the class arriving after the function name has already been written._Bufbelow is the reference'sstring, with the same prepend/append/truncate operations, because several places index into the buffer (work.temp_start) or cut a character off the end (the extra comma in an ARM template argument list). -
It is stateful across the whole name.
_Workis the reference'swork_stuff: remembered types forTn back-references, the squanglingBandKvectors, the constructor and destructor counters thatdemangle_prefixsets anddemangle_classconsumes, andtemp_start, the offset in the buffer where a class's template arguments began.iterate_demangle_functionsnapshots the whole of it, because GNU mangling is genuinely ambiguous -- there is no token marking where a function name ends and its signature begins, only a run of__that might be either -- so the demangler guesses, and backtracks when the guess does not parse.
Scored against the reference's own vectors: libiberty/testsuite/demangle-expected from
the same tree, every case marked --format=gnu, --format=lucid, --format=arm or
--format=hp, under both settings of DMGL_PARAMS. See tests/conformance/.
Nodes¶
demangle.schemes.gnuv2.nodes
¶
The pre-Itanium C++ tree.
The same parts-in-order shape as the Go, Nim, D and JNI trees: a node is the fragments it was built from, so rendering concatenates and the tree cannot spell a symbol differently from the text path.
The reference builds text by prepending and appending into one buffer, and there is no point pretending otherwise -- so the structure here is sliced out of the finished spelling at boundaries the parser recorded while it ran: where the function's name ended, and exactly what the argument list contributed. Everything else stays as literal text, in the position the demangler wrote it.
Where the slice does not add up -- the recorded name is not found where it should be --
the whole spelling is emitted as one name part rather than a structure that might not
be true. tests/test_gnuv2.py pins how many of libiberty's own vectors take each path.
GnuV2Name
¶
GnuV2Name(parts)
Bases: _GnuV2
The entity's name with its scope: ivTSolver::AddAlignment.
Template arguments belong to the component they qualify, so they are inside this and
not split out: foo::bar<int> is one name.
Source code in src/demangle/schemes/gnuv2/nodes.py
26 27 28 29 30 31 | |
Type
¶
Type(parts)
Bases: _GnuV2
One parameter type, spelled.
Source code in src/demangle/schemes/gnuv2/nodes.py
26 27 28 29 30 31 | |
Parameters
¶
Parameters(parts)
Bases: _GnuV2
The argument list, parentheses included, with each type a Type inside it.
Source code in src/demangle/schemes/gnuv2/nodes.py
26 27 28 29 30 31 | |
Symbol
¶
Symbol(parts)
Bases: _GnuV2
A whole pre-Itanium C++ symbol.
Source code in src/demangle/schemes/gnuv2/nodes.py
26 27 28 29 30 31 | |
build
¶
build(symbol)
Assemble the tree for a parsed GnuV2Symbol.
Falls back to one flat name part whenever the recorded boundaries do not line up
with the spelling, so render() is the spelling in every case.
Source code in src/demangle/schemes/gnuv2/nodes.py
83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 | |
CodeWarrior¶
demangle.schemes.codewarrior
¶
Metrowerks CodeWarrior C++.
The other pre-Itanium C++ mangling, and a scheme of its own rather than a sixth style of
gnuv2 because libiberty never read it: cplus-dem.c has no CodeWarrior flag,
demangle-expected has no vectors for it, and binutils has never demangled one. What
built with it: Nintendo GameCube and Wii titles, Palm OS, BeOS, classic Mac OS, and a
great deal of other embedded work -- which is why the reference is a decompilation
project's tool, encounter/cwdemangle, rather than a compiler vendor's.
It looks like the ARM encoding and is not. A template argument list is written out
literally in the symbol, single_ptr<10CModelData>, so finding where the name ends
means counting brackets before looking for the __; a pointer-to-member carries two
hidden parameters whose spelling says whether the member function is const; and the
type spelling is the reference's own -- const char*, not char const *.
Detection has the same problem gnuv2 has and answers it the same way: a CodeWarrior
symbol is an ordinary C identifier with a __ in it, so detect parses the whole name
rather than testing a prefix, and this scheme is offered last -- after everything with
a marker and after gnuv2, which goes first because a name valid under both should go
to the commoner mangling. A __dt that the gnu style does not know falls through to
this scheme, so __dt__6CActorFv is read here rather than as a function called __dt.
Measured over every corpus in tests/conformance/ and over the system's own C
libraries: see tests/test_codewarrior.py, which pins the count both ways.
PLUGIN
module-attribute
¶
PLUGIN = LanguagePlugin(
name="codewarrior",
node_kinds=("name", "parameters", "symbol", "type"),
detect=detect,
parse=parse,
description="Metrowerks CodeWarrior C++ symbol names",
options_type=CodeWarriorOptions,
aliases=("cw", "metrowerks", "mwcc"),
symbol_table_decorations=True,
priority=300,
)
The scheme as the registry holds it, registered when this package is imported.
CodeWarriorSymbol
¶
CodeWarriorSymbol(
text,
qualified_name,
parameters,
arguments_text,
return_type,
static_variable,
)
What one of these names says, spelled and in pieces.
Source code in src/demangle/schemes/codewarrior/_parser.py
410 411 412 413 414 415 416 417 418 | |
detect
¶
detect(name)
Whether name is a CodeWarrior symbol, decided by reading it.
There is no prefix that says so, so the whole name is parsed and the answer is whether it parsed. Screened first, so that the great majority of symbols cost a substring search and nothing else.
Source code in src/demangle/schemes/codewarrior/__init__.py
127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 | |
parse
¶
parse(
mangled,
builder,
limits=DEFAULT_LIMITS,
options=DEFAULT_OPTIONS,
)
Parse a CodeWarrior symbol into builder.
Source code in src/demangle/schemes/codewarrior/__init__.py
166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 | |
demangle_codewarrior
¶
demangle_codewarrior(
mangled, omit_empty_parameters=True, mw_extensions=False
)
Read one CodeWarrior symbol. Raises DemangleFailure where it is not one.
Source code in src/demangle/schemes/codewarrior/_parser.py
424 425 426 427 428 429 430 431 432 433 | |
Options¶
demangle.schemes.codewarrior.options
¶
How much of a CodeWarrior name to spell, and whether to read Metrowerks' extensions.
Both are the reference's own options, kept because they change what comes out and the
vectors in cwdemangle's test module pin both settings of each.
DEFAULT_OPTIONS
module-attribute
¶
DEFAULT_OPTIONS = CodeWarriorOptions()
What demangle() uses when a caller names no options: the reference's own defaults.
CodeWarriorOptions
dataclass
¶
CodeWarriorOptions(
omit_empty_parameters=True, mw_extensions=False
)
How to spell a CodeWarrior name.
omit_empty_parameters
class-attribute
instance-attribute
¶
omit_empty_parameters = True
Write () for a function that takes none, rather than (void).
omit_empty_parameters. True is the reference's default and what its CLI prints.
mw_extensions
class-attribute
instance-attribute
¶
mw_extensions = False
Read Metrowerks' extension types, __int128 and __vec2x32float__.
mw_extensions. Off by default, and that is the reference's choice rather than a
conservative reading of it: the two are spelled 1 and 2, which collide with a
template argument literal, so a name carrying either cannot always be told from one
carrying a small integer. Turning this on is a caller saying they know which.
Parser¶
The Metrowerks CodeWarrior demangler, ported from encounter/cwdemangle.
CodeWarrior is the other pre-Itanium C++ mangling, and the reason it is a scheme of its
own rather than a sixth style of gnuv2 is that libiberty never read it: cplus-dem.c
has no CodeWarrior flag, demangle-expected has no vectors for it, and binutils has
never demangled one. It is what Nintendo GameCube and Wii titles, and a great deal of
other 1990s and 2000s embedded work, were built with -- which is why the reference is a
decompilation project's tool rather than a compiler vendor's.
The reference is encounter/cwdemangle, dedicated to the public domain under CC0-1.0.
This is a transcription of its demangle, with its function names kept.
The encoding looks like ARM's and is not:
- a template argument list is written literally,
single_ptr<10CModelData>, brackets and commas and all, rather than being encoded -- so finding where the name ends and the signature begins means counting<and>before looking for the__; Q2introduces a qualified name whose component count is a single digit, and each component is length-prefixed and may itself carry a template argument list;- a pointer-to-member is
M<class>Ffollowed by two hidden parameters,PCvPvorPCvPCv, and which of the two decides whether the member function isconst; - the spelling is the reference's own:
const char*with the*against the type, andvoid (*)(const char*, void*)built from apre/postpair around the declarator.
Two options are the reference's: omit_empty_parameters, which is on by default and
turns (void) into (), and mw_extensions, which is off by default because the
Metrowerks extension types __int128 and __vec2x32float__ are spelled 1 and 2
and collide with template argument literals -- the reference says so, and this keeps its
default rather than guessing.
Nodes¶
demangle.schemes.codewarrior.nodes
¶
The CodeWarrior tree.
The same parts-in-order shape as the Go, Nim, D, JNI and pre-Itanium C++ trees: a node is the fragments it was built from, so rendering concatenates and the tree cannot spell a symbol differently from the text path.
The reference builds one string out of a pre/post pair, so the structure here is
sliced out of the finished spelling at the boundaries the parser recorded -- where the
qualified name ended, and exactly what the argument list contributed. Where the slice
does not add up, the whole spelling becomes one name part rather than a structure that
might not be true.
CodeWarriorName
¶
CodeWarriorName(parts)
Bases: _CodeWarrior
The entity's name with its scope: rstl::basic_string<wchar_t>::mNull.
Source code in src/demangle/schemes/codewarrior/nodes.py
22 23 24 25 26 27 | |
Type
¶
Type(parts)
Bases: _CodeWarrior
One parameter type, spelled.
Source code in src/demangle/schemes/codewarrior/nodes.py
22 23 24 25 26 27 | |
Parameters
¶
Parameters(parts)
Bases: _CodeWarrior
The argument list, with each type a Type inside it.
Source code in src/demangle/schemes/codewarrior/nodes.py
22 23 24 25 26 27 | |
Symbol
¶
Symbol(parts)
Bases: _CodeWarrior
A whole CodeWarrior symbol.
Source code in src/demangle/schemes/codewarrior/nodes.py
22 23 24 25 26 27 | |
build
¶
build(symbol)
Assemble the tree for a parsed CodeWarriorSymbol.
Source code in src/demangle/schemes/codewarrior/nodes.py
75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 | |
Delphi / C++Builder¶
demangle.schemes.delphi
¶
Borland/Embarcadero Delphi and C++Builder symbol names.
Delphi packages (BPLs) and C++Builder objects use the same mangling: @Unit@Class@Method
then $ and a type encoding. Free Pascal's $-delimited scheme is a different one and
is not read here.
There is no Delphi compiler on the platforms this package is developed on, so the grammar
is a transcription of Embarcadero's own unmangler -- unmangle.c in the C++Builder RTL,
the same code tdump -um runs -- via the comments and control flow preserved in that
file. Spelling is what that unmangler prints, including the C++ :: qualifier, because
that is the output a Delphi-built PE's exports are compared against by the toolchain.
tests/conformance/delphi-tdump.txt is a dump of the real tdump.exe -q -um over the
export tables of real BPLs and C++Builder DLLs; its expected column is what that
unmangler printed, and every readable entry is replayed. delphi-real-world.txt is a
per-kind sample of the same dump. Independently, a reading must consume the whole symbol
-- a parse that cannot account for the bytes is refused.
PLUGIN
module-attribute
¶
PLUGIN = LanguagePlugin(
name="delphi",
node_kinds=("name", "parameters", "symbol"),
detect=detect,
parse=parse,
description="Borland/Embarcadero Delphi and C++Builder symbol mangling",
aliases=("borland", "bcc", "c++builder", "embarcadero"),
first_characters="@",
priority=35,
)
The scheme as the registry holds it, registered when this package is imported.
DelphiSymbol
¶
DelphiSymbol(raw, text, kind)
One parsed Delphi/C++Builder symbol.
Source code in src/demangle/schemes/delphi/_parser.py
156 157 158 159 | |
detect
¶
detect(name)
Whether name is a Delphi/C++Builder export, not MSVC @name@N.
@__swiftmacro_ is Swift's macro mangling and is left for that scheme: the two
share a first character, and guessing would rewrite a Swift name.
A leading @@ without a $ encoding is __linkproc__ in the unmangler. Real BPL
exports of that shape are a single identifier (@@InitExe). @@bug@@x is not, and
is left unclaimed so a broken plugin's fixture is not rewritten. A qualified data
name such as @System@Var has no $ and is still claimed.
@LOCAL@ and @GUARD@ are left for the CodeWarrior scheme, which is where they come
from: Wii CodeWarrior spells a function-local static @LOCAL@<function>@<variable>
and its guard @GUARD@.... Both parse here as a unit called LOCAL or GUARD,
which is a name Borland never wrote.
A ? is refused for the same reason and a stronger one: no Borland production
writes one -- there is none in any of the 11,363 recorded exports -- and it is the
MSVC marker, so a name carrying one that reaches this scheme is a piece of somebody
else's. This scheme copies characters through rather than checking an alphabet, so
it would read the @?0??define_lambda@@YAHXZ@QBE@XZ left over when demangle_text
tokenises an MSVC lambda name at its angle brackets as a Delphi declaration built
out of half an MSVC symbol. Asked for by language it is still read; what this
decides is whether to claim a name nobody said was Delphi's.
For the same reason the alphabet is checked as a whole rather than one forbidden
character at a time. This scheme declares @ as its first character and is offered
every symbol that starts with one, so it must not claim what no Borland tool wrote:
a demangled Swift type such as @convention(block) (Swift.Int) -> Swift.UInt, whose
@ markers would read as this grammar's qualifier separator, or @feat.00 and
@comp.id, which every COFF object from MSVC and clang-cl carries. Reading either
yields a name that is neither the symbol nor a declaration, the one answer this
package treats as worse than leaving a name alone.
_SYMBOL_CHARACTERS is what the 11,363 readable recorded exports are made of and
nothing else. See the module docstring for why that is the whole alphabet: a name is
@-delimited qualifiers -- Pascal identifiers -- with $, %, & and # as the
encoding's own markers. A . is not among them, which is what turns @feat.00
away.
Source code in src/demangle/schemes/delphi/_parser.py
830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 | |
parse_delphi_symbol
¶
parse_delphi_symbol(name, limits=DEFAULT_LIMITS)
Parse name, returning a DelphiSymbol, or raise DemangleFailure.
A name that exceeds limits raises LimitExceeded instead.
Source code in src/demangle/schemes/delphi/_parser.py
805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 | |
parse
¶
parse(
mangled, builder, limits=DEFAULT_LIMITS, options=None
)
Parse a Delphi/C++Builder symbol into builder.
Source code in src/demangle/schemes/delphi/__init__.py
40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 | |
Parser¶
Reading Borland/Embarcadero mangled names.
Transcribed from unmangle.c in the C++Builder RTL -- the unmangler TDUMP, the linker
and the debugger all run -- as ported with its comments intact. The control flow is that
file's, not a reconstruction from examples.
A name is @-delimited qualifiers, then optionally $ and a type. Calling conventions
are extra q letters after the function marker: qqr is __fastcall (Delphi's
register), qqs is __stdcall, qqc is __cdecl. Types are single letters, or a
decimal length followed by an @-qualified name. tN is a back-reference to an earlier
parameter, 1-based in base 36.
Microsoft's 32-bit __fastcall C decoration is @name@bytes with a decimal count and
no $. It is refused here, because it is not this scheme.
Nodes¶
demangle.schemes.delphi.nodes
¶
The Delphi/C++Builder symbol tree.
Left-to-right like Go and Free Pascal: a node is its fragments in order, rendering concatenates, and the tree cannot spell a symbol differently from the text path.
What a caller wants is the unit, the class, and the encoded parameters -- none of which
survives splitting the spelling, because a length-prefixed type can itself hold @ and
% template markup.
DelphiName
¶
DelphiName(parts)
Bases: _Delphi
A unit, class, or routine name.
Source code in src/demangle/schemes/delphi/nodes.py
19 20 21 22 23 24 | |
Parameters
¶
Parameters(parts)
Bases: _Delphi
The parameter list, as the mangling recorded it.
Source code in src/demangle/schemes/delphi/nodes.py
19 20 21 22 23 24 | |
Symbol
¶
Symbol(parts, delphi_kind)
Bases: _Delphi
A whole Delphi/C++Builder symbol.
delphi_kind is what the mangling said this is -- function, constructor,
data, and so on -- which the spelling alone does not always say.
Source code in src/demangle/schemes/delphi/nodes.py
67 68 69 | |
build
¶
build(symbol)
Assemble the tree for a parsed DelphiSymbol.
Source code in src/demangle/schemes/delphi/nodes.py
72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 | |
Ada / GNAT¶
demangle.schemes.ada
¶
Ada, as GNAT encodes it.
The last of the pre-Itanium formats libiberty still carries, reached as
c++filt --format=gnat and dropped from nothing: when the GNU v2, lucid, ARM and HP
styles were removed from the default, gnat stayed. Narrow but concentrated -- avionics,
rail, defence -- and the reference is ada_demangle in libiberty/cplus-dem.c, with
GCC's own exp_dbug.ads documenting the encoding normatively.
Detection is the whole difficulty, and it is settled by measurement rather than
argument. An Ada symbol carries no types and no marker: yz__qrs is a package and a
subprogram, and it is also exactly what a C program writes. Parsing the name and
claiming whatever parses -- what a demangler for a scheme with a marker can afford --
reads 797 names from the other schemes' corpora and 6,764 real symbols from this
machine's own libraries as Ada. That is not detection, it is a coin toss with a
confident voice.
So a name is claimed only when it carries something GNAT wrote and a C compiler would
not: the _ada_ prefix, an O-operator, a TK task suffix, a P/N protected
subprogram, a stream S[RWIO], a controlled D[FA], an X body-nested marker, a
___elabb-style special name, a _B/_E entry body, or an overload number -- and the
whole name has to be accounted for, because several of the reference's suffixes stop
reading and abandon the rest. Measured under that rule: 0 claims over every other
checked-in corpus, and 0 over 339,117 symbols from this machine's libraries.
The cost is that a name with no such marker -- yz__qrs, and four of the 34
reference vectors -- is not auto-detected (x_E is not counted: the reference itself
declines it). It demangles when a caller says language="ada", which is the same
bargain the Go scheme makes and for the same reason: failing to claim a name returns it
unchanged, which is what an unreadable name does anyway, while claiming someone else's
rewrites it into a plausible lie. tests/test_ada.py pins both numbers, so neither
can drift quietly.
PLUGIN
module-attribute
¶
PLUGIN = LanguagePlugin(
name="ada",
node_kinds=("attribute", "component", "symbol"),
detect=detect,
parse=parse,
description="Ada symbol names as GNAT encodes them (c++filt --format=gnat)",
aliases=("gnat",),
symbol_table_decorations=True,
priority=280,
)
The scheme as the registry holds it, registered when this package is imported.
AdaSymbol
¶
AdaSymbol(text, parts, evidence, unread='')
One demangled GNAT name.
text is the whole spelling. parts are the dot-separated components in order,
which is what the tree is built from: an Ada name is a path and nothing else, and
splitting text on . would be wrong the moment a component is .":=" or the
entity carries a .Finalize.
Source code in src/demangle/schemes/ada/_parser.py
99 100 101 102 103 104 105 106 107 | |
demangle_ada
¶
demangle_ada(mangled)
Read a GNAT-encoded name, or raise DemangleFailure.
A transcription of ada_demangle, following its control flow rather than
re-deriving the grammar: the loop, the ordered suffix tests, and every goto
unknown. Comments name the reference's own.
Source code in src/demangle/schemes/ada/_parser.py
121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 | |
detect
¶
detect(name)
Whether name is a GNAT symbol, decided by reading it and by what it carries.
Two conditions, and the second is the one that matters. The name has to parse, and it has to hold at least one encoding GNAT writes and a C compiler does not -- see the module docstring for why, and for what the alternative measured at.
Source code in src/demangle/schemes/ada/__init__.py
66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 | |
parse
¶
parse(
mangled, builder, limits=DEFAULT_LIMITS, options=None
)
Parse a GNAT symbol into builder.
No second detection test. parse is reached either because detect claimed the
name or because a caller named the language, and in the second case the caller has
said what it is -- which is how yz__qrs, correctly not auto-detected, is still
readable on request.
Source code in src/demangle/schemes/ada/__init__.py
108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 | |
Nodes¶
demangle.schemes.ada.nodes
¶
The Ada symbol tree.
The same parts-in-order shape as the Go, Nim, D and JNI trees: a node is the fragments it was built from, so rendering concatenates and the tree cannot spell a symbol differently from the text path.
What the structure is for is the one question the spelling cannot answer. An Ada name is
a dotted path, but text.split(".") is wrong the moment a component is an operator --
oper."+", and system.finalization_root.":=" has a . inside a component -- or the
entity carries a .Finalize. The parts here were separated while reading, so they are
the components the parser actually found.
Component
¶
Component(parts)
Bases: _Ada
One element of the path: a unit, a subprogram, or a quoted operator name.
Source code in src/demangle/schemes/ada/nodes.py
22 23 24 25 26 27 | |
Attribute
¶
Attribute(parts)
Bases: _Ada
What the entity is, where GNAT encoded that rather than leaving it implicit.
'Size, 'Elab_Body, 'Read, .Finalize. Written by the compiler rather than the
programmer, and carrying its own leading punctuation because the reference does --
an attribute joins with ' and a controlled-type operation with ..
Source code in src/demangle/schemes/ada/nodes.py
22 23 24 25 26 27 | |
Symbol
¶
Symbol(parts)
Bases: _Ada
A whole GNAT symbol.
Source code in src/demangle/schemes/ada/nodes.py
22 23 24 25 26 27 | |
build
¶
build(symbol)
Assemble the tree for a parsed AdaSymbol.
Source code in src/demangle/schemes/ada/nodes.py
77 78 79 80 81 82 83 84 85 86 87 | |
JNI¶
demangle.schemes.jni
¶
JNI native method names.
A Java method declared native is called through a C function whose name encodes the
method's class, its name, and -- where it is overloaded -- its parameter types. Android
apps ship these by the thousand, and the usual way to read one is by eye.
The encoding is in the JNI specification's Design Overview under "Resolving Native
Method Names", which is unusually good fortune: every other scheme here had to be
transcribed from a reference implementation, and this one is written down normatively.
See _parser.py for what it says and for the one thing it does not preserve.
Reference: https://docs.oracle.com/en/java/javase/17/docs/specs/jni/design.html
PLUGIN
module-attribute
¶
PLUGIN = LanguagePlugin(
name="jni",
node_kinds=("name", "parameters", "path", "symbol"),
detect=detect,
parse=parse,
description="JNI native method names (Java_pkg_Class_method)",
aliases=("java",),
first_characters="J",
priority=15,
)
The scheme as the registry holds it, registered when this package is imported.
JniSymbol
¶
JniSymbol(raw, declaring, method, parameters)
One parsed JNI symbol: its dotted path, and its parameter types where it has any.
Source code in src/demangle/schemes/jni/_parser.py
73 74 75 76 77 78 79 80 81 | |
descriptor_types
¶
descriptor_types(text)
The parameter types of a JVM descriptor's argument list, spelled as source.
text is the unescaped argument run -- no brackets and no return type, which is
what JNI mangles. Ljava/lang/String;I[B is java.lang.String, int, byte[].
Source code in src/demangle/schemes/jni/_parser.py
124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 | |
parse_jni_symbol
¶
parse_jni_symbol(name, limits=DEFAULT_LIMITS)
Parse name, returning a JniSymbol, or raise DemangleFailure.
Source code in src/demangle/schemes/jni/_parser.py
160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 | |
detect
¶
detect(name)
Whether name is a JNI native method symbol.
The prefix is distinctive but not proof: a C function may be called Java_helper,
and claiming one would rewrite a name this library was not sure about. So the name
must also decode: a declaring class as well as a method -- one separator at least,
which Java_helper does not have -- and, where a signature is present, one that is
a valid JVM descriptor. Java_C_m__Q is refused because Q names no type, and
Java_C_m__ because the separator promises a signature that is not there.
A malformed escape is not a refusal, because it cannot be told from the thing it
would be malformed as: _0abc is _0 and three hex digits, and it is also a /
followed by 0abc. The mangling gives / no escape of its own, so the second
reading is always available and the first is never certain.
Source code in src/demangle/schemes/jni/__init__.py
26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 | |
parse
¶
parse(
mangled, builder, limits=DEFAULT_LIMITS, options=None
)
Parse a JNI symbol into builder.
Source code in src/demangle/schemes/jni/__init__.py
61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 | |
Nodes¶
demangle.schemes.jni.nodes
¶
The JNI symbol tree.
The same parts-in-order shape as the Go, Nim and D trees: a node is the fragments it was built from, so rendering concatenates and the tree cannot spell a symbol differently from the text path.
What the structure is for is the one question splitting the spelling cannot answer: a
parameter type may itself contain a . -- java.lang.String is three of them -- so
text.split(", ") on the argument list is right and split(".") on the path is not.
The parameters node hands the types over already separated.
Path
¶
Path(parts)
Bases: _Jni
The package and class the method was declared in, as far as the name says.
Both are joined with the same character the mangling uses for a package separator, so the two cannot be told apart; this is the whole of what precedes the method.
Source code in src/demangle/schemes/jni/nodes.py
21 22 23 24 25 26 | |
JniName
¶
JniName(parts)
Bases: _Jni
The method's own name, with its escapes undone.
Source code in src/demangle/schemes/jni/nodes.py
21 22 23 24 25 26 | |
Parameters
¶
Parameters(parts)
Bases: _Jni
The parameter types, present only where the method is overloaded.
Source code in src/demangle/schemes/jni/nodes.py
21 22 23 24 25 26 | |
Symbol
¶
Symbol(parts)
Bases: _Jni
A whole JNI native method symbol.
Source code in src/demangle/schemes/jni/nodes.py
21 22 23 24 25 26 | |
build
¶
build(symbol)
Assemble the tree for a parsed JniSymbol.
Source code in src/demangle/schemes/jni/nodes.py
78 79 80 81 82 83 84 85 86 87 88 89 90 91 | |
Objective-C¶
demangle.schemes.objc
¶
Objective-C symbol names.
Objective-C is barely mangled, and what mangling there is comes from the compiler rather
than from the language: clang and GCC each write the runtime's data structures into
symbols of their own devising, and only the GNU-family method form is a mangling in the
sense the other schemes here use the word. So the rules are transcribed from clang's
lib/AST/Mangle.cpp, lib/CodeGen/CGObjCMac.cpp and lib/CodeGen/CGObjCGNU.cpp, and
from the symbols GCC's own front end left in the shipped libobjc.a.
Four families are read:
-[NSString stringWithFormat:] the Apple runtimes, and what every crash log shows
_i_NSString__stringWithFormat_ the GNU family: GCC's runtime and GNUstep's
_OBJC_CLASS_$_NSString Apple's non-fragile ABI
.objc_class_name_NSString Apple's fragile ABI, and the GNU runtimes' own
Clang's block invocation functions (___<len><method>_block_invoke,
__block_literal_global, __block_descriptor) are also read. The length prefix before
a method is verified against the method's text; a mismatched count is refused rather
than guessed at.
Correctness rests on re-assembly, as it does for Go, Nim and Free Pascal, and on
agreement with what the compiler emitted for declarations this package wrote: 445 of 445
symbols clang produced for Objective-C written the way Objective-C is written, across
the macOS, fragile and GNUstep ABIs. Over a corpus built to put underscores inside class
names, category names and selectors -- the collision clang's own source warns about --
396 of 422, and Symbol.ambiguous marks every name where more than one reading exists.
The GNU-family method mangling is not injective. clang says so where it writes it:
"This is the mangling we've always used on the GNU runtimes, but it has obvious
collisions in the face of underscores within class names, category names, and
selectors." A : and a separator are both written _, so _i_A_B_c is -[A(B) c] and
-[A_B c] and -[A(B_c) ] alike. Every reading that re-mangles to the symbol is found;
the one preferred is the one needing no category, because a method outside a category
leaves the field empty and its two separators fall together into a visible doubled
underscore.
Two GNUstep forms are refused rather than guessed at. .objc_category_FooBar joins
the class and category names with no separator at all, and nothing can say where one
ends. .objc_selector_<name>_<types> is split at the rightmost underscore whose left
half is a selector, which is right unless the type encoding names a struct.
PLUGIN
module-attribute
¶
PLUGIN = LanguagePlugin(
name="objc",
node_kinds=("name", "path", "symbol"),
detect=detect,
parse=parse,
description="Objective-C method, class and runtime symbol names",
aliases=("objective-c", "objectivec"),
priority=30,
first_characters="-+_.lL",
)
The scheme as the registry holds it, registered when this package is imported.
DemangleFailure
¶
Bases: Exception
Raised when a name is not one this reads.
ObjcSymbol
¶
ObjcSymbol(
raw,
text,
kind,
*,
runtime="",
class_name=None,
category=None,
selector=None,
ivar=None,
block=None,
ambiguous=False,
label="",
)
One Objective-C symbol.
text is the readable spelling. The remaining fields are what the mangling said,
and raw is the symbol exactly as it came in, so a caller can rejoin. label is
the words a runtime data symbol's spelling opens with, Objective-C class say,
and empty for a method or a block.
Source code in src/demangle/schemes/objc/_parser.py
67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 | |
detect
¶
detect(name)
Whether name is one this reads, and distinctive enough to claim unasked.
The screen is only there to keep the parse off every symbol in a binary; what
actually claims a name is the parse succeeding. A form that says objc somewhere in
it is worth trying, and so are the two that do not: -[/+[, and the _i_/_c_
method mangling.
The markers are tested one by one rather than through any(...) over
_SCREEN_MARKERS. This plugin declares _ among its first characters, so it is
offered every underscore-prefixed symbol in a binary -- 85% of the benchmark corpus
-- and the generator would cost six interpreter frames per name to run five
membership tests that are each a single C-level scan. Written out it is the same
five scans in the same short-circuiting order, and no frames at all: 71,778 calls
fewer over the cold corpus. _SCREEN_MARKERS stays as the documentation of what the
screen is.
Source code in src/demangle/schemes/objc/_parser.py
744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 | |
gnu_method_readings
¶
gnu_method_readings(name, limit=None)
Every (class, category, selector) that re-mangles to name, best first.
The mangling writes _ for both a separator and a :, and does nothing to mark
which is which, so a symbol can have several readings and all of them are equally
well-formed. Every split is tried and only those that re-mangle to the input
survive; what orders them is the one structural signal the mangling does carry.
A method that is not in a category has an empty category field, and the two separators around it fall together into a doubled underscore. That is visible, and it is the common case, so a reading that needs no category is preferred over one that does. Within each group the leftmost split wins, which is the shortest class name -- the reading a class whose name holds no underscore produces.
limit stops the search once that many readings are in hand. The caller that wants
a spelling passes 2, because all it needs is the best reading and whether a second
exists; enumerating the rest is work nobody reads.
Measured against the compiler, over symbols clang emitted for declarations this
package generated: 131 of 131 correct when class, category and selector are ordinary
identifiers without underscores, which is how Objective-C is conventionally written;
96 of 122 on a corpus built to put underscores in all three, where 65 of the 122 have
more than one reading at all. ObjcSymbol.ambiguous says which those are.
Source code in src/demangle/schemes/objc/_parser.py
186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 | |
mangle_gnu_method
¶
mangle_gnu_method(
is_class_method, class_name, category, selector
)
The GNU-family symbol for a method, from clang's mangleObjCMethodName.
Written out because it is what makes reading one checkable: a reading is only accepted when re-mangling it reproduces the symbol.
Source code in src/demangle/schemes/objc/_parser.py
139 140 141 142 143 144 145 146 147 148 | |
parse_objc_symbol
¶
parse_objc_symbol(name)
Parse name, returning an ObjcSymbol, or raise DemangleFailure.
A Mach-O symbol table carries decoration the assembler added and the compiler did
not write: one leading underscore on every symbol, and l or L before that on a
private label. Each is stripped in turn and the stripped form is tried first.
Trying it first rather than second is what gets ___cfunc_block_invoke right. Both
readings parse: __ is the block prefix, so the outer name is either _cfunc with
no Mach-O underscore or cfunc with one. The second is the answer on Darwin, where
these symbols come from, and on ELF the stripped form does not begin with __ at
all and so does not parse -- which leaves the unstripped reading, correctly. A C
function genuinely called _cfunc in an ELF object is the one case the two are
indistinguishable, and it reads as cfunc.
Source code in src/demangle/schemes/objc/_parser.py
670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 | |
parse
¶
parse(
mangled, builder, limits=DEFAULT_LIMITS, options=None
)
Parse an Objective-C symbol into builder.
Source code in src/demangle/schemes/objc/__init__.py
81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 | |
Parser¶
Objective-C symbol names, as the compiler writes them.
Objective-C is barely mangled, and what mangling there is comes from clang rather than
from a language specification. So the rules here are transcribed from clang's own
sources -- lib/AST/Mangle.cpp for method and block names, lib/CodeGen/CGObjCMac.cpp
for the Apple runtime's data symbols and lib/CodeGen/CGObjCGNU.cpp for the GNUstep
runtime's -- and correctness rests on re-assembly, as it does for Go, Nim and Free
Pascal: the parts this splits out, rejoined with the compiler's own separators, must
reproduce the symbol exactly.
Three runtimes emit three different sets of names and this reads all three:
-[NSString stringWithFormat:] Apple, and the readable form everywhere
_i_NSString__stringWithFormat_ GNUstep and the GCC runtime
_OBJC_CLASS_$_NSString Apple, non-fragile ABI
_OBJC_CLASS_NSString Apple fragile ABI, and GNUstep
.objc_class_name_NSString Apple fragile ABI
The GNU-family method mangling is not injective, and clang says so where it writes
it: "This is the mangling we've always used on the GNU runtimes, but it has obvious
collisions in the face of underscores within class names, category names, and
selectors." _i_A_B_c is -[A(B) c] if A is the class, and -[A_B c]... is
impossible to tell apart from it. This reads the leftmost split that re-mangles, and
Method.ambiguous says when another reading exists.
decode_type_encoding
¶
decode_type_encoding(text)
An Objective-C type encoding, with the GNU runtime's substitutions undone.
Source code in src/demangle/schemes/objc/_parser.py
500 501 502 503 504 | |
Nodes¶
demangle.schemes.objc.nodes
¶
The Objective-C symbol tree.
The same parts-in-order shape as the other schemes: a node is the fragments it was built from, so rendering concatenates and the tree cannot spell a symbol differently from the text path.
What a caller wants from one of these is the class, the category and the selector, which
is exactly what splitting -[NSString(Extra) stringWithFormat:] back out of a string
gets wrong -- a category name can hold a bracket in no runtime, but a selector holds
colons and spaces, and a block's parent is a whole method with brackets of its own.
ClassName
¶
ClassName(parts)
Bases: _Objc
The class, or the protocol, the symbol belongs to.
Source code in src/demangle/schemes/objc/nodes.py
21 22 23 24 25 26 | |
Category
¶
Category(parts)
Bases: _Objc
The category, when the symbol names one.
Source code in src/demangle/schemes/objc/nodes.py
21 22 23 24 25 26 | |
Selector
¶
Selector(parts)
Bases: _Objc
The selector, colons and all.
Source code in src/demangle/schemes/objc/nodes.py
21 22 23 24 25 26 | |
Symbol
¶
Symbol(parts, objc_kind, runtime='', ambiguous=False)
Bases: _Objc
A whole Objective-C symbol.
objc_kind is what the mangling said this is -- instance method, class method,
class, category, ivar, selector, block, reference, encoding or
label -- which the spelling alone does not always say.
runtime is which family wrote it: apple, gnu, or empty where the form is
shared. ambiguous is set when the mangling admits more than one reading and this
is the preferred one rather than the only one.
Source code in src/demangle/schemes/objc/nodes.py
82 83 84 85 86 | |
build
¶
build(symbol)
Assemble the tree for a parsed ObjcSymbol.
The pieces are placed by searching the spelling for them rather than by rebuilding it, so the tree renders to the same string the text path produces however the spelling is worded. A piece that is not in the spelling -- a block's class, say, which is inside the parent it names -- simply is not a node.
Source code in src/demangle/schemes/objc/nodes.py
89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 | |