cpp_demangle-0.4.0/.cargo_vcs_info.json0000644000000001360000000000100134530ustar { "git": { "sha1": "0b7b54575456614e7f31958bc3d9ce0d31728085" }, "path_in_vcs": "" }cpp_demangle-0.4.0/.coveralls.yml000064400000000000000000000000001046102023000150240ustar 00000000000000cpp_demangle-0.4.0/.github/dependabot.yml000064400000000000000000000001771046102023000164400ustar 00000000000000version: 2 updates: - package-ecosystem: cargo directory: "/" schedule: interval: daily open-pull-requests-limit: 10 cpp_demangle-0.4.0/.github/workflows/ci.yml000064400000000000000000000022151046102023000167560ustar 00000000000000name: ci on: [push, pull_request] env: CARGO_TERM_COLOR: always jobs: debug: runs-on: ubuntu-18.04 steps: - uses: actions/checkout@v2 - name: Build run: cargo build --verbose - name: Run tests run: cargo test --verbose release: runs-on: ubuntu-18.04 steps: - uses: actions/checkout@v2 - name: Build run: cargo build --verbose --release - name: Run tests run: cargo test --verbose --release debug-nostd: runs-on: ubuntu-18.04 steps: - uses: actions/checkout@v2 - name: Build run: cargo build --verbose --no-default-features --features alloc - name: Run tests run: cargo test --verbose --no-default-features --features alloc release-nostd: runs-on: ubuntu-18.04 steps: - uses: actions/checkout@v2 - name: Build run: cargo build --verbose --release --no-default-features --features alloc - name: Run tests run: cargo test --verbose --release --no-default-features --features alloc c_api: runs-on: ubuntu-18.04 steps: - uses: actions/checkout@v2 - name: Run c_api test run: cd c_api && ./run_test.sh cpp_demangle-0.4.0/.gitignore000064400000000000000000000000571046102023000142350ustar 00000000000000target Cargo.lock *.rs.bk out tests/libxul.log cpp_demangle-0.4.0/.travis.yml000064400000000000000000000015271046102023000143610ustar 00000000000000sudo: false language: rust matrix: include: - rust: nightly env: JOB=fuzz script: cargo build --features fuzz - rust: nightly env: JOB=test-nostd script: - cargo test --no-default-features - cargo test --no-default-features --features alloc - rust: stable env: JOB=doc script: cargo doc - rust: stable env: JOB=stable-test script: - cargo build - cargo test - rust: beta env: JOB=beta-test script: - cargo build - cargo test - rust: nightly env: JOB=nightly-test script: - cargo build - cargo test - rust: stable env: JOB=stable-bench script: cargo bench - rust: stable env: JOB=c_api script: - cd c_api && cargo build - ./run_test.sh cpp_demangle-0.4.0/CHANGELOG.md000064400000000000000000000234001046102023000140530ustar 00000000000000# Unreleased Released YYYY/MM/DD ## Added * TODO (or remove section if none) ## Changed * TODO (or remove section if none) ## Deprecated * TODO (or remove section if none) ## Removed * TODO (or remove section if none) ## Fixed * TODO (or remove section if none) ## Security * TODO (or remove section if none) -------------------------------------------------------------------------------- # 0.4.0 Released 2022/10/20 ## Removed * The deprecated nightly and cppfilt features are gone. ## Changed * If no-default-features is used the alloc feature must be explicitly specified. -------------------------------------------------------------------------------- # 0.3.6 Released 2022/10/20 ## Added * The non-deprecated versions of noexcept are supported. #273 * Most of the subobject production that clang uses is supported. #273 ## Changed * Rust 2018 is now used. #251 * cppfilt now uses clap 4.0. #271 ## Fixed * no_std works. #251 * Inheriting constructors no longer produce substitutable values. #272 -------------------------------------------------------------------------------- # 0.3.5 Released 2021/12/02 ## Changed * The LLVM mangling for vector types with a dimension expression is now supported. The libiberty mangling which appears to be unused by gcc has been removed. -------------------------------------------------------------------------------- # 0.3.4 Released 2021/11/21 ## Added * `DemangleOptions` now has a `hide_expression_literal_types` method that can make it easier to match user-provided template instance names. #230 -------------------------------------------------------------------------------- # 0.3.3 Released 2021/7/8 ## Added * The builtin `char8_t` type is now recognized #224 ## Changed * `glob` is no longer a build time dependency #220 -------------------------------------------------------------------------------- # 0.3.2 Released 2020/11/27 ## Added * `ParseOptions` is introduced, with new API variants `Symbol::new_with_options` and `Symbol::with_tail_and_options`. The existing APIs use the default parsing options. * Recursion limits are now configurable via `ParseOptions` and `DemangleOptions`. * Transaction clone symbols are supported #217 ## Changed * The default parsing recursion limit is now 96 (up from 64). The value was chosen to avoid pathological symbols overflowing the stack of a debug build. Users may be able to safely raise the limits substantially depending on their expected workload and tolerance for crashes. -------------------------------------------------------------------------------- # 0.3.1 Released 2020/10/09 ## Added * Java Resource symbols are now supported #200 ## Fixed * C++ reference collapsing rules are honored. * Misc style fixes. ## Changed * DemangleOptions is now repr(C) -------------------------------------------------------------------------------- # 0.3.0 Released 2020/06/11 ## Changed * The DemangleOptions API has changed to be more future-proof and the DemangleNodeType enum now has an __NonExhaustive variant to discourage pattern matching without a `_ => ()` arm. -------------------------------------------------------------------------------- # 0.2.17 Released 2020/06/09 ## Added * Return types can now be elided from demangled symbols via DemangleOptions::no_return_typ. #202 * A vtable marker is now emitted for semantic consumers. -------------------------------------------------------------------------------- # 0.2.16 Released 2020/05/13 ## Added * Block invocation symbols. #197 * The spaceship operator <=>. #198 -------------------------------------------------------------------------------- # 0.2.15 Released 2020/04/24 ## Added * A C API to cpp_demangle is now available. #191 * Additional AST markers are emitted for semantic consumers. #189 ## Fixed * Multiple clone suffixes are now supported. #194 -------------------------------------------------------------------------------- # 0.2.14 Released 2019/11/15 ## Fixed * Certain symbols can have cyclic back references, or at least very deep stacks of back references. Many of those symbols are valid! But as a practical implementation to avoid stack overflows and infinite loops, we now place a limit on the depth of back references we will follow. This is similar to the parse limit that we already had, but for a different phase of the demangling. [#186](https://github.com/gimli-rs/cpp_demangle/pull/186) -------------------------------------------------------------------------------- # 0.2.13 Released 2019/07/30 ## Fixed * Fix parsing of outdated `sr` forms that prevented parsing other symbols. See #173 for details. * Ensures a space is printed before a `&` or `&&` reference qualifier. #176 * Fixed placement of parentheses in symbols with function pointer arguments that have `const` qualifiers. #175 -------------------------------------------------------------------------------- # 0.2.12 Released 2018/08/09 ## Fixed * *Actually* fixed builds using `no-default-features = true` to not accidentally enable `no_std` mode, which requires nightly rust, and break builds on non-nightly channels. Enabling the `no_std` mode now requires disabling the `std` feature *and* enabling the `alloc` feature. -------------------------------------------------------------------------------- # 0.2.11 Released 2018/08/09 ## Fixed * Fixed builds using `no-default-features = true` to not accidentally enable `no_std` mode, which requires nightly rust, and break builds on non-nightly channels. Enabling the `no_std` mode now requires disabling the `std` feature *and* enabling the `alloc` feature. -------------------------------------------------------------------------------- # 0.2.10 Released 2018/08/08 ## Added * Added support for `no_std`! This currently requires nightly Rust's `alloc` feature to get access to `BTreeMap`. Enable `no_std` support by building without the on-by-default `std` feature. [#148][] ## Fixed * Fixed formatting of some conversion operators. [#149][] * Fixed parsing some tricky symbols with template argument packs that came out of boost. [#150][] [#152][] [#148]: https://github.com/gimli-rs/cpp_demangle/pull/148 [#149]: https://github.com/gimli-rs/cpp_demangle/pull/149 [#150]: https://github.com/gimli-rs/cpp_demangle/pull/150 [#152]: https://github.com/gimli-rs/cpp_demangle/pull/152 -------------------------------------------------------------------------------- # 0.2.9 Released 2018/05/14 ## Fixed * Fixed a few issues with parentheticals. * Should not force recompilation via build.rs for every compile anymore (bug introduced in 0.2.8 when trying to make the package that is distributed on crates.io smaller). -------------------------------------------------------------------------------- # 0.2.8 Released 2018/05/11 Bug fixes, more `libiberty` tests passing, and we can now parse and demangle all but one symbol from Firefox's `libxul`: ``` Total number of libxul symbols: 274346 Number of libxul symbols parsed: 274345 (100.00%) Number of libxul symbols demangled: 274345 (100.00%) Number of libxul symbols demangled same as libiberty: 227259 (82.84%) ``` ## Fixed * AFL.rs fuzzing integration is fixed for the new AFL.rs releases. * Fixed formatting of constructors and destructors. * Fixed parsing of the `` production. * Fixed parsing of call expression productions. * Parsing an operator's operands will only parse as many operands as the operator's arity, instead of as many as it can. -------------------------------------------------------------------------------- # 0.2.7 Released 2017/11/27 Making lots of progress on symbols found in the wild! Here are our stats for symbols from Firefox's `libxul`: ``` Total number of libxul symbols: 274346 Number of libxul symbols parsed: 274319 (99.99%) Number of libxul symbols demangled: 274319 (99.99%) Number of libxul symbols demangled same as libiberty: 199928 (72.88%) ``` Additionally, the `libiberty` test threshold bumped up from 70 to 83 during this release. ## Added * Added support for GCC's "global constructors" and "global destructors" extensions. * Added support for GCC's extensions to the `` production: construction vtables, typeinfo functions, TLS initialization functions, TLS wrapper functions. * Added support for the now-defunct `` production. My understanding is that this is from an older version of the ABI standard. It isn't in the current version, but all the other demanglers support it, so we will too. ## Changed * `cpp_demangle` is now part of the `gimli-rs` GitHub organization. The canonical repository is now https://github.com/gimli-rs/cpp_demangle * Literals are now formatted how `libiberty` formats them. For example, `foo()` rather than `foo<1>()`. * Unary operators are now formatted with parentheses, matching `libiberty`. ## Fixed * Nested array types and multi-dimensional arrays are now mangled correctly. * Nested function types and their qualifiers are now mangled correctly. * Arrays of function types and function types with array return and parameter types are now mangled correctly. * The `new` operator is now correctly formatted as `operator new` rather than `operatornew`. Same for the `delete` operator. -------------------------------------------------------------------------------- # 0.2.6 Released 2017/11/08 ## Added * Added support for vector types * Added support for ABI tags * Added support for ` ::= ` productions ## Fixed * Fixed erroneous insertions into the substitutions table with prefixes and nested names * Well known components were previously incorrectly not permitted to prefix template arguments cpp_demangle-0.4.0/CONTRIBUTING.md000064400000000000000000000066321046102023000145030ustar 00000000000000# Contributing to `cpp_demangle` Hi! We'd love to have your contributions! If you want help or mentorship, reach out to us in a GitHub issue, or ping `fitzgen` in [#rust on irc.mozilla.org](irc://irc.mozilla.org#rust) and introduce yourself. - [Code of Conduct](#code-of-conduct) - [Filing an Issue](#filing-an-issue) - [Building](#building) - [Testing](#testing) - [Testing `libiberty` Compatibility](#testing-libiberty-compatibility) - [Debugging](#debugging) - [Fuzzing with AFL](#fuzzing-with-afl) - [Automatic code formatting](#automatic-code-formatting) ## Code of Conduct We abide by the [Rust Code of Conduct][coc] and ask that you do as well. [coc]: https://www.rust-lang.org/en-US/conduct.html ## Filing an Issue When filing an issue, please provide: * The mangled C++ symbol name, and * The way that `cpp_demangle` demangled it (or failed to) `cpp_demangle` should *never* panic or crash. If you find some input that causes a panic or crash, **please file an issue!** ## Building ``` $ cargo build ``` ## Testing To run all the tests: ``` $ cargo test ``` ### Testing `libiberty` Compatibility We currently have partial compatibility with the canonical GNU C++ demangler in `libiberty`. Work towards full compatibility is ongoing. To run all of `libiberty`'s tests (many of which are failing due to formatting differences and malformatting on `cpp_demangle`'s part) you can enable the `run_libiberty_tests` feature when testing: ``` $ cargo test --features run_libiberty_tests ``` As more `libiberty` tests start passing, we start including them in our test suite by default. Help getting more of `libiberty`'s tests passing is very appreciated! See `LIBIBERTY_TEST_THRESHOLD` in `build.rs` for details. ## Debugging The `logging` feature adds debug logging that is very helpful when trying to determine how a mangled symbol is parsed, and what its substitutions table looks like: ``` $ cargo test --feature logging ``` ## Fuzzing ### Fuzzing with `cargo-fuzz` and `libFuzzer` This is a bit easier to set up than AFL. See [the `cargo-fuzz` book for details](https://rust-fuzz.github.io/book/cargo-fuzz/tutorial.html). 1. `$ cargo install cargo-fuzz` 2. `$ cargo fuzz parse_and_stringify` Alternatively, run `cargo fuzz list` to get a list of fuzz targets to run instead of the `parse_and_stringify` target. ### Fuzzing with AFL What follows is a TLDR, for detailed instructions see the [`afl.rs` book](https://rust-fuzz.github.io/book/afl/setup.html). 1. Install the afl.rs command line tool: `cargo install afl` 1. Build the cpp_mangle AFL fuzz target: `cargo afl build --features afl` 1. Run AFL: `cargo afl fuzz -i in -o out target/debug/afl_runner` ## Automatic code formatting We use [`rustfmt`](https://github.com/rust-lang-nursery/rustfmt) to enforce a consistent code style across the whole `cpp_demangle` code base. You can install the latest version of `rustfmt` with this command: ``` $ cargo install -f rustfmt ``` Ensure that `~/.cargo/bin` is on your path. Once that is taken care of, you can (re)format all code by running this command: ``` $ cargo fmt ``` The code style is described in the `rustfmt.toml` file in top level of the repo. cpp_demangle-0.4.0/Cargo.lock0000644000000230770000000000100114370ustar # This file is automatically @generated by Cargo. # It is not intended for manual editing. version = 3 [[package]] name = "afl" version = "0.12.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "29d4b3411687df6020ec99478c56acd3c23b1caf0485575918f9c8852fda854d" dependencies = [ "clap", "fs_extra", "libc", "rustc_version", "tempfile", "xdg", ] [[package]] name = "atty" version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" dependencies = [ "hermit-abi", "libc", "winapi", ] [[package]] name = "bitflags" version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "cfg-if" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "clap" version = "4.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "06badb543e734a2d6568e19a40af66ed5364360b9226184926f89d229b4b4267" dependencies = [ "atty", "bitflags", "clap_derive", "clap_lex", "once_cell", "strsim", "termcolor", ] [[package]] name = "clap_derive" version = "4.0.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c42f169caba89a7d512b5418b09864543eeb4d497416c917d7137863bd2076ad" dependencies = [ "heck", "proc-macro-error", "proc-macro2", "quote", "syn", ] [[package]] name = "clap_lex" version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0d4198f73e42b4936b35b5bb248d81d2b595ecb170da0bac7655c54eedfa8da8" dependencies = [ "os_str_bytes", ] [[package]] name = "cpp_demangle" version = "0.4.0" dependencies = [ "afl", "cfg-if", "clap", "diff", ] [[package]] name = "diff" version = "0.1.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "56254986775e3233ffa9c4d7d3faaf6d36a2c09d30b20687e9f88bc8bafc16c8" [[package]] name = "dirs" version = "4.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ca3aa72a6f96ea37bbc5aa912f6788242832f75369bdfdadcb0e38423f100059" dependencies = [ "dirs-sys", ] [[package]] name = "dirs-sys" version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1b1d1d91c932ef41c0f2663aa8b0ca0342d444d842c06914aa0a7e352d0bada6" dependencies = [ "libc", "redox_users", "winapi", ] [[package]] name = "fastrand" version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a7a407cfaa3385c4ae6b23e84623d48c2798d06e3e6a1878f7f59f17b3f86499" dependencies = [ "instant", ] [[package]] name = "fs_extra" version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2022715d62ab30faffd124d40b76f4134a550a87792276512b18d63272333394" [[package]] name = "getrandom" version = "0.2.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4eb1a864a501629691edf6c15a593b7a51eebaa1e8468e9ddc623de7c9b58ec6" dependencies = [ "cfg-if", "libc", "wasi", ] [[package]] name = "heck" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2540771e65fc8cb83cd6e8a237f70c319bd5c29f78ed1084ba5d50eeac86f7f9" [[package]] name = "hermit-abi" version = "0.1.19" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" dependencies = [ "libc", ] [[package]] name = "instant" version = "0.1.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" dependencies = [ "cfg-if", ] [[package]] name = "libc" version = "0.2.135" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "68783febc7782c6c5cb401fbda4de5a9898be1762314da0bb2c10ced61f18b0c" [[package]] name = "once_cell" version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e82dad04139b71a90c080c8463fe0dc7902db5192d939bd0950f074d014339e1" [[package]] name = "os_str_bytes" version = "6.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9ff7415e9ae3fff1225851df9e0d9e4e5479f947619774677a63572e55e80eff" [[package]] name = "proc-macro-error" version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" dependencies = [ "proc-macro-error-attr", "proc-macro2", "quote", "syn", "version_check", ] [[package]] name = "proc-macro-error-attr" version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" dependencies = [ "proc-macro2", "quote", "version_check", ] [[package]] name = "proc-macro2" version = "1.0.47" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5ea3d908b0e36316caf9e9e2c4625cdde190a7e6f440d794667ed17a1855e725" dependencies = [ "unicode-ident", ] [[package]] name = "quote" version = "1.0.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bbe448f377a7d6961e30f5955f9b8d106c3f5e449d493ee1b125c1d43c2b5179" dependencies = [ "proc-macro2", ] [[package]] name = "redox_syscall" version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" dependencies = [ "bitflags", ] [[package]] name = "redox_users" version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b033d837a7cf162d7993aded9304e30a83213c648b6e389db233191f891e5c2b" dependencies = [ "getrandom", "redox_syscall", "thiserror", ] [[package]] name = "remove_dir_all" version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7" dependencies = [ "winapi", ] [[package]] name = "rustc_version" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" dependencies = [ "semver", ] [[package]] name = "semver" version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e25dfac463d778e353db5be2449d1cce89bd6fd23c9f1ea21310ce6e5a1b29c4" [[package]] name = "strsim" version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" [[package]] name = "syn" version = "1.0.102" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3fcd952facd492f9be3ef0d0b7032a6e442ee9b361d4acc2b1d0c4aaa5f613a1" dependencies = [ "proc-macro2", "quote", "unicode-ident", ] [[package]] name = "tempfile" version = "3.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5cdb1ef4eaeeaddc8fbd371e5017057064af0911902ef36b39801f67cc6d79e4" dependencies = [ "cfg-if", "fastrand", "libc", "redox_syscall", "remove_dir_all", "winapi", ] [[package]] name = "termcolor" version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bab24d30b911b2376f3a13cc2cd443142f0c81dda04c118693e35b3835757755" dependencies = [ "winapi-util", ] [[package]] name = "thiserror" version = "1.0.37" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "10deb33631e3c9018b9baf9dcbbc4f737320d2b576bac10f6aefa048fa407e3e" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" version = "1.0.37" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "982d17546b47146b28f7c22e3d08465f6b8903d0ea13c1660d9d84a6e7adcdbb" dependencies = [ "proc-macro2", "quote", "syn", ] [[package]] name = "unicode-ident" version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6ceab39d59e4c9499d4e5a8ee0e2735b891bb7308ac83dfb4e80cad195c9f6f3" [[package]] name = "version_check" version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" [[package]] name = "wasi" version = "0.11.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "winapi" version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" dependencies = [ "winapi-i686-pc-windows-gnu", "winapi-x86_64-pc-windows-gnu", ] [[package]] name = "winapi-i686-pc-windows-gnu" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" [[package]] name = "winapi-util" version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" dependencies = [ "winapi", ] [[package]] name = "winapi-x86_64-pc-windows-gnu" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" [[package]] name = "xdg" version = "2.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0c4583db5cbd4c4c0303df2d15af80f0539db703fa1c68802d4cbbd2dd0f88f6" dependencies = [ "dirs", ] cpp_demangle-0.4.0/Cargo.toml0000644000000030760000000000100114570ustar # THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies. # # If you are reading this file be aware that the original Cargo.toml # will likely look very different (and much more reasonable). # See Cargo.toml.orig for the original contents. [package] edition = "2018" name = "cpp_demangle" version = "0.4.0" authors = [ "Nick Fitzgerald ", "Jim Blandy ", "Kyle Huey ", ] build = "build.rs" exclude = [ "tests/**", "in/**", ] description = "A crate for demangling C++ symbols" documentation = "https://docs.rs/cpp_demangle" readme = "./README.md" keywords = [ "demangle", "symbolicate", "c-plus-plus", "itanium", ] categories = [ "development-tools::debugging", "development-tools::ffi", ] license = "MIT OR Apache-2.0" repository = "https://github.com/gimli-rs/cpp_demangle" [profile.release] debug = true [[bin]] name = "afl_runner" path = "src/bin/afl_runner.rs" required-features = ["afl"] [dependencies.afl] version = "0.12.0" optional = true [dependencies.cfg-if] version = "1.0.0" [dev-dependencies.clap] version = "4.0" features = ["derive"] [dev-dependencies.diff] version = "0.1.11" [features] alloc = [] default = ["std"] fuzz = ["afl"] logging = ["std"] run_libiberty_tests = [] std = ["alloc"] [badges.travis-ci] repository = "gimli-rs/cpp_demangle" cpp_demangle-0.4.0/Cargo.toml.orig0000644000000027460000000000100124210ustar [package] authors = ["Nick Fitzgerald ", "Jim Blandy ", "Kyle Huey "] build = "build.rs" categories = ["development-tools::debugging", "development-tools::ffi"] description = "A crate for demangling C++ symbols" documentation = "https://docs.rs/cpp_demangle" exclude = ["tests/**", "in/**"] keywords = ["demangle", "symbolicate", "c-plus-plus", "itanium"] license = "MIT OR Apache-2.0" name = "cpp_demangle" readme = "./README.md" repository = "https://github.com/gimli-rs/cpp_demangle" version = "0.4.0" edition = "2018" [badges] [badges.travis-ci] repository = "gimli-rs/cpp_demangle" [[bin]] name = "afl_runner" path = "src/bin/afl_runner.rs" required-features = ["afl"] [dependencies] cfg-if = "1.0.0" [dependencies.afl] optional = true version = "0.12.0" [dev-dependencies] clap = { version = "4.0", features = ["derive"] } diff = "0.1.11" [features] # Default features. default = ["std"] # Build using the `std` library. Disabling this enables `no_std` support. std = ["alloc"] # Use collections from the `alloc` crate rather than from `std`. alloc = [] # Enable copious amounts of logging. This is for internal use only, and is only # useful for hacking on `cpp_demangle` itself. logging = ["std"] # Run all libiberty tests, even the ones that are known not to pass yet. This is # for internal use only. run_libiberty_tests = [] # Enable fuzzing support. This is for internal use only. fuzz = ["afl"] [profile.release] debug = true cpp_demangle-0.4.0/Cargo.toml.orig000064400000000000000000000027461046102023000151430ustar 00000000000000[package] authors = ["Nick Fitzgerald ", "Jim Blandy ", "Kyle Huey "] build = "build.rs" categories = ["development-tools::debugging", "development-tools::ffi"] description = "A crate for demangling C++ symbols" documentation = "https://docs.rs/cpp_demangle" exclude = ["tests/**", "in/**"] keywords = ["demangle", "symbolicate", "c-plus-plus", "itanium"] license = "MIT OR Apache-2.0" name = "cpp_demangle" readme = "./README.md" repository = "https://github.com/gimli-rs/cpp_demangle" version = "0.4.0" edition = "2018" [badges] [badges.travis-ci] repository = "gimli-rs/cpp_demangle" [[bin]] name = "afl_runner" path = "src/bin/afl_runner.rs" required-features = ["afl"] [dependencies] cfg-if = "1.0.0" [dependencies.afl] optional = true version = "0.12.0" [dev-dependencies] clap = { version = "4.0", features = ["derive"] } diff = "0.1.11" [features] # Default features. default = ["std"] # Build using the `std` library. Disabling this enables `no_std` support. std = ["alloc"] # Use collections from the `alloc` crate rather than from `std`. alloc = [] # Enable copious amounts of logging. This is for internal use only, and is only # useful for hacking on `cpp_demangle` itself. logging = ["std"] # Run all libiberty tests, even the ones that are known not to pass yet. This is # for internal use only. run_libiberty_tests = [] # Enable fuzzing support. This is for internal use only. fuzz = ["afl"] [profile.release] debug = true cpp_demangle-0.4.0/LICENSE-APACHE000064400000000000000000000251371046102023000141770ustar 00000000000000 Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright [yyyy] [name of copyright owner] Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. cpp_demangle-0.4.0/LICENSE-MIT000064400000000000000000000020571046102023000137030ustar 00000000000000Copyright (c) 2015 The Rust Project Developers Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. cpp_demangle-0.4.0/README.md000064400000000000000000000075271046102023000135350ustar 00000000000000# `cpp_demangle`: a C++ linker symbol demangler [![](https://docs.rs/cpp_demangle/badge.svg)](https://docs.rs/cpp_demangle/) [![](https://img.shields.io/crates/v/cpp_demangle.svg) ![](https://img.shields.io/crates/d/cpp_demangle.svg)](https://crates.io/crates/cpp_demangle) [![Build Status](https://github.com/gimli-rs/cpp_demangle/workflows/ci/badge.svg)](https://github.com/gimli-rs/cpp_demangle/actions) This crate can parse a C++ “mangled” linker symbol name into a Rust value describing what the name refers to: a variable, a function, a virtual table, etc. The description type implements `Display`, producing human-readable text describing the mangled name. Debuggers and profilers can use this crate to provide more meaningful output. C++ requires the compiler to choose names for linker symbols consistently across compilation units, so that two compilation units that have seen the same declarations can pair up definitions in one unit with references in another. Almost all platforms other than Microsoft Windows follow the [Itanium C++ ABI][itanium]'s rules for this. [itanium]: https://itanium-cxx-abi.github.io/cxx-abi/abi.html#mangling For example, suppose a C++ compilation unit has the definition: namespace space { int foo(int x, int y) { return x+y; } } The Itanium C++ ABI specifies that the linker symbol for that function must be named `_ZN5space3fooEii`. This crate can parse that name into a Rust value representing its structure. Formatting the value with the `format!` macro or the `std::string::ToString::to_string` trait method yields the string `space::foo(int, int)`, which is more meaningful to the C++ developer. ## Usage Add `cpp_demangle` to your crate's `Cargo.toml`: ```toml [dependencies] cpp_demangle = "0.4.0" ``` And then demangle some C++ symbols! ```rust extern crate cpp_demangle; use cpp_demangle::Symbol; use std::string::ToString; let mangled = b"_ZN5space3fooEibc"; let sym = Symbol::new(&mangled[..]) .expect("Could not parse mangled symbol!"); let demangled = sym.to_string(); assert_eq!(demangled, "space::foo(int, bool, char)"); ``` ### `no_std` Support `cpp_demangle` may be configured for working in `no_std` environments that still have allocation support via the `alloc` crate. This is nightly rust only, at the moment, since the `alloc` crate's collections aren't stabilized. Disable the "std" feature, and enable the "alloc" feature: ```toml [dependencies] cpp_demangle = { version = "0.4.0", default-features = false, features = ["alloc"] } ``` ## Documentation [Documentation on docs.rs](https://docs.rs/cpp_demangle) Example programs: * [A `c++filt` clone.](./examples/cppfilt.rs) Install it locally with this command: ``` cargo install cpp_demangle --example cppfilt ``` ## Implementation Status Work is ongoing. While `cpp_demangle` can parse every mangled symbol in `libiberty`'s demangler's test suite (the canonical Itanium C++ symbol demangler used by GNU tools such as `c++filt`), it does not format all of them character-for-character identically. I'm working on fixing that ;) Despite that, I believe `cpp_demangle` is fairly robust. I've been running [AFL][] on `cpp_demangle` overnight and it hasn't found any panics for a long time now (and never found any crashes -- thanks Rust!). [AFL]: https://github.com/rust-fuzz/afl.rs ## License Licensed under either of * Apache License, Version 2.0 ([`LICENSE-APACHE`](./LICENSE-APACHE) or https://www.apache.org/licenses/LICENSE-2.0) * MIT license ([`LICENSE-MIT`](./LICENSE-MIT) or https://opensource.org/licenses/MIT) at your option. ## Contribution See [CONTRIBUTING.md](./CONTRIBUTING.md) for hacking. Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions. cpp_demangle-0.4.0/build.rs000064400000000000000000000220431046102023000137110ustar 00000000000000use std::collections::HashSet; use std::env; use std::fs; use std::io::{self, BufRead, Write}; use std::path::{self, Path}; fn get_crate_dir() -> io::Result { Ok(path::PathBuf::from( env::var("CARGO_MANIFEST_DIR") .map_err(|_| io::Error::new(io::ErrorKind::Other, "no CARGO_MANIFEST_DIR"))?, )) } fn get_out_dir() -> io::Result { Ok(path::PathBuf::from(env::var("OUT_DIR").map_err(|_| { io::Error::new(io::ErrorKind::Other, "no OUT_DIR") })?)) } fn get_crate_test_path(file_name: &str) -> io::Result { let mut test_path = get_crate_dir()?; test_path.push("tests"); assert!(test_path.is_dir()); test_path.push(file_name); Ok(test_path) } fn get_test_path(file_name: &str) -> io::Result { let mut test_path = get_out_dir()?; assert!(test_path.is_dir()); test_path.push(file_name); Ok(test_path) } /// Generate tests that ensure that we don't panic when parsing and demangling /// the seed test cases that we pass to AFL.rs assert (including the failing /// test cases historically found by AFL.rs). fn generate_sanity_tests_from_afl_seeds() -> io::Result<()> { let mut in_dir = get_crate_dir()?; in_dir.push("in"); if !in_dir.is_dir() { // We are in `cargo publish` and the `in/` directory isn't included in // the distributed package. return Ok(()); } let test_path = get_test_path("afl_seeds.rs")?; let mut test_file = fs::File::create(test_path)?; writeln!( &mut test_file, " extern crate cpp_demangle; use std::fs; use std::io::Read; " )?; println!("cargo:rerun-if-changed=tests/afl_seeds.rs"); let entries = fs::read_dir(in_dir)?; for entry in entries { let entry = entry?; let path = entry.path(); let file_name = path.file_name().ok_or_else(|| { io::Error::new( io::ErrorKind::Other, "no file name for AFL.rs seed test case", ) })?; println!( "cargo:rerun-if-changed=in/{}", Path::new(file_name).display() ); // properly escape windows paths let path = path.to_string_lossy().replace("\\", "\\\\"); writeln!( &mut test_file, r#" #[test] fn test_afl_seed_{}() {{ let mut file = fs::File::open("{}").unwrap(); let mut contents = Vec::new(); file.read_to_end(&mut contents).unwrap(); let _ = cpp_demangle::Symbol::new(contents); assert!(true, "did not panic when parsing"); }} "#, file_name.to_string_lossy(), path )?; } Ok(()) } /// Read `tests/libiberty-demangle-expected`, parse its input mangled symbols, /// and expected output demangled symbols, and generate test cases for them. /// /// We do not support all of the options that the libiberty demangler does, /// therefore we skip tests that use options we do not intend to /// support. Basically, we only support `--format=gnu-v3` (which is the System V /// C++ ABI), and none of the legacy C/C++ compiler formats, nor Java/D/etc /// language symbol mangling. fn generate_compatibility_tests_from_libiberty() -> io::Result<()> { let mut tests_dir = get_crate_dir()?; tests_dir.push("tests"); if !tests_dir.is_dir() { // We are in `cargo publish` and the `tests/` directory isn't included // in the distributed package. return Ok(()); } println!("cargo:rerun-if-changed=tests/libiberty-demangle-expected"); let test_path = get_test_path("libiberty.rs")?; let _ = fs::remove_file(&test_path); let mut test_file = fs::File::create(test_path)?; writeln!( &mut test_file, " extern crate cpp_demangle; extern crate diff; use std::fmt::Write; " )?; // The set of libiberty tests that pass. This should only ever grow! let libiberty_passing_tests = { let mut s: HashSet<_> = (0..86).collect(); s.extend(87..89); s.extend(91..93); s.extend(94..113); s.extend(115..118); s.extend(119..120); s.extend(121..123); s.extend(127..131); s.extend(133..134); s.extend(137..138); s.extend(139..140); s.extend(141..144); s.extend(146..147); s.extend(149..154); s.extend(158..168); s.extend(169..180); s.extend(181..183); s.extend(184..186); s.extend(187..190); s.extend(199..200); s.extend(201..202); s.extend(203..204); s }; let libiberty_tests = get_crate_test_path("libiberty-demangle-expected")?; let libiberty_tests = fs::File::open(libiberty_tests)?; let libiberty_tests = io::BufReader::new(libiberty_tests); let mut lines = libiberty_tests .lines() .filter(|line| line.as_ref().map(|l| !l.starts_with('#')).unwrap_or(true)); let mut n = 0; loop { let options = match lines.next() { None => break, Some(Ok(line)) => line, Some(Err(e)) => return Err(e), }; let mangled = match lines.next() { Some(Ok(line)) => line, None => { return Err(io::Error::new( io::ErrorKind::Other, "expected a line with a mangled symbol", )) } Some(Err(e)) => return Err(e), }; let demangled = match lines.next() { Some(Ok(line)) => line, None => { return Err(io::Error::new( io::ErrorKind::Other, "expected a line with the demangled symbol", )) } Some(Err(e)) => return Err(e), }; if options.contains("--no-params") { // This line is the expected demangled output without function and // template parameters, but we don't currently have such an option // in `cpp_demangle`, so just consume and ignore the line. match lines.next() { Some(Ok(_)) => {} None => { return Err(io::Error::new( io::ErrorKind::Other, "expected a line with the demangled symbol without parameters", )) } Some(Err(e)) => return Err(e), } } // Skip tests for unsupported languages or options. if !options.contains("--format=gnu-v3") || options.contains("--is-v3-ctor") || options.contains("--is-v3-dtor") || options.contains("--ret-postfix") { continue; } let cfg = if libiberty_passing_tests.contains(&n) { "" } else { r###"#[cfg(feature = "run_libiberty_tests")]"### }; writeln!( test_file, r###" {} #[test] fn test_libiberty_demangle_{}_() {{ let mangled = br#"{}"#; let mangled_str = String::from_utf8_lossy(mangled).into_owned(); println!("Parsing mangled symbol: {{}}", mangled_str); let expected = r#"{}"#; let sym = match cpp_demangle::Symbol::new(&mangled[..]) {{ Ok(sym) => sym, Err(_) if mangled_str == expected => return, Err(e) => panic!("Should parse mangled symbol {{}}", e), }}; let mut actual = String::new(); if let Err(e) = write!(&mut actual, "{{}}", sym) {{ panic!("Error while demangling '{{}}': {{}}", mangled_str, e); }} println!(" Expect demangled symbol: {{}}", expected); println!("Actually demangled symbol as: {{}}", actual); if expected != actual {{ println!(""); println!("Diff:"); println!("--- expected"); print!("+++ actual"); let mut last = None; for cmp in diff::chars(expected, &actual) {{ match (last, cmp.clone()) {{ (Some(diff::Result::Left(_)), diff::Result::Left(_)) | (Some(diff::Result::Both(..)), diff::Result::Both(..)) | (Some(diff::Result::Right(_)), diff::Result::Right(_)) => {{}} (_, diff::Result::Left(_)) => print!("\n-"), (_, diff::Result::Both(..)) => print!("\n "), (_, diff::Result::Right(_)) => print!("\n+"), }}; match cmp.clone() {{ diff::Result::Left(c) | diff::Result::Both(c, _) | diff::Result::Right(c) => print!("{{}}", c), }} last = Some(cmp); }} println!(""); }} assert_eq!(expected, actual); }} "###, cfg, n, mangled.trim(), demangled.trim() )?; n += 1; } Ok(()) } fn main() { println!("cargo:rerun-if-changed=build.rs"); generate_sanity_tests_from_afl_seeds() .expect("should generate sanity tests from AFL.rs seed test cases"); generate_compatibility_tests_from_libiberty() .expect("should generate compatibility tests from tests/libiberty-demangle-expected"); } cpp_demangle-0.4.0/examples/cppfilt.rs000064400000000000000000000077501046102023000161010ustar 00000000000000// For clippy. #![allow(unknown_lints)] extern crate cpp_demangle; // For command line integration extern crate clap; use clap::Parser; use cpp_demangle::{BorrowedSymbol, DemangleOptions}; use std::io::{self, BufRead, Cursor, Write}; use std::process; /// Find the index of the first (potential) occurrence of a mangled C++ symbol /// in the given `haystack`. fn find_mangled(haystack: &[u8]) -> Option { if haystack.is_empty() { return None; } for i in 0..haystack.len() - 1 { if haystack[i] == b'_' { match ( haystack[i + 1], haystack.get(i + 2), haystack.get(i + 3), haystack.get(i + 4), ) { (b'Z', _, _, _) | (b'_', Some(b'Z'), _, _) | (b'_', Some(b'_'), Some(b'Z'), _) => { return Some(i) } (b'_', Some(b'_'), Some(b'_'), Some(b'Z')) => return Some(i), _ => (), } } } None } /// Print the given `line` to `out`, with all mangled C++ symbols replaced with /// their demangled form. fn demangle_line(out: &mut W, line: &[u8], options: DemangleOptions) -> io::Result<()> where W: Write, { let mut line = line; while let Some(idx) = find_mangled(line) { write!(out, "{}", String::from_utf8_lossy(&line[..idx]))?; if let Ok((sym, tail)) = BorrowedSymbol::with_tail(&line[idx..]) { let demangled = sym .demangle(&options) .map_err(|e| io::Error::new(io::ErrorKind::Other, e))?; write!(out, "{}", demangled)?; line = tail; } else { write!(out, "_Z")?; line = &line[2..]; } } write!(out, "{}", String::from_utf8_lossy(line)) } /// Print all the lines from the given `input` to `out`, with all mangled C++ /// symbols replaced with their demangled form. fn demangle_all(input: &mut R, out: &mut W, options: DemangleOptions) -> io::Result<()> where R: BufRead, W: Write, { let mut buf = vec![]; while input.read_until(b'\n', &mut buf)? > 0 { let nl = buf.ends_with(&[b'\n']); if nl { buf.pop(); } demangle_line(out, &buf[..], options)?; if nl { write!(out, "\n")?; } buf.clear(); } Ok(()) } /// A c++filt clone as an example of how to use the cpp_demangle crate! #[derive(Parser)] #[clap(version, author)] struct Cli { #[clap(short = 'p', long)] /// Do not display function arguments. no_params: bool, /// Do not display function return types. #[clap(long)] no_return_type: bool, #[clap(long)] /// Hide types in template parameter expression literals hide_expression_literal_types: bool, mangled_names: Vec, } fn main() { let cli = Cli::parse(); let stdin = io::stdin(); let mut stdin = stdin.lock(); let stdout = io::stdout(); let mut stdout = stdout.lock(); let stderr = io::stderr(); let mut stderr = stderr.lock(); let mut options = DemangleOptions::new(); if cli.no_params { options = options.no_params(); } if cli.hide_expression_literal_types { options = options.hide_expression_literal_types(); } if cli.no_return_type { options = options.no_return_type(); } let demangle_result = if !cli.mangled_names.is_empty() { let mut input = Cursor::new(cli.mangled_names.into_iter().fold( String::new(), |mut accumulated, name| { accumulated.push_str(&name); accumulated.push_str("\n"); accumulated }, )); demangle_all(&mut input, &mut stdout, options) } else { demangle_all(&mut stdin, &mut stdout, options) }; let code = match demangle_result { Ok(_) => 0, Err(e) => { let _ = writeln!(&mut stderr, "error: {}", e); 1 } }; process::exit(code); } cpp_demangle-0.4.0/src/ast.rs000064400000000000000000014426271046102023000142070ustar 00000000000000//! Abstract syntax tree types for mangled symbols. use super::{DemangleNodeType, DemangleOptions, DemangleWrite, ParseOptions}; use crate::error::{self, Result}; use crate::index_str::IndexStr; use crate::subs::{Substitutable, SubstitutionTable}; use alloc::boxed::Box; use alloc::string::String; use alloc::vec::Vec; use core::cell::Cell; #[cfg(feature = "logging")] use core::cell::RefCell; use core::fmt::{self, Write}; use core::hash::{Hash, Hasher}; use core::mem; use core::ops; use core::ptr; use core::str; struct AutoLogParse; #[cfg(feature = "logging")] thread_local! { static LOG_DEPTH: RefCell = RefCell::new(0); } impl AutoLogParse { #[cfg(feature = "logging")] fn new(production: &'static str, input: IndexStr<'_>) -> AutoLogParse { LOG_DEPTH.with(|depth| { if *depth.borrow() == 0 { println!(); } let indent: String = (0..*depth.borrow() * 4).map(|_| ' ').collect(); log!( "{}({} \"{}\" {}", indent, production, String::from_utf8_lossy(input.as_ref()), input.len(), ); *depth.borrow_mut() += 1; }); AutoLogParse } #[cfg(not(feature = "logging"))] #[inline(always)] fn new(_: &'static str, _: IndexStr) -> AutoLogParse { AutoLogParse } } #[cfg(feature = "logging")] impl Drop for AutoLogParse { fn drop(&mut self) { LOG_DEPTH.with(|depth| { *depth.borrow_mut() -= 1; let indent: String = (0..*depth.borrow() * 4).map(|_| ' ').collect(); log!("{})", indent); }); } } /// Performs the two operations that begin every parse: /// /// 1. Keeps track of recursion levels and early returns with an error if there /// is too much recursion. /// /// 2. Automatically log start and end parsing in an s-expression format, when the /// `logging` feature is enabled. macro_rules! try_begin_parse { ( $production:expr , $ctx:expr , $input:expr ) => { let _log = AutoLogParse::new($production, $input); let _auto_check_recursion = AutoParseRecursion::new($ctx)?; }; } struct AutoLogDemangle; impl AutoLogDemangle { #[cfg(feature = "logging")] fn new( production: &P, ctx: &DemangleContext, scope: Option, is_inner: bool, ) -> AutoLogDemangle where P: ?Sized + fmt::Debug, W: DemangleWrite, { LOG_DEPTH.with(|depth| { if *depth.borrow() == 0 { println!(); } let indent: String = (0..*depth.borrow() * 4).map(|_| ' ').collect(); log!("{}(", indent); log!( "{} {}{:?}", indent, if is_inner { "as_inner: " } else { "" }, production ); log!("{} inner = {:?}", indent, ctx.inner); log!("{} scope = {:?}", indent, scope); *depth.borrow_mut() += 1; }); AutoLogDemangle } #[cfg(not(feature = "logging"))] #[inline(always)] fn new( _: &P, _: &DemangleContext, _: Option, _: bool, ) -> AutoLogDemangle where P: ?Sized + fmt::Debug, W: DemangleWrite, { AutoLogDemangle } } #[cfg(feature = "logging")] impl Drop for AutoLogDemangle { fn drop(&mut self) { LOG_DEPTH.with(|depth| { *depth.borrow_mut() -= 1; let indent: String = (0..*depth.borrow() * 4).map(|_| ' ').collect(); log!("{})", indent); }); } } /// Automatically log start and end demangling in an s-expression format, when /// the `logging` feature is enabled. macro_rules! try_begin_demangle { ( $production:expr, $ctx:expr, $scope:expr ) => {{ let _log = AutoLogDemangle::new($production, $ctx, $scope, false); &mut AutoParseDemangle::new($ctx)? }}; } /// Automatically log start and end demangling in an s-expression format, when /// the `logging` feature is enabled. macro_rules! try_begin_demangle_as_inner { ( $production:expr, $ctx:expr, $scope:expr ) => {{ let _log = AutoLogDemangle::new($production, $ctx, $scope, true); &mut AutoParseDemangle::new($ctx)? }}; } #[derive(Debug, Default, Clone, Copy)] struct ParseContextState { // The current recursion level. Should always be less than or equal to the // maximum. recursion_level: u32, // Whether or not we are currently parsing a conversion operator. in_conversion: bool, } /// Common context needed when parsing. #[derive(Debug, Clone)] pub struct ParseContext { // Maximum amount of recursive parsing calls we will allow. If this is too // large, we can blow the stack. max_recursion: u32, // Mutable state within the `ParseContext`. state: Cell, } impl ParseContext { /// Construct a new `ParseContext`. pub fn new(options: ParseOptions) -> ParseContext { ParseContext { max_recursion: options.recursion_limit.map(|v| v.get()).unwrap_or(96), state: Cell::new(ParseContextState::default()), } } /// Get the current recursion level for this context. pub fn recursion_level(&self) -> u32 { self.state.get().recursion_level } #[inline] fn enter_recursion(&self) -> error::Result<()> { let mut state = self.state.get(); let new_recursion_level = state.recursion_level + 1; if new_recursion_level >= self.max_recursion { log!("Hit too much recursion at level {}", self.max_recursion); Err(error::Error::TooMuchRecursion) } else { state.recursion_level = new_recursion_level; self.state.set(state); Ok(()) } } #[inline] fn exit_recursion(&self) { let mut state = self.state.get(); debug_assert!(state.recursion_level >= 1); state.recursion_level -= 1; self.state.set(state); } #[inline] fn in_conversion(&self) -> bool { self.state.get().in_conversion } fn set_in_conversion(&self, in_conversion: bool) -> bool { let mut state = self.state.get(); let previously_in_conversion = state.in_conversion; state.in_conversion = in_conversion; self.state.set(state); previously_in_conversion } } /// An RAII type to automatically check the recursion level against the /// maximum. If the maximum has been crossed, return an error. Otherwise, /// increment the level upon construction, and decrement it upon destruction. struct AutoParseRecursion<'a>(&'a ParseContext); impl<'a> AutoParseRecursion<'a> { #[inline] fn new(ctx: &'a ParseContext) -> error::Result> { ctx.enter_recursion()?; Ok(AutoParseRecursion(ctx)) } } impl<'a> Drop for AutoParseRecursion<'a> { #[inline] fn drop(&mut self) { self.0.exit_recursion(); } } /// A trait for anything that can be parsed from an `IndexStr` and return a /// `Result` of the parsed `Self` value and the rest of the `IndexStr` input /// that has not been consumed in parsing the `Self` value. /// /// For AST types representing productions which have `` as a /// possible right hand side, do not implement this trait directly. Instead, /// make a newtype over `usize`, parse either the `` back /// reference or "real" value, insert the "real" value into the substitution /// table if needed, and *always* return the newtype index into the substitution /// table. #[doc(hidden)] pub trait Parse: Sized { /// Parse the `Self` value from `input` and return it, updating the /// substitution table as needed. fn parse<'a, 'b>( ctx: &'a ParseContext, subs: &'a mut SubstitutionTable, input: IndexStr<'b>, ) -> Result<(Self, IndexStr<'b>)>; } /// Determine whether this AST node is an instantiated[*] template function, and /// get its concrete template arguments. /// /// [*] Note that we will never see an abstract, un-instantiated template /// function, since they don't end up in object files and don't get mangled /// names. trait GetTemplateArgs { /// Returns `Some` if this is a template function, `None` otherwise. fn get_template_args<'a>(&'a self, subs: &'a SubstitutionTable) -> Option<&'a TemplateArgs>; } /// A leaf name is the part the name that describes some type or class without /// any leading namespace qualifiers. /// /// This is used when figuring out how to format constructors and destructors, /// which are formatted as `gooble::dodo::Thing::~Thing()` but we don't have /// direct access to `Thing` in the `CtorDtorName` AST. #[derive(Debug)] pub(crate) enum LeafName<'a> { SourceName(&'a SourceName), WellKnownComponent(&'a WellKnownComponent), Closure(&'a ClosureTypeName), UnnamedType(&'a UnnamedTypeName), } impl<'subs, W> DemangleAsLeaf<'subs, W> for LeafName<'subs> where W: 'subs + DemangleWrite, { fn demangle_as_leaf<'me, 'ctx>( &'me self, ctx: &'ctx mut DemangleContext<'subs, W>, ) -> fmt::Result { match *self { LeafName::SourceName(sn) => sn.demangle(ctx, None), LeafName::Closure(c) => c.demangle(ctx, None), LeafName::WellKnownComponent(wkc) => wkc.demangle_as_leaf(ctx), LeafName::UnnamedType(utn) => utn.demangle_as_leaf(ctx), } } } /// Determine whether this AST node is some kind (potentially namespaced) name /// and if so get its leaf name. pub(crate) trait GetLeafName<'a> { fn get_leaf_name(&'a self, subs: &'a SubstitutionTable) -> Option>; } /// Determine whether this AST node is a constructor, destructor, or conversion /// function. pub(crate) trait IsCtorDtorConversion { fn is_ctor_dtor_conversion(&self, subs: &SubstitutionTable) -> bool; } /// When formatting a mangled symbol's parsed AST as a demangled symbol, we need /// to resolve indirect references to template and function arguments with /// direct `TemplateArg` and `Type` references respectively. /// /// Note that which set of arguments are implicitly referenced change as we /// enter and leave different functions' scope. One might usually use de Brujin /// indices to keep arguments within scopes separated from each other, but the /// Itanium C++ ABI does not allow us the luxury. AFAIK, when the ABI was first /// drafted, C++ did not have lambdas, and the issue did not come up at all /// since a function simply couldn't refer to the types of closed over /// variables. /// /// This trait is implemented by anything that can potentially resolve arguments /// for us. trait ArgScope<'me, 'ctx>: fmt::Debug { /// Get the current scope's leaf name. fn leaf_name(&'me self) -> Result>; /// Get the current scope's `index`th template argument. fn get_template_arg(&'me self, index: usize) -> Result<(&'ctx TemplateArg, &'ctx TemplateArgs)>; /// Get the current scope's `index`th function argument's type. fn get_function_arg(&'me self, index: usize) -> Result<&'ctx Type>; } /// An `ArgScopeStack` represents the current function and template demangling /// scope we are within. As we enter new demangling scopes, we construct new /// `ArgScopeStack`s whose `prev` references point back to the old ones. These /// `ArgScopeStack`s are kept on the native stack, and as functions return, they /// go out of scope and we use the previous `ArgScopeStack`s again. #[derive(Copy, Clone, Debug)] pub struct ArgScopeStack<'prev, 'subs> where 'subs: 'prev, { item: &'subs dyn ArgScope<'subs, 'subs>, in_arg: Option<(usize, &'subs TemplateArgs)>, prev: Option<&'prev ArgScopeStack<'prev, 'subs>>, } /// When we first begin demangling, we haven't entered any function or template /// demangling scope and we don't have any useful `ArgScopeStack`. Therefore, we /// are never actually dealing with `ArgScopeStack` directly in practice, but /// always an `Option` instead. Nevertheless, we want to define /// useful methods on `Option`. /// /// A custom "extension" trait with exactly one implementor: Rust's principled /// monkey patching! trait ArgScopeStackExt<'prev, 'subs>: Copy { /// Push a new `ArgScope` onto this `ArgScopeStack` and return the new /// `ArgScopeStack` with the pushed resolver on top. fn push( &'prev self, item: &'subs dyn ArgScope<'subs, 'subs>, ) -> Option>; } impl<'prev, 'subs> ArgScopeStackExt<'prev, 'subs> for Option> { fn push( &'prev self, item: &'subs dyn ArgScope<'subs, 'subs>, ) -> Option> { log!("ArgScopeStack::push: {:?}", item); Some(ArgScopeStack { prev: self.as_ref(), in_arg: None, item: item, }) } } /// A stack of `ArgScope`s is itself an `ArgScope`! impl<'prev, 'subs> ArgScope<'prev, 'subs> for Option> { fn leaf_name(&'prev self) -> Result> { let mut scope = self.as_ref(); while let Some(s) = scope { if let Ok(c) = s.item.leaf_name() { return Ok(c); } scope = s.prev; } Err(error::Error::BadLeafNameReference) } fn get_template_arg( &'prev self, idx: usize, ) -> Result<(&'subs TemplateArg, &'subs TemplateArgs)> { let mut scope = self.as_ref(); while let Some(s) = scope { if let Ok((arg, args)) = s.item.get_template_arg(idx) { if let Some((in_idx, in_args)) = s.in_arg { if args as *const TemplateArgs == in_args as *const TemplateArgs && in_idx <= idx { return Err(error::Error::ForwardTemplateArgReference); } } return Ok((arg, args)); } scope = s.prev; } Err(error::Error::BadTemplateArgReference) } fn get_function_arg(&'prev self, idx: usize) -> Result<&'subs Type> { let mut scope = self.as_ref(); while let Some(s) = scope { if let Ok(arg) = s.item.get_function_arg(idx) { return Ok(arg); } scope = s.prev; } Err(error::Error::BadFunctionArgReference) } } #[derive(Debug, Copy, Clone)] struct DemangleState { /// How deep in the demangling are we? pub recursion_level: u32, } /// An RAII type to automatically check the recursion level against the /// maximum. If the maximum has been crossed, return an error. Otherwise, /// increment the level upon construction, and decrement it upon destruction. struct AutoParseDemangle<'a, 'b, W: 'a + DemangleWrite>(&'b mut DemangleContext<'a, W>); impl<'a, 'b, W: 'a + DemangleWrite> AutoParseDemangle<'a, 'b, W> { #[inline] fn new(ctx: &'b mut DemangleContext<'a, W>) -> core::result::Result { ctx.enter_recursion()?; Ok(AutoParseDemangle(ctx)) } } impl<'a, 'b, W: 'a + DemangleWrite> ops::Deref for AutoParseDemangle<'a, 'b, W> { type Target = DemangleContext<'a, W>; fn deref(&self) -> &Self::Target { self.0 } } impl<'a, 'b, W: 'a + DemangleWrite> ops::DerefMut for AutoParseDemangle<'a, 'b, W> { fn deref_mut(&mut self) -> &mut Self::Target { self.0 } } impl<'a, 'b, W: 'a + DemangleWrite> Drop for AutoParseDemangle<'a, 'b, W> { #[inline] fn drop(&mut self) { self.0.exit_recursion(); } } /// Common state that is required when demangling a mangled symbol's parsed AST. #[doc(hidden)] #[derive(Debug)] pub struct DemangleContext<'a, W> where W: 'a + DemangleWrite, { // The substitution table built up when parsing the mangled symbol into an // AST. subs: &'a SubstitutionTable, // The maximum recursion max_recursion: u32, // Sometimes an AST node needs to insert itself as an inner item within one // of its children when demangling that child. For example, the AST // // (array 10 int) // // is demangled as `int[10]`, but if we were to demangle the AST // // (lvalue-ref (array 10 int)) // // then we would want this demangled form: `int (&) [10]`, which requires // the parent lvalue-ref to be passed into the child array's demangling // method. This kind of thing also pops up with function pointers. // // The `inner` stack enables such behavior by allowing us to pass AST // parents down to their children as inner items. inner: Vec<&'a dyn DemangleAsInner<'a, W>>, // The original input string. input: &'a [u8], // `Identifier`s will be placed here, so `UnnamedTypeName` can utilize and print // out the Constructor/Destructor used. source_name: Option<&'a str>, // What the demangled name is being written to. out: &'a mut W, // The total number of bytes written to `out`. This is maintained by the // `Write` implementation for `DemangleContext`. bytes_written: usize, // The last char written to `out`, if any. last_char_written: Option, // We are currently demangling a lambda argument, so template substitution // should be suppressed to match libiberty. is_lambda_arg: bool, // We are currently demangling a template-prefix. is_template_prefix: bool, // We are currently demangling a template-prefix in a nested-name. is_template_prefix_in_nested_name: bool, // `PackExpansion`'s should only print '...', only when there is no template // argument pack. is_template_argument_pack: bool, // Whether to show function parameters. // This must be set to true before calling `demangle` on `Encoding` // unless that call is via the toplevel call to `MangledName::demangle`. show_params: bool, // Whether to show function return types. // This must be set to true before calling `demangle` on `Encoding` // unless that call is via the toplevel call to `MangledName::demangle`. show_return_type: bool, // Whether to show types of expression literals. show_expression_literal_types: bool, // recursion protection. state: Cell, } impl<'a, W> fmt::Write for DemangleContext<'a, W> where W: 'a + DemangleWrite, { fn write_str(&mut self, s: &str) -> fmt::Result { if s.is_empty() { return Ok(()); } log!("DemangleContext::write: '{}'", s); self.out.write_string(s).map(|_| { self.last_char_written = s.chars().last(); self.bytes_written += s.len(); }) } } impl<'a, W> DemangleContext<'a, W> where W: 'a + DemangleWrite, { /// Construct a new `DemangleContext`. pub fn new( subs: &'a SubstitutionTable, input: &'a [u8], options: DemangleOptions, out: &'a mut W, ) -> DemangleContext<'a, W> { DemangleContext { subs: subs, max_recursion: options.recursion_limit.map(|v| v.get()).unwrap_or(128), inner: vec![], input: input, source_name: None, out: out, bytes_written: 0, last_char_written: None, is_lambda_arg: false, is_template_prefix: false, is_template_prefix_in_nested_name: false, is_template_argument_pack: false, show_params: !options.no_params, show_return_type: !options.no_return_type, show_expression_literal_types: !options.hide_expression_literal_types, state: Cell::new(DemangleState { recursion_level: 0 }), } } /// Get the current recursion level for this context. pub fn recursion_level(&self) -> u32 { self.state.get().recursion_level } #[inline] fn enter_recursion(&self) -> fmt::Result { let mut state = self.state.get(); let new_recursion_level = state.recursion_level + 1; if new_recursion_level >= self.max_recursion { log!("Hit too much recursion at level {}", self.max_recursion); Err(Default::default()) } else { state.recursion_level = new_recursion_level; self.state.set(state); Ok(()) } } #[inline] fn exit_recursion(&self) { let mut state = self.state.get(); debug_assert!(state.recursion_level >= 1); state.recursion_level -= 1; self.state.set(state); } #[inline] fn ensure(&mut self, ch: char) -> fmt::Result { if self.last_char_written == Some(ch) { Ok(()) } else { write!(self, "{}", ch)?; Ok(()) } } #[inline] fn ensure_space(&mut self) -> fmt::Result { self.ensure(' ') } #[inline] fn push_inner(&mut self, item: &'a dyn DemangleAsInner<'a, W>) { log!("DemangleContext::push_inner: {:?}", item); self.inner.push(item); } #[inline] fn pop_inner(&mut self) -> Option<&'a dyn DemangleAsInner<'a, W>> { let popped = self.inner.pop(); log!("DemangleContext::pop_inner: {:?}", popped); popped } #[inline] fn pop_inner_if(&mut self, inner: &'a dyn DemangleAsInner<'a, W>) -> bool { let last = match self.inner.last() { None => return false, Some(last) => *last, }; if ptr::eq(last, inner) { self.inner.pop(); true } else { false } } fn demangle_inner_prefixes<'prev>( &mut self, scope: Option>, ) -> fmt::Result { log!("DemangleContext::demangle_inner_prefixes"); let mut new_inner = vec![]; while let Some(inner) = self.pop_inner() { if inner .downcast_to_function_type() .map_or(false, |f| !f.cv_qualifiers.is_empty()) { log!( "DemangleContext::demangle_inner_prefixes: not a prefix, saving: {:?}", inner ); new_inner.push(inner); } else { log!( "DemangleContext::demangle_inner_prefixes: demangling prefix: {:?}", inner ); inner.demangle_as_inner(self, scope)?; } } new_inner.reverse(); self.inner = new_inner; Ok(()) } fn demangle_inners<'prev>(&mut self, scope: Option>) -> fmt::Result { while let Some(inner) = self.pop_inner() { inner.demangle_as_inner(self, scope)?; } Ok(()) } fn set_source_name(&mut self, start: usize, end: usize) { let ident = &self.input[start..end]; self.source_name = str::from_utf8(ident).ok(); } fn push_demangle_node(&mut self, t: DemangleNodeType) { self.out.push_demangle_node(t); } /// This should not be called on error paths. /// pop_inner_if already doesn't balance if there are errors. fn pop_demangle_node(&mut self) { self.out.pop_demangle_node(); } } #[doc(hidden)] #[derive(Debug)] pub struct AutoDemangleContextInnerBarrier<'ctx, 'a, W> where W: 'a + DemangleWrite, 'a: 'ctx, { ctx: &'ctx mut DemangleContext<'a, W>, saved_inner: Vec<&'a dyn DemangleAsInner<'a, W>>, } impl<'ctx, 'a, W> AutoDemangleContextInnerBarrier<'ctx, 'a, W> where W: 'a + DemangleWrite, 'a: 'ctx, { /// Set aside the current inner stack on the demangle context. pub fn new(ctx: &'ctx mut DemangleContext<'a, W>) -> Self { let mut saved_inner = vec![]; mem::swap(&mut saved_inner, &mut ctx.inner); AutoDemangleContextInnerBarrier { ctx: ctx, saved_inner: saved_inner, } } } impl<'ctx, 'a, W> ops::Deref for AutoDemangleContextInnerBarrier<'ctx, 'a, W> where W: 'a + DemangleWrite, 'a: 'ctx, { type Target = DemangleContext<'a, W>; fn deref(&self) -> &Self::Target { self.ctx } } impl<'ctx, 'a, W> ops::DerefMut for AutoDemangleContextInnerBarrier<'ctx, 'a, W> where W: 'a + DemangleWrite, 'a: 'ctx, { fn deref_mut(&mut self) -> &mut Self::Target { self.ctx } } impl<'ctx, 'a, W> Drop for AutoDemangleContextInnerBarrier<'ctx, 'a, W> where W: 'a + DemangleWrite, 'a: 'ctx, { fn drop(&mut self) { // NB: We cannot assert that the context's inner is empty here, // because if demangling failed we'll unwind the stack without // using everything that put on the inner. if !self.ctx.inner.is_empty() { log!("Context inner was not emptied, did demangling fail?"); } mem::swap(&mut self.saved_inner, &mut self.ctx.inner); } } /// The inner stack allows passing AST nodes down deeper into the tree so that /// nodes that logically precede something (e.g. PointerRef) can show up after /// that thing in the demangled output. What's on the stack may not always be /// intended for the first node that looks at the stack to grab, though. /// /// Consider a function with template arguments and parameters, f(a). /// The function parameters logically precede the template arguments in the AST, /// but they must be reversed in the output. The parameters end up on the inner /// stack before processing the template argument nodes. If we're not careful, /// a node inside the template arguments might pick the function parameters /// off of the inner stack! /// /// To solve this, certain nodes act as "inner barriers". By using this macro, /// they set the existing inner stack aside and replace it with an empty stack /// while visiting their children. This allows these barrier nodes to have /// completely self-contained children. macro_rules! inner_barrier { ( $ctx:ident ) => { let mut _ctx = AutoDemangleContextInnerBarrier::new($ctx); let $ctx = &mut _ctx; }; } /// Any AST node that can be printed in a demangled form. #[doc(hidden)] pub trait Demangle<'subs, W>: fmt::Debug where W: 'subs + DemangleWrite, { /// Write the demangled form of this AST node to the given context. fn demangle<'prev, 'ctx>( &'subs self, ctx: &'ctx mut DemangleContext<'subs, W>, scope: Option>, ) -> fmt::Result; } /// Any AST node that can be printed as an inner type. /// /// See the comments surrounding `DemangleContext::inner` for details. #[doc(hidden)] pub trait DemangleAsInner<'subs, W>: Demangle<'subs, W> where W: 'subs + DemangleWrite, { /// Write the inner demangling form of this AST node to the given context. fn demangle_as_inner<'prev, 'ctx>( &'subs self, ctx: &'ctx mut DemangleContext<'subs, W>, scope: Option>, ) -> fmt::Result { self.demangle(ctx, scope) } /// Cast this `DemangleAsInner` to a `Type`. fn downcast_to_type(&self) -> Option<&Type> { None } /// Cast this `DemangleAsInner` to a `FunctionType`. fn downcast_to_function_type(&self) -> Option<&FunctionType> { None } /// Cast this `DemangleAsInner` to an `ArrayType`. fn downcast_to_array_type(&self) -> Option<&ArrayType> { None } /// Cast this `DemangleAsInner` to a `PointerToMember`. fn downcast_to_pointer_to_member(&self) -> Option<&PointerToMemberType> { None } fn is_qualified(&self) -> bool { false } } /// Demangle this thing in the leaf name position. /// /// For most things this should be the same as its `Demangle` /// implementation. For `WellKnownComponent`s we need to strip the embedded /// `std::` namespace prefix. pub(crate) trait DemangleAsLeaf<'subs, W> where W: 'subs + DemangleWrite, { fn demangle_as_leaf<'me, 'ctx>( &'me self, ctx: &'ctx mut DemangleContext<'subs, W>, ) -> fmt::Result; } macro_rules! reference_newtype { ( $newtype_name:ident , $oldtype:ty ) => { #[derive(Debug)] struct $newtype_name($oldtype); impl $newtype_name { #[allow(clippy::ptr_arg)] #[allow(unsafe_code)] fn new(types: &$oldtype) -> &$newtype_name { unsafe { // This is safe because we only create an immutable // reference. We are not breaking unique mutable aliasing // requirements. An immutable reference does not allow // dropping the referent, so no worries about double-free // (additionally, see the assertion inside `Drop` below). &*(types as *const $oldtype as *const $newtype_name) } } } impl Drop for $newtype_name { fn drop(&mut self) { unreachable!( "Dropping implies we dereferenced and took ownership, which \ is not safe for this newtype" ); } } impl ops::Deref for $newtype_name { type Target = $oldtype; fn deref(&self) -> &Self::Target { &self.0 } } }; } // We can't implement `DemangleAsInner` for newtypes of `[TypeHandle]` like we // want to because it is unsized and we need to make trait objects out of // `DemangleAsInner` for pushing onto the context's inner stack. Therefore, we // have this inelegant newtyping of `Vec`. // A set of function arguments. reference_newtype!(FunctionArgList, Vec); // A set of function arguments prefixed by a return type (which we want to // ignore). reference_newtype!(FunctionArgListAndReturnType, Vec); // A newtype around a slice of type handles that we format as function // arguments. reference_newtype!(FunctionArgSlice, [TypeHandle]); // Demangle a slice of TypeHandle as a function argument list. impl<'subs, W> Demangle<'subs, W> for FunctionArgSlice where W: 'subs + DemangleWrite, { fn demangle<'prev, 'ctx>( &'subs self, ctx: &'ctx mut DemangleContext<'subs, W>, scope: Option>, ) -> fmt::Result { let ctx = try_begin_demangle!(self, ctx, scope); let mut saw_needs_paren = false; let (needs_space, needs_paren) = ctx .inner .iter() .rev() .map(|inner| { if inner.downcast_to_pointer_to_member().is_some() { (true, true) } else { match inner.downcast_to_type() { Some(&Type::Qualified(..)) | Some(&Type::Complex(_)) | Some(&Type::Imaginary(_)) | Some(&Type::PointerToMember(_)) => (true, true), Some(&Type::PointerTo(_)) | Some(&Type::LvalueRef(_)) | Some(&Type::RvalueRef(_)) => (false, true), _ => (false, false), } } }) .take_while(|&(_, needs_paren)| { if saw_needs_paren { false } else { saw_needs_paren |= needs_paren; true } }) .fold( (false, false), |(space, paren), (next_space, next_paren)| { (space || next_space, paren || next_paren) }, ); if needs_paren { let needs_space = needs_space || match ctx.last_char_written { Some('(') | Some('*') => false, _ => true, }; if needs_space { ctx.ensure_space()?; } write!(ctx, "(")?; } ctx.demangle_inner_prefixes(scope)?; if needs_paren { write!(ctx, ")")?; } write!(ctx, "(")?; // To maintain compatibility with libiberty, print `()` instead of // `(void)` for functions that take no arguments. if self.len() == 1 && self[0].is_void() { write!(ctx, ")")?; return Ok(()); } let mut need_comma = false; for arg in self.iter() { if need_comma { write!(ctx, ", ")?; } arg.demangle(ctx, scope)?; need_comma = true; } write!(ctx, ")")?; ctx.demangle_inners(scope) } } impl<'subs, W> Demangle<'subs, W> for FunctionArgList where W: 'subs + DemangleWrite, { fn demangle<'prev, 'ctx>( &'subs self, ctx: &'ctx mut DemangleContext<'subs, W>, scope: Option>, ) -> fmt::Result { FunctionArgSlice::new(&self.0[..]).demangle(ctx, scope) } } impl<'subs, W> DemangleAsInner<'subs, W> for FunctionArgList where W: 'subs + DemangleWrite {} impl<'subs, W> Demangle<'subs, W> for FunctionArgListAndReturnType where W: 'subs + DemangleWrite, { fn demangle<'prev, 'ctx>( &'subs self, ctx: &'ctx mut DemangleContext<'subs, W>, scope: Option>, ) -> fmt::Result { FunctionArgSlice::new(&self.0[1..]).demangle(ctx, scope) } } impl<'subs, W> DemangleAsInner<'subs, W> for FunctionArgListAndReturnType where W: 'subs + DemangleWrite { } /// Define a handle to a AST type that lives inside the substitution table. A /// handle is always either an index into the substitution table, or it is a /// reference to a "well-known" component. /// /// This declares: /// /// - The enum of either a back reference into the substitution table or a /// reference to a "well-known" component /// - a `Demangle` impl that proxies to the appropriate `Substitutable` in the /// `SubstitutionTable` macro_rules! define_handle { ( $(#[$attr:meta])* pub enum $typename:ident ) => { define_handle! { $(#[$attr])* pub enum $typename {} } }; ( $(#[$attr:meta])* pub enum $typename:ident { $( $( #[$extra_attr:meta] )* extra $extra_variant:ident ( $extra_variant_ty:ty ), )* } ) => { $(#[$attr])* #[derive(Clone, Debug, PartialEq, Eq)] pub enum $typename { /// A reference to a "well-known" component. WellKnown(WellKnownComponent), /// A back-reference into the substitution table to a component we /// have already parsed. BackReference(usize), $( $( #[$extra_attr] )* $extra_variant( $extra_variant_ty ), )* } impl $typename { /// If this is a `BackReference`, get its index. pub fn back_reference(&self) -> Option { match *self { $typename::BackReference(n) => Some(n), _ => None, } } } impl<'subs, W> Demangle<'subs, W> for $typename where W: 'subs + DemangleWrite { #[inline] fn demangle<'prev, 'ctx>(&'subs self, ctx: &'ctx mut DemangleContext<'subs, W>, scope: Option>) -> fmt::Result { match *self { $typename::WellKnown(ref comp) => comp.demangle(ctx, scope), $typename::BackReference(idx) => ctx.subs[idx].demangle(ctx, scope), $( $typename::$extra_variant(ref extra) => extra.demangle(ctx, scope), )* } } } impl<'a> GetLeafName<'a> for $typename { fn get_leaf_name(&'a self, subs: &'a SubstitutionTable) -> Option> { match *self { $typename::WellKnown(ref wk) => wk.get_leaf_name(subs), $typename::BackReference(idx) => { subs.get(idx).and_then(|s| s.get_leaf_name(subs)) } $( $typename::$extra_variant(ref e) => e.get_leaf_name(subs), )* } } } }; } /// A handle to a component that is usually substitutable, and lives in the /// substitutions table, but in this particular case does not qualify for /// substitutions. #[derive(Clone, Debug, PartialEq, Eq)] pub struct NonSubstitution(usize); impl<'subs, W> Demangle<'subs, W> for NonSubstitution where W: 'subs + DemangleWrite, { fn demangle<'prev, 'ctx>( &'subs self, ctx: &'ctx mut DemangleContext<'subs, W>, scope: Option>, ) -> fmt::Result { ctx.subs.non_substitution(self.0).demangle(ctx, scope) } } impl<'a> GetLeafName<'a> for NonSubstitution { fn get_leaf_name(&'a self, subs: &'a SubstitutionTable) -> Option> { subs.get_non_substitution(self.0) .and_then(|ns| ns.get_leaf_name(subs)) } } /// Define a "vocabulary" nonterminal, something like `OperatorName` or /// `CtorDtorName` that's basically a big list of constant strings. /// /// This declares: /// /// - the enum itself /// - a `Parse` impl /// - a `Demangle` impl /// /// See the definition of `CTorDtorName` for an example of its use. /// /// Optionally, a piece of user data can be attached to the definitions /// and be returned by a generated accessor. See `SimpleOperatorName` for /// an example. macro_rules! define_vocabulary { ( $(#[$attr:meta])* pub enum $typename:ident { $($variant:ident ( $mangled:expr, $printable:expr )),* } ) => { $(#[$attr])* pub enum $typename { $( #[doc=$printable] $variant ),* } impl Parse for $typename { fn parse<'a, 'b>(ctx: &'a ParseContext, _subs: &'a mut SubstitutionTable, input: IndexStr<'b>) -> Result<($typename, IndexStr<'b>)> { try_begin_parse!(stringify!($typename), ctx, input); let mut found_prefix = false; $( if let Some((head, tail)) = input.try_split_at($mangled.len()) { if head.as_ref() == $mangled { return Ok(($typename::$variant, tail)); } } else { found_prefix |= 0 < input.len() && input.len() < $mangled.len() && input.as_ref() == &$mangled[..input.len()]; } )* if input.is_empty() || found_prefix { Err(error::Error::UnexpectedEnd) } else { Err(error::Error::UnexpectedText) } } } impl<'subs, W> Demangle<'subs, W> for $typename where W: 'subs + DemangleWrite, { fn demangle<'prev, 'ctx>( &'subs self, ctx: &'ctx mut DemangleContext<'subs, W>, scope: Option> ) -> fmt::Result { let ctx = try_begin_demangle!(self, ctx, scope); write!(ctx, "{}", match *self { $( $typename::$variant => $printable ),* }) } } impl $typename { #[allow(dead_code)] #[inline] fn starts_with(byte: u8) -> bool { $( if $mangled[0] == byte { return true; } )* false } } }; ( $(#[$attr:meta])* pub enum $typename:ident { $($variant:ident ( $mangled:expr, $printable:expr, $userdata:expr)),* } impl $typename2:ident { fn $fn_name:ident(&self) -> $userdata_ty:ty; } ) => { define_vocabulary! { $(#[$attr])* pub enum $typename { $( $variant ( $mangled, $printable ) ),* } } impl $typename2 { fn $fn_name(&self) -> $userdata_ty { match *self { $( $typename2::$variant => $userdata, )* } } } }; } /// The root AST node, and starting production. /// /// ```text /// ::= _Z []* /// ::= ___Z /// ::= /// /// ::= _block_invoke /// ::= _block_invoke+ /// ::= _block_invoke_+ /// ``` #[derive(Clone, Debug, PartialEq, Eq)] pub enum MangledName { /// The encoding of the mangled symbol name. Encoding(Encoding, Vec), /// The encoding of the mangled symbol name. BlockInvoke(Encoding, Option), /// A top-level type. Technically not allowed by the standard, however in /// practice this can happen, and is tested for by libiberty. Type(TypeHandle), /// A global constructor or destructor. This is another de facto standard /// extension (I think originally from `g++`?) that is not actually part of /// the standard proper. GlobalCtorDtor(GlobalCtorDtor), } impl Parse for MangledName { fn parse<'a, 'b>( ctx: &'a ParseContext, subs: &'a mut SubstitutionTable, input: IndexStr<'b>, ) -> Result<(MangledName, IndexStr<'b>)> { try_begin_parse!("MangledName", ctx, input); if let Ok(tail) = consume(b"_Z", input).or_else(|_| consume(b"__Z", input)) { let (encoding, tail) = Encoding::parse(ctx, subs, tail)?; let (clone_suffixes, tail) = zero_or_more(ctx, subs, tail)?; return Ok((MangledName::Encoding(encoding, clone_suffixes), tail)); } if let Ok(tail) = consume(b"___Z", input).or_else(|_| consume(b"____Z", input)) { let (encoding, tail) = Encoding::parse(ctx, subs, tail)?; let tail = consume(b"_block_invoke", tail)?; let tail_opt = match consume(b"_", tail).or_else(|_| consume(b".", tail)) { Ok(tail) => Some(parse_number(10, false, tail)?), Err(_) => parse_number(10, false, tail).ok(), }; let (digits, tail) = match tail_opt { Some((digits, tail)) => (Some(digits), tail), None => (None, tail), }; return Ok((MangledName::BlockInvoke(encoding, digits), tail)); } if let Ok(tail) = consume(b"_GLOBAL_", input) { let (global_ctor_dtor, tail) = GlobalCtorDtor::parse(ctx, subs, tail)?; return Ok((MangledName::GlobalCtorDtor(global_ctor_dtor), tail)); } // The libiberty tests also specify that a type can be top level, // and they are not prefixed with "_Z". let (ty, tail) = TypeHandle::parse(ctx, subs, input)?; Ok((MangledName::Type(ty), tail)) } } impl<'subs, W> Demangle<'subs, W> for MangledName where W: 'subs + DemangleWrite, { fn demangle<'prev, 'ctx>( &'subs self, ctx: &'ctx mut DemangleContext<'subs, W>, scope: Option>, ) -> fmt::Result { let ctx = try_begin_demangle!(self, ctx, scope); match *self { MangledName::Encoding(ref enc, ref cs) => { enc.demangle(ctx, scope)?; if !cs.is_empty() && ctx.show_params { for clone_suffix in cs { clone_suffix.demangle(ctx, scope)?; } } Ok(()) } MangledName::BlockInvoke(ref enc, _) => { write!(ctx, "invocation function for block in ")?; enc.demangle(ctx, scope)?; Ok(()) } MangledName::Type(ref ty) => ty.demangle(ctx, scope), MangledName::GlobalCtorDtor(ref gcd) => gcd.demangle(ctx, scope), } } } /// The `` production. /// /// ```text /// ::= /// ::= /// ::= /// ``` #[derive(Clone, Debug, PartialEq, Eq)] pub enum Encoding { /// An encoded function. Function(Name, BareFunctionType), /// An encoded static variable. Data(Name), /// A special encoding. Special(SpecialName), } impl Parse for Encoding { fn parse<'a, 'b>( ctx: &'a ParseContext, subs: &'a mut SubstitutionTable, input: IndexStr<'b>, ) -> Result<(Encoding, IndexStr<'b>)> { try_begin_parse!("Encoding", ctx, input); if let Ok((name, tail)) = Name::parse(ctx, subs, input) { if let Ok((ty, tail)) = BareFunctionType::parse(ctx, subs, tail) { return Ok((Encoding::Function(name, ty), tail)); } else { return Ok((Encoding::Data(name), tail)); } } let (name, tail) = SpecialName::parse(ctx, subs, input)?; Ok((Encoding::Special(name), tail)) } } impl<'subs, W> Demangle<'subs, W> for Encoding where W: 'subs + DemangleWrite, { fn demangle<'prev, 'ctx>( &'subs self, ctx: &'ctx mut DemangleContext<'subs, W>, scope: Option>, ) -> fmt::Result { let ctx = try_begin_demangle!(self, ctx, scope); inner_barrier!(ctx); match *self { Encoding::Function(ref name, ref fun_ty) => { // Even if this function takes no args and doesn't have a return // value (see below), it will have the void parameter. debug_assert!(!fun_ty.0.is_empty()); let scope = if let Some(leaf) = name.get_leaf_name(ctx.subs) { match leaf { LeafName::SourceName(leaf) => scope.push(leaf), LeafName::WellKnownComponent(leaf) => scope.push(leaf), LeafName::Closure(leaf) => scope.push(leaf), LeafName::UnnamedType(leaf) => scope.push(leaf), } } else { scope }; // Whether the first type in the BareFunctionType is a return // type or parameter depends on the context in which it // appears. // // * Templates and functions in a type or parameter position // have return types, unless they are constructors, destructors, // or conversion operator functions. // // * Non-template functions that are not in a type or parameter // position do not have a return type. // // We know we are not printing a type, so we only need to check // whether this is a template. // // For the details, see // https://itanium-cxx-abi.github.io/cxx-abi/abi.html#mangle.function-type let scope = if let Some(template_args) = name.get_template_args(ctx.subs) { let scope = scope.push(template_args); if ctx.show_return_type && !name.is_ctor_dtor_conversion(ctx.subs) { fun_ty.0[0].demangle(ctx, scope)?; write!(ctx, " ")?; } scope } else { scope }; if ctx.show_params { ctx.push_inner(self); name.demangle(ctx, scope)?; if ctx.pop_inner_if(self) { self.demangle_as_inner(ctx, scope)?; } } else { name.demangle(ctx, scope)?; } Ok(()) } Encoding::Data(ref name) => name.demangle(ctx, scope), Encoding::Special(ref name) => name.demangle(ctx, scope), } } } impl<'subs, W> DemangleAsInner<'subs, W> for Encoding where W: 'subs + DemangleWrite, { fn demangle_as_inner<'prev, 'ctx>( &'subs self, ctx: &'ctx mut DemangleContext<'subs, W>, scope: Option>, ) -> fmt::Result { if let Encoding::Function(ref name, ref fun_ty) = *self { let (scope, function_args) = if let Some(template_args) = name.get_template_args(ctx.subs) { let scope = scope.push(template_args); let function_args = FunctionArgListAndReturnType::new(&fun_ty.0); (scope, function_args as &dyn DemangleAsInner) } else { let function_args = FunctionArgList::new(&fun_ty.0); (scope, function_args as &dyn DemangleAsInner) }; function_args.demangle_as_inner(ctx, scope) } else { unreachable!("we only push Encoding::Function onto the inner stack"); } } } /// ::= [ . ] [ . ]* #[derive(Clone, Debug, PartialEq, Eq)] pub struct CloneSuffix(CloneTypeIdentifier, Vec); impl Parse for CloneSuffix { fn parse<'a, 'b>( ctx: &'a ParseContext, subs: &'a mut SubstitutionTable, input: IndexStr<'b>, ) -> Result<(CloneSuffix, IndexStr<'b>)> { try_begin_parse!("CloneSuffix", ctx, input); let tail = consume(b".", input)?; let (identifier, mut tail) = CloneTypeIdentifier::parse(ctx, subs, tail)?; let mut numbers = Vec::with_capacity(1); while let Ok((n, t)) = consume(b".", tail).and_then(|t| parse_number(10, false, t)) { numbers.push(n); tail = t; } let clone_suffix = CloneSuffix(identifier, numbers); Ok((clone_suffix, tail)) } } impl<'subs, W> Demangle<'subs, W> for CloneSuffix where W: 'subs + DemangleWrite, { fn demangle<'prev, 'ctx>( &'subs self, ctx: &'ctx mut DemangleContext<'subs, W>, scope: Option>, ) -> fmt::Result { let ctx = try_begin_demangle!(self, ctx, scope); write!(ctx, " [clone")?; self.0.demangle(ctx, scope)?; for nonnegative in &self.1 { write!(ctx, ".{}", nonnegative)?; } write!(ctx, "]")?; Ok(()) } } /// A global constructor or destructor. #[derive(Clone, Debug, PartialEq, Eq)] pub enum GlobalCtorDtor { /// A global constructor. Ctor(Box), /// A global destructor. Dtor(Box), } impl Parse for GlobalCtorDtor { fn parse<'a, 'b>( ctx: &'a ParseContext, subs: &'a mut SubstitutionTable, input: IndexStr<'b>, ) -> Result<(GlobalCtorDtor, IndexStr<'b>)> { try_begin_parse!("GlobalCtorDtor", ctx, input); let tail = match input.next_or(error::Error::UnexpectedEnd)? { (b'_', t) | (b'.', t) | (b'$', t) => t, _ => return Err(error::Error::UnexpectedText), }; match tail.next_or(error::Error::UnexpectedEnd)? { (b'I', tail) => { let tail = consume(b"_", tail)?; let (name, tail) = MangledName::parse(ctx, subs, tail)?; Ok((GlobalCtorDtor::Ctor(Box::new(name)), tail)) } (b'D', tail) => { let tail = consume(b"_", tail)?; let (name, tail) = MangledName::parse(ctx, subs, tail)?; Ok((GlobalCtorDtor::Dtor(Box::new(name)), tail)) } _ => Err(error::Error::UnexpectedText), } } } impl<'subs, W> Demangle<'subs, W> for GlobalCtorDtor where W: 'subs + DemangleWrite, { fn demangle<'prev, 'ctx>( &'subs self, ctx: &'ctx mut DemangleContext<'subs, W>, scope: Option>, ) -> fmt::Result { let ctx = try_begin_demangle!(self, ctx, scope); inner_barrier!(ctx); let saved_show_params = ctx.show_params; ctx.show_params = true; let ret = match *self { GlobalCtorDtor::Ctor(ref name) => { write!(ctx, "global constructors keyed to ")?; name.demangle(ctx, scope) } GlobalCtorDtor::Dtor(ref name) => { write!(ctx, "global destructors keyed to ")?; name.demangle(ctx, scope) } }; ctx.show_params = saved_show_params; ret } } /// The `` production. /// /// ```text /// ::= /// ::= /// ::= /// ::= /// ``` #[derive(Clone, Debug, PartialEq, Eq)] pub enum Name { /// A nested name Nested(NestedName), /// An unscoped name. Unscoped(UnscopedName), /// An unscoped template. UnscopedTemplate(UnscopedTemplateNameHandle, TemplateArgs), /// A local name. Local(LocalName), } impl Parse for Name { fn parse<'a, 'b>( ctx: &'a ParseContext, subs: &'a mut SubstitutionTable, input: IndexStr<'b>, ) -> Result<(Name, IndexStr<'b>)> { try_begin_parse!("Name", ctx, input); if let Ok((name, tail)) = NestedName::parse(ctx, subs, input) { return Ok((Name::Nested(name), tail)); } if let Ok((name, tail)) = UnscopedName::parse(ctx, subs, input) { if tail.peek() == Some(b'I') { let name = UnscopedTemplateName(name); let idx = subs.insert(Substitutable::UnscopedTemplateName(name)); let handle = UnscopedTemplateNameHandle::BackReference(idx); let (args, tail) = TemplateArgs::parse(ctx, subs, tail)?; return Ok((Name::UnscopedTemplate(handle, args), tail)); } else { return Ok((Name::Unscoped(name), tail)); } } if let Ok((name, tail)) = UnscopedTemplateNameHandle::parse(ctx, subs, input) { let (args, tail) = TemplateArgs::parse(ctx, subs, tail)?; return Ok((Name::UnscopedTemplate(name, args), tail)); } let (name, tail) = LocalName::parse(ctx, subs, input)?; Ok((Name::Local(name), tail)) } } impl<'subs, W> Demangle<'subs, W> for Name where W: 'subs + DemangleWrite, { fn demangle<'prev, 'ctx>( &'subs self, ctx: &'ctx mut DemangleContext<'subs, W>, scope: Option>, ) -> fmt::Result { let ctx = try_begin_demangle!(self, ctx, scope); match *self { Name::Nested(ref nested) => nested.demangle(ctx, scope), Name::Unscoped(ref unscoped) => unscoped.demangle(ctx, scope), Name::UnscopedTemplate(ref template, ref args) => { template.demangle(ctx, scope.push(args))?; args.demangle(ctx, scope) } Name::Local(ref local) => local.demangle(ctx, scope), } } } impl GetTemplateArgs for Name { fn get_template_args<'a>(&'a self, subs: &'a SubstitutionTable) -> Option<&'a TemplateArgs> { match *self { Name::UnscopedTemplate(_, ref args) => Some(args), Name::Nested(ref nested) => nested.get_template_args(subs), Name::Local(ref local) => local.get_template_args(subs), Name::Unscoped(_) => None, } } } impl<'a> GetLeafName<'a> for Name { fn get_leaf_name(&'a self, subs: &'a SubstitutionTable) -> Option> { match *self { Name::UnscopedTemplate(ref templ, _) => templ.get_leaf_name(subs), Name::Nested(ref nested) => nested.get_leaf_name(subs), Name::Unscoped(ref unscoped) => unscoped.get_leaf_name(subs), Name::Local(ref local) => local.get_leaf_name(subs), } } } impl IsCtorDtorConversion for Name { fn is_ctor_dtor_conversion(&self, subs: &SubstitutionTable) -> bool { match *self { Name::Unscoped(ref unscoped) => unscoped.is_ctor_dtor_conversion(subs), Name::Nested(ref nested) => nested.is_ctor_dtor_conversion(subs), Name::Local(_) | Name::UnscopedTemplate(..) => false, } } } /// The `` production. /// /// ```text /// ::= /// ::= St # ::std:: /// ``` #[derive(Clone, Debug, PartialEq, Eq)] pub enum UnscopedName { /// An unqualified name. Unqualified(UnqualifiedName), /// A name within the `std::` namespace. Std(UnqualifiedName), } impl Parse for UnscopedName { fn parse<'a, 'b>( ctx: &'a ParseContext, subs: &'a mut SubstitutionTable, input: IndexStr<'b>, ) -> Result<(UnscopedName, IndexStr<'b>)> { try_begin_parse!("UnscopedName", ctx, input); if let Ok(tail) = consume(b"St", input) { let (name, tail) = UnqualifiedName::parse(ctx, subs, tail)?; return Ok((UnscopedName::Std(name), tail)); } let (name, tail) = UnqualifiedName::parse(ctx, subs, input)?; Ok((UnscopedName::Unqualified(name), tail)) } } impl<'subs, W> Demangle<'subs, W> for UnscopedName where W: 'subs + DemangleWrite, { fn demangle<'prev, 'ctx>( &'subs self, ctx: &'ctx mut DemangleContext<'subs, W>, scope: Option>, ) -> fmt::Result { let ctx = try_begin_demangle!(self, ctx, scope); match *self { UnscopedName::Unqualified(ref unqualified) => unqualified.demangle(ctx, scope), UnscopedName::Std(ref std) => { write!(ctx, "std::")?; std.demangle(ctx, scope) } } } } impl<'a> GetLeafName<'a> for UnscopedName { fn get_leaf_name(&'a self, subs: &'a SubstitutionTable) -> Option> { match *self { UnscopedName::Unqualified(ref name) | UnscopedName::Std(ref name) => { name.get_leaf_name(subs) } } } } impl IsCtorDtorConversion for UnscopedName { fn is_ctor_dtor_conversion(&self, subs: &SubstitutionTable) -> bool { match *self { UnscopedName::Unqualified(ref name) | UnscopedName::Std(ref name) => { name.is_ctor_dtor_conversion(subs) } } } } /// The `` production. /// /// ```text /// ::= /// ::= /// ``` #[derive(Clone, Debug, PartialEq, Eq)] pub struct UnscopedTemplateName(UnscopedName); define_handle! { /// A handle to an `UnscopedTemplateName`. pub enum UnscopedTemplateNameHandle { /// A handle to some `` component that isn't by itself /// substitutable. extra NonSubstitution(NonSubstitution), } } impl Parse for UnscopedTemplateNameHandle { fn parse<'a, 'b>( ctx: &'a ParseContext, subs: &'a mut SubstitutionTable, input: IndexStr<'b>, ) -> Result<(UnscopedTemplateNameHandle, IndexStr<'b>)> { try_begin_parse!("UnscopedTemplateNameHandle", ctx, input); if let Ok((name, tail)) = UnscopedName::parse(ctx, subs, input) { let name = UnscopedTemplateName(name); let idx = subs.insert(Substitutable::UnscopedTemplateName(name)); let handle = UnscopedTemplateNameHandle::BackReference(idx); return Ok((handle, tail)); } let (sub, tail) = Substitution::parse(ctx, subs, input)?; match sub { Substitution::WellKnown(component) => { Ok((UnscopedTemplateNameHandle::WellKnown(component), tail)) } Substitution::BackReference(idx) => { // TODO: should this check/assert that subs[idx] is an // UnscopedTemplateName? Ok((UnscopedTemplateNameHandle::BackReference(idx), tail)) } } } } impl<'subs, W> Demangle<'subs, W> for UnscopedTemplateName where W: 'subs + DemangleWrite, { fn demangle<'prev, 'ctx>( &'subs self, ctx: &'ctx mut DemangleContext<'subs, W>, scope: Option>, ) -> fmt::Result { let ctx = try_begin_demangle!(self, ctx, scope); self.0.demangle(ctx, scope) } } impl<'a> GetLeafName<'a> for UnscopedTemplateName { fn get_leaf_name(&'a self, subs: &'a SubstitutionTable) -> Option> { self.0.get_leaf_name(subs) } } /// The `` production. /// /// ```text /// ::= N [] [] E /// ::= N [] [] E /// ``` #[derive(Clone, Debug, PartialEq, Eq)] pub enum NestedName { /// A nested name. Unqualified( CvQualifiers, Option, PrefixHandle, UnqualifiedName, ), /// A nested template name. The `` are part of the `PrefixHandle`. Template(CvQualifiers, Option, PrefixHandle), } impl Parse for NestedName { fn parse<'a, 'b>( ctx: &'a ParseContext, subs: &'a mut SubstitutionTable, input: IndexStr<'b>, ) -> Result<(NestedName, IndexStr<'b>)> { try_begin_parse!("NestedName", ctx, input); let tail = consume(b"N", input)?; let (cv_qualifiers, tail) = if let Ok((q, tail)) = CvQualifiers::parse(ctx, subs, tail) { (q, tail) } else { (Default::default(), tail) }; let (ref_qualifier, tail) = if let Ok((r, tail)) = RefQualifier::parse(ctx, subs, tail) { (Some(r), tail) } else { (None, tail) }; let (prefix, tail) = PrefixHandle::parse(ctx, subs, tail)?; let tail = consume(b"E", tail)?; let substitutable = match prefix { PrefixHandle::BackReference(idx) => subs.get(idx), PrefixHandle::NonSubstitution(NonSubstitution(idx)) => subs.get_non_substitution(idx), PrefixHandle::WellKnown(_) => None, }; match substitutable { Some(&Substitutable::Prefix(Prefix::Nested(ref prefix, ref name))) => Ok(( NestedName::Unqualified(cv_qualifiers, ref_qualifier, prefix.clone(), name.clone()), tail, )), Some(&Substitutable::Prefix(Prefix::Template(..))) => Ok(( NestedName::Template(cv_qualifiers, ref_qualifier, prefix), tail, )), _ => Err(error::Error::UnexpectedText), } } } impl NestedName { /// Get the CV-qualifiers for this name. pub fn cv_qualifiers(&self) -> &CvQualifiers { match *self { NestedName::Unqualified(ref q, ..) | NestedName::Template(ref q, ..) => q, } } /// Get the ref-qualifier for this name, if one exists. pub fn ref_qualifier(&self) -> Option<&RefQualifier> { match *self { NestedName::Unqualified(_, Some(ref r), ..) | NestedName::Template(_, Some(ref r), ..) => Some(r), _ => None, } } // Not public because the prefix means different things for different // variants, and for `::Template` it actually contains part of what // conceptually belongs to ``. fn prefix(&self) -> &PrefixHandle { match *self { NestedName::Unqualified(_, _, ref p, _) | NestedName::Template(_, _, ref p) => p, } } } impl<'subs, W> Demangle<'subs, W> for NestedName where W: 'subs + DemangleWrite, { fn demangle<'prev, 'ctx>( &'subs self, ctx: &'ctx mut DemangleContext<'subs, W>, scope: Option>, ) -> fmt::Result { let ctx = try_begin_demangle!(self, ctx, scope); match *self { NestedName::Unqualified(_, _, ref p, ref name) => { ctx.push_demangle_node(DemangleNodeType::NestedName); p.demangle(ctx, scope)?; if name.accepts_double_colon() { ctx.write_str("::")?; } name.demangle(ctx, scope)?; ctx.pop_demangle_node(); } NestedName::Template(_, _, ref p) => { ctx.is_template_prefix_in_nested_name = true; p.demangle(ctx, scope)?; ctx.is_template_prefix_in_nested_name = false; } } if let Some(inner) = ctx.pop_inner() { inner.demangle_as_inner(ctx, scope)?; } if self.cv_qualifiers() != &CvQualifiers::default() && ctx.show_params { self.cv_qualifiers().demangle(ctx, scope)?; } if let Some(ref refs) = self.ref_qualifier() { ctx.ensure_space()?; refs.demangle(ctx, scope)?; } Ok(()) } } impl GetTemplateArgs for NestedName { fn get_template_args<'a>(&'a self, subs: &'a SubstitutionTable) -> Option<&'a TemplateArgs> { match *self { NestedName::Template(_, _, ref prefix) => prefix.get_template_args(subs), _ => None, } } } impl<'a> GetLeafName<'a> for NestedName { fn get_leaf_name(&'a self, subs: &'a SubstitutionTable) -> Option> { match *self { NestedName::Unqualified(_, _, ref prefix, ref name) => name .get_leaf_name(subs) .or_else(|| prefix.get_leaf_name(subs)), NestedName::Template(_, _, ref prefix) => prefix.get_leaf_name(subs), } } } impl IsCtorDtorConversion for NestedName { fn is_ctor_dtor_conversion(&self, subs: &SubstitutionTable) -> bool { self.prefix().is_ctor_dtor_conversion(subs) } } /// The `` production. /// /// ```text /// ::= /// ::= /// ::= /// ::= /// ::= /// ::= /// ::= /// /// ::=