├── .gitignore ├── fuzz ├── .gitignore ├── Cargo.toml ├── fuzz_targets │ └── basic.rs └── Cargo.lock ├── rustfmt.toml ├── .github └── workflows │ ├── cargo_deny.yml │ ├── cargo_bench.yml │ ├── deploy_docs.yml │ └── main_ci.yml ├── README.md ├── src ├── alloc.rs ├── extensions.rs ├── nodes │ ├── mod.rs │ ├── enums.rs │ ├── intermediate.rs │ └── derived.rs ├── mangled_string.rs ├── lib.rs └── cache.rs ├── Cargo.toml ├── examples └── undecorate_name.rs ├── deny.toml ├── LICENSE └── Cargo.lock /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | -------------------------------------------------------------------------------- /fuzz/.gitignore: -------------------------------------------------------------------------------- 1 | target 2 | corpus 3 | artifacts 4 | coverage 5 | -------------------------------------------------------------------------------- /rustfmt.toml: -------------------------------------------------------------------------------- 1 | group_imports = "One" 2 | imports_granularity = "Crate" 3 | imports_layout = "Vertical" 4 | -------------------------------------------------------------------------------- /fuzz/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | edition = "2021" 3 | name = "undname-fuzz" 4 | publish = false 5 | version = "0.0.0" 6 | 7 | [package.metadata] 8 | cargo-fuzz = true 9 | 10 | [dependencies] 11 | libfuzzer-sys = "0.4" 12 | 13 | [dependencies.undname] 14 | path = ".." 15 | 16 | [[bin]] 17 | bench = false 18 | doc = false 19 | name = "basic" 20 | path = "fuzz_targets/basic.rs" 21 | test = false 22 | -------------------------------------------------------------------------------- /.github/workflows/cargo_deny.yml: -------------------------------------------------------------------------------- 1 | name: Cargo Deny 2 | 3 | on: 4 | push: 5 | branches: ['main'] 6 | paths: 7 | - '.github/workflows/cargo_deny.yml' 8 | - 'Cargo.lock' 9 | - 'Cargo.toml' 10 | - 'deny.toml' 11 | pull_request: 12 | branches: ['main'] 13 | workflow_dispatch: 14 | 15 | jobs: 16 | cargo-deny: 17 | runs-on: ubuntu-latest 18 | 19 | steps: 20 | - uses: actions/checkout@v4 21 | 22 | - uses: EmbarkStudios/cargo-deny-action@v1 23 | with: 24 | command: check bans licenses sources 25 | -------------------------------------------------------------------------------- /.github/workflows/cargo_bench.yml: -------------------------------------------------------------------------------- 1 | name: Cargo Bench 2 | 3 | on: 4 | push: 5 | branches: ['main'] 6 | paths: 7 | - '.github/workflows/cargo_bench.yml' 8 | - 'benches/**/*.rs' 9 | - 'src/**/*.rs' 10 | - 'Cargo.lock' 11 | - 'Cargo.toml' 12 | pull_request: 13 | branches: ['main'] 14 | workflow_dispatch: 15 | 16 | jobs: 17 | cargo-bench: 18 | runs-on: ubuntu-latest 19 | 20 | steps: 21 | - uses: actions/checkout@v4 22 | 23 | - name: Nightly 24 | run: | 25 | rustup toolchain install nightly --component clippy 26 | rustup override set nightly 27 | 28 | - name: Bench 29 | run: | 30 | cargo bench 31 | -------------------------------------------------------------------------------- /.github/workflows/deploy_docs.yml: -------------------------------------------------------------------------------- 1 | name: Deploy Docs 2 | 3 | on: 4 | push: 5 | branches: ['main'] 6 | paths: 7 | - '.github/workflows/deploy_docs.yml' 8 | - 'src/**/*.rs' 9 | - 'Cargo.toml' 10 | - 'README.md' 11 | 12 | permissions: 13 | contents: write 14 | 15 | jobs: 16 | deploy-docs: 17 | runs-on: ubuntu-latest 18 | 19 | steps: 20 | - uses: actions/checkout@v4 21 | 22 | - name: Build 23 | run: cargo doc --no-deps 24 | 25 | - name: Deploy 26 | uses: peaceiris/actions-gh-pages@v3 27 | with: 28 | force_orphan: true 29 | github_token: ${{ secrets.GITHUB_TOKEN }} 30 | publish_dir: ${{ github.workspace }}/target/doc/ 31 | -------------------------------------------------------------------------------- /fuzz/fuzz_targets/basic.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2024 Ryan McKenzie 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #![no_main] 16 | 17 | use libfuzzer_sys::fuzz_target; 18 | use undname::Flags; 19 | 20 | fuzz_target!(|data: &str| { 21 | _ = undname::demangle(data, Flags::default()); 22 | }); 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Overview 2 | `undname` is a purely Rust-based implementation of a Microsoft symbol demangler. It functions as an alternative to [`msvc-demangler`](https://crates.io/crates/msvc-demangler) and Microsoft's own [`UnDecorateSymbolName`](https://learn.microsoft.com/en-us/windows/win32/api/dbghelp/nf-dbghelp-undecoratesymbolnamew). It is closely based off of LLVM's own [`llvm-undname`](https://github.com/llvm/llvm-project/tree/4985f25ffcc4735c36967fcdbd5d46e009b25827/llvm/tools/llvm-undname) and boasts competitive [performance](https://github.com/Ryan-rsm-McKenzie/undname-rs/tree/main/benches) and better accuracy when compared to existing implementations. 3 | 4 | The latest development docs are available at: 5 | 6 | The stable release docs are available at: 7 | 8 | Changelogs are available at: 9 | 10 | # Example 11 | 12 | ```rust 13 | use undname::Flags; 14 | let result = undname::demangle("?world@@YA?AUhello@@XZ", Flags::default()).unwrap(); 15 | assert_eq!(result, "struct hello __cdecl world(void)"); 16 | ``` 17 | -------------------------------------------------------------------------------- /.github/workflows/main_ci.yml: -------------------------------------------------------------------------------- 1 | name: Main CI 2 | 3 | on: 4 | push: 5 | branches: ['main'] 6 | paths: 7 | - '.github/workflows/main_ci.yml' 8 | - 'examples/**/*.rs' 9 | - 'src/**/*.rs' 10 | - 'Cargo.lock' 11 | - 'Cargo.toml' 12 | pull_request: 13 | branches: ['main'] 14 | workflow_dispatch: 15 | 16 | env: 17 | CARGO_TERM_COLOR: always 18 | 19 | jobs: 20 | build: 21 | runs-on: ${{ matrix.os }} 22 | strategy: 23 | matrix: 24 | os: [ubuntu-latest, windows-latest, macos-latest] 25 | 26 | steps: 27 | - uses: actions/checkout@v4 28 | 29 | - name: Nightly 30 | run: | 31 | rustup toolchain install nightly --component clippy 32 | rustup override set nightly 33 | 34 | - name: Clippy 35 | run: cargo clippy -- -Dwarnings 36 | 37 | - name: Build 38 | run: cargo build 39 | 40 | - name: Miri Test 41 | if: ${{ matrix.os == 'ubuntu-latest' }} 42 | run: | 43 | rustup component add miri 44 | cargo miri setup 45 | cargo miri test 46 | 47 | - name: Test 48 | if: ${{ matrix.os != 'ubuntu-latest' }} 49 | run: cargo test 50 | -------------------------------------------------------------------------------- /src/alloc.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2024 Ryan McKenzie 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | use bumpalo::{ 16 | collections::Vec as BumpVec, 17 | Bump, 18 | }; 19 | use std::mem; 20 | 21 | #[must_use] 22 | pub(crate) fn allocate(allocator: &Bump, val: T) -> &mut T { 23 | debug_assert!(!mem::needs_drop::()); 24 | allocator.alloc(val) 25 | } 26 | 27 | #[must_use] 28 | pub(crate) fn allocate_slice<'alloc, T>(allocator: &'alloc Bump, src: &[T]) -> &'alloc [T] 29 | where 30 | T: Copy, 31 | { 32 | debug_assert!(!mem::needs_drop::()); 33 | allocator.alloc_slice_copy(src) 34 | } 35 | 36 | #[must_use] 37 | pub(crate) fn new_vec(allocator: &Bump) -> BumpVec<'_, T> { 38 | debug_assert!(!mem::needs_drop::()); 39 | BumpVec::new_in(allocator) 40 | } 41 | -------------------------------------------------------------------------------- /src/extensions.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2024 Ryan McKenzie 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | pub(crate) trait CharExt { 16 | fn is_rebased_ascii_hexdigit(&self) -> bool; 17 | fn try_convert_rebased_ascii_hexdigit_to_number(&self) -> Option; 18 | } 19 | 20 | impl CharExt for char { 21 | fn is_rebased_ascii_hexdigit(&self) -> bool { 22 | ('A'..='P').contains(self) 23 | } 24 | 25 | fn try_convert_rebased_ascii_hexdigit_to_number(&self) -> Option { 26 | if self.is_rebased_ascii_hexdigit() { 27 | let this = *self as u8; 28 | if this <= b'J' { 29 | Some(this - b'A') 30 | } else { 31 | Some(10 + this - b'K') 32 | } 33 | } else { 34 | None 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | authors = ["Ryan McKenzie"] 3 | categories = ["development-tools::debugging"] 4 | description = "A Rust library for demangling Microsoft symbols" 5 | documentation = "https://ryan-rsm-mckenzie.github.io/undname-rs/undname/index.html" 6 | edition = "2021" 7 | homepage = "https://github.com/Ryan-rsm-McKenzie/undname-rs" 8 | include = [ 9 | "benches/*", 10 | "src/*", 11 | "Cargo.toml", 12 | "LICENSE", 13 | "README.md", 14 | ] 15 | keywords = ["demangle", "c-plus-plus", "msvc"] 16 | license = "Apache-2.0 WITH LLVM-exception" 17 | name = "undname" 18 | readme = "README.md" 19 | repository = "https://github.com/Ryan-rsm-McKenzie/undname-rs" 20 | version = "2.1.2" 21 | 22 | [dependencies] 23 | arrayvec = {version = "0.7.6", default-features = false} 24 | bitflags = {version = "2.6.0", default-features = false} 25 | bumpalo = {version = "3.16.0", features = ["collections", "std"], default-features = false} 26 | nonmax = {version = "0.5.5", default-features = false} 27 | smallvec = {version = "1.13.2", default-features = false} 28 | thiserror = {version = "1.0.63", default-features = false} 29 | 30 | [dev-dependencies] 31 | clap = {version = "4.5.16", features = ["derive"]} 32 | criterion = "0.5.1" 33 | memchr = "2.7.4" 34 | msvc-demangler = "0.10.1" 35 | 36 | [[bench]] 37 | harness = false 38 | name = "comparison" 39 | 40 | [target."cfg(windows)".dev-dependencies] 41 | windows = {version = "0.58.0", features = [ 42 | "Win32_System_Diagnostics_Debug", 43 | ]} 44 | 45 | [profile.release] 46 | debug = 1 47 | -------------------------------------------------------------------------------- /examples/undecorate_name.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2024 Ryan McKenzie 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | use clap::Parser; 16 | use undname::Flags; 17 | 18 | #[derive(Parser)] 19 | struct Cli { 20 | mangled_string: String, 21 | 22 | #[arg(long)] 23 | no_calling_convention: bool, 24 | 25 | #[arg(long)] 26 | no_tag_specifier: bool, 27 | 28 | #[arg(long)] 29 | no_access_specifier: bool, 30 | 31 | #[arg(long)] 32 | no_member_type: bool, 33 | 34 | #[arg(long)] 35 | no_return_type: bool, 36 | 37 | #[arg(long)] 38 | no_variable_type: bool, 39 | 40 | #[arg(long)] 41 | no_this_type: bool, 42 | 43 | #[arg(long)] 44 | no_leading_underscores: bool, 45 | 46 | #[arg(long)] 47 | no_ms_keywords: bool, 48 | 49 | #[arg(long)] 50 | name_only: bool, 51 | } 52 | 53 | fn main() { 54 | let cli = Cli::parse(); 55 | let flags = { 56 | let mut flags = Flags::empty(); 57 | if cli.no_calling_convention { 58 | flags |= Flags::NO_CALLING_CONVENTION; 59 | } 60 | if cli.no_tag_specifier { 61 | flags |= Flags::NO_TAG_SPECIFIER; 62 | } 63 | if cli.no_access_specifier { 64 | flags |= Flags::NO_ACCESS_SPECIFIER; 65 | } 66 | if cli.no_member_type { 67 | flags |= Flags::NO_MEMBER_TYPE; 68 | } 69 | if cli.no_return_type { 70 | flags |= Flags::NO_RETURN_TYPE; 71 | } 72 | if cli.no_variable_type { 73 | flags |= Flags::NO_VARIABLE_TYPE; 74 | } 75 | if cli.no_this_type { 76 | flags |= Flags::NO_THISTYPE; 77 | } 78 | if cli.no_leading_underscores { 79 | flags |= Flags::NO_LEADING_UNDERSCORES; 80 | } 81 | if cli.no_ms_keywords { 82 | flags |= Flags::NO_MS_KEYWORDS; 83 | } 84 | if cli.name_only { 85 | flags |= Flags::NAME_ONLY; 86 | } 87 | flags 88 | }; 89 | 90 | let mangled_string = cli.mangled_string; 91 | println!("{mangled_string}"); 92 | let result = undname::demangle(&mangled_string, flags); 93 | match result { 94 | Ok(ok) => println!("{ok}"), 95 | Err(_) => println!("error: Invalid mangled name"), 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /fuzz/Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 3 4 | 5 | [[package]] 6 | name = "arbitrary" 7 | version = "1.3.2" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "7d5a26814d8dcb93b0e5a0ff3c6d80a8843bafb21b39e8e18a6f05471870e110" 10 | 11 | [[package]] 12 | name = "arrayvec" 13 | version = "0.7.4" 14 | source = "registry+https://github.com/rust-lang/crates.io-index" 15 | checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711" 16 | 17 | [[package]] 18 | name = "bitflags" 19 | version = "2.5.0" 20 | source = "registry+https://github.com/rust-lang/crates.io-index" 21 | checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1" 22 | 23 | [[package]] 24 | name = "bumpalo" 25 | version = "3.16.0" 26 | source = "registry+https://github.com/rust-lang/crates.io-index" 27 | checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" 28 | 29 | [[package]] 30 | name = "cc" 31 | version = "1.0.98" 32 | source = "registry+https://github.com/rust-lang/crates.io-index" 33 | checksum = "41c270e7540d725e65ac7f1b212ac8ce349719624d7bcff99f8e2e488e8cf03f" 34 | dependencies = [ 35 | "jobserver", 36 | "libc", 37 | "once_cell", 38 | ] 39 | 40 | [[package]] 41 | name = "jobserver" 42 | version = "0.1.31" 43 | source = "registry+https://github.com/rust-lang/crates.io-index" 44 | checksum = "d2b099aaa34a9751c5bf0878add70444e1ed2dd73f347be99003d4577277de6e" 45 | dependencies = [ 46 | "libc", 47 | ] 48 | 49 | [[package]] 50 | name = "libc" 51 | version = "0.2.155" 52 | source = "registry+https://github.com/rust-lang/crates.io-index" 53 | checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c" 54 | 55 | [[package]] 56 | name = "libfuzzer-sys" 57 | version = "0.4.7" 58 | source = "registry+https://github.com/rust-lang/crates.io-index" 59 | checksum = "a96cfd5557eb82f2b83fed4955246c988d331975a002961b07c81584d107e7f7" 60 | dependencies = [ 61 | "arbitrary", 62 | "cc", 63 | "once_cell", 64 | ] 65 | 66 | [[package]] 67 | name = "nonmax" 68 | version = "0.5.5" 69 | source = "registry+https://github.com/rust-lang/crates.io-index" 70 | checksum = "610a5acd306ec67f907abe5567859a3c693fb9886eb1f012ab8f2a47bef3db51" 71 | 72 | [[package]] 73 | name = "once_cell" 74 | version = "1.19.0" 75 | source = "registry+https://github.com/rust-lang/crates.io-index" 76 | checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" 77 | 78 | [[package]] 79 | name = "proc-macro2" 80 | version = "1.0.84" 81 | source = "registry+https://github.com/rust-lang/crates.io-index" 82 | checksum = "ec96c6a92621310b51366f1e28d05ef11489516e93be030060e5fc12024a49d6" 83 | dependencies = [ 84 | "unicode-ident", 85 | ] 86 | 87 | [[package]] 88 | name = "quote" 89 | version = "1.0.36" 90 | source = "registry+https://github.com/rust-lang/crates.io-index" 91 | checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" 92 | dependencies = [ 93 | "proc-macro2", 94 | ] 95 | 96 | [[package]] 97 | name = "smallvec" 98 | version = "1.13.2" 99 | source = "registry+https://github.com/rust-lang/crates.io-index" 100 | checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" 101 | 102 | [[package]] 103 | name = "syn" 104 | version = "2.0.66" 105 | source = "registry+https://github.com/rust-lang/crates.io-index" 106 | checksum = "c42f3f41a2de00b01c0aaad383c5a45241efc8b2d1eda5661812fda5f3cdcff5" 107 | dependencies = [ 108 | "proc-macro2", 109 | "quote", 110 | "unicode-ident", 111 | ] 112 | 113 | [[package]] 114 | name = "thiserror" 115 | version = "1.0.61" 116 | source = "registry+https://github.com/rust-lang/crates.io-index" 117 | checksum = "c546c80d6be4bc6a00c0f01730c08df82eaa7a7a61f11d656526506112cc1709" 118 | dependencies = [ 119 | "thiserror-impl", 120 | ] 121 | 122 | [[package]] 123 | name = "thiserror-impl" 124 | version = "1.0.61" 125 | source = "registry+https://github.com/rust-lang/crates.io-index" 126 | checksum = "46c3384250002a6d5af4d114f2845d37b57521033f30d5c3f46c4d70e1197533" 127 | dependencies = [ 128 | "proc-macro2", 129 | "quote", 130 | "syn", 131 | ] 132 | 133 | [[package]] 134 | name = "undname" 135 | version = "1.1.2" 136 | dependencies = [ 137 | "arrayvec", 138 | "bitflags", 139 | "bumpalo", 140 | "nonmax", 141 | "smallvec", 142 | "thiserror", 143 | ] 144 | 145 | [[package]] 146 | name = "undname-fuzz" 147 | version = "0.0.0" 148 | dependencies = [ 149 | "libfuzzer-sys", 150 | "undname", 151 | ] 152 | 153 | [[package]] 154 | name = "unicode-ident" 155 | version = "1.0.12" 156 | source = "registry+https://github.com/rust-lang/crates.io-index" 157 | checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" 158 | -------------------------------------------------------------------------------- /src/nodes/mod.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2024 Ryan McKenzie 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | mod derived; 16 | mod enums; 17 | mod intermediate; 18 | 19 | // Node* 20 | // ├── TypeNode* 21 | // │ ├── PrimitiveTypeNode 22 | // │ ├── FunctionSignatureNode 23 | // │ │ └── ThunkSignatureNode 24 | // │ ├── PointerTypeNode 25 | // │ ├── TagTypeNode 26 | // │ ├── ArrayTypeNode 27 | // │ ├── IntrinsicNode* 28 | // │ └── CustomTypeNode 29 | // ├── IdentifierNode* 30 | // │ ├── VcallThunkIdentifierNode 31 | // │ ├── DynamicStructorIdentifierNode 32 | // │ ├── NamedIdentifierNode 33 | // │ ├── IntrinsicFunctionIdentifierNode 34 | // │ ├── LiteralOperatorIdentifierNode 35 | // │ ├── LocalStaticGuardIdentifierNode 36 | // │ ├── ConversionOperatorIdentifierNode 37 | // │ ├── StructorIdentifierNode 38 | // │ └── RttiBaseClassDescriptorNode 39 | // ├── NodeArrayNode 40 | // ├── QualifiedNameNode 41 | // ├── TemplateParameterReferenceNode 42 | // ├── IntegerLiteralNode 43 | // └── SymbolNode 44 | // ├── SpecialTableSymbolNode 45 | // ├── LocalStaticGuardVariableNode 46 | // ├── EncodedStringLiteralNode 47 | // ├── VariableSymbolNode 48 | // └── FunctionSymbolNode 49 | 50 | use crate::{ 51 | cache::NodeCache, 52 | OutputFlags, 53 | Result, 54 | Writer, 55 | }; 56 | pub(crate) use derived::{ 57 | ArrayTypeNode, 58 | ConversionOperatorIdentifierNode, 59 | CustomTypeNode, 60 | DynamicStructorIdentifierNode, 61 | EncodedStringLiteralNode, 62 | FunctionSignatureNode, 63 | FunctionSymbolNode, 64 | IntegerLiteralNode, 65 | IntrinsicFunctionIdentifierNode, 66 | LiteralOperatorIdentifierNode, 67 | LocalStaticGuardIdentifierNode, 68 | LocalStaticGuardVariableNode, 69 | Md5SymbolNode, 70 | NamedIdentifierNode, 71 | NodeArrayNode, 72 | PointerTypeNode, 73 | PrimitiveTypeNode, 74 | QualifiedNameNode, 75 | RttiBaseClassDescriptorNode, 76 | SpecialTableSymbolNode, 77 | StructorIdentifierNode, 78 | TagTypeNode, 79 | TemplateParameterReferenceNode, 80 | TemplateParameters, 81 | ThunkSignatureNode, 82 | VariableSymbolName, 83 | VariableSymbolNode, 84 | VcallThunkIdentifierNode, 85 | }; 86 | pub(crate) use enums::{ 87 | CallingConv, 88 | CharKind, 89 | FuncClass, 90 | FunctionRefQualifier, 91 | IntrinsicFunctionKind, 92 | PointerAffinity, 93 | PrimitiveKind, 94 | Qualifiers, 95 | SpecialIntrinsicKind, 96 | StorageClass, 97 | TagKind, 98 | }; 99 | pub(crate) use intermediate::{ 100 | Downcast, 101 | IIdentifierNode, 102 | INode, 103 | ISignatureNode, 104 | ISymbolNode, 105 | ITypeNode, 106 | IdentifierNode, 107 | IntermediateNode, 108 | Node, 109 | SignatureNode, 110 | SymbolNode, 111 | TypeNode, 112 | }; 113 | use std::mem; 114 | 115 | fn output_space_if_necessary(ob: &mut dyn Writer) -> Result<()> { 116 | if let Some(c) = ob.last_char() { 117 | if c.is_alphanumeric() || c == '>' { 118 | write!(ob, " ")?; 119 | } 120 | } 121 | Ok(()) 122 | } 123 | 124 | pub(crate) trait WriteableNode { 125 | fn output(&self, cache: &NodeCache, ob: &mut dyn Writer, flags: OutputFlags) -> Result<()>; 126 | } 127 | 128 | trait WriteableTypeNode { 129 | fn output_pair( 130 | &self, 131 | cache: &NodeCache, 132 | ob: &mut dyn Writer, 133 | flags: OutputFlags, 134 | ) -> Result<()> { 135 | self.output_pre(cache, ob, flags)?; 136 | self.output_post(cache, ob, flags)?; 137 | Ok(()) 138 | } 139 | 140 | fn output_pre(&self, cache: &NodeCache, ob: &mut dyn Writer, flags: OutputFlags) -> Result<()>; 141 | 142 | fn output_post(&self, cache: &NodeCache, ob: &mut dyn Writer, flags: OutputFlags) 143 | -> Result<()>; 144 | } 145 | 146 | macro_rules! assert_trivial_drop { 147 | ($t:ty) => { 148 | const _: () = assert!(!mem::needs_drop::<$t>()); 149 | }; 150 | } 151 | 152 | assert_trivial_drop!(PrimitiveTypeNode); 153 | assert_trivial_drop!(FunctionSignatureNode); 154 | assert_trivial_drop!(ThunkSignatureNode); 155 | assert_trivial_drop!(PointerTypeNode); 156 | assert_trivial_drop!(TagTypeNode); 157 | assert_trivial_drop!(ArrayTypeNode); 158 | assert_trivial_drop!(CustomTypeNode); 159 | 160 | assert_trivial_drop!(VcallThunkIdentifierNode); 161 | assert_trivial_drop!(DynamicStructorIdentifierNode); 162 | assert_trivial_drop!(NamedIdentifierNode); 163 | assert_trivial_drop!(IntrinsicFunctionIdentifierNode); 164 | assert_trivial_drop!(LiteralOperatorIdentifierNode); 165 | assert_trivial_drop!(LocalStaticGuardIdentifierNode); 166 | assert_trivial_drop!(ConversionOperatorIdentifierNode); 167 | assert_trivial_drop!(StructorIdentifierNode); 168 | assert_trivial_drop!(RttiBaseClassDescriptorNode); 169 | 170 | assert_trivial_drop!(NodeArrayNode); 171 | assert_trivial_drop!(QualifiedNameNode); 172 | assert_trivial_drop!(TemplateParameterReferenceNode); 173 | assert_trivial_drop!(IntegerLiteralNode); 174 | 175 | assert_trivial_drop!(SpecialTableSymbolNode); 176 | assert_trivial_drop!(LocalStaticGuardVariableNode); 177 | assert_trivial_drop!(EncodedStringLiteralNode); 178 | assert_trivial_drop!(VariableSymbolNode); 179 | assert_trivial_drop!(FunctionSymbolNode); 180 | -------------------------------------------------------------------------------- /src/mangled_string.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2024 Ryan McKenzie 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | use crate::extensions::CharExt as _; 16 | use std::slice::SliceIndex; 17 | 18 | #[derive(Clone, Copy)] 19 | pub(crate) struct MangledString<'string> { 20 | string: &'string str, 21 | chars: usize, 22 | } 23 | 24 | impl<'string> MangledString<'string> { 25 | pub(crate) fn new(string: &'string str) -> Self { 26 | Self { 27 | string, 28 | chars: string.chars().count(), 29 | } 30 | } 31 | 32 | // advancing mangled string 33 | 34 | pub(crate) fn try_consume(&mut self) -> Option { 35 | let mut iter = self.string.char_indices(); 36 | let (_, c) = iter.next()?; 37 | self.string = iter.next().map_or("", |(i, _)| &self.string[i..]); 38 | self.chars -= 1; 39 | Some(c) 40 | } 41 | 42 | pub(crate) fn try_consume_char(&mut self, expected: char) -> Option { 43 | let mut iter = self.string.char_indices(); 44 | let (_, read) = iter.next()?; 45 | (read == expected).then(|| { 46 | self.string = iter.next().map_or("", |(i, _)| &self.string[i..]); 47 | self.chars -= 1; 48 | expected 49 | }) 50 | } 51 | 52 | pub(crate) fn try_consume_char_if(&mut self, f: F) -> Option 53 | where 54 | F: FnOnce(&char) -> bool, 55 | { 56 | let mut iter = self.string.char_indices(); 57 | let (_, c) = iter.next()?; 58 | f(&c).then(|| { 59 | self.string = iter.next().map_or("", |(i, _)| &self.string[i..]); 60 | self.chars -= 1; 61 | c 62 | }) 63 | } 64 | 65 | pub(crate) fn try_consume_n_bytes(&mut self, n: usize) -> Option<&'string str> { 66 | self.string.split_at_checked(n).map(|(first, rest)| { 67 | self.string = rest; 68 | self.chars -= first.chars().count(); 69 | first 70 | }) 71 | } 72 | 73 | pub(crate) fn try_consume_n_chars(&mut self) -> Option<[char; N]> { 74 | let mut iter = self.string.char_indices(); 75 | let mut chars = ['\0'; N]; 76 | for ch in &mut chars { 77 | let (_, c) = iter.next()?; 78 | *ch = c; 79 | } 80 | self.string = iter.next().map_or("", |(i, _)| &self.string[i..]); 81 | self.chars -= N; 82 | Some(chars) 83 | } 84 | 85 | pub(crate) fn try_consume_str<'s>(&mut self, s: &'s str) -> Option<&'s str> { 86 | self.string.starts_with(s).then(|| { 87 | self.string = &self.string[s.len()..]; 88 | self.chars -= s.chars().count(); 89 | s 90 | }) 91 | } 92 | 93 | // inner string utils 94 | 95 | pub(crate) fn as_str(&self) -> &'string str { 96 | self.string 97 | } 98 | 99 | pub(crate) fn find_char(&self, needle: char) -> Option { 100 | for (i, c) in self.string.char_indices() { 101 | if c == needle { 102 | return Some(i); 103 | } 104 | } 105 | None 106 | } 107 | 108 | pub(crate) fn first_char(&self) -> Option { 109 | self.string.chars().next() 110 | } 111 | 112 | pub(crate) fn is_empty(&self) -> bool { 113 | self.string.is_empty() 114 | } 115 | 116 | pub(crate) fn last_char(&self) -> Option { 117 | self.string.chars().next_back() 118 | } 119 | 120 | pub(crate) fn len_bytes(&self) -> usize { 121 | self.string.len() 122 | } 123 | 124 | pub(crate) fn len_chars(&self) -> usize { 125 | self.chars 126 | } 127 | 128 | fn slice(&self, index: I) -> Self 129 | where 130 | I: SliceIndex, 131 | { 132 | let string = &self.string[index]; 133 | Self { 134 | string, 135 | chars: string.chars().count(), 136 | } 137 | } 138 | 139 | pub(crate) fn starts_with(&self, what: &str) -> bool { 140 | self.string.starts_with(what) 141 | } 142 | 143 | // mangled string utils 144 | 145 | pub(crate) fn starts_with_local_scope_pattern(mut self) -> bool { 146 | if self.try_consume_char('?').is_none() { 147 | return false; 148 | } 149 | 150 | let Some(end) = self.find_char('?') else { 151 | return false; 152 | }; 153 | let candidate = self.slice(..end); 154 | if candidate.is_empty() { 155 | return false; 156 | } 157 | 158 | // \?[0-9]\? 159 | // ?@? is the discriminator 0. 160 | if candidate.len_chars() == 1 { 161 | // SAFETY: we just checked that the string has a length of 1 162 | let c = unsafe { candidate.first_char().unwrap_unchecked() }; 163 | return c == '@' || c.is_ascii_digit(); 164 | } 165 | 166 | // If it's not 0-9, then it's an encoded number terminated with an @ 167 | let Some('@') = candidate.last_char() else { 168 | return false; 169 | }; 170 | let mut candidate = candidate.slice(..candidate.len_bytes() - 1); 171 | 172 | // An encoded number starts with B-P and all subsequent digits are in A-P. 173 | // Note that the reason the first digit cannot be A is two fold. First, it 174 | // would create an ambiguity with ?A which delimits the beginning of an 175 | // anonymous namespace. Second, A represents 0, and you don't start a multi 176 | // digit number with a leading 0. Presumably the anonymous namespace 177 | // ambiguity is also why single digit encoded numbers use 0-9 rather than A-J. 178 | if candidate 179 | .try_consume_char_if(|x| ('B'..='P').contains(x)) 180 | .is_none() 181 | { 182 | return false; 183 | } 184 | while let Some(c) = candidate.try_consume() { 185 | if !c.is_rebased_ascii_hexdigit() { 186 | return false; 187 | } 188 | } 189 | 190 | true 191 | } 192 | 193 | pub(crate) fn is_array_type(self) -> bool { 194 | self.first_char().is_some_and(|x| x == 'Y') 195 | } 196 | 197 | pub(crate) fn is_custom_type(self) -> bool { 198 | self.first_char().is_some_and(|x| x == '?') 199 | } 200 | 201 | pub(crate) fn is_function_type(self) -> bool { 202 | self.starts_with("$$A8@@") || self.starts_with("$$A6") 203 | } 204 | 205 | pub(crate) fn is_member_pointer(mut self) -> Option { 206 | match self.try_consume()? { 207 | // This is probably an rvalue reference (e.g. $$Q), and you cannot have an 208 | // rvalue reference to a member. 209 | // 'A' indicates a reference, and you cannot have a reference to a member 210 | // function or member. 211 | '$' | 'A' => return Some(false), 212 | // These 4 values indicate some kind of pointer, but we still don't know 213 | // what. 214 | 'P' | 'Q' | 'R' | 'S' => (), 215 | _ => return None, 216 | } 217 | 218 | // If it starts with a number, then 6 indicates a non-member function 219 | // pointer, and 8 indicates a member function pointer. 220 | if let Some(digit) = self.try_consume_char_if(char::is_ascii_digit) { 221 | return match digit { 222 | '6' => Some(false), 223 | '8' => Some(true), 224 | _ => None, 225 | }; 226 | } 227 | 228 | // Remove ext qualifiers since those can appear on either type and are 229 | // therefore not indicative. 230 | _ = self.try_consume_char('E'); // 64-bit 231 | _ = self.try_consume_char('I'); // restrict 232 | _ = self.try_consume_char('F'); // unaligned 233 | 234 | if self.is_empty() { 235 | return None; 236 | } 237 | 238 | // The next value should be either ABCD (non-member) or QRST (member). 239 | match self.first_char() { 240 | Some('A' | 'B' | 'C' | 'D') => Some(false), 241 | Some('Q' | 'R' | 'S' | 'T') => Some(true), 242 | _ => None, 243 | } 244 | } 245 | 246 | pub(crate) fn is_pointer_type(self) -> bool { 247 | if self.starts_with("$$Q") { 248 | // foo && 249 | true 250 | } else { 251 | // A -> foo & 252 | // P -> foo * 253 | // Q -> foo *const 254 | // R -> foo *volatile 255 | // S -> foo *const volatile 256 | matches!(self.first_char(), Some('A' | 'P' | 'Q' | 'R' | 'S')) 257 | } 258 | } 259 | 260 | pub(crate) fn is_tag_type(self) -> bool { 261 | // T -> union 262 | // U -> struct 263 | // V -> class 264 | // W -> enum 265 | matches!(self.first_char(), Some('T' | 'U' | 'V' | 'W')) 266 | } 267 | } 268 | -------------------------------------------------------------------------------- /deny.toml: -------------------------------------------------------------------------------- 1 | # This template contains all of the possible sections and their default values 2 | 3 | # Note that all fields that take a lint level have these possible values: 4 | # * deny - An error will be produced and the check will fail 5 | # * warn - A warning will be produced, but the check will not fail 6 | # * allow - No warning or error will be produced, though in some cases a note 7 | # will be 8 | 9 | # The values provided in this template are the default values that will be used 10 | # when any section or field is not specified in your own configuration 11 | 12 | # Root options 13 | 14 | # The graph table configures how the dependency graph is constructed and thus 15 | # which crates the checks are performed against 16 | [graph] 17 | # If 1 or more target triples (and optionally, target_features) are specified, 18 | # only the specified targets will be checked when running `cargo deny check`. 19 | # This means, if a particular package is only ever used as a target specific 20 | # dependency, such as, for example, the `nix` crate only being used via the 21 | # `target_family = "unix"` configuration, that only having windows targets in 22 | # this list would mean the nix crate, as well as any of its exclusive 23 | # dependencies not shared by any other crates, would be ignored, as the target 24 | # list here is effectively saying which targets you are building for. 25 | targets = [ 26 | # The triple can be any string, but only the target triples built in to 27 | # rustc (as of 1.40) can be checked against actual config expressions 28 | #"x86_64-unknown-linux-musl", 29 | # You can also specify which target_features you promise are enabled for a 30 | # particular target. target_features are currently not validated against 31 | # the actual valid features supported by the target architecture. 32 | #{ triple = "wasm32-unknown-unknown", features = ["atomics"] }, 33 | ] 34 | # When creating the dependency graph used as the source of truth when checks are 35 | # executed, this field can be used to prune crates from the graph, removing them 36 | # from the view of cargo-deny. This is an extremely heavy hammer, as if a crate 37 | # is pruned from the graph, all of its dependencies will also be pruned unless 38 | # they are connected to another crate in the graph that hasn't been pruned, 39 | # so it should be used with care. The identifiers are [Package ID Specifications] 40 | # (https://doc.rust-lang.org/cargo/reference/pkgid-spec.html) 41 | #exclude = [] 42 | # If true, metadata will be collected with `--all-features`. Note that this can't 43 | # be toggled off if true, if you want to conditionally enable `--all-features` it 44 | # is recommended to pass `--all-features` on the cmd line instead 45 | all-features = false 46 | # If true, metadata will be collected with `--no-default-features`. The same 47 | # caveat with `all-features` applies 48 | no-default-features = false 49 | # If set, these feature will be enabled when collecting metadata. If `--features` 50 | # is specified on the cmd line they will take precedence over this option. 51 | #features = [] 52 | 53 | # The output table provides options for how/if diagnostics are outputted 54 | [output] 55 | # When outputting inclusion graphs in diagnostics that include features, this 56 | # option can be used to specify the depth at which feature edges will be added. 57 | # This option is included since the graphs can be quite large and the addition 58 | # of features from the crate(s) to all of the graph roots can be far too verbose. 59 | # This option can be overridden via `--feature-depth` on the cmd line 60 | feature-depth = 1 61 | 62 | # This section is considered when running `cargo deny check advisories` 63 | # More documentation for the advisories section can be found here: 64 | # https://embarkstudios.github.io/cargo-deny/checks/advisories/cfg.html 65 | [advisories] 66 | # The path where the advisory databases are cloned/fetched into 67 | #db-path = "$CARGO_HOME/advisory-dbs" 68 | # The url(s) of the advisory databases to use 69 | #db-urls = ["https://github.com/rustsec/advisory-db"] 70 | # A list of advisory IDs to ignore. Note that ignored advisories will still 71 | # output a note when they are encountered. 72 | ignore = [ 73 | #"RUSTSEC-0000-0000", 74 | #{ id = "RUSTSEC-0000-0000", reason = "you can specify a reason the advisory is ignored" }, 75 | #"a-crate-that-is-yanked@0.1.1", # you can also ignore yanked crate versions if you wish 76 | #{ crate = "a-crate-that-is-yanked@0.1.1", reason = "you can specify why you are ignoring the yanked crate" }, 77 | ] 78 | # If this is true, then cargo deny will use the git executable to fetch advisory database. 79 | # If this is false, then it uses a built-in git library. 80 | # Setting this to true can be helpful if you have special authentication requirements that cargo-deny does not support. 81 | # See Git Authentication for more information about setting up git authentication. 82 | #git-fetch-with-cli = true 83 | 84 | # This section is considered when running `cargo deny check licenses` 85 | # More documentation for the licenses section can be found here: 86 | # https://embarkstudios.github.io/cargo-deny/checks/licenses/cfg.html 87 | [licenses] 88 | # List of explicitly allowed licenses 89 | # See https://spdx.org/licenses/ for list of possible licenses 90 | # [possible values: any SPDX 3.11 short identifier (+ optional exception)]. 91 | allow = [ 92 | "MIT", 93 | "Unicode-DFS-2016", 94 | "Apache-2.0 WITH LLVM-exception", 95 | ] 96 | # The confidence threshold for detecting a license from license text. 97 | # The higher the value, the more closely the license text must be to the 98 | # canonical license text of a valid SPDX license file. 99 | # [possible values: any between 0.0 and 1.0]. 100 | confidence-threshold = 0.8 101 | # Allow 1 or more licenses on a per-crate basis, so that particular licenses 102 | # aren't accepted for every possible crate as with the normal allow list 103 | exceptions = [ 104 | # Each entry is the crate and version constraint, and its specific allow 105 | # list 106 | #{ allow = ["Zlib"], crate = "adler32" }, 107 | ] 108 | 109 | # Some crates don't have (easily) machine readable licensing information, 110 | # adding a clarification entry for it allows you to manually specify the 111 | # licensing information 112 | #[[licenses.clarify]] 113 | # The package spec the clarification applies to 114 | #crate = "ring" 115 | # The SPDX expression for the license requirements of the crate 116 | #expression = "MIT AND ISC AND OpenSSL" 117 | # One or more files in the crate's source used as the "source of truth" for 118 | # the license expression. If the contents match, the clarification will be used 119 | # when running the license check, otherwise the clarification will be ignored 120 | # and the crate will be checked normally, which may produce warnings or errors 121 | # depending on the rest of your configuration 122 | #license-files = [ 123 | # Each entry is a crate relative path, and the (opaque) hash of its contents 124 | #{ path = "LICENSE", hash = 0xbd0eed23 } 125 | #] 126 | 127 | [licenses.private] 128 | # If true, ignores workspace crates that aren't published, or are only 129 | # published to private registries. 130 | # To see how to mark a crate as unpublished (to the official registry), 131 | # visit https://doc.rust-lang.org/cargo/reference/manifest.html#the-publish-field. 132 | ignore = false 133 | # One or more private registries that you might publish crates to, if a crate 134 | # is only published to private registries, and ignore is true, the crate will 135 | # not have its license(s) checked 136 | registries = [ 137 | #"https://sekretz.com/registry 138 | ] 139 | 140 | # This section is considered when running `cargo deny check bans`. 141 | # More documentation about the 'bans' section can be found here: 142 | # https://embarkstudios.github.io/cargo-deny/checks/bans/cfg.html 143 | [bans] 144 | # Lint level for when multiple versions of the same crate are detected 145 | multiple-versions = "warn" 146 | # Lint level for when a crate version requirement is `*` 147 | wildcards = "allow" 148 | # The graph highlighting used when creating dotgraphs for crates 149 | # with multiple versions 150 | # * lowest-version - The path to the lowest versioned duplicate is highlighted 151 | # * simplest-path - The path to the version with the fewest edges is highlighted 152 | # * all - Both lowest-version and simplest-path are used 153 | highlight = "all" 154 | # The default lint level for `default` features for crates that are members of 155 | # the workspace that is being checked. This can be overridden by allowing/denying 156 | # `default` on a crate-by-crate basis if desired. 157 | workspace-default-features = "allow" 158 | # The default lint level for `default` features for external crates that are not 159 | # members of the workspace. This can be overridden by allowing/denying `default` 160 | # on a crate-by-crate basis if desired. 161 | external-default-features = "allow" 162 | # List of crates that are allowed. Use with care! 163 | allow = [ 164 | #"ansi_term@0.11.0", 165 | #{ crate = "ansi_term@0.11.0", reason = "you can specify a reason it is allowed" }, 166 | ] 167 | # List of crates to deny 168 | deny = [ 169 | #"ansi_term@0.11.0", 170 | #{ crate = "ansi_term@0.11.0", reason = "you can specify a reason it is banned" }, 171 | # Wrapper crates can optionally be specified to allow the crate when it 172 | # is a direct dependency of the otherwise banned crate 173 | #{ crate = "ansi_term@0.11.0", wrappers = ["this-crate-directly-depends-on-ansi_term"] }, 174 | ] 175 | 176 | # List of features to allow/deny 177 | # Each entry the name of a crate and a version range. If version is 178 | # not specified, all versions will be matched. 179 | #[[bans.features]] 180 | #crate = "reqwest" 181 | # Features to not allow 182 | #deny = ["json"] 183 | # Features to allow 184 | #allow = [ 185 | # "rustls", 186 | # "__rustls", 187 | # "__tls", 188 | # "hyper-rustls", 189 | # "rustls", 190 | # "rustls-pemfile", 191 | # "rustls-tls-webpki-roots", 192 | # "tokio-rustls", 193 | # "webpki-roots", 194 | #] 195 | # If true, the allowed features must exactly match the enabled feature set. If 196 | # this is set there is no point setting `deny` 197 | #exact = true 198 | 199 | # Certain crates/versions that will be skipped when doing duplicate detection. 200 | skip = [ 201 | #"ansi_term@0.11.0", 202 | #{ crate = "ansi_term@0.11.0", reason = "you can specify a reason why it can't be updated/removed" }, 203 | ] 204 | # Similarly to `skip` allows you to skip certain crates during duplicate 205 | # detection. Unlike skip, it also includes the entire tree of transitive 206 | # dependencies starting at the specified crate, up to a certain depth, which is 207 | # by default infinite. 208 | skip-tree = [ 209 | #"ansi_term@0.11.0", # will be skipped along with _all_ of its direct and transitive dependencies 210 | #{ crate = "ansi_term@0.11.0", depth = 20 }, 211 | ] 212 | 213 | # This section is considered when running `cargo deny check sources`. 214 | # More documentation about the 'sources' section can be found here: 215 | # https://embarkstudios.github.io/cargo-deny/checks/sources/cfg.html 216 | [sources] 217 | # Lint level for what to happen when a crate from a crate registry that is not 218 | # in the allow list is encountered 219 | unknown-registry = "warn" 220 | # Lint level for what to happen when a crate from a git repository that is not 221 | # in the allow list is encountered 222 | unknown-git = "warn" 223 | # List of URLs for allowed crate registries. Defaults to the crates.io index 224 | # if not specified. If it is specified but empty, no registries are allowed. 225 | allow-registry = ["https://github.com/rust-lang/crates.io-index"] 226 | # List of URLs for allowed Git repositories 227 | allow-git = [] 228 | 229 | [sources.allow-org] 230 | # github.com organizations to allow git sources for 231 | github = [] 232 | # gitlab.com organizations to allow git sources for 233 | gitlab = [] 234 | # bitbucket.org organizations to allow git sources for 235 | bitbucket = [] 236 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright [yyyy] [name of copyright owner] 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | http://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. 203 | 204 | 205 | ---- LLVM Exceptions to the Apache 2.0 License ---- 206 | 207 | As an exception, if, as a result of your compiling your source code, portions 208 | of this Software are embedded into an Object form of such source code, you 209 | may redistribute such embedded portions in such Object form without complying 210 | with the conditions of Sections 4(a), 4(b) and 4(d) of the License. 211 | 212 | In addition, if you combine or link compiled forms of this Software with 213 | software that is licensed under the GPLv2 ("Combined Software") and if a 214 | court of competent jurisdiction determines that the patent provision (Section 215 | 3), the indemnity provision (Section 9) or other Section of the License 216 | conflicts with the conditions of the GPLv2, you may retroactively and 217 | prospectively choose to deem waived or otherwise exclude such Section(s) of 218 | the License, but only in their entirety and only with respect to the Combined 219 | Software. 220 | -------------------------------------------------------------------------------- /src/nodes/enums.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2024 Ryan McKenzie 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | use crate::{ 16 | nodes::Result, 17 | OutputFlags, 18 | Writer, 19 | }; 20 | 21 | bitflags::bitflags! { 22 | // Storage classes 23 | #[derive(Clone, Copy, Default, Eq, PartialEq)] 24 | pub(crate) struct Qualifiers: u8 { 25 | const Q_None = 0; 26 | const Q_Const = 1 << 0; 27 | const Q_Volatile = 1 << 1; 28 | const Q_Far = 1 << 2; 29 | const Q_Huge = 1 << 3; 30 | const Q_Unaligned = 1 << 4; 31 | const Q_Restrict = 1 << 5; 32 | const Q_Pointer64 = 1 << 6; 33 | } 34 | } 35 | 36 | impl From for Qualifiers { 37 | fn from(value: SingleQualifier) -> Self { 38 | match value { 39 | SingleQualifier::Const => Self::Q_Const, 40 | SingleQualifier::Volatile => Self::Q_Volatile, 41 | SingleQualifier::Restrict => Self::Q_Restrict, 42 | } 43 | } 44 | } 45 | 46 | #[derive(Clone, Copy)] 47 | enum SingleQualifier { 48 | Const, 49 | Volatile, 50 | Restrict, 51 | } 52 | 53 | impl SingleQualifier { 54 | fn output_single_qualifier(self, ob: &mut dyn Writer, flags: OutputFlags) -> Result<()> { 55 | let qualifier = match self { 56 | Self::Const => "const", 57 | Self::Volatile => "volatile", 58 | Self::Restrict => { 59 | if flags.no_ms_keywords() { 60 | return Ok(()); 61 | } 62 | if flags.no_leading_underscores() { 63 | "restrict" 64 | } else { 65 | "__restrict" 66 | } 67 | } 68 | }; 69 | write!(ob, "{qualifier}")?; 70 | Ok(()) 71 | } 72 | } 73 | 74 | impl Qualifiers { 75 | #[must_use] 76 | pub(super) fn is_const(self) -> bool { 77 | self.contains(Self::Q_Const) 78 | } 79 | 80 | #[must_use] 81 | pub(super) fn is_volatile(self) -> bool { 82 | self.contains(Self::Q_Volatile) 83 | } 84 | 85 | #[must_use] 86 | pub(super) fn is_unaligned(self) -> bool { 87 | self.contains(Self::Q_Unaligned) 88 | } 89 | 90 | #[must_use] 91 | pub(super) fn is_restrict(self) -> bool { 92 | self.contains(Self::Q_Restrict) 93 | } 94 | 95 | pub(super) fn output( 96 | self, 97 | ob: &mut dyn Writer, 98 | flags: OutputFlags, 99 | space_before: bool, 100 | space_after: bool, 101 | ) -> Result<()> { 102 | if self != Self::Q_None { 103 | let len_before = ob.len_bytes(); 104 | let space_before = 105 | self.output_if_present(ob, flags, SingleQualifier::Const, space_before)?; 106 | let space_before = 107 | self.output_if_present(ob, flags, SingleQualifier::Volatile, space_before)?; 108 | self.output_if_present(ob, flags, SingleQualifier::Restrict, space_before)?; 109 | let len_after = ob.len_bytes(); 110 | if space_after && len_after > len_before { 111 | write!(ob, " ")?; 112 | } 113 | } 114 | 115 | Ok(()) 116 | } 117 | 118 | fn output_if_present( 119 | self, 120 | ob: &mut dyn Writer, 121 | flags: OutputFlags, 122 | mask: SingleQualifier, 123 | needs_space: bool, 124 | ) -> Result { 125 | if !self.contains(mask.into()) { 126 | return Ok(needs_space); 127 | } 128 | 129 | if needs_space { 130 | write!(ob, " ")?; 131 | } 132 | 133 | mask.output_single_qualifier(ob, flags)?; 134 | Ok(true) 135 | } 136 | } 137 | 138 | #[derive(Clone, Copy)] 139 | pub(crate) enum StorageClass { 140 | PrivateStatic, 141 | ProtectedStatic, 142 | PublicStatic, 143 | Global, 144 | FunctionLocalStatic, 145 | } 146 | 147 | #[derive(Clone, Copy, Eq, PartialEq)] 148 | pub(crate) enum PointerAffinity { 149 | Pointer, 150 | Reference, 151 | RValueReference, 152 | } 153 | 154 | #[derive(Clone, Copy)] 155 | pub(crate) enum FunctionRefQualifier { 156 | Reference, 157 | RValueReference, 158 | } 159 | 160 | // Calling conventions 161 | #[derive(Clone, Copy)] 162 | pub(crate) enum CallingConv { 163 | Cdecl, 164 | Pascal, 165 | Thiscall, 166 | Stdcall, 167 | Fastcall, 168 | Clrcall, 169 | Eabi, 170 | Vectorcall, 171 | Swift, // Clang-only 172 | SwiftAsync, // Clang-only 173 | } 174 | 175 | impl CallingConv { 176 | pub(super) fn output(self, ob: &mut dyn Writer, flags: OutputFlags) -> Result<()> { 177 | super::output_space_if_necessary(ob)?; 178 | let cc = if flags.no_leading_underscores() { 179 | match self { 180 | CallingConv::Cdecl => "cdecl", 181 | CallingConv::Fastcall => "fastcall", 182 | CallingConv::Pascal => "pascal", 183 | CallingConv::Stdcall => "stdcall", 184 | CallingConv::Thiscall => "thiscall", 185 | CallingConv::Eabi => "eabi", 186 | CallingConv::Vectorcall => "vectorcall", 187 | CallingConv::Clrcall => "clrcall", 188 | CallingConv::Swift => "__attribute__((__swiftcall__)) ", 189 | CallingConv::SwiftAsync => "__attribute__((__swiftasynccall__)) ", 190 | } 191 | } else { 192 | match self { 193 | CallingConv::Cdecl => "__cdecl", 194 | CallingConv::Fastcall => "__fastcall", 195 | CallingConv::Pascal => "__pascal", 196 | CallingConv::Stdcall => "__stdcall", 197 | CallingConv::Thiscall => "__thiscall", 198 | CallingConv::Eabi => "__eabi", 199 | CallingConv::Vectorcall => "__vectorcall", 200 | CallingConv::Clrcall => "__clrcall", 201 | CallingConv::Swift => "__attribute__((__swiftcall__)) ", 202 | CallingConv::SwiftAsync => "__attribute__((__swiftasynccall__)) ", 203 | } 204 | }; 205 | write!(ob, "{cc}")?; 206 | Ok(()) 207 | } 208 | } 209 | 210 | #[derive(Clone, Copy)] 211 | pub(crate) enum PrimitiveKind { 212 | Void, 213 | Bool, 214 | Char, 215 | Schar, 216 | Uchar, 217 | Char8, 218 | Char16, 219 | Char32, 220 | Short, 221 | Ushort, 222 | Int, 223 | Uint, 224 | Long, 225 | Ulong, 226 | Int64, 227 | Uint64, 228 | Wchar, 229 | Float, 230 | Double, 231 | Ldouble, 232 | Nullptr, 233 | Auto, 234 | DecltypeAuto, 235 | } 236 | 237 | #[derive(Clone, Copy)] 238 | pub(crate) enum CharKind { 239 | Char, 240 | Char16, 241 | Char32, 242 | Wchar, 243 | } 244 | 245 | #[derive(Clone, Copy)] 246 | pub(crate) enum IntrinsicFunctionKind { 247 | New, // ?2 # operator new 248 | Delete, // ?3 # operator delete 249 | Assign, // ?4 # operator= 250 | RightShift, // ?5 # operator>> 251 | LeftShift, // ?6 # operator<< 252 | LogicalNot, // ?7 # operator! 253 | Equals, // ?8 # operator== 254 | NotEquals, // ?9 # operator!= 255 | ArraySubscript, // ?A # operator[] 256 | Pointer, // ?C # operator-> 257 | Dereference, // ?D # operator* 258 | Increment, // ?E # operator++ 259 | Decrement, // ?F # operator-- 260 | Minus, // ?G # operator- 261 | Plus, // ?H # operator+ 262 | BitwiseAnd, // ?I # operator& 263 | MemberPointer, // ?J # operator->* 264 | Divide, // ?K # operator/ 265 | Modulus, // ?L # operator% 266 | LessThan, // ?M operator< 267 | LessThanEqual, // ?N operator<= 268 | GreaterThan, // ?O operator> 269 | GreaterThanEqual, // ?P operator>= 270 | Comma, // ?Q operator, 271 | Parens, // ?R operator() 272 | BitwiseNot, // ?S operator~ 273 | BitwiseXor, // ?T operator^ 274 | BitwiseOr, // ?U operator| 275 | LogicalAnd, // ?V operator&& 276 | LogicalOr, // ?W operator|| 277 | TimesEqual, // ?X operator*= 278 | PlusEqual, // ?Y operator+= 279 | MinusEqual, // ?Z operator-= 280 | DivEqual, // ?_0 operator/= 281 | ModEqual, // ?_1 operator%= 282 | RshEqual, // ?_2 operator>>= 283 | LshEqual, // ?_3 operator<<= 284 | BitwiseAndEqual, // ?_4 operator&= 285 | BitwiseOrEqual, // ?_5 operator|= 286 | BitwiseXorEqual, // ?_6 operator^= 287 | VbaseDtor, // ?_D # vbase destructor 288 | VecDelDtor, // ?_E # vector deleting destructor 289 | DefaultCtorClosure, // ?_F # default constructor closure 290 | ScalarDelDtor, // ?_G # scalar deleting destructor 291 | VecCtorIter, // ?_H # vector constructor iterator 292 | VecDtorIter, // ?_I # vector destructor iterator 293 | VecVbaseCtorIter, // ?_J # vector vbase constructor iterator 294 | VdispMap, // ?_K # virtual displacement map 295 | EHVecCtorIter, // ?_L # eh vector constructor iterator 296 | EHVecDtorIter, // ?_M # eh vector destructor iterator 297 | EHVecVbaseCtorIter, // ?_N # eh vector vbase constructor iterator 298 | CopyCtorClosure, // ?_O # copy constructor closure 299 | LocalVftableCtorClosure, // ?_T # local vftable constructor closure 300 | ArrayNew, // ?_U operator new[] 301 | ArrayDelete, // ?_V operator delete[] 302 | ManVectorCtorIter, // ?__A managed vector ctor iterator 303 | ManVectorDtorIter, // ?__B managed vector dtor iterator 304 | EHVectorCopyCtorIter, // ?__C EH vector copy ctor iterator 305 | EHVectorVbaseCopyCtorIter, // ?__D EH vector vbase copy ctor iterator 306 | VectorCopyCtorIter, // ?__G vector copy constructor iterator 307 | VectorVbaseCopyCtorIter, // ?__H vector vbase copy constructor iterator 308 | ManVectorVbaseCopyCtorIter, // ?__I managed vector vbase copy constructor 309 | CoAwait, // ?__L operator co_await 310 | Spaceship, // ?__M operator<=> 311 | } 312 | 313 | #[derive(Clone, Copy)] 314 | pub(crate) enum SpecialIntrinsicKind { 315 | Vftable, 316 | Vbtable, 317 | Typeof, 318 | VcallThunk, 319 | LocalStaticGuard, 320 | StringLiteralSymbol, 321 | UdtReturning, 322 | DynamicInitializer, 323 | DynamicAtexitDestructor, 324 | RttiTypeDescriptor, 325 | RttiBaseClassDescriptor, 326 | RttiBaseClassArray, 327 | RttiClassHierarchyDescriptor, 328 | RttiCompleteObjLocator, 329 | LocalVftable, 330 | LocalStaticThreadGuard, 331 | } 332 | 333 | bitflags::bitflags! { 334 | // Function classes 335 | #[derive(Clone, Copy, Default)] 336 | pub(crate) struct FuncClass: u16 { 337 | const FC_None = 0; 338 | const FC_Public = 1 << 0; 339 | const FC_Protected = 1 << 1; 340 | const FC_Private = 1 << 2; 341 | const FC_Global = 1 << 3; 342 | const FC_Static = 1 << 4; 343 | const FC_Virtual = 1 << 5; 344 | const FC_Far = 1 << 6; 345 | const FC_ExternC = 1 << 7; 346 | const FC_NoParameterList = 1 << 8; 347 | const FC_VirtualThisAdjust = 1 << 9; 348 | const FC_VirtualThisAdjustEx = 1 << 10; 349 | const FC_StaticThisAdjust = 1 << 11; 350 | } 351 | } 352 | 353 | impl FuncClass { 354 | #[must_use] 355 | pub(crate) fn is_public(self) -> bool { 356 | self.contains(Self::FC_Public) 357 | } 358 | 359 | #[must_use] 360 | pub(crate) fn is_protected(self) -> bool { 361 | self.contains(Self::FC_Protected) 362 | } 363 | 364 | #[must_use] 365 | pub(crate) fn is_private(self) -> bool { 366 | self.contains(Self::FC_Private) 367 | } 368 | 369 | #[must_use] 370 | pub(crate) fn is_global(self) -> bool { 371 | self.contains(Self::FC_Global) 372 | } 373 | 374 | #[must_use] 375 | pub(crate) fn is_static(self) -> bool { 376 | self.contains(Self::FC_Static) 377 | } 378 | 379 | #[must_use] 380 | pub(crate) fn is_virtual(self) -> bool { 381 | self.contains(Self::FC_Virtual) 382 | } 383 | 384 | #[must_use] 385 | pub(crate) fn is_extern_c(self) -> bool { 386 | self.contains(Self::FC_ExternC) 387 | } 388 | 389 | #[must_use] 390 | pub(crate) fn no_parameter_list(self) -> bool { 391 | self.contains(Self::FC_NoParameterList) 392 | } 393 | 394 | #[must_use] 395 | pub(crate) fn has_virtual_this_adjust(self) -> bool { 396 | self.contains(Self::FC_VirtualThisAdjust) 397 | } 398 | 399 | #[must_use] 400 | pub(crate) fn has_virtual_this_adjust_ex(self) -> bool { 401 | self.contains(Self::FC_VirtualThisAdjustEx) 402 | } 403 | 404 | #[must_use] 405 | pub(crate) fn has_static_this_adjust(self) -> bool { 406 | self.contains(Self::FC_StaticThisAdjust) 407 | } 408 | } 409 | 410 | #[derive(Clone, Copy)] 411 | pub(crate) enum TagKind { 412 | Class, 413 | Struct, 414 | Union, 415 | Enum, 416 | } 417 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2024 Ryan McKenzie 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #![doc = include_str!("../README.md")] 16 | #![warn(clippy::pedantic)] 17 | #![deny(clippy::undocumented_unsafe_blocks)] 18 | #![allow( 19 | clippy::missing_errors_doc, 20 | clippy::similar_names, 21 | clippy::struct_field_names, 22 | clippy::too_many_lines 23 | )] 24 | 25 | mod alloc; 26 | mod cache; 27 | mod demangler; 28 | mod extensions; 29 | mod mangled_string; 30 | mod nodes; 31 | 32 | #[cfg(test)] 33 | mod tests; 34 | 35 | use crate::demangler::Demangler; 36 | use bumpalo::Bump; 37 | use std::{ 38 | io, 39 | str::Utf8Error, 40 | string::FromUtf8Error, 41 | }; 42 | 43 | type OutputFlags = Flags; 44 | 45 | trait Writer: io::Write { 46 | fn last_char(&self) -> Option; 47 | fn len_bytes(&self) -> usize; 48 | } 49 | 50 | #[non_exhaustive] 51 | #[derive(thiserror::Error, Debug)] 52 | pub enum Error { 53 | #[error("failed to demangle anonymous namespace name")] 54 | InvalidAnonymousNamespaceName, 55 | 56 | #[error("failed to demangle array type")] 57 | InvalidArrayType, 58 | 59 | #[error("tried to access a backref that does not exist")] 60 | InvalidBackRef, 61 | 62 | #[error("failed to demangle calling convention")] 63 | InvalidCallingConvention, 64 | 65 | #[error("failed to demangle char literal")] 66 | InvalidCharLiteral, 67 | 68 | #[error("failed to demangle class type")] 69 | InvalidClassType, 70 | 71 | #[error("failed to demangle custom type")] 72 | InvalidCustomType, 73 | 74 | #[error("failed to demangle declarator")] 75 | InvalidDeclarator, 76 | 77 | #[error("failed to demangle encoded symbol")] 78 | InvalidEncodedSymbol, 79 | 80 | #[error("failed to demangle fully qualified symbol name")] 81 | InvalidFullyQualifiedSymbolName, 82 | 83 | #[error("failed to demangle function class")] 84 | InvalidFunctionClass, 85 | 86 | #[error("failed to demangle function encoding")] 87 | InvalidFunctionEncoding, 88 | 89 | #[error("failed to demangle function identifier code")] 90 | InvalidFunctionIdentifierCode, 91 | 92 | #[error("failed to demangle function parameter list")] 93 | InvalidFunctionParameterList, 94 | 95 | #[error("failed to demangle function type")] 96 | InvalidFunctionType, 97 | 98 | #[error("failed to demangle init fini stub")] 99 | InvalidInitFiniStub, 100 | 101 | #[error("failed to demangle intrinsic function code")] 102 | InvalidIntrinsicFunctionCode, 103 | 104 | #[error("failed to demangle locally scoped name piece")] 105 | InvalidLocallyScopedNamePiece, 106 | 107 | #[error("failed to demangle local static guard")] 108 | InvalidLocalStaticGuard, 109 | 110 | #[error("failed to demangle md5 name")] 111 | InvalidMd5Name, 112 | 113 | #[error("failed to demangle member pointer type")] 114 | InvalidMemberPointerType, 115 | 116 | #[error("failed to demangle name scope chain")] 117 | InvalidNameScopeChain, 118 | 119 | #[error("failed to demangle number")] 120 | InvalidNumber, 121 | 122 | #[error("failed to demangle pointer cv qualifiers")] 123 | InvalidPointerCVQualifiers, 124 | 125 | #[error("failed to demangle pointer type")] 126 | InvalidPointerType, 127 | 128 | #[error("failed to demangle primitive type")] 129 | InvalidPrimitiveType, 130 | 131 | #[error("failed to demangle qualifiers")] 132 | InvalidQualifiers, 133 | 134 | #[error("failed to demangle rtti base class descriptor node")] 135 | InvalidRttiBaseClassDescriptorNode, 136 | 137 | #[error("failed to demangle signed number")] 138 | InvalidSigned, 139 | 140 | #[error("failed to demangle simple string")] 141 | InvalidSimpleString, 142 | 143 | #[error("failed to demangle special intrinsic")] 144 | InvalidSpecialIntrinsic, 145 | 146 | #[error("failed to demangle special table symbol node")] 147 | InvalidSpecialTableSymbolNode, 148 | 149 | #[error("failed to demangle string literal")] 150 | InvalidStringLiteral, 151 | 152 | #[error("failed to demangle tag unique name")] 153 | InvalidTagUniqueName, 154 | 155 | #[error("failed to demangle template instantiation name")] 156 | InvalidTemplateInstantiationName, 157 | 158 | #[error("failed to demangle template parameter list")] 159 | InvalidTemplateParameterList, 160 | 161 | #[error("failed to demangle throw specification")] 162 | InvalidThrowSpecification, 163 | 164 | #[error("failed to demangle type")] 165 | InvalidType, 166 | 167 | #[error("failed to demangle typinfo name")] 168 | InvalidTypeinfoName, 169 | 170 | #[error("failed to demangle unsigned number")] 171 | InvalidUnsigned, 172 | 173 | #[error("failed to demangle untyped variable")] 174 | InvalidUntypedVariable, 175 | 176 | #[error("failed to demangle variable storage class")] 177 | InvalidVariableStorageClass, 178 | 179 | #[error("failed to demangle vcall thunk node")] 180 | InvalidVcallThunkNode, 181 | 182 | #[error(transparent)] 183 | Io(#[from] io::Error), 184 | 185 | #[error("string demangled to an invalid utf-8 sequence")] 186 | Utf8Error, 187 | 188 | #[error("input string was likely malicious and would have triggered an out of memory panic")] 189 | MaliciousInput, 190 | } 191 | 192 | impl From for Error { 193 | fn from(_: Utf8Error) -> Self { 194 | Self::Utf8Error 195 | } 196 | } 197 | 198 | impl From for Error { 199 | fn from(_: FromUtf8Error) -> Self { 200 | Self::Utf8Error 201 | } 202 | } 203 | 204 | pub type Result = std::result::Result; 205 | 206 | bitflags::bitflags! { 207 | /// `Flags` control how types are printed during demangling. See each flag for more info on what exactly they do. 208 | #[derive(Clone, Copy, Debug, Default, Eq, Hash, PartialEq)] 209 | pub struct Flags: u16 { 210 | /// Suppress calling conventions (`__cdecl`/`__fastcall`/`__thiscall`) from being included in the output. 211 | /// ```rust 212 | /// use undname::Flags; 213 | /// let input = "?func@MyClass@@UEAAHHH@Z"; 214 | /// let without_flag = undname::demangle(input, Flags::default()).unwrap(); 215 | /// let with_flag = undname::demangle(input, Flags::NO_CALLING_CONVENTION).unwrap(); 216 | /// assert_eq!(without_flag, "public: virtual int __cdecl MyClass::func(int, int)"); 217 | /// assert_eq!(with_flag, "public: virtual int MyClass::func(int, int)"); 218 | /// ``` 219 | const NO_CALLING_CONVENTION = 1 << 0; 220 | 221 | /// See also [`NO_CALLING_CONVENTION`](Self::NO_CALLING_CONVENTION). 222 | const NO_ALLOCATION_LANGUAGE = Self::NO_CALLING_CONVENTION.bits(); 223 | 224 | /// Suppress tag specifiers (`class`/`struct`/`union`) from being included in the output. 225 | /// ```rust 226 | /// use undname::Flags; 227 | /// let input = "?x@@3PEAVty@@EA"; 228 | /// let without_flag = undname::demangle(input, Flags::default()).unwrap(); 229 | /// let with_flag = undname::demangle(input, Flags::NO_TAG_SPECIFIER).unwrap(); 230 | /// assert_eq!(without_flag, "class ty *x"); 231 | /// assert_eq!(with_flag, "ty *x"); 232 | /// ``` 233 | const NO_TAG_SPECIFIER = 1 << 1; 234 | 235 | /// See also [`NO_TAG_SPECIFIER`](Self::NO_TAG_SPECIFIER). 236 | const NO_ECSU = Self::NO_TAG_SPECIFIER.bits(); 237 | 238 | /// Suppress access specifiers (`private`/`public`/`protected`) from being included in the output. 239 | /// ```rust 240 | /// use undname::Flags; 241 | /// let input = "?func@MyClass@@UEAAHHH@Z"; 242 | /// let without_flag = undname::demangle(input, Flags::default()).unwrap(); 243 | /// let with_flag = undname::demangle(input, Flags::NO_ACCESS_SPECIFIER).unwrap(); 244 | /// assert_eq!(without_flag, "public: virtual int __cdecl MyClass::func(int, int)"); 245 | /// assert_eq!(with_flag, "virtual int __cdecl MyClass::func(int, int)"); 246 | /// ``` 247 | const NO_ACCESS_SPECIFIER = 1 << 2; 248 | 249 | /// See also [`NO_ACCESS_SPECIFIER`](Self::NO_ACCESS_SPECIFIER). 250 | const NO_ACCESS_SPECIFIERS = Self::NO_ACCESS_SPECIFIER.bits(); 251 | 252 | /// Suppress member types (`static`/`virtual`/`extern "C"`) from being included in the output. 253 | /// ```rust 254 | /// use undname::Flags; 255 | /// let input = "?func@MyClass@@UEAAHHH@Z"; 256 | /// let without_flag = undname::demangle(input, Flags::default()).unwrap(); 257 | /// let with_flag = undname::demangle(input, Flags::NO_MEMBER_TYPE).unwrap(); 258 | /// assert_eq!(without_flag, "public: virtual int __cdecl MyClass::func(int, int)"); 259 | /// assert_eq!(with_flag, "public: int __cdecl MyClass::func(int, int)"); 260 | /// ``` 261 | const NO_MEMBER_TYPE = 1 << 3; 262 | 263 | /// Suppress return types from being included in the output. 264 | /// ```rust 265 | /// use undname::Flags; 266 | /// let input = "?func@MyClass@@UEAAHHH@Z"; 267 | /// let without_flag = undname::demangle(input, Flags::default()).unwrap(); 268 | /// let with_flag = undname::demangle(input, Flags::NO_RETURN_TYPE).unwrap(); 269 | /// assert_eq!(without_flag, "public: virtual int __cdecl MyClass::func(int, int)"); 270 | /// assert_eq!(with_flag, "public: virtual __cdecl MyClass::func(int, int)"); 271 | /// ``` 272 | const NO_RETURN_TYPE = 1 << 4; 273 | 274 | /// See also [`NO_RETURN_TYPE`](Self::NO_RETURN_TYPE). 275 | const NO_FUNCTION_RETURNS = Self::NO_RETURN_TYPE.bits(); 276 | 277 | /// Suppress variable types from being included in the output. 278 | /// ```rust 279 | /// use undname::Flags; 280 | /// let input = "?x@@3PEAEEA"; 281 | /// let without_flag = undname::demangle(input, Flags::default()).unwrap(); 282 | /// let with_flag = undname::demangle(input, Flags::NO_VARIABLE_TYPE).unwrap(); 283 | /// assert_eq!(without_flag, "unsigned char *x"); 284 | /// assert_eq!(with_flag, "x"); 285 | /// ``` 286 | const NO_VARIABLE_TYPE = 1 << 5; 287 | 288 | /// Suppress modifiers on the `this` type (`const`/`volatile`/`__restrict`) from being included in the output. 289 | /// ```rust 290 | /// use undname::Flags; 291 | /// let input = "?world@hello@@QEDAXXZ"; 292 | /// let without_flag = undname::demangle(input, Flags::default()).unwrap(); 293 | /// let with_flag = undname::demangle(input, Flags::NO_THISTYPE).unwrap(); 294 | /// assert_eq!(without_flag, "public: void __cdecl hello::world(void) const volatile"); 295 | /// assert_eq!(with_flag, "public: void __cdecl hello::world(void)"); 296 | /// ``` 297 | const NO_THISTYPE = 1 << 6; 298 | 299 | /// Suppress leading underscores on Microsoft extended keywords (`__restrict`/`__cdecl`/`__fastcall`) from being included in the output. 300 | /// ```rust 301 | /// use undname::Flags; 302 | /// let input = "?foo_piad@@YAXPIAD@Z"; 303 | /// let without_flag = undname::demangle(input, Flags::default()).unwrap(); 304 | /// let with_flag = undname::demangle(input, Flags::NO_LEADING_UNDERSCORES).unwrap(); 305 | /// assert_eq!(without_flag, "void __cdecl foo_piad(char *__restrict)"); 306 | /// assert_eq!(with_flag, "void cdecl foo_piad(char *restrict)"); 307 | /// ``` 308 | const NO_LEADING_UNDERSCORES = 1 << 7; 309 | 310 | /// Suppress Microsoft keywords (`__restrict`/`__unaligned`/`__cdecl`) from being included in the output. 311 | /// ```rust 312 | /// use undname::Flags; 313 | /// let input = "?f@@YAXPEIFAH@Z"; 314 | /// let without_flag = undname::demangle(input, Flags::default()).unwrap(); 315 | /// let with_flag = undname::demangle(input, Flags::NO_MS_KEYWORDS).unwrap(); 316 | /// assert_eq!(without_flag, "void __cdecl f(int __unaligned *__restrict)"); 317 | /// assert_eq!(with_flag, "void f(int *)"); 318 | /// ``` 319 | const NO_MS_KEYWORDS = 1 << 8; 320 | 321 | /// Output only the name for the primary declaration. 322 | /// ```rust 323 | /// use undname::Flags; 324 | /// let input = "?world@hello@@QEDAXXZ"; 325 | /// let without_flag = undname::demangle(input, Flags::default()).unwrap(); 326 | /// let with_flag = undname::demangle(input, Flags::NAME_ONLY).unwrap(); 327 | /// assert_eq!(without_flag, "public: void __cdecl hello::world(void) const volatile"); 328 | /// assert_eq!(with_flag, "hello::world"); 329 | /// ``` 330 | const NAME_ONLY = 1 << 9; 331 | } 332 | } 333 | 334 | impl Flags { 335 | #[must_use] 336 | fn no_calling_convention(self) -> bool { 337 | self.contains(Self::NO_CALLING_CONVENTION) 338 | } 339 | 340 | #[must_use] 341 | fn no_tag_specifier(self) -> bool { 342 | self.contains(Self::NO_TAG_SPECIFIER) 343 | } 344 | 345 | #[must_use] 346 | fn no_access_specifier(self) -> bool { 347 | self.contains(Self::NO_ACCESS_SPECIFIER) 348 | } 349 | 350 | #[must_use] 351 | fn no_member_type(self) -> bool { 352 | self.contains(Self::NO_MEMBER_TYPE) 353 | } 354 | 355 | #[must_use] 356 | fn no_return_type(self) -> bool { 357 | self.contains(Self::NO_RETURN_TYPE) 358 | } 359 | 360 | #[must_use] 361 | fn no_variable_type(self) -> bool { 362 | self.contains(Self::NO_VARIABLE_TYPE) 363 | } 364 | 365 | #[must_use] 366 | fn no_this_type(self) -> bool { 367 | self.contains(Self::NO_THISTYPE) 368 | } 369 | 370 | #[must_use] 371 | fn no_leading_underscores(self) -> bool { 372 | self.contains(Self::NO_LEADING_UNDERSCORES) 373 | } 374 | 375 | #[must_use] 376 | fn no_ms_keywords(self) -> bool { 377 | self.contains(Self::NO_MS_KEYWORDS) 378 | } 379 | 380 | #[must_use] 381 | fn name_only(self) -> bool { 382 | self.contains(Self::NAME_ONLY) 383 | } 384 | } 385 | 386 | /// Demangles a Microsoft symbol stored in `mangled_name`. 387 | /// ```rust 388 | /// use undname::Flags; 389 | /// let result = undname::demangle("?world@@YA?AUhello@@XZ", Flags::default()).unwrap(); 390 | /// assert_eq!(result, "struct hello __cdecl world(void)"); 391 | /// ``` 392 | pub fn demangle(mangled_name: &str, flags: Flags) -> Result { 393 | let mut result = String::default(); 394 | demangle_into(mangled_name, flags, &mut result)?; 395 | Ok(result) 396 | } 397 | 398 | /// See [`demangle`] for more info. 399 | pub fn demangle_into(mangled_name: &str, flags: Flags, result: &mut String) -> Result<()> { 400 | let alloc = Bump::default(); 401 | let d = Demangler::new(mangled_name, flags, &alloc); 402 | result.clear(); 403 | d.parse_into(result) 404 | } 405 | -------------------------------------------------------------------------------- /src/cache.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2024 Ryan McKenzie 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | use crate::{ 16 | alloc, 17 | nodes::{ 18 | ArrayTypeNode, 19 | ConversionOperatorIdentifierNode, 20 | CustomTypeNode, 21 | DynamicStructorIdentifierNode, 22 | EncodedStringLiteralNode, 23 | FunctionSignatureNode, 24 | FunctionSymbolNode, 25 | IIdentifierNode, 26 | INode, 27 | ISignatureNode, 28 | ISymbolNode, 29 | ITypeNode, 30 | IdentifierNode, 31 | IntegerLiteralNode, 32 | IntermediateNode, 33 | IntrinsicFunctionIdentifierNode, 34 | LiteralOperatorIdentifierNode, 35 | LocalStaticGuardIdentifierNode, 36 | LocalStaticGuardVariableNode, 37 | Md5SymbolNode, 38 | NamedIdentifierNode, 39 | Node, 40 | NodeArrayNode, 41 | PointerTypeNode, 42 | PrimitiveTypeNode, 43 | QualifiedNameNode, 44 | RttiBaseClassDescriptorNode, 45 | SignatureNode, 46 | SpecialTableSymbolNode, 47 | StructorIdentifierNode, 48 | SymbolNode, 49 | TagTypeNode, 50 | TemplateParameterReferenceNode, 51 | ThunkSignatureNode, 52 | TypeNode, 53 | VariableSymbolNode, 54 | VcallThunkIdentifierNode, 55 | }, 56 | Error, 57 | Result, 58 | }; 59 | use bumpalo::{ 60 | collections::Vec as BumpVec, 61 | Bump, 62 | }; 63 | use nonmax::NonMaxUsize; 64 | use std::marker::PhantomData; 65 | 66 | pub(crate) enum NodeStorage<'alloc> { 67 | PrimitiveType(&'alloc mut PrimitiveTypeNode), 68 | FunctionSignature(&'alloc mut FunctionSignatureNode), 69 | ThunkSignature(&'alloc mut ThunkSignatureNode), 70 | PointerType(&'alloc mut PointerTypeNode), 71 | TagType(&'alloc mut TagTypeNode), 72 | ArrayType(&'alloc mut ArrayTypeNode), 73 | CustomType(&'alloc mut CustomTypeNode), 74 | 75 | VcallThunkIdentifier(&'alloc mut VcallThunkIdentifierNode), 76 | DynamicStructorIdentifier(&'alloc mut DynamicStructorIdentifierNode), 77 | NamedIdentifier(&'alloc mut NamedIdentifierNode<'alloc>), 78 | IntrinsicFunctionIdentifier(&'alloc mut IntrinsicFunctionIdentifierNode), 79 | LiteralOperatorIdentifier(&'alloc mut LiteralOperatorIdentifierNode<'alloc>), 80 | LocalStaticGuardIdentifier(&'alloc mut LocalStaticGuardIdentifierNode), 81 | ConversionOperatorIdentifier(&'alloc mut ConversionOperatorIdentifierNode), 82 | StructorIdentifier(&'alloc mut StructorIdentifierNode), 83 | RttiBaseClassDescriptor(&'alloc mut RttiBaseClassDescriptorNode), 84 | 85 | NodeArray(&'alloc mut NodeArrayNode<'alloc>), 86 | QualifiedName(&'alloc mut QualifiedNameNode), 87 | TemplateParameterReference(&'alloc mut TemplateParameterReferenceNode), 88 | IntegerLiteral(&'alloc mut IntegerLiteralNode), 89 | 90 | Md5Symbol(&'alloc mut Md5SymbolNode), 91 | SpecialTableSymbol(&'alloc mut SpecialTableSymbolNode), 92 | LocalStaticGuardVariable(&'alloc mut LocalStaticGuardVariableNode), 93 | EncodedStringLiteral(&'alloc mut EncodedStringLiteralNode<'alloc>), 94 | VariableSymbol(&'alloc mut VariableSymbolNode), 95 | FunctionSymbol(&'alloc mut FunctionSymbolNode), 96 | } 97 | 98 | macro_rules! impl_into_storage { 99 | ($from:ty => $to:ident) => { 100 | impl<'alloc> From<&'alloc mut $from> for NodeStorage<'alloc> { 101 | fn from(value: &'alloc mut $from) -> Self { 102 | Self::$to(value.into()) 103 | } 104 | } 105 | }; 106 | } 107 | 108 | impl_into_storage!(PrimitiveTypeNode => PrimitiveType); 109 | impl_into_storage!(FunctionSignatureNode => FunctionSignature); 110 | impl_into_storage!(ThunkSignatureNode => ThunkSignature); 111 | impl_into_storage!(PointerTypeNode => PointerType); 112 | impl_into_storage!(TagTypeNode => TagType); 113 | impl_into_storage!(ArrayTypeNode => ArrayType); 114 | impl_into_storage!(CustomTypeNode => CustomType); 115 | 116 | impl_into_storage!(VcallThunkIdentifierNode => VcallThunkIdentifier); 117 | impl_into_storage!(DynamicStructorIdentifierNode => DynamicStructorIdentifier); 118 | impl_into_storage!(NamedIdentifierNode<'alloc> => NamedIdentifier); 119 | impl_into_storage!(IntrinsicFunctionIdentifierNode => IntrinsicFunctionIdentifier); 120 | impl_into_storage!(LiteralOperatorIdentifierNode<'alloc> => LiteralOperatorIdentifier); 121 | impl_into_storage!(LocalStaticGuardIdentifierNode => LocalStaticGuardIdentifier); 122 | impl_into_storage!(ConversionOperatorIdentifierNode => ConversionOperatorIdentifier); 123 | impl_into_storage!(StructorIdentifierNode => StructorIdentifier); 124 | impl_into_storage!(RttiBaseClassDescriptorNode => RttiBaseClassDescriptor); 125 | 126 | impl_into_storage!(NodeArrayNode<'alloc> => NodeArray); 127 | impl_into_storage!(QualifiedNameNode => QualifiedName); 128 | impl_into_storage!(TemplateParameterReferenceNode => TemplateParameterReference); 129 | impl_into_storage!(IntegerLiteralNode => IntegerLiteral); 130 | 131 | impl_into_storage!(Md5SymbolNode => Md5Symbol); 132 | impl_into_storage!(SpecialTableSymbolNode => SpecialTableSymbol); 133 | impl_into_storage!(LocalStaticGuardVariableNode => LocalStaticGuardVariable); 134 | impl_into_storage!(EncodedStringLiteralNode<'alloc> => EncodedStringLiteral); 135 | impl_into_storage!(VariableSymbolNode => VariableSymbol); 136 | impl_into_storage!(FunctionSymbolNode => FunctionSymbol); 137 | 138 | pub(crate) trait UnwrapStorage<'storage, 'alloc: 'storage> { 139 | type Output; 140 | type OutputMut; 141 | 142 | #[must_use] 143 | fn try_unwrap(this: &'storage NodeStorage<'alloc>) -> Option; 144 | 145 | #[must_use] 146 | fn try_unwrap_mut(this: &'storage mut NodeStorage<'alloc>) -> Option; 147 | } 148 | 149 | macro_rules! impl_from_storage { 150 | ($t:ident) => { 151 | impl<'storage, 'alloc: 'storage> UnwrapStorage<'storage, 'alloc> for $t { 152 | type Output = &'storage <$t as ResolverToNode<'alloc>>::Node; 153 | type OutputMut = &'storage mut <$t as ResolverToNode<'alloc>>::Node; 154 | 155 | fn try_unwrap(this: &'storage NodeStorage<'alloc>) -> Option { 156 | if let NodeStorage::$t(x) = this { 157 | Some(x) 158 | } else { 159 | None 160 | } 161 | } 162 | 163 | fn try_unwrap_mut(this: &'storage mut NodeStorage<'alloc>) -> Option { 164 | if let NodeStorage::$t(x) = this { 165 | Some(x) 166 | } else { 167 | None 168 | } 169 | } 170 | } 171 | }; 172 | } 173 | 174 | impl_from_storage!(PrimitiveType); 175 | impl_from_storage!(FunctionSignature); 176 | impl_from_storage!(ThunkSignature); 177 | impl_from_storage!(PointerType); 178 | impl_from_storage!(TagType); 179 | impl_from_storage!(ArrayType); 180 | impl_from_storage!(CustomType); 181 | 182 | impl_from_storage!(VcallThunkIdentifier); 183 | impl_from_storage!(DynamicStructorIdentifier); 184 | impl_from_storage!(NamedIdentifier); 185 | impl_from_storage!(IntrinsicFunctionIdentifier); 186 | impl_from_storage!(LiteralOperatorIdentifier); 187 | impl_from_storage!(LocalStaticGuardIdentifier); 188 | impl_from_storage!(ConversionOperatorIdentifier); 189 | impl_from_storage!(StructorIdentifier); 190 | impl_from_storage!(RttiBaseClassDescriptor); 191 | 192 | impl_from_storage!(NodeArray); 193 | impl_from_storage!(QualifiedName); 194 | impl_from_storage!(TemplateParameterReference); 195 | impl_from_storage!(IntegerLiteral); 196 | 197 | impl_from_storage!(Md5Symbol); 198 | impl_from_storage!(SpecialTableSymbol); 199 | impl_from_storage!(LocalStaticGuardVariable); 200 | impl_from_storage!(EncodedStringLiteral); 201 | impl_from_storage!(VariableSymbol); 202 | impl_from_storage!(FunctionSymbol); 203 | 204 | macro_rules! impl_from_storage_interface { 205 | ($interface:ident = [ $($variant:ident),+ $(,)? ]) => { 206 | impl<'storage, 'alloc: 'storage> UnwrapStorage<'storage, 'alloc> for $interface { 207 | type Output = <$interface as IntermediateNode<'storage, 'alloc>>::Const; 208 | type OutputMut = <$interface as IntermediateNode<'storage, 'alloc>>::Mut; 209 | 210 | fn try_unwrap(this: &'storage NodeStorage<'alloc>) -> Option { 211 | match this { 212 | $(NodeStorage::$variant(x) => { 213 | let x: &_ = *x; 214 | Some(x.into()) 215 | })+ 216 | #[allow(unreachable_patterns)] 217 | _ => None, 218 | } 219 | } 220 | 221 | fn try_unwrap_mut(this: &'storage mut NodeStorage<'alloc>) -> Option { 222 | match this { 223 | $(NodeStorage::$variant(x) => Some((*x).into()),)+ 224 | #[allow(unreachable_patterns)] 225 | _ => None, 226 | } 227 | } 228 | } 229 | }; 230 | } 231 | 232 | impl_from_storage_interface!( 233 | INode = [ 234 | PrimitiveType, 235 | FunctionSignature, 236 | ThunkSignature, 237 | PointerType, 238 | TagType, 239 | ArrayType, 240 | CustomType, 241 | VcallThunkIdentifier, 242 | DynamicStructorIdentifier, 243 | NamedIdentifier, 244 | IntrinsicFunctionIdentifier, 245 | LiteralOperatorIdentifier, 246 | LocalStaticGuardIdentifier, 247 | ConversionOperatorIdentifier, 248 | StructorIdentifier, 249 | RttiBaseClassDescriptor, 250 | NodeArray, 251 | QualifiedName, 252 | TemplateParameterReference, 253 | IntegerLiteral, 254 | Md5Symbol, 255 | SpecialTableSymbol, 256 | LocalStaticGuardVariable, 257 | EncodedStringLiteral, 258 | VariableSymbol, 259 | FunctionSymbol, 260 | ] 261 | ); 262 | 263 | impl_from_storage_interface!( 264 | ITypeNode = [ 265 | PrimitiveType, 266 | FunctionSignature, 267 | ThunkSignature, 268 | PointerType, 269 | TagType, 270 | ArrayType, 271 | CustomType, 272 | ] 273 | ); 274 | 275 | impl_from_storage_interface!(ISignatureNode = [FunctionSignature, ThunkSignature]); 276 | 277 | impl_from_storage_interface!( 278 | IIdentifierNode = [ 279 | VcallThunkIdentifier, 280 | DynamicStructorIdentifier, 281 | NamedIdentifier, 282 | IntrinsicFunctionIdentifier, 283 | LiteralOperatorIdentifier, 284 | LocalStaticGuardIdentifier, 285 | ConversionOperatorIdentifier, 286 | StructorIdentifier, 287 | RttiBaseClassDescriptor, 288 | ] 289 | ); 290 | 291 | impl_from_storage_interface!( 292 | ISymbolNode = [ 293 | Md5Symbol, 294 | SpecialTableSymbol, 295 | LocalStaticGuardVariable, 296 | EncodedStringLiteral, 297 | VariableSymbol, 298 | FunctionSymbol, 299 | ] 300 | ); 301 | 302 | pub(crate) struct NodeCache<'alloc> { 303 | storage: BumpVec<'alloc, NodeStorage<'alloc>>, 304 | } 305 | 306 | impl<'alloc> NodeCache<'alloc> { 307 | pub(crate) fn new(allocator: &'alloc Bump) -> Self { 308 | Self { 309 | storage: alloc::new_vec(allocator), 310 | } 311 | } 312 | 313 | pub(crate) fn intern( 314 | &mut self, 315 | node: T, 316 | ) -> Result::Resolver>> 317 | where 318 | T: NodeToResolver + 'alloc, 319 | &'alloc mut T: Into>, 320 | { 321 | if self.storage.len() + 1 > (1 << 11) { 322 | // a mangled string with this many nodes is probably malformed... bail 323 | return Err(Error::MaliciousInput); 324 | } 325 | 326 | let allocator = self.storage.bump(); 327 | let node = alloc::allocate(allocator, node); 328 | self.storage.push(node.into()); 329 | let id = self.storage.len() - 1; 330 | // SAFETY: we would oom before allocating usize::MAX nodes 331 | let id = unsafe { NonMaxUsize::new_unchecked(id) }; 332 | Ok(NodeHandle::new(id)) 333 | } 334 | } 335 | 336 | #[repr(transparent)] 337 | pub(crate) struct NodeHandle { 338 | id: NonMaxUsize, // enables niche optimization 339 | marker: PhantomData, 340 | } 341 | 342 | impl NodeHandle { 343 | fn new(id: NonMaxUsize) -> Self { 344 | Self { 345 | id, 346 | marker: PhantomData, 347 | } 348 | } 349 | 350 | #[inline] 351 | pub(crate) fn resolve<'storage, 'alloc: 'storage>( 352 | self, 353 | cache: &'storage NodeCache<'alloc>, 354 | ) -> >::Output 355 | where 356 | T: UnwrapStorage<'storage, 'alloc>, 357 | { 358 | let node = &cache.storage[self.id.get()]; 359 | T::try_unwrap(node).expect("actual node type does not match encoded type") 360 | } 361 | 362 | #[inline] 363 | pub(crate) fn resolve_mut<'storage, 'alloc: 'storage>( 364 | self, 365 | cache: &'storage mut NodeCache<'alloc>, 366 | ) -> >::OutputMut 367 | where 368 | T: UnwrapStorage<'storage, 'alloc>, 369 | { 370 | let node = &mut cache.storage[self.id.get()]; 371 | T::try_unwrap_mut(node).expect("actual node type does not match encoded type") 372 | } 373 | 374 | #[must_use] 375 | pub(crate) fn downcast<'storage, 'alloc: 'storage, To>( 376 | self, 377 | cache: &'storage NodeCache<'alloc>, 378 | ) -> Option> 379 | where 380 | Self: Downcast, 381 | { 382 | >::downcast(self, cache) 383 | } 384 | } 385 | 386 | impl Clone for NodeHandle { 387 | fn clone(&self) -> Self { 388 | *self 389 | } 390 | } 391 | 392 | impl Copy for NodeHandle {} 393 | 394 | macro_rules! impl_upcast { 395 | ($from:ident => $to:ty) => { 396 | impl From> for NodeHandle<$to> { 397 | fn from(value: NodeHandle<$from>) -> Self { 398 | Self::new(value.id) 399 | } 400 | } 401 | }; 402 | } 403 | 404 | impl_upcast!(ITypeNode => INode); 405 | impl_upcast!(ISignatureNode => INode); 406 | impl_upcast!(PrimitiveType => INode); 407 | impl_upcast!(FunctionSignature => INode); 408 | impl_upcast!(ThunkSignature => INode); 409 | impl_upcast!(PointerType => INode); 410 | impl_upcast!(TagType => INode); 411 | impl_upcast!(ArrayType => INode); 412 | impl_upcast!(CustomType => INode); 413 | 414 | impl_upcast!(IIdentifierNode => INode); 415 | impl_upcast!(VcallThunkIdentifier => INode); 416 | impl_upcast!(DynamicStructorIdentifier => INode); 417 | impl_upcast!(NamedIdentifier => INode); 418 | impl_upcast!(IntrinsicFunctionIdentifier => INode); 419 | impl_upcast!(LiteralOperatorIdentifier => INode); 420 | impl_upcast!(LocalStaticGuardIdentifier => INode); 421 | impl_upcast!(ConversionOperatorIdentifier => INode); 422 | impl_upcast!(StructorIdentifier => INode); 423 | impl_upcast!(RttiBaseClassDescriptor => INode); 424 | 425 | impl_upcast!(NodeArray => INode); 426 | impl_upcast!(QualifiedName => INode); 427 | impl_upcast!(TemplateParameterReference => INode); 428 | impl_upcast!(IntegerLiteral => INode); 429 | 430 | impl_upcast!(ISymbolNode => INode); 431 | impl_upcast!(Md5Symbol => INode); 432 | impl_upcast!(SpecialTableSymbol => INode); 433 | impl_upcast!(LocalStaticGuardVariable => INode); 434 | impl_upcast!(EncodedStringLiteral => INode); 435 | impl_upcast!(VariableSymbol => INode); 436 | impl_upcast!(FunctionSymbol => INode); 437 | 438 | impl_upcast!(PrimitiveType => ITypeNode); 439 | impl_upcast!(ISignatureNode => ITypeNode); 440 | impl_upcast!(FunctionSignature => ITypeNode); 441 | impl_upcast!(ThunkSignature => ITypeNode); 442 | impl_upcast!(PointerType => ITypeNode); 443 | impl_upcast!(TagType => ITypeNode); 444 | impl_upcast!(ArrayType => ITypeNode); 445 | impl_upcast!(CustomType => ITypeNode); 446 | 447 | impl_upcast!(FunctionSignature => ISignatureNode); 448 | impl_upcast!(ThunkSignature => ISignatureNode); 449 | 450 | impl_upcast!(VcallThunkIdentifier => IIdentifierNode); 451 | impl_upcast!(DynamicStructorIdentifier => IIdentifierNode); 452 | impl_upcast!(NamedIdentifier => IIdentifierNode); 453 | impl_upcast!(IntrinsicFunctionIdentifier => IIdentifierNode); 454 | impl_upcast!(LiteralOperatorIdentifier => IIdentifierNode); 455 | impl_upcast!(LocalStaticGuardIdentifier => IIdentifierNode); 456 | impl_upcast!(ConversionOperatorIdentifier => IIdentifierNode); 457 | impl_upcast!(StructorIdentifier => IIdentifierNode); 458 | impl_upcast!(RttiBaseClassDescriptor => IIdentifierNode); 459 | 460 | impl_upcast!(Md5Symbol => ISymbolNode); 461 | impl_upcast!(SpecialTableSymbol => ISymbolNode); 462 | impl_upcast!(LocalStaticGuardVariable => ISymbolNode); 463 | impl_upcast!(EncodedStringLiteral => ISymbolNode); 464 | impl_upcast!(VariableSymbol => ISymbolNode); 465 | impl_upcast!(FunctionSymbol => ISymbolNode); 466 | 467 | pub(crate) trait Downcast { 468 | #[must_use] 469 | fn downcast<'storage, 'alloc: 'storage>( 470 | self, 471 | cache: &'storage NodeCache<'alloc>, 472 | ) -> Option>; 473 | } 474 | 475 | macro_rules! impl_downcast { 476 | ($for:ident, $from:ident::$variant:ident => $to:ty) => { 477 | impl<'node> Downcast<$to> for NodeHandle<$for> { 478 | #[inline] 479 | fn downcast<'storage, 'alloc: 'storage>( 480 | self, 481 | cache: &'storage NodeCache<'alloc>, 482 | ) -> Option> { 483 | if let $from::$variant(_) = self.resolve(cache) { 484 | Some(NodeHandle::new(self.id)) 485 | } else { 486 | None 487 | } 488 | } 489 | } 490 | }; 491 | } 492 | 493 | impl_downcast!(INode, Node::Type => ITypeNode); 494 | impl_downcast!(INode, Node::Identifier => IIdentifierNode); 495 | impl_downcast!(INode, Node::NodeArray => NodeArray); 496 | impl_downcast!(INode, Node::QualifiedName => QualifiedName); 497 | impl_downcast!(INode, Node::TemplateParameterReference => TemplateParameterReference); 498 | impl_downcast!(INode, Node::IntegerLiteral => IntegerLiteral); 499 | impl_downcast!(INode, Node::Symbol => ISymbolNode); 500 | 501 | impl_downcast!(ITypeNode, TypeNode::PrimitiveType => PrimitiveType); 502 | impl_downcast!(ITypeNode, TypeNode::Signature => ISignatureNode); 503 | impl_downcast!(ITypeNode, TypeNode::PointerType => PointerType); 504 | impl_downcast!(ITypeNode, TypeNode::TagType => TagType); 505 | impl_downcast!(ITypeNode, TypeNode::ArrayType => ArrayType); 506 | impl_downcast!(ITypeNode, TypeNode::CustomType => CustomType); 507 | 508 | impl_downcast!(ISignatureNode, SignatureNode::FunctionSignature => FunctionSignature); 509 | impl_downcast!(ISignatureNode, SignatureNode::ThunkSignature => ThunkSignature); 510 | 511 | impl_downcast!(IIdentifierNode, IdentifierNode::VcallThunkIdentifier => VcallThunkIdentifier); 512 | impl_downcast!(IIdentifierNode, IdentifierNode::DynamicStructorIdentifier => DynamicStructorIdentifier); 513 | impl_downcast!(IIdentifierNode, IdentifierNode::NamedIdentifier => NamedIdentifier); 514 | impl_downcast!(IIdentifierNode, IdentifierNode::IntrinsicFunctionIdentifier => IntrinsicFunctionIdentifier); 515 | impl_downcast!(IIdentifierNode, IdentifierNode::LiteralOperatorIdentifier => LiteralOperatorIdentifier); 516 | impl_downcast!(IIdentifierNode, IdentifierNode::LocalStaticGuardIdentifier => LocalStaticGuardIdentifier); 517 | impl_downcast!(IIdentifierNode, IdentifierNode::ConversionOperatorIdentifier => ConversionOperatorIdentifier); 518 | impl_downcast!(IIdentifierNode, IdentifierNode::StructorIdentifier => StructorIdentifier); 519 | impl_downcast!(IIdentifierNode, IdentifierNode::RttiBaseClassDescriptor => RttiBaseClassDescriptor); 520 | 521 | impl_downcast!(ISymbolNode, SymbolNode::SpecialTableSymbol => SpecialTableSymbol); 522 | impl_downcast!(ISymbolNode, SymbolNode::LocalStaticGuardVariable => LocalStaticGuardVariable); 523 | impl_downcast!(ISymbolNode, SymbolNode::EncodedStringLiteral => EncodedStringLiteral); 524 | impl_downcast!(ISymbolNode, SymbolNode::VariableSymbol => VariableSymbol); 525 | impl_downcast!(ISymbolNode, SymbolNode::FunctionSymbol => FunctionSymbol); 526 | 527 | impl Downcast for NodeHandle { 528 | #[inline] 529 | fn downcast<'storage, 'alloc: 'storage>( 530 | self, 531 | cache: &'storage NodeCache<'alloc>, 532 | ) -> Option> { 533 | if let Node::Type(TypeNode::Signature(_)) = self.resolve(cache) { 534 | Some(NodeHandle::new(self.id)) 535 | } else { 536 | None 537 | } 538 | } 539 | } 540 | 541 | impl Downcast for NodeHandle { 542 | #[inline] 543 | fn downcast<'storage, 'alloc: 'storage>( 544 | self, 545 | cache: &'storage NodeCache<'alloc>, 546 | ) -> Option> { 547 | if let Node::Type(TypeNode::Signature(SignatureNode::FunctionSignature(_))) = 548 | self.resolve(cache) 549 | { 550 | Some(NodeHandle::new(self.id)) 551 | } else { 552 | None 553 | } 554 | } 555 | } 556 | 557 | impl Downcast for NodeHandle { 558 | #[inline] 559 | fn downcast<'storage, 'alloc: 'storage>( 560 | self, 561 | cache: &'storage NodeCache<'alloc>, 562 | ) -> Option> { 563 | if let Node::Type(TypeNode::Signature(SignatureNode::ThunkSignature(_))) = 564 | self.resolve(cache) 565 | { 566 | Some(NodeHandle::new(self.id)) 567 | } else { 568 | None 569 | } 570 | } 571 | } 572 | 573 | pub(crate) trait ResolverToNode<'alloc> { 574 | type Node; 575 | } 576 | 577 | pub(crate) trait NodeToResolver { 578 | type Resolver; 579 | } 580 | 581 | macro_rules! impl_node_handle { 582 | ($resolver:ident => $node:ty) => { 583 | pub(crate) struct $resolver; 584 | 585 | impl<'alloc> ResolverToNode<'alloc> for $resolver { 586 | type Node = $node; 587 | } 588 | 589 | impl<'alloc> NodeToResolver for $node { 590 | type Resolver = $resolver; 591 | } 592 | }; 593 | } 594 | 595 | impl_node_handle!(PrimitiveType => PrimitiveTypeNode); 596 | impl_node_handle!(FunctionSignature => FunctionSignatureNode); 597 | impl_node_handle!(ThunkSignature => ThunkSignatureNode); 598 | impl_node_handle!(PointerType => PointerTypeNode); 599 | impl_node_handle!(TagType => TagTypeNode); 600 | impl_node_handle!(ArrayType => ArrayTypeNode); 601 | impl_node_handle!(CustomType => CustomTypeNode); 602 | 603 | impl_node_handle!(VcallThunkIdentifier => VcallThunkIdentifierNode); 604 | impl_node_handle!(DynamicStructorIdentifier => DynamicStructorIdentifierNode); 605 | impl_node_handle!(NamedIdentifier => NamedIdentifierNode<'alloc>); 606 | impl_node_handle!(IntrinsicFunctionIdentifier => IntrinsicFunctionIdentifierNode); 607 | impl_node_handle!(LiteralOperatorIdentifier => LiteralOperatorIdentifierNode<'alloc>); 608 | impl_node_handle!(LocalStaticGuardIdentifier => LocalStaticGuardIdentifierNode); 609 | impl_node_handle!(ConversionOperatorIdentifier => ConversionOperatorIdentifierNode); 610 | impl_node_handle!(StructorIdentifier => StructorIdentifierNode); 611 | impl_node_handle!(RttiBaseClassDescriptor => RttiBaseClassDescriptorNode); 612 | 613 | impl_node_handle!(NodeArray => NodeArrayNode<'alloc>); 614 | impl_node_handle!(QualifiedName => QualifiedNameNode); 615 | impl_node_handle!(TemplateParameterReference => TemplateParameterReferenceNode); 616 | impl_node_handle!(IntegerLiteral => IntegerLiteralNode); 617 | 618 | impl_node_handle!(Md5Symbol => Md5SymbolNode); 619 | impl_node_handle!(SpecialTableSymbol => SpecialTableSymbolNode); 620 | impl_node_handle!(LocalStaticGuardVariable => LocalStaticGuardVariableNode); 621 | impl_node_handle!(EncodedStringLiteral => EncodedStringLiteralNode<'alloc>); 622 | impl_node_handle!(VariableSymbol => VariableSymbolNode); 623 | impl_node_handle!(FunctionSymbol => FunctionSymbolNode); 624 | -------------------------------------------------------------------------------- /Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 4 4 | 5 | [[package]] 6 | name = "aho-corasick" 7 | version = "1.1.3" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" 10 | dependencies = [ 11 | "memchr", 12 | ] 13 | 14 | [[package]] 15 | name = "anes" 16 | version = "0.1.6" 17 | source = "registry+https://github.com/rust-lang/crates.io-index" 18 | checksum = "4b46cbb362ab8752921c97e041f5e366ee6297bd428a31275b9fcf1e380f7299" 19 | 20 | [[package]] 21 | name = "anstream" 22 | version = "0.6.15" 23 | source = "registry+https://github.com/rust-lang/crates.io-index" 24 | checksum = "64e15c1ab1f89faffbf04a634d5e1962e9074f2741eef6d97f3c4e322426d526" 25 | dependencies = [ 26 | "anstyle", 27 | "anstyle-parse", 28 | "anstyle-query", 29 | "anstyle-wincon", 30 | "colorchoice", 31 | "is_terminal_polyfill", 32 | "utf8parse", 33 | ] 34 | 35 | [[package]] 36 | name = "anstyle" 37 | version = "1.0.8" 38 | source = "registry+https://github.com/rust-lang/crates.io-index" 39 | checksum = "1bec1de6f59aedf83baf9ff929c98f2ad654b97c9510f4e70cf6f661d49fd5b1" 40 | 41 | [[package]] 42 | name = "anstyle-parse" 43 | version = "0.2.5" 44 | source = "registry+https://github.com/rust-lang/crates.io-index" 45 | checksum = "eb47de1e80c2b463c735db5b217a0ddc39d612e7ac9e2e96a5aed1f57616c1cb" 46 | dependencies = [ 47 | "utf8parse", 48 | ] 49 | 50 | [[package]] 51 | name = "anstyle-query" 52 | version = "1.1.1" 53 | source = "registry+https://github.com/rust-lang/crates.io-index" 54 | checksum = "6d36fc52c7f6c869915e99412912f22093507da8d9e942ceaf66fe4b7c14422a" 55 | dependencies = [ 56 | "windows-sys 0.52.0", 57 | ] 58 | 59 | [[package]] 60 | name = "anstyle-wincon" 61 | version = "3.0.4" 62 | source = "registry+https://github.com/rust-lang/crates.io-index" 63 | checksum = "5bf74e1b6e971609db8ca7a9ce79fd5768ab6ae46441c572e46cf596f59e57f8" 64 | dependencies = [ 65 | "anstyle", 66 | "windows-sys 0.52.0", 67 | ] 68 | 69 | [[package]] 70 | name = "arrayvec" 71 | version = "0.7.6" 72 | source = "registry+https://github.com/rust-lang/crates.io-index" 73 | checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" 74 | 75 | [[package]] 76 | name = "autocfg" 77 | version = "1.3.0" 78 | source = "registry+https://github.com/rust-lang/crates.io-index" 79 | checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" 80 | 81 | [[package]] 82 | name = "bitflags" 83 | version = "2.6.0" 84 | source = "registry+https://github.com/rust-lang/crates.io-index" 85 | checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" 86 | 87 | [[package]] 88 | name = "bumpalo" 89 | version = "3.16.0" 90 | source = "registry+https://github.com/rust-lang/crates.io-index" 91 | checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" 92 | 93 | [[package]] 94 | name = "cast" 95 | version = "0.3.0" 96 | source = "registry+https://github.com/rust-lang/crates.io-index" 97 | checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" 98 | 99 | [[package]] 100 | name = "cfg-if" 101 | version = "1.0.0" 102 | source = "registry+https://github.com/rust-lang/crates.io-index" 103 | checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" 104 | 105 | [[package]] 106 | name = "ciborium" 107 | version = "0.2.2" 108 | source = "registry+https://github.com/rust-lang/crates.io-index" 109 | checksum = "42e69ffd6f0917f5c029256a24d0161db17cea3997d185db0d35926308770f0e" 110 | dependencies = [ 111 | "ciborium-io", 112 | "ciborium-ll", 113 | "serde", 114 | ] 115 | 116 | [[package]] 117 | name = "ciborium-io" 118 | version = "0.2.2" 119 | source = "registry+https://github.com/rust-lang/crates.io-index" 120 | checksum = "05afea1e0a06c9be33d539b876f1ce3692f4afea2cb41f740e7743225ed1c757" 121 | 122 | [[package]] 123 | name = "ciborium-ll" 124 | version = "0.2.2" 125 | source = "registry+https://github.com/rust-lang/crates.io-index" 126 | checksum = "57663b653d948a338bfb3eeba9bb2fd5fcfaecb9e199e87e1eda4d9e8b240fd9" 127 | dependencies = [ 128 | "ciborium-io", 129 | "half", 130 | ] 131 | 132 | [[package]] 133 | name = "clap" 134 | version = "4.5.16" 135 | source = "registry+https://github.com/rust-lang/crates.io-index" 136 | checksum = "ed6719fffa43d0d87e5fd8caeab59be1554fb028cd30edc88fc4369b17971019" 137 | dependencies = [ 138 | "clap_builder", 139 | "clap_derive", 140 | ] 141 | 142 | [[package]] 143 | name = "clap_builder" 144 | version = "4.5.15" 145 | source = "registry+https://github.com/rust-lang/crates.io-index" 146 | checksum = "216aec2b177652e3846684cbfe25c9964d18ec45234f0f5da5157b207ed1aab6" 147 | dependencies = [ 148 | "anstream", 149 | "anstyle", 150 | "clap_lex", 151 | "strsim", 152 | ] 153 | 154 | [[package]] 155 | name = "clap_derive" 156 | version = "4.5.13" 157 | source = "registry+https://github.com/rust-lang/crates.io-index" 158 | checksum = "501d359d5f3dcaf6ecdeee48833ae73ec6e42723a1e52419c79abf9507eec0a0" 159 | dependencies = [ 160 | "heck", 161 | "proc-macro2", 162 | "quote", 163 | "syn", 164 | ] 165 | 166 | [[package]] 167 | name = "clap_lex" 168 | version = "0.7.2" 169 | source = "registry+https://github.com/rust-lang/crates.io-index" 170 | checksum = "1462739cb27611015575c0c11df5df7601141071f07518d56fcc1be504cbec97" 171 | 172 | [[package]] 173 | name = "colorchoice" 174 | version = "1.0.2" 175 | source = "registry+https://github.com/rust-lang/crates.io-index" 176 | checksum = "d3fd119d74b830634cea2a0f58bbd0d54540518a14397557951e79340abc28c0" 177 | 178 | [[package]] 179 | name = "criterion" 180 | version = "0.5.1" 181 | source = "registry+https://github.com/rust-lang/crates.io-index" 182 | checksum = "f2b12d017a929603d80db1831cd3a24082f8137ce19c69e6447f54f5fc8d692f" 183 | dependencies = [ 184 | "anes", 185 | "cast", 186 | "ciborium", 187 | "clap", 188 | "criterion-plot", 189 | "is-terminal", 190 | "itertools", 191 | "num-traits", 192 | "once_cell", 193 | "oorandom", 194 | "plotters", 195 | "rayon", 196 | "regex", 197 | "serde", 198 | "serde_derive", 199 | "serde_json", 200 | "tinytemplate", 201 | "walkdir", 202 | ] 203 | 204 | [[package]] 205 | name = "criterion-plot" 206 | version = "0.5.0" 207 | source = "registry+https://github.com/rust-lang/crates.io-index" 208 | checksum = "6b50826342786a51a89e2da3a28f1c32b06e387201bc2d19791f622c673706b1" 209 | dependencies = [ 210 | "cast", 211 | "itertools", 212 | ] 213 | 214 | [[package]] 215 | name = "crossbeam-deque" 216 | version = "0.8.5" 217 | source = "registry+https://github.com/rust-lang/crates.io-index" 218 | checksum = "613f8cc01fe9cf1a3eb3d7f488fd2fa8388403e97039e2f73692932e291a770d" 219 | dependencies = [ 220 | "crossbeam-epoch", 221 | "crossbeam-utils", 222 | ] 223 | 224 | [[package]] 225 | name = "crossbeam-epoch" 226 | version = "0.9.18" 227 | source = "registry+https://github.com/rust-lang/crates.io-index" 228 | checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" 229 | dependencies = [ 230 | "crossbeam-utils", 231 | ] 232 | 233 | [[package]] 234 | name = "crossbeam-utils" 235 | version = "0.8.20" 236 | source = "registry+https://github.com/rust-lang/crates.io-index" 237 | checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80" 238 | 239 | [[package]] 240 | name = "crunchy" 241 | version = "0.2.2" 242 | source = "registry+https://github.com/rust-lang/crates.io-index" 243 | checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" 244 | 245 | [[package]] 246 | name = "either" 247 | version = "1.13.0" 248 | source = "registry+https://github.com/rust-lang/crates.io-index" 249 | checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" 250 | 251 | [[package]] 252 | name = "half" 253 | version = "2.4.1" 254 | source = "registry+https://github.com/rust-lang/crates.io-index" 255 | checksum = "6dd08c532ae367adf81c312a4580bc67f1d0fe8bc9c460520283f4c0ff277888" 256 | dependencies = [ 257 | "cfg-if", 258 | "crunchy", 259 | ] 260 | 261 | [[package]] 262 | name = "heck" 263 | version = "0.5.0" 264 | source = "registry+https://github.com/rust-lang/crates.io-index" 265 | checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" 266 | 267 | [[package]] 268 | name = "hermit-abi" 269 | version = "0.4.0" 270 | source = "registry+https://github.com/rust-lang/crates.io-index" 271 | checksum = "fbf6a919d6cf397374f7dfeeea91d974c7c0a7221d0d0f4f20d859d329e53fcc" 272 | 273 | [[package]] 274 | name = "is-terminal" 275 | version = "0.4.13" 276 | source = "registry+https://github.com/rust-lang/crates.io-index" 277 | checksum = "261f68e344040fbd0edea105bef17c66edf46f984ddb1115b775ce31be948f4b" 278 | dependencies = [ 279 | "hermit-abi", 280 | "libc", 281 | "windows-sys 0.52.0", 282 | ] 283 | 284 | [[package]] 285 | name = "is_terminal_polyfill" 286 | version = "1.70.1" 287 | source = "registry+https://github.com/rust-lang/crates.io-index" 288 | checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" 289 | 290 | [[package]] 291 | name = "itertools" 292 | version = "0.10.5" 293 | source = "registry+https://github.com/rust-lang/crates.io-index" 294 | checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" 295 | dependencies = [ 296 | "either", 297 | ] 298 | 299 | [[package]] 300 | name = "itoa" 301 | version = "1.0.11" 302 | source = "registry+https://github.com/rust-lang/crates.io-index" 303 | checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" 304 | 305 | [[package]] 306 | name = "js-sys" 307 | version = "0.3.70" 308 | source = "registry+https://github.com/rust-lang/crates.io-index" 309 | checksum = "1868808506b929d7b0cfa8f75951347aa71bb21144b7791bae35d9bccfcfe37a" 310 | dependencies = [ 311 | "wasm-bindgen", 312 | ] 313 | 314 | [[package]] 315 | name = "libc" 316 | version = "0.2.158" 317 | source = "registry+https://github.com/rust-lang/crates.io-index" 318 | checksum = "d8adc4bb1803a324070e64a98ae98f38934d91957a99cfb3a43dcbc01bc56439" 319 | 320 | [[package]] 321 | name = "log" 322 | version = "0.4.22" 323 | source = "registry+https://github.com/rust-lang/crates.io-index" 324 | checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" 325 | 326 | [[package]] 327 | name = "memchr" 328 | version = "2.7.4" 329 | source = "registry+https://github.com/rust-lang/crates.io-index" 330 | checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" 331 | 332 | [[package]] 333 | name = "msvc-demangler" 334 | version = "0.10.1" 335 | source = "registry+https://github.com/rust-lang/crates.io-index" 336 | checksum = "c4c25a3bb7d880e8eceab4822f3141ad0700d20f025991c1f03bd3d00219a5fc" 337 | dependencies = [ 338 | "bitflags", 339 | ] 340 | 341 | [[package]] 342 | name = "nonmax" 343 | version = "0.5.5" 344 | source = "registry+https://github.com/rust-lang/crates.io-index" 345 | checksum = "610a5acd306ec67f907abe5567859a3c693fb9886eb1f012ab8f2a47bef3db51" 346 | 347 | [[package]] 348 | name = "num-traits" 349 | version = "0.2.19" 350 | source = "registry+https://github.com/rust-lang/crates.io-index" 351 | checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" 352 | dependencies = [ 353 | "autocfg", 354 | ] 355 | 356 | [[package]] 357 | name = "once_cell" 358 | version = "1.19.0" 359 | source = "registry+https://github.com/rust-lang/crates.io-index" 360 | checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" 361 | 362 | [[package]] 363 | name = "oorandom" 364 | version = "11.1.4" 365 | source = "registry+https://github.com/rust-lang/crates.io-index" 366 | checksum = "b410bbe7e14ab526a0e86877eb47c6996a2bd7746f027ba551028c925390e4e9" 367 | 368 | [[package]] 369 | name = "plotters" 370 | version = "0.3.6" 371 | source = "registry+https://github.com/rust-lang/crates.io-index" 372 | checksum = "a15b6eccb8484002195a3e44fe65a4ce8e93a625797a063735536fd59cb01cf3" 373 | dependencies = [ 374 | "num-traits", 375 | "plotters-backend", 376 | "plotters-svg", 377 | "wasm-bindgen", 378 | "web-sys", 379 | ] 380 | 381 | [[package]] 382 | name = "plotters-backend" 383 | version = "0.3.6" 384 | source = "registry+https://github.com/rust-lang/crates.io-index" 385 | checksum = "414cec62c6634ae900ea1c56128dfe87cf63e7caece0852ec76aba307cebadb7" 386 | 387 | [[package]] 388 | name = "plotters-svg" 389 | version = "0.3.6" 390 | source = "registry+https://github.com/rust-lang/crates.io-index" 391 | checksum = "81b30686a7d9c3e010b84284bdd26a29f2138574f52f5eb6f794fc0ad924e705" 392 | dependencies = [ 393 | "plotters-backend", 394 | ] 395 | 396 | [[package]] 397 | name = "proc-macro2" 398 | version = "1.0.86" 399 | source = "registry+https://github.com/rust-lang/crates.io-index" 400 | checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77" 401 | dependencies = [ 402 | "unicode-ident", 403 | ] 404 | 405 | [[package]] 406 | name = "quote" 407 | version = "1.0.36" 408 | source = "registry+https://github.com/rust-lang/crates.io-index" 409 | checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" 410 | dependencies = [ 411 | "proc-macro2", 412 | ] 413 | 414 | [[package]] 415 | name = "rayon" 416 | version = "1.10.0" 417 | source = "registry+https://github.com/rust-lang/crates.io-index" 418 | checksum = "b418a60154510ca1a002a752ca9714984e21e4241e804d32555251faf8b78ffa" 419 | dependencies = [ 420 | "either", 421 | "rayon-core", 422 | ] 423 | 424 | [[package]] 425 | name = "rayon-core" 426 | version = "1.12.1" 427 | source = "registry+https://github.com/rust-lang/crates.io-index" 428 | checksum = "1465873a3dfdaa8ae7cb14b4383657caab0b3e8a0aa9ae8e04b044854c8dfce2" 429 | dependencies = [ 430 | "crossbeam-deque", 431 | "crossbeam-utils", 432 | ] 433 | 434 | [[package]] 435 | name = "regex" 436 | version = "1.10.6" 437 | source = "registry+https://github.com/rust-lang/crates.io-index" 438 | checksum = "4219d74c6b67a3654a9fbebc4b419e22126d13d2f3c4a07ee0cb61ff79a79619" 439 | dependencies = [ 440 | "aho-corasick", 441 | "memchr", 442 | "regex-automata", 443 | "regex-syntax", 444 | ] 445 | 446 | [[package]] 447 | name = "regex-automata" 448 | version = "0.4.7" 449 | source = "registry+https://github.com/rust-lang/crates.io-index" 450 | checksum = "38caf58cc5ef2fed281f89292ef23f6365465ed9a41b7a7754eb4e26496c92df" 451 | dependencies = [ 452 | "aho-corasick", 453 | "memchr", 454 | "regex-syntax", 455 | ] 456 | 457 | [[package]] 458 | name = "regex-syntax" 459 | version = "0.8.4" 460 | source = "registry+https://github.com/rust-lang/crates.io-index" 461 | checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b" 462 | 463 | [[package]] 464 | name = "ryu" 465 | version = "1.0.18" 466 | source = "registry+https://github.com/rust-lang/crates.io-index" 467 | checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" 468 | 469 | [[package]] 470 | name = "same-file" 471 | version = "1.0.6" 472 | source = "registry+https://github.com/rust-lang/crates.io-index" 473 | checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" 474 | dependencies = [ 475 | "winapi-util", 476 | ] 477 | 478 | [[package]] 479 | name = "serde" 480 | version = "1.0.208" 481 | source = "registry+https://github.com/rust-lang/crates.io-index" 482 | checksum = "cff085d2cb684faa248efb494c39b68e522822ac0de72ccf08109abde717cfb2" 483 | dependencies = [ 484 | "serde_derive", 485 | ] 486 | 487 | [[package]] 488 | name = "serde_derive" 489 | version = "1.0.208" 490 | source = "registry+https://github.com/rust-lang/crates.io-index" 491 | checksum = "24008e81ff7613ed8e5ba0cfaf24e2c2f1e5b8a0495711e44fcd4882fca62bcf" 492 | dependencies = [ 493 | "proc-macro2", 494 | "quote", 495 | "syn", 496 | ] 497 | 498 | [[package]] 499 | name = "serde_json" 500 | version = "1.0.125" 501 | source = "registry+https://github.com/rust-lang/crates.io-index" 502 | checksum = "83c8e735a073ccf5be70aa8066aa984eaf2fa000db6c8d0100ae605b366d31ed" 503 | dependencies = [ 504 | "itoa", 505 | "memchr", 506 | "ryu", 507 | "serde", 508 | ] 509 | 510 | [[package]] 511 | name = "smallvec" 512 | version = "1.13.2" 513 | source = "registry+https://github.com/rust-lang/crates.io-index" 514 | checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" 515 | 516 | [[package]] 517 | name = "strsim" 518 | version = "0.11.1" 519 | source = "registry+https://github.com/rust-lang/crates.io-index" 520 | checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" 521 | 522 | [[package]] 523 | name = "syn" 524 | version = "2.0.75" 525 | source = "registry+https://github.com/rust-lang/crates.io-index" 526 | checksum = "f6af063034fc1935ede7be0122941bafa9bacb949334d090b77ca98b5817c7d9" 527 | dependencies = [ 528 | "proc-macro2", 529 | "quote", 530 | "unicode-ident", 531 | ] 532 | 533 | [[package]] 534 | name = "thiserror" 535 | version = "1.0.63" 536 | source = "registry+https://github.com/rust-lang/crates.io-index" 537 | checksum = "c0342370b38b6a11b6cc11d6a805569958d54cfa061a29969c3b5ce2ea405724" 538 | dependencies = [ 539 | "thiserror-impl", 540 | ] 541 | 542 | [[package]] 543 | name = "thiserror-impl" 544 | version = "1.0.63" 545 | source = "registry+https://github.com/rust-lang/crates.io-index" 546 | checksum = "a4558b58466b9ad7ca0f102865eccc95938dca1a74a856f2b57b6629050da261" 547 | dependencies = [ 548 | "proc-macro2", 549 | "quote", 550 | "syn", 551 | ] 552 | 553 | [[package]] 554 | name = "tinytemplate" 555 | version = "1.2.1" 556 | source = "registry+https://github.com/rust-lang/crates.io-index" 557 | checksum = "be4d6b5f19ff7664e8c98d03e2139cb510db9b0a60b55f8e8709b689d939b6bc" 558 | dependencies = [ 559 | "serde", 560 | "serde_json", 561 | ] 562 | 563 | [[package]] 564 | name = "undname" 565 | version = "2.1.2" 566 | dependencies = [ 567 | "arrayvec", 568 | "bitflags", 569 | "bumpalo", 570 | "clap", 571 | "criterion", 572 | "memchr", 573 | "msvc-demangler", 574 | "nonmax", 575 | "smallvec", 576 | "thiserror", 577 | "windows", 578 | ] 579 | 580 | [[package]] 581 | name = "unicode-ident" 582 | version = "1.0.12" 583 | source = "registry+https://github.com/rust-lang/crates.io-index" 584 | checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" 585 | 586 | [[package]] 587 | name = "utf8parse" 588 | version = "0.2.2" 589 | source = "registry+https://github.com/rust-lang/crates.io-index" 590 | checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" 591 | 592 | [[package]] 593 | name = "walkdir" 594 | version = "2.5.0" 595 | source = "registry+https://github.com/rust-lang/crates.io-index" 596 | checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" 597 | dependencies = [ 598 | "same-file", 599 | "winapi-util", 600 | ] 601 | 602 | [[package]] 603 | name = "wasm-bindgen" 604 | version = "0.2.93" 605 | source = "registry+https://github.com/rust-lang/crates.io-index" 606 | checksum = "a82edfc16a6c469f5f44dc7b571814045d60404b55a0ee849f9bcfa2e63dd9b5" 607 | dependencies = [ 608 | "cfg-if", 609 | "once_cell", 610 | "wasm-bindgen-macro", 611 | ] 612 | 613 | [[package]] 614 | name = "wasm-bindgen-backend" 615 | version = "0.2.93" 616 | source = "registry+https://github.com/rust-lang/crates.io-index" 617 | checksum = "9de396da306523044d3302746f1208fa71d7532227f15e347e2d93e4145dd77b" 618 | dependencies = [ 619 | "bumpalo", 620 | "log", 621 | "once_cell", 622 | "proc-macro2", 623 | "quote", 624 | "syn", 625 | "wasm-bindgen-shared", 626 | ] 627 | 628 | [[package]] 629 | name = "wasm-bindgen-macro" 630 | version = "0.2.93" 631 | source = "registry+https://github.com/rust-lang/crates.io-index" 632 | checksum = "585c4c91a46b072c92e908d99cb1dcdf95c5218eeb6f3bf1efa991ee7a68cccf" 633 | dependencies = [ 634 | "quote", 635 | "wasm-bindgen-macro-support", 636 | ] 637 | 638 | [[package]] 639 | name = "wasm-bindgen-macro-support" 640 | version = "0.2.93" 641 | source = "registry+https://github.com/rust-lang/crates.io-index" 642 | checksum = "afc340c74d9005395cf9dd098506f7f44e38f2b4a21c6aaacf9a105ea5e1e836" 643 | dependencies = [ 644 | "proc-macro2", 645 | "quote", 646 | "syn", 647 | "wasm-bindgen-backend", 648 | "wasm-bindgen-shared", 649 | ] 650 | 651 | [[package]] 652 | name = "wasm-bindgen-shared" 653 | version = "0.2.93" 654 | source = "registry+https://github.com/rust-lang/crates.io-index" 655 | checksum = "c62a0a307cb4a311d3a07867860911ca130c3494e8c2719593806c08bc5d0484" 656 | 657 | [[package]] 658 | name = "web-sys" 659 | version = "0.3.70" 660 | source = "registry+https://github.com/rust-lang/crates.io-index" 661 | checksum = "26fdeaafd9bd129f65e7c031593c24d62186301e0c72c8978fa1678be7d532c0" 662 | dependencies = [ 663 | "js-sys", 664 | "wasm-bindgen", 665 | ] 666 | 667 | [[package]] 668 | name = "winapi-util" 669 | version = "0.1.9" 670 | source = "registry+https://github.com/rust-lang/crates.io-index" 671 | checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" 672 | dependencies = [ 673 | "windows-sys 0.59.0", 674 | ] 675 | 676 | [[package]] 677 | name = "windows" 678 | version = "0.58.0" 679 | source = "registry+https://github.com/rust-lang/crates.io-index" 680 | checksum = "dd04d41d93c4992d421894c18c8b43496aa748dd4c081bac0dc93eb0489272b6" 681 | dependencies = [ 682 | "windows-core", 683 | "windows-targets", 684 | ] 685 | 686 | [[package]] 687 | name = "windows-core" 688 | version = "0.58.0" 689 | source = "registry+https://github.com/rust-lang/crates.io-index" 690 | checksum = "6ba6d44ec8c2591c134257ce647b7ea6b20335bf6379a27dac5f1641fcf59f99" 691 | dependencies = [ 692 | "windows-implement", 693 | "windows-interface", 694 | "windows-result", 695 | "windows-strings", 696 | "windows-targets", 697 | ] 698 | 699 | [[package]] 700 | name = "windows-implement" 701 | version = "0.58.0" 702 | source = "registry+https://github.com/rust-lang/crates.io-index" 703 | checksum = "2bbd5b46c938e506ecbce286b6628a02171d56153ba733b6c741fc627ec9579b" 704 | dependencies = [ 705 | "proc-macro2", 706 | "quote", 707 | "syn", 708 | ] 709 | 710 | [[package]] 711 | name = "windows-interface" 712 | version = "0.58.0" 713 | source = "registry+https://github.com/rust-lang/crates.io-index" 714 | checksum = "053c4c462dc91d3b1504c6fe5a726dd15e216ba718e84a0e46a88fbe5ded3515" 715 | dependencies = [ 716 | "proc-macro2", 717 | "quote", 718 | "syn", 719 | ] 720 | 721 | [[package]] 722 | name = "windows-result" 723 | version = "0.2.0" 724 | source = "registry+https://github.com/rust-lang/crates.io-index" 725 | checksum = "1d1043d8214f791817bab27572aaa8af63732e11bf84aa21a45a78d6c317ae0e" 726 | dependencies = [ 727 | "windows-targets", 728 | ] 729 | 730 | [[package]] 731 | name = "windows-strings" 732 | version = "0.1.0" 733 | source = "registry+https://github.com/rust-lang/crates.io-index" 734 | checksum = "4cd9b125c486025df0eabcb585e62173c6c9eddcec5d117d3b6e8c30e2ee4d10" 735 | dependencies = [ 736 | "windows-result", 737 | "windows-targets", 738 | ] 739 | 740 | [[package]] 741 | name = "windows-sys" 742 | version = "0.52.0" 743 | source = "registry+https://github.com/rust-lang/crates.io-index" 744 | checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" 745 | dependencies = [ 746 | "windows-targets", 747 | ] 748 | 749 | [[package]] 750 | name = "windows-sys" 751 | version = "0.59.0" 752 | source = "registry+https://github.com/rust-lang/crates.io-index" 753 | checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" 754 | dependencies = [ 755 | "windows-targets", 756 | ] 757 | 758 | [[package]] 759 | name = "windows-targets" 760 | version = "0.52.6" 761 | source = "registry+https://github.com/rust-lang/crates.io-index" 762 | checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" 763 | dependencies = [ 764 | "windows_aarch64_gnullvm", 765 | "windows_aarch64_msvc", 766 | "windows_i686_gnu", 767 | "windows_i686_gnullvm", 768 | "windows_i686_msvc", 769 | "windows_x86_64_gnu", 770 | "windows_x86_64_gnullvm", 771 | "windows_x86_64_msvc", 772 | ] 773 | 774 | [[package]] 775 | name = "windows_aarch64_gnullvm" 776 | version = "0.52.6" 777 | source = "registry+https://github.com/rust-lang/crates.io-index" 778 | checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" 779 | 780 | [[package]] 781 | name = "windows_aarch64_msvc" 782 | version = "0.52.6" 783 | source = "registry+https://github.com/rust-lang/crates.io-index" 784 | checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" 785 | 786 | [[package]] 787 | name = "windows_i686_gnu" 788 | version = "0.52.6" 789 | source = "registry+https://github.com/rust-lang/crates.io-index" 790 | checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" 791 | 792 | [[package]] 793 | name = "windows_i686_gnullvm" 794 | version = "0.52.6" 795 | source = "registry+https://github.com/rust-lang/crates.io-index" 796 | checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" 797 | 798 | [[package]] 799 | name = "windows_i686_msvc" 800 | version = "0.52.6" 801 | source = "registry+https://github.com/rust-lang/crates.io-index" 802 | checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" 803 | 804 | [[package]] 805 | name = "windows_x86_64_gnu" 806 | version = "0.52.6" 807 | source = "registry+https://github.com/rust-lang/crates.io-index" 808 | checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" 809 | 810 | [[package]] 811 | name = "windows_x86_64_gnullvm" 812 | version = "0.52.6" 813 | source = "registry+https://github.com/rust-lang/crates.io-index" 814 | checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" 815 | 816 | [[package]] 817 | name = "windows_x86_64_msvc" 818 | version = "0.52.6" 819 | source = "registry+https://github.com/rust-lang/crates.io-index" 820 | checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" 821 | -------------------------------------------------------------------------------- /src/nodes/intermediate.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2024 Ryan McKenzie 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | use crate::{ 16 | cache::{ 17 | NodeArray, 18 | NodeCache, 19 | NodeHandle, 20 | QualifiedName, 21 | }, 22 | nodes::{ 23 | ArrayTypeNode, 24 | ConversionOperatorIdentifierNode, 25 | CustomTypeNode, 26 | DynamicStructorIdentifierNode, 27 | EncodedStringLiteralNode, 28 | FuncClass, 29 | FunctionSignatureNode, 30 | FunctionSymbolNode, 31 | IntegerLiteralNode, 32 | IntrinsicFunctionIdentifierNode, 33 | LiteralOperatorIdentifierNode, 34 | LocalStaticGuardIdentifierNode, 35 | LocalStaticGuardVariableNode, 36 | Md5SymbolNode, 37 | NamedIdentifierNode, 38 | NodeArrayNode, 39 | PointerTypeNode, 40 | PrimitiveTypeNode, 41 | QualifiedNameNode, 42 | Qualifiers, 43 | Result, 44 | RttiBaseClassDescriptorNode, 45 | SpecialTableSymbolNode, 46 | StructorIdentifierNode, 47 | TagTypeNode, 48 | TemplateParameterReferenceNode, 49 | ThunkSignatureNode, 50 | VariableSymbolName, 51 | VariableSymbolNode, 52 | VcallThunkIdentifierNode, 53 | WriteableNode, 54 | WriteableTypeNode, 55 | }, 56 | OutputFlags, 57 | Writer, 58 | }; 59 | 60 | pub(crate) trait Downcast { 61 | #[must_use] 62 | fn downcast(self) -> Option; 63 | } 64 | 65 | macro_rules! impl_downcast { 66 | ($from:ident::$variant:ident => $to:ty) => { 67 | impl<'storage, 'alloc: 'storage> Downcast<$to> for $from<'storage, 'alloc> { 68 | fn downcast(self) -> Option<$to> { 69 | if let Self::$variant(x) = self { 70 | Some(x) 71 | } else { 72 | None 73 | } 74 | } 75 | } 76 | }; 77 | } 78 | 79 | macro_rules! impl_upcast { 80 | ($from:ty => $to:ident::$variant:ident) => { 81 | impl<'storage, 'alloc: 'storage> From<$from> for $to<'storage, 'alloc> { 82 | fn from(value: $from) -> Self { 83 | Self::$variant(value.into()) 84 | } 85 | } 86 | }; 87 | } 88 | 89 | #[allow(clippy::enum_variant_names)] 90 | #[derive(Clone, Copy)] 91 | pub(crate) enum Node< 92 | TypeT, 93 | IdentifierT, 94 | NodeArrayT, 95 | QualifiedNameT, 96 | TemplateParameterReferenceT, 97 | IntegerLiteralT, 98 | SymbolT, 99 | > { 100 | Type(TypeT), 101 | Identifier(IdentifierT), 102 | NodeArray(NodeArrayT), 103 | QualifiedName(QualifiedNameT), 104 | TemplateParameterReference(TemplateParameterReferenceT), 105 | IntegerLiteral(IntegerLiteralT), 106 | Symbol(SymbolT), 107 | } 108 | 109 | pub(super) type NodeConst<'storage, 'alloc> = Node< 110 | TypeNodeConst<'storage, 'alloc>, 111 | IdentifierNodeConst<'storage, 'alloc>, 112 | &'storage NodeArrayNode<'alloc>, 113 | &'storage QualifiedNameNode, 114 | &'storage TemplateParameterReferenceNode, 115 | &'storage IntegerLiteralNode, 116 | SymbolNodeConst<'storage, 'alloc>, 117 | >; 118 | 119 | impl<'storage, 'alloc: 'storage> WriteableNode for NodeConst<'storage, 'alloc> { 120 | fn output(&self, cache: &NodeCache, ob: &mut dyn Writer, flags: OutputFlags) -> Result<()> { 121 | match self { 122 | Self::Type(x) => x.output(cache, ob, flags), 123 | Self::Identifier(x) => x.output(cache, ob, flags), 124 | Self::NodeArray(x) => x.output(cache, ob, flags), 125 | Self::QualifiedName(x) => x.output(cache, ob, flags), 126 | Self::TemplateParameterReference(x) => x.output(cache, ob, flags), 127 | Self::IntegerLiteral(x) => x.output(cache, ob, flags), 128 | Self::Symbol(x) => x.output(cache, ob, flags), 129 | } 130 | } 131 | } 132 | 133 | impl_upcast!(TypeNodeConst<'storage, 'alloc> => NodeConst::Type); 134 | impl_upcast!(&'storage PrimitiveTypeNode => NodeConst::Type); 135 | impl_upcast!(SignatureNodeConst<'storage, 'alloc> => NodeConst::Type); 136 | impl_upcast!(&'storage PointerTypeNode => NodeConst::Type); 137 | impl_upcast!(&'storage TagTypeNode => NodeConst::Type); 138 | impl_upcast!(&'storage ArrayTypeNode => NodeConst::Type); 139 | impl_upcast!(&'storage CustomTypeNode => NodeConst::Type); 140 | 141 | impl_upcast!(IdentifierNodeConst<'storage, 'alloc> => NodeConst::Identifier); 142 | impl_upcast!(&'storage VcallThunkIdentifierNode => NodeConst::Identifier); 143 | impl_upcast!(&'storage DynamicStructorIdentifierNode => NodeConst::Identifier); 144 | impl_upcast!(&'storage NamedIdentifierNode<'alloc> => NodeConst::Identifier); 145 | impl_upcast!(&'storage IntrinsicFunctionIdentifierNode => NodeConst::Identifier); 146 | impl_upcast!(&'storage LiteralOperatorIdentifierNode<'alloc> => NodeConst::Identifier); 147 | impl_upcast!(&'storage LocalStaticGuardIdentifierNode => NodeConst::Identifier); 148 | impl_upcast!(&'storage ConversionOperatorIdentifierNode => NodeConst::Identifier); 149 | impl_upcast!(&'storage StructorIdentifierNode => NodeConst::Identifier); 150 | impl_upcast!(&'storage RttiBaseClassDescriptorNode => NodeConst::Identifier); 151 | 152 | impl_upcast!(&'storage NodeArrayNode<'alloc> => NodeConst::NodeArray); 153 | impl_upcast!(&'storage QualifiedNameNode => NodeConst::QualifiedName); 154 | impl_upcast!(&'storage TemplateParameterReferenceNode => NodeConst::TemplateParameterReference); 155 | impl_upcast!(&'storage IntegerLiteralNode => NodeConst::IntegerLiteral); 156 | 157 | impl_upcast!(SymbolNodeConst<'storage, 'alloc> => NodeConst::Symbol); 158 | impl_upcast!(&'storage Md5SymbolNode => NodeConst::Symbol); 159 | impl_upcast!(&'storage SpecialTableSymbolNode => NodeConst::Symbol); 160 | impl_upcast!(&'storage LocalStaticGuardVariableNode => NodeConst::Symbol); 161 | impl_upcast!(&'storage EncodedStringLiteralNode<'alloc> => NodeConst::Symbol); 162 | impl_upcast!(&'storage VariableSymbolNode => NodeConst::Symbol); 163 | impl_upcast!(&'storage FunctionSymbolNode => NodeConst::Symbol); 164 | 165 | impl<'storage, 'alloc: 'storage> From<&'storage FunctionSignatureNode> 166 | for NodeConst<'storage, 'alloc> 167 | { 168 | fn from(value: &'storage FunctionSignatureNode) -> Self { 169 | let value: SignatureNodeConst = value.into(); 170 | Self::Type(value.into()) 171 | } 172 | } 173 | 174 | impl<'storage, 'alloc: 'storage> From<&'storage ThunkSignatureNode> 175 | for NodeConst<'storage, 'alloc> 176 | { 177 | fn from(value: &'storage ThunkSignatureNode) -> Self { 178 | let value: SignatureNodeConst = value.into(); 179 | Self::Type(value.into()) 180 | } 181 | } 182 | 183 | impl_downcast!(NodeConst::Type => TypeNodeConst<'storage, 'alloc>); 184 | impl_downcast!(NodeConst::Identifier => IdentifierNodeConst<'storage, 'alloc>); 185 | impl_downcast!(NodeConst::NodeArray => &'storage NodeArrayNode<'alloc>); 186 | impl_downcast!(NodeConst::QualifiedName => &'storage QualifiedNameNode); 187 | impl_downcast!(NodeConst::TemplateParameterReference => &'storage TemplateParameterReferenceNode); 188 | impl_downcast!(NodeConst::IntegerLiteral => &'storage IntegerLiteralNode); 189 | impl_downcast!(NodeConst::Symbol => SymbolNodeConst<'storage, 'alloc>); 190 | 191 | impl<'storage, 'alloc: 'storage> Downcast> 192 | for NodeConst<'storage, 'alloc> 193 | { 194 | fn downcast(self) -> Option> { 195 | if let Self::Type(TypeNode::Signature(inner)) = self { 196 | Some(inner) 197 | } else { 198 | None 199 | } 200 | } 201 | } 202 | 203 | impl<'storage, 'alloc: 'storage> Downcast<&'storage FunctionSignatureNode> 204 | for NodeConst<'storage, 'alloc> 205 | { 206 | fn downcast(self) -> Option<&'storage FunctionSignatureNode> { 207 | if let Self::Type(TypeNode::Signature(SignatureNode::FunctionSignature(node))) = self { 208 | Some(node) 209 | } else { 210 | None 211 | } 212 | } 213 | } 214 | 215 | impl<'storage, 'alloc: 'storage> Downcast<&'storage ThunkSignatureNode> 216 | for NodeConst<'storage, 'alloc> 217 | { 218 | fn downcast(self) -> Option<&'storage ThunkSignatureNode> { 219 | if let Self::Type(TypeNode::Signature(SignatureNode::ThunkSignature(node))) = self { 220 | Some(node) 221 | } else { 222 | None 223 | } 224 | } 225 | } 226 | 227 | pub(super) type NodeMut<'storage, 'alloc> = Node< 228 | TypeNodeMut<'storage, 'alloc>, 229 | IdentifierNodeMut<'storage, 'alloc>, 230 | &'storage mut NodeArrayNode<'alloc>, 231 | &'storage mut QualifiedNameNode, 232 | &'storage mut TemplateParameterReferenceNode, 233 | &'storage mut IntegerLiteralNode, 234 | SymbolNodeMut<'storage, 'alloc>, 235 | >; 236 | 237 | impl_upcast!(TypeNodeMut<'storage, 'alloc> => NodeMut::Type); 238 | impl_upcast!(&'storage mut PrimitiveTypeNode => NodeMut::Type); 239 | impl_upcast!(SignatureNodeMut<'storage, 'alloc> => NodeMut::Type); 240 | impl_upcast!(&'storage mut PointerTypeNode => NodeMut::Type); 241 | impl_upcast!(&'storage mut TagTypeNode => NodeMut::Type); 242 | impl_upcast!(&'storage mut ArrayTypeNode => NodeMut::Type); 243 | impl_upcast!(&'storage mut CustomTypeNode => NodeMut::Type); 244 | 245 | impl_upcast!(IdentifierNodeMut<'storage, 'alloc> => NodeMut::Identifier); 246 | impl_upcast!(&'storage mut VcallThunkIdentifierNode => NodeMut::Identifier); 247 | impl_upcast!(&'storage mut DynamicStructorIdentifierNode => NodeMut::Identifier); 248 | impl_upcast!(&'storage mut NamedIdentifierNode<'alloc> => NodeMut::Identifier); 249 | impl_upcast!(&'storage mut IntrinsicFunctionIdentifierNode => NodeMut::Identifier); 250 | impl_upcast!(&'storage mut LiteralOperatorIdentifierNode<'alloc> => NodeMut::Identifier); 251 | impl_upcast!(&'storage mut LocalStaticGuardIdentifierNode => NodeMut::Identifier); 252 | impl_upcast!(&'storage mut ConversionOperatorIdentifierNode => NodeMut::Identifier); 253 | impl_upcast!(&'storage mut StructorIdentifierNode => NodeMut::Identifier); 254 | impl_upcast!(&'storage mut RttiBaseClassDescriptorNode => NodeMut::Identifier); 255 | 256 | impl_upcast!(&'storage mut NodeArrayNode<'alloc> => NodeMut::NodeArray); 257 | impl_upcast!(&'storage mut QualifiedNameNode => NodeMut::QualifiedName); 258 | impl_upcast!(&'storage mut TemplateParameterReferenceNode => NodeMut::TemplateParameterReference); 259 | impl_upcast!(&'storage mut IntegerLiteralNode => NodeMut::IntegerLiteral); 260 | 261 | impl_upcast!(SymbolNodeMut<'storage, 'alloc> => NodeMut::Symbol); 262 | impl_upcast!(&'storage mut Md5SymbolNode => NodeMut::Symbol); 263 | impl_upcast!(&'storage mut SpecialTableSymbolNode => NodeMut::Symbol); 264 | impl_upcast!(&'storage mut LocalStaticGuardVariableNode => NodeMut::Symbol); 265 | impl_upcast!(&'storage mut EncodedStringLiteralNode<'alloc> => NodeMut::Symbol); 266 | impl_upcast!(&'storage mut VariableSymbolNode => NodeMut::Symbol); 267 | impl_upcast!(&'storage mut FunctionSymbolNode => NodeMut::Symbol); 268 | 269 | impl<'storage, 'alloc: 'storage> From<&'storage mut FunctionSignatureNode> 270 | for NodeMut<'storage, 'alloc> 271 | { 272 | fn from(value: &'storage mut FunctionSignatureNode) -> Self { 273 | let value: SignatureNodeMut = value.into(); 274 | Self::Type(value.into()) 275 | } 276 | } 277 | 278 | impl<'storage, 'alloc: 'storage> From<&'storage mut ThunkSignatureNode> 279 | for NodeMut<'storage, 'alloc> 280 | { 281 | fn from(value: &'storage mut ThunkSignatureNode) -> Self { 282 | let value: SignatureNodeMut = value.into(); 283 | Self::Type(value.into()) 284 | } 285 | } 286 | 287 | impl_downcast!(NodeMut::Type => TypeNodeMut<'storage, 'alloc>); 288 | impl_downcast!(NodeMut::Identifier => IdentifierNodeMut<'storage, 'alloc>); 289 | impl_downcast!(NodeMut::NodeArray => &'storage mut NodeArrayNode<'alloc>); 290 | impl_downcast!(NodeMut::QualifiedName => &'storage mut QualifiedNameNode); 291 | impl_downcast!(NodeMut::TemplateParameterReference => &'storage mut TemplateParameterReferenceNode); 292 | impl_downcast!(NodeMut::IntegerLiteral => &'storage mut IntegerLiteralNode); 293 | impl_downcast!(NodeMut::Symbol => SymbolNodeMut<'storage, 'alloc>); 294 | 295 | impl<'storage, 'alloc: 'storage> Downcast> 296 | for NodeMut<'storage, 'alloc> 297 | { 298 | fn downcast(self) -> Option> { 299 | if let Self::Type(TypeNode::Signature(inner)) = self { 300 | Some(inner) 301 | } else { 302 | None 303 | } 304 | } 305 | } 306 | 307 | impl<'storage, 'alloc: 'storage> Downcast<&'storage mut FunctionSignatureNode> 308 | for NodeMut<'storage, 'alloc> 309 | { 310 | fn downcast(self) -> Option<&'storage mut FunctionSignatureNode> { 311 | if let Self::Type(TypeNode::Signature(SignatureNode::FunctionSignature(node))) = self { 312 | Some(node) 313 | } else { 314 | None 315 | } 316 | } 317 | } 318 | 319 | impl<'storage, 'alloc: 'storage> Downcast<&'storage mut ThunkSignatureNode> 320 | for NodeMut<'storage, 'alloc> 321 | { 322 | fn downcast(self) -> Option<&'storage mut ThunkSignatureNode> { 323 | if let Self::Type(TypeNode::Signature(SignatureNode::ThunkSignature(node))) = self { 324 | Some(node) 325 | } else { 326 | None 327 | } 328 | } 329 | } 330 | 331 | #[derive(Clone, Copy)] 332 | pub(crate) enum TypeNode< 333 | PrimitiveTypeT, 334 | SignatureT, 335 | PointerTypeT, 336 | TagTypeT, 337 | ArrayTypeT, 338 | CustomTypeT, 339 | > { 340 | PrimitiveType(PrimitiveTypeT), 341 | Signature(SignatureT), 342 | PointerType(PointerTypeT), 343 | TagType(TagTypeT), 344 | ArrayType(ArrayTypeT), 345 | CustomType(CustomTypeT), 346 | } 347 | 348 | pub(super) type TypeNodeConst<'storage, 'alloc> = TypeNode< 349 | &'storage PrimitiveTypeNode, 350 | SignatureNodeConst<'storage, 'alloc>, 351 | &'storage PointerTypeNode, 352 | &'storage TagTypeNode, 353 | &'storage ArrayTypeNode, 354 | &'storage CustomTypeNode, 355 | >; 356 | 357 | impl<'storage, 'alloc: 'storage> WriteableNode for TypeNodeConst<'storage, 'alloc> { 358 | fn output(&self, cache: &NodeCache, ob: &mut dyn Writer, flags: OutputFlags) -> Result<()> { 359 | self.output_pair(cache, ob, flags) 360 | } 361 | } 362 | 363 | impl<'storage, 'alloc: 'storage> WriteableTypeNode for TypeNodeConst<'storage, 'alloc> { 364 | fn output_pair( 365 | &self, 366 | cache: &NodeCache, 367 | ob: &mut dyn Writer, 368 | flags: OutputFlags, 369 | ) -> Result<()> { 370 | match self { 371 | Self::PrimitiveType(x) => x.output_pair(cache, ob, flags), 372 | Self::Signature(x) => x.output_pair(cache, ob, flags), 373 | Self::PointerType(x) => x.output_pair(cache, ob, flags), 374 | Self::TagType(x) => x.output_pair(cache, ob, flags), 375 | Self::ArrayType(x) => x.output_pair(cache, ob, flags), 376 | Self::CustomType(x) => x.output_pair(cache, ob, flags), 377 | } 378 | } 379 | 380 | fn output_pre(&self, cache: &NodeCache, ob: &mut dyn Writer, flags: OutputFlags) -> Result<()> { 381 | match self { 382 | Self::PrimitiveType(x) => x.output_pre(cache, ob, flags), 383 | Self::Signature(x) => x.output_pre(cache, ob, flags), 384 | Self::PointerType(x) => x.output_pre(cache, ob, flags), 385 | Self::TagType(x) => x.output_pre(cache, ob, flags), 386 | Self::ArrayType(x) => x.output_pre(cache, ob, flags), 387 | Self::CustomType(x) => x.output_pre(cache, ob, flags), 388 | } 389 | } 390 | 391 | fn output_post( 392 | &self, 393 | cache: &NodeCache, 394 | ob: &mut dyn Writer, 395 | flags: OutputFlags, 396 | ) -> Result<()> { 397 | match self { 398 | Self::PrimitiveType(x) => x.output_post(cache, ob, flags), 399 | Self::Signature(x) => x.output_post(cache, ob, flags), 400 | Self::PointerType(x) => x.output_post(cache, ob, flags), 401 | Self::TagType(x) => x.output_post(cache, ob, flags), 402 | Self::ArrayType(x) => x.output_post(cache, ob, flags), 403 | Self::CustomType(x) => x.output_post(cache, ob, flags), 404 | } 405 | } 406 | } 407 | 408 | impl_upcast!(&'storage PrimitiveTypeNode => TypeNodeConst::PrimitiveType); 409 | impl_upcast!(SignatureNodeConst<'storage, 'alloc> => TypeNodeConst::Signature); 410 | impl_upcast!(&'storage FunctionSignatureNode => TypeNodeConst::Signature); 411 | impl_upcast!(&'storage ThunkSignatureNode => TypeNodeConst::Signature); 412 | impl_upcast!(&'storage PointerTypeNode => TypeNodeConst::PointerType); 413 | impl_upcast!(&'storage TagTypeNode => TypeNodeConst::TagType); 414 | impl_upcast!(&'storage ArrayTypeNode => TypeNodeConst::ArrayType); 415 | impl_upcast!(&'storage CustomTypeNode => TypeNodeConst::CustomType); 416 | 417 | impl_downcast!(TypeNodeConst::PrimitiveType => &'storage PrimitiveTypeNode); 418 | impl_downcast!(TypeNodeConst::Signature => SignatureNodeConst<'storage, 'alloc>); 419 | impl_downcast!(TypeNodeConst::PointerType => &'storage PointerTypeNode); 420 | impl_downcast!(TypeNodeConst::TagType => &'storage TagTypeNode); 421 | impl_downcast!(TypeNodeConst::ArrayType => &'storage ArrayTypeNode); 422 | impl_downcast!(TypeNodeConst::CustomType => &'storage CustomTypeNode); 423 | 424 | impl<'storage, 'alloc: 'storage> Downcast<&'storage FunctionSignatureNode> 425 | for TypeNodeConst<'storage, 'alloc> 426 | { 427 | fn downcast(self) -> Option<&'storage FunctionSignatureNode> { 428 | if let Self::Signature(SignatureNode::FunctionSignature(inner)) = self { 429 | Some(inner) 430 | } else { 431 | None 432 | } 433 | } 434 | } 435 | 436 | impl<'storage, 'alloc: 'storage> Downcast<&'storage ThunkSignatureNode> 437 | for TypeNodeConst<'storage, 'alloc> 438 | { 439 | fn downcast(self) -> Option<&'storage ThunkSignatureNode> { 440 | if let Self::Signature(SignatureNode::ThunkSignature(inner)) = self { 441 | Some(inner) 442 | } else { 443 | None 444 | } 445 | } 446 | } 447 | 448 | pub(super) type TypeNodeMut<'storage, 'alloc> = TypeNode< 449 | &'storage mut PrimitiveTypeNode, 450 | SignatureNodeMut<'storage, 'alloc>, 451 | &'storage mut PointerTypeNode, 452 | &'storage mut TagTypeNode, 453 | &'storage mut ArrayTypeNode, 454 | &'storage mut CustomTypeNode, 455 | >; 456 | 457 | impl<'storage, 'alloc: 'storage> TypeNodeMut<'storage, 'alloc> { 458 | pub(crate) fn append_quals(&mut self, quals: Qualifiers) { 459 | match self { 460 | Self::PrimitiveType(x) => x.quals |= quals, 461 | Self::Signature(x) => match x { 462 | SignatureNode::FunctionSignature(x) => x.quals |= quals, 463 | SignatureNode::ThunkSignature(x) => x.quals |= quals, 464 | }, 465 | Self::PointerType(x) => x.quals |= quals, 466 | Self::TagType(x) => x.quals |= quals, 467 | Self::ArrayType(x) => x.quals |= quals, 468 | Self::CustomType(x) => x.quals |= quals, 469 | } 470 | } 471 | 472 | pub(crate) fn set_quals(&mut self, quals: Qualifiers) { 473 | match self { 474 | Self::PrimitiveType(x) => x.quals = quals, 475 | Self::Signature(x) => match x { 476 | SignatureNode::FunctionSignature(x) => x.quals = quals, 477 | SignatureNode::ThunkSignature(x) => x.quals = quals, 478 | }, 479 | Self::PointerType(x) => x.quals = quals, 480 | Self::TagType(x) => x.quals = quals, 481 | Self::ArrayType(x) => x.quals = quals, 482 | Self::CustomType(x) => x.quals = quals, 483 | } 484 | } 485 | } 486 | 487 | impl_upcast!(&'storage mut PrimitiveTypeNode => TypeNodeMut::PrimitiveType); 488 | impl_upcast!(SignatureNodeMut<'storage, 'alloc> => TypeNodeMut::Signature); 489 | impl_upcast!(&'storage mut FunctionSignatureNode => TypeNodeMut::Signature); 490 | impl_upcast!(&'storage mut ThunkSignatureNode => TypeNodeMut::Signature); 491 | impl_upcast!(&'storage mut PointerTypeNode => TypeNodeMut::PointerType); 492 | impl_upcast!(&'storage mut TagTypeNode => TypeNodeMut::TagType); 493 | impl_upcast!(&'storage mut ArrayTypeNode => TypeNodeMut::ArrayType); 494 | impl_upcast!(&'storage mut CustomTypeNode => TypeNodeMut::CustomType); 495 | 496 | impl_downcast!(TypeNodeMut::PrimitiveType => &'storage mut PrimitiveTypeNode); 497 | impl_downcast!(TypeNodeMut::Signature => SignatureNodeMut<'storage, 'alloc>); 498 | impl_downcast!(TypeNodeMut::PointerType => &'storage mut PointerTypeNode); 499 | impl_downcast!(TypeNodeMut::TagType => &'storage mut TagTypeNode); 500 | impl_downcast!(TypeNodeMut::ArrayType => &'storage mut ArrayTypeNode); 501 | impl_downcast!(TypeNodeMut::CustomType => &'storage mut CustomTypeNode); 502 | 503 | impl<'storage, 'alloc: 'storage> Downcast<&'storage mut FunctionSignatureNode> 504 | for TypeNodeMut<'storage, 'alloc> 505 | { 506 | fn downcast(self) -> Option<&'storage mut FunctionSignatureNode> { 507 | if let Self::Signature(SignatureNode::FunctionSignature(inner)) = self { 508 | Some(inner) 509 | } else { 510 | None 511 | } 512 | } 513 | } 514 | 515 | impl<'storage, 'alloc: 'storage> Downcast<&'storage mut ThunkSignatureNode> 516 | for TypeNodeMut<'storage, 'alloc> 517 | { 518 | fn downcast(self) -> Option<&'storage mut ThunkSignatureNode> { 519 | if let Self::Signature(SignatureNode::ThunkSignature(inner)) = self { 520 | Some(inner) 521 | } else { 522 | None 523 | } 524 | } 525 | } 526 | 527 | #[derive(Clone, Copy)] 528 | pub(crate) enum SignatureNode { 529 | FunctionSignature(FunctionSignatureT), 530 | ThunkSignature(ThunkSignatureT), 531 | } 532 | 533 | pub(super) type SignatureNodeConst<'storage, 'alloc> = 534 | SignatureNode<&'storage FunctionSignatureNode, &'storage ThunkSignatureNode>; 535 | 536 | impl<'storage, 'alloc: 'storage> SignatureNodeConst<'storage, 'alloc> { 537 | pub(crate) fn as_node(&self) -> &FunctionSignatureNode { 538 | match self { 539 | Self::FunctionSignature(x) => x, 540 | Self::ThunkSignature(x) => &x.function_node, 541 | } 542 | } 543 | } 544 | 545 | impl<'storage, 'alloc: 'storage> WriteableNode for SignatureNodeConst<'storage, 'alloc> { 546 | fn output(&self, cache: &NodeCache, ob: &mut dyn Writer, flags: OutputFlags) -> Result<()> { 547 | self.output_pair(cache, ob, flags) 548 | } 549 | } 550 | 551 | impl<'storage, 'alloc: 'storage> WriteableTypeNode for SignatureNodeConst<'storage, 'alloc> { 552 | fn output_pair( 553 | &self, 554 | cache: &NodeCache, 555 | ob: &mut dyn Writer, 556 | flags: OutputFlags, 557 | ) -> Result<()> { 558 | match self { 559 | Self::FunctionSignature(x) => x.output_pair(cache, ob, flags), 560 | Self::ThunkSignature(x) => x.output_pair(cache, ob, flags), 561 | } 562 | } 563 | 564 | fn output_pre(&self, cache: &NodeCache, ob: &mut dyn Writer, flags: OutputFlags) -> Result<()> { 565 | match self { 566 | Self::FunctionSignature(x) => x.output_pre(cache, ob, flags), 567 | Self::ThunkSignature(x) => x.output_pre(cache, ob, flags), 568 | } 569 | } 570 | 571 | fn output_post( 572 | &self, 573 | cache: &NodeCache, 574 | ob: &mut dyn Writer, 575 | flags: OutputFlags, 576 | ) -> Result<()> { 577 | match self { 578 | Self::FunctionSignature(x) => x.output_post(cache, ob, flags), 579 | Self::ThunkSignature(x) => x.output_post(cache, ob, flags), 580 | } 581 | } 582 | } 583 | 584 | impl_upcast!(&'storage FunctionSignatureNode => SignatureNodeConst::FunctionSignature); 585 | impl_upcast!(&'storage ThunkSignatureNode => SignatureNodeConst::ThunkSignature); 586 | 587 | impl_downcast!(SignatureNodeConst::FunctionSignature => &'storage FunctionSignatureNode); 588 | impl_downcast!(SignatureNodeConst::ThunkSignature => &'storage ThunkSignatureNode); 589 | 590 | pub(super) type SignatureNodeMut<'storage, 'alloc> = 591 | SignatureNode<&'storage mut FunctionSignatureNode, &'storage mut ThunkSignatureNode>; 592 | 593 | impl<'storage, 'alloc: 'storage> SignatureNodeMut<'storage, 'alloc> { 594 | pub(crate) fn set_function_class(&mut self, function_class: FuncClass) { 595 | match self { 596 | Self::FunctionSignature(x) => x.function_class = function_class, 597 | Self::ThunkSignature(x) => x.function_class = function_class, 598 | } 599 | } 600 | } 601 | 602 | impl_upcast!(&'storage mut FunctionSignatureNode => SignatureNodeMut::FunctionSignature); 603 | impl_upcast!(&'storage mut ThunkSignatureNode => SignatureNodeMut::ThunkSignature); 604 | 605 | impl_downcast!(SignatureNodeMut::FunctionSignature => &'storage mut FunctionSignatureNode); 606 | impl_downcast!(SignatureNodeMut::ThunkSignature => &'storage mut ThunkSignatureNode); 607 | 608 | #[derive(Clone, Copy)] 609 | pub(crate) enum IdentifierNode< 610 | VcallThunkIdentifierT, 611 | DynamicStructorIdentifierT, 612 | NamedIdentifierT, 613 | IntrinsicFunctionIdentifierT, 614 | LiteralOperatorIdentifierT, 615 | LocalStaticGuardIdentifierT, 616 | ConversionOperatorIdentifierT, 617 | StructorIdentifierT, 618 | RttiBaseClassDescriptorT, 619 | > { 620 | VcallThunkIdentifier(VcallThunkIdentifierT), 621 | DynamicStructorIdentifier(DynamicStructorIdentifierT), 622 | NamedIdentifier(NamedIdentifierT), 623 | IntrinsicFunctionIdentifier(IntrinsicFunctionIdentifierT), 624 | LiteralOperatorIdentifier(LiteralOperatorIdentifierT), 625 | LocalStaticGuardIdentifier(LocalStaticGuardIdentifierT), 626 | ConversionOperatorIdentifier(ConversionOperatorIdentifierT), 627 | StructorIdentifier(StructorIdentifierT), 628 | RttiBaseClassDescriptor(RttiBaseClassDescriptorT), 629 | } 630 | 631 | pub(super) type IdentifierNodeConst<'storage, 'alloc> = IdentifierNode< 632 | &'storage VcallThunkIdentifierNode, 633 | &'storage DynamicStructorIdentifierNode, 634 | &'storage NamedIdentifierNode<'alloc>, 635 | &'storage IntrinsicFunctionIdentifierNode, 636 | &'storage LiteralOperatorIdentifierNode<'alloc>, 637 | &'storage LocalStaticGuardIdentifierNode, 638 | &'storage ConversionOperatorIdentifierNode, 639 | &'storage StructorIdentifierNode, 640 | &'storage RttiBaseClassDescriptorNode, 641 | >; 642 | 643 | impl<'storage, 'alloc: 'storage> WriteableNode for IdentifierNodeConst<'storage, 'alloc> { 644 | fn output(&self, cache: &NodeCache, ob: &mut dyn Writer, flags: OutputFlags) -> Result<()> { 645 | match self { 646 | Self::VcallThunkIdentifier(x) => x.output(cache, ob, flags), 647 | Self::DynamicStructorIdentifier(x) => x.output(cache, ob, flags), 648 | Self::NamedIdentifier(x) => x.output(cache, ob, flags), 649 | Self::IntrinsicFunctionIdentifier(x) => x.output(cache, ob, flags), 650 | Self::LiteralOperatorIdentifier(x) => x.output(cache, ob, flags), 651 | Self::LocalStaticGuardIdentifier(x) => x.output(cache, ob, flags), 652 | Self::ConversionOperatorIdentifier(x) => x.output(cache, ob, flags), 653 | Self::StructorIdentifier(x) => x.output(cache, ob, flags), 654 | Self::RttiBaseClassDescriptor(x) => x.output(cache, ob, flags), 655 | } 656 | } 657 | } 658 | 659 | impl_upcast!(&'storage VcallThunkIdentifierNode => IdentifierNodeConst::VcallThunkIdentifier); 660 | impl_upcast!(&'storage DynamicStructorIdentifierNode => IdentifierNodeConst::DynamicStructorIdentifier); 661 | impl_upcast!(&'storage NamedIdentifierNode<'alloc> => IdentifierNodeConst::NamedIdentifier); 662 | impl_upcast!(&'storage IntrinsicFunctionIdentifierNode => IdentifierNodeConst::IntrinsicFunctionIdentifier); 663 | impl_upcast!(&'storage LiteralOperatorIdentifierNode<'alloc> => IdentifierNodeConst::LiteralOperatorIdentifier); 664 | impl_upcast!(&'storage LocalStaticGuardIdentifierNode => IdentifierNodeConst::LocalStaticGuardIdentifier); 665 | impl_upcast!(&'storage ConversionOperatorIdentifierNode => IdentifierNodeConst::ConversionOperatorIdentifier); 666 | impl_upcast!(&'storage StructorIdentifierNode => IdentifierNodeConst::StructorIdentifier); 667 | impl_upcast!(&'storage RttiBaseClassDescriptorNode => IdentifierNodeConst::RttiBaseClassDescriptor); 668 | 669 | impl_downcast!(IdentifierNodeConst::VcallThunkIdentifier => &'storage VcallThunkIdentifierNode); 670 | impl_downcast!(IdentifierNodeConst::DynamicStructorIdentifier => &'storage DynamicStructorIdentifierNode); 671 | impl_downcast!(IdentifierNodeConst::NamedIdentifier => &'storage NamedIdentifierNode<'alloc>); 672 | impl_downcast!(IdentifierNodeConst::IntrinsicFunctionIdentifier => &'storage IntrinsicFunctionIdentifierNode); 673 | impl_downcast!(IdentifierNodeConst::LiteralOperatorIdentifier => &'storage LiteralOperatorIdentifierNode<'alloc>); 674 | impl_downcast!(IdentifierNodeConst::LocalStaticGuardIdentifier => &'storage LocalStaticGuardIdentifierNode); 675 | impl_downcast!(IdentifierNodeConst::ConversionOperatorIdentifier => &'storage ConversionOperatorIdentifierNode); 676 | impl_downcast!(IdentifierNodeConst::StructorIdentifier => &'storage StructorIdentifierNode); 677 | impl_downcast!(IdentifierNodeConst::RttiBaseClassDescriptor => &'storage RttiBaseClassDescriptorNode); 678 | 679 | pub(super) type IdentifierNodeMut<'storage, 'alloc> = IdentifierNode< 680 | &'storage mut VcallThunkIdentifierNode, 681 | &'storage mut DynamicStructorIdentifierNode, 682 | &'storage mut NamedIdentifierNode<'alloc>, 683 | &'storage mut IntrinsicFunctionIdentifierNode, 684 | &'storage mut LiteralOperatorIdentifierNode<'alloc>, 685 | &'storage mut LocalStaticGuardIdentifierNode, 686 | &'storage mut ConversionOperatorIdentifierNode, 687 | &'storage mut StructorIdentifierNode, 688 | &'storage mut RttiBaseClassDescriptorNode, 689 | >; 690 | 691 | impl<'storage, 'alloc: 'storage> IdentifierNodeMut<'storage, 'alloc> { 692 | pub(crate) fn set_template_params(&mut self, template_params: NodeHandle) { 693 | let params = match self { 694 | Self::VcallThunkIdentifier(x) => &mut x.template_params, 695 | Self::DynamicStructorIdentifier(x) => &mut x.template_params, 696 | Self::NamedIdentifier(x) => &mut x.template_params, 697 | Self::IntrinsicFunctionIdentifier(x) => &mut x.template_params, 698 | Self::LiteralOperatorIdentifier(x) => &mut x.template_params, 699 | Self::LocalStaticGuardIdentifier(x) => &mut x.template_params, 700 | Self::ConversionOperatorIdentifier(x) => &mut x.template_params, 701 | Self::StructorIdentifier(x) => &mut x.template_params, 702 | Self::RttiBaseClassDescriptor(x) => &mut x.template_params, 703 | }; 704 | params.0 = Some(template_params); 705 | } 706 | } 707 | 708 | impl_upcast!(&'storage mut VcallThunkIdentifierNode => IdentifierNodeMut::VcallThunkIdentifier); 709 | impl_upcast!(&'storage mut DynamicStructorIdentifierNode => IdentifierNodeMut::DynamicStructorIdentifier); 710 | impl_upcast!(&'storage mut NamedIdentifierNode<'alloc> => IdentifierNodeMut::NamedIdentifier); 711 | impl_upcast!(&'storage mut IntrinsicFunctionIdentifierNode => IdentifierNodeMut::IntrinsicFunctionIdentifier); 712 | impl_upcast!(&'storage mut LiteralOperatorIdentifierNode<'alloc> => IdentifierNodeMut::LiteralOperatorIdentifier); 713 | impl_upcast!(&'storage mut LocalStaticGuardIdentifierNode => IdentifierNodeMut::LocalStaticGuardIdentifier); 714 | impl_upcast!(&'storage mut ConversionOperatorIdentifierNode => IdentifierNodeMut::ConversionOperatorIdentifier); 715 | impl_upcast!(&'storage mut StructorIdentifierNode => IdentifierNodeMut::StructorIdentifier); 716 | impl_upcast!(&'storage mut RttiBaseClassDescriptorNode => IdentifierNodeMut::RttiBaseClassDescriptor); 717 | 718 | impl_downcast!(IdentifierNodeMut::VcallThunkIdentifier => &'storage mut VcallThunkIdentifierNode); 719 | impl_downcast!(IdentifierNodeMut::DynamicStructorIdentifier => &'storage mut DynamicStructorIdentifierNode); 720 | impl_downcast!(IdentifierNodeMut::NamedIdentifier => &'storage mut NamedIdentifierNode<'alloc>); 721 | impl_downcast!(IdentifierNodeMut::IntrinsicFunctionIdentifier => &'storage mut IntrinsicFunctionIdentifierNode); 722 | impl_downcast!(IdentifierNodeMut::LiteralOperatorIdentifier => &'storage mut LiteralOperatorIdentifierNode<'alloc>); 723 | impl_downcast!(IdentifierNodeMut::LocalStaticGuardIdentifier => &'storage mut LocalStaticGuardIdentifierNode); 724 | impl_downcast!(IdentifierNodeMut::ConversionOperatorIdentifier => &'storage mut ConversionOperatorIdentifierNode); 725 | impl_downcast!(IdentifierNodeMut::StructorIdentifier => &'storage mut StructorIdentifierNode); 726 | impl_downcast!(IdentifierNodeMut::RttiBaseClassDescriptor => &'storage mut RttiBaseClassDescriptorNode); 727 | 728 | #[derive(Clone, Copy)] 729 | pub(crate) enum SymbolNode< 730 | Md5SymbolT, 731 | SpecialTableSymbolT, 732 | LocalStaticGuardVariableT, 733 | EncodedStringLiteralT, 734 | VariableSymbolT, 735 | FunctionSymbolT, 736 | > { 737 | Md5Symbol(Md5SymbolT), 738 | SpecialTableSymbol(SpecialTableSymbolT), 739 | LocalStaticGuardVariable(LocalStaticGuardVariableT), 740 | EncodedStringLiteral(EncodedStringLiteralT), 741 | VariableSymbol(VariableSymbolT), 742 | FunctionSymbol(FunctionSymbolT), 743 | } 744 | 745 | pub(super) type SymbolNodeConst<'storage, 'alloc> = SymbolNode< 746 | &'storage Md5SymbolNode, 747 | &'storage SpecialTableSymbolNode, 748 | &'storage LocalStaticGuardVariableNode, 749 | &'storage EncodedStringLiteralNode<'alloc>, 750 | &'storage VariableSymbolNode, 751 | &'storage FunctionSymbolNode, 752 | >; 753 | 754 | impl<'storage, 'alloc: 'storage> SymbolNodeConst<'storage, 'alloc> { 755 | #[must_use] 756 | pub(crate) fn get_name(&self) -> Option> { 757 | match self { 758 | Self::Md5Symbol(x) => Some(x.name), 759 | Self::SpecialTableSymbol(x) => Some(x.name), 760 | Self::LocalStaticGuardVariable(x) => Some(x.name), 761 | Self::EncodedStringLiteral(x) => x.name, 762 | Self::VariableSymbol(x) => { 763 | if let Some(VariableSymbolName::Qualified(name)) = x.name { 764 | Some(name) 765 | } else { 766 | None 767 | } 768 | } 769 | Self::FunctionSymbol(x) => x.name, 770 | } 771 | } 772 | } 773 | 774 | impl<'storage, 'alloc: 'storage> WriteableNode for SymbolNodeConst<'storage, 'alloc> { 775 | fn output(&self, cache: &NodeCache, ob: &mut dyn Writer, flags: OutputFlags) -> Result<()> { 776 | match self { 777 | Self::Md5Symbol(x) => x.output(cache, ob, flags), 778 | Self::SpecialTableSymbol(x) => x.output(cache, ob, flags), 779 | Self::LocalStaticGuardVariable(x) => x.output(cache, ob, flags), 780 | Self::EncodedStringLiteral(x) => x.output(cache, ob, flags), 781 | Self::VariableSymbol(x) => x.output(cache, ob, flags), 782 | Self::FunctionSymbol(x) => x.output(cache, ob, flags), 783 | } 784 | } 785 | } 786 | 787 | impl_upcast!(&'storage Md5SymbolNode => SymbolNodeConst::Md5Symbol); 788 | impl_upcast!(&'storage SpecialTableSymbolNode => SymbolNodeConst::SpecialTableSymbol); 789 | impl_upcast!(&'storage LocalStaticGuardVariableNode => SymbolNodeConst::LocalStaticGuardVariable); 790 | impl_upcast!(&'storage EncodedStringLiteralNode<'alloc> => SymbolNodeConst::EncodedStringLiteral); 791 | impl_upcast!(&'storage VariableSymbolNode => SymbolNodeConst::VariableSymbol); 792 | impl_upcast!(&'storage FunctionSymbolNode => SymbolNodeConst::FunctionSymbol); 793 | 794 | impl_downcast!(SymbolNodeConst::Md5Symbol => &'storage Md5SymbolNode); 795 | impl_downcast!(SymbolNodeConst::SpecialTableSymbol => &'storage SpecialTableSymbolNode); 796 | impl_downcast!(SymbolNodeConst::LocalStaticGuardVariable => &'storage LocalStaticGuardVariableNode); 797 | impl_downcast!(SymbolNodeConst::EncodedStringLiteral => &'storage EncodedStringLiteralNode<'alloc>); 798 | impl_downcast!(SymbolNodeConst::VariableSymbol => &'storage VariableSymbolNode); 799 | impl_downcast!(SymbolNodeConst::FunctionSymbol => &'storage FunctionSymbolNode); 800 | 801 | pub(super) type SymbolNodeMut<'storage, 'alloc> = SymbolNode< 802 | &'storage mut Md5SymbolNode, 803 | &'storage mut SpecialTableSymbolNode, 804 | &'storage mut LocalStaticGuardVariableNode, 805 | &'storage mut EncodedStringLiteralNode<'alloc>, 806 | &'storage mut VariableSymbolNode, 807 | &'storage mut FunctionSymbolNode, 808 | >; 809 | 810 | impl<'storage, 'alloc: 'storage> SymbolNodeMut<'storage, 'alloc> { 811 | pub(crate) fn set_name(&mut self, name: NodeHandle) { 812 | match self { 813 | Self::Md5Symbol(x) => x.name = name, 814 | Self::SpecialTableSymbol(x) => x.name = name, 815 | Self::LocalStaticGuardVariable(x) => x.name = name, 816 | Self::EncodedStringLiteral(x) => x.name = Some(name), 817 | Self::VariableSymbol(x) => x.name = Some(VariableSymbolName::Qualified(name)), 818 | Self::FunctionSymbol(x) => x.name = Some(name), 819 | } 820 | } 821 | } 822 | 823 | impl_upcast!(&'storage mut Md5SymbolNode => SymbolNodeMut::Md5Symbol); 824 | impl_upcast!(&'storage mut SpecialTableSymbolNode => SymbolNodeMut::SpecialTableSymbol); 825 | impl_upcast!(&'storage mut LocalStaticGuardVariableNode => SymbolNodeMut::LocalStaticGuardVariable); 826 | impl_upcast!(&'storage mut EncodedStringLiteralNode<'alloc> => SymbolNodeMut::EncodedStringLiteral); 827 | impl_upcast!(&'storage mut VariableSymbolNode => SymbolNodeMut::VariableSymbol); 828 | impl_upcast!(&'storage mut FunctionSymbolNode => SymbolNodeMut::FunctionSymbol); 829 | 830 | impl_downcast!(SymbolNodeMut::Md5Symbol => &'storage mut Md5SymbolNode); 831 | impl_downcast!(SymbolNodeMut::SpecialTableSymbol => &'storage mut SpecialTableSymbolNode); 832 | impl_downcast!(SymbolNodeMut::LocalStaticGuardVariable => &'storage mut LocalStaticGuardVariableNode); 833 | impl_downcast!(SymbolNodeMut::EncodedStringLiteral => &'storage mut EncodedStringLiteralNode<'alloc>); 834 | impl_downcast!(SymbolNodeMut::VariableSymbol => &'storage mut VariableSymbolNode); 835 | impl_downcast!(SymbolNodeMut::FunctionSymbol => &'storage mut FunctionSymbolNode); 836 | 837 | pub(crate) trait IntermediateNode<'storage, 'alloc: 'storage> { 838 | type Const; 839 | type Mut; 840 | } 841 | 842 | macro_rules! is_intermediate_node { 843 | ($interface:ident => ($const:ident, $mut:ident)) => { 844 | pub(crate) struct $interface; 845 | 846 | impl<'storage, 'alloc: 'storage> IntermediateNode<'storage, 'alloc> for $interface { 847 | type Const = $const<'storage, 'alloc>; 848 | type Mut = $mut<'storage, 'alloc>; 849 | } 850 | }; 851 | } 852 | 853 | is_intermediate_node!(INode => (NodeConst, NodeMut)); 854 | is_intermediate_node!(ITypeNode => (TypeNodeConst, TypeNodeMut)); 855 | is_intermediate_node!(ISignatureNode => (SignatureNodeConst, SignatureNodeMut)); 856 | is_intermediate_node!(IIdentifierNode => (IdentifierNodeConst, IdentifierNodeMut)); 857 | is_intermediate_node!(ISymbolNode => (SymbolNodeConst, SymbolNodeMut)); 858 | -------------------------------------------------------------------------------- /src/nodes/derived.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2024 Ryan McKenzie 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | use crate::{ 16 | alloc, 17 | cache::{ 18 | NodeArray, 19 | NodeCache, 20 | NodeHandle, 21 | QualifiedName, 22 | VariableSymbol, 23 | }, 24 | nodes::{ 25 | CallingConv, 26 | CharKind, 27 | Downcast as _, 28 | FuncClass, 29 | FunctionRefQualifier, 30 | IIdentifierNode, 31 | INode, 32 | ISignatureNode, 33 | ISymbolNode, 34 | ITypeNode, 35 | IntrinsicFunctionKind, 36 | PointerAffinity, 37 | PrimitiveKind, 38 | Qualifiers, 39 | Result, 40 | SignatureNode, 41 | StorageClass, 42 | TagKind, 43 | TypeNode, 44 | WriteableNode, 45 | WriteableTypeNode, 46 | }, 47 | OutputFlags, 48 | Writer, 49 | }; 50 | use arrayvec::ArrayVec; 51 | use bumpalo::Bump; 52 | use std::{ 53 | mem::ManuallyDrop, 54 | ops::{ 55 | Deref, 56 | DerefMut, 57 | }, 58 | }; 59 | 60 | #[derive(Clone, Copy)] 61 | pub(crate) struct PrimitiveTypeNode { 62 | pub(crate) quals: Qualifiers, 63 | pub(crate) prim_kind: PrimitiveKind, 64 | } 65 | 66 | impl PrimitiveTypeNode { 67 | #[must_use] 68 | pub(crate) fn new(prim_kind: PrimitiveKind) -> Self { 69 | Self { 70 | quals: Qualifiers::Q_None, 71 | prim_kind, 72 | } 73 | } 74 | } 75 | 76 | impl WriteableNode for PrimitiveTypeNode { 77 | fn output(&self, cache: &NodeCache, ob: &mut dyn Writer, flags: OutputFlags) -> Result<()> { 78 | self.output_pair(cache, ob, flags) 79 | } 80 | } 81 | 82 | impl WriteableTypeNode for PrimitiveTypeNode { 83 | fn output_pre(&self, _: &NodeCache, ob: &mut dyn Writer, flags: OutputFlags) -> Result<()> { 84 | let kind = match self.prim_kind { 85 | PrimitiveKind::Void => "void", 86 | PrimitiveKind::Bool => "bool", 87 | PrimitiveKind::Char => "char", 88 | PrimitiveKind::Schar => "signed char", 89 | PrimitiveKind::Uchar => "unsigned char", 90 | PrimitiveKind::Char8 => "char8_t", 91 | PrimitiveKind::Char16 => "char16_t", 92 | PrimitiveKind::Char32 => "char32_t", 93 | PrimitiveKind::Short => "short", 94 | PrimitiveKind::Ushort => "unsigned short", 95 | PrimitiveKind::Int => "int", 96 | PrimitiveKind::Uint => "unsigned int", 97 | PrimitiveKind::Long => "long", 98 | PrimitiveKind::Ulong => "unsigned long", 99 | PrimitiveKind::Int64 => "__int64", 100 | PrimitiveKind::Uint64 => "unsigned __int64", 101 | PrimitiveKind::Wchar => "wchar_t", 102 | PrimitiveKind::Float => "float", 103 | PrimitiveKind::Double => "double", 104 | PrimitiveKind::Ldouble => "long double", 105 | PrimitiveKind::Nullptr => "std::nullptr_t", 106 | PrimitiveKind::Auto => "auto", 107 | PrimitiveKind::DecltypeAuto => "decltype(auto)", 108 | }; 109 | write!(ob, "{kind}")?; 110 | self.quals.output(ob, flags, true, false) 111 | } 112 | 113 | fn output_post(&self, _: &NodeCache, _: &mut dyn Writer, _: OutputFlags) -> Result<()> { 114 | Ok(()) 115 | } 116 | } 117 | 118 | #[derive(Clone, Copy)] 119 | pub(crate) struct FunctionSignatureNode { 120 | pub(crate) quals: Qualifiers, 121 | 122 | // Valid if this FunctionTypeNode is the Pointee of a PointerType or 123 | // MemberPointerType. 124 | #[allow(unused)] 125 | pub(crate) affinity: Option, 126 | 127 | // The function's calling convention. 128 | pub(crate) call_convention: Option, 129 | 130 | // Function flags (gloabl, public, etc) 131 | pub(crate) function_class: FuncClass, 132 | 133 | pub(crate) ref_qualifier: Option, 134 | 135 | // The return type of the function. 136 | pub(crate) return_type: Option>, 137 | 138 | // True if this is a C-style ... varargs function. 139 | pub(crate) is_variadic: bool, 140 | 141 | // Function parameters 142 | pub(crate) params: Option>, 143 | 144 | // True if the function type is noexcept. 145 | pub(crate) is_noexcept: bool, 146 | } 147 | 148 | impl FunctionSignatureNode { 149 | fn do_output_pre( 150 | &self, 151 | cache: &NodeCache, 152 | ob: &mut dyn Writer, 153 | flags: OutputFlags, 154 | is_function_ptr: bool, 155 | ) -> Result<()> { 156 | if !flags.no_access_specifier() && !flags.name_only() { 157 | if self.function_class.is_public() { 158 | write!(ob, "public: ")?; 159 | } 160 | if self.function_class.is_protected() { 161 | write!(ob, "protected: ")?; 162 | } 163 | if self.function_class.is_private() { 164 | write!(ob, "private: ")?; 165 | } 166 | } 167 | 168 | if !flags.no_member_type() && !flags.name_only() { 169 | if !self.function_class.is_global() && self.function_class.is_static() { 170 | write!(ob, "static ")?; 171 | } 172 | if self.function_class.is_virtual() { 173 | write!(ob, "virtual ")?; 174 | } 175 | if self.function_class.is_extern_c() { 176 | write!(ob, "extern \"C\" ")?; 177 | } 178 | } 179 | 180 | if !flags.no_return_type() && (is_function_ptr || !flags.name_only()) { 181 | if let Some(return_type) = self.return_type.map(|x| x.resolve(cache)) { 182 | return_type.output_pre(cache, ob, flags)?; 183 | write!(ob, " ")?; 184 | } 185 | } 186 | 187 | if !is_function_ptr 188 | && !flags.no_calling_convention() 189 | && !flags.no_ms_keywords() 190 | && !flags.name_only() 191 | { 192 | if let Some(call_convention) = self.call_convention { 193 | call_convention.output(ob, flags)?; 194 | } 195 | } 196 | 197 | Ok(()) 198 | } 199 | 200 | fn do_output_post( 201 | &self, 202 | cache: &NodeCache, 203 | ob: &mut dyn Writer, 204 | flags: OutputFlags, 205 | is_function_ptr: bool, 206 | ) -> Result<()> { 207 | if (is_function_ptr || !flags.name_only()) && !self.function_class.no_parameter_list() { 208 | write!(ob, "(")?; 209 | if let Some(params) = self.params.map(|x| x.resolve(cache)) { 210 | params.output(cache, ob, flags)?; 211 | } else { 212 | write!(ob, "void")?; 213 | } 214 | 215 | if self.is_variadic { 216 | if ob.last_char().is_some_and(|x| x != '(') { 217 | write!(ob, ", ")?; 218 | } 219 | write!(ob, "...")?; 220 | } 221 | write!(ob, ")")?; 222 | } 223 | 224 | if !flags.no_this_type() && !flags.name_only() { 225 | if self.quals.is_const() { 226 | write!(ob, " const")?; 227 | } 228 | if self.quals.is_volatile() { 229 | write!(ob, " volatile")?; 230 | } 231 | if !flags.no_ms_keywords() { 232 | if self.quals.is_restrict() { 233 | if flags.no_leading_underscores() { 234 | write!(ob, " restrict")?; 235 | } else { 236 | write!(ob, " __restrict")?; 237 | } 238 | } 239 | if self.quals.is_unaligned() { 240 | if flags.no_leading_underscores() { 241 | write!(ob, " unaligned")?; 242 | } else { 243 | write!(ob, " __unaligned")?; 244 | } 245 | } 246 | } 247 | } 248 | 249 | if self.is_noexcept { 250 | write!(ob, " noexcept")?; 251 | } 252 | 253 | if !flags.no_this_type() && !flags.name_only() { 254 | match self.ref_qualifier { 255 | Some(FunctionRefQualifier::Reference) => write!(ob, " &")?, 256 | Some(FunctionRefQualifier::RValueReference) => write!(ob, " &&")?, 257 | _ => (), 258 | } 259 | } 260 | 261 | if !flags.no_return_type() && !flags.name_only() { 262 | if let Some(return_type) = self.return_type.map(|x| x.resolve(cache)) { 263 | return_type.output_post(cache, ob, flags)?; 264 | } 265 | } 266 | 267 | Ok(()) 268 | } 269 | } 270 | 271 | impl Default for FunctionSignatureNode { 272 | fn default() -> Self { 273 | Self { 274 | quals: Qualifiers::Q_None, 275 | affinity: None, 276 | call_convention: None, 277 | function_class: FuncClass::FC_Global, 278 | ref_qualifier: None, 279 | return_type: None, 280 | is_variadic: false, 281 | params: None, 282 | is_noexcept: false, 283 | } 284 | } 285 | } 286 | 287 | impl WriteableNode for FunctionSignatureNode { 288 | fn output(&self, cache: &NodeCache, ob: &mut dyn Writer, flags: OutputFlags) -> Result<()> { 289 | self.output_pair(cache, ob, flags) 290 | } 291 | } 292 | 293 | impl WriteableTypeNode for FunctionSignatureNode { 294 | fn output_pre(&self, cache: &NodeCache, ob: &mut dyn Writer, flags: OutputFlags) -> Result<()> { 295 | self.do_output_pre(cache, ob, flags, false) 296 | } 297 | 298 | fn output_post( 299 | &self, 300 | cache: &NodeCache, 301 | ob: &mut dyn Writer, 302 | flags: OutputFlags, 303 | ) -> Result<()> { 304 | self.do_output_post(cache, ob, flags, false) 305 | } 306 | } 307 | 308 | #[derive(Clone, Copy, Default)] 309 | pub(crate) struct ThisAdjustor { 310 | pub(crate) static_offset: u32, 311 | pub(crate) vbptr_offset: i32, 312 | pub(crate) vboffset_offset: i32, 313 | pub(crate) vtor_disp_offset: i32, 314 | } 315 | 316 | #[derive(Clone, Copy, Default)] 317 | pub(crate) struct ThunkSignatureNode { 318 | pub(crate) function_node: FunctionSignatureNode, 319 | pub(crate) this_adjust: ThisAdjustor, 320 | } 321 | 322 | impl ThunkSignatureNode { 323 | fn do_output_pre( 324 | &self, 325 | cache: &NodeCache, 326 | ob: &mut dyn Writer, 327 | flags: OutputFlags, 328 | is_function_ptr: bool, 329 | ) -> Result<()> { 330 | if !flags.name_only() { 331 | write!(ob, "[thunk]: ")?; 332 | } 333 | self.function_node 334 | .do_output_pre(cache, ob, flags, is_function_ptr) 335 | } 336 | 337 | fn do_output_post( 338 | &self, 339 | cache: &NodeCache, 340 | ob: &mut dyn Writer, 341 | flags: OutputFlags, 342 | is_function_ptr: bool, 343 | ) -> Result<()> { 344 | let ThisAdjustor { 345 | static_offset, 346 | vbptr_offset, 347 | vboffset_offset, 348 | vtor_disp_offset, 349 | } = self.this_adjust; 350 | 351 | if self.function_class.has_static_this_adjust() { 352 | write!(ob, "`adjustor{{{static_offset}}}'")?; 353 | } else if self.function_class.has_virtual_this_adjust() { 354 | if self.function_class.has_virtual_this_adjust_ex() { 355 | write!(ob, "`vtordispex{{{vbptr_offset}, {vboffset_offset}, {vtor_disp_offset}, {static_offset}}}'")?; 356 | } else { 357 | write!(ob, "`vtordisp{{{vtor_disp_offset}, {static_offset}}}'")?; 358 | } 359 | } 360 | 361 | self.function_node 362 | .do_output_post(cache, ob, flags, is_function_ptr) 363 | } 364 | } 365 | 366 | impl Deref for ThunkSignatureNode { 367 | type Target = FunctionSignatureNode; 368 | 369 | fn deref(&self) -> &Self::Target { 370 | &self.function_node 371 | } 372 | } 373 | 374 | impl DerefMut for ThunkSignatureNode { 375 | fn deref_mut(&mut self) -> &mut Self::Target { 376 | &mut self.function_node 377 | } 378 | } 379 | 380 | impl WriteableNode for ThunkSignatureNode { 381 | fn output(&self, cache: &NodeCache, ob: &mut dyn Writer, flags: OutputFlags) -> Result<()> { 382 | self.output_pair(cache, ob, flags) 383 | } 384 | } 385 | 386 | impl WriteableTypeNode for ThunkSignatureNode { 387 | fn output_pre(&self, cache: &NodeCache, ob: &mut dyn Writer, flags: OutputFlags) -> Result<()> { 388 | self.do_output_pre(cache, ob, flags, false) 389 | } 390 | 391 | fn output_post( 392 | &self, 393 | cache: &NodeCache, 394 | ob: &mut dyn Writer, 395 | flags: OutputFlags, 396 | ) -> Result<()> { 397 | self.do_output_post(cache, ob, flags, false) 398 | } 399 | } 400 | 401 | #[derive(Clone, Copy)] 402 | pub(crate) struct PointerTypeNode { 403 | pub(crate) quals: Qualifiers, 404 | 405 | // Is this a pointer, reference, or rvalue-reference? 406 | pub(crate) affinity: Option, 407 | 408 | // If this is a member pointer, this is the class that the member is in. 409 | pub(crate) class_parent: Option>, 410 | 411 | // Represents a type X in "a pointer to X", "a reference to X", or 412 | // "rvalue-reference to X" 413 | pub(crate) pointee: NodeHandle, 414 | } 415 | 416 | impl WriteableNode for PointerTypeNode { 417 | fn output(&self, cache: &NodeCache, ob: &mut dyn Writer, flags: OutputFlags) -> Result<()> { 418 | self.output_pair(cache, ob, flags) 419 | } 420 | } 421 | 422 | impl WriteableTypeNode for PointerTypeNode { 423 | fn output_pre(&self, cache: &NodeCache, ob: &mut dyn Writer, flags: OutputFlags) -> Result<()> { 424 | let pointee = self.pointee.resolve(cache); 425 | if let TypeNode::Signature(sig) = pointee { 426 | // If this is a pointer to a function, don't output the calling convention. 427 | // It needs to go inside the parentheses. 428 | match sig { 429 | SignatureNode::FunctionSignature(func) => { 430 | func.do_output_pre(cache, ob, flags, true)?; 431 | } 432 | SignatureNode::ThunkSignature(thunk) => { 433 | thunk.do_output_pre(cache, ob, flags, true)?; 434 | } 435 | } 436 | } else { 437 | pointee.output_pre(cache, ob, flags)?; 438 | } 439 | 440 | super::output_space_if_necessary(ob)?; 441 | 442 | if !flags.no_ms_keywords() && self.quals.is_unaligned() { 443 | if flags.no_leading_underscores() { 444 | write!(ob, "unaligned ")?; 445 | } else { 446 | write!(ob, "__unaligned ")?; 447 | } 448 | } 449 | 450 | match pointee { 451 | TypeNode::ArrayType(_) => write!(ob, "(")?, 452 | TypeNode::Signature(sig) => { 453 | write!(ob, "(")?; 454 | if !flags.no_calling_convention() && !flags.no_ms_keywords() { 455 | if let Some(call_convention) = sig.as_node().call_convention { 456 | call_convention.output(ob, flags)?; 457 | write!(ob, " ")?; 458 | } 459 | } 460 | } 461 | _ => (), 462 | } 463 | 464 | if let Some(class_parent) = self.class_parent.map(|x| x.resolve(cache)) { 465 | class_parent.output(cache, ob, flags)?; 466 | write!(ob, "::")?; 467 | } 468 | 469 | let affinity = self 470 | .affinity 471 | .expect("pointer should have an affinity by this point"); 472 | match affinity { 473 | PointerAffinity::Pointer => write!(ob, "*")?, 474 | PointerAffinity::Reference => write!(ob, "&")?, 475 | PointerAffinity::RValueReference => write!(ob, "&&")?, 476 | } 477 | 478 | self.quals.output(ob, flags, false, false) 479 | } 480 | 481 | fn output_post( 482 | &self, 483 | cache: &NodeCache, 484 | ob: &mut dyn Writer, 485 | flags: OutputFlags, 486 | ) -> Result<()> { 487 | let pointee = self.pointee.resolve(cache); 488 | if matches!(pointee, TypeNode::ArrayType(_) | TypeNode::Signature(_)) { 489 | write!(ob, ")")?; 490 | } 491 | if let TypeNode::Signature(sig) = pointee { 492 | match sig { 493 | SignatureNode::FunctionSignature(func) => { 494 | func.do_output_post(cache, ob, flags, true) 495 | } 496 | SignatureNode::ThunkSignature(thunk) => { 497 | thunk.do_output_post(cache, ob, flags, true) 498 | } 499 | } 500 | } else { 501 | pointee.output_post(cache, ob, flags) 502 | } 503 | } 504 | } 505 | 506 | #[derive(Clone, Copy)] 507 | pub(crate) struct TagTypeNode { 508 | pub(crate) quals: Qualifiers, 509 | pub(crate) qualified_name: NodeHandle, 510 | pub(crate) tag: TagKind, 511 | } 512 | 513 | impl WriteableNode for TagTypeNode { 514 | fn output(&self, cache: &NodeCache, ob: &mut dyn Writer, flags: OutputFlags) -> Result<()> { 515 | self.output_pair(cache, ob, flags) 516 | } 517 | } 518 | 519 | impl WriteableTypeNode for TagTypeNode { 520 | fn output_pre(&self, cache: &NodeCache, ob: &mut dyn Writer, flags: OutputFlags) -> Result<()> { 521 | if !flags.no_tag_specifier() && !flags.name_only() { 522 | let tag = match self.tag { 523 | TagKind::Class => "class", 524 | TagKind::Struct => "struct", 525 | TagKind::Union => "union", 526 | TagKind::Enum => "enum", 527 | }; 528 | write!(ob, "{tag} ")?; 529 | } 530 | 531 | self.qualified_name 532 | .resolve(cache) 533 | .output(cache, ob, flags)?; 534 | 535 | self.quals.output(ob, flags, true, false) 536 | } 537 | 538 | fn output_post(&self, _: &NodeCache, _: &mut dyn Writer, _: OutputFlags) -> Result<()> { 539 | Ok(()) 540 | } 541 | } 542 | 543 | #[derive(Clone, Copy)] 544 | pub(crate) struct ArrayTypeNode { 545 | pub(crate) quals: Qualifiers, 546 | 547 | // A list of array dimensions. e.g. [3,4,5] in `int Foo[3][4][5]` 548 | pub(crate) dimensions: NodeHandle, 549 | 550 | // The type of array element. 551 | pub(crate) element_type: NodeHandle, 552 | } 553 | 554 | impl ArrayTypeNode { 555 | fn output_one_dimension( 556 | cache: &NodeCache, 557 | ob: &mut dyn Writer, 558 | flags: OutputFlags, 559 | node: NodeHandle, 560 | ) -> Result<()> { 561 | let node = node.resolve(cache); 562 | let iln: &IntegerLiteralNode = node.downcast().expect( 563 | "the dimensions of an ArrayTypeNode should always be instances of IntegerLiteralNode", 564 | ); 565 | 566 | if iln.value != 0 { 567 | iln.output(cache, ob, flags) 568 | } else { 569 | Ok(()) 570 | } 571 | } 572 | 573 | fn output_dimensions_impl( 574 | &self, 575 | cache: &NodeCache, 576 | ob: &mut dyn Writer, 577 | flags: OutputFlags, 578 | ) -> Result<()> { 579 | let dimensions = self.dimensions.resolve(cache); 580 | if let Some((&first, rest)) = dimensions.nodes.split_first() { 581 | Self::output_one_dimension(cache, ob, flags, first)?; 582 | for &handle in rest { 583 | write!(ob, "][")?; 584 | Self::output_one_dimension(cache, ob, flags, handle)?; 585 | } 586 | } 587 | 588 | Ok(()) 589 | } 590 | } 591 | 592 | impl WriteableNode for ArrayTypeNode { 593 | fn output(&self, cache: &NodeCache, ob: &mut dyn Writer, flags: OutputFlags) -> Result<()> { 594 | self.output_pair(cache, ob, flags) 595 | } 596 | } 597 | 598 | impl WriteableTypeNode for ArrayTypeNode { 599 | fn output_pre(&self, cache: &NodeCache, ob: &mut dyn Writer, flags: OutputFlags) -> Result<()> { 600 | self.element_type 601 | .resolve(cache) 602 | .output_pre(cache, ob, flags)?; 603 | self.quals.output(ob, flags, true, false) 604 | } 605 | 606 | fn output_post( 607 | &self, 608 | cache: &NodeCache, 609 | ob: &mut dyn Writer, 610 | flags: OutputFlags, 611 | ) -> Result<()> { 612 | write!(ob, "[")?; 613 | self.output_dimensions_impl(cache, ob, flags)?; 614 | write!(ob, "]")?; 615 | self.element_type 616 | .resolve(cache) 617 | .output_post(cache, ob, flags) 618 | } 619 | } 620 | 621 | #[derive(Clone, Copy)] 622 | pub(crate) struct CustomTypeNode { 623 | pub(crate) quals: Qualifiers, 624 | pub(crate) identifier: NodeHandle, 625 | } 626 | 627 | impl WriteableNode for CustomTypeNode { 628 | fn output(&self, cache: &NodeCache, ob: &mut dyn Writer, flags: OutputFlags) -> Result<()> { 629 | self.output_pair(cache, ob, flags) 630 | } 631 | } 632 | 633 | impl WriteableTypeNode for CustomTypeNode { 634 | fn output_pre(&self, cache: &NodeCache, ob: &mut dyn Writer, flags: OutputFlags) -> Result<()> { 635 | self.identifier.resolve(cache).output(cache, ob, flags) 636 | } 637 | 638 | fn output_post(&self, _: &NodeCache, _: &mut dyn Writer, _: OutputFlags) -> Result<()> { 639 | Ok(()) 640 | } 641 | } 642 | 643 | #[derive(Clone, Copy, Default)] 644 | pub(crate) struct TemplateParameters(pub(crate) Option>); 645 | 646 | impl TemplateParameters { 647 | fn output(self, cache: &NodeCache, ob: &mut dyn Writer, flags: OutputFlags) -> Result<()> { 648 | if let Some(this) = self.map(|x| x.resolve(cache)) { 649 | write!(ob, "<")?; 650 | this.output(cache, ob, flags)?; 651 | write!(ob, ">")?; 652 | } 653 | Ok(()) 654 | } 655 | } 656 | 657 | impl Deref for TemplateParameters { 658 | type Target = Option>; 659 | 660 | fn deref(&self) -> &Self::Target { 661 | &self.0 662 | } 663 | } 664 | 665 | impl DerefMut for TemplateParameters { 666 | fn deref_mut(&mut self) -> &mut Self::Target { 667 | &mut self.0 668 | } 669 | } 670 | 671 | #[derive(Clone, Copy, Default)] 672 | pub(crate) struct VcallThunkIdentifierNode { 673 | pub(crate) template_params: TemplateParameters, 674 | pub(crate) offset_in_vtable: u64, 675 | } 676 | 677 | impl WriteableNode for VcallThunkIdentifierNode { 678 | fn output(&self, _: &NodeCache, ob: &mut dyn Writer, flags: OutputFlags) -> Result<()> { 679 | if flags.name_only() { 680 | write!(ob, "`vcall'{{{}}}", self.offset_in_vtable)?; 681 | } else { 682 | write!(ob, "`vcall'{{{}, {{flat}}}}", self.offset_in_vtable)?; 683 | } 684 | Ok(()) 685 | } 686 | } 687 | 688 | #[derive(Clone, Copy)] 689 | pub(crate) enum DynamicStructorIdentifier { 690 | Variable(NodeHandle), 691 | Name(NodeHandle), 692 | } 693 | 694 | impl From> for DynamicStructorIdentifier { 695 | fn from(value: NodeHandle) -> Self { 696 | Self::Variable(value) 697 | } 698 | } 699 | 700 | impl From> for DynamicStructorIdentifier { 701 | fn from(value: NodeHandle) -> Self { 702 | Self::Name(value) 703 | } 704 | } 705 | 706 | #[derive(Clone, Copy)] 707 | pub(crate) struct DynamicStructorIdentifierNode { 708 | pub(crate) template_params: TemplateParameters, 709 | pub(crate) identifier: DynamicStructorIdentifier, 710 | pub(crate) is_destructor: bool, 711 | } 712 | 713 | impl WriteableNode for DynamicStructorIdentifierNode { 714 | fn output(&self, cache: &NodeCache, ob: &mut dyn Writer, flags: OutputFlags) -> Result<()> { 715 | if self.is_destructor { 716 | write!(ob, "`dynamic atexit destructor for ")?; 717 | } else { 718 | write!(ob, "`dynamic initializer for ")?; 719 | } 720 | 721 | match self.identifier { 722 | DynamicStructorIdentifier::Variable(variable) => { 723 | write!(ob, "`")?; 724 | variable.resolve(cache).output(cache, ob, flags)?; 725 | write!(ob, "''")?; 726 | } 727 | DynamicStructorIdentifier::Name(name) => { 728 | write!(ob, "'")?; 729 | name.resolve(cache).output(cache, ob, flags)?; 730 | write!(ob, "''")?; 731 | } 732 | } 733 | 734 | Ok(()) 735 | } 736 | } 737 | 738 | #[derive(Clone, Default)] 739 | pub(crate) struct NamedIdentifierNode<'alloc> { 740 | pub(crate) template_params: TemplateParameters, 741 | pub(crate) name: &'alloc str, 742 | } 743 | 744 | impl WriteableNode for NamedIdentifierNode<'_> { 745 | fn output(&self, cache: &NodeCache, ob: &mut dyn Writer, flags: OutputFlags) -> Result<()> { 746 | write!(ob, "{}", self.name)?; 747 | self.template_params.output(cache, ob, flags) 748 | } 749 | } 750 | 751 | #[derive(Clone, Copy, Default)] 752 | pub(crate) struct IntrinsicFunctionIdentifierNode { 753 | pub(crate) template_params: TemplateParameters, 754 | pub(crate) operator: Option, 755 | } 756 | 757 | impl IntrinsicFunctionIdentifierNode { 758 | #[must_use] 759 | pub(crate) fn new(operator: Option) -> Self { 760 | Self { 761 | operator, 762 | ..Default::default() 763 | } 764 | } 765 | } 766 | 767 | impl WriteableNode for IntrinsicFunctionIdentifierNode { 768 | fn output(&self, cache: &NodeCache, ob: &mut dyn Writer, flags: OutputFlags) -> Result<()> { 769 | if let Some(operator) = self.operator { 770 | let op = match operator { 771 | IntrinsicFunctionKind::New => "operator new", 772 | IntrinsicFunctionKind::Delete => "operator delete", 773 | IntrinsicFunctionKind::Assign => "operator=", 774 | IntrinsicFunctionKind::RightShift => "operator>>", 775 | IntrinsicFunctionKind::LeftShift => "operator<<", 776 | IntrinsicFunctionKind::LogicalNot => "operator!", 777 | IntrinsicFunctionKind::Equals => "operator==", 778 | IntrinsicFunctionKind::NotEquals => "operator!=", 779 | IntrinsicFunctionKind::ArraySubscript => "operator[]", 780 | IntrinsicFunctionKind::Pointer => "operator->", 781 | IntrinsicFunctionKind::Increment => "operator++", 782 | IntrinsicFunctionKind::Decrement => "operator--", 783 | IntrinsicFunctionKind::Minus => "operator-", 784 | IntrinsicFunctionKind::Plus => "operator+", 785 | IntrinsicFunctionKind::Dereference => "operator*", 786 | IntrinsicFunctionKind::BitwiseAnd => "operator&", 787 | IntrinsicFunctionKind::MemberPointer => "operator->*", 788 | IntrinsicFunctionKind::Divide => "operator/", 789 | IntrinsicFunctionKind::Modulus => "operator%", 790 | IntrinsicFunctionKind::LessThan => "operator<", 791 | IntrinsicFunctionKind::LessThanEqual => "operator<=", 792 | IntrinsicFunctionKind::GreaterThan => "operator>", 793 | IntrinsicFunctionKind::GreaterThanEqual => "operator>=", 794 | IntrinsicFunctionKind::Comma => "operator,", 795 | IntrinsicFunctionKind::Parens => "operator()", 796 | IntrinsicFunctionKind::BitwiseNot => "operator~", 797 | IntrinsicFunctionKind::BitwiseXor => "operator^", 798 | IntrinsicFunctionKind::BitwiseOr => "operator|", 799 | IntrinsicFunctionKind::LogicalAnd => "operator&&", 800 | IntrinsicFunctionKind::LogicalOr => "operator||", 801 | IntrinsicFunctionKind::TimesEqual => "operator*=", 802 | IntrinsicFunctionKind::PlusEqual => "operator+=", 803 | IntrinsicFunctionKind::MinusEqual => "operator-=", 804 | IntrinsicFunctionKind::DivEqual => "operator/=", 805 | IntrinsicFunctionKind::ModEqual => "operator%=", 806 | IntrinsicFunctionKind::RshEqual => "operator>>=", 807 | IntrinsicFunctionKind::LshEqual => "operator<<=", 808 | IntrinsicFunctionKind::BitwiseAndEqual => "operator&=", 809 | IntrinsicFunctionKind::BitwiseOrEqual => "operator|=", 810 | IntrinsicFunctionKind::BitwiseXorEqual => "operator^=", 811 | IntrinsicFunctionKind::VbaseDtor => "`vbase dtor'", 812 | IntrinsicFunctionKind::VecDelDtor => "`vector deleting dtor'", 813 | IntrinsicFunctionKind::DefaultCtorClosure => "`default constructor closure'", 814 | IntrinsicFunctionKind::ScalarDelDtor => "`scalar deleting dtor'", 815 | IntrinsicFunctionKind::VecCtorIter => "`vector ctor iterator'", 816 | IntrinsicFunctionKind::VecDtorIter => "`vector dtor iterator'", 817 | IntrinsicFunctionKind::VecVbaseCtorIter => "`vector vbase ctor iterator'", 818 | IntrinsicFunctionKind::VdispMap => "`virtual displacement map'", 819 | IntrinsicFunctionKind::EHVecCtorIter => "`eh vector ctor iterator'", 820 | IntrinsicFunctionKind::EHVecDtorIter => "`eh vector dtor iterator'", 821 | IntrinsicFunctionKind::EHVecVbaseCtorIter => "`eh vector vbase ctor iterator'", 822 | IntrinsicFunctionKind::CopyCtorClosure => "`copy ctor closure'", 823 | IntrinsicFunctionKind::LocalVftableCtorClosure => "`local vftable ctor closure'", 824 | IntrinsicFunctionKind::ArrayNew => "operator new[]", 825 | IntrinsicFunctionKind::ArrayDelete => "operator delete[]", 826 | IntrinsicFunctionKind::ManVectorCtorIter => "`managed vector ctor iterator'", 827 | IntrinsicFunctionKind::ManVectorDtorIter => "`managed vector dtor iterator'", 828 | IntrinsicFunctionKind::EHVectorCopyCtorIter => "`EH vector copy ctor iterator'", 829 | IntrinsicFunctionKind::EHVectorVbaseCopyCtorIter => { 830 | "`EH vector vbase copy ctor iterator'" 831 | } 832 | IntrinsicFunctionKind::VectorCopyCtorIter => "`vector copy ctor iterator'", 833 | IntrinsicFunctionKind::VectorVbaseCopyCtorIter => { 834 | "`vector vbase copy constructor iterator'" 835 | } 836 | IntrinsicFunctionKind::ManVectorVbaseCopyCtorIter => { 837 | "`managed vector vbase copy constructor iterator'" 838 | } 839 | IntrinsicFunctionKind::CoAwait => "operator co_await", 840 | IntrinsicFunctionKind::Spaceship => "operator<=>", 841 | }; 842 | write!(ob, "{op}")?; 843 | } 844 | self.template_params.output(cache, ob, flags) 845 | } 846 | } 847 | 848 | #[derive(Clone, Default)] 849 | pub(crate) struct LiteralOperatorIdentifierNode<'alloc> { 850 | pub(crate) template_params: TemplateParameters, 851 | pub(crate) name: &'alloc str, 852 | } 853 | 854 | impl WriteableNode for LiteralOperatorIdentifierNode<'_> { 855 | fn output(&self, cache: &NodeCache, ob: &mut dyn Writer, flags: OutputFlags) -> Result<()> { 856 | write!(ob, "operator \"\"{}", self.name)?; 857 | self.template_params.output(cache, ob, flags) 858 | } 859 | } 860 | 861 | #[derive(Clone, Copy, Default)] 862 | pub(crate) struct LocalStaticGuardIdentifierNode { 863 | pub(crate) template_params: TemplateParameters, 864 | pub(crate) is_thread: bool, 865 | pub(crate) scope_index: u32, 866 | } 867 | 868 | impl WriteableNode for LocalStaticGuardIdentifierNode { 869 | fn output(&self, _: &NodeCache, ob: &mut dyn Writer, _: OutputFlags) -> Result<()> { 870 | if self.is_thread { 871 | write!(ob, "`local static thread guard'")?; 872 | } else { 873 | write!(ob, "`local static guard'")?; 874 | } 875 | 876 | if self.scope_index > 0 { 877 | write!(ob, "{{{}}}", self.scope_index)?; 878 | } 879 | 880 | Ok(()) 881 | } 882 | } 883 | 884 | #[derive(Clone, Copy, Default)] 885 | pub(crate) struct ConversionOperatorIdentifierNode { 886 | pub(crate) template_params: TemplateParameters, 887 | pub(crate) target_type: Option>, 888 | } 889 | 890 | impl WriteableNode for ConversionOperatorIdentifierNode { 891 | fn output(&self, cache: &NodeCache, ob: &mut dyn Writer, flags: OutputFlags) -> Result<()> { 892 | write!(ob, "operator")?; 893 | self.template_params.output(cache, ob, flags)?; 894 | write!(ob, " ")?; 895 | 896 | if let Some(target_type) = self.target_type.map(|x| x.resolve(cache)) { 897 | target_type.output(cache, ob, flags)?; 898 | } 899 | 900 | Ok(()) 901 | } 902 | } 903 | 904 | #[derive(Clone, Copy, Default)] 905 | pub(crate) struct StructorIdentifierNode { 906 | pub(crate) template_params: TemplateParameters, 907 | pub(crate) class: Option>, 908 | pub(crate) is_destructor: bool, 909 | } 910 | 911 | impl WriteableNode for StructorIdentifierNode { 912 | fn output(&self, cache: &NodeCache, ob: &mut dyn Writer, flags: OutputFlags) -> Result<()> { 913 | if self.is_destructor { 914 | write!(ob, "~")?; 915 | } 916 | if let Some(class) = self.class { 917 | class.resolve(cache).output(cache, ob, flags)?; 918 | } 919 | self.template_params.output(cache, ob, flags) 920 | } 921 | } 922 | 923 | #[derive(Clone, Copy, Default)] 924 | pub(crate) struct RttiBaseClassDescriptorNode { 925 | pub(crate) template_params: TemplateParameters, 926 | pub(crate) nv_offset: u32, 927 | pub(crate) vbptr_offset: i32, 928 | pub(crate) vbtable_offset: u32, 929 | pub(crate) flags: u32, 930 | } 931 | 932 | impl WriteableNode for RttiBaseClassDescriptorNode { 933 | fn output(&self, _: &NodeCache, ob: &mut dyn Writer, _: OutputFlags) -> Result<()> { 934 | write!( 935 | ob, 936 | "`RTTI Base Class Descriptor at ({}, {}, {}, {})'", 937 | self.nv_offset, self.vbptr_offset, self.vbtable_offset, self.flags 938 | )?; 939 | Ok(()) 940 | } 941 | } 942 | 943 | #[derive(Clone, Default)] 944 | pub(crate) struct NodeArrayNode<'alloc> { 945 | pub(crate) nodes: &'alloc [NodeHandle], 946 | } 947 | 948 | impl NodeArrayNode<'_> { 949 | pub(crate) fn do_output( 950 | &self, 951 | cache: &NodeCache, 952 | ob: &mut dyn Writer, 953 | flags: OutputFlags, 954 | separator: &str, 955 | ) -> Result<()> { 956 | if let Some((&first, rest)) = self.nodes.split_first() { 957 | first.resolve(cache).output(cache, ob, flags)?; 958 | for &node in rest { 959 | write!(ob, "{separator}")?; 960 | node.resolve(cache).output(cache, ob, flags)?; 961 | } 962 | } 963 | Ok(()) 964 | } 965 | } 966 | 967 | impl WriteableNode for NodeArrayNode<'_> { 968 | fn output(&self, cache: &NodeCache, ob: &mut dyn Writer, flags: OutputFlags) -> Result<()> { 969 | self.do_output(cache, ob, flags, ", ") 970 | } 971 | } 972 | 973 | #[derive(Clone, Copy)] 974 | pub(crate) struct QualifiedNameNode { 975 | pub(crate) components: NodeHandle, 976 | } 977 | 978 | impl QualifiedNameNode { 979 | #[must_use] 980 | pub(crate) fn get_unqualified_identifier( 981 | self, 982 | cache: &NodeCache<'_>, 983 | ) -> Option> { 984 | let components = self.components.resolve(cache); 985 | if let Some(&node) = components.nodes.last() { 986 | node.downcast(cache) 987 | } else { 988 | None 989 | } 990 | } 991 | 992 | pub(crate) fn synthesize_from_id<'alloc>( 993 | allocator: &'alloc Bump, 994 | cache: &mut NodeCache<'alloc>, 995 | identifier: NodeHandle, 996 | ) -> Result { 997 | let components = cache.intern(NodeArrayNode { 998 | nodes: alloc::allocate_slice(allocator, &[identifier.into()]), 999 | })?; 1000 | Ok(Self { components }) 1001 | } 1002 | 1003 | pub(crate) fn synthesize_from_name<'alloc, 'string: 'alloc>( 1004 | allocator: &'alloc Bump, 1005 | cache: &mut NodeCache<'alloc>, 1006 | name: &'string str, 1007 | ) -> Result { 1008 | let id = cache.intern(NamedIdentifierNode { 1009 | name, 1010 | ..Default::default() 1011 | })?; 1012 | Self::synthesize_from_id(allocator, cache, id.into()) 1013 | } 1014 | } 1015 | 1016 | impl WriteableNode for QualifiedNameNode { 1017 | fn output(&self, cache: &NodeCache, ob: &mut dyn Writer, flags: OutputFlags) -> Result<()> { 1018 | self.components 1019 | .resolve(cache) 1020 | .do_output(cache, ob, flags, "::") 1021 | } 1022 | } 1023 | 1024 | #[derive(Clone, Default)] 1025 | pub(crate) struct TemplateParameterReferenceNode { 1026 | pub(crate) symbol: Option>, 1027 | pub(crate) thunk_offsets: ManuallyDrop>, // it's safe to ignore dropping here: i64 is trivial 1028 | pub(crate) affinity: Option, 1029 | #[allow(unused)] 1030 | pub(crate) is_member_pointer: bool, 1031 | } 1032 | 1033 | impl WriteableNode for TemplateParameterReferenceNode { 1034 | fn output(&self, cache: &NodeCache, ob: &mut dyn Writer, flags: OutputFlags) -> Result<()> { 1035 | if !self.thunk_offsets.is_empty() { 1036 | write!(ob, "{{")?; 1037 | } else if self.affinity.is_some_and(|x| x == PointerAffinity::Pointer) { 1038 | write!(ob, "&")?; 1039 | } 1040 | 1041 | if let Some(symbol) = self.symbol.map(|x| x.resolve(cache)) { 1042 | symbol.output(cache, ob, flags)?; 1043 | if !self.thunk_offsets.is_empty() { 1044 | write!(ob, ", ")?; 1045 | } 1046 | } 1047 | 1048 | if let Some((&first, rest)) = self.thunk_offsets.split_first() { 1049 | write!(ob, "{first}")?; 1050 | for offset in rest { 1051 | write!(ob, ", {offset}")?; 1052 | } 1053 | write!(ob, "}}")?; 1054 | } 1055 | 1056 | Ok(()) 1057 | } 1058 | } 1059 | 1060 | #[derive(Clone, Copy, Default)] 1061 | pub(crate) struct IntegerLiteralNode { 1062 | pub(crate) value: u64, 1063 | pub(crate) is_negative: bool, 1064 | } 1065 | 1066 | impl WriteableNode for IntegerLiteralNode { 1067 | fn output(&self, _: &NodeCache, ob: &mut dyn Writer, _: OutputFlags) -> Result<()> { 1068 | let sign = if self.is_negative { "-" } else { "" }; 1069 | write!(ob, "{sign}{}", self.value)?; 1070 | Ok(()) 1071 | } 1072 | } 1073 | 1074 | #[derive(Clone, Copy)] 1075 | pub(crate) struct Md5SymbolNode { 1076 | pub(crate) name: NodeHandle, 1077 | } 1078 | 1079 | impl WriteableNode for Md5SymbolNode { 1080 | fn output(&self, cache: &NodeCache, ob: &mut dyn Writer, flags: OutputFlags) -> Result<()> { 1081 | self.name.resolve(cache).output(cache, ob, flags) 1082 | } 1083 | } 1084 | 1085 | #[derive(Clone, Copy)] 1086 | pub(crate) struct SpecialTableSymbolNode { 1087 | pub(crate) name: NodeHandle, 1088 | pub(crate) target_name: Option>, 1089 | pub(crate) quals: Qualifiers, 1090 | } 1091 | 1092 | impl WriteableNode for SpecialTableSymbolNode { 1093 | fn output(&self, cache: &NodeCache, ob: &mut dyn Writer, flags: OutputFlags) -> Result<()> { 1094 | if !flags.name_only() { 1095 | self.quals.output(ob, flags, false, true)?; 1096 | } 1097 | self.name.resolve(cache).output(cache, ob, flags)?; 1098 | if let Some(target_name) = self.target_name.map(|x| x.resolve(cache)) { 1099 | write!(ob, "{{for `")?; 1100 | target_name.output(cache, ob, flags)?; 1101 | write!(ob, "'}}")?; 1102 | } 1103 | Ok(()) 1104 | } 1105 | } 1106 | 1107 | #[derive(Clone, Copy)] 1108 | pub(crate) struct LocalStaticGuardVariableNode { 1109 | pub(crate) name: NodeHandle, 1110 | #[allow(unused)] 1111 | pub(crate) is_visible: bool, 1112 | } 1113 | 1114 | impl WriteableNode for LocalStaticGuardVariableNode { 1115 | fn output(&self, cache: &NodeCache, ob: &mut dyn Writer, flags: OutputFlags) -> Result<()> { 1116 | self.name.resolve(cache).output(cache, ob, flags) 1117 | } 1118 | } 1119 | 1120 | #[derive(Clone)] 1121 | pub(crate) struct EncodedStringLiteralNode<'alloc> { 1122 | pub(crate) name: Option>, 1123 | pub(crate) decoded_string: &'alloc str, 1124 | pub(crate) is_truncated: bool, 1125 | pub(crate) char: CharKind, 1126 | } 1127 | 1128 | impl WriteableNode for EncodedStringLiteralNode<'_> { 1129 | fn output(&self, _: &NodeCache, ob: &mut dyn Writer, _: OutputFlags) -> Result<()> { 1130 | let prefix = match self.char { 1131 | CharKind::Wchar => "L\"", 1132 | CharKind::Char => "\"", 1133 | CharKind::Char16 => "u\"", 1134 | CharKind::Char32 => "U\"", 1135 | }; 1136 | let truncation = if self.is_truncated { "..." } else { "" }; 1137 | write!(ob, "{prefix}{}\"{truncation}", self.decoded_string)?; 1138 | Ok(()) 1139 | } 1140 | } 1141 | 1142 | #[derive(Clone, Copy)] 1143 | pub(crate) enum VariableSymbolName { 1144 | Qualified(NodeHandle), 1145 | TypeDescriptor, 1146 | } 1147 | 1148 | impl From> for VariableSymbolName { 1149 | fn from(value: NodeHandle) -> Self { 1150 | Self::Qualified(value) 1151 | } 1152 | } 1153 | 1154 | #[derive(Clone, Copy, Default)] 1155 | pub(crate) struct VariableSymbolNode { 1156 | pub(crate) name: Option, 1157 | pub(crate) sc: Option, 1158 | pub(crate) r#type: Option>, 1159 | } 1160 | 1161 | impl VariableSymbolNode { 1162 | pub(crate) fn synthesize<'alloc, 'string: 'alloc>( 1163 | allocator: &'alloc Bump, 1164 | cache: &mut NodeCache<'alloc>, 1165 | r#type: NodeHandle, 1166 | variable_name: &'string str, 1167 | ) -> Result { 1168 | let name = { 1169 | let x = QualifiedNameNode::synthesize_from_name(allocator, cache, variable_name)?; 1170 | cache.intern(x)? 1171 | }; 1172 | 1173 | Ok(Self { 1174 | name: Some(name.into()), 1175 | sc: None, 1176 | r#type: Some(r#type), 1177 | }) 1178 | } 1179 | } 1180 | 1181 | impl WriteableNode for VariableSymbolNode { 1182 | fn output(&self, cache: &NodeCache, ob: &mut dyn Writer, flags: OutputFlags) -> Result<()> { 1183 | let (access_spec, is_static) = match self.sc { 1184 | Some(StorageClass::PrivateStatic) => (Some("private"), true), 1185 | Some(StorageClass::PublicStatic) => (Some("public"), true), 1186 | Some(StorageClass::ProtectedStatic) => (Some("protected"), true), 1187 | _ => (None, false), 1188 | }; 1189 | 1190 | if !flags.no_access_specifier() && !flags.name_only() { 1191 | if let Some(access_spec) = access_spec { 1192 | write!(ob, "{access_spec}: ")?; 1193 | } 1194 | } 1195 | if !flags.no_member_type() && !flags.name_only() && is_static { 1196 | write!(ob, "static ")?; 1197 | } 1198 | 1199 | let name = self 1200 | .name 1201 | .expect("VariableSymbolNode should have a name by this point"); 1202 | match name { 1203 | VariableSymbolName::Qualified(name) => { 1204 | let r#type = (!flags.no_variable_type() && !flags.name_only()) 1205 | .then(|| self.r#type.map(|x| x.resolve(cache))) 1206 | .flatten(); 1207 | if let Some(r#type) = r#type { 1208 | r#type.output_pre(cache, ob, flags)?; 1209 | super::output_space_if_necessary(ob)?; 1210 | } 1211 | name.resolve(cache).output(cache, ob, flags)?; 1212 | if let Some(r#type) = r#type { 1213 | r#type.output_post(cache, ob, flags)?; 1214 | } 1215 | } 1216 | VariableSymbolName::TypeDescriptor => { 1217 | if let Some(r#type) = self.r#type { 1218 | r#type.resolve(cache).output(cache, ob, flags)?; 1219 | } 1220 | if !flags.name_only() { 1221 | super::output_space_if_necessary(ob)?; 1222 | write!(ob, "`RTTI Type Descriptor Name'")?; 1223 | } 1224 | } 1225 | } 1226 | 1227 | Ok(()) 1228 | } 1229 | } 1230 | 1231 | #[derive(Clone, Copy)] 1232 | pub(crate) struct FunctionSymbolNode { 1233 | pub(crate) name: Option>, 1234 | pub(crate) signature: NodeHandle, 1235 | } 1236 | 1237 | impl WriteableNode for FunctionSymbolNode { 1238 | fn output(&self, cache: &NodeCache, ob: &mut dyn Writer, flags: OutputFlags) -> Result<()> { 1239 | self.signature.resolve(cache).output_pre(cache, ob, flags)?; 1240 | super::output_space_if_necessary(ob)?; 1241 | if let Some(name) = self.name { 1242 | name.resolve(cache).output(cache, ob, flags)?; 1243 | } 1244 | self.signature.resolve(cache).output_post(cache, ob, flags) 1245 | } 1246 | } 1247 | --------------------------------------------------------------------------------